// Google User Interface Enhancement
// Version 0.11
// 2006-11-21
// Copyright (c) 2006, Richard Vodden, http://vodden.com/
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
// ==UserScript==
// @name          	Google User Interface Enhancement
// @description	Provides Keyboard shortcuts and mousewheel support for google.
// @include       	http://google.*
// @include      	http://www.google.*
// ==/UserScript==

window.SelectedSearch = -1;

window.getAbsoluteTop = function (o) {
	// Get an object top position from the upper left viewport corner
	// Tested with relative and nested objects
	oTop = o.offsetTop;            // Get top position from the parent object
	while(o.offsetParent != null) {   // Parse the parent hierarchy up to the document element
		oParent = o.offsetParent;    // Get parent object reference
		oTop += oParent.offsetTop;  // Add parent top position
		o = oParent;
	}
	// Return top position
	return oTop;
}

window.aquireSearchResults = function () {
	window.SearchResults = document.evaluate("//div[@class='g']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	window.NumberOfSearchResults = SearchResults.snapshotLength;
}

window.aquireNextPrevLinks = function() {	
	window.NextLink = window.PrevLink = null;

	window.NextPrevLinks = document.evaluate("//td[@class='b']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	if ( NextPrevLinks.snapshotLength == 2 ) {
		PrevLink = NextPrevLinks.snapshotItem(0).firstChild.getAttribute("href");
		NextLink = NextPrevLinks.snapshotItem(1).firstChild.getAttribute("href");
	} else {
		NextLink = NextPrevLinks.snapshotItem(0).firstChild.getAttribute("href");
	}
}

window.highlightSearchResult = function ( SearchResultNumber ) {
	SearchResult = window.SearchResults.snapshotItem(SearchResultNumber);
	SearchResult.style.backgroundColor = "#FFFF99";	
}

window.unhighlightSearchResult = function (SearchResultNumber ) {
	SearchResult = window.SearchResults.snapshotItem(SearchResultNumber);
	SearchResult.style.backgroundColor = "#FFFFFF";
}

window.checkWindowScroll = function (SearchResultNumber) {
	
	SearchResult = window.SearchResults.snapshotItem(SearchResultNumber);
	
	if( getAbsoluteTop(SearchResult) < window.pageYOffset )  // Search result off the top of the page
		SearchResult.scrollIntoView(true)
	else ( getAbsoluteTop(SearchResult) > (window.pageYOffset + window.innerHeight) ) 
		SearchResult.scrollIntoView(false);
}	

window.focusSearchTextBox = function () {
	TextBox = document.evaluate(".//input[@name='q']",document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	TextBox.focus();
}

window.followDidYouMeanLink = function () {

	SearchLink = document.evaluate(".//a[@class='p']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	uri = SearchLink.getAttribute("href");

	window.location = uri;
}

window.followNextLink = function () {
	if (NextLink != null) {
		window.location = NextLink;
	}	
}

window.followPrevLink = function () {
	if (PrevLink != null) {
		window.location = PrevLink;
	}
}

window.followSearchResultLink = function ( SearchResultNumber ) {

	SearchResult = window.SearchResults.snapshotItem(SearchResultNumber);
	SearchLink = document.evaluate(".//a[@class='l']", SearchResult, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	uri = SearchLink.getAttribute("href");

	window.location = uri;
}

window.followSearchResultCacheLink = function ( SearchResultNumber ) {

	SearchResult = window.SearchResults.snapshotItem(SearchResultNumber);
	SearchLink = document.evaluate(".//a[@class='fl' and text()='Cached']", SearchResult, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	uri = SearchLink.getAttribute("href");

	window.location = uri;
}

window.followSearchResultRelatedLink = function ( SearchResultNumber ) {

	SearchResult = window.SearchResults.snapshotItem(SearchResultNumber);
	SearchLink = document.evaluate(".//a[@class='fl' and text()='Similar pages']", SearchResult, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	uri = SearchLink.getAttribute("href");

	window.location = uri;
}

window.numberSearchResults = function() {
	window.aquireSearchResults();
	window.aquireNextPrevLinks();
	
	for ( j = 0; j < window.NumberOfSearchResults; j++ ) {
		SearchResult = window.SearchResults.snapshotItem(j);
		SearchResult.innerHTML = '<span style="font-size: 80%">' + (j+1) + '. '  + '</span>' + SearchResult.innerHTML;
	}
	
	window.NumberOfSearchResults = j - 1;
}

window.highlightNextSearchResult = function () {
	
	if (window.SelectedSearch == window.NumberOfSearchResults)
		return;
		
	if (window.SelectedSearch >= 0 ) 
		window.unhighlightSearchResult(window.SelectedSearch);
	
	window.SelectedSearch++;
	window.highlightSearchResult(window.SelectedSearch);
	window.checkWindowScroll(window.SelectedSearch);
}

window.highlightPrevSearchResult = function () {
	
	if (window.SelectedSearch == 0)
		return;
		
	if (window.SelectedSearch <= window.NumberOfSearchResults ) 
		window.unhighlightSearchResult(window.SelectedSearch);
	
	window.SelectedSearch--;
	window.highlightSearchResult(window.SelectedSearch);
	window.checkWindowScroll(window.SelectedSearch);
}

window.processKeyPress = function( e ) {

	// We want only single keys, no modifiers
	if(e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)
		return false;
	
	// We don't want to intercept keystrokes when the user is typing
	switch(e.target.tagName)
	{
		case "TEXTAREA":
			return false;
		case "INPUT":
			return false;
		// CODE TK: Detect if focus is on any of the search buttons and allow keys to work.
	}
	
	key = String.fromCharCode(e.keyCode);
	
	if (e.keyCode == 13) { // This is Enter
		if ( window.SelectedSearch < 0 ) { //if a search result is selected, go to it, otherwise look for a "did you mean" and go to that.
			window.followDidYouMeanLink();
		} else{
			window.followSearchResultLink(window.SelectedSearch);
		}
		return;
	}
	
	switch(e.keyCode)
	{
		case 75: // "K"
			window.highlightPrevSearchResult();
			break;
		case 74: // "J"
			window.highlightNextSearchResult();
			break;
		case 70: // "F"
			window.followSearchResultLink(window.SelectedSearch);
			break;
		case 67: // "C"
			window.followSearchResultCacheLink(window.SelectedSearch);
			break;
		case 82: // "R"
			window.followSearchResultRelatedLink(window.SelectedSearch);
			break;
		case 83: // "S"
			window.focusSearchTextBox();
			e.preventDefault(); // Stop an S appearing in the text box
			break;
		case 190: // "."
			window.followNextLink();
			break;
		case 188: // ","
			window.followPrevLink();
			break;
		case 49: // 1
			window.followSearchResultLink(0);
			break;
		case 50: // 2
			window.followSearchResultLink(1);
			break;
		case 51: // 3
			window.followSearchResultLink(2);
			break;
		case 52: // 4
			window.followSearchResultLink(3);
			break;
		case 53: // 5
			window.followSearchResultLink(4);
			break;
		case 54: // 6
			window.followSearchResultLink(5);
			break;
		case 55: // 7
			window.followSearchResultLink(6);
			break;
		case 56: // 8
			window.followSearchResultLink(7);
			break;
		case 57: // 9
			window.followSearchResultLink(8);
			break;
		case 48: // 0
			window.followSearchResultLink(9);
			break;
			
	}

}

window.processScrollWheel  = function (e)
{
	// This function converts a mousewheel event into a keyboard event
	// It will generate a j (keycode 75) if the wheel scrolls up
	// It will generate a k (keycode 74) if the wheel scrolls down

	var x = {};

	x.target = e.target;
	x.keyCode = (e.detail < 0) ? 75 : 74;

	processKeyPress(x);
}

about=document.createElement("div");
about.innerHTML="<font size=1 color=gray>&nbsp;&nbsp;j:up k:down c:cache r:similar .:prev ,:next</font>";
document.body.appendChild(about);
about.style.position="absolute";
about.style.top= "0";
about.style.left="0"	


window.addEventListener("load", window.numberSearchResults, false);
window.addEventListener("keydown", window.processKeyPress, false);
window.addEventListener("DOMMouseScroll",window.processScrollWheel, false);
