autocomplete.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. (function ($) {
  2. /**
  3. * Attaches the autocomplete behavior to all required fields.
  4. */
  5. Drupal.behaviors.autocomplete = {
  6. attach: function (context) {
  7. var $context = $(context);
  8. var acdb = [];
  9. $context.find('input.autocomplete').once('autocomplete', function () {
  10. var uri = this.value;
  11. if (!acdb[uri]) {
  12. acdb[uri] = new Drupal.ACDB(uri);
  13. }
  14. var $input = $context.find('#' + this.id.substr(0, this.id.length - 13))
  15. .attr('autocomplete', 'OFF')
  16. .attr('aria-autocomplete', 'list');
  17. $context.find($input[0].form).submit(Drupal.autocompleteSubmit);
  18. $input.parents('.form-item')
  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], $context);
  24. });
  25. }
  26. };
  27. /**
  28. * Prevents the form from submitting if the suggestions popup is open
  29. * and closes the suggestions popup when doing so.
  30. */
  31. Drupal.autocompleteSubmit = function () {
  32. return $('.form-autocomplete > .dropdown').each(function () {
  33. this.owner.hidePopup();
  34. }).length == 0;
  35. };
  36. /**
  37. * Highlights a suggestion.
  38. */
  39. Drupal.jsAC.prototype.highlight = function (node) {
  40. if (this.selected) {
  41. $(this.selected).removeClass('active');
  42. }
  43. $(node).addClass('active');
  44. this.selected = node;
  45. $(this.ariaLive).html($(this.selected).html());
  46. };
  47. /**
  48. * Unhighlights a suggestion.
  49. */
  50. Drupal.jsAC.prototype.unhighlight = function (node) {
  51. $(node).removeClass('active');
  52. this.selected = false;
  53. $(this.ariaLive).empty();
  54. };
  55. /**
  56. * Positions the suggestions popup and starts a search.
  57. */
  58. Drupal.jsAC.prototype.populatePopup = function () {
  59. var $input = $(this.input);
  60. // Show popup.
  61. if (this.popup) {
  62. $(this.popup).remove();
  63. }
  64. this.selected = false;
  65. this.popup = $('<div class="dropdown"></div>')[0];
  66. this.popup.owner = this;
  67. $input.parent().after(this.popup);
  68. // Do search.
  69. this.db.owner = this;
  70. this.db.search(this.input.value);
  71. };
  72. /**
  73. * Fills the suggestion popup with any matches received.
  74. */
  75. Drupal.jsAC.prototype.found = function (matches) {
  76. // If no value in the textfield, do not show the popup.
  77. if (!this.input.value.length) {
  78. return false;
  79. }
  80. // Prepare matches.
  81. var ul = $('<ul class="dropdown-menu"></ul>');
  82. var ac = this;
  83. ul.css({
  84. display: 'block',
  85. right: 0
  86. });
  87. for (var key in matches) {
  88. $('<li></li>')
  89. .html($('<a href="#"></a>').html(matches[key]).click(function (e) { e.preventDefault(); }))
  90. .mousedown(function () { ac.select(this); })
  91. .mouseover(function () { ac.highlight(this); })
  92. .mouseout(function () { ac.unhighlight(this); })
  93. .data('autocompleteValue', key)
  94. .appendTo(ul);
  95. }
  96. // Show popup with matches, if any.
  97. if (this.popup) {
  98. if (ul.children().length) {
  99. $(this.popup).empty().append(ul).show();
  100. $(this.ariaLive).html(Drupal.t('Autocomplete popup'));
  101. }
  102. else {
  103. $(this.popup).css({ visibility: 'hidden' });
  104. this.hidePopup();
  105. }
  106. }
  107. };
  108. Drupal.jsAC.prototype.setStatus = function (status) {
  109. var $throbber = $(this.input).parent().find('.glyphicon-refresh, .autocomplete-throbber').first();
  110. var throbbingClass = $throbber.is('.autocomplete-throbber') ? 'throbbing' : 'glyphicon-spin';
  111. switch (status) {
  112. case 'begin':
  113. $throbber.addClass(throbbingClass);
  114. $(this.ariaLive).html(Drupal.t('Searching for matches...'));
  115. break;
  116. case 'cancel':
  117. case 'error':
  118. case 'found':
  119. $throbber.removeClass(throbbingClass);
  120. break;
  121. }
  122. };
  123. // Save the previous autocomplete prototype.
  124. var oldPrototype = Drupal.jsAC.prototype;
  125. /**
  126. * Override the autocomplete constructor.
  127. */
  128. Drupal.jsAC = function ($input, db, $context) {
  129. var ac = this;
  130. this.$context = $context;
  131. this.input = $input[0];
  132. this.ariaLive = $context.find('#' + this.input.id + '-autocomplete-aria-live');
  133. this.db = db;
  134. $input
  135. .keydown(function (event) { return ac.onkeydown(this, event); })
  136. .keyup(function (event) { ac.onkeyup(this, event); })
  137. .blur(function () { ac.hidePopup(); ac.db.cancel(); });
  138. };
  139. // Restore the previous prototype.
  140. Drupal.jsAC.prototype = oldPrototype;
  141. })(jQuery);