Mercurial > hg > cmmr2012-drupal-site
diff core/misc/autocomplete.es6.js @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/misc/autocomplete.es6.js Thu Jul 05 14:24:15 2018 +0000 @@ -0,0 +1,280 @@ +/** + * @file + * Autocomplete based on jQuery UI. + */ + +(function ($, Drupal) { + let autocomplete; + + /** + * Helper splitting terms from the autocomplete value. + * + * @function Drupal.autocomplete.splitValues + * + * @param {string} value + * The value being entered by the user. + * + * @return {Array} + * Array of values, split by comma. + */ + function autocompleteSplitValues(value) { + // We will match the value against comma-separated terms. + const result = []; + let quote = false; + let current = ''; + const valueLength = value.length; + let character; + + for (let 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. + * + * @function Drupal.autocomplete.extractLastTerm + * + * @param {string} terms + * The value of the field. + * + * @return {string} + * The last value of the input field. + */ + function extractLastTerm(terms) { + return autocomplete.splitValues(terms).pop(); + } + + /** + * The search handler is called before a search is performed. + * + * @function Drupal.autocomplete.options.search + * + * @param {object} event + * The event triggered. + * + * @return {bool} + * Whether to perform a search or not. + */ + function searchHandler(event) { + const options = autocomplete.options; + + if (options.isComposing) { + return false; + } + + const term = autocomplete.extractLastTerm(event.target.value); + // Abort search if the first character is in firstCharacterBlacklist. + if (term.length > 0 && options.firstCharacterBlacklist.indexOf(term[0]) !== -1) { + return false; + } + // Only search when the term is at least the minimum length. + return term.length >= options.minLength; + } + + /** + * JQuery UI autocomplete source callback. + * + * @param {object} request + * The request object. + * @param {function} response + * The function to call with the response. + */ + function sourceData(request, response) { + const 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 + * Suggestions returned by the server. + */ + function showSuggestions(suggestions) { + const tagged = autocomplete.splitValues(request.term); + const il = tagged.length; + for (let i = 0; i < il; i++) { + const 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 + * The data sent back from the server. + */ + 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. + const term = autocomplete.extractLastTerm(request.term); + + // Check if the term is already cached. + if (autocomplete.cache[elementId].hasOwnProperty(term)) { + showSuggestions(autocomplete.cache[elementId][term]); + } + else { + const options = $.extend({ success: sourceCallbackHandler, data: { q: term } }, autocomplete.ajax); + $.ajax(this.element.attr('data-autocomplete-path'), options); + } + } + + /** + * Handles an autocompletefocus event. + * + * @return {bool} + * Always returns false. + */ + function focusHandler() { + return false; + } + + /** + * Handles an autocompleteselect event. + * + * @param {jQuery.Event} event + * The event triggered. + * @param {object} ui + * The jQuery UI settings object. + * + * @return {bool} + * Returns false to indicate the event status. + */ + function selectHandler(event, ui) { + const terms = autocomplete.splitValues(event.target.value); + // Remove the current input. + terms.pop(); + // Add the selected item. + 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; + } + + /** + * Override jQuery UI _renderItem function to output HTML by default. + * + * @param {jQuery} ul + * jQuery collection of the ul element. + * @param {object} item + * The list item to append. + * + * @return {jQuery} + * jQuery collection of the ul element. + */ + function renderItem(ul, item) { + return $('<li>') + .append($('<a>').html(item.label)) + .appendTo(ul); + } + + /** + * Attaches the autocomplete behavior to all required fields. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches the autocomplete behaviors. + * @prop {Drupal~behaviorDetach} detach + * Detaches the autocomplete behaviors. + */ + Drupal.behaviors.autocomplete = { + attach(context) { + // Act on textfields with the "form-autocomplete" class. + const $autocomplete = $(context).find('input.form-autocomplete').once('autocomplete'); + if ($autocomplete.length) { + // Allow options to be overriden per instance. + const blacklist = $autocomplete.attr('data-autocomplete-first-character-blacklist'); + $.extend(autocomplete.options, { + firstCharacterBlacklist: (blacklist) || '', + }); + // Use jQuery UI Autocomplete on the textfield. + $autocomplete.autocomplete(autocomplete.options) + .each(function () { + $(this).data('ui-autocomplete')._renderItem = autocomplete.options.renderItem; + }); + + // Use CompositionEvent to handle IME inputs. It requests remote server on "compositionend" event only. + $autocomplete.on('compositionstart.autocomplete', () => { + autocomplete.options.isComposing = true; + }); + $autocomplete.on('compositionend.autocomplete', () => { + autocomplete.options.isComposing = false; + }); + } + }, + detach(context, settings, trigger) { + if (trigger === 'unload') { + $(context).find('input.form-autocomplete') + .removeOnce('autocomplete') + .autocomplete('destroy'); + } + }, + }; + + /** + * Autocomplete object implementation. + * + * @namespace Drupal.autocomplete + */ + autocomplete = { + cache: {}, + // Exposes options to allow overriding by contrib. + splitValues: autocompleteSplitValues, + extractLastTerm, + // jQuery UI autocomplete options. + + /** + * JQuery UI option object. + * + * @name Drupal.autocomplete.options + */ + options: { + source: sourceData, + focus: focusHandler, + search: searchHandler, + select: selectHandler, + renderItem, + minLength: 1, + // Custom options, used by Drupal.autocomplete. + firstCharacterBlacklist: '', + // Custom options, indicate IME usage status. + isComposing: false, + }, + ajax: { + dataType: 'json', + }, + }; + + Drupal.autocomplete = autocomplete; +}(jQuery, Drupal));