Greasy Fork is available in English.

BlastarBegoner ][+

further enhancements for Kuro5hin.org

// ==UserScript==
// @name          BlastarBegoner ][+
// @namespace     
// @description   further enhancements for Kuro5hin.org
// @include       http://www.kuro5hin.org/*
// @include       https://www.kuro5hin.org/*
// @version       0.5.6
// ==/UserScript==

var ShitList = new Object();

// start config

/*
 * set to false to also kill replies to killed comments.
 */
var Orphans = true; 


/* 
 * if you wish to ignore a user, add them, like below.
 * note this is case sensitive.
 */
 
ShitList['Edmund Blackadder'] = true;
ShitList['Cancer'] = true;
ShitList['Thomas Hard'] = true;
ShitList['Riddick'] = true;
ShitList['Atari 2600'] = true;
ShitList['Horsecock Sodomy'] = true;
ShitList['horsecock sodomy'] = true;
ShitList['Pirate Ninja'] = true;
ShitList['i am a pretty big deal'] = true;

// end config


var xmlRequest = new XMLHttpRequest();

var forms = document.forms;

var rating_re = /^rating_(\d+)$/;
var user_re = /\/user\/uid:(\d+)$/;
var diary_re = /\/user\/([a-zA-Z0-9% ]+)\/diary$/;


// check if there's anybody on the shit list.

var DoSL = false;
for (var i in ShitList)
{
	DoSL = true;
	break;
}

//utility functions to make life easier.


// creates a new option element.  
// GreaseMonkey doesn't support new Option() / select.add()
function newOption(name, value)
{
	var x = document.createElement('option');
	x.value = value;
	x.appendChild(document.createTextNode(name));
	return x;	
}

// find a specific parent element for a tag.
// k5 is chock full of nested tables and such.
function outerElement(node, tag, count)
{
	if (count == 0) return null;
	if (node == null) return null;
		
	if (node.nodeType == 1 && node.tagName == tag)
	{
		if (--count == 0) return node;	
	}	

	node = node.parentNode;
	
	return node ? outerElement(node, tag, count) : null;
	
}

// returns the first element or null if none.
//
function firstChildElement(node)
{
	if (node == null) return null;
	node = node.firstChild;
	while (node)
	{
		if (node.nodeType == 1) return node;
	}
	return null;
}

// gets the next element (tag), ignoring text, cdata, comments, etc.
function nextElement(element)
{
	while (element = element.nextSibling)
	{
		if (element.nodeType == 1) return element;
	}
	return null;
}
// gets the previous element (tag), ignoring text, cdata, comments, etc.
function previousElement(element)
{
	while (element = element.previousSibling)
	{
		if (element.nodeType == 1) return element;
	}
	return null;
}


// copys elements from a NodeList (which is live) to an array (which is not)
function nodeArray(nodeList)
{
	var array = new Array(nodeList.length);
	for (var i = 0; i < nodeList.length; i++)
		array[i] = nodeList[i];
	return array;
}

// onclick, onsubmit callbacks.

// replacement comment rater.  This uses xmlhttprequest and is done asynchronously.
function rateComments(event)
{
			
	event.preventDefault();
	var form = event.target;
	var post = '';
	var inc = new Object();
	// build up the post data.
	for (var j = 0; j < form.elements.length; j++)
	{
		var e = form.elements[j];
		if (e.name != '')
		{
			if (inc[e.name]) continue;
			inc[e.name] = true;
			post = post + '&' + encodeURIComponent(e.name) 
				+ '=' + encodeURIComponent(e.value);
		}
	}
	post = post.substr(1);
	//alert(post);
	
	xmlRequest.open('POST', form.action, true);
	xmlRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
	
	xmlRequest.onreadystatechange = function()
	{
		if (xmlRequest.readyState == 4)
		{
			//alert('done');
		}
	}
	xmlRequest.send(post);
}

// set all comment ratings to the specified rating.
function bitchSlap(event)
{

	event.preventDefault();
	var form = event.target.form;
	var value = document.getElementById('__select').value;

	for (var j = 0; j < form.elements.length; j++)
	{
		var e = form.elements[j];
		
		if (e.type == 'select-one')
		{
			if (e.name.match(rating_re))
			{
				e.value = value;
			}	
		}	
		
	}
}

// show/hide comment replies.  replies are placed in a <dl><dt> after the comment.
function showHide(event)
{
	event.preventDefault();						
	var target = event.target;
	var hide = target.firstChild.data == 'Hide Replies';
	
	target.firstChild.data = hide ? 'Show Replies' : 'Hide Replies';
		
	var comment = outerElement(target, 'TABLE', 2);
	if (comment)
	{
		comment = nextElement(comment);

		if (comment.tagName == 'DL' || comment.tagName == 'dl')
		{
			
			comment.style.visibility = hide ? 'collapse' : 'visible';	
			
		}	
		
	}	
}


// remove comments from people on the shitlist.
function killComments(form)
{
	var delta = false;
	
	var links = nodeArray(form.getElementsByTagName('a'));
	for (var j = 0; j < links.length; j++)
	{
		var link = links[j];
		//if (link.href.indexOf('uid') != -1) 
		//	alert(link.href + ' ' + link.firstChild.data);
		if (!link.href.match(user_re)) continue;
		
		var user = link.firstChild.data;
		if (!ShitList[user]) continue;
		var comment = outerElement(link, 'TABLE', 3);
		if (comment)
		{
			if (Orphans  == false)
			{
				var temp = nextElement(comment);
				if (temp && temp.tagName == 'DL')
				temp.parentNode.removeChild(temp);	
			}
			
			comment.parentNode.removeChild(comment);
			delta = true;
		}
		
	}
	if (delta)
	{
		// now go through and kill any blank indentations 
		// (<dl><dt></dt><dd></dd></dl>)
		var dls = nodeArray(form.getElementsByTagName('dl'));
		for (var j = 0; j < dls.length; j++)
		{
			var dl = dls[j];
			var kill = true;
			var x = dl.firstChild;
			while(x && kill)
			{
				if (firstChildElement(x)) kill = false;
				x = x.nextSibling();	
			}
			if (kill)
			{
				dl.parentNode.removeChild(dl);
			}		
		}
		
	}
}

// remove diary entries from people on the shitlist
function killDiaryEntries()
{
	var links = nodeArray(document.body.getElementsByTagName('a'));
	for (var j = 0; j < links.length; j++)
	{
		var match;
		var link = links[j];
		if (match = link.href.match(diary_re))
		{
			var name = match[1];
			// convert %20 to ' '
			name = decodeURIComponent(name);
			if (!ShitList[name]) continue;
			var p = outerElement(link, 'TABLE', 1);
			if (p) p.parentNode.removeChild(p);
			
		}
	}
}


// remove diary sidebar entries from people on the shitlist
function killDiarySidebars()
{

	var links = nodeArray(document.body.getElementsByTagName('a'));
	for (var j = 0; j < links.length; j++)
	{
		var match;
		var link = links[j];
		if (match = link.href.match(diary_re))
		{
			var name = match[1];
			// convert %20 to ' '
			name = decodeURIComponent(name);
			if (!ShitList[name]) continue;
			var p = outerElement(link, 'P', 1);
			if (p) p.parentNode.removeChild(p);
			
		}
	}
}

// add a link to show/hide child comments.
function addShowHide(form)
{
	// I wanted to base this on the "rate" button, but that fails
	// for your own comments.... sigh...
	
	var links = nodeArray(form.getElementsByTagName('a'));	
	
	for (var j = 0; j < links.length; j++)
	{
		var link = links[j];
		if (link.href.indexOf('/post#here') == -1) continue;
		
		var comment = outerElement(link, 'TABLE', 2);		
		// check for any replies...

		var temp = nextElement(comment);
		if (temp && temp.tagName == 'DL')
		{			
			var parent = link.parentNode;
			
			parent.insertBefore(document.createTextNode(' '), link);
			var newlink = parent.insertBefore(document.createElement('a'), link);
			newlink.href = '#';
			newlink.appendChild(document.createTextNode('Hide Replies'));
			newlink.addEventListener('click', showHide, true);	
			
			parent.insertBefore(document.createTextNode(' | '), link);

								
		}	
	}
		
}


if (window.location.href.indexOf('/story/') > 0 
	|| window.location.href.indexOf('/comments/') > 0 )
{
		
	for (var i = 0; i < forms.length; i++)
	{
		var form = forms[i];
		if (form.name == 'rate')
		{
			// 1. posting will be done via xmlhttprequest.
			
			form.addEventListener('submit', rateComments, true);
			
			// 4 kills comments from users on the shit list.
			// this will not kill any replies, however, 
			// which it probably should do as well.
			
			if (DoSL) killComments(form);
			
			
			// - - - - - - - - 
			
			// 2a add a reset button
			// 2b. add dhtml button to rate all comments.
			
			var div = document.createElement('div');
			var x;
	
			div.style.textAlign = 'center';
			form.insertBefore(div, form.firstChild);
			x = div.appendChild(document.createElement('input'));
			x.type = 'reset';
			x.value = 'Reset Ratings';
			
			div.appendChild(document.createTextNode(' '));
			
			x = div.appendChild(document.createElement('select'));
			x.id = '__select';
			
			// greasemonkey can't use the select.add() method.
						
			x.appendChild(newOption('None', 'none'));
			x.appendChild(newOption('Hide', 0));
			x.appendChild(newOption('Discourage', 1));
			x.appendChild(newOption('Neutral', 2));
			x.appendChild(newOption('Encourage', 3));
			
			div.appendChild(document.createTextNode(' '));
	
			x = div.appendChild(document.createElement('input'));
			x.type = 'button';
			x.value = 'Set Ratings';
			
			x.addEventListener('click', bitchSlap, true);
			
			// - - - - - - - - 

			// 3. add show/hide replies link
			addShowHide(form);
			
			
		}
		
	}
}// /story/ page
else
{
	// kill off diary pages from people in the shitlist
	if (DoSL)
	    if (window.location.href.indexOf('/section/') > 0)
		killDiaryEntries();
	    else
		killDiarySidebars();
	
}

// fix up indentation a bit.
GM_addStyle('dd { padding-left: 0px; margin-left: 0px;}');
GM_addStyle('dl { padding-left: 1.5em; margin-left: 0px;}');