PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.9.5
Secure Custom Fields v6.9.5
6.9.5 6.9.4 6.9.3 6.9.2 6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / assets / build / js / acf.js
secure-custom-fields / assets / build / js Last commit date
commands 1 month ago pro 3 days ago acf-escaped-html-notice.asset.php 1 year ago acf-escaped-html-notice.js 1 year ago acf-escaped-html-notice.js.map 1 year ago acf-escaped-html-notice.min.asset.php 8 months ago acf-escaped-html-notice.min.js 8 months ago acf-field-group.asset.php 4 months ago acf-field-group.js 4 months ago acf-field-group.js.map 4 months ago acf-field-group.min.asset.php 4 months ago acf-field-group.min.js 4 months ago acf-input.asset.php 2 weeks ago acf-input.js 2 weeks ago acf-input.js.map 2 weeks ago acf-input.min.asset.php 2 weeks ago acf-input.min.js 2 weeks ago acf-internal-post-type.asset.php 8 months ago acf-internal-post-type.js 8 months ago acf-internal-post-type.js.map 8 months ago acf-internal-post-type.min.asset.php 8 months ago acf-internal-post-type.min.js 8 months ago acf.asset.php 2 weeks ago acf.js 2 weeks ago acf.js.map 2 weeks ago acf.min.asset.php 2 weeks ago acf.min.js 2 weeks ago index.php 1 year ago scf-bindings.asset.php 1 month ago scf-bindings.js 1 month ago scf-bindings.js.map 1 month ago scf-bindings.min.asset.php 1 month ago scf-bindings.min.js 1 month ago
acf.js
7023 lines
1 /******/ (() => { // webpackBootstrap
2 /******/ var __webpack_modules__ = ({
3
4 /***/ "./assets/src/js/_acf-hooks.js":
5 /*!*************************************!*\
6 !*** ./assets/src/js/_acf-hooks.js ***!
7 \*************************************/
8 /***/ (() => {
9
10 (function (window, undefined) {
11 'use strict';
12
13 /**
14 * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
15 * that, lowest priority hooks are fired first.
16 */
17 var EventManager = function () {
18 /**
19 * Maintain a reference to the object scope so our public methods never get confusing.
20 */
21 var MethodsAvailable = {
22 removeFilter: removeFilter,
23 applyFilters: applyFilters,
24 addFilter: addFilter,
25 removeAction: removeAction,
26 doAction: doAction,
27 addAction: addAction,
28 storage: getStorage
29 };
30
31 /**
32 * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
33 * object literal such that looking up the hook utilizes the native object literal hash.
34 */
35 var STORAGE = {
36 actions: {},
37 filters: {}
38 };
39 function getStorage() {
40 return STORAGE;
41 }
42
43 /**
44 * Adds an action to the event manager.
45 *
46 * @param action Must contain namespace.identifier
47 * @param callback Must be a valid callback function before this action is added
48 * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
49 * @param [context] Supply a value to be used for this
50 */
51 function addAction(action, callback, priority, context) {
52 if (typeof action === 'string' && typeof callback === 'function') {
53 priority = parseInt(priority || 10, 10);
54 _addHook('actions', action, callback, priority, context);
55 }
56 return MethodsAvailable;
57 }
58
59 /**
60 * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
61 * that the first argument must always be the action.
62 */
63 function doAction(/* action, arg1, arg2, ... */
64 ) {
65 var args = Array.prototype.slice.call(arguments);
66 var action = args.shift();
67 if (typeof action === 'string') {
68 _runHook('actions', action, args);
69 }
70 return MethodsAvailable;
71 }
72
73 /**
74 * Removes the specified action if it contains a namespace.identifier & exists.
75 *
76 * @param action The action to remove
77 * @param [callback] Callback function to remove
78 */
79 function removeAction(action, callback) {
80 if (typeof action === 'string') {
81 _removeHook('actions', action, callback);
82 }
83 return MethodsAvailable;
84 }
85
86 /**
87 * Adds a filter to the event manager.
88 *
89 * @param filter Must contain namespace.identifier
90 * @param callback Must be a valid callback function before this action is added
91 * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
92 * @param [context] Supply a value to be used for this
93 */
94 function addFilter(filter, callback, priority, context) {
95 if (typeof filter === 'string' && typeof callback === 'function') {
96 priority = parseInt(priority || 10, 10);
97 _addHook('filters', filter, callback, priority, context);
98 }
99 return MethodsAvailable;
100 }
101
102 /**
103 * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
104 * the first argument must always be the filter.
105 */
106 function applyFilters(/* filter, filtered arg, arg2, ... */
107 ) {
108 var args = Array.prototype.slice.call(arguments);
109 var filter = args.shift();
110 if (typeof filter === 'string') {
111 return _runHook('filters', filter, args);
112 }
113 return MethodsAvailable;
114 }
115
116 /**
117 * Removes the specified filter if it contains a namespace.identifier & exists.
118 *
119 * @param filter The action to remove
120 * @param [callback] Callback function to remove
121 */
122 function removeFilter(filter, callback) {
123 if (typeof filter === 'string') {
124 _removeHook('filters', filter, callback);
125 }
126 return MethodsAvailable;
127 }
128
129 /**
130 * Removes the specified hook by resetting the value of it.
131 *
132 * @param type Type of hook, either 'actions' or 'filters'
133 * @param hook The hook (namespace.identifier) to remove
134 * @private
135 */
136 function _removeHook(type, hook, callback, context) {
137 if (!STORAGE[type][hook]) {
138 return;
139 }
140 if (!callback) {
141 STORAGE[type][hook] = [];
142 } else {
143 var handlers = STORAGE[type][hook];
144 var i;
145 if (!context) {
146 for (i = handlers.length; i--;) {
147 if (handlers[i].callback === callback) {
148 handlers.splice(i, 1);
149 }
150 }
151 } else {
152 for (i = handlers.length; i--;) {
153 var handler = handlers[i];
154 if (handler.callback === callback && handler.context === context) {
155 handlers.splice(i, 1);
156 }
157 }
158 }
159 }
160 }
161
162 /**
163 * Adds the hook to the appropriate storage container
164 *
165 * @param type 'actions' or 'filters'
166 * @param hook The hook (namespace.identifier) to add to our event manager
167 * @param callback The function that will be called when the hook is executed.
168 * @param priority The priority of this hook. Must be an integer.
169 * @param [context] A value to be used for this
170 * @private
171 */
172 function _addHook(type, hook, callback, priority, context) {
173 var hookObject = {
174 callback: callback,
175 priority: priority,
176 context: context
177 };
178
179 // Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
180 var hooks = STORAGE[type][hook];
181 if (hooks) {
182 hooks.push(hookObject);
183 hooks = _hookInsertSort(hooks);
184 } else {
185 hooks = [hookObject];
186 }
187 STORAGE[type][hook] = hooks;
188 }
189
190 /**
191 * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
192 * than bubble sort, etc: http://jsperf.com/javascript-sort
193 *
194 * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
195 * @private
196 */
197 function _hookInsertSort(hooks) {
198 var tmpHook, j, prevHook;
199 for (var i = 1, len = hooks.length; i < len; i++) {
200 tmpHook = hooks[i];
201 j = i;
202 while ((prevHook = hooks[j - 1]) && prevHook.priority > tmpHook.priority) {
203 hooks[j] = hooks[j - 1];
204 --j;
205 }
206 hooks[j] = tmpHook;
207 }
208 return hooks;
209 }
210
211 /**
212 * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
213 *
214 * @param type 'actions' or 'filters'
215 * @param hook The hook ( namespace.identifier ) to be ran.
216 * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
217 * @private
218 */
219 function _runHook(type, hook, args) {
220 var handlers = STORAGE[type][hook];
221 if (!handlers) {
222 return type === 'filters' ? args[0] : false;
223 }
224 var i = 0,
225 len = handlers.length;
226 if (type === 'filters') {
227 for (; i < len; i++) {
228 args[0] = handlers[i].callback.apply(handlers[i].context, args);
229 }
230 } else {
231 for (; i < len; i++) {
232 handlers[i].callback.apply(handlers[i].context, args);
233 }
234 }
235 return type === 'filters' ? args[0] : true;
236 }
237
238 // return all of the publicly available methods
239 return MethodsAvailable;
240 };
241
242 // instantiate
243 acf.hooks = new EventManager();
244 })(window);
245
246 /***/ }),
247
248 /***/ "./assets/src/js/_acf-modal.js":
249 /*!*************************************!*\
250 !*** ./assets/src/js/_acf-modal.js ***!
251 \*************************************/
252 /***/ (() => {
253
254 (function ($, undefined) {
255 acf.models.Modal = acf.Model.extend({
256 data: {
257 title: '',
258 content: '',
259 toolbar: ''
260 },
261 events: {
262 'click .acf-modal-close': 'onClickClose'
263 },
264 setup: function (props) {
265 $.extend(this.data, props);
266 this.$el = $();
267 this.render();
268 },
269 initialize: function () {
270 this.open();
271 },
272 render: function () {
273 // Extract vars.
274 var title = this.get('title');
275 var content = this.get('content');
276 var toolbar = this.get('toolbar');
277
278 // Create element.
279 var $el = $(['<div>', '<div class="acf-modal">', '<div class="acf-modal-title">', '<h2>' + title + '</h2>', '<button class="acf-modal-close" type="button"><span class="dashicons dashicons-no"></span></button>', '</div>', '<div class="acf-modal-content">' + content + '</div>', '<div class="acf-modal-toolbar">' + toolbar + '</div>', '</div>', '<div class="acf-modal-backdrop acf-modal-close"></div>', '</div>'].join(''));
280
281 // Update DOM.
282 if (this.$el) {
283 this.$el.replaceWith($el);
284 }
285 this.$el = $el;
286
287 // Trigger action.
288 acf.doAction('append', $el);
289 },
290 update: function (props) {
291 this.data = acf.parseArgs(props, this.data);
292 this.render();
293 },
294 title: function (title) {
295 this.$('.acf-modal-title h2').html(title);
296 },
297 content: function (content) {
298 this.$('.acf-modal-content').html(content);
299 },
300 toolbar: function (toolbar) {
301 this.$('.acf-modal-toolbar').html(toolbar);
302 },
303 open: function () {
304 $('body').append(this.$el);
305 },
306 close: function () {
307 this.remove();
308 },
309 onClickClose: function (e, $el) {
310 e.preventDefault();
311 this.close();
312 },
313 /**
314 * Places focus within the popup.
315 */
316 focus: function () {
317 this.$el.find('.acf-icon').first().trigger('focus');
318 },
319 /**
320 * Locks focus within the modal.
321 *
322 * @param {boolean} locked True to lock focus, false to unlock.
323 */
324 lockFocusToModal: function (locked) {
325 let inertElement = $('#wpwrap');
326 if (!inertElement.length) {
327 return;
328 }
329 inertElement[0].inert = locked;
330 inertElement.attr('aria-hidden', locked);
331 },
332 /**
333 * Returns focus to the element that opened the popup
334 * if it still exists in the DOM.
335 */
336 returnFocusToOrigin: function () {
337 if (this.data.openedBy instanceof $ && this.data.openedBy.closest('body').length > 0) {
338 this.data.openedBy.trigger('focus');
339 }
340 }
341 });
342
343 /**
344 * Returns a new modal.
345 *
346 * @date 21/4/20
347 * @since ACF 5.9.0
348 *
349 * @param object props The modal props.
350 * @return object
351 */
352 acf.newModal = function (props) {
353 return new acf.models.Modal(props);
354 };
355 })(jQuery);
356
357 /***/ }),
358
359 /***/ "./assets/src/js/_acf-model.js":
360 /*!*************************************!*\
361 !*** ./assets/src/js/_acf-model.js ***!
362 \*************************************/
363 /***/ (() => {
364
365 (function ($, undefined) {
366 // Cached regex to split keys for `addEvent`.
367 var delegateEventSplitter = /^(\S+)\s*(.*)$/;
368
369 /**
370 * extend
371 *
372 * Helper function to correctly set up the prototype chain for subclasses
373 * Heavily inspired by backbone.js
374 *
375 * @date 14/12/17
376 * @since ACF 5.6.5
377 *
378 * @param object protoProps New properties for this object.
379 * @return function.
380 */
381
382 var extend = function (protoProps) {
383 // vars
384 var Parent = this;
385 var Child;
386
387 // The constructor function for the new subclass is either defined by you
388 // (the "constructor" property in your `extend` definition), or defaulted
389 // by us to simply call the parent constructor.
390 if (protoProps && protoProps.hasOwnProperty('constructor')) {
391 Child = protoProps.constructor;
392 } else {
393 Child = function () {
394 return Parent.apply(this, arguments);
395 };
396 }
397
398 // Add static properties to the constructor function, if supplied.
399 $.extend(Child, Parent);
400
401 // Set the prototype chain to inherit from `parent`, without calling
402 // `parent`'s constructor function and add the prototype properties.
403 Child.prototype = Object.create(Parent.prototype);
404 $.extend(Child.prototype, protoProps);
405 Child.prototype.constructor = Child;
406
407 // Set a convenience property in case the parent's prototype is needed later.
408 //Child.prototype.__parent__ = Parent.prototype;
409
410 // return
411 return Child;
412 };
413
414 /**
415 * Model
416 *
417 * Base class for all inheritance
418 *
419 * @date 14/12/17
420 * @since ACF 5.6.5
421 *
422 * @param object props
423 * @return function.
424 */
425
426 var Model = acf.Model = function () {
427 // generate unique client id
428 this.cid = acf.uniqueId('acf');
429
430 // set vars to avoid modifying prototype
431 this.data = $.extend(true, {}, this.data);
432
433 // pass props to setup function
434 this.setup.apply(this, arguments);
435
436 // store on element (allow this.setup to create this.$el)
437 if (this.$el && !this.$el.data('acf')) {
438 this.$el.data('acf', this);
439 }
440
441 // initialize
442 var initialize = function () {
443 this.initialize();
444 this.addEvents();
445 this.addActions();
446 this.addFilters();
447 };
448
449 // initialize on action
450 if (this.wait && !acf.didAction(this.wait)) {
451 this.addAction(this.wait, initialize);
452
453 // initialize now
454 } else {
455 initialize.apply(this);
456 }
457 };
458
459 // Attach all inheritable methods to the Model prototype.
460 $.extend(Model.prototype, {
461 // Unique model id
462 id: '',
463 // Unique client id
464 cid: '',
465 // jQuery element
466 $el: null,
467 // Data specific to this instance
468 data: {},
469 // toggle used when changing data
470 busy: false,
471 changed: false,
472 // Setup events hooks
473 events: {},
474 actions: {},
475 filters: {},
476 // class used to avoid nested event triggers
477 eventScope: '',
478 // action to wait until initialize
479 wait: false,
480 // action priority default
481 priority: 10,
482 /**
483 * get
484 *
485 * Gets a specific data value
486 *
487 * @date 14/12/17
488 * @since ACF 5.6.5
489 *
490 * @param string name
491 * @return mixed
492 */
493
494 get: function (name) {
495 return this.data[name];
496 },
497 /**
498 * has
499 *
500 * Returns `true` if the data exists and is not null
501 *
502 * @date 14/12/17
503 * @since ACF 5.6.5
504 *
505 * @param string name
506 * @return boolean
507 */
508
509 has: function (name) {
510 return this.get(name) != null;
511 },
512 /**
513 * set
514 *
515 * Sets a specific data value
516 *
517 * @date 14/12/17
518 * @since ACF 5.6.5
519 *
520 * @param string name
521 * @param mixed value
522 * @return this
523 */
524
525 set: function (name, value, silent) {
526 // bail if unchanged
527 var prevValue = this.get(name);
528 if (prevValue == value) {
529 return this;
530 }
531
532 // set data
533 this.data[name] = value;
534
535 // trigger events
536 if (!silent) {
537 this.changed = true;
538 this.trigger('changed:' + name, [value, prevValue]);
539 this.trigger('changed', [name, value, prevValue]);
540 }
541
542 // return
543 return this;
544 },
545 /**
546 * inherit
547 *
548 * Inherits the data from a jQuery element
549 *
550 * @date 14/12/17
551 * @since ACF 5.6.5
552 *
553 * @param jQuery $el
554 * @return this
555 */
556
557 inherit: function (data) {
558 // allow jQuery
559 if (data instanceof jQuery) {
560 data = data.data();
561 }
562
563 // extend
564 $.extend(this.data, data);
565
566 // return
567 return this;
568 },
569 /**
570 * prop
571 *
572 * mimics the jQuery prop function
573 *
574 * @date 4/6/18
575 * @since ACF 5.6.9
576 *
577 * @param type $var Description. Default.
578 * @return type Description.
579 */
580
581 prop: function () {
582 return this.$el.prop.apply(this.$el, arguments);
583 },
584 /**
585 * setup
586 *
587 * Run during constructor function
588 *
589 * @date 14/12/17
590 * @since ACF 5.6.5
591 *
592 * @param n/a
593 * @return n/a
594 */
595
596 setup: function (props) {
597 $.extend(this, props);
598 },
599 /**
600 * initialize
601 *
602 * Also run during constructor function
603 *
604 * @date 14/12/17
605 * @since ACF 5.6.5
606 *
607 * @param n/a
608 * @return n/a
609 */
610
611 initialize: function () {},
612 /**
613 * addElements
614 *
615 * Adds multiple jQuery elements to this object
616 *
617 * @date 9/5/18
618 * @since ACF 5.6.9
619 *
620 * @param type $var Description. Default.
621 * @return type Description.
622 */
623
624 addElements: function (elements) {
625 elements = elements || this.elements || null;
626 if (!elements || !Object.keys(elements).length) return false;
627 for (var i in elements) {
628 this.addElement(i, elements[i]);
629 }
630 },
631 /**
632 * addElement
633 *
634 * description
635 *
636 * @date 9/5/18
637 * @since ACF 5.6.9
638 *
639 * @param type $var Description. Default.
640 * @return type Description.
641 */
642
643 addElement: function (name, selector) {
644 this['$' + name] = this.$(selector);
645 },
646 /**
647 * addEvents
648 *
649 * Adds multiple event handlers
650 *
651 * @date 14/12/17
652 * @since ACF 5.6.5
653 *
654 * @param object events {event1 : callback, event2 : callback, etc }
655 * @return n/a
656 */
657
658 addEvents: function (events) {
659 events = events || this.events || null;
660 if (!events) return false;
661 for (var key in events) {
662 var match = key.match(delegateEventSplitter);
663 this.on(match[1], match[2], events[key]);
664 }
665 },
666 /**
667 * removeEvents
668 *
669 * Removes multiple event handlers
670 *
671 * @date 14/12/17
672 * @since ACF 5.6.5
673 *
674 * @param object events {event1 : callback, event2 : callback, etc }
675 * @return n/a
676 */
677
678 removeEvents: function (events) {
679 events = events || this.events || null;
680 if (!events) return false;
681 for (var key in events) {
682 var match = key.match(delegateEventSplitter);
683 this.off(match[1], match[2], events[key]);
684 }
685 },
686 /**
687 * getEventTarget
688 *
689 * Returns a jQuery element to trigger an event on.
690 *
691 * @date 5/6/18
692 * @since ACF 5.6.9
693 *
694 * @param jQuery $el The default jQuery element. Optional.
695 * @param string event The event name. Optional.
696 * @return jQuery
697 */
698
699 getEventTarget: function ($el, event) {
700 return $el || this.$el || $(document);
701 },
702 /**
703 * validateEvent
704 *
705 * Returns true if the event target's closest $el is the same as this.$el
706 * Requires both this.el and this.$el to be defined
707 *
708 * @date 5/6/18
709 * @since ACF 5.6.9
710 *
711 * @param type $var Description. Default.
712 * @return type Description.
713 */
714
715 validateEvent: function (e) {
716 if (this.eventScope) {
717 return $(e.target).closest(this.eventScope).is(this.$el);
718 } else {
719 return true;
720 }
721 },
722 /**
723 * proxyEvent
724 *
725 * Returns a new event callback function scoped to this model
726 *
727 * @date 29/3/18
728 * @since ACF 5.6.9
729 *
730 * @param function callback
731 * @return function
732 */
733
734 proxyEvent: function (callback) {
735 return this.proxy(function (e) {
736 // validate
737 if (!this.validateEvent(e)) {
738 return;
739 }
740
741 // construct args
742 var args = acf.arrayArgs(arguments);
743 var extraArgs = args.slice(1);
744 var eventArgs = [e, $(e.currentTarget)].concat(extraArgs);
745
746 // callback
747 callback.apply(this, eventArgs);
748 });
749 },
750 /**
751 * on
752 *
753 * Adds an event handler similar to jQuery
754 * Uses the instance 'cid' to namespace event
755 *
756 * @date 14/12/17
757 * @since ACF 5.6.5
758 *
759 * @param string name
760 * @param string callback
761 * @return n/a
762 */
763
764 on: function (a1, a2, a3, a4) {
765 // vars
766 var $el, event, selector, callback, args;
767
768 // find args
769 if (a1 instanceof jQuery) {
770 // 1. args( $el, event, selector, callback )
771 if (a4) {
772 $el = a1;
773 event = a2;
774 selector = a3;
775 callback = a4;
776
777 // 2. args( $el, event, callback )
778 } else {
779 $el = a1;
780 event = a2;
781 callback = a3;
782 }
783 } else {
784 // 3. args( event, selector, callback )
785 if (a3) {
786 event = a1;
787 selector = a2;
788 callback = a3;
789
790 // 4. args( event, callback )
791 } else {
792 event = a1;
793 callback = a2;
794 }
795 }
796
797 // element
798 $el = this.getEventTarget($el);
799
800 // modify callback
801 if (typeof callback === 'string') {
802 callback = this.proxyEvent(this[callback]);
803 }
804
805 // modify event
806 event = event + '.' + this.cid;
807
808 // args
809 if (selector) {
810 args = [event, selector, callback];
811 } else {
812 args = [event, callback];
813 }
814
815 // on()
816 $el.on.apply($el, args);
817 },
818 /**
819 * off
820 *
821 * Removes an event handler similar to jQuery
822 *
823 * @date 14/12/17
824 * @since ACF 5.6.5
825 *
826 * @param string name
827 * @param string callback
828 * @return n/a
829 */
830
831 off: function (a1, a2, a3) {
832 // vars
833 var $el, event, selector, args;
834
835 // find args
836 if (a1 instanceof jQuery) {
837 // 1. args( $el, event, selector )
838 if (a3) {
839 $el = a1;
840 event = a2;
841 selector = a3;
842
843 // 2. args( $el, event )
844 } else {
845 $el = a1;
846 event = a2;
847 }
848 } else {
849 // 3. args( event, selector )
850 if (a2) {
851 event = a1;
852 selector = a2;
853
854 // 4. args( event )
855 } else {
856 event = a1;
857 }
858 }
859
860 // element
861 $el = this.getEventTarget($el);
862
863 // modify event
864 event = event + '.' + this.cid;
865
866 // args
867 if (selector) {
868 args = [event, selector];
869 } else {
870 args = [event];
871 }
872
873 // off()
874 $el.off.apply($el, args);
875 },
876 /**
877 * trigger
878 *
879 * Triggers an event similar to jQuery
880 *
881 * @date 14/12/17
882 * @since ACF 5.6.5
883 *
884 * @param string name
885 * @param string callback
886 * @return n/a
887 */
888
889 trigger: function (name, args, bubbles) {
890 var $el = this.getEventTarget();
891 if (bubbles) {
892 $el.trigger.apply($el, arguments);
893 } else {
894 $el.triggerHandler.apply($el, arguments);
895 }
896 return this;
897 },
898 /**
899 * addActions
900 *
901 * Adds multiple action handlers
902 *
903 * @date 14/12/17
904 * @since ACF 5.6.5
905 *
906 * @param object actions {action1 : callback, action2 : callback, etc }
907 * @return n/a
908 */
909
910 addActions: function (actions) {
911 actions = actions || this.actions || null;
912 if (!actions) return false;
913 for (var i in actions) {
914 this.addAction(i, actions[i]);
915 }
916 },
917 /**
918 * removeActions
919 *
920 * Removes multiple action handlers
921 *
922 * @date 14/12/17
923 * @since ACF 5.6.5
924 *
925 * @param object actions {action1 : callback, action2 : callback, etc }
926 * @return n/a
927 */
928
929 removeActions: function (actions) {
930 actions = actions || this.actions || null;
931 if (!actions) return false;
932 for (var i in actions) {
933 this.removeAction(i, actions[i]);
934 }
935 },
936 /**
937 * addAction
938 *
939 * Adds an action using the wp.hooks library
940 *
941 * @date 14/12/17
942 * @since ACF 5.6.5
943 *
944 * @param string name
945 * @param string callback
946 * @return n/a
947 */
948
949 addAction: function (name, callback, priority) {
950 //console.log('addAction', name, priority);
951 // defaults
952 priority = priority || this.priority;
953
954 // modify callback
955 if (typeof callback === 'string') {
956 callback = this[callback];
957 }
958
959 // add
960 acf.addAction(name, callback, priority, this);
961 },
962 /**
963 * removeAction
964 *
965 * Remove an action using the wp.hooks library
966 *
967 * @date 14/12/17
968 * @since ACF 5.6.5
969 *
970 * @param string name
971 * @param string callback
972 * @return n/a
973 */
974
975 removeAction: function (name, callback) {
976 acf.removeAction(name, this[callback]);
977 },
978 /**
979 * addFilters
980 *
981 * Adds multiple filter handlers
982 *
983 * @date 14/12/17
984 * @since ACF 5.6.5
985 *
986 * @param object filters {filter1 : callback, filter2 : callback, etc }
987 * @return n/a
988 */
989
990 addFilters: function (filters) {
991 filters = filters || this.filters || null;
992 if (!filters) return false;
993 for (var i in filters) {
994 this.addFilter(i, filters[i]);
995 }
996 },
997 /**
998 * addFilter
999 *
1000 * Adds a filter using the wp.hooks library
1001 *
1002 * @date 14/12/17
1003 * @since ACF 5.6.5
1004 *
1005 * @param string name
1006 * @param string callback
1007 * @return n/a
1008 */
1009
1010 addFilter: function (name, callback, priority) {
1011 // defaults
1012 priority = priority || this.priority;
1013
1014 // modify callback
1015 if (typeof callback === 'string') {
1016 callback = this[callback];
1017 }
1018
1019 // add
1020 acf.addFilter(name, callback, priority, this);
1021 },
1022 /**
1023 * removeFilters
1024 *
1025 * Removes multiple filter handlers
1026 *
1027 * @date 14/12/17
1028 * @since ACF 5.6.5
1029 *
1030 * @param object filters {filter1 : callback, filter2 : callback, etc }
1031 * @return n/a
1032 */
1033
1034 removeFilters: function (filters) {
1035 filters = filters || this.filters || null;
1036 if (!filters) return false;
1037 for (var i in filters) {
1038 this.removeFilter(i, filters[i]);
1039 }
1040 },
1041 /**
1042 * removeFilter
1043 *
1044 * Remove a filter using the wp.hooks library
1045 *
1046 * @date 14/12/17
1047 * @since ACF 5.6.5
1048 *
1049 * @param string name
1050 * @param string callback
1051 * @return n/a
1052 */
1053
1054 removeFilter: function (name, callback) {
1055 acf.removeFilter(name, this[callback]);
1056 },
1057 /**
1058 * $
1059 *
1060 * description
1061 *
1062 * @date 16/12/17
1063 * @since ACF 5.6.5
1064 *
1065 * @param type $var Description. Default.
1066 * @return type Description.
1067 */
1068
1069 $: function (selector) {
1070 return this.$el.find(selector);
1071 },
1072 /**
1073 * remove
1074 *
1075 * Removes the element and listeners
1076 *
1077 * @date 19/12/17
1078 * @since ACF 5.6.5
1079 *
1080 * @param type $var Description. Default.
1081 * @return type Description.
1082 */
1083
1084 remove: function () {
1085 this.removeEvents();
1086 this.removeActions();
1087 this.removeFilters();
1088 this.$el.remove();
1089 },
1090 /**
1091 * setTimeout
1092 *
1093 * description
1094 *
1095 * @date 16/1/18
1096 * @since ACF 5.6.5
1097 *
1098 * @param type $var Description. Default.
1099 * @return type Description.
1100 */
1101
1102 setTimeout: function (callback, milliseconds) {
1103 return setTimeout(this.proxy(callback), milliseconds);
1104 },
1105 /**
1106 * time
1107 *
1108 * used for debugging
1109 *
1110 * @date 7/3/18
1111 * @since ACF 5.6.9
1112 *
1113 * @param type $var Description. Default.
1114 * @return type Description.
1115 */
1116
1117 time: function () {
1118 console.time(this.id || this.cid);
1119 },
1120 /**
1121 * timeEnd
1122 *
1123 * used for debugging
1124 *
1125 * @date 7/3/18
1126 * @since ACF 5.6.9
1127 *
1128 * @param type $var Description. Default.
1129 * @return type Description.
1130 */
1131
1132 timeEnd: function () {
1133 console.timeEnd(this.id || this.cid);
1134 },
1135 /**
1136 * show
1137 *
1138 * description
1139 *
1140 * @date 15/3/18
1141 * @since ACF 5.6.9
1142 *
1143 * @param type $var Description. Default.
1144 * @return type Description.
1145 */
1146
1147 show: function () {
1148 acf.show(this.$el);
1149 },
1150 /**
1151 * hide
1152 *
1153 * description
1154 *
1155 * @date 15/3/18
1156 * @since ACF 5.6.9
1157 *
1158 * @param type $var Description. Default.
1159 * @return type Description.
1160 */
1161
1162 hide: function () {
1163 acf.hide(this.$el);
1164 },
1165 /**
1166 * proxy
1167 *
1168 * Returns a new function scoped to this model
1169 *
1170 * @date 29/3/18
1171 * @since ACF 5.6.9
1172 *
1173 * @param function callback
1174 * @return function
1175 */
1176
1177 proxy: function (callback) {
1178 return $.proxy(callback, this);
1179 }
1180 });
1181
1182 // Set up inheritance for the model
1183 Model.extend = extend;
1184
1185 // Global model storage
1186 acf.models = {};
1187
1188 /**
1189 * acf.getInstance
1190 *
1191 * This function will get an instance from an element
1192 *
1193 * @date 5/3/18
1194 * @since ACF 5.6.9
1195 *
1196 * @param type $var Description. Default.
1197 * @return type Description.
1198 */
1199
1200 acf.getInstance = function ($el) {
1201 return $el.data('acf');
1202 };
1203
1204 /**
1205 * acf.getInstances
1206 *
1207 * This function will get an array of instances from multiple elements
1208 *
1209 * @date 5/3/18
1210 * @since ACF 5.6.9
1211 *
1212 * @param type $var Description. Default.
1213 * @return type Description.
1214 */
1215
1216 acf.getInstances = function ($el) {
1217 var instances = [];
1218 $el.each(function () {
1219 instances.push(acf.getInstance($(this)));
1220 });
1221 return instances;
1222 };
1223 })(jQuery);
1224
1225 /***/ }),
1226
1227 /***/ "./assets/src/js/_acf-notice.js":
1228 /*!**************************************!*\
1229 !*** ./assets/src/js/_acf-notice.js ***!
1230 \**************************************/
1231 /***/ (() => {
1232
1233 (function ($, undefined) {
1234 var Notice = acf.Model.extend({
1235 data: {
1236 text: '',
1237 type: '',
1238 timeout: 0,
1239 dismiss: true,
1240 target: false,
1241 location: 'before',
1242 close: function () {}
1243 },
1244 events: {
1245 'click .acf-notice-dismiss': 'onClickClose'
1246 },
1247 tmpl: function () {
1248 return '<div class="acf-notice"></div>';
1249 },
1250 setup: function (props) {
1251 $.extend(this.data, props);
1252 this.$el = $(this.tmpl());
1253 },
1254 initialize: function () {
1255 // render
1256 this.render();
1257
1258 // show
1259 this.show();
1260 },
1261 render: function () {
1262 // class
1263 this.type(this.get('type'));
1264
1265 // text
1266 this.html('<p>' + acf.escHtml(this.get('text')) + '</p>');
1267
1268 // close
1269 if (this.get('dismiss')) {
1270 this.$el.append('<a href="#" class="acf-notice-dismiss acf-icon -cancel small"></a>');
1271 this.$el.addClass('-dismiss');
1272 }
1273
1274 // timeout
1275 var timeout = this.get('timeout');
1276 if (timeout) {
1277 this.away(timeout);
1278 }
1279 },
1280 update: function (props) {
1281 // update
1282 $.extend(this.data, props);
1283
1284 // re-initialize
1285 this.initialize();
1286
1287 // refresh events
1288 this.removeEvents();
1289 this.addEvents();
1290 },
1291 show: function () {
1292 var $target = this.get('target');
1293 var location = this.get('location');
1294 if ($target) {
1295 if (location === 'after') {
1296 $target.append(this.$el);
1297 } else {
1298 $target.prepend(this.$el);
1299 }
1300 }
1301 },
1302 hide: function () {
1303 this.$el.remove();
1304 },
1305 away: function (timeout) {
1306 this.setTimeout(function () {
1307 acf.remove(this.$el);
1308 }, timeout);
1309 },
1310 type: function (type) {
1311 // remove prev type
1312 var prevType = this.get('type');
1313 if (prevType) {
1314 this.$el.removeClass('-' + prevType);
1315 }
1316
1317 // add new type
1318 this.$el.addClass('-' + type);
1319
1320 // backwards compatibility
1321 if (type == 'error') {
1322 this.$el.addClass('acf-error-message');
1323 }
1324 },
1325 html: function (html) {
1326 this.$el.html(acf.escHtml(html));
1327 },
1328 text: function (text) {
1329 this.$('p').html(acf.escHtml(text));
1330 },
1331 onClickClose: function (e, $el) {
1332 e.preventDefault();
1333 this.get('close').apply(this, arguments);
1334 this.remove();
1335 }
1336 });
1337 acf.newNotice = function (props) {
1338 // ensure object
1339 if (typeof props !== 'object') {
1340 props = {
1341 text: props
1342 };
1343 }
1344
1345 // instantiate
1346 return new Notice(props);
1347 };
1348 var noticeManager = new acf.Model({
1349 wait: 'prepare',
1350 priority: 1,
1351 initialize: function () {
1352 const $notices = $('.acf-admin-notice');
1353 if (!$notices.length) {
1354 return;
1355 }
1356 $notices.each(function () {
1357 if ($(this).data('persisted')) {
1358 let dismissed = acf.getPreference('dismissed-notices');
1359 if (dismissed && typeof dismissed == 'object' && dismissed.includes($(this).data('persist-id'))) {
1360 $(this).remove();
1361 } else {
1362 $(this).show();
1363 $(this).on('click', '.notice-dismiss', function (e) {
1364 dismissed = acf.getPreference('dismissed-notices');
1365 if (!dismissed || typeof dismissed != 'object') {
1366 dismissed = [];
1367 }
1368 dismissed.push($(this).closest('.acf-admin-notice').data('persist-id'));
1369 acf.setPreference('dismissed-notices', dismissed);
1370 });
1371 }
1372 }
1373 });
1374 }
1375 });
1376 })(jQuery);
1377
1378 /***/ }),
1379
1380 /***/ "./assets/src/js/_acf-panel.js":
1381 /*!*************************************!*\
1382 !*** ./assets/src/js/_acf-panel.js ***!
1383 \*************************************/
1384 /***/ (() => {
1385
1386 (function ($, undefined) {
1387 var panel = new acf.Model({
1388 events: {
1389 'click .acf-panel-title': 'onClick'
1390 },
1391 onClick: function (e, $el) {
1392 e.preventDefault();
1393 this.toggle($el.parent());
1394 },
1395 isOpen: function ($el) {
1396 return $el.hasClass('-open');
1397 },
1398 toggle: function ($el) {
1399 this.isOpen($el) ? this.close($el) : this.open($el);
1400 },
1401 open: function ($el) {
1402 $el.addClass('-open');
1403 $el.find('.acf-panel-title i').attr('class', 'dashicons dashicons-arrow-down');
1404 },
1405 close: function ($el) {
1406 $el.removeClass('-open');
1407 $el.find('.acf-panel-title i').attr('class', 'dashicons dashicons-arrow-right');
1408 }
1409 });
1410 })(jQuery);
1411
1412 /***/ }),
1413
1414 /***/ "./assets/src/js/_acf-popup.js":
1415 /*!*************************************!*\
1416 !*** ./assets/src/js/_acf-popup.js ***!
1417 \*************************************/
1418 /***/ (() => {
1419
1420 (function ($, undefined) {
1421 acf.models.Popup = acf.Model.extend({
1422 data: {
1423 title: '',
1424 content: '',
1425 width: 0,
1426 height: 0,
1427 loading: false,
1428 openedBy: null,
1429 confirmRemove: false
1430 },
1431 events: {
1432 'click [data-event="close"]': 'onClickClose',
1433 'click .acf-close-popup': 'onClickClose',
1434 keydown: 'onPressEscapeClose'
1435 },
1436 setup: function (props) {
1437 $.extend(this.data, props);
1438 this.$el = $(this.tmpl());
1439 },
1440 initialize: function () {
1441 this.render();
1442 this.open();
1443 this.focus();
1444 this.lockFocusToPopup(true);
1445 },
1446 tmpl: function () {
1447 return ['<div id="acf-popup" role="dialog" tabindex="-1">', '<div class="acf-popup-box acf-box">', '<div class="title"><h3></h3><a href="#" class="acf-icon -cancel grey" data-event="close" aria-label="' + acf.__('Close modal') + '"></a></div>', '<div class="inner"></div>', '<div class="loading"><i class="acf-loading"></i></div>', '</div>', '<div class="bg" data-event="close"></div>', '</div>'].join('');
1448 },
1449 render: function () {
1450 // Extract Vars.
1451 var title = this.get('title');
1452 var content = this.get('content');
1453 var loading = this.get('loading');
1454 var width = this.get('width');
1455 var height = this.get('height');
1456
1457 // Update.
1458 this.title(title);
1459 this.content(content);
1460 if (width) {
1461 this.$('.acf-popup-box').css('width', width);
1462 }
1463 if (height) {
1464 this.$('.acf-popup-box').css('min-height', height);
1465 }
1466 this.loading(loading);
1467
1468 // Trigger action.
1469 acf.doAction('append', this.$el);
1470 },
1471 /**
1472 * Places focus within the popup.
1473 */
1474 focus: function () {
1475 this.$el.find('.acf-icon').first().trigger('focus');
1476 },
1477 /**
1478 * Locks focus within the popup.
1479 *
1480 * @param {boolean} locked True to lock focus, false to unlock.
1481 */
1482 lockFocusToPopup: function (locked) {
1483 let inertElement = $('#wpwrap');
1484 if (!inertElement.length) {
1485 return;
1486 }
1487 inertElement[0].inert = locked;
1488 inertElement.attr('aria-hidden', locked);
1489 },
1490 update: function (props) {
1491 this.data = acf.parseArgs(props, this.data);
1492 this.render();
1493 },
1494 title: function (title) {
1495 this.$('.title:first h3').html(title);
1496 },
1497 content: function (content) {
1498 this.$('.inner:first').html(content);
1499 },
1500 loading: function (show) {
1501 var $loading = this.$('.loading:first');
1502 show ? $loading.show() : $loading.hide();
1503 },
1504 open: function () {
1505 $('body').append(this.$el);
1506 },
1507 close: function () {
1508 this.lockFocusToPopup(false);
1509 this.returnFocusToOrigin();
1510 this.remove();
1511 },
1512 onClickClose: function (e, $el) {
1513 e.preventDefault();
1514 this.close();
1515 },
1516 /**
1517 * Closes the popup when the escape key is pressed.
1518 *
1519 * @param {KeyboardEvent} e
1520 */
1521 onPressEscapeClose: function (e) {
1522 if (e.key === 'Escape') {
1523 this.close();
1524 }
1525 },
1526 /**
1527 * Returns focus to the element that opened the popup
1528 * if it still exists in the DOM.
1529 */
1530 returnFocusToOrigin: function () {
1531 if (this.data.openedBy instanceof $ && this.data.openedBy.closest('body').length > 0) {
1532 this.data.openedBy.trigger('focus');
1533 }
1534 }
1535 });
1536
1537 /**
1538 * PopupConfirm
1539 *
1540 * Extends the Popup model to provide confirmation functionality
1541 *
1542 * @date 17/12/17
1543 * @since ACF 5.6.5
1544 */
1545
1546 acf.models.PopupConfirm = acf.models.Popup.extend({
1547 data: {
1548 text: '',
1549 textConfirm: '',
1550 textCancel: '',
1551 context: false,
1552 confirm: function () {},
1553 cancel: function () {}
1554 },
1555 events: {
1556 'click [data-event="close"]': 'onCancel',
1557 'click .acf-close-popup': 'onClickClose',
1558 keydown: 'onPressEscapeClose',
1559 'click [data-event="confirm"]': 'onConfirm'
1560 },
1561 tmpl: function () {
1562 return `
1563 <div id="acf-popup" role="dialog" tabindex="-1">
1564 <div class="acf-popup-box acf-box acf-confirm-popup">
1565 <div class="title">
1566 <h3>${this.get('title')}</h3>
1567 <a href="#" data-event="close" aria-label="${acf.__('Close modal')}">
1568 <i class="acf-icon -close"></i>
1569 </a>
1570 </div>
1571 <div class="inner">
1572 <p>${acf.escHtml(this.get('text'))}</p>
1573 <div class="acf-actions">
1574 <button tabindex="0" type="button" data-event="close" class="acf-btn acf-btn-secondary acf-close-popup">${acf.strEscape(this.get('textCancel'))}</button>
1575 <button tabindex="0" type="submit" data-event="confirm" class="acf-btn acf-btn-primary acf-confirm">${acf.strEscape(this.get('textConfirm'))}</button>
1576 </div>
1577 </div>
1578 </div>
1579 <div class="bg" data-event="close"></div>
1580 </div>`;
1581 },
1582 render: function () {
1583 const loading = this.get('loading');
1584 const width = this.get('width');
1585 const height = this.get('height');
1586 const self = this;
1587 if (width) {
1588 this.$('.acf-popup-box').css('width', width);
1589 }
1590 if (height) {
1591 this.$('.acf-popup-box').css('min-height', height);
1592 }
1593 this.loading(loading);
1594 acf.doAction('append', this.$el);
1595 setTimeout(function () {
1596 self.$el.find('.acf-close-popup').trigger('focus');
1597 }, 1);
1598 },
1599 onConfirm: function (e, $el) {
1600 e.preventDefault();
1601 e.stopPropagation();
1602 this.close();
1603 const confirm = this.get('confirm');
1604 const context = this.get('context') || this;
1605 confirm.apply(context, arguments);
1606 },
1607 onCancel: function (e, $el) {
1608 e.preventDefault();
1609 e.stopPropagation();
1610 this.close();
1611 const cancel = this.get('cancel');
1612 const context = this.get('context') || this;
1613 cancel.apply(context, arguments);
1614 }
1615 });
1616
1617 /**
1618 * newPopup
1619 *
1620 * Creates a new Popup with the supplied props
1621 *
1622 * @date 17/12/17
1623 * @since ACF 5.6.5
1624 *
1625 * @param object props
1626 * @return object
1627 */
1628
1629 acf.newPopup = function (props) {
1630 return props.confirmRemove ? new acf.models.PopupConfirm(props) : new acf.models.Popup(props);
1631 };
1632 })(jQuery);
1633
1634 /***/ }),
1635
1636 /***/ "./assets/src/js/_acf-tooltip.js":
1637 /*!***************************************!*\
1638 !*** ./assets/src/js/_acf-tooltip.js ***!
1639 \***************************************/
1640 /***/ (() => {
1641
1642 (function ($, undefined) {
1643 acf.newTooltip = function (props) {
1644 // ensure object
1645 if (typeof props !== 'object') {
1646 props = {
1647 text: props
1648 };
1649 }
1650
1651 // confirmRemove
1652 if (props.confirmRemove !== undefined) {
1653 props.textConfirm = acf.__('Remove');
1654 props.textCancel = acf.__('Cancel');
1655 return new TooltipConfirm(props);
1656
1657 // confirm
1658 } else if (props.confirm !== undefined) {
1659 return new TooltipConfirm(props);
1660
1661 // default
1662 } else {
1663 return new Tooltip(props);
1664 }
1665 };
1666 var Tooltip = acf.Model.extend({
1667 data: {
1668 text: '',
1669 timeout: 0,
1670 target: null
1671 },
1672 tmpl: function () {
1673 return '<div class="acf-tooltip"></div>';
1674 },
1675 setup: function (props) {
1676 $.extend(this.data, props);
1677 this.$el = $(this.tmpl());
1678 },
1679 initialize: function () {
1680 // render
1681 this.render();
1682
1683 // append
1684 this.show();
1685
1686 // position
1687 this.position();
1688
1689 // timeout
1690 var timeout = this.get('timeout');
1691 if (timeout) {
1692 setTimeout($.proxy(this.fade, this), timeout);
1693 }
1694 },
1695 update: function (props) {
1696 $.extend(this.data, props);
1697 this.initialize();
1698 },
1699 render: function () {
1700 this.$el.text(this.get('text'));
1701 },
1702 show: function () {
1703 $('body').append(this.$el);
1704 },
1705 hide: function () {
1706 this.$el.remove();
1707 },
1708 fade: function () {
1709 // add class
1710 this.$el.addClass('acf-fade-up');
1711
1712 // remove
1713 this.setTimeout(function () {
1714 this.remove();
1715 }, 250);
1716 },
1717 html: function (html) {
1718 this.$el.html(html);
1719 },
1720 position: function () {
1721 // vars
1722 var $tooltip = this.$el;
1723 var $target = this.get('target');
1724 if (!$target) return;
1725
1726 // Reset position.
1727 $tooltip.removeClass('right left bottom top').css({
1728 top: 0,
1729 left: 0
1730 });
1731
1732 // Declare tolerance to edge of screen.
1733 var tolerance = 10;
1734
1735 // Find target position.
1736 var targetWidth = $target.outerWidth();
1737 var targetHeight = $target.outerHeight();
1738 var targetTop = $target.offset().top;
1739 var targetLeft = $target.offset().left;
1740
1741 // Find tooltip position.
1742 var tooltipWidth = $tooltip.outerWidth();
1743 var tooltipHeight = $tooltip.outerHeight();
1744 var tooltipTop = $tooltip.offset().top; // Should be 0, but WP media grid causes this to be 32 (toolbar padding).
1745
1746 // Assume default top alignment.
1747 var top = targetTop - tooltipHeight - tooltipTop;
1748 var left = targetLeft + targetWidth / 2 - tooltipWidth / 2;
1749
1750 // Check if too far left.
1751 if (left < tolerance) {
1752 $tooltip.addClass('right');
1753 left = targetLeft + targetWidth;
1754 top = targetTop + targetHeight / 2 - tooltipHeight / 2 - tooltipTop;
1755
1756 // Check if too far right.
1757 } else if (left + tooltipWidth + tolerance > $(window).width()) {
1758 $tooltip.addClass('left');
1759 left = targetLeft - tooltipWidth;
1760 top = targetTop + targetHeight / 2 - tooltipHeight / 2 - tooltipTop;
1761
1762 // Check if too far up.
1763 } else if (top - $(window).scrollTop() < tolerance) {
1764 $tooltip.addClass('bottom');
1765 top = targetTop + targetHeight - tooltipTop;
1766
1767 // No collision with edges.
1768 } else {
1769 $tooltip.addClass('top');
1770 }
1771
1772 // update css
1773 $tooltip.css({
1774 top: top,
1775 left: left
1776 });
1777 }
1778 });
1779 var TooltipConfirm = Tooltip.extend({
1780 data: {
1781 text: '',
1782 textConfirm: '',
1783 textCancel: '',
1784 target: null,
1785 targetConfirm: true,
1786 confirm: function () {},
1787 cancel: function () {},
1788 context: false
1789 },
1790 events: {
1791 'click [data-event="cancel"]': 'onCancel',
1792 'click [data-event="confirm"]': 'onConfirm'
1793 },
1794 addEvents: function () {
1795 // add events
1796 acf.Model.prototype.addEvents.apply(this);
1797
1798 // vars
1799 var $document = $(document);
1800 var $target = this.get('target');
1801
1802 // add global 'cancel' click event
1803 // - use timeout to avoid the current 'click' event triggering the onCancel function
1804 this.setTimeout(function () {
1805 this.on($document, 'click', 'onCancel');
1806 });
1807
1808 // add target 'confirm' click event
1809 // - allow setting to control this feature
1810 if (this.get('targetConfirm')) {
1811 this.on($target, 'click', 'onConfirm');
1812 }
1813 },
1814 removeEvents: function () {
1815 // remove events
1816 acf.Model.prototype.removeEvents.apply(this);
1817
1818 // vars
1819 var $document = $(document);
1820 var $target = this.get('target');
1821
1822 // remove custom events
1823 this.off($document, 'click');
1824 this.off($target, 'click');
1825 },
1826 render: function () {
1827 // defaults
1828 var text = this.get('text') || acf.__('Are you sure?');
1829 var textConfirm = this.get('textConfirm') || acf.__('Yes');
1830 var textCancel = this.get('textCancel') || acf.__('No');
1831
1832 // html
1833 var html = [text, '<a href="#" data-event="confirm">' + textConfirm + '</a>', '<a href="#" data-event="cancel">' + textCancel + '</a>'].join(' ');
1834
1835 // html
1836 this.html(html);
1837
1838 // class
1839 this.$el.addClass('-confirm');
1840 },
1841 onCancel: function (e, $el) {
1842 // prevent default
1843 e.preventDefault();
1844 e.stopImmediatePropagation();
1845
1846 // callback
1847 var callback = this.get('cancel');
1848 var context = this.get('context') || this;
1849 callback.apply(context, arguments);
1850
1851 //remove
1852 this.remove();
1853 },
1854 onConfirm: function (e, $el) {
1855 // Prevent event from propagating completely to allow "targetConfirm" to be clicked.
1856 e.preventDefault();
1857 e.stopImmediatePropagation();
1858
1859 // callback
1860 var callback = this.get('confirm');
1861 var context = this.get('context') || this;
1862 callback.apply(context, arguments);
1863
1864 //remove
1865 this.remove();
1866 }
1867 });
1868
1869 // storage
1870 acf.models.Tooltip = Tooltip;
1871 acf.models.TooltipConfirm = TooltipConfirm;
1872
1873 /**
1874 * tooltipManager
1875 *
1876 * description
1877 *
1878 * @date 17/4/18
1879 * @since ACF 5.6.9
1880 *
1881 * @param type $var Description. Default.
1882 * @return type Description.
1883 */
1884
1885 var tooltipHoverHelper = new acf.Model({
1886 tooltip: false,
1887 events: {
1888 'mouseenter .acf-js-tooltip': 'showTitle',
1889 'mouseup .acf-js-tooltip': 'hideTitle',
1890 'mouseleave .acf-js-tooltip': 'hideTitle',
1891 'focus .acf-js-tooltip': 'showTitle',
1892 'blur .acf-js-tooltip': 'hideTitle',
1893 'keyup .acf-js-tooltip': 'onKeyUp'
1894 },
1895 showTitle: function (e, $el) {
1896 // vars
1897 let title = $el.attr('title');
1898
1899 // bail early if no title
1900 if (!title) {
1901 return;
1902 }
1903
1904 // clear title to avoid default browser tooltip
1905 $el.attr('title', '');
1906 $el.data('acf-js-tooltip-title', title);
1907 title = acf.strEscape(title);
1908
1909 // create
1910 if (!this.tooltip) {
1911 this.tooltip = acf.newTooltip({
1912 text: title,
1913 target: $el
1914 });
1915
1916 // update
1917 } else {
1918 this.tooltip.update({
1919 text: title,
1920 target: $el
1921 });
1922 }
1923 },
1924 hideTitle: function (e, $el) {
1925 // hide tooltip
1926 this.tooltip.hide();
1927 $el.attr('title', $el.data('acf-js-tooltip-title'));
1928
1929 // restore title
1930 $el.removeData('acf-js-tooltip-title');
1931 },
1932 onKeyUp: function (e, $el) {
1933 if ('Escape' === e.key) {
1934 this.hideTitle(e, $el);
1935 }
1936 }
1937 });
1938 })(jQuery);
1939
1940 /***/ }),
1941
1942 /***/ "./assets/src/js/_acf.js":
1943 /*!*******************************!*\
1944 !*** ./assets/src/js/_acf.js ***!
1945 \*******************************/
1946 /***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
1947
1948 (function ($, undefined) {
1949 /**
1950 * acf
1951 *
1952 * description
1953 *
1954 * @date 14/12/17
1955 * @since ACF 5.6.5
1956 *
1957 * @param type $var Description. Default.
1958 * @return type Description.
1959 */
1960
1961 // The global acf object
1962 var acf = {};
1963
1964 // Set as a browser global
1965 window.acf = acf;
1966
1967 /** @var object Data sent from PHP */
1968 acf.data = {};
1969
1970 /**
1971 * get
1972 *
1973 * Gets a specific data value
1974 *
1975 * @date 14/12/17
1976 * @since ACF 5.6.5
1977 *
1978 * @param string name
1979 * @return mixed
1980 */
1981
1982 acf.get = function (name) {
1983 return this.data[name] || null;
1984 };
1985
1986 /**
1987 * has
1988 *
1989 * Returns `true` if the data exists and is not null
1990 *
1991 * @date 14/12/17
1992 * @since ACF 5.6.5
1993 *
1994 * @param string name
1995 * @return boolean
1996 */
1997
1998 acf.has = function (name) {
1999 return this.get(name) !== null;
2000 };
2001
2002 /**
2003 * set
2004 *
2005 * Sets a specific data value
2006 *
2007 * @date 14/12/17
2008 * @since ACF 5.6.5
2009 *
2010 * @param string name
2011 * @param mixed value
2012 * @return this
2013 */
2014
2015 acf.set = function (name, value) {
2016 this.data[name] = value;
2017 return this;
2018 };
2019
2020 /**
2021 * uniqueId
2022 *
2023 * Returns a unique ID
2024 *
2025 * @date 9/11/17
2026 * @since ACF 5.6.3
2027 *
2028 * @param string prefix Optional prefix.
2029 * @return string
2030 */
2031
2032 var idCounter = 0;
2033 acf.uniqueId = function (prefix) {
2034 var id = ++idCounter + '';
2035 return prefix ? prefix + id : id;
2036 };
2037
2038 /**
2039 * acf.uniqueArray
2040 *
2041 * Returns a new array with only unique values
2042 * Credit: https://stackoverflow.com/questions/1960473/get-all-unique-values-in-an-array-remove-duplicates
2043 *
2044 * @date 23/3/18
2045 * @since ACF 5.6.9
2046 *
2047 * @param type $var Description. Default.
2048 * @return type Description.
2049 */
2050
2051 acf.uniqueArray = function (array) {
2052 function onlyUnique(value, index, self) {
2053 return self.indexOf(value) === index;
2054 }
2055 return array.filter(onlyUnique);
2056 };
2057
2058 /**
2059 * uniqid
2060 *
2061 * Returns a unique ID (PHP version)
2062 *
2063 * @date 9/11/17
2064 * @since ACF 5.6.3
2065 * @source http://locutus.io/php/misc/uniqid/
2066 *
2067 * @param string prefix Optional prefix.
2068 * @return string
2069 */
2070
2071 var uniqidSeed = '';
2072 acf.uniqid = function (prefix, moreEntropy) {
2073 // discuss at: http://locutus.io/php/uniqid/
2074 // original by: Kevin van Zonneveld (http://kvz.io)
2075 // revised by: Kankrelune (http://www.webfaktory.info/)
2076 // note 1: Uses an internal counter (in locutus global) to avoid collision
2077 // example 1: var $id = uniqid()
2078 // example 1: var $result = $id.length === 13
2079 // returns 1: true
2080 // example 2: var $id = uniqid('foo')
2081 // example 2: var $result = $id.length === (13 + 'foo'.length)
2082 // returns 2: true
2083 // example 3: var $id = uniqid('bar', true)
2084 // example 3: var $result = $id.length === (23 + 'bar'.length)
2085 // returns 3: true
2086 if (typeof prefix === 'undefined') {
2087 prefix = '';
2088 }
2089 var retId;
2090 var formatSeed = function (seed, reqWidth) {
2091 seed = parseInt(seed, 10).toString(16); // to hex str
2092 if (reqWidth < seed.length) {
2093 // so long we split
2094 return seed.slice(seed.length - reqWidth);
2095 }
2096 if (reqWidth > seed.length) {
2097 // so short we pad
2098 return Array(1 + (reqWidth - seed.length)).join('0') + seed;
2099 }
2100 return seed;
2101 };
2102 if (!uniqidSeed) {
2103 // init seed with big random int
2104 uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
2105 }
2106 uniqidSeed++;
2107 retId = prefix; // start with prefix, add current milliseconds hex string
2108 retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
2109 retId += formatSeed(uniqidSeed, 5); // add seed hex string
2110 if (moreEntropy) {
2111 // for more entropy we add a float lower to 10
2112 retId += (Math.random() * 10).toFixed(8).toString();
2113 }
2114 return retId;
2115 };
2116
2117 /**
2118 * strReplace
2119 *
2120 * Performs a string replace
2121 *
2122 * @date 14/12/17
2123 * @since ACF 5.6.5
2124 *
2125 * @param string search
2126 * @param string replace
2127 * @param string subject
2128 * @return string
2129 */
2130
2131 acf.strReplace = function (search, replace, subject) {
2132 return subject.split(search).join(replace);
2133 };
2134
2135 /**
2136 * strCamelCase
2137 *
2138 * Converts a string into camelCase
2139 * Thanks to https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case
2140 *
2141 * @date 14/12/17
2142 * @since ACF 5.6.5
2143 *
2144 * @param string str
2145 * @return string
2146 */
2147
2148 acf.strCamelCase = function (str) {
2149 var matches = str.match(/([a-zA-Z0-9]+)/g);
2150 return matches ? matches.map(function (s, i) {
2151 var c = s.charAt(0);
2152 return (i === 0 ? c.toLowerCase() : c.toUpperCase()) + s.slice(1);
2153 }).join('') : '';
2154 };
2155
2156 /**
2157 * strPascalCase
2158 *
2159 * Converts a string into PascalCase
2160 * Thanks to https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript
2161 *
2162 * @date 14/12/17
2163 * @since ACF 5.6.5
2164 *
2165 * @param string str
2166 * @return string
2167 */
2168
2169 acf.strPascalCase = function (str) {
2170 var camel = acf.strCamelCase(str);
2171 return camel.charAt(0).toUpperCase() + camel.slice(1);
2172 };
2173
2174 /**
2175 * acf.strSlugify
2176 *
2177 * Converts a string into a HTML class friendly slug
2178 *
2179 * @date 21/3/18
2180 * @since ACF 5.6.9
2181 *
2182 * @param string str
2183 * @return string
2184 */
2185
2186 acf.strSlugify = function (str) {
2187 return acf.strReplace('_', '-', str.toLowerCase());
2188 };
2189 acf.strSanitize = function (str, toLowerCase = true) {
2190 // chars (https://jsperf.com/replace-foreign-characters)
2191 var map = {
2192 À: 'A',
2193 Á: 'A',
2194 Â: 'A',
2195 Ã: 'A',
2196 Ä: 'A',
2197
2198 : 'A',
2199 Æ: 'AE',
2200 Ç: 'C',
2201 È: 'E',
2202 É: 'E',
2203 Ê: 'E',
2204 Ë: 'E',
2205 Ì: 'I',
2206 Í: 'I',
2207 Î: 'I',
2208 Ï: 'I',
2209 Ð: 'D',
2210 Ñ: 'N',
2211 Ò: 'O',
2212 Ó: 'O',
2213 Ô: 'O',
2214 Õ: 'O',
2215 Ö: 'O',
2216 Ø: 'O',
2217 Ù: 'U',
2218 Ú: 'U',
2219 Û: 'U',
2220 Ü: 'U',
2221 Ý: 'Y',
2222 ß: 's',
2223 à: 'a',
2224 á: 'a',
2225 â: 'a',
2226 ã: 'a',
2227 ä: 'a',
2228 å: 'a',
2229 æ: 'ae',
2230 ç: 'c',
2231 è: 'e',
2232 é: 'e',
2233 ê: 'e',
2234 ë: 'e',
2235 ì: 'i',
2236 í: 'i',
2237 î: 'i',
2238 ï: 'i',
2239 ñ: 'n',
2240 ò: 'o',
2241 ó: 'o',
2242 ô: 'o',
2243 õ: 'o',
2244 ö: 'o',
2245 ø: 'o',
2246 ù: 'u',
2247 ú: 'u',
2248 û: 'u',
2249 ü: 'u',
2250 ý: 'y',
2251 ÿ: 'y',
2252 Ā: 'A',
2253 ā: 'a',
2254 Ă: 'A',
2255 ă: 'a',
2256 Ą: 'A',
2257
2258 : 'a',
2259 Ć: 'C',
2260 ć: 'c',
2261 Ĉ: 'C',
2262 ĉ: 'c',
2263 Ċ: 'C',
2264 ċ: 'c',
2265 Č: 'C',
2266 č: 'c',
2267 Ď: 'D',
2268 ď: 'd',
2269 Đ: 'D',
2270 đ: 'd',
2271 Ē: 'E',
2272 ē: 'e',
2273 Ĕ: 'E',
2274 ĕ: 'e',
2275 Ė: 'E',
2276 ė: 'e',
2277 Ę: 'E',
2278 ę: 'e',
2279 Ě: 'E',
2280 ě: 'e',
2281 Ĝ: 'G',
2282 ĝ: 'g',
2283 Ğ: 'G',
2284 ğ: 'g',
2285 Ġ: 'G',
2286 ġ: 'g',
2287 Ģ: 'G',
2288 ģ: 'g',
2289 Ĥ: 'H',
2290 ĥ: 'h',
2291 Ħ: 'H',
2292 ħ: 'h',
2293 Ĩ: 'I',
2294 ĩ: 'i',
2295 Ī: 'I',
2296 ī: 'i',
2297 Ĭ: 'I',
2298 ĭ: 'i',
2299 Į: 'I',
2300 į: 'i',
2301 İ: 'I',
2302 ı: 'i',
2303 IJ: 'IJ',
2304 ij: 'ij',
2305 Ĵ: 'J',
2306 ĵ: 'j',
2307 Ķ: 'K',
2308 ķ: 'k',
2309 Ĺ: 'L',
2310 ĺ: 'l',
2311 Ļ: 'L',
2312 ļ: 'l',
2313 Ľ: 'L',
2314 ľ: 'l',
2315 Ŀ: 'L',
2316 ŀ: 'l',
2317 Ł: 'l',
2318 ł: 'l',
2319 Ń: 'N',
2320 ń: 'n',
2321
2322 : 'N',
2323 ņ: 'n',
2324 Ň: 'N',
2325 ň: 'n',
2326 ʼn: 'n',
2327 Ō: 'O',
2328 ō: 'o',
2329 Ŏ: 'O',
2330 ŏ: 'o',
2331 Ő: 'O',
2332 ő: 'o',
2333 Œ: 'OE',
2334 œ: 'oe',
2335 Ŕ: 'R',
2336 ŕ: 'r',
2337 Ŗ: 'R',
2338 ŗ: 'r',
2339 Ř: 'R',
2340 ř: 'r',
2341 Ś: 'S',
2342 ś: 's',
2343 Ŝ: 'S',
2344 ŝ: 's',
2345 Ş: 'S',
2346 ş: 's',
2347 Š: 'S',
2348 š: 's',
2349 Ţ: 'T',
2350 ţ: 't',
2351 Ť: 'T',
2352 ť: 't',
2353 Ŧ: 'T',
2354 ŧ: 't',
2355 Ũ: 'U',
2356 ũ: 'u',
2357 Ū: 'U',
2358 ū: 'u',
2359 Ŭ: 'U',
2360 ŭ: 'u',
2361 Ů: 'U',
2362 ů: 'u',
2363 Ű: 'U',
2364 ű: 'u',
2365 Ų: 'U',
2366 ų: 'u',
2367 Ŵ: 'W',
2368 ŵ: 'w',
2369 Ŷ: 'Y',
2370 ŷ: 'y',
2371 Ÿ: 'Y',
2372 Ź: 'Z',
2373 ź: 'z',
2374 Ż: 'Z',
2375 ż: 'z',
2376 Ž: 'Z',
2377 ž: 'z',
2378 ſ: 's',
2379 ƒ: 'f',
2380 Ơ: 'O',
2381 ơ: 'o',
2382 Ư: 'U',
2383 ư: 'u',
2384 Ǎ: 'A',
2385 ǎ: 'a',
2386 Ǐ: 'I',
2387 ǐ: 'i',
2388 Ǒ: 'O',
2389 ǒ: 'o',
2390 Ǔ: 'U',
2391 ǔ: 'u',
2392 Ǖ: 'U',
2393 ǖ: 'u',
2394 Ǘ: 'U',
2395 ǘ: 'u',
2396 Ǚ: 'U',
2397 ǚ: 'u',
2398 Ǜ: 'U',
2399 ǜ: 'u',
2400 Ǻ: 'A',
2401 ǻ: 'a',
2402 Ǽ: 'AE',
2403 ǽ: 'ae',
2404 Ǿ: 'O',
2405 ǿ: 'o',
2406 // extra
2407 ' ': '_',
2408 "'": '',
2409 '?': '',
2410 '/': '',
2411 '\\': '',
2412 '.': '',
2413 ',': '',
2414 '`': '',
2415 '>': '',
2416 '<': '',
2417 '"': '',
2418 '[': '',
2419 ']': '',
2420 '|': '',
2421 '{': '',
2422 '}': '',
2423 '(': '',
2424 ')': ''
2425 };
2426
2427 // vars
2428 var nonWord = /\W/g;
2429 var mapping = function (c) {
2430 return map[c] !== undefined ? map[c] : c;
2431 };
2432
2433 // replace
2434 str = str.replace(nonWord, mapping);
2435
2436 // lowercase
2437 if (toLowerCase) {
2438 str = str.toLowerCase();
2439 }
2440
2441 // return
2442 return str;
2443 };
2444
2445 /**
2446 * acf.strMatch
2447 *
2448 * Returns the number of characters that match between two strings
2449 *
2450 * @date 1/2/18
2451 * @since ACF 5.6.5
2452 *
2453 * @param type $var Description. Default.
2454 * @return type Description.
2455 */
2456
2457 acf.strMatch = function (s1, s2) {
2458 // vars
2459 var val = 0;
2460 var min = Math.min(s1.length, s2.length);
2461
2462 // loop
2463 for (var i = 0; i < min; i++) {
2464 if (s1[i] !== s2[i]) {
2465 break;
2466 }
2467 val++;
2468 }
2469
2470 // return
2471 return val;
2472 };
2473
2474 /**
2475 * Escapes HTML entities from a string.
2476 *
2477 * @date 08/06/2020
2478 * @since ACF 5.9.0
2479 *
2480 * @param string string The input string.
2481 * @return string
2482 */
2483 acf.strEscape = function (string) {
2484 var htmlEscapes = {
2485 '&': '&amp;',
2486 '<': '&lt;',
2487 '>': '&gt;',
2488 '"': '&quot;',
2489 "'": '&#39;'
2490 };
2491 return ('' + string).replace(/[&<>"']/g, function (chr) {
2492 return htmlEscapes[chr];
2493 });
2494 };
2495
2496 // Tests.
2497 //console.log( acf.strEscape('Test 1') );
2498 //console.log( acf.strEscape('Test & 1') );
2499 //console.log( acf.strEscape('Test\'s &amp; 1') );
2500 //console.log( acf.strEscape('<script>js</script>') );
2501
2502 /**
2503 * Unescapes HTML entities from a string.
2504 *
2505 * @date 08/06/2020
2506 * @since ACF 5.9.0
2507 *
2508 * @param string string The input string.
2509 * @return string
2510 */
2511 acf.strUnescape = function (string) {
2512 var htmlUnescapes = {
2513 '&amp;': '&',
2514 '&lt;': '<',
2515 '&gt;': '>',
2516 '&quot;': '"',
2517 '&#39;': "'"
2518 };
2519 return ('' + string).replace(/&amp;|&lt;|&gt;|&quot;|&#39;/g, function (entity) {
2520 return htmlUnescapes[entity];
2521 });
2522 };
2523
2524 // Tests.
2525 //console.log( acf.strUnescape( acf.strEscape('Test 1') ) );
2526 //console.log( acf.strUnescape( acf.strEscape('Test & 1') ) );
2527 //console.log( acf.strUnescape( acf.strEscape('Test\'s &amp; 1') ) );
2528 //console.log( acf.strUnescape( acf.strEscape('<script>js</script>') ) );
2529
2530 /**
2531 * Escapes HTML entities from a string.
2532 *
2533 * @date 08/06/2020
2534 * @since ACF 5.9.0
2535 *
2536 * @param string string The input string.
2537 * @return string
2538 */
2539 acf.escAttr = acf.strEscape;
2540
2541 /**
2542 * Encodes <script> tags for safe HTML output.
2543 *
2544 * @date 08/06/2020
2545 * @since ACF 5.9.0
2546 * @since ACF 6.4.3 - Use DOMPurify for better security.
2547 *
2548 * @param string string The input string.
2549 * @return string
2550 */
2551
2552 acf.escHtml = function (string) {
2553 string = '' + string; // Convert to string if not already.
2554 const DOMPurify = __webpack_require__(/*! dompurify */ "./node_modules/dompurify/dist/purify.cjs.js");
2555 if (DOMPurify === undefined || !DOMPurify.isSupported) {
2556 console.warn('ACF: DOMPurify not loaded or not supported. Falling back to basic HTML escaping for security.');
2557 return acf.strEscape(string); // Fallback to basic escaping.
2558 }
2559 const options = acf.applyFilters('esc_html_dompurify_config', {
2560 USE_PROFILES: {
2561 html: true
2562 },
2563 ADD_TAGS: ['audio', 'video'],
2564 ADD_ATTR: ['controls', 'loop', 'muted', 'preload'],
2565 FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'base', 'meta', 'form'],
2566 FORBID_ATTR: ['style', 'srcset', 'action', 'background', 'dynsrc', 'lowsrc', 'on*'],
2567 ALLOW_DATA_ATTR: false,
2568 ALLOW_ARIA_ATTR: false
2569 });
2570 return DOMPurify.sanitize(string, options);
2571 };
2572
2573 // Tests.
2574 //console.log( acf.escHtml('<script>js</script>') );
2575 //console.log( acf.escHtml( acf.strEscape('<script>js</script>') ) );
2576 //console.log( acf.escHtml( '<script>js1</script><script>js2</script>' ) );
2577
2578 /**
2579 * Encode a string potentially containing HTML into it's HTML entities equivalent.
2580 *
2581 * @since ACF 6.3.6
2582 *
2583 * @param {string} string String to encode.
2584 * @return {string} The encoded string
2585 */
2586 acf.encode = function (string) {
2587 return $('<textarea/>').text(string).html();
2588 };
2589
2590 /**
2591 * Decode a HTML encoded string into it's original form.
2592 *
2593 * @since ACF 5.6.5
2594 *
2595 * @param {string} string String to encode.
2596 * @return {string} The encoded string
2597 */
2598 acf.decode = function (string) {
2599 return $('<textarea/>').html(string).text();
2600 };
2601
2602 /**
2603 * parseArgs
2604 *
2605 * Merges together defaults and args much like the WP wp_parse_args function
2606 *
2607 * @date 14/12/17
2608 * @since ACF 5.6.5
2609 *
2610 * @param object args
2611 * @param object defaults
2612 * @return object
2613 */
2614
2615 acf.parseArgs = function (args, defaults) {
2616 if (typeof args !== 'object') args = {};
2617 if (typeof defaults !== 'object') defaults = {};
2618 return $.extend({}, defaults, args);
2619 };
2620
2621 /**
2622 * __
2623 *
2624 * Retrieve the translation of $text.
2625 *
2626 * @date 16/4/18
2627 * @since ACF 5.6.9
2628 *
2629 * @param string text Text to translate.
2630 * @return string Translated text.
2631 */
2632
2633 // Make sure a global acfL10n object exists to prevent errors in other scopes
2634 window.acfL10n = window.acfL10n || {};
2635 acf.__ = function (text) {
2636 return acfL10n[text] || text;
2637 };
2638
2639 /**
2640 * _x
2641 *
2642 * Retrieve translated string with gettext context.
2643 *
2644 * @date 16/4/18
2645 * @since ACF 5.6.9
2646 *
2647 * @param string text Text to translate.
2648 * @param string context Context information for the translators.
2649 * @return string Translated text.
2650 */
2651
2652 acf._x = function (text, context) {
2653 return acfL10n[text + '.' + context] || acfL10n[text] || text;
2654 };
2655
2656 /**
2657 * _n
2658 *
2659 * Retrieve the plural or single form based on the amount.
2660 *
2661 * @date 16/4/18
2662 * @since ACF 5.6.9
2663 *
2664 * @param string single Single text to translate.
2665 * @param string plural Plural text to translate.
2666 * @param int number The number to compare against.
2667 * @return string Translated text.
2668 */
2669
2670 acf._n = function (single, plural, number) {
2671 if (number == 1) {
2672 return acf.__(single);
2673 } else {
2674 return acf.__(plural);
2675 }
2676 };
2677 acf.isArray = function (a) {
2678 return Array.isArray(a);
2679 };
2680 acf.isObject = function (a) {
2681 return typeof a === 'object';
2682 };
2683
2684 /**
2685 * serialize
2686 *
2687 * description
2688 *
2689 * @date 24/12/17
2690 * @since ACF 5.6.5
2691 *
2692 * @param type $var Description. Default.
2693 * @return type Description.
2694 */
2695
2696 var buildObject = function (obj, name, value) {
2697 // replace [] with placeholder
2698 name = name.replace('[]', '[%%index%%]');
2699
2700 // vars
2701 var keys = name.match(/([^\[\]])+/g);
2702 if (!keys) return;
2703 var length = keys.length;
2704 var ref = obj;
2705
2706 // loop
2707 for (var i = 0; i < length; i++) {
2708 // vars
2709 var key = String(keys[i]);
2710
2711 // value
2712 if (i == length - 1) {
2713 // %%index%%
2714 if (key === '%%index%%') {
2715 ref.push(value);
2716
2717 // default
2718 } else {
2719 ref[key] = value;
2720 }
2721
2722 // path
2723 } else {
2724 // array
2725 if (keys[i + 1] === '%%index%%') {
2726 if (!acf.isArray(ref[key])) {
2727 ref[key] = [];
2728 }
2729
2730 // object
2731 } else {
2732 if (!acf.isObject(ref[key])) {
2733 ref[key] = {};
2734 }
2735 }
2736
2737 // crawl
2738 ref = ref[key];
2739 }
2740 }
2741 };
2742 acf.serialize = function ($el, prefix) {
2743 // vars
2744 var obj = {};
2745 var inputs = acf.serializeArray($el);
2746
2747 // prefix
2748 if (prefix !== undefined) {
2749 // filter and modify
2750 inputs = inputs.filter(function (item) {
2751 return item.name.indexOf(prefix) === 0;
2752 }).map(function (item) {
2753 item.name = item.name.slice(prefix.length);
2754 return item;
2755 });
2756 }
2757
2758 // loop
2759 for (var i = 0; i < inputs.length; i++) {
2760 buildObject(obj, inputs[i].name, inputs[i].value);
2761 }
2762
2763 // return
2764 return obj;
2765 };
2766
2767 /**
2768 * Check if an object has only numeric string keys.
2769 * Used to detect objects that should be converted to arrays (e.g., checkbox values).
2770 *
2771 * Semantics:
2772 * - Accepts base-10, non-negative integer strings composed of digits only (e.g. "0", "12").
2773 * - Leading zeros are allowed (e.g. "0012") and treated as numeric by downstream logic.
2774 * - Negative ("-1"), decimal ("1.0"), and non-numeric keys are rejected.
2775 *
2776 * @since SCF 6.6.0
2777 * @private
2778 *
2779 * @param object obj The object to check
2780 * @return boolean True if all keys are numeric strings
2781 */
2782 const hasOnlyNumericKeys = function (obj) {
2783 const keys = Object.keys(obj);
2784 if (keys.length === 0) {
2785 return false;
2786 }
2787 for (let i = 0; i < keys.length; i++) {
2788 if (!/^\d+$/.test(keys[i])) {
2789 return false;
2790 }
2791 }
2792 return true;
2793 };
2794
2795 /**
2796 * Convert an object with numeric string keys to a numerically sorted array.
2797 * Example: {"0": "one", "2": "three", "1": "two"} becomes ["one", "two", "three"].
2798 *
2799 * Notes on edge-cases:
2800 * - Leading zeros (e.g. "00123") are supported; order is based on the numeric value
2801 * but the original string key is used to read the value to avoid lookup mismatches.
2802 * - Assumes {@link hasOnlyNumericKeys} has already gated out negatives/decimals.
2803 *
2804 * @since SCF 6.6.0
2805 * @private
2806 *
2807 * @param object obj The object to convert
2808 * @return array The numerically sorted array of values
2809 */
2810 const numericObjectToArray = function (obj) {
2811 const arr = [];
2812 // Pair each original key with its numeric value for stable lookup and sorting.
2813 const entries = Object.keys(obj).map(function (k) {
2814 return {
2815 k: k,
2816 n: parseInt(k, 10)
2817 };
2818 }).sort(function (a, b) {
2819 return a.n - b.n;
2820 });
2821 for (let i = 0; i < entries.length; i++) {
2822 arr.push(obj[entries[i].k]);
2823 }
2824 return arr;
2825 };
2826
2827 /**
2828 * Check if a value looks like flexible content data.
2829 * Flexible content objects contain rows where each row object has an 'acf_fc_layout' property.
2830 * Keys for flexible rows are not guaranteed to be numeric: they are typically unique IDs
2831 * (e.g. '69171156640b5') or strings like 'row-0'. Therefore, flexible content detection does
2832 * not rely on numeric keys and is handled separately from numeric-keyed object normalization.
2833 *
2834 * @since SCF 6.6.0
2835 *
2836 * @param object value The value to check
2837 * @return boolean True if this looks like flexible content data
2838 */
2839 acf.isFlexibleContentData = function (value) {
2840 if (!acf.isObject(value)) {
2841 return false;
2842 }
2843 var keys = Object.keys(value);
2844 for (var i = 0; i < keys.length; i++) {
2845 var key = keys[i];
2846 if (key === 'acfcloneindex') {
2847 continue;
2848 }
2849 var subvalue = value[key];
2850 if (acf.isObject(subvalue) && subvalue.acf_fc_layout) {
2851 return true;
2852 }
2853 }
2854 return false;
2855 };
2856
2857 /**
2858 * Normalizes flexible content data structure by converting objects to arrays.
2859 * Private helper function.
2860 *
2861 * @since 6.6.0
2862 *
2863 * @param {Object} obj The object to normalize.
2864 * @return {Object|Array} The normalized data.
2865 */
2866 const normalizeFlexibleContentData = function (obj) {
2867 if (!acf.isObject(obj)) {
2868 return obj;
2869 }
2870 let result = {};
2871 for (let key in obj) {
2872 if (!obj.hasOwnProperty(key)) {
2873 continue;
2874 }
2875 var value = obj[key];
2876
2877 // Primitives pass through unchanged
2878 if (!acf.isObject(value)) {
2879 result[key] = value;
2880 continue;
2881 }
2882
2883 // Convert numeric-keyed objects to arrays (e.g., checkbox values)
2884 if (hasOnlyNumericKeys(value)) {
2885 result[key] = numericObjectToArray(value);
2886 continue;
2887 } // Convert flexible content to arrays
2888 if (acf.isFlexibleContentData(value)) {
2889 var arr = [];
2890 var keys = Object.keys(value);
2891 for (var i = 0; i < keys.length; i++) {
2892 var subkey = keys[i];
2893 if (subkey === 'acfcloneindex') {
2894 continue;
2895 }
2896 var subvalue = value[subkey];
2897 if (acf.isObject(subvalue) && subvalue.acf_fc_layout) {
2898 arr.push(normalizeFlexibleContentData(subvalue));
2899 }
2900 }
2901 result[key] = arr;
2902 } else {
2903 // Recursively process nested objects
2904 result[key] = normalizeFlexibleContentData(value);
2905 }
2906 }
2907 return result;
2908 };
2909
2910 /**
2911 * Public API wrapper for normalizeFlexibleContentData.
2912 * Normalizes flexible content data structure by converting objects to arrays.
2913 *
2914 * @since 6.6.0
2915 *
2916 * @param {Object} obj The object to normalize.
2917 * @return {Object|Array} The normalized data.
2918 */
2919 acf.normalizeFlexibleContentData = function (obj) {
2920 return normalizeFlexibleContentData(obj);
2921 };
2922
2923 /**
2924 * acf.serializeArray
2925 *
2926 * Similar to $.serializeArray() but works with a parent wrapping element.
2927 *
2928 * @date 19/8/18
2929 * @since ACF 5.7.3
2930 *
2931 * @param jQuery $el The element or form to serialize.
2932 * @return array
2933 */
2934
2935 acf.serializeArray = function ($el) {
2936 return $el.find('select, textarea, input').serializeArray();
2937 };
2938
2939 /**
2940 * acf.serializeForAjax
2941 *
2942 * Returns an object containing name => value data ready to be encoded for Ajax.
2943 *
2944 * @date 17/12/18
2945 * @since ACF 5.8.0
2946 *
2947 * @param jQuery $el The element or form to serialize.
2948 * @return object
2949 */
2950 acf.serializeForAjax = function ($el) {
2951 // vars
2952 var data = {};
2953 var index = {};
2954
2955 // Serialize inputs.
2956 var inputs = acf.serializeArray($el);
2957
2958 // Loop over inputs and build data.
2959 inputs.map(function (item) {
2960 // Append to array.
2961 if (item.name.slice(-2) === '[]') {
2962 data[item.name] = data[item.name] || [];
2963 data[item.name].push(item.value);
2964 // Append
2965 } else {
2966 data[item.name] = item.value;
2967 }
2968 });
2969
2970 // return
2971 return data;
2972 };
2973
2974 /**
2975 * addAction
2976 *
2977 * Wrapper for acf.hooks.addAction
2978 *
2979 * @date 14/12/17
2980 * @since ACF 5.6.5
2981 *
2982 * @param n/a
2983 * @return this
2984 */
2985
2986 /*
2987 var prefixAction = function( action ){
2988 return 'acf_' + action;
2989 }
2990 */
2991
2992 acf.addAction = function () {
2993 //action = prefixAction(action);
2994 acf.hooks.addAction.apply(this, arguments);
2995 return this;
2996 };
2997
2998 /**
2999 * removeAction
3000 *
3001 * Wrapper for acf.hooks.removeAction
3002 *
3003 * @date 14/12/17
3004 * @since ACF 5.6.5
3005 *
3006 * @param n/a
3007 * @return this
3008 */
3009
3010 acf.removeAction = function () {
3011 //action = prefixAction(action);
3012 acf.hooks.removeAction.apply(this, arguments);
3013 return this;
3014 };
3015
3016 /**
3017 * doAction
3018 *
3019 * Wrapper for acf.hooks.doAction
3020 *
3021 * @date 14/12/17
3022 * @since ACF 5.6.5
3023 *
3024 * @param n/a
3025 * @return this
3026 */
3027
3028 var actionHistory = {};
3029 //var currentAction = false;
3030 acf.doAction = function (action) {
3031 //action = prefixAction(action);
3032 //currentAction = action;
3033 actionHistory[action] = 1;
3034 acf.hooks.doAction.apply(this, arguments);
3035 actionHistory[action] = 0;
3036 return this;
3037 };
3038
3039 /**
3040 * doingAction
3041 *
3042 * Return true if doing action
3043 *
3044 * @date 14/12/17
3045 * @since ACF 5.6.5
3046 *
3047 * @param n/a
3048 * @return this
3049 */
3050
3051 acf.doingAction = function (action) {
3052 //action = prefixAction(action);
3053 return actionHistory[action] === 1;
3054 };
3055
3056 /**
3057 * didAction
3058 *
3059 * Wrapper for acf.hooks.doAction
3060 *
3061 * @date 14/12/17
3062 * @since ACF 5.6.5
3063 *
3064 * @param n/a
3065 * @return this
3066 */
3067
3068 acf.didAction = function (action) {
3069 //action = prefixAction(action);
3070 return actionHistory[action] !== undefined;
3071 };
3072
3073 /**
3074 * currentAction
3075 *
3076 * Wrapper for acf.hooks.doAction
3077 *
3078 * @date 14/12/17
3079 * @since ACF 5.6.5
3080 *
3081 * @param n/a
3082 * @return this
3083 */
3084
3085 acf.currentAction = function () {
3086 for (var k in actionHistory) {
3087 if (actionHistory[k]) {
3088 return k;
3089 }
3090 }
3091 return false;
3092 };
3093
3094 /**
3095 * addFilter
3096 *
3097 * Wrapper for acf.hooks.addFilter
3098 *
3099 * @date 14/12/17
3100 * @since ACF 5.6.5
3101 *
3102 * @param n/a
3103 * @return this
3104 */
3105
3106 acf.addFilter = function () {
3107 //action = prefixAction(action);
3108 acf.hooks.addFilter.apply(this, arguments);
3109 return this;
3110 };
3111
3112 /**
3113 * removeFilter
3114 *
3115 * Wrapper for acf.hooks.removeFilter
3116 *
3117 * @date 14/12/17
3118 * @since ACF 5.6.5
3119 *
3120 * @param n/a
3121 * @return this
3122 */
3123
3124 acf.removeFilter = function () {
3125 //action = prefixAction(action);
3126 acf.hooks.removeFilter.apply(this, arguments);
3127 return this;
3128 };
3129
3130 /**
3131 * applyFilters
3132 *
3133 * Wrapper for acf.hooks.applyFilters
3134 *
3135 * @date 14/12/17
3136 * @since ACF 5.6.5
3137 *
3138 * @param n/a
3139 * @return this
3140 */
3141
3142 acf.applyFilters = function () {
3143 //action = prefixAction(action);
3144 return acf.hooks.applyFilters.apply(this, arguments);
3145 };
3146
3147 /**
3148 * getArgs
3149 *
3150 * description
3151 *
3152 * @date 15/12/17
3153 * @since ACF 5.6.5
3154 *
3155 * @param type $var Description. Default.
3156 * @return type Description.
3157 */
3158
3159 acf.arrayArgs = function (args) {
3160 return Array.prototype.slice.call(args);
3161 };
3162
3163 /**
3164 * extendArgs
3165 *
3166 * description
3167 *
3168 * @date 15/12/17
3169 * @since ACF 5.6.5
3170 *
3171 * @param type $var Description. Default.
3172 * @return type Description.
3173 */
3174
3175 /*
3176 acf.extendArgs = function( ){
3177 var args = Array.prototype.slice.call( arguments );
3178 var realArgs = args.shift();
3179
3180 Array.prototype.push.call(arguments, 'bar')
3181 return Array.prototype.push.apply( args, arguments );
3182 };
3183 */
3184
3185 // Preferences
3186 // - use try/catch to avoid JS error if cookies are disabled on front-end form
3187 try {
3188 var preferences = JSON.parse(localStorage.getItem('acf')) || {};
3189 } catch (e) {
3190 var preferences = {};
3191 }
3192
3193 /**
3194 * getPreferenceName
3195 *
3196 * Gets the true preference name.
3197 * Converts "this.thing" to "thing-123" if editing post 123.
3198 *
3199 * @date 11/11/17
3200 * @since ACF 5.6.5
3201 *
3202 * @param string name
3203 * @return string
3204 */
3205
3206 var getPreferenceName = function (name) {
3207 if (name.substr(0, 5) === 'this.') {
3208 name = name.substr(5) + '-' + acf.get('post_id');
3209 }
3210 return name;
3211 };
3212
3213 /**
3214 * acf.getPreference
3215 *
3216 * Gets a preference setting or null if not set.
3217 *
3218 * @date 11/11/17
3219 * @since ACF 5.6.5
3220 *
3221 * @param string name
3222 * @return mixed
3223 */
3224
3225 acf.getPreference = function (name) {
3226 name = getPreferenceName(name);
3227 return preferences[name] || null;
3228 };
3229
3230 /**
3231 * acf.setPreference
3232 *
3233 * Sets a preference setting.
3234 *
3235 * @date 11/11/17
3236 * @since ACF 5.6.5
3237 *
3238 * @param string name
3239 * @param mixed value
3240 * @return n/a
3241 */
3242
3243 acf.setPreference = function (name, value) {
3244 name = getPreferenceName(name);
3245 if (value === null) {
3246 delete preferences[name];
3247 } else {
3248 preferences[name] = value;
3249 }
3250 localStorage.setItem('acf', JSON.stringify(preferences));
3251 };
3252
3253 /**
3254 * acf.removePreference
3255 *
3256 * Removes a preference setting.
3257 *
3258 * @date 11/11/17
3259 * @since ACF 5.6.5
3260 *
3261 * @param string name
3262 * @return n/a
3263 */
3264
3265 acf.removePreference = function (name) {
3266 acf.setPreference(name, null);
3267 };
3268
3269 /**
3270 * remove
3271 *
3272 * Removes an element with fade effect
3273 *
3274 * @date 1/1/18
3275 * @since ACF 5.6.5
3276 *
3277 * @param type $var Description. Default.
3278 * @return type Description.
3279 */
3280
3281 acf.remove = function (props) {
3282 // allow jQuery
3283 if (props instanceof jQuery) {
3284 props = {
3285 target: props
3286 };
3287 }
3288
3289 // defaults
3290 props = acf.parseArgs(props, {
3291 target: false,
3292 endHeight: 0,
3293 complete: function () {}
3294 });
3295
3296 // action
3297 acf.doAction('remove', props.target);
3298
3299 // tr
3300 if (props.target.is('tr')) {
3301 removeTr(props);
3302
3303 // div
3304 } else {
3305 removeDiv(props);
3306 }
3307 };
3308
3309 /**
3310 * removeDiv
3311 *
3312 * description
3313 *
3314 * @date 16/2/18
3315 * @since ACF 5.6.9
3316 *
3317 * @param type $var Description. Default.
3318 * @return type Description.
3319 */
3320
3321 var removeDiv = function (props) {
3322 // vars
3323 var $el = props.target;
3324 var height = $el.height();
3325 var width = $el.width();
3326 var margin = $el.css('margin');
3327 var outerHeight = $el.outerHeight(true);
3328 var style = $el.attr('style') + ''; // needed to copy
3329
3330 // wrap
3331 $el.wrap('<div class="acf-temp-remove" style="height:' + outerHeight + 'px"></div>');
3332 var $wrap = $el.parent();
3333
3334 // set pos
3335 $el.css({
3336 height: height,
3337 width: width,
3338 margin: margin,
3339 position: 'absolute'
3340 });
3341
3342 // fade wrap
3343 setTimeout(function () {
3344 $wrap.css({
3345 opacity: 0,
3346 height: props.endHeight
3347 });
3348 }, 50);
3349
3350 // remove
3351 setTimeout(function () {
3352 $el.attr('style', style);
3353 $wrap.remove();
3354 props.complete();
3355 }, 301);
3356 };
3357
3358 /**
3359 * removeTr
3360 *
3361 * description
3362 *
3363 * @date 16/2/18
3364 * @since ACF 5.6.9
3365 *
3366 * @param type $var Description. Default.
3367 * @return type Description.
3368 */
3369
3370 var removeTr = function (props) {
3371 // vars
3372 var $tr = props.target;
3373 var height = $tr.height();
3374 var children = $tr.children().length;
3375
3376 // create dummy td
3377 var $td = $('<td class="acf-temp-remove" style="padding:0; height:' + height + 'px" colspan="' + children + '"></td>');
3378
3379 // fade away tr
3380 $tr.addClass('acf-remove-element');
3381
3382 // update HTML after fade animation
3383 setTimeout(function () {
3384 $tr.html($td);
3385 }, 251);
3386
3387 // allow .acf-temp-remove to exist before changing CSS
3388 setTimeout(function () {
3389 // remove class
3390 $tr.removeClass('acf-remove-element');
3391
3392 // collapse
3393 $td.css({
3394 height: props.endHeight
3395 });
3396 }, 300);
3397
3398 // remove
3399 setTimeout(function () {
3400 $tr.remove();
3401 props.complete();
3402 }, 451);
3403 };
3404
3405 /**
3406 * duplicate
3407 *
3408 * description
3409 *
3410 * @date 3/1/18
3411 * @since ACF 5.6.5
3412 *
3413 * @param type $var Description. Default.
3414 * @return type Description.
3415 */
3416
3417 acf.duplicate = function (args) {
3418 // allow jQuery
3419 if (args instanceof jQuery) {
3420 args = {
3421 target: args
3422 };
3423 }
3424
3425 // defaults
3426 args = acf.parseArgs(args, {
3427 target: false,
3428 search: '',
3429 replace: '',
3430 rename: true,
3431 before: function ($el) {},
3432 after: function ($el, $el2) {},
3433 append: function ($el, $el2) {
3434 $el.after($el2);
3435 }
3436 });
3437
3438 // compatibility
3439 args.target = args.target || args.$el;
3440
3441 // vars
3442 var $el = args.target;
3443
3444 // search
3445 args.search = args.search || $el.attr('data-id');
3446 args.replace = args.replace || acf.uniqid();
3447
3448 // before
3449 // - allow acf to modify DOM
3450 // - fixes bug where select field option is not selected
3451 args.before($el);
3452 acf.doAction('before_duplicate', $el);
3453
3454 // clone
3455 var $el2 = $el.clone();
3456
3457 // rename
3458 if (args.rename) {
3459 acf.rename({
3460 target: $el2,
3461 search: args.search,
3462 replace: args.replace,
3463 replacer: typeof args.rename === 'function' ? args.rename : null
3464 });
3465 }
3466
3467 // remove classes
3468 $el2.removeClass('acf-clone');
3469 $el2.find('.ui-sortable').removeClass('ui-sortable');
3470
3471 // remove any initialised select2s prevent the duplicated object stealing the previous select2.
3472 $el2.find('[data-select2-id]').removeAttr('data-select2-id');
3473 $el2.find('.select2').remove();
3474
3475 // subfield select2 renames happen after init and contain a duplicated ID. force change those IDs to prevent this.
3476 $el2.find('.acf-is-subfields select[data-ui="1"]').each(function () {
3477 $(this).prop('id', $(this).prop('id').replace('acf_fields', acf.uniqid('duplicated_') + '_acf_fields'));
3478 });
3479
3480 // remove tab wrapper to ensure proper init
3481 $el2.find('.acf-field-settings > .acf-tab-wrap').remove();
3482
3483 // after
3484 // - allow acf to modify DOM
3485 args.after($el, $el2);
3486 acf.doAction('after_duplicate', $el, $el2);
3487
3488 // append
3489 args.append($el, $el2);
3490
3491 /**
3492 * Fires after an element has been duplicated and appended to the DOM.
3493 *
3494 * @date 30/10/19
3495 * @since ACF 5.8.7
3496 *
3497 * @param jQuery $el The original element.
3498 * @param jQuery $el2 The duplicated element.
3499 */
3500 acf.doAction('duplicate', $el, $el2);
3501
3502 // append
3503 acf.doAction('append', $el2);
3504
3505 // return
3506 return $el2;
3507 };
3508
3509 /**
3510 * rename
3511 *
3512 * description
3513 *
3514 * @date 7/1/18
3515 * @since ACF 5.6.5
3516 *
3517 * @param type $var Description. Default.
3518 * @return type Description.
3519 */
3520
3521 acf.rename = function (args) {
3522 // Allow jQuery param.
3523 if (args instanceof jQuery) {
3524 args = {
3525 target: args
3526 };
3527 }
3528
3529 // Apply default args.
3530 args = acf.parseArgs(args, {
3531 target: false,
3532 destructive: false,
3533 search: '',
3534 replace: '',
3535 replacer: null
3536 });
3537
3538 // Extract args.
3539 var $el = args.target;
3540
3541 // Provide backup for empty args.
3542 if (!args.search) {
3543 args.search = $el.attr('data-id');
3544 }
3545 if (!args.replace) {
3546 args.replace = acf.uniqid('acf');
3547 }
3548 if (!args.replacer) {
3549 args.replacer = function (name, value, search, replace) {
3550 return value.replace(search, replace);
3551 };
3552 }
3553
3554 // Callback function for jQuery replacing.
3555 var withReplacer = function (name) {
3556 return function (i, value) {
3557 return args.replacer(name, value, args.search, args.replace);
3558 };
3559 };
3560
3561 // Destructive Replace.
3562 if (args.destructive) {
3563 var html = acf.strReplace(args.search, args.replace, $el.outerHTML());
3564 $el.replaceWith(html);
3565
3566 // Standard Replace.
3567 } else {
3568 $el.attr('data-id', args.replace);
3569 $el.find('[id*="' + args.search + '"]').attr('id', withReplacer('id'));
3570 $el.find('[for*="' + args.search + '"]').attr('for', withReplacer('for'));
3571 $el.find('[name*="' + args.search + '"]').attr('name', withReplacer('name'));
3572 }
3573
3574 // return
3575 return $el;
3576 };
3577
3578 /**
3579 * Prepares AJAX data prior to being sent.
3580 *
3581 * @since ACF 5.6.5
3582 *
3583 * @param Object data The data to prepare
3584 * @param boolean use_global_nonce Should we ignore any nonce provided in the data object and force ACF's global nonce for this request
3585 * @return Object The prepared data.
3586 */
3587 acf.prepareForAjax = function (data, use_global_nonce = false) {
3588 // Set a default nonce if we don't have one already.
3589 if (use_global_nonce || 'undefined' === typeof data.nonce) {
3590 data.nonce = acf.get('nonce');
3591 }
3592 data.post_id = acf.get('post_id');
3593 if (acf.has('language')) {
3594 data.lang = acf.get('language');
3595 }
3596
3597 // Filter for 3rd party customization.
3598 data = acf.applyFilters('prepare_for_ajax', data);
3599 return data;
3600 };
3601
3602 /**
3603 * acf.startButtonLoading
3604 *
3605 * description
3606 *
3607 * @date 5/1/18
3608 * @since ACF 5.6.5
3609 *
3610 * @param type $var Description. Default.
3611 * @return type Description.
3612 */
3613
3614 acf.startButtonLoading = function ($el) {
3615 $el.prop('disabled', true);
3616 $el.after(' <i class="acf-loading"></i>');
3617 };
3618 acf.stopButtonLoading = function ($el) {
3619 $el.prop('disabled', false);
3620 $el.next('.acf-loading').remove();
3621 };
3622
3623 /**
3624 * acf.showLoading
3625 *
3626 * description
3627 *
3628 * @date 12/1/18
3629 * @since ACF 5.6.5
3630 *
3631 * @param type $var Description. Default.
3632 * @return type Description.
3633 */
3634
3635 acf.showLoading = function ($el) {
3636 $el.append('<div class="acf-loading-overlay"><i class="acf-loading"></i></div>');
3637 };
3638 acf.hideLoading = function ($el) {
3639 $el.children('.acf-loading-overlay').remove();
3640 };
3641
3642 /**
3643 * acf.updateUserSetting
3644 *
3645 * description
3646 *
3647 * @date 5/1/18
3648 * @since ACF 5.6.5
3649 *
3650 * @param type $var Description. Default.
3651 * @return type Description.
3652 */
3653
3654 acf.updateUserSetting = function (name, value) {
3655 var ajaxData = {
3656 action: 'acf/ajax/user_setting',
3657 name: name,
3658 value: value
3659 };
3660 $.ajax({
3661 url: acf.get('ajaxurl'),
3662 data: acf.prepareForAjax(ajaxData),
3663 type: 'post',
3664 dataType: 'html'
3665 });
3666 };
3667
3668 /**
3669 * acf.val
3670 *
3671 * description
3672 *
3673 * @date 8/1/18
3674 * @since ACF 5.6.5
3675 *
3676 * @param type $var Description. Default.
3677 * @return type Description.
3678 */
3679
3680 acf.val = function ($input, value, silent) {
3681 // vars
3682 var prevValue = $input.val();
3683
3684 // bail if no change
3685 if (value === prevValue) {
3686 return false;
3687 }
3688
3689 // update value
3690 $input.val(value);
3691
3692 // prevent select elements displaying blank value if option doesn't exist
3693 if ($input.is('select') && $input.val() === null) {
3694 $input.val(prevValue);
3695 return false;
3696 }
3697
3698 // update with trigger
3699 if (silent !== true) {
3700 $input.trigger('change');
3701 }
3702
3703 // return
3704 return true;
3705 };
3706
3707 /**
3708 * acf.show
3709 *
3710 * description
3711 *
3712 * @date 9/2/18
3713 * @since ACF 5.6.5
3714 *
3715 * @param type $var Description. Default.
3716 * @return type Description.
3717 */
3718
3719 acf.show = function ($el, lockKey) {
3720 // unlock
3721 if (lockKey) {
3722 acf.unlock($el, 'hidden', lockKey);
3723 }
3724
3725 // bail early if $el is still locked
3726 if (acf.isLocked($el, 'hidden')) {
3727 //console.log( 'still locked', getLocks( $el, 'hidden' ));
3728 return false;
3729 }
3730
3731 // $el is hidden, remove class and return true due to change in visibility
3732 if ($el.hasClass('acf-hidden')) {
3733 $el.removeClass('acf-hidden');
3734 return true;
3735
3736 // $el is visible, return false due to no change in visibility
3737 } else {
3738 return false;
3739 }
3740 };
3741
3742 /**
3743 * acf.hide
3744 *
3745 * description
3746 *
3747 * @date 9/2/18
3748 * @since ACF 5.6.5
3749 *
3750 * @param type $var Description. Default.
3751 * @return type Description.
3752 */
3753
3754 acf.hide = function ($el, lockKey) {
3755 // lock
3756 if (lockKey) {
3757 acf.lock($el, 'hidden', lockKey);
3758 }
3759
3760 // $el is hidden, return false due to no change in visibility
3761 if ($el.hasClass('acf-hidden')) {
3762 return false;
3763
3764 // $el is visible, add class and return true due to change in visibility
3765 } else {
3766 $el.addClass('acf-hidden');
3767 return true;
3768 }
3769 };
3770
3771 /**
3772 * acf.isHidden
3773 *
3774 * description
3775 *
3776 * @date 9/2/18
3777 * @since ACF 5.6.5
3778 *
3779 * @param type $var Description. Default.
3780 * @return type Description.
3781 */
3782
3783 acf.isHidden = function ($el) {
3784 return $el.hasClass('acf-hidden');
3785 };
3786
3787 /**
3788 * acf.isVisible
3789 *
3790 * description
3791 *
3792 * @date 9/2/18
3793 * @since ACF 5.6.5
3794 *
3795 * @param type $var Description. Default.
3796 * @return type Description.
3797 */
3798
3799 acf.isVisible = function ($el) {
3800 return !acf.isHidden($el);
3801 };
3802
3803 /**
3804 * enable
3805 *
3806 * description
3807 *
3808 * @date 12/3/18
3809 * @since ACF 5.6.9
3810 *
3811 * @param type $var Description. Default.
3812 * @return type Description.
3813 */
3814
3815 var enable = function ($el, lockKey) {
3816 // check class. Allow .acf-disabled to overrule all JS
3817 if ($el.hasClass('acf-disabled')) {
3818 return false;
3819 }
3820
3821 // unlock
3822 if (lockKey) {
3823 acf.unlock($el, 'disabled', lockKey);
3824 }
3825
3826 // bail early if $el is still locked
3827 if (acf.isLocked($el, 'disabled')) {
3828 return false;
3829 }
3830
3831 // $el is disabled, remove prop and return true due to change
3832 if ($el.prop('disabled')) {
3833 $el.prop('disabled', false);
3834 return true;
3835
3836 // $el is enabled, return false due to no change
3837 } else {
3838 return false;
3839 }
3840 };
3841
3842 /**
3843 * acf.enable
3844 *
3845 * description
3846 *
3847 * @date 9/2/18
3848 * @since ACF 5.6.5
3849 *
3850 * @param type $var Description. Default.
3851 * @return type Description.
3852 */
3853
3854 acf.enable = function ($el, lockKey) {
3855 // enable single input
3856 if ($el.attr('name')) {
3857 return enable($el, lockKey);
3858 }
3859
3860 // find and enable child inputs
3861 // return true if any inputs have changed
3862 var results = false;
3863 $el.find('[name]').each(function () {
3864 var result = enable($(this), lockKey);
3865 if (result) {
3866 results = true;
3867 }
3868 });
3869 return results;
3870 };
3871
3872 /**
3873 * disable
3874 *
3875 * description
3876 *
3877 * @date 12/3/18
3878 * @since ACF 5.6.9
3879 *
3880 * @param type $var Description. Default.
3881 * @return type Description.
3882 */
3883
3884 var disable = function ($el, lockKey) {
3885 // lock
3886 if (lockKey) {
3887 acf.lock($el, 'disabled', lockKey);
3888 }
3889
3890 // $el is disabled, return false due to no change
3891 if ($el.prop('disabled')) {
3892 return false;
3893
3894 // $el is enabled, add prop and return true due to change
3895 } else {
3896 $el.prop('disabled', true);
3897 return true;
3898 }
3899 };
3900
3901 /**
3902 * acf.disable
3903 *
3904 * description
3905 *
3906 * @date 9/2/18
3907 * @since ACF 5.6.5
3908 *
3909 * @param type $var Description. Default.
3910 * @return type Description.
3911 */
3912
3913 acf.disable = function ($el, lockKey) {
3914 // disable single input
3915 if ($el.attr('name')) {
3916 return disable($el, lockKey);
3917 }
3918
3919 // find and enable child inputs
3920 // return true if any inputs have changed
3921 var results = false;
3922 $el.find('[name]').each(function () {
3923 var result = disable($(this), lockKey);
3924 if (result) {
3925 results = true;
3926 }
3927 });
3928 return results;
3929 };
3930
3931 /**
3932 * acf.isset
3933 *
3934 * description
3935 *
3936 * @date 10/1/18
3937 * @since ACF 5.6.5
3938 *
3939 * @param type $var Description. Default.
3940 * @return type Description.
3941 */
3942
3943 acf.isset = function (obj /*, level1, level2, ... */) {
3944 for (var i = 1; i < arguments.length; i++) {
3945 if (!obj || !obj.hasOwnProperty(arguments[i])) {
3946 return false;
3947 }
3948 obj = obj[arguments[i]];
3949 }
3950 return true;
3951 };
3952
3953 /**
3954 * acf.isget
3955 *
3956 * description
3957 *
3958 * @date 10/1/18
3959 * @since ACF 5.6.5
3960 *
3961 * @param type $var Description. Default.
3962 * @return type Description.
3963 */
3964
3965 acf.isget = function (obj /*, level1, level2, ... */) {
3966 for (var i = 1; i < arguments.length; i++) {
3967 if (!obj || !obj.hasOwnProperty(arguments[i])) {
3968 return null;
3969 }
3970 obj = obj[arguments[i]];
3971 }
3972 return obj;
3973 };
3974
3975 /**
3976 * acf.getFileInputData
3977 *
3978 * description
3979 *
3980 * @date 10/1/18
3981 * @since ACF 5.6.5
3982 *
3983 * @param type $var Description. Default.
3984 * @return type Description.
3985 */
3986
3987 acf.getFileInputData = function ($input, callback) {
3988 // vars
3989 var value = $input.val();
3990
3991 // bail early if no value
3992 if (!value) {
3993 return false;
3994 }
3995
3996 // data
3997 var data = {
3998 url: value
3999 };
4000
4001 // modern browsers
4002 var file = $input[0].files.length ? acf.isget($input[0].files, 0) : false;
4003 if (file) {
4004 // update data
4005 data.size = file.size;
4006 data.type = file.type;
4007
4008 // image
4009 if (file.type.indexOf('image') > -1) {
4010 // vars
4011 var windowURL = window.URL || window.webkitURL;
4012 var img = new Image();
4013 img.onload = function () {
4014 // update
4015 data.width = this.width;
4016 data.height = this.height;
4017 callback(data);
4018 };
4019 img.src = windowURL.createObjectURL(file);
4020 } else {
4021 callback(data);
4022 }
4023 } else {
4024 callback(data);
4025 }
4026 };
4027
4028 /**
4029 * acf.isAjaxSuccess
4030 *
4031 * description
4032 *
4033 * @date 18/1/18
4034 * @since ACF 5.6.5
4035 *
4036 * @param type $var Description. Default.
4037 * @return type Description.
4038 */
4039
4040 acf.isAjaxSuccess = function (json) {
4041 return json && json.success;
4042 };
4043
4044 /**
4045 * acf.getAjaxMessage
4046 *
4047 * description
4048 *
4049 * @date 18/1/18
4050 * @since ACF 5.6.5
4051 *
4052 * @param type $var Description. Default.
4053 * @return type Description.
4054 */
4055
4056 acf.getAjaxMessage = function (json) {
4057 return acf.isget(json, 'data', 'message');
4058 };
4059
4060 /**
4061 * acf.getAjaxError
4062 *
4063 * description
4064 *
4065 * @date 18/1/18
4066 * @since ACF 5.6.5
4067 *
4068 * @param type $var Description. Default.
4069 * @return type Description.
4070 */
4071
4072 acf.getAjaxError = function (json) {
4073 return acf.isget(json, 'data', 'error');
4074 };
4075
4076 /**
4077 * Returns the error message from an XHR object.
4078 *
4079 * @date 17/3/20
4080 * @since ACF 5.8.9
4081 *
4082 * @param object xhr The XHR object.
4083 * @return (string)
4084 */
4085 acf.getXhrError = function (xhr) {
4086 if (xhr.responseJSON) {
4087 // Responses via `return new WP_Error();`
4088 if (xhr.responseJSON.message) {
4089 return xhr.responseJSON.message;
4090 }
4091
4092 // Responses via `wp_send_json_error();`.
4093 if (xhr.responseJSON.data && xhr.responseJSON.data.error) {
4094 return xhr.responseJSON.data.error;
4095 }
4096 } else if (xhr.statusText) {
4097 return xhr.statusText;
4098 }
4099 return '';
4100 };
4101
4102 /**
4103 * acf.renderSelect
4104 *
4105 * Renders the inner html for a select field.
4106 *
4107 * @date 19/2/18
4108 * @since ACF 5.6.9
4109 *
4110 * @param jQuery $select The select element.
4111 * @param array choices An array of choices.
4112 * @return void
4113 */
4114
4115 acf.renderSelect = function ($select, choices) {
4116 // vars
4117 var value = $select.val();
4118 var values = [];
4119
4120 // callback
4121 var crawl = function (items) {
4122 // vars
4123 var itemsHtml = '';
4124
4125 // loop
4126 items.map(function (item) {
4127 // vars
4128 var text = item.text || item.label || '';
4129 var id = item.id || item.value || '';
4130
4131 // append
4132 values.push(id);
4133
4134 // optgroup
4135 if (item.children) {
4136 itemsHtml += '<optgroup label="' + acf.escAttr(text) + '">' + crawl(item.children) + '</optgroup>';
4137
4138 // option
4139 } else {
4140 itemsHtml += '<option value="' + acf.escAttr(id) + '"' + (item.disabled ? ' disabled="disabled"' : '') + '>' + acf.strEscape(text) + '</option>';
4141 }
4142 });
4143 // return
4144 return itemsHtml;
4145 };
4146
4147 // update HTML
4148 $select.html(crawl(choices));
4149
4150 // update value
4151 if (values.indexOf(value) > -1) {
4152 $select.val(value);
4153 }
4154
4155 // return selected value
4156 return $select.val();
4157 };
4158
4159 /**
4160 * acf.lock
4161 *
4162 * Creates a "lock" on an element for a given type and key
4163 *
4164 * @date 22/2/18
4165 * @since ACF 5.6.9
4166 *
4167 * @param jQuery $el The element to lock.
4168 * @param string type The type of lock such as "condition" or "visibility".
4169 * @param string key The key that will be used to unlock.
4170 * @return void
4171 */
4172
4173 var getLocks = function ($el, type) {
4174 return $el.data('acf-lock-' + type) || [];
4175 };
4176 var setLocks = function ($el, type, locks) {
4177 $el.data('acf-lock-' + type, locks);
4178 };
4179 acf.lock = function ($el, type, key) {
4180 var locks = getLocks($el, type);
4181 var i = locks.indexOf(key);
4182 if (i < 0) {
4183 locks.push(key);
4184 setLocks($el, type, locks);
4185 }
4186 };
4187
4188 /**
4189 * acf.unlock
4190 *
4191 * Unlocks a "lock" on an element for a given type and key
4192 *
4193 * @date 22/2/18
4194 * @since ACF 5.6.9
4195 *
4196 * @param jQuery $el The element to lock.
4197 * @param string type The type of lock such as "condition" or "visibility".
4198 * @param string key The key that will be used to unlock.
4199 * @return void
4200 */
4201
4202 acf.unlock = function ($el, type, key) {
4203 var locks = getLocks($el, type);
4204 var i = locks.indexOf(key);
4205 if (i > -1) {
4206 locks.splice(i, 1);
4207 setLocks($el, type, locks);
4208 }
4209
4210 // return true if is unlocked (no locks)
4211 return locks.length === 0;
4212 };
4213
4214 /**
4215 * acf.isLocked
4216 *
4217 * Returns true if a lock exists for a given type
4218 *
4219 * @date 22/2/18
4220 * @since ACF 5.6.9
4221 *
4222 * @param jQuery $el The element to lock.
4223 * @param string type The type of lock such as "condition" or "visibility".
4224 * @return void
4225 */
4226
4227 acf.isLocked = function ($el, type) {
4228 return getLocks($el, type).length > 0;
4229 };
4230
4231 /**
4232 * acf.isGutenberg
4233 *
4234 * Returns true if the Gutenberg editor is being used.
4235 *
4236 * @since ACF 5.8.0
4237 *
4238 * @return bool
4239 */
4240 acf.isGutenberg = function () {
4241 return !!(window.wp && wp.data && wp.data.select && wp.data.select('core/editor'));
4242 };
4243
4244 /**
4245 * acf.isGutenbergPostEditor
4246 *
4247 * Returns true if the Gutenberg post editor is being used.
4248 *
4249 * @since ACF 6.2.2
4250 *
4251 * @return bool
4252 */
4253 acf.isGutenbergPostEditor = function () {
4254 return !!(window.wp && wp.data && wp.data.select && wp.data.select('core/edit-post'));
4255 };
4256
4257 /**
4258 * acf.objectToArray
4259 *
4260 * Returns an array of items from the given object.
4261 *
4262 * @date 20/11/18
4263 * @since ACF 5.8.0
4264 *
4265 * @param object obj The object of items.
4266 * @return array
4267 */
4268 acf.objectToArray = function (obj) {
4269 return Object.keys(obj).map(function (key) {
4270 return obj[key];
4271 });
4272 };
4273
4274 /**
4275 * acf.debounce
4276 *
4277 * Returns a debounced version of the passed function which will postpone its execution until after `wait` milliseconds have elapsed since the last time it was invoked.
4278 *
4279 * @date 28/8/19
4280 * @since ACF 5.8.1
4281 *
4282 * @param function callback The callback function.
4283 * @return int wait The number of milliseconds to wait.
4284 */
4285 acf.debounce = function (callback, wait) {
4286 var timeout;
4287 return function () {
4288 var context = this;
4289 var args = arguments;
4290 var later = function () {
4291 callback.apply(context, args);
4292 };
4293 clearTimeout(timeout);
4294 timeout = setTimeout(later, wait);
4295 };
4296 };
4297
4298 /**
4299 * acf.throttle
4300 *
4301 * Returns a throttled version of the passed function which will allow only one execution per `limit` time period.
4302 *
4303 * @date 28/8/19
4304 * @since ACF 5.8.1
4305 *
4306 * @param function callback The callback function.
4307 * @return int wait The number of milliseconds to wait.
4308 */
4309 acf.throttle = function (callback, limit) {
4310 var busy = false;
4311 return function () {
4312 if (busy) return;
4313 busy = true;
4314 setTimeout(function () {
4315 busy = false;
4316 }, limit);
4317 callback.apply(this, arguments);
4318 };
4319 };
4320
4321 /**
4322 * acf.isInView
4323 *
4324 * Returns true if the given element is in view.
4325 *
4326 * @date 29/8/19
4327 * @since ACF 5.8.1
4328 *
4329 * @param elem el The dom element to inspect.
4330 * @return bool
4331 */
4332 acf.isInView = function (el) {
4333 if (el instanceof jQuery) {
4334 el = el[0];
4335 }
4336 var rect = el.getBoundingClientRect();
4337 return rect.top !== rect.bottom && rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
4338 };
4339
4340 /**
4341 * acf.onceInView
4342 *
4343 * Watches for a dom element to become visible in the browser and then executes the passed callback.
4344 *
4345 * @date 28/8/19
4346 * @since ACF 5.8.1
4347 *
4348 * @param dom el The dom element to inspect.
4349 * @param function callback The callback function.
4350 */
4351 acf.onceInView = function () {
4352 // Define list.
4353 var items = [];
4354 var id = 0;
4355
4356 // Define check function.
4357 var check = function () {
4358 items.forEach(function (item) {
4359 if (acf.isInView(item.el)) {
4360 item.callback.apply(this);
4361 pop(item.id);
4362 }
4363 });
4364 };
4365
4366 // And create a debounced version.
4367 var debounced = acf.debounce(check, 300);
4368
4369 // Define add function.
4370 var push = function (el, callback) {
4371 // Add event listener.
4372 if (!items.length) {
4373 $(window).on('scroll resize', debounced).on('acfrefresh orientationchange', check);
4374 }
4375
4376 // Append to list.
4377 items.push({
4378 id: id++,
4379 el: el,
4380 callback: callback
4381 });
4382 };
4383
4384 // Define remove function.
4385 var pop = function (id) {
4386 // Remove from list.
4387 items = items.filter(function (item) {
4388 return item.id !== id;
4389 });
4390
4391 // Clean up listener.
4392 if (!items.length) {
4393 $(window).off('scroll resize', debounced).off('acfrefresh orientationchange', check);
4394 }
4395 };
4396
4397 // Define returned function.
4398 return function (el, callback) {
4399 // Allow jQuery object.
4400 if (el instanceof jQuery) el = el[0];
4401
4402 // Execute callback if already in view or add to watch list.
4403 if (acf.isInView(el)) {
4404 callback.apply(this);
4405 } else {
4406 push(el, callback);
4407 }
4408 };
4409 }();
4410
4411 /**
4412 * acf.once
4413 *
4414 * Creates a function that is restricted to invoking `func` once.
4415 *
4416 * @date 2/9/19
4417 * @since ACF 5.8.1
4418 *
4419 * @param function func The function to restrict.
4420 * @return function
4421 */
4422 acf.once = function (func) {
4423 var i = 0;
4424 return function () {
4425 if (i++ > 0) {
4426 return func = undefined;
4427 }
4428 return func.apply(this, arguments);
4429 };
4430 };
4431
4432 /**
4433 * Focuses attention to a specific element.
4434 *
4435 * @date 05/05/2020
4436 * @since ACF 5.9.0
4437 *
4438 * @param jQuery $el The jQuery element to focus.
4439 * @return void
4440 */
4441 acf.focusAttention = function ($el) {
4442 var wait = 1000;
4443
4444 // Apply class to focus attention.
4445 $el.addClass('acf-attention -focused');
4446
4447 // Scroll to element if needed.
4448 var scrollTime = 500;
4449 if (!acf.isInView($el)) {
4450 $('body, html').animate({
4451 scrollTop: $el.offset().top - $(window).height() / 2
4452 }, scrollTime);
4453 wait += scrollTime;
4454 }
4455
4456 // Remove class after $wait amount of time.
4457 var fadeTime = 250;
4458 setTimeout(function () {
4459 $el.removeClass('-focused');
4460 setTimeout(function () {
4461 $el.removeClass('acf-attention');
4462 }, fadeTime);
4463 }, wait);
4464 };
4465
4466 /**
4467 * Description
4468 *
4469 * @date 05/05/2020
4470 * @since ACF 5.9.0
4471 *
4472 * @param type Var Description.
4473 * @return type Description.
4474 */
4475 acf.onFocus = function ($el, callback) {
4476 // Only run once per element.
4477 // if( $el.data('acf.onFocus') ) {
4478 // return false;
4479 // }
4480
4481 // Vars.
4482 var ignoreBlur = false;
4483 var focus = false;
4484
4485 // Functions.
4486 var onFocus = function () {
4487 ignoreBlur = true;
4488 setTimeout(function () {
4489 ignoreBlur = false;
4490 }, 1);
4491 setFocus(true);
4492 };
4493 var onBlur = function () {
4494 if (!ignoreBlur) {
4495 setFocus(false);
4496 }
4497 };
4498 var addEvents = function () {
4499 $(document).on('click', onBlur);
4500 //$el.on('acfBlur', onBlur);
4501 $el.on('blur', 'input, select, textarea', onBlur);
4502 };
4503 var removeEvents = function () {
4504 $(document).off('click', onBlur);
4505 //$el.off('acfBlur', onBlur);
4506 $el.off('blur', 'input, select, textarea', onBlur);
4507 };
4508 var setFocus = function (value) {
4509 if (focus === value) {
4510 return;
4511 }
4512 if (value) {
4513 addEvents();
4514 } else {
4515 removeEvents();
4516 }
4517 focus = value;
4518 callback(value);
4519 };
4520
4521 // Add events and set data.
4522 $el.on('click', onFocus);
4523 //$el.on('acfFocus', onFocus);
4524 $el.on('focus', 'input, select, textarea', onFocus);
4525 //$el.data('acf.onFocus', true);
4526 };
4527
4528 /**
4529 * Disable form submit buttons
4530 *
4531 * @since ACF 6.2.3
4532 *
4533 * @param event e
4534 * @returns void
4535 */
4536 acf.disableForm = function (e) {
4537 // Disable submit button.
4538 if (e.submitter) e.submitter.classList.add('disabled');
4539 };
4540
4541 /*
4542 * exists
4543 *
4544 * This function will return true if a jQuery selection exists
4545 *
4546 * @type function
4547 * @date 8/09/2014
4548 * @since ACF 5.0.0
4549 *
4550 * @param n/a
4551 * @return (boolean)
4552 */
4553
4554 $.fn.exists = function () {
4555 return $(this).length > 0;
4556 };
4557
4558 /*
4559 * outerHTML
4560 *
4561 * This function will return a string containing the HTML of the selected element
4562 *
4563 * @type function
4564 * @date 19/11/2013
4565 * @since ACF 5.0.0
4566 *
4567 * @param $.fn
4568 * @return (string)
4569 */
4570
4571 $.fn.outerHTML = function () {
4572 return $(this).get(0).outerHTML;
4573 };
4574
4575 /*
4576 * indexOf
4577 *
4578 * This function will provide compatibility for ie8
4579 *
4580 * @type function
4581 * @date 5/3/17
4582 * @since ACF 5.5.10
4583 *
4584 * @param n/a
4585 * @return n/a
4586 */
4587
4588 if (!Array.prototype.indexOf) {
4589 Array.prototype.indexOf = function (val) {
4590 return $.inArray(val, this);
4591 };
4592 }
4593
4594 /**
4595 * Returns true if value is a number or a numeric string.
4596 *
4597 * @date 30/11/20
4598 * @since ACF 5.9.4
4599 * @link https://stackoverflow.com/questions/9716468/pure-javascript-a-function-like-jquerys-isnumeric/9716488#9716488
4600 *
4601 * @param mixed n The variable being evaluated.
4602 * @return bool.
4603 */
4604 acf.isNumeric = function (n) {
4605 return !isNaN(parseFloat(n)) && isFinite(n);
4606 };
4607
4608 /**
4609 * Triggers a "refresh" action used by various Components to redraw the DOM.
4610 *
4611 * @date 26/05/2020
4612 * @since ACF 5.9.0
4613 *
4614 * @param void
4615 * @return void
4616 */
4617 acf.refresh = acf.debounce(function () {
4618 $(window).trigger('acfrefresh');
4619 acf.doAction('refresh');
4620 }, 0);
4621
4622 /**
4623 * Log something to console if we're in debug mode.
4624 *
4625 * @since ACF 6.3
4626 */
4627 acf.debug = function () {
4628 if (acf.get('debug')) console.log.apply(null, arguments);
4629 };
4630
4631 // Set up actions from events
4632 $(document).ready(function () {
4633 acf.doAction('ready');
4634 });
4635 $(window).on('load', function () {
4636 // Use timeout to ensure action runs after Gutenberg has modified DOM elements during "DOMContentLoaded".
4637 setTimeout(function () {
4638 acf.doAction('load');
4639 });
4640 });
4641 $(window).on('beforeunload', function () {
4642 acf.doAction('unload');
4643 });
4644 $(window).on('resize', function () {
4645 acf.doAction('resize');
4646 });
4647 $(document).on('sortstart', function (event, ui) {
4648 acf.doAction('sortstart', ui.item, ui.placeholder);
4649 });
4650 $(document).on('sortstop', function (event, ui) {
4651 acf.doAction('sortstop', ui.item, ui.placeholder);
4652 });
4653 })(jQuery);
4654
4655 /***/ }),
4656
4657 /***/ "./node_modules/dompurify/dist/purify.cjs.js":
4658 /*!***************************************************!*\
4659 !*** ./node_modules/dompurify/dist/purify.cjs.js ***!
4660 \***************************************************/
4661 /***/ ((module) => {
4662
4663 "use strict";
4664 /*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE */
4665
4666
4667
4668 function _arrayLikeToArray(r, a) {
4669 (null == a || a > r.length) && (a = r.length);
4670 for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
4671 return n;
4672 }
4673 function _arrayWithHoles(r) {
4674 if (Array.isArray(r)) return r;
4675 }
4676 function _iterableToArrayLimit(r, l) {
4677 var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
4678 if (null != t) {
4679 var e,
4680 n,
4681 i,
4682 u,
4683 a = [],
4684 f = true,
4685 o = false;
4686 try {
4687 if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
4688 } catch (r) {
4689 o = true, n = r;
4690 } finally {
4691 try {
4692 if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
4693 } finally {
4694 if (o) throw n;
4695 }
4696 }
4697 return a;
4698 }
4699 }
4700 function _nonIterableRest() {
4701 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
4702 }
4703 function _slicedToArray(r, e) {
4704 return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
4705 }
4706 function _unsupportedIterableToArray(r, a) {
4707 if (r) {
4708 if ("string" == typeof r) return _arrayLikeToArray(r, a);
4709 var t = {}.toString.call(r).slice(8, -1);
4710 return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
4711 }
4712 }
4713
4714 const entries = Object.entries,
4715 setPrototypeOf = Object.setPrototypeOf,
4716 isFrozen = Object.isFrozen,
4717 getPrototypeOf = Object.getPrototypeOf,
4718 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
4719 let freeze = Object.freeze,
4720 seal = Object.seal,
4721 create = Object.create; // eslint-disable-line import/no-mutable-exports
4722 let _ref = typeof Reflect !== 'undefined' && Reflect,
4723 apply = _ref.apply,
4724 construct = _ref.construct;
4725 if (!freeze) {
4726 freeze = function freeze(x) {
4727 return x;
4728 };
4729 }
4730 if (!seal) {
4731 seal = function seal(x) {
4732 return x;
4733 };
4734 }
4735 if (!apply) {
4736 apply = function apply(func, thisArg) {
4737 for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
4738 args[_key - 2] = arguments[_key];
4739 }
4740 return func.apply(thisArg, args);
4741 };
4742 }
4743 if (!construct) {
4744 construct = function construct(Func) {
4745 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
4746 args[_key2 - 1] = arguments[_key2];
4747 }
4748 return new Func(...args);
4749 };
4750 }
4751 const arrayForEach = unapply(Array.prototype.forEach);
4752 const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
4753 const arrayPop = unapply(Array.prototype.pop);
4754 const arrayPush = unapply(Array.prototype.push);
4755 const arraySplice = unapply(Array.prototype.splice);
4756 const arrayIsArray = Array.isArray;
4757 const stringToLowerCase = unapply(String.prototype.toLowerCase);
4758 const stringToString = unapply(String.prototype.toString);
4759 const stringMatch = unapply(String.prototype.match);
4760 const stringReplace = unapply(String.prototype.replace);
4761 const stringIndexOf = unapply(String.prototype.indexOf);
4762 const stringTrim = unapply(String.prototype.trim);
4763 const numberToString = unapply(Number.prototype.toString);
4764 const booleanToString = unapply(Boolean.prototype.toString);
4765 const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
4766 const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
4767 const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
4768 const objectToString = unapply(Object.prototype.toString);
4769 const regExpTest = unapply(RegExp.prototype.test);
4770 const typeErrorCreate = unconstruct(TypeError);
4771 /**
4772 * Creates a new function that calls the given function with a specified thisArg and arguments.
4773 *
4774 * @param func - The function to be wrapped and called.
4775 * @returns A new function that calls the given function with a specified thisArg and arguments.
4776 */
4777 function unapply(func) {
4778 return function (thisArg) {
4779 if (thisArg instanceof RegExp) {
4780 thisArg.lastIndex = 0;
4781 }
4782 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
4783 args[_key3 - 1] = arguments[_key3];
4784 }
4785 return apply(func, thisArg, args);
4786 };
4787 }
4788 /**
4789 * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
4790 *
4791 * @param func - The constructor function to be wrapped and called.
4792 * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
4793 */
4794 function unconstruct(Func) {
4795 return function () {
4796 for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
4797 args[_key4] = arguments[_key4];
4798 }
4799 return construct(Func, args);
4800 };
4801 }
4802 /**
4803 * Add properties to a lookup table
4804 *
4805 * @param set - The set to which elements will be added.
4806 * @param array - The array containing elements to be added to the set.
4807 * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
4808 * @returns The modified set with added elements.
4809 */
4810 function addToSet(set, array) {
4811 let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
4812 if (setPrototypeOf) {
4813 // Make 'in' and truthy checks like Boolean(set.constructor)
4814 // independent of any properties defined on Object.prototype.
4815 // Prevent prototype setters from intercepting set as a this value.
4816 setPrototypeOf(set, null);
4817 }
4818 if (!arrayIsArray(array)) {
4819 return set;
4820 }
4821 let l = array.length;
4822 while (l--) {
4823 let element = array[l];
4824 if (typeof element === 'string') {
4825 const lcElement = transformCaseFunc(element);
4826 if (lcElement !== element) {
4827 // Config presets (e.g. tags.js, attrs.js) are immutable.
4828 if (!isFrozen(array)) {
4829 array[l] = lcElement;
4830 }
4831 element = lcElement;
4832 }
4833 }
4834 set[element] = true;
4835 }
4836 return set;
4837 }
4838 /**
4839 * Clean up an array to harden against CSPP
4840 *
4841 * @param array - The array to be cleaned.
4842 * @returns The cleaned version of the array
4843 */
4844 function cleanArray(array) {
4845 for (let index = 0; index < array.length; index++) {
4846 const isPropertyExist = objectHasOwnProperty(array, index);
4847 if (!isPropertyExist) {
4848 array[index] = null;
4849 }
4850 }
4851 return array;
4852 }
4853 /**
4854 * Shallow clone an object
4855 *
4856 * @param object - The object to be cloned.
4857 * @returns A new object that copies the original.
4858 */
4859 function clone(object) {
4860 const newObject = create(null);
4861 for (const _ref2 of entries(object)) {
4862 var _ref3 = _slicedToArray(_ref2, 2);
4863 const property = _ref3[0];
4864 const value = _ref3[1];
4865 const isPropertyExist = objectHasOwnProperty(object, property);
4866 if (isPropertyExist) {
4867 if (arrayIsArray(value)) {
4868 newObject[property] = cleanArray(value);
4869 } else if (value && typeof value === 'object' && value.constructor === Object) {
4870 newObject[property] = clone(value);
4871 } else {
4872 newObject[property] = value;
4873 }
4874 }
4875 }
4876 return newObject;
4877 }
4878 /**
4879 * Convert non-node values into strings without depending on direct property access.
4880 *
4881 * @param value - The value to stringify.
4882 * @returns A string representation of the provided value.
4883 */
4884 function stringifyValue(value) {
4885 switch (typeof value) {
4886 case 'string':
4887 {
4888 return value;
4889 }
4890 case 'number':
4891 {
4892 return numberToString(value);
4893 }
4894 case 'boolean':
4895 {
4896 return booleanToString(value);
4897 }
4898 case 'bigint':
4899 {
4900 return bigintToString ? bigintToString(value) : '0';
4901 }
4902 case 'symbol':
4903 {
4904 return symbolToString ? symbolToString(value) : 'Symbol()';
4905 }
4906 case 'undefined':
4907 {
4908 return objectToString(value);
4909 }
4910 case 'function':
4911 case 'object':
4912 {
4913 if (value === null) {
4914 return objectToString(value);
4915 }
4916 const valueAsRecord = value;
4917 const valueToString = lookupGetter(valueAsRecord, 'toString');
4918 if (typeof valueToString === 'function') {
4919 const stringified = valueToString(valueAsRecord);
4920 return typeof stringified === 'string' ? stringified : objectToString(stringified);
4921 }
4922 return objectToString(value);
4923 }
4924 default:
4925 {
4926 return objectToString(value);
4927 }
4928 }
4929 }
4930 /**
4931 * This method automatically checks if the prop is function or getter and behaves accordingly.
4932 *
4933 * @param object - The object to look up the getter function in its prototype chain.
4934 * @param prop - The property name for which to find the getter function.
4935 * @returns The getter function found in the prototype chain or a fallback function.
4936 */
4937 function lookupGetter(object, prop) {
4938 while (object !== null) {
4939 const desc = getOwnPropertyDescriptor(object, prop);
4940 if (desc) {
4941 if (desc.get) {
4942 return unapply(desc.get);
4943 }
4944 if (typeof desc.value === 'function') {
4945 return unapply(desc.value);
4946 }
4947 }
4948 object = getPrototypeOf(object);
4949 }
4950 function fallbackValue() {
4951 return null;
4952 }
4953 return fallbackValue;
4954 }
4955 function isRegex(value) {
4956 try {
4957 regExpTest(value, '');
4958 return true;
4959 } catch (_unused) {
4960 return false;
4961 }
4962 }
4963
4964 const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
4965 const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
4966 const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
4967 // List of SVG elements that are disallowed by default.
4968 // We still need to know them so that we can do namespace
4969 // checks properly in case one wants to add them to
4970 // allow-list.
4971 const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
4972 const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
4973 // Similarly to SVG, we want to know all MathML elements,
4974 // even those that we disallow by default.
4975 const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
4976 const text = freeze(['#text']);
4977
4978 const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
4979 const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
4980 const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
4981 const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
4982
4983 const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
4984 const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
4985 const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
4986 const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
4987 const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
4988 const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
4989 );
4990 const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
4991 const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
4992 );
4993 const DOCTYPE_NAME = seal(/^html$/i);
4994 const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
4995 // Markup-significant character probes used by _sanitizeElements.
4996 // Shared module-level instances are safe despite the sticky /g flags:
4997 // unapply() resets lastIndex for RegExp receivers before every call.
4998 const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
4999 const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
5000 const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
5001 const SELF_CLOSING_TAG = seal(/\/>/i);
5002
5003 // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
5004 const NODE_TYPE = {
5005 element: 1,
5006 attribute: 2,
5007 text: 3,
5008 cdataSection: 4,
5009 entityReference: 5,
5010 // Deprecated
5011 entityNode: 6,
5012 // Deprecated
5013 processingInstruction: 7,
5014 comment: 8,
5015 document: 9,
5016 documentType: 10,
5017 documentFragment: 11,
5018 notation: 12 // Deprecated
5019 };
5020 const getGlobal = function getGlobal() {
5021 return typeof window === 'undefined' ? null : window;
5022 };
5023 /**
5024 * Creates a no-op policy for internal use only.
5025 * Don't export this function outside this module!
5026 * @param trustedTypes The policy factory.
5027 * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
5028 * @return The policy created (or null, if Trusted Types
5029 * are not supported or creating the policy failed).
5030 */
5031 const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
5032 if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
5033 return null;
5034 }
5035 // Allow the callers to control the unique policy name
5036 // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
5037 // Policy creation with duplicate names throws in Trusted Types.
5038 let suffix = null;
5039 const ATTR_NAME = 'data-tt-policy-suffix';
5040 if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
5041 suffix = purifyHostElement.getAttribute(ATTR_NAME);
5042 }
5043 const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
5044 try {
5045 return trustedTypes.createPolicy(policyName, {
5046 createHTML(html) {
5047 return html;
5048 },
5049 createScriptURL(scriptUrl) {
5050 return scriptUrl;
5051 }
5052 });
5053 } catch (_) {
5054 // Policy creation failed (most likely another DOMPurify script has
5055 // already run). Skip creating the policy, as this will only cause errors
5056 // if TT are enforced.
5057 console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
5058 return null;
5059 }
5060 };
5061 const _createHooksMap = function _createHooksMap() {
5062 return {
5063 afterSanitizeAttributes: [],
5064 afterSanitizeElements: [],
5065 afterSanitizeShadowDOM: [],
5066 beforeSanitizeAttributes: [],
5067 beforeSanitizeElements: [],
5068 beforeSanitizeShadowDOM: [],
5069 uponSanitizeAttribute: [],
5070 uponSanitizeElement: [],
5071 uponSanitizeShadowNode: []
5072 };
5073 };
5074 /**
5075 * Resolve a set-valued configuration option: a fresh set built from
5076 * cfg[key] when it is an own array property (seeded with a clone of
5077 * options.base when given, case-normalized via options.transform),
5078 * the fallback set otherwise.
5079 *
5080 * @param cfg the cloned, prototype-free configuration object
5081 * @param key the configuration property to read
5082 * @param fallback the set to use when the option is absent or not an array
5083 * @param options transform and optional base set to merge into
5084 * @returns the resolved set
5085 */
5086 const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
5087 return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
5088 };
5089 function createDOMPurify() {
5090 let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
5091 const DOMPurify = root => createDOMPurify(root);
5092 DOMPurify.version = '3.4.11';
5093 DOMPurify.removed = [];
5094 if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
5095 // Not running in a browser, provide a factory function
5096 // so that you can pass your own Window
5097 DOMPurify.isSupported = false;
5098 return DOMPurify;
5099 }
5100 let document = window.document;
5101 const originalDocument = document;
5102 const currentScript = originalDocument.currentScript;
5103 window.DocumentFragment;
5104 const HTMLTemplateElement = window.HTMLTemplateElement,
5105 Node = window.Node,
5106 Element = window.Element,
5107 NodeFilter = window.NodeFilter,
5108 _window$NamedNodeMap = window.NamedNodeMap;
5109 _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;
5110 window.HTMLFormElement;
5111 const DOMParser = window.DOMParser,
5112 trustedTypes = window.trustedTypes;
5113 const ElementPrototype = Element.prototype;
5114 const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
5115 const remove = lookupGetter(ElementPrototype, 'remove');
5116 const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
5117 const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
5118 const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
5119 const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
5120 const getAttributes = lookupGetter(ElementPrototype, 'attributes');
5121 const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
5122 const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
5123 // As per issue #47, the web-components registry is inherited by a
5124 // new document created via createHTMLDocument. As per the spec
5125 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
5126 // a new empty registry is used when creating a template contents owner
5127 // document, so we use that as our parent document to ensure nothing
5128 // is inherited.
5129 if (typeof HTMLTemplateElement === 'function') {
5130 const template = document.createElement('template');
5131 if (template.content && template.content.ownerDocument) {
5132 document = template.content.ownerDocument;
5133 }
5134 }
5135 let trustedTypesPolicy;
5136 let emptyHTML = '';
5137 // The instance's own internal Trusted Types policy. Unlike a caller-supplied
5138 // `TRUSTED_TYPES_POLICY`, this is created at most once — Trusted Types throws
5139 // on duplicate policy names — and is the only policy allowed to persist
5140 // across configurations and survive `clearConfig()`.
5141 let defaultTrustedTypesPolicy;
5142 let defaultTrustedTypesPolicyResolved = false;
5143 // Tracks whether we are already inside a call to the configured Trusted Types
5144 // policy (`createHTML` or `createScriptURL`). If a supplied policy callback
5145 // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would
5146 // re-enter the policy and recurse until the stack overflows. We detect that
5147 // re-entry and throw a clear, actionable error instead. The guard is shared
5148 // across both callbacks, because either one re-entering `sanitize` triggers
5149 // the same unbounded recursion.
5150 let IN_TRUSTED_TYPES_POLICY = 0;
5151 const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {
5152 if (IN_TRUSTED_TYPES_POLICY > 0) {
5153 throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or ' + 'createScriptURL) must not call DOMPurify.sanitize, as that causes ' + 'infinite recursion. Do not pass a policy whose callbacks wrap ' + 'DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted ' + 'Types" section of the README.');
5154 }
5155 };
5156 const _createTrustedHTML = function _createTrustedHTML(html) {
5157 _assertNotInTrustedTypesPolicy();
5158 IN_TRUSTED_TYPES_POLICY++;
5159 try {
5160 return trustedTypesPolicy.createHTML(html);
5161 } finally {
5162 IN_TRUSTED_TYPES_POLICY--;
5163 }
5164 };
5165 const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {
5166 _assertNotInTrustedTypesPolicy();
5167 IN_TRUSTED_TYPES_POLICY++;
5168 try {
5169 return trustedTypesPolicy.createScriptURL(scriptUrl);
5170 } finally {
5171 IN_TRUSTED_TYPES_POLICY--;
5172 }
5173 };
5174 // Lazily resolve (and cache) the instance's internal default policy.
5175 // Resolution is attempted at most once: a successful `createPolicy` cannot be
5176 // repeated (Trusted Types throws on duplicate names), and a failed or
5177 // unsupported attempt must not be retried on every parse.
5178 const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {
5179 if (!defaultTrustedTypesPolicyResolved) {
5180 defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
5181 defaultTrustedTypesPolicyResolved = true;
5182 }
5183 return defaultTrustedTypesPolicy;
5184 };
5185 const _document = document,
5186 implementation = _document.implementation,
5187 createNodeIterator = _document.createNodeIterator,
5188 createDocumentFragment = _document.createDocumentFragment,
5189 getElementsByTagName = _document.getElementsByTagName;
5190 const importNode = originalDocument.importNode;
5191 let hooks = _createHooksMap();
5192 /**
5193 * Expose whether this browser supports running the full DOMPurify.
5194 */
5195 DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
5196 const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
5197 ERB_EXPR$1 = ERB_EXPR,
5198 TMPLIT_EXPR$1 = TMPLIT_EXPR,
5199 DATA_ATTR$1 = DATA_ATTR,
5200 ARIA_ATTR$1 = ARIA_ATTR,
5201 IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
5202 ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
5203 CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
5204 let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
5205 /**
5206 * We consider the elements and attributes below to be safe. Ideally
5207 * don't add any new ones but feel free to remove unwanted ones.
5208 */
5209 /* allowed element names */
5210 let ALLOWED_TAGS = null;
5211 const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
5212 /* Allowed attribute names */
5213 let ALLOWED_ATTR = null;
5214 const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
5215 /*
5216 * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
5217 * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
5218 * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
5219 * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
5220 */
5221 let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
5222 tagNameCheck: {
5223 writable: true,
5224 configurable: false,
5225 enumerable: true,
5226 value: null
5227 },
5228 attributeNameCheck: {
5229 writable: true,
5230 configurable: false,
5231 enumerable: true,
5232 value: null
5233 },
5234 allowCustomizedBuiltInElements: {
5235 writable: true,
5236 configurable: false,
5237 enumerable: true,
5238 value: false
5239 }
5240 }));
5241 /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
5242 let FORBID_TAGS = null;
5243 /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
5244 let FORBID_ATTR = null;
5245 /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
5246 const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
5247 tagCheck: {
5248 writable: true,
5249 configurable: false,
5250 enumerable: true,
5251 value: null
5252 },
5253 attributeCheck: {
5254 writable: true,
5255 configurable: false,
5256 enumerable: true,
5257 value: null
5258 }
5259 }));
5260 /* Decide if ARIA attributes are okay */
5261 let ALLOW_ARIA_ATTR = true;
5262 /* Decide if custom data attributes are okay */
5263 let ALLOW_DATA_ATTR = true;
5264 /* Decide if unknown protocols are okay */
5265 let ALLOW_UNKNOWN_PROTOCOLS = false;
5266 /* Decide if self-closing tags in attributes are allowed.
5267 * Usually removed due to a mXSS issue in jQuery 3.0 */
5268 let ALLOW_SELF_CLOSE_IN_ATTR = true;
5269 /* Output should be safe for common template engines.
5270 * This means, DOMPurify removes data attributes, mustaches and ERB
5271 */
5272 let SAFE_FOR_TEMPLATES = false;
5273 /* Output should be safe even for XML used within HTML and alike.
5274 * This means, DOMPurify removes comments when containing risky content.
5275 */
5276 let SAFE_FOR_XML = true;
5277 /* Decide if document with <html>... should be returned */
5278 let WHOLE_DOCUMENT = false;
5279 /* Track whether config is already set on this instance of DOMPurify. */
5280 let SET_CONFIG = false;
5281 /* Pristine allowlist bindings captured at setConfig() time. On the
5282 * persistent-config path sanitize() restores the sets from these before
5283 * the per-walk hook clone-guard, so a hook's in-call widening cannot
5284 * carry across calls. Null until setConfig() is called; reset by
5285 * clearConfig(). */
5286 let SET_CONFIG_ALLOWED_TAGS = null;
5287 let SET_CONFIG_ALLOWED_ATTR = null;
5288 /* Decide if all elements (e.g. style, script) must be children of
5289 * document.body. By default, browsers might move them to document.head */
5290 let FORCE_BODY = false;
5291 /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
5292 * string (or a TrustedHTML object if Trusted Types are supported).
5293 * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
5294 */
5295 let RETURN_DOM = false;
5296 /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
5297 * string (or a TrustedHTML object if Trusted Types are supported) */
5298 let RETURN_DOM_FRAGMENT = false;
5299 /* Try to return a Trusted Type object instead of a string, return a string in
5300 * case Trusted Types are not supported */
5301 let RETURN_TRUSTED_TYPE = false;
5302 /* Output should be free from DOM clobbering attacks?
5303 * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
5304 */
5305 let SANITIZE_DOM = true;
5306 /* Achieve full DOM Clobbering protection by isolating the namespace of named
5307 * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
5308 *
5309 * HTML/DOM spec rules that enable DOM Clobbering:
5310 * - Named Access on Window (§7.3.3)
5311 * - DOM Tree Accessors (§3.1.5)
5312 * - Form Element Parent-Child Relations (§4.10.3)
5313 * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
5314 * - HTMLCollection (§4.2.10.2)
5315 *
5316 * Namespace isolation is implemented by prefixing `id` and `name` attributes
5317 * with a constant string, i.e., `user-content-`
5318 */
5319 let SANITIZE_NAMED_PROPS = false;
5320 const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
5321 /* Keep element content when removing element? */
5322 let KEEP_CONTENT = true;
5323 /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
5324 * of importing it into a new Document and returning a sanitized copy */
5325 let IN_PLACE = false;
5326 /* Allow usage of profiles like html, svg and mathMl */
5327 let USE_PROFILES = {};
5328 /* Tags to ignore content of when KEEP_CONTENT is true */
5329 let FORBID_CONTENTS = null;
5330 const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script',
5331 // <selectedcontent> mirrors the selected <option>'s subtree, cloned by
5332 // the UA (customizable <select>) — including any on* handlers — and the
5333 // engine re-mirrors synchronously whenever a removal changes which
5334 // option/selectedcontent is current, even inside DOMPurify's inert
5335 // DOMParser document. Hoisting its children on removal re-inserts a fresh
5336 // mirror target ahead of the walk, which the engine refills, looping
5337 // forever (DoS) and amplifying output. Dropping its content on removal
5338 // (rather than hoisting) breaks that cascade; the content is a duplicate
5339 // of the option, which is sanitized on its own. See campaign-3 F1/F6.
5340 'selectedcontent', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
5341 /* Tags that are safe for data: URIs */
5342 let DATA_URI_TAGS = null;
5343 const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
5344 /* Attributes safe for values like "javascript:" */
5345 let URI_SAFE_ATTRIBUTES = null;
5346 const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
5347 const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
5348 const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
5349 const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
5350 /* Document namespace */
5351 let NAMESPACE = HTML_NAMESPACE;
5352 let IS_EMPTY_INPUT = false;
5353 /* Allowed XHTML+XML namespaces */
5354 let ALLOWED_NAMESPACES = null;
5355 const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
5356 const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(['mi', 'mo', 'mn', 'ms', 'mtext']);
5357 let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
5358 const DEFAULT_HTML_INTEGRATION_POINTS = freeze(['annotation-xml']);
5359 let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
5360 // Certain elements are allowed in both SVG and HTML
5361 // namespace. We need to specify them explicitly
5362 // so that they don't get erroneously deleted from
5363 // HTML namespace.
5364 const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
5365 /* Parsing of strict XHTML documents */
5366 let PARSER_MEDIA_TYPE = null;
5367 const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
5368 const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
5369 let transformCaseFunc = null;
5370 /* Keep a reference to config to pass to hooks */
5371 let CONFIG = null;
5372 /* Ideally, do not touch anything below this line */
5373 /* ______________________________________________ */
5374 const formElement = document.createElement('form');
5375 const isRegexOrFunction = function isRegexOrFunction(testValue) {
5376 return testValue instanceof RegExp || testValue instanceof Function;
5377 };
5378 /**
5379 * _parseConfig
5380 *
5381 * @param cfg optional config literal
5382 */
5383 // eslint-disable-next-line complexity
5384 const _parseConfig = function _parseConfig() {
5385 let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
5386 if (CONFIG && CONFIG === cfg) {
5387 return;
5388 }
5389 /* Shield configuration object from tampering */
5390 if (!cfg || typeof cfg !== 'object') {
5391 cfg = {};
5392 }
5393 /* Shield configuration object from prototype pollution */
5394 cfg = clone(cfg);
5395 PARSER_MEDIA_TYPE =
5396 // eslint-disable-next-line unicorn/prefer-includes
5397 SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
5398 // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
5399 transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
5400 /* Set configuration parameters */
5401 ALLOWED_TAGS = _resolveSetOption(cfg, 'ALLOWED_TAGS', DEFAULT_ALLOWED_TAGS, {
5402 transform: transformCaseFunc
5403 });
5404 ALLOWED_ATTR = _resolveSetOption(cfg, 'ALLOWED_ATTR', DEFAULT_ALLOWED_ATTR, {
5405 transform: transformCaseFunc
5406 });
5407 ALLOWED_NAMESPACES = _resolveSetOption(cfg, 'ALLOWED_NAMESPACES', DEFAULT_ALLOWED_NAMESPACES, {
5408 transform: stringToString
5409 });
5410 URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, 'ADD_URI_SAFE_ATTR', DEFAULT_URI_SAFE_ATTRIBUTES, {
5411 transform: transformCaseFunc,
5412 base: DEFAULT_URI_SAFE_ATTRIBUTES
5413 });
5414 DATA_URI_TAGS = _resolveSetOption(cfg, 'ADD_DATA_URI_TAGS', DEFAULT_DATA_URI_TAGS, {
5415 transform: transformCaseFunc,
5416 base: DEFAULT_DATA_URI_TAGS
5417 });
5418 FORBID_CONTENTS = _resolveSetOption(cfg, 'FORBID_CONTENTS', DEFAULT_FORBID_CONTENTS, {
5419 transform: transformCaseFunc
5420 });
5421 FORBID_TAGS = _resolveSetOption(cfg, 'FORBID_TAGS', clone({}), {
5422 transform: transformCaseFunc
5423 });
5424 FORBID_ATTR = _resolveSetOption(cfg, 'FORBID_ATTR', clone({}), {
5425 transform: transformCaseFunc
5426 });
5427 USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
5428 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
5429 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
5430 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
5431 ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
5432 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
5433 SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
5434 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
5435 RETURN_DOM = cfg.RETURN_DOM || false; // Default false
5436 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
5437 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
5438 FORCE_BODY = cfg.FORCE_BODY || false; // Default false
5439 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
5440 SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
5441 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
5442 IN_PLACE = cfg.IN_PLACE || false; // Default false
5443 IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
5444 NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
5445 MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS); // Default built-in map
5446 HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS); // Default built-in map
5447 const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
5448 CUSTOM_ELEMENT_HANDLING = create(null);
5449 if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
5450 CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
5451 }
5452 if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
5453 CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
5454 }
5455 if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
5456 CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
5457 }
5458 seal(CUSTOM_ELEMENT_HANDLING);
5459 if (SAFE_FOR_TEMPLATES) {
5460 ALLOW_DATA_ATTR = false;
5461 }
5462 if (RETURN_DOM_FRAGMENT) {
5463 RETURN_DOM = true;
5464 }
5465 /* Parse profile info */
5466 if (USE_PROFILES) {
5467 ALLOWED_TAGS = addToSet({}, text);
5468 ALLOWED_ATTR = create(null);
5469 if (USE_PROFILES.html === true) {
5470 addToSet(ALLOWED_TAGS, html$1);
5471 addToSet(ALLOWED_ATTR, html);
5472 }
5473 if (USE_PROFILES.svg === true) {
5474 addToSet(ALLOWED_TAGS, svg$1);
5475 addToSet(ALLOWED_ATTR, svg);
5476 addToSet(ALLOWED_ATTR, xml);
5477 }
5478 if (USE_PROFILES.svgFilters === true) {
5479 addToSet(ALLOWED_TAGS, svgFilters);
5480 addToSet(ALLOWED_ATTR, svg);
5481 addToSet(ALLOWED_ATTR, xml);
5482 }
5483 if (USE_PROFILES.mathMl === true) {
5484 addToSet(ALLOWED_TAGS, mathMl$1);
5485 addToSet(ALLOWED_ATTR, mathMl);
5486 addToSet(ALLOWED_ATTR, xml);
5487 }
5488 }
5489 /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
5490 * leaking across calls when switching from function to array config */
5491 EXTRA_ELEMENT_HANDLING.tagCheck = null;
5492 EXTRA_ELEMENT_HANDLING.attributeCheck = null;
5493 /* Merge configuration parameters */
5494 if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
5495 if (typeof cfg.ADD_TAGS === 'function') {
5496 EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
5497 } else if (arrayIsArray(cfg.ADD_TAGS)) {
5498 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
5499 ALLOWED_TAGS = clone(ALLOWED_TAGS);
5500 }
5501 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
5502 }
5503 }
5504 if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
5505 if (typeof cfg.ADD_ATTR === 'function') {
5506 EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
5507 } else if (arrayIsArray(cfg.ADD_ATTR)) {
5508 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
5509 ALLOWED_ATTR = clone(ALLOWED_ATTR);
5510 }
5511 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
5512 }
5513 }
5514 if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
5515 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
5516 }
5517 if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
5518 if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
5519 FORBID_CONTENTS = clone(FORBID_CONTENTS);
5520 }
5521 addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
5522 }
5523 if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
5524 if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
5525 FORBID_CONTENTS = clone(FORBID_CONTENTS);
5526 }
5527 addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
5528 }
5529 /* Add #text in case KEEP_CONTENT is set to true */
5530 if (KEEP_CONTENT) {
5531 ALLOWED_TAGS['#text'] = true;
5532 }
5533 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
5534 if (WHOLE_DOCUMENT) {
5535 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
5536 }
5537 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
5538 if (ALLOWED_TAGS.table) {
5539 addToSet(ALLOWED_TAGS, ['tbody']);
5540 delete FORBID_TAGS.tbody;
5541 }
5542 // Re-derive the active Trusted Types policy from this configuration on
5543 // every parse. The active policy must never be sticky closure state that
5544 // outlives the config that set it: a caller-supplied policy left in place
5545 // after `clearConfig()` — or after a later call that supplied none, or
5546 // `TRUSTED_TYPES_POLICY: null` — could sign a subsequent "default"
5547 // `RETURN_TRUSTED_TYPE` result with a foreign, possibly unsafe policy.
5548 // See GHSA-vxr8-fq34-vvx9.
5549 if (cfg.TRUSTED_TYPES_POLICY) {
5550 if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
5551 throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
5552 }
5553 if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
5554 throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
5555 }
5556 // A caller-supplied policy applies to this configuration only.
5557 const previousTrustedTypesPolicy = trustedTypesPolicy;
5558 trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
5559 // Sign local variables required by `sanitize`. If the supplied policy's
5560 // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this
5561 // throws via the re-entrancy guard. Restore the previous policy first so
5562 // the instance is not left in a poisoned state. See #1422.
5563 try {
5564 emptyHTML = _createTrustedHTML('');
5565 } catch (error) {
5566 trustedTypesPolicy = previousTrustedTypesPolicy;
5567 throw error;
5568 }
5569 } else if (cfg.TRUSTED_TYPES_POLICY === null) {
5570 // Explicit opt-out for this call: perform no Trusted Types signing and
5571 // create nothing (so a strict `trusted-types` CSP that disallows a
5572 // `dompurify` policy can still call `sanitize` from inside its own
5573 // policy — see #1422). Resetting to `undefined` rather than a sticky
5574 // `null` also drops any previously retained caller policy, so it cannot
5575 // resurface on a later call, while still allowing the next config-less
5576 // call to restore the internal default policy. See GHSA-vxr8-fq34-vvx9.
5577 trustedTypesPolicy = undefined;
5578 emptyHTML = '';
5579 } else {
5580 // No policy supplied: keep the currently active policy if one is set — a
5581 // previously supplied policy is intentionally sticky across config-less
5582 // calls — otherwise fall back to the instance's own internal policy,
5583 // created at most once. (A policy supplied for a *single* call still
5584 // lingers by design; what must not linger is a policy whose configuration
5585 // has been torn down via `clearConfig()`, which restores the default.)
5586 if (trustedTypesPolicy === undefined) {
5587 trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
5588 }
5589 // Sign internal variables only when a policy is active. A falsy policy
5590 // (Trusted Types unsupported, creation failed, or an explicit opt-out)
5591 // leaves `emptyHTML` as a plain string, so we never call `.createHTML` on
5592 // a non-policy and throw. See #1422.
5593 if (trustedTypesPolicy && typeof emptyHTML === 'string') {
5594 emptyHTML = _createTrustedHTML('');
5595 }
5596 }
5597 // Prevent further manipulation of configuration.
5598 // Not available in IE8, Safari 5, etc.
5599 if (freeze) {
5600 freeze(cfg);
5601 }
5602 CONFIG = cfg;
5603 };
5604 /* Keep track of all possible SVG and MathML tags
5605 * so that we can perform the namespace checks
5606 * correctly. */
5607 const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
5608 const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
5609 /**
5610 * Namespace rules for an element in the SVG namespace.
5611 *
5612 * @param tagName the element's lowercase tag name
5613 * @param parent the (possibly simulated) parent node
5614 * @param parentTagName the parent's lowercase tag name
5615 * @returns true if a spec-compliant parser could produce this element
5616 */
5617 const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
5618 // The only way to switch from HTML namespace to SVG
5619 // is via <svg>. If it happens via any other tag, then
5620 // it should be killed.
5621 if (parent.namespaceURI === HTML_NAMESPACE) {
5622 return tagName === 'svg';
5623 }
5624 // The only way to switch from MathML to SVG is via <svg>
5625 // if the parent is either <annotation-xml> or a MathML
5626 // text integration point.
5627 if (parent.namespaceURI === MATHML_NAMESPACE) {
5628 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
5629 }
5630 // We only allow elements that are defined in SVG
5631 // spec. All others are disallowed in SVG namespace.
5632 return Boolean(ALL_SVG_TAGS[tagName]);
5633 };
5634 /**
5635 * Namespace rules for an element in the MathML namespace.
5636 *
5637 * @param tagName the element's lowercase tag name
5638 * @param parent the (possibly simulated) parent node
5639 * @param parentTagName the parent's lowercase tag name
5640 * @returns true if a spec-compliant parser could produce this element
5641 */
5642 const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
5643 // The only way to switch from HTML namespace to MathML
5644 // is via <math>. If it happens via any other tag, then
5645 // it should be killed.
5646 if (parent.namespaceURI === HTML_NAMESPACE) {
5647 return tagName === 'math';
5648 }
5649 // The only way to switch from SVG to MathML is via
5650 // <math> and HTML integration points
5651 if (parent.namespaceURI === SVG_NAMESPACE) {
5652 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
5653 }
5654 // We only allow elements that are defined in MathML
5655 // spec. All others are disallowed in MathML namespace.
5656 return Boolean(ALL_MATHML_TAGS[tagName]);
5657 };
5658 /**
5659 * Namespace rules for an element in the HTML namespace.
5660 *
5661 * @param tagName the element's lowercase tag name
5662 * @param parent the (possibly simulated) parent node
5663 * @param parentTagName the parent's lowercase tag name
5664 * @returns true if a spec-compliant parser could produce this element
5665 */
5666 const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
5667 // The only way to switch from SVG to HTML is via
5668 // HTML integration points, and from MathML to HTML
5669 // is via MathML text integration points
5670 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
5671 return false;
5672 }
5673 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
5674 return false;
5675 }
5676 // We disallow tags that are specific for MathML
5677 // or SVG and should never appear in HTML namespace
5678 return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
5679 };
5680 /**
5681 * @param element a DOM element whose namespace is being checked
5682 * @returns Return false if the element has a
5683 * namespace that a spec-compliant parser would never
5684 * return. Return true otherwise.
5685 */
5686 const _checkValidNamespace = function _checkValidNamespace(element) {
5687 let parent = getParentNode(element);
5688 // In JSDOM, if we're inside shadow DOM, then parentNode
5689 // can be null. We just simulate parent in this case.
5690 if (!parent || !parent.tagName) {
5691 parent = {
5692 namespaceURI: NAMESPACE,
5693 tagName: 'template'
5694 };
5695 }
5696 const tagName = stringToLowerCase(element.tagName);
5697 const parentTagName = stringToLowerCase(parent.tagName);
5698 if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
5699 return false;
5700 }
5701 if (element.namespaceURI === SVG_NAMESPACE) {
5702 return _checkSvgNamespace(tagName, parent, parentTagName);
5703 }
5704 if (element.namespaceURI === MATHML_NAMESPACE) {
5705 return _checkMathMlNamespace(tagName, parent, parentTagName);
5706 }
5707 if (element.namespaceURI === HTML_NAMESPACE) {
5708 return _checkHtmlNamespace(tagName, parent, parentTagName);
5709 }
5710 // For XHTML and XML documents that support custom namespaces
5711 if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
5712 return true;
5713 }
5714 // The code should never reach this place (this means
5715 // that the element somehow got namespace that is not
5716 // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
5717 // Return false just in case.
5718 return false;
5719 };
5720 /**
5721 * _forceRemove
5722 *
5723 * @param node a DOM node
5724 */
5725 const _forceRemove = function _forceRemove(node) {
5726 arrayPush(DOMPurify.removed, {
5727 element: node
5728 });
5729 try {
5730 // eslint-disable-next-line unicorn/prefer-dom-node-remove
5731 getParentNode(node).removeChild(node);
5732 } catch (_) {
5733 /* The normal detach failed — this is reached for a parentless node
5734 (getParentNode() is null, so .removeChild throws). Element.prototype
5735 .remove() is itself a spec no-op on a parentless node, so a recorded
5736 "removal" would otherwise hand the caller back an intact,
5737 payload-bearing node (e.g. a detached IN_PLACE root the mXSS canary or
5738 the style-with-element-child rule decided to kill). Fail closed by
5739 throwing — exactly as a clobbered root does at the IN_PLACE entry —
5740 rather than trying to "neutralize" the node via its own methods.
5741 Neutralizing would mean calling getAttributeNames()/removeAttribute()
5742 on the node, both of which a <form> root can clobber via a named child
5743 (and _isClobbered does not even probe getAttributeNames), so the
5744 neutralize step could itself be silently defeated, leaving the payload
5745 intact. A throw touches only the cached, clobber-safe remove() and
5746 getParentNode(). Generalizes GHSA-r47g-fvhr-h676 (clobbered-form root)
5747 to every root-kill reason. REPORT-3.
5748 This lives inside the catch, so it never fires for a normally-removed
5749 in-tree node: those have a parent, removeChild() succeeds, and the
5750 catch is not entered. Only a kept (parentless) root reaches here. */
5751 remove(node);
5752 if (!getParentNode(node)) {
5753 throw typeErrorCreate('a node selected for removal could not be detached from its tree ' + 'and cannot be safely returned; refusing to sanitize in place');
5754 }
5755 }
5756 };
5757 /**
5758 * _neutralizeRoot
5759 *
5760 * Fail-closed teardown of an in-place root after the sanitize walk aborts
5761 * (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
5762 * custom element's reaction detaches a node so `_forceRemove`'s deliberate
5763 * parentless guard throws, or any other re-entrant engine mutation — would
5764 * otherwise leave the caller's *live* tree half-sanitized, with everything
5765 * after the abort point still carrying its handlers. There is no safe way
5766 * to resume the walk (the tree mutated under us), so we strip the root bare:
5767 * remove every child and every attribute, then let the caller's catch see
5768 * the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
5769 * getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
5770 *
5771 * @param root the in-place root to empty
5772 */
5773 const _neutralizeRoot = function _neutralizeRoot(root) {
5774 const childNodes = getChildNodes(root);
5775 if (childNodes) {
5776 const snapshot = [];
5777 arrayForEach(childNodes, child => {
5778 arrayPush(snapshot, child);
5779 });
5780 arrayForEach(snapshot, child => {
5781 try {
5782 remove(child);
5783 } catch (_) {
5784 /* Best-effort teardown; a still-attached child is handled below */
5785 }
5786 });
5787 }
5788 const attributes = getAttributes(root);
5789 if (attributes) {
5790 for (let i = attributes.length - 1; i >= 0; --i) {
5791 const attribute = attributes[i];
5792 const name = attribute && attribute.name;
5793 if (typeof name === 'string') {
5794 try {
5795 root.removeAttribute(name);
5796 } catch (_) {
5797 /* Clobbered removeAttribute — ignore (fail-closed best effort) */
5798 }
5799 }
5800 }
5801 }
5802 };
5803 /**
5804 * _removeAttribute
5805 *
5806 * @param name an Attribute name
5807 * @param element a DOM node
5808 */
5809 const _removeAttribute = function _removeAttribute(name, element) {
5810 try {
5811 arrayPush(DOMPurify.removed, {
5812 attribute: element.getAttributeNode(name),
5813 from: element
5814 });
5815 } catch (_) {
5816 arrayPush(DOMPurify.removed, {
5817 attribute: null,
5818 from: element
5819 });
5820 }
5821 element.removeAttribute(name);
5822 // We void attribute values for unremovable "is" attributes
5823 if (name === 'is') {
5824 if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
5825 try {
5826 _forceRemove(element);
5827 } catch (_) {}
5828 } else {
5829 try {
5830 element.setAttribute(name, '');
5831 } catch (_) {}
5832 }
5833 }
5834 };
5835 /**
5836 * _stripDisallowedAttributes
5837 *
5838 * Removes every attribute the active configuration does not allow from a
5839 * single element, using the same allowlist as the main attribute pass (so
5840 * `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
5841 * neutralise nodes that are being discarded from an in-place tree.
5842 *
5843 * @param element the element to strip
5844 */
5845 const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) {
5846 const attributes = getAttributes(element);
5847 if (!attributes) {
5848 return;
5849 }
5850 for (let i = attributes.length - 1; i >= 0; --i) {
5851 const attribute = attributes[i];
5852 const name = attribute && attribute.name;
5853 if (typeof name !== 'string' || ALLOWED_ATTR[transformCaseFunc(name)]) {
5854 continue;
5855 }
5856 try {
5857 element.removeAttribute(name);
5858 } catch (_) {
5859 /* Clobbered removeAttribute on a doomed node — ignore */
5860 }
5861 }
5862 };
5863 /**
5864 * _neutralizeSubtree
5865 *
5866 * Completes the audit-5 F1 fix across every removal path. The KEEP_CONTENT
5867 * move-hoist neutralises only disallowed-tag removals; clobber, mXSS-canary,
5868 * namespace, comment, processing-instruction and KEEP_CONTENT:false removals
5869 * all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
5870 * those dropped nodes are detached from the caller's LIVE tree but a
5871 * handler-bearing original among them (an `<img onerror>`/`<video>` that was
5872 * loading) keeps its queued resource event, which fires in page scope after
5873 * sanitize returns. This walks a removed subtree and strips every attribute
5874 * the active configuration does not allow — so `on*` handlers are cancelled
5875 * through the SAME allowlist that governs kept nodes, not a separate `/^on/`
5876 * blocklist. Run synchronously before sanitize returns, i.e. before any
5877 * queued event can fire. Hook-free by design: these nodes leave the output,
5878 * so firing attribute hooks for them would be surprising. Clobber-safe reads;
5879 * a doomed clobbered node may shadow `removeAttribute` (its own attributes are
5880 * irrelevant — it is discarded — while its non-clobbered descendants, e.g.
5881 * the `<img>`, are reached and scrubbed).
5882 *
5883 * @param root the root of a removed subtree to neutralise
5884 */
5885 const _neutralizeSubtree = function _neutralizeSubtree(root) {
5886 const stack = [root];
5887 while (stack.length > 0) {
5888 const node = stack.pop();
5889 const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
5890 if (nodeType === NODE_TYPE.element) {
5891 _stripDisallowedAttributes(node);
5892 }
5893 const childNodes = getChildNodes(node);
5894 if (childNodes) {
5895 for (let i = childNodes.length - 1; i >= 0; --i) {
5896 stack.push(childNodes[i]);
5897 }
5898 }
5899 }
5900 };
5901 /**
5902 * _initDocument
5903 *
5904 * @param dirty - a string of dirty markup
5905 * @return a DOM, filled with the dirty markup
5906 */
5907 const _initDocument = function _initDocument(dirty) {
5908 /* Create a HTML document */
5909 let doc = null;
5910 let leadingWhitespace = null;
5911 if (FORCE_BODY) {
5912 dirty = '<remove></remove>' + dirty;
5913 } else {
5914 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
5915 const matches = stringMatch(dirty, /^[\r\n\t ]+/);
5916 leadingWhitespace = matches && matches[0];
5917 }
5918 if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
5919 // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
5920 dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
5921 }
5922 const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
5923 /*
5924 * Use the DOMParser API by default, fallback later if needs be
5925 * DOMParser not work for svg when has multiple root element.
5926 */
5927 if (NAMESPACE === HTML_NAMESPACE) {
5928 try {
5929 doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
5930 } catch (_) {}
5931 }
5932 /* Use createHTMLDocument in case DOMParser is not available */
5933 if (!doc || !doc.documentElement) {
5934 doc = implementation.createDocument(NAMESPACE, 'template', null);
5935 try {
5936 doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
5937 } catch (_) {
5938 // Syntax error if dirtyPayload is invalid xml
5939 }
5940 }
5941 const body = doc.body || doc.documentElement;
5942 if (dirty && leadingWhitespace) {
5943 body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
5944 }
5945 /* Work on whole document or just its body */
5946 if (NAMESPACE === HTML_NAMESPACE) {
5947 return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
5948 }
5949 return WHOLE_DOCUMENT ? doc.documentElement : body;
5950 };
5951 /**
5952 * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
5953 *
5954 * @param root The root element or node to start traversing on.
5955 * @return The created NodeIterator
5956 */
5957 const _createNodeIterator = function _createNodeIterator(root) {
5958 return createNodeIterator.call(root.ownerDocument || root, root,
5959 // eslint-disable-next-line no-bitwise
5960 NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
5961 };
5962 /**
5963 * Replace template expression syntax (mustache, ERB, template
5964 * literal) with a space; shared by all SAFE_FOR_TEMPLATES scrub
5965 * sites. Order matters: mustache, then ERB, then template literal.
5966 *
5967 * @param value the string to scrub
5968 * @returns the scrubbed string
5969 */
5970 const _stripTemplateExpressions = function _stripTemplateExpressions(value) {
5971 value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
5972 value = stringReplace(value, ERB_EXPR$1, ' ');
5973 value = stringReplace(value, TMPLIT_EXPR$1, ' ');
5974 return value;
5975 };
5976 /**
5977 * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
5978 * character data of an element subtree. Used as the final safety net for
5979 * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
5980 * which only form after text-node normalization (e.g. fragments split across
5981 * stripped elements) cannot survive into a template-evaluating framework.
5982 *
5983 * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
5984 * in place rather than round-tripping through innerHTML. This preserves
5985 * descendant node references (important for IN_PLACE callers), avoids a
5986 * serialize/reparse cycle, and reads literal character data — which means
5987 * `<%...%>` in text content matches the ERB regex against its real bytes
5988 * instead of the HTML-entity-escaped form innerHTML would produce.
5989 *
5990 * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
5991 * attributes is performed during the per-node `_sanitizeAttributes` pass.
5992 *
5993 * @param node The root element whose character data should be scrubbed.
5994 */
5995 const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
5996 var _node$querySelectorAl;
5997 node.normalize();
5998 const walker = createNodeIterator.call(node.ownerDocument || node, node,
5999 // eslint-disable-next-line no-bitwise
6000 NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
6001 let currentNode = walker.nextNode();
6002 while (currentNode) {
6003 currentNode.data = _stripTemplateExpressions(currentNode.data);
6004 currentNode = walker.nextNode();
6005 }
6006 // NodeIterator does not descend into <template>.content per the DOM spec,
6007 // so we must explicitly recurse into each template's content fragment,
6008 // mirroring the approach used by _sanitizeShadowDOM.
6009 const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, 'template');
6010 if (templates) {
6011 arrayForEach(templates, tmpl => {
6012 if (_isDocumentFragment(tmpl.content)) {
6013 _scrubTemplateExpressions2(tmpl.content);
6014 }
6015 });
6016 }
6017 };
6018 /**
6019 * _isClobbered
6020 *
6021 * Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
6022 * interface with [LegacyOverrideBuiltIns]; a descendant element with a
6023 * `name` attribute matching a prototype property shadows that property
6024 * on direct reads. We use this check at the IN_PLACE entry-point and
6025 * during attribute sanitization to refuse clobbered forms.
6026 *
6027 * @param element element to check for clobbering attacks
6028 * @return true if clobbered, false if safe
6029 */
6030 const _isClobbered = function _isClobbered(element) {
6031 // Realm-independent tag-name probe. If we can't determine the tag
6032 // name at all, we can't reason about clobbering — return false
6033 // (the caller's other defences still apply).
6034 const realTagName = getNodeName ? getNodeName(element) : null;
6035 if (typeof realTagName !== 'string') {
6036 return false;
6037 }
6038 if (transformCaseFunc(realTagName) !== 'form') {
6039 return false;
6040 }
6041 return typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' ||
6042 // Realm-safe NamedNodeMap detection: equality against the cached
6043 // prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
6044 // makes the direct read diverge from the cached read; a clean form
6045 // (same-realm OR foreign-realm) has both reads pointing at the same
6046 // canonical NamedNodeMap.
6047 element.attributes !== getAttributes(element) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function' ||
6048 // NodeType clobbering probe. Cached Node.prototype.nodeType getter
6049 // returns the integer 1 for any Element regardless of realm; direct
6050 // read on a clobbered form (e.g. <input name="nodeType">) returns
6051 // the named child element. Cheap addition — nodeType is read from
6052 // an internal slot, no serialization cost — and removes a residual
6053 // clobbering surface used by several mXSS / PI / comment branches
6054 // in _sanitizeElements that compare currentNode.nodeType directly.
6055 element.nodeType !== getNodeType(element) ||
6056 // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
6057 // "childNodes" shadows the prototype getter. Direct reads of
6058 // form.childNodes from a clobbered form return the named child
6059 // instead of the real NodeList, so any walk that reads it directly
6060 // skips the form's real children. Compare the direct read to the
6061 // cached Node.prototype getter — when the form's named-property
6062 // getter intercepts the read, the two values differ and we flag
6063 // the form. This catches every clobbering child type (input,
6064 // select, etc.) regardless of whether the named child happens to
6065 // carry a numeric .length, which a typeof-based probe would miss
6066 // (e.g. HTMLSelectElement.length is a defined unsigned-long).
6067 element.childNodes !== getChildNodes(element);
6068 };
6069 /**
6070 * Checks whether the given value is a DocumentFragment from any realm.
6071 *
6072 * The realm-independent replacement reads `nodeType` through the cached
6073 * Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
6074 * constant (11). nodeType is a numeric value resolved from the node's
6075 * internal slot, identical across realms for the same kind of node.
6076 *
6077 * @param value object to check
6078 * @return true if value is a DocumentFragment-shaped node from any realm
6079 */
6080 const _isDocumentFragment = function _isDocumentFragment(value) {
6081 if (!getNodeType || typeof value !== 'object' || value === null) {
6082 return false;
6083 }
6084 try {
6085 return getNodeType(value) === NODE_TYPE.documentFragment;
6086 } catch (_) {
6087 return false;
6088 }
6089 };
6090 /**
6091 * Checks whether the given object is a DOM node, including nodes that
6092 * originate from a different window/realm (e.g. an iframe's
6093 * contentDocument). The previous `value instanceof Node` check was
6094 * realm-bound: nodes from a different window failed it, causing
6095 * sanitize() to silently stringify them and reset IN_PLACE to false,
6096 * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
6097 *
6098 * @param value object to check whether it's a DOM node
6099 * @return true if value is a DOM node from any realm
6100 */
6101 const _isNode = function _isNode(value) {
6102 if (!getNodeType || typeof value !== 'object' || value === null) {
6103 return false;
6104 }
6105 try {
6106 return typeof getNodeType(value) === 'number';
6107 } catch (_) {
6108 return false;
6109 }
6110 };
6111 function _executeHooks(hooks, currentNode, data) {
6112 if (hooks.length === 0) {
6113 return;
6114 }
6115 arrayForEach(hooks, hook => {
6116 hook.call(DOMPurify, currentNode, data, CONFIG);
6117 });
6118 }
6119 /**
6120 * Structural-threat checks that condemn a node regardless of the
6121 * allowlists: mXSS via namespace confusion, risky CSS construction,
6122 * processing instructions, markup-bearing comments. Pure predicate;
6123 * the caller removes. Check order is load-bearing.
6124 *
6125 * @param currentNode the node to inspect
6126 * @param tagName the node's transformCaseFunc'd tag name
6127 * @return true if the node must be removed
6128 */
6129 const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
6130 /* Detect mXSS attempts abusing namespace confusion */
6131 if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
6132 return true;
6133 }
6134 /* Remove risky CSS construction leading to mXSS */
6135 if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
6136 return true;
6137 }
6138 /* Remove any occurrence of processing instructions */
6139 if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
6140 return true;
6141 }
6142 /* Remove any kind of possibly harmful comments */
6143 if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
6144 return true;
6145 }
6146 return false;
6147 };
6148 /**
6149 * Handle a node whose tag is forbidden or not allowlisted: keep
6150 * allowed custom elements (false return exits _sanitizeElements
6151 * early - namespace/fallback checks and the afterSanitizeElements
6152 * hook are intentionally skipped for kept custom elements), else
6153 * hoist content per KEEP_CONTENT and remove.
6154 *
6155 * @param currentNode the disallowed node
6156 * @param tagName the node's transformCaseFunc'd tag name
6157 * @return true if the node was removed, false if kept
6158 */
6159 const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName) {
6160 /* Check if we have a custom element to handle */
6161 if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
6162 if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
6163 return false;
6164 }
6165 if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
6166 return false;
6167 }
6168 }
6169 /* Keep content except for bad-listed elements.
6170 Use the cached prototype getters exclusively — the previous code
6171 had `|| currentNode.parentNode` / `|| currentNode.childNodes`
6172 fallbacks, but the cached getters always return the canonical
6173 value (or null for a real parent-less node), so the fallback
6174 path was dead in safe cases and a clobbering surface in unsafe
6175 ones. Falsy cached results stay falsy; the `if (childNodes &&
6176 parentNode)` check already gates correctly. */
6177 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
6178 const parentNode = getParentNode(currentNode);
6179 const childNodes = getChildNodes(currentNode);
6180 if (childNodes && parentNode) {
6181 const childCount = childNodes.length;
6182 /* In-place: hoist the *original* children so the iterator visits
6183 and sanitises them through the same allowlist pass as every other
6184 node. The caller built the tree in the live document, so the
6185 originals carry already-queued resource events (`<img onerror>`,
6186 `<video>`/`<audio>` error, lazy/`onload`, …); cloning would leave
6187 those originals detached but still armed, firing in page scope
6188 while the returned tree looked clean. Moving is safe in-place: the
6189 root is pre-validated as an allowed tag and so is never the node
6190 being removed, which keeps `parentNode` inside the iterator root
6191 and the relocated child inside the serialised tree.
6192 Otherwise (string / DOM-copy paths): clone. The iterator is rooted
6193 at — and the result serialised from — `body`, so a restrictive
6194 ALLOWED_TAGS that removes `body` itself must leave its content in
6195 place, which only cloning does; and those paths parse into an
6196 inert document, so their discarded originals never had a queued
6197 event to neutralise.
6198 `childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
6199 valid whether we move (drops the trailing entry) or clone (leaves
6200 the list intact). */
6201 for (let i = childCount - 1; i >= 0; --i) {
6202 const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
6203 parentNode.insertBefore(hoisted, getNextSibling(currentNode));
6204 }
6205 }
6206 }
6207 _forceRemove(currentNode);
6208 return true;
6209 };
6210 /**
6211 * _sanitizeElements
6212 *
6213 * @protect nodeName
6214 * @protect textContent
6215 * @protect removeChild
6216 * @param currentNode to check for permission to exist
6217 * @return true if node was killed, false if left alive
6218 */
6219 const _sanitizeElements = function _sanitizeElements(currentNode) {
6220 /* Execute a hook if present */
6221 _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
6222 /* Check if element is clobbered or can clobber */
6223 if (_isClobbered(currentNode)) {
6224 _forceRemove(currentNode);
6225 return true;
6226 }
6227 /* Now let's check the element's type and name */
6228 const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
6229 /* Execute a hook if present */
6230 _executeHooks(hooks.uponSanitizeElement, currentNode, {
6231 tagName,
6232 allowedTags: ALLOWED_TAGS
6233 });
6234 /* Remove mXSS vectors, processing instructions and risky comments */
6235 if (_isUnsafeNode(currentNode, tagName)) {
6236 _forceRemove(currentNode);
6237 return true;
6238 }
6239 /* Remove element if anything forbids its presence */
6240 if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
6241 return _sanitizeDisallowedNode(currentNode, tagName);
6242 }
6243 /* Check whether element has a valid namespace.
6244 Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
6245 nodeType getter rather than `instanceof Element`, which is realm-
6246 bound and short-circuits to false for any node minted in a different
6247 realm — letting a foreign-realm element with a forbidden namespace
6248 slip past the namespace check entirely. */
6249 const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
6250 if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
6251 _forceRemove(currentNode);
6252 return true;
6253 }
6254 /* Make sure that older browsers don't get fallback-tag mXSS */
6255 if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
6256 _forceRemove(currentNode);
6257 return true;
6258 }
6259 /* Sanitize element content to be template-safe */
6260 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
6261 /* Get the element's text content */
6262 const content = _stripTemplateExpressions(currentNode.textContent);
6263 if (currentNode.textContent !== content) {
6264 arrayPush(DOMPurify.removed, {
6265 element: currentNode.cloneNode()
6266 });
6267 currentNode.textContent = content;
6268 }
6269 }
6270 /* Execute a hook if present */
6271 _executeHooks(hooks.afterSanitizeElements, currentNode, null);
6272 return false;
6273 };
6274 /**
6275 * _isValidAttribute
6276 *
6277 * @param lcTag Lowercase tag name of containing element.
6278 * @param lcName Lowercase attribute name.
6279 * @param value Attribute value.
6280 * @return Returns true if `value` is valid, otherwise false.
6281 */
6282 // eslint-disable-next-line complexity
6283 const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
6284 /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
6285 if (FORBID_ATTR[lcName]) {
6286 return false;
6287 }
6288 /* Make sure attribute cannot clobber */
6289 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
6290 return false;
6291 }
6292 const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
6293 /* Allow valid data-* attributes: At least one character after "-"
6294 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
6295 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
6296 We don't need to check the value; it's always URI safe. */
6297 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted) {
6298 if (
6299 // First condition does a very basic check if a) it's basically a valid custom element tagname AND
6300 // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
6301 // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
6302 _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||
6303 // Alternative, second condition checks if it's an `is`-attribute, AND
6304 // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
6305 lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
6306 return false;
6307 }
6308 /* Check value is safe. First, is attr inert? If so, is safe */
6309 } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
6310 return false;
6311 } else ;
6312 return true;
6313 };
6314 /* Names the HTML spec reserves from valid-custom-element-name; these must
6315 * never be treated as basic custom elements even when a permissive
6316 * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
6317 const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
6318 /**
6319 * _isBasicCustomElement
6320 * checks if at least one dash is included in tagName, and it's not the first char
6321 * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
6322 *
6323 * @param tagName name of the tag of the node to sanitize
6324 * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
6325 */
6326 const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
6327 return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
6328 };
6329 /**
6330 * Wrap an attribute value in the matching Trusted Types object when
6331 * the active policy requires it. Namespaced attributes pass through
6332 * unchanged (no TT support yet, see
6333 * https://bugs.chromium.org/p/chromium/issues/detail?id=1305293).
6334 *
6335 * @param lcTag lowercase tag name of the containing element
6336 * @param lcName lowercase attribute name
6337 * @param namespaceURI the attribute's namespace, if any
6338 * @param value the attribute value to wrap
6339 * @return the value, wrapped when Trusted Types demand it
6340 */
6341 const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value) {
6342 if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function' && !namespaceURI) {
6343 switch (trustedTypes.getAttributeType(lcTag, lcName)) {
6344 case 'TrustedHTML':
6345 {
6346 return _createTrustedHTML(value);
6347 }
6348 case 'TrustedScriptURL':
6349 {
6350 return _createTrustedScriptURL(value);
6351 }
6352 }
6353 }
6354 return value;
6355 };
6356 /**
6357 * Write a modified attribute value back onto the element. On
6358 * success, re-probe for clobbering introduced by the new value and
6359 * remove the element when found; otherwise pop the removal entry
6360 * recorded by the earlier _removeAttribute (long-standing pairing
6361 * with the SANITIZE_NAMED_PROPS path - do not "fix" casually). On
6362 * failure, remove the attribute instead.
6363 *
6364 * @param currentNode the element carrying the attribute
6365 * @param name the attribute name as present on the element
6366 * @param namespaceURI the attribute's namespace, if any
6367 * @param value the new attribute value
6368 */
6369 const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
6370 try {
6371 if (namespaceURI) {
6372 currentNode.setAttributeNS(namespaceURI, name, value);
6373 } else {
6374 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
6375 currentNode.setAttribute(name, value);
6376 }
6377 if (_isClobbered(currentNode)) {
6378 _forceRemove(currentNode);
6379 } else {
6380 arrayPop(DOMPurify.removed);
6381 }
6382 } catch (_) {
6383 _removeAttribute(name, currentNode);
6384 }
6385 };
6386 /**
6387 * _sanitizeAttributes
6388 *
6389 * @protect attributes
6390 * @protect nodeName
6391 * @protect removeAttribute
6392 * @protect setAttribute
6393 *
6394 * @param currentNode to sanitize
6395 */
6396 const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
6397 /* Execute a hook if present */
6398 _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
6399 const attributes = currentNode.attributes;
6400 /* Check if we have attributes; if not we might have a text node */
6401 if (!attributes || _isClobbered(currentNode)) {
6402 return;
6403 }
6404 const hookEvent = {
6405 attrName: '',
6406 attrValue: '',
6407 keepAttr: true,
6408 allowedAttributes: ALLOWED_ATTR,
6409 forceKeepAttr: undefined
6410 };
6411 let l = attributes.length;
6412 const lcTag = transformCaseFunc(currentNode.nodeName);
6413 /* Go backwards over all attributes; safely remove bad ones */
6414 while (l--) {
6415 const attr = attributes[l];
6416 const name = attr.name,
6417 namespaceURI = attr.namespaceURI,
6418 attrValue = attr.value;
6419 const lcName = transformCaseFunc(name);
6420 const initValue = attrValue;
6421 let value = name === 'value' ? initValue : stringTrim(initValue);
6422 /* Execute a hook if present */
6423 hookEvent.attrName = lcName;
6424 hookEvent.attrValue = value;
6425 hookEvent.keepAttr = true;
6426 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
6427 _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
6428 value = hookEvent.attrValue;
6429 /* Full DOM Clobbering protection via namespace isolation,
6430 * Prefix id and name attributes with `user-content-`
6431 */
6432 if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
6433 // Remove the attribute with this value
6434 _removeAttribute(name, currentNode);
6435 // Prefix the value and later re-create the attribute with the sanitized value
6436 value = SANITIZE_NAMED_PROPS_PREFIX + value;
6437 }
6438 // Else: already prefixed, leave the attribute alone — the prefix is
6439 // itself the clobbering protection, and re-applying it is incorrect.
6440 /* Work around a security issue with comments inside attributes */
6441 if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
6442 _removeAttribute(name, currentNode);
6443 continue;
6444 }
6445 /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
6446 if (lcName === 'attributename' && stringMatch(value, 'href')) {
6447 _removeAttribute(name, currentNode);
6448 continue;
6449 }
6450 /* Did the hooks force-keep the attribute? */
6451 if (hookEvent.forceKeepAttr) {
6452 continue;
6453 }
6454 /* Did the hooks approve of the attribute? */
6455 if (!hookEvent.keepAttr) {
6456 _removeAttribute(name, currentNode);
6457 continue;
6458 }
6459 /* Work around a security issue in jQuery 3.0 */
6460 if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
6461 _removeAttribute(name, currentNode);
6462 continue;
6463 }
6464 /* Sanitize attribute content to be template-safe */
6465 if (SAFE_FOR_TEMPLATES) {
6466 value = _stripTemplateExpressions(value);
6467 }
6468 /* Is `value` valid for this attribute? */
6469 if (!_isValidAttribute(lcTag, lcName, value)) {
6470 _removeAttribute(name, currentNode);
6471 continue;
6472 }
6473 /* Handle attributes that require Trusted Types */
6474 value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
6475 /* Handle invalid data-* attribute set by try-catching it */
6476 if (value !== initValue) {
6477 _setAttributeValue(currentNode, name, namespaceURI, value);
6478 }
6479 }
6480 /* Execute a hook if present */
6481 _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
6482 };
6483 /**
6484 * _sanitizeShadowDOM
6485 *
6486 * @param fragment to iterate over recursively
6487 */
6488 const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
6489 let shadowNode = null;
6490 const shadowIterator = _createNodeIterator(fragment);
6491 /* Execute a hook if present */
6492 _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
6493 while (shadowNode = shadowIterator.nextNode()) {
6494 /* Execute a hook if present */
6495 _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
6496 /* Sanitize tags and elements */
6497 _sanitizeElements(shadowNode);
6498 /* Check attributes next */
6499 _sanitizeAttributes(shadowNode);
6500 /* Deep shadow DOM detected.
6501 Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType against the
6502 DOCUMENT_FRAGMENT_NODE constant rather than instanceof, so we
6503 recurse into <template>.content from foreign realms too. */
6504 if (_isDocumentFragment(shadowNode.content)) {
6505 _sanitizeShadowDOM2(shadowNode.content);
6506 }
6507 /* An element iterated here may itself host an attached
6508 shadow root. The default NodeIterator does not enter shadow
6509 trees, so a shadow root nested inside template.content was
6510 previously reached by no walk at all (the pre-pass at
6511 _sanitizeAttachedShadowRoots descends via childNodes, which
6512 doesn't enter template.content; the template-content recursion
6513 above iterates the content but never inspected shadowRoot).
6514 Walk it explicitly. The nodeType guard avoids reading
6515 shadowRoot off text / comment / CDATA / PI nodes that the
6516 iterator also surfaces. */
6517 const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
6518 if (shadowNodeType === NODE_TYPE.element) {
6519 const innerSr = getShadowRoot(shadowNode);
6520 if (_isDocumentFragment(innerSr)) {
6521 _sanitizeAttachedShadowRoots(innerSr);
6522 _sanitizeShadowDOM2(innerSr);
6523 }
6524 }
6525 }
6526 /* Execute a hook if present */
6527 _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
6528 };
6529 /**
6530 * _sanitizeAttachedShadowRoots
6531 *
6532 * Walks `root` and feeds every attached shadow root we encounter into
6533 * the existing _sanitizeShadowDOM pipeline. The default node iterator
6534 * does not descend into shadow trees, so nodes inside an attached
6535 * shadow root would otherwise be skipped entirely.
6536 *
6537 * Two real input paths put attached shadow roots in front of us:
6538 * 1. IN_PLACE on a DOM node that already has shadow roots attached.
6539 * 2. DOM-node input where importNode(dirty, true) deep-clones the
6540 * shadow root because it was created with `clonable: true`.
6541 *
6542 * This pass runs once, up front, so the main iteration loop (and the
6543 * existing _sanitizeShadowDOM template-content recursion) stay
6544 * untouched — string-input paths are not affected.
6545 *
6546 * @param root the subtree root to walk for attached shadow roots
6547 */
6548 const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots(root) {
6549 /* Iterative (explicit stack) rather than per-child recursion. DOM APIs
6550 impose no depth cap, so an attacker-shaped tree (JSON/CRDT/editor data
6551 built straight into the DOM — the IN_PLACE surface) deeper than the JS
6552 call-stack budget would otherwise overflow native recursion here and
6553 throw at the IN_PLACE entry pre-pass, before a single node is
6554 sanitized, leaving the caller's live tree untouched (fail-open). See
6555 campaign-3 F4. A heap stack keeps depth off the call stack.
6556 Each work item is either a node to descend into, or a deferred
6557 `_sanitizeShadowDOM` for an already-walked shadow root. The deferred
6558 form preserves the original post-order discipline: a shadow root's
6559 nested shadow roots are discovered before the outer shadow is
6560 sanitized (which may remove hosts). Pushes are in reverse of the
6561 desired processing order (LIFO): template content, then children, then
6562 the shadow-sanitize, then the shadow walk — so the order matches the
6563 previous recursion exactly. */
6564 const stack = [{
6565 node: root,
6566 shadow: null
6567 }];
6568 while (stack.length > 0) {
6569 const item = stack.pop();
6570 /* Deferred shadow-DOM sanitisation: runs after its subtree was walked. */
6571 if (item.shadow) {
6572 _sanitizeShadowDOM2(item.shadow);
6573 continue;
6574 }
6575 const node = item.node;
6576 const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
6577 const isElement = nodeType === NODE_TYPE.element;
6578 /* (pushed last → processed first) Children, snapshotted in reverse so
6579 the first child is processed first. Snapshotting matters because a
6580 hook may detach siblings mid-walk. */
6581 const childNodes = getChildNodes(node);
6582 if (childNodes) {
6583 for (let i = childNodes.length - 1; i >= 0; --i) {
6584 stack.push({
6585 node: childNodes[i],
6586 shadow: null
6587 });
6588 }
6589 }
6590 /* (pushed before children → processed after them, matching the old
6591 "template content last" order) When the node is a <template>,
6592 descend into its content. */
6593 if (isElement) {
6594 const rootName = getNodeName ? getNodeName(node) : null;
6595 if (typeof rootName === 'string' && transformCaseFunc(rootName) === 'template') {
6596 const content = node.content;
6597 if (_isDocumentFragment(content)) {
6598 stack.push({
6599 node: content,
6600 shadow: null
6601 });
6602 }
6603 }
6604 }
6605 /* Shadow root (processed first): walk its subtree, then sanitise it.
6606 Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
6607 rather than `instanceof DocumentFragment`, which is realm-bound and
6608 silently skipped foreign-realm shadow roots (e.g.
6609 iframe.contentDocument attachShadow). */
6610 if (isElement) {
6611 const sr = getShadowRoot(node);
6612 if (_isDocumentFragment(sr)) {
6613 /* Push the deferred sanitise first so it pops after the shadow
6614 walk we push next, i.e. nested shadow roots are discovered
6615 before this one is sanitised. */
6616 stack.push({
6617 node: null,
6618 shadow: sr
6619 }, {
6620 node: sr,
6621 shadow: null
6622 });
6623 }
6624 }
6625 }
6626 };
6627 // eslint-disable-next-line complexity
6628 DOMPurify.sanitize = function (dirty) {
6629 let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6630 let body = null;
6631 let importedNode = null;
6632 let currentNode = null;
6633 let returnNode = null;
6634 /* Make sure we have a string to sanitize.
6635 DO NOT return early, as this will return the wrong type if
6636 the user has requested a DOM object rather than a string */
6637 IS_EMPTY_INPUT = !dirty;
6638 if (IS_EMPTY_INPUT) {
6639 dirty = '<!-->';
6640 }
6641 /* Stringify, in case dirty is an object */
6642 if (typeof dirty !== 'string' && !_isNode(dirty)) {
6643 dirty = stringifyValue(dirty);
6644 if (typeof dirty !== 'string') {
6645 throw typeErrorCreate('dirty is not a string, aborting');
6646 }
6647 }
6648 /* Return dirty HTML if DOMPurify cannot run */
6649 if (!DOMPurify.isSupported) {
6650 return dirty;
6651 }
6652 /* Assign config vars */
6653 if (SET_CONFIG) {
6654 /* Persistent setConfig() path: _parseConfig is skipped, so the sets are
6655 * not re-derived per call. Restore them from the pristine bindings
6656 * captured at setConfig() time so a previous call's hook clone (mutated
6657 * below) does not carry over. */
6658 ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
6659 ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
6660 } else {
6661 _parseConfig(cfg);
6662 }
6663 /* Clone the hook-mutable allowlists before the walk whenever an
6664 * uponSanitize* hook is registered. The hook event exposes ALLOWED_TAGS
6665 * and ALLOWED_ATTR by reference (as allowedTags / allowedAttributes), so
6666 * a hook that widens them would otherwise mutate the shared set
6667 * permanently: across later calls and across every element. Cloning per
6668 * walk keeps documented in-call widening working while scoping it to the
6669 * call. A single guard for both config paths - the per-call path rebinds
6670 * the sets in _parseConfig each call, the persistent path restores them
6671 * from the captured bindings just above - so the two cannot diverge. */
6672 if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) {
6673 ALLOWED_TAGS = clone(ALLOWED_TAGS);
6674 }
6675 if (hooks.uponSanitizeAttribute.length > 0) {
6676 ALLOWED_ATTR = clone(ALLOWED_ATTR);
6677 }
6678 /* Clean up removed elements */
6679 DOMPurify.removed = [];
6680 /* Resolve IN_PLACE for this call without mutating persistent config.
6681 Writing the IN_PLACE closure variable here leaks under setConfig(),
6682 where _parseConfig is skipped on later calls: a single string call would
6683 disable in-place mode for every subsequent node call, returning a
6684 sanitized copy while leaving the caller's node — which in-place callers
6685 keep using and whose return value they ignore — unsanitized. REPORT-2. */
6686 const inPlace = IN_PLACE && typeof dirty !== 'string' && _isNode(dirty);
6687 if (inPlace) {
6688 /* Do some early pre-sanitization to avoid unsafe root nodes.
6689 Read nodeName through the cached prototype getter — a clobbering
6690 child named "nodeName" on the form root would otherwise shadow
6691 the property and let this check skip the root-allowlist
6692 validation entirely. */
6693 const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
6694 if (typeof nn === 'string') {
6695 const tagName = transformCaseFunc(nn);
6696 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
6697 throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
6698 }
6699 }
6700 /* Pre-flight the root through _isClobbered. The iterator-driven
6701 removal path can not detach a parent-less root: _forceRemove
6702 falls through to Element.prototype.remove(), which per spec
6703 is a no-op on a node with no parent. A clobbered root would
6704 then survive the main loop with its attributes uninspected,
6705 because _sanitizeAttributes early-returns on _isClobbered. The
6706 result would be an attacker-controlled form, complete with any
6707 event-handler attributes the caller passed in, handed back to
6708 the application unsanitized. Refuse to sanitize such a root
6709 the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
6710 if (_isClobbered(dirty)) {
6711 throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
6712 }
6713 /* Sanitize attached shadow roots before the main iterator runs.
6714 The iterator does not descend into shadow trees. Same fail-closed
6715 barrier as the main walk (campaign-3 F2): a custom-element reaction
6716 inside a shadow root could abort this pre-pass before the walk runs,
6717 which would otherwise leave the entire live tree unsanitized. */
6718 try {
6719 _sanitizeAttachedShadowRoots(dirty);
6720 } catch (error) {
6721 _neutralizeRoot(dirty);
6722 throw error;
6723 }
6724 } else if (_isNode(dirty)) {
6725 /* If dirty is a DOM element, append to an empty document to avoid
6726 elements being stripped by the parser */
6727 body = _initDocument('<!---->');
6728 importedNode = body.ownerDocument.importNode(dirty, true);
6729 if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
6730 /* Node is already a body, use as is */
6731 body = importedNode;
6732 } else if (importedNode.nodeName === 'HTML') {
6733 body = importedNode;
6734 } else {
6735 // eslint-disable-next-line unicorn/prefer-dom-node-append
6736 body.appendChild(importedNode);
6737 }
6738 /* Clonable shadow roots are deep-cloned by importNode(); sanitize
6739 them before the main iterator runs, since the iterator does not
6740 descend into shadow trees. The walk routes every read through a
6741 cached prototype getter so clobbering descendants on a form root
6742 cannot hide a shadow host from this pass. */
6743 _sanitizeAttachedShadowRoots(importedNode);
6744 } else {
6745 /* Exit directly if we have nothing to do */
6746 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
6747 // eslint-disable-next-line unicorn/prefer-includes
6748 dirty.indexOf('<') === -1) {
6749 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
6750 }
6751 /* Initialize the document to work on */
6752 body = _initDocument(dirty);
6753 /* Check we have a DOM node from the data */
6754 if (!body) {
6755 return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
6756 }
6757 }
6758 /* Remove first element node (ours) if FORCE_BODY is set */
6759 if (body && FORCE_BODY) {
6760 _forceRemove(body.firstChild);
6761 }
6762 /* Get node iterator */
6763 const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
6764 /* Now start iterating over the created document.
6765 The walk runs inside an exception barrier (campaign-3 F2): a re-entrant
6766 engine/custom-element mutation can detach a node mid-walk so
6767 `_forceRemove`'s parentless guard throws, aborting the loop. Without the
6768 barrier the caller's in-place tree would be left half-sanitized with the
6769 unvisited tail still armed. On any throw we fail closed — strip the
6770 in-place root bare — then rethrow so the existing throw contract is
6771 preserved. (String/DOM-copy paths never return the partial body, so the
6772 propagating throw is already fail-closed there.) */
6773 try {
6774 while (currentNode = nodeIterator.nextNode()) {
6775 /* Sanitize tags and elements */
6776 _sanitizeElements(currentNode);
6777 /* Check attributes next */
6778 _sanitizeAttributes(currentNode);
6779 /* Shadow DOM detected, sanitize it.
6780 Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
6781 instead of instanceof, so foreign-realm <template>.content is
6782 walked correctly. */
6783 if (_isDocumentFragment(currentNode.content)) {
6784 _sanitizeShadowDOM2(currentNode.content);
6785 }
6786 }
6787 } catch (error) {
6788 if (inPlace) {
6789 _neutralizeRoot(dirty);
6790 }
6791 throw error;
6792 }
6793 /* If we sanitized `dirty` in-place, return it. */
6794 if (inPlace) {
6795 /* Fail-closed completion of the audit-5 F1 fix: every node removed from
6796 the caller's live tree is detached but may still hold a queued
6797 resource-event handler that fires in page scope after we return. The
6798 move-hoist covers only disallowed-tag KEEP_CONTENT removals; strip the
6799 non-allow-listed attributes off every other removed subtree (clobber,
6800 mXSS, namespace, comments, KEEP_CONTENT:false, …) so those handlers are
6801 cancelled before any event can fire. Runs synchronously, pre-return. */
6802 arrayForEach(DOMPurify.removed, entry => {
6803 if (entry.element) {
6804 _neutralizeSubtree(entry.element);
6805 }
6806 });
6807 if (SAFE_FOR_TEMPLATES) {
6808 _scrubTemplateExpressions2(dirty);
6809 }
6810 return dirty;
6811 }
6812 /* Return sanitized string or DOM */
6813 if (RETURN_DOM) {
6814 if (SAFE_FOR_TEMPLATES) {
6815 _scrubTemplateExpressions2(body);
6816 }
6817 if (RETURN_DOM_FRAGMENT) {
6818 returnNode = createDocumentFragment.call(body.ownerDocument);
6819 while (body.firstChild) {
6820 // eslint-disable-next-line unicorn/prefer-dom-node-append
6821 returnNode.appendChild(body.firstChild);
6822 }
6823 } else {
6824 returnNode = body;
6825 }
6826 if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
6827 /*
6828 AdoptNode() is not used because internal state is not reset
6829 (e.g. the past names map of a HTMLFormElement), this is safe
6830 in theory but we would rather not risk another attack vector.
6831 The state that is cloned by importNode() is explicitly defined
6832 by the specs.
6833 */
6834 returnNode = importNode.call(originalDocument, returnNode, true);
6835 }
6836 return returnNode;
6837 }
6838 let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
6839 /* Serialize doctype if allowed */
6840 if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
6841 serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
6842 }
6843 /* Sanitize final string template-safe */
6844 if (SAFE_FOR_TEMPLATES) {
6845 serializedHTML = _stripTemplateExpressions(serializedHTML);
6846 }
6847 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
6848 };
6849 DOMPurify.setConfig = function () {
6850 let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6851 _parseConfig(cfg);
6852 SET_CONFIG = true;
6853 SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
6854 SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
6855 };
6856 DOMPurify.clearConfig = function () {
6857 CONFIG = null;
6858 SET_CONFIG = false;
6859 SET_CONFIG_ALLOWED_TAGS = null;
6860 SET_CONFIG_ALLOWED_ATTR = null;
6861 // Drop any caller-supplied Trusted Types policy so it cannot poison later
6862 // `RETURN_TRUSTED_TYPE` output. The internal default policy (cached, and
6863 // never recreated — Trusted Types throws on duplicate names) is restored by
6864 // the next `_parseConfig`. See GHSA-vxr8-fq34-vvx9.
6865 trustedTypesPolicy = defaultTrustedTypesPolicy;
6866 emptyHTML = '';
6867 };
6868 DOMPurify.isValidAttribute = function (tag, attr, value) {
6869 /* Initialize shared config vars if necessary. */
6870 if (!CONFIG) {
6871 _parseConfig({});
6872 }
6873 const lcTag = transformCaseFunc(tag);
6874 const lcName = transformCaseFunc(attr);
6875 return _isValidAttribute(lcTag, lcName, value);
6876 };
6877 DOMPurify.addHook = function (entryPoint, hookFunction) {
6878 if (typeof hookFunction !== 'function') {
6879 return;
6880 }
6881 /* Reject unknown entry points. Without this, a non-hook key (e.g.
6882 * '__proto__') indexes off the prototype chain rather than a real
6883 * hook array, and arrayPush then writes to Object.prototype. Guard
6884 * with an own-property check against the known hook names. */
6885 if (!objectHasOwnProperty(hooks, entryPoint)) {
6886 return;
6887 }
6888 arrayPush(hooks[entryPoint], hookFunction);
6889 };
6890 DOMPurify.removeHook = function (entryPoint, hookFunction) {
6891 if (!objectHasOwnProperty(hooks, entryPoint)) {
6892 return undefined;
6893 }
6894 if (hookFunction !== undefined) {
6895 const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
6896 return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
6897 }
6898 return arrayPop(hooks[entryPoint]);
6899 };
6900 DOMPurify.removeHooks = function (entryPoint) {
6901 if (!objectHasOwnProperty(hooks, entryPoint)) {
6902 return;
6903 }
6904 hooks[entryPoint] = [];
6905 };
6906 DOMPurify.removeAllHooks = function () {
6907 hooks = _createHooksMap();
6908 };
6909 return DOMPurify;
6910 }
6911 var purify = createDOMPurify();
6912
6913 module.exports = purify;
6914 //# sourceMappingURL=purify.cjs.js.map
6915
6916
6917 /***/ })
6918
6919 /******/ });
6920 /************************************************************************/
6921 /******/ // The module cache
6922 /******/ var __webpack_module_cache__ = {};
6923 /******/
6924 /******/ // The require function
6925 /******/ function __webpack_require__(moduleId) {
6926 /******/ // Check if module is in cache
6927 /******/ var cachedModule = __webpack_module_cache__[moduleId];
6928 /******/ if (cachedModule !== undefined) {
6929 /******/ return cachedModule.exports;
6930 /******/ }
6931 /******/ // Create a new module (and put it into the cache)
6932 /******/ var module = __webpack_module_cache__[moduleId] = {
6933 /******/ // no module.id needed
6934 /******/ // no module.loaded needed
6935 /******/ exports: {}
6936 /******/ };
6937 /******/
6938 /******/ // Execute the module function
6939 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
6940 /******/
6941 /******/ // Return the exports of the module
6942 /******/ return module.exports;
6943 /******/ }
6944 /******/
6945 /************************************************************************/
6946 /******/ /* webpack/runtime/compat get default export */
6947 /******/ (() => {
6948 /******/ // getDefaultExport function for compatibility with non-harmony modules
6949 /******/ __webpack_require__.n = (module) => {
6950 /******/ var getter = module && module.__esModule ?
6951 /******/ () => (module['default']) :
6952 /******/ () => (module);
6953 /******/ __webpack_require__.d(getter, { a: getter });
6954 /******/ return getter;
6955 /******/ };
6956 /******/ })();
6957 /******/
6958 /******/ /* webpack/runtime/define property getters */
6959 /******/ (() => {
6960 /******/ // define getter functions for harmony exports
6961 /******/ __webpack_require__.d = (exports, definition) => {
6962 /******/ for(var key in definition) {
6963 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
6964 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
6965 /******/ }
6966 /******/ }
6967 /******/ };
6968 /******/ })();
6969 /******/
6970 /******/ /* webpack/runtime/hasOwnProperty shorthand */
6971 /******/ (() => {
6972 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
6973 /******/ })();
6974 /******/
6975 /******/ /* webpack/runtime/make namespace object */
6976 /******/ (() => {
6977 /******/ // define __esModule on exports
6978 /******/ __webpack_require__.r = (exports) => {
6979 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
6980 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
6981 /******/ }
6982 /******/ Object.defineProperty(exports, '__esModule', { value: true });
6983 /******/ };
6984 /******/ })();
6985 /******/
6986 /************************************************************************/
6987 var __webpack_exports__ = {};
6988 // This entry needs to be wrapped in an IIFE because it needs to be in strict mode.
6989 (() => {
6990 "use strict";
6991 /*!******************************!*\
6992 !*** ./assets/src/js/acf.js ***!
6993 \******************************/
6994 __webpack_require__.r(__webpack_exports__);
6995 /* harmony import */ var _acf_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./_acf.js */ "./assets/src/js/_acf.js");
6996 /* harmony import */ var _acf_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_acf_js__WEBPACK_IMPORTED_MODULE_0__);
6997 /* harmony import */ var _acf_hooks_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./_acf-hooks.js */ "./assets/src/js/_acf-hooks.js");
6998 /* harmony import */ var _acf_hooks_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_acf_hooks_js__WEBPACK_IMPORTED_MODULE_1__);
6999 /* harmony import */ var _acf_model_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./_acf-model.js */ "./assets/src/js/_acf-model.js");
7000 /* harmony import */ var _acf_model_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_acf_model_js__WEBPACK_IMPORTED_MODULE_2__);
7001 /* harmony import */ var _acf_popup_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./_acf-popup.js */ "./assets/src/js/_acf-popup.js");
7002 /* harmony import */ var _acf_popup_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_acf_popup_js__WEBPACK_IMPORTED_MODULE_3__);
7003 /* harmony import */ var _acf_modal_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./_acf-modal.js */ "./assets/src/js/_acf-modal.js");
7004 /* harmony import */ var _acf_modal_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_acf_modal_js__WEBPACK_IMPORTED_MODULE_4__);
7005 /* harmony import */ var _acf_panel_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./_acf-panel.js */ "./assets/src/js/_acf-panel.js");
7006 /* harmony import */ var _acf_panel_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_acf_panel_js__WEBPACK_IMPORTED_MODULE_5__);
7007 /* harmony import */ var _acf_notice_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./_acf-notice.js */ "./assets/src/js/_acf-notice.js");
7008 /* harmony import */ var _acf_notice_js__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_acf_notice_js__WEBPACK_IMPORTED_MODULE_6__);
7009 /* harmony import */ var _acf_tooltip_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./_acf-tooltip.js */ "./assets/src/js/_acf-tooltip.js");
7010 /* harmony import */ var _acf_tooltip_js__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(_acf_tooltip_js__WEBPACK_IMPORTED_MODULE_7__);
7011
7012
7013
7014
7015
7016
7017
7018
7019 })();
7020
7021 /******/ })()
7022 ;
7023 //# sourceMappingURL=acf.js.map