/*
*	Ajax suggestion box controls
*
*	Author: Stéphane Fortin
*
*	20/11/2007
*
*	See HOW-TO EXAMPLE at end of file
*	
*	TODO: 	Some work if we want to go back to a multi-column popup
*				- Trap right/left key
*				- New css
*
*			Add possibility to have more than one control per page.
*			
*	
*/

	//Key codes for keyboard key press event
	var DOWN_KEY_CODE = 40;
	var UP_KEY_CODE = 38;
	var PAGE_DOWN_KEY_CODE = 34;
	var PAGE_UP_KEY_CODE = 33;
	var ENTER_KEY_CODE = 13;
			
	//CSS classes
	var CLASS_INPUT_SUGGESTION_BOX_COLUMN = "InputSuggestionBoxListColumn";
	var CLASS_INPUT_SUGGESTION_BOX_LAST_COLUMN = "InputSuggestionBoxListLastColumn";
	var CLASS_INPUT_SUGGESTION_BOX_VALUE = "InputSuggestionBoxListValue";
	var CLASS_INPUT_SUGGESTION_BOX_SELECTED_VALUE = "InputSuggestionBoxListSelectedValue";
	
	//Default values constants
	var NB_VISIBLE_SUGGESTION_VALUES_UNDEFINED = -1;
	var MAX_ITEM_PER_COLUMN_PARAM_INFINITE_VALUE = -1;
	var NO_SELECTED_ITEM = -1;
	var SUGGESTION_BOX_HIDE_DELAY_FROM_USER_ACTION = 500;
	
	var xmlHttp;
	var inputSuggestionBoxContainerObj;
	var inputSuggestionBoxListObj;
	var fieldToFillObj;
	var cacheResults;
	var nbVisibleSuggestionValues;
	var maxItemsPerColumn;
	var controllerForData;
	var controllerParams = "";
	var targetDataElementNameInControllerOutput = "";
	var displayDelay = 0;
	var isSuggestionBoxVisible = false;
	var suggestionBoxControlsHaveFocus = false;
	var closeBoxDisplay = false;
	var selectedItemIndex = NO_SELECTED_ITEM;
	var selectedItemIndexInNbVisibleSuggestionValues = NO_SELECTED_ITEM;
	var nbCurrentItemSuggested = 0;
	var pixelToSkipOnUpDownArrow = null;
	var pixelToSkipOnPageUpPageDown = null;

	var currentTimerId = null;
	var currentPausedInputValue = null;

	var lastActionByKeyboard = false;
	
	/*
	*	Main method.  Receive objects to use and drives the synchronization of the suggestion box
	*	by dispatching the request client-side (if results are cache) and server side (for new results)
	*
	*
	*	pControllerForData 	Controller to call to get data
	*	
	*	pControllerParams 	Parameters passed to controller
	*
	*	pTargetDataElementNameInControllerOutput	Element name where to fetch the data.  All elements in document with
	*												the name specified will be counted as values. 
	*
	*	pInputSuggestionBoxContainerObj	Input Suggestion box container object
	*
	*	pInputSuggestionBoxListObj		Input Suggestion Box list object
	*
	*	pFieldToFillObj		Field to feed with selected data
	*
	*	pNbVisibleSuggestionValues	Number of values seen inside the suggestion box when poped (css dependent).  Used to know how many record to skip
	*								on pageUp/pageDown key press and know when to begin scrolling results in list on up/down key press.
	*	
	*	pMaxItemsPerColumn	Max entries by column (CSS styles dependent a.k.a. no float:left = 1 column only)
	*
	*	pDisplayDelay		Delay before starting the auto-completion process
	*
	*	pEvent				Event fired object
	* 
	*/
	function refreshSuggestionBox( pControllerForData, pControllerParams, pTargetDataElementNameInControllerOutput, pInputSuggestionBoxContainerObj, pInputSuggestionBoxListObj, pFieldToFillObj, pNbVisibleSuggestionValues, pMaxItemsPerColumn, pDisplayDelay, pPixelToSkipOnUpDownArrow, pPixelToSkipOnPageUpPageDown, pEvent ){
		if( !userMovedInSuggestionBoxWithKeyboard( pEvent ) ){
			selectedItemIndex = NO_SELECTED_ITEM;
			selectedItemIndexInNbVisibleSuggestionValues = NO_SELECTED_ITEM;
		 	initializeSuggestionBoxControl( pControllerForData, pControllerParams, pTargetDataElementNameInControllerOutput, pInputSuggestionBoxContainerObj, pInputSuggestionBoxListObj, pFieldToFillObj, pNbVisibleSuggestionValues, pMaxItemsPerColumn, pDisplayDelay, pPixelToSkipOnUpDownArrow, pPixelToSkipOnPageUpPageDown );
			
		 	//If suggestion box is visible and user has enter character to be more precise 
		 	if( isSuggestionBoxVisible && pFieldToFillObj.value != null && pFieldToFillObj.value.length > 1 ){
		 		dispatchToAppropriateDataLoadingProcess();
		 	}else{
		 		switchSuggestionBoxDisplay(false);
		 	
				currentPausedInputValue = null;
				
				//If a timer was existent, refresh the displayDelay time (since user added or removed a char in input)
				if( currentTimerId != null ){
					clearTimeout( currentTimerId );
					currentTimerId = null;
				}
		
				currentTimerId = setTimeout( 'dispatchToAppropriateDataLoadingProcess()', displayDelay);
				currentPausedInputValue = fieldToFillObj.value;
			}
		}else{
			controlSelectionByUserInput( pEvent );
		}
	} 			
	
