/*******************************************************************************
*Title			:	AJAX Auto-complete Suggest ComboBox
*Author			:	Wong Zi Wei
*contributors	:	-
*Date			:	Version 1.0.0	18/08/05
*				:	Version 1.0.1	22/08/05
********************************************************************************/
//---An autosuggest textbox control---
function AutoSuggestControl(vs_oTextBox, vs_oProvider, vs_iLimit){
	//currently selected suggests.
	this.cur = -1;
	//dropdown list layer
	this.layer = null;
	//suggest provider for the autosuggest feature.(Suggest Provider)
	this.provider = vs_oProvider;
	this.iLimit = vs_iLimit;
	//textbox to capture (HTMLInputElement) 
	this.textbox = vs_oTextBox;
	//initialize the control
	this.init();
	
	this.iCurr = 0;
	this.iTop = 0;
	this.iBot = this.limit;
	this.bPress = 1;	//|0-Previous|1-Next|2-Mouseover|
}

//---Autosuggests one or more suggests for what the user had typed.---
//-If no suggests are passed in, then no autosuggest occurs.
//-param vs_asSuggest - an array of suggest strings.
//-param vs_bTypeAhead If the control should provide a type ahead suggest.
//-----------------------------------------------------------------------
AutoSuggestControl.prototype.autosuggest = function(vs_asSuggest, vs_bTypeAhead){
	//make sure there's at least one suggest
	if(vs_asSuggest.length > 0){
		this.showSuggest(vs_asSuggest);		
		if(vs_bTypeAhead){ this.typeAhead(vs_asSuggest[0]); }	//$ Specially locate right after showSuggest, default is before
	}else{
		this.hideSuggest(vs_asSuggest);		
	}
};

//---Creates the dropdown layer to display multiple suggest.
AutoSuggestControl.prototype.createDropDown = function(){
	var oThis = this;
	//create the layer and assign styles
	this.layer = document.createElement('div');
	this.layer.className = 'suggest';
	this.layer.style.overflow = 'auto';	//make it scrollable
	this.layer.style.visibility = 'hidden';
	this.layer.style.width = this.textbox.offsetWidth;
	//when the user clicks on the suggest, get the text (innerHTML) and place it into a textbox
	this.layer.onmousedown = 
	this.layer.onmouseup = 
	this.layer.onmouseover = function(oEvent){
		oEvent = oEvent || window.event;
		oTarget = oEvent.target || oEvent.srcElement;
		
		if(oEvent.type == 'mousedown'){
			///control div from hiding when click scrollbar
			if(oTarget.firstChild.nodeValue != null){
				oThis.textbox.value = oTarget.firstChild.nodeValue;
				oThis.hideSuggest();
			}			
		}else if(oEvent.type == 'mouseover'){
			oThis.bPress = 2;
			oThis.highlightSuggest(oTarget);
		}else{
			oThis.textbox.focus();
		}
	};				

	document.body.appendChild(this.layer);
};

//---To control div from hiding when click scrollbar---
AutoSuggestControl.prototype.ShowRemoveSuggest = function(){
	var iPosX = 0;
	var iPosY = 0;
	var iMinWidth = this.getLeft();	
	var iMinHeight = this.getTop();	
	var iMaxWidth = parseInt(this.layer.style.width) + iMinWidth;
	var iMaxHeight = parseInt(this.layer.style.height) + iMinHeight + this.textbox.offsetHeight;
	
	if (document.layers){
		iPosX = e.pageX;
		iPosY = e.pageY;
	}else if (document.all){
		iPosX = window.event.x + document.body.scrollLeft;
		iPosY = window.event.y + document.body.scrollTop;
	}
	
	if(iMaxWidth < iPosX || iMaxHeight < iPosY || iMinWidth > iPosX || iMinHeight > iPosY)
		this.hideSuggest;
	else
		this.textbox.focus();
};
	
//---Gets the left coordinate of the textbox---
//-return the left coordinate of the textbox in pixels
//---------------------------------------------
AutoSuggestControl.prototype.getLeft = function(){
	var oNode = this.textbox;
	var iLeft = 0;
	
	while(oNode.tagName != "BODY"){
		iLeft += oNode.offsetLeft;
		oNode = oNode.offsetParent;
	}
	return iLeft;
};

