/*******************************************************AutoSuggest - a javascript automatic text input completion componentCopyright (C) 2005 Joe Kepley, The Sling & Rock Design Group, Inc.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*******************************************************Please send any useful modifications or improvements via email to joekepley at yahoo (dot) com*******************************************************//******************************************************** The AutoSuggest class binds to a text input field and creates an automatic suggestion dropdown in the style of the "IntelliSense" and "AutoComplete" features of some desktop apps.  Parameters:  elem: A DOM element for an INPUT TYPE="text" form field suggestions: an array of strings to be used as suggestions              when someone's typing. Example usage:   Please enter the name of a fruit. <input type="text" id="fruit" name="fruit" /> <script language="Javascript"> var fruits=new Array("apple","orange","grape","kiwi","cumquat","banana"); new AutoSuggest(document.getElementById("fruit",fruits)); </script> Requirements:  Unfortunately the AutoSuggest class doesn't seem to work  well with dynamically-created DIVs. So, somewhere in your  HTML, you'll need to add this:  <div id="autosuggest"><ul></ul></div> Here's a default set of style rules that you'll also want to  add to your CSS:  .suggestion_list { background: white; border: 1px solid; padding: 4px; } .suggestion_list ul { padding: 0; margin: 0; list-style-type: none; } .suggestion_list a { text-decoration: none; color: navy; } .suggestion_list .selected { background: navy; color: white; } .suggestion_list .selected a { color: white; } #autosuggest { display: none; }*********************************************************/function AutoSuggest(elem, suggestions){	//The 'me' variable allow you to access the AutoSuggest object	//from the elem's event handlers defined below.	var me = this;	//A reference to the element we're binding the list to.	this.elem = elem;	this.suggestions = suggestions;	//Arrow to store a subset of eligible suggestions that match the user's input	this.eligible = new Array();	//The text input by the user.	this.inputText = null;	//A pointer to the index of the highlighted eligible item. -1 means nothing highlighted.	this.highlighted = -1;	//A div to use to create the dropdown.	this.div = document.getElementById("autosuggest");	//Do you want to remember what keycode means what? Me neither.	var TAB = 9;	var ESC = 27;	var KEYUP = 38;	var KEYDN = 40;		//The browsers' own autocomplete feature can be problematic, since it will 	//be making suggestions from the users' past input.	//Setting this attribute should turn it off.	elem.setAttribute("autocomplete","off");	//We need to be able to reference the elem by id. If it doesn't have an id, set one.	if(!elem.id)	{		var id = "autosuggest" + idCounter;		idCounter++;		elem.id = id;	}	/********************************************************	onkeydown event handler for the input elem.	Tab key = use the highlighted suggestion, if there is one.	Esc key = get rid of the autosuggest dropdown	Up/down arrows = Move the highlight up and down in the suggestions.	********************************************************/	elem.onkeydown = function(ev)	{		var key = me.getKeyCode(ev);		switch(key)		{			case TAB:			me.useSuggestion();			break;			case ESC:			me.hideDiv();			break;			case KEYUP:			if (me.highlighted > 0)			{				me.highlighted--;			}			me.changeHighlight(key);			break;			case KEYDN:			if (me.highlighted < (me.eligible.length - 1))			{				me.highlighted++;			}			me.changeHighlight(key);			break;		}	};	/********************************************************	onkeyup handler for the elem	If the text is of sufficient length, and has been changed, 	then display a list of eligible suggestions.	********************************************************/	elem.onkeyup = function(ev) 	{		var key = me.getKeyCode(ev);		switch(key)		{		//The control keys were already handled by onkeydown, so do nothing.		case TAB:		case ESC:		case KEYUP:		case KEYDN:			return;		default:			if (this.value != me.inputText && this.value.length > 0)			{				me.inputText = this.value;				me.getEligible();				me.createDiv();				me.positionDiv();				me.showDiv();			}			else			{				me.hideDiv();			}		}	};	/********************************************************	Insert the highlighted suggestion into the input box, and 	remove the suggestion dropdown.	********************************************************/	this.useSuggestion = function()	{		if (this.highlighted > -1)		{			this.elem.value = this.eligible[this.highlighted];			this.hideDiv();			//It's impossible to cancel the Tab key's default behavior. 			//So this undoes it by moving the focus back to our field right after			//the event completes.			setTimeout("document.getElementById('" + this.elem.id + "').focus()",0);		}	};	/********************************************************	Display the dropdown. Pretty straightforward.	********************************************************/	this.showDiv = function()	{		this.div.style.display = 'block';	};	/********************************************************	Hide the dropdown and clear any highlight.	********************************************************/	this.hideDiv = function()	{		this.div.style.display = 'none';		this.highlighted = -1;	};	/********************************************************	Modify the HTML in the dropdown to move the highlight.	********************************************************/	this.changeHighlight = function()	{		var lis = this.div.getElementsByTagName('LI');		for (i in lis)		{			var li = lis[i];			if (this.highlighted == i)			{				li.className = "selected";			}			else			{				li.className = "";			}		}	};	/********************************************************	Position the dropdown div below the input text field.	********************************************************/	this.positionDiv = function()	{		var el = this.elem;		var x = 0;		var y = el.offsetHeight;			//Walk up the DOM and add up all of the offset positions.		while (el.offsetParent && el.tagName.toUpperCase() != 'BODY')		{			x += el.offsetLeft;			y += el.offsetTop;			el = el.offsetParent;		}		x += el.offsetLeft;		y += el.offsetTop;		this.div.style.left = x + 'px';		this.div.style.top = y + 'px';	};	/********************************************************	Build the HTML for the dropdown div	********************************************************/	this.createDiv = function()	{		var ul = document.createElement('ul');			//Create an array of LI's for the words.		for (i in this.eligible)		{			var word = this.eligible[i];				var li = document.createElement('li');			var a = document.createElement('a');			a.href="javascript:false";			a.innerHTML = word;			li.appendChild(a);				if (me.highlighted == i)			{				li.className = "selected";			}				ul.appendChild(li);		}			this.div.replaceChild(ul,this.div.childNodes[0]);			/********************************************************		mouseover handler for the dropdown ul		move the highlighted suggestion with the mouse		********************************************************/		ul.onmouseover = function(ev)		{			//Walk up from target until you find the LI.			var target = me.getEventSource(ev);			while (target.parentNode && target.tagName.toUpperCase() != 'LI')			{				target = target.parentNode;			}					var lis = me.div.getElementsByTagName('LI');							for (i in lis)			{				var li = lis[i];				if(li == target)				{					me.highlighted = i;					break;				}			}			me.changeHighlight();		};		/********************************************************		click handler for the dropdown ul		insert the clicked suggestion into the input		********************************************************/		ul.onclick = function(ev)		{			me.useSuggestion();			me.hideDiv();			me.cancelEvent(ev);			return false;		};			this.div.className="suggestion_list";		this.div.style.position = 'absolute';	};	/********************************************************	determine which of the suggestions matches the input	********************************************************/	this.getEligible = function()	{		this.eligible = new Array();		for (i in this.suggestions) 		{			var suggestion = this.suggestions[i];						if(suggestion.toLowerCase().indexOf(this.inputText.toLowerCase()) == "0")			{				this.eligible[this.eligible.length]=suggestion;			}		}	};	/********************************************************	Helper function to determine the keycode pressed in a 	browser-independent manner.	********************************************************/	this.getKeyCode = function(ev)	{		if(ev)			//Moz		{			return ev.keyCode;		}		if(window.event)	//IE		{			return window.event.keyCode;		}	};	/********************************************************	Helper function to determine the event source element in a 	browser-independent manner.	********************************************************/	this.getEventSource = function(ev)	{		if(ev)			//Moz		{			return ev.target;		}			if(window.event)	//IE		{			return window.event.srcElement;		}	};	/********************************************************	Helper function to cancel an event in a 	browser-independent manner.	(Returning false helps too).	********************************************************/	this.cancelEvent = function(ev)	{		if(ev)			//Moz		{			ev.preventDefault();			ev.stopPropagation();		}		if(window.event)	//IE		{			window.event.returnValue = false;		}	}}//counter to help create unique ID'svar idCounter = 0;