BlastarBegoner ][+

further enhancements for Kuro5hin.org

目前為 2014-05-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name          BlastarBegoner ][+
// @namespace     
// @description   further enhancements for Kuro5hin.org
// @include       http://www.kuro5hin.org/*
// @include       https://www.kuro5hin.org/*
// @version       0.5.2.1
// ==/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['Skeletor'] = true;
ShitList['Thomas Hard'] = true;
ShitList['Riddick'] = true;
ShitList['Atari 2600'] = true;
ShitList['Horsecock Sodomy'] = 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;}');