self.AutoComplete = function(_textField, _data, _limit)
{
	var IE = (window.ActiveXObject !== undefined);
	var _box;
	var _boxShape;
	var _boxShapeList;
	var _width;
	var _height;
	var _border;
	var _fgColor;
	var _bgColor;
	var _hasLimit;
	var _selected;
	var _selectedBgColor;
	var _selectedPressBgColor;
	var _lastText;
	var _lastTextFocused;
	var _isOver;

	this.construct = function()
	{
		if (_textField !== undefined)
		{
			_textField.instanceRef = this;
			init();
			build();
		}
		else
		{
			alert("Erro ao criar instância:\nEra esperada a referência do campo de texto (HTMLInputElement)!");
		}
	};

	var init = function()
	{
		_textField.keyUp = _textField.onkeyup;
		_textField.onkeyup = fill;
		_textField.keyDown = _textField.onkeydown;
		_textField.onkeydown = keyControl;
		_textField.keyPress = _textField.onkeypress;
		_textField.onkeypress = keyFilter;
		_textField.lostFocus = _textField.onblur;
		_textField.onblur = hideFocus;
		_textField.gotFocus = _textField.onfocus;
		_textField.onfocus = showFocus;

		_width = "200px";
		_height = "40px";
		_border = ["1px", "solid", "#666"];
		_fgColor = "#333";
		_fgSize = "12px";
		_fgFont = "Verdana, Tahoma, Arial, Helvetica";
		_bgColor = "#EEE";
		_boxShapeList = [];
		_hasLimit = false;
		_selected = null;
		_selectedBgColor = "#CCC";
		_selectedPressBgColor = "#BBB";
		_lastText = "";
		_isOver = false;
	};

	var build = function()
	{
		var pos;

		pos = getPosition();

		_box = document.createElement("div");
		_box.style.position = "absolute";
		_box.style.width = (_hasLimit) ? _width : "";
		_box.style.height = (_hasLimit) ? _height : "";
		_box.style.left = pos.x + "px";
		_box.style.top = pos.y + "px";
		_box.style.border = _border.join(" ");
		_box.style.backgroundColor = _bgColor;
		_box.style.overflowY = (_hasLimit) ? "scroll" : "auto";
		_box.style.display = "none";

		document.body.appendChild(_box);

		_boxShape = document.createElement("ul");
		_boxShape.style.listStyleType = "none";
		_boxShape.style.marginLeft = _boxShape.style.marginRight = _boxShape.style.marginTop = _boxShape.style.marginBottom = "3px";
		_boxShape.style.padding = 0;
		_boxShape.style.fontFamily = _fgFont;
		_boxShape.style.fontSize = _fgSize;
		_boxShape.style.color = _fgColor;

		_box.appendChild(_boxShape);
	};

	var updateBorder = function()
	{
		_box.style.border = _border.join(" ");
	};

	var updateLimit = function()
	{
		_box.style.width = (_hasLimit) ? _width : "";
		_box.style.height = (_hasLimit) ? _height : "";
		_box.style.overflowY = (_hasLimit) ? "scroll" : "auto";
	};

	var getPosition = function()
	{
		var osParent, x, y;

		osParent = _textField;
		x = osParent.offsetLeft;
		y = osParent.offsetTop + osParent.offsetHeight;

		while ((osParent = osParent.offsetParent) !== null)
		{
			x += osParent.offsetLeft;
			y += osParent.offsetTop;
		}

		return {x: x, y: y};
	};

	var fill = function(e)
	{
		var text, keyUp, re, value, data, valids;

		text = _textField.value;
		keyUp = _textField.keyUp;
		valids = 0;

		if (typeof(keyUp) == "function") // Se houver ações definidas para o evento "onkeyup" ...
		{
			keyUp(e); // Executa as ações já definidas
		}

		e = (IE) ? event : e;

		if (text != _lastText)
		{
			_lastText = text;

			if (_boxShapeList.length > 0) // Se houver itens na lista de sugestões ...
			{
				_boxShape.innerHTML = ""; // Limpa a lista de sugestões
				_boxShapeList = []; // Apaga as referências das linhas
				hide();
				_selected = null;
			}

			if (text !== "") // Se houver algum texto na caixa de texto ...
			{
				if (_data !== undefined) // Se existir o Array com os dados ...
				{
					for (var i = 0; i < _data.length && ((_limit == null) ? true : (valids < _limit)); i++)
					{
						if (typeof(_data[i]) == "object")
						{
							value = _data[i].text;
						}
						else
						{
							value = _data[i];
						}

						if (value != text) // Se tiver havido alteração na caixa de texto ...
						{
							re = value.match(new RegExp("^((?=^" + text + ")|(.+\\s+)*)(" + text + ")(.*)", "i"));
	
							if (re !== null) // Se o texto digitado existir no início de cada palavra do vetor atual do Array de dados ...
							{
								add(((re[2] !== undefined) ? re[2] : "") + "<b>" + re[3] + "</b>" + re[4], _data[i], this.instanceRef); // Adiciona a sugestão na lista
								valids++;
							}
						}
					}

					if (_boxShapeList.length > 0) // Se houver itens na lista de sugestões ...
					{
						show();
						_boxShapeList[0].select(); // Seleciona a primeira linha
					}
				}
			}
		}
	};

	var keyControl = function(e)
	{
		var keyDown;

		keyDown = _textField.keyDown;

		if (typeof(keyDown) == "function") // Se houver ações definidas para o evento "onkeydown" ...
		{
			keyDown(e); // Executa as ações já definidas
		}

		e = (IE) ? event : e;

		if (_selected !== null)
		{
			switch (e.keyCode)
			{
				case 13:
					_textField.value = _selected.text;
					this.instanceRef.onSelect({text: _selected.text, data: _selected.data});
					_selected = null;
				break;

				case 27:
					hide();
					_selected = null;
				break;

				case 38:
					if (_selected.index > 0)
					{
						_boxShapeList[_selected.index - 1].select();
					}
				break;

				case 40:
					if (_selected.index < _boxShape.childNodes.length - 1)
					{
						_boxShapeList[_selected.index + 1].select();
					}
				break;
			}
		}
	};

	var keyFilter = function(e)
	{
		var keyPress;

		keyPress = _textField.keyPress;

		if (typeof(keyPress) == "function") // Se houver ações definidas para o evento "onkeypress" ...
		{
			keyPress(e); // Executa as ações já definidas
		}

		e = (IE) ? event : e;

		if (_box.style.display != "none")
		{
			switch (e.keyCode)
			{
				case 13:
					return false;
				break;
			}
		}
	};

	var show = function()
	{
		var pos;

		pos = getPosition();

		_box.style.left = pos.x + "px";
		_box.style.top = pos.y + "px";
		_box.style.display = "block";
	};

	var hide = function()
	{
		_box.style.display = "none";
	};

	var showFocus = function(e)
	{
		var gotFocus;

		gotFocus = _textField.gotFocus;

		if (typeof(gotFocus) == "function") // Se houver ações definidas para o evento "onfocus" ...
		{
			gotFocus(e); // Executa as ações já definidas
		}

		if (_selected !== null)
		{
			show();
		}

		_lastTextFocused = this.value;
	};

	var hideFocus = function(e)
	{
		var lostFocus;

		lostFocus = _textField.lostFocus;

		if (typeof(lostFocus) == "function") // Se houver ações definidas para o evento "onblur" ...
		{
			lostFocus(e); // Executa as ações já definidas
		}

		if (_box.style.display != "none" && !_isOver)
		{
			hide();
		}

		if (this.value !== _lastTextFocused)
		{
			_lastTextFocused = this.value;
			this.instanceRef.onChange({text: this.value});
		}
	};

	var add = function(text, data, instanceRef)
	{
		var row, column1, column2;

		_boxShapeList.push(row = document.createElement("li"));
		row.appendChild(column1 = document.createElement("div"));
		column1.innerHTML = text;

		if (typeof(data) == "object")
		{
			if (data.button !== undefined)
			{
				row.appendChild(column2 = document.createElement("a"));
				column1.style.display = "inline";
				column2.style.marginLeft = "4px";
				column2.innerHTML = data.button;
				
				column2.onmouseover = function()
				{
					this.style.cursor = "pointer";
				};
				
				column2.onclick = function()
				{
					instanceRef.onButtonClick({text: data.text, data: data.data});
				}
			}
			
			row.text = data.text;
			row.data = data.data;
		}
		else
		{
			row.text = data;
			row.data = data;
		}

		row.index = _boxShape.childNodes.length;

		row.select = function()
		{
			if (_selected !== null)
			{
				_selected.unselect();
			}

			this.style.backgroundColor = _selectedBgColor;
			_selected = this;
		};

		row.unselect = function()
		{
			this.style.backgroundColor = _bgColor;
			_selected = null;
		};

		row.onmouseover = function()
		{
			this.select();
			this.style.cursor = "pointer";
			_isOver = true;
		};

		row.onmousedown = function()
		{
			this.style.backgroundColor = _selectedPressBgColor;
		};

		row.onmouseup = function()
		{
			_textField.value = this.text;
			instanceRef.onSelect({text: this.text, data: this.data});
			hide();
			//_textField.focus(); (!) Provalmente dispara o evento "onfocus" ao comando "focus()" no Firefox
			_selected = null;
			_isOver = false;
			_lastText = this.text;
		};

		row.onmouseout = function()
		{
			_isOver = false;
		};

		_boxShape.appendChild(row);
	};

	this.hasLimit = function(has)
	{
		_hasLimit = has;
		updateLimit();
	};

	this.setWidth = function(width)
	{
		_box.style.width = _width = width + "px";
	};

	this.setHeight = function(height)
	{
		_box.style.height = _height = height + "px";
	};

	this.setBorderSize = function(size)
	{
		_border[0] = s + "px";
		updateBorder();
	};

	this.setBorderStyle = function(style)
	{
		_border[1] = style;
		updateBorder();
	};

	this.setBorderColor = function(color)
	{
		_border[2] = color;
		updateBorder();
	};

	this.setData = function(data)
	{
		_data = data;
	};

	this.getData = function()
	{
		return _data;
	};

	this.setLimit = function(limit)
	{
		_limit = limit;
	};
	
	this.onSelect = function(){};
	this.onChange = function(){};
	this.onButtonClick = function(){};

	this.construct();
}