function suggest(obj_target, result_id, do_get_function)
{
    var self = this;

    this.target = null; //input box
    this.service = '';
    this.doGetFunction = do_get_function;
    this.unselectedClass = 'sr';
    this.selectedClass = 'srs';
    this.noCitiesMsg = "No Airports - Please backspace";
    this.holdUnselectedClas = false;
    this.ALTunselectedClass = 'airtran';
    this.parentObj = false;
    this.childObj = false;
    this.threshold = false;
    this.overLapObj = false;
    this.showOverLapObj = false;
    this.overLapObjType = "block";
    this.waiteTime = 230; //milliseconds
    this.tp = 0;
    this.maxResultCount = 15;
    this.format = 'text';
    this.lastReq = '';
    this.show = false;
    this.relation = false;
    this.selectedNode = false;
    this.selectedEle = false;
    this.resultDiv = document.getElementById(result_id); //div results are displayed in
    if(!this.resultDiv)alert("Invalid Element id");

	this.resultDiv['suggest'] = this;

    // validate input parameters
    if (!obj_target)			      return false;
    if (obj_target.value == null)     return false;

    this.target = obj_target;
	obj_target['suggest'] = this;

    this.handleReq			= SuggestHandleReq;
    this.getCodeFromName	= SuggestGetCodeFromName;
    this.blurEvent			= SuggestBlurEvent;
    this.selectDown			= SuggestSelectDown;
    this.selectUp			= SuggestSelectUp;
    this.moveSelection		= SuggestMoveSelection;
    this.startTimer			= SuggestStartTimer;
    this.resetTimer			= SuggestResetTimer;
    this.getkeycode			= SuggestGetkeycode;
    this.makeReq			= SuggestMakeReq;
    this.printResultJsArray = SuggestPrintResultJsArray;
    this.selectAndHide		= SuggestSelectAndHide;
    this.getTargetVal		= SuggestGetTargetVal;
    this.mouseOver			= SuggestMouseOver;
    this.mouseClick			= SuggestMouseClick;
    this.mouseOut			= SuggestMouseOut;
    this.highlightNode		= SuggestHighlightNode;
    this.getFirstChild		= SuggestGetFirstChild;
    this.clearSelected		= SuggestClearSelected;
    this.getElementTrigger	= SuggestGetElementTrigger;
    this.showResultDiv		= SuggestShowResultDiv;
    this.hideResultDiv		= SuggestHideResultDiv;
    this.trim				= SuggestTrim;
    this.setChildObj		= SuggestSetChildObj;
    this.setParentObj		= SuggestSetParentObj;

    this.target.onkeydown	= SuggestOnkeydown;

	this.onblurChainto		= this.target.onblur;
    this.target.onblur		= this.blurEvent;

    if (this.target.value != '') this.relation = this.getCodeFromName(this.target.value);
}

// -----------------------------------

function SuggestHandleReq(self, str)
{
	if (str != '')
	{
		eval('var resp =' + str);
		self.printResultJsArray(self, resp);
	}
}

function SuggestSetChildObj(self, child)
{
	self.childObj = child;
}

function SuggestSetParentObj(self, parent)
{
	self.parentObj= parent;
}

function SuggestGetCodeFromName(val)
{
	var reg = /\(([a-z]{3})\)/i;
	var arr = reg.exec(val);
	if (!arr)
	{	
		reg = /([\w\s\'\(\)]{3,10})/i;
		arr = reg.exec(val);
	}
	return (arr ? arr[1] : false);
}

function SuggestOnkeydown(e)
{
	var self = this['suggest'];
	var key = self.getkeycode(e);
	switch (key) {
//		case 9: //tab
//				if ( self.selectedNode )
//				{
//					listEle = self.getFirstChild(self.resultDiv);
//					self.selectAndHide(self, listEle.childNodes[self.selectedNode]);
//				}
//				self.target.onSuggestion(self.target.value);
//				return true;
//				break;
		case 13: //enter
				if ( self.selectedNode )
				{
					listEle = self.getFirstChild(self.resultDiv);
					self.selectAndHide(self, listEle.childNodes[self.selectedNode]);
				}
				else
				{
					self.target.onSuggestion('')
					self.hideResultDiv(self);
				}

				return false;
				break;
		case 27: // escape
				if ( self.selectedNode )
				{
					listEle = self.getFirstChild(self.resultDiv);
					self.selectAndHide(self, listEle.childNodes[self.selectedNode]);
				}
				else
				{
					self.target.onSuggestion('')
					self.hideResultDiv(self);
				}

				return false;
				break;
		case 38: // up arrow
				self.selectUp(self);
				return false;
				break;
		case 40: // down arrow
				self.selectDown(self);
				return false;
				break;
		default:
				if(key != 9 && key != 16 && key != 18)
				{
						self.startTimer(self, function(){SuggestMakeReq(self)}, self.waiteTime);
						self.relation = false;
				}
	}
	return true;
}

function SuggestBlurEvent(e)
{
	var self = this['suggest'];
	if ( self )
	{
		ele = self.getElementTrigger(e);

		self.resetTimer(self);

		if (self.resultDiv.style.display == 'block')
		{
			listEle = self.getFirstChild(self.resultDiv);
			if (listEle)
			{
				if (listEle.childNodes[self.selectedNode])
				{
					city = self.trim(listEle.childNodes[self.selectedNode].childNodes[0].innerHTML);
					if (city != self.noCitiesMsg) { ele.value = city; ele.onSuggestion(city); }
				}
				else if (self.selectedEle)
				{
					city = self.trim(self.selectedEle.childNodes[0].innerHTML);
					if (city != self.noCitiesMsg) { ele.value = city; ele.onSuggestion(city); }
				}
			}
		}

		self.hideResultDiv(self);
		if (self.relation == false)
		{
			if (ele.value.length > 2) self.relation = self.getCodeFromName(ele.value);
			else					  self.relation = false;
		}

		self.onblurChainto();
	}
}

function SuggestSelectDown(self)
{
	self.clearSelected(self);
	self.moveSelection(self, "down");
}

function SuggestSelectUp(self)
{
	self.clearSelected(self);
	self.moveSelection(self, "up");
}

function SuggestMoveSelection(self, dir)
{
	var listEle = self.getFirstChild(self.resultDiv);
	if (listEle)
	{
		if (dir == "up") m = -1;
		if (dir == "down") m = 1
		if (self.selectedNode + m >= listEle.childNodes.length || self.selectedNode + m < 0) m = 0;
		self.selectedNode = self.selectedNode + m;
		self.holdUnselectedClass = listEle.childNodes[self.selectedNode].className;
		listEle.childNodes[self.selectedNode].className = self.selectedClass;
		var city = listEle.childNodes[self.selectedNode].childNodes[0].innerHTML;
		//self.target.value = city;
		self.relation = self.getCodeFromName(city);
	}
}

function SuggestStartTimer(self, cmd, ms)
{
	if (self.tp > 0) self.resetTimer(self);
	self.tp = window.setTimeout(cmd, ms);
}

function SuggestResetTimer(self)
{
	if (self.tp > 0) window.clearTimeout(self.tp);
	self.tp = 0;
}

function SuggestGetkeycode(e)
{
	if (document.layers) return e.which;
	if (document.all) return event.keyCode;
	if (document.getElementById) return e.keyCode;
	return 0;
}

function SuggestMakeReq(self)
{
	var childField  = false;
	var parentField = false;
    if (self.target.value == '')
    {
    	self.hideResultDiv(self);
    	return false;
    }
	//if (self.target.value == self.lastReq) return false;
	if (self.childObj != false)
		if(self.childObj.target.value != '')
			childField = self.childObj.relation;

	if (self.parentObj != false)
		if (self.parentObj.target.value != '')
			parentField = self.parentObj.relation;

	self.lastReq = self.target.value;
	var url =	self.service + '?mode=query'
		+ "&count=" + self.maxResultCount
		+ "&format=" + self.format 
		+ (parentField ? "&parent=" + encodeURI(parentField) : '')
		+ (childField  ? "&child="  + encodeURI(childField)  : '')
		+ '&q=' + self.target.value;
	//alert(url);
	self.doGetFunction(url, self.handleReq, self.format, self);
}

function SuggestPrintResultJsArray(self, resArr)
{
	if(resArr.length > 0)
	{
		// alert(resArr);
		self.holdUnselectedClass = self.unselectedClass;
		var list = '<ul class="suggestResults" >';
		for(var i in resArr)
		{
			// code = resArr[i];
			// alert(code);
			var sr = self.unselectedClass;
			if(resArr[i].match(/\+$/))
			{
				var len = resArr[i].length - 1;
				resArr[i] = resArr[i].substr(0, len);
				sr = self.ALTunselectedClass;
				if(i == 0)
				{
					self.holdUnselectedClass = sr;
				}
			}

			list += '<li class="' + (i == 0 ? self.selectedClass : sr ) //srs and sr are references to css classes.
				+'" onmouseover="SuggestMouseOver(this,event);return true"'
				//+'onmouseout="SuggestMouseOut(this,event);return true" '
				+'onmousedown="SuggestMouseClick(this,event);return true"><span>'
				+ resArr[i] + '</span> ' 
				+ '</li>';
		}

		list += "</ul>";
		self.selectedNode = 0;
		self.resultDiv.innerHTML = list;
		self.showResultDiv(self,resArr);
	}
	else
	{
		self.hideResultDiv(self);
	}
}

function SuggestSelectAndHide(self, ele)
{
	var city;
	try
	{
		city = self.trim(self.getTargetVal(ele));	
	}
	catch (e)
	{
		try
		{
			city = self.trim(self.selectedEle.childNodes[0].innerHTML);
		}
		catch (e)
		{
			//we havent found a valid city entry
		}
	}

	if (city && ( city != self.noCitiesMsg) )
	{
		self.target.value = city;
		self.target.onSuggestion(city)
		self.relation = self.getCodeFromName(city);
	}
	else
	{
		self.target.onSuggestion('')
	}

	self.hideResultDiv(self);
}

function SuggestGetTargetVal(ele)
{
	if (ele)
	{
		if (ele.nodeName != "LI")
		{
			return ele.parentNode.childNodes[0].innerHTML;
		}
		else return ele.childNodes[0].innerHTML;		
	}
	else return null;
}

function SuggestMouseOver(obj, e)
{
	var self = obj.parentNode.parentNode['suggest'];
	var ele = self.getElementTrigger(e);
	self.clearSelected(self);
	self.highlightNode(self, ele, true);
}

function SuggestMouseClick(obj, e)
{
	var self = obj.parentNode.parentNode['suggest'];
	var ele = self.getElementTrigger(e);
	self.selectAndHide(self, ele);
}

function SuggestMouseOut(obj, e)
{
	var self = obj.parentNode.parentNode['suggest'];
	var ele = self.getElementTrigger(e);
	self.highlightNode(self, ele, false);
}

function SuggestHighlightNode(self, ele, hilight)
{
	if (ele.nodeName != "LI") ele = ele.parentNode;
	if (self.selectedEle) self.selectedEle['className'] = self.holdUnselectedClass;
	if (hilight)
	{
		self.holdUnselectedClass = (ele['className'] == self.ALTunselectedClass) ? self.ALTunselectedClass : self.unselectedClass;
		ele['className'] = self.selectedClass;
		self.selectedNode = false;
		self.selectedEle = ele;
	}
	else ele['className'] = self.holdUnselectedClass;
}

function SuggestGetFirstChild(n)
{
	var x=n.firstChild;
	if (x != null)
	{
		while (x.nodeType!=1)
		{
	//alert('a '+x)
			x=x.nextSibling;
	//alert('b '+x)
		}
	}

	//alert(x.nodeName);
	return x;
}

function SuggestClearSelected(self)
{
	var listEle = self.getFirstChild(self.resultDiv);
	if (listEle)
	{
		if(self.selectedNode == false)
		{
			for (var i=0; i<listEle.childNodes.length; i++)
			{
				if (listEle.childNodes[i].nodeName=="LI")
				{
					//alert("LI " + i);
					if(listEle.childNodes[i].className)
					{
						//alert("has class attr " + i);

						if(listEle.childNodes[i].className == self.selectedClass)
						{
							self.selectedNode = i;
							//alert("changing classattr " + i);
							listEle.childNodes[i].className = self.holdUnselectedClass;
						}
					}
				}
			}
		}
		else
		{
			listEle.childNodes[self.selectedNode].className = self.holdUnselectedClass;
		}
	}
}

function SuggestGetElementTrigger(arg_ele)
{
	var ele = arg_ele ? arg_ele : window.event;
	var targ = ele.target ? ele.target : ele.srcElement ? ele.srcElement : null;
	if (targ.nodeType == 3) targ = targ.parentNode;	// defeat Safari bug	

	return targ;
}

function SuggestShowResultDiv(self, results)
{
	self.resultDiv.style.display = 'block';
}

function SuggestHideResultDiv(self)
{
	self.resultDiv.innerHTML = null;
	self.resultDiv.style.display = 'none';
	self.selectedNode = false;
}

function SuggestTrim(st)
{
	return st.replace(/^\s+|\s+$/g,"");
}



function DumpObj(obj, name) { alert(DumpObj2(obj,name,'',0)); }

var MAX_DUMP_DEPTH = 0;
function DumpObj2(obj, name, indent, depth)
{
	if (depth > MAX_DUMP_DEPTH) { return indent + name + ": <object>; "; }
	if (typeof obj == "object")
	{
		var child = null;
		var output = indent + name + "\n";
		//indent += "\t";
		for (var item in obj)
		{
			try				{ child = obj[item];						}
			catch (e)		{ child = "<Unable to Evaluate>";			}

			if (typeof child == "object")			{	output += DumpObj2(child, item, indent, depth + 1);			}
			else if (typeof child == "function")	{	output += indent + item + ": <function>; ";					}
			else									{	output += indent + item + ": " + child + "; ";				}
		}

		return output;
	}
	else { return obj; }
}

