User JavaScript: RegExp Find

This script is exactly what its name suggests: RegExp Find.

Works much like regular find in browsers, but is JavaScript (Greasemonkey) and supports (to a certain degree) regular expressions.

One can press (default) Ctrl + Shift + F to Find, Ctrl + Shift + N jumps to the Next match.

The "Next match" function is supposed to mimic the less Linux command, which scrolls until the match is at the top of the screen, and stops only once per line.

The script file.

Unfortunately, there are currently some limitations:

Update: Ctrl + Shift + M now changes between case sensitive and case insensitive mode.

Tested in Firefox and Opera.

You can try searching this page by clicking here. You can also use Ctrl+Shift+F and Ctrl+Shift+N.

Related, but different: Beef up the Find command in Firefox - "Greasemonkey script to highlight search entries relative to nearby content."

The code:

// RegExp Find user script
// version 0.01 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.
//
// ==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 selectedNode = null;
var style = "background-color:#FFFF00";
var startTag = "";
var endTag = "";
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 == 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 + ")", "g");
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");
var match = false;
var search = true;
while (search) {
	search = false;
	while ((myArray = reg.exec(startNode.innerHTML)) != undefined) {
		var old = startNode.innerHTML;
		var hits = document.documentElement.getElementsByTagName("SPAN").length;
		startNode.innerHTML = startNode.innerHTML.substring(0, myArray.index) + 
			myArray[1] + myArray[2].replace(regex, startTag + "$1" + endTag, 'g') + 
			myArray[3] +
			//myArray[1] + startTag + myArray[2] + endTag + myArray[3] + 
			startNode.innerHTML.substring(reg.lastIndex);
		if (document.documentElement.getElementsByTagName("SPAN").length > hits) { //Do
			//not match e.g. textarea contents.
			match = true;
			//search = true;
		}
		else {
			startNode.innerHTML = old;
		}
	}
}
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);
	}
}

}
)();