Greasy Fork is available in English.

diediedead

killfile script, re-rebooted

// diediedead.user.js
//
// Written by: Michael Devore
// Released to the public domain
//
// This is a Greasemonkey script.
// See http://www.greasespot.net/ for more information on Greasemonkey.
//
// ==UserScript==
// @name			diediedead
// @namespace		http://www.devoresoftware.com/gm/dddead
// @description		killfile script, re-rebooted
// @match			https://*.metafilter.com/*
// @match			http://*.metafilter.com/*
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_setValue 
// @grant GM_getValue 
// @run-at document-start
// @version 3.4
// ==/UserScript==
//

"use strict";
 
GM_addStyle('body{display:none;}');
GM_addStyle('#div_configureBox{\
	position: fixed;\
	left: 50%;\
	margin-left: -11em;\
//	top: 5px;\
	bottom: 5px;\
	color: black;\
	background-color: white;\
	border: 2px green solid;\
	padding: 2px;\
	opacity: 0.95;\
}');
GM_addStyle('.textarea_configureBox{\
	width: 20em;\
	height: 5.34em;\
	margin-left: 5px;\
	margin-right: 5px;\
	border: 2px black solid;\
	color: black;\
	background-color: white;\
	white-space: pre;\
	word-wrap: normal;\
	overflow-x: scroll;\
}');
GM_addStyle('.header_configureBox{\
	text-align: center;\
}');
GM_addStyle('.button_configureBox{\
	display:block;\
	margin: 0 auto;\
}');

var filterActionEnum =
{
	NONE:				"none",
	POSTSANDCOMMENTS:	"postsandcomments",
	POSTSONLY:			"postsonly",
	COMMENTSONLY:		"commentsonly"
};
Object.freeze(filterActionEnum);
var filterAction = filterActionEnum.POSTSANDCOMMENTS;
var noActiveSelectionText = "<no active selection>";
var showAllContextText = "Show all content";
var filterPostsAndComments = "Filter posts & comments";
var filterPostsOnly = "Filter posts only";
var filterCommentsOnly = "Filter comments only";
var configureFilters = "Configure filters";
var filterForUser = "Filter for user ";
var filterForText = "Filter for text ";
var filterForSite = "Filter for site ";
var noneText = "none";
var blockText = "block";

var userFilters = new Array();
var textFilters = new Array();
var siteFilters = new Array();

function onLoaded()
{
	buildContextMenu();
	buildConfigureBox();
	loadConfiguration();
	performFiltering();
	GM_addStyle('body{display:block;}');
}

function contextClick(e)
{
	var menuitem = e.target;
	var label = menuitem.label;
	if (label == configureFilters)
	{
		showConfigure();
		return;
	}

	var reload = false;
	if (label == showAllContextText)
	{
		filterAction = filterActionEnum.NONE;
		GM_setValue("filterAction", showAllContextText);
		var menuitem = $("#show_all_content");
		menuitem.checked = true;
		reload = true;
	}
	else if (label == filterPostsAndComments)
	{
		filterAction = filterActionEnum.POSTSANDCOMMENTS;
		GM_setValue("filterAction", filterPostsAndComments);
		menuitem = $("#filter_posts_and_comments");
		menuitem.checked = true;
		reload = true;
	}
	else if (label == filterPostsOnly)
	{
		filterAction = filterActionEnum.POSTSONLY;
		GM_setValue("filterAction", filterPostsOnly);
		menuitem = $("#filter_posts_only");
		menuitem.checked = true;
		reload = true;
	}
	else if (label == filterCommentsOnly)
	{
		filterAction = filterActionEnum.COMMENTSONLY;
		GM_setValue("filterAction", filterCommentsOnly);
		menuitem = $("#filter_comments_only");
		menuitem.checked = true;
		reload = true;
	}

	if (!reload)
	{
		var workString = label.slice(0, filterForUser.length);
		if (workString == filterForUser)
		{
			workString = label.slice(filterForUser.length)
			addUserToFilter(workString);
			var tempString = GM_getValue("filteredUsers", "");
			if (tempString.length > 0)
			{
				tempString += ",";
			}
			workString.trim();
			tempString += encodeURIComponent(workString);
			GM_setValue("filteredUsers", tempString);
			reload = true;
		}
	}

	if (!reload)
	{
		workString = label.slice(0, filterForText.length);
		if (workString == filterForText)
		{
			workString = label.slice(filterForText.length)
			addTextToFilter(workString);
			var tempString = GM_getValue("filteredText", "");
			if (tempString.length > 0)
			{
				tempString += ",";
			}
			workString.trim();
			tempString += encodeURIComponent(workString);
			GM_setValue("filteredText", tempString);
			reload = true;
		}
	}
	if (!reload)
	{
		workString = label.slice(0, filterForSite.length);
		if (workString == filterForSite)
		{
			workString = label.slice(filterForSite.length)
			addSiteToFilter(workString);
			var tempString = GM_getValue("filteredSites", "");
			if (tempString.length > 0)
			{
				tempString += ",";
			}
			workString.trim();
			tempString += encodeURIComponent(workString);
			GM_setValue("filteredSites", tempString);
			reload = true;
		}
	}

	if (reload)
	{
		location.reload();
		return;
	}
}