//---Gets the top coordinate of the textbox---
//-@return The top coordinate of the textbox in pixels---
AutoSuggestControl.prototype.getTop = function (){

    var oNode = this.textbox;
    var iTop = 0;
    
    while(oNode.tagName != "BODY") {
        iTop += oNode.offsetTop;
        oNode = oNode.offsetParent;
    }
    
    return iTop;
};

//---Handles three keydown events---
//-param-vs_oEvent object for the keydown event
//----------------------------------
AutoSuggestControl.prototype.handleKeyDown = function(vs_oEvent){
	switch(vs_oEvent.keyCode){
		case 38: //up arrow
			this.previousSuggest();
			break;
		case 40: //down arrow
			this.nextSuggest();
			break;
		case 13: //enter
			this.autoSetSuggest();	//auto set matched word(1st row)
			this.hideSuggest();
			break;
	}	
};

//---Handles keyup events---
//-param oEvent-event object for the keyup event.
AutoSuggestControl.prototype.handleKeyUp = function(vs_oEvent){
	var iKeyCode = vs_oEvent.keyCode;
	//for backspace(8) and delete (46), shows suggests without typeahead
	if(iKeyCode == 8 ||iKeyCode == 46){
		this.provider.requestSuggest(this, false);
	//make sure not to interfere with non-character keys
	}else if(iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode < 46) || (iKeyCode >= 112 && iKeyCode <= 123)){
		//ignore
	}else{
		//request suggest from the suggest provider with typeahead
		this.provider.requestSuggest(this, true);
	}
};

//Suggest dropdown
AutoSuggestControl.prototype.hideSuggest = function(){
	this.layer.style.visibility = "hidden";
	//#03/10/06-Solve bugs on visible = hidden, and block focus of other textfield below
	this.layer.style.height = 0; //set height to 0 to return to default, and not just visible false
};
	
//---Highlights the given node in the suggest dropdown---
//-param SuggestNode - the node representing a suggest in the dropdown---
AutoSuggestControl.prototype.highlightSuggest = function (vs_oSuggestNode){
	for(var i=0; i<this.layer.childNodes.length; i++){
		var oNode = this.layer.childNodes[i];
		if(oNode == vs_oSuggestNode){
			oNode.className = "current";
			//alert("this.iCurr=["+this.iCurr+"]this.bPress=["+this.bPress+"]");
			//To control scroll when up & down key is press.
			if(this.bPress == 0){
				if(this.iCurr == this.iTop + 1){
					this.layer.scrollTop -= 16;	//let it scroll					
					//update iTop and iBot
					this.iTop -= 1;
					this.iBot -= 1;
					//this.layer.doScroll("up");
					//this.iTop -= this.iURow;
					//this.iBot -= this.iURow;
					//this.iURow = this.switchRow(this.iURow);
					//this.iDRow = this.switchRow(this.iDRow);
					if (this.iTop < 0){	//when this.iCurr is reach top.
						this.iTop = 0;
						this.iCurr = 2;
						this.iBot = this.iLimit;
					}
				}
				this.iCurr--;
			}
			
			if(this.bPress == 1){
				if(this.iCurr == this.iBot){ //this.layer.doScroll("scrollbarDown");
					this.layer.scrollTop += 16;
					//update iTop and iBot.
					this.iTop += 1;
					this.iBot += 1;
					//this.layer.doScroll("down");
					//this.iTop += this.iDRow;
					//this.iBot += this.iDRow;
					//this.iURow = this.switchRow(this.iURow);
					//this.iDRow = this.switchRow(this.iDRow);
					if (this.iBot > this.layer.childNodes.length){	//when this.iCurr is reach bottom.
						this.iBot = this.layer.childNodes.length;
						this.iCurr = this.iBot - 1;
						this.iTop = this.iBot - this.iLimit;
					}
				}				
			this.iCurr++;
			}
			//window.status = this.iTop + " : " + this.iCurr + " : " + this.iBot;
		} else if (oNode.className == "current") {
			oNode.className = "";
		}
	}
	//window.status = this.cur
};							

//---To switch the row---
/*AutoSuggestControl.prototype.switch = function (iRow) {
	var iNewRow = 0;
	if(iRow == this.iscroll - 1)
		iNewRow = this.iscroll;
	else if(iRow == this.iscroll)
		iNewRow = this.iscroll - 1;
	
	return iNewRow;
};*/

