//
// extensions to script.aculo.us
//
//
// Ajax.WCFServiceAutocompleter is a copy of the regular Ajax.Autocompleter but it calls a javascript delegate to obtain its data.
// It is the responsibility of the delegate to instantiate the service and call the service method, e.g.:
//
//	Ajax.WCFServiceAutocompleter("text_input_id", "list_element_id", dictionaryOnLookup, {
//		minChars: 1,
//		indicator: "indicator_element_id",
//		afterUpdateElement: dictionaryAfterUpdate
//	});
//
//	function dictionaryOnLookup(word, onSuccess, onFail)
//	{
//		var proxy = new JobSearch.IAutoComplete();
//		proxy.Lookup("dictionary_id", word, onSuccess, onFail);
//	}
//
//	function dictionaryAfterUpdate()
//	{
//		... do stuff after item was selected from list
//	}
//
// The WCFServiceAutocompleter will invoke the delegate whenever data is needed.
// A cache is used to reduce the number of server-side requests.
//
// The WCFServiceAutocompleter is based on scriptaculous.js v1.8.2 and might need a rewrite
// if the Autocompleter.Base is changed substantially in future releases
//
Ajax.WCFServiceAutocompleter = Class.create(Autocompleter.Base, {
    initialize: function (element, update, lookup, options) {
        options = this.setDefaultOptions(options);
        this.baseInitialize(element, update, options);
        this.options.asynchronous = true;
        this.lookup = lookup;
        this.cache = new Hash();
    },

    getUpdatedChoices: function () {
        var context = this;
        var word = this.getToken().toLowerCase();
        var found = this.cache.get(word);
        if (found != undefined) {
            // Show result
            context.updateChoices(found);
        }
        else {
            // Perform lookup
            this.startIndicator();
            this.lookup(
                word,
                function (result) { context.onSuccess(word, result, context); },
                function (result) { context.onFail(word, result, context); },
                this);
        }
    },

    onSuccess: function (word, result, context) {
        // Don't cache the result if it's blank or an empty HTML list as we have seen behaviour with an incorrect empty result
        // This way we force a re-query if the text is tried again
        if ((result != "") && (result != "<ul></ul>")) {
            // Cache result
            context.cache.set(word, result);
        }

        // Show result
        context.updateChoices(result);
    },

    onFail: function () {
        // Ignore
    },

    // Make scrollIntoView optional in default markPrevious
    // (This is the default code with scrollIntoView optional and the parameter 'false' instead of 'true')
    markPrevious: function () {
        if (this.index > 0) this.index--;
        else this.index = this.entryCount - 1;
        if (this.options.scrollIntoView) {
            this.getEntry(this.index).scrollIntoView(false);
        }
    },

    // Make scrollIntoView optional in default markNext
    // (This is the default code with scrollIntoView optional)
    markNext: function () {
        if (this.index < this.entryCount - 1) this.index++;
        else this.index = 0;
        if (this.options.scrollIntoView) {
            this.getEntry(this.index).scrollIntoView(false);
        }
    },

    // Only select first item if there is only one item and autoSelect is true, otherwise use starting-index
    updateChoices: function (choices) {
        if (!this.changed && this.hasFocus) {
            this.update.innerHTML = choices;
            Element.cleanWhitespace(this.update);
            Element.cleanWhitespace(this.update.down());

            if (this.update.firstChild && this.update.down().childNodes) {
                this.entryCount =
          this.update.down().childNodes.length;
                for (var i = 0; i < this.entryCount; i++) {
                    var entry = this.getEntry(i);
                    entry.autocompleteIndex = i;
                    this.addObservers(entry);
                }
            } else {
                this.entryCount = 0;
            }

            this.stopIndicator();

            if (this.entryCount == 1 && this.options.autoSelect) {
                this.index = 0;
                this.selectEntry();
                this.hide();
            } else {
                this.index = this.options.startingIndex;
                this.render();
            }
        }
    },

    // Optionally allow TAB and RETURN to propagate (ie. do not stop event)
    onKeyPress: function (event) {
        if (this.active)
            switch (event.keyCode) {
            case Event.KEY_TAB:
            case Event.KEY_RETURN:
                if (this.index >= 0) {
                    this.selectEntry();
                    if (this.options.stopEventOnKeypress)
                        Event.stop(event);
                }
                this.hide();
                this.active = false;
                break;
            case Event.KEY_ESC:
                this.hide();
                this.active = false;
                Event.stop(event);
                return;
            case Event.KEY_LEFT:
            case Event.KEY_RIGHT:
                return;
            case Event.KEY_UP:
                this.markPrevious();
                this.render();
                Event.stop(event);
                return;
            case Event.KEY_DOWN:
                this.markNext();
                this.render();
                Event.stop(event);
                return;
        }
        else
            if (event.keyCode == Event.KEY_TAB || event.keyCode == Event.KEY_RETURN ||
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

        this.changed = true;
        this.hasFocus = true;

        if (this.observer) clearTimeout(this.observer);
        this.observer =
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency * 1000);
    },

    setDefaultOptions: function (options) {
        // Initialize
        if (!options) {
            options = {};
        }

        // minChars
        if (typeof (options.minChars) == 'undefined') {
            options.minChars = 1;
        }

        // scrollIntoView
        if (typeof (options.scrollIntoView) == 'undefined') {
            options.scrollIntoView = false;
        }

        // startingIndex
        if (typeof (options.startingIndex) == 'undefined') {
            options.startingIndex = -1;
        }

        // stopEventOnKeypress
        if (typeof (options.stopEventOnKeypress) == 'undefined') {
            options.stopEventOnKeypress = true;
        }

        // Remove effect from default onShow
        // (This is the default code with effect removed and update.show used instead)
        options.onShow = function (element, update) {
            if (!update.style.position || update.style.position == 'absolute') {
                update.style.position = 'absolute';
                Position.clone(element, update, {
                    setHeight: false,
                    offsetTop: element.offsetHeight
                });
            }
            //Effect.Appear(update, { duration: 0.15 });
            update.show();
        };

        // Remove effect from onHide
        // (This is the default code with effect removed and update.hide used instead)
        options.onHide = function (element, update) {
            //new Effect.Fade(update, { duration: 0.15 }) 
            update.hide();
        };

        return options;
    }
});