// Removes leading whitespaces
function LTrim( value ) {
	var re = /\s*((\S+\s*)*)/;
	return value.replace(re, "$1");
}

// Removes ending whitespaces
function RTrim( value ) {
	var re = /((\s*\S+)*)\s*/;
	return value.replace(re, "$1");
}

// Removes leading and ending whitespaces
function trim( value ) {
	return LTrim(RTrim(value));
}

function AutoCompleteDB()
{
	// set the initial values.
	this.bEnd = false;
	this.nCount = 0;
	this.aStr = new Object;
	this.numbers = new Array();
}
/*
AutoCompleteDB.prototype.add = function(str)
{
	// increment the count value.
	this.nCount++;

	// if at the end of the string, flag this node as an end point.
	if ( str == "" )
		this.bEnd = true;
	else
	{
		// otherwise, pull the first letter off the string
		var letter = str.substring(0,1);
		var rest = str.substring(1,str.length);
		
		// and either create a child node for it or reuse an old one.
		if ( !this.aStr[letter] ) this.aStr[letter] = new AutoCompleteDB();
		this.aStr[letter].add(rest);
	}
}
*/
AutoCompleteDB.prototype.add2 = function(idx, keyword)
{
	// increment the count value.
	this.nCount++;

	// if at the end of the string, flag this node as an end point.
	if ( keyword == "" )
	{
		this.bEnd = true;
		this.numbers.push(idx);
	}
	else
	{
		// otherwise, pull the first letter off the string
		var letter = keyword.substring(0,1);
		var rest = keyword.substring(1,keyword.length);
		
		// and either create a child node for it or reuse an old one.
		if ( !this.aStr[letter] ) this.aStr[letter] = new AutoCompleteDB();
		this.aStr[letter].add2(idx, rest);
	}
}

AutoCompleteDB.prototype.getCount = function(str, bExact)
{
	// if end of search string, return number
	if ( str == "" )
		if ( this.bEnd && bExact && (this.nCount == 1) ) return 0;
		else return this.nCount;
	
	// otherwise, pull the first letter off the string
	var letter = str.substring(0,1);
	var rest = str.substring(1,str.length);
	
	// and look for case-insensitive matches
	var nCount = 0;
	var lLetter = letter.toLowerCase();
	if ( this.aStr[lLetter] )
		nCount += this.aStr[lLetter].getCount(rest, bExact && (letter == lLetter));
	
	var uLetter = letter.toUpperCase();
	if ( this.aStr[uLetter] )
		nCount += this.aStr[uLetter].getCount(rest, bExact && (letter == uLetter));
	
	return nCount;	
}
/*
AutoCompleteDB.prototype.getStrings = function(str1, str2, outStr)
{
	if ( str1 == "" )
	{
		// add matching strings to the array
		if ( this.bEnd ) 
			outStr.push(str2);

		// get strings for each child node
		for ( var i in this.aStr )
			this.aStr[i].getStrings(str1, str2 + i, outStr);
	}
	else
	{
		// pull the first letter off the string
		var letter = str1.substring(0,1);
		var rest = str1.substring(1,str1.length);
		
		// and get the case-insensitive matches.
		var lLetter = letter.toLowerCase();
		if ( this.aStr[lLetter] )
			this.aStr[lLetter].getStrings(rest, str2 + lLetter, outStr);

		var uLetter = letter.toUpperCase();
		if ( this.aStr[uLetter] )
			this.aStr[uLetter].getStrings(rest, str2 + uLetter, outStr);
	}
}
*/
AutoCompleteDB.prototype.getStrings2 = function(str1, str2, outIndexes)
{
	if ( str1 == "" )
	{
		// add matching strings to the array
		if ( this.bEnd ) 
		{
			for (var i in this.numbers)
			{
				var found = false;
				for (var c in outIndexes)
				{
					if (outIndexes[c] == this.numbers[i])
					{
						found = true;
						break;
					}
				}
				if (!found)
					outIndexes.push(this.numbers[i]);
			}
			//outStr.push(str2);
		}

		// get strings for each child node
		for ( var i in this.aStr )
			this.aStr[i].getStrings2(str1, str2 + i, outIndexes);
	}
	else
	{
		// pull the first letter off the string
		var letter = str1.substring(0,1);
		var rest = str1.substring(1,str1.length);
		
		// and get the case-insensitive matches.
		var lLetter = letter.toLowerCase();
		if ( this.aStr[lLetter] )
			this.aStr[lLetter].getStrings2(rest, str2 + lLetter, outIndexes);

//		var uLetter = letter.toUpperCase();
//		if ( this.aStr[uLetter] )
//			this.aStr[uLetter].getStrings2(rest, str2 + uLetter, outIndexes);
	}
}


function AutoComplete(aStr, oText, oDiv, nMaxSize, aurl)
{
	// initialize member variables
	this.aText = aStr;
	this.oText = oText;
	this.oDiv = oDiv;
	this.nMaxSize = nMaxSize;
	this.aurl = aurl;
	
	if (this.aurl == "")
	{
		// preprocess the texts for fast access
		this.db = new AutoCompleteDB();
		for (var i in aStr)
		{
			var aWords = trim(aStr[i]).split(" ");
			for (var j in aWords)
			{
				aWords[j] = trim(aWords[j]).toLowerCase();
				if (aWords[j] != '')
					this.db.add2(i, aWords[j]);
			}
		}
	}
	// attach handlers to the text-box
	oText.AutoComplete = this;
	oText.onkeyup = AutoComplete.prototype.onTextChange;
	oText.onblur = AutoComplete.prototype.onTextBlur;
}

AutoComplete.prototype.onTextBlur = function()
{
	this.AutoComplete.onblur();
}

AutoComplete.prototype.onblur = function()
{
	this.oDiv.style.visibility = "hidden";
}

AutoComplete.prototype.onTextChange = function()
{
	this.AutoComplete.onchange();
}

AutoComplete.prototype.onDivMouseDown = function()
{
	if (this.AutoComplete.aurl == "")
	{
		this.AutoComplete.oText.value = this.AutoComplete.aText[this.atindex];
	}
	else
	{
		this.AutoComplete.oText.value = this.AutoComplete.aStr[this.atindex];
	}
}

AutoComplete.prototype.onDivMouseOver = function()
{
	this.className = "AutoCompleteHighlight";
}

AutoComplete.prototype.onDivMouseOut = function()
{
	this.className = "AutoCompleteBackground";
}

AutoComplete.prototype.onchange = function()
{
	var txt = this.oText.value;
	var aStr = [];
	if (this.aurl != "")
	{
		if (txt != "" && txt != null)
		{
			var list = synchronousXMLHttpRequest(this.aurl + '?domain=printer&varname=aStr&search=' + txt);
			list = getlastelement(list);
			eval(list);
			this.aStr = aStr;
			this.nMaxSize = -1;
		}
	}
	else
	{
		// get all the matching strings from the AutoCompleteDB
		var aIndexes = new Array();
		var aWords = trim(txt).split(" ");
		var j, m = aWords.length;
		for (j in aWords)
		{
			aWords[j] = trim(aWords[j]);
			if (aWords[j] != '')
				this.db.getStrings2(trim(aWords[j]).toLowerCase(), "", aIndexes);
		}
		//this.db.getStrings2(txt, "", aStr);
	
		// count the number of strings that match the text-box value
		var aStr = new Array();
		for (var c in aIndexes)
		{
			aStr[c] = [trim(this.aText[aIndexes[c]]), aIndexes[c]];
		}
	}
	var nCount = aStr.length;
	// if a suitable number then show the popup-div
	if ( (this.nMaxSize == -1 ) || ((nCount < this.nMaxSize) && (nCount > 0)) )
	{
		// clear the popup-div.
		while ( this.oDiv.hasChildNodes() )
			this.oDiv.removeChild(this.oDiv.firstChild);
			
		// add each string to the popup-div
		var i, n = aStr.length;
		for ( i = 0; i < n; i++ )
		{
			var oDiv = document.createElement('div');
			this.oDiv.appendChild(oDiv);
			if (this.aurl != "")
			{
				oDiv.innerHTML = aStr[i];
				oDiv.atindex = i;
			}
			else
			{
				oDiv.innerHTML = aStr[i][0];
				oDiv.atindex = aStr[i][1];
			}
			oDiv.onmousedown = AutoComplete.prototype.onDivMouseDown;
			oDiv.onmouseover = AutoComplete.prototype.onDivMouseOver;
			oDiv.onmouseout = AutoComplete.prototype.onDivMouseOut;
			oDiv.AutoComplete = this;			
		}
		this.oDiv.style.top = elementTop(this.oText)+'px';
		this.oDiv.style.left = elementLeft(this.oText)+'px';
		this.oDiv.style.width = this.oText.scrollWidth+'px';
		this.oDiv.style.visibility = "visible";
	}
	else // hide the popup-div
	{
		this.oDiv.innerHTML = "";
//		this.oDiv.style.height = this.oDiv.style.width = '0px';
		this.oDiv.style.visibility = "hidden";
	}
}

function createAutoComplete(aData, textId, choiceId, maxMatches, aurl)
{
	new AutoComplete(
		aData, 
		document.getElementById(textId), 
		document.getElementById(choiceId), 
		maxMatches,
		aurl
	);
}