function addUserToFilter(user)
{
	userFilters.push(user);
}

function addTextToFilter(text)
{
	textFilters.push(text);
}

function addSiteToFilter(site)
{
	siteFilters.push(site);
}

function loadConfiguration()
{
	var workString = GM_getValue("filteredUsers", "");
	if (workString.length > 0)
	{
		var workArray = workString.split(',');
		userFilters = [];
		for (var loop = 0; loop < workArray.length; loop++)
		{
			userFilters.push(decodeURIComponent(workArray[loop]));
		}
		var userFiltersString = userFilters.join("\n");
		var textArea = $("#userTextArea");
		textArea.value = userFiltersString;
	}

	workString = GM_getValue("filteredText", "");
	if (workString.length > 0)
	{
		var workArray = workString.split(',');
		textFilters = [];
		for (var loop = 0; loop < workArray.length; loop++)
		{
			textFilters.push(decodeURIComponent(workArray[loop]));
		}
		var textFiltersString = textFilters.join("\n");
		var textArea = $("#contentTextArea");
		textArea.value = textFiltersString;
	}

	workString = GM_getValue("filteredSites", "");
	if (workString.length > 0)
	{
		var workArray = workString.split(',');
		siteFilters = [];
		for (var loop = 0; loop < workArray.length; loop++)
		{
			siteFilters.push(decodeURIComponent(workArray[loop]));
		}
		var siteFiltersString = siteFilters.join("\n");
		var textArea = $("#siteTextArea");
		textArea.value = siteFiltersString;
	}

	var menuitem = $("#show_all_content");
	menuitem.checked = false;
	menuitem = $("#filter_posts_only");
	menuitem.checked = false;
	menuitem = $("#filter_comments_only");
	menuitem.checked = false;
	menuitem = $("#filter_posts_and_comments");
	menuitem.checked = false;

	workString = GM_getValue("filterAction", filterPostsAndComments);
	if (workString == showAllContextText)
	{
		menuitem = $("#show_all_content");
		menuitem.checked = true;
		filterAction = filterActionEnum.NONE;
	}
	else if (workString == filterPostsOnly)
	{
		menuitem = $("#filter_posts_only");
		menuitem.checked = true;
		filterAction = filterActionEnum.POSTSONLY;
	}
	else if (workString == filterCommentsOnly)
	{
		menuitem = $("#filter_comments_only");
		menuitem.checked = true;
		filterAction = filterActionEnum.COMMENTSONLY;
	}
	else
	{
		menuitem = $("#filter_posts_and_comments");
		menuitem.checked = true;
		filterAction = filterActionEnum.POSTSANDCOMMENTS;
	}

	var radio = $("#radio_ShowAll");
	if (filterAction == filterActionEnum.NONE)
	{
		radio.checked = true;
	}
	else
	{
		radio.checked = false;
	}
	radio = $("#radio_CommentsAndPosts");
	if (filterAction == filterActionEnum.POSTSANDCOMMENTS)
	{
		radio.checked = true;
	}
	else
	{
		radio.checked = false;
	}
	radio = $("#radio_PostsOnly");
	if (filterAction == filterActionEnum.POSTSONLY)
	{
		radio.checked = true;
	}
	else
	{
		radio.checked = false;
	}
	radio = $("#radio_CommentsOnly");
	if (filterAction == filterActionEnum.COMMENTSONLY)
	{
		radio.checked = true;
	}
	else
	{
		radio.checked = false;
	}
}

