autocomplete.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /**
  2. * @file
  3. * Linkit Autocomplete based on jQuery UI.
  4. */
  5. (function ($, Drupal, _, document) {
  6. 'use strict';
  7. var autocomplete;
  8. /**
  9. * JQuery UI autocomplete source callback.
  10. *
  11. * @param {object} request
  12. * @param {function} response
  13. */
  14. function sourceData(request, response) {
  15. var elementId = this.element.attr('id');
  16. if (!(elementId in autocomplete.cache)) {
  17. autocomplete.cache[elementId] = {};
  18. }
  19. /**
  20. * @param {object} suggestions
  21. */
  22. function showSuggestions(suggestions) {
  23. response(suggestions.matches);
  24. }
  25. /**
  26. * Transforms the data object into an array and update autocomplete results.
  27. *
  28. * @param {object} data
  29. */
  30. function sourceCallbackHandler(data) {
  31. autocomplete.cache[elementId][term] = data;
  32. showSuggestions(data);
  33. }
  34. // Get the desired term and construct the autocomplete URL for it.
  35. var term = request.term;
  36. // Check if the term is already cached.
  37. if (autocomplete.cache[elementId].hasOwnProperty(term)) {
  38. showSuggestions(autocomplete.cache[elementId][term]);
  39. }
  40. else {
  41. var options = $.extend({success: sourceCallbackHandler, data: {q: term}}, autocomplete.ajax);
  42. $.ajax(this.element.attr('data-autocomplete-path'), options);
  43. }
  44. }
  45. /**
  46. * Handles an autocomplete select event.
  47. *
  48. * @param {jQuery.Event} event
  49. * @param {object} ui
  50. *
  51. * @return {boolean}
  52. */
  53. function selectHandler(event, ui) {
  54. if (ui.item.hasOwnProperty('path')) {
  55. event.target.value = ui.item.path;
  56. }
  57. $(document).trigger('linkit.autocomplete.select', [event, ui]);
  58. return false;
  59. }
  60. /**
  61. * Override jQuery UI _renderItem function to output HTML by default.
  62. *
  63. * @param {object} ul
  64. * The <ul> element that the newly created <li> element must be appended to.
  65. * @param {object} item
  66. *
  67. * @return {object}
  68. */
  69. function renderItem(ul, item) {
  70. var $line = $('<li>').addClass('linkit-result');
  71. $line.append($('<span>').html(item.title).addClass('linkit-result--title'));
  72. if (item.description !== null) {
  73. $line.append($('<span>').html(item.description).addClass('linkit-result--description'));
  74. }
  75. return $line.appendTo(ul);
  76. }
  77. /**
  78. * Override jQuery UI _renderMenu function to handle groups.
  79. *
  80. * @param {object} ul
  81. * An empty <ul> element to use as the widget's menu.
  82. * @param {array} items
  83. * An Array of items that match the user typed term.
  84. */
  85. function renderMenu(ul, items) {
  86. var self = this.element.autocomplete('instance');
  87. var grouped_items = _.groupBy(items, function (item) {
  88. return item.hasOwnProperty('group') ? item.group : '';
  89. });
  90. $.each(grouped_items, function (group, items) {
  91. if (group.length) {
  92. ul.append('<li class="linkit-result--group">' + group + '</li>');
  93. }
  94. $.each(items, function (index, item) {
  95. self._renderItemData(ul, item);
  96. });
  97. });
  98. }
  99. /**
  100. * Attaches the autocomplete behavior to all required fields.
  101. *
  102. * @type {Drupal~behavior}
  103. */
  104. Drupal.behaviors.linkit_autocomplete = {
  105. attach: function (context) {
  106. // Act on textfields with the "form-autocomplete" class.
  107. var $autocomplete = $(context).find('input.form-linkit-autocomplete').once('linkit-autocomplete');
  108. if ($autocomplete.length) {
  109. $.widget('custom.autocomplete', $.ui.autocomplete, {
  110. _create: function () {
  111. this._super();
  112. this.widget().menu('option', 'items', '> :not(.linkit-result--group)');
  113. },
  114. _renderMenu: autocomplete.options.renderMenu,
  115. _renderItem: autocomplete.options.renderItem
  116. });
  117. // Use jQuery UI Autocomplete on the textfield.
  118. $autocomplete.autocomplete(autocomplete.options);
  119. $autocomplete.autocomplete('widget').addClass('linkit-ui-autocomplete');
  120. }
  121. },
  122. detach: function (context, settings, trigger) {
  123. if (trigger === 'unload') {
  124. $(context).find('input.form-linkit-autocomplete')
  125. .removeOnce('linkit-autocomplete')
  126. .autocomplete('destroy');
  127. }
  128. }
  129. };
  130. /**
  131. * Autocomplete object implementation.
  132. */
  133. autocomplete = {
  134. cache: {},
  135. options: {
  136. source: sourceData,
  137. renderItem: renderItem,
  138. renderMenu: renderMenu,
  139. select: selectHandler,
  140. minLength: 1
  141. },
  142. ajax: {
  143. dataType: 'json'
  144. }
  145. };
  146. })(jQuery, Drupal, _, document);