Stack Exchange: "View Vote totals" without 1000 rep

Enables the total vote counts feature without requiring an account or 1k+ reputation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// ==UserScript==
// @name           Stack Exchange: "View Vote totals" without 1000 rep
// @namespace      Rob W
// @description    Enables the total vote counts feature without requiring an account or 1k+ reputation.
// @match          *://*.stackoverflow.com/questions/*
// @match          *://superuser.com/questions/*
// @match          *://meta.superuser.com/questions/*
// @match          *://serverfault.com/questions/*
// @match          *://meta.serverfault.com/questions/*
// @match          *://askubuntu.com/questions/*
// @match          *://meta.askubuntu.com/questions/*
// @match          *://mathoverflow.net/questions/*
// @match          *://meta.mathoverflow.net/questions/*
// @match          *://*.stackexchange.com/questions/*
// @match          *://answers.onstartups.com/questions/*
// @match          *://meta.answers.onstartups.com/questions/*
// @match          *://stackapps.com/questions/*
// @match          *://*.stackoverflow.com/review/*
// @match          *://superuser.com/review/*
// @match          *://meta.superuser.com/review/*
// @match          *://serverfault.com/review/*
// @match          *://meta.serverfault.com/review/*
// @match          *://askubuntu.com/review/*
// @match          *://meta.askubuntu.com/review/*
// @match          *://mathoverflow.net/review/*
// @match          *://meta.mathoverflow.net/review/*
// @match          *://*.stackexchange.com/review/*
// @match          *://answers.onstartups.com/review/*
// @match          *://meta.answers.onstartups.com/review/*
// @match          *://stackapps.com/review/*
// @match          *://*.stackoverflow.com/search*
// @match          *://superuser.com/search*
// @match          *://meta.superuser.com/search*
// @match          *://serverfault.com/search*
// @match          *://meta.serverfault.com/search*
// @match          *://askubuntu.com/search*
// @match          *://meta.askubuntu.com/search*
// @match          *://mathoverflow.net/search*
// @match          *://meta.mathoverflow.net/search*
// @match          *://*.stackexchange.com/search*
// @match          *://answers.onstartups.com/search*
// @match          *://meta.answers.onstartups.com/search*
// @match          *://stackapps.com/search*
// @exclude        *://area51.stackexchange.com/*
// @author         Rob Wu <rob@robwu.nl>
// @version        1.5.7
// @website        http://stackapps.com/q/3082/9699
// @run-at         document-end
// @grant          none
// ==/UserScript==

// Chrome extension: https://chrome.google.com/webstore/detail/oibfliilcglieepgkdkahpfiiigdijdd
// Greasemonkey script: https://greasyfork.org/en/scripts/6192-stack-exchange-view-vote-totals-without-1000-rep
// Bookmarklet: http://web.archive.org/web/20131104100735/http://userscripts.org/scripts/show/125051

// @history        06-feb-2012 Release
// @history        07-feb-2012 Added CSS fix for IE7-. Added reference to optimized bookmarklet.
// @history        07-feb-2012 Modified click handler, so that it's also working after voting.
// @history        13-jun-2012 Upgraded to the SE 2.0 API (from 1.1)
// @history        10-nov-2012 Released Chrome extension
// @history        16-nov-2012 Added support for /review/ (previously limited to /questions/ only)
// @history        16-nov-2012 Added support for older jQuery versions (1.4.4 at Area 51)
// @history        16-nov-2012 Added support for /review/ (Suggested edits)
// @history        19-nov-2012 Corrected one-letter typo
// @history        12-dec-2012 Corrected $.fn.click override: .call replaced with .apply
// @history        23-may-2013 Added support for /questions/tagged/... and /search
// @history        05-sep-2013 Added Math Overflow to list of sites
// @history        21-feb-2014 Use https SE API on https SE sites.
// @history        18-jun-2014 Replace http with * to match https as well.
// @history        31-oct-2014 Use *.stackoverflow.com to match pt.stackoverflow.com and others.

/*
 * How does this script work?
 * - The Vote count feature is detected through the existence of the
 *          tooltip (title) on the Vote Count buttons.
 *  When the title is present, the script does not have any side-effects.
 *  When the title is absent, this script adds the feature:
 * 1) A style is appended to the head, which turns the cursor in a pointer on the 
 *      Vote Count elements.
 * 2) One global event listener is bound to the document.
 *    This click listener captures clicks on the Vote Count elements (now + future)
 * 3) On click, the vote counts are requested through the Stack Exchange API.
 *    The response is parsed, and the vote totals are shown.
 *    A tooltip is also added, in accordance with the normal behaviour.
 *    As a result, the Vote count button is not affected by the script any more.
 * 
 *  Usually, when a user casts a vote, the buttons are reset, and a click handler
 *  is added. This click handler is attached in a closure, and cannot be modified.
 * To prevent this method from being triggered, jQuery.fn.click() is modified.
 */

