jQuery.fn.autocomplete = function(url, settings) {
    return this.each(function()//do it for each matched element
    {
        //this is the original input
        var textInput = $(this);
        //create the ul that will hold the text and values
        textInput.after('<ul class="autocomplete"></ul>');
        var list = textInput.next().css({ top: textInput.offset().top + textInput.outerHeight(), left: textInput.offset().left -200, width: textInput.width() });
        var oldText = '';
        var typingTimeout;
        var size = 0;
        var selected = 0;

        settings = jQuery.extend(  //provide default settings
		{
		minChars: 1,
		timeout: 800,
		after: null,
		before: null,
		parametersReset: null,
		postClick: null,
		parameters: {}
}, settings);

        function getData(text) {
            window.clearInterval(typingTimeout);
            if (text != oldText && (settings.minChars != null && text.length >= settings.minChars)) {
                clear();
                if (settings.before != null) {
                    settings.before(textInput, text);
                }
                textInput.addClass('autocomplete-loading');
                if (settings.parametersReset != null) {
                    settings.parametersReset(settings.parameters);
                }
                settings.parameters.text = text;
                $.getJSON(url, settings.parameters, function(data) {
                    var items = '';
                    if (data) {
                        $.each(data, function(name, value) {
                            items += '<li data="' + value + '">' + name + '</li>';
                            size++;
                        });
                        list.html(items);
                        //on mouse hover over elements set selected class and on click set the selected value and close list
                        list.show().children().hover(function() {
                            $(this).addClass("selected").siblings().removeClass("selected");
                        },
                               function() {
                                   $(this).removeClass("selected")
                               }).click(function() {
                                   if ($(this).attr('data') != null) {
                                       var values = $(this).attr('data').split('|');
                                       textInput.val(values[0]);
                                   }
                                   clear();
                                   if (settings.postClick != null)
                                       settings.postClick($(this).attr('data'));
                               });
                    }
                });

                $("body").click(function() { clear(); });
                
                if (settings.after != null) {
                    settings.after(textInput, text);
                }
                textInput.removeClass('autocomplete-loading');
                oldText = text;
            }
        }

        function clear() {
            list.hide();
            size = 0;
            selected = 0;
        }

        textInput.keydown(function(e) {
            window.clearInterval(typingTimeout);
            if (e.which == 27 || e.which == 9)//escape
            {
                clear();
            } else if (false) //.which == 46)// || e.which == 8)//delete and backspace
            {
                clear();
            }
            else if (e.which == 13)//enter 
            {
                if (list.css("display") == "none")//if the list is not visible then make a new request, otherwise hide the list
                {
                    getData(textInput.val());
                } else {
                    list.children().eq(selected).click();
                }
                e.preventDefault();
                return false;
            }
            else if (e.which == 40 || e.which == 38)//move up, down 
            {
                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;
                }
                //set selected item and input values
                list.children().removeClass('selected').eq(selected).addClass('selected');
            } else {
                typingTimeout = window.setTimeout(function() { getData(textInput.val()) }, settings.timeout);
            }
        });
    });
};
