
	var AutoCompleter = function (oUserSetting) {
		this.oDefaultSetting = {
				'sUrl' : 'http://yoursite.com/search_something=SEARCH_KEYWORD&more_parameter=123', //URL of ajax, the SEARCH_KEYWORD will be replace by what user had type in with char limit of 'iCharLimit'
				'iCharLimit' : 3, //Char limit to trigger the ajax, cache array key, and maximum lenght of SEARCH_KEYWORD
				'iZebraLine' : 2, //dim every # line for result list, a zebra effect
				'iRenderLimit' : 10, //how many result to show for match result
				'sElementId' : 'location_search', //the outer div that contents all the needed div inside
				'sResultShowAllCSS' : {}, //result list style, when show all mode
				'sResultShowMinCSS' : {}, //result list style, when show less mode (default)
				'onInputClick': function(){}, //trigger when user selected the result
				'onStaticInputClick':function(){},
				'onData' : function(oData, sKeyword){}, //function to build the ajax-json return into auto complete's array list, a[] = {'id':123, 'value':'any data type', 'display':'my own display design'}, every array index is a list item
				'onSelect' : function(id,value,Display){}, //this function will be call when user click on the list item, all related data will be pass back to this function
				'bPersisField' : false, // If this turn true, when user click on dropdown list, then the search field will switch to text, else will remain the same
				'bNoEscForSearchField' : true, // then this is true, press escape will do nothing such as switch to text display 
				'bShowSearchFieldAfterEsc':false, //turn on this will always show search field, dun care wat
				'bHideSearchField' : false //true=show the search field and displaying the static text, false = show as normal
		};
		this.oParam = jQuery.extend({},this.oDefaultSetting, oUserSetting);

		this.oUkey = {};//the cache bank
		this.oUkeyLock = {};//the cache bank key, when someone pressed the key words, instantly it will true, but oUkey will filled with data if ajax is back.

		this.sUserInputTxt = ''; //the latest user input
		this.iInternalSchLimit = 100; //this is internal limit, when list all result, it will stop at this number
		this.sBufferKey = ''; //the latest cache array's key
		this.sLastSelectedDisElem = ''; //last selected display name
		this.sSelectedDisElem = ''; //current selected display name
		this.sSelectedElemID = ''; //current selected (clicked/enter) element ID
		this.oSelectedElem = false; //current selected element
		this.oHighlightedElemID = ''; //current higlighted (keyboard updown/mouse) element ID
		this.oShowAllResult = false; //should be always false
		this.sResultFetchState = false; //should be always false, only true during ajax reading the data

		//on start, hide all div
		$('#'+this.oParam.sElementId+' .result_list_onload').hide();
		$('#'+this.oParam.sElementId+' .result_list_noresult').hide();
		
		if(this.oParam.bHideSearchField==true) {
			$('#'+this.oParam.sElementId+' .search_input'	).hide();
			$('#'+this.oParam.sElementId+' .search_inputstatic').show();
		}else{
			$('#'+this.oParam.sElementId+' .search_inputstatic').hide();
			$('#'+this.oParam.sElementId+' .search_input'	).show();
		}

		$('#'+this.oParam.sElementId+' .result_output_extra').hide();
		$('#'+this.oParam.sElementId+' .result_list').hide();

		this.setFormStaticData = function(sDisplay, sSearchWord) {
				$('#'+_self.oParam.sElementId+' .search_input'	).hide().val(sSearchWord);
				$('#'+_self.oParam.sElementId+' .result_list'	).hide();
				$('#'+_self.oParam.sElementId+' .search_inputstatic').show().html('<span id="'+_self.oParam.sElementId+'__static" name="'+_self.oParam.sElementId+'__static" class="sch_result_list">'+sDisplay+'</span>');
				$('#'+_self.oParam.sElementId+' .search_inputstatic_c').val(sDisplay);
		}


		this.selectResultElem = function (oSelectedItem, bShowResult) {
			var _self = this;
			var aIDKey = oSelectedItem.attr('id').split("__");

			if(_self.sSelectedElemID != ''){
				$('#'+_self.sSelectedElemID).removeClass('sch_result_list_hover');
			}

			_self.oSelectedElem = oSelectedItem;
			_self.sSelectedElemID = oSelectedItem.attr('id');
			_self.oHighlightedElemID = oSelectedItem.attr('id');
			_self.sSelectedDisElem = _self.oUkey[aIDKey[0]][aIDKey[1]]['display'];
			_self.oSelectedElem.addClass('sch_result_list_hover');
			
			if( bShowResult != true ) {
				$('#'+_self.oParam.sElementId+' .result_list'	).hide();
				$('#'+_self.oParam.sElementId+' .search_inputstatic_c').val(_self.oUkey[aIDKey[0]][aIDKey[1]]['display']);

				if(_self.oParam.bPersisField) {
					$('#'+_self.oParam.sElementId+' .search_input').hide();
					$('#'+_self.oParam.sElementId+' .search_inputstatic').show().html('<span id="'+_self.oParam.sElementId+'__static" name="'+_self.oParam.sElementId+'__static" class="sch_result_list">'+_self.oUkey[aIDKey[0]][aIDKey[1]]['displaytxt']+'</span>');
					$('#'+_self.oParam.sElementId+' .search_inputstatic').unbind('click').bind('click', function(){
						
						$('#'+_self.oParam.sElementId+' .search_input').show().focus();
						$('#'+_self.oParam.sElementId+' .search_inputstatic').hide();
						$('#'+_self.oParam.sElementId+' .result_output').fadeIn('slow');
						if($('#'+_self.oParam.sElementId+' .result_output_extra').html() !=''){
							$('#'+_self.oParam.sElementId+' .result_output_extra').fadeIn('slow');
						}else{
							$('#'+_self.oParam.sElementId+' .result_output_extra').hide;
						}
						$('#'+_self.oParam.sElementId+' .result_list').fadeIn('slow');
						_self.oParam.onStaticInputClick();
					});
				}else{
					$('#'+_self.oParam.sElementId+' .search_input'	).show();
					$('#'+_self.oParam.sElementId+' .search_inputstatic').hide();
					$('#'+_self.oParam.sElementId+' .result_list').hide();
					$('#'+_self.oParam.sElementId+' .search_input').val(_self.oUkey[aIDKey[0]][aIDKey[1]]['displaytxt']);

				}
				_self.extraResultField(false,'',false,true);

			}

			//call user function
			_self.oParam.onSelect(
				_self.oUkey[aIDKey[0]][aIDKey[1]]['id'],
				_self.oUkey[aIDKey[0]][aIDKey[1]]['value'],
				_self.oUkey[aIDKey[0]][aIDKey[1]]['display'],
				_self.oUkey[aIDKey[0]][aIDKey[1]]['displaytxt'],
				_self.oUkey[aIDKey[0]][aIDKey[1]]['count']
			);

		}

		/*will do the rendering of result list
		sKeyword: full keyword that user inserted
		bShowAll: true mean ignore the display limit that user inserted
		*/
		this.filterData = function (sKeyword, bShowAll) {
			var _self = this;
			var sDivOut = [];
			var h = g = 0;
			iLimit = parseInt(this.oParam.iRenderLimit);

			//get the match list
			for(var i in this.oUkey[this.sBufferKey]) {
				var sDisplay = this.oUkey[this.sBufferKey][i]['displaytxt'];
				var iCount = this.oUkey[this.sBufferKey][i]['count'];
				var sId = this.oUkey[this.sBufferKey][i]['id'];


				if(sDisplay && sDisplay!='undefined' && sDisplay.toLowerCase().indexOf(sKeyword.toLowerCase()) != -1) {
					if( (h<=iLimit || bShowAll) && h <_self.iInternalSchLimit ) {

						var sS='';
						if(iCount>0) {
							sS=iCount+'&nbsp;'+((iCount==1 || iCount=='1') ? 'product' : 'products');
						}

						sDivOut.push('<span name=\''+this.sBufferKey+'__'+i+'__'+sId+'__'+h+'\' id=\''+this.sBufferKey+'__'+i+'__'+sId+'__'+h+'\' class="'+( (h%_self.oParam.iZebraLine==0) ?'sch_result_item_dim':'sch_result_item')+'" > <div style="float:left;">'+_self.makeHighlight(sDisplay,sKeyword)+'</div> <div class="__cl" style="float:right;"> '+sS+'</div> <div style="clear:both;"/> </span>');
						h++;
					}
					g++;
				}
			}



			//if having result
			if(h>0) {
				//build the result HTML
				$('#'+_self.oParam.sElementId+' .result_output').html(sDivOut.join(''));
				$('#'+this.oParam.sElementId+' .result_list').show();
				$('#'+_self.oParam.sElementId+' .result_output'	).fadeIn('slow');

				var bHavSelect = false;
				//bind all the results with function
				$('#'+_self.oParam.sElementId+' .result_output span').each(function() {
					if( $(this).attr('id') != undefined){
						var aIDKey = $(this).attr('id').split("__");
						if(aIDKey[1] != undefined && aIDKey[0] != undefined){

						  if(_self.sSelectedDisElem == _self.oUkey[aIDKey[0]][aIDKey[1]]['display']){$(this).addClass('sch_result_list_hover');}

							if(!bHavSelect){
								_self.selectResultElem($(this), true);
								bHavSelect = true;
							}
							//when result on click
							$(this).click(function() {
								_self.selectResultElem($(this), false);
								_self.oParam.onInputClick();
							});

							$(this).mouseover(function() {
								$(this).addClass('sch_result_list_hover');
							});

							$(this).mouseout(function() {
								if(_self.sSelectedDisElem!=_self.oUkey[aIDKey[0]][aIDKey[1]]['display']){ $(this).removeClass('sch_result_list_hover'); }
							});
						}
					}
				});

				//control the extra field (show/hide all)
				if( (g>0 && h<g) || bShowAll ){
					_self.extraResultField(true,'<span id=\''+_self.oParam.sElementId+'__extra\' name=\''+_self.oParam.sElementId+'__extra\' class="sch_result_extra">'+(_self.oShowAllResult?'Show less, '+(h<iLimit?h:iLimit)+' matches':'Show more, total '+g+' matches')+'</span>');

					$('#'+_self.oParam.sElementId+'__extra').click(function(){
						_self.oShowAllResult = !_self.oShowAllResult;
						_self.filterData(sKeyword,_self.oShowAllResult);
						_self.scrollControl();
					});

				}

			}else{
				//display no result
				$('#'+_self.oParam.sElementId+' .result_output').html($('#'+_self.oParam.sElementId+' .result_list_noresult').html());
				$('#'+_self.oParam.sElementId+' .result_output'	).show();
				$('#'+_self.oParam.sElementId+' .result_list'	).show();
				_self.extraResultField(false,'',true);
			}

			//if output is less then limit, hide the extra field
			if(h<=iLimit && !bShowAll){
				_self.extraResultField(false,'',false);
			}
			return h;
		}

		//style switch for result list
		this.scrollControl = function (){
			var _self = this;
			if(_self.oShowAllResult){
			//browser compatible issue, need to assign one by one
				for(var c in _self.oParam.sResultShowAllCSS ){
					try{
						$('#'+_self.oParam.sElementId+' .result_list').css(c,_self.oParam.sResultShowAllCSS[c]);
					}catch(e){}
				}
				//$('#'+_self.oParam.sElementId+' .result_list').css(_self.oParam.sResultShowAllCSS);
			}else{
				for(var c in _self.oParam.sResultShowMinCSS ){
					try{
						$('#'+_self.oParam.sElementId+' .result_list').css(c,_self.oParam.sResultShowMinCSS[c]);
					}catch(e){}
				}
				//$('#'+_self.oParam.sElementId+' .result_list').css(_self.oParam.sResultShowMinCSS);
			}
		}

		//replace with url friendly code, for pass to ajax
		this.urlencode = function (str) {
			return escape(str).replace(/\+/g,'%2B').replace(/%20/g, '+').replace(/\*/g, '%2A').replace(/\//g, '%2F').replace(/@/g, '%40');
		}

		/*Return HTML highlighted string
		sContent: input string
		sKeyword: keyword to highlight
		*/
		this.makeHighlight = function (sContent,sKeyword) {
			//this function is for recursive call
			var iKeyLen = sKeyword.length;
			if(iKeyLen>0 && sContent.length>0){
				var sKeywordLw = sKeyword.toLowerCase();
				var sContentLw = sContent.toLowerCase();
				var iCurrIndex = sContentLw.indexOf(sKeywordLw,0); sContentLw = '';
				if(iCurrIndex >= 0){
					var sTemp = [];
					sTemp.push(sContent.substr(0,iCurrIndex));
					sTemp.push('<span class="sch_highlight">');
					sTemp.push(sContent.substr(iCurrIndex,iKeyLen ));
					sTemp.push('</span>');
					sTemp.push( this.makeHighlight( sContent.substr(iCurrIndex+iKeyLen) ,sKeyword) );
					return sTemp.join('');
				}else{
					return sContent;
				}
			}else{
				return '';
			}
		}

		/* Controller of Extra field (Show/Hide All)
		bShow: true/false for display
		sMsg: text to embed
		bClrMsg: true mean remove any text inside
		bFast: display effect, true will use show/hide
		*/
		this.extraResultField = function(bShow, sMsg, bClrMsg, bFast){
			var _self = this;
			_self.scrollControl();
			if(bShow){
				if(bFast==true){
					$('#'+_self.oParam.sElementId+' .result_output_extra').show();
				}else{
					$('#'+_self.oParam.sElementId+' .result_output_extra').fadeIn('slow');
				}
				$('#'+_self.oParam.sElementId+' .result_output_extra').html(sMsg);
			}else{
				if(bFast==true){
					$('#'+_self.oParam.sElementId+' .result_output_extra').hide();
				}else{
					$('#'+_self.oParam.sElementId+' .result_output_extra').fadeOut('slow');
				}
			}
			if(bClrMsg==true){
				$('#'+_self.oParam.sElementId+' .result_output_extra').html('');
			}
		}

		/*start the auto complete on selected div
		*/
		this.start = function () {
			var _self = this;
			var sOriginalInput='';
			var sOriginalLoc='';
			var sOriginalCcode='';
			
			$('#'+_self.oParam.sElementId+' .search_inputstatic').unbind('click').bind('click', function(){
				$('#'+_self.oParam.sElementId+' .search_input').show().focus();
				$('#'+_self.oParam.sElementId+' .search_inputstatic').hide();
				$('#'+_self.oParam.sElementId+' .result_output').fadeIn('slow');
				_self.oParam.onStaticInputClick();
			});

			try{ //void error, for non IE browser
				$('#'+_self.oParam.sElementId+' .result_list').bgiframe(); //to handle z-index issue on IE, just include bgiframe plugin
			}catch(e){}

			$('#'+_self.oParam.sElementId).each(function () {
				$(this).find('.search_input').each(function () {
					$(this).attr('autocomplete', 'off'); //remove browser history
					var oInputPos = $(this).position();
					$('#'+_self.oParam.sElementId+' .result_list').css({ "position": "absolute", "marginLeft": 0, "marginTop": 0, "top": (oInputPos.top+$(this).height()+6), "left": oInputPos.left });
					
					sOriginalInput = $('#'+_self.oParam.sElementId+' .search_input').val();
					sOriginalCcode=$('#'+_self.oParam.sElementId+' .sCountryCode').val();
					sOriginalLoce=$('#'+_self.oParam.sElementId+' .sLocationList').val();

					//jQuery cant work well on enter
					$(this).keypress(function(e) {
						if( !_self.oParam.bNoEscForSearchField && (e.which==27)) {
								$('#'+_self.oParam.sElementId+' .result_list').hide();
								if(!_self.oParam.bShowSearchFieldAfterEsc){
									$('#'+_self.oParam.sElementId+' .search_input').hide();
								}
								$('#'+_self.oParam.sElementId+' .search_inputstatic_c').val('');
								$('#'+_self.oParam.sElementId+' .search_inputstatic').show().html('<span id="'+_self.oParam.sElementId+'__static" name="'+_self.oParam.sElementId+'__static" class="sch_result_list">'+sOriginalInput+'</span>');
								$('#'+_self.oParam.sElementId+' .search_inputstatic_c').val(sOriginalInput);
								$('#'+_self.oParam.sElementId+' .search_input').val(sOriginalInput);
								$('#'+_self.oParam.sElementId+' .sCountryCode').val(sOriginalCcode);
								$('#'+_self.oParam.sElementId+' .sLocationList').val(sOriginalLoce);
//evo								$('.show_search_field').show();
							
								if(_self.oParam.bShowSearchFieldAfterEsc) {
									$('#'+_self.oParam.sElementId+' .search_input').show();
								}
						}

						if(_self.sUserInputTxt.length>=_self.oParam.iCharLimit && e.which==13) {
							_self.oSelectedElem.click();
//EVO:6Nov2009							$('.submit_button').click();
						}
					});

					$(this).keyup(function(e) {
						if( !_self.oParam.bNoEscForSearchField && (e.which==27)) {
							
								$('#'+_self.oParam.sElementId+' .result_list').hide();
								if(!_self.oParam.bShowSearchFieldAfterEsc) {
									$('#'+_self.oParam.sElementId+' .search_input').hide();
								}
								$('#'+_self.oParam.sElementId+' .search_inputstatic_c').val('');
								$('#'+_self.oParam.sElementId+' .search_inputstatic').show().html('<span id="'+_self.oParam.sElementId+'__static" name="'+_self.oParam.sElementId+'__static" class="sch_result_list">'+sOriginalInput+'</span>');
								$('#'+_self.oParam.sElementId+' .search_inputstatic_c').val(sOriginalInput);
								$('#'+_self.oParam.sElementId+' .search_input').val(sOriginalInput);
								$('#'+_self.oParam.sElementId+' .sCountryCode').val(sOriginalCcode);
								$('#'+_self.oParam.sElementId+' .sLocationList').val(sOriginalLoce);


								if(_self.oParam.sElementId=='departure_location_search') {
								  	$('#flight_refinesearchform #departure_search_input').hide();
								  	$('#flight_refinesearchform .show_search_field_departure').show();
								}
								else if(_self.oParam.sElementId=='destination_location_search') {
								  $('#flight_refinesearchform #destination_search_input').hide();
								  $('#flight_refinesearchform .show_search_field_destination').show();
								}
								else {		
									$('.show_search_field').show();
									if(_self.oParam.bShowSearchFieldAfterEsc) {
										$('#'+_self.oParam.sElementId+' .search_input').show();
									}
								}
						} else {

							var oInputPos = $(this).position();
							$('#'+_self.oParam.sElementId+' .result_list').css({ "position": "absolute", "marginLeft": 0, "marginTop": 0, "top": (oInputPos.top+$(this).height()+6), "left": oInputPos.left });
						
							_self.sSelectedElem = '';//clear selected element on typing activity
							_self.sUserInputTxt = $(this).attr('value').replace(/^\s+|\s+$/g,'');
							_self.sBufferKey = _self.sUserInputTxt.toUpperCase().substring(0,_self.oParam.iCharLimit); //key of cache, will pass for search
							
							if(_self.sUserInputTxt.length>=_self.oParam.iCharLimit){
								
								if( e.which==38 || e.which==40 || e.which==37 || e.which==39 || e.which==13 ){
									if(e.which==38){
										try{
											if( _self.oSelectedElem.prev().attr('id') != undefined ){
												_self.oSelectedElem.mouseout();
												_self.oSelectedElem = _self.oSelectedElem.prev();
												_self.selectResultElem(_self.oSelectedElem,true);
												_self.oSelectedElem.mouseover();
											}
										}catch(e){}
									}
									if(e.which==40){
										try{
											if( _self.oSelectedElem.next().attr('id') != undefined){
												_self.oSelectedElem.mouseout();
												_self.oSelectedElem = _self.oSelectedElem.next();
												_self.selectResultElem(_self.oSelectedElem,true);
												_self.oSelectedElem.mouseover();
											}
										}catch(e){}
									}

									if(_self.sUserInputTxt.length>=_self.oParam.iCharLimit && e.which==13){	
										_self.oSelectedElem.click();
//EVO:6Nov2009										$('.submit_button').click();
									}

								}else{
									_self.extraResultField(false,'',true,true); //clear show/hideall if any
									_self.oShowAllResult = false; //&& _self.sResultFetchState == false
									if(_self.oUkey[_self.sBufferKey] == undefined && _self.oUkeyLock[_self.sBufferKey] == undefined ){
										_self.oUkeyLock[_self.sBufferKey] = true;
										//get from ajax if cache not found
										_self.sResultFetchState = true;
										$('#'+_self.oParam.sElementId+' .result_list'	).show();
										$('#'+_self.oParam.sElementId+' .result_output'	).html($('#'+_self.oParam.sElementId+' .result_list_onload').html());
										var sAutoCompSchUrl = _self.oParam.sUrl.replace(/SEARCH_KEYWORD/,_self.urlencode(_self.sBufferKey));


										$.getJSON(sAutoCompSchUrl,
										function(data) {
											//data is back, pass to user defined function for processing.
											_self.oUkey[_self.sBufferKey] = _self.oParam.onData(data,_self.sBufferKey);
											//go for render
											_self.filterData(_self.sUserInputTxt,false);
											_self.sResultFetchState = false;
										});
										$('#'+_self.oParam.sElementId+' .result_output'	).fadeIn('slow');
										$('#'+_self.oParam.sElementId+' .result_list'	).fadeIn('slow');
									} else {
										//cache found, direct render the data (if ajax is running and user is typing, it will wait)
										if(_self.sResultFetchState==false){_self.filterData(_self.sUserInputTxt,false);}
									}
								}
							}else{
								//user input less then the limit char, close existing list
								//$('#'+_self.oParam.sElementId+' .result_output'	).fadeOut('slow');
								$('#'+_self.oParam.sElementId+' .result_list'	).hide();
							}

						}
					});
				});
			});
		};
	};
