// $Id: autocomplete.js,v 1.17 2007/01/09 07:31:04 drumm Exp $

/**
 * Attaches the autocomplete behaviour to all required fields
 */
Drupal.autocompleteAutoAttach = function () {
    var acdb = [];
    $('input.autocomplete').each(function () {
        var uri = this.value;
        if (!acdb[uri]) {
            acdb[uri] = new Drupal.ACDB(uri);
        }
        var input = $('#' + this.id.substr(0, this.id.length - 13))
        .attr('autocomplete', 'OFF')[0];
        $(input.form).submit(Drupal.autocompleteSubmit);
        new Drupal.jsAC(input, acdb[uri]);
    });
}

/**
 * Prevents the form from submitting if the suggestions popup is open
 * and closes the suggestions popup when doing so.
 */
Drupal.autocompleteSubmit = function () {
    return $('#autocomplete').each(function () {
        this.owner.hidePopup();
    }).size() == 0;
}

/**
 * An AutoComplete object
 */
Drupal.jsAC = function (input, db) {
    var ac = this;
    this.input = input;
    this.db = db;

    $(this.input)
    .keydown(function (event) { 
        return ac.onkeydown(this, event);
    })
    .keyup(function (event) { 
        ac.onkeyup(this, event)
    })
    .keypress(function (event) {
        ac.onkeypress(this, event)
    })
    .blur(function () { 
        ac.hidePopup(); ac.db.cancel();
    });

};

/**
 * Handler for the "keydown" event
 */
Drupal.jsAC.prototype.onkeydown = function (input, e) {
    if (!e) {
        e = window.event;
    }
    switch (e.keyCode) {
        case 40: // down arrow
            this.selectDown();
            return false;
        case 38: // up arrow
            this.selectUp();
            return false;
        default: // all other keys
            return true;
    }
}

/**
 * Handler for the "keyup" event
 */
Drupal.jsAC.prototype.onkeyup = function (input, e) {
    if (!e) {
        e = window.event;
    }
    switch (e.keyCode) {
        case 16: // shift
        case 17: // ctrl
        case 18: // alt
        case 20: // caps lock
        case 33: // page up
        case 34: // page down
        case 35: // end
        case 36: // home
        case 37: // left arrow
        case 38: // up arrow
        case 39: // right arrow
        case 40: // down arrow
            return true;

        case 9:  // tab
        case 27: // esc
            this.hidePopup(e.keyCode);
            return true;

        case 13: // enter
            this.hidePopup(e.keyCode);
            if (input.value.length > 0) {
                //console.log('ret false');
                this.db.cancel();
                this.input.form.submit();
                return false;
            }       else {
                //console.log('ret true');
                return true;
            }

        default: // all other keys
            if (input.value.length > 0)
                this.populatePopup();
            else
                this.hidePopup(e.keyCode);
            return true;
    }
}

/**
 *handler for keypress. IE only *sigh*
 */
Drupal.jsAC.prototype.onkeypress = function (input, e) {
    /* ie filter */
    var isIE = window.ActiveXObject ? true : false; // ActiveX is only used in Internet Explorer
    if (!isIE)
        return true;

    if (!e) {
        e = window.event;
    }
    
    switch (e.keyCode) {
        case 9:  // tab
        case 27: // esc
            this.hidePopup(e.keyCode);
            return true;

        case 13: // enter
            this.hidePopup(e.keyCode);
            if (input.value.length > 0) {
                //console.log('ret false');
                this.db.cancel();
                this.input.form.submit();
                return false;
            }       else {
                //console.log('ret true');
                return true;
            }
    }
}

/**
 * Puts the currently highlighted suggestion into the autocomplete field
 */
Drupal.jsAC.prototype.select = function (node) {
    this.input.value = node.autocompleteValue;
}

/**
 * Highlights the next suggestion
 */
Drupal.jsAC.prototype.selectDown = function () {
    if (this.selected && this.selected.nextSibling) {
        this.highlight(this.selected.nextSibling);
    }
    else {
        var lis = $('li', this.popup);
        if (lis.size() > 0) {
            this.highlight(lis.get(0));
        }
    }
}

/**
 * Highlights the previous suggestion
 */
Drupal.jsAC.prototype.selectUp = function () {
    if (this.selected && this.selected.previousSibling) {
        this.highlight(this.selected.previousSibling);
    }
}

/**
 * Highlights a suggestion
 */