/*********************************************
*********************************************	
 	Data fetching methods	
*********************************************	
********************************************/	
	
	/*
	*	Data loading process dispatcher.  To server if new data must be loaded or in memory
	*	if data already loaded.
	*
	*/	
	function dispatchToAppropriateDataLoadingProcess(){
	 	var wInputValue = fieldToFillObj.value;
	 	var wInputValueFirstChar = fieldToFillObj.value.charAt(0);
		
		//Clean old results
		clearSuggestionBox();	
			 	
	 	//If user has started typing in input box
	 	if( wInputValue.length > 0 ){
			var wResultArray = cacheResults[wInputValueFirstChar];
			
			//If existent values for corresponding first char
			if( wResultArray != null ){
		 		buildSuggestionBoxContent( wResultArray );
			}else{
		 		prepareAjaxCallToServer();
			}
		}
	}
	
	/*
	*	Prepare call to server 
	*
	*/
	function prepareAjaxCallToServer( ){
		if( xmlHttp == null ){
			alert("Your browser does not support AJAX!");
			return;
		}
					
		//Prepare input param and call server to refresh list
		try{
			if( fieldToFillObj.value != '' ){
				var wInputParam = controllerParams;
		    	xmlHttp.open("GET", controllerForData + escape(wInputParam), true);
				xmlHttp.onreadystatechange=loadDataFromServerAndDispatchToBuildSuggestionBoxContent;
		    	xmlHttp.send(null);
		    }
		}catch( e ){
			switchSuggestionBoxDisplay(false);
		}
	}
	
	/*
	*	Synchronize suggestion box entries and call prepare suggestion method (reason why it is call in here
	*	is because method called on xmlHttp.onreadystatechange event does not wait for operation to complete
	*	before calling next line code in prepareAjaxCallToServer() method ) . 
	*
	*/			
	function loadDataFromServerAndDispatchToBuildSuggestionBoxContent(){

	  if(xmlHttp.readyState==4)
	    {
	    	if( xmlHttp.responseXML != null && xmlHttp.responseXML.documentElement != null){
				var xmlDoc = xmlHttp.responseXML.documentElement;
				var wResultArray;
				var wColumnObj;
				
				if( targetDataElementNameInControllerOutput != null && targetDataElementNameInControllerOutput.length > 0 ){
					var wDataToDisplayNodes = xmlDoc.getElementsByTagName( targetDataElementNameInControllerOutput );
	
					if( wDataToDisplayNodes.length > 0 ){
						cacheResults[fieldToFillObj.value.charAt(0)]= new Array();
						wResultArray = cacheResults[fieldToFillObj.value.charAt(0)];
	
						//Iterate through results
						for( var i = 0; i < wDataToDisplayNodes.length; i++ ){
							//Add result to corresponding array and in suggestion box
							wResultArray[i] = wDataToDisplayNodes[i].firstChild.nodeValue;
						}
	
						//Must be call here because method called on xmlHttp.onreadystatechange event does not wait 
						//for operation to complete before calling next line code in prepareAjaxCallToServer() method.
					    buildSuggestionBoxContent( wResultArray );
					}
				}else{
					alert('Impossible to fetch data.  Cause: target data element name was not specified.')				
				}
			}
	    }
	}	
	
