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