// $Id: $
// AutoComplete, 绑定一个输入框自动完成, 底下有一个下拉选框及后方增加一个小三角形
// 采用 ajax + dom 分析处理
//

// 参数1: 被绑定的输入元素 (只能是input且为text类型?)
// 参数2: 发起查询数据来源的Url格式, 会自动将绑定元素的值追加到后头！！
//        http://x.y.z/getac.php?kw= || function
var AllAcList = new Array();

// add init function into onload
function AllAcInit()
{
	for (var i = 0; i < AllAcList.length; i++)
	{
		AllAcList[i].Init();
	}
}

// add Init caller by ie & fireforx
if (typeof window.attachEvent != 'undefined')
{
	window.attachEvent('onload', AllAcInit);
}
else if (typeof window.addEventListener != 'undefined')
{
	window.addEventListener('load', AllAcInit, false);
}

// Start the object prototype declare
function AutoComplete(bObject, qArg, aSubmit)
{
	// check the binded object
	if (!bObject.tagName || bObject.tagName != 'INPUT' || bObject.type != 'text')
		return null;
	
	// variables
	// put into the AcList
	this.aIndex = AllAcList.length;
	AllAcList[this.aIndex] = this;

	this.bObject = bObject;
	this.qUrl = null;
	this.qFunc = null;
	if (typeof qArg != 'undefined')
	{
		if (typeof qArg == 'string')
			this.qUrl = qArg;
		else
			this.qFunc = qArg;
	}

	// other vars
	this.oDiv = null;	// div for selection
	this.oValue = null;
	this.ajax = null;
	this.onDiv = false;
	this.aSubmit = (aSubmit ? true : false);
	this.Timeout = false;
	this.oFrame = null;
    this.actDivIdx = -1;
}

