

//	2006.06.20

//	Autosuggest

//	Ressources for autosuggest function in search fields.
//
//	Code inspired from http://www.hagus.net/taxonomy/term/8

//	TO DO
//		- fully understand that one

//	2006.06.20 Patrice added autosubmit when only one result is found
//	2006.05.20 Patrice created.

//	GET : (none)





// Javascript class constructor. Our arguments are: url to call
// during XML request, a function to turn that into some anchor tags,
// a div in which to place them, and the form element that's getting all
// this attention. 
//	- resultDiv : the pseudo drop down list that holds the found value. Each child MUST contain a pID attribute that holds the ID of the person or product.
// 	- formElementFound : the hidden input that will hold the ID of the found product or person. 
//	- actionFunction : the name of the function to call when a single item is clicked upon.
//	- extraField : the name of the additional field to fill with the found data (such as an item price, ...)

function AutoComplete(url, xmlFunction, resultDiv, formElement ,formElementFound ,actionFunction ,extraField1 ,extraField2 ,extraField3)
{
	this.populateXML = xmlFunction;
	this.resultDiv = resultDiv;
	this.formElement = formElement;
	this.formElementFound = formElementFound;
	this.actionFunction = actionFunction;
	this.req = "";
	this.url = url;
	this.extraField1 = extraField1;
	this.extraField2 = extraField2;
	this.extraField3 = extraField3;
	
	this.processClick = AutoComplete_processClick;
	this.processTheOne = AutoComplete_processTheOne;
	this.processReqChange = AutoComplete_processReqChange;
	this.repositionSearchResult = AutoComplete_repositionSearchResult;
	this.closeResults = AutoComplete_closeResults;
	this.openResults = AutoComplete_openResults;
	this.keyUp = AutoComplete_keyUp;
	this.blur = AutoComplete_blur;
	this.checkTab = AutoComplete_checkTab;

	// through javascript's "magic" scoping capabilities, this will
	// remain valid when we switch back to the context of any of these
	// functions. "this" on the other hand, cannot be relied upon.
	var realThis = this;
	
	// set up some event handling routines for the input box.
	addEvent(formElement, "keyup", this.keyUp);
	addEvent(formElement, "keydown", this.checkTab);
	
	// yes ... no closing }, we're declaring them inside this constructor.
	
	// handle someone clicking on an anchor.
	function AutoComplete_processClick(e)
	{
		// just making sure pID is meaningful
		if (e.getAttribute('pID') > 0)
		{
			// fill the input field
			realThis.formElement.value = e.firstChild.nodeValue;
			// fill the hidden ID field
			realThis.formElementFound.value = e.getAttribute('pID');
			// fill the extra field
			if (realThis.extraField1 != 0) realThis.extraField1.value = e.getAttribute('pExtra1');
			// fill the extra field
			if (realThis.extraField2 != 0) realThis.extraField2.value = e.getAttribute('pExtra2');
			// fill the extra field
			if (realThis.extraField3 != 0) realThis.extraField3.value = e.getAttribute('pExtra3');
			// hide results
			realThis.closeResults();
			// auto submit
			realThis.actionFunction(realThis.formElement);
		}
	}
	
	// handle Xml return of only one result (as might happen when scanning a unique id with a barcode reader)
	function AutoComplete_processTheOne(e)
	{
		// fill the input field
		realThis.formElement.value = e.firstChild.nodeValue;
		// fill the hidden ID field
		realThis.formElementFound.value = e.getAttribute('pID');
		// fill the extra field
		if (realThis.extraField1 != 0) realThis.extraField1.value = e.getAttribute('pExtra1');
		// fill the extra field
		if (realThis.extraField2 != 0) realThis.extraField2.value = e.getAttribute('pExtra2');
		// fill the extra field
		if (realThis.extraField3 != 0) realThis.extraField3.value = e.getAttribute('pExtra3');
		// hide results
		realThis.closeResults();
		// auto submit
		realThis.actionFunction(realThis.formElement);
	}	
	
	// Check for someone tabbing out of our field.
	function AutoComplete_checkTab(e)
	{
		// If it's a tab keydown ...
		if (e.keyCode == 9)
		{
			realThis.closeResults();
			if (!e) var e = window.event;
			e.cancelBubble = true;
			if (e.stopPropagation) e.stopPropagation();
		}
		return true;
	}
	
	// Couldn't get this to work correctly.
	function AutoComplete_blur(e)
	{
		//realThis.closeResults();
	}
	
	// Callback invoked when there are XML results from our XMLHttpRequest
	// class call.
	function AutoComplete_processReqChange()
	{
		if (typeof req == 'object' )
		{
			// something is ready.
			if (req.readyState == 4)
			{
				// and it was 200, OK.
				if (req.status == 200)
				{	
					// pull out the XML and give it to our user supplied mangling functions
					var xml = req.responseXML;
					var result = realThis.populateXML(xml, realThis.resultDiv);	
							
					// now iterate through the anchors and give each one an onclick handler
					var anchors = realThis.resultDiv.getElementsByTagName("a");
					for (var i = 0; i < anchors.length; i++)
					{
						addEvent(anchors[i], "click", function(evt)
						{
							var evt = evt.target ? evt.target : evt.srcElement;
							realThis.processClick(evt);
						});
					}
					
					// make sure the result div position is correct
					realThis.repositionSearchResult();
					
					// test for The One (unique result)
					if (anchors.length == 1) realThis.processTheOne(anchors[0]);
				}
			}
		}
	}
	
	// Figure out where we want our search results div, and how wide.
	function AutoComplete_repositionSearchResult()
	{	
		var left = findPosX(realThis.formElement);
		var top = findPosY(realThis.formElement) + realThis.formElement.offsetHeight;
		
		realThis.resultDiv.style.left = left + "px";
		realThis.resultDiv.style.top = top + "px";
		realThis.resultDiv.style.width = realThis.formElement.offsetWidth + "px";
	}
	
	// Helper function from quicksmode.org
	function findPosX(obj)
	{
		var curleft = 0;
		if (obj.offsetParent)
		{
			while (obj.offsetParent)
			{
				curleft += obj.offsetLeft
				obj = obj.offsetParent;
			}
		}
		else if (obj.x)
			curleft += obj.x;
		return curleft;
	}
	
	// Ditto.
	function findPosY(obj)
	{
		var curtop = 0;
		if (obj.offsetParent)
		{
			while (obj.offsetParent)
			{
				curtop += obj.offsetTop
				obj = obj.offsetParent;
			}
		}
		else if (obj.y)
			curtop += obj.y;
		return curtop;
	}
	
	// Close the results div and flush its results.
	function AutoComplete_closeResults()
	{
		realThis.resultDiv.style.display = "none";
		realThis.resultDiv.innerHTML = null;
	}
	
	// Display the results div.
	function AutoComplete_openResults()
	{
		realThis.resultDiv.style.display = "block";
	}
	
	// Handle what happens when a user presses a key in the input box.
	function AutoComplete_keyUp(evt)
	{	
		// clear hidden ID field
		realThis.formElementFound.value = 0;
		// clear extra fields
		if (realThis.extraField1 != 0) realThis.extraField1.value = '';					
		if (realThis.extraField2 != 0) realThis.extraField2.value = '';					
		if (realThis.extraField3 != 0) realThis.extraField3.value = '';					
			
		var whichKey = evt.keyCode;
			
		evt = evt.target ? evt.target : evt.srcElement;
	
		realThis.repositionSearchResult();
	 
	 	if (whichKey == 27)
	 	{
	 		evt.value = "";
	 		realThis.closeResults();
	 		return;
	 	}
		
		// grab the string ...
		var str = new String(evt.value);
		
		if (str.length > 0)
			realThis.openResults();
		else
			realThis.closeResults();
		
		// Despatch the XMLHttpRequest.
		if (window.XMLHttpRequest)
			req = new XMLHttpRequest();
		else if (window.ActiveXObject)
			req = new ActiveXObject("Microsoft.XMLHTTP");
		
		req.onreadystatechange = realThis.processReqChange;
		req.open("GET", realThis.url + str.toUpperCase(), true);
		req.send(null);
	}

}

// Helper function from quicksmode.org (I think?)
function addEvent(obj, evType, fn, useCapture)
{
	if (obj.addEventListener)
	{
    	obj.addEventListener(evType, fn, useCapture);
    	return true;
	} 
	else if (obj.attachEvent)
	{
		var r = obj.attachEvent("on"+evType, fn);
		return r;
	}
	else
	{
		alert("Handler could not be attached");
	}
}
