webmaster@1: // $Id: autocomplete.js,v 1.23 2008/01/04 11:53:21 goba Exp $ webmaster@1: webmaster@1: /** webmaster@1: * Attaches the autocomplete behavior to all required fields webmaster@1: */ webmaster@1: Drupal.behaviors.autocomplete = function (context) { webmaster@1: var acdb = []; webmaster@1: $('input.autocomplete:not(.autocomplete-processed)', context).each(function () { webmaster@1: var uri = this.value; webmaster@1: if (!acdb[uri]) { webmaster@1: acdb[uri] = new Drupal.ACDB(uri); webmaster@1: } webmaster@1: var input = $('#' + this.id.substr(0, this.id.length - 13)) webmaster@1: .attr('autocomplete', 'OFF')[0]; webmaster@1: $(input.form).submit(Drupal.autocompleteSubmit); webmaster@1: new Drupal.jsAC(input, acdb[uri]); webmaster@1: $(this).addClass('autocomplete-processed'); webmaster@1: }); webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Prevents the form from submitting if the suggestions popup is open webmaster@1: * and closes the suggestions popup when doing so. webmaster@1: */ webmaster@1: Drupal.autocompleteSubmit = function () { webmaster@1: return $('#autocomplete').each(function () { webmaster@1: this.owner.hidePopup(); webmaster@1: }).size() == 0; webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * An AutoComplete object webmaster@1: */ webmaster@1: Drupal.jsAC = function (input, db) { webmaster@1: var ac = this; webmaster@1: this.input = input; webmaster@1: this.db = db; webmaster@1: webmaster@1: $(this.input) webmaster@1: .keydown(function (event) { return ac.onkeydown(this, event); }) webmaster@1: .keyup(function (event) { ac.onkeyup(this, event); }) webmaster@1: .blur(function () { ac.hidePopup(); ac.db.cancel(); }); webmaster@1: webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Handler for the "keydown" event webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.onkeydown = function (input, e) { webmaster@1: if (!e) { webmaster@1: e = window.event; webmaster@1: } webmaster@1: switch (e.keyCode) { webmaster@1: case 40: // down arrow webmaster@1: this.selectDown(); webmaster@1: return false; webmaster@1: case 38: // up arrow webmaster@1: this.selectUp(); webmaster@1: return false; webmaster@1: default: // all other keys webmaster@1: return true; webmaster@1: } webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Handler for the "keyup" event webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.onkeyup = function (input, e) { webmaster@1: if (!e) { webmaster@1: e = window.event; webmaster@1: } webmaster@1: switch (e.keyCode) { webmaster@1: case 16: // shift webmaster@1: case 17: // ctrl webmaster@1: case 18: // alt webmaster@1: case 20: // caps lock webmaster@1: case 33: // page up webmaster@1: case 34: // page down webmaster@1: case 35: // end webmaster@1: case 36: // home webmaster@1: case 37: // left arrow webmaster@1: case 38: // up arrow webmaster@1: case 39: // right arrow webmaster@1: case 40: // down arrow webmaster@1: return true; webmaster@1: webmaster@1: case 9: // tab webmaster@1: case 13: // enter webmaster@1: case 27: // esc webmaster@1: this.hidePopup(e.keyCode); webmaster@1: return true; webmaster@1: webmaster@1: default: // all other keys webmaster@1: if (input.value.length > 0) webmaster@1: this.populatePopup(); webmaster@1: else webmaster@1: this.hidePopup(e.keyCode); webmaster@1: return true; webmaster@1: } webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Puts the currently highlighted suggestion into the autocomplete field webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.select = function (node) { webmaster@1: this.input.value = node.autocompleteValue; webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Highlights the next suggestion webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.selectDown = function () { webmaster@1: if (this.selected && this.selected.nextSibling) { webmaster@1: this.highlight(this.selected.nextSibling); webmaster@1: } webmaster@1: else { webmaster@1: var lis = $('li', this.popup); webmaster@1: if (lis.size() > 0) { webmaster@1: this.highlight(lis.get(0)); webmaster@1: } webmaster@1: } webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Highlights the previous suggestion webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.selectUp = function () { webmaster@1: if (this.selected && this.selected.previousSibling) { webmaster@1: this.highlight(this.selected.previousSibling); webmaster@1: } webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Highlights a suggestion webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.highlight = function (node) { webmaster@1: if (this.selected) { webmaster@1: $(this.selected).removeClass('selected'); webmaster@1: } webmaster@1: $(node).addClass('selected'); webmaster@1: this.selected = node; webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Unhighlights a suggestion webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.unhighlight = function (node) { webmaster@1: $(node).removeClass('selected'); webmaster@1: this.selected = false; webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Hides the autocomplete suggestions webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.hidePopup = function (keycode) { webmaster@1: // Select item if the right key or mousebutton was pressed webmaster@1: if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) { webmaster@1: this.input.value = this.selected.autocompleteValue; webmaster@1: } webmaster@1: // Hide popup webmaster@1: var popup = this.popup; webmaster@1: if (popup) { webmaster@1: this.popup = null; webmaster@1: $(popup).fadeOut('fast', function() { $(popup).remove(); }); webmaster@1: } webmaster@1: this.selected = false; webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Positions the suggestions popup and starts a search webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.populatePopup = function () { webmaster@1: // Show popup webmaster@1: if (this.popup) { webmaster@1: $(this.popup).remove(); webmaster@1: } webmaster@1: this.selected = false; webmaster@1: this.popup = document.createElement('div'); webmaster@1: this.popup.id = 'autocomplete'; webmaster@1: this.popup.owner = this; webmaster@1: $(this.popup).css({ webmaster@1: marginTop: this.input.offsetHeight +'px', webmaster@1: width: (this.input.offsetWidth - 4) +'px', webmaster@1: display: 'none' webmaster@1: }); webmaster@1: $(this.input).before(this.popup); webmaster@1: webmaster@1: // Do search webmaster@1: this.db.owner = this; webmaster@1: this.db.search(this.input.value); webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Fills the suggestion popup with any matches received webmaster@1: */ webmaster@1: Drupal.jsAC.prototype.found = function (matches) { webmaster@1: // If no value in the textfield, do not show the popup. webmaster@1: if (!this.input.value.length) { webmaster@1: return false; webmaster@1: } webmaster@1: webmaster@1: // Prepare matches webmaster@1: var ul = document.createElement('ul'); webmaster@1: var ac = this; webmaster@1: for (key in matches) { webmaster@1: var li = document.createElement('li'); webmaster@1: $(li) webmaster@1: .html('
'+ matches[key] +'
') webmaster@1: .mousedown(function () { ac.select(this); }) webmaster@1: .mouseover(function () { ac.highlight(this); }) webmaster@1: .mouseout(function () { ac.unhighlight(this); }); webmaster@1: li.autocompleteValue = key; webmaster@1: $(ul).append(li); webmaster@1: } webmaster@1: webmaster@1: // Show popup with matches, if any webmaster@1: if (this.popup) { webmaster@1: if (ul.childNodes.length > 0) { webmaster@1: $(this.popup).empty().append(ul).show(); webmaster@1: } webmaster@1: else { webmaster@1: $(this.popup).css({visibility: 'hidden'}); webmaster@1: this.hidePopup(); webmaster@1: } webmaster@1: } webmaster@1: }; webmaster@1: webmaster@1: Drupal.jsAC.prototype.setStatus = function (status) { webmaster@1: switch (status) { webmaster@1: case 'begin': webmaster@1: $(this.input).addClass('throbbing'); webmaster@1: break; webmaster@1: case 'cancel': webmaster@1: case 'error': webmaster@1: case 'found': webmaster@1: $(this.input).removeClass('throbbing'); webmaster@1: break; webmaster@1: } webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * An AutoComplete DataBase object webmaster@1: */ webmaster@1: Drupal.ACDB = function (uri) { webmaster@1: this.uri = uri; webmaster@1: this.delay = 300; webmaster@1: this.cache = {}; webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Performs a cached and delayed search webmaster@1: */ webmaster@1: Drupal.ACDB.prototype.search = function (searchString) { webmaster@1: var db = this; webmaster@1: this.searchString = searchString; webmaster@1: webmaster@1: // See if this key has been searched for before webmaster@1: if (this.cache[searchString]) { webmaster@1: return this.owner.found(this.cache[searchString]); webmaster@1: } webmaster@1: webmaster@1: // Initiate delayed search webmaster@1: if (this.timer) { webmaster@1: clearTimeout(this.timer); webmaster@1: } webmaster@1: this.timer = setTimeout(function() { webmaster@1: db.owner.setStatus('begin'); webmaster@1: webmaster@1: // Ajax GET request for autocompletion webmaster@1: $.ajax({ webmaster@1: type: "GET", webmaster@1: url: db.uri +'/'+ Drupal.encodeURIComponent(searchString), webmaster@1: dataType: 'json', webmaster@1: success: function (matches) { webmaster@1: if (typeof matches['status'] == 'undefined' || matches['status'] != 0) { webmaster@1: db.cache[searchString] = matches; webmaster@1: // Verify if these are still the matches the user wants to see webmaster@1: if (db.searchString == searchString) { webmaster@1: db.owner.found(matches); webmaster@1: } webmaster@1: db.owner.setStatus('found'); webmaster@1: } webmaster@1: }, webmaster@1: error: function (xmlhttp) { webmaster@1: alert(Drupal.ahahError(xmlhttp, db.uri)); webmaster@1: } webmaster@1: }); webmaster@1: }, this.delay); webmaster@1: }; webmaster@1: webmaster@1: /** webmaster@1: * Cancels the current autocomplete request webmaster@1: */ webmaster@1: Drupal.ACDB.prototype.cancel = function() { webmaster@1: if (this.owner) this.owner.setStatus('cancel'); webmaster@1: if (this.timer) clearTimeout(this.timer); webmaster@1: this.searchString = ''; webmaster@1: };