/**

Ajax4Suggest Framework Core 1.0 - Service Pack 1.0

Copyright (c) 2005 - Guillaume Leleu



This program is free software; you can redistribute it and/or

modify it under the terms of the GNU General Public License

as published by the Free Software Foundation; either version 2

of the License, or (at your option) any later version.



This program is distributed in the hope that it will be useful,

but WITHOUT ANY WARRANTY; without even the implied warranty of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

GNU General Public License for more details.



You should have received a copy of the GNU General Public License

along with this program; if not, write to the Free Software

Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.



@note from the author

You are not allowed to use this library (or part of it) into a proprietary program

 or applications which is not under the GNU GPL term (free software itself).

If you intend to do so, contact me and you will be charged for the usage rights.



@inspired from the inital library of Julian Robichaux, http://www.nsftools.com

@author Guillaume Leleu - antispam1@myweb-services.com



Supported Browsers: IE 6.0+, Mozilla based browsers

*/



// Begin of Work around for Mozilla browser that cannot cancel a ENTER event

function noEnter(e){

	// For IE

	if (!e) {

		e = window.event;

	}

	var key = e.keyCode;

	if(key == '13') {

		return false;

	}

}

// End of Work around for Mozilla browser that cannot cancel a ENTER event



/**

 * @class Ajax4Suggest (form object (input), string (URL), string (GET/POST), 

 *                      form object (form), backend response (JS/XML/SOAP), classStyle(CSS)

 * @access public

 * @see If you use POST, the whole form will be submitted not only the input field

 */

