//-------------------- lazyFilter.js

function LazyFilter(id, textInputID, suggestContainerElementID, triggerID) {
    this.id = id;
    this.select = document.getElementById(id);
    this.input = document.getElementById(textInputID);
    this.suggestContainer = document.getElementById(suggestContainerElementID);
    this.triggerID = triggerID;
    this.injectTriggerBehaviour();
    var browser = navigator.userAgent.toLowerCase();
    this.isIE = browser.indexOf("msie") != -1;
    this.isOpera = browser.indexOf("opera") != -1;
    this.setOptions();
    this.suggestions = [];
    this.seeMoreSuggestionsIndex = -1;
    this.showAll = false;
    this.selectedIndex = 0;
    this.currentValue = this.select.selectedIndex >= 0 ? this.select.options[this.select.selectedIndex].value : '';
    this.currentText = this.select.selectedIndex >= 0 ? this.select.options[this.select.selectedIndex].text : '';
    this.mouseOut = true;
    this.show = false;

    // this is so that Firefox fires onblur on the panel div
    this.suggestContainer.tabIndex = 0;

    this.injectFilterBehavior();
}

LazyFilter.prototype = {

    setOptions: function() {
        this.options = {
            textInputClassName: 'suggestInput',
            textInputOnTypeClassName: 'suggestInputOnType',
            suggestDivClassName: 'suggestDiv',
            suggestionClassName: 'suggestion',
            seeMoreSuggestionsClassName: 'seeMoreSuggestions',
            selectionClassName: 'selection',
            matchClassName: 'match',
            matchTextWidth: false,
            matchAnywhere: true,
            ignoreCase: true
        };
    },

    injectTriggerBehaviour: function() {
        this.trigger = document.getElementById(this.triggerID);
        var thisObj = this;
        jQuery(this.trigger).click(function(e) { thisObj.handleClick(e) }).mouseover(function(e) { thisObj.mouseOut = false }).mouseout(function(e) { thisObj.mouseOut = true });
    },

    injectFilterBehavior: function() {

        this.createSuggestionsDiv();

        this.update();

        if (this.isIE)
            this.input.autocomplete = "off";

        this.addKeyHandling();
    },

    update: function() {
        if (!this.showAll)
            this.suggestionsDiv.style.height = '';
        if (this.show) {
            this.positionSuggestContainer();
            this.updateSuggestions();
            this.show = false;
        }
        this.handlingRequest = false;
    },

    handleTextInput: function() {
        this.lastRequestString = this.input.value;
        if (this.timeoutID) {
            clearTimeout(this.timeoutID);
        }
        var thisObj = this;
        this.timeoutID = setTimeout(function() { thisObj.sendRequestForSuggestions() }, 250);
    },

    moveSelectionUp: function() {
        if (this.selectedIndex > 0) {
            this.updateSelection(this.selectedIndex - 1);
        }
    },

    moveSelectionDown: function() {
        if (this.selectedIndex < (this.suggestions.length - 1)) {
            this.updateSelection(this.selectedIndex + 1);
        }
    },

    updateSelection: function(n) {
        var span = document.getElementById(this.id + "_" + this.selectedIndex);

        var suggestionClassName;

        if (this.selectedIndex == this.seeMoreSuggestionsIndex)
            suggestionClassName = this.options.seeMoreSuggestionsClassName;
        else
            suggestionClassName = this.options.suggestionClassName;

        if (span) {
            span.className = suggestionClassName;
        }

        this.selectedIndex = n;

        if (this.selectedIndex == this.seeMoreSuggestionsIndex)
            suggestionClassName = this.options.seeMoreSuggestionsClassName;
        else
            suggestionClassName = this.options.suggestionClassName;

        var span = document.getElementById(this.id + "_" + this.selectedIndex);

        if (span) {
            span.className = suggestionClassName + ' ' + this.options.selectionClassName;
        }
    },

    sendRequestForSuggestions: function() {

        if (this.handlingRequest) {
            this.pendingRequest = true;
            return;
        }

        this.handlingRequest = true;

        if (typeof (window.LazyFilter_onFocus) == 'function') {
            window.LazyFilter_onFocus(this);
        }

        this.show = true;

    },

    updateSuggestions: function() {

        this.createSuggestions();

        this.updateSuggestionsDiv();
        this.showSuggestions();

        this.selectedIndex = -1;
        for (var i = 0; i < this.suggestions.length; i++) {
            if (this.suggestions[i].value == this.currentValue) {
                this.selectedIndex = i;
            }
        }

        if (this.selectedIndex == -1 && this.select.selectedIndex > -1) {
            this.selectedIndex = this.select.selectedIndex;
            this.currentValue = this.select.options[this.select.selectedIndex].value;
        }

        this.updateSelection(this.selectedIndex);

        if (this.pendingRequest) {
            this.pendingRequest = false;
            this.lastRequestString = this.input.value;
            this.sendRequestForSuggestions();
        }
    },

    createSuggestions: function() {

        this.suggestions = [];
        this.seeMoreSuggestionsIndex = -1
        for (var i = 0; i < this.select.options.length; i++) {

            var option = this.select.options[i];
            var strText = option.text; //.replace(/\s/,' ');
            var strValue = option.value;
            if (strValue == '+') {
                this.seeMoreSuggestionsIndex = i;
            }
            this.suggestions.push({ text: strText, value: strValue });

        }

    },

    reset: function() {
        this.select.length = 0;
        this.select.options.add(new Option(this.currentText, this.currentValue));
    },

    setSelectionRange: function(input, selectionStart, selectionEnd) {
        if (input.setSelectionRange) {
            input.focus();
            input.setSelectionRange(selectionStart, selectionEnd);
        }
        else if (input.createTextRange) {
            var range = input.createTextRange();
            range.collapse(true);
            range.moveEnd('character', selectionEnd);
            range.moveStart('character', selectionStart);
            range.select();
        }
    },

    setCaretToBegin: function(input) {
        this.setSelectionRange(input, 0, 0);
    },


    setInputFromSelection: function() {
        var suggestion = this.suggestions[this.selectedIndex];
        if (!suggestion) {
            this.hideSuggestions();
        } else if (suggestion.value == '+') {
            this.suggestionsDiv.style.height = jQuery(this.suggestionsDiv).height() + "px";

            this.input.focus();
            this.setCaretToBegin(this.input);
            this.showAll = true;
            this.sendRequestForSuggestions();

            if (typeof (window.LazyFilter_onSeeMore) == 'function') {
                window.LazyFilter_onSeeMore(this);
            }
        } else {
            var changed = false;
            for (var i = 0; i < this.select.options.length; i++) {
                if (this.select.options[i].value == suggestion.value) {
                    this.select.selectedIndex = i;
                    if (suggestion.value != this.currentValue) {
                        this.currentValue = suggestion.value;
                        this.currentText = suggestion.text;
                        changed = true;
                    }
                }
            }

            this.hideSuggestions();

            // call LazyFilter_onClick "event" in calling page
            if (typeof (window.LazyFilter_onClick) == 'function') {
                window.LazyFilter_onClick(this);
            }

            // call LazyFilter_onChange "event" in calling page
            if (changed && typeof (window.LazyFilter_onChange) == 'function') {
                window.LazyFilter_onChange(this);
            }
        }
    },

    showSuggestions: function() {
        var divStyle = this.suggestContainer.style;
        if (divStyle.display != '') {
            this.positionSuggestContainer();
            divStyle.display = '';
        }
        this.input.focus();
    },

    positionSuggestContainer: function() {
        if (this.trigger) {
            textPos = jQuery(this.trigger).position();

            var divStyle = this.suggestContainer.style;
            divStyle.left = textPos.left + "px";
            divStyle.top = (textPos.top + this.trigger.offsetHeight) + "px";
        }
        else {
            var textPos = jQuery(this.input).position();
            var divStyle = this.suggestContainer.style;
            divStyle.left = textPos.left + "px";
            divStyle.top = (textPos.top + this.input.offsetHeight) + "px";

            if (this.options.matchTextWidth) {
                divStyle.width = jQuery(this.input).width() + "px";
            }
        }
    },

    hideSuggestions: function() {
        this.showAll = false;
        this.suggestContainer.style.display = 'none';
        this.input.value = this.input.defaultValue;
    },

    createSuggestionsDiv: function() {
        this.suggestionsDiv = document.createElement("div");
        this.suggestionsDiv.id = this.id + '_suggestions';
        this.suggestionsDiv.className = this.options.suggestDivClassName;
        var thisObj = this;
        jQuery(this.suggestContainer).mouseover(function(e) { thisObj.mouseoverDivHandler(e) }).mouseout(function(e) { thisObj.mouseoutDivHandler(e) });
        this.suggestContainer.appendChild(this.suggestionsDiv);
    },

    mouseoverDivHandler: function(e) {
        this.mouseOut = false;
    },

    mouseoutDivHandler: function(e) {
        this.mouseOut = true;
    },

    updateSuggestionsDiv: function() {
        this.suggestionsDiv.innerHTML = "";
        var suggestLines = this.createSuggestionSpans();
        for (var i = 0; i < suggestLines.length; i++)
            this.suggestionsDiv.appendChild(suggestLines[i]);
    },

    createSuggestionSpans: function() {
        var regExpFlags = "";
        if (this.options.ignoreCase)
            regExpFlags = 'i';
        var startRegExp = "^";
        if (this.options.matchAnywhere)
            startRegExp = '';

        var regExp = new RegExp(startRegExp + this.lastRequestString, regExpFlags);

        var suggestionSpans = [];
        for (var i = 0; i < this.suggestions.length; i++)
            suggestionSpans.push(this.createSuggestionSpan(i, regExp))

        return suggestionSpans;
    },

    createSuggestionSpan: function(n, regExp) {
        var suggestion = this.suggestions[n];

        var suggestionSpan = document.createElement("span");
        if (n == this.seeMoreSuggestionsIndex)
            suggestionSpan.className = this.options.seeMoreSuggestionsClassName;
        else
            suggestionSpan.className = this.options.suggestionClassName;

        suggestionSpan.id = this.id + "_" + n;
        var thisObj = this;
        jQuery(suggestionSpan).mouseover(function(e) { thisObj.mouseoverHandler(e) }).click(function(e) { thisObj.itemClickHandler(e) })

        var textValues = this.splitTextValues(suggestion.text,
                                             this.lastRequestString.length,
                                             regExp);

        if (textValues.start != '') {
            suggestionSpan.appendChild(document.createTextNode(textValues.start));
        }

        if (textValues.mid != '') {
            var textMatchSpan = document.createElement("span");
            textMatchSpan.id = this.id + "_match_" + n;
            textMatchSpan.className = this.options.matchClassName;
            jQuery(textMatchSpan).mouseover(function(e) { thisObj.mouseoverHandler(e) }).click(function(e) { thisObj.itemClickHandler(e) });

            textMatchSpan.appendChild(document.createTextNode(textValues.mid));
            suggestionSpan.appendChild(textMatchSpan);
        }

        if (textValues.end != '') {
            suggestionSpan.appendChild(document.createTextNode(textValues.end));
        }

        return suggestionSpan;
    },

    mouseoverHandler: function(e) {
        var src = e.srcElement ? e.srcElement : e.target;
        var index = parseInt(src.id.substring(src.id.lastIndexOf('_') + 1));
        this.updateSelection(index);
    },

    itemClickHandler: function(e) {
        this.mouseoverHandler(e);
        this.setInputFromSelection();
    },

    splitTextValues: function(text, len, regExp) {
        var startPos = text.search(regExp);
        var matchText = startPos < 0 ? "" : text.substring(startPos, startPos + len);
        var startText = startPos <= 0 ? "" : text.substring(0, startPos);
        var endText = startPos < 0 ? text : text.substring(startPos + len);
        return { start: startText, mid: matchText, end: endText };
    },

    getElementContent: function(element) {
        return element.firstChild.data;
    },

    handleClick: function() {
        if (this.suggestContainer.style.display == '') {
            this.hideSuggestions();
            this.reset();
            this.input.blur();
        } else {
            //this.input.value = '';
            this.lastRequestString = '';
            this.sendRequestForSuggestions();
        }
    },

    addKeyHandling: function() {
        var thisObj = this;
        jQuery(this.input).keyup(function(e) { thisObj.keyupHandler(e) }).keydown(function(e) { thisObj.keydownHandler(e) }).blur(function(e) { thisObj.onblurHandler(e) });
        if (this.isOpera)
            jQuery(this.input).keypress(function(e) { thisObj.keyupHandler(e) });
    },

    keydownHandler: function(e) {
        var upArrow = 38;
        var downArrow = 40;
        var enterKey = 13;

        if (e.keyCode == upArrow) {
            this.moveSelectionUp();
        }
        else if (e.keyCode == downArrow) {
            this.moveSelectionDown();
        }
        else if (e.keyCode == enterKey) {
            e.preventDefault();
            return false;
        }
        else {
            if (this.input.value == this.input.defaultValue) {
                this.input.value = ''
                this.input.className = this.options.textInputOnTypeClassName;
            }
        }
    },

    keyupHandler: function(e) {

        if (!this.handledSpecialKeys(e)) {

            this.handleTextInput();

            if (this.input.value == '') {
                this.input.value = this.input.defaultValue;
                this.setCaretToBegin(this.input);
                this.input.className = this.options.textInputClassName;

            }

        }
    },

    handledSpecialKeys: function(e) {
        var enterKey = 13;
        var upArrow = 38;
        var downArrow = 40;

        if (e.keyCode == upArrow || e.keyCode == downArrow) {
            return true;
        }
        else if (e.keyCode == enterKey) {
            this.setInputFromSelection();
            this.input.blur();
            return true;
        }

        return false;
    },

    moveCaretToEnd: function() {
        var pos = this.input.value.length;
        if (this.input.setSelectionRange) {
            this.input.setSelectionRange(pos, pos);
        }
        else if (this.input.createTextRange) {
            var m = this.input.createTextRange();
            m.moveStart('character', pos);
            m.collapse();
            m.select();
        }
    },

    onblurHandler: function(e) {
        if (this.mouseOut) {
            this.hideSuggestions();
            this.reset();
            this.input.value = this.input.defaultValue;
            this.input.className = this.options.textInputClassName;
        } else if (this.suggestContainer.style.display == '') {
            var thisObj = this;
            window.setTimeout(function() { thisObj.input.focus() }, 1); // Firefox can't handle a direct input.focus() from within the corresponding onblur
        }
    }
}

