| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 | (function ($) {/** * Attaches the autocomplete behavior to all required fields. */Drupal.behaviors.autocomplete = {  attach: function (context, settings) {    var acdb = [];    $('input.autocomplete', context).once('autocomplete', 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')        .attr('aria-autocomplete', 'list');      $($input[0].form).submit(Drupal.autocompleteSubmit);      $input.parent()        .attr('role', 'application')        .append($('<span class="element-invisible" aria-live="assertive"></span>')          .attr('id', $input.attr('id') + '-autocomplete-aria-live')        );      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();  }).length == 0;};/** * An AutoComplete object. */Drupal.jsAC = function ($input, db) {  var ac = this;  this.input = $input[0];  this.ariaLive = $('#' + this.input.id + '-autocomplete-aria-live');  this.db = db;  $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 && !input.readOnly) {        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).data('autocompleteValue');  $(this.input).trigger('autocompleteSelect', [node]);};/** * Highlights the next suggestion. */Drupal.jsAC.prototype.selectDown = function () {  if (this.selected && this.selected.nextSibling) {    this.highlight(this.selected.nextSibling);  }  else if (this.popup) {    var lis = $('li', this.popup);    if (lis.length > 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;  $(this.ariaLive).html($(this.selected).html());};/** * Unhighlights a suggestion. */Drupal.jsAC.prototype.unhighlight = function (node) {  $(node).removeClass('selected');  this.selected = false;  $(this.ariaLive).empty();};/** * 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.select(this.selected);  }  // Hide popup.  var popup = this.popup;  if (popup) {    this.popup = null;    $(popup).fadeOut('fast', function () { $(popup).remove(); });  }  this.selected = false;  $(this.ariaLive).empty();};/** * Positions the suggestions popup and starts a search. */Drupal.jsAC.prototype.populatePopup = function () {  var $input = $(this.input);  var position = $input.position();  // Show popup.  if (this.popup) {    $(this.popup).remove();  }  this.selected = false;  this.popup = $('<div id="autocomplete"></div>')[0];  this.popup.owner = this;  $(this.popup).css({    top: parseInt(position.top + this.input.offsetHeight, 10) + 'px',    left: parseInt(position.left, 10) + 'px',    width: $input.innerWidth() + 'px',    display: 'none'  });  $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 = $('<ul></ul>');  var ac = this;  for (key in matches) {    $('<li></li>')      .html($('<div></div>').html(matches[key]))      .mousedown(function () { ac.hidePopup(this); })      .mouseover(function () { ac.highlight(this); })      .mouseout(function () { ac.unhighlight(this); })      .data('autocompleteValue', key)      .appendTo(ul);  }  // Show popup with matches, if any.  if (this.popup) {    if (ul.children().length) {      $(this.popup).empty().append(ul).show();      $(this.ariaLive).html(Drupal.t('Autocomplete popup'));    }    else {      $(this.popup).css({ visibility: 'hidden' });      this.hidePopup();    }  }};Drupal.jsAC.prototype.setStatus = function (status) {  switch (status) {    case 'begin':      $(this.input).addClass('throbbing');      $(this.ariaLive).html(Drupal.t('Searching for matches...'));      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 string needs to be searched for anyway. The pattern ../ is  // stripped since it may be misinterpreted by the browser.  searchString = searchString.replace(/^\s+|\.{2,}\/|\s+$/g, '');  // Skip empty search strings, or search strings ending with a comma, since  // that is the separator between search terms.  if (searchString.length <= 0 ||    searchString.charAt(searchString.length - 1) == ',') {    return;  }  // 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. We use Drupal.encodePath instead of    // encodeURIComponent to allow autocomplete search terms to contain slashes.    $.ajax({      type: 'GET',      url: db.uri + '/' + Drupal.encodePath(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.ajaxError(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 = '';};})(jQuery);
 |