function Ajax4Suggest(keyFieldName, queryFieldName, lookupURLPrefix, _reqprotocol, _formname, _btype) {



	// Key event definition

	var KEYUP = 38;

	var KEYDOWN = 40;

	var KEYENTER = 13;

	var KEYTAB = 9;

	var KEYESC = 27;

		

	// Create the cache Object

	var cache = new Object();

	// For Mozilla

	var evt = window.event;



	// Keyboard counter

	var kcounter = 3;

	// Delay time to avoid server overload

	var delaytime = 1000;

	// Searh in progress flag

	var searching = false;

	// Value

	var val = null;	

	// last Value (query) done

	var lastVal = null;

	// Cache Results

	var cacheResult = null;

	// Div Results visible or not

	var isVisible = false;

	// Get the query field using DOM		

	var queryField = document.getElementById(queryFieldName);	
	
	var keyField = document.getElementById(keyFieldName);

	// ResultsDiv

	var ResultsDivId = null;

	// Set the URL for the backend

	var lookupURL = lookupURLPrefix;

	// Set the req protocol (GET/POST) - GET by default

	var reqprotocol = _reqprotocol;

	if (reqprotocol != 'POST') {

		reqprotocol = 'GET';

	}

	// Get the form (used with POST)

	var formname = _formname;

	if ((!formname || formname == 'undefined') && reqprotocol == 'POST') {

		alert('With POST protocol, you have to specify which form will be submitted.');

		return false;

	}	

	//Set the backend type (Javascript -JS-, XML or SOAP)

	var btype = _btype;

	// Control the params

	switch(btype) {

		case 'JS': break;

		case 'XML': break;

		case 'SOAP': break;

		default: btype = 'JS';

	}

	// Set the DIV results name	

	//var ResultsDivId =  createDiv4Results("ResultsDiv4" + queryFieldName);



	// onBlur remove the div visibility

	queryField.onblur = outOfList;

	// onBlur remove the div visibility

	queryField.onfocus = deleteField;	

	// Main page event - a key is pressed

	queryField.onkeyup = keypressUpHandler;

	// Main page event - a key is pressed

	queryField.onkeydown = keypressDownHandler;



	/**

	* @method keypressDownHandler(object)

	* @access private

	* @see Used to handle TAB and ENTER event

	* @return void

	*/		

	function keypressDownHandler(evt) {

		

		// For IE

		if (!evt) {

			evt = window.event;

		}

 		var key = evt.keyCode;

 		val = escape(queryField.value);

		

		// A value exists in the input and no search in progress

 		if (queryField.value.length > 0 && searching == false && isVisible == true) {

		

			if (key == KEYDOWN) {

				// get where the user is in the list (row in the list)

				getSelectedRowNum(ResultsDivId, 1);

			} else if (key == KEYUP) {

				// get where the user is in the list (row in the list)

				getSelectedRowNum(ResultsDivId, -1);

			}

		  // select the entry (row in the list)

		  var selSpan = setSelectedSpan(ResultsDivId, kcounter);

		  // A line is selected

		  if (selSpan) {

				// Tab key is used

			  if (key == KEYTAB) {

			      _selectResult(selSpan);

			      kcounter = -1;

			    	return false;

			  }	

				// Tab key is used

			  if (key == KEYENTER) {

			      _selectResult(selSpan);

			      kcounter = -1;

			      // Work for IE but not for Mozilla

			    	return false;

			  }			  

			}

		}

	}

	

	/**

	* @method keypressUpHandler(object)

	* @access private

	* @see Used to handle KEY DOWN, KEY UP and normal entry event

	* @return void

	*/		

	function keypressUpHandler(evt) {

		

		// For IE

		if (!evt) {

			evt = window.event;

		}

 		var key = evt.keyCode;

 		// Avoid to overload the server

 		setTimeout(DoNothing, delaytime);

 		//val = GetInputVal;

 		val = escape(queryField.value);

		

 		// A value exists in the input and no search in progress

 		if (queryField.value.length > 0 && searching == false) {

 			// A "normal" key is entered

			if ((key != KEYUP) && (key != KEYDOWN) && (key != KEYENTER) && (key != KEYTAB) && (key != KEYESC)) {

				// The last key entered is different from the current one - NO cache

				if (lastVal != val) {

					//POST requested: we have to create a pair/value

					if (reqprotocol == 'POST') {

						val = getFormValues(formname);

					}

					doRemoteQuery();

			  	// Use the cache

				} else {

				  	cacheResult = cache[val];

				  	getCache(); 

				}

				// Set the last Val entry with the current one

				lastVal = val;

			}

		 	// A "ESC" key is used

			else if (key == KEYESC) {

				queryField.value = "";
				keyField.value ="";

				showDivResults(false);

			}

  		// End of a value exists in the input and no search in progress

	  }	  	

	  // Bug for IE - Solved it by removing the list

	  else if (queryField.value.length < 1) {

			showDivResults(false);

	 	}

	}	

	/**

	* @method showDivResults(boolean)

	* @access private

	* @see 	This either shows or hides the lookup div, depending on the value of

	*       the "show" parameter.

	* @return void

	*/

	function showDivResults(show)

	{

	  if (show) {

	    ResultsDivId.style.visibility = "visible";

	    isVisible = true;

	  }

	  else {

	    ResultsDivId.style.visibility = "hidden";

	    isVisible = false;

	  }

	}

	/**

	* @method outOfList(void)

	* @access private

	* @see 

	* @return void

	*/	

	function outOfList() {

		// Hide the DIV

		showDivResults(false)

	}

	/**

	* @method deleteField(void)

	* @access private

	* @see 

	* @return void

	*/	

	function deleteField() {

		queryField.value ="";
		keyField.value="";

		// Set the DIV results name	(IE problem with DOM - The page needs to be fully loaded

		ResultsDivId =  createDiv4Results("ResultsDiv4" + queryFieldName);

		showDivResults(false)		

	}



	/**

	* @method createXMLHTTP(void)

	* @access private

	* @see Set up the XMLHTTP object we're using for the dynamic lookups.

	* @return _xmlhttp(object)

	*/

	function createXMLHTTP() {

		var _xmlhttp = null;

	  

	  // Try IE with MSXML 2

	  try {

	  	_xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");

	  }

	  catch(e) {

	  	// Try IE with XML HTTP

	    try {

	      _xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

	    }

	    catch(e1) {

	    	// Try XMLHTTPRequest()

	    	try {

	      	_xmlhttp = new XMLHttpRequest();

	    	}

	    	catch(e2) {

	    	  alert("Sorry, your browser does not support XML over HTTP using Javascript.");

	    	}

	    }

	  }

	  return _xmlhttp;

	}

	/**

	* @method doRemoteQuery(string)

	* @access private

	* @see sends the lookup request (as a URL with a query string) to a server in the background

	* @return void

	*/

	function doRemoteQuery() {

				

			  /* xmlHTTP state

 		    	*0 (uninitialized) 

				*1 (loading) 

				*2 (loaded) 

				*3 (interactive) 

				*4 (complete)

				*/	

		// Create the XML over HTTP object

	  xmlHttp = createXMLHTTP();

	  if (xmlHttp){

	  	// Send the GET or POST request

	  	if (reqprotocol != 'POST') {

	    	xmlHttp.open("GET", lookupURL + val, true);

	    // Send the POST request		    	

	  	} else {

	  		xmlHttp.open("POST", lookupURL, true);

	  	}

	    // What do we do when the response comes back?

	    xmlHttp.onreadystatechange = function() {

	    	// OK Response is here and call successful

	      if (xmlHttp.readyState == 4 && xmlHttp.responseText) {

		    	searching = false;

	      	// Fine HTTP status OK

	      	if (xmlHttp.status == 200) {

	      		window.status = "Call done successfully.";

	        	// We have a XML or SOAP response

	        	if (btype == 'XML' || btype == 'SOAP') {

					ResultsFromXML(xmlHttp.responseXML);

	        	// We have a javascript response

	        	} else {

	        		//alert(xmlHttp.responseText);

		    		eval(xmlHttp.responseText);

	        	}        	

    		window.status = "";

			// Probably an error msg

			} else if (xmlHttp.status == 500) {

				window.status = "Call done but application error found.";

				alert("HTTP error: " + xmlHttp.status);

			// Anything else (404 - not found, 403/401 - not authenticated...)

			} else {

				window.status = "Call cancelled. HTTP error.";

				alert("HTTP error: " + xmlHttp.status);

			}

	      // Call in progress and response not yet received

	      } else if (xmlHttp.readyState == 1) {

	      	 window.status = "Call in progress!";

	      	 searching = true;

	      }

	    };

	  	// Send the GET request

	  	if (reqprotocol != 'POST') {

	    	xmlHttp.send(null);

	    // Send the POST request	

	  	} else {

	  		xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");

	  		xmlHttp.send(val);

	  	}	    

	  } else {

	  	alert("Sorry, I am not able to create the XML over HTTP object.");

	  }

	}

	/**

	* @method createDiv4Results(object: div)

	* @access private

	* @see 	Create the <DIV> we're using to display the lookup results

	* @return object (div)

	*/		

	function createDiv4Results(divId)

	{

		// Create  the DIV node

	    var newNode = document.createElement("div");

	    // Set the DIV id

	    newNode.setAttribute("id", divId);

		// Append the HTML page witht the DIV

		// Bug with IE!! the form is inside a table, you cannot set the positioning

		// but if not positionned inside the table...IE does not accept the append...argg

	    queryField.parentNode.appendChild(newNode);

	    // set the div reference

	    divId = document.getElementById(divId);

	    

	    // figure out where the top corner of the div should be, based on the

	    // bottom left corner of the input field

	    var x = queryField.offsetLeft;

	    var y = queryField.offsetTop + queryField.offsetHeight;

	    var parent = queryField;

	    while (parent.offsetParent && parent.style.position=="") {

	      parent = parent.offsetParent;
	      x += parent.offsetLeft;

	      y += parent.offsetTop;

	    }

	    // Style by default

	    divId.setAttribute("class", "A4SMainWindow");

	    divId.className = "A4SMainWindow";

	    divId.style.position = "absolute";

	    divId.style.left = x + "px";

	    divId.style.top = y + "px";

	    divId.style.width = "400px";

	    divId.style.visibility = "hidden";

	    divId.style.zIndex = 10000;

	

	    return divId;

	}

	/**

	* @method showResultsJS(array(), array())

	* @access private

	* @see 	This is the function that should be returned by the XMLHTTP call. It will

	*        format and display the lookup results.

	* @return void

	*/	

	function showResultsJS(resultArray1, resultArray2)

	{

		// Show the results via the DIV

		showDivResults(true);

		// remove any results that are already there

		while (ResultsDivId.childNodes.length > 0) {

			ResultsDivId.removeChild(ResultsDivId.childNodes[0]);

		}

		// if we had at least one result

		if (resultArray1.length < 1) {
			keyField.value="";
			resultArray1 = new Array("");

			resultArray2 = new Array("No results found.");

		} 

		// add an entry for each of the results in the resultArray

		for (var i = 0; i < resultArray1.length; i++)

		{

			// each result will be contained within its own div

			var result = document.createElement("div");



		  //Set the default design

		  _unhighlightResult(result);



		  // Handles event on the DIV

		  result.onmousedown = selectResult;

		  result.onmouseover = highlightResult;

		  result.onmouseout = unhighlightResult;

		  

			// SPAN for TEXT ELEMENT [0]

		  var result1 = document.createElement("span");

	    result1.setAttribute("id", "guid");		  

		  result1.style.textAlign = "left";

		  result1.style.fontWeight = "normal";
		  result1.style.width = "10";
		  result1.style.visibility = "hidden";

		  result1.innerHTML = resultArray1[i];

		 	// SPAN FOR TEXT ELEMENT [1]

		 	var result2 = document.createElement("span");

		 	result2.setAttribute("id", "description");		

		 	result2.style.textAlign = "right";

		 	result2.style.fontWeight = "bold";

		 	result2.style.paddingLeft = "20px";

		 	result2.innerHTML = resultArray2[i];

		  // Commit to the document


	  	result.appendChild(result2);
	  	result.appendChild(result1);

	  	ResultsDivId.appendChild(result);

		}

	  	// if this resultset is not in our cache, add it

  		var isCached = cache[queryField.value];

  		if (!isCached) {

     		addToCache(queryField.value, resultArray1, resultArray2);

	  	}

	}

	/**

	* @method selectResult()

	* @access private

	* @see 		The user clicks one of the lookup results.

	*           It puts the value of the result in the queryField and hides the

	*           lookup div.

	* @return void

	*/	

	function selectResult()

	{

	  _selectResult(this);

	}

	/**

	* @method _selectResult(object)

	* @access private

	* @see 		This actually fills the field with the selected result and hides the div

	* @return void

	*/	

	function _selectResult(item)

	{

	  var spans = item.getElementsByTagName("span");

	  if (spans) {

	    for (var i = 0; i < spans.length; i++) {

	      if (spans[i].getAttribute("id") == "description") {

	        queryField.value = spans[i].innerHTML;
			keyField.value = spans[i+1].innerHTML;
	        showDivResults(false);
			checksubmit();
//			keyField.form.submit();
	      }

	    }

	  }	  

	}	

	/**

	* @method highlightResult()

	* @access private

	* @see This is called when a user mouses over a lookup result

	* @return void

	*/		

	function highlightResult()

	{

	  _highlightResult(this);

	}

	/**

	* @method _highlightResult()

	* @access private

	* @see This actually highlights the selected result

	* @return void

	*/

	function _highlightResult(item)

	{

		// IE

		item.className = "A4SResultHighlighted";

		// Firefox

		item.setAttribute("class", "A4SResultHighlighted");

	}

	/**

	* @method unhighlightResult()

	* @access private

	* @see This is called when a user mouses away from a lookup result

	* @return void

	*/	

	function unhighlightResult()

	{

	  _unhighlightResult(this);

	}

	/**

	* @method _unhighlightResult()

	* @access private

	* @see This actually unhighlights the selected result

	* @return void

	*/		

	function _unhighlightResult(item)

	{

		// IE

		item.className = "A4SResult";

		// Firefox

		item.setAttribute("class", "A4SResult");	

	}	

	/**

	* @method addToCache(string, array(), array())

	* @access private

	* @see Add to cache function

	* @return void

	*/			

	function addToCache(queryString, resultArray1, resultArray2) {

		cache[queryString] = new Array(resultArray1, resultArray2);

	}

	/**

	* @method getSelectedSpanNum(object)

	* @access private

	* @see Get the number of the result that's currently selected/highlighted

	*      (the first result is 0, the second is 1, etc.)

	* @return num

	*/		

	function getSelectedRowNum(div, _int)

	{

	  var spans = div.getElementsByTagName("div");

	  kcounter = kcounter + _int;

	  // First row reached

	  if (kcounter < 0) {

	  	kcounter = 0;

	  }

	  // Last row reached

	  if (kcounter == spans.length) {

	  	kcounter--;

	  }

	  // Keydown - Unhighlight the last row

	  if (kcounter > 0 && _int == 1) {

	  	_unhighlightResult(spans[kcounter-1]);

	  }

	  // Keyup - Unhighlight the last row	  

	  if (kcounter >= 0 && _int == -1) {

	  	_unhighlightResult(spans[kcounter+1]);

	  }

	  _highlightResult(spans[kcounter]);

	  

	}

	/**

	* @method setSelectedSpan(object, num)

	* @access private

	* @see Select/highlight the result at the given position

	* @return object

	*/	

	function setSelectedSpan(div, spanNum)

	{

	  var spans = div.getElementsByTagName("div");

	  return spans[spanNum];

	}

	/**

	* @method getFormValues(object -form-, string -fct name-)

	* @access private

	* @see read all the form values and create a string with ?name=value&...

	* @return string

	*/	

	function getFormValues(_fobj) 

	{

	   var str = ""; 

	   var valueArr = null; 

	   var val = ""; 

	   var cmd = "";

	   var fobj = eval('document.forms.' + _fobj);

	   for(var i = 0;i < fobj.elements.length;i++) 

	   { 

	       switch(fobj.elements[i].type) 

	       { 

	           case "text": 

	                str += fobj.elements[i].name + 

	                 "=" + escape(fobj.elements[i].value) + "&"; 

	                 break;

	           case "hidden": 

	                str += fobj.elements[i].name + 

	                 "=" + escape(fobj.elements[i].value) + "&"; 

	                 break; 	                 

	           case "select-one": 

	                str += fobj.elements[i].name + 

	                "=" + fobj.elements[i].options[fobj.elements[i].selectedIndex].value + "&"; 

	                break; 

	       } 

	   } 

	   str = str.substr(0,(str.length - 1)); 

	   return str; 

	}

	/**

	* @method ArrayFromXML()

	* @access private

	* @see read XML and call the JS function

	* @return string

	*/	

	function ResultsFromXML(_xmlHttpResponse) 

	{

		// 2 arrays for the final results

    	var myArrayId = new Array();

		var myArrayDesc = new Array();

		// Create the XML response obj

		var XMLresponse = _xmlHttpResponse;	

		// Put the elements in the Arrays

		for (i = 0; i < XMLresponse.getElementsByTagName('item').length; i++) {

			myArrayId.push(XMLresponse.getElementsByTagName('guid')[i].firstChild.nodeValue);

			myArrayDesc.push(XMLresponse.getElementsByTagName('description')[i].firstChild.nodeValue);

		}

		// Call the final fct

		showResultsJS(myArrayId, myArrayDesc);

	}

	

	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;

	}

	

	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;

	}

	/**

	* @method getCache(void)

	* @access private

	* @see needed to able to use the timeout function

	* @return object

	*/		

	function getCache() {

		showResultsJS(cacheResult[0], cacheResult[1]);

	}

	/**

	* @method DoNothing(void)

	* @access private

	* @see needed to able to use the timeout function

	* @return string

	*/		

	function DoNothing() {

	}

}