/**************************************************************
**************************************************************	
 	Suggestion box generation and visibility control methods	
**************************************************************
**************************************************************/
		
	/*
	*	Build results table from client-side cache
	*
	*/
	function buildSuggestionBoxContent( pResultsArray ){
		var wStartsWithValueLowerCase = fieldToFillObj.value.toLowerCase();
		var wColumnObj;
		var wLastValue = "";
		nbCurrentItemSuggested = 0;
		
		//Find corresponding results
		for( var i = 0; i < pResultsArray.length; i ++ ){
			var wValue = pResultsArray[i];

			//Create column if first result or if number of item in one column is reached and there are still other results to process
			if( nbCurrentItemSuggested == 0 || ( maxItemsPerColumn != MAX_ITEM_PER_COLUMN_PARAM_INFINITE_VALUE && (nbCurrentItemSuggested % maxItemsPerColumn == 0)) ){
				wColumnObj = document.createElement("div");
				wColumnObj.className = CLASS_INPUT_SUGGESTION_BOX_COLUMN;
				inputSuggestionBoxListObj.appendChild( wColumnObj );
			}
			
			//If current value matches the startsWith param
			if( pResultsArray[i].toLowerCase().indexOf(wStartsWithValueLowerCase)== 0 ){
				wLastValue = pResultsArray[i];
				addSuggestionBoxEntry( pResultsArray[i], fieldToFillObj.value, wColumnObj, nbCurrentItemSuggested );
				nbCurrentItemSuggested++;
			}	
		}

		//Mark last column with another css class
		wColumnObj.className = CLASS_INPUT_SUGGESTION_BOX_LAST_COLUMN;

		//if only one suggestion and characters are already all entered input box
		if( suggestionBoxControlsHaveFocus && (( nbCurrentItemSuggested > 1 && fieldToFillObj.value == currentPausedInputValue ) ||
			( nbCurrentItemSuggested == 1 && fieldToFillObj.value.toLowerCase() != wLastValue.toLowerCase() ))  ){
			switchSuggestionBoxDisplay(true);
		}else{

			//If no entry found or if one entry and matches completely value entered in input box 
			if( nbCurrentItemSuggested == 0 || 
			( nbCurrentItemSuggested == 1 && fieldToFillObj.value.toLowerCase() == wLastValue.toLowerCase() ) ){  
				switchSuggestionBoxDisplay(false);
			}
		}
		
	}
	/*
	*	Add an entry in the suggestion list
	*
	*/
	function addSuggestionBoxEntry( pValue, pStartsWithValue, pColumnElement, pIndex ){
		var wParagraphElement = document.createElement("p");
		wParagraphElement.innerHTML = prepareDisplayValue( pValue, pStartsWithValue );
		
		wParagraphElement.className = CLASS_INPUT_SUGGESTION_BOX_VALUE;
		addEvent( wParagraphElement, 'click', function(){switchSuggestionBoxDisplay(false);} );
		addEvent( wParagraphElement, 'mouseover', function(){
																if( !lastActionByKeyboard ){
																	fieldToFillObj.value = pValue ;
															 		wParagraphElement.className = CLASS_INPUT_SUGGESTION_BOX_SELECTED_VALUE;
															 		this.id='SelectedSuggestion';
															 		switchListSelectedElement( selectedItemIndex, pIndex );
															 		selectedItemIndex = pIndex;
															 	}
															 } );
		pColumnElement.appendChild(wParagraphElement);
	}
	
	/*
	*	Prepare value that will be displayed to user.
	*
	*	Highlight corresponding letters to user input. 
	*
	*/
	function prepareDisplayValue( pValue, pStartsWithValue ){
		var wOutputValue = new StringBuffer();
		wOutputValue.append("<b>");
		for( var i = 0; i < pStartsWithValue.length; i++ ){
			var wChar;
			if( pValue.toLowerCase().charAt(i) == pStartsWithValue.toLowerCase().charAt(i) ){
				wChar = pValue.charAt(i);
			}else{
				if( pValue.toUpperCase().charAt(i) == pStartsWithValue.toUpperCase().charAt(i) ){
					wChar = pValue.charAt(i);
				}
			}
			 
			wOutputValue.append(wChar);
		}
	
		wOutputValue.append( "</b>" + pValue.substring(pStartsWithValue.length) );
		return wOutputValue.toString();
	}
	
	/*
	*	Remove all suggestions
	*
	*/
	function clearSuggestionBox(){
		if( inputSuggestionBoxListObj.firstChild ){
			while (inputSuggestionBoxListObj.firstChild) {
				inputSuggestionBoxListObj.removeChild(inputSuggestionBoxListObj.firstChild);
			}
		}	
		selectedItemIndex = NO_SELECTED_ITEM;
		selectedItemIndexInNbVisibleSuggestionValues = NO_SELECTED_ITEM;
	}
		
	/*
	*	Swith visibility with delay, user driven
	*
	*/
	function switchSuggestionBoxDisplay( pSetVisible ){
		if( pSetVisible ){
			closeBoxDisplay = false;
			setTimeout('displaySuggestionBox()', SUGGESTION_BOX_HIDE_DELAY_FROM_USER_ACTION );
			isSuggestionBoxVisible = true;
		}else{
			closeBoxDisplay = true;
			setTimeout('hideSuggestionBox()', SUGGESTION_BOX_HIDE_DELAY_FROM_USER_ACTION );
			isSuggestionBoxVisible = false;
		}
	}

	function displaySuggestionBox(){
		if( inputSuggestionBoxContainerObj != null && inputSuggestionBoxContainerObj.style.display != 'block' ){
			inputSuggestionBoxContainerObj.style.display = 'block';
			hideSelectComponentFromUnderlyingPageForSuggestionBox();
			inputSuggestionBoxContainerObj.scrollTop = 0;
			isSuggestionBoxVisible = true;
		}
	}
			
	function hideSuggestionBox(){
		if( closeBoxDisplay && inputSuggestionBoxContainerObj != null && inputSuggestionBoxContainerObj.style.display != 'none' ){
			inputSuggestionBoxContainerObj.style.display = 'none';
			showSelectComponentFromUnderlyingPageForSuggestionBox();			
			isSuggestionBoxVisible = false;
			selectedItemIndex = NO_SELECTED_ITEM;
			selectedItemIndexInNbVisibleSuggestionValues = NO_SELECTED_ITEM;
		}
	}
	
	function setSuggestionBoxControlsHaveFocusFlag( pFlag ){
		suggestionBoxControlsHaveFocus = pFlag;
	}


