diff misc/autocomplete.js @ 1:c1f4ac30525a 6.0

Drupal 6.0
author Franck Deroche <webmaster@defr.org>
date Tue, 23 Dec 2008 14:28:28 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/autocomplete.js	Tue Dec 23 14:28:28 2008 +0100
@@ -0,0 +1,298 @@
+// $Id: autocomplete.js,v 1.23 2008/01/04 11:53:21 goba Exp $
+
+/**
+ * Attaches the autocomplete behavior to all required fields
+ */
+Drupal.behaviors.autocomplete = function (context) {
+  var acdb = [];
+  $('input.autocomplete:not(.autocomplete-processed)', context).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]);
+    $(this).addClass('autocomplete-processed');
+  });
+};
+
+/**
+ * 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); })
+    .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 13: // enter
+    case 27: // esc
+      this.hidePopup(e.keyCode);
+      return true;
+
+    default: // all other keys
+      if (input.value.length > 0)
+        this.populatePopup();
+      else
+        this.hidePopup(e.keyCode);
+      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;
+  }
+};
+
+/**
+ * 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),
+      dataType: 'json',
+      success: function (matches) {
+        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) {
+        alert(Drupal.ahahError(xmlhttp, db.uri));
+      }
+    });
+  }, 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 = '';
+};