(function (b) {
    b.fn.extend({
        autocomplete: function (a, c) {
            var p = "string" == typeof a, c = b.extend({}, b.Autocompleter.defaults, { url: p ? a : null, data: p ? null : a, delay: p ? b.Autocompleter.defaults.delay : 10, max: c && !c.scroll ? 10 : 150 }, c);
            c.highlight = c.highlight || function (a) {
                return a
            };
            c.formatMatch = c.formatMatch || c.formatItem;
            return this.each(function () {
                new b.Autocompleter(this, c)
            })
        },
        result: function (a) {
            return this.bind("result", a)
        },
        search: function (a) {
            return this.trigger("search", [a])
        },
        flushCache: function () {
            return this.trigger("flushCache")
        },
        setOptions: function (a) {
            return this.trigger("setOptions", [a])
        },
        unautocomplete: function () {
            return this.trigger("unautocomplete")
        }
    });
    b.Autocompleter = function (a, c) {
        function p() {
            var t = k.selected();
            if (!t) return !1;
            var d = t.result;
            l = d;
            if (c.multiple) {
                var e = o(f.val());
                if (1 < e.length) {
                    var h = c.multipleSeparator.length, i = b(a).selection().start, j, g = 0;
                    b.each(e, function (a, c) {
                        g += c.length;
                        if (i <= g) return j = a, !1;
                        g += h
                    });
                    e[j] = d;
                    d = e.join(c.multipleSeparator)
                }
                d += c.multipleSeparator
            }
            f.val(d);
            n();
            f.trigger("result", [t.data, t.value]);
            return !0
        }
        function m(a, b) {
            if (u == h.DEL) k.hide();
            else {
                var i = f.val();
                if (b || i != l) l = i, i = g(i), i.length >= c.minChars ? (f.addClass(c.loadingClass), c.matchCase || (i = i.toLowerCase()), e(i, d, n)) : (f.removeClass(c.loadingClass), k.hide())
            }
        }
        function o(a) {
            return !a ? [""] : !c.multiple ? [b.trim(a)] : b.map(a.split(c.multipleSeparator), function (c) {
                return b.trim(a).length ? b.trim(c) : null
            })
        }
        function g(d) {
            if (!c.multiple) return d;
            var i = o(d);
            if (1 == i.length) return i[0];
            i = b(a).selection().start;
            i = i == d.length ? o(d) : o(d.replace(d.substring(i), ""));
            return i[i.length - 1]
        }
        function n() {
            k.visible();
            k.hide();
            clearTimeout(q);
            f.removeClass(c.loadingClass);
            c.mustMatch && f.search(function (a) {
                a || (c.multiple ? (a = o(f.val()).slice(0, -1), f.val(a.join(c.multipleSeparator) + (a.length ? c.multipleSeparator : ""))) : (f.val(""), f.trigger("result", null)))
            })
        }
        function d(d, e) {
            if (e && e.length && i) {
                f.removeClass(c.loadingClass);
                k.display(e, d);
                var j = e[0].value;
                c.autoFill && (g(f.val()).toLowerCase() == d.toLowerCase() && u != h.BACKSPACE) && (f.val(f.val() + j.substring(g(l).length)), b(a).selection(l.length, l.length + j.length));
                k.show()
            } else n()
        }
        function e(d, i, e) {
            c.matchCase || (d = d.toLowerCase());
            var h = j.load(d);
            if (h && h.length) i(d, h);
            else if ("string" == typeof c.url && 0 < c.url.length) {
                var f = { timestamp: +new Date };
                if (typeof c.extraParams == "function")
                    f = $.extend({}, f, c.extraParams());
                else {
                    b.each(c.extraParams, function (a, d) {
                        f[a] = "function" == typeof d ? d() : d
                    });
                }
                b.ajax({
                    mode: "abort",
                    port: "autocomplete" + a.name,
                    dataType: c.dataType,
                    url: c.url,
                    data: b.extend({ q: g(d), limit: c.max }, f),
                    success: function (a) {
                        var e;
                        if (!(e = c.parse && c.parse(a))) {
                            e = a;
                            a = [];
                            "string" != typeof e && (e = "");
                            e = e.split("\n");
                            for (var h = 0; h < e.length; h++) {
                                var f = b.trim(e[h]);
                                f && (f = f.split("|"), a[a.length] = {
                                    data: f,
                                    value: f[0],
                                    result: c.formatResult && c.formatResult(f, f[0]) || f[0]
                                })
                            }
                            e = a
                        }
                        a = e;
                        j.add(d, a);
                        i(d, a)
                    }
                })
            }
            else k.emptyList(), e(d)
        }
        var h = {
            UP: 38,
            DOWN: 40,
            DEL: 46,
            TAB: 9,
            RETURN: 13,
            ESC: 27,
            COMMA: 188,
            PAGEUP: 33,
            PAGEDOWN: 34,
            BACKSPACE: 8
        }, f = b(a).attr("autocomplete", "off").addClass(c.inputClass), q, l = "", j = b.Autocompleter.Cache(c), i = 0, u, s = { mouseDownOnSelect: !1 }, k = b.Autocompleter.Select(c, a, p, s), r;
        b.browser.opera && b(a.form).bind("submit.autocomplete", function () {
            if (r) return r = !1
        });
        f.bind((b.browser.opera ? "keypress" : "keydown") + ".autocomplete", function (a) {
            i = 1;
            u = a.keyCode;
            switch (a.keyCode) {
                case h.UP:
                    a.preventDefault();
                    k.visible() ? k.prev() : m(0, !0);
                    break;
                case h.DOWN:
                    a.preventDefault();
                    k.visible() ? k.next() : m(0, !0);
                    break;
                case h.PAGEUP:
                    a.preventDefault();
                    k.visible() ? k.pageUp() : m(0, !0);
                    break;
                case h.PAGEDOWN:
                    a.preventDefault();
                    k.visible() ? k.pageDown() : m(0, !0);
                    break;
                case c.multiple && "," == b.trim(c.multipleSeparator) && h.COMMA:
                case h.TAB:
                case h.RETURN:
                    if (p()) return a.preventDefault(), r = !0, !1;
                    break;
                case h.ESC:
                    k.hide();
                    break;
                default:
                    clearTimeout(q), q = setTimeout(m, c.delay)
            }
        }).focus(function () {
            i++
        }).blur(function () {
            i = 0;
            s.mouseDownOnSelect || (clearTimeout(q), q = setTimeout(n, 200))
        }).click(function () {
            1 < i++ && !k.visible() && m(0, !0)
        }).bind("search", function () {
            function a(c, e) {
                var i;
                if (e && e.length)
                    for (var b = 0; b < e.length; b++)
                        if (e[b].result.toLowerCase() == c.toLowerCase()) {
                            i = e[b];
                            break
                        }
                "function" == typeof d ? d(i) : f.trigger("result", i && [i.data, i.value])
            }
            var d = 1 < arguments.length ? arguments[1] : null;
            b.each(o(f.val()), function (d, c) {
                e(c, a, a)
            })
        }).bind("flushCache", function () {
            j.flush()
        }).bind("setOptions", function (a, d) {
            b.extend(c, d);
            "data" in d && j.populate()
        }).bind("unautocomplete", function () {
            k.unbind();
            f.unbind();
            b(a.form).unbind(".autocomplete")
        })
    };
    b.Autocompleter.defaults = {
        inputClass: "ac_input",
        resultsClass: "ac_results",
        loadingClass: "ac_loading",
        minChars: 1,
        delay: 400,
        matchCase: !1,
        matchSubset: !0,
        matchContains: !0,
        cacheLength: 10,
        max: 100,
        mustMatch: !1,
        extraParams: {},
        selectFirst: !0,
        formatItem: function (a) {
            return a[0]
        },
        formatMatch: null,
        autoFill: !1,
        width: 0,
        multiple: !1,
        multipleSeparator: ", ",
        highlight: function (a, c) {
            return a.replace(RegExp("(?![^&;]+;)(?!<[^<>]*)(" + c.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>")
        },
        scroll: !0, scrollHeight: 180
    };
    b.Autocompleter.Cache = function (a) {
        function c(d, c) {
            a.matchCase || (d = d.toLowerCase());
            var b = d.indexOf(c);
            "word" == a.matchContains && (b = d.toLowerCase().search("\\b" + c.toLowerCase()));
            return -1 == b ? !1 : 0 == b || a.matchContains
        }
        function p(d, c) {
            n > a.cacheLength && o();
            g[d] || n++;
            g[d] = c
        } function m() {
            if (!a.data) return !1;
            var d = {}, c = 0;
            a.url || (a.cacheLength = 1);
            d[""] = [];
            for (var h = 0, f = a.data.length; h < f; h++) {
                var g = a.data[h], g = "string" == typeof g ? [g] : g, l = a.formatMatch(g, h + 1, a.data.length);
                if (!1 !== l) {
                    var j = l.charAt(0).toLowerCase();
                    d[j] || (d[j] = []);
                    g = {
                        value: l,
                        data: g,
                        result: a.formatResult && a.formatResult(g) || l
                    };
                    d[j].push(g);
                    c++ < a.max && d[""].push(g)
                }
            }
            b.each(d, function (d, c) {
                a.cacheLength++;
                p(d, c)
            })
        }
        function o() {
            g = {};
            n = 0
        }
        var g = {}, n = 0;
        setTimeout(m, 25);
        return {
            flush: o,
            add: p,
            populate: m,
            load: function (d) {
                if (!a.cacheLength || !n) return null;
                if (!a.url && a.matchContains) {
                    var e = [], h;
                    for (h in g)
                        if (0 < h.length) {
                            var f = g[h];
                            b.each(f, function (a, b) {
                                c(b.value, d) && e.push(b)
                            })
                        }
                    return e
                }
                if (g[d]) return g[d];
                if (a.matchSubset)
                    for (h = d.length - 1; h >= a.minChars; h--)
                        if (f = g[d.substr(0, h)]) return e = [], b.each(f, function (a, b) {
                            c(b.value, d) && (e[e.length] = b)
                        }), e;
                return null
            }
        }
    };
    b.Autocompleter.Select = function (a, c, p, m) {
        function o(a) {
            for (a = a.target; a && "LI" != a.tagName;)
                a = a.parentNode;
            return !a ? [] : a
        }
        function g(c) {
            d.slice(e, e + 1).removeClass(n.ACTIVE);
            e += c;
            0 > e ? e = d.size() - 1 : e >= d.size() && (e = 0);
            c = d.slice(e, e + 1).addClass(n.ACTIVE);
            if (a.scroll) {
                var b = 0;
                d.slice(0, e).each(function () {
                    b += this.offsetHeight
                });
                b + c[0].offsetHeight - j.scrollTop() > j[0].clientHeight ? j.scrollTop(b + c[0].offsetHeight - j.innerHeight()) : b < j.scrollTop() && j.scrollTop(b)
            }
        }
        var n = { ACTIVE: "ac_over" }, d, e = -1, h, f = "", q = !0, l, j;
        return {
            display: function (i, g) {
                q && (l = b("<div/>").hide().addClass(a.resultsClass).css("position", "absolute").appendTo(document.body), j = b("<ul/>").appendTo(l).mouseover(function (a) {
                    o(a).nodeName && "LI" == o(a).nodeName.toUpperCase() && (e = b("li", j).removeClass(n.ACTIVE).index(o(a)), b(o(a)).addClass(n.ACTIVE))
                }).click(function (a) {
                    b(o(a)).addClass(n.ACTIVE);
                    p();
                    c.focus();
                    return !1
                }).mousedown(function () {
                    m.mouseDownOnSelect = !0
                }).mouseup(function () {
                    m.mouseDownOnSelect = !1
                }), 0 < a.width && l.css("width", a.width), q = !1);
                h = i;
                f = g;
                j.empty();
                for (var s = a.max && a.max < h.length ? a.max : h.length, k = 0; k < s; k++)
                    if (h[k]) {
                        var r = a.formatItem(h[k].data, k + 1, s, h[k].value, f);
                        !1 !== r && (r = b("<li/>").html(a.highlight(r, f)).addClass(0 == k % 2 ? "ac_even" : "ac_odd").appendTo(j)[0], b.data(r, "ac_data", h[k]))
                    }
                d = j.find("li");
                a.selectFirst && (d.slice(0, 1).addClass(n.ACTIVE), e = 0);
                b.fn.bgiframe && j.bgiframe()
            },
            next: function () {
                g(1)
            },
            prev: function () {
                g(-1)
            },
            pageUp: function () {
                0 != e && 0 > e - 8 ? g(-e) : g(-8)
            },
            pageDown: function () {
                e != d.size() - 1 && e + 8 > d.size() ? g(d.size() - 1 - e) : g(8)
            },
            hide: function () {
                l && l.hide();
                d && d.removeClass(n.ACTIVE);
                e = -1
            },
            visible: function () {
                return l && l.is(":visible")
            },
            current: function () {
                return this.visible() && (d.filter("." + n.ACTIVE)[0] || a.selectFirst && d[0])
            },
            show: function () {
                var e = b(c).offset();
                l.css({ width: "string" == typeof a.width || 0 < a.width ? a.width : b(c).width(), top: e.top + c.offsetHeight, left: e.left }).show();
                if (a.scroll && (j.scrollTop(0), j.css({ maxHeight: a.scrollHeight, overflow: "auto" }), b.browser.msie && "undefined" === typeof document.body.style.maxHeight)) {
                    var f = 0;
                    d.each(function () {
                        f += this.offsetHeight
                    }
					);
                    e = f > a.scrollHeight;
                    j.css("height", e ? a.scrollHeight : f);
                    e || d.width(j.width() - parseInt(d.css("padding-left")) - parseInt(d.css("padding-right")))
                }
            },
            selected: function () {
                var a = d && d.filter("." + n.ACTIVE).removeClass(n.ACTIVE);
                return a && a.length && b.data(a[0], "ac_data")
            },
            emptyList: function () {
                j && j.empty()
            },
            unbind: function () {
                l && l.remove()
            }
        }
    };
    b.fn.selection = function (a, c) {
        if (void 0 !== a) return this.each(function () {
            if (this.createTextRange) {
                var b = this.createTextRange();
                void 0 === c || a == c ? b.move("character", a) : (b.collapse(!0), b.moveStart("character", a), b.moveEnd("character", c));
                b.select()
            }
            else this.setSelectionRange ? this.setSelectionRange(a, c) : this.selectionStart && (this.selectionStart = a, this.selectionEnd = c)
        });
        var b = this[0];
        if (b.createTextRange) {
            var m = document.selection.createRange(), o = b.value, g = m.text.length;
            m.text = "<->";
            m = b.value.indexOf("<->");
            b.value = o;
            this.selection(m, m + g);
            return {
                start: m,
                end: m + g
            }
        }
        if (void 0 !== b.selectionStart) return {
            start: b.selectionStart,
            end: b.selectionEnd
        }
    }
})(jQuery);