var SuggestionBox = 
{
	MATCH_NONE: 0,
	MATCH_BEGIN: 1,
	MATCH_END: 2,	
	MATCH_WORD_BEGIN: 4,
	MATCH_WORD_END: 8,

	_private_data:
	{
		_accented: 
		{
			'A': '[Aa\xaa\xc0-\xc5\xe0-\xe5\u0100-\u0105\u01cd\u01ce\u0200-\u0203\u0226\u0227\u1d2c\u1d43\u1e00\u1e01\u1e9a\u1ea0-\u1ea3\u2090\u2100\u2101\u213b\u249c\u24b6\u24d0\u3371-\u3374\u3380-\u3384\u3388\u3389\u33a9-\u33af\u33c2\u33ca\u33df\u33ff\uff21\uff41]',
			'B': '[Bb\u1d2e\u1d47\u1e02-\u1e07\u212c\u249d\u24b7\u24d1\u3374\u3385-\u3387\u33c3\u33c8\u33d4\u33dd\uff22\uff42]',
			'C': '[Cc\xc7\xe7\u0106-\u010d\u1d9c\u2100\u2102\u2103\u2105\u2106\u212d\u216d\u217d\u249e\u24b8\u24d2\u3376\u3388\u3389\u339d\u33a0\u33a4\u33c4-\u33c7\uff23\uff43]',
			'D': '[Dd\u010e\u010f\u01c4-\u01c6\u01f1-\u01f3\u1d30\u1d48\u1e0a-\u1e13\u2145\u2146\u216e\u217e\u249f\u24b9\u24d3\u32cf\u3372\u3377-\u3379\u3397\u33ad-\u33af\u33c5\u33c8\uff24\uff44]',
			'E': '[Ee\xc8-\xcb\xe8-\xeb\u0112-\u011b\u0204-\u0207\u0228\u0229\u1d31\u1d49\u1e18-\u1e1b\u1eb8-\u1ebd\u2091\u2121\u212f\u2130\u2147\u24a0\u24ba\u24d4\u3250\u32cd\u32ce\uff25\uff45]',
			'F': '[Ff\u1da0\u1e1e\u1e1f\u2109\u2131\u213b\u24a1\u24bb\u24d5\u338a-\u338c\u3399\ufb00-\ufb04\uff26\uff46]',
			'G': '[Gg\u011c-\u0123\u01e6\u01e7\u01f4\u01f5\u1d33\u1d4d\u1e20\u1e21\u210a\u24a2\u24bc\u24d6\u32cc\u32cd\u3387\u338d-\u338f\u3393\u33ac\u33c6\u33c9\u33d2\u33ff\uff27\uff47]',
			'H': '[Hh\u0124\u0125\u021e\u021f\u02b0\u1d34\u1e22-\u1e2b\u1e96\u210b-\u210e\u24a3\u24bd\u24d7\u32cc\u3371\u3390-\u3394\u33ca\u33cb\u33d7\uff28\uff48]',
			'I': '[Ii\xcc-\xcf\xec-\xef\u0128-\u0130\u0132\u0133\u01cf\u01d0\u0208-\u020b\u1d35\u1d62\u1e2c\u1e2d\u1ec8-\u1ecb\u2071\u2110\u2111\u2139\u2148\u2160-\u2163\u2165-\u2168\u216a\u216b\u2170-\u2173\u2175-\u2178\u217a\u217b\u24a4\u24be\u24d8\u337a\u33cc\u33d5\ufb01\ufb03\uff29\uff49]',
			'J': '[Jj\u0132-\u0135\u01c7-\u01cc\u01f0\u02b2\u1d36\u2149\u24a5\u24bf\u24d9\u2c7c\uff2a\uff4a]',
			'K': '[Kk\u0136\u0137\u01e8\u01e9\u1d37\u1d4f\u1e30-\u1e35\u212a\u24a6\u24c0\u24da\u3384\u3385\u3389\u338f\u3391\u3398\u339e\u33a2\u33a6\u33aa\u33b8\u33be\u33c0\u33c6\u33cd-\u33cf\uff2b\uff4b]',
			'L': '[Ll\u0139-\u0140\u01c7-\u01c9\u02e1\u1d38\u1e36\u1e37\u1e3a-\u1e3d\u2112\u2113\u2121\u216c\u217c\u24a7\u24c1\u24db\u32cf\u3388\u3389\u33d0-\u33d3\u33d5\u33d6\u33ff\ufb02\ufb04\uff2c\uff4c]',
			'M': '[Mm\u1d39\u1d50\u1e3e-\u1e43\u2120\u2122\u2133\u216f\u217f\u24a8\u24c2\u24dc\u3377-\u3379\u3383\u3386\u338e\u3392\u3396\u3399-\u33a8\u33ab\u33b3\u33b7\u33b9\u33bd\u33bf\u33c1\u33c2\u33ce\u33d0\u33d4-\u33d6\u33d8\u33d9\u33de\u33df\uff2d\uff4d]',
			'N': '[Nn\xd1\xf1\u0143-\u0149\u01ca-\u01cc\u01f8\u01f9\u1d3a\u1e44-\u1e4b\u207f\u2115\u2116\u24a9\u24c3\u24dd\u3381\u338b\u339a\u33b1\u33b5\u33bb\u33cc\u33d1\uff2e\uff4e]',
			'O': '[Oo\xba\xd2-\xd6\xf2-\xf6\u014c-\u0151\u01a0\u01a1\u01d1\u01d2\u01ea\u01eb\u020c-\u020f\u022e\u022f\u1d3c\u1d52\u1ecc-\u1ecf\u2092\u2105\u2116\u2134\u24aa\u24c4\u24de\u3375\u33c7\u33d2\u33d6\uff2f\uff4f]',
			'P': '[Pp\u1d3e\u1d56\u1e54-\u1e57\u2119\u24ab\u24c5\u24df\u3250\u3371\u3376\u3380\u338a\u33a9-\u33ac\u33b0\u33b4\u33ba\u33cb\u33d7-\u33da\uff30\uff50]',
			'Q': '[Qq\u211a\u24ac\u24c6\u24e0\u33c3\uff31\uff51]',
			'R': '[Rr\u0154-\u0159\u0210-\u0213\u02b3\u1d3f\u1d63\u1e58-\u1e5b\u1e5e\u1e5f\u20a8\u211b-\u211d\u24ad\u24c7\u24e1\u32cd\u3374\u33ad-\u33af\u33da\u33db\uff32\uff52]',
			'S': '[Ss\u015a-\u0161\u017f\u0218\u0219\u02e2\u1e60-\u1e63\u20a8\u2101\u2120\u24ae\u24c8\u24e2\u33a7\u33a8\u33ae-\u33b3\u33db\u33dc\ufb06\uff33\uff53]',
			'T': '[Tt\u0162-\u0165\u021a\u021b\u1d40\u1d57\u1e6a-\u1e71\u1e97\u2121\u2122\u24af\u24c9\u24e3\u3250\u32cf\u3394\u33cf\ufb05\ufb06\uff34\uff54]',
			'U': '[Uu\xd9-\xdc\xf9-\xfc\u0168-\u0173\u01af\u01b0\u01d3\u01d4\u0214-\u0217\u1d41\u1d58\u1d64\u1e72-\u1e77\u1ee4-\u1ee7\u2106\u24b0\u24ca\u24e4\u3373\u337a\uff35\uff55]',
			'V': '[Vv\u1d5b\u1d65\u1e7c-\u1e7f\u2163-\u2167\u2173-\u2177\u24b1\u24cb\u24e5\u2c7d\u32ce\u3375\u33b4-\u33b9\u33dc\u33de\uff36\uff56]',
			'W': '[Ww\u0174\u0175\u02b7\u1d42\u1e80-\u1e89\u1e98\u24b2\u24cc\u24e6\u33ba-\u33bf\u33dd\uff37\uff57]',
			'X': '[Xx\u02e3\u1e8a-\u1e8d\u2093\u213b\u2168-\u216b\u2178-\u217b\u24b3\u24cd\u24e7\u33d3\uff38\uff58]',
			'Y': '[Yy\xdd\xfd\xff\u0176-\u0178\u0232\u0233\u02b8\u1e8e\u1e8f\u1e99\u1ef2-\u1ef9\u24b4\u24ce\u24e8\u33c9\uff39\uff59]',
			'Z': '[Zz\u0179-\u017e\u01f1-\u01f3\u1dbb\u1e90-\u1e95\u2124\u2128\u24b5\u24cf\u24e9\u3390-\u3394\uff3a\uff5a]'
		}
	},

	_static_functions:
	{
		_setClass: function (elem, iClass)
		{
			elem.setAttribute("class", iClass);
			elem.setAttribute("className", iClass);
		},

		_findPos: function(elem)
		{
			var curleft = 0;
			var curtop = 0;

			if (elem.offsetParent)
			{
				do
				{
					curleft += elem.offsetLeft;
					curtop += elem.offsetTop;
				} while ( (elem = elem.offsetParent) != null);
				return [ curleft, curtop ];
			}
			return null;
		},

		_addEventListener: function(elem, event, func, capture)
		{
			if (elem.addEventListener)
			{
				elem.addEventListener(event, func, capture);
			}
			else if (elem.attachEvent)
			{
				elem.attachEvent("on" + event, func);
			}
		},

	},

	_private_functions:
	{
		/**
		 * Creates a RegExp that matches the words in the search string.
		 * Case and accent insensitive.
		 */
		_make_pattern: function(search_string, options) 
		{
			// escape meta characters
			search_string = search_string.replace(/([|()[{.+*?^$\\])/g,"\\$1");

			// replace characters by their compositors
			var accent_replacer = function(chr) 
			{
				return SuggestionBox._private_data._accented[chr.toUpperCase()] || chr;
			}

			search_string = search_string.replace(/\S/g,accent_replacer);

			var regexp = search_string;

			if (options > 0)
				regexp = "(" + regexp + ")";

			// join as alternatives
			if ( (options & SuggestionBox.MATCH_BEGIN) > 0)
				regexp = "^" + regexp;

			if ( (options & SuggestionBox.MATCH_END) > 0)
				regexp += "$";

			if ( (options & SuggestionBox.MATCH_WORD_BEGIN) > 0)
				regexp = "\\b" + regexp;

			if ( (options & SuggestionBox.MATCH_WORD_END) > 0)
				regexp += "\\b";

			return new RegExp(regexp,'g');
		},

		_setSuggestionData: function(list)
		{
			if (typeof(list) == 'object' && list.length != undefined)
			{
				this._SuggestionList = list;
			}
			else
			{
				this._SuggestionList = [];
			}
		},

		_setBlankSuggestionData: function(list)
		{
			if (typeof(list) == 'object' && list.length != undefined)
			{
				this._BlankSuggestionList = list;
			}
			else
			{
				this._BlankSuggestionList = [];
			}
		},
		
		_TextBox_Event: function(event)
		{
			var target;

			if (event == undefined)
				event = window.event;

			if (event.target)
				target = event.target;
			else
				target = event.srcElement;

			if (target != undefined)
			{
				if (event.type == 'focus')
				{
					this._AcceptText = 1;
					this._updateSuggestions(target.value);
				}
				else if (event.type == 'keyup' && this._AcceptText == 1)
				{
					if (event.keyCode == 40)
					{
						if (this._BaseDiv.style.display == "none")
							this._updateSuggestions(target.value);

						var n = this._BaseTBody.childNodes.length;

						this._selectedSuggestion += 1;
						if (this._selectedSuggestion == n)
							this._selectedSuggestion = -1;

						this._updateSelectedSuggestion();
					}
					else if (event.keyCode == 38)
					{
						if (this._BaseDiv.style.display == "none")
							this._updateSuggestions(target.value);

						var n = this._BaseTBody.childNodes.length;
						this._selectedSuggestion -= 1;

						if (this._selectedSuggestion == -2)
							this._selectedSuggestion = n-1;

						this._updateSelectedSuggestion();
					}
					else if (event.keyCode == 13)
					{
						if (this._selectedSuggestion == -1)
						{
							if (event.preventDefault) 
								event.preventDefault();

							event.cancelBubble = true;
							return false;
						}

						this._TextBox.value = this._BaseTBody.childNodes[this._selectedSuggestion].firstChild.innerHTML;
						this._hideSuggestions();
						this._StopBlur = 0;

						if (event.preventDefault) 
							event.preventDefault();


						event.cancelBubble = true;

						return false;
					}
					else
					{
						this._updateSuggestions(target.value);
					}
				}
				else if (event.type == 'keypress')
				{
					if (event.keyCode == 13)
					{
						if (event.preventDefault) 
							event.preventDefault();

						event.cancelBubble = true;

						return false;
					}
				}
				else if (event.type == 'blur')
				{
					var b = this;
					var f = function () { if (!b._StopBlur) { b._hideSuggestions.call(b); b._AcceptText = 0; } else { setTimeout(f, 50); } };
					setTimeout(f, 200);
				}
			}

			return;
		},

		_SuggestionBox_Event: function(event)
		{
			var target;

			if (event == undefined)
				event = window.event;

			if (event.target)
				target = event.target;
			else
				target = event.srcElement;

			if (target != undefined)
			{
				if (event.type == 'mouseover')
				{
					SuggestionBox._static_functions._setClass(target, "cell-hover");
					this._StopBlur = 1;
				}
				else if (event.type == 'mouseout')
				{
					SuggestionBox._static_functions._setClass(target, "cell-no-hover");
					this._StopBlur = 0;
				}
				else if (event.type == 'click')
				{
					this._TextBox.value = target.innerHTML;
					this._hideSuggestions();
					this._StopBlur = 0;
				}
			}
		},

		_updateSuggestions: function(text)
		{
			var matches = this._collectMatches(text);

			if (typeof(matches) != 'object' || matches.length == 0)
			{
				return this._hideSuggestions();
			}
			else
			{
				var x;
				this._removeAllSuggestions();
				for (x = 0; x < matches.length; x++)
				{
					this._addSuggestion(matches[x]);
					if ( (x+1) == this._MaxSuggestions)
						break;
				}
				this._displaySuggestions();
			}
		},	

		_collectMatches: function(text)
		{
			var x;
			var matches = [];

			if (text == undefined || text == "")
			{
				return this._BlankSuggestionList;
			}

			var reg = SuggestionBox._private_functions._make_pattern(text, SuggestionBox.MATCH_WORD_BEGIN);

			var list_of_cities = this._SuggestionList;

			for (x = 0; x < this._SuggestionList.length; x++)
			{
				if (this._SuggestionList[x].match(reg))
				{
					matches.push(this._SuggestionList[x]);
				}
			}

			return matches;
		},

		_hideSuggestions: function()
		{
			this._BaseDiv.style.display = "none";
		},

		_displaySuggestions: function()
		{
			var height = this._TextBox.offsetHeight;
			var width = this._TextBox.offsetWidth;
			this._BaseDiv.style.display = "";
			this._BaseDiv.style.position = "absolute";
			var pos = SuggestionBox._static_functions._findPos(this._TextBox);

			this._BaseDiv.style.left = pos[0] + "px";
			this._BaseDiv.style.top = (pos[1]+height-1) + "px";

			this._BaseDiv.style.width = (width - 2) + "px";

			this._selectedSuggestion = -1;
			this._updateSelectedSuggestion();
		},

		_updateSelectedSuggestion: function()
		{
			var x;
			for (x = 0; x < this._BaseTBody.childNodes.length; x++)
			{
				if (x == this._selectedSuggestion)
				{
					SuggestionBox._static_functions._setClass(this._BaseTBody.childNodes[x].firstChild, "cell-hover");
				}
				else
				{
					SuggestionBox._static_functions._setClass(this._BaseTBody.childNodes[x].firstChild, "cell-no-hover");
				}
			}
		},

		_removeAllSuggestions: function()
		{
			while (this._BaseTBody.firstChild) { this._BaseTBody.removeChild(this._BaseTBody.firstChild); }
		},

		_addSuggestion: function(text)
		{
			var b = this;
			var f = function (event) { b._SuggestionBox_Event.call(b, event); };
			var row = document.createElement("TR");
			var data = document.createElement("TD");

			data.innerHTML = text;
			SuggestionBox._static_functions._addEventListener(data, "click", f, false);
			SuggestionBox._static_functions._addEventListener(data, "mouseover", f, false);
			SuggestionBox._static_functions._addEventListener(data, "mouseout", f, false);
			SuggestionBox._static_functions._setClass(data, "cell-no-hover");

			row.appendChild(data);
			this._BaseTBody.appendChild(row);
		},	

		_initSuggestionBox: function()
		{
			this._BaseDiv = document.createElement("DIV");

			SuggestionBox._static_functions._setClass(this._BaseDiv, "suggestion-box");

			var table = document.createElement("TABLE");
			this._BaseTBody = document.createElement("TBODY");

			table.appendChild(this._BaseTBody);
			this._BaseDiv.appendChild(table);

			if (this._TextBox != undefined && this._TextBox.parentNode != undefined)
			{
				var b = this;
				var f = function (event) { return b._TextBox_Event.call(b, event); };

				var g = function (event) 
				{
					if (event == undefined) window.event = event; 

					var target = (event.target ? event.target : event.srcElement); 

					if (event.keyCode == 13 && target == b._TextBox) 
						return false; 
					else 
						return true; 
				};

				this._TextBox.parentNode.appendChild(this._BaseDiv);
				SuggestionBox._static_functions._addEventListener(this._TextBox, "keyup", f, false);
				SuggestionBox._static_functions._addEventListener(this._TextBox, "keypress", f, false);
				SuggestionBox._static_functions._addEventListener(this._TextBox, "focus", f, false);
				SuggestionBox._static_functions._addEventListener(this._TextBox, "blur", f, false);
//				SuggestionBox._static_functions._addEventListener(window, "keypress", g, false);
				this._TextBox.setAttribute("autocomplete", "off");
				this._hideSuggestions();
			}
		}

	},

	inst: function(text_box, max_suggestions)
	{
		if (typeof(text_box) == 'string')
			text_box = document.getElementById(text_box);

		if (max_suggestions == undefined)
			max_suggestions = 10;

		this._TextBox = text_box;
		this._SuggestionList = [];
		this._AcceptText = 0;

		this._MaxSuggestions = max_suggestions;

		this.setSuggestionData = SuggestionBox._private_functions._setSuggestionData;
		this.setBlankSuggestionData = SuggestionBox._private_functions._setBlankSuggestionData;

		this._hideSuggestions = SuggestionBox._private_functions._hideSuggestions;
		this._displaySuggestions = SuggestionBox._private_functions._displaySuggestions;
		this._updateSelectedSuggestion = SuggestionBox._private_functions._updateSelectedSuggestion;

		this._collectMatches = SuggestionBox._private_functions._collectMatches;

		this._updateSuggestions = SuggestionBox._private_functions._updateSuggestions;

		this._removeAllSuggestions = SuggestionBox._private_functions._removeAllSuggestions;
		this._addSuggestion = SuggestionBox._private_functions._addSuggestion;

		this._TextBox_Event = SuggestionBox._private_functions._TextBox_Event;
		this._SuggestionBox_Event = SuggestionBox._private_functions._SuggestionBox_Event;

		this._initSuggestionBox = SuggestionBox._private_functions._initSuggestionBox;

		this._initSuggestionBox();
	}
		
};

