Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / misc / ajax.es6.js
1 /**
2  * @file
3  * Provides Ajax page updating via jQuery $.ajax.
4  *
5  * Ajax is a method of making a request via JavaScript while viewing an HTML
6  * page. The request returns an array of commands encoded in JSON, which is
7  * then executed to make any changes that are necessary to the page.
8  *
9  * Drupal uses this file to enhance form elements with `#ajax['url']` and
10  * `#ajax['wrapper']` properties. If set, this file will automatically be
11  * included to provide Ajax capabilities.
12  */
13
14 (function ($, window, Drupal, drupalSettings) {
15   /**
16    * Attaches the Ajax behavior to each Ajax form element.
17    *
18    * @type {Drupal~behavior}
19    *
20    * @prop {Drupal~behaviorAttach} attach
21    *   Initialize all {@link Drupal.Ajax} objects declared in
22    *   `drupalSettings.ajax` or initialize {@link Drupal.Ajax} objects from
23    *   DOM elements having the `use-ajax-submit` or `use-ajax` css class.
24    * @prop {Drupal~behaviorDetach} detach
25    *   During `unload` remove all {@link Drupal.Ajax} objects related to
26    *   the removed content.
27    */
28   Drupal.behaviors.AJAX = {
29     attach(context, settings) {
30       function loadAjaxBehavior(base) {
31         const elementSettings = settings.ajax[base];
32         if (typeof elementSettings.selector === 'undefined') {
33           elementSettings.selector = `#${base}`;
34         }
35         $(elementSettings.selector).once('drupal-ajax').each(function () {
36           elementSettings.element = this;
37           elementSettings.base = base;
38           Drupal.ajax(elementSettings);
39         });
40       }
41
42       // Load all Ajax behaviors specified in the settings.
43       Object.keys(settings.ajax || {}).forEach(base => loadAjaxBehavior(base));
44
45       Drupal.ajax.bindAjaxLinks(document.body);
46
47       // This class means to submit the form to the action using Ajax.
48       $('.use-ajax-submit').once('ajax').each(function () {
49         const elementSettings = {};
50
51         // Ajax submits specified in this manner automatically submit to the
52         // normal form action.
53         elementSettings.url = $(this.form).attr('action');
54         // Form submit button clicks need to tell the form what was clicked so
55         // it gets passed in the POST request.
56         elementSettings.setClick = true;
57         // Form buttons use the 'click' event rather than mousedown.
58         elementSettings.event = 'click';
59         // Clicked form buttons look better with the throbber than the progress
60         // bar.
61         elementSettings.progress = { type: 'throbber' };
62         elementSettings.base = $(this).attr('id');
63         elementSettings.element = this;
64
65         Drupal.ajax(elementSettings);
66       });
67     },
68
69     detach(context, settings, trigger) {
70       if (trigger === 'unload') {
71         Drupal.ajax.expired().forEach((instance) => {
72           // Set this to null and allow garbage collection to reclaim
73           // the memory.
74           Drupal.ajax.instances[instance.instanceIndex] = null;
75         });
76       }
77     },
78   };
79
80   /**
81    * Extends Error to provide handling for Errors in Ajax.
82    *
83    * @constructor
84    *
85    * @augments Error
86    *
87    * @param {XMLHttpRequest} xmlhttp
88    *   XMLHttpRequest object used for the failed request.
89    * @param {string} uri
90    *   The URI where the error occurred.
91    * @param {string} customMessage
92    *   The custom message.
93    */
94   Drupal.AjaxError = function (xmlhttp, uri, customMessage) {
95     let statusCode;
96     let statusText;
97     let responseText;
98     if (xmlhttp.status) {
99       statusCode = `\n${Drupal.t('An AJAX HTTP error occurred.')}\n${Drupal.t('HTTP Result Code: !status', { '!status': xmlhttp.status })}`;
100     }
101     else {
102       statusCode = `\n${Drupal.t('An AJAX HTTP request terminated abnormally.')}`;
103     }
104     statusCode += `\n${Drupal.t('Debugging information follows.')}`;
105     const pathText = `\n${Drupal.t('Path: !uri', { '!uri': uri })}`;
106     statusText = '';
107     // In some cases, when statusCode === 0, xmlhttp.statusText may not be
108     // defined. Unfortunately, testing for it with typeof, etc, doesn't seem to
109     // catch that and the test causes an exception. So we need to catch the
110     // exception here.
111     try {
112       statusText = `\n${Drupal.t('StatusText: !statusText', { '!statusText': $.trim(xmlhttp.statusText) })}`;
113     }
114     catch (e) {
115       // Empty.
116     }
117
118     responseText = '';
119     // Again, we don't have a way to know for sure whether accessing
120     // xmlhttp.responseText is going to throw an exception. So we'll catch it.
121     try {
122       responseText = `\n${Drupal.t('ResponseText: !responseText', { '!responseText': $.trim(xmlhttp.responseText) })}`;
123     }
124     catch (e) {
125       // Empty.
126     }
127
128     // Make the responseText more readable by stripping HTML tags and newlines.
129     responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi, '');
130     responseText = responseText.replace(/[\n]+\s+/g, '\n');
131
132     // We don't need readyState except for status == 0.
133     const readyStateText = xmlhttp.status === 0 ? (`\n${Drupal.t('ReadyState: !readyState', { '!readyState': xmlhttp.readyState })}`) : '';
134
135     customMessage = customMessage ? (`\n${Drupal.t('CustomMessage: !customMessage', { '!customMessage': customMessage })}`) : '';
136
137     /**
138      * Formatted and translated error message.
139      *
140      * @type {string}
141      */
142     this.message = statusCode + pathText + statusText + customMessage + responseText + readyStateText;
143
144     /**
145      * Used by some browsers to display a more accurate stack trace.
146      *
147      * @type {string}
148      */
149     this.name = 'AjaxError';
150   };
151
152   Drupal.AjaxError.prototype = new Error();
153   Drupal.AjaxError.prototype.constructor = Drupal.AjaxError;
154
155   /**
156    * Provides Ajax page updating via jQuery $.ajax.
157    *
158    * This function is designed to improve developer experience by wrapping the
159    * initialization of {@link Drupal.Ajax} objects and storing all created
160    * objects in the {@link Drupal.ajax.instances} array.
161    *
162    * @example
163    * Drupal.behaviors.myCustomAJAXStuff = {
164    *   attach: function (context, settings) {
165    *
166    *     var ajaxSettings = {
167    *       url: 'my/url/path',
168    *       // If the old version of Drupal.ajax() needs to be used those
169    *       // properties can be added
170    *       base: 'myBase',
171    *       element: $(context).find('.someElement')
172    *     };
173    *
174    *     var myAjaxObject = Drupal.ajax(ajaxSettings);
175    *
176    *     // Declare a new Ajax command specifically for this Ajax object.
177    *     myAjaxObject.commands.insert = function (ajax, response, status) {
178    *       $('#my-wrapper').append(response.data);
179    *       alert('New content was appended to #my-wrapper');
180    *     };
181    *
182    *     // This command will remove this Ajax object from the page.
183    *     myAjaxObject.commands.destroyObject = function (ajax, response, status) {
184    *       Drupal.ajax.instances[this.instanceIndex] = null;
185    *     };
186    *
187    *     // Programmatically trigger the Ajax request.
188    *     myAjaxObject.execute();
189    *   }
190    * };
191    *
192    * @param {object} settings
193    *   The settings object passed to {@link Drupal.Ajax} constructor.
194    * @param {string} [settings.base]
195    *   Base is passed to {@link Drupal.Ajax} constructor as the 'base'
196    *   parameter.
197    * @param {HTMLElement} [settings.element]
198    *   Element parameter of {@link Drupal.Ajax} constructor, element on which
199    *   event listeners will be bound.
200    *
201    * @return {Drupal.Ajax}
202    *   The created Ajax object.
203    *
204    * @see Drupal.AjaxCommands
205    */
206   Drupal.ajax = function (settings) {
207     if (arguments.length !== 1) {
208       throw new Error('Drupal.ajax() function must be called with one configuration object only');
209     }
210     // Map those config keys to variables for the old Drupal.ajax function.
211     const base = settings.base || false;
212     const element = settings.element || false;
213     delete settings.base;
214     delete settings.element;
215
216     // By default do not display progress for ajax calls without an element.
217     if (!settings.progress && !element) {
218       settings.progress = false;
219     }
220
221     const ajax = new Drupal.Ajax(base, element, settings);
222     ajax.instanceIndex = Drupal.ajax.instances.length;
223     Drupal.ajax.instances.push(ajax);
224
225     return ajax;
226   };
227
228   /**
229    * Contains all created Ajax objects.
230    *
231    * @type {Array.<Drupal.Ajax|null>}
232    */
233   Drupal.ajax.instances = [];
234
235   /**
236    * List all objects where the associated element is not in the DOM
237    *
238    * This method ignores {@link Drupal.Ajax} objects not bound to DOM elements
239    * when created with {@link Drupal.ajax}.
240    *
241    * @return {Array.<Drupal.Ajax>}
242    *   The list of expired {@link Drupal.Ajax} objects.
243    */
244   Drupal.ajax.expired = function () {
245     return Drupal.ajax.instances.filter(instance => instance && instance.element !== false && !document.body.contains(instance.element));
246   };
247
248   /**
249    * Bind Ajax functionality to links that use the 'use-ajax' class.
250    *
251    * @param {HTMLElement} element
252    *   Element to enable Ajax functionality for.
253    */
254   Drupal.ajax.bindAjaxLinks = (element) => {
255     // Bind Ajax behaviors to all items showing the class.
256     $(element).find('.use-ajax').once('ajax').each((i, ajaxLink) => {
257       const $linkElement = $(ajaxLink);
258
259       const elementSettings = {
260         // Clicked links look better with the throbber than the progress bar.
261         progress: { type: 'throbber' },
262         dialogType: $linkElement.data('dialog-type'),
263         dialog: $linkElement.data('dialog-options'),
264         dialogRenderer: $linkElement.data('dialog-renderer'),
265         base: $linkElement.attr('id'),
266         element: ajaxLink,
267       };
268       const href = $linkElement.attr('href');
269       /**
270        * For anchor tags, these will go to the target of the anchor rather
271        * than the usual location.
272        */
273       if (href) {
274         elementSettings.url = href;
275         elementSettings.event = 'click';
276       }
277       Drupal.ajax(elementSettings);
278     });
279   };
280
281   /**
282    * Settings for an Ajax object.
283    *
284    * @typedef {object} Drupal.Ajax~elementSettings
285    *
286    * @prop {string} url
287    *   Target of the Ajax request.
288    * @prop {?string} [event]
289    *   Event bound to settings.element which will trigger the Ajax request.
290    * @prop {bool} [keypress=true]
291    *   Triggers a request on keypress events.
292    * @prop {?string} selector
293    *   jQuery selector targeting the element to bind events to or used with
294    *   {@link Drupal.AjaxCommands}.
295    * @prop {string} [effect='none']
296    *   Name of the jQuery method to use for displaying new Ajax content.
297    * @prop {string|number} [speed='none']
298    *   Speed with which to apply the effect.
299    * @prop {string} [method]
300    *   Name of the jQuery method used to insert new content in the targeted
301    *   element.
302    * @prop {object} [progress]
303    *   Settings for the display of a user-friendly loader.
304    * @prop {string} [progress.type='throbber']
305    *   Type of progress element, core provides `'bar'`, `'throbber'` and
306    *   `'fullscreen'`.
307    * @prop {string} [progress.message=Drupal.t('Please wait...')]
308    *   Custom message to be used with the bar indicator.
309    * @prop {object} [submit]
310    *   Extra data to be sent with the Ajax request.
311    * @prop {bool} [submit.js=true]
312    *   Allows the PHP side to know this comes from an Ajax request.
313    * @prop {object} [dialog]
314    *   Options for {@link Drupal.dialog}.
315    * @prop {string} [dialogType]
316    *   One of `'modal'` or `'dialog'`.
317    * @prop {string} [prevent]
318    *   List of events on which to stop default action and stop propagation.
319    */
320
321   /**
322    * Ajax constructor.
323    *
324    * The Ajax request returns an array of commands encoded in JSON, which is
325    * then executed to make any changes that are necessary to the page.
326    *
327    * Drupal uses this file to enhance form elements with `#ajax['url']` and
328    * `#ajax['wrapper']` properties. If set, this file will automatically be
329    * included to provide Ajax capabilities.
330    *
331    * @constructor
332    *
333    * @param {string} [base]
334    *   Base parameter of {@link Drupal.Ajax} constructor
335    * @param {HTMLElement} [element]
336    *   Element parameter of {@link Drupal.Ajax} constructor, element on which
337    *   event listeners will be bound.
338    * @param {Drupal.Ajax~elementSettings} elementSettings
339    *   Settings for this Ajax object.
340    */
341   Drupal.Ajax = function (base, element, elementSettings) {
342     const defaults = {
343       event: element ? 'mousedown' : null,
344       keypress: true,
345       selector: base ? `#${base}` : null,
346       effect: 'none',
347       speed: 'none',
348       method: 'replaceWith',
349       progress: {
350         type: 'throbber',
351         message: Drupal.t('Please wait...'),
352       },
353       submit: {
354         js: true,
355       },
356     };
357
358     $.extend(this, defaults, elementSettings);
359
360     /**
361      * @type {Drupal.AjaxCommands}
362      */
363     this.commands = new Drupal.AjaxCommands();
364
365     /**
366      * @type {bool|number}
367      */
368     this.instanceIndex = false;
369
370     // @todo Remove this after refactoring the PHP code to:
371     //   - Call this 'selector'.
372     //   - Include the '#' for ID-based selectors.
373     //   - Support non-ID-based selectors.
374     if (this.wrapper) {
375       /**
376        * @type {string}
377        */
378       this.wrapper = `#${this.wrapper}`;
379     }
380
381     /**
382      * @type {HTMLElement}
383      */
384     this.element = element;
385
386     /**
387      * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
388      * Use elementSettings.
389      *
390      * @type {Drupal.Ajax~elementSettings}
391      */
392     this.element_settings = elementSettings;
393
394     /**
395      * @type {Drupal.Ajax~elementSettings}
396      */
397     this.elementSettings = elementSettings;
398
399     // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
400     // bind Ajax to links as well.
401     if (this.element && this.element.form) {
402       /**
403        * @type {jQuery}
404        */
405       this.$form = $(this.element.form);
406     }
407
408     // If no Ajax callback URL was given, use the link href or form action.
409     if (!this.url) {
410       const $element = $(this.element);
411       if ($element.is('a')) {
412         this.url = $element.attr('href');
413       }
414       else if (this.element && element.form) {
415         this.url = this.$form.attr('action');
416       }
417     }
418
419     // Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
420     // the server detect when it needs to degrade gracefully.
421     // There are four scenarios to check for:
422     // 1. /nojs/
423     // 2. /nojs$ - The end of a URL string.
424     // 3. /nojs? - Followed by a query (e.g. path/nojs?destination=foobar).
425     // 4. /nojs# - Followed by a fragment (e.g.: path/nojs#myfragment).
426     const originalUrl = this.url;
427
428     /**
429      * Processed Ajax URL.
430      *
431      * @type {string}
432      */
433     this.url = this.url.replace(/\/nojs(\/|$|\?|#)/g, '/ajax$1');
434     // If the 'nojs' version of the URL is trusted, also trust the 'ajax'
435     // version.
436     if (drupalSettings.ajaxTrustedUrl[originalUrl]) {
437       drupalSettings.ajaxTrustedUrl[this.url] = true;
438     }
439
440     // Set the options for the ajaxSubmit function.
441     // The 'this' variable will not persist inside of the options object.
442     const ajax = this;
443
444     /**
445      * Options for the jQuery.ajax function.
446      *
447      * @name Drupal.Ajax#options
448      *
449      * @type {object}
450      *
451      * @prop {string} url
452      *   Ajax URL to be called.
453      * @prop {object} data
454      *   Ajax payload.
455      * @prop {function} beforeSerialize
456      *   Implement jQuery beforeSerialize function to call
457      *   {@link Drupal.Ajax#beforeSerialize}.
458      * @prop {function} beforeSubmit
459      *   Implement jQuery beforeSubmit function to call
460      *   {@link Drupal.Ajax#beforeSubmit}.
461      * @prop {function} beforeSend
462      *   Implement jQuery beforeSend function to call
463      *   {@link Drupal.Ajax#beforeSend}.
464      * @prop {function} success
465      *   Implement jQuery success function to call
466      *   {@link Drupal.Ajax#success}.
467      * @prop {function} complete
468      *   Implement jQuery success function to clean up ajax state and trigger an
469      *   error if needed.
470      * @prop {string} dataType='json'
471      *   Type of the response expected.
472      * @prop {string} type='POST'
473      *   HTTP method to use for the Ajax request.
474      */
475     ajax.options = {
476       url: ajax.url,
477       data: ajax.submit,
478       beforeSerialize(elementSettings, options) {
479         return ajax.beforeSerialize(elementSettings, options);
480       },
481       beforeSubmit(formValues, elementSettings, options) {
482         ajax.ajaxing = true;
483         return ajax.beforeSubmit(formValues, elementSettings, options);
484       },
485       beforeSend(xmlhttprequest, options) {
486         ajax.ajaxing = true;
487         return ajax.beforeSend(xmlhttprequest, options);
488       },
489       success(response, status, xmlhttprequest) {
490         // Sanity check for browser support (object expected).
491         // When using iFrame uploads, responses must be returned as a string.
492         if (typeof response === 'string') {
493           response = $.parseJSON(response);
494         }
495
496         // Prior to invoking the response's commands, verify that they can be
497         // trusted by checking for a response header. See
498         // \Drupal\Core\EventSubscriber\AjaxResponseSubscriber for details.
499         // - Empty responses are harmless so can bypass verification. This
500         //   avoids an alert message for server-generated no-op responses that
501         //   skip Ajax rendering.
502         // - Ajax objects with trusted URLs (e.g., ones defined server-side via
503         //   #ajax) can bypass header verification. This is especially useful
504         //   for Ajax with multipart forms. Because IFRAME transport is used,
505         //   the response headers cannot be accessed for verification.
506         if (response !== null && !drupalSettings.ajaxTrustedUrl[ajax.url]) {
507           if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
508             const customMessage = Drupal.t('The response failed verification so will not be processed.');
509             return ajax.error(xmlhttprequest, ajax.url, customMessage);
510           }
511         }
512
513         return ajax.success(response, status);
514       },
515       complete(xmlhttprequest, status) {
516         ajax.ajaxing = false;
517         if (status === 'error' || status === 'parsererror') {
518           return ajax.error(xmlhttprequest, ajax.url);
519         }
520       },
521       dataType: 'json',
522       type: 'POST',
523     };
524
525     if (elementSettings.dialog) {
526       ajax.options.data.dialogOptions = elementSettings.dialog;
527     }
528
529     // Ensure that we have a valid URL by adding ? when no query parameter is
530     // yet available, otherwise append using &.
531     if (ajax.options.url.indexOf('?') === -1) {
532       ajax.options.url += '?';
533     }
534     else {
535       ajax.options.url += '&';
536     }
537     // If this element has a dialog type use if for the wrapper if not use 'ajax'.
538     let wrapper = `drupal_${(elementSettings.dialogType || 'ajax')}`;
539     if (elementSettings.dialogRenderer) {
540       wrapper += `.${elementSettings.dialogRenderer}`;
541     }
542     ajax.options.url += `${Drupal.ajax.WRAPPER_FORMAT}=${wrapper}`;
543
544
545     // Bind the ajaxSubmit function to the element event.
546     $(ajax.element).on(elementSettings.event, function (event) {
547       if (!drupalSettings.ajaxTrustedUrl[ajax.url] && !Drupal.url.isLocal(ajax.url)) {
548         throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', { '!url': ajax.url }));
549       }
550       return ajax.eventResponse(this, event);
551     });
552
553     // If necessary, enable keyboard submission so that Ajax behaviors
554     // can be triggered through keyboard input as well as e.g. a mousedown
555     // action.
556     if (elementSettings.keypress) {
557       $(ajax.element).on('keypress', function (event) {
558         return ajax.keypressResponse(this, event);
559       });
560     }
561
562     // If necessary, prevent the browser default action of an additional event.
563     // For example, prevent the browser default action of a click, even if the
564     // Ajax behavior binds to mousedown.
565     if (elementSettings.prevent) {
566       $(ajax.element).on(elementSettings.prevent, false);
567     }
568   };
569
570   /**
571    * URL query attribute to indicate the wrapper used to render a request.
572    *
573    * The wrapper format determines how the HTML is wrapped, for example in a
574    * modal dialog.
575    *
576    * @const {string}
577    *
578    * @default
579    */
580   Drupal.ajax.WRAPPER_FORMAT = '_wrapper_format';
581
582   /**
583    * Request parameter to indicate that a request is a Drupal Ajax request.
584    *
585    * @const {string}
586    *
587    * @default
588    */
589   Drupal.Ajax.AJAX_REQUEST_PARAMETER = '_drupal_ajax';
590
591   /**
592    * Execute the ajax request.
593    *
594    * Allows developers to execute an Ajax request manually without specifying
595    * an event to respond to.
596    *
597    * @return {object}
598    *   Returns the jQuery.Deferred object underlying the Ajax request. If
599    *   pre-serialization fails, the Deferred will be returned in the rejected
600    *   state.
601    */
602   Drupal.Ajax.prototype.execute = function () {
603     // Do not perform another ajax command if one is already in progress.
604     if (this.ajaxing) {
605       return;
606     }
607
608     try {
609       this.beforeSerialize(this.element, this.options);
610       // Return the jqXHR so that external code can hook into the Deferred API.
611       return $.ajax(this.options);
612     }
613     catch (e) {
614       // Unset the ajax.ajaxing flag here because it won't be unset during
615       // the complete response.
616       this.ajaxing = false;
617       window.alert(`An error occurred while attempting to process ${this.options.url}: ${e.message}`);
618       // For consistency, return a rejected Deferred (i.e., jqXHR's superclass)
619       // so that calling code can take appropriate action.
620       return $.Deferred().reject();
621     }
622   };
623
624   /**
625    * Handle a key press.
626    *
627    * The Ajax object will, if instructed, bind to a key press response. This
628    * will test to see if the key press is valid to trigger this event and
629    * if it is, trigger it for us and prevent other keypresses from triggering.
630    * In this case we're handling RETURN and SPACEBAR keypresses (event codes 13
631    * and 32. RETURN is often used to submit a form when in a textfield, and
632    * SPACE is often used to activate an element without submitting.
633    *
634    * @param {HTMLElement} element
635    *   Element the event was triggered on.
636    * @param {jQuery.Event} event
637    *   Triggered event.
638    */
639   Drupal.Ajax.prototype.keypressResponse = function (element, event) {
640     // Create a synonym for this to reduce code confusion.
641     const ajax = this;
642
643     // Detect enter key and space bar and allow the standard response for them,
644     // except for form elements of type 'text', 'tel', 'number' and 'textarea',
645     // where the spacebar activation causes inappropriate activation if
646     // #ajax['keypress'] is TRUE. On a text-type widget a space should always
647     // be a space.
648     if (event.which === 13 || (event.which === 32 && element.type !== 'text' &&
649       element.type !== 'textarea' && element.type !== 'tel' && element.type !== 'number')) {
650       event.preventDefault();
651       event.stopPropagation();
652       $(element).trigger(ajax.elementSettings.event);
653     }
654   };
655
656   /**
657    * Handle an event that triggers an Ajax response.
658    *
659    * When an event that triggers an Ajax response happens, this method will
660    * perform the actual Ajax call. It is bound to the event using
661    * bind() in the constructor, and it uses the options specified on the
662    * Ajax object.
663    *
664    * @param {HTMLElement} element
665    *   Element the event was triggered on.
666    * @param {jQuery.Event} event
667    *   Triggered event.
668    */
669   Drupal.Ajax.prototype.eventResponse = function (element, event) {
670     event.preventDefault();
671     event.stopPropagation();
672
673     // Create a synonym for this to reduce code confusion.
674     const ajax = this;
675
676     // Do not perform another Ajax command if one is already in progress.
677     if (ajax.ajaxing) {
678       return;
679     }
680
681     try {
682       if (ajax.$form) {
683         // If setClick is set, we must set this to ensure that the button's
684         // value is passed.
685         if (ajax.setClick) {
686           // Mark the clicked button. 'form.clk' is a special variable for
687           // ajaxSubmit that tells the system which element got clicked to
688           // trigger the submit. Without it there would be no 'op' or
689           // equivalent.
690           element.form.clk = element;
691         }
692
693         ajax.$form.ajaxSubmit(ajax.options);
694       }
695       else {
696         ajax.beforeSerialize(ajax.element, ajax.options);
697         $.ajax(ajax.options);
698       }
699     }
700     catch (e) {
701       // Unset the ajax.ajaxing flag here because it won't be unset during
702       // the complete response.
703       ajax.ajaxing = false;
704       window.alert(`An error occurred while attempting to process ${ajax.options.url}: ${e.message}`);
705     }
706   };
707
708   /**
709    * Handler for the form serialization.
710    *
711    * Runs before the beforeSend() handler (see below), and unlike that one, runs
712    * before field data is collected.
713    *
714    * @param {object} [element]
715    *   Ajax object's `elementSettings`.
716    * @param {object} options
717    *   jQuery.ajax options.
718    */
719   Drupal.Ajax.prototype.beforeSerialize = function (element, options) {
720     // Allow detaching behaviors to update field values before collecting them.
721     // This is only needed when field values are added to the POST data, so only
722     // when there is a form such that this.$form.ajaxSubmit() is used instead of
723     // $.ajax(). When there is no form and $.ajax() is used, beforeSerialize()
724     // isn't called, but don't rely on that: explicitly check this.$form.
725     if (this.$form) {
726       const settings = this.settings || drupalSettings;
727       Drupal.detachBehaviors(this.$form.get(0), settings, 'serialize');
728     }
729
730     // Inform Drupal that this is an AJAX request.
731     options.data[Drupal.Ajax.AJAX_REQUEST_PARAMETER] = 1;
732
733     // Allow Drupal to return new JavaScript and CSS files to load without
734     // returning the ones already loaded.
735     // @see \Drupal\Core\Theme\AjaxBasePageNegotiator
736     // @see \Drupal\Core\Asset\LibraryDependencyResolverInterface::getMinimalRepresentativeSubset()
737     // @see system_js_settings_alter()
738     const pageState = drupalSettings.ajaxPageState;
739     options.data['ajax_page_state[theme]'] = pageState.theme;
740     options.data['ajax_page_state[theme_token]'] = pageState.theme_token;
741     options.data['ajax_page_state[libraries]'] = pageState.libraries;
742   };
743
744   /**
745    * Modify form values prior to form submission.
746    *
747    * @param {Array.<object>} formValues
748    *   Processed form values.
749    * @param {jQuery} element
750    *   The form node as a jQuery object.
751    * @param {object} options
752    *   jQuery.ajax options.
753    */
754   Drupal.Ajax.prototype.beforeSubmit = function (formValues, element, options) {
755     // This function is left empty to make it simple to override for modules
756     // that wish to add functionality here.
757   };
758
759   /**
760    * Prepare the Ajax request before it is sent.
761    *
762    * @param {XMLHttpRequest} xmlhttprequest
763    *   Native Ajax object.
764    * @param {object} options
765    *   jQuery.ajax options.
766    */
767   Drupal.Ajax.prototype.beforeSend = function (xmlhttprequest, options) {
768     // For forms without file inputs, the jQuery Form plugin serializes the
769     // form values, and then calls jQuery's $.ajax() function, which invokes
770     // this handler. In this circumstance, options.extraData is never used. For
771     // forms with file inputs, the jQuery Form plugin uses the browser's normal
772     // form submission mechanism, but captures the response in a hidden IFRAME.
773     // In this circumstance, it calls this handler first, and then appends
774     // hidden fields to the form to submit the values in options.extraData.
775     // There is no simple way to know which submission mechanism will be used,
776     // so we add to extraData regardless, and allow it to be ignored in the
777     // former case.
778     if (this.$form) {
779       options.extraData = options.extraData || {};
780
781       // Let the server know when the IFRAME submission mechanism is used. The
782       // server can use this information to wrap the JSON response in a
783       // TEXTAREA, as per http://jquery.malsup.com/form/#file-upload.
784       options.extraData.ajax_iframe_upload = '1';
785
786       // The triggering element is about to be disabled (see below), but if it
787       // contains a value (e.g., a checkbox, textfield, select, etc.), ensure
788       // that value is included in the submission. As per above, submissions
789       // that use $.ajax() are already serialized prior to the element being
790       // disabled, so this is only needed for IFRAME submissions.
791       const v = $.fieldValue(this.element);
792       if (v !== null) {
793         options.extraData[this.element.name] = v;
794       }
795     }
796
797     // Disable the element that received the change to prevent user interface
798     // interaction while the Ajax request is in progress. ajax.ajaxing prevents
799     // the element from triggering a new request, but does not prevent the user
800     // from changing its value.
801     $(this.element).prop('disabled', true);
802
803     if (!this.progress || !this.progress.type) {
804       return;
805     }
806
807     // Insert progress indicator.
808     const progressIndicatorMethod = `setProgressIndicator${this.progress.type.slice(0, 1).toUpperCase()}${this.progress.type.slice(1).toLowerCase()}`;
809     if (progressIndicatorMethod in this && typeof this[progressIndicatorMethod] === 'function') {
810       this[progressIndicatorMethod].call(this);
811     }
812   };
813
814   /**
815    * Sets the progress bar progress indicator.
816    */
817   Drupal.Ajax.prototype.setProgressIndicatorBar = function () {
818     const progressBar = new Drupal.ProgressBar(`ajax-progress-${this.element.id}`, $.noop, this.progress.method, $.noop);
819     if (this.progress.message) {
820       progressBar.setProgress(-1, this.progress.message);
821     }
822     if (this.progress.url) {
823       progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500);
824     }
825     this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar');
826     this.progress.object = progressBar;
827     $(this.element).after(this.progress.element);
828   };
829
830   /**
831    * Sets the throbber progress indicator.
832    */
833   Drupal.Ajax.prototype.setProgressIndicatorThrobber = function () {
834     this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
835     if (this.progress.message) {
836       this.progress.element.find('.throbber').after(`<div class="message">${this.progress.message}</div>`);
837     }
838     $(this.element).after(this.progress.element);
839   };
840
841   /**
842    * Sets the fullscreen progress indicator.
843    */
844   Drupal.Ajax.prototype.setProgressIndicatorFullscreen = function () {
845     this.progress.element = $('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
846     $('body').after(this.progress.element);
847   };
848
849   /**
850    * Handler for the form redirection completion.
851    *
852    * @param {Array.<Drupal.AjaxCommands~commandDefinition>} response
853    *   Drupal Ajax response.
854    * @param {number} status
855    *   XMLHttpRequest status.
856    */
857   Drupal.Ajax.prototype.success = function (response, status) {
858     // Remove the progress element.
859     if (this.progress.element) {
860       $(this.progress.element).remove();
861     }
862     if (this.progress.object) {
863       this.progress.object.stopMonitoring();
864     }
865     $(this.element).prop('disabled', false);
866
867     // Save element's ancestors tree so if the element is removed from the dom
868     // we can try to refocus one of its parents. Using addBack reverse the
869     // result array, meaning that index 0 is the highest parent in the hierarchy
870     // in this situation it is usually a <form> element.
871     const elementParents = $(this.element).parents('[data-drupal-selector]').addBack().toArray();
872
873     // Track if any command is altering the focus so we can avoid changing the
874     // focus set by the Ajax command.
875     let focusChanged = false;
876     Object.keys(response || {}).forEach((i) => {
877       if (response[i].command && this.commands[response[i].command]) {
878         this.commands[response[i].command](this, response[i], status);
879         if (response[i].command === 'invoke' && response[i].method === 'focus') {
880           focusChanged = true;
881         }
882       }
883     });
884
885     // If the focus hasn't be changed by the ajax commands, try to refocus the
886     // triggering element or one of its parents if that element does not exist
887     // anymore.
888     if (!focusChanged && this.element && !$(this.element).data('disable-refocus')) {
889       let target = false;
890
891       for (let n = elementParents.length - 1; !target && n > 0; n--) {
892         target = document.querySelector(`[data-drupal-selector="${elementParents[n].getAttribute('data-drupal-selector')}"]`);
893       }
894
895       if (target) {
896         $(target).trigger('focus');
897       }
898     }
899
900     // Reattach behaviors, if they were detached in beforeSerialize(). The
901     // attachBehaviors() called on the new content from processing the response
902     // commands is not sufficient, because behaviors from the entire form need
903     // to be reattached.
904     if (this.$form) {
905       const settings = this.settings || drupalSettings;
906       Drupal.attachBehaviors(this.$form.get(0), settings);
907     }
908
909     // Remove any response-specific settings so they don't get used on the next
910     // call by mistake.
911     this.settings = null;
912   };
913
914   /**
915    * Build an effect object to apply an effect when adding new HTML.
916    *
917    * @param {object} response
918    *   Drupal Ajax response.
919    * @param {string} [response.effect]
920    *   Override the default value of {@link Drupal.Ajax#elementSettings}.
921    * @param {string|number} [response.speed]
922    *   Override the default value of {@link Drupal.Ajax#elementSettings}.
923    *
924    * @return {object}
925    *   Returns an object with `showEffect`, `hideEffect` and `showSpeed`
926    *   properties.
927    */
928   Drupal.Ajax.prototype.getEffect = function (response) {
929     const type = response.effect || this.effect;
930     const speed = response.speed || this.speed;
931
932     const effect = {};
933     if (type === 'none') {
934       effect.showEffect = 'show';
935       effect.hideEffect = 'hide';
936       effect.showSpeed = '';
937     }
938     else if (type === 'fade') {
939       effect.showEffect = 'fadeIn';
940       effect.hideEffect = 'fadeOut';
941       effect.showSpeed = speed;
942     }
943     else {
944       effect.showEffect = `${type}Toggle`;
945       effect.hideEffect = `${type}Toggle`;
946       effect.showSpeed = speed;
947     }
948
949     return effect;
950   };
951
952   /**
953    * Handler for the form redirection error.
954    *
955    * @param {object} xmlhttprequest
956    *   Native XMLHttpRequest object.
957    * @param {string} uri
958    *   Ajax Request URI.
959    * @param {string} [customMessage]
960    *   Extra message to print with the Ajax error.
961    */
962   Drupal.Ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
963     // Remove the progress element.
964     if (this.progress.element) {
965       $(this.progress.element).remove();
966     }
967     if (this.progress.object) {
968       this.progress.object.stopMonitoring();
969     }
970     // Undo hide.
971     $(this.wrapper).show();
972     // Re-enable the element.
973     $(this.element).prop('disabled', false);
974     // Reattach behaviors, if they were detached in beforeSerialize().
975     if (this.$form) {
976       const settings = this.settings || drupalSettings;
977       Drupal.attachBehaviors(this.$form.get(0), settings);
978     }
979     throw new Drupal.AjaxError(xmlhttprequest, uri, customMessage);
980   };
981
982   /**
983    * @typedef {object} Drupal.AjaxCommands~commandDefinition
984    *
985    * @prop {string} command
986    * @prop {string} [method]
987    * @prop {string} [selector]
988    * @prop {string} [data]
989    * @prop {object} [settings]
990    * @prop {bool} [asterisk]
991    * @prop {string} [text]
992    * @prop {string} [title]
993    * @prop {string} [url]
994    * @prop {object} [argument]
995    * @prop {string} [name]
996    * @prop {string} [value]
997    * @prop {string} [old]
998    * @prop {string} [new]
999    * @prop {bool} [merge]
1000    * @prop {Array} [args]
1001    *
1002    * @see Drupal.AjaxCommands
1003    */
1004
1005   /**
1006    * Provide a series of commands that the client will perform.
1007    *
1008    * @constructor
1009    */
1010   Drupal.AjaxCommands = function () {};
1011   Drupal.AjaxCommands.prototype = {
1012
1013     /**
1014      * Command to insert new content into the DOM.
1015      *
1016      * @param {Drupal.Ajax} ajax
1017      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1018      * @param {object} response
1019      *   The response from the Ajax request.
1020      * @param {string} response.data
1021      *   The data to use with the jQuery method.
1022      * @param {string} [response.method]
1023      *   The jQuery DOM manipulation method to be used.
1024      * @param {string} [response.selector]
1025      *   A optional jQuery selector string.
1026      * @param {object} [response.settings]
1027      *   An optional array of settings that will be used.
1028      * @param {number} [status]
1029      *   The XMLHttpRequest status.
1030      */
1031     insert(ajax, response, status) {
1032       // Get information from the response. If it is not there, default to
1033       // our presets.
1034       const $wrapper = response.selector ? $(response.selector) : $(ajax.wrapper);
1035       const method = response.method || ajax.method;
1036       const effect = ajax.getEffect(response);
1037       let settings;
1038
1039       // We don't know what response.data contains: it might be a string of text
1040       // without HTML, so don't rely on jQuery correctly interpreting
1041       // $(response.data) as new HTML rather than a CSS selector. Also, if
1042       // response.data contains top-level text nodes, they get lost with either
1043       // $(response.data) or $('<div></div>').replaceWith(response.data).
1044       const $newContentWrapped = $('<div></div>').html(response.data);
1045       let $newContent = $newContentWrapped.contents();
1046
1047       // For legacy reasons, the effects processing code assumes that
1048       // $newContent consists of a single top-level element. Also, it has not
1049       // been sufficiently tested whether attachBehaviors() can be successfully
1050       // called with a context object that includes top-level text nodes.
1051       // However, to give developers full control of the HTML appearing in the
1052       // page, and to enable Ajax content to be inserted in places where <div>
1053       // elements are not allowed (e.g., within <table>, <tr>, and <span>
1054       // parents), we check if the new content satisfies the requirement
1055       // of a single top-level element, and only use the container <div> created
1056       // above when it doesn't. For more information, please see
1057       // https://www.drupal.org/node/736066.
1058       if ($newContent.length !== 1 || $newContent.get(0).nodeType !== 1) {
1059         $newContent = $newContentWrapped;
1060       }
1061
1062       // If removing content from the wrapper, detach behaviors first.
1063       switch (method) {
1064         case 'html':
1065         case 'replaceWith':
1066         case 'replaceAll':
1067         case 'empty':
1068         case 'remove':
1069           settings = response.settings || ajax.settings || drupalSettings;
1070           Drupal.detachBehaviors($wrapper.get(0), settings);
1071       }
1072
1073       // Add the new content to the page.
1074       $wrapper[method]($newContent);
1075
1076       // Immediately hide the new content if we're using any effects.
1077       if (effect.showEffect !== 'show') {
1078         $newContent.hide();
1079       }
1080
1081       // Determine which effect to use and what content will receive the
1082       // effect, then show the new content.
1083       if ($newContent.find('.ajax-new-content').length > 0) {
1084         $newContent.find('.ajax-new-content').hide();
1085         $newContent.show();
1086         $newContent.find('.ajax-new-content')[effect.showEffect](effect.showSpeed);
1087       }
1088       else if (effect.showEffect !== 'show') {
1089         $newContent[effect.showEffect](effect.showSpeed);
1090       }
1091
1092       // Attach all JavaScript behaviors to the new content, if it was
1093       // successfully added to the page, this if statement allows
1094       // `#ajax['wrapper']` to be optional.
1095       if ($newContent.parents('html').length > 0) {
1096         // Apply any settings from the returned JSON if available.
1097         settings = response.settings || ajax.settings || drupalSettings;
1098         Drupal.attachBehaviors($newContent.get(0), settings);
1099       }
1100     },
1101
1102     /**
1103      * Command to remove a chunk from the page.
1104      *
1105      * @param {Drupal.Ajax} [ajax]
1106      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1107      * @param {object} response
1108      *   The response from the Ajax request.
1109      * @param {string} response.selector
1110      *   A jQuery selector string.
1111      * @param {object} [response.settings]
1112      *   An optional array of settings that will be used.
1113      * @param {number} [status]
1114      *   The XMLHttpRequest status.
1115      */
1116     remove(ajax, response, status) {
1117       const settings = response.settings || ajax.settings || drupalSettings;
1118       $(response.selector).each(function () {
1119         Drupal.detachBehaviors(this, settings);
1120       })
1121         .remove();
1122     },
1123
1124     /**
1125      * Command to mark a chunk changed.
1126      *
1127      * @param {Drupal.Ajax} [ajax]
1128      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1129      * @param {object} response
1130      *   The JSON response object from the Ajax request.
1131      * @param {string} response.selector
1132      *   A jQuery selector string.
1133      * @param {bool} [response.asterisk]
1134      *   An optional CSS selector. If specified, an asterisk will be
1135      *   appended to the HTML inside the provided selector.
1136      * @param {number} [status]
1137      *   The request status.
1138      */
1139     changed(ajax, response, status) {
1140       const $element = $(response.selector);
1141       if (!$element.hasClass('ajax-changed')) {
1142         $element.addClass('ajax-changed');
1143         if (response.asterisk) {
1144           $element.find(response.asterisk).append(` <abbr class="ajax-changed" title="${Drupal.t('Changed')}">*</abbr> `);
1145         }
1146       }
1147     },
1148
1149     /**
1150      * Command to provide an alert.
1151      *
1152      * @param {Drupal.Ajax} [ajax]
1153      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1154      * @param {object} response
1155      *   The JSON response from the Ajax request.
1156      * @param {string} response.text
1157      *   The text that will be displayed in an alert dialog.
1158      * @param {number} [status]
1159      *   The XMLHttpRequest status.
1160      */
1161     alert(ajax, response, status) {
1162       window.alert(response.text, response.title);
1163     },
1164
1165     /**
1166      * Command to set the window.location, redirecting the browser.
1167      *
1168      * @param {Drupal.Ajax} [ajax]
1169      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1170      * @param {object} response
1171      *   The response from the Ajax request.
1172      * @param {string} response.url
1173      *   The URL to redirect to.
1174      * @param {number} [status]
1175      *   The XMLHttpRequest status.
1176      */
1177     redirect(ajax, response, status) {
1178       window.location = response.url;
1179     },
1180
1181     /**
1182      * Command to provide the jQuery css() function.
1183      *
1184      * @param {Drupal.Ajax} [ajax]
1185      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1186      * @param {object} response
1187      *   The response from the Ajax request.
1188      * @param {string} response.selector
1189      *   A jQuery selector string.
1190      * @param {object} response.argument
1191      *   An array of key/value pairs to set in the CSS for the selector.
1192      * @param {number} [status]
1193      *   The XMLHttpRequest status.
1194      */
1195     css(ajax, response, status) {
1196       $(response.selector).css(response.argument);
1197     },
1198
1199     /**
1200      * Command to set the settings used for other commands in this response.
1201      *
1202      * This method will also remove expired `drupalSettings.ajax` settings.
1203      *
1204      * @param {Drupal.Ajax} [ajax]
1205      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1206      * @param {object} response
1207      *   The response from the Ajax request.
1208      * @param {bool} response.merge
1209      *   Determines whether the additional settings should be merged to the
1210      *   global settings.
1211      * @param {object} response.settings
1212      *   Contains additional settings to add to the global settings.
1213      * @param {number} [status]
1214      *   The XMLHttpRequest status.
1215      */
1216     settings(ajax, response, status) {
1217       const ajaxSettings = drupalSettings.ajax;
1218
1219       // Clean up drupalSettings.ajax.
1220       if (ajaxSettings) {
1221         Drupal.ajax.expired().forEach((instance) => {
1222           // If the Ajax object has been created through drupalSettings.ajax
1223           // it will have a selector. When there is no selector the object
1224           // has been initialized with a special class name picked up by the
1225           // Ajax behavior.
1226
1227           if (instance.selector) {
1228             const selector = instance.selector.replace('#', '');
1229             if (selector in ajaxSettings) {
1230               delete ajaxSettings[selector];
1231             }
1232           }
1233         });
1234       }
1235
1236       if (response.merge) {
1237         $.extend(true, drupalSettings, response.settings);
1238       }
1239       else {
1240         ajax.settings = response.settings;
1241       }
1242     },
1243
1244     /**
1245      * Command to attach data using jQuery's data API.
1246      *
1247      * @param {Drupal.Ajax} [ajax]
1248      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1249      * @param {object} response
1250      *   The response from the Ajax request.
1251      * @param {string} response.name
1252      *   The name or key (in the key value pair) of the data attached to this
1253      *   selector.
1254      * @param {string} response.selector
1255      *   A jQuery selector string.
1256      * @param {string|object} response.value
1257      *   The value of to be attached.
1258      * @param {number} [status]
1259      *   The XMLHttpRequest status.
1260      */
1261     data(ajax, response, status) {
1262       $(response.selector).data(response.name, response.value);
1263     },
1264
1265     /**
1266      * Command to apply a jQuery method.
1267      *
1268      * @param {Drupal.Ajax} [ajax]
1269      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1270      * @param {object} response
1271      *   The response from the Ajax request.
1272      * @param {Array} response.args
1273      *   An array of arguments to the jQuery method, if any.
1274      * @param {string} response.method
1275      *   The jQuery method to invoke.
1276      * @param {string} response.selector
1277      *   A jQuery selector string.
1278      * @param {number} [status]
1279      *   The XMLHttpRequest status.
1280      */
1281     invoke(ajax, response, status) {
1282       const $element = $(response.selector);
1283       $element[response.method](...response.args);
1284     },
1285
1286     /**
1287      * Command to restripe a table.
1288      *
1289      * @param {Drupal.Ajax} [ajax]
1290      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1291      * @param {object} response
1292      *   The response from the Ajax request.
1293      * @param {string} response.selector
1294      *   A jQuery selector string.
1295      * @param {number} [status]
1296      *   The XMLHttpRequest status.
1297      */
1298     restripe(ajax, response, status) {
1299       // :even and :odd are reversed because jQuery counts from 0 and
1300       // we count from 1, so we're out of sync.
1301       // Match immediate children of the parent element to allow nesting.
1302       $(response.selector)
1303         .find('> tbody > tr:visible, > tr:visible')
1304         .removeClass('odd even')
1305         .filter(':even')
1306         .addClass('odd')
1307         .end()
1308         .filter(':odd')
1309         .addClass('even');
1310     },
1311
1312     /**
1313      * Command to update a form's build ID.
1314      *
1315      * @param {Drupal.Ajax} [ajax]
1316      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1317      * @param {object} response
1318      *   The response from the Ajax request.
1319      * @param {string} response.old
1320      *   The old form build ID.
1321      * @param {string} response.new
1322      *   The new form build ID.
1323      * @param {number} [status]
1324      *   The XMLHttpRequest status.
1325      */
1326     update_build_id(ajax, response, status) {
1327       $(`input[name="form_build_id"][value="${response.old}"]`).val(response.new);
1328     },
1329
1330     /**
1331      * Command to add css.
1332      *
1333      * Uses the proprietary addImport method if available as browsers which
1334      * support that method ignore @import statements in dynamically added
1335      * stylesheets.
1336      *
1337      * @param {Drupal.Ajax} [ajax]
1338      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
1339      * @param {object} response
1340      *   The response from the Ajax request.
1341      * @param {string} response.data
1342      *   A string that contains the styles to be added.
1343      * @param {number} [status]
1344      *   The XMLHttpRequest status.
1345      */
1346     add_css(ajax, response, status) {
1347       // Add the styles in the normal way.
1348       $('head').prepend(response.data);
1349       // Add imports in the styles using the addImport method if available.
1350       let match;
1351       const importMatch = /^@import url\("(.*)"\);$/igm;
1352       if (document.styleSheets[0].addImport && importMatch.test(response.data)) {
1353         importMatch.lastIndex = 0;
1354         do {
1355           match = importMatch.exec(response.data);
1356           document.styleSheets[0].addImport(match[1]);
1357         } while (match);
1358       }
1359     },
1360   };
1361 }(jQuery, window, Drupal, drupalSettings));