/*
	Add one-press access keys to Google search results. Search results
	are numbered in red and pressing 1..0 selects that search result.
	A Firefox Greasemonkey script,
	Version 0.2
	Adam Langley <agl@imperialviolet.org>
	Neil Dunn <ndunn@ndunn.com>
*/

// ==UserScript==
// @name 			Google Searchkeys
// @namespace 		http://www.imperialviolet.org
// @description 	Adds one-press access keys to Google search results
// @include 		http://www.google.*/search*
// ==/UserScript==

(function() {
	// Search results are in p elements with a class of 'g'
	// This uses XPath to find all such elements and returns a 
	// snapshot. (A snapshot doesnt become invalid after changing
	// the DOM
	
	var results = document.evaluate("//p[@class='g']", document, null, 
			XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	var counter = 1;
	var querybox = document.evaluate("//input[@name='q']", document, null,
			XPathResult.ORDERED_NODE_ITERATOR_TYPE, null).iterateNext();
	var next_nodes = document.evaluate(
		"//a[span[@class='b' and text()='Next']]",
		document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	var prev_nodes = document.evaluate(
		"//a[span[@class='b' and text()='Previous']]",
		document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	
	var nextlink = null;
	var prevlink = null;

	if (next_nodes.snapshotLength) {
		nextlink = next_nodes.snapshotItem(0).getAttribute('href');
	}
	if (prev_nodes.snapshotLength) {
		prevlink = prev_nodes.snapshotItem(0).getAttribute('href');
	}

	prev_nodes = next_nodes = null;
	
	// We store the links in this array which is used by the keypress
	// handler function
	var links = new Array();
	
	for (var i = 0; i < results.snapshotLength; ++i) {
		var result = results.snapshotItem(i);
		// the first child of the paragraph is a comment element
		// this is a little fragile, maybe should be an XPath lookup
		links.push(result.firstChild.nextSibling.getAttribute("href"));

		// We put the result number in a small-caps red span
		var newspan = document.createElement("span");
		newspan.setAttribute("style", "color:red; font-variant: small-caps;");
		newspan.appendChild(document.createTextNode('' + counter++ + ' '));
		result.insertBefore(newspan, result.firstChild);
	}
	results = null;

	function keypress_handler(e) {
		// thanks to Mark Pilgrim for this:
		// I love Google Searchkeys.  Here's a patch to make it better by
		// ignoring keypresses if a modifer key is held down.  (I found it by
		// typing Ctrl-L to get to the location bar, and Google Searchkeys went
		// to the next page of results.)

		if (e.ctrlKey || e.altKey || e.metaKey) { return true; }
		if (e.target == querybox) {
			return true;
		}
		// e.which contains the ASCII char code of the
		// key which was pressed
		// see: http://web.archive.org/web/20040214161257/devedge.netscape.com/library/manuals/2000/javascript/1.3/reference/handlers.html#1120313
		
		var keypressed = String.fromCharCode(e.which);
		// Thanks to Scott R. Turner, you can hold down shift to open
		// links in a new window (but sadly not a tab till a future version of GM
		// adds that as an API call).

		if (nextlink && (keypressed == 'l' || keypressed == 'L' ||
		                 keypressed == '.')) {
			if (e.shiftKey) {
				window.open(nextlink,'Search Results','');
			} else {
				document.location = nextlink;
			}
			return false;
		}
		if (prevlink && (keypressed == 'h' || keypressed == 'H' ||
		                 keypressed == ',')) {
			if (e.shiftKey) {
				window.open(prevlink,'Search Results','');
			} else {
				document.location = prevlink;
			}
			return false;
		}
		
		if (keypressed < '0' || keypressed > '9') {
			return true;
		}

		var resnum = e.which - "0".charCodeAt(0);
		if (resnum == 0) {
			resnum = 10;
		}

		if (e.shiftKey) {
			window.open(links[resnum - 1],'Search Results','');
		} else {
			document.location = links[resnum - 1];
		}

		return false;
	}

	document.addEventListener('keydown', keypress_handler, false);
})();