/* This script injection method is used to have one single script that works in all modern browsers.
 * Against sandboxed window-objects (Chrome)
 * The code itself has successfully been tested in:
 * Firefox 3.0 - 32.0
 * Opera 9.00 - 12.00
 * Chrome 1 - 40
 * IE 7 - 10 (for IE6, see http://web.archive.org/web/20131104100735/http://userscripts.org/scripts/show/125051)
 * Safari 3.2.3 - 5.1.7 */

/*This script can ALSO be used as a BOOKMARKLET. Just copy-paste anything below this line!*/

javascript:void(function(doc){
var head = (doc.head||doc.getElementsByTagName('head')[0]||doc.documentElement);
var style = doc.createElement('style');
var css = '/*Added through UserScript*/' + 
          '.vote-count-post{cursor:pointer;}' + 
          '.vote-count-post[title]{cursor:default;}' +
          '.vote-count-separator{height:0;*margin-left:0;}'; /* IE7- */
head.appendChild(style);

if (style.styleSheet) {
    /* This is for IE-users.*/
    style.styleSheet.cssText = css;
} else {
    style.appendChild(doc.createTextNode(css));
}


var script = doc.createElement('script');
script['textContent' in script ? 'textContent' : 'text'] = '(' + function() {
    var api_url = location.protocol + '//api.stackexchange.com/2.0/posts/';
    var api_filter_and_site = '?filter=!)q3b*aB43Xc&key=DwnkTjZvdT0qLs*o8rNDWw((&site=' + location.host;
    var canViewVotes = 1; /* Intercepts click handler when the user doesn't have 1k rep.*/
    var b = StackExchange.helpers;
    var original_click = $.fn.click;
    $.fn.click = function() {
        if (this.hasClass('vote-count-post') && !canViewVotes) return this;
        return original_click.apply(this, arguments);
    };
    var voteCountClickHandler = function(e) {
        var $this = $(this), t=this.title, $tmp;
        if (/up \/ /.test(t) || /View/.test(t) && canViewVotes) return;
        canViewVotes = 0; /* At this point, not a 1k+ user */
        var postId = $this.siblings('input[type="hidden"]').val();
        if (!postId) {
            // At /review/, for instance:
            $tmp = $this.closest('[data-questionid],[data-answerid]');
            postId = $tmp.attr('data-answerid') || $tmp.attr('data-questionid');
        }
        if (!postId) {
            // At /review/ of Suggested edits
            $tmp =  $this.closest('.suggested-edit');
            postId = $.trim($tmp.find('.post-id').text());
        }
        if (!postId) {
            // At /questions/tagged/....
            // At /search
            $tmp = $this.closest('.question-summary');
            postId = /\d+/.exec($tmp.attr('id'));
            postId = postId && postId[0];
        }   
        if (!postId) {
            console.error('Post ID not found! Please report this at http://stackapps.com/q/3082/9699');
            return;
        }
        b.addSpinner($this);
        $.ajax({
            type: 'GET',
            url: api_url + postId + api_filter_and_site + '&callback=?', /* JSONP for cross-site requests */
            dataType: 'json',
            success: function(json) {
                json = json.items[0];
                var up = json.up_vote_count, down = json.down_vote_count;
                up = up ? '+' + up : 0;       /* If up > 0, prefix a plus sign*/
                down = down ? '-' + down : 0; /* If down > 0, prefix a minus sign */
                $('.error-notification').fadeOut('fast', function() {
                    $(this).remove();
                });
                $this.css('cursor','default').attr('title', up + ' up / ' + down + ' down')
                     .html('<div style="color:green">' + up + '</div>' +
                           '<div class="vote-count-separator"></div>'  +
                           '<div style="color:maroon">' + down + '</div>');
            },
            error: function(N) {
                b.removeSpinner();
                b.showErrorPopup($this.parent(), N.responseText && N.responseText.length < 100 ?
                        N.responseText : 'An error occurred during vote count fetch');
            }
        });
        e.stopImmediatePropagation();
    };
    if ($.fn.on) $(document).on('click', '.vote-count-post', voteCountClickHandler);
    else { // For old jQuery versions, e.g. Area 51
        $(document).delegate('.vote-count-post', 'click', voteCountClickHandler);
    }
} + ')();';

head.appendChild(script);
script.parentNode.removeChild(script);
})(document);