﻿var xmlAsyncSuggest = new AkimanXmlAsyncHTTP();
var suggestCollection = new SuggestCollection();

function SuggestCollection() {
	this.objCurrentSuggest = null;
	this.arrSuggests = new Array();	
	
	/**
	 * Adds the suggest object
	 *			
	 * @param	suggestBoxId				id of the text box
	 * @param	suggestDivId				id of the suggest div
	 * @param	nextElemId					id of the object which will gain focus if suggested item is accepted
	 * @param	serviceUrl					url of the web service
	 * @param	webMethodName				name of the method which will be called from the web service
	 * @param	params						params array
	 * @param	onSelected					expression to be evaluated when a suggest is accepted
	 * @param	onVisibleChanged			expression to be evaluated when the visibility of the sugest div changed
	 * @param	xslUrl						optional, url of the xsl which will transform the xml returned by the service
	 * @param	suggestDivClass				optional, css class of the suggest div
	 * @param	suggestLineClass			optional, css class of the unselected suggest lines
	 * @param	suggestLineSelectedClass	optional, css class of the selected suggest lines
	 */
	this.add = function(suggestBoxId, suggestDivId, nextElemId, serviceUrl, webMethodName, params, onSelected, onVisibleChanged, xslUrl, suggestDivClass, suggestLineClass, suggestLineSelectedClass) {
		newSuggest = new Suggest();
		var result = newSuggest.init(this, suggestBoxId, suggestDivId, nextElemId, serviceUrl, webMethodName, params, onSelected, onVisibleChanged, xslUrl, suggestDivClass, suggestLineClass, suggestLineSelectedClass);
		
		if(result) {
			this.arrSuggests.push(newSuggest);
		}
	}

	this.remove = function(suggestBoxId) {
		var objSuggestBox = document.getElementById(suggestBoxId);
		
		if(objSuggestBox) {
			var objSuggest = null;
			var arrTemp = new Array(); 

			for(var i=0; i<this.arrSuggests.length; i++) {
				if(this.arrSuggests[i].isMySuggestBox(objSuggestBox)) {
					objSuggest = this.arrSuggests[i];
				}
				else {
					arrTemp.push(this.arrSuggests[i]);
				}
			}
			
			this.arrSuggests = arrTemp;
			
			if(objSuggest) {
				objSuggest.dispose();
				objSuggest = null;
			}
		}	
	}
	
	this.removeAll = function() {
		while(this.arrSuggests.length > 0) {
			var objSuggest = this.arrSuggests.pop();
			objSuggest.dispose();
			objSuggest = null;
		}
	}
	
	this.count = function() { return this.arrSuggests.length; }
	
	function Suggest() {
		this.isSuggest = true;					// constant
		var intSelectedLine = -1;				// selected suggest line index		
		var strOriginalText = null;				// text written to text box before any line is selected
		var intSuggestLineCount = 0;			// # of suggest lines
		var suggestEvents = new EventList();	// event collection for the suggest box
		var suggestLineEvents = new EventList();// event collection for the suggest lines

		var collection;
		var objSuggestBox = null;
		var objSuggestDiv = null;
		var objNextElem = null;
		var strServiceUrl = null;
		var strWebMethodName = null;
		var strXslUrl = null;
		var strSuggestLineClass = null;
		var strSuggestLineSelectedClass = null;
		
		var arrParams = new Array();
		var fnOnSelected = null;
		var fnOnVisibleChanged = null;
		
		var lastUnsuccessful = "";

		/**
		 * Initializes the suggest object
		 *
		 * @param	suggestCollection			parent SuggestCollection object			
		 * @param	suggestBoxId				id of the text box
		 * @param	suggestDivId				id of the suggest div
		 * @param	nextElemId					id of the object which will gain focus if suggested item is accepted
		 * @param	serviceUrl					url of the web service				
		 * @param	webMethodName				name of the method which will be called from the web service
		 * @param	params						params array
		 * @param	onSelected					expression which will be evaluated when a suggest is accepted
		 * @param	xslUrl						optional, url of the xsl which will transform the xml returned by the service
		 * @param	suggestDivClass				optional, css class of the suggest div
		 * @param	suggestLineClass			optional, css class of the unselected suggest lines
		 * @param	suggestLineSelectedClass	optional, css class of the selected suggest lines
		 *
		 * @return								true if the initialization is successful, false otherwise
		 */
		this.init = function(suggestCollection, suggestBoxId, suggestDivId, nextElemId, serviceUrl, webMethodName, params, onSelected, onVisibleChanged, xslUrl, suggestDivClass, suggestLineClass, suggestLineSelectedClass) {
			if(suggestCollection == null || serviceUrl == null || webMethodName == null)
				return false;
			
			collection = suggestCollection;
			objSuggestBox = document.getElementById(suggestBoxId);
			objSuggestDiv = document.getElementById(suggestDivId);
			
			if(!objSuggestBox || !objSuggestDiv || objSuggestBox.readOnly || objSuggestBox.disabled) {
				return false;
			}
			
			objSuggestBox.setAttribute('autocomplete', 'off');
			objSuggestDiv.style.width = objSuggestBox.clientWidth;
			
			strServiceUrl = serviceUrl;
			strWebMethodName = webMethodName;
			
			if (params!=null && params.constructor &&  params.constructor == Array) {
				arrParams = params;
			}
			
			fnOnSelected = onSelected;
			fnOnVisibleChanged = onVisibleChanged;
			
			strXslUrl = (xslUrl != null) ? xslUrl : 'suggest.xsl';
			strSuggestLineClass = (suggestLineClass != null) ? suggestLineClass : 'suggestLine';
			strSuggestLineSelectedClass = (suggestLineSelectedClass != null) ? suggestLineSelectedClass : 'suggestLineSelected';
			
			objSuggestDiv.className = (suggestDivClass == null) ? 'suggestDiv' : suggestDivClass;

			if(nextElemId) {
				objNextElem = document.getElementById(nextElemId);
			}

			suggestEvents.add(objSuggestBox, 'keyup', this.suggestBoxKeyUp);
			suggestEvents.add(objSuggestBox, 'blur', this.suggestBoxBlur);

			return true;
		}
			
		/**
		 * Removes the events attached to the suggest objects
		 */
		this.dispose = function() {
			suggestEvents.clear();
			suggestEvents = null;
			
			suggestLineEvents.clear();
			suggestLineEvents = null;
		}
		
		this.isMySuggestBox = function(obj) {
			return (objSuggestBox == obj)
		}
		
		this.getSuggestLines = function() {
			return objSuggestDiv.childNodes;
		}
		
		this.setSuggestLines = function(innerHTML) {	
			objSuggestDiv.innerHTML = innerHTML;

			var arrSuggestLines = this.getSuggestLines();
			intSuggestLineCount = arrSuggestLines.length;
			
			if(intSuggestLineCount > 0) {
				suggestLineEvents.clear();
				var arrSuggestLines = this.getSuggestLines();
				
				for(var i=0; i<arrSuggestLines.length; i++) {
					suggestLineEvents.add(arrSuggestLines[i], 'mouseover', this.suggestLineMouseOver);
					suggestLineEvents.add(arrSuggestLines[i], 'mouseout', this.suggestLineMouseOut);
					suggestLineEvents.add(arrSuggestLines[i], 'mousedown', this.suggestLineMouseDown);
				
					arrSuggestLines[i].className = strSuggestLineClass;
				}			
				
				this.showSuggestDiv();
			}
			else {
				this.hideSuggestDiv();
				lastUnsuccessful = objSuggestBox.value;
			}
		}

		this.showSuggestDiv = function() {
			intSelectedLine = -1;
			if(objSuggestDiv.style.visibility != 'visible') {
				objSuggestDiv.style.visibility = 'visible';
				try {
					eval(fnOnVisibleChanged);
				}
				catch(e){}
			}
		}

		this.hideSuggestDiv = function() {
			intSelectedLine = -1;
			if(objSuggestDiv.style.visibility != 'hidden') {
				objSuggestDiv.style.visibility = 'hidden';
				try {
					eval(fnOnVisibleChanged);
				}
				catch(e){}
			}
		}

		this.getSuggestLineIndex = function(objSuggestLine) {
			var index = -1;
			
			if(objSuggestLine) {	
				var arrSuggestLines = this.getSuggestLines();

				for(var i=0; i<arrSuggestLines.length; i++) {
					if(objSuggestLine == arrSuggestLines[i]) {
						index = i;
						break;
					}
				}
			}

			return index;
		}

		this.selectSuggestLine = function(objSuggestLine) {
			if(objSuggestLine) {
				objSuggestLine.className = strSuggestLineSelectedClass;
				objSuggestBox.value = objSuggestLine.innerText;
			}
		}

		this.selectSuggestLineByIndex = function(index) {
			var arrSuggestLines = this.getSuggestLines();
			
			if(arrSuggestLines.length>index) {
				this.selectSuggestLine(arrSuggestLines[index]);
			}
		}

		this.unselectSuggestLine = function(objSuggestLine) {
			objSuggestLine.className = strSuggestLineClass;
		}

		this.unselectAllSuggestLines = function() {
			var arrSuggestLines = this.getSuggestLines();

			for(var i=0; i<arrSuggestLines.length; i++) {
				this.unselectSuggestLine(arrSuggestLines[i]);
			}
		}

		this.acceptSuggestLine = function(objSuggestLine) {
			if(objSuggestLine) {
				objSuggestBox.value = objSuggestLine.innerText;
				strOriginalText = objSuggestBox.value;
				
				intSelectedLine = -1;
				this.hideSuggestDiv();

				try {
					eval(fnOnSelected);
				}
				catch(e){}
			}			
		}

		this.acceptSuggestLineByIndex = function(index) {
			var arrSuggestLines = this.getSuggestLines();
			
			if(arrSuggestLines.length>index) {
				this.acceptSuggestLine(arrSuggestLines[index]);
			}
		}
		
		/*** Event Handlers ***/
		this.suggestBoxBlur = function() {
			if(this.isSuggest) {
				if(objSuggestBox) {
					intSelectedLine = -1;
					lastUnsuccessful = "";
					this.hideSuggestDiv();
				}
			}
			else if(collection.objCurrentSuggest) {
				collection.objCurrentSuggest.suggestBoxBlur();
			}
		}
		
		this.suggestLineMouseOver = function(e) {
			if(this.isSuggest) {
				this.unselectAllSuggestLines();
				
				var obj = GetEventSource(e);
				this.selectSuggestLine(obj);
				intSelectedLine = this.getSuggestLineIndex(obj);
			}
			else if(collection.objCurrentSuggest) {
				collection.objCurrentSuggest.suggestLineMouseOver(e);
			}
		}
		
		this.suggestLineMouseOut = function(e) {
			if(this.isSuggest) {
				var obj = GetEventSource(e);
				
				index = this.getSuggestLineIndex(obj);
				if(index == intSelectedLine) {
					this.unselectSuggestLine(obj);
					objSuggestBox.value = strOriginalText; // restore original text
					intSelectedLine = -1;
				}
			}
			else if(collection.objCurrentSuggest) {
				collection.objCurrentSuggest.suggestLineMouseOut(e);
			}
		}
		
		this.suggestLineMouseDown = function(e) {
			if(this.isSuggest) {
				var obj = GetEventSource(e);
				this.acceptSuggestLine(obj);
			}
			else if(collection.objCurrentSuggest) {
				collection.objCurrentSuggest.suggestLineMouseDown(e);
			}
		}

		this.suggestBoxKeyUp = function(e) {
			if(this.isSuggest) {
				if(objSuggestBox.value.length == 0) {
					this.hideSuggestDiv();
				}
				else {
					switch(GetEventKey(e)) {
						case 13:	// enter
							if(intSelectedLine > -1) {
								this.acceptSuggestLineByIndex(intSelectedLine);

								if(objNextElem) {
									try {
										objNextElem.focus();
									}
									catch(e) {}
								}
							}
							break;
						case 40:	// down
							if(intSelectedLine < intSuggestLineCount-1) {
								intSelectedLine++;

								this.unselectAllSuggestLines();
								this.selectSuggestLineByIndex(intSelectedLine);
							}
							break;
						case 38:	// up
							if(intSelectedLine >= 0) {
								intSelectedLine--;
								
								this.unselectAllSuggestLines();
								
								if(intSelectedLine == -1) {
									objSuggestBox.value = strOriginalText;
								}
								else {
									this.selectSuggestLineByIndex(intSelectedLine);
								}
							}
							break;		
						default:
							if(lastUnsuccessful.length > 0 
								&& lastUnsuccessful.length < objSuggestBox.value.length
								&& objSuggestBox.value.indexOf(lastUnsuccessful) > -1) {
								;	
								// if lastUnsuccessful is exists
								// and search text contains that, 
								// do not send a request
							}
							else {
								strOriginalText = objSuggestBox.value;

								var strEval = "CustomParamBuilder('input','" + objSuggestBox.value.replace(/'/g, "\\'") + "'";

								for(i=0; i<arrParams.length; i++) {
									var obj = document.getElementById(arrParams[i]);
									if(obj) {
										strEval += ",'" + obj.name + "','" + obj.value.replace(/'/g, "\\'") + "'";
									}
								}

								strEval += ")";

								var param = eval(strEval);
								
								xmlAsyncSuggest.loadURL(strServiceUrl , SoapDataBuilder(strWebMethodName, param), this.suggestStateChange, strXslUrl);
								break;
							}
					}
				}
			}
			else if(this.parentNode) {
				collection.objCurrentSuggest = null;
				
				for(var i=0; i<collection.arrSuggests.length; i++) {
					if(collection.arrSuggests[i].isMySuggestBox(this)) {
						collection.objCurrentSuggest = collection.arrSuggests[i];
						break;
					}
				}
				
				if(collection.objCurrentSuggest) {
					collection.objCurrentSuggest.suggestBoxKeyUp(e);
				}
			}
		}
		
		
		this.suggestStateChange = function() {
			if(this.isSuggest) {
				if (xmlAsyncSuggest.ReadyState() == 4) {
					if (xmlAsyncSuggest.Status() == 200) {
						var innerHTML = xmlAsyncSuggest.TransformResponse();
						this.setSuggestLines(innerHTML);
					}
				}
			}
			else if(collection.objCurrentSuggest) {
				collection.objCurrentSuggest.suggestStateChange();
			}
		}
	}
}