/**
Script to create a drop-down search suggestions box
below an input box.
Copyright 2009 Will Kemp.

$Date: 2009-01-26 06:32:44 +0000 (Mon, 26 Jan 2009) $

*/


/**
URL for script that returns the search suggestions as JSON
*/
var url = "getSearchSuggestions.php";

/**
Minimum number of characters that must be typed before getting
search suggestion.
*/
var minSearch = 2;
var dropDownHeight = 10;

/**
Variable for accessing timeout timer which is used
for hiding the dropdown onBlur.
*/
var hideDropDown;

/**
Working storage.
*/
var suggestionsArray = new Array();
var suggestionList;
var lastField;

var usableBrowser = ( document.createElement && document.getElementsByTagName && !/MSIE 6/i.test(navigator.userAgent));


window.onload = function ()
{
	if ( usableBrowser )
	{
		document.getElementById('suggestiondiv').innerHTML = '<form name="suggestionlistform">'+
			'<select id="suggestions" name="suggestions" onclick="insertSuggestion( this , \'searchstring\' )" size="0" onFocus="dropdownFocus()" onBlur="handleInputBlur()">'+
			'</select>'+
			'</form>';
;
	}
}



/**
Function to clear the suggestion list
*/
function clearSuggestionList( elementid )
{
	/**
	Clear suggestions list selection box, if any.
	*/
	suggestionList = document.getElementById( elementid );
//	suggestionList = document.getElementById( 'suggestions' );
	suggestionList.style.display = 'none';
	var child = suggestionList.childNodes[0];
	while(child != null)
	{
		suggestionList.removeChild(child);
		child = suggestionList.childNodes[0];
	}
}


function keyUpHandler( searchfield , selector , suggestionList ) {
	/**
	Clear timeout timer in case we've come from focus on dropdown.
	*/
	clearTimeout( hideDropDown );

	/**
	Get the text from the input field.
	*/
	var searchString = searchfield.value;

	/**
	Get the value from the select box.
	*/
	var field = document.getElementById( selector ).value;

	/**
	If text string is minSearch or more characters, check if we've
	already got results for this string.
	*/
	if ( searchString.length >= minSearch )
	{
		/**
		Have we already got some suggestions?
		*/
		if ( suggestionsArray[0] )
		{
			/**
			Yes - are they relevant?
			*/
			// Declare regexp
			var ssre2 = new RegExp( '^' + searchString.slice( 0 , 2 ) , 'i' );
			// test 1st 2 chars of search string against 1st 2 chars of suggestions
			if (( ssre2.test( suggestionsArray[0].slice( 0 , 2 ) ) ) && ( lastField == field ))
			{
				/**
				We've already got relevant results - just need to narrow them down.
				Results are for all matches of 1st two characters - now we've
				got more than 1st two, we need to weed out non-matching strings.
				*/
				var currentSuggestions = new Array();
				var startingLength = suggestionsArray.length;
				for ( var i = 0 ; i < startingLength ; i++ )
				{
					/**
					Extract strings that match our current search string. */
					var ssreFull = new RegExp( '^' + searchString , 'i' );
					if ( ssreFull.test( suggestionsArray[i] ) ) 
					{
						currentSuggestions.push( suggestionsArray[i] );
					}
				}
	
				/**
				Go and display results (if any).
				*/
				showSearchSuggestions( currentSuggestions , suggestionList );
			}
			else
			{
			/**
			No, existing search results aren't relevant - send request to server.
			*/
			getSuggestions( field , searchString , suggestionList )
			}
		}
		else
		{
			/**
			No, we haven't already got results - send request to server.
			*/
			getSuggestions( field , searchString , suggestionList )
		}

	}
	else
	{
		/**
		Input string length is less than minimum number of characters
		- stop displaying results.
		*/
		clearSuggestionList( 'suggestions' )
	}
}