/*********************************************
*********************************************	
 	Suggestion selection by keyboard methods	
*********************************************	
*********************************************/	
	
	/*
	*	Check if event has been triggered by down/up arrow keys or pageup/pageDown keys
	*
	*/
	function userMovedInSuggestionBoxWithKeyboard( pEvent ){
		if( pEvent.keyCode == DOWN_KEY_CODE || 
			pEvent.keyCode == UP_KEY_CODE || 
			pEvent.keyCode == PAGE_DOWN_KEY_CODE || 
			pEvent.keyCode == PAGE_UP_KEY_CODE){
			return true;
		}
		
		return false;
	}
	
	/*
	*	Change selected suggestion and scroll down/up list
	*
	*/
	function controlSelectionByUserInput( pEvent ){

		if( isSuggestionBoxVisible ){
			lastActionByKeyboard = true;
			var wCurrentTop = inputSuggestionBoxContainerObj.scrollTop;
			var wElements = inputSuggestionBoxListObj.getElementsByTagName('p');

			if( selectedItemIndex == -1 ){
				inputSuggestionBoxContainerObj.scrollTop = 0;
			}
			
			//Select new entry in list and Scroll list
			switch( pEvent.keyCode ){

				//Down arrow
				case DOWN_KEY_CODE:
					if( !(selectedItemIndex + 1 > wElements.length - 1) ){//if enough entry to move one down
						switchListSelectedElement( selectedItemIndex, selectedItemIndex + 1 );
						
						//if selected line is not the last line visible in the box
						if( selectedItemIndexInNbVisibleSuggestionValues < nbVisibleSuggestionValues ){
							selectedItemIndexInNbVisibleSuggestionValues++;
						}
	
						//if newly selected entry must scroll page
						if( selectedItemIndex >= nbVisibleSuggestionValues ){//if newly selected entry must scroll page
							inputSuggestionBoxContainerObj.scrollTop = wCurrentTop + pixelToSkipOnUpDownArrow;
						}
					}									
					break;
					
				//Up arrow
				case UP_KEY_CODE:
					if( !(selectedItemIndex - 1 < 0) ){//if enough entry to move one up
						switchListSelectedElement( selectedItemIndex, selectedItemIndex - 1 );
	
	
						//if selected line is not the first line visible in the box
						if( selectedItemIndexInNbVisibleSuggestionValues > 0 ){
							selectedItemIndexInNbVisibleSuggestionValues--;
						}
						
						//More entry available above
						if( selectedItemIndex <= (wElements.length - 1) - nbVisibleSuggestionValues ){
							inputSuggestionBoxContainerObj.scrollTop = wCurrentTop - pixelToSkipOnUpDownArrow;
						}
					}								
					break;
					
				//Page Down
				case PAGE_DOWN_KEY_CODE:
					if( selectedItemIndex > NO_SELECTED_ITEM ){//if list has a selected entry
						if( !(selectedItemIndex + nbVisibleSuggestionValues > wElements.length - 1) ){//if enough entry to do a complete page down
							switchListSelectedElement( selectedItemIndex, selectedItemIndex + nbVisibleSuggestionValues );				
						}else{
							switchListSelectedElement( selectedItemIndex, wElements.length - 1 );				
						}				
						inputSuggestionBoxContainerObj.scrollTop = wCurrentTop + pixelToSkipOnPageUpPageDown ;
					}
					break;			
				
				//Page up	
				case PAGE_UP_KEY_CODE:
					if( selectedItemIndex > NO_SELECTED_ITEM ){//if list has a selected entry
						if( !(selectedItemIndex - nbVisibleSuggestionValues < 0) ){//if enough entry to do a complete page up
							switchListSelectedElement( selectedItemIndex, selectedItemIndex - nbVisibleSuggestionValues );
						}else{
							switchListSelectedElement( selectedItemIndex, 0 );
						}
						inputSuggestionBoxContainerObj.scrollTop = wCurrentTop - pixelToSkipOnPageUpPageDown;
					}						
					break;
			}	
		}
	}
	
	/*
	*	Switch selected element in list
	*
	*/
	function switchListSelectedElement( pOldIndex, pNewIndex ){
		var wElements = inputSuggestionBoxListObj.getElementsByTagName('p');	
		if( pOldIndex > NO_SELECTED_ITEM ){
			wElements[pOldIndex].id = null;
			wElements[pOldIndex].className = CLASS_INPUT_SUGGESTION_BOX_VALUE;
		}
		setSelectedElement( wElements, pNewIndex );
	}	
	
	/*
	*	Set Selected Element in list
	*
	*/	
	function setSelectedElement( pElementList, pIndexToSelect ){
		pElementList[pIndexToSelect].id = 'SelectedSuggestion';
		pElementList[pIndexToSelect].className = CLASS_INPUT_SUGGESTION_BOX_SELECTED_VALUE;
		selectedItemIndex = pIndexToSelect;
		
		//Changed selected value (and remove bold tags)
		var wValue = pElementList[pIndexToSelect].innerHTML;
		wValue = wValue.replace(/<[\/]{0,1}(B|b)[^><]*>/g,'');
		fieldToFillObj.value = wValue;
	}	
	
	/*
	*	Keep track of last action on list has been fired by which input
	*/
	function setLastActionByKeyboard( pBoolean ){
		lastActionByKeyboard = pBoolean;
	}


