(function ($, Drupal) { "use strict"; var autocomplete; /** * Helper splitting terms from the autocomplete value. * * @param {String} value * * @return {Array} */ function autocompleteSplitValues (value) { // We will match the value against comma-seperated terms. var result = []; var quote = false; var current = ''; var valueLength = value.length; var i, character; for (i = 0; i < valueLength; i++) { character = value.charAt(i); if (character === '"') { current += character; quote = !quote; } else if (character === ',' && !quote) { result.push(current.trim()); current = ''; } else { current += character; } } if (value.length > 0) { result.push($.trim(current)); } return result; } /** * Returns the last value of an multi-value textfield. * * @param {String} terms * * @return {String} */ function extractLastTerm (terms) { return autocomplete.splitValues(terms).pop(); } /** * The search handler is called before a search is performed. * * @param {Object} event * * @return {Boolean} */ function searchHandler (event) { // Only search when the term is two characters or larger. var term = autocomplete.extractLastTerm(event.target.value); return term.length >= autocomplete.minLength; } /** * jQuery UI autocomplete source callback. * * @param {Object} request * @param {Function} response */ function sourceData (request, response) { /*jshint validthis:true */ var elementId = this.element.attr('id'); if (!(elementId in autocomplete.cache)) { autocomplete.cache[elementId] = {}; } /** * Filter through the suggestions removing all terms already tagged and * display the available terms to the user. * * @param {Object} suggestions */ function showSuggestions (suggestions) { var tagged = autocomplete.splitValues(request.term); for (var i = 0, il = tagged.length; i < il; i++) { var index = suggestions.indexOf(tagged[i]); if (index >= 0) { suggestions.splice(index, 1); } } response(suggestions); } /** * Transforms the data object into an array and update autocomplete results. * * @param {Object} data */ function sourceCallbackHandler (data) { autocomplete.cache[elementId][term] = data; // Send the new string array of terms to the jQuery UI list. showSuggestions(data); } // Get the desired term and construct the autocomplete URL for it. var term = autocomplete.extractLastTerm(request.term); // Check if the term is already cached. if (autocomplete.cache[elementId].hasOwnProperty(term)) { showSuggestions(autocomplete.cache[elementId][term]); } else { var options = $.extend({ success: sourceCallbackHandler, data: { q: term } }, autocomplete.ajax); /*jshint validthis:true */ $.ajax(this.element.attr('data-autocomplete-path'), options); } } /** * Handles an autocompletefocus event. * * @return {Boolean} */ function focusHandler () { return false; } /** * Handles an autocompleteselect event. * * @param {Object} event * @param {Object} ui * * @return {Boolean} */ function selectHandler (event, ui) { var terms = autocomplete.splitValues(event.target.value); // Remove the current input. terms.pop(); // Add the selected item. if (ui.item.value.search(",") > 0) { terms.push('"' + ui.item.value + '"'); } else { terms.push(ui.item.value); } event.target.value = terms.join(', '); // Return false to tell jQuery UI that we've filled in the value already. return false; } /** * Attaches the autocomplete behavior to all required fields. */ Drupal.behaviors.autocomplete = { attach: function (context) { // Act on textfields with the "form-autocomplete" class. var $autocomplete = $(context).find('input.form-autocomplete').once('autocomplete'); if ($autocomplete.length) { // Use jQuery UI Autocomplete on the textfield. $autocomplete.autocomplete(autocomplete.options); } }, detach: function (context, settings, trigger) { if (trigger === 'unload') { $(context).find('input.form-autocomplete') .removeOnce('autocomplete') .autocomplete('destroy'); } } }; /** * Autocomplete object implementation. */ autocomplete = { cache: {}, // Exposes methods to allow overriding by contrib. minLength: 1, splitValues: autocompleteSplitValues, extractLastTerm: extractLastTerm, // jQuery UI autocomplete options. options: { source: sourceData, focus: focusHandler, search: searchHandler, select: selectHandler }, ajax: { dataType: 'json' } }; Drupal.autocomplete = autocomplete; })(jQuery, Drupal);