Skip to content

Instantly share code, notes, and snippets.

@sizzlemctwizzle
Created February 28, 2011 00:29
Show Gist options
  • Select an option

  • Save sizzlemctwizzle/846738 to your computer and use it in GitHub Desktop.

Select an option

Save sizzlemctwizzle/846738 to your computer and use it in GitHub Desktop.
Making this script more cross-browser compatible: http://userscripts.org/scripts/show/56641
// ==UserScript==
// @name Reddit Uppers and Downers Enhanced
// @namespace mistercow
// @description Show up-votes and down-votes next to the total score on reddit comments.
// @require http://sizzlemctwizzle.com/updater.php?id=56641
// @include http://www.reddit.com/*/comments/*
// @include http://www.reddit.com/user/*
// ==/UserScript==
/*
This code is provided as is, with no warranty of any kind.
I hacked it together in one night for my own use, and have not tested it extensively.
The script can slow down comment page load time; if the lag is noticeable, you may want
to change your preferences to load fewer comments per page.
Also note that the ups and downs will not always add up to the score displayed on reddit.
I think this is because of caching on reddit's part. It's usually within one or two points though.
Update: Allan Bogh contributed code which significantly sped up the processing of comments.
1500 comments could be processed in 113ms, over previous cases of 3000+ms.
skeww contributed code which chunks the rendering of comments into 50ms time slots, then pauses
for 25ms. Additionally, he cleaned up various parts of the code to enable faster processing.
Allan included skeww's contribution and also cleaned up the code to further improve speed.
Time testing code (in comments) are left in quick user tests. 1500 records processed at 6ms,
although this may be related to when the function returns, but not when it's actually complete.
SizzleMctwizzle added code to update the up and down vote totals when you click one of the
arrows by intercepting calls to the vote function. The code gets dumped to the page in an
anonymous function so that it gets executed in the scope of the page.
Code contributors: Allan Bogh - http://www.opencodeproject.com
brasso - http://userscripts.org/scripts/show/56641
savetheclocktower - http://gist.github.com/174069
skeww (jslint, fragment, chunking) - http://kaioa.com
sizzlemctwizzle - http://sizzlemctwizzle.com
*/
// Get the URL for the JSON details of this comments page
var loc = window.location.href;
var jsonURL = loc + "/.json";
if(loc.indexOf("?") != -1) {
jsonURL = loc.replace("?","/.json?");
}
// Define a function to add styles if not in Greasemonkey
if (typeof GM_addStyle == 'undefined')
var GM_addStyle = function(css) {
var head = document.getElementsByTagName('head')[0],
style = document.createElement('style');
if (!head) {return}
style.type = 'text/css';
style.textContent = css;
head.appendChild(style);
};
var voteTable = {};
function onloadJSON(response) {
var jsonText = response.responseText,data;
// Parse the json text
// Use native JSON (if it's available) because it's much faster.
// code by savetheclocktower - http://gist.github.com/174069
if (window.JSON && JSON.parse) {
data = JSON.parse(jsonText);
} else {
data = (new Function('return ' + jsonText + ';'))();
}
// Load the vote table by processing the tree
processTree(data); //this takes up no time (4ms on 4000 records)
// Display the loaded votes
displayVotes();
};
// spend up to 50msec a time with a task, wait for 25msec and continue if necessary
var chunker = function (items, process) {
var todo = items.concat();
setTimeout(function () {
var start = Date.now();
do {
process(todo.shift());
} while (todo.length && Date.now() - start < 50);
if (todo.length) {
setTimeout(arguments.callee, 25);
}
}, 25);
};
function displayVotes(){
// Add the style sheets for up and down ratings
GM_addStyle(".moo_ups { color:rgb(255, 139, 36); font-weight:bold; }\n" +
".moo_downs { color:rgb(148,148,255); font-weight:bold; }");
var taglines,
commentID = null,
toArray;
toArray = function(col){
var a = [], i, len;
for(i=0, len=col.length; i< len; i++){
a[i] = col[i];
}
return a;
};
taglines = toArray(document.getElementsByClassName("tagline"));
chunker(taglines, function(item){
var votes, openparen, mooups, pipe, moodowns, voteDowns, voteUps, closeparen, frag;
if(item.nextSibling.nodeName === "FORM"){ // the first item is the title of the post
commentID = item.nextSibling.firstChild.value;
if(voteTable[commentID]){
frag = document.createDocumentFragment(); // using a fragment speeds this up by a factor of about 2
votes = voteTable[commentID];
openparen = document.createTextNode(" (");
frag.appendChild(openparen);
mooups = document.createElement("span");
mooups.className = "moo_ups";
voteUps = document.createTextNode(votes.ups);
mooups.appendChild(voteUps);
frag.appendChild(mooups);
pipe = document.createTextNode("|");
item.appendChild(pipe);
moodowns = document.createElement("span");
moodowns.className = "moo_downs";
voteDowns = document.createTextNode(votes.downs);
moodowns.appendChild(voteDowns);
frag.appendChild(moodowns);
closeparen = document.createTextNode(")");
frag.appendChild(closeparen);
frag.appendChild(openparen);
frag.appendChild(mooups);
frag.appendChild(pipe);
frag.appendChild(moodowns);
frag.appendChild(closeparen);
item.appendChild(frag);
}
}
});
}
// Recursively process the comment tree
function processTree(obj) {
var i, il, data, name;
if(obj instanceof Array) {
for(var i=0, il=obj.length; i < il; ++i) {
processTree(obj[i]);
}
}
data = obj.data;
if(data) { // Data found
if(isComment(obj) && data.author !== "[deleted]") {
name = data.name;
if(name) { // Store the votes in the vote table
voteTable[name] = {
downs:data.downs || 0,
ups:data.ups || 0
};
}
}
// Process any subtrees
processChildren(data);
processReplies(data);
}
};
function isComment(obj) {
return obj.kind === "t1";
};
function processChildren(data) {
var children = data.children, i, il;
if(children) {
for(i=0, il=children.length; i < il; ++i){
processTree(children[i]);
}
}
};
function processReplies(data) {
var replies = data.replies;
if(replies) processTree(replies);
};
// load the JSON
// SizzleMctwizzle made this a regular xhr request because
// cross-domain requests are unnecessary
// This makes the script cross-browser compatible
if (jsonURL.indexOf('/comscore-iframe/') === -1) {
var res = new XMLHttpRequest();
res.onreadystatechange = function() {
if (res.readyState==4 && res.status==200) onloadJSON(res);
};
res.open('GET', jsonURL, true);
res.send(null);
}
// Update vote counts when user up/down votes a comment
// Yes I made it all one giant string because other browsers don't support toString
// Injecting it in the page also gets rid of the need for unsafeWindow
var codeWrapper = '(function() {' +
'var origFnVote = $.fn.vote;' +
'$.fn.vote = function(vh, callback, event, ui_only) {' +
// Store the state of the errors before we call the
// original vote function
'if (reddit.logged &&' +
'$(this).hasClass("arrow")) {' +
'var arrow = $(this);' +
'var arrows = arrow.parent();' +
'var isUpPrev = arrows.find(".upmod").length > 0;' +
'var isDownPrev = arrows.find(".downmod").length > 0;' +
'}' +
// Call the original vote function
'origFnVote.call(this, vh, callback, event, ui_only);' +
'if (!reddit.logged &&' +
'!$(this).hasClass("arrow")) { return; }' +
'var comment = arrow.parent().parent();' +
'var upperAndDowers = comment.find(".moo_ups, .moo_downs");' +
'var upper = upperAndDowers.get(0);' +
'var downer = upperAndDowers.get(1);' +
'var ups = parseInt(upper.textContent);' +
'var downs = parseInt(downer.textContent);' +
'if (arrow.hasClass("upmod")) {' +
'upper.textContent = ++ups;' +
'if (isDownPrev) {' +
'downer.textContent = --downs;' +
'}' +
'} else if (arrow.hasClass("downmod")) {' +
'downer.textContent = ++downs;' +
'if (isUpPrev) {' +
'upper.textContent = --ups;' +
'}' +
'} else if (isUpPrev && arrow.hasClass("up")) {' +
'upper.textContent = --ups;' +
'} else if (isDownPrev && arrow.hasClass("down"))' +
'downer.textContent = --downs;' +
'}' +
'})();';
// Dump code to the page
var scriptEl = document.createElement('script');
scriptEl.type = "text/javascript";
scriptEl.textContent = codeWrapper;
document.body.appendChild(scriptEl);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment