jQuery.fn.autocomplete = function(url, valueInputId, settings) {
	return this.each(function(){
		var textInput = $(this).attr('autocomplete', 'off');
		var valueInput = $('input#'+valueInputId);
		var oldText='';
		var typingTimeout;
		var size=0;
		var selected=-1;
		var ul = '<ul class="autocomplete" style="font-weight: normal;"></ul>';

		settings = jQuery.extend({
			minChars:       1,
			timeout:        1000,
			div:            null,
			onSelect:       null,
			validSelection: true,
			parameters: {
				'inputName': valueInput.attr('name'),
				'inputId':   textInput.attr('id')
			}},
			settings
		);

		var list;
		if(settings.div != null) {
			$('#' + settings.div).html(ul);
			list = $('#' + settings.div + ' ul.autocomplete');
		} else {
			textInput.after(ul);
			list = textInput.next();
		}

		list.css({
			top: textInput.offset().top + textInput.outerHeight(),
			left: textInput.offset().left,
			width: textInput.width()
		});

		function select() {
			if(typeof settings.onSelect == 'function') {
				settings.onSelect(valueInput.val(), textInput.val());
			}
		}

		function clear() {
			list.hide();
			size = 0;
			selected = -1;
		}

		function getData(text) {
			window.clearInterval(typingTimeout);
			if(text != oldText && (settings.minChars != null && text.length >= settings.minChars)) {
				clear();
				textInput.addClass('autocomplete-loading');
				settings.parameters.text=text;
				$.getJSON(url, settings.parameters, function(data) {
					textInput.removeClass('autocomplete-loading');

					var items='';
					if(data) {
						size = data.length;
						for(i = 0; i < data.length; i++) {
							for(key in data[i]) {
								items += '<li value="'+key+'">' + data[i][key].replace(new RegExp("(" + text + ")", "i"),"<strong>$1</strong>") + '</li>';
							}
						}
						list.html(items);
						list.show().children().hover(
							function(){ $(this).addClass("selected").siblings().removeClass("selected");},
							function(){ $(this).removeClass("selected")}
						).click(function() {
							valueInput.val($(this).attr('value'));
							textInput.val($(this).text());
							select();
							clear();
						});
					}
				});
				oldText=text;
			}
		}

		textInput.keydown(function(e) {
			window.clearInterval(typingTimeout);
			if(e.which == 27){
				clear();
			} else if(e.which == 46 || e.which == 8) {
				clear();
				if(settings.validSelection) {
					valueInput.val('');
				}
			} else if(e.which == 13) {
				if(list.css("display") == "none") {
					getData(textInput.val());
				} else {
					select();clear();
				}
				e.preventDefault();
				return false;
			} else if(e.which == 40 || e.which == 38) {
				switch(e.which) {
					case 40:
						selected = selected >= size-1 ? 0 : selected + 1;
						break;
					case 38:
						selected = selected <= 0 ? size-1 : selected-1;
						break;
					default:
						break;
				}
				textInput.val(list.children().removeClass('selected').eq(selected).addClass('selected').text());
				valueInput.val(list.children().eq(selected).attr('value'));
			} else {
				if(settings.validSelection) {
					valueInput.val('');
				}
				typingTimeout = window.setTimeout(
					function(){ getData(textInput.val())},
					settings.timeout
				);
			}
		});
	});
};