From 5bce5eeb25f2f292e2059dfb51ca540d878233f3 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Fri, 5 Mar 2021 13:13:45 +0000 Subject: [PATCH] Issue #2402103 by nod_, bnjmnm, larowlan, flxa, anmolgoyal74, droplet, xjm, catch, gabesullice, lauriii, Dries, justafish: Add once.js to core --- core/.eslintrc.json | 1 + core/assets/vendor/once/once.js | 387 +++++++++++++++++++++ core/assets/vendor/once/once.min.js | 3 + core/assets/vendor/once/once.min.js.map | 1 + core/core.libraries.yml | 19 +- core/misc/date.es6.js | 37 +- core/misc/date.js | 17 +- core/misc/polyfills/element.matches.es6.js | 17 + core/misc/polyfills/element.matches.js | 10 + core/themes/claro/claro.libraries.yml | 2 + core/themes/claro/js/autocomplete.es6.js | 13 +- core/themes/claro/js/autocomplete.js | 6 +- 12 files changed, 456 insertions(+), 57 deletions(-) create mode 100644 core/assets/vendor/once/once.js create mode 100644 core/assets/vendor/once/once.min.js create mode 100644 core/assets/vendor/once/once.min.js.map create mode 100644 core/misc/polyfills/element.matches.es6.js create mode 100644 core/misc/polyfills/element.matches.js diff --git a/core/.eslintrc.json b/core/.eslintrc.json index a133f542bb8..7cd9b27b375 100644 --- a/core/.eslintrc.json +++ b/core/.eslintrc.json @@ -20,6 +20,7 @@ "Modernizr": true, "Popper": true, "Sortable": true, + "once": true, "CKEDITOR": true, "tabbable": true }, diff --git a/core/assets/vendor/once/once.js b/core/assets/vendor/once/once.js new file mode 100644 index 00000000000..cf13eeced12 --- /dev/null +++ b/core/assets/vendor/once/once.js @@ -0,0 +1,387 @@ +/*! @drupal/once - v1.0.0 - 2021-03-04 */ +/** + * Mark DOM elements as processed to prevent multiple initializations. + * + * @module @drupal/once + * + * @example + * + * + * @example + * + * + * @example + * + * + */ + +/** + * Illegal spaces in ids. + * + * @private + * + * @type {RegExp} + */ +const wsRE = /[\11\12\14\15\40]+/; + +/** + * Name of the HTML attribute containing an element's once ids. + * + * @private + * + * @type {string} + */ +const attrName = 'data-once'; + +/** + * Shortcut to access the html element. + * + * @private + * + * @type {HTMLElement} + */ +const doc = document; + +/** + * Helper to access element attributes. + * + * @private + * + * @param {Element} element + * The Element to access the data-once attribute from. + * @param {string} op + * The action to take on the element. + * @param {string} [value] + * Optional value for setAttribute. + * + * @return {string|undefined|null|boolean} + * Result of the attribute method. + */ +function attr(element, op, value) { + return element[`${op}Attribute`](attrName, value); +} + +/** + * Return the attribute selector. + * + * @private + * + * @param {string} id + * The id passed by a call to a once() function. + * + * @return {string} + * The full CSS attribute selector. + * + * @throws {TypeError|RangeError} + */ +function attrSelector(id) { + // Verify the validity of the once id. + if (typeof id !== 'string') { + throw new TypeError('once ID must be a string'); + } + if (id === '' || wsRE.test(id)) { + throw new RangeError('once ID must not be empty or contain spaces'); + } + // The id is valid, return the full CSS selector. + return `[${attrName}~="${id}"]`; +} + +/** + * Verifies that an item is an instance of Element. + * + * This function is used during filtering to ensure only DOM elements are + * processed. once() makes use of get/setAttribute, which are methods + * inherited from the Element object, so only of Element can be used. + * + * @private + * + * @param {*} itemToCheck + * The item to check. + * + * @return {boolean} + * True if the item is an instance of Element + * + * @throws {TypeError} + */ +function checkElement(itemToCheck) { + if (!(itemToCheck instanceof Element)) { + throw new TypeError('The element must be an instance of Element'); + } + return true; +} + +/** + * Process arguments, query the DOM if necessary. + * + * @private + * + * @param {NodeList|Array.|Element|string} selector + * A NodeList or array of elements. + * @param {Document|Element} [context=document] + * An element to use as context for querySelectorAll. + * + * @return {Array.} + * An array with the processed Id and the list of elements to process. + */ +function getElements(selector, context = doc) { + if (!selector) { + throw new TypeError('Selector must not be empty'); + } + // Assume selector is an array-like value. + let elements = selector; + + // This is a selector, query the elements. + if ( + typeof selector === 'string' && + (context === doc || checkElement(context)) + ) { + elements = context.querySelectorAll(selector); + } + // This is a single element. + else if (selector instanceof Element) { + elements = [selector]; + } + + // Make sure an array is returned and not a NodeList or an Array-like object. + return Array.prototype.slice.call(elements); +} + +/** + * A helper for applying DOM changes to a filtered set of elements. + * + * This makes it possible to filter items that are not instances of Element, + * then modify their DOM attributes in a single array traversal. + * + * @private + * + * @param {string} selector + * A CSS selector to check against to each element in the array. + * @param {Array.} elements + * A NodeList or array of elements passed by a call to a once() function. + * @param {function} [apply] + * An optional function to apply on all matched elements. + * + * @return {Array.} + * The array of elements that match the CSS selector. + */ +function filterAndModify(selector, elements, apply) { + return elements.filter(element => { + const selected = checkElement(element) && element.matches(selector); + if (selected && apply) { + apply(element); + } + return selected; + }); +} + +/** + * Add or remove an item from a list of once values. + * + * This function removes duplicates while adding or removing a once id in a + * single array traversal. + * + * @private + * + * @param {Element} element + * A space separated string of once ids from a data-drupal-once attribute. + * @param {string} [add] + * The once id to add to the list of values. + * @param {string} [remove] + * The once id to remove from the list of values. + * + * @return {undefined} + * Nothing to return this is a callback in a foreach. + */ +function updateAttribute(element, { add, remove }) { + const result = []; + if (attr(element, 'has')) { + attr(element, 'get') + .trim() + .split(wsRE) + .forEach(item => { + if (result.indexOf(item) < 0 && item !== remove) { + result.push(item); + } + }); + } + if (add) { + result.push(add); + } + const attribute = result.join(' '); + attr(element, attribute === '' ? 'remove' : 'set', attribute); +} + +/** + * Ensures a JavaScript callback is only executed once on a set of elements. + * + * Filters a NodeList or array of elements, removing those already processed + * by a callback with a given id. + * This method adds a `data-once` attribute on DOM elements. The value of + * this attribute identifies if a given callback has been executed on that + * element. + * + * @global + * + * @example Basic usage + * const elements = once('my-once-id', '[data-myelement]'); + * @example Input parameters accepted + * // NodeList. + * once('my-once-id', document.querySelectorAll('[data-myelement]')); + * // Array or Array-like of Element. + * once('my-once-id', jQuery('[data-myelement]')); + * // A CSS selector without a context. + * once('my-once-id', '[data-myelement]'); + * // A CSS selector with a context. + * once('my-once-id', '[data-myelement]', document.head); + * // Single Element. + * once('my-once-id', document.querySelector('#some-id')); + * @example Using a single element + * // Once always returns an array, even when passing a single element. Some + * // forms that can be used to keep code readable. + * // Destructuring: + * const [myElement] = once('my-once-id', document.body); + * // By changing the resulting array, es5 compatible. + * const myElement = once('my-once-id', document.body).shift(); + * + * @param {string} id + * The id of the once call. + * @param {NodeList|Array.|Element|string} selector + * A NodeList or array of elements. + * @param {Document|Element} [context=document] + * An element to use as context for querySelectorAll. + * + * @return {Array.} + * An array of elements that have not yet been processed by a once call + * with a given id. + */ +function once(id, selector, context) { + return filterAndModify( + `:not(${attrSelector(id)})`, + getElements(selector, context), + element => updateAttribute(element, { add: id }), + ); +} + +/** + * Removes a once id from an element's data-drupal-once attribute value. + * + * If a once id is removed from an element's data-drupal-once attribute value, + * the JavaScript callback associated with that id can be executed on that + * element again. + * + * @method once.remove + * + * @example Basic usage + * const elements = once.remove('my-once-id', '[data-myelement]'); + * @example Input parameters accepted + * // NodeList. + * once.remove('my-once-id', document.querySelectorAll('[data-myelement]')); + * // Array or Array-like of Element. + * once.remove('my-once-id', jQuery('[data-myelement]')); + * // A CSS selector without a context. + * once.remove('my-once-id', '[data-myelement]'); + * // A CSS selector with a context. + * once.remove('my-once-id', '[data-myelement]', document.head); + * // Single Element. + * once.remove('my-once-id', document.querySelector('#some-id')); + * + * @param {string} id + * The id of a once call. + * @param {NodeList|Array.|Element|string} selector + * A NodeList or array of elements to remove the once id from. + * @param {Document|Element} [context=document] + * An element to use as context for querySelectorAll. + * + * @return {Array.} + * A filtered array of elements that had been processed by the provided id, + * and are now able to be processed again. + */ +once.remove = (id, selector, context) => { + return filterAndModify( + attrSelector(id), + getElements(selector, context), + element => updateAttribute(element, { remove: id }), + ); +}; + +/** + * Finds elements that have been processed by a given once id. + * + * Behaves like {@link once} and {@link once.remove} without changing the DOM. + * To select all DOM nodes processed by a given id, use {@link once.find}. + * + * @method once.filter + * + * @example Basic usage + * const filteredElements = once.filter('my-once-id', '[data-myelement]'); + * @example Input parameters accepted + * // NodeList. + * once.filter('my-once-id', document.querySelectorAll('[data-myelement]')); + * // Array or Array-like of Element. + * once.filter('my-once-id', jQuery('[data-myelement]')); + * // A CSS selector without a context. + * once.filter('my-once-id', '[data-myelement]'); + * // A CSS selector with a context. + * once.filter('my-once-id', '[data-myelement]', document.head); + * // Single Element. + * once.filter('my-once-id', document.querySelector('#some-id')); + * + * @param {string} id + * The id of the once call. + * @param {NodeList|Array.|Element|string} selector + * A NodeList or array of elements to remove the once id from. + * @param {Document|Element} [context=document] + * An element to use as context for querySelectorAll. + * + * @return {Array.} + * A filtered array of elements that have already been processed by the + * provided once id. + */ +once.filter = (id, selector, context) => + filterAndModify(attrSelector(id), getElements(selector, context)); + +/** + * Finds elements that have been processed by a given once id. + * + * Query the 'context' element for elements that already have the + * corresponding once id value. + * + * @method once.find + * + * @example Basic usage + * const oncedElements = once.find('my-once-id'); + * @example Input parameters accepted + * // Call without parameters, return all elements with a `data-once` attribute. + * once.find(); + * // Call without a context. + * once.find('my-once-id'); + * // Call with a context. + * once.find('my-once-id', document.head); + * + * @param {string} [id] + * The id of the once call. + * @param {Document|Element} [context=document] + * Scope of the search for matching elements. + * + * @return {Array.} + * A filtered array of elements that have already been processed by the + * provided once id. + */ +once.find = (id, context) => + getElements(!id ? `[${attrName}]` : attrSelector(id), context); + +export default once; diff --git a/core/assets/vendor/once/once.min.js b/core/assets/vendor/once/once.min.js new file mode 100644 index 00000000000..b972c2a6c74 --- /dev/null +++ b/core/assets/vendor/once/once.min.js @@ -0,0 +1,3 @@ +/*! @drupal/once - v1.0.0 - 2021-03-04 */ +var once=function(){"use strict";var n=/[\11\12\14\15\40]+/,t="data-once",e=document;function r(n,e,r){return n[e+"Attribute"](t,r)}function o(t){if("string"!=typeof t)throw new TypeError("once ID must be a string");if(""===t||n.test(t))throw new RangeError("once ID must not be empty or contain spaces");return'[data-once~="'+t+'"]'}function u(n){if(!(n instanceof Element))throw new TypeError("The element must be an instance of Element");return!0}function i(n,t){if(void 0===t&&(t=e),!n)throw new TypeError("Selector must not be empty");var r=n;return"string"!=typeof n||t!==e&&!u(t)?n instanceof Element&&(r=[n]):r=t.querySelectorAll(n),Array.prototype.slice.call(r)}function c(n,t,e){return t.filter((function(t){var r=u(t)&&t.matches(n);return r&&e&&e(t),r}))}function f(t,e){var o=e.add,u=e.remove,i=[];r(t,"has")&&r(t,"get").trim().split(n).forEach((function(n){i.indexOf(n)<0&&n!==u&&i.push(n)})),o&&i.push(o);var c=i.join(" ");r(t,""===c?"remove":"set",c)}function a(n,t,e){return c(":not("+o(n)+")",i(t,e),(function(t){return f(t,{add:n})}))}return a.remove=function(n,t,e){return c(o(n),i(t,e),(function(t){return f(t,{remove:n})}))},a.filter=function(n,t,e){return c(o(n),i(t,e))},a.find=function(n,t){return i(n?o(n):"[data-once]",t)},a}(); +//# sourceMappingURL=once.min.js.map diff --git a/core/assets/vendor/once/once.min.js.map b/core/assets/vendor/once/once.min.js.map new file mode 100644 index 00000000000..2b29e5c1656 --- /dev/null +++ b/core/assets/vendor/once/once.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"once.min.js","sources":["once.js"],"sourcesContent":null,"names":["const","wsRE","attrName","doc","document","attr","element","op","value","attrSelector","id","TypeError","test","RangeError","checkElement","itemToCheck","Element","getElements","selector","context","let","elements","querySelectorAll","Array","prototype","slice","call","filterAndModify","apply","filter","selected","matches","updateAttribute","result","trim","split","forEach","item","indexOf","remove","push","add","attribute","join","once","find"],"mappings":";iCAsCAA,IAAMC,EAAO,qBASPC,EAAW,YASXC,EAAMC,SAiBZ,SAASC,EAAKC,EAASC,EAAIC,GACzB,OAAOF,EAAWC,eAAeL,EAAUM,GAgB7C,SAASC,EAAaC,GAEpB,GAAkB,iBAAPA,EACT,MAAM,IAAIC,UAAU,4BAEtB,GAAW,KAAPD,GAAaT,EAAKW,KAAKF,GACzB,MAAM,IAAIG,WAAW,+CAGvB,sBAAyBH,OAoB3B,SAASI,EAAaC,GACpB,KAAMA,aAAuBC,SAC3B,MAAM,IAAIL,UAAU,8CAEtB,OAAO,EAgBT,SAASM,EAAYC,EAAUC,GAC7B,kBADuChB,IAClCe,EACH,MAAM,IAAIP,UAAU,8BAGtBS,IAAIC,EAAWH,EAef,MAXsB,iBAAbA,GACNC,IAAYhB,IAAOW,EAAaK,GAK1BD,aAAoBF,UAC3BK,EAAW,CAACH,IAJZG,EAAWF,EAAQG,iBAAiBJ,GAQ/BK,MAAMC,UAAUC,MAAMC,KAAKL,GAqBpC,SAASM,EAAgBT,EAAUG,EAAUO,GAC3C,OAAOP,EAASQ,iBAAOvB,GACrBN,IAAM8B,EAAWhB,EAAaR,IAAYA,EAAQyB,QAAQb,GAI1D,OAHIY,GAAYF,GACdA,EAAMtB,GAEDwB,KAsBX,SAASE,EAAgB1B,4BACjB2B,EAAS,GACX5B,EAAKC,EAAS,QAChBD,EAAKC,EAAS,OACX4B,OACAC,MAAMlC,GACNmC,kBAAQC,GACHJ,EAAOK,QAAQD,GAAQ,GAAKA,IAASE,GACvCN,EAAOO,KAAKH,MAIhBI,GACFR,EAAOO,KAAKC,GAEdzC,IAAM0C,EAAYT,EAAOU,KAAK,KAC9BtC,EAAKC,EAAuB,KAAdoC,EAAmB,SAAW,MAAOA,GA8CrD,SAASE,EAAKlC,EAAIQ,EAAUC,GAC1B,OAAOQ,UACGlB,EAAaC,OACrBO,EAAYC,EAAUC,aACtBb,UAAW0B,EAAgB1B,EAAS,CAAEmC,IAAK/B,cAsC/CkC,EAAKL,gBAAU7B,EAAIQ,EAAUC,GAC3B,OAAOQ,EACLlB,EAAaC,GACbO,EAAYC,EAAUC,aACtBb,UAAW0B,EAAgB1B,EAAS,CAAEiC,OAAQ7B,QAqClDkC,EAAKf,gBAAUnB,EAAIQ,EAAUC,UAC3BQ,EAAgBlB,EAAaC,GAAKO,EAAYC,EAAUC,KA6B1DyB,EAAKC,cAAQnC,EAAIS,UACfF,EAAaP,EAAuBD,EAAaC,iBAAKS"} \ No newline at end of file diff --git a/core/core.libraries.yml b/core/core.libraries.yml index f998d76c87d..2951aaaa7cc 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -181,7 +181,7 @@ drupal.date: dependencies: - core/drupal - core/modernizr - - core/jquery.once + - core/once drupal.debounce: version: VERSION @@ -279,6 +279,11 @@ drupal.dropbutton: - core/drupalSettings - core/jquery.once +drupal.element.matches: + version: VERSION + js: + misc/polyfills/element.matches.js: { weight: -20 } + drupal.entity-form: version: VERSION js: @@ -705,6 +710,18 @@ normalize: assets/vendor/normalize-css/normalize.css: { weight: -20 } misc/normalize-fixes.css: { weight: -19 } +once: + remote: https://git.drupalcode.org/project/once + version: "1.0.0" + license: + name: GNU-GPL-2.0-or-later + url: https://git.drupalcode.org/project/once/-/raw/v1.0.0/LICENSE.md + gpl-compatible: true + js: + assets/vendor/once/once.min.js: { weight: -19, minified: true } + dependencies: + - core/drupal.element.matches + picturefill: remote: https://github.com/scottjehl/picturefill version: "3.0.3" diff --git a/core/misc/date.es6.js b/core/misc/date.es6.js index 97fde1825fe..a1862133f68 100644 --- a/core/misc/date.es6.js +++ b/core/misc/date.es6.js @@ -3,7 +3,7 @@ * Polyfill for HTML5 date input. */ -(function ($, Modernizr, Drupal) { +(function ($, Modernizr, Drupal, once) { /** * Attach datepicker fallback on date elements. * @@ -15,32 +15,10 @@ */ Drupal.behaviors.date = { attach(context, settings) { - const dataFieldElements = 'data-drupal-field-elements'; - const dataDatepickerProcessed = 'data-datepicker-is-processed'; - - /** - * Returns a CSS selector for a date field to process. - * - * The dataDatepickerProcessed attribute prevents a field from being - * selected and processed more than once. - * - * @param {string} elements - * The data attribute value. - * - * @return {string} - * A CSS Selector. - */ - const getDateSelector = (elements) => - [ - `[${dataFieldElements}="${elements}"]`, - `:not([${dataDatepickerProcessed}="${elements}"])`, - ].join(''); - // If the browser does not support a native datepicker, add date // formatting instructions on date/time fields. if (Modernizr.inputtypes.date === false) { - Array.prototype.forEach.call( - document.querySelectorAll(getDateSelector('date-time')), + once('datepicker', '[data-drupal-field-elements="date-time"]').forEach( (dateTime) => { const dateInput = dateTime.querySelector('input[type="date"]'); const timeInput = dateTime.querySelector('input[type="time"]'); @@ -63,14 +41,10 @@ }); Drupal.DatepickerPolyfill.attachDescription(dateTime, help); - - // Set attribute to prevent element from being processed again. - dateTime.setAttribute(dataDatepickerProcessed, 'date-time'); }, ); - Array.prototype.forEach.call( - document.querySelectorAll(getDateSelector('date')), + once('datepicker', '[data-drupal-field-elements="date"]').forEach( (date) => { const dateInput = date.querySelector('input[type="date"]'); const help = Drupal.theme.dateHelp({ @@ -86,9 +60,6 @@ // changed to reflect this. dateInput.setAttribute('type', 'text'); Drupal.DatepickerPolyfill.attachDescription(date, help, id); - - // Set attribute to prevent element from selection on next run. - date.setAttribute(dataDatepickerProcessed, 'date'); }, ); } @@ -180,4 +151,4 @@ `
${dateDesc} ${timeDesc}
`; -})(jQuery, Modernizr, Drupal); +})(jQuery, Modernizr, Drupal, once); diff --git a/core/misc/date.js b/core/misc/date.js index 4cfa477eb9d..4ae1e561986 100644 --- a/core/misc/date.js +++ b/core/misc/date.js @@ -11,18 +11,11 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } -(function ($, Modernizr, Drupal) { +(function ($, Modernizr, Drupal, once) { Drupal.behaviors.date = { attach: function attach(context, settings) { - var dataFieldElements = 'data-drupal-field-elements'; - var dataDatepickerProcessed = 'data-datepicker-is-processed'; - - var getDateSelector = function getDateSelector(elements) { - return ["[".concat(dataFieldElements, "=\"").concat(elements, "\"]"), ":not([".concat(dataDatepickerProcessed, "=\"").concat(elements, "\"])")].join(''); - }; - if (Modernizr.inputtypes.date === false) { - Array.prototype.forEach.call(document.querySelectorAll(getDateSelector('date-time')), function (dateTime) { + once('datepicker', '[data-drupal-field-elements="date-time"]').forEach(function (dateTime) { var dateInput = dateTime.querySelector('input[type="date"]'); var timeInput = dateTime.querySelector('input[type="time"]'); var help = Drupal.theme.dateTimeHelp({ @@ -36,9 +29,8 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d input.setAttribute('type', 'text'); }); Drupal.DatepickerPolyfill.attachDescription(dateTime, help); - dateTime.setAttribute(dataDatepickerProcessed, 'date-time'); }); - Array.prototype.forEach.call(document.querySelectorAll(getDateSelector('date')), function (date) { + once('datepicker', '[data-drupal-field-elements="date"]').forEach(function (date) { var dateInput = date.querySelector('input[type="date"]'); var help = Drupal.theme.dateHelp({ dateDesc: dateInput.dataset.help @@ -47,7 +39,6 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d dateInput.setAttribute('aria-describedby', id); dateInput.setAttribute('type', 'text'); Drupal.DatepickerPolyfill.attachDescription(date, help, id); - date.setAttribute(dataDatepickerProcessed, 'date'); }); } } @@ -100,4 +91,4 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d timeDesc = _ref2.timeDesc; return "
\n ").concat(dateDesc, " ").concat(timeDesc, "\n
"); }; -})(jQuery, Modernizr, Drupal); \ No newline at end of file +})(jQuery, Modernizr, Drupal, once); \ No newline at end of file diff --git a/core/misc/polyfills/element.matches.es6.js b/core/misc/polyfills/element.matches.es6.js new file mode 100644 index 00000000000..0b456325a73 --- /dev/null +++ b/core/misc/polyfills/element.matches.es6.js @@ -0,0 +1,17 @@ +/** + * @file + * Provides a polyfill for Element.prototype.matches(). + * + * This is needed for Internet Explorer 9+ + * + * This has been copied from MDN Web Docs code samples. Code samples in the MDN + * Web Docs are licensed under CC0. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill + * @see https://developer.mozilla.org/en-US/docs/MDN/About#Code_samples_and_snippets + */ +if (!Element.prototype.matches) { + Element.prototype.matches = + Element.prototype.msMatchesSelector || + Element.prototype.webkitMatchesSelector; +} diff --git a/core/misc/polyfills/element.matches.js b/core/misc/polyfills/element.matches.js new file mode 100644 index 00000000000..8fc544b4b64 --- /dev/null +++ b/core/misc/polyfills/element.matches.js @@ -0,0 +1,10 @@ +/** +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ + +if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; +} \ No newline at end of file diff --git a/core/themes/claro/claro.libraries.yml b/core/themes/claro/claro.libraries.yml index 5b178612db8..cde2ce22db1 100644 --- a/core/themes/claro/claro.libraries.yml +++ b/core/themes/claro/claro.libraries.yml @@ -208,6 +208,8 @@ autocomplete: version: VERSION js: js/autocomplete.js: {} + dependencies: + - core/once drupal.shortcut: version: VERSION diff --git a/core/themes/claro/js/autocomplete.es6.js b/core/themes/claro/js/autocomplete.es6.js index 245529ba2c3..a7abf4da18e 100644 --- a/core/themes/claro/js/autocomplete.es6.js +++ b/core/themes/claro/js/autocomplete.es6.js @@ -3,13 +3,11 @@ * Claro's enhancement for autocomplete form element. */ -(($, Drupal) => { +(($, Drupal, once) => { Drupal.behaviors.claroAutoCompete = { attach(context) { - $(context) - .find('input.form-autocomplete') - .once('claroAutoComplete') - .each((index, value) => { + once('claroAutoComplete', 'input.form-autocomplete', context).forEach( + (value) => { const $input = $(value); const timeout = 400; let classRemoveTimeout; @@ -37,7 +35,8 @@ ); }, ); - }); + }, + ); }, }; -})(jQuery, Drupal); +})(jQuery, Drupal, once); diff --git a/core/themes/claro/js/autocomplete.js b/core/themes/claro/js/autocomplete.js index d86cc300463..79db89081fc 100644 --- a/core/themes/claro/js/autocomplete.js +++ b/core/themes/claro/js/autocomplete.js @@ -5,10 +5,10 @@ * @preserve **/ -(function ($, Drupal) { +(function ($, Drupal, once) { Drupal.behaviors.claroAutoCompete = { attach: function attach(context) { - $(context).find('input.form-autocomplete').once('claroAutoComplete').each(function (index, value) { + once('claroAutoComplete', 'input.form-autocomplete', context).forEach(function (value) { var $input = $(value); var timeout = 400; var classRemoveTimeout; @@ -30,4 +30,4 @@ }); } }; -})(jQuery, Drupal); \ No newline at end of file +})(jQuery, Drupal, once); \ No newline at end of file