Source for "Wikiproxy: Greasemonkey Edition "


// ==UserScript==
// @name		Wikiproxy: Greasemonkey Edition
// @namespace		http://www.allpeers.com/blog/greasemonkey
// @description		Adds Wikipedia links to key terms in webpages
// @include		http://*
// @exclude		http://wikiproxy.whitelabel.org/*
// @exclude		http://www.theyworkforyou.com/*
// @exclude		http://*.wikipedia.*

// ==/UserScript==

// original version by Matthew Gertner
// modified by Valentin Laube to identify Wikipedia links with an icon
// the design of the icon is inspired by the wikipedia icon for external links

(function() {
	var iconcolor = 0; // 0 blue, 1 green, 2 red
	var icons = [
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAgMAAADwXCcuAAAADFBMVEUWJgGkyP%2F%2F%2F%2F8AZv87Gt1vAAAAAXRSTlMAQObYZgAAAC1JREFUCNdjYP7%2FgYF51QYG%2Fv8bGHhXb2BgXg2hzco%2FMNw8z8BgzsDAwMPAAAAtcQzEPgrvTwAAAABJRU5ErkJggg%3D%3D",
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAgMAAADwXCcuAAAADFBMVEUbHgGW0nL%2F%2F%2F9SqxuFak3hAAAAAXRSTlMAQObYZgAAAC1JREFUCNdjYP7%2FgYF51QYG%2Fv8bGHhXb2BgXg2hzco%2FMNw8z8BgzsDAwMPAAAAtcQzEPgrvTwAAAABJRU5ErkJggg%3D%3D",
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAgMAAADwXCcuAAAADFBMVEUbHgH%2Fq47%2F%2F%2F%2F%2FVBnJ4fJlAAAAAXRSTlMAQObYZgAAAC1JREFUCNdjYP7%2FgYF51QYG%2Fv8bGHhXb2BgXg2hzco%2FMNw8z8BgzsDAwMPAAAAtcQzEPgrvTwAAAABJRU5ErkJggg%3D%3D"
		];
	var icons2 = [
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs%2B9AAAAeElEQVQY02NkQAIB0%2F7%2FZ8ABWNAF1mdiKgqczsDASMi09ZkQhSgmFjozMNipoZoEA0wwxoYsRsb%2BvQw4AROy1YXOuBUyIyuyU2NgOHSLgaFgFQODFD8Dw4n7DAwrzyCZdPDm%2F%2F%2F%2F%2F0NomKcCpiHYKOGHrAgvIKQIAECSPtEmaizfAAAAAElFTkSuQmCC",
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs%2B9AAAAi0lEQVQY02NkQAIdR5P%2FM%2BAALOgC5VZzMBR1HkthYCRkWrnVHIbOYymoJupLWzN4yCeimAQDTDBGhfVcxotPj%2BJyIkJhx9Hk%2F%2FrS1jgVMiIr8pBPZNjxcD7DxadHGfSlrRlQbOg4mvx%2F%2B4N5%2F%2F%2F%2F%2F%2F9%2F%2B4N5%2F2Ge6jia%2FB%2FDgzDF%2BMKRqMBmYGBgAAApFkF%2BHyXzbAAAAABJRU5ErkJggg%3D%3D",
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs%2B9AAAAf0lEQVQY042QyxGCQBBE36BRQQ4UKSw5QA4sOeDVo5gDxOCdQNqDfN1asS8zNfWqa7qNneSdiOgaXKoupNoSO3WrOmjLL8esgDQ%2FOC1KlsXqmzE8Yi9uoLwTWREFLwcozWF8wr0BM5heMPabk4Zekj5zDiXvFARc4R89%2FlU2wBsinj50jQNjuAAAAABJRU5ErkJggg%3D%3D"
		];
	var bgprefix = "url("
	var bgsuffix = ") center right no-repeat";

	function addWikiLinkStyle()
	{
		var wikiLinkStyle = document.createElement('style');
		wikiLinkStyle.id = "wikilinkstyle";
		wikiLinkStyle.type = "text/css";
		wikiLinkStyle.innerHTML = '.wikilink, .wikilink_over {\n'
			+ 'color: inherit;\n'
			+ 'padding-right: 13px;\n'
			+ '}\n'
		    + '.wikilink {\n'
			+ 'background: transparent ' + bgprefix + icons[iconcolor] + bgsuffix + ';\n'
			+ '}\n'
		    + '.wikilink_over {\n'
			+ 'background: transparent ' + bgprefix + icons2[iconcolor] + bgsuffix + ';\n'
			+ '}';
		document.getElementsByTagName('head')[0].appendChild(wikiLinkStyle);
	}

	var requestUrl = "http://wikiproxy.whitelabel.org/xml.php";
	var wikipediaUrlPrefix = "http://en.wikipedia.org/wiki/";
	var excludeAncestors = new Array("a", "script", "style", "input", "textarea", "select", "option");
	
	var excludeXPath = "ancestor::*[";
	for (var tagNum=0; tagNum<excludeAncestors.length; tagNum++)
		excludeXPath += (tagNum == 0 ? "" : " or ") + "self::" + excludeAncestors[tagNum];
	excludeXPath += "]";
	
	// Regular expression definitions from News Wikiproxy
  	var capsword = "A|[A-Z][a-zA-Z'0-9]{1,}"; // not starting with number, as catches too much
  	var fillerwords = "a|of|and|in|on|under|the";
  	var middlewordre = "(" + capsword + "|" + fillerwords + "|[A-Z]\.)[ \\t]*";
  	var endwordre = "(" + capsword + ")[ \\t]*"; // and, of etc. can't appear at ends
  	var acronymre = "\\b([A-Z][A-Z0-9]{2,})\\b";

	// Match either "Two Endwords" or "Endword and Some Middle Words"
  	var greedyproperre = "\\b(" + endwordre + "(" + middlewordre + ")*" + endwordre + ")\\b";
	// Match without filler words (so if you have a phrase like
	// "Amnesty International and Human Rights Watch" you also get both parts
	// separately "Amnesty International" and "Human Rights Watch")
	var frugalproperre = "\\b((" + endwordre + "){2,})\\b";
	
	var usedTerms = new Array();

	function getTerms(str, regexpstr, terms)
	{
		var candidates = str.match(new RegExp(regexpstr, "mg"));
		for (var i=0; i<candidates.length; i++)
		{
			var term = candidates[i];
			while (term.charAt(term.length-1) == " ")
				term = term.substring(0, term.length-1);
			if (usedTerms[term] == null)
			{
				if (terms.length > 0)
					terms += " ";
				terms += term.replace(/ /g, "_");
				usedTerms[term] = term;
			}
		}
		return terms;
	}

	if (document.documentElement.tagName == "HTML")
	{
		var treeWalker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_TEXT, null, false);
		var text = "";
		var textNode;
		while (textNode = treeWalker.nextNode())
		{
			if (!document.evaluate("ancestor::script", textNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue)
				text += textNode.nodeValue + "\n";
		}
			
			
		var terms = getTerms(text, greedyproperre, "");
		terms = getTerms(text, frugalproperre, terms);
		terms = getTerms(text, acronymre, terms);
		
		GM_xmlhttpRequest({
		    method: 'POST',
		    url: requestUrl,
		    headers: {
		        'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
		        'Content-type': 'application/x-www-form-urlencoded'
		    },
		    data: 'text=' + escape(terms),
		    onload: function(responseDetails) {
		    	var parser = new DOMParser();
			var responseXML = parser.parseFromString(responseDetails.responseText, "text/xml");
			var termSnapshot = responseXML.evaluate("/wikiproxy/term/text()", responseXML, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
			var normalizedTerms = new Array();
			var termRegExp = "";
			for (var i=0; i<termSnapshot.snapshotLength; i++)
			{
				var termNodeValue = termSnapshot.snapshotItem(i).nodeValue.replace(/_/g, " ");
				normalizedTerms[termNodeValue.toLowerCase()] = termNodeValue;
				if (termRegExp.length > 0)
					termRegExp += "|";
				termRegExp += termNodeValue;
			}
			termRegExp = new RegExp("\\b(" + termRegExp + ")\\b", "mg");			
			
			treeWalker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_TEXT, null, false);
			while (textNode = treeWalker.nextNode())
			{
				if (responseXML.evaluate(excludeXPath, textNode, null, 
					XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue)
					continue;
				
				var matches = textNode.nodeValue.match(termRegExp);
				if (!matches)
					continue;
					
				// add wiki link style
				if (!document.getElementById('wikilinkstyle')) {
					addWikiLinkStyle();
				}

				for (i=0; i<matches.length; i++)
				{
					var term = matches[i];
					if(!term)continue;
					var displayTerm = term.replace(/_/g, " ");
					term = normalizedTerms[term.toLowerCase()];
					var termIndex = textNode.nodeValue.indexOf(displayTerm);
					var preTermNode = document.createTextNode(textNode.nodeValue.substring(0, termIndex));
					textNode.nodeValue = textNode.nodeValue.substring(termIndex+displayTerm.length);
					var anchor = document.createElement("a");
					anchor.className = "wikilink";
					anchor.onmousemove = function () {this.className = 'wikilink_over';};
					anchor.onmouseout = function () {this.className = 'wikilink';};
					anchor.href = wikipediaUrlPrefix + term;
					var termNode = document.createTextNode(displayTerm);
					anchor.insertBefore(termNode, anchor.firstChild);
					textNode.parentNode.insertBefore(preTermNode, textNode);
					textNode.parentNode.insertBefore(anchor, textNode);
				}
			}
		    }
		});
	}

	if (window.GM_registerMenuCommand) {

		function undoWikify()
		{
			var wlinks = document.evaluate('//a[@class="wikilink"]', document, null,
						       XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
			// GM_log(wlinks.snapshotLength + ' wikilinks found.');
			for (var i = 0; i < wlinks.snapshotLength; i++) {
				var wlink = wlinks.snapshotItem(i);
				var text = document.createTextNode(wlink.textContent);
				wlink.parentNode.replaceChild(text, wlink);
			}
		}

		GM_registerMenuCommand('Undo Wikify', undoWikify);

	}

})();