jQuery.fn.autocomplete = function(settings) {
	return this.each(function() {
		var textInput = $(this);

		textInput.after('<input type=hidden name="' + textInput.attr('name') + '"/>').attr('name', textInput.attr('name') + '_text');

		var valueInput = $(this).next();

		valueInput.after('<ul class="autocomplete"></ul>');

		var list = valueInput.next().css({width: textInput.width()});
		var oldText = '';
		var typingTimeout;
		var size = 0;
		var selected = 0;

		settings = jQuery.extend({
			minChars : 1,
			timeout: 400,
			after : null,
			before : null,
			validSelection : true,
			parameters : {'inputName' : valueInput.attr('name'), 'inputId' : textInput.attr('id')}
		} , settings);

		function getData(text) {
			window.clearInterval(typingTimeout);

			if (text != oldText && (settings.minChars != null && text.length >= settings.minChars)) {
				clear();

				if (settings.before == 'function') {
					settings.before(textInput, text);
				}

                textInput.css({'background-image': 'url(/img/ui/loading-gray.gif)', 'background-repeat':  'no-repeat', 'background-position': 'center right'});
				settings.parameters.text = text;

				$.ajax({method: 'get', dataType: 'xml', url: textInput.attr('href') + '?q=' + text, success: function(data) {
					var items = '';

					size = 0;

					$(data).find('item').each(function() {
						var item_name = '';
						var item_data = '<item>';

						$(this).children().each(function() {
							if (this.tagName == 'name') {
								item_name = $(this).text();
							} else {
								item_data = item_data + '<' + this.tagName + '>' + $(this).text() + '</' + this.tagName + '>';
							}
						});

						item_data = item_data + '</item>';

						items += '<li data="' + escape(item_data) + '">' + item_name.replace(new RegExp('(^| )(' + text + ')', 'i'), '$1<strong>$2</strong>') + '</li>';
						size++;
					});

					selected = size;

					list.html(items);
					list.show().children().hover(function() {
						$(this).addClass('selected').siblings().removeClass('selected');
					}, function() {
						$(this).removeClass('selected');
					}).click(function () {
						valueInput.val($(this).attr('data'));
						textInput.val($(this).text());

						clear();
					});

					if (settings.after == 'function') {
						settings.after(textInput,text);
					}

					textInput.css({'background': ''});
				}});

				oldText = text;
			}
		}

		function clear() {
			list.hide();
			size = 0;
			selected = 0;
		}

		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 {
					clear();
				}

				e.preventDefault();

				return false;
			} else if(e.which == 40 || e.which == 9 || e.which == 38) {
				switch(e.which) {
					case 40: 
					case 9:
						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('data') );
			} else { 
				if (settings.validSelection) valueInput.val('');
				typingTimeout = window.setTimeout(function() { getData(textInput.val()) },settings.timeout);
			}
		});
	});
};
