Chris@0: /** Chris@0: * @file Chris@0: * Autocomplete based on jQuery UI. Chris@0: */ Chris@0: Chris@17: (function($, Drupal) { Chris@0: let autocomplete; Chris@0: Chris@0: /** Chris@0: * Helper splitting terms from the autocomplete value. Chris@0: * Chris@0: * @function Drupal.autocomplete.splitValues Chris@0: * Chris@0: * @param {string} value Chris@0: * The value being entered by the user. Chris@0: * Chris@0: * @return {Array} Chris@0: * Array of values, split by comma. Chris@0: */ Chris@0: function autocompleteSplitValues(value) { Chris@0: // We will match the value against comma-separated terms. Chris@0: const result = []; Chris@0: let quote = false; Chris@0: let current = ''; Chris@0: const valueLength = value.length; Chris@0: let character; Chris@0: Chris@0: for (let i = 0; i < valueLength; i++) { Chris@0: character = value.charAt(i); Chris@0: if (character === '"') { Chris@0: current += character; Chris@0: quote = !quote; Chris@17: } else if (character === ',' && !quote) { Chris@0: result.push(current.trim()); Chris@0: current = ''; Chris@17: } else { Chris@0: current += character; Chris@0: } Chris@0: } Chris@0: if (value.length > 0) { Chris@0: result.push($.trim(current)); Chris@0: } Chris@0: Chris@0: return result; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the last value of an multi-value textfield. Chris@0: * Chris@0: * @function Drupal.autocomplete.extractLastTerm Chris@0: * Chris@0: * @param {string} terms Chris@0: * The value of the field. Chris@0: * Chris@0: * @return {string} Chris@0: * The last value of the input field. Chris@0: */ Chris@0: function extractLastTerm(terms) { Chris@0: return autocomplete.splitValues(terms).pop(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * The search handler is called before a search is performed. Chris@0: * Chris@0: * @function Drupal.autocomplete.options.search Chris@0: * Chris@0: * @param {object} event Chris@0: * The event triggered. Chris@0: * Chris@0: * @return {bool} Chris@0: * Whether to perform a search or not. Chris@0: */ Chris@0: function searchHandler(event) { Chris@0: const options = autocomplete.options; Chris@0: Chris@0: if (options.isComposing) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: const term = autocomplete.extractLastTerm(event.target.value); Chris@0: // Abort search if the first character is in firstCharacterBlacklist. Chris@17: if ( Chris@17: term.length > 0 && Chris@17: options.firstCharacterBlacklist.indexOf(term[0]) !== -1 Chris@17: ) { Chris@0: return false; Chris@0: } Chris@0: // Only search when the term is at least the minimum length. Chris@0: return term.length >= options.minLength; Chris@0: } Chris@0: Chris@0: /** Chris@0: * JQuery UI autocomplete source callback. Chris@0: * Chris@0: * @param {object} request Chris@0: * The request object. Chris@0: * @param {function} response Chris@0: * The function to call with the response. Chris@0: */ Chris@0: function sourceData(request, response) { Chris@0: const elementId = this.element.attr('id'); Chris@0: Chris@0: if (!(elementId in autocomplete.cache)) { Chris@0: autocomplete.cache[elementId] = {}; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Filter through the suggestions removing all terms already tagged and Chris@0: * display the available terms to the user. Chris@0: * Chris@0: * @param {object} suggestions Chris@0: * Suggestions returned by the server. Chris@0: */ Chris@0: function showSuggestions(suggestions) { Chris@0: const tagged = autocomplete.splitValues(request.term); Chris@0: const il = tagged.length; Chris@0: for (let i = 0; i < il; i++) { Chris@0: const index = suggestions.indexOf(tagged[i]); Chris@0: if (index >= 0) { Chris@0: suggestions.splice(index, 1); Chris@0: } Chris@0: } Chris@0: response(suggestions); Chris@0: } Chris@0: Chris@17: // Get the desired term and construct the autocomplete URL for it. Chris@17: const term = autocomplete.extractLastTerm(request.term); Chris@17: Chris@0: /** Chris@0: * Transforms the data object into an array and update autocomplete results. Chris@0: * Chris@0: * @param {object} data Chris@0: * The data sent back from the server. Chris@0: */ Chris@0: function sourceCallbackHandler(data) { Chris@0: autocomplete.cache[elementId][term] = data; Chris@0: Chris@0: // Send the new string array of terms to the jQuery UI list. Chris@0: showSuggestions(data); Chris@0: } Chris@0: Chris@0: // Check if the term is already cached. Chris@0: if (autocomplete.cache[elementId].hasOwnProperty(term)) { Chris@0: showSuggestions(autocomplete.cache[elementId][term]); Chris@17: } else { Chris@17: const options = $.extend( Chris@17: { success: sourceCallbackHandler, data: { q: term } }, Chris@17: autocomplete.ajax, Chris@17: ); Chris@0: $.ajax(this.element.attr('data-autocomplete-path'), options); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Handles an autocompletefocus event. Chris@0: * Chris@0: * @return {bool} Chris@0: * Always returns false. Chris@0: */ Chris@0: function focusHandler() { Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Handles an autocompleteselect event. Chris@0: * Chris@0: * @param {jQuery.Event} event Chris@0: * The event triggered. Chris@0: * @param {object} ui Chris@0: * The jQuery UI settings object. Chris@0: * Chris@0: * @return {bool} Chris@0: * Returns false to indicate the event status. Chris@0: */ Chris@0: function selectHandler(event, ui) { Chris@0: const terms = autocomplete.splitValues(event.target.value); Chris@0: // Remove the current input. Chris@0: terms.pop(); Chris@0: // Add the selected item. Chris@0: terms.push(ui.item.value); Chris@0: Chris@0: event.target.value = terms.join(', '); Chris@0: // Return false to tell jQuery UI that we've filled in the value already. Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Override jQuery UI _renderItem function to output HTML by default. Chris@0: * Chris@0: * @param {jQuery} ul Chris@0: * jQuery collection of the ul element. Chris@0: * @param {object} item Chris@0: * The list item to append. Chris@0: * Chris@0: * @return {jQuery} Chris@0: * jQuery collection of the ul element. Chris@0: */ Chris@0: function renderItem(ul, item) { Chris@0: return $('