// 初始化操作, 绑定三角形, 设定好 div layer
AutoComplete.prototype.Init = function()
{
	var me = this;
	
	// count the height
	me.height = Math.round(me.bObject.offsetWidth * 0.6);

	// create the div layer
	me.oDiv = document.createElement('DIV');
	me.oDiv.style.border = '1px solid #000000';
	me.oDiv.style.zIndex = '99';
	me.oDiv.style.padding = '0px';
	me.oDiv.style.display = 'none';
	me.oDiv.style.position = 'absolute';
	me.oDiv.style.backgroundColor = '#ffffff';
	me.oDiv.style.fontSize = '12px'
	me.oDiv.style.fontFamily = 'tahoma';
	me.oDiv.style.lineHeight = '160%';

	// intend
	if (me.bObject.style.backgroundColor != '')
		me.oDiv.style.backgroundColor = me.bObject.style.backgroundColor;	
	if (me.bObject.style.fontSize != '')
		me.oDiv.style.fontSize = me.bObject.style.fontSize;	
	if (me.bObject.style.color != '')
		me.oDiv.style.color = me.bObject.style.color;
	if (me.bObject.style.borderColor != '')
		me.oDiv.style.borderColor = me.bObject.style.borderColor;	

	// position: left | top | width
	me.AutoPos();
	me.oDiv.style.width =  (me.bObject.offsetWidth /* + me.bObject.offsetHeight */ ) + 'px';
	me.oDiv.style.height = me.height + 'px';
	me.oDiv.style.overflowY = 'auto';
	me.oDiv.style.overflowX = 'hidden';
	document.body.appendChild(me.oDiv);

	me.oDiv.onmouseover = function () { me.onDiv = true; }
	me.oDiv.onmouseout = function () { me.onDiv = false; }

	// create the show 'button' & insert after the input field
	/**
	var button = document.createElement('INPUT');
	button.value = '▼';
	button.style.fontSize = '10px';
	button.style.height = me.bObject.offsetHeight + 'px';
	button.style.width = me.bObject.offsetHeight + 'px';
	button.type = 'button';
	button.style.border = '1px solid #456';
	button.onclick = function()
	{
		this.blur();
		if (me.oDiv.style.display == 'none')
		{
			// expand
			me.Reload();
		}
		else
		{
			// hide
			me.HideDiv();
		}
	}

	var pnode = me.bObject.parentNode;
	for (var i = 0; i < pnode.childNodes.length; i++)
	{
		if (pnode.childNodes[i] == me.bObject)
			break;
	}
	i++;
	if (i == pnode.childNodes.length)
	{
		pnode.appendChild(buttoon);
	}
	else
	{
		pnode.insertBefore(button, pnode.childNodes[i]);
	}
	**/

	// create a hidden iframe
	if (me.qUrl != null && me.qUrl.substring(0,7) == 'http://')
	{
		me.oFrame = document.createElement('IFRAME');
		me.oFrame.style.display = 'none';
		me.oFrame.width = '100%';
		me.oFrame.height = '60';
		me.oFrame.src = 'about:blank';
		document.body.appendChild(me.oFrame);
	}

	// turn off the autocomplete from by web browse
	// & bind the some event for keyboard
	me.bObject.autocomplete = 'off';
	me.bObject.onblur = function ()
	{
		// hide
		if (!me.onDiv) { me.HideDiv(); }
	};
	me.bObject.onkeyup = function (e)
	{
		// reload the data from AJAX server or by callbackfunction?
		var ev = e || event;
		var oDiv = me.oDiv;

		// just enter, skip
		if (ev.keyCode == 13)
		{
			if (me.actDivIdx >= 0)
			{
				me.bObject.value = oDiv.childNodes[me.actDivIdx].innerHTML;
				me.HideDiv();
				
				if (me.aSubmit && me.bObject.form)
				{
					me.aSubmit.disabled = true;
					try
					{
						me.bObject.form.submit();
					}
					catch (err)
					{
						me.bObject.form.submit.click();
					}
				}
			}
			return;
        }

		// Reload the content
		me.Reload();

        // check the UP&DOWN&ENTER keyCode
        if ((ev.keyCode == 40 || ev.keyCode == 38) && oDiv.childNodes.length > 0)
        {
			if (me.actDivIdx >= 0 && me.actDivIdx < oDiv.childNodes.length)
			{
				oDiv.childNodes[me.actDivIdx].style.backgroundColor = '#ffffff';
				oDiv.childNodes[me.actDivIdx].style.color = '#000000';
			}

			if (ev.keyCode == 40) me.actDivIdx++;
			else me.actDivIdx--;

			if (me.actDivIdx < 0) me.actDivIdx = oDiv.childNodes.length - 1;
			if (me.actDivIdx >= oDiv.childNodes.length) me.actDivIdx = 0;	

			oDiv.childNodes[me.actDivIdx].style.backgroundColor = '#3366cc';
			oDiv.childNodes[me.actDivIdx].style.color = '#ffffff';

			// count the height
			var height_all = parseInt(oDiv.style.height);
			var offset_me = oDiv.childNodes[me.actDivIdx].offsetHeight * me.actDivIdx;
			if ((offset_me - oDiv.scrollTop) > height_all)
				oDiv.scrollTop = offset_me;
			if ((offset_me - oDiv.scrollTop) < 0)
				oDiv.scrollTop = 0;			
        }

	}
	me.bObject.focus();

	// on resize?
	window.onresize = function () { me.AutoPos(); }
}

// reset the pos
AutoComplete.prototype.AutoPos = function()
{
	var me = this;
	var tt = getOffset(me.bObject, 'offsetTop');
	var ll = getOffset(me.bObject, 'offsetLeft')
	var t0 = document.body.clientHeight + document.body.scrollTop - me.height;
	var t1 = tt + me.bObject.offsetHeight;
	var t2 = tt - me.height;

	// set the top
	if (t1 > t0 && t2 > document.body.scrollTop)
	{
		me.oDiv.style.top = t2 + 'px';
	}
	else
	{
		me.oDiv.style.top = t1 + 'px';
	}

	// set the left
	me.oDiv.style.left = ll + 'px';
}

// show the div
AutoComplete.prototype.ShowDiv = function ()
{
	// count the top, 必要时往上面显示
	if (this.oDiv.style.display == '')
		return;
	this.AutoPos();
	this.oDiv.style.display = '';
}

// hide the div
AutoComplete.prototype.HideDiv = function ()
{
	this.oDiv.style.display = 'none';
}

// reload
AutoComplete.prototype.Reload = function ()
{
	var ca = strTrim(this.bObject.value);
	//this.ShowDiv();

	if (ca != this.oValue)
	{
		var callee = 'AllAcList[' + this.aIndex + '].GetData()';
		this.oValue = ca;
		//this.SetData('Loading...');
		if (this.Timeout) clearTimeout(this.Timeout);
		this.Timeout = window.setTimeout(callee, 200);
	}
}