function getSuggestions( field , searchString , suggestionList )
{
	/**
	Save field in case we reuse these results.
	*/
	lastField = field;
	var httpRequest = false;

	if ( window.XMLHttpRequest )
	{
		/* Firefox, Opera, Safari, etc. */
		httpRequest = new XMLHttpRequest();
		if ( httpRequest.overrideMimeType )
		{
			httpRequest.overrideMimeType( 'text/xml' );
		}
	}
	else if ( window.ActiveXObject )
	{
		/* IE */
		try
		{
			httpRequest = new ActiveXObject( "Msxml2.XMLHTTP" );
		}
		catch( error1 )
		{
			try
			{
				httpRequest = new ActiveXObject( "Microsoft.XMLHTTP" );
			}
			catch( error2 )
			{
			}
		}
	}

	httpRequest.onreadystatechange = function()
	{
		/**
		The request can return with a few different states,
		but we're interested in "4" (complete).
		*/
		if (httpRequest.readyState == 4)
		{
			if (httpRequest.status == 200)
			{
				handleRequestResults( httpRequest.responseText , suggestionList );
			}
			/* Apparently some versions of IE can return 0 */
			else if (httpRequest.status != 0)
			{
				alert('There was a problem with the request.( Code: ' + httpRequest.status + ')');
			}
		}
	}
	httpRequest.open( 'GET' , url + "?field=" + field + "&string=" + searchString , true );
	httpRequest.send( null );
}


/** Function to take the JSON response, turn it into an array,
	and then call the function to display the suggestions. */
function handleRequestResults( results , suggestionList )
{
	suggestionsArray = eval( '(' + results + ')' );
	showSearchSuggestions( suggestionsArray , suggestionList );
}


function showSearchSuggestions( currentSuggestionList , suggestionList )
{
	suggestions = document.getElementById( suggestionList );
	/**
	Check if any search results
	*/
	if ( currentSuggestionList.length > 0 )
	{
		/**
		Clear the previous results, if there are any.
		*/
		clearSuggestionList( suggestionList )	

		/**
		Set length of suggestions list to number of results or dropDownHeight, whichever is
		smaller.
		*/
		suggestions.attributes.getNamedItem("size").value = ( currentSuggestionList.length <= dropDownHeight ) ? currentSuggestionList.length : dropDownHeight;
		suggestions.style.display = 'block';
		
		/**
		Iterate through the returned search results and add them to
		the selection box. Convert them to lower case because the raw data
		is all over the place (in true Windows style!).
		*/
		for(count = 0; count < currentSuggestionList.length; count++)
		{
			suggestions.appendChild( document.createElement('option') );
			var thisItem = currentSuggestionList[count] + '';
			suggestions.options[count].text = thisItem.toLowerCase();
			suggestions.options[count].value = currentSuggestionList[count];
		}	
	}
	else
	{
		/**
		No results. Clear old suggestions, if any.
		*/
		clearSuggestionList( suggestionList );
	}
}

function dropdownFocus()
{
	/** Clear the timeout which has been set by moving focus off
	the input. */
	clearTimeout( hideDropDown );
	
}

function insertSuggestion( suggestionList , fieldId )
{
	/**
	This function takes a text string as the parameter, inserts
	it into the search box input, and does the form action.
	Convert it to lower case for the same reasons it's done above.
	*/

	/** Clear the timeout which has been set by moving focus off
	the input. */
	clearTimeout( hideDropDown );

	/**
	Send the suggestion string to the input field.
	*/
	document.getElementById( fieldId ).value = suggestionList.value.toLowerCase();
	clearSuggestionList( suggestionList.id );

	/**
	Add a hidden field to the search form to say the search string
	has come from a suggestion - i.e., it's in the database as sent
	and therefore search for this exact string, not an approximate
	match.
	from=suggestions
	*/
	var fromtag = document.createElement( 'input' );
	fromtag.setAttribute( 'type' , 'hidden' );
	fromtag.setAttribute( 'name' , 'from' );
	fromtag.setAttribute( 'value' , 'suggestions' );
	document.getElementById( fieldId ).parentNode.appendChild( fromtag );

	/**
	And submit the form.
	*/
	document.getElementById( fieldId ).parentNode.submit();
}


/** Function to hide the suggestion list if the mouse focus moves
off the input. When focus moves back to input, we show it again by
calling the keyUp handler. */
function handleInputBlur() {
	hideDropDown = setTimeout( "suggestionList.style.display = 'none'" , 100 );
}