function saveConfiguration()
{
	var div = $("#div_configureBox");
	div.style.display = noneText;

	var textArea = $("#userTextArea");
	var text;
	if (!textArea || !textArea.value)
	{
		text = "";
	}
	else
	{
		text = textArea.value.trim();
	}
	var userFiltersArray = text.split("\n");

	textArea = $("#contentTextArea");
	if (!textArea || !textArea.value)
	{
		text = "";
	}
	else
	{
		text = textArea.value.trim();
	}
	var textFiltersArray = text.split("\n");

	textArea = $("#siteTextArea");
	if (!textArea || !textArea.value)
	{
		text = "";
	}
	else
	{
		text = textArea.value.trim();
	}
	var siteFiltersArray = text.split("\n");

	userFilters = [];
	var tempArray = [];
	for (var loop = 0; loop < userFiltersArray.length; loop++)
	{
		userFilters.push(userFiltersArray[loop]);
		tempArray.push(encodeURIComponent(userFiltersArray[loop]));
	}
	var tempString = tempArray.join(",");
	GM_setValue("filteredUsers", tempString);

	textFilters = [];
	tempArray = [];
	for (var loop = 0; loop < textFiltersArray.length; loop++)
	{
		textFilters.push(textFiltersArray[loop]);
		tempArray.push(encodeURIComponent(textFiltersArray[loop]));
	}
	var tempString = tempArray.join(",");
	GM_setValue("filteredText", tempString);

	siteFilters = [];
	tempArray = [];
	for (var loop = 0; loop < siteFiltersArray.length; loop++)
	{
		siteFilters.push(siteFiltersArray[loop]);
		tempArray.push(encodeURIComponent(siteFiltersArray[loop]));
	}
	var tempString = tempArray.join(",");
	GM_setValue("filteredSites", tempString);

	var menuitem1 = $("#radio_ShowAll");
	var menuitem2 = $("#radio_PostsOnly");
	var menuitem3 = $("#radio_CommentsOnly");
	if (menuitem1.checked)
	{
		filterAction = filterActionEnum.NONE;
		GM_setValue("filterAction", showAllContextText);
	}
	else if (menuitem2.checked)
	{
		filterAction = filterActionEnum.POSTSONLY;
		GM_setValue("filterAction", filterPostsOnly);
	}
	else if (menuitem3.checked)
	{
		filterAction = filterActionEnum.COMMENTSONLY;
		GM_setValue("filterAction", filterCommentsOnly);
	}
	else
	{
		filterAction = filterActionEnum.POSTSANDCOMMENTS;
		GM_setValue("filterAction", filterPostsAndComments);
	}
	location.reload();
}

