jQuery.fn.autocomplete = function(url, countryList, geographyCodeInput, geographyControlGUID, settings) {
    return this.each(function()//do it for each matched element
    {
        //this is the original input
        var textInput = $(this);
        //create a new hidden input that will be used for holding the return value when posting the form, then swap names with the original input
        textInput.after('<input type="hidden" name="' + textInput.attr("name") + "_value" + '"/>');
        var valueInput = $(this).next();
        //create the ul that will hold the text and values
        valueInput.after('<ul class="autocomplete"></ul>');
        var list = valueInput.next();
        var oldText = '';
        var oldInput = '';
        var oldGUID = '';
        var typingTimeout;
        var size = 0;
        var selected = 0;

        settings = jQuery.extend(//provide default settings
		{minChars: 1, timeout: 300 }, settings);

        function getData(text) {
            list.css({ top: textInput.offset().top + textInput.outerHeight(), left: textInput.offset().left, width: textInput.width() + 2 });
            window.clearInterval(typingTimeout);
            if (text != oldText && (settings.minChars != null && text.length >= settings.minChars)) {
                clear();
                var countryGUID = $(countryList).val();
                $.get(url + "?text=" + escape(text) + "&country=" + countryGUID, function(data) {
                    var items = '';
                    if (data) {
                        var result = new Array();
                        var fields = new Array();
                        result = data.split('|');
                        size = result.length;
                        for (i = 0; i < result.length; i++)//iterate over all options
                        {
                            items += result[i];
                            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() { valueInput.val($(this).attr('value')); var textSelected = $(this).text(); var divideLocator = textSelected.lastIndexOf("#"); oldGUID = textSelected.substr(divideLocator + 1); $(geographyCodeInput).val(oldGUID); textSelected = textSelected.substr(0, divideLocator); var divideLocator = textSelected.indexOf("#"); oldInput = textSelected.substr(0, divideLocator); $(geographyControlGUID).val($(geographyCodeInput).val()); textInput.val(oldInput); clear(); });
                        }
                        list.children().eq(selected).addClass('selected').text();
                    }
                }
                );
                oldText = text;
            }
        }

        function clear() {
            list.hide();
            size = 0;
            selected = 0;
        }

        textInput.keydown(function(e) {
            window.clearInterval(typingTimeout);
            if (e.which == 27)//escape
            {
                clear();
                textInput.val(oldInput);
                $(geographyCodeInput).val(oldGUID);
            }
            else if (e.which == 13)//enter
            {
                e.preventDefault();
                e.returnValue = false;
                if (list.css("display") == "none")//if the list is not visible then make a new request, otherwise hide the list
                {
                    getData(textInput.val());
                } else {
                    if (selected == 0) {
                        var textSelected = list.children().eq(selected).text();
                        var divideLocator = textSelected.lastIndexOf("#");
                        $(geographyCodeInput).val(textSelected.substr(divideLocator + 1));
                        textSelected = textSelected.substr(0, divideLocator);
                        divideLocator = textSelected.indexOf("#");
                        textInput.val(textSelected.substr(0, divideLocator));
                        valueInput.val(list.children().eq(selected).attr('value'));
                        $(geographyControlGUID).val($(geographyCodeInput).val());
                    }
                    clear();
                }
                return false;
            }
            else if (e.which == 9)//tab
            {
                if (list.css("display") != "none") {
                    var textSelected = list.children().eq(selected).text();
                    var divideLocator = textSelected.lastIndexOf("#");
                    $(geographyCodeInput).val(textSelected.substr(divideLocator + 1));
                    textSelected = textSelected.substr(0, divideLocator);
                    divideLocator = textSelected.indexOf("#");
                    textInput.val(textSelected.substr(0, divideLocator));
                    valueInput.val(list.children().eq(selected).attr('value'));
                    $(geographyControlGUID).val($(geographyCodeInput).val());
                    clear();
                }
                return true;
            }
            else if (e.which == 40 || e.which == 38)//move up, down 
            {
                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;
                }
                e.preventDefault();
                //set selected item and input values
                var textSelected = list.children().removeClass('selected').eq(selected).addClass('selected').text();
                var divideLocator = textSelected.lastIndexOf("#");
                $(geographyCodeInput).val(textSelected.substr(divideLocator + 1));
                textSelected = textSelected.substr(0, divideLocator);
                divideLocator = textSelected.indexOf("#");
                textInput.val(textSelected.substr(0, divideLocator));
                valueInput.val(list.children().eq(selected).attr('value'));
                $(geographyControlGUID).val($(geographyCodeInput).val());
            } else {
                //invalidate previous selection
                valueInput.val('');
                $(geographyCodeInput).val('');
                typingTimeout = window.setTimeout(function() { getData(textInput.val()) }, settings.timeout);
            }
        });

        $('body').click(function() {
            if (list.css("display") != "none") {
                clear();
                textInput.val(oldInput);
                $(geographyCodeInput).val(oldGUID);
            }
        });
    });
};

