(function($) {

$.fn.suggestions = function(callbackUrl, settings) {
    function escapeHTML(s) {
        return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
    }
    
    var conf = $.extend({
        autoSize: true,
        autoSizeMinWidth: 150,
        fadeSpeed: 0,
        updateInterval: 500,
        selectedItemClass: "selected",
        keywordHighlightClass: "keyword",
        submitOnCommit: false,
        submitButton: "",
        scrollIntoView: false,
        prompt: ""
    }, settings);
    
    $.each(this, function() {
        var root = $(this);
        
        var input;
        if (root.is("input"))
            input = root;
        else
            input = $("input[type=text]", root);
        
        var container, list = $(".Suggestions", root);
        if (list.length == 0) {
            container = list = $("<ul class=\"Suggestions\" style=\"display: none; position: absolute;\"></ul>");
            input.after(list);
        } else {
            container = $(".SuggestionContainer", root);
            if (container.length == 0)
                container = list;
        }
        
        if (conf.autoSize)
            container.width(Math.max(conf.autoSizeMinWidth, input.outerWidth()));
        
        var lastValue = "", selected = null, timer = null, req = null;
        
        function commitText(text) {
            input.val(text);
            hideSuggestions();
            
            if (conf.submitOnCommit) {
                if (conf.submitButton)
                    $(conf.submitButton)[0].click();
                else
                    input.parent("form").submit();
            }
        }
        
        function checkEnter(e) {
            if (e.which == 13 && selected) {
                if (selected)
                    commitText(selected.text());
                
                e.preventDefault();
            }
        }
        
        function itemClick() {
            commitText($(this).text());
        }
        
        function check(e) {
            var key = e ? (e.which || e.keyCode) : null;
            
            if (key == 38 || key == 40) {
                if (selected) {
                    var n = selected[key == 38 ? "prev" : "next"]();
                    if (n.length) {
                        selected.removeClass("selected");
                        n.addClass("selected");
                        selected = n;
                    }
                } else {
                    selected = $("li:" + (key == 38 ? "last" : "first"), list).addClass("selected");
                    if (selected.length == 0)
                        selected = null;
                }
                
                if (selected && conf.scrollIntoView)
                    selected[0].scrollIntoView();
                
                e.preventDefault();
            } else if (key == 27) {
                hideSuggestions();
                
                e.preventDefault();
            } else if (key != 13) {
                if (!timer) {
                    if (!req)
                        request();
                    else
                        timer = setTimeout(request, conf.updateInterval);
                }
            }
        }
        
        function requestComplete(data) {
            req = null;
            
            if (!data || data.error)
                return;
            
            list.empty();
            container.fadeIn(conf.fadeSpeed);
            
            $.each(data.suggestions, function() {
                var s = escapeHTML(this).replace(new RegExp("(" + escapeHTML(data.search) + ")", "i"), "<span class=\"" + conf.keywordHighlightClass + "\">$1</span>");
                var item = $("<li>" + s + "</li>");
                list.append(item);
                item.click(itemClick).hover(function() { $(this).addClass("hover"); }, function() { $(this).removeClass("hover"); });
            });
            
            selected = null;
            
            check();
        }
        
        function request() {
            if (timer)
                clearTimeout(timer);
            timer = null;
            
            if (input.val() != lastValue) {
                lastValue = input.val();
                
                if (req)
                    req.abort();
                
                if (lastValue == "")
                    hideSuggestions();
                else
                    req = $.get(callbackUrl, {search: input.val()}, requestComplete, "json");
            }
        }
        
        function hideSuggestions() {
            if (req)
                req.abort();
            req = null;
            
            if (timer)
                clearTimeout(timer);
            timer = null;
            
            container.fadeOut(conf.fadeSpeed);
            selected = null;
        }
        container.click(function(e) { e.stopPropagation(); });
        $(document).click(hideSuggestions);
        input.keypress(checkEnter).keyup(check).blur(hideSuggestions);
    });
};

$.fn.prompt = function(prompt, promptClass) {
    $.each(this, function() {
        var input = $(this);
        var pos = input.position();
        var h = input.outerHeight() + "px";
        
        var el = $("<div></div>");
        el.addClass(promptClass || "prompt");
        el.css({position: "absolute", overflow: "hidden", zIndex: 10, left: pos.left + 1 + "px",
            top: pos.top + ($.browser.msie ? -3 : 0) + "px",
            width: input.outerWidth() + "px", height: h, lineHeight: h, cursor: "text", display: "none",
            paddingLeft: input.css("paddingLeft")});
        el.text(prompt);
        
        function focus() {
            el.hide();
        }
        
        function blur() {
            if (!input.val())
                el.show();
        }
        
        function click() {
            el.hide();
            input.focus();
        }
        
        el.click(click);
        input.focus(focus).blur(blur).after(el);
        
        if (!input.val())
            el.show();
    });
};

})(jQuery);