/*
 autocomplete library 0.8 2006-10-12 charon@episode-iv.de
 May contain nuts.
 
 Use like this:
    var myCompleter=new autocompleter('field','http://mysite.com/cgi-bin/ajax.cgi', {[OPTIONS]});
    
 Possible options:
    o minChars      (default 2)         minimum number of characters to start looking for alternatives
    o timeout       (default 0.2)       keyboard inactivity in seconds before starting to look
    o parameters    (default value=)    parameters toappended to the url
    
    
 You can disable autocompletion like this:
    myCompleter.disable();
    
 And enable it again like this:
    myCompleter.enable();
*/

var autocompleter=Class.create();

autocompleter.prototype = {
    // "constructor"
    initialize: function(inputId, url, options) {
        // disable on IE for now
        if(navigator.appVersion.indexOf('MSIE')>0 && navigator.userAgent.indexOf('Opera')<0) {
            return;
        }
        
        this.input=$(inputId);
        this.url=url;
        
        // disable form autocomplete
        this.input.setAttribute('autocomplete','off');
        
        // install event handlers
        Event.observe(this.input, "keypress", this.keypress.bindAsEventListener(this));
        Event.observe(this.input, "blur", this.blur.bindAsEventListener(this));
        
        // create display node
        if(this.input.nextSibling) {
            this.target=this.input.parentNode.insertBefore(document.createElement("div"),this.input.nextSibling);
        }
        else {
            this.target=this.input.parentNode.appendChild(document.createElement("div"));
        }
        Element.addClassName(this.target,'autocomplete');
        Element.hide(this.target);
        
        // parse options & set defaults
        this.options=options || {};
        this.options.minChars=this.options.minChars || 2;
        this.options.timeout=this.options.timeout || 0.2;
        this.options.parameters=this.options.parameters || "value=";
        this.options.callback=this.options.callback || null;
        
        this.manager = null;
        this.selection = null;
        this.elementCount = null;
        this.active = true;
    },
    
    // user pressed a key inside the form
    keypress: function(event) {
        if(!this.active) return;
        switch(event.keyCode) {
            case Event.KEY_TAB:
                this.selectElement();
                this.hide();
                return;
            case Event.KEY_RETURN:
                this.selectElement();
                Event.stop(event);
                return;
            case Event.KEY_ESC:
                this.hide();
                Event.stop(event);
                return;
            case Event.KEY_UP:
                this.selectPrevious();
                Event.stop(event);
                return;
            case Event.KEY_DOWN:
                this.selectNext();
                Event.stop(event);
                return;

            // give up when user continues typing
            default:
                this.hide();
        }

    
        if(this.manager) clearTimeout(this.manager);
        this.manager = setTimeout(this.managerCalled.bind(this), this.options.timeout*1000);
    },
    
    // timeout expired. Start AJAX request
    managerCalled: function() {
        if(this.input.value.length >= this.options.minChars) {
            new Ajax.Request(
                this.url,
                {
                    method: 'get', 
                    parameters: this.options.parameters+this.input.value,
                    onComplete: this.completedRequest.bind(this)
                }
            );
            Element.addClassName(this.input,"loading");
        }
        else {
            Element.hide(this.target);
        }        
    },
    
    // AJAX request completed, data is here!
    completedRequest: function(request) {
        // remove prior options
        for(i=0; i<this.target.childNodes.length; i++) {
            this.target.removeChild(this.target.firstChild);
        }
        this.selection=null;
        
        // check if we have any results
        this.elementCount=request.responseXML.getElementsByTagName('s').length;
        if(this.elementCount==0) {
            Element.removeClassName(this.input,"loading");
            return;
        }
        
        // append <ul>-element
        this.target.appendChild(document.createElement("ul"));
        
        var t=this.target.firstChild;
        var g=this;
        
        // watch for user clicking out of suggestion box - hide box then
        Event.observe(t, "blur", this.hide.bindAsEventListener(this));
        
        // add elements
        for(i=0; i<request.responseXML.getElementsByTagName('s').length; i++) {
            t.appendChild(document.createElement("li"));

            // add event listeners to children
            Event.observe(t.lastChild, "mouseover", g.hoverElement.bindAsEventListener(g));
            Event.observe(t.lastChild, "click", g.clickElement.bindAsEventListener(g));
            
            // add data node. Set autocompleteindex-property identifying this element
            t.lastChild.appendChild(document.createTextNode(request.responseXML.getElementsByTagName('s')[i].firstChild.data));
            t.lastChild.autocompleteindex=i;
        }
        this.show();
        Element.removeClassName(this.input,"loading");
    },

    // user pushes mouse over element
    hoverElement: function(e) {
        var element=Event.findElement(e, 'LI');
        this.selection=element.autocompleteindex;
        this.redraw();
    },
    
    // user clicks element! CALL THE FIRE BRIGADE!
    clickElement: function(e) {
        this.selectElement();
    },

    // user has chosen an element. Rejoice!
    selectElement: function(e) {
        this.input.value=this.target.firstChild.childNodes[this.selection].firstChild.data;
        this.hide();
        if(this.options.callback) {
            alert("Using callbacks isn't implemented yet. Sorry!");
            alert(this.options.callback);
            eval(this.options.callback);
        }
    },
    
    // user wanted previous element (typing "up"-key)
    selectPrevious: function(e) {
        if(this.selection>0) {
            this.selection--;
            this.redraw();
        }
    },
    
    // user wanted next element (typing "down"-key)
    selectNext: function(e) {
        if(this.selection==null) {
            this.selection=-1;
        }
        if(this.selection<this.elementCount-1) {
            this.selection++;
            this.redraw();
        }
    },
    
    // something changed. Redraw the suggestion box
    redraw: function() {
        for(i=0;i<this.target.firstChild.childNodes.length; i++) {
            if(this.target.firstChild.childNodes[i].autocompleteindex==this.selection) {
                Element.addClassName(this.target.firstChild.childNodes[i],"selected");

            }
            else {
                Element.removeClassName(this.target.firstChild.childNodes[i],"selected");
            }
        }
    },
    
    // we lost focus. Mayday! Wait a little (making clicking work), then hurry off
    blur: function() {
        setTimeout(this.hide.bind(this), 250);
    },
    
    // hide us
    hide: function() {
        Element.hide(this.target);
    },
    
    // show us
    show: function() {
        Element.show(this.target);
    },
    
    // external call: no more autocompletion
    disable: function() {
        this.active=false;
        this.hide();
    },
    
    // external call: re-enable autocompletion
    enable: function() {
        this.active=true;
    }
};
