// RegExp Find user script
// version 0.03 Beta
// Richard H. Tingstad
//
// Works much like regular find in browsers, but is JavaScript and supports (to a certain degree) regular expressions.
//
// To use this script with Firefox, you need Greasemonkey. Works with Opera as well.
//
// 20091002 0.03 Can switch between case sensitive and case insensitive search.
// 20090324 0.02 Slight performance improvement.
// 20081213 0.01 First version.
//
// ==UserScript==
// @name           RegExp-Find
// @namespace      http://drop.by
// @description    Regular expression find, search web pages.
// @include        *
// ==/UserScript==
(function () {
window.addEventListener('keyup', globalKeyUp, 'true');
window.addEventListener('keydown', globalKeyDown, 'true');
var c=0;
var m=0;
var className = "rht";
var isCtrl = false;
var isShift = false;
var caseSensitive = true;
var selectedNode = null;
var style = "background-color:#FFFF00";
var startTag = "<SPAN class=\"" + className + "\" style=\"" + style + "\">";
var endTag = "</SPAN>";
function globalKeyDown(e){
	if(e.keyCode == 17) isCtrl = true;
	else if (e.keyCode == 16) isShift = true;
}
function globalKeyUp(e) {
if(e.keyCode == 16) {
	isShift = false;
	return;
}
if(e.keyCode == 17) {
	isCtrl = false;
	return;
}
if (isCtrl && isShift && e.keyCode == 77) { // Ctrl + Shift + M
	caseSensitive = !caseSensitive;
	alert("Mode set to: Case " + (caseSensitive ? "" : "in") + "sensitive.");
}
if (isCtrl && isShift && e.keyCode == 78) { // Ctrl + Shift + N
	jumpTo(++c);
	return;
}
if (!isCtrl || !isShift || e.keyCode != 70) { // Ctrl + Shift + F
	return;
}
var s = prompt('RegExp Find:');
if (s == null || s == '') return;
var regex = new RegExp("(" + s + ")", caseSensitive ? "g" : "ig");
c=0;
m=0;
var startNode = document.documentElement.getElementsByTagName('body')[0];
var reg = new RegExp("<[^>]+class=\"" + className + "\"[^>]+>([^<]*)<[^>]+>", "g"); //Remove highlightings from previous hits. The SPAN tags are different in Opera and Firefox, so the regular expression has to be fairly general.
while (startNode.innerHTML.indexOf(" class=\"" + className + "\"") >= 0) {
	startNode.innerHTML=startNode.innerHTML.replace(reg, "$1");
}

//reg = new RegExp("((?:^|<[^>]+>)[^<]*)(" + s + ")([^<]*(?:$|<[^>]+>))", "g");
reg = new RegExp("(^|<[^>]+>)([^<]*" + s + "[^<]*)($|<[^>]+>)", caseSensitive ? "g" : "ig");
var match = false;
var search = true;
while (search) {
	search = false;
	var html = startNode.innerHTML;
	var mustUpdate = false;
	while ((myArray = reg.exec(html)) != undefined) {
		var old = html;
		mustUpdate = false;
		var hits = document.documentElement.getElementsByTagName("SPAN").length;
		startNode.innerHTML = html.substring(0, myArray.index) + 
			myArray[1] + myArray[2].replace(regex, startTag + "$1" + endTag, 'g') + myArray[3] +
			//myArray[1] + startTag + myArray[2] + endTag + myArray[3] + 
			html.substring(reg.lastIndex);
		if (document.documentElement.getElementsByTagName("SPAN").length > hits) { //Do not match e.g. textarea contents.
			match = true;
			//search = true;
			html = startNode.innerHTML;
		}
		else {
			html = old;
			mustUpdate = true;
		}
	}
	if (mustUpdate) {
		startNode.innerHTML = html;
	}
}
if (match) {
	jumpTo(0);
}
else {
	alert("Not found");
}
}

function jumpTo(number) {
	if (selectedNode != null) selectedNode.setAttribute('style', style);
	var nodes = document.documentElement.getElementsByTagName("SPAN");
	var j = 0;
	var node = null;
	for (var i = 0; i < nodes.length; i++) {
		if (nodes[i].className == className) {
			if (j++ == number) {
				node = nodes[i];
				break;
			}
		}
	}
	selectedNode = node;
	if (node == null) { //No more matches, jump to first match.
		if (c == 0) return;
		c = 0;
		jumpTo(0);
		return;
	}
	node.setAttribute('style', "background-color:#00FF00");
	var y = node.offsetTop;
	node = node.parent;
	while (node != null) {
		y += node.offsetTop;
		node = node.parent;
	}
	if (y == m) { //If match is on same vertical line as previous match, scroll to next instead.
		if (window.pageYOffset == undefined || window.pageYOffset == y) { //Do not jump to next if window is not at current match.
			jumpTo(number + 1);
			return;
		}
	}
	m = y;
	var pos = window.pageYOffset;
	window.scroll(0, y);
	if (pos != undefined && pos == window.pageYOffset) { //At the bottom of the page.
		if (c == 0) return; //Do not loop endlessly if only one match at bottom of page.
		c = 0;
		jumpTo(0);
	}
}

}
)();
