/**
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
*
*
*/

function AutoComplete2(sInputId, objDataSource, regex)
{
	if(!window.encodeURIComponent || arguments.length==0){
		//alert('error');
		return;
	};

	var _Class = this;
	_Class.objDataSource = objDataSource;

	var oNode = sInputId;
	if(!oNode){
		alert("<input /> does not exist");
		return;
	};
	oNode.setAttribute("autocomplete","off");

	var oNodeList = document.createElement("div");
	if(document.body.firstChild) {
		document.body.insertBefore(oNodeList,document.body.firstChild);
	} else {
		document.body.appendChild(oNodeList);
	};


	oNodeList.className = "select-free1 AutoComplete-list";
	var os = oNodeList["style"];
	os["position"] = "absolute";
	os["top"] = 0;
	os["left"] = 0;
	os["width"] = "auto";
	os["overflow"] = "hidden";
	os["zIndex"] = 99999;
	os["visibility"] = "hidden";

	_Class.tSetListPosition = setTimeout("void(0)",0);
	_Class.tGetData = setTimeout("void(0)",0);
	_Class.tBlur = setTimeout("void(0)",0);

	_Class.sKeyValue = _Class.trim(oNode.value);


	function _onInputKeyUp(e)
	{
		// this object might have been destructed already!
		if(!_Class.oNode) return;

		clearTimeout( _Class.tBlur );

		var ek = e.keyCode;
		if(ek==27) {
			/*esc*/
			_clearData();
		} else if(	(ek==40 || ek==38) && (window.opera==null)	) {
			/*
				40>down
				38>up
			*/
			if(oNodeList.style.visibility.toLowerCase()!="hidden")
			{
				var tds = oNodeList.getElementsByTagName("td");
				if(tds.length>0)
				{
					var i= (ek==40)?1:-1;
					var n = oNodeList.selectedIndex;
					n+= i;
					if(n<0){n=tds.length-1}
					else if(n>(tds.length-1)){n=0;};
					if(tds[n]) {
						oNodeList.selectedIndex = n;
						_tdHover( e,tds[n]);

						YuEvent.stopPropagation(e);
						YuEvent.preventDefault(e) ;

						return false;
					};
				};
			} else {
			}
		} else if(_Class.inFocus) {
			_Class.suggest();
		};
	};

	// invoke method to get and display suggestion list
	function suggest(bForce)
	{
		var v = _Class.trim(oNode.value);
//		var temp = v.split(',');
		var temp = v.split(regex);
		var input = (temp.length>0) && _Class.trim(temp[temp.length-1]);
		if (v.lastIndexOf(input)!= v.length-input.length) {
			_clearData();
			clearTimeout(_Class.tGetData);	
			_Class.sKeyValue = input;		
			return; // double check if split is done correctly
		}
		YAHOO.log(this + '.suggest: _Class.sKeyValue=' + _Class.sKeyValue + '; input=' + input);
		if(bForce || _Class.sKeyValue != input)
		{
			_clearData();
			clearTimeout(_Class.tGetData);		// clear the previous request if the user type fast enough
			if (input.length > 2) {
				_Class.tGetData = setTimeout(_getData(input),400);
			}
			_Class.sKeyValue = input;
		}
	};

	function _onInputKeyDown(e)
	{
		// this object might have been destructed already!
		if(!_Class.oNode) return;

		clearTimeout( _Class.tBlur );

		var ek = e.keyCode;
		if(ek==40 || ek==38) {
			YuEvent.stopPropagation(e);
			YuEvent.preventDefault(e) ;
			return false;
		};

		if(ek==13) {
			/*enter*/
			if(oNodeList.style.visibility.toLowerCase()!="hidden")
			{
				var tds = oNodeList.getElementsByTagName("td");
				if(tds.length>0) {
					var n = oNodeList.selectedIndex;
					if(tds[n])
					{
						_tdHover( e,tds[n]);
						_tdSelect(e,tds[n]);
						YuEvent.stopPropagation(e);
						YuEvent.preventDefault(e) ;

						return false;
					};
				};
			};
		};
	};


	function _tdHover(e,td)
	{
		td = td || this;

		if(oNodeList.selectedTD!=null) {
			if(oNodeList.selectedTD!=td ) {
				oNodeList.selectedTD.className='';
			};
		};

		if(oNodeList.selectedTD!=td ) {
			oNodeList.selectedTD=td ;
			td.className = 'AutoComplete-row-select';
		};

		YuEvent.stopPropagation(e);
		YuEvent.preventDefault(e) ;
		return false;
	};

	// encode a value into display-able html string
	// strValue: the value
	// strRefKey: the reference search key
	// return: {'html': the formatted html , 'match' : true|false }
	// match indicates whether this suggestion considered matching exactly
	function encodeItem(jsObj, strKey){
		var jsItem = {'html':"", 'match': false};
		var strValue = jsObj;
		var k1 = strKey;
		var k2 = _Class.trim( strValue );
		if(k1!="" && k2!="")
		{
			// break result into tokens: k2 = k0 + k1 + k3; where k1 is the keyword
			var ndx = k2.toLowerCase().indexOf(strKey.toLowerCase());
			if(ndx>=0){
				var k0 = k2.substr(0,ndx);
				k1 = k2.substr(ndx,k1.length);
				var k3 = k2.substr(ndx+k1.length);
				var html = ['<em>',_Class.htmlEncode(k2),'</em><span>',_Class.htmlEncode(k0),'</span><strong>',_Class.htmlEncode(k1),'</strong><span>',( _Class.htmlEncode(k3)) , '</span>'].join('');
				jsItem = {'html':html, 'match': k2==k1};
			}
			else{
				var html = ['<em>',_Class.htmlEncode(k2),'</em><span>',( _Class.htmlEncode(k2)) , '</span>'].join('');
				jsItem = {'html':html, 'match': false};
			}
		}
//		YAHOO.log('ac2.encodeItem: html = ' + _Class.htmlEncode(jsItem.html) + '<br> match = ' + jsItem.match);
		return jsItem;
	};

	// decode an html string to value
	// html: the html generated by encodeItem
	function decodeItem(html){
		var elTmp = cex ('div', {innerHTML: html});
		var elEm = elTmp.getElementsByTagName('em')[0];
		var value = elEm.innerHTML;
		return value;
	};

	//
	function _tdSelect(e,td)
	{
		td = td || this;

		var temp = oNode.value.split(regex);
		_Class.sKeyValue = _Class.decodeItem(td.innerHTML);
		oNode.value = oNode.value.substr(0,  oNode.value.length - temp[temp.length-1].length) + _Class.sKeyValue;
		YuEvent.stopPropagation(e);
		YuEvent.preventDefault(e) ;
		_clearData();

		if(_Class.onSelect){ _Class.onSelect(td.jsObj); }
		return false;
	};

	function _getData(tmpstr)
	{
	//	YAHOO.log('auto_complete._getData # _Class.inFocus = ' + _Class.inFocus);
	//	YAHOO.log('auto_complete._getData # _Class.bLazyMode = ' + _Class.bLazyMode);
		if(!_Class.inFocus && !_Class.bLazyMode) return;
		var f = function(){
			objDataSource.getData(tmpstr);
		};
		return f;
	};

	function _clearData()
	{
		// break events
		var tds = oNodeList.getElementsByTagName("td");
		for(var i=0;i<tds.length;i++)
		{
			YuEvent.removeListener(tds[i], "mouseover",_tdHover);
			YuEvent.removeListener(tds[i], "mousedown",_tdSelect);
		};
		tds = null;

		if(oNodeList.style.visibility.toLowerCase()!="hidden") {
			oNodeList.style.visibility = "hidden";
			oNodeList.innerHTML = "";
			clearTimeout( _Class.tSetListPosition);
		};

	};

	function _showData(arrData)
	{
		var aHTML = [];
		var w = oNode['offsetWidth'];
		if (!w || (!_Class.bLazyMode && !_Class.inFocus)) return;
		var x = 18;
		var ndxSelected = 0;
		for(var i=0;i<arrData.length;i++)
		{
			var jsItem = _Class.encodeItem( arrData[i] , _Class.sKeyValue);
			if(jsItem.match){ ndxSelected = i; }
			aHTML[i] = ['\n<tr class="AutoComplete-row ' + (i%2?'odd':'even') + '"><td>',jsItem.html,'</td></tr>'].join('');
		}

		if(arrData.length>0)
		{
			oNodeList.innerHTML = [ '<table class="AutoComplete-table"><tbody>',aHTML.join(''),'</tbody></table>','<!--[if lte IE 6.5]><iframe src="./"></iframe><![endif]-->'].join('');
			var os = oNodeList['style'];
			os['visibility'] = "visible";
			oNode.focus();

			var tds = oNodeList.getElementsByTagName("td");
			for(var i=0;i<tds.length;i++)
			{
				tds[i].jsObj = arrData[i];
				YuEvent.addListener(tds[i], "mouseover",_tdHover);
				YuEvent.addListener(tds[i], "mousedown",_tdSelect);
			};

			tds[ndxSelected].className = 'AutoComplete-row-select';
			oNodeList.selectedTD =  tds[ndxSelected];
			oNodeList.selectedIndex = ndxSelected;

			function __rePosition()
			{
				clearTimeout( _Class.tSetListPosition);

				if (!oNode['offsetWidth']) return;
				var p = getXY(oNode);
				if(document.uniqueID && ( location.href.toLowerCase().indexOf("tw")>0	||	location.href.toLowerCase().indexOf("kimo")>0 ) )
				{
					os['top'] = p.y + oNode['offsetHeight'] - 4 + "px";
					os['left'] = p.x + "px";
					os['width'] = oNode['offsetWidth'] + "px";
				}
				else
				{
					os['top'] = p.y + oNode['offsetHeight']  + "px";
					os['left'] = p.x + "px";
					os['width'] = oNode['offsetWidth']  - 3 + "px";
				}


				_Class.tSetListPosition = setTimeout(__rePosition,400);
			};
			clearTimeout( _Class.tSetListPosition);
			__rePosition();
		}
		else
		{
			_clearData()
		};
	};

	function _returnData(arrData, searchKey)
	{
//		YAHOO.log('ac2: _returnData: ' + arrData);
//		oNode.className = '';
		if(_Class.sKeyValue != "" && _Class.sKeyValue==searchKey){
			if(arrData.constructor == Array && arrData.length > 0){
				_showData(arrData);
				return;
			}
		}
		_clearData();
	};

	function _onInputBlur(e)
	{
		clearTimeout( _Class.tBlur );
		YuEvent.removeListener(oNode, "keyup",_onInputKeyUp);
		YuEvent.removeListener(oNode, "keydown",_onInputKeyDown);

		YuEvent.removeListener(oNode, 'blur', _onInputBlur);
		YuEvent.addListener(oNode, 'focus', _onInputFocus);
		var byebye = function(){_clearData();};
		_Class.tBlur = setTimeout(byebye,100);
		_Class.inFocus = false;
	};


	function _onInputFocus(e)
	{
		clearTimeout( _Class.tBlur );
		YuEvent.addListener(oNode, 'blur', _onInputBlur);
		YuEvent.removeListener(oNode, 'focus', _onInputFocus);
		if(!_Class.bLazyMode){ // bind only if lazy mode is not set
			YuEvent.addListener(oNode, "keyup",_onInputKeyUp);
			YuEvent.addListener(oNode, "keydown",_onInputKeyDown);
		}
		_Class.inFocus = true;
	};

	YuEvent.addListener(oNode, 'focus', _onInputFocus);

	_Class.objDataSource.onDataReturn = function(arrData, searchKey){
		if(_Class.bLazyMode && _Class.onDataReturned){ _Class.onDataReturned(arrData, searchKey); }
//		if(arrData && arrData.constructor==Array && arrData.length>0){
			_returnData(arrData, searchKey);
//		}
	};

	// store variables for later clean up!
	_Class.oNode = oNode;
	_Class.oNodeList = oNodeList;

	_Class._onInputFocus = _onInputFocus;
	_Class._onInputBlur = _onInputBlur;
	_Class._showData = _showData;
	_Class._clearData = _clearData;
	_Class._getData = _getData;
	_Class._tdSelect = _tdSelect;
	_Class._tdHover = _tdHover;
	_Class._onInputKeyDown = _onInputKeyDown;
	_Class._onInputKeyUp = _onInputKeyUp;
	_Class.suggest = suggest;
	_Class.encodeItem = encodeItem;
	_Class.decodeItem = decodeItem;
};


AutoComplete2.prototype.trim=function(sString){return sString.replace(/(^\s+)|\s+$/g,"");}
AutoComplete2.prototype.htmlEncode = function(str) {
	str = str.replace(/&/ig, "&amp;");
	str = str.replace(/</ig, "&lt;");
	str = str.replace(/>/ig, "&gt;");
	str = str.replace(/\x22/ig, "&quot;");
	str = str.replace(/\xA0/gi,"&nbsp;");
	str = str.replace(/\xC6/g,"&#198;");
	str = str.replace(/\xE6/g,"&#230;");
	str = str.replace(/\xD8/g,"&#216;");
	str = str.replace(/\xF8/g,"&#248;");
	str = str.replace(/\xC5/g,"&#197;");
	str = str.replace(/\xE5/g,"&#229;");
	return str;
};

AutoComplete2.prototype.htmlDecode = function(str) {
	str = str.replace(/&amp;/ig, "&");
	str = str.replace(/&lt;/ig, "<");
	str = str.replace(/&gt;/ig, ">");
	str = str.replace(/&quot;/ig, "x22");
	str = str.replace(/&nbsp;/gi,"&xA0");
	str = str.replace(/&#198;/g,"xC6");
	str = str.replace(/&#230;/g,"xE6");
	str = str.replace(/&#216;/g,"xD8");
	str = str.replace(/&#248;/g,"xF8");
	str = str.replace(/&#197;/g,"xC5");
	str = str.replace(/&#229;/g,"xE5");
	return str;
};

AutoComplete2.prototype.deactivate = function(_Class) {
	_Class = _Class || this;
	this.destruct(_Class);
};

AutoComplete2.prototype.destruct = function(_Class) {
	YAHOO.log(this + '.destruct');
	_Class = _Class || this;

	// remove bindings for suggestions, if any
	if(_Class.oNodeList){
		var tds = _Class.oNodeList.getElementsByTagName("td");
		for(var i=0;i<tds.length;i++)
		{
//			YuEvent.removeListener(tds[i], "mouseover",_Class._tdHover);
//			YuEvent.removeListener(tds[i], "mousedown",_Class._tdSelect);
		};
		tds = null;
	}
	// remove text-input binding
	YuEvent.removeListener(_Class.oNode, 'focus', _Class._onInputFocus);
	YuEvent.removeListener(_Class.oNode, 'blur', _Class._onInputBlur);

//	YuEvent.removeListener(_Class.oNode, "keyup",_Class._onInputKeyUp);
//	YuEvent.removeListener(_Class.oNode, "keydown",_Class._onInputKeyDown);

	// remove class variables, this step not be necessary
	_Class._onInputFocus = null;
	_Class._onInputBlur = null;
	_Class._showData = null;
	_Class._clearData = null;
	_Class._getData = null;
	_Class._tdSelect = null;
	_Class._tdHover = null;
	_Class._onInputKeyDown = null;
	_Class._onInputKeyUp = null;

	_Class.tSetListPosition = null;
	_Class.tGetData = null;
	_Class.tBlur = null;
	_Class.sKeyValue = null;

	_Class.oNode = null;
	_Class.oNodeList.selectedTD = null;
	rn(_Class.oNodeList);
	_Class.oNodeList = null;
	_Class.objDataSource = null;
	_Class.suggest = null;

	_Class.onSelect = null;
	_Class.encodeItem = null;
	_Class.decodeItem = null;
	_Class.onDataReturned = null;

	_Class = null;
}

AutoComplete2.prototype.toString = function(){
	this._className = this._className || 'AutoComplete2';
	return this._className;
}

////////////////////////////////////////////////////////////////////////////////////////////
// auto-complete datasource
function ACDataSource(){
};
ACDataSource.prototype.constructor = ACDataSource;
// override this method to provide getData implementation
// return array of string
// responsibilities:
//   1. get data
//   2. call onDataReturn in some way: either sync or async
// this function should be implemented by data provider
ACDataSource.prototype.getData = function(searchKey){
	YAHOO.log('Error: ACDataSource.getData has been implemented!');
};
// override the following method to consume data
// param: array of string
// this function should be implemented by data consumer
ACDataSource.prototype.onDataReturn = function(arrData){
	YAHOO.log('Error: ACDataSource.onDataReturn has been implemented!');
};
////////////////////////////////////////////////////////////////////////////////////////////

//function CachedDataSource(){
//	this.strKey = '';
//	this.arrCache = [];
//};
//CachedDataSource.prototype = new ACDataSource;
//CachedDataSource.prototype.constructor = CachedDataSource;
//CachedDataSource.prototype.getData = function(searchKey){
//	searchKey = searchKey.toLowerCase();
//	if(this.strKey.indexOf(searchKey)>=0){}
//	else{}
//};

////////////////////////////////////////////////////////////////////////////////////////////
// class definition for tag auto completion
//function TagAutoComplete(sInputId){
//	this.base = AutoComplete2;
//
//	// construct a data source
//	var ds = new ACDataSource();
//	ds.getData = function(searchKey){
//		//var tokens = searchKey.split(',');
//		//var tok = tokens[tokens.length-1];		
//		var callback = {
//			success: function(o) {
//				var arrData = JSON.parse(o.responseText);
//				ds.onDataReturn(arrData, searchKey);
//			},
//			failure: function() {void(0);}
//		}
//		var sUrl = '/autocomplete/tagAutoCompletion?key='+escape(searchKey);
//		var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback); 
//	};
//
//	this.base.call(this, sInputId, ds);
//	sInputId = null;
//};
//TagAutoComplete.prototype = new AutoComplete2;
//TagAutoComplete.prototype.constructor = TagAutoComplete;
//
////////////////////////////////////////////////////////////////////////////////////////////
// class definition for tag auto completion
function TagAutoComplete(sInputId){
	this.base = AutoComplete2;
	var regex = /]|\[/;
	// construct a data source
	var ds = new ACDataSource();
	ds.getData = function(searchKey){
		if (searchKey.charAt(0)=='[') {
			var tokens = searchKey.split(regex);
			var tok = tokens[tokens.length-1];
		} else {
			var tok = searchKey;
		}
		var callback = {
			success: function(o) {
				var arrData = JSON.parse(o.responseText);
				ds.onDataReturn(arrData, searchKey);
			},
			failure: function() {void(0);}
		}
		var sUrl = '/autocomplete/tagAutoCompletion.php?key='+escape(tok);
		var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback); 
	};

	this.base.call(this, sInputId, ds, regex);
	sInputId = null;
};
TagAutoComplete.prototype = new AutoComplete2;
TagAutoComplete.prototype.constructor = TagAutoComplete;

////////////////////////////////////////////////////////////////////////////////////////////
