var Autocompleter = {};
Autocompleter.Base = new Class({
    options: {
        minLength: 1,
        useSelection: true,
        markQuery: false,
        inheritWidth: true,
        maxChoices: 10,
        injectChoice: null,
        onSelect: Class.empty,
        onShow: Class.empty,
        onHide: Class.empty,
        customTarget: null,
        className: "autocompleter-choices",
        zIndex: 42,
        observerOptions: {},
        fxOptions: {},
        overflown: []
    },
    initialize: function (b, a) {
        this.setOptions(a);
        this.element = $(b);
        this.build();
        this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({
            delay: 200
        }, this.options.observerOptions));
        this.value = this.observer.value;
        this.queryValue = null
    },
    build: function () {
        if ($(this.options.customTarget)) {
            this.choices = this.options.customTarget
        } else {
            this.choices = new Element("ul", {
                "class": this.options.className,
                styles: {
                    zIndex: this.options.zIndex
                }
            }).injectInside(document.body);
            this.fix = new OverlayFix(this.choices)
        }
        this.fx = this.choices.effect("opacity", $merge({
            wait: false,
            duration: 50
        }, this.options.fxOptions)).addEvent("onStart", function () {
            if (this.fx.now) {
                return
            }
            this.choices.setStyle("display", "");
            this.fix.show()
        }.bind(this)).addEvent("onComplete", function () {
            if (this.fx.now) {
                return
            }
            this.choices.setStyle("display", "none");
            this.fix.hide()
        }.bind(this)).set(0);
        this.element.setProperty("autocomplete", "off").addEvent(window.ie ? "keydown" : "keypress", this.onCommand.bindWithEvent(this)).addEvent("mousedown", this.onCommand.bindWithEvent(this, [true])).addEvent("focus", this.toggleFocus.bind(this, [true])).addEvent("blur", this.toggleFocus.bind(this, [false])).addEvent("trash", this.destroy.bind(this))
    },
    destroy: function () {
        this.choices.remove()
    },
    toggleFocus: function (a) {
        this.focussed = a;
        if (!a) {
            this.hideChoices()
        }
    },
    onCommand: function (b, a) {
        if (a && this.focussed) {
            this.prefetch()
        }
        if (b.key && !b.shift) {
            switch (b.key) {
            case "enter":
                if (this.selected && this.visible) {
                    this.hideChoices()
                }
                return;
            case "up":
            case "down":
                if (this.observer.value != (this.value || this.queryValue)) {
                    this.prefetch()
                } else {
                    if (this.queryValue === null) {
                        break
                    } else {
                        if (!this.visible) {
                            this.showChoices()
                        } else {
                            this.choiceOver((b.key == "up") ? this.selected.getPrevious() || this.choices.getLast() : this.selected.getNext() || this.choices.getFirst());
                            this.setSelection()
                        }
                    }
                }
                b.stop();
                return;
            case "esc":
                this.hideChoices();
                return
            }
        }
        this.value = false
    },
    setSelection: function () {
        if (!this.options.useSelection) {
            return
        }
        var c = 0;
        var b = this.selected.inputValue.substr(c);
        if (document.getSelection) {
            this.element.value = b;
            this.element.selectionStart = c;
            this.element.selectionEnd = this.element.value.length
        } else {
            if (document.selection) {
                this.element.value = "";
                var a = document.selection.createRange();
                a.text = b;
                a.move("character", -b.length);
                a.findText(b);
                a.select()
            }
        }
        this.value = this.observer.value = this.element.value
    },
    hideChoices: function () {
        if (!this.visible) {
            return
        }
        this.visible = this.value = false;
        this.observer.clear();
        this.fx.start(0);
        this.fireEvent("onHide", [this.element, this.choices])
    },
    showChoices: function () {
        if (this.visible || !this.choices.getFirst()) {
            return
        }
        this.visible = true;
        var a = this.element.getCoordinates(this.options.overflown);
        this.choices.setStyles({
            left: a.left,
            top: a.bottom
        });
        if (this.options.inheritWidth) {
            this.choices.setStyle("width", a.width)
        }
        this.fx.start(1);
        this.choiceOver(this.choices.getFirst());
        this.fireEvent("onShow", [this.element, this.choices])
    },
    prefetch: function () {
        if (this.element.value.length < this.options.minLength) {
            this.hideChoices()
        } else {
            if (this.element.value == this.queryValue) {
                this.showChoices()
            } else {
                this.query()
            }
        }
    },
    updateChoices: function (a) {
        this.choices.empty();
        this.selected = null;
        if (!a || !a.length) {
            return
        }
        if (this.options.maxChoices < a.length) {
            a.length = this.options.maxChoices
        }
        a.each(this.options.injectChoice ||
        function (b, c) {
            var d = new Element("li").setHTML(this.markQueryValue(b));
            if (c == 0) {
                d.className = "first"
            }
            d.inputValue = b;
            this.addChoiceEvents(d).injectInside(this.choices)
        }, this);
        this.showChoices()
    },
    choiceOver: function (a) {
        if (this.selected) {
            this.selected.removeClass("autocompleter-selected")
        }
        this.selected = a.addClass("autocompleter-selected")
    },
    choiceSelect: function (a) {
        this.observer.value = this.element.value = a.inputValue;
        this.hideChoices();
        this.fireEvent("onSelect", [this.element], 20)
    },
    markQueryValue: function (a) {
        return (this.options.markQuery && this.queryValue) ? a.replace(new RegExp("^(" + this.queryValue.escapeRegExp() + ")", "i"), '<span class="autocompleter-queried">$1</span>') : a
    },
    addChoiceEvents: function (a) {
        return a.addEvents({
            //mouseover: this.choiceOver.bind(this, [a]),
            //mousedown: this.choiceSelect.bind(this, [a])
        })
    }
});
Autocompleter.Base.implement(new Events);
Autocompleter.Base.implement(new Options);
Autocompleter.Local = Autocompleter.Base.extend({
    options: {
        minLength: 0,
        filterTokens: null
    },
    initialize: function (b, c, a) {
        this.parent(b, a);
        this.tokens = c;
        if (this.options.filterTokens) {
            this.filterTokens = this.options.filterTokens.bind(this)
        }
    },
    query: function () {
        this.hideChoices();
        this.queryValue = this.element.value;
        this.updateChoices(this.filterTokens())
    },
    filterTokens: function (a) {
        var b = new RegExp("^" + this.queryValue.escapeRegExp(), "i");
        return this.tokens.filter(function (c) {
            return b.test(c)
        })
    }
});
Autocompleter.Ajax = {};
Autocompleter.Ajax.Base = Autocompleter.Base.extend({
    options: {
        postVar: "value",
        postData: {},
        ajaxOptions: {},
        onRequest: Class.empty,
        onComplete: Class.empty
    },
    initialize: function (c, b, a) {
        this.parent(c, a);
        this.ajax = new Ajax(b, $merge({
            autoCancel: true
        }, this.options.ajaxOptions));
        this.ajax.addEvent("onComplete", this.queryResponse.bind(this));
        this.ajax.addEvent("onFailure", this.queryResponse.bind(this, [false]))
    },
    query: function () {
        var a = $extend({}, this.options.postData);
        a[this.options.postVar] = this.element.value;
        this.fireEvent("onRequest", [this.element, this.ajax]);
        this.ajax.request(a)
    },
    queryResponse: function (a) {
        this.value = this.queryValue = this.element.value;
        this.selected = false;
        this.hideChoices();
        this.fireEvent(a ? "onComplete" : "onFailure", [this.element, this.ajax], 20)
    }
});
Autocompleter.Ajax.Json = Autocompleter.Ajax.Base.extend({
    queryResponse: function (a) {
        this.parent(a);
        var b = Json.evaluate(a || false);
        if (!b || !b.length) {
            return
        }
        this.updateChoices(b)
    }
});
Autocompleter.Ajax.Xhtml = Autocompleter.Ajax.Base.extend({
    options: {
        parseChoices: null
    },
    queryResponse: function (a) {
        this.parent(a);
        if (!a) {
            return
        }
        this.choices.setHTML(a).getChildren().each(this.options.parseChoices || this.parseChoices, this);
        this.showChoices()
    },
    parseChoices: function (a) {
        var b = a.innerHTML;
        a.inputValue = b;
        a.setHTML(this.markQueryValue(b))
    }
});
var OverlayFix = new Class({
    initialize: function (a) {
        this.element = $(a);
        if (window.ie) {
            this.element.addEvent("trash", this.destroy.bind(this));
            this.fix = new Element("iframe", {
                properties: {
                    frameborder: "0",
                    scrolling: "no",
                    src: "javascript:false;"
                },
                styles: {
                    position: "absolute",
                    border: "none",
                    display: "none",
                    filter: "progid:DXImageTransform.Microsoft.Alpha(opacity=0)"
                }
            }).injectAfter(this.element)
        }
    },
    show: function () {
        if (this.fix) {
            this.fix.setStyles($extend(this.element.getCoordinates(), {
                display: "",
                zIndex: (this.element.getStyle("zIndex") || 1) - 1
            }))
        }
        return this
    },
    hide: function () {
        if (this.fix) {
            this.fix.setStyle("display", "none")
        }
        return this
    },
    destroy: function () {
        this.fix.remove()
    }
});
