// 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.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.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.numberSearchResults = function() {
	window.aquireSearchResults();
	
	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
		window.followSearchResultLink(window.SelectedSearch);
		return;
	}
	
	switch(key)
	{
		case "K": // up
			window.highlightPrevSearchResult();
			break;
		case "J": // down
			window.highlightNextSearchResult();
			break;
		case "F": // enter
			window.followSearchResultLink(window.SelectedSearch);
			break;
		case "1":
			window.followSearchResultLink(0);
			break;
		case "2":
			window.followSearchResultLink(1);
			break;
		case "3":
			window.followSearchResultLink(2);
			break;
		case "4":
			window.followSearchResultLink(3);
			break;
		case "5":
			window.followSearchResultLink(4);
			break;
		case "6":
			window.followSearchResultLink(5);
			break;
		case "7":
			window.followSearchResultLink(6);
			break;
		case "8":
			window.followSearchResultLink(7);
			break;
		case "9":
			window.followSearchResultLink(8);
			break;
		case "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);
}

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