noticeMF

highlight MetaFilter posts and comments containing user-specified text

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// 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");