noticeMF

highlight MetaFilter posts and comments containing user-specified text

// noticeMF.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			noticeMF
// @namespace		http://www.devoresoftware.com/gm/noticeMF
// @description		highlight MetaFilter posts and comments containing user-specified text
// @match			https://*.metafilter.com/*
// @match			http://*.metafilter.com/*
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_setValue 
// @grant GM_getValue 
// @run-at document-end
// @version 1.0
// ==/UserScript==
//

"use strict";

var theBorderWidth = "5px";

GM_addStyle('#div_configureHighlightBox{\
	position: fixed;\
	left: 50%;\
	margin-left: -11em;\
	bottom: 5px;\
	color: black;\
	background-color: white;\
	border: 2px green solid;\
	padding: 2px;\
	opacity: 0.95;\
}');
GM_addStyle('.textarea_configureHighlightBox{\
	width: 25em;\
	height: 8.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_configureHighlightBox, .text_configureHighlightBox{\
	text-align: center;\
}');
GM_addStyle('.button_configureHighlightBox{\
	display:block;\
	margin: 0 auto;\
}');

var aquaText = "aqua";
var blackText = "black";
var limeText = "lime";
var orangeText = "orange";
var pinkText = "pink";
var redText = "red";
var yellowText = "yellow";
var noneText = "none";
var blockText = "block";
var highlightList = new Array();
var highlightColor = redText;
 
function onLoaded()
{
	loadConfiguration();
	buildConfigureBox();
	performHighlight();
}

function loadConfiguration()
{
	var workString = GM_getValue("highlightList", "");
	if (workString.length > 0)
	{
		var workArray = workString.split(',');
		highlightList = [];
		for (var loop = 0; loop < workArray.length; loop++)
		{
			highlightList.push(decodeURIComponent(workArray[loop]));
		}
	}

	highlightColor = GM_getValue("highlightColor", redText);
}

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

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

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

	GM_setValue("highlightColor", highlightColor);
	location.reload();
}

function performHighlight()
{
	if (highlightList.length < 1)
	{
		// no filters
		return;
	}

	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;

		// check for match in text
		var highlightPost = false;
		var text = copyDiv.textContent;
		for (var j = 0; j < highlightList.length; j++)
		{
			var textPattern = new RegExp("\\b"+highlightList[j]+"\\b","i");
			if (text.match(textPattern))
			{
				highlightPost = true;
				break;
			}
		}
		if (highlightPost)
		{
			copyDiv.style.border = theBorderWidth+" "+highlightColor+" solid";
		}
	}
}

function buildConfigureBox()
{
	var mainDiv = document.createElement('div');
	mainDiv.id = "div_configureHighlightBox";
	var highlightTextArea = document.createElement('textarea');
	highlightTextArea.id = "highlightTextArea";
	highlightTextArea.className = "textarea_configureHighlightBox";
	var highlightListString = highlightList.join("\n");
	highlightTextArea.value = highlightListString;

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

	var minorDiv = document.createElement('div');
	minorDiv.appendChild(document.createElement('br'));
	minorDiv.appendChild(document.createTextNode('Box outline a post or comment containing text'));
	minorDiv.appendChild(document.createElement('br'));
	minorDiv.appendChild(document.createTextNode('(one entry per line)'));
	minorDiv.appendChild(document.createElement('br'));
	minorDiv.appendChild(highlightTextArea);

	minorDiv.appendChild(document.createElement('br'));
	minorDiv.appendChild(document.createElement('br'));
	minorDiv.appendChild(document.createTextNode('Outline box color'));
	minorDiv.appendChild(document.createElement('br'));
	minorDiv.className = "text_configureHighlightBox";

	var select = document.createElement("select");
	for (var i = 0; i < 7; i++)
	{
		var option = document.createElement("option");
		var which;
		switch(i)
		{
			case 0:
				which = aquaText;
				break;
			case 1:
				which = blackText;
				break;
			case 2:
				which = limeText;
				break;
			case 3:
				which = orangeText;
				break;
			case 4:
				which = pinkText;
				break;
			case 5:
				which = redText;
				break;
			case 6:
			default:
				which = yellowText;
				break;
		}
		if (highlightColor == which)
		{
			option.selected = true;
		}
		else
		{
			option.selected = false;
		}
		option.id = "option_"+which;
		option.value = which;
		option.appendChild(document.createTextNode(which));
		select.appendChild(option);
	}
	minorDiv.appendChild(select);
	minorDiv.appendChild(document.createElement('br'));
	select.addEventListener('change', newColor, true);
	mainDiv.appendChild(minorDiv);

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

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

function newColor()
{
	highlightColor = this.options[this.selectedIndex].value;
}

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

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

document.addEventListener('DOMContentLoaded',onLoaded,true);
GM_registerMenuCommand("Configure noticeMF", showConfigure, "n");