//---Initializes the textbox with event handlers for auto suggest functionality---
AutoSuggestControl.prototype.init = function(){
	//save a reference to this object
	var oThis = this;
	//assign the onkeyup event handler
	this.textbox.onkeyup = function (oEvent){	
		//check for the proper location of the event object
		if(!oEvent){
			oEvent = window.event;
		}		
		//call the handleKeyUp() method with the event object
		oThis.handleKeyUp(oEvent);
	};
	
	//assign onkeydown event handler
	this.textbox.onkeydown = function (oEvent){
		//check for the proper location of the event object		
		if(!oEvent){
			oEvent = window.event;
		}
		//call the handleKeyDown() method with the event object
		oThis.handleKeyDown(oEvent);
	};
		
	//assign onblur event handler (hides suggest)
	this.textbox.onblur = function(){
		oThis.ShowRemoveSuggest();	//control div from hiding when click scrollbar
		//oThis.hideSuggest();
	};
	//create the suggest dropdown
	this.createDropDown();
};

//---Highlights the next suggestion in the dropdown and places the suggest into the textbox.
AutoSuggestControl.prototype.nextSuggest = function () {
    var cSuggestNodes = this.layer.childNodes;
    if (cSuggestNodes.length > 0 && this.cur < cSuggestNodes.length - 1) {
        var oNode = cSuggestNodes[++this.cur];
		this.bPress = 1;	
        this.highlightSuggest(oNode);
		this.textbox.value = oNode.firstChild.nodeValue;
    }
};
	
//---Highlights the next suggest in the dropdown and places the suggest into the textbox---
AutoSuggestControl.prototype.previousSuggest = function(){
	var cSuggestNodes = this.layer.childNodes;
	if(cSuggestNodes.length > 0 && this.cur > 0){
		var oNode = cSuggestNodes[--this.cur];
		this.bPress = 0;
		this.highlightSuggest(oNode);
		this.textbox.value = oNode.firstChild.nodeValue;
	}
};

AutoSuggestControl.prototype.selectRange = function (vs_iStart, vs_iLength){
	//use text ranges for Internet Explorer
	if(this.textbox.createTextRange){
		var oRange = this.textbox.createTextRange();
		oRange.moveStart("character",vs_iStart);
		oRange.moveEnd("character",vs_iLength - this.textbox.value.length);
		oRange.select();
	//use setSelectionRange() for Mozilla, FireFox, netscape
	}else if(this.textbox.setSelectionRange){
		this.textbox.setSelectionRange(vs_iStart, vs_iLength);
	}
	//set focus back to the textbox
	this.textbox.focus();
};

//---builds suggest layer contents, move it into position, and displays the layer---
//-param vs_asSuggest - An array of suggest for the control---
AutoSuggestControl.prototype.showSuggest = function(vs_asSuggest){
	var oDiv = null;
	this.layer.innerHTML = ""; //clear contents of the layer.

	this.layer.style.height = "0";	//to set height of the div.
	this.layer.style.height = "" + ((16 * vs_asSuggest.length) + 2); //fix bug for div height = 0px on mozilla & netscape.
	if(vs_asSuggest.length > this.iLimit){
		this.layer.style.height = "" + ((16 * this.iLimit) + 2);
	}
	
	for(var i=0; i<vs_asSuggest.length; i++){
		oDiv = document.createElement("div");
		oDiv.appendChild(document.createTextNode(vs_asSuggest[i]));
		this.layer.appendChild(oDiv);
	}
	
	this.layer.style.left = this.getLeft() + "px";
	this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px";
	this.layer.style.visibility = "visible";
	this.cur = -1;	//Always reset this.cur = -1 when new data is filter out.
	this.iCurr = 0;
	
	//---Reset Scrolling---
	this.layer.scrollTop = 0;
	this.iCurr = 0;
	this.iTop = 0;
	this.iBot = this.iLimit;
};
	
//---Inserts a suggest into the textbox, highlighting the suggested part of the text---
//-param vs_asSuggest - the suggest for the textbox---
AutoSuggestControl.prototype.typeAhead = function(vs_asSuggest){
	var cSuggestNodes = this.layer.childNodes;	
	//check for support of typeahead functionality
	if(this.textbox.createTextRange || this.textbox.setSelectionRange){
		var iLen = this.textbox.value.length;
		//$ comment this line to avoid auto select value for textbox
		//this.textbox.value = vs_asSuggest;
		//---Special code for hightlight first selection---		
		if (cSuggestNodes.length > 0 && this.cur < cSuggestNodes.length - 1) {
			var oNode = cSuggestNodes[++this.cur];
			this.bPress = 1;	//down key
			this.highlightSuggest(oNode);
			//this.textbox.value = oNode.firstChild.nodeValue;	
		}
		this.selectRange(iLen, vs_asSuggest.length);
	}
};

AutoSuggestControl.prototype.autoSetSuggest = function(){
		//var cSuggestNodes = this.layer.childNodes;	
		//var oNode = cSuggestNodes[0];	//auto set number 1 stock - nearest
		//this.textbox.value = oNode.firstChild.nodeValue;			
		//*** to avoid error in firstChild when user haven't press down to select any stock(this.cur=-1),include this line
		//alert('cSuggestNodes=['+cSuggestNodes+'],this.cur=['+this.cur+']');
		if(this.cur > -1){
			var cSuggestNodes = this.layer.childNodes;
			var oNode = cSuggestNodes[this.cur];
			this.textbox.value = oNode.firstChild.nodeValue;
		}
}

//--------------------------------------------------------------
//---Suggest(sgtControl)---
function isDigit (c){
   var strAllowed = "1234567890";
   return (strAllowed.indexOf (c) != -1);
}

function ControlSuggest(vs_sCode){
	this.states = vs_sCode;
	var asSuggest = [];
}

//---Request suggest for the given autosuggest control---
//---param vs_oAutoSuggestControl the autosuggest control to provide suggests for---
ControlSuggest.prototype.requestSuggest = function (vs_oAutoSuggestControl, vs_bTypeAhead){	
	var bDigit = new Boolean(true);
	var asSuggest = [];
	var sTextboxValue = vs_oAutoSuggestControl.textbox.value;
		
	//---Detect whether is a word or digit (isBlank), why use desc?detect err faster,as input normally different at the end---	
	for(var i=sTextboxValue.length-1; i>=0; i--){
		if(!isDigit(sTextboxValue.charAt(i))){
			bDigit = false;
			break;
		}
	}	
    //---Compare either with word or digit base on the bDigit---    
    if (sTextboxValue.length > 0){
		if(!bDigit){
			//convert value in textbox to lowercase
			var sTextboxValueLC = sTextboxValue.toLowerCase();
			var iCount = 0;			
			//search for matching states
			for(var i=0; i<this.states.length; i++){
				//convert state name to lowercase
				var sStateLC = this.states[i].toLowerCase();
				//compare the lowercase versions for case-insensitive comparison				
				if(sStateLC.indexOf(sTextboxValueLC) == 0){
					//add a suggest using what's already in the textbox to begin it
					asSuggest.push(this.states[i]);
					//asSuggest.push(sTextboxValue + this.states[i].substring(sTextboxValue.length));
				}
			}
		}else{
			var sDelim = new String("(");
			var sDelim2 = new String(")");
			var iNextIndex = new Number(0);
			var iDelimIndex = new Number(0);
			var iDelimIndex2 = new Number(0);
			var sState = new String("");
			var sStateStkCode = new String("");	
			//User key-in stock code
			var sTextboxValue = sTextboxValue;				
			for(var i=0; i<this.states.length; i++){
				sState = this.states[i];
				iDelimIndex = sState.indexOf(sDelim,iNextIndex);
				iDelimIndex2 = sState.indexOf(sDelim2,iNextIndex);				
				sStateStkCode = sState.substring(iDelimIndex+1, iDelimIndex2);
				if(sStateStkCode.indexOf(sTextboxValue) == 0){
					//add a suggest using what's already in the textbox for the code(digit only)
					asSuggest.push(this.states[i]);
				}
			}
		}
	}
	//if undefined problem occur, maybe due to the feed data(sStkCodeName)error, or change char error.			
	//alert("asSuggest[0]=["+asSuggest[0]+"]");
	//provide suggests to the control
	vs_oAutoSuggestControl.autosuggest(asSuggest, vs_bTypeAhead);
};