/*********************************************
*********************************************	
 	Initialize method
*********************************************	
*********************************************/	

	/*
	*	Initialize method.  ** MUST BE CALLED **
	*/
	function initializeSuggestionBoxControl( pControllerForData, pControllerParams, pTargetDataElementNameInControllerOutput, pInputSuggestionBoxContainerObj, pInputSuggestionBoxListObj, pFieldToFillObj, pNbVisibleSuggestionValues, pMaxItemsPerColumn, pDisplayDelay, pPixelToSkipOnUpDownArrow, pPixelToSkipOnPageUpPageDown ){
		if( inputSuggestionBoxContainerObj == null ){
			inputSuggestionBoxContainerObj = pInputSuggestionBoxContainerObj;
		}

		if( inputSuggestionBoxListObj == null ){
			inputSuggestionBoxListObj = pInputSuggestionBoxListObj;
		}
		
		if( fieldToFillObj == null ){
			fieldToFillObj = pFieldToFillObj;
		}

		if( xmlHttp == null ){		
		 	xmlHttp = getXmlHttpObject(); 
		}
		
		if( cacheResults == null ){
			cacheResults = new Array();
		}
		
		if( nbVisibleSuggestionValues == null ){
			if( pNbVisibleSuggestionValues != null ){
				nbVisibleSuggestionValues = pNbVisibleSuggestionValues;
			}else{
				nbVisibleSuggestionValues = NB_VISIBLE_SUGGESTION_VALUES_UNDEFINED;
			}
		}
		
		if( maxItemsPerColumn == null ){
			if( pMaxItemsPerColumn != null ){
				maxItemsPerColumn = pMaxItemsPerColumn;
			}else{
				maxItemsPerColumn = MAX_ITEM_PER_COLUMN_PARAM_INFINITE_VALUE;
			}
		}		
		
		if( controllerForData == null ){
			if( pControllerForData != null ){
				controllerForData = pControllerForData;
			} 
		}
		
		if( pControllerParams != null ){
			controllerParams = pControllerParams;
		}

		if( pTargetDataElementNameInControllerOutput != null ){
			targetDataElementNameInControllerOutput = pTargetDataElementNameInControllerOutput;
		}
		
		
		if( pDisplayDelay != null){
			displayDelay = pDisplayDelay;
		}

		if( pixelToSkipOnUpDownArrow == null ){
			pixelToSkipOnUpDownArrow = pPixelToSkipOnUpDownArrow;
		}
				
		if( pixelToSkipOnPageUpPageDown == null){
			pixelToSkipOnPageUpPageDown = pPixelToSkipOnPageUpPageDown;
		}
	}
		