function performFiltering()
{
	if (filterAction == filterActionEnum.NONE)
	{
		// don't filter
		return;
	}
	if (userFilters.length < 1 && textFilters.length < 1 && siteFilters.length < 1)
	{
		// no filters
		return;
	}

//	var xpath = "//DIV/SPAN[starts-with(text(),'posted by') and (@class='smallcopy' or @class='smallcopy byline')]";
	var xpath = "//DIV/SPAN[starts-with(text(),'posted by') and contains(@class, 'smallcopy')]";
	var postNodes = document.evaluate(
		xpath,
		document,
		null,
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
		null
	);
	var total = postNodes.snapshotLength;
	for (var i = 0; i < total; i++)
	{
		// not much validation here, cuts performance overhead by avoiding extra tests against the nodes
		// tighten it down later if it conflicts with other add-ons or Metafilter bling
		var userSpan = postNodes.snapshotItem(i);
		var copyDiv = userSpan.parentNode;
		var titleDiv = copyDiv.previousSibling.previousSibling;
		// music uses H1 instead of DIV, whatever
		if (titleDiv.nodeName != "DIV" && titleDiv.nodeName != "H1" && titleDiv.nodeName != "H2")
		{
			titleDiv = null;
		}
		if (!titleDiv && filterAction == filterActionEnum.POSTSONLY)
		{
			// not a post (assumed comment), and only filtering posts
			continue;
		}
		if (titleDiv && filterAction == filterActionEnum.COMMENTSONLY)
		{
			// post, and only filtering comments
			continue;
		}
		var currentNode = userSpan.firstChild;
		var found = false;
		var userName;
		while (currentNode && !found)
		{
			if (currentNode.nodeName === "A")
			{
				var href_value = currentNode.getAttribute('href');
				if (href_value.match(/\/user\/\d/))
				{
					var childNode = currentNode.firstChild;
					while (childNode)
					{
						if (childNode.nodeName === '#text')
						{
							userName = childNode.nodeValue;
							found = true;
							break;
						}
						childNode = childNode.nextSibling;
					}
				}
			}
			currentNode = currentNode.nextSibling;
		}
		if (!userName)
		{
			// even if we're not filtering by user name, if there isn't one associated with a post, it's not a good target
			continue;
		}

		var postHidden = false;
		// check user name, applies to posts and comments
		for (var j = 0; j < userFilters.length; j++)
		{
			if (userName == userFilters[j])
			{
				postHidden = true;
				break;
			}
		}

		if (filterAction == filterActionEnum.POSTSONLY || (filterAction == filterActionEnum.POSTSANDCOMMENTS && titleDiv)) 
		{
			// we know this is a post title, not a comment, and it should be subject to filtering
			// check for link
			currentNode = titleDiv.firstChild;
			found = false;
			var link = null;
			while (currentNode && !found)
			{
				if (currentNode.nodeName === "A")
				{
					link = currentNode.getAttribute('href');
					found = true;
					break;
				}
				currentNode = currentNode.nextSibling;
			}
			for (var j = 0; link && j < siteFilters.length; j++)
			{
				var sitePattern = new RegExp("^(https?://)?"+siteFilters[j]);
				if (link.match(sitePattern))
				{
					postHidden = true;
					break;
				}
			}
			if (!postHidden)
			{
				// check for text
				var text = titleDiv.textContent;
				for (var j = 0; j < textFilters.length; j++)
				{
					var textPattern = new RegExp("\\b"+textFilters[j]+"\\b","i");
					if (text.match(textPattern))
					{
						postHidden = true;
						break;
					}
				}
			}
		}

		if (!postHidden && (filterAction == filterActionEnum.COMMENTSONLY || filterAction == filterActionEnum.POSTSANDCOMMENTS))
		{
			// this is a comment or post copy and it should be subject to filtering
			// check for matching links
			currentNode = copyDiv.firstChild;
			var link = null;
			while (currentNode)
			{
				if (currentNode.nodeName === "A")
				{
					link = currentNode.getAttribute('href');
					for (var j = 0; link && j < siteFilters.length; j++)
					{
						var sitePattern = new RegExp("^(https?://)?"+siteFilters[j]);
						if (link.match(sitePattern))
						{
							postHidden = true;
							break;
						}
					}
				}
				currentNode = currentNode.nextSibling;
			}
			if (!postHidden)
			{
				// check for text
				var text = copyDiv.textContent;
				for (var j = 0; j < textFilters.length; j++)
				{
					var textPattern = new RegExp("\\b"+textFilters[j]+"\\b","i");
					if (text.match(textPattern))
					{
						postHidden = true;
						break;
					}
				}
			}
		}
		if (postHidden)
		{
			if (titleDiv)
			{
				titleDiv.style.display = noneText;
			}
			copyDiv.style.display = noneText;

			// try and squash the trailing BR, or 2
			var sibling = copyDiv.nextSibling;
			while (sibling)
			{
				if (sibling.nodeName === 'BR')
				{
					// found it
					sibling.style.display = noneText;
				}
				else if (sibling.nodeName !== '#text')
				{
					// apparently shot past the post/comment entry, bail
					break;
				}
				sibling = sibling.nextSibling;
			}
		}
	}
}

function buildContextMenu()
{
	var menu = document.body.appendChild(document.createElement("menu"));
	menu.outerHTML =	'<menu id="userscript-context-menu" type="context">\
							<menu label="diediedead!">\
								<menuitem id="filter_user_or_content" label="'+noActiveSelectionText+'">\
								</menuitem>\
								<menuitem label="\u2014">\
								</menuitem>\
								<menuitem id="show_all_content" type="checkbox" label="'+showAllContextText+'">\
								</menuitem>\
								<menuitem id="filter_posts_and_comments" type="checkbox" label="'+filterPostsAndComments+'">\
								</menuitem>\
								<menuitem id="filter_posts_only" type="checkbox" label="'+filterPostsOnly+'">\
								</menuitem>\
								<menuitem id="filter_comments_only" type="checkbox" label="'+filterCommentsOnly+'">\
								</menuitem>\
								<menuitem label="\u2014">\
								</menuitem>\
								<menuitem id="configure_filters" label="'+configureFilters+'">\
								</menuitem>\
							</menu>\
						 </menu>';

	var html = document.documentElement;
	html.setAttribute("contextmenu", "userscript-context-menu");
	if ("contextMenu" in html && "HTMLMenuItemElement" in window)
	{
	  $("#userscript-context-menu menu").addEventListener("click", contextClick, false);
	  html.addEventListener("contextmenu", initMenu, false);
	}
}

function buildConfigureBox()
{
	var mainDiv = document.createElement('div');
	mainDiv.id = "div_configureBox";
	var userTextArea = document.createElement('textarea');
	userTextArea.id = "userTextArea";
	userTextArea.className = "textarea_configureBox";
	var contentTextArea = document.createElement('textarea');
	contentTextArea.id = "contentTextArea";
	contentTextArea.className = "textarea_configureBox";
	var siteTextArea = document.createElement('textarea');
	siteTextArea.id = "siteTextArea";
	siteTextArea.className = "textarea_configureBox";

	var h2 = document.createElement('h2');
	h2.className = "header_configureBox";
	h2.appendChild(document.createTextNode('Configure diediedead'));
	mainDiv.appendChild(h2);

	mainDiv.appendChild(document.createTextNode('Filter users (one per line)'));
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(userTextArea);
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(document.createTextNode('Filter if contains text (1 entry/line)'));
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(contentTextArea);
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(document.createTextNode('Filter if contains site (1 site/line)'));
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(siteTextArea);
	mainDiv.appendChild(document.createElement('br'));

	var radio = document.createElement("input");
	radio.id = "radio_ShowAll";
	radio.type = "radio";
	radio.name = "showWhich";
	if (filterAction == filterActionEnum.NONE)
	{
		radio.checked = true;
	}
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(radio);
	mainDiv.appendChild(document.createTextNode(showAllContextText));

	radio = document.createElement("input");
	radio.id = "radio_CommentsAndPosts";
	radio.type = "radio";
	radio.name = "showWhich";
	if (filterAction == filterActionEnum.POSTSANDCOMMENTS)
	{
		radio.checked = true;
	}
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(radio);
	mainDiv.appendChild(document.createTextNode(filterPostsAndComments));

	radio = document.createElement("input");
	radio.id = "radio_PostsOnly";
	radio.type = "radio";
	radio.name = "showWhich";
	if (filterAction == filterActionEnum.POSTSONLY)
	{
		radio.checked = true;
	}
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(radio);
	mainDiv.appendChild(document.createTextNode(filterPostsOnly));

	radio = document.createElement("input");
	radio.id = "radio_CommentsOnly";
	radio.type = "radio";
	radio.name = "showWhich";
	if (filterAction == filterActionEnum.COMMENTSONLY)
	{
		radio.checked = true;
	}
	mainDiv.appendChild(document.createElement('br'));
	mainDiv.appendChild(radio);
	mainDiv.appendChild(document.createTextNode(filterCommentsOnly));
	mainDiv.appendChild(document.createElement('br'));

	mainDiv.appendChild(document.createElement('br'));
	var saveNode = document.createElement("button");
	saveNode.appendChild(document.createTextNode("Save"));
	saveNode.className = "button_configureBox";
	saveNode.addEventListener("click", saveConfiguration, true);
	mainDiv.appendChild(saveNode);

	mainDiv.style.display = noneText;
	document.getElementsByTagName('body')[0].appendChild(mainDiv);
}