Drupal.jsAC.prototype.highlight = function (node) {
    if (this.selected) {
        $(this.selected).removeClass('selected');
    }
    $(node).addClass('selected');
    this.selected = node;
}

/**
 * Unhighlights a suggestion
 */
Drupal.jsAC.prototype.unhighlight = function (node) {
    $(node).removeClass('selected');
    this.selected = false;
}

/**
 * Hides the autocomplete suggestions
 */
Drupal.jsAC.prototype.hidePopup = function (keycode) {
    // Select item if the right key or mousebutton was pressed
    if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
        this.input.value = this.selected.autocompleteValue;
    }
    // Hide popup
    var popup = this.popup;
    if (popup) {
        this.popup = null;
        $(popup).fadeOut('fast', function() {
            $(popup).remove();
        });
    }
    this.selected = false;
}

/**
 * Positions the suggestions popup and starts a search
 */
Drupal.jsAC.prototype.populatePopup = function () {
    // Show popup
    if (this.popup) {
        $(this.popup).remove();
    }
    this.selected = false;
    this.popup = document.createElement('div');
    this.popup.id = 'autocomplete';
    this.popup.owner = this;
    $(this.popup).css({
        marginTop: this.input.offsetHeight +'px',
        // width: (this.input.offsetWidth - 4) +'px',
        display: 'none'
    });
    $(this.input).before(this.popup);

    // Do search
    this.db.owner = this;
    this.db.search(this.input.value);
}

/**
 * Fills the suggestion popup with any matches received
 */
Drupal.jsAC.prototype.found = function (matches) {
    // If no value in the textfield, do not show the popup.
    if (!this.input.value.length) {
        return false;
    }

    // Prepare matches
    var ul = document.createElement('ul');
    var ac = this;
    for (key in matches) {
        var li = document.createElement('li');
        $(li)
        .html('<div>'+ matches[key] +'</div>')
        .mousedown(function () {
            ac.select(this);
        })
        .mouseover(function () {
            ac.highlight(this);
        })
        .mouseout(function () {
            ac.unhighlight(this);
        });
        li.autocompleteValue = key;
        $(ul).append(li);
    }

    // Show popup with matches, if any
    if (this.popup) {
        if (ul.childNodes.length > 0) {
            $(this.popup).empty().append(ul).show();
        }
        else {
            $(this.popup).css({
                visibility: 'hidden'
            });
            this.hidePopup();
        }
    }
}

Drupal.jsAC.prototype.setStatus = function (status) {
    switch (status) {
        case 'begin':
            $(this.input).addClass('throbbing');
            break;
        case 'cancel':
        case 'error':
        case 'found':
            $(this.input).removeClass('throbbing');
            break;
    }
    this.status = status;
}

/**
 * An AutoComplete DataBase object
 */
Drupal.ACDB = function (uri) {
    this.uri = uri;
    this.delay = 300;
    this.cache = {};
}

/**
 * Performs a cached and delayed search
 */
Drupal.ACDB.prototype.search = function (searchString) {
    var db = this;
    this.searchString = searchString;

    // See if this key has been searched for before
    if (this.cache[searchString]) {
        return this.owner.found(this.cache[searchString]);
    }

    // Initiate delayed search
    if (this.timer) {
        clearTimeout(this.timer);
    }
    this.timer = setTimeout(function() {
        db.owner.setStatus('begin');

        // Ajax GET request for autocompletion
        $.ajax({
            type: "GET",
            url: db.uri +'/'+ Drupal.encodeURIComponent(searchString),
            success: function (data) {
                // Parse back result
                var matches = Drupal.parseJson(data);
                if (typeof matches['status'] == 'undefined' || matches['status'] != 0) {
                    db.cache[searchString] = matches;
                    // Verify if these are still the matches the user wants to see
                    if (db.searchString == searchString) {
                        db.owner.found(matches);
                    }
                    db.owner.setStatus('found');
                }
            },
            error: function (xmlhttp) {
                if (db.owner.status != 'cancel') alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ db.uri + ' .status=' + this.status);
            }
        });
    }, this.delay);
}

/**
 * Cancels the current autocomplete request
 */
Drupal.ACDB.prototype.cancel = function() {
    if (this.owner) this.owner.setStatus('cancel');
    if (this.timer) clearTimeout(this.timer);
    this.searchString = '';
}

// Global Killswitch
if (Drupal.jsEnabled) {
    $(document).ready(Drupal.autocompleteAutoAttach);
}
