function vHint(inputElement, templateHTML, url) {
	var self = this;
	element = v$('element');

	self.inputElement = v$(inputElement);
	self.hintElement = document.createElement('div');
	self.templateHTML = templateHTML;
	self.url = (url.indexOf('?') == -1) ? url + '?h=' : url + '&h=';
	self.columnDelimiter = ',';
	self.rowDelimiter = '\n';
	self.parameters = {};
	self.onBeforePopulate = null;
	self.onAfterPopulate = null;
	self.onHover = null;
	self.onSelect = null;
	self.activeData = null;
	self.activeRow = null;
	self.activeText = '';
	self.autoSubmit = true;
	self.minChars = 1;

	self.onKeyUp = function(event) {
		var keyCode = event.keyCode || event.charCode;
		if (keyCode == 27 || keyCode == 40 || keyCode == 38 || keyCode == 13 || keyCode == 39) {
			//27 = escape
			//13 = enter
			//40 = down
			//38 = up
			if (keyCode == 27) {
				self.empty();
			}
			else if (keyCode == 13) {
			}
			else if (self.activeData) {
				var targetRow = self.activeRow;
				if (keyCode == 40) {
					if (targetRow && targetRow.nextSibling) {
						targetRow = targetRow.nextSibling;
					}
					else {
						targetRow = self.hintElement.firstChild;
					}
				}
				else if (keyCode == 38) {
					if (targetRow && targetRow.previousSibling) {
						targetRow = targetRow.previousSibling;
					}
					else {
						targetRow = self.hintElement.lastChild;
					}
				}
				targetRow.onmouseover();
				self.inputElement.value = self.activeData[targetRow.referenceID][0];
			}
		}
		else if (self.activeText != self.inputElement.value.toLowerCase()) {
			self.activeText = self.inputElement.value.toLowerCase();
			if (self.minChars <= self.inputElement.value.length) {
				if (self.onBeforePopulate) {
						self.onBeforePopulate();
				}
				var paramQS = '';
				for (param in self.parameters) {
					paramQS += '&' + param + '=' + self.parameters[param].toString().replace(/&/g, '%26');
				}
				paramQS = paramQS.substr(1);
				sendAjax('GET', self.url + self.inputElement.value, '', self.receiveAjax, throwAjaxError);
			}
		}
	}
	
	self.onMouseOver = function(event) {
		if (self.activeRow) {
			self.activeRow.className = 'row';
		}
		self.activeRow = this;
		this.className = 'row selected';
		if (self.onHover) {
			self.onHover(self.activeData[this.referenceID]);
		}
	}
	
	self.onClick = function(event) {
		var activeDataRow = null;
		if (self.activeRow) {
			activeDataRow = self.activeData[self.activeRow.referenceID];
			self.inputElement.value = activeDataRow[0];
		}
		if (self.onSelect) {
			self.onSelect(activeDataRow);
		}
		self.empty();
		if (self.autoSubmit && self.inputElement.form) {
			self.inputElement.form.submit();
		}
	}
	
	self.empty = function() {
		self.hintElement.style.display = 'none';
		self.hintElement.innerHTML = '';
		self.activeData = null;
		self.activeRow = null;
		self.activeText = '';
	}
	
	self.receiveAjax = function(result) {
		var rowValues = null;

		self.activeData = null;
		self.hintElement.innerHTML = '';

		if (result != '') {
			rowValues = result.split(self.rowDelimiter);
			self.activeData = new Array();
			for (var i = 0; i < rowValues.length; i++) {
				var baseHTML = self.templateHTML;
				var columnValues = rowValues[i].split(self.columnDelimiter);
				self.activeData[i] = columnValues;
				for (var x = 0; x < columnValues.length; x++) {
					baseHTML = baseHTML.replace('$(' + x + ')', columnValues[x]);
				}
				var div = document.createElement('div');
				div.className = 'row';
				div.innerHTML = baseHTML;
				div.referenceID = i;
				div.onmouseover = self.onMouseOver;
				div.onclick = self.onClick;
				self.hintElement.appendChild(div);
			}
			self.draw();
		}
		else {
			self.empty();
		}
		if (self.onAfterPopulate) {
			self.onAfterPopulate(rowValues);
		}
	}

	self.cancelFormSubmit = function(event) {
		DetachEvent(self.inputElement.form, 'submit', self.cancelFormSubmit);
		return false;
	}

	self.draw = function() {
		var parent = self.inputElement;
		var offsetX = 0;
		var offsetY = parent.offsetHeight;
	
		while (parent) {
			offsetX += parent.offsetLeft;
			offsetY += parent.offsetTop;
			parent = parent.offsetParent;
		}
		self.hintElement.style.left = offsetX + 'px';
		self.hintElement.style.top = offsetY + 'px';
		self.hintElement.style.display = '';
	}

	self.hintElement.id = inputElement + 'Hint';
	self.hintElement.className = 'hint';
	
	self.inputElement.setAttribute('autoComplete', 'off');
	self.hintElement.style.position = 'absolute';
	self.empty();
	document.body.appendChild(self.hintElement);

	AttachEvent(self.inputElement, 'keyup', self.onKeyUp);
	AttachEvent(document, 'click', function(event) {
		var target = event.srcElement || event.target;
		if (target != self.inputElement) {
			self.empty();
		}
	});
	AttachEvent(self.inputElement, 'keydown', function(event) {
			var keyCode = event.keyCode || event.charCode;
			if (keyCode == 13) {
				if (self.activeRow) {
					self.inputElement.value = self.activeData[self.activeRow.referenceID][0];
				}
				self.onClick(event);
				if (!self.autoSubmit) {
					AttachEvent(self.inputElement.form, 'submit', self.cancelFormSubmit);
				}
			}
	});
}