function showConfigure()
{
	loadConfiguration();
	var div = $("#div_configureBox");
	div.style.display = blockText;
}

function initMenu(e)
{
	var menuitem = $("#show_all_content");
	menuitem.checked = false;
	menuitem = $("#filter_posts_and_comments");
	menuitem.checked = false;
	menuitem = $("#filter_posts_only");
	menuitem.checked = false;
	menuitem = $("#filter_comments_only");
	menuitem.checked = false;

	menuitem = $("#filter_user_or_content")
	menuitem.label = noActiveSelectionText;

	switch(filterAction)
	{
		case filterActionEnum.POSTSANDCOMMENTS:
			menuitem = $("#filter_posts_and_comments");
			menuitem.checked = true;
			break;
		case filterActionEnum.POSTSONLY:
			menuitem = $("#filter_posts_only");
			menuitem.checked = true;
			break;
		case filterActionEnum.COMMENTSONLY:
			menuitem = $("#filter_comments_only");
			menuitem.checked = true;
			break;
		case filterActionEnum.NONE:
		default:
			menuitem = $("#show_all_content");
			menuitem.checked = true;
			break;
	}

	var node = e.target;

	var pNode = node;
	// fanfare does a user link of //www.metafilter.com, don't ask me
	if (pNode.nodeName == 'A' &&
		pNode.target && pNode.target == "_self" &&
		pNode.getAttribute("href").match(/^((https?:\/\/)|(\/\/))?(www.metafilter.com)?\/user\/\d+$/))
	{
		// right click on user link
		menuitem = $("#filter_user_or_content")
		menuitem.label = filterForUser + pNode.innerHTML;
		return;
	}

	while (pNode && pNode.nodeName != "A")
	{
		pNode = pNode.parentNode;
	}

	if (pNode && pNode.hasAttribute("href"))
	{
		// right click on a site link
		var result = pNode.getAttribute("href").match(/^(https?:\/\/)?([^\/\?]{3,})/);
		if (!result || !result[2])
		{
			return;
		}
		menuitem = $("#filter_user_or_content")
		menuitem.label = filterForSite + result[2];
	}
	else
	{
		// right click on text, see if selected
		var text = document.getSelection().toString();
		if (text)
		{
			text.trim();
		}
		if (text.length < 1)
		{
			return;
		}
		menuitem = $("#filter_user_or_content");
		menuitem.label = filterForText + text;
	}

	return;
}

function $(aSelector, aNode)
{
	return (aNode || document).querySelector(aSelector);
}


document.addEventListener('DOMContentLoaded',onLoaded,true);
GM_registerMenuCommand(configureFilters, showConfigure, "c");