PageSourceSearch

https://www.bsc.es/misc/autocomplete.js?v=7.73

js bsc.es collected 2026-09-24 08:41:50 UTC 8,470 bytes, 329 lines download raw bytes

1(function ($) {
2
3/**
4 * Attaches the autocomplete behavior to all required fields.
5 */
6Drupal.behaviors.autocomplete = {
7  attach: function (context, settings) {
8    var acdb = [];
9    $('input.autocomplete', context).once('autocomplete', function () {
10      var uri = this.value;
11      if (!acdb[uri]) {
12        acdb[uri] = new Drupal.ACDB(uri);
13      }
14      var $input = $('#' + this.id.substr(0, this.id.length - 13))
15        .attr('autocomplete', 'OFF')
16        .attr('aria-autocomplete', 'list');
17      $($input[0].form).submit(Drupal.autocompleteSubmit);
18      $input.parent()
19        .attr('role', 'application')
20        .append($('<span class="element-invisible" aria-live="assertive"></span>')
21          .attr('id', $input.attr('id') + '-autocomplete-aria-live')
22        );
23      new Drupal.jsAC($input, acdb[uri]);
24    });
25  }
26};
27
28/**
29 * Prevents the form from submitting if the suggestions popup is open
30 * and closes the suggestions popup when doing so.
31 */
32Drupal.autocompleteSubmit = function () {
33  return $('#autocomplete').each(function () {
34    this.owner.hidePopup();
35  }).length == 0;
36};
37
38/**
39 * An AutoComplete object.
40 */
41Drupal.jsAC = function ($input, db) {
42  var ac = this;
43  this.input = $input[0];
44  this.ariaLive = $('#' + this.input.id + '-autocomplete-aria-live');
45  this.db = db;
46
47  $input
48    .keydown(function (event) { return ac.onkeydown(this, event); })
49    .keyup(function (event) { ac.onkeyup(this, event); })
50    .blur(function () { ac.hidePopup(); ac.db.cancel(); });
51
52};
53
54/**
55 * Handler for the "keydown" event.
56 */
57Drupal.jsAC.prototype.onkeydown = function (input, e) {
58  if (!e) {
59    e = window.event;
60  }
61  switch (e.keyCode) {
62    case 40: // down arrow.
63      this.selectDown();
64      return false;
65    case 38: // up arrow.
66      this.selectUp();
67      return false;
68    default: // All other keys.
69      return true;
70  }
71};
72
73/**
74 * Handler for the "keyup" event.
75 */
76Drupal.jsAC.prototype.onkeyup = function (input, e) {
77  if (!e) {
78    e = window.event;
79  }
80  switch (e.keyCode) {
81    case 16: // Shift.
82    case 17: // Ctrl.
83    case 18: // Alt.
84    case 20: // Caps lock.
85    case 33: // Page up.
86    case 34: // Page down.
87    case 35: // End.
88    case 36: // Home.
89    case 37: // Left arrow.
90    case 38: // Up arrow.
91    case 39: // Right arrow.
92    case 40: // Down arrow.
93      return true;
94
95    case 9:  // Tab.
96    case 13: // Enter.
97    case 27: // Esc.
98      this.hidePopup(e.keyCode);
99      return true;
100
101    default: // All other keys.
102      if (input.value.length > 0 && !input.readOnly) {
103        this.populatePopup();
104      }
105      else {
106        this.hidePopup(e.keyCode);
107      }
108      return true;
109  }
110};
111
112/**
113 * Puts the currently highlighted suggestion into the autocomplete field.
114 */
115Drupal.jsAC.prototype.select = function (node) {
116  this.input.value = $(node).data('autocompleteValue');
117  $(this.input).trigger('autocompleteSelect', [node]);
118};
119
120/**
121 * Highlights the next suggestion.
122 */
123Drupal.jsAC.prototype.selectDown = function () {
124  if (this.selected && this.selected.nextSibling) {
125    this.highlight(this.selected.nextSibling);
126  }
127  else if (this.popup) {
128    var lis = $('li', this.popup);
129    if (lis.length > 0) {
130      this.highlight(lis.get(0));
131    }
132  }
133};
134
135/**
136 * Highlights the previous suggestion.
137 */
138Drupal.jsAC.prototype.selectUp = function () {
139  if (this.selected && this.selected.previousSibling) {
140    this.highlight(this.selected.previousSibling);
141  }
142};
143
144/**
145 * Highlights a suggestion.
146 */
147Drupal.jsAC.prototype.highlight = function (node) {
148  if (this.selected) {
149    $(this.selected).removeClass('selected');
150  }
151  $(node).addClass('selected');
152  this.selected = node;
153  $(this.ariaLive).html($(this.selected).html());
154};
155
156/**
157 * Unhighlights a suggestion.
158 */
159Drupal.jsAC.prototype.unhighlight = function (node) {
160  $(node).removeClass('selected');
161  this.selected = false;
162  $(this.ariaLive).empty();
163};
164
165/**
166 * Hides the autocomplete suggestions.
167 */
168Drupal.jsAC.prototype.hidePopup = function (keycode) {
169  // Select item if the right key or mousebutton was pressed.
170  if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
171    this.select(this.selected);
172  }
173  // Hide popup.
174  var popup = this.popup;
175  if (popup) {
176    this.popup = null;
177    $(popup).fadeOut('fast', function () { $(popup).remove(); });
178  }
179  this.selected = false;
180  $(this.ariaLive).empty();
181};
182
183/**
184 * Positions the suggestions popup and starts a search.
185 */
186Drupal.jsAC.prototype.populatePopup = function () {
187  var $input = $(this.input);
188  var position = $input.position();
189  // Show popup.
190  if (this.popup) {
191    $(this.popup).remove();
192  }
193  this.selected = false;
194  this.popup = $('<div id="autocomplete"></div>')[0];
195  this.popup.owner = this;
196  $(this.popup).css({
197    top: parseInt(position.top + this.input.offsetHeight, 10) + 'px',
198    left: parseInt(position.left, 10) + 'px',
199    width: $input.innerWidth() + 'px',
200    display: 'none'
201  });
202  $input.before(this.popup);
203
204  // Do search.
205  this.db.owner = this;
206  this.db.search(this.input.value);
207};
208
209/**
210 * Fills the suggestion popup with any matches received.
211 */
212Drupal.jsAC.prototype.found = function (matches) {
213  // If no value in the textfield, do not show the popup.
214  if (!this.input.value.length) {
215    return false;
216  }
217
218  // Prepare matches.
219  var ul = $('<ul></ul>');
220  var ac = this;
221  for (key in matches) {
222    $('<li></li>')
223      .html($('<div></div>').html(matches[key]))
224      .mousedown(function () { ac.hidePopup(this); })
225      .mouseover(function () { ac.highlight(this); })
226      .mouseout(function () { ac.unhighlight(this); })
227      .data('autocompleteValue', key)
228      .appendTo(ul);
229  }
230
231  // Show popup with matches, if any.
232  if (this.popup) {
233    if (ul.children().length) {
234      $(this.popup).empty().append(ul).show();
235      $(this.ariaLive).html(Drupal.t('Autocomplete popup'));
236    }
237    else {
238      $(this.popup).css({ visibility: 'hidden' });
239      this.hidePopup();
240    }
241  }
242};
243
244Drupal.jsAC.prototype.setStatus = function (status) {
245  switch (status) {
246    case 'begin':
247      $(this.input).addClass('throbbing');
248      $(this.ariaLive).html(Drupal.t('Searching for matches...'));
249      break;
250    case 'cancel':
251    case 'error':
252    case 'found':
253      $(this.input).removeClass('throbbing');
254      break;
255  }
256};
257
258/**
259 * An AutoComplete DataBase object.
260 */
261Drupal.ACDB = function (uri) {
262  this.uri = uri;
263  this.delay = 300;
264  this.cache = {};
265};
266
267/**
268 * Performs a cached and delayed search.
269 */
270Drupal.ACDB.prototype.search = function (searchString) {
271  var db = this;
272  this.searchString = searchString;
273
274  // See if this string needs to be searched for anyway. The pattern ../ is
275  // stripped since it may be misinterpreted by the browser.
276  searchString = searchString.replace(/^\s+|\.{2,}\/|\s+$/g, '');
277  // Skip empty search strings, or search strings ending with a comma, since
278  // that is the separator between search terms.
279  if (searchString.length <= 0 ||
280    searchString.charAt(searchString.length - 1) == ',') {
281    return;
282  }
283
284  // See if this key has been searched for before.
285  if (this.cache[searchString]) {
286    return this.owner.found(this.cache[searchString]);
287  }
288
289  // Initiate delayed search.
290  if (this.timer) {
291    clearTimeout(this.timer);
292  }
293  this.timer = setTimeout(function () {
294    db.owner.setStatus('begin');
295
296    // Ajax GET request for autocompletion. We use Drupal.encodePath instead of
297    // encodeURIComponent to allow autocomplete search terms to contain slashes.
298    $.ajax({
299      type: 'GET',
300      url: Drupal.sanitizeAjaxUrl(db.uri + '/' + Drupal.encodePath(searchString)),
301      dataType: 'json',
302      jsonp: false,
303      success: function (matches) {
304        if (typeof matches.status == 'undefined' || matches.status != 0) {
305          db.cache[searchString] = matches;
306          // Verify if these are still the matches the user wants to see.
307          if (db.searchString == searchString) {
308            db.owner.found(matches);
309          }
310          db.owner.setStatus('found');
311        }
312      },
313      error: function (xmlhttp) {
314        Drupal.displayAjaxError(Drupal.ajaxError(xmlhttp, db.uri));
315      }
316    });
317  }, this.delay);
318};
319
320/**
321 * Cancels the current autocomplete request.
322 */
323Drupal.ACDB.prototype.cancel = function () {
324  if (this.owner) this.owner.setStatus('cancel');
325  if (this.timer) clearTimeout(this.timer);
326  this.searchString = '';
327};
328
329})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.