Reddit quickreply

By Arvid Last update Dec 18, 2006 — Installed 271 times.
// Reddit quickreply 1.1

// ==UserScript==
// @name          	Reddit quickreply
// @namespace     	http://determinist.org/greasemonkey/
// @description   	Replies to comment without reloading page. Version 1.1
// @include       	http://reddit.com/info/*/comments*
// @include       	http://*.reddit.com/info/*/comments*

// ==/UserScript==

/*

Changelog:

2006-08-14	1.1
* Now fake inserts the new comment
* Added support for quickediting

2006-08-11	1.0
* First release

*/

/*
BEGIN LICENSE BLOCK
Copyright (C) 2005 Arvid Jakobsson

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You can download a copy of the GNU General Public License at
http://www.gnu.org/licenses/gpl.html
or get a free printed copy by writing to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
END LICENSE BLOCK
*/

function xpath(query, context, doc) {
	context = context ? context : document;
	doc = doc ? doc : document;
	
	return doc.evaluate(query, context, null,
		XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}

function foreachxp(xp, fc, context) {
	var els = xpath(xp, context);
	if (els) {
		for (var i = 0; el = els.snapshotItem(i); i++) {
			fc(el);
		}
	}
}

function xpathOne(query, context, doc) {
	context = context ? context : document;
	doc = doc ? doc : document;
	
	return doc.evaluate(query, context, null,
		XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
}

function $(id) {
	return document.getElementById(id);
}

/*
const sorting = {hot:0, 'new':1, top:2};
const style = {nested:0, flat:1};

var options = getOptions();
*/

foreachxp("//tr[starts-with(@id, 'reply')]//div[2] | //form[@id='commentform']/div[2]", function (el) {
	addCommentButton(el.firstChild, 'quickreply');
});

foreachxp("//tr[starts-with(@id, 'edit')]//div[2]", function (el) {
	addCommentButton(el.firstChild, 'quicksave');
});

function addCommentButton(before, text) {
	var btn = document.createElement('button');
	btn.setAttribute('class', 'btn');
	btn.innerHTML = text;
	btn.addEventListener('click', replyQuick, true);
	
	before.parentNode.insertBefore(btn, before);
}

/*function getOptions() {
	var options = new Object();
	var setOptions = xpath("//div[@class='sortbox']/ul/li/strong");
	options['sorting'] = sorting[setOptions.snapshotItem(0).textContent];
	options['style'] = style[setOptions.snapshotItem(1).textContent];
	return options;
}*/

function replyQuick(event) {
	event.stopPropagation();
    event.preventDefault();
	
	var trg = event.target;
	var frm = xpathOne("./ancestor::form[1]", trg);
	var id = xpathOne("./ancestor::div[1]/preceding-sibling::textarea/@id", trg).value;
	id = id.match(/comment(reply[0-9]+|edit[0-9]+|root)/)[1];
	
	if (!chkcomment(id)) 
		return;
	
	var params = serializeForm(frm);
	
	GM_xmlhttpRequest({
		method: 'POST',
		url: 'http://' + location.host +'/comment',
		data: params,
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
			'Accept': 'application/atom+xml,application/xml,text/xml',
			'Content-type' : 'application/x-www-form-urlencoded'
		},
		onload: function(responseDetails) {
			GM_log(responseDetails);
			
			if (responseDetails.status == 200) {
				$('err' + id).innerHTML = 'saved.';
				
				//get new id
				var newId, rt, renderedMessage;
				rt = responseDetails.responseText;
				
				//hide comment form if id isnt root
				if (id != 'root') {
					$(id).style.display = 'none';
				}
				
				insertFakeComment(id, rt);
			}
			else {
				$('err' + id).innerHTML = 'Something went wrong, error code: ' +  reponseDetails.status;
			}
			
		},
		onerror: function(responseDetials) {
			$('err' + id).innerHTML = 'Some thing went wrong, error code: ' +  reponseDetails.status;
		},
		onreadystatechange: function(rd) { 
			//animate
			$('err' + id).innerHTML += '.';
		}
	});
	
	$('err' + id).innerHTML = 'saving...';
    
}

function serializeForm(frm) {
	var paramArr = new Array();
	
	for (var i = 0; inp = frm.elements[i]; i++) {
		var name, val;
		if ((name = inp.getAttribute('name')) && (val = inp.value))
			paramArr.push(inp.getAttribute('name') + "=" + inp.value);
	}
	
	var str = paramArr.join('&');
	return str;
}


//borrowed from reddit source with permission
function chkcomment(id) {
    ta = $("comment" + id);
    err = $("err" + id);
    if (err.innerHTML != "") err.innerHTML = ""
    if (ta == null) {return false;}
    if (ta.style.color == "gray" || ta.value == "") {
        err.innerHTML = "enter a comment";
        ta.focus();
        return false;
    }
    else if (ta.value.length > 10000) {
        err.innerHTML = "comment is too long";
        ta.focus();
        return false;
    }
    else {
        if (err.innerHTML != "") err.innerHTML = ""
    }
    return true;
}


function insertFakeComment(replyToId, responseText) {
	//create hidden iframe to steal the new comment from
	var hiddenIFrame = document.createElement('iframe');
	hiddenIFrame.setAttribute('src', "about:blank");
	hiddenIFrame.style.display = 'none';
	document.body.appendChild(hiddenIFrame);
	
	responseText = responseText.replace(/<[\s]*\/?[\s]*html[\s]*>/g, ''); 
	hiddenIFrame.contentDocument.documentElement.innerHTML = responseText;
	
	
	//add quickbuttons
	var theReply = xpathOne("//div[starts-with(@id, 'table') and table[@class='commenttable border']]",
		hiddenIFrame.contentDocument, 
		hiddenIFrame.contentDocument);
	
	var buttonbar = xpathOne(".//tr[starts-with(@id, 'reply')]//div[2]",
		theReply, 
		hiddenIFrame.contentDocument);
	addCommentButton(buttonbar.firstChild, 'quickreply');
	
	buttonbar = xpathOne(".//tr[starts-with(@id, 'edit')]//div[2]",
		theReply, 
		hiddenIFrame.contentDocument);
	addCommentButton(buttonbar.firstChild, 'quicksave');
	
	
	//insert or replace
	if (replyToId.match(/^edit/)) {
		var replaceMe = $('table' + replyToId.replace('edit', ''));
		replaceMe.parentNode.replaceChild(theReply, replaceMe);
	}
	else {
		var insertBeforeMe;
		if (replyToId != 'root')
			insertBeforeMe = $('table' + replyToId.replace('reply', '')).nextSibling;
		else 
			insertBeforeMe = xpathOne("//div[starts-with(@id, 'table')]");
			
		insertBeforeMe.parentNode.insertBefore(theReply, insertBeforeMe);
	}
		
	document.body.removeChild(hiddenIFrame);
}