// ==UserScript==
// @name           Text URL Linker
// @namespace      http://ss-o.net/
// @include        http://*
// @version        0.6
// ==/UserScript==

(function TextURLLinker() {
	var BLOCK_REFERRER = true;
	var TAG = 'a';
	var STYLE = 'cursor:help;display:inline !important;';
	var isEVENT = BLOCK_REFERRER || TAG != 'a';
	if (document.contentType && !/html/i.test(document.contentType))
		return;
	var isChromium = window.chromium ? true : false;
	var htmlDoc = isChromium ? document.implementation.createHTMLDocument('hogehoge') : document;
	var fn = function(doc) {
		$XA('descendant::text()[contains(self::text(),"ttp") and not(ancestor::a) and not(ancestor::textarea) and not(ancestor::script) and not(ancestor::style)]', doc)(function(txt) {
			var df, text = txt.nodeValue.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'),
			parent = txt.parentNode, range = document.createRange(),
			newText = text.replace(/h?(ttps?:\/\/[^\s\])'"）」】]+)/g, function($0, $1) {
				return '<' + TAG + ' href="h' + $1 + '" ini="1" style="' + STYLE + '">' + $0 + '</' + TAG + '>';
			});
			if (isChromium) {
				range.selectNodeContents(htmlDoc.documentElement);
			} else {
				range.selectNode(txt);
			}
			df = range.createContextualFragment(newText);
			if (df.firstChild) parent.replaceChild(df, txt);
			range.detach();
			if (isEVENT) {
				Array.prototype.forEach.call(parent.getElementsByTagName(TAG), function(nota) {
					if (nota.getAttribute('ini')==1) {
						nota.addEventListener('click', function(e) {
							if (e.ctrlKey) {
								openThisTab(nota.getAttribute('href'));
							} else {
								openNewTab(nota.getAttribute('href'));
							}
							e.preventDefault();
						}, false);
						nota.removeAttribute('ini');
					}
				});
			}
		});
	};
	fn(document.body);
	if (window.AutoPagerize) init();
	else window.addEventListener('GM_AutoPagerizeLoaded',init,false);
	window.addEventListener('AutoPatchWork#DOMNodeInserted',function(e){fn(e.target);},false);

	function init(e){
		if (window.AutoPagerize) window.AutoPagerize.addFilter(function(docs) {docs.forEach(fn);});
	}

	function openThisTab(url) {
		if (!BLOCK_REFERRER) {
			location.href = url;
		} else if (window.opera || window.getMatchedCSSRules) {
			location.href = 'data:text/html,<script type="text/javascript">location.replace("' + url + '");</script>';
		} else if (typeof GM_openInTab == 'function') {
			document.write('<meta http-equiv="refresh" content="0;url=' + url + '">');
			document.close();
		} else {
			location.href = url;
		}
	}
	function openNewTab(url) {
		if (!BLOCK_REFERRER) {
			window.open(url);
		} else if (window.opera || window.getMatchedCSSRules) {
			window.open('data:text/html,<script type="text/javascript">location.replace("' + url + '");</script>');
		} else if (typeof GM_openInTab == 'function') {
			GM_openInTab(url);
		} else {
			window.open(url);
		}
	}
	function $XA(exp, context, resolver) {
		context || (context = document);
		var XPE = new XPathEvaluator();
		var Doc = context.ownerDocument || context, expr = XPE.createExpression(exp, resolver ? resolver :
			Doc.documentElement.namespaceURI ? function(prefix) {
				return document.createNSResolver(Doc.documentElement).lookupNamespaceURI(prefix) ||
				context.namespaceURI || document.documentElement.namespaceURI || "";
		} : null);
		var result = expr.evaluate(context, XPathResult.ANY_TYPE, null);
		switch (result.resultType) {
		case XPathResult.STRING_TYPE : return function(fnc,thisObject) {fnc.call(thisObject,result.stringValue);};
		case XPathResult.NUMBER_TYPE : return function(fnc,thisObject) {fnc.call(thisObject,result.numberValue);};
		case XPathResult.BOOLEAN_TYPE: return function(fnc,thisObject) {fnc.call(thisObject,result.booleanValue);};
		case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
			result = expr.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, result);
			return function(fnc, thisObject) {
				for (var i = 0, len = result.snapshotLength; i < len; i++) {
					fnc.call(thisObject, result.snapshotItem(i));
				}
			};
		}
		return null;
	}
})();