// getData
AutoComplete.prototype.GetData = function()
{
	// clear the timeout setting
	this.Timeout = false;

	// disabled form?
	if (typeof this.bObject.form.submit != 'undefined' && this.bObject.form.submit.disabled)
		return;

	// get the data by function callback
	if (this.qFunc != null)
	{
		var buf = this.qFunc(this.oValue);
		this.SetData(buf);
		return;
	}

	// set the url
	if (this.qUrl == null) return;

	// save the query value into cookie
	//document.cookie = 'acArgument=' + escape(this.oValue) + ';';

	// remote url call by frame
	if (this.qUrl.substring(0,7) == 'http://')
	{
		var url = this.qUrl + urlEncode(this.oValue) + '&ac=';
		url += urlEncode('AllAcList[' + this.aIndex + ']');
		this.oFrame.src = url;
		return;
	}

	// empty value?
	if (this.oValue == '')
	{
		this.HideDiv();
		return;
	}

	// local url callby ajax
	if (this.ajax == null)
	{

		var x = null;
		if (typeof XMLHttpRequest != 'undefined')	
			x = new XMLHttpRequest();
		else
		{
			try { x = new ActiveXObject("Msxml1.XMLHTTP"); }
			catch (e)
			{	
				try { x = new ActiveXObject("Microsoft.XMLHTTP"); }
				catch (e) { x = false; }
			}
		}

		if (!x) return;
		this.ajax = x;
	}

	var me = this;
	var url = me.qUrl + urlEncode(me.oValue);
	var x = me.ajax;
	x.open('GET', url, true);
	//x.setRequestHeader('Accept-Encoding', '');	
	//x.setRequestHeader('If-Modified-Since', '0');
	x.onreadystatechange = function ()
	{
		if (x.readyState == 4 && x.status == 200)
		{
			me.SetData(x.responseText);
		}
	}
	x.send(null);
}

// setdata
AutoComplete.prototype.SetData = function(buf)
{
	if (typeof buf != 'string')
		return;	

	// clean the old data
	while (this.oDiv.childNodes.length > 0)
		this.oDiv.removeChild(this.oDiv.childNodes[0]);

	buf = strTrim(buf);
	if (buf == '')
	{
		this.HideDiv();
		return;
	}
	
	// parse the data
	var lines = buf.split('\n');
	var num = lines.length;
	var j = 0;
	for (var i = 0; i < num; i++)
	{
		var line = strTrim(lines[i]);
		if (line == '' || line.substring(0,2) == '//') continue;
		this.Additem(line, j++);
	}

	// show the div
	this.ShowDiv();
}

// additem
AutoComplete.prototype.Additem = function(str, j)
{
	var me = this;
	var item = document.createElement('DIV');
	item.style.paddingLeft = '4px';
	item.innerHTML = str;	
	me.oDiv.appendChild(item);

	if (str == 'Loading...') return;
	item.onmouseover = function ()
	{
		this.style.backgroundColor = '#3366cc';
		this.style.color = '#ffffff';
		if (me.actDivIdx >= 0 && me.actDivIdx < me.oDiv.childNodes.length)
		{
			me.oDiv.childNodes[me.actDivIdx].style.backgroundColor = '#ffffff';
			me.oDiv.childNodes[me.actDivIdx].style.color = '#000000';
		}
		me.actDivIdx = j;
	}

	item.onmousedown = function ()
	{
		me.bObject.value = this.innerHTML;
		me.HideDiv();

		if (me.aSubmit && me.bObject.form)
		{
			try
			{
				me.bObject.form.submit();
			}
			catch (err)
			{
				me.bObject.form.submit.click();
			}
		}
	};
}

// 其它一些共公小函数
function urlEncode(val)
{
	//return val.replace(/[\u0000-\u00FF]/g, function($0) { return escape($0); });	
	return encodeURIComponent(val);	       
}

// get offset
function getOffset(o,n)
{
	var w = 0;
	do { w = w + o[n]; } while (o = o.offsetParent);	
	return w;
}

// trim for string
function strTrim(str)
{
	return str.replace(/(^\s*)|(\s*$)/g, '');
}
