function CityHelper(url, aver, dataSet, sourceId, targetId, listId, tableId, toolTipId, busyId,
showBusyCursor, unselectedItemClass, selectedItemClass, startAfterLength, debugId) {
	this.active = false;
	this.url = url;
	this.aver = aver;
	this.sourceInputId = sourceId;
	this.targetInputId = targetId;
	this.listId = listId;
	this.tableId = tableId;
	this.toolTipId = toolTipId;
	this.busyId = busyId;
	this.showBusyCursor = showBusyCursor;
	this.unselectedItemClass = unselectedItemClass;
	this.selectedItemClass = selectedItemClass;
	this.startAfterLength = startAfterLength;
	this.debugId = debugId;
	this.debugging = false;
	this.searchFor = "";
	this.selectedIndex = 0;
	this.isSelecting = false;
	this.dataSet = dataSet;

	this.FilterDate = function(searchFor) {
		if (searchFor.length < 3) {
			this.dataSet.filter(null);
			return;
		}
		var regExpStr = '^' + searchFor;
		var regExp = new RegExp(regExpStr, "i");

		var filterFunc = function(ds, row, rowNumber) {
			var strn = row['n'];
			var strk = (searchFor.length <= 3) ? row['k'] : '';

			if ((strn && strn.search(regExp) != -1) || (strk && strk.search(regExp) != -1))
				return row;
			return null;
		};
		this.dataSet.filter(filterFunc);
	}

	this.search = function(event) {
		if (document != 'undefined' && document.getElementById(this.tableId)) {
			var key = this.getKeyCode(event);
			var inputElement = this.getEventTarget(event);

			if (inputElement.value.length > this.startAfterLength) {
				if (this.searchFor != inputElement.value) {
					this.searchFor = inputElement.value;
					this.dataSet.addObserver(this);
					this.dataSet.setURL(this.url);
					this.FilterDate(this.searchFor);
					this.dataSet.loadData();

					this.showLoading(true);
				}
				else {
					this.onChangeValue();

					if (this.dataSet.getRowCount() > 0) {
						this.onDataChanged();
					}
					else {
						this.selectedIndex = 0;
						this.showList(false);
					}
				}
			}
			else {
				this.selectedIndex = 0;
				this.showList(false);
			}

			if (this.isSelecting) {
				var rows = this.getTableRows();
				var rowsCount = this.getTableRowsCount();

				if (key == 38) {
					this.selectedIndex--;

					if (this.selectedIndex < 0) {
						this.selectedIndex = rowsCount - 1;
					}
				}
				else if (key == 40) {
					this.selectedIndex++;

					if (this.selectedIndex > rowsCount - 1) {
						this.selectedIndex = 0;
					}
				}
				else if (key == 13) {
					var row = this.getSelectedRow();
					this.returnValue(row);
					this.removeFocus();
					this.selectedIndex = 0;
				}
				else if (key == 27) {
					inputElement.value = "";
					this.removeFocus();
					this.selectedIndex = 0;
				}
				else {
					this.selectedIndex = 0;
				}
				this.scrollTo(this.selectedIndex);
				this.updateSelection();
			}
		}
	}

	this.onChangeValue = function() {
	}

	this.getSelectedRow = function() {
		return this.getTableRowAt(this.selectedIndex);
	}
	this.getTable = function() {
		return document.getElementById(this.tableId);
	}
	this.getTableRowAt = function(index) {
		var rows = this.getTableRows();
		return rows[index];
	}
	this.getTableRows = function() {
		var table = this.getTable();
		return table.rows;
	}
	this.getTableRowsCount = function() {
		var rows = this.getTableRows();
		return rows.length;
	}
	this.getTableRowIndex = function(target) {
		var rows = this.getTableRows();
		var rowsCount = this.getTableRowsCount();

		for (var i = 0; i < rowsCount; i++) {
			var row = rows[i];

			if (row == target) {
				return i;
			}
		}
	}
	this.getEventTarget = function(e) {
		var parent;

		if (!e) {
			var e = window.event;
		}
		else if (e.target) {
			parent = e.target;
		}
		else if (e.srcElement) {
			parent = e.srcElement;
		}

		if (parent.nodeType == 3) //safari fix
		{
			parent = parent.parentNode;
		}
		return parent;
	}
	this.getToolTip = function() {
		return document.getElementById(this.toolTipId);
	}
	this.getList = function() {
		return document.getElementById(this.listId);
	}
	this.getSourceInput = function() {
		return document.getElementById(this.sourceInputId);
	}
	this.getTargetInput = function() {
		return document.getElementById(this.targetInputId);
	}

	this.hasRows = function() {
		return (this.dataSet.getRowCount(this.getToolTip) > 0);
	}
	this.onDataChanged = function() {
		this.showLoading(false);

		if (this.hasRows()) {
			this.showList(true);
			this.selectFirstItem();
		}
		else {
			this.showList(false);
		}
	}
	this.getLoading = function() {
		return document.getElementById(this.busyId);
	}
	this.selectFirstItem = function() {
		if (!this.isSelecting) {
			this.selectedIndex = 0;
			this.updateSelection();
		}
		else {
			this.updateSelection();
		}
	}
	this.overItem = function(event) {
		var target = this.getEventTarget(event);
		var index = this.getTableRowIndex(target.parentNode);
		this.selectedIndex = index;
		this.updateSelection();
	}
	this.mouseSelectItem = function(event) {
		var selectedRow = this.getSelectedRow();
		this.returnValue(selectedRow);
		this.removeFocus();
	}
	this.writeTooltip = function(row) {
		if (row) {
			var toolTip = this.getToolTip();
			//alert(this);
			var data = this.getTableRowItemByName(row.childNodes, 'name');
			//this.writeDump(data);
			toolTip.innerHTML = data.innerHTML;
		}
	}
	this.getTableRowItemByName = function(nodes, name) {
		for (var i = 0; i < nodes.length; i++) {
			//var cell=new HTMLTableCellElement();
			var item = nodes[i];
			//this.writeLine(item.id);
			if (item.id == name)
				return item;
		}
	}

	this.returnValue = function(row) {
		var source = this.getSourceInput();
		var sdata = this.getTableRowItemByName(row.childNodes, 'name');
		//		alert(source);
		source.value = sdata.innerHTML;
		var target = this.getTargetInput();
		var tdata = this.getTableRowItemByName(row.childNodes, 'code');
		target.value = tdata.innerHTML;
	}
	this.scrollTo = function(index) {
		var list = this.getList();
		var rowsCount = this.getTableRowsCount();
		var rowHeight = (list.scrollHeight / rowsCount);

		//var scrollToValue=rowHeight*index;
		scrollToValue = 0;

		for (var i = 0; i < index; i++) {
			var row = this.getTableRowAt(i);
			scrollToValue += row.offsetHeight;
		}
		list.scrollTop = scrollToValue;
	}
	this.updateSelection = function() {
		var table = this.getTable();
		var rows = this.getTableRows();
		var rowsCount = this.getTableRowsCount();

		for (var i = 0; i < rowsCount; i++) {
			var row = rows[i];
			row.className = (i == this.selectedIndex) ? this.selectedItemClass : this.unselectedItemClass;
		}
		var selectedRow = this.getSelectedRow();
		this.writeTooltip(selectedRow);
	}
	this.getKeyCode = function(event) {
		var keynum;

		if (window.event) // IE
		{
			keynum = event.keyCode;
		}
		else if (event.which) // Netscape/Firefox/Opera
		{
			keynum = event.which;
		}
		return keynum;
	}
	this.showLoading = function(isVisible) {
	//		var showLoadingTarget=this.getLoading();
	//		showLoadingTarget.style.visibility=(isVisible) ? 'visible':'hidden';
	}

	this.showList = function(isVisible) {
		this.isSelecting = isVisible;
		var list = this.getList();
		list.style.visibility = (isVisible) ? 'visible' : 'hidden';
		var toolTip = this.getToolTip();
		toolTip.style.visibility = (isVisible) ? 'visible' : 'hidden';
		this.onShowList(isVisible);
	}
	this.onShowList = function(isVisible) {
	}

	this.removeFocus = function() {
		this.selectedIndex = 0;
		this.showList(false);
	}
	this.getDebugTarget = function() {
		return document.getElementById(this.debugId);
	}
	this.writeLine = function(string) {
		var debug = this.getDebugTarget();
		debug.value += string + "\n";
	}
	this.write = function(string) {
		var debug = this.getDebugTarget();
		debug.value += string + ", ";
	}
	this.dump = function(object) {
		var dumped = "";
		dumped += object.toString() + " {" + "\n";

		for (var i in object) {
			dumped += "[" + i + "] = " + object[i] + "\n";
		}
		dumped += object.toString() + " }" + "\n";
		;
		return dumped;
	}
	this.writeDump = function(object) {
		this.writeLine(this.dump(object));
	}
	this.findMatchingCity = function() {
		var inputElement = document.getElementById(this.sourceInputId);
		var searchText = inputElement.value.toLocaleLowerCase().replace(" ", "");
		var itemFounded = false;

		for (var i = 0; i < this.getTableRowsCount(); i++) {
			var row = this.getTableRowAt(i);
			var cityNameRow = this.getTableRowItemByName(row.childNodes, 'name');
			var cityCodeRow = this.getTableRowItemByName(row.childNodes, 'code');
			var cityNameRowText = cityNameRow.innerHTML.toLocaleLowerCase().replace(" ", "");
			var cityCodeRowText = cityCodeRow.innerHTML.toLocaleLowerCase().replace(" ", "");

			//alert(cityNameRowText+";"+cityCodeRowText);
			if (searchText == cityNameRowText || searchText == cityCodeRowText) {
				this.returnValue(row);
				itemFounded = true;
				break;
			}
		}

		if (!itemFounded) {
			for (var i = 0; i < this.getTableRowsCount(); i++) {
				var row = this.getTableRowAt(i);
				var cityNameRow = this.getTableRowItemByName(row.childNodes, 'name');
				var cityCodeRow = this.getTableRowItemByName(row.childNodes, 'code');
				var cityNameRowText = cityNameRow.innerHTML.toLocaleLowerCase().replace(" ", "");
				var cityCodeRowText = cityCodeRow.innerHTML.toLocaleLowerCase().replace(" ", "");

				//alert(cityNameRowText+";"+cityCodeRowText);
				if (cityNameRowText.indexOf(searchText) != -1 || cityCodeRowText.indexOf(searchText) != -1) {
					this.returnValue(row);
					itemFounded = true;
					break;
				}
			}
		}
		this.showList(false);
	}
}