/*********************************************
*********************************************	
 	Different utilities	
*********************************************	
*********************************************/	
	
	/*
	*	Get ajax main object
	*
	*/			
	function getXmlHttpObject(){
		var xmlHttp;
		
		try// Firefox, Opera 8.0+, Safari
		{        
		    xmlHttp=new XMLHttpRequest();
		}catch (e){    // Internet Explorer    
			try
			{      
			   	xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");    
			}catch (e){      
			    try
			    {        
			      	xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
			    }catch (e){       
			      	alert("Your browser does not support AJAX!");        
			      	return false;        
			    }      
			}  
		}
		return xmlHttp;  
	}
	
	
	/*
	*	Simulate String buffer object
	*/

	function StringBuffer() { 
		this.buffer = []; 
	} 
		
	StringBuffer.prototype.append = function(string) { 
		this.buffer.push(string); 
		return this; 
	} 

	StringBuffer.prototype.toString = function(){ 
		return this.buffer.join(""); 
	}

	/*
	*	Add event method to work with multiple browser
	*/
	function addEvent( obj, type, fn ) { 
	  if ( obj.attachEvent ) { 
	    obj['e'+type+fn] = fn; 
	    obj[type+fn] = function(){obj['e'+type+fn]( window.event );}; 
	    obj.attachEvent( 'on'+type, obj[type+fn] ); 
	  } else {
	    obj.addEventListener( type, fn, false );
	  } 
	} 

	/*
	*	return false if key code refers to ENTER key on keyboard
	*/
	function cancelSubmitOnEnterKeyPressAndCloseSuggestionList( pEvent ){
		if( pEvent.keyCode == ENTER_KEY_CODE ){
			switchSuggestionBoxDisplay(false);
		}
			
		return !(pEvent.keyCode == ENTER_KEY_CODE);
	} 

	 /*
	 * Add/remove event methods to work with multiple browsers
	 */
	 function addEvent( obj, type, fn ) {
	   if ( obj.attachEvent ) {
	     obj['e'+type+fn] = fn;
	     obj[type+fn] = function(){obj['e'+type+fn]( window.event );};
	     obj.attachEvent( 'on'+type, obj[type+fn] );
	   } else {
	     obj.addEventListener( type, fn, false );
	   }
	 }

function hideSelectComponentFromUnderlyingPageForSuggestionBox(){
	if( isIE6orPrevious() ){
		var wElements = document.getElementsByTagName("select");
		if ( wElements ){
		    gAffectedSelect = new Array(wElements.length);
    		for( var i = 0; i < wElements.length; i++ ){
                if (wElements[i] && wElements[i].style && wElements[i].style.display != 'none'){
        			wElements[i].style.display = 'none';
        			gAffectedSelect[i] = wElements[i];
    			}
    		}
    	}
    }
}

function showSelectComponentFromUnderlyingPageForSuggestionBox(){
	if( isIE6orPrevious() ){
		if ( gAffectedSelect ){
    		for( var i = 0; i < gAffectedSelect.length; i++ ){
    			if (gAffectedSelect[i] && gAffectedSelect[i].style){
        			gAffectedSelect[i].style.display = '';
        		}
    		}
		}
	}
}
	
function isIE6orPrevious(){
    var wVersion = vIE();
    return (wVersion <= 6 && wVersion != -1);
}
	
function vIE(){
    //source: http://www.thefutureoftheweb.com/blog/detect-ie6-in-javascript
    return (navigator.appName=='Microsoft Internet Explorer')?parseFloat((new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})")).exec(navigator.userAgent)[1]):-1;
}

/*
*	HOW-TO EXAMPLE:
*
*		1. Link java script methods and css files (styles could need to be redefine)
*			<link href="/cgi/en/css/InputSuggestionBoxControl.css" rel="stylesheet" type="text/css"/>
*			<xsl:comment><![CDATA[[if IE 7]><link href="/cgi/en/css/InputSuggestionBoxControl_ie7.css" type="text/css" rel="stylesheet" media="all"/><![endif]]]></xsl:comment>
*			<xsl:comment><![CDATA[[if lte IE 6]><link href="/cgi/en/css/InputSuggestionBoxControl_ie6.css" type="text/css" rel="stylesheet" media="all"/><![endif]]]></xsl:comment>
*			<script type="text/javascript" language="JavaScript" src="/cgi/en/Js/InputSuggestionBoxControl.js"/>
*
*		2. First create a text box and add/edit events to pass working parameters (see param descriptions in refreshSuggestionBox() method header)
*	
*				<input class="txt" type="text" id="LineItem.OriginalManufacturerCode" 
*					name="LineItem.OriginalManufacturerCode" size="23" autocomplete="off"
*					value="{LineItem/OriginalManufacturerCode}" tabindex="3"
*					onkeyup="refreshSuggestionBox( '/cgi/en/manufacturer.list',
*						'/SearchCriteria.StartsWith=' + this.value.substring(0, 1),	
*						'LongName',					
*						findObject('InputSuggestionBox'),
*						findObject('InputSuggestionBoxEntryList'),
*						findObject('LineItem.OriginalManufacturerCode'), 
*						9, null,
*						1000,						
*						22, 198,
*						event);" 
*					onblur="switchSuggestionBoxDisplay(false)"
*					onkeypress="return cancelSubmitOnEnterKeyPressAndCloseSuggestionList( event )"/>
*
*		3. Add these div where you want the popup to show
*				<!-- Popup list containing results  -->                    
*				<div id="InputSuggestionBox" class="InputSuggestionBoxInventory" 
*					onblur="javascript:switchSuggestionBoxDisplay(false);" 
*					onfocus="javascript:switchSuggestionBoxDisplay(true);"
*					onmousemove="setLastActionByKeyboard(false)">
*					<div id="InputSuggestionBoxEntryList"/>
*				</div>
*
*		4. To complete, create css stylesheets
*/
	