PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.6
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.6
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / assets / js / dist / frontend / ai-assistant.js

ai-assistant.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.6, at assets/js/dist/frontend/ai-assistant.js

6,060 lines 217.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ (() => { // webpackBootstrap
2 /******/ var __webpack_modules__ = ({
3
4 /***/ "./assets/src/js/utils.js"
5 /*!********************************!*\
6 !*** ./assets/src/js/utils.js ***!
7 \********************************/
8 (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
9
10 "use strict";
11 __webpack_require__.r(__webpack_exports__);
12 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
13 /* harmony export */ debounce: () => (/* binding */ debounce),
14 /* harmony export */ eventHandlers: () => (/* binding */ eventHandlers),
15 /* harmony export */ fullScreenView: () => (/* binding */ fullScreenView),
16 /* harmony export */ getDataOfForm: () => (/* binding */ getDataOfForm),
17 /* harmony export */ getFieldKeysOfForm: () => (/* binding */ getFieldKeysOfForm),
18 /* harmony export */ listenElementCreated: () => (/* binding */ listenElementCreated),
19 /* harmony export */ listenElementViewed: () => (/* binding */ listenElementViewed),
20 /* harmony export */ lpAddQueryArgs: () => (/* binding */ lpAddQueryArgs),
21 /* harmony export */ lpAjaxParseJsonOld: () => (/* binding */ lpAjaxParseJsonOld),
22 /* harmony export */ lpClassName: () => (/* binding */ lpClassName),
23 /* harmony export */ lpFetchAPI: () => (/* binding */ lpFetchAPI),
24 /* harmony export */ lpGetCurrentURLNoParam: () => (/* binding */ lpGetCurrentURLNoParam),
25 /* harmony export */ lpOnElementReady: () => (/* binding */ lpOnElementReady),
26 /* harmony export */ lpSetLoadingEl: () => (/* binding */ lpSetLoadingEl),
27 /* harmony export */ lpShowHideEl: () => (/* binding */ lpShowHideEl),
28 /* harmony export */ mergeDataWithDatForm: () => (/* binding */ mergeDataWithDatForm),
29 /* harmony export */ toggleCollapse: () => (/* binding */ toggleCollapse),
30 /* harmony export */ toggleEnable: () => (/* binding */ toggleEnable)
31 /* harmony export */ });
32 /**
33 * Utils functions
34 *
35 * @param url
36 * @param data
37 * @param functions
38 * @since 4.2.5.1
39 * @version 1.0.7
40 */
41 const lpClassName = {
42 hidden: 'lp-hidden',
43 loading: 'loading',
44 elCollapse: 'lp-collapse',
45 elSectionToggle: '.lp-section-toggle',
46 elTriggerToggle: '.lp-trigger-toggle',
47 elBtnFullScreen: '.lp-btn-full-screen-view',
48 elFullScreen: 'lp-full-screen-view',
49 elBtnFullScreenClose: 'lp-full-screen-view__close'
50 };
51 const lpFetchAPI = (url, data = {}, functions = {}) => {
52 if ('function' === typeof functions.before) {
53 functions.before();
54 }
55 fetch(url, {
56 method: 'GET',
57 ...data
58 }).then(response => response.json()).then(response => {
59 if ('function' === typeof functions.success) {
60 functions.success(response);
61 }
62 }).catch(err => {
63 if ('function' === typeof functions.error) {
64 functions.error(err);
65 }
66 }).finally(() => {
67 if ('function' === typeof functions.completed) {
68 functions.completed();
69 }
70 });
71 };
72
73 /**
74 * Get current URL without params.
75 *
76 * @since 4.2.5.1
77 */
78 const lpGetCurrentURLNoParam = () => {
79 let currentUrl = window.location.href;
80 const hasParams = currentUrl.includes('?');
81 if (hasParams) {
82 currentUrl = currentUrl.split('?')[0];
83 }
84 return currentUrl;
85 };
86 const lpAddQueryArgs = (endpoint, args) => {
87 const url = new URL(endpoint);
88 Object.keys(args).forEach(arg => {
89 url.searchParams.set(arg, args[arg]);
90 });
91 return url;
92 };
93
94 /**
95 * Listen element viewed.
96 *
97 * @param el
98 * @param callback
99 * @since 4.2.5.8
100 */
101 const listenElementViewed = (el, callback) => {
102 const observerSeeItem = new IntersectionObserver(function (entries) {
103 for (const entry of entries) {
104 if (entry.isIntersecting) {
105 callback(entry);
106 }
107 }
108 });
109 observerSeeItem.observe(el);
110 };
111
112 /**
113 * Listen element created.
114 *
115 * @param callback
116 * @since 4.2.5.8
117 */
118 const listenElementCreated = callback => {
119 const observerCreateItem = new MutationObserver(function (mutations) {
120 mutations.forEach(function (mutation) {
121 if (mutation.addedNodes) {
122 mutation.addedNodes.forEach(function (node) {
123 if (node.nodeType === 1) {
124 callback(node);
125 }
126 });
127 }
128 });
129 });
130 observerCreateItem.observe(document, {
131 childList: true,
132 subtree: true
133 });
134 // End.
135 };
136
137 /**
138 * Listen element created.
139 *
140 * @param selector
141 * @param callback
142 * @since 4.2.7.1
143 */
144 const lpOnElementReady = (selector, callback) => {
145 const element = document.querySelector(selector);
146 if (element) {
147 callback(element);
148 return;
149 }
150 const observer = new MutationObserver((mutations, obs) => {
151 const element = document.querySelector(selector);
152 if (element) {
153 obs.disconnect();
154 callback(element);
155 }
156 });
157 observer.observe(document.documentElement, {
158 childList: true,
159 subtree: true
160 });
161 };
162
163 // Parse JSON from string with content include LP_AJAX_START.
164 const lpAjaxParseJsonOld = data => {
165 if (typeof data !== 'string') {
166 return data;
167 }
168 const m = String.raw({
169 raw: data
170 }).match(/<-- LP_AJAX_START -->(.*)<-- LP_AJAX_END -->/s);
171 try {
172 if (m) {
173 data = JSON.parse(m[1].replace(/(?:\r\n|\r|\n)/g, ''));
174 } else {
175 data = JSON.parse(data);
176 }
177 } catch (e) {
178 data = {};
179 }
180 return data;
181 };
182
183 // status 0: hide, 1: show
184 const lpShowHideEl = (el, status = 0) => {
185 if (!el) {
186 return;
187 }
188 if (!status) {
189 el.classList.add(lpClassName.hidden);
190 } else {
191 el.classList.remove(lpClassName.hidden);
192 }
193 };
194
195 // status 0: hide, 1: show
196 const lpSetLoadingEl = (el, status) => {
197 if (!el) {
198 return;
199 }
200 if (!status) {
201 el.classList.remove(lpClassName.loading);
202 } else {
203 el.classList.add(lpClassName.loading);
204 }
205 };
206
207 // Toggle collapse section
208 const toggleCollapse = (e, target, elTriggerClassName = '', elsExclude = [], callback) => {
209 if (!elTriggerClassName) {
210 elTriggerClassName = lpClassName.elTriggerToggle;
211 }
212
213 // Exclude elements, which should not trigger the collapse toggle
214 if (elsExclude && elsExclude.length > 0) {
215 for (const elExclude of elsExclude) {
216 if (target.closest(elExclude)) {
217 return;
218 }
219 }
220 }
221 const elTrigger = target.closest(elTriggerClassName);
222 if (!elTrigger) {
223 return;
224 }
225
226 //console.log( 'elTrigger', elTrigger );
227
228 const elSectionToggle = elTrigger.closest(`${lpClassName.elSectionToggle}`);
229 if (!elSectionToggle) {
230 return;
231 }
232 elSectionToggle.classList.toggle(`${lpClassName.elCollapse}`);
233 if ('function' === typeof callback) {
234 callback(elSectionToggle);
235 }
236 };
237
238 // Get data of form
239 const getDataOfForm = form => {
240 const dataSend = {};
241 const formData = new FormData(form);
242 for (const pair of formData.entries()) {
243 const key = pair[0];
244 const value = formData.getAll(key);
245 if (!dataSend.hasOwnProperty(key)) {
246 // Convert value array to string.
247 dataSend[key] = value.join(',');
248 }
249 }
250 return dataSend;
251 };
252
253 // Get field keys of form
254 const getFieldKeysOfForm = form => {
255 const keys = [];
256 const elements = form.elements;
257 for (let i = 0; i < elements.length; i++) {
258 const name = elements[i].name;
259 if (name && !keys.includes(name)) {
260 keys.push(name);
261 }
262 }
263 return keys;
264 };
265
266 // Merge data handle with data form.
267 const mergeDataWithDatForm = (elForm, dataHandle) => {
268 const dataForm = getDataOfForm(elForm);
269 const keys = getFieldKeysOfForm(elForm);
270 keys.forEach(key => {
271 if (!dataForm.hasOwnProperty(key)) {
272 delete dataHandle[key];
273 } else if (dataForm[key][0] === '') {
274 delete dataForm[key];
275 delete dataHandle[key];
276 }
277 });
278 dataHandle = {
279 ...dataHandle,
280 ...dataForm
281 };
282 return dataHandle;
283 };
284
285 /**
286 * Event trigger
287 * For each list of event handlers, listen event on document.
288 *
289 * eventName: 'click', 'change', ...
290 * eventHandlers = [ { selector: '.lp-button', callBack: function(){}, class: object } ]
291 *
292 * @param eventName
293 * @param eventHandlers
294 */
295 const eventHandlers = (eventName, eventHandlers) => {
296 document.addEventListener(eventName, e => {
297 const target = e.target;
298 let args = {
299 e,
300 target
301 };
302 eventHandlers.forEach(eventHandler => {
303 args = {
304 ...args,
305 ...eventHandler
306 };
307
308 //console.log( args );
309
310 // Check condition before call back
311 if (eventHandler.conditionBeforeCallBack) {
312 if (eventHandler.conditionBeforeCallBack(args) !== true) {
313 return;
314 }
315 }
316
317 // Special check for keydown event with checkIsEventEnter = true
318 if (eventName === 'keydown' && eventHandler.checkIsEventEnter) {
319 if (e.key !== 'Enter') {
320 return;
321 }
322 }
323 if (target.closest(eventHandler.selector)) {
324 if (eventHandler.class) {
325 // Call method of class, function callBack will understand exactly {this} is class object.
326 eventHandler.class[eventHandler.callBack](args);
327 } else {
328 // For send args is objected, {this} is eventHandler object, not class object.
329 eventHandler.callBack(args);
330 }
331 }
332 });
333 });
334 };
335
336 /**
337 * Debounce - delays function execution until after `wait` ms of inactivity.
338 *
339 * Each call resets the timer. Only the last call in a burst executes.
340 *
341 * USE CASES:
342 * - Search inputs, form validation, window resize
343 * - Multiple elements need independent timers
344 * - When you need to call with different arguments
345 *
346 * EXAMPLES:
347 * const debouncedSearch = debounce( (query) => fetchResults(query), 300 );
348 * searchInput.addEventListener('input', (e) => debouncedSearch(e.target.value));
349 *
350 * const debouncedResize = debounce( recalculateLayout, 250 );
351 * window.addEventListener('resize', debouncedResize);
352 *
353 * ⚠️ Create ONCE outside event handlers, not inside.
354 *
355 * @param {Function} func - Function to debounce (can be anonymous)
356 * @param {number} wait - Milliseconds to wait (default: 500)
357 * @return {Function} Debounced wrapper function
358 * @since 4.3.7
359 * @version 1.0.0
360 */
361 const debounce = (func, wait = 500) => {
362 let timer;
363 return args => {
364 clearTimeout(timer);
365 timer = setTimeout(() => func(args), wait);
366 };
367 };
368
369 /**
370 * Initialize lp-toggle-enable components.
371 *
372 * Finds all `.lp-toggle-enable` elements and wires up toggle behavior.
373 * Reads initial state from `data-enabled` attribute ("true"/"false").
374 * Calls `data-on-toggle` callback (if provided via options) on state change.
375 *
376 * HTML structure:
377 * <label class="lp-toggle-enable" data-enabled="true">
378 * <input type="checkbox" class="lp-toggle-enable__input" />
379 * <span class="lp-toggle-enable__track"></span>
380 * </label>
381 *
382 * @param {string} selector CSS selector for toggle elements (default: '.lp-toggle-enable')
383 * @param {Function} onToggle Optional callback( el, isEnabled ) called on state change
384 * @since 4.4.5
385 * @version 1.0.0
386 */
387 window.lpToggleEnableInit = 0;
388 const toggleEnable = (onToggle = null) => {
389 if (window.lpToggleEnableInit) {
390 return;
391 }
392 window.lpToggleEnableInit = 1;
393 const selector = '.lp-toggle-enable';
394 const updateUI = (toggle, isEnabled) => {
395 toggle.classList.toggle('is-enabled', isEnabled);
396 const input = toggle.querySelector('.lp-toggle-enable__input');
397 if (input) {
398 input.checked = isEnabled;
399 input.value = isEnabled ? '1' : '0';
400 }
401 };
402
403 // Delegate click handling via eventHandlers.
404 eventHandlers('click', [{
405 selector,
406 callBack: args => {
407 const {
408 e,
409 target
410 } = args;
411 const toggle = target.closest(selector);
412 if (!toggle || toggle.classList.contains('is-disabled')) {
413 return;
414 }
415 e.preventDefault();
416 const isEnabled = !toggle.classList.contains('is-enabled');
417 updateUI(toggle, isEnabled);
418 if ('function' === typeof onToggle) {
419 onToggle(toggle, isEnabled);
420 }
421 }
422 }]);
423 };
424
425 /**
426 * Initialize custom fullscreen view buttons.
427 *
428 * Delegates clicks on `.lp-btn-full-screen-view` buttons to
429 * `lpToggleFullscreenView`. Reads the `data-target` attribute to find the
430 * target element. Falls back to the button's parent element when
431 * `data-target` is not provided.
432 *
433 * @since 4.4.5
434 * @version 1.0.0
435 */
436 window.lpFullScreenViewInit = 0;
437 const fullScreenView = () => {
438 if (window.lpFullScreenViewInit) {
439 return;
440 }
441 window.lpFullScreenViewInit = 1;
442 let lastScrollY = 0;
443 const lpToggleFullscreenView = (elTarget, elBtnFullScreen = null) => {
444 const isFullscreen = elTarget.classList.contains(lpClassName.elFullScreen);
445 if (isFullscreen) {
446 elTarget.classList.remove(lpClassName.elFullScreen);
447 document.documentElement.classList.remove('lp-full-screen-active');
448 window.scrollTo(0, lastScrollY);
449 } else {
450 lastScrollY = window.scrollY;
451 elTarget.classList.add(lpClassName.elFullScreen);
452 document.documentElement.classList.add('lp-full-screen-active');
453 }
454 if (!isFullscreen) {
455 if (!elTarget.querySelector(`.${lpClassName.elBtnFullScreenClose}`)) {
456 const closeButton = document.createElement('button');
457 closeButton.type = 'button';
458 closeButton.className = lpClassName.elBtnFullScreenClose;
459 closeButton.setAttribute('aria-label', 'Close');
460 closeButton.innerHTML = lpData.i18n.closeButtonFullScreen || 'Close &times;';
461 closeButton.addEventListener('click', e => {
462 e.preventDefault();
463 lpToggleFullscreenView(elTarget);
464 });
465 elTarget.appendChild(closeButton);
466 }
467 } else {
468 const closeButton = elTarget.querySelector(`.${lpClassName.elBtnFullScreenClose}`);
469 if (closeButton) {
470 closeButton.remove();
471 }
472 }
473 };
474 eventHandlers('click', [{
475 selector: lpClassName.elBtnFullScreen,
476 callBack: args => {
477 const {
478 e,
479 target
480 } = args;
481 const elBtnFullScreen = target.closest(lpClassName.elBtnFullScreen);
482 if (!elBtnFullScreen) {
483 console.log('No full screen button found');
484 return;
485 }
486 e.preventDefault();
487 let elTarget = null;
488 const targetSelector = elBtnFullScreen.dataset.targetFullscreen;
489 console.log(targetSelector);
490 if (targetSelector) {
491 elTarget = document.querySelector(targetSelector);
492 }
493 if (!elTarget) {
494 console.log('No target element found');
495 return;
496 }
497 lpToggleFullscreenView(elTarget, elBtnFullScreen);
498 }
499 }]);
500 };
501
502 /***/ },
503
504 /***/ "./node_modules/sweetalert2/dist/sweetalert2.all.js"
505 /*!**********************************************************!*\
506 !*** ./node_modules/sweetalert2/dist/sweetalert2.all.js ***!
507 \**********************************************************/
508 (module) {
509
510 /*!
511 * sweetalert2 v11.26.17
512 * Released under the MIT License.
513 */
514 (function (global, factory) {
515 true ? module.exports = factory() :
516 0;
517 })(this, (function () { 'use strict';
518
519 function _assertClassBrand(e, t, n) {
520 if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
521 throw new TypeError("Private element is not present on this object");
522 }
523 function _checkPrivateRedeclaration(e, t) {
524 if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
525 }
526 function _classPrivateFieldGet2(s, a) {
527 return s.get(_assertClassBrand(s, a));
528 }
529 function _classPrivateFieldInitSpec(e, t, a) {
530 _checkPrivateRedeclaration(e, t), t.set(e, a);
531 }
532 function _classPrivateFieldSet2(s, a, r) {
533 return s.set(_assertClassBrand(s, a), r), r;
534 }
535
536 const RESTORE_FOCUS_TIMEOUT = 100;
537
538 /** @type {GlobalState} */
539 const globalState = {};
540 const focusPreviousActiveElement = () => {
541 if (globalState.previousActiveElement instanceof HTMLElement) {
542 globalState.previousActiveElement.focus();
543 globalState.previousActiveElement = null;
544 } else if (document.body) {
545 document.body.focus();
546 }
547 };
548
549 /**
550 * Restore previous active (focused) element
551 *
552 * @param {boolean} returnFocus
553 * @returns {Promise<void>}
554 */
555 const restoreActiveElement = returnFocus => {
556 return new Promise(resolve => {
557 if (!returnFocus) {
558 return resolve();
559 }
560 const x = window.scrollX;
561 const y = window.scrollY;
562 globalState.restoreFocusTimeout = setTimeout(() => {
563 focusPreviousActiveElement();
564 resolve();
565 }, RESTORE_FOCUS_TIMEOUT); // issues/900
566
567 window.scrollTo(x, y);
568 });
569 };
570
571 const swalPrefix = 'swal2-';
572
573 /**
574 * @typedef {Record<SwalClass, string>} SwalClasses
575 */
576
577 /**
578 * @typedef {'success' | 'warning' | 'info' | 'question' | 'error'} SwalIcon
579 * @typedef {Record<SwalIcon, string>} SwalIcons
580 */
581
582 /** @type {SwalClass[]} */
583 const classNames = ['container', 'shown', 'height-auto', 'iosfix', 'popup', 'modal', 'no-backdrop', 'no-transition', 'toast', 'toast-shown', 'show', 'hide', 'close', 'title', 'html-container', 'actions', 'confirm', 'deny', 'cancel', 'footer', 'icon', 'icon-content', 'image', 'input', 'file', 'range', 'select', 'radio', 'checkbox', 'label', 'textarea', 'inputerror', 'input-label', 'validation-message', 'progress-steps', 'active-progress-step', 'progress-step', 'progress-step-line', 'loader', 'loading', 'styled', 'top', 'top-start', 'top-end', 'top-left', 'top-right', 'center', 'center-start', 'center-end', 'center-left', 'center-right', 'bottom', 'bottom-start', 'bottom-end', 'bottom-left', 'bottom-right', 'grow-row', 'grow-column', 'grow-fullscreen', 'rtl', 'timer-progress-bar', 'timer-progress-bar-container', 'scrollbar-measure', 'icon-success', 'icon-warning', 'icon-info', 'icon-question', 'icon-error', 'draggable', 'dragging'];
584 const swalClasses = classNames.reduce((acc, className) => {
585 acc[className] = swalPrefix + className;
586 return acc;
587 }, /** @type {SwalClasses} */{});
588
589 /** @type {SwalIcon[]} */
590 const icons = ['success', 'warning', 'info', 'question', 'error'];
591 const iconTypes = icons.reduce((acc, icon) => {
592 acc[icon] = swalPrefix + icon;
593 return acc;
594 }, /** @type {SwalIcons} */{});
595
596 const consolePrefix = 'SweetAlert2:';
597
598 /**
599 * Capitalize the first letter of a string
600 *
601 * @param {string} str
602 * @returns {string}
603 */
604 const capitalizeFirstLetter = str => str.charAt(0).toUpperCase() + str.slice(1);
605
606 /**
607 * Standardize console warnings
608 *
609 * @param {string | string[]} message
610 */
611 const warn = message => {
612 console.warn(`${consolePrefix} ${typeof message === 'object' ? message.join(' ') : message}`);
613 };
614
615 /**
616 * Standardize console errors
617 *
618 * @param {string} message
619 */
620 const error = message => {
621 console.error(`${consolePrefix} ${message}`);
622 };
623
624 /**
625 * Private global state for `warnOnce`
626 *
627 * @type {string[]}
628 * @private
629 */
630 const previousWarnOnceMessages = [];
631
632 /**
633 * Show a console warning, but only if it hasn't already been shown
634 *
635 * @param {string} message
636 */
637 const warnOnce = message => {
638 if (!previousWarnOnceMessages.includes(message)) {
639 previousWarnOnceMessages.push(message);
640 warn(message);
641 }
642 };
643
644 /**
645 * Show a one-time console warning about deprecated params/methods
646 *
647 * @param {string} deprecatedParam
648 * @param {string?} useInstead
649 */
650 const warnAboutDeprecation = (deprecatedParam, useInstead = null) => {
651 warnOnce(`"${deprecatedParam}" is deprecated and will be removed in the next major release.${useInstead ? ` Use "${useInstead}" instead.` : ''}`);
652 };
653
654 /**
655 * If `arg` is a function, call it (with no arguments or context) and return the result.
656 * Otherwise, just pass the value through
657 *
658 * @param {(() => *) | *} arg
659 * @returns {*}
660 */
661 const callIfFunction = arg => typeof arg === 'function' ? arg() : arg;
662
663 /**
664 * @param {*} arg
665 * @returns {boolean}
666 */
667 const hasToPromiseFn = arg => arg && typeof arg.toPromise === 'function';
668
669 /**
670 * @param {*} arg
671 * @returns {Promise<*>}
672 */
673 const asPromise = arg => hasToPromiseFn(arg) ? arg.toPromise() : Promise.resolve(arg);
674
675 /**
676 * @param {*} arg
677 * @returns {boolean}
678 */
679 const isPromise = arg => arg && Promise.resolve(arg) === arg;
680
681 /**
682 * Gets the popup container which contains the backdrop and the popup itself.
683 *
684 * @returns {HTMLElement | null}
685 */
686 const getContainer = () => document.body.querySelector(`.${swalClasses.container}`);
687
688 /**
689 * @param {string} selectorString
690 * @returns {HTMLElement | null}
691 */
692 const elementBySelector = selectorString => {
693 const container = getContainer();
694 return container ? container.querySelector(selectorString) : null;
695 };
696
697 /**
698 * @param {string} className
699 * @returns {HTMLElement | null}
700 */
701 const elementByClass = className => {
702 return elementBySelector(`.${className}`);
703 };
704
705 /**
706 * @returns {HTMLElement | null}
707 */
708 const getPopup = () => elementByClass(swalClasses.popup);
709
710 /**
711 * @returns {HTMLElement | null}
712 */
713 const getIcon = () => elementByClass(swalClasses.icon);
714
715 /**
716 * @returns {HTMLElement | null}
717 */
718 const getIconContent = () => elementByClass(swalClasses['icon-content']);
719
720 /**
721 * @returns {HTMLElement | null}
722 */
723 const getTitle = () => elementByClass(swalClasses.title);
724
725 /**
726 * @returns {HTMLElement | null}
727 */
728 const getHtmlContainer = () => elementByClass(swalClasses['html-container']);
729
730 /**
731 * @returns {HTMLElement | null}
732 */
733 const getImage = () => elementByClass(swalClasses.image);
734
735 /**
736 * @returns {HTMLElement | null}
737 */
738 const getProgressSteps = () => elementByClass(swalClasses['progress-steps']);
739
740 /**
741 * @returns {HTMLElement | null}
742 */
743 const getValidationMessage = () => elementByClass(swalClasses['validation-message']);
744
745 /**
746 * @returns {HTMLButtonElement | null}
747 */
748 const getConfirmButton = () => (/** @type {HTMLButtonElement} */elementBySelector(`.${swalClasses.actions} .${swalClasses.confirm}`));
749
750 /**
751 * @returns {HTMLButtonElement | null}
752 */
753 const getCancelButton = () => (/** @type {HTMLButtonElement} */elementBySelector(`.${swalClasses.actions} .${swalClasses.cancel}`));
754
755 /**
756 * @returns {HTMLButtonElement | null}
757 */
758 const getDenyButton = () => (/** @type {HTMLButtonElement} */elementBySelector(`.${swalClasses.actions} .${swalClasses.deny}`));
759
760 /**
761 * @returns {HTMLElement | null}
762 */
763 const getInputLabel = () => elementByClass(swalClasses['input-label']);
764
765 /**
766 * @returns {HTMLElement | null}
767 */
768 const getLoader = () => elementBySelector(`.${swalClasses.loader}`);
769
770 /**
771 * @returns {HTMLElement | null}
772 */
773 const getActions = () => elementByClass(swalClasses.actions);
774
775 /**
776 * @returns {HTMLElement | null}
777 */
778 const getFooter = () => elementByClass(swalClasses.footer);
779
780 /**
781 * @returns {HTMLElement | null}
782 */
783 const getTimerProgressBar = () => elementByClass(swalClasses['timer-progress-bar']);
784
785 /**
786 * @returns {HTMLElement | null}
787 */
788 const getCloseButton = () => elementByClass(swalClasses.close);
789
790 // https://github.com/jkup/focusable/blob/master/index.js
791 const focusable = `
792 a[href],
793 area[href],
794 input:not([disabled]),
795 select:not([disabled]),
796 textarea:not([disabled]),
797 button:not([disabled]),
798 iframe,
799 object,
800 embed,
801 [tabindex="0"],
802 [contenteditable],
803 audio[controls],
804 video[controls],
805 summary
806 `;
807 /**
808 * @returns {HTMLElement[]}
809 */
810 const getFocusableElements = () => {
811 const popup = getPopup();
812 if (!popup) {
813 return [];
814 }
815 /** @type {NodeListOf<HTMLElement>} */
816 const focusableElementsWithTabindex = popup.querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])');
817 const focusableElementsWithTabindexSorted = Array.from(focusableElementsWithTabindex)
818 // sort according to tabindex
819 .sort((a, b) => {
820 const tabindexA = parseInt(a.getAttribute('tabindex') || '0');
821 const tabindexB = parseInt(b.getAttribute('tabindex') || '0');
822 if (tabindexA > tabindexB) {
823 return 1;
824 } else if (tabindexA < tabindexB) {
825 return -1;
826 }
827 return 0;
828 });
829
830 /** @type {NodeListOf<HTMLElement>} */
831 const otherFocusableElements = popup.querySelectorAll(focusable);
832 const otherFocusableElementsFiltered = Array.from(otherFocusableElements).filter(el => el.getAttribute('tabindex') !== '-1');
833 return [...new Set(focusableElementsWithTabindexSorted.concat(otherFocusableElementsFiltered))].filter(el => isVisible$1(el));
834 };
835
836 /**
837 * @returns {boolean}
838 */
839 const isModal = () => {
840 return hasClass(document.body, swalClasses.shown) && !hasClass(document.body, swalClasses['toast-shown']) && !hasClass(document.body, swalClasses['no-backdrop']);
841 };
842
843 /**
844 * @returns {boolean}
845 */
846 const isToast = () => {
847 const popup = getPopup();
848 if (!popup) {
849 return false;
850 }
851 return hasClass(popup, swalClasses.toast);
852 };
853
854 /**
855 * @returns {boolean}
856 */
857 const isLoading = () => {
858 const popup = getPopup();
859 if (!popup) {
860 return false;
861 }
862 return popup.hasAttribute('data-loading');
863 };
864
865 /**
866 * Securely set innerHTML of an element
867 * https://github.com/sweetalert2/sweetalert2/issues/1926
868 *
869 * @param {HTMLElement} elem
870 * @param {string} html
871 */
872 const setInnerHtml = (elem, html) => {
873 elem.textContent = '';
874 if (html) {
875 const parser = new DOMParser();
876 const parsed = parser.parseFromString(html, `text/html`);
877 const head = parsed.querySelector('head');
878 if (head) {
879 Array.from(head.childNodes).forEach(child => {
880 elem.appendChild(child);
881 });
882 }
883 const body = parsed.querySelector('body');
884 if (body) {
885 Array.from(body.childNodes).forEach(child => {
886 if (child instanceof HTMLVideoElement || child instanceof HTMLAudioElement) {
887 elem.appendChild(child.cloneNode(true)); // https://github.com/sweetalert2/sweetalert2/issues/2507
888 } else {
889 elem.appendChild(child);
890 }
891 });
892 }
893 }
894 };
895
896 /**
897 * @param {HTMLElement} elem
898 * @param {string} className
899 * @returns {boolean}
900 */
901 const hasClass = (elem, className) => {
902 if (!className) {
903 return false;
904 }
905 const classList = className.split(/\s+/);
906 for (let i = 0; i < classList.length; i++) {
907 if (!elem.classList.contains(classList[i])) {
908 return false;
909 }
910 }
911 return true;
912 };
913
914 /**
915 * @param {HTMLElement} elem
916 * @param {SweetAlertOptions} params
917 */
918 const removeCustomClasses = (elem, params) => {
919 Array.from(elem.classList).forEach(className => {
920 if (!Object.values(swalClasses).includes(className) && !Object.values(iconTypes).includes(className) && !Object.values(params.showClass || {}).includes(className)) {
921 elem.classList.remove(className);
922 }
923 });
924 };
925
926 /**
927 * @param {HTMLElement} elem
928 * @param {SweetAlertOptions} params
929 * @param {string} className
930 */
931 const applyCustomClass = (elem, params, className) => {
932 removeCustomClasses(elem, params);
933 if (!params.customClass) {
934 return;
935 }
936 const customClass = params.customClass[(/** @type {keyof SweetAlertCustomClass} */className)];
937 if (!customClass) {
938 return;
939 }
940 if (typeof customClass !== 'string' && !customClass.forEach) {
941 warn(`Invalid type of customClass.${className}! Expected string or iterable object, got "${typeof customClass}"`);
942 return;
943 }
944 addClass(elem, customClass);
945 };
946
947 /**
948 * @param {HTMLElement} popup
949 * @param {import('./renderers/renderInput').InputClass | SweetAlertInput} inputClass
950 * @returns {HTMLInputElement | null}
951 */
952 const getInput$1 = (popup, inputClass) => {
953 if (!inputClass) {
954 return null;
955 }
956 switch (inputClass) {
957 case 'select':
958 case 'textarea':
959 case 'file':
960 return popup.querySelector(`.${swalClasses.popup} > .${swalClasses[inputClass]}`);
961 case 'checkbox':
962 return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.checkbox} input`);
963 case 'radio':
964 return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.radio} input:checked`) || popup.querySelector(`.${swalClasses.popup} > .${swalClasses.radio} input:first-child`);
965 case 'range':
966 return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.range} input`);
967 default:
968 return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.input}`);
969 }
970 };
971
972 /**
973 * @param {HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement} input
974 */
975 const focusInput = input => {
976 input.focus();
977
978 // place cursor at end of text in text input
979 if (input.type !== 'file') {
980 // http://stackoverflow.com/a/2345915
981 const val = input.value;
982 input.value = '';
983 input.value = val;
984 }
985 };
986
987 /**
988 * @param {HTMLElement | HTMLElement[] | null} target
989 * @param {string | string[] | readonly string[] | undefined} classList
990 * @param {boolean} condition
991 */
992 const toggleClass = (target, classList, condition) => {
993 if (!target || !classList) {
994 return;
995 }
996 if (typeof classList === 'string') {
997 classList = classList.split(/\s+/).filter(Boolean);
998 }
999 classList.forEach(className => {
1000 if (Array.isArray(target)) {
1001 target.forEach(elem => {
1002 if (condition) {
1003 elem.classList.add(className);
1004 } else {
1005 elem.classList.remove(className);
1006 }
1007 });
1008 } else {
1009 if (condition) {
1010 target.classList.add(className);
1011 } else {
1012 target.classList.remove(className);
1013 }
1014 }
1015 });
1016 };
1017
1018 /**
1019 * @param {HTMLElement | HTMLElement[] | null} target
1020 * @param {string | string[] | readonly string[] | undefined} classList
1021 */
1022 const addClass = (target, classList) => {
1023 toggleClass(target, classList, true);
1024 };
1025
1026 /**
1027 * @param {HTMLElement | HTMLElement[] | null} target
1028 * @param {string | string[] | readonly string[] | undefined} classList
1029 */
1030 const removeClass = (target, classList) => {
1031 toggleClass(target, classList, false);
1032 };
1033
1034 /**
1035 * Get direct child of an element by class name
1036 *
1037 * @param {HTMLElement} elem
1038 * @param {string} className
1039 * @returns {HTMLElement | undefined}
1040 */
1041 const getDirectChildByClass = (elem, className) => {
1042 const children = Array.from(elem.children);
1043 for (let i = 0; i < children.length; i++) {
1044 const child = children[i];
1045 if (child instanceof HTMLElement && hasClass(child, className)) {
1046 return child;
1047 }
1048 }
1049 };
1050
1051 /**
1052 * @param {HTMLElement} elem
1053 * @param {string} property
1054 * @param {string | number | null | undefined} value
1055 */
1056 const applyNumericalStyle = (elem, property, value) => {
1057 if (value === `${parseInt(`${value}`)}`) {
1058 value = parseInt(value);
1059 }
1060 if (value || parseInt(`${value}`) === 0) {
1061 elem.style.setProperty(property, typeof value === 'number' ? `${value}px` : (/** @type {string} */value));
1062 } else {
1063 elem.style.removeProperty(property);
1064 }
1065 };
1066
1067 /**
1068 * @param {HTMLElement | null} elem
1069 * @param {string} display
1070 */
1071 const show = (elem, display = 'flex') => {
1072 if (!elem) {
1073 return;
1074 }
1075 elem.style.display = display;
1076 };
1077
1078 /**
1079 * @param {HTMLElement | null} elem
1080 */
1081 const hide = elem => {
1082 if (!elem) {
1083 return;
1084 }
1085 elem.style.display = 'none';
1086 };
1087
1088 /**
1089 * @param {HTMLElement | null} elem
1090 * @param {string} display
1091 */
1092 const showWhenInnerHtmlPresent = (elem, display = 'block') => {
1093 if (!elem) {
1094 return;
1095 }
1096 new MutationObserver(() => {
1097 toggle(elem, elem.innerHTML, display);
1098 }).observe(elem, {
1099 childList: true,
1100 subtree: true
1101 });
1102 };
1103
1104 /**
1105 * @param {HTMLElement} parent
1106 * @param {string} selector
1107 * @param {string} property
1108 * @param {string} value
1109 */
1110 const setStyle = (parent, selector, property, value) => {
1111 /** @type {HTMLElement | null} */
1112 const el = parent.querySelector(selector);
1113 if (el) {
1114 el.style.setProperty(property, value);
1115 }
1116 };
1117
1118 /**
1119 * @param {HTMLElement} elem
1120 * @param {boolean | string | null | undefined} condition
1121 * @param {string} display
1122 */
1123 const toggle = (elem, condition, display = 'flex') => {
1124 if (condition) {
1125 show(elem, display);
1126 } else {
1127 hide(elem);
1128 }
1129 };
1130
1131 /**
1132 * borrowed from jquery $(elem).is(':visible') implementation
1133 *
1134 * @param {HTMLElement | null} elem
1135 * @returns {boolean}
1136 */
1137 const isVisible$1 = elem => Boolean(elem && (elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length));
1138
1139 /**
1140 * @returns {boolean}
1141 */
1142 const allButtonsAreHidden = () => !isVisible$1(getConfirmButton()) && !isVisible$1(getDenyButton()) && !isVisible$1(getCancelButton());
1143
1144 /**
1145 * @param {HTMLElement} elem
1146 * @returns {boolean}
1147 */
1148 const isScrollable = elem => Boolean(elem.scrollHeight > elem.clientHeight);
1149
1150 /**
1151 * @param {HTMLElement} element
1152 * @param {HTMLElement} stopElement
1153 * @returns {boolean}
1154 */
1155 const selfOrParentIsScrollable = (element, stopElement) => {
1156 let parent = /** @type {HTMLElement | null} */element;
1157 while (parent && parent !== stopElement) {
1158 if (isScrollable(parent)) {
1159 return true;
1160 }
1161 parent = parent.parentElement;
1162 }
1163 return false;
1164 };
1165
1166 /**
1167 * borrowed from https://stackoverflow.com/a/46352119
1168 *
1169 * @param {HTMLElement} elem
1170 * @returns {boolean}
1171 */
1172 const hasCssAnimation = elem => {
1173 const style = window.getComputedStyle(elem);
1174 const animDuration = parseFloat(style.getPropertyValue('animation-duration') || '0');
1175 const transDuration = parseFloat(style.getPropertyValue('transition-duration') || '0');
1176 return animDuration > 0 || transDuration > 0;
1177 };
1178
1179 /**
1180 * @param {number} timer
1181 * @param {boolean} reset
1182 */
1183 const animateTimerProgressBar = (timer, reset = false) => {
1184 const timerProgressBar = getTimerProgressBar();
1185 if (!timerProgressBar) {
1186 return;
1187 }
1188 if (isVisible$1(timerProgressBar)) {
1189 if (reset) {
1190 timerProgressBar.style.transition = 'none';
1191 timerProgressBar.style.width = '100%';
1192 }
1193 setTimeout(() => {
1194 timerProgressBar.style.transition = `width ${timer / 1000}s linear`;
1195 timerProgressBar.style.width = '0%';
1196 }, 10);
1197 }
1198 };
1199 const stopTimerProgressBar = () => {
1200 const timerProgressBar = getTimerProgressBar();
1201 if (!timerProgressBar) {
1202 return;
1203 }
1204 const timerProgressBarWidth = parseInt(window.getComputedStyle(timerProgressBar).width);
1205 timerProgressBar.style.removeProperty('transition');
1206 timerProgressBar.style.width = '100%';
1207 const timerProgressBarFullWidth = parseInt(window.getComputedStyle(timerProgressBar).width);
1208 const timerProgressBarPercent = timerProgressBarWidth / timerProgressBarFullWidth * 100;
1209 timerProgressBar.style.width = `${timerProgressBarPercent}%`;
1210 };
1211
1212 /**
1213 * Detect Node env
1214 *
1215 * @returns {boolean}
1216 */
1217 const isNodeEnv = () => typeof window === 'undefined' || typeof document === 'undefined';
1218
1219 const sweetHTML = `
1220 <div aria-labelledby="${swalClasses.title}" aria-describedby="${swalClasses['html-container']}" class="${swalClasses.popup}" tabindex="-1">
1221 <button type="button" class="${swalClasses.close}"></button>
1222 <ul class="${swalClasses['progress-steps']}"></ul>
1223 <div class="${swalClasses.icon}"></div>
1224 <img class="${swalClasses.image}" />
1225 <h2 class="${swalClasses.title}" id="${swalClasses.title}"></h2>
1226 <div class="${swalClasses['html-container']}" id="${swalClasses['html-container']}"></div>
1227 <input class="${swalClasses.input}" id="${swalClasses.input}" />
1228 <input type="file" class="${swalClasses.file}" />
1229 <div class="${swalClasses.range}">
1230 <input type="range" />
1231 <output></output>
1232 </div>
1233 <select class="${swalClasses.select}" id="${swalClasses.select}"></select>
1234 <div class="${swalClasses.radio}"></div>
1235 <label class="${swalClasses.checkbox}">
1236 <input type="checkbox" id="${swalClasses.checkbox}" />
1237 <span class="${swalClasses.label}"></span>
1238 </label>
1239 <textarea class="${swalClasses.textarea}" id="${swalClasses.textarea}"></textarea>
1240 <div class="${swalClasses['validation-message']}" id="${swalClasses['validation-message']}"></div>
1241 <div class="${swalClasses.actions}">
1242 <div class="${swalClasses.loader}"></div>
1243 <button type="button" class="${swalClasses.confirm}"></button>
1244 <button type="button" class="${swalClasses.deny}"></button>
1245 <button type="button" class="${swalClasses.cancel}"></button>
1246 </div>
1247 <div class="${swalClasses.footer}"></div>
1248 <div class="${swalClasses['timer-progress-bar-container']}">
1249 <div class="${swalClasses['timer-progress-bar']}"></div>
1250 </div>
1251 </div>
1252 `.replace(/(^|\n)\s*/g, '');
1253
1254 /**
1255 * @returns {boolean}
1256 */
1257 const resetOldContainer = () => {
1258 const oldContainer = getContainer();
1259 if (!oldContainer) {
1260 return false;
1261 }
1262 oldContainer.remove();
1263 removeClass([document.documentElement, document.body], [swalClasses['no-backdrop'], swalClasses['toast-shown'],
1264 // @ts-ignore: 'has-column' is not defined in swalClasses but may be set dynamically
1265 swalClasses['has-column']]);
1266 return true;
1267 };
1268 const resetValidationMessage$1 = () => {
1269 if (globalState.currentInstance) {
1270 globalState.currentInstance.resetValidationMessage();
1271 }
1272 };
1273 const addInputChangeListeners = () => {
1274 const popup = getPopup();
1275 if (!popup) {
1276 return;
1277 }
1278 const input = getDirectChildByClass(popup, swalClasses.input);
1279 const file = getDirectChildByClass(popup, swalClasses.file);
1280 /** @type {HTMLInputElement | null} */
1281 const range = popup.querySelector(`.${swalClasses.range} input`);
1282 /** @type {HTMLOutputElement | null} */
1283 const rangeOutput = popup.querySelector(`.${swalClasses.range} output`);
1284 const select = getDirectChildByClass(popup, swalClasses.select);
1285 /** @type {HTMLInputElement | null} */
1286 const checkbox = popup.querySelector(`.${swalClasses.checkbox} input`);
1287 const textarea = getDirectChildByClass(popup, swalClasses.textarea);
1288 if (input) {
1289 input.oninput = resetValidationMessage$1;
1290 }
1291 if (file) {
1292 file.onchange = resetValidationMessage$1;
1293 }
1294 if (select) {
1295 select.onchange = resetValidationMessage$1;
1296 }
1297 if (checkbox) {
1298 checkbox.onchange = resetValidationMessage$1;
1299 }
1300 if (textarea) {
1301 textarea.oninput = resetValidationMessage$1;
1302 }
1303 if (range && rangeOutput) {
1304 range.oninput = () => {
1305 resetValidationMessage$1();
1306 rangeOutput.value = range.value;
1307 };
1308 range.onchange = () => {
1309 resetValidationMessage$1();
1310 rangeOutput.value = range.value;
1311 };
1312 }
1313 };
1314
1315 /**
1316 * @param {string | HTMLElement} target
1317 * @returns {HTMLElement}
1318 */
1319 const getTarget = target => {
1320 if (typeof target === 'string') {
1321 const element = document.querySelector(target);
1322 if (!element) {
1323 throw new Error(`Target element "${target}" not found`);
1324 }
1325 return /** @type {HTMLElement} */element;
1326 }
1327 return target;
1328 };
1329
1330 /**
1331 * @param {SweetAlertOptions} params
1332 */
1333 const setupAccessibility = params => {
1334 const popup = getPopup();
1335 if (!popup) {
1336 return;
1337 }
1338 popup.setAttribute('role', params.toast ? 'alert' : 'dialog');
1339 popup.setAttribute('aria-live', params.toast ? 'polite' : 'assertive');
1340 if (!params.toast) {
1341 popup.setAttribute('aria-modal', 'true');
1342 }
1343 };
1344
1345 /**
1346 * @param {HTMLElement} targetElement
1347 */
1348 const setupRTL = targetElement => {
1349 if (window.getComputedStyle(targetElement).direction === 'rtl') {
1350 addClass(getContainer(), swalClasses.rtl);
1351 globalState.isRTL = true;
1352 }
1353 };
1354
1355 /**
1356 * Add modal + backdrop to DOM
1357 *
1358 * @param {SweetAlertOptions} params
1359 */
1360 const init = params => {
1361 // Clean up the old popup container if it exists
1362 const oldContainerExisted = resetOldContainer();
1363 if (isNodeEnv()) {
1364 error('SweetAlert2 requires document to initialize');
1365 return;
1366 }
1367 const container = document.createElement('div');
1368 container.className = swalClasses.container;
1369 if (oldContainerExisted) {
1370 addClass(container, swalClasses['no-transition']);
1371 }
1372 setInnerHtml(container, sweetHTML);
1373 container.dataset['swal2Theme'] = params.theme;
1374 const targetElement = getTarget(params.target || 'body');
1375 targetElement.appendChild(container);
1376 if (params.topLayer) {
1377 container.setAttribute('popover', '');
1378 container.showPopover();
1379 }
1380 setupAccessibility(params);
1381 setupRTL(targetElement);
1382 addInputChangeListeners();
1383 };
1384
1385 /**
1386 * @param {HTMLElement | object | string} param
1387 * @param {HTMLElement} target
1388 */
1389 const parseHtmlToContainer = (param, target) => {
1390 // DOM element
1391 if (param instanceof HTMLElement) {
1392 target.appendChild(param);
1393 }
1394
1395 // Object
1396 else if (typeof param === 'object') {
1397 handleObject(param, target);
1398 }
1399
1400 // Plain string
1401 else if (param) {
1402 setInnerHtml(target, param);
1403 }
1404 };
1405
1406 /**
1407 * @param {object} param
1408 * @param {HTMLElement} target
1409 */
1410 const handleObject = (param, target) => {
1411 // JQuery element(s)
1412 if ('jquery' in param) {
1413 handleJqueryElem(target, param);
1414 }
1415
1416 // For other objects use their string representation
1417 else {
1418 setInnerHtml(target, param.toString());
1419 }
1420 };
1421
1422 /**
1423 * @param {HTMLElement} target
1424 * @param {any} elem
1425 */
1426 const handleJqueryElem = (target, elem) => {
1427 target.textContent = '';
1428 if (0 in elem) {
1429 for (let i = 0; i in elem; i++) {
1430 target.appendChild(elem[i].cloneNode(true));
1431 }
1432 } else {
1433 target.appendChild(elem.cloneNode(true));
1434 }
1435 };
1436
1437 /**
1438 * @param {SweetAlert} instance
1439 * @param {SweetAlertOptions} params
1440 */
1441 const renderActions = (instance, params) => {
1442 const actions = getActions();
1443 const loader = getLoader();
1444 if (!actions || !loader) {
1445 return;
1446 }
1447
1448 // Actions (buttons) wrapper
1449 if (!params.showConfirmButton && !params.showDenyButton && !params.showCancelButton) {
1450 hide(actions);
1451 } else {
1452 show(actions);
1453 }
1454
1455 // Custom class
1456 applyCustomClass(actions, params, 'actions');
1457
1458 // Render all the buttons
1459 renderButtons(actions, loader, params);
1460
1461 // Loader
1462 setInnerHtml(loader, params.loaderHtml || '');
1463 applyCustomClass(loader, params, 'loader');
1464 };
1465
1466 /**
1467 * @param {HTMLElement} actions
1468 * @param {HTMLElement} loader
1469 * @param {SweetAlertOptions} params
1470 */
1471 function renderButtons(actions, loader, params) {
1472 const confirmButton = getConfirmButton();
1473 const denyButton = getDenyButton();
1474 const cancelButton = getCancelButton();
1475 if (!confirmButton || !denyButton || !cancelButton) {
1476 return;
1477 }
1478
1479 // Render buttons
1480 renderButton(confirmButton, 'confirm', params);
1481 renderButton(denyButton, 'deny', params);
1482 renderButton(cancelButton, 'cancel', params);
1483 handleButtonsStyling(confirmButton, denyButton, cancelButton, params);
1484 if (params.reverseButtons) {
1485 if (params.toast) {
1486 actions.insertBefore(cancelButton, confirmButton);
1487 actions.insertBefore(denyButton, confirmButton);
1488 } else {
1489 actions.insertBefore(cancelButton, loader);
1490 actions.insertBefore(denyButton, loader);
1491 actions.insertBefore(confirmButton, loader);
1492 }
1493 }
1494 }
1495
1496 /**
1497 * @param {HTMLElement} confirmButton
1498 * @param {HTMLElement} denyButton
1499 * @param {HTMLElement} cancelButton
1500 * @param {SweetAlertOptions} params
1501 */
1502 function handleButtonsStyling(confirmButton, denyButton, cancelButton, params) {
1503 if (!params.buttonsStyling) {
1504 removeClass([confirmButton, denyButton, cancelButton], swalClasses.styled);
1505 return;
1506 }
1507 addClass([confirmButton, denyButton, cancelButton], swalClasses.styled);
1508
1509 // Apply custom background colors to action buttons
1510 if (params.confirmButtonColor) {
1511 confirmButton.style.setProperty('--swal2-confirm-button-background-color', params.confirmButtonColor);
1512 }
1513 if (params.denyButtonColor) {
1514 denyButton.style.setProperty('--swal2-deny-button-background-color', params.denyButtonColor);
1515 }
1516 if (params.cancelButtonColor) {
1517 cancelButton.style.setProperty('--swal2-cancel-button-background-color', params.cancelButtonColor);
1518 }
1519
1520 // Apply the outline color to action buttons
1521 applyOutlineColor(confirmButton);
1522 applyOutlineColor(denyButton);
1523 applyOutlineColor(cancelButton);
1524 }
1525
1526 /**
1527 * @param {HTMLElement} button
1528 */
1529 function applyOutlineColor(button) {
1530 const buttonStyle = window.getComputedStyle(button);
1531 if (buttonStyle.getPropertyValue('--swal2-action-button-focus-box-shadow')) {
1532 // If the button already has a custom outline color, no need to change it
1533 return;
1534 }
1535 const outlineColor = buttonStyle.backgroundColor.replace(/rgba?\((\d+), (\d+), (\d+).*/, 'rgba($1, $2, $3, 0.5)');
1536 button.style.setProperty('--swal2-action-button-focus-box-shadow', buttonStyle.getPropertyValue('--swal2-outline').replace(/ rgba\(.*/, ` ${outlineColor}`));
1537 }
1538
1539 /**
1540 * @param {HTMLElement} button
1541 * @param {'confirm' | 'deny' | 'cancel'} buttonType
1542 * @param {SweetAlertOptions} params
1543 */
1544 function renderButton(button, buttonType, params) {
1545 const buttonName = /** @type {'Confirm' | 'Deny' | 'Cancel'} */capitalizeFirstLetter(buttonType);
1546 toggle(button, params[`show${buttonName}Button`], 'inline-block');
1547 setInnerHtml(button, params[`${buttonType}ButtonText`] || ''); // Set caption text
1548 button.setAttribute('aria-label', params[`${buttonType}ButtonAriaLabel`] || ''); // ARIA label
1549
1550 // Add buttons custom classes
1551 button.className = swalClasses[buttonType];
1552 applyCustomClass(button, params, `${buttonType}Button`);
1553 }
1554
1555 /**
1556 * @param {SweetAlert} instance
1557 * @param {SweetAlertOptions} params
1558 */
1559 const renderCloseButton = (instance, params) => {
1560 const closeButton = getCloseButton();
1561 if (!closeButton) {
1562 return;
1563 }
1564 setInnerHtml(closeButton, params.closeButtonHtml || '');
1565
1566 // Custom class
1567 applyCustomClass(closeButton, params, 'closeButton');
1568 toggle(closeButton, params.showCloseButton);
1569 closeButton.setAttribute('aria-label', params.closeButtonAriaLabel || '');
1570 };
1571
1572 /**
1573 * @param {SweetAlert} instance
1574 * @param {SweetAlertOptions} params
1575 */
1576 const renderContainer = (instance, params) => {
1577 const container = getContainer();
1578 if (!container) {
1579 return;
1580 }
1581 handleBackdropParam(container, params.backdrop);
1582 handlePositionParam(container, params.position);
1583 handleGrowParam(container, params.grow);
1584
1585 // Custom class
1586 applyCustomClass(container, params, 'container');
1587 };
1588
1589 /**
1590 * @param {HTMLElement} container
1591 * @param {SweetAlertOptions['backdrop']} backdrop
1592 */
1593 function handleBackdropParam(container, backdrop) {
1594 if (typeof backdrop === 'string') {
1595 container.style.background = backdrop;
1596 } else if (!backdrop) {
1597 addClass([document.documentElement, document.body], swalClasses['no-backdrop']);
1598 }
1599 }
1600
1601 /**
1602 * @param {HTMLElement} container
1603 * @param {SweetAlertOptions['position']} position
1604 */
1605 function handlePositionParam(container, position) {
1606 if (!position) {
1607 return;
1608 }
1609 if (position in swalClasses) {
1610 addClass(container, swalClasses[position]);
1611 } else {
1612 warn('The "position" parameter is not valid, defaulting to "center"');
1613 addClass(container, swalClasses.center);
1614 }
1615 }
1616
1617 /**
1618 * @param {HTMLElement} container
1619 * @param {SweetAlertOptions['grow']} grow
1620 */
1621 function handleGrowParam(container, grow) {
1622 if (!grow) {
1623 return;
1624 }
1625 addClass(container, swalClasses[`grow-${grow}`]);
1626 }
1627
1628 /**
1629 * This module contains `WeakMap`s for each effectively-"private property" that a `Swal` has.
1630 * For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
1631 * This is the approach that Babel will probably take to implement private methods/fields
1632 * https://github.com/tc39/proposal-private-methods
1633 * https://github.com/babel/babel/pull/7555
1634 * Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
1635 * then we can use that language feature.
1636 */
1637
1638 var privateProps = {
1639 innerParams: new WeakMap(),
1640 domCache: new WeakMap()
1641 };
1642
1643 /// <reference path="../../../../sweetalert2.d.ts"/>
1644
1645
1646 /** @type {InputClass[]} */
1647 const inputClasses = ['input', 'file', 'range', 'select', 'radio', 'checkbox', 'textarea'];
1648
1649 /**
1650 * @param {SweetAlert} instance
1651 * @param {SweetAlertOptions} params
1652 */
1653 const renderInput = (instance, params) => {
1654 const popup = getPopup();
1655 if (!popup) {
1656 return;
1657 }
1658 const innerParams = privateProps.innerParams.get(instance);
1659 const rerender = !innerParams || params.input !== innerParams.input;
1660 inputClasses.forEach(inputClass => {
1661 const inputContainer = getDirectChildByClass(popup, swalClasses[inputClass]);
1662 if (!inputContainer) {
1663 return;
1664 }
1665
1666 // set attributes
1667 setAttributes(inputClass, params.inputAttributes);
1668
1669 // set class
1670 inputContainer.className = swalClasses[inputClass];
1671 if (rerender) {
1672 hide(inputContainer);
1673 }
1674 });
1675 if (params.input) {
1676 if (rerender) {
1677 showInput(params);
1678 }
1679 // set custom class
1680 setCustomClass(params);
1681 }
1682 };
1683
1684 /**
1685 * @param {SweetAlertOptions} params
1686 */
1687 const showInput = params => {
1688 if (!params.input) {
1689 return;
1690 }
1691 if (!renderInputType[params.input]) {
1692 error(`Unexpected type of input! Expected ${Object.keys(renderInputType).join(' | ')}, got "${params.input}"`);
1693 return;
1694 }
1695 const inputContainer = getInputContainer(params.input);
1696 if (!inputContainer) {
1697 return;
1698 }
1699 const input = renderInputType[params.input](inputContainer, params);
1700 show(inputContainer);
1701
1702 // input autofocus
1703 if (params.inputAutoFocus) {
1704 setTimeout(() => {
1705 focusInput(input);
1706 });
1707 }
1708 };
1709
1710 /**
1711 * @param {HTMLInputElement} input
1712 */
1713 const removeAttributes = input => {
1714 for (let i = 0; i < input.attributes.length; i++) {
1715 const attrName = input.attributes[i].name;
1716 if (!['id', 'type', 'value', 'style'].includes(attrName)) {
1717 input.removeAttribute(attrName);
1718 }
1719 }
1720 };
1721
1722 /**
1723 * @param {InputClass} inputClass
1724 * @param {SweetAlertOptions['inputAttributes']} inputAttributes
1725 */
1726 const setAttributes = (inputClass, inputAttributes) => {
1727 const popup = getPopup();
1728 if (!popup) {
1729 return;
1730 }
1731 const input = getInput$1(popup, inputClass);
1732 if (!input) {
1733 return;
1734 }
1735 removeAttributes(input);
1736 for (const attr in inputAttributes) {
1737 input.setAttribute(attr, inputAttributes[attr]);
1738 }
1739 };
1740
1741 /**
1742 * @param {SweetAlertOptions} params
1743 */
1744 const setCustomClass = params => {
1745 if (!params.input) {
1746 return;
1747 }
1748 const inputContainer = getInputContainer(params.input);
1749 if (inputContainer) {
1750 applyCustomClass(inputContainer, params, 'input');
1751 }
1752 };
1753
1754 /**
1755 * @param {HTMLInputElement | HTMLTextAreaElement} input
1756 * @param {SweetAlertOptions} params
1757 */
1758 const setInputPlaceholder = (input, params) => {
1759 if (!input.placeholder && params.inputPlaceholder) {
1760 input.placeholder = params.inputPlaceholder;
1761 }
1762 };
1763
1764 /**
1765 * @param {Input} input
1766 * @param {Input} prependTo
1767 * @param {SweetAlertOptions} params
1768 */
1769 const setInputLabel = (input, prependTo, params) => {
1770 if (params.inputLabel) {
1771 const label = document.createElement('label');
1772 const labelClass = swalClasses['input-label'];
1773 label.setAttribute('for', input.id);
1774 label.className = labelClass;
1775 if (typeof params.customClass === 'object') {
1776 addClass(label, params.customClass.inputLabel);
1777 }
1778 label.innerText = params.inputLabel;
1779 prependTo.insertAdjacentElement('beforebegin', label);
1780 }
1781 };
1782
1783 /**
1784 * @param {SweetAlertInput} inputType
1785 * @returns {HTMLElement | undefined}
1786 */
1787 const getInputContainer = inputType => {
1788 const popup = getPopup();
1789 if (!popup) {
1790 return;
1791 }
1792 return getDirectChildByClass(popup, swalClasses[(/** @type {SwalClass} */inputType)] || swalClasses.input);
1793 };
1794
1795 /**
1796 * @param {HTMLInputElement | HTMLOutputElement | HTMLTextAreaElement} input
1797 * @param {SweetAlertOptions['inputValue']} inputValue
1798 */
1799 const checkAndSetInputValue = (input, inputValue) => {
1800 if (['string', 'number'].includes(typeof inputValue)) {
1801 input.value = `${inputValue}`;
1802 } else if (!isPromise(inputValue)) {
1803 warn(`Unexpected type of inputValue! Expected "string", "number" or "Promise", got "${typeof inputValue}"`);
1804 }
1805 };
1806
1807 /** @type {Record<SweetAlertInput, (input: Input | HTMLElement, params: SweetAlertOptions) => Input>} */
1808 const renderInputType = {};
1809
1810 /**
1811 * @param {Input | HTMLElement} input
1812 * @param {SweetAlertOptions} params
1813 * @returns {Input}
1814 */
1815 renderInputType.text = renderInputType.email = renderInputType.password = renderInputType.number = renderInputType.tel = renderInputType.url = renderInputType.search = renderInputType.date = renderInputType['datetime-local'] = renderInputType.time = renderInputType.week = renderInputType.month = /** @type {(input: Input | HTMLElement, params: SweetAlertOptions) => Input} */
1816 (input, params) => {
1817 const inputElement = /** @type {HTMLInputElement} */input;
1818 checkAndSetInputValue(inputElement, params.inputValue);
1819 setInputLabel(inputElement, inputElement, params);
1820 setInputPlaceholder(inputElement, params);
1821 inputElement.type = /** @type {string} */params.input;
1822 return inputElement;
1823 };
1824
1825 /**
1826 * @param {Input | HTMLElement} input
1827 * @param {SweetAlertOptions} params
1828 * @returns {Input}
1829 */
1830 renderInputType.file = (input, params) => {
1831 const inputElement = /** @type {HTMLInputElement} */input;
1832 setInputLabel(inputElement, inputElement, params);
1833 setInputPlaceholder(inputElement, params);
1834 return inputElement;
1835 };
1836
1837 /**
1838 * @param {Input | HTMLElement} range
1839 * @param {SweetAlertOptions} params
1840 * @returns {Input}
1841 */
1842 renderInputType.range = (range, params) => {
1843 const rangeContainer = /** @type {HTMLElement} */range;
1844 const rangeInput = rangeContainer.querySelector('input');
1845 const rangeOutput = rangeContainer.querySelector('output');
1846 if (rangeInput) {
1847 checkAndSetInputValue(rangeInput, params.inputValue);
1848 rangeInput.type = /** @type {string} */params.input;
1849 setInputLabel(rangeInput, /** @type {Input} */range, params);
1850 }
1851 if (rangeOutput) {
1852 checkAndSetInputValue(rangeOutput, params.inputValue);
1853 }
1854 return /** @type {Input} */range;
1855 };
1856
1857 /**
1858 * @param {Input | HTMLElement} select
1859 * @param {SweetAlertOptions} params
1860 * @returns {Input}
1861 */
1862 renderInputType.select = (select, params) => {
1863 const selectElement = /** @type {HTMLSelectElement} */select;
1864 selectElement.textContent = '';
1865 if (params.inputPlaceholder) {
1866 const placeholder = document.createElement('option');
1867 setInnerHtml(placeholder, params.inputPlaceholder);
1868 placeholder.value = '';
1869 placeholder.disabled = true;
1870 placeholder.selected = true;
1871 selectElement.appendChild(placeholder);
1872 }
1873 setInputLabel(selectElement, selectElement, params);
1874 return selectElement;
1875 };
1876
1877 /**
1878 * @param {Input | HTMLElement} radio
1879 * @returns {Input}
1880 */
1881 renderInputType.radio = radio => {
1882 const radioElement = /** @type {HTMLElement} */radio;
1883 radioElement.textContent = '';
1884 return /** @type {Input} */radio;
1885 };
1886
1887 /**
1888 * @param {Input | HTMLElement} checkboxContainer
1889 * @param {SweetAlertOptions} params
1890 * @returns {Input}
1891 */
1892 renderInputType.checkbox = (checkboxContainer, params) => {
1893 const popup = getPopup();
1894 if (!popup) {
1895 throw new Error('Popup not found');
1896 }
1897 const checkbox = getInput$1(popup, 'checkbox');
1898 if (!checkbox) {
1899 throw new Error('Checkbox input not found');
1900 }
1901 checkbox.value = '1';
1902 checkbox.checked = Boolean(params.inputValue);
1903 const containerElement = /** @type {HTMLElement} */checkboxContainer;
1904 const label = containerElement.querySelector('span');
1905 if (label) {
1906 const placeholderOrLabel = params.inputPlaceholder || params.inputLabel;
1907 if (placeholderOrLabel) {
1908 setInnerHtml(label, placeholderOrLabel);
1909 }
1910 }
1911 return checkbox;
1912 };
1913
1914 /**
1915 * @param {Input | HTMLElement} textarea
1916 * @param {SweetAlertOptions} params
1917 * @returns {Input}
1918 */
1919 renderInputType.textarea = (textarea, params) => {
1920 const textareaElement = /** @type {HTMLTextAreaElement} */textarea;
1921 checkAndSetInputValue(textareaElement, params.inputValue);
1922 setInputPlaceholder(textareaElement, params);
1923 setInputLabel(textareaElement, textareaElement, params);
1924
1925 /**
1926 * @param {HTMLElement} el
1927 * @returns {number}
1928 */
1929 const getMargin = el => parseInt(window.getComputedStyle(el).marginLeft) + parseInt(window.getComputedStyle(el).marginRight);
1930
1931 // https://github.com/sweetalert2/sweetalert2/issues/2291
1932 setTimeout(() => {
1933 // https://github.com/sweetalert2/sweetalert2/issues/1699
1934 if ('MutationObserver' in window) {
1935 const popup = getPopup();
1936 if (!popup) {
1937 return;
1938 }
1939 const initialPopupWidth = parseInt(window.getComputedStyle(popup).width);
1940 const textareaResizeHandler = () => {
1941 // check if texarea is still in document (i.e. popup wasn't closed in the meantime)
1942 if (!document.body.contains(textareaElement)) {
1943 return;
1944 }
1945 const textareaWidth = textareaElement.offsetWidth + getMargin(textareaElement);
1946 const popupElement = getPopup();
1947 if (popupElement) {
1948 if (textareaWidth > initialPopupWidth) {
1949 popupElement.style.width = `${textareaWidth}px`;
1950 } else {
1951 applyNumericalStyle(popupElement, 'width', params.width);
1952 }
1953 }
1954 };
1955 new MutationObserver(textareaResizeHandler).observe(textareaElement, {
1956 attributes: true,
1957 attributeFilter: ['style']
1958 });
1959 }
1960 });
1961 return textareaElement;
1962 };
1963
1964 /**
1965 * @param {SweetAlert} instance
1966 * @param {SweetAlertOptions} params
1967 */
1968 const renderContent = (instance, params) => {
1969 const htmlContainer = getHtmlContainer();
1970 if (!htmlContainer) {
1971 return;
1972 }
1973 showWhenInnerHtmlPresent(htmlContainer);
1974 applyCustomClass(htmlContainer, params, 'htmlContainer');
1975
1976 // Content as HTML
1977 if (params.html) {
1978 parseHtmlToContainer(params.html, htmlContainer);
1979 show(htmlContainer, 'block');
1980 }
1981
1982 // Content as plain text
1983 else if (params.text) {
1984 htmlContainer.textContent = params.text;
1985 show(htmlContainer, 'block');
1986 }
1987
1988 // No content
1989 else {
1990 hide(htmlContainer);
1991 }
1992 renderInput(instance, params);
1993 };
1994
1995 /**
1996 * @param {SweetAlert} instance
1997 * @param {SweetAlertOptions} params
1998 */
1999 const renderFooter = (instance, params) => {
2000 const footer = getFooter();
2001 if (!footer) {
2002 return;
2003 }
2004 showWhenInnerHtmlPresent(footer);
2005 toggle(footer, Boolean(params.footer), 'block');
2006 if (params.footer) {
2007 parseHtmlToContainer(params.footer, footer);
2008 }
2009
2010 // Custom class
2011 applyCustomClass(footer, params, 'footer');
2012 };
2013
2014 /**
2015 * @param {SweetAlert} instance
2016 * @param {SweetAlertOptions} params
2017 */
2018 const renderIcon = (instance, params) => {
2019 const innerParams = privateProps.innerParams.get(instance);
2020 const icon = getIcon();
2021 if (!icon) {
2022 return;
2023 }
2024
2025 // if the given icon already rendered, apply the styling without re-rendering the icon
2026 if (innerParams && params.icon === innerParams.icon) {
2027 // Custom or default content
2028 setContent(icon, params);
2029 applyStyles(icon, params);
2030 return;
2031 }
2032 if (!params.icon && !params.iconHtml) {
2033 hide(icon);
2034 return;
2035 }
2036 if (params.icon && Object.keys(iconTypes).indexOf(params.icon) === -1) {
2037 error(`Unknown icon! Expected "success", "error", "warning", "info" or "question", got "${params.icon}"`);
2038 hide(icon);
2039 return;
2040 }
2041 show(icon);
2042
2043 // Custom or default content
2044 setContent(icon, params);
2045 applyStyles(icon, params);
2046
2047 // Animate icon
2048 addClass(icon, params.showClass && params.showClass.icon);
2049
2050 // Re-adjust the success icon on system theme change
2051 const colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
2052 colorSchemeQueryList.addEventListener('change', adjustSuccessIconBackgroundColor);
2053 };
2054
2055 /**
2056 * @param {HTMLElement} icon
2057 * @param {SweetAlertOptions} params
2058 */
2059 const applyStyles = (icon, params) => {
2060 for (const [iconType, iconClassName] of Object.entries(iconTypes)) {
2061 if (params.icon !== iconType) {
2062 removeClass(icon, iconClassName);
2063 }
2064 }
2065 addClass(icon, params.icon && iconTypes[params.icon]);
2066
2067 // Icon color
2068 setColor(icon, params);
2069
2070 // Success icon background color
2071 adjustSuccessIconBackgroundColor();
2072
2073 // Custom class
2074 applyCustomClass(icon, params, 'icon');
2075 };
2076
2077 // Adjust success icon background color to match the popup background color
2078 const adjustSuccessIconBackgroundColor = () => {
2079 const popup = getPopup();
2080 if (!popup) {
2081 return;
2082 }
2083 const popupBackgroundColor = window.getComputedStyle(popup).getPropertyValue('background-color');
2084 /** @type {NodeListOf<HTMLElement>} */
2085 const successIconParts = popup.querySelectorAll('[class^=swal2-success-circular-line], .swal2-success-fix');
2086 for (let i = 0; i < successIconParts.length; i++) {
2087 successIconParts[i].style.backgroundColor = popupBackgroundColor;
2088 }
2089 };
2090
2091 /**
2092 *
2093 * @param {SweetAlertOptions} params
2094 * @returns {string}
2095 */
2096 const successIconHtml = params => `
2097 ${params.animation ? '<div class="swal2-success-circular-line-left"></div>' : ''}
2098 <span class="swal2-success-line-tip"></span> <span class="swal2-success-line-long"></span>
2099 <div class="swal2-success-ring"></div>
2100 ${params.animation ? '<div class="swal2-success-fix"></div>' : ''}
2101 ${params.animation ? '<div class="swal2-success-circular-line-right"></div>' : ''}
2102 `;
2103 const errorIconHtml = `
2104 <span class="swal2-x-mark">
2105 <span class="swal2-x-mark-line-left"></span>
2106 <span class="swal2-x-mark-line-right"></span>
2107 </span>
2108 `;
2109
2110 /**
2111 * @param {HTMLElement} icon
2112 * @param {SweetAlertOptions} params
2113 */
2114 const setContent = (icon, params) => {
2115 if (!params.icon && !params.iconHtml) {
2116 return;
2117 }
2118 let oldContent = icon.innerHTML;
2119 let newContent = '';
2120 if (params.iconHtml) {
2121 newContent = iconContent(params.iconHtml);
2122 } else if (params.icon === 'success') {
2123 newContent = successIconHtml(params);
2124 oldContent = oldContent.replace(/ style=".*?"/g, ''); // undo adjustSuccessIconBackgroundColor()
2125 } else if (params.icon === 'error') {
2126 newContent = errorIconHtml;
2127 } else if (params.icon) {
2128 const defaultIconHtml = {
2129 question: '?',
2130 warning: '!',
2131 info: 'i'
2132 };
2133 newContent = iconContent(defaultIconHtml[params.icon]);
2134 }
2135 if (oldContent.trim() !== newContent.trim()) {
2136 setInnerHtml(icon, newContent);
2137 }
2138 };
2139
2140 /**
2141 * @param {HTMLElement} icon
2142 * @param {SweetAlertOptions} params
2143 */
2144 const setColor = (icon, params) => {
2145 if (!params.iconColor) {
2146 return;
2147 }
2148 icon.style.color = params.iconColor;
2149 icon.style.borderColor = params.iconColor;
2150 for (const sel of ['.swal2-success-line-tip', '.swal2-success-line-long', '.swal2-x-mark-line-left', '.swal2-x-mark-line-right']) {
2151 setStyle(icon, sel, 'background-color', params.iconColor);
2152 }
2153 setStyle(icon, '.swal2-success-ring', 'border-color', params.iconColor);
2154 };
2155
2156 /**
2157 * @param {string} content
2158 * @returns {string}
2159 */
2160 const iconContent = content => `<div class="${swalClasses['icon-content']}">${content}</div>`;
2161
2162 /**
2163 * @param {SweetAlert} instance
2164 * @param {SweetAlertOptions} params
2165 */
2166 const renderImage = (instance, params) => {
2167 const image = getImage();
2168 if (!image) {
2169 return;
2170 }
2171 if (!params.imageUrl) {
2172 hide(image);
2173 return;
2174 }
2175 show(image, '');
2176
2177 // Src, alt
2178 image.setAttribute('src', params.imageUrl);
2179 image.setAttribute('alt', params.imageAlt || '');
2180
2181 // Width, height
2182 applyNumericalStyle(image, 'width', params.imageWidth);
2183 applyNumericalStyle(image, 'height', params.imageHeight);
2184
2185 // Class
2186 image.className = swalClasses.image;
2187 applyCustomClass(image, params, 'image');
2188 };
2189
2190 let dragging = false;
2191 let mousedownX = 0;
2192 let mousedownY = 0;
2193 let initialX = 0;
2194 let initialY = 0;
2195
2196 /**
2197 * @param {HTMLElement} popup
2198 */
2199 const addDraggableListeners = popup => {
2200 popup.addEventListener('mousedown', down);
2201 document.body.addEventListener('mousemove', move);
2202 popup.addEventListener('mouseup', up);
2203 popup.addEventListener('touchstart', down);
2204 document.body.addEventListener('touchmove', move);
2205 popup.addEventListener('touchend', up);
2206 };
2207
2208 /**
2209 * @param {HTMLElement} popup
2210 */
2211 const removeDraggableListeners = popup => {
2212 popup.removeEventListener('mousedown', down);
2213 document.body.removeEventListener('mousemove', move);
2214 popup.removeEventListener('mouseup', up);
2215 popup.removeEventListener('touchstart', down);
2216 document.body.removeEventListener('touchmove', move);
2217 popup.removeEventListener('touchend', up);
2218 };
2219
2220 /**
2221 * @param {MouseEvent | TouchEvent} event
2222 */
2223 const down = event => {
2224 const popup = getPopup();
2225 if (!popup) {
2226 return;
2227 }
2228 const icon = getIcon();
2229 if (event.target === popup || icon && icon.contains(/** @type {HTMLElement} */event.target)) {
2230 dragging = true;
2231 const clientXY = getClientXY(event);
2232 mousedownX = clientXY.clientX;
2233 mousedownY = clientXY.clientY;
2234 initialX = parseInt(popup.style.insetInlineStart) || 0;
2235 initialY = parseInt(popup.style.insetBlockStart) || 0;
2236 addClass(popup, 'swal2-dragging');
2237 }
2238 };
2239
2240 /**
2241 * @param {MouseEvent | TouchEvent} event
2242 */
2243 const move = event => {
2244 const popup = getPopup();
2245 if (!popup) {
2246 return;
2247 }
2248 if (dragging) {
2249 let {
2250 clientX,
2251 clientY
2252 } = getClientXY(event);
2253 const deltaX = clientX - mousedownX;
2254 // In RTL mode, negate the horizontal delta since insetInlineStart refers to the right edge
2255 popup.style.insetInlineStart = `${initialX + (globalState.isRTL ? -deltaX : deltaX)}px`;
2256 popup.style.insetBlockStart = `${initialY + (clientY - mousedownY)}px`;
2257 }
2258 };
2259 const up = () => {
2260 const popup = getPopup();
2261 dragging = false;
2262 removeClass(popup, 'swal2-dragging');
2263 };
2264
2265 /**
2266 * @param {MouseEvent | TouchEvent} event
2267 * @returns {{ clientX: number, clientY: number }}
2268 */
2269 const getClientXY = event => {
2270 let clientX = 0,
2271 clientY = 0;
2272 if (event.type.startsWith('mouse')) {
2273 clientX = /** @type {MouseEvent} */event.clientX;
2274 clientY = /** @type {MouseEvent} */event.clientY;
2275 } else if (event.type.startsWith('touch')) {
2276 clientX = /** @type {TouchEvent} */event.touches[0].clientX;
2277 clientY = /** @type {TouchEvent} */event.touches[0].clientY;
2278 }
2279 return {
2280 clientX,
2281 clientY
2282 };
2283 };
2284
2285 /**
2286 * @param {SweetAlert} instance
2287 * @param {SweetAlertOptions} params
2288 */
2289 const renderPopup = (instance, params) => {
2290 const container = getContainer();
2291 const popup = getPopup();
2292 if (!container || !popup) {
2293 return;
2294 }
2295
2296 // Width
2297 // https://github.com/sweetalert2/sweetalert2/issues/2170
2298 if (params.toast) {
2299 applyNumericalStyle(container, 'width', params.width);
2300 popup.style.width = '100%';
2301 const loader = getLoader();
2302 if (loader) {
2303 popup.insertBefore(loader, getIcon());
2304 }
2305 } else {
2306 applyNumericalStyle(popup, 'width', params.width);
2307 }
2308
2309 // Padding
2310 applyNumericalStyle(popup, 'padding', params.padding);
2311
2312 // Color
2313 if (params.color) {
2314 popup.style.color = params.color;
2315 }
2316
2317 // Background
2318 if (params.background) {
2319 popup.style.background = params.background;
2320 }
2321 hide(getValidationMessage());
2322
2323 // Classes
2324 addClasses$1(popup, params);
2325 if (params.draggable && !params.toast) {
2326 addClass(popup, swalClasses.draggable);
2327 addDraggableListeners(popup);
2328 } else {
2329 removeClass(popup, swalClasses.draggable);
2330 removeDraggableListeners(popup);
2331 }
2332 };
2333
2334 /**
2335 * @param {HTMLElement} popup
2336 * @param {SweetAlertOptions} params
2337 */
2338 const addClasses$1 = (popup, params) => {
2339 const showClass = params.showClass || {};
2340 // Default Class + showClass when updating Swal.update({})
2341 popup.className = `${swalClasses.popup} ${isVisible$1(popup) ? showClass.popup : ''}`;
2342 if (params.toast) {
2343 addClass([document.documentElement, document.body], swalClasses['toast-shown']);
2344 addClass(popup, swalClasses.toast);
2345 } else {
2346 addClass(popup, swalClasses.modal);
2347 }
2348
2349 // Custom class
2350 applyCustomClass(popup, params, 'popup');
2351 // TODO: remove in the next major
2352 if (typeof params.customClass === 'string') {
2353 addClass(popup, params.customClass);
2354 }
2355
2356 // Icon class (#1842)
2357 if (params.icon) {
2358 addClass(popup, swalClasses[`icon-${params.icon}`]);
2359 }
2360 };
2361
2362 /**
2363 * @param {SweetAlert} instance
2364 * @param {SweetAlertOptions} params
2365 */
2366 const renderProgressSteps = (instance, params) => {
2367 const progressStepsContainer = getProgressSteps();
2368 if (!progressStepsContainer) {
2369 return;
2370 }
2371 const {
2372 progressSteps,
2373 currentProgressStep
2374 } = params;
2375 if (!progressSteps || progressSteps.length === 0 || currentProgressStep === undefined) {
2376 hide(progressStepsContainer);
2377 return;
2378 }
2379 show(progressStepsContainer);
2380 progressStepsContainer.textContent = '';
2381 if (currentProgressStep >= progressSteps.length) {
2382 warn('Invalid currentProgressStep parameter, it should be less than progressSteps.length ' + '(currentProgressStep like JS arrays starts from 0)');
2383 }
2384 progressSteps.forEach((step, index) => {
2385 const stepEl = createStepElement(step);
2386 progressStepsContainer.appendChild(stepEl);
2387 if (index === currentProgressStep) {
2388 addClass(stepEl, swalClasses['active-progress-step']);
2389 }
2390 if (index !== progressSteps.length - 1) {
2391 const lineEl = createLineElement(params);
2392 progressStepsContainer.appendChild(lineEl);
2393 }
2394 });
2395 };
2396
2397 /**
2398 * @param {string} step
2399 * @returns {HTMLLIElement}
2400 */
2401 const createStepElement = step => {
2402 const stepEl = document.createElement('li');
2403 addClass(stepEl, swalClasses['progress-step']);
2404 setInnerHtml(stepEl, step);
2405 return stepEl;
2406 };
2407
2408 /**
2409 * @param {SweetAlertOptions} params
2410 * @returns {HTMLLIElement}
2411 */
2412 const createLineElement = params => {
2413 const lineEl = document.createElement('li');
2414 addClass(lineEl, swalClasses['progress-step-line']);
2415 if (params.progressStepsDistance) {
2416 applyNumericalStyle(lineEl, 'width', params.progressStepsDistance);
2417 }
2418 return lineEl;
2419 };
2420
2421 /**
2422 * @param {SweetAlert} instance
2423 * @param {SweetAlertOptions} params
2424 */
2425 const renderTitle = (instance, params) => {
2426 const title = getTitle();
2427 if (!title) {
2428 return;
2429 }
2430 showWhenInnerHtmlPresent(title);
2431 toggle(title, Boolean(params.title || params.titleText), 'block');
2432 if (params.title) {
2433 parseHtmlToContainer(params.title, title);
2434 }
2435 if (params.titleText) {
2436 title.innerText = params.titleText;
2437 }
2438
2439 // Custom class
2440 applyCustomClass(title, params, 'title');
2441 };
2442
2443 /**
2444 * @param {SweetAlert} instance
2445 * @param {SweetAlertOptions} params
2446 */
2447 const render = (instance, params) => {
2448 var _globalState$eventEmi;
2449 renderPopup(instance, params);
2450 renderContainer(instance, params);
2451 renderProgressSteps(instance, params);
2452 renderIcon(instance, params);
2453 renderImage(instance, params);
2454 renderTitle(instance, params);
2455 renderCloseButton(instance, params);
2456 renderContent(instance, params);
2457 renderActions(instance, params);
2458 renderFooter(instance, params);
2459 const popup = getPopup();
2460 if (typeof params.didRender === 'function' && popup) {
2461 params.didRender(popup);
2462 }
2463 (_globalState$eventEmi = globalState.eventEmitter) === null || _globalState$eventEmi === void 0 || _globalState$eventEmi.emit('didRender', popup);
2464 };
2465
2466 /*
2467 * Global function to determine if SweetAlert2 popup is shown
2468 */
2469 const isVisible = () => {
2470 return isVisible$1(getPopup());
2471 };
2472
2473 /*
2474 * Global function to click 'Confirm' button
2475 */
2476 const clickConfirm = () => {
2477 var _dom$getConfirmButton;
2478 return (_dom$getConfirmButton = getConfirmButton()) === null || _dom$getConfirmButton === void 0 ? void 0 : _dom$getConfirmButton.click();
2479 };
2480
2481 /*
2482 * Global function to click 'Deny' button
2483 */
2484 const clickDeny = () => {
2485 var _dom$getDenyButton;
2486 return (_dom$getDenyButton = getDenyButton()) === null || _dom$getDenyButton === void 0 ? void 0 : _dom$getDenyButton.click();
2487 };
2488
2489 /*
2490 * Global function to click 'Cancel' button
2491 */
2492 const clickCancel = () => {
2493 var _dom$getCancelButton;
2494 return (_dom$getCancelButton = getCancelButton()) === null || _dom$getCancelButton === void 0 ? void 0 : _dom$getCancelButton.click();
2495 };
2496
2497 /** @type {Record<DismissReason, DismissReason>} */
2498 const DismissReason = Object.freeze({
2499 cancel: 'cancel',
2500 backdrop: 'backdrop',
2501 close: 'close',
2502 esc: 'esc',
2503 timer: 'timer'
2504 });
2505
2506 /**
2507 * @param {GlobalState} globalState
2508 */
2509 const removeKeydownHandler = globalState => {
2510 if (globalState.keydownTarget && globalState.keydownHandlerAdded && globalState.keydownHandler) {
2511 const handler = /** @type {EventListenerOrEventListenerObject} */ /** @type {unknown} */globalState.keydownHandler;
2512 globalState.keydownTarget.removeEventListener('keydown', handler, {
2513 capture: globalState.keydownListenerCapture
2514 });
2515 globalState.keydownHandlerAdded = false;
2516 }
2517 };
2518
2519 /**
2520 * @param {GlobalState} globalState
2521 * @param {SweetAlertOptions} innerParams
2522 * @param {(dismiss: DismissReason) => void} dismissWith
2523 */
2524 const addKeydownHandler = (globalState, innerParams, dismissWith) => {
2525 removeKeydownHandler(globalState);
2526 if (!innerParams.toast) {
2527 /** @type {(this: HTMLElement, event: KeyboardEvent) => void} */
2528 const handler = e => keydownHandler(innerParams, e, dismissWith);
2529 globalState.keydownHandler = handler;
2530 const target = innerParams.keydownListenerCapture ? window : getPopup();
2531 if (target) {
2532 globalState.keydownTarget = target;
2533 globalState.keydownListenerCapture = innerParams.keydownListenerCapture;
2534 const eventHandler = /** @type {EventListenerOrEventListenerObject} */ /** @type {unknown} */handler;
2535 globalState.keydownTarget.addEventListener('keydown', eventHandler, {
2536 capture: globalState.keydownListenerCapture
2537 });
2538 globalState.keydownHandlerAdded = true;
2539 }
2540 }
2541 };
2542
2543 /**
2544 * @param {number} index
2545 * @param {number} increment
2546 */
2547 const setFocus = (index, increment) => {
2548 var _dom$getPopup;
2549 const focusableElements = getFocusableElements();
2550 // search for visible elements and select the next possible match
2551 if (focusableElements.length) {
2552 index = index + increment;
2553
2554 // shift + tab when .swal2-popup is focused
2555 if (index === -2) {
2556 index = focusableElements.length - 1;
2557 }
2558
2559 // rollover to first item
2560 if (index === focusableElements.length) {
2561 index = 0;
2562
2563 // go to last item
2564 } else if (index === -1) {
2565 index = focusableElements.length - 1;
2566 }
2567 focusableElements[index].focus();
2568 return;
2569 }
2570 // no visible focusable elements, focus the popup
2571 (_dom$getPopup = getPopup()) === null || _dom$getPopup === void 0 || _dom$getPopup.focus();
2572 };
2573 const arrowKeysNextButton = ['ArrowRight', 'ArrowDown'];
2574 const arrowKeysPreviousButton = ['ArrowLeft', 'ArrowUp'];
2575
2576 /**
2577 * @param {SweetAlertOptions} innerParams
2578 * @param {KeyboardEvent} event
2579 * @param {(dismiss: DismissReason) => void} dismissWith
2580 */
2581 const keydownHandler = (innerParams, event, dismissWith) => {
2582 if (!innerParams) {
2583 return; // This instance has already been destroyed
2584 }
2585
2586 // Ignore keydown during IME composition
2587 // https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event#ignoring_keydown_during_ime_composition
2588 // https://github.com/sweetalert2/sweetalert2/issues/720
2589 // https://github.com/sweetalert2/sweetalert2/issues/2406
2590 if (event.isComposing || event.keyCode === 229) {
2591 return;
2592 }
2593 if (innerParams.stopKeydownPropagation) {
2594 event.stopPropagation();
2595 }
2596
2597 // ENTER
2598 if (event.key === 'Enter') {
2599 handleEnter(event, innerParams);
2600 }
2601
2602 // TAB
2603 else if (event.key === 'Tab') {
2604 handleTab(event);
2605 }
2606
2607 // ARROWS - switch focus between buttons
2608 else if ([...arrowKeysNextButton, ...arrowKeysPreviousButton].includes(event.key)) {
2609 handleArrows(event.key);
2610 }
2611
2612 // ESC
2613 else if (event.key === 'Escape') {
2614 handleEsc(event, innerParams, dismissWith);
2615 }
2616 };
2617
2618 /**
2619 * @param {KeyboardEvent} event
2620 * @param {SweetAlertOptions} innerParams
2621 */
2622 const handleEnter = (event, innerParams) => {
2623 // https://github.com/sweetalert2/sweetalert2/issues/2386
2624 if (!callIfFunction(innerParams.allowEnterKey)) {
2625 return;
2626 }
2627 const popup = getPopup();
2628 if (!popup || !innerParams.input) {
2629 return;
2630 }
2631 const input = getInput$1(popup, innerParams.input);
2632 if (event.target && input && event.target instanceof HTMLElement && event.target.outerHTML === input.outerHTML) {
2633 if (['textarea', 'file'].includes(innerParams.input)) {
2634 return; // do not submit
2635 }
2636 clickConfirm();
2637 event.preventDefault();
2638 }
2639 };
2640
2641 /**
2642 * @param {KeyboardEvent} event
2643 */
2644 const handleTab = event => {
2645 const targetElement = event.target;
2646 const focusableElements = getFocusableElements();
2647 let btnIndex = -1;
2648 for (let i = 0; i < focusableElements.length; i++) {
2649 if (targetElement === focusableElements[i]) {
2650 btnIndex = i;
2651 break;
2652 }
2653 }
2654
2655 // Cycle to the next button
2656 if (!event.shiftKey) {
2657 setFocus(btnIndex, 1);
2658 }
2659
2660 // Cycle to the prev button
2661 else {
2662 setFocus(btnIndex, -1);
2663 }
2664 event.stopPropagation();
2665 event.preventDefault();
2666 };
2667
2668 /**
2669 * @param {string} key
2670 */
2671 const handleArrows = key => {
2672 const actions = getActions();
2673 const confirmButton = getConfirmButton();
2674 const denyButton = getDenyButton();
2675 const cancelButton = getCancelButton();
2676 if (!actions || !confirmButton || !denyButton || !cancelButton) {
2677 return;
2678 }
2679 /** @type HTMLElement[] */
2680 const buttons = [confirmButton, denyButton, cancelButton];
2681 if (document.activeElement instanceof HTMLElement && !buttons.includes(document.activeElement)) {
2682 return;
2683 }
2684 const sibling = arrowKeysNextButton.includes(key) ? 'nextElementSibling' : 'previousElementSibling';
2685 let buttonToFocus = document.activeElement;
2686 if (!buttonToFocus) {
2687 return;
2688 }
2689 for (let i = 0; i < actions.children.length; i++) {
2690 buttonToFocus = buttonToFocus[sibling];
2691 if (!buttonToFocus) {
2692 return;
2693 }
2694 if (buttonToFocus instanceof HTMLButtonElement && isVisible$1(buttonToFocus)) {
2695 break;
2696 }
2697 }
2698 if (buttonToFocus instanceof HTMLButtonElement) {
2699 buttonToFocus.focus();
2700 }
2701 };
2702
2703 /**
2704 * @param {KeyboardEvent} event
2705 * @param {SweetAlertOptions} innerParams
2706 * @param {(dismiss: DismissReason) => void} dismissWith
2707 */
2708 const handleEsc = (event, innerParams, dismissWith) => {
2709 event.preventDefault();
2710 if (callIfFunction(innerParams.allowEscapeKey)) {
2711 dismissWith(DismissReason.esc);
2712 }
2713 };
2714
2715 /**
2716 * This module contains `WeakMap`s for each effectively-"private property" that a `Swal` has.
2717 * For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
2718 * This is the approach that Babel will probably take to implement private methods/fields
2719 * https://github.com/tc39/proposal-private-methods
2720 * https://github.com/babel/babel/pull/7555
2721 * Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
2722 * then we can use that language feature.
2723 */
2724
2725 var privateMethods = {
2726 swalPromiseResolve: new WeakMap(),
2727 swalPromiseReject: new WeakMap()
2728 };
2729
2730 // From https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/
2731 // Adding aria-hidden="true" to elements outside of the active modal dialog ensures that
2732 // elements not within the active modal dialog will not be surfaced if a user opens a screen
2733 // reader’s list of elements (headings, form controls, landmarks, etc.) in the document.
2734
2735 const setAriaHidden = () => {
2736 const container = getContainer();
2737 const bodyChildren = Array.from(document.body.children);
2738 bodyChildren.forEach(el => {
2739 if (el.contains(container)) {
2740 return;
2741 }
2742 if (el.hasAttribute('aria-hidden')) {
2743 el.setAttribute('data-previous-aria-hidden', el.getAttribute('aria-hidden') || '');
2744 }
2745 el.setAttribute('aria-hidden', 'true');
2746 });
2747 };
2748 const unsetAriaHidden = () => {
2749 const bodyChildren = Array.from(document.body.children);
2750 bodyChildren.forEach(el => {
2751 if (el.hasAttribute('data-previous-aria-hidden')) {
2752 el.setAttribute('aria-hidden', el.getAttribute('data-previous-aria-hidden') || '');
2753 el.removeAttribute('data-previous-aria-hidden');
2754 } else {
2755 el.removeAttribute('aria-hidden');
2756 }
2757 });
2758 };
2759
2760 // @ts-ignore
2761 const isSafariOrIOS = typeof window !== 'undefined' && Boolean(window.GestureEvent); // true for Safari desktop + all iOS browsers https://stackoverflow.com/a/70585394
2762
2763 /**
2764 * Fix iOS scrolling
2765 * http://stackoverflow.com/q/39626302
2766 */
2767 const iOSfix = () => {
2768 if (isSafariOrIOS && !hasClass(document.body, swalClasses.iosfix)) {
2769 const offset = document.body.scrollTop;
2770 document.body.style.top = `${offset * -1}px`;
2771 addClass(document.body, swalClasses.iosfix);
2772 lockBodyScroll();
2773 }
2774 };
2775
2776 /**
2777 * https://github.com/sweetalert2/sweetalert2/issues/1246
2778 */
2779 const lockBodyScroll = () => {
2780 const container = getContainer();
2781 if (!container) {
2782 return;
2783 }
2784 /** @type {boolean} */
2785 let preventTouchMove;
2786 /**
2787 * @param {TouchEvent} event
2788 */
2789 container.ontouchstart = event => {
2790 preventTouchMove = shouldPreventTouchMove(event);
2791 };
2792 /**
2793 * @param {TouchEvent} event
2794 */
2795 container.ontouchmove = event => {
2796 if (preventTouchMove) {
2797 event.preventDefault();
2798 event.stopPropagation();
2799 }
2800 };
2801 };
2802
2803 /**
2804 * @param {TouchEvent} event
2805 * @returns {boolean}
2806 */
2807 const shouldPreventTouchMove = event => {
2808 const target = event.target;
2809 const container = getContainer();
2810 const htmlContainer = getHtmlContainer();
2811 if (!container || !htmlContainer) {
2812 return false;
2813 }
2814 if (isStylus(event) || isZoom(event)) {
2815 return false;
2816 }
2817 if (target === container) {
2818 return true;
2819 }
2820 if (!isScrollable(container) && target instanceof HTMLElement && !selfOrParentIsScrollable(target, htmlContainer) &&
2821 // #2823
2822 target.tagName !== 'INPUT' &&
2823 // #1603
2824 target.tagName !== 'TEXTAREA' &&
2825 // #2266
2826 !(isScrollable(htmlContainer) &&
2827 // #1944
2828 htmlContainer.contains(target))) {
2829 return true;
2830 }
2831 return false;
2832 };
2833
2834 /**
2835 * https://github.com/sweetalert2/sweetalert2/issues/1786
2836 *
2837 * @param {TouchEvent} event
2838 * @returns {boolean}
2839 */
2840 const isStylus = event => {
2841 return Boolean(event.touches && event.touches.length &&
2842 // @ts-ignore - touchType is not a standard property
2843 event.touches[0].touchType === 'stylus');
2844 };
2845
2846 /**
2847 * https://github.com/sweetalert2/sweetalert2/issues/1891
2848 *
2849 * @param {TouchEvent} event
2850 * @returns {boolean}
2851 */
2852 const isZoom = event => {
2853 return event.touches && event.touches.length > 1;
2854 };
2855 const undoIOSfix = () => {
2856 if (hasClass(document.body, swalClasses.iosfix)) {
2857 const offset = parseInt(document.body.style.top, 10);
2858 removeClass(document.body, swalClasses.iosfix);
2859 document.body.style.top = '';
2860 document.body.scrollTop = offset * -1;
2861 }
2862 };
2863
2864 /**
2865 * Measure scrollbar width for padding body during modal show/hide
2866 * https://github.com/twbs/bootstrap/blob/master/js/src/modal.js
2867 *
2868 * @returns {number}
2869 */
2870 const measureScrollbar = () => {
2871 const scrollDiv = document.createElement('div');
2872 scrollDiv.className = swalClasses['scrollbar-measure'];
2873 document.body.appendChild(scrollDiv);
2874 const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
2875 document.body.removeChild(scrollDiv);
2876 return scrollbarWidth;
2877 };
2878
2879 /**
2880 * Remember state in cases where opening and handling a modal will fiddle with it.
2881 * @type {number | null}
2882 */
2883 let previousBodyPadding = null;
2884
2885 /**
2886 * @param {string} initialBodyOverflow
2887 */
2888 const replaceScrollbarWithPadding = initialBodyOverflow => {
2889 // for queues, do not do this more than once
2890 if (previousBodyPadding !== null) {
2891 return;
2892 }
2893 // if the body has overflow
2894 if (document.body.scrollHeight > window.innerHeight || initialBodyOverflow === 'scroll' // https://github.com/sweetalert2/sweetalert2/issues/2663
2895 ) {
2896 // add padding so the content doesn't shift after removal of scrollbar
2897 previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'));
2898 document.body.style.paddingRight = `${previousBodyPadding + measureScrollbar()}px`;
2899 }
2900 };
2901 const undoReplaceScrollbarWithPadding = () => {
2902 if (previousBodyPadding !== null) {
2903 document.body.style.paddingRight = `${previousBodyPadding}px`;
2904 previousBodyPadding = null;
2905 }
2906 };
2907
2908 /**
2909 * @param {SweetAlert} instance
2910 * @param {HTMLElement} container
2911 * @param {boolean} returnFocus
2912 * @param {(() => void) | undefined} didClose
2913 */
2914 function removePopupAndResetState(instance, container, returnFocus, didClose) {
2915 if (isToast()) {
2916 triggerDidCloseAndDispose(instance, didClose);
2917 } else {
2918 restoreActiveElement(returnFocus).then(() => triggerDidCloseAndDispose(instance, didClose));
2919 removeKeydownHandler(globalState);
2920 }
2921
2922 // workaround for https://github.com/sweetalert2/sweetalert2/issues/2088
2923 // for some reason removing the container in Safari will scroll the document to bottom
2924 if (isSafariOrIOS) {
2925 container.setAttribute('style', 'display:none !important');
2926 container.removeAttribute('class');
2927 container.innerHTML = '';
2928 } else {
2929 container.remove();
2930 }
2931 if (isModal()) {
2932 undoReplaceScrollbarWithPadding();
2933 undoIOSfix();
2934 unsetAriaHidden();
2935 }
2936 removeBodyClasses();
2937 }
2938
2939 /**
2940 * Remove SweetAlert2 classes from body
2941 */
2942 function removeBodyClasses() {
2943 removeClass([document.documentElement, document.body], [swalClasses.shown, swalClasses['height-auto'], swalClasses['no-backdrop'], swalClasses['toast-shown']]);
2944 }
2945
2946 /**
2947 * Instance method to close sweetAlert
2948 *
2949 * @param {SweetAlertResult | undefined} resolveValue
2950 * @this {SweetAlert}
2951 */
2952 function close(resolveValue) {
2953 resolveValue = prepareResolveValue(resolveValue);
2954 const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this);
2955 const didClose = triggerClosePopup(this);
2956 if (this.isAwaitingPromise) {
2957 // A swal awaiting for a promise (after a click on Confirm or Deny) cannot be dismissed anymore #2335
2958 if (!resolveValue.isDismissed) {
2959 handleAwaitingPromise(this);
2960 swalPromiseResolve(resolveValue);
2961 }
2962 } else if (didClose) {
2963 // Resolve Swal promise
2964 swalPromiseResolve(resolveValue);
2965 }
2966 }
2967
2968 /**
2969 * @param {SweetAlert} instance
2970 * @returns {boolean}
2971 */
2972 const triggerClosePopup = instance => {
2973 const popup = getPopup();
2974 if (!popup) {
2975 return false;
2976 }
2977 const innerParams = privateProps.innerParams.get(instance);
2978 if (!innerParams || hasClass(popup, innerParams.hideClass.popup)) {
2979 return false;
2980 }
2981 removeClass(popup, innerParams.showClass.popup);
2982 addClass(popup, innerParams.hideClass.popup);
2983 const backdrop = getContainer();
2984 removeClass(backdrop, innerParams.showClass.backdrop);
2985 addClass(backdrop, innerParams.hideClass.backdrop);
2986 handlePopupAnimation(instance, popup, innerParams);
2987 return true;
2988 };
2989
2990 /**
2991 * @param {Error | string} error
2992 * @this {SweetAlert}
2993 */
2994 function rejectPromise(error) {
2995 const rejectPromise = privateMethods.swalPromiseReject.get(this);
2996 handleAwaitingPromise(this);
2997 if (rejectPromise) {
2998 // Reject Swal promise
2999 rejectPromise(error);
3000 }
3001 }
3002
3003 /**
3004 * @param {SweetAlert} instance
3005 */
3006 const handleAwaitingPromise = instance => {
3007 if (instance.isAwaitingPromise) {
3008 // @ts-ignore
3009 delete instance.isAwaitingPromise;
3010 // The instance might have been previously partly destroyed, we must resume the destroy process in this case #2335
3011 if (!privateProps.innerParams.get(instance)) {
3012 instance._destroy();
3013 }
3014 }
3015 };
3016
3017 /**
3018 * @param {SweetAlertResult | undefined} resolveValue
3019 * @returns {SweetAlertResult}
3020 */
3021 const prepareResolveValue = resolveValue => {
3022 // When user calls Swal.close()
3023 if (typeof resolveValue === 'undefined') {
3024 return {
3025 isConfirmed: false,
3026 isDenied: false,
3027 isDismissed: true
3028 };
3029 }
3030 return Object.assign({
3031 isConfirmed: false,
3032 isDenied: false,
3033 isDismissed: false
3034 }, resolveValue);
3035 };
3036
3037 /**
3038 * @param {SweetAlert} instance
3039 * @param {HTMLElement} popup
3040 * @param {SweetAlertOptions} innerParams
3041 */
3042 const handlePopupAnimation = (instance, popup, innerParams) => {
3043 var _globalState$eventEmi;
3044 const container = getContainer();
3045 // If animation is supported, animate
3046 const animationIsSupported = hasCssAnimation(popup);
3047 if (typeof innerParams.willClose === 'function') {
3048 innerParams.willClose(popup);
3049 }
3050 (_globalState$eventEmi = globalState.eventEmitter) === null || _globalState$eventEmi === void 0 || _globalState$eventEmi.emit('willClose', popup);
3051 if (animationIsSupported && container) {
3052 animatePopup(instance, popup, container, Boolean(innerParams.returnFocus), innerParams.didClose);
3053 } else if (container) {
3054 // Otherwise, remove immediately
3055 removePopupAndResetState(instance, container, Boolean(innerParams.returnFocus), innerParams.didClose);
3056 }
3057 };
3058
3059 /**
3060 * @param {SweetAlert} instance
3061 * @param {HTMLElement} popup
3062 * @param {HTMLElement} container
3063 * @param {boolean} returnFocus
3064 * @param {(() => void) | undefined} didClose
3065 */
3066 const animatePopup = (instance, popup, container, returnFocus, didClose) => {
3067 globalState.swalCloseEventFinishedCallback = removePopupAndResetState.bind(null, instance, container, returnFocus, didClose);
3068 /**
3069 * @param {AnimationEvent | TransitionEvent} e
3070 */
3071 const swalCloseAnimationFinished = function (e) {
3072 if (e.target === popup) {
3073 var _globalState$swalClos;
3074 (_globalState$swalClos = globalState.swalCloseEventFinishedCallback) === null || _globalState$swalClos === void 0 || _globalState$swalClos.call(globalState);
3075 delete globalState.swalCloseEventFinishedCallback;
3076 popup.removeEventListener('animationend', swalCloseAnimationFinished);
3077 popup.removeEventListener('transitionend', swalCloseAnimationFinished);
3078 }
3079 };
3080 popup.addEventListener('animationend', swalCloseAnimationFinished);
3081 popup.addEventListener('transitionend', swalCloseAnimationFinished);
3082 };
3083
3084 /**
3085 * @param {SweetAlert} instance
3086 * @param {(() => void) | undefined} didClose
3087 */
3088 const triggerDidCloseAndDispose = (instance, didClose) => {
3089 setTimeout(() => {
3090 var _globalState$eventEmi2;
3091 if (typeof didClose === 'function') {
3092 didClose.bind(instance.params)();
3093 }
3094 (_globalState$eventEmi2 = globalState.eventEmitter) === null || _globalState$eventEmi2 === void 0 || _globalState$eventEmi2.emit('didClose');
3095 // instance might have been destroyed already
3096 if (instance._destroy) {
3097 instance._destroy();
3098 }
3099 });
3100 };
3101
3102 /**
3103 * Shows loader (spinner), this is useful with AJAX requests.
3104 * By default the loader be shown instead of the "Confirm" button.
3105 *
3106 * @param {HTMLButtonElement | null} [buttonToReplace]
3107 */
3108 const showLoading = buttonToReplace => {
3109 let popup = getPopup();
3110 if (!popup) {
3111 new Swal();
3112 }
3113 popup = getPopup();
3114 if (!popup) {
3115 return;
3116 }
3117 const loader = getLoader();
3118 if (isToast()) {
3119 hide(getIcon());
3120 } else {
3121 replaceButton(popup, buttonToReplace);
3122 }
3123 show(loader);
3124 popup.setAttribute('data-loading', 'true');
3125 popup.setAttribute('aria-busy', 'true');
3126 popup.focus();
3127 };
3128
3129 /**
3130 * @param {HTMLElement} popup
3131 * @param {HTMLButtonElement | null} [buttonToReplace]
3132 */
3133 const replaceButton = (popup, buttonToReplace) => {
3134 const actions = getActions();
3135 const loader = getLoader();
3136 if (!actions || !loader) {
3137 return;
3138 }
3139 if (!buttonToReplace && isVisible$1(getConfirmButton())) {
3140 buttonToReplace = getConfirmButton();
3141 }
3142 show(actions);
3143 if (buttonToReplace) {
3144 hide(buttonToReplace);
3145 loader.setAttribute('data-button-to-replace', buttonToReplace.className);
3146 actions.insertBefore(loader, buttonToReplace);
3147 }
3148 addClass([popup, actions], swalClasses.loading);
3149 };
3150
3151 /**
3152 * @param {SweetAlert} instance
3153 * @param {SweetAlertOptions} params
3154 */
3155 const handleInputOptionsAndValue = (instance, params) => {
3156 if (params.input === 'select' || params.input === 'radio') {
3157 handleInputOptions(instance, params);
3158 } else if (['text', 'email', 'number', 'tel', 'textarea'].some(i => i === params.input) && (hasToPromiseFn(params.inputValue) || isPromise(params.inputValue))) {
3159 showLoading(getConfirmButton());
3160 handleInputValue(instance, params);
3161 }
3162 };
3163
3164 /**
3165 * @param {SweetAlert} instance
3166 * @param {SweetAlertOptions} innerParams
3167 * @returns {SweetAlertInputValue}
3168 */
3169 const getInputValue = (instance, innerParams) => {
3170 const input = instance.getInput();
3171 if (!input) {
3172 return null;
3173 }
3174 switch (innerParams.input) {
3175 case 'checkbox':
3176 return getCheckboxValue(input);
3177 case 'radio':
3178 return getRadioValue(input);
3179 case 'file':
3180 return getFileValue(input);
3181 default:
3182 return innerParams.inputAutoTrim ? input.value.trim() : input.value;
3183 }
3184 };
3185
3186 /**
3187 * @param {HTMLInputElement} input
3188 * @returns {number}
3189 */
3190 const getCheckboxValue = input => input.checked ? 1 : 0;
3191
3192 /**
3193 * @param {HTMLInputElement} input
3194 * @returns {string | null}
3195 */
3196 const getRadioValue = input => input.checked ? input.value : null;
3197
3198 /**
3199 * @param {HTMLInputElement} input
3200 * @returns {FileList | File | null}
3201 */
3202 const getFileValue = input => input.files && input.files.length ? input.getAttribute('multiple') !== null ? input.files : input.files[0] : null;
3203
3204 /**
3205 * @param {SweetAlert} instance
3206 * @param {SweetAlertOptions} params
3207 */
3208 const handleInputOptions = (instance, params) => {
3209 const popup = getPopup();
3210 if (!popup) {
3211 return;
3212 }
3213 /**
3214 * @param {*} inputOptions
3215 */
3216 const processInputOptions = inputOptions => {
3217 if (params.input === 'select') {
3218 populateSelectOptions(popup, formatInputOptions(inputOptions), params);
3219 } else if (params.input === 'radio') {
3220 populateRadioOptions(popup, formatInputOptions(inputOptions), params);
3221 }
3222 };
3223 if (hasToPromiseFn(params.inputOptions) || isPromise(params.inputOptions)) {
3224 showLoading(getConfirmButton());
3225 asPromise(params.inputOptions).then(inputOptions => {
3226 instance.hideLoading();
3227 processInputOptions(inputOptions);
3228 });
3229 } else if (typeof params.inputOptions === 'object') {
3230 processInputOptions(params.inputOptions);
3231 } else {
3232 error(`Unexpected type of inputOptions! Expected object, Map or Promise, got ${typeof params.inputOptions}`);
3233 }
3234 };
3235
3236 /**
3237 * @param {SweetAlert} instance
3238 * @param {SweetAlertOptions} params
3239 */
3240 const handleInputValue = (instance, params) => {
3241 const input = instance.getInput();
3242 if (!input) {
3243 return;
3244 }
3245 hide(input);
3246 asPromise(params.inputValue).then(inputValue => {
3247 input.value = params.input === 'number' ? `${parseFloat(inputValue) || 0}` : `${inputValue}`;
3248 show(input);
3249 input.focus();
3250 instance.hideLoading();
3251 }).catch(err => {
3252 error(`Error in inputValue promise: ${err}`);
3253 input.value = '';
3254 show(input);
3255 input.focus();
3256 instance.hideLoading();
3257 });
3258 };
3259
3260 /**
3261 * @param {HTMLElement} popup
3262 * @param {InputOptionFlattened[]} inputOptions
3263 * @param {SweetAlertOptions} params
3264 */
3265 function populateSelectOptions(popup, inputOptions, params) {
3266 const select = getDirectChildByClass(popup, swalClasses.select);
3267 if (!select) {
3268 return;
3269 }
3270 /**
3271 * @param {HTMLElement} parent
3272 * @param {string} optionLabel
3273 * @param {string} optionValue
3274 */
3275 const renderOption = (parent, optionLabel, optionValue) => {
3276 const option = document.createElement('option');
3277 option.value = optionValue;
3278 setInnerHtml(option, optionLabel);
3279 option.selected = isSelected(optionValue, params.inputValue);
3280 parent.appendChild(option);
3281 };
3282 inputOptions.forEach(inputOption => {
3283 const optionValue = inputOption[0];
3284 const optionLabel = inputOption[1];
3285 // <optgroup> spec:
3286 // https://www.w3.org/TR/html401/interact/forms.html#h-17.6
3287 // "...all OPTGROUP elements must be specified directly within a SELECT element (i.e., groups may not be nested)..."
3288 // check whether this is a <optgroup>
3289 if (Array.isArray(optionLabel)) {
3290 // if it is an array, then it is an <optgroup>
3291 const optgroup = document.createElement('optgroup');
3292 optgroup.label = optionValue;
3293 optgroup.disabled = false; // not configurable for now
3294 select.appendChild(optgroup);
3295 optionLabel.forEach(o => renderOption(optgroup, o[1], o[0]));
3296 } else {
3297 // case of <option>
3298 renderOption(select, optionLabel, optionValue);
3299 }
3300 });
3301 select.focus();
3302 }
3303
3304 /**
3305 * @param {HTMLElement} popup
3306 * @param {InputOptionFlattened[]} inputOptions
3307 * @param {SweetAlertOptions} params
3308 */
3309 function populateRadioOptions(popup, inputOptions, params) {
3310 const radio = getDirectChildByClass(popup, swalClasses.radio);
3311 if (!radio) {
3312 return;
3313 }
3314 inputOptions.forEach(inputOption => {
3315 const radioValue = inputOption[0];
3316 const radioLabel = inputOption[1];
3317 const radioInput = document.createElement('input');
3318 const radioLabelElement = document.createElement('label');
3319 radioInput.type = 'radio';
3320 radioInput.name = swalClasses.radio;
3321 radioInput.value = radioValue;
3322 if (isSelected(radioValue, params.inputValue)) {
3323 radioInput.checked = true;
3324 }
3325 const label = document.createElement('span');
3326 setInnerHtml(label, radioLabel);
3327 label.className = swalClasses.label;
3328 radioLabelElement.appendChild(radioInput);
3329 radioLabelElement.appendChild(label);
3330 radio.appendChild(radioLabelElement);
3331 });
3332 const radios = radio.querySelectorAll('input');
3333 if (radios.length) {
3334 radios[0].focus();
3335 }
3336 }
3337
3338 /**
3339 * Converts `inputOptions` into an array of `[value, label]`s
3340 *
3341 * @param {*} inputOptions
3342 * @typedef {string[]} InputOptionFlattened
3343 * @returns {InputOptionFlattened[]}
3344 */
3345 const formatInputOptions = inputOptions => {
3346 /** @type {InputOptionFlattened[]} */
3347 const result = [];
3348 if (inputOptions instanceof Map) {
3349 inputOptions.forEach((value, key) => {
3350 let valueFormatted = value;
3351 if (typeof valueFormatted === 'object') {
3352 // case of <optgroup>
3353 valueFormatted = formatInputOptions(valueFormatted);
3354 }
3355 result.push([key, valueFormatted]);
3356 });
3357 } else {
3358 Object.keys(inputOptions).forEach(key => {
3359 let valueFormatted = inputOptions[key];
3360 if (typeof valueFormatted === 'object') {
3361 // case of <optgroup>
3362 valueFormatted = formatInputOptions(valueFormatted);
3363 }
3364 result.push([key, valueFormatted]);
3365 });
3366 }
3367 return result;
3368 };
3369
3370 /**
3371 * @param {string} optionValue
3372 * @param {SweetAlertInputValue} inputValue
3373 * @returns {boolean}
3374 */
3375 const isSelected = (optionValue, inputValue) => {
3376 return Boolean(inputValue) && inputValue !== null && inputValue !== undefined && inputValue.toString() === optionValue.toString();
3377 };
3378
3379 /**
3380 * @param {SweetAlert} instance
3381 */
3382 const handleConfirmButtonClick = instance => {
3383 const innerParams = privateProps.innerParams.get(instance);
3384 instance.disableButtons();
3385 if (innerParams.input) {
3386 handleConfirmOrDenyWithInput(instance, 'confirm');
3387 } else {
3388 confirm(instance, true);
3389 }
3390 };
3391
3392 /**
3393 * @param {SweetAlert} instance
3394 */
3395 const handleDenyButtonClick = instance => {
3396 const innerParams = privateProps.innerParams.get(instance);
3397 instance.disableButtons();
3398 if (innerParams.returnInputValueOnDeny) {
3399 handleConfirmOrDenyWithInput(instance, 'deny');
3400 } else {
3401 deny(instance, false);
3402 }
3403 };
3404
3405 /**
3406 * @param {SweetAlert} instance
3407 * @param {(dismiss: DismissReason) => void} dismissWith
3408 */
3409 const handleCancelButtonClick = (instance, dismissWith) => {
3410 instance.disableButtons();
3411 dismissWith(DismissReason.cancel);
3412 };
3413
3414 /**
3415 * @param {SweetAlert} instance
3416 * @param {'confirm' | 'deny'} type
3417 */
3418 const handleConfirmOrDenyWithInput = (instance, type) => {
3419 const innerParams = privateProps.innerParams.get(instance);
3420 if (!innerParams.input) {
3421 error(`The "input" parameter is needed to be set when using returnInputValueOn${capitalizeFirstLetter(type)}`);
3422 return;
3423 }
3424 const input = instance.getInput();
3425 const inputValue = getInputValue(instance, innerParams);
3426 if (innerParams.inputValidator) {
3427 handleInputValidator(instance, inputValue, type);
3428 } else if (input && !input.checkValidity()) {
3429 instance.enableButtons();
3430 instance.showValidationMessage(innerParams.validationMessage || input.validationMessage);
3431 } else if (type === 'deny') {
3432 deny(instance, inputValue);
3433 } else {
3434 confirm(instance, inputValue);
3435 }
3436 };
3437
3438 /**
3439 * @param {SweetAlert} instance
3440 * @param {SweetAlertInputValue} inputValue
3441 * @param {'confirm' | 'deny'} type
3442 */
3443 const handleInputValidator = (instance, inputValue, type) => {
3444 const innerParams = privateProps.innerParams.get(instance);
3445 instance.disableInput();
3446 const validationPromise = Promise.resolve().then(() => asPromise(innerParams.inputValidator(inputValue, innerParams.validationMessage)));
3447 validationPromise.then(validationMessage => {
3448 instance.enableButtons();
3449 instance.enableInput();
3450 if (validationMessage) {
3451 instance.showValidationMessage(validationMessage);
3452 } else if (type === 'deny') {
3453 deny(instance, inputValue);
3454 } else {
3455 confirm(instance, inputValue);
3456 }
3457 });
3458 };
3459
3460 /**
3461 * @param {SweetAlert} instance
3462 * @param {*} value
3463 */
3464 const deny = (instance, value) => {
3465 const innerParams = privateProps.innerParams.get(instance);
3466 if (innerParams.showLoaderOnDeny) {
3467 showLoading(getDenyButton());
3468 }
3469 if (innerParams.preDeny) {
3470 instance.isAwaitingPromise = true; // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesn't get destroyed until the result from this preDeny's promise is received
3471 const preDenyPromise = Promise.resolve().then(() => asPromise(innerParams.preDeny(value, innerParams.validationMessage)));
3472 preDenyPromise.then(preDenyValue => {
3473 if (preDenyValue === false) {
3474 instance.hideLoading();
3475 handleAwaitingPromise(instance);
3476 } else {
3477 instance.close(/** @type SweetAlertResult */{
3478 isDenied: true,
3479 value: typeof preDenyValue === 'undefined' ? value : preDenyValue
3480 });
3481 }
3482 }).catch(error => rejectWith(instance, error));
3483 } else {
3484 instance.close(/** @type SweetAlertResult */{
3485 isDenied: true,
3486 value
3487 });
3488 }
3489 };
3490
3491 /**
3492 * @param {SweetAlert} instance
3493 * @param {*} value
3494 */
3495 const succeedWith = (instance, value) => {
3496 instance.close(/** @type SweetAlertResult */{
3497 isConfirmed: true,
3498 value
3499 });
3500 };
3501
3502 /**
3503 *
3504 * @param {SweetAlert} instance
3505 * @param {string} error
3506 */
3507 const rejectWith = (instance, error) => {
3508 instance.rejectPromise(error);
3509 };
3510
3511 /**
3512 *
3513 * @param {SweetAlert} instance
3514 * @param {*} value
3515 */
3516 const confirm = (instance, value) => {
3517 const innerParams = privateProps.innerParams.get(instance);
3518 if (innerParams.showLoaderOnConfirm) {
3519 showLoading();
3520 }
3521 if (innerParams.preConfirm) {
3522 instance.resetValidationMessage();
3523 instance.isAwaitingPromise = true; // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesn't get destroyed until the result from this preConfirm's promise is received
3524 const preConfirmPromise = Promise.resolve().then(() => asPromise(innerParams.preConfirm(value, innerParams.validationMessage)));
3525 preConfirmPromise.then(preConfirmValue => {
3526 if (isVisible$1(getValidationMessage()) || preConfirmValue === false) {
3527 instance.hideLoading();
3528 handleAwaitingPromise(instance);
3529 } else {
3530 succeedWith(instance, typeof preConfirmValue === 'undefined' ? value : preConfirmValue);
3531 }
3532 }).catch(error => rejectWith(instance, error));
3533 } else {
3534 succeedWith(instance, value);
3535 }
3536 };
3537
3538 /**
3539 * Hides loader and shows back the button which was hidden by .showLoading()
3540 * @this {SweetAlert}
3541 */
3542 function hideLoading() {
3543 // do nothing if popup is closed
3544 const innerParams = privateProps.innerParams.get(this);
3545 if (!innerParams) {
3546 return;
3547 }
3548 const domCache = privateProps.domCache.get(this);
3549 hide(domCache.loader);
3550 if (isToast()) {
3551 if (innerParams.icon) {
3552 show(getIcon());
3553 }
3554 } else {
3555 showRelatedButton(domCache);
3556 }
3557 removeClass([domCache.popup, domCache.actions], swalClasses.loading);
3558 domCache.popup.removeAttribute('aria-busy');
3559 domCache.popup.removeAttribute('data-loading');
3560 domCache.confirmButton.disabled = false;
3561 domCache.denyButton.disabled = false;
3562 domCache.cancelButton.disabled = false;
3563 }
3564
3565 /**
3566 * @param {DomCache} domCache
3567 */
3568 const showRelatedButton = domCache => {
3569 const dataButtonToReplace = domCache.loader.getAttribute('data-button-to-replace');
3570 const buttonToReplace = dataButtonToReplace ? domCache.popup.getElementsByClassName(dataButtonToReplace) : [];
3571 if (buttonToReplace.length) {
3572 show(/** @type {HTMLElement} */buttonToReplace[0], 'inline-block');
3573 } else if (allButtonsAreHidden()) {
3574 hide(domCache.actions);
3575 }
3576 };
3577
3578 /**
3579 * Gets the input DOM node, this method works with input parameter.
3580 *
3581 * @returns {HTMLInputElement | null}
3582 * @this {SweetAlert}
3583 */
3584 function getInput() {
3585 const innerParams = privateProps.innerParams.get(this);
3586 const domCache = privateProps.domCache.get(this);
3587 if (!domCache) {
3588 return null;
3589 }
3590 return getInput$1(domCache.popup, innerParams.input);
3591 }
3592
3593 /**
3594 * @param {SweetAlert} instance
3595 * @param {string[]} buttons
3596 * @param {boolean} disabled
3597 */
3598 function setButtonsDisabled(instance, buttons, disabled) {
3599 const domCache = privateProps.domCache.get(instance);
3600 buttons.forEach(button => {
3601 domCache[button].disabled = disabled;
3602 });
3603 }
3604
3605 /**
3606 * @param {HTMLInputElement | null} input
3607 * @param {boolean} disabled
3608 */
3609 function setInputDisabled(input, disabled) {
3610 const popup = getPopup();
3611 if (!popup || !input) {
3612 return;
3613 }
3614 if (input.type === 'radio') {
3615 /** @type {NodeListOf<HTMLInputElement>} */
3616 const radios = popup.querySelectorAll(`[name="${swalClasses.radio}"]`);
3617 for (let i = 0; i < radios.length; i++) {
3618 radios[i].disabled = disabled;
3619 }
3620 } else {
3621 input.disabled = disabled;
3622 }
3623 }
3624
3625 /**
3626 * Enable all the buttons
3627 * @this {SweetAlert}
3628 */
3629 function enableButtons() {
3630 setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], false);
3631 }
3632
3633 /**
3634 * Disable all the buttons
3635 * @this {SweetAlert}
3636 */
3637 function disableButtons() {
3638 setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], true);
3639 }
3640
3641 /**
3642 * Enable the input field
3643 * @this {SweetAlert}
3644 */
3645 function enableInput() {
3646 setInputDisabled(this.getInput(), false);
3647 }
3648
3649 /**
3650 * Disable the input field
3651 * @this {SweetAlert}
3652 */
3653 function disableInput() {
3654 setInputDisabled(this.getInput(), true);
3655 }
3656
3657 /**
3658 * Show block with validation message
3659 *
3660 * @param {string} error
3661 * @this {SweetAlert}
3662 */
3663 function showValidationMessage(error) {
3664 const domCache = privateProps.domCache.get(this);
3665 const params = privateProps.innerParams.get(this);
3666 setInnerHtml(domCache.validationMessage, error);
3667 domCache.validationMessage.className = swalClasses['validation-message'];
3668 if (params.customClass && params.customClass.validationMessage) {
3669 addClass(domCache.validationMessage, params.customClass.validationMessage);
3670 }
3671 show(domCache.validationMessage);
3672 const input = this.getInput();
3673 if (input) {
3674 input.setAttribute('aria-invalid', 'true');
3675 input.setAttribute('aria-describedby', swalClasses['validation-message']);
3676 focusInput(input);
3677 addClass(input, swalClasses.inputerror);
3678 }
3679 }
3680
3681 /**
3682 * Hide block with validation message
3683 *
3684 * @this {SweetAlert}
3685 */
3686 function resetValidationMessage() {
3687 const domCache = privateProps.domCache.get(this);
3688 if (domCache.validationMessage) {
3689 hide(domCache.validationMessage);
3690 }
3691 const input = this.getInput();
3692 if (input) {
3693 input.removeAttribute('aria-invalid');
3694 input.removeAttribute('aria-describedby');
3695 removeClass(input, swalClasses.inputerror);
3696 }
3697 }
3698
3699 const defaultParams = {
3700 title: '',
3701 titleText: '',
3702 text: '',
3703 html: '',
3704 footer: '',
3705 icon: undefined,
3706 iconColor: undefined,
3707 iconHtml: undefined,
3708 template: undefined,
3709 toast: false,
3710 draggable: false,
3711 animation: true,
3712 theme: 'light',
3713 showClass: {
3714 popup: 'swal2-show',
3715 backdrop: 'swal2-backdrop-show',
3716 icon: 'swal2-icon-show'
3717 },
3718 hideClass: {
3719 popup: 'swal2-hide',
3720 backdrop: 'swal2-backdrop-hide',
3721 icon: 'swal2-icon-hide'
3722 },
3723 customClass: {},
3724 target: 'body',
3725 color: undefined,
3726 backdrop: true,
3727 heightAuto: true,
3728 allowOutsideClick: true,
3729 allowEscapeKey: true,
3730 allowEnterKey: true,
3731 stopKeydownPropagation: true,
3732 keydownListenerCapture: false,
3733 showConfirmButton: true,
3734 showDenyButton: false,
3735 showCancelButton: false,
3736 preConfirm: undefined,
3737 preDeny: undefined,
3738 confirmButtonText: 'OK',
3739 confirmButtonAriaLabel: '',
3740 confirmButtonColor: undefined,
3741 denyButtonText: 'No',
3742 denyButtonAriaLabel: '',
3743 denyButtonColor: undefined,
3744 cancelButtonText: 'Cancel',
3745 cancelButtonAriaLabel: '',
3746 cancelButtonColor: undefined,
3747 buttonsStyling: true,
3748 reverseButtons: false,
3749 focusConfirm: true,
3750 focusDeny: false,
3751 focusCancel: false,
3752 returnFocus: true,
3753 showCloseButton: false,
3754 closeButtonHtml: '&times;',
3755 closeButtonAriaLabel: 'Close this dialog',
3756 loaderHtml: '',
3757 showLoaderOnConfirm: false,
3758 showLoaderOnDeny: false,
3759 imageUrl: undefined,
3760 imageWidth: undefined,
3761 imageHeight: undefined,
3762 imageAlt: '',
3763 timer: undefined,
3764 timerProgressBar: false,
3765 width: undefined,
3766 padding: undefined,
3767 background: undefined,
3768 input: undefined,
3769 inputPlaceholder: '',
3770 inputLabel: '',
3771 inputValue: '',
3772 inputOptions: {},
3773 inputAutoFocus: true,
3774 inputAutoTrim: true,
3775 inputAttributes: {},
3776 inputValidator: undefined,
3777 returnInputValueOnDeny: false,
3778 validationMessage: undefined,
3779 grow: false,
3780 position: 'center',
3781 progressSteps: [],
3782 currentProgressStep: undefined,
3783 progressStepsDistance: undefined,
3784 willOpen: undefined,
3785 didOpen: undefined,
3786 didRender: undefined,
3787 willClose: undefined,
3788 didClose: undefined,
3789 didDestroy: undefined,
3790 scrollbarPadding: true,
3791 topLayer: false
3792 };
3793 const updatableParams = ['allowEscapeKey', 'allowOutsideClick', 'background', 'buttonsStyling', 'cancelButtonAriaLabel', 'cancelButtonColor', 'cancelButtonText', 'closeButtonAriaLabel', 'closeButtonHtml', 'color', 'confirmButtonAriaLabel', 'confirmButtonColor', 'confirmButtonText', 'currentProgressStep', 'customClass', 'denyButtonAriaLabel', 'denyButtonColor', 'denyButtonText', 'didClose', 'didDestroy', 'draggable', 'footer', 'hideClass', 'html', 'icon', 'iconColor', 'iconHtml', 'imageAlt', 'imageHeight', 'imageUrl', 'imageWidth', 'preConfirm', 'preDeny', 'progressSteps', 'returnFocus', 'reverseButtons', 'showCancelButton', 'showCloseButton', 'showConfirmButton', 'showDenyButton', 'text', 'title', 'titleText', 'theme', 'willClose'];
3794
3795 /** @type {Record<string, string | undefined>} */
3796 const deprecatedParams = {
3797 allowEnterKey: undefined
3798 };
3799 const toastIncompatibleParams = ['allowOutsideClick', 'allowEnterKey', 'backdrop', 'draggable', 'focusConfirm', 'focusDeny', 'focusCancel', 'returnFocus', 'heightAuto', 'keydownListenerCapture'];
3800
3801 /**
3802 * Is valid parameter
3803 *
3804 * @param {string} paramName
3805 * @returns {boolean}
3806 */
3807 const isValidParameter = paramName => {
3808 return Object.prototype.hasOwnProperty.call(defaultParams, paramName);
3809 };
3810
3811 /**
3812 * Is valid parameter for Swal.update() method
3813 *
3814 * @param {string} paramName
3815 * @returns {boolean}
3816 */
3817 const isUpdatableParameter = paramName => {
3818 return updatableParams.indexOf(paramName) !== -1;
3819 };
3820
3821 /**
3822 * Is deprecated parameter
3823 *
3824 * @param {string} paramName
3825 * @returns {string | undefined}
3826 */
3827 const isDeprecatedParameter = paramName => {
3828 return deprecatedParams[paramName];
3829 };
3830
3831 /**
3832 * @param {string} param
3833 */
3834 const checkIfParamIsValid = param => {
3835 if (!isValidParameter(param)) {
3836 warn(`Unknown parameter "${param}"`);
3837 }
3838 };
3839
3840 /**
3841 * @param {string} param
3842 */
3843 const checkIfToastParamIsValid = param => {
3844 if (toastIncompatibleParams.includes(param)) {
3845 warn(`The parameter "${param}" is incompatible with toasts`);
3846 }
3847 };
3848
3849 /**
3850 * @param {string} param
3851 */
3852 const checkIfParamIsDeprecated = param => {
3853 const isDeprecated = isDeprecatedParameter(param);
3854 if (isDeprecated) {
3855 warnAboutDeprecation(param, isDeprecated);
3856 }
3857 };
3858
3859 /**
3860 * Show relevant warnings for given params
3861 *
3862 * @param {SweetAlertOptions} params
3863 */
3864 const showWarningsForParams = params => {
3865 if (params.backdrop === false && params.allowOutsideClick) {
3866 warn('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`');
3867 }
3868 if (params.theme && !['light', 'dark', 'auto', 'minimal', 'borderless', 'bootstrap-4', 'bootstrap-4-light', 'bootstrap-4-dark', 'bootstrap-5', 'bootstrap-5-light', 'bootstrap-5-dark', 'material-ui', 'material-ui-light', 'material-ui-dark', 'embed-iframe', 'bulma', 'bulma-light', 'bulma-dark'].includes(params.theme)) {
3869 warn(`Invalid theme "${params.theme}"`);
3870 }
3871 for (const param in params) {
3872 checkIfParamIsValid(param);
3873 if (params.toast) {
3874 checkIfToastParamIsValid(param);
3875 }
3876 checkIfParamIsDeprecated(param);
3877 }
3878 };
3879
3880 /**
3881 * Updates popup parameters.
3882 *
3883 * @this {any}
3884 * @param {SweetAlertOptions} params
3885 */
3886 function update(params) {
3887 const container = getContainer();
3888 const popup = getPopup();
3889 const innerParams = privateProps.innerParams.get(this);
3890 if (!popup || hasClass(popup, innerParams.hideClass.popup)) {
3891 warn(`You're trying to update the closed or closing popup, that won't work. Use the update() method in preConfirm parameter or show a new popup.`);
3892 return;
3893 }
3894 const validUpdatableParams = filterValidParams(params);
3895 const updatedParams = Object.assign({}, innerParams, validUpdatableParams);
3896 showWarningsForParams(updatedParams);
3897 if (container) {
3898 container.dataset['swal2Theme'] = updatedParams.theme;
3899 }
3900 render(this, updatedParams);
3901 privateProps.innerParams.set(this, updatedParams);
3902 Object.defineProperties(this, {
3903 params: {
3904 value: Object.assign({}, this.params, params),
3905 writable: false,
3906 enumerable: true
3907 }
3908 });
3909 }
3910
3911 /**
3912 * @param {SweetAlertOptions} params
3913 * @returns {SweetAlertOptions}
3914 */
3915 const filterValidParams = params => {
3916 /** @type {Record<string, any>} */
3917 const validUpdatableParams = {};
3918 Object.keys(params).forEach(param => {
3919 if (isUpdatableParameter(param)) {
3920 const typedParams = /** @type {Record<string, any>} */params;
3921 validUpdatableParams[param] = typedParams[param];
3922 } else {
3923 warn(`Invalid parameter to update: ${param}`);
3924 }
3925 });
3926 return validUpdatableParams;
3927 };
3928
3929 /**
3930 * Dispose the current SweetAlert2 instance
3931 * @this {SweetAlert}
3932 */
3933 function _destroy() {
3934 var _globalState$eventEmi;
3935 const domCache = privateProps.domCache.get(this);
3936 const innerParams = privateProps.innerParams.get(this);
3937 if (!innerParams) {
3938 disposeWeakMaps(this); // The WeakMaps might have been partly destroyed, we must recall it to dispose any remaining WeakMaps #2335
3939 return; // This instance has already been destroyed
3940 }
3941
3942 // Check if there is another Swal closing
3943 if (domCache.popup && globalState.swalCloseEventFinishedCallback) {
3944 globalState.swalCloseEventFinishedCallback();
3945 delete globalState.swalCloseEventFinishedCallback;
3946 }
3947 if (typeof innerParams.didDestroy === 'function') {
3948 innerParams.didDestroy();
3949 }
3950 (_globalState$eventEmi = globalState.eventEmitter) === null || _globalState$eventEmi === void 0 || _globalState$eventEmi.emit('didDestroy');
3951 disposeSwal(this);
3952 }
3953
3954 /**
3955 * @param {SweetAlert} instance
3956 */
3957 const disposeSwal = instance => {
3958 disposeWeakMaps(instance);
3959 // Unset this.params so GC will dispose it (#1569)
3960 // @ts-ignore
3961 delete instance.params;
3962 // Unset globalState props so GC will dispose globalState (#1569)
3963 delete globalState.keydownHandler;
3964 delete globalState.keydownTarget;
3965 // Unset currentInstance
3966 delete globalState.currentInstance;
3967 };
3968
3969 /**
3970 * @param {SweetAlert} instance
3971 */
3972 const disposeWeakMaps = instance => {
3973 // If the current instance is awaiting a promise result, we keep the privateMethods to call them once the promise result is retrieved #2335
3974 if (instance.isAwaitingPromise) {
3975 unsetWeakMaps(privateProps, instance);
3976 instance.isAwaitingPromise = true;
3977 } else {
3978 unsetWeakMaps(privateMethods, instance);
3979 unsetWeakMaps(privateProps, instance);
3980
3981 // @ts-ignore
3982 delete instance.isAwaitingPromise;
3983 // Unset instance methods
3984 // @ts-ignore
3985 delete instance.disableButtons;
3986 // @ts-ignore
3987 delete instance.enableButtons;
3988 // @ts-ignore
3989 delete instance.getInput;
3990 // @ts-ignore
3991 delete instance.disableInput;
3992 // @ts-ignore
3993 delete instance.enableInput;
3994 // @ts-ignore
3995 delete instance.hideLoading;
3996 // @ts-ignore
3997 delete instance.disableLoading;
3998 // @ts-ignore
3999 delete instance.showValidationMessage;
4000 // @ts-ignore
4001 delete instance.resetValidationMessage;
4002 // @ts-ignore
4003 delete instance.close;
4004 // @ts-ignore
4005 delete instance.closePopup;
4006 // @ts-ignore
4007 delete instance.closeModal;
4008 // @ts-ignore
4009 delete instance.closeToast;
4010 // @ts-ignore
4011 delete instance.rejectPromise;
4012 // @ts-ignore
4013 delete instance.update;
4014 // @ts-ignore
4015 delete instance._destroy;
4016 }
4017 };
4018
4019 /**
4020 * @param {Record<string, WeakMap<any, any>>} obj
4021 * @param {SweetAlert} instance
4022 */
4023 const unsetWeakMaps = (obj, instance) => {
4024 for (const i in obj) {
4025 obj[i].delete(instance);
4026 }
4027 };
4028
4029 var instanceMethods = /*#__PURE__*/Object.freeze({
4030 __proto__: null,
4031 _destroy: _destroy,
4032 close: close,
4033 closeModal: close,
4034 closePopup: close,
4035 closeToast: close,
4036 disableButtons: disableButtons,
4037 disableInput: disableInput,
4038 disableLoading: hideLoading,
4039 enableButtons: enableButtons,
4040 enableInput: enableInput,
4041 getInput: getInput,
4042 handleAwaitingPromise: handleAwaitingPromise,
4043 hideLoading: hideLoading,
4044 rejectPromise: rejectPromise,
4045 resetValidationMessage: resetValidationMessage,
4046 showValidationMessage: showValidationMessage,
4047 update: update
4048 });
4049
4050 /**
4051 * @param {SweetAlertOptions} innerParams
4052 * @param {DomCache} domCache
4053 * @param {(dismiss: DismissReason) => void} dismissWith
4054 */
4055 const handlePopupClick = (innerParams, domCache, dismissWith) => {
4056 if (innerParams.toast) {
4057 handleToastClick(innerParams, domCache, dismissWith);
4058 } else {
4059 // Ignore click events that had mousedown on the popup but mouseup on the container
4060 // This can happen when the user drags a slider
4061 handleModalMousedown(domCache);
4062
4063 // Ignore click events that had mousedown on the container but mouseup on the popup
4064 handleContainerMousedown(domCache);
4065 handleModalClick(innerParams, domCache, dismissWith);
4066 }
4067 };
4068
4069 /**
4070 * @param {SweetAlertOptions} innerParams
4071 * @param {DomCache} domCache
4072 * @param {(dismiss: DismissReason) => void} dismissWith
4073 */
4074 const handleToastClick = (innerParams, domCache, dismissWith) => {
4075 // Closing toast by internal click
4076 domCache.popup.onclick = () => {
4077 if (innerParams && (isAnyButtonShown(innerParams) || innerParams.timer || innerParams.input)) {
4078 return;
4079 }
4080 dismissWith(DismissReason.close);
4081 };
4082 };
4083
4084 /**
4085 * @param {SweetAlertOptions} innerParams
4086 * @returns {boolean}
4087 */
4088 const isAnyButtonShown = innerParams => {
4089 return Boolean(innerParams.showConfirmButton || innerParams.showDenyButton || innerParams.showCancelButton || innerParams.showCloseButton);
4090 };
4091 let ignoreOutsideClick = false;
4092
4093 /**
4094 * @param {DomCache} domCache
4095 */
4096 const handleModalMousedown = domCache => {
4097 domCache.popup.onmousedown = () => {
4098 domCache.container.onmouseup = function (e) {
4099 domCache.container.onmouseup = () => {};
4100 // We only check if the mouseup target is the container because usually it doesn't
4101 // have any other direct children aside of the popup
4102 if (e.target === domCache.container) {
4103 ignoreOutsideClick = true;
4104 }
4105 };
4106 };
4107 };
4108
4109 /**
4110 * @param {DomCache} domCache
4111 */
4112 const handleContainerMousedown = domCache => {
4113 domCache.container.onmousedown = e => {
4114 // prevent the modal text from being selected on double click on the container (allowOutsideClick: false)
4115 if (e.target === domCache.container) {
4116 e.preventDefault();
4117 }
4118 domCache.popup.onmouseup = function (e) {
4119 domCache.popup.onmouseup = () => {};
4120 // We also need to check if the mouseup target is a child of the popup
4121 if (e.target === domCache.popup || e.target instanceof HTMLElement && domCache.popup.contains(e.target)) {
4122 ignoreOutsideClick = true;
4123 }
4124 };
4125 };
4126 };
4127
4128 /**
4129 * @param {SweetAlertOptions} innerParams
4130 * @param {DomCache} domCache
4131 * @param {(dismiss: DismissReason) => void} dismissWith
4132 */
4133 const handleModalClick = (innerParams, domCache, dismissWith) => {
4134 domCache.container.onclick = e => {
4135 if (ignoreOutsideClick) {
4136 ignoreOutsideClick = false;
4137 return;
4138 }
4139 if (e.target === domCache.container && callIfFunction(innerParams.allowOutsideClick)) {
4140 dismissWith(DismissReason.backdrop);
4141 }
4142 };
4143 };
4144
4145 /**
4146 * @param {any} elem
4147 * @returns {boolean}
4148 */
4149 const isJqueryElement = elem => typeof elem === 'object' && elem.jquery;
4150
4151 /**
4152 * @param {any} elem
4153 * @returns {boolean}
4154 */
4155 const isElement = elem => elem instanceof Element || isJqueryElement(elem);
4156
4157 /**
4158 * @param {any[]} args
4159 * @returns {SweetAlertOptions}
4160 */
4161 const argsToParams = args => {
4162 /** @type {Record<string, any>} */
4163 const params = {};
4164 if (typeof args[0] === 'object' && !isElement(args[0])) {
4165 Object.assign(params, args[0]);
4166 } else {
4167 ['title', 'html', 'icon'].forEach((name, index) => {
4168 const arg = args[index];
4169 if (typeof arg === 'string' || isElement(arg)) {
4170 params[name] = arg;
4171 } else if (arg !== undefined) {
4172 error(`Unexpected type of ${name}! Expected "string" or "Element", got ${typeof arg}`);
4173 }
4174 });
4175 }
4176 return params;
4177 };
4178
4179 /**
4180 * Main method to create a new SweetAlert2 popup
4181 *
4182 * @this {new (...args: any[]) => any}
4183 * @param {...SweetAlertOptions} args
4184 * @returns {Promise<SweetAlertResult>}
4185 */
4186 function fire(...args) {
4187 return new this(...args);
4188 }
4189
4190 /**
4191 * Returns an extended version of `Swal` containing `params` as defaults.
4192 * Useful for reusing Swal configuration.
4193 *
4194 * For example:
4195 *
4196 * Before:
4197 * const textPromptOptions = { input: 'text', showCancelButton: true }
4198 * const {value: firstName} = await Swal.fire({ ...textPromptOptions, title: 'What is your first name?' })
4199 * const {value: lastName} = await Swal.fire({ ...textPromptOptions, title: 'What is your last name?' })
4200 *
4201 * After:
4202 * const TextPrompt = Swal.mixin({ input: 'text', showCancelButton: true })
4203 * const {value: firstName} = await TextPrompt('What is your first name?')
4204 * const {value: lastName} = await TextPrompt('What is your last name?')
4205 *
4206 * @param {SweetAlertOptions} mixinParams
4207 * @returns {SweetAlert}
4208 * @this {typeof import('../SweetAlert.js').SweetAlert}
4209 */
4210 function mixin(mixinParams) {
4211 // @ts-ignore: 'this' refers to the SweetAlert constructor
4212 class MixinSwal extends this {
4213 /**
4214 * @param {any} params
4215 * @param {any} priorityMixinParams
4216 */
4217 _main(params, priorityMixinParams) {
4218 return super._main(params, Object.assign({}, mixinParams, priorityMixinParams));
4219 }
4220 }
4221 // @ts-ignore
4222 return MixinSwal;
4223 }
4224
4225 /**
4226 * If `timer` parameter is set, returns number of milliseconds of timer remained.
4227 * Otherwise, returns undefined.
4228 *
4229 * @returns {number | undefined}
4230 */
4231 const getTimerLeft = () => {
4232 return globalState.timeout && globalState.timeout.getTimerLeft();
4233 };
4234
4235 /**
4236 * Stop timer. Returns number of milliseconds of timer remained.
4237 * If `timer` parameter isn't set, returns undefined.
4238 *
4239 * @returns {number | undefined}
4240 */
4241 const stopTimer = () => {
4242 if (globalState.timeout) {
4243 stopTimerProgressBar();
4244 return globalState.timeout.stop();
4245 }
4246 };
4247
4248 /**
4249 * Resume timer. Returns number of milliseconds of timer remained.
4250 * If `timer` parameter isn't set, returns undefined.
4251 *
4252 * @returns {number | undefined}
4253 */
4254 const resumeTimer = () => {
4255 if (globalState.timeout) {
4256 const remaining = globalState.timeout.start();
4257 animateTimerProgressBar(remaining);
4258 return remaining;
4259 }
4260 };
4261
4262 /**
4263 * Resume timer. Returns number of milliseconds of timer remained.
4264 * If `timer` parameter isn't set, returns undefined.
4265 *
4266 * @returns {number | undefined}
4267 */
4268 const toggleTimer = () => {
4269 const timer = globalState.timeout;
4270 return timer && (timer.running ? stopTimer() : resumeTimer());
4271 };
4272
4273 /**
4274 * Increase timer. Returns number of milliseconds of an updated timer.
4275 * If `timer` parameter isn't set, returns undefined.
4276 *
4277 * @param {number} ms
4278 * @returns {number | undefined}
4279 */
4280 const increaseTimer = ms => {
4281 if (globalState.timeout) {
4282 const remaining = globalState.timeout.increase(ms);
4283 animateTimerProgressBar(remaining, true);
4284 return remaining;
4285 }
4286 };
4287
4288 /**
4289 * Check if timer is running. Returns true if timer is running
4290 * or false if timer is paused or stopped.
4291 * If `timer` parameter isn't set, returns undefined
4292 *
4293 * @returns {boolean}
4294 */
4295 const isTimerRunning = () => {
4296 return Boolean(globalState.timeout && globalState.timeout.isRunning());
4297 };
4298
4299 let bodyClickListenerAdded = false;
4300 /** @type {Record<string, any>} */
4301 const clickHandlers = {};
4302
4303 /**
4304 * @this {any}
4305 * @param {string} attr
4306 */
4307 function bindClickHandler(attr = 'data-swal-template') {
4308 clickHandlers[attr] = this;
4309 if (!bodyClickListenerAdded) {
4310 document.body.addEventListener('click', bodyClickListener);
4311 bodyClickListenerAdded = true;
4312 }
4313 }
4314
4315 /**
4316 * @param {MouseEvent} event
4317 */
4318 const bodyClickListener = event => {
4319 for (let el = /** @type {any} */event.target; el && el !== document; el = el.parentNode) {
4320 for (const attr in clickHandlers) {
4321 const template = el.getAttribute && el.getAttribute(attr);
4322 if (template) {
4323 clickHandlers[attr].fire({
4324 template
4325 });
4326 return;
4327 }
4328 }
4329 }
4330 };
4331
4332 // Source: https://gist.github.com/mudge/5830382?permalink_comment_id=2691957#gistcomment-2691957
4333
4334 class EventEmitter {
4335 constructor() {
4336 /** @type {Events} */
4337 this.events = {};
4338 }
4339
4340 /**
4341 * @param {string} eventName
4342 * @returns {EventHandlers}
4343 */
4344 _getHandlersByEventName(eventName) {
4345 if (typeof this.events[eventName] === 'undefined') {
4346 // not Set because we need to keep the FIFO order
4347 // https://github.com/sweetalert2/sweetalert2/pull/2763#discussion_r1748990334
4348 this.events[eventName] = [];
4349 }
4350 return this.events[eventName];
4351 }
4352
4353 /**
4354 * @param {string} eventName
4355 * @param {EventHandler} eventHandler
4356 */
4357 on(eventName, eventHandler) {
4358 const currentHandlers = this._getHandlersByEventName(eventName);
4359 if (!currentHandlers.includes(eventHandler)) {
4360 currentHandlers.push(eventHandler);
4361 }
4362 }
4363
4364 /**
4365 * @param {string} eventName
4366 * @param {EventHandler} eventHandler
4367 */
4368 once(eventName, eventHandler) {
4369 /**
4370 * @param {...any} args
4371 */
4372 const onceFn = (...args) => {
4373 this.removeListener(eventName, onceFn);
4374 // @ts-ignore
4375 eventHandler.apply(this, args);
4376 };
4377 this.on(eventName, onceFn);
4378 }
4379
4380 /**
4381 * @param {string} eventName
4382 * @param {...any} args
4383 */
4384 emit(eventName, ...args) {
4385 this._getHandlersByEventName(eventName).forEach(
4386 /**
4387 * @param {EventHandler} eventHandler
4388 */
4389 eventHandler => {
4390 try {
4391 // @ts-ignore
4392 eventHandler.apply(this, args);
4393 } catch (error) {
4394 console.error(error);
4395 }
4396 });
4397 }
4398
4399 /**
4400 * @param {string} eventName
4401 * @param {EventHandler} eventHandler
4402 */
4403 removeListener(eventName, eventHandler) {
4404 const currentHandlers = this._getHandlersByEventName(eventName);
4405 const index = currentHandlers.indexOf(eventHandler);
4406 if (index > -1) {
4407 currentHandlers.splice(index, 1);
4408 }
4409 }
4410
4411 /**
4412 * @param {string} eventName
4413 */
4414 removeAllListeners(eventName) {
4415 if (this.events[eventName] !== undefined) {
4416 // https://github.com/sweetalert2/sweetalert2/pull/2763#discussion_r1749239222
4417 this.events[eventName].length = 0;
4418 }
4419 }
4420 reset() {
4421 this.events = {};
4422 }
4423 }
4424
4425 globalState.eventEmitter = new EventEmitter();
4426
4427 /**
4428 * @param {string} eventName
4429 * @param {EventHandler} eventHandler
4430 */
4431 const on = (eventName, eventHandler) => {
4432 if (globalState.eventEmitter) {
4433 globalState.eventEmitter.on(eventName, eventHandler);
4434 }
4435 };
4436
4437 /**
4438 * @param {string} eventName
4439 * @param {EventHandler} eventHandler
4440 */
4441 const once = (eventName, eventHandler) => {
4442 if (globalState.eventEmitter) {
4443 globalState.eventEmitter.once(eventName, eventHandler);
4444 }
4445 };
4446
4447 /**
4448 * @param {string} [eventName]
4449 * @param {EventHandler} [eventHandler]
4450 */
4451 const off = (eventName, eventHandler) => {
4452 if (!globalState.eventEmitter) {
4453 return;
4454 }
4455
4456 // Remove all handlers for all events
4457 if (!eventName) {
4458 globalState.eventEmitter.reset();
4459 return;
4460 }
4461 if (eventHandler) {
4462 // Remove a specific handler
4463 globalState.eventEmitter.removeListener(eventName, eventHandler);
4464 } else {
4465 // Remove all handlers for a specific event
4466 globalState.eventEmitter.removeAllListeners(eventName);
4467 }
4468 };
4469
4470 var staticMethods = /*#__PURE__*/Object.freeze({
4471 __proto__: null,
4472 argsToParams: argsToParams,
4473 bindClickHandler: bindClickHandler,
4474 clickCancel: clickCancel,
4475 clickConfirm: clickConfirm,
4476 clickDeny: clickDeny,
4477 enableLoading: showLoading,
4478 fire: fire,
4479 getActions: getActions,
4480 getCancelButton: getCancelButton,
4481 getCloseButton: getCloseButton,
4482 getConfirmButton: getConfirmButton,
4483 getContainer: getContainer,
4484 getDenyButton: getDenyButton,
4485 getFocusableElements: getFocusableElements,
4486 getFooter: getFooter,
4487 getHtmlContainer: getHtmlContainer,
4488 getIcon: getIcon,
4489 getIconContent: getIconContent,
4490 getImage: getImage,
4491 getInputLabel: getInputLabel,
4492 getLoader: getLoader,
4493 getPopup: getPopup,
4494 getProgressSteps: getProgressSteps,
4495 getTimerLeft: getTimerLeft,
4496 getTimerProgressBar: getTimerProgressBar,
4497 getTitle: getTitle,
4498 getValidationMessage: getValidationMessage,
4499 increaseTimer: increaseTimer,
4500 isDeprecatedParameter: isDeprecatedParameter,
4501 isLoading: isLoading,
4502 isTimerRunning: isTimerRunning,
4503 isUpdatableParameter: isUpdatableParameter,
4504 isValidParameter: isValidParameter,
4505 isVisible: isVisible,
4506 mixin: mixin,
4507 off: off,
4508 on: on,
4509 once: once,
4510 resumeTimer: resumeTimer,
4511 showLoading: showLoading,
4512 stopTimer: stopTimer,
4513 toggleTimer: toggleTimer
4514 });
4515
4516 class Timer {
4517 /**
4518 * @param {() => void} callback
4519 * @param {number} delay
4520 */
4521 constructor(callback, delay) {
4522 this.callback = callback;
4523 this.remaining = delay;
4524 this.running = false;
4525 this.start();
4526 }
4527
4528 /**
4529 * @returns {number}
4530 */
4531 start() {
4532 if (!this.running) {
4533 this.running = true;
4534 this.started = new Date();
4535 this.id = setTimeout(this.callback, this.remaining);
4536 }
4537 return this.remaining;
4538 }
4539
4540 /**
4541 * @returns {number}
4542 */
4543 stop() {
4544 if (this.started && this.running) {
4545 this.running = false;
4546 clearTimeout(this.id);
4547 this.remaining -= new Date().getTime() - this.started.getTime();
4548 }
4549 return this.remaining;
4550 }
4551
4552 /**
4553 * @param {number} n
4554 * @returns {number}
4555 */
4556 increase(n) {
4557 const running = this.running;
4558 if (running) {
4559 this.stop();
4560 }
4561 this.remaining += n;
4562 if (running) {
4563 this.start();
4564 }
4565 return this.remaining;
4566 }
4567
4568 /**
4569 * @returns {number}
4570 */
4571 getTimerLeft() {
4572 if (this.running) {
4573 this.stop();
4574 this.start();
4575 }
4576 return this.remaining;
4577 }
4578
4579 /**
4580 * @returns {boolean}
4581 */
4582 isRunning() {
4583 return this.running;
4584 }
4585 }
4586
4587 const swalStringParams = ['swal-title', 'swal-html', 'swal-footer'];
4588
4589 /**
4590 * @param {SweetAlertOptions} params
4591 * @returns {SweetAlertOptions}
4592 */
4593 const getTemplateParams = params => {
4594 const template = typeof params.template === 'string' ? (/** @type {HTMLTemplateElement} */document.querySelector(params.template)) : params.template;
4595 if (!template) {
4596 return {};
4597 }
4598 /** @type {DocumentFragment} */
4599 const templateContent = template.content;
4600 showWarningsForElements(templateContent);
4601 const result = Object.assign(getSwalParams(templateContent), getSwalFunctionParams(templateContent), getSwalButtons(templateContent), getSwalImage(templateContent), getSwalIcon(templateContent), getSwalInput(templateContent), getSwalStringParams(templateContent, swalStringParams));
4602 return result;
4603 };
4604
4605 /**
4606 * @param {DocumentFragment} templateContent
4607 * @returns {Record<string, string | boolean | number>}
4608 */
4609 const getSwalParams = templateContent => {
4610 /** @type {Record<string, string | boolean | number>} */
4611 const result = {};
4612 /** @type {HTMLElement[]} */
4613 const swalParams = Array.from(templateContent.querySelectorAll('swal-param'));
4614 swalParams.forEach(param => {
4615 showWarningsForAttributes(param, ['name', 'value']);
4616 const paramName = /** @type {keyof SweetAlertOptions} */param.getAttribute('name');
4617 const value = param.getAttribute('value');
4618 if (!paramName || !value) {
4619 return;
4620 }
4621 if (paramName in defaultParams && typeof defaultParams[(/** @type {keyof typeof defaultParams} */paramName)] === 'boolean') {
4622 result[paramName] = value !== 'false';
4623 } else if (paramName in defaultParams && typeof defaultParams[(/** @type {keyof typeof defaultParams} */paramName)] === 'object') {
4624 result[paramName] = JSON.parse(value);
4625 } else {
4626 result[paramName] = value;
4627 }
4628 });
4629 return result;
4630 };
4631
4632 /**
4633 * @param {DocumentFragment} templateContent
4634 * @returns {Record<string, () => void>}
4635 */
4636 const getSwalFunctionParams = templateContent => {
4637 /** @type {Record<string, () => void>} */
4638 const result = {};
4639 /** @type {HTMLElement[]} */
4640 const swalFunctions = Array.from(templateContent.querySelectorAll('swal-function-param'));
4641 swalFunctions.forEach(param => {
4642 const paramName = /** @type {keyof SweetAlertOptions} */param.getAttribute('name');
4643 const value = param.getAttribute('value');
4644 if (!paramName || !value) {
4645 return;
4646 }
4647 result[paramName] = new Function(`return ${value}`)();
4648 });
4649 return result;
4650 };
4651
4652 /**
4653 * @param {DocumentFragment} templateContent
4654 * @returns {Record<string, string | boolean>}
4655 */
4656 const getSwalButtons = templateContent => {
4657 /** @type {Record<string, string | boolean>} */
4658 const result = {};
4659 /** @type {HTMLElement[]} */
4660 const swalButtons = Array.from(templateContent.querySelectorAll('swal-button'));
4661 swalButtons.forEach(button => {
4662 showWarningsForAttributes(button, ['type', 'color', 'aria-label']);
4663 const type = button.getAttribute('type');
4664 if (!type || !['confirm', 'cancel', 'deny'].includes(type)) {
4665 return;
4666 }
4667 result[`${type}ButtonText`] = button.innerHTML;
4668 result[`show${capitalizeFirstLetter(type)}Button`] = true;
4669 if (button.hasAttribute('color')) {
4670 const color = button.getAttribute('color');
4671 if (color !== null) {
4672 result[`${type}ButtonColor`] = color;
4673 }
4674 }
4675 if (button.hasAttribute('aria-label')) {
4676 const ariaLabel = button.getAttribute('aria-label');
4677 if (ariaLabel !== null) {
4678 result[`${type}ButtonAriaLabel`] = ariaLabel;
4679 }
4680 }
4681 });
4682 return result;
4683 };
4684
4685 /**
4686 * @param {DocumentFragment} templateContent
4687 * @returns {Pick<SweetAlertOptions, 'imageUrl' | 'imageWidth' | 'imageHeight' | 'imageAlt'>}
4688 */
4689 const getSwalImage = templateContent => {
4690 const result = {};
4691 /** @type {HTMLElement | null} */
4692 const image = templateContent.querySelector('swal-image');
4693 if (image) {
4694 showWarningsForAttributes(image, ['src', 'width', 'height', 'alt']);
4695 if (image.hasAttribute('src')) {
4696 result.imageUrl = image.getAttribute('src') || undefined;
4697 }
4698 if (image.hasAttribute('width')) {
4699 result.imageWidth = image.getAttribute('width') || undefined;
4700 }
4701 if (image.hasAttribute('height')) {
4702 result.imageHeight = image.getAttribute('height') || undefined;
4703 }
4704 if (image.hasAttribute('alt')) {
4705 result.imageAlt = image.getAttribute('alt') || undefined;
4706 }
4707 }
4708 return result;
4709 };
4710
4711 /**
4712 * @param {DocumentFragment} templateContent
4713 * @returns {object}
4714 */
4715 const getSwalIcon = templateContent => {
4716 const result = {};
4717 /** @type {HTMLElement | null} */
4718 const icon = templateContent.querySelector('swal-icon');
4719 if (icon) {
4720 showWarningsForAttributes(icon, ['type', 'color']);
4721 if (icon.hasAttribute('type')) {
4722 result.icon = icon.getAttribute('type');
4723 }
4724 if (icon.hasAttribute('color')) {
4725 result.iconColor = icon.getAttribute('color');
4726 }
4727 result.iconHtml = icon.innerHTML;
4728 }
4729 return result;
4730 };
4731
4732 /**
4733 * @param {DocumentFragment} templateContent
4734 * @returns {object}
4735 */
4736 const getSwalInput = templateContent => {
4737 /** @type {Record<string, any>} */
4738 const result = {};
4739 /** @type {HTMLElement | null} */
4740 const input = templateContent.querySelector('swal-input');
4741 if (input) {
4742 showWarningsForAttributes(input, ['type', 'label', 'placeholder', 'value']);
4743 result.input = input.getAttribute('type') || 'text';
4744 if (input.hasAttribute('label')) {
4745 result.inputLabel = input.getAttribute('label');
4746 }
4747 if (input.hasAttribute('placeholder')) {
4748 result.inputPlaceholder = input.getAttribute('placeholder');
4749 }
4750 if (input.hasAttribute('value')) {
4751 result.inputValue = input.getAttribute('value');
4752 }
4753 }
4754 /** @type {HTMLElement[]} */
4755 const inputOptions = Array.from(templateContent.querySelectorAll('swal-input-option'));
4756 if (inputOptions.length) {
4757 result.inputOptions = {};
4758 inputOptions.forEach(option => {
4759 showWarningsForAttributes(option, ['value']);
4760 const optionValue = option.getAttribute('value');
4761 if (!optionValue) {
4762 return;
4763 }
4764 const optionName = option.innerHTML;
4765 result.inputOptions[optionValue] = optionName;
4766 });
4767 }
4768 return result;
4769 };
4770
4771 /**
4772 * @param {DocumentFragment} templateContent
4773 * @param {string[]} paramNames
4774 * @returns {Record<string, string>}
4775 */
4776 const getSwalStringParams = (templateContent, paramNames) => {
4777 /** @type {Record<string, string>} */
4778 const result = {};
4779 for (const i in paramNames) {
4780 const paramName = paramNames[i];
4781 /** @type {HTMLElement | null} */
4782 const tag = templateContent.querySelector(paramName);
4783 if (tag) {
4784 showWarningsForAttributes(tag, []);
4785 result[paramName.replace(/^swal-/, '')] = tag.innerHTML.trim();
4786 }
4787 }
4788 return result;
4789 };
4790
4791 /**
4792 * @param {DocumentFragment} templateContent
4793 */
4794 const showWarningsForElements = templateContent => {
4795 const allowedElements = swalStringParams.concat(['swal-param', 'swal-function-param', 'swal-button', 'swal-image', 'swal-icon', 'swal-input', 'swal-input-option']);
4796 Array.from(templateContent.children).forEach(el => {
4797 const tagName = el.tagName.toLowerCase();
4798 if (!allowedElements.includes(tagName)) {
4799 warn(`Unrecognized element <${tagName}>`);
4800 }
4801 });
4802 };
4803
4804 /**
4805 * @param {HTMLElement} el
4806 * @param {string[]} allowedAttributes
4807 */
4808 const showWarningsForAttributes = (el, allowedAttributes) => {
4809 Array.from(el.attributes).forEach(attribute => {
4810 if (allowedAttributes.indexOf(attribute.name) === -1) {
4811 warn([`Unrecognized attribute "${attribute.name}" on <${el.tagName.toLowerCase()}>.`, `${allowedAttributes.length ? `Allowed attributes are: ${allowedAttributes.join(', ')}` : 'To set the value, use HTML within the element.'}`]);
4812 }
4813 });
4814 };
4815
4816 const SHOW_CLASS_TIMEOUT = 10;
4817
4818 /**
4819 * Open popup, add necessary classes and styles, fix scrollbar
4820 *
4821 * @param {SweetAlertOptions} params
4822 */
4823 const openPopup = params => {
4824 var _globalState$eventEmi, _globalState$eventEmi2;
4825 const container = getContainer();
4826 const popup = getPopup();
4827 if (!container || !popup) {
4828 return;
4829 }
4830 if (typeof params.willOpen === 'function') {
4831 params.willOpen(popup);
4832 }
4833 (_globalState$eventEmi = globalState.eventEmitter) === null || _globalState$eventEmi === void 0 || _globalState$eventEmi.emit('willOpen', popup);
4834 const bodyStyles = window.getComputedStyle(document.body);
4835 const initialBodyOverflow = bodyStyles.overflowY;
4836 addClasses(container, popup, params);
4837
4838 // scrolling is 'hidden' until animation is done, after that 'auto'
4839 setTimeout(() => {
4840 setScrollingVisibility(container, popup);
4841 }, SHOW_CLASS_TIMEOUT);
4842 if (isModal()) {
4843 // Using ternary instead of ?? operator for Webpack 4 compatibility
4844 fixScrollContainer(container, params.scrollbarPadding !== undefined ? params.scrollbarPadding : false, initialBodyOverflow);
4845 setAriaHidden();
4846 }
4847 if (!isToast() && !globalState.previousActiveElement) {
4848 globalState.previousActiveElement = document.activeElement;
4849 }
4850 if (typeof params.didOpen === 'function') {
4851 const didOpen = params.didOpen;
4852 setTimeout(() => didOpen(popup));
4853 }
4854 (_globalState$eventEmi2 = globalState.eventEmitter) === null || _globalState$eventEmi2 === void 0 || _globalState$eventEmi2.emit('didOpen', popup);
4855 };
4856
4857 /**
4858 * @param {Event} event
4859 */
4860 const swalOpenAnimationFinished = event => {
4861 const popup = getPopup();
4862 if (!popup || event.target !== popup) {
4863 return;
4864 }
4865 const container = getContainer();
4866 if (!container) {
4867 return;
4868 }
4869 popup.removeEventListener('animationend', swalOpenAnimationFinished);
4870 popup.removeEventListener('transitionend', swalOpenAnimationFinished);
4871 container.style.overflowY = 'auto';
4872
4873 // no-transition is added in init() in case one swal is opened right after another
4874 removeClass(container, swalClasses['no-transition']);
4875 };
4876
4877 /**
4878 * @param {HTMLElement} container
4879 * @param {HTMLElement} popup
4880 */
4881 const setScrollingVisibility = (container, popup) => {
4882 if (hasCssAnimation(popup)) {
4883 container.style.overflowY = 'hidden';
4884 popup.addEventListener('animationend', swalOpenAnimationFinished);
4885 popup.addEventListener('transitionend', swalOpenAnimationFinished);
4886 } else {
4887 container.style.overflowY = 'auto';
4888 }
4889 };
4890
4891 /**
4892 * @param {HTMLElement} container
4893 * @param {boolean} scrollbarPadding
4894 * @param {string} initialBodyOverflow
4895 */
4896 const fixScrollContainer = (container, scrollbarPadding, initialBodyOverflow) => {
4897 iOSfix();
4898 if (scrollbarPadding && initialBodyOverflow !== 'hidden') {
4899 replaceScrollbarWithPadding(initialBodyOverflow);
4900 }
4901
4902 // sweetalert2/issues/1247
4903 setTimeout(() => {
4904 container.scrollTop = 0;
4905 });
4906 };
4907
4908 /**
4909 * @param {HTMLElement} container
4910 * @param {HTMLElement} popup
4911 * @param {SweetAlertOptions} params
4912 */
4913 const addClasses = (container, popup, params) => {
4914 var _params$showClass;
4915 if ((_params$showClass = params.showClass) !== null && _params$showClass !== void 0 && _params$showClass.backdrop) {
4916 addClass(container, params.showClass.backdrop);
4917 }
4918 if (params.animation) {
4919 // this workaround with opacity is needed for https://github.com/sweetalert2/sweetalert2/issues/2059
4920 popup.style.setProperty('opacity', '0', 'important');
4921 show(popup, 'grid');
4922 setTimeout(() => {
4923 var _params$showClass2;
4924 // Animate popup right after showing it
4925 if ((_params$showClass2 = params.showClass) !== null && _params$showClass2 !== void 0 && _params$showClass2.popup) {
4926 addClass(popup, params.showClass.popup);
4927 }
4928 // and remove the opacity workaround
4929 popup.style.removeProperty('opacity');
4930 }, SHOW_CLASS_TIMEOUT); // 10ms in order to fix #2062
4931 } else {
4932 show(popup, 'grid');
4933 }
4934 addClass([document.documentElement, document.body], swalClasses.shown);
4935 if (params.heightAuto && params.backdrop && !params.toast) {
4936 addClass([document.documentElement, document.body], swalClasses['height-auto']);
4937 }
4938 };
4939
4940 var defaultInputValidators = {
4941 /**
4942 * @param {string} string
4943 * @param {string} [validationMessage]
4944 * @returns {Promise<string | void>}
4945 */
4946 email: (string, validationMessage) => {
4947 return /^[a-zA-Z0-9.+_'-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]+$/.test(string) ? Promise.resolve() : Promise.resolve(validationMessage || 'Invalid email address');
4948 },
4949 /**
4950 * @param {string} string
4951 * @param {string} [validationMessage]
4952 * @returns {Promise<string | void>}
4953 */
4954 url: (string, validationMessage) => {
4955 // taken from https://stackoverflow.com/a/3809435 with a small change from #1306 and #2013
4956 return /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/.test(string) ? Promise.resolve() : Promise.resolve(validationMessage || 'Invalid URL');
4957 }
4958 };
4959
4960 /**
4961 * @param {SweetAlertOptions} params
4962 */
4963 function setDefaultInputValidators(params) {
4964 // Use default `inputValidator` for supported input types if not provided
4965 if (params.inputValidator) {
4966 return;
4967 }
4968 if (params.input === 'email') {
4969 params.inputValidator = defaultInputValidators['email'];
4970 }
4971 if (params.input === 'url') {
4972 params.inputValidator = defaultInputValidators['url'];
4973 }
4974 }
4975
4976 /**
4977 * @param {SweetAlertOptions} params
4978 */
4979 function validateCustomTargetElement(params) {
4980 // Determine if the custom target element is valid
4981 if (!params.target || typeof params.target === 'string' && !document.querySelector(params.target) || typeof params.target !== 'string' && !params.target.appendChild) {
4982 warn('Target parameter is not valid, defaulting to "body"');
4983 params.target = 'body';
4984 }
4985 }
4986
4987 /**
4988 * Set type, text and actions on popup
4989 *
4990 * @param {SweetAlertOptions} params
4991 */
4992 function setParameters(params) {
4993 setDefaultInputValidators(params);
4994
4995 // showLoaderOnConfirm && preConfirm
4996 if (params.showLoaderOnConfirm && !params.preConfirm) {
4997 warn('showLoaderOnConfirm is set to true, but preConfirm is not defined.\n' + 'showLoaderOnConfirm should be used together with preConfirm, see usage example:\n' + 'https://sweetalert2.github.io/#ajax-request');
4998 }
4999 validateCustomTargetElement(params);
5000
5001 // Replace newlines with <br> in title
5002 if (typeof params.title === 'string') {
5003 params.title = params.title.split('\n').join('<br />');
5004 }
5005 init(params);
5006 }
5007
5008 /** @type {SweetAlert} */
5009 let currentInstance;
5010 var _promise = /*#__PURE__*/new WeakMap();
5011 class SweetAlert {
5012 /**
5013 * @param {...(SweetAlertOptions | string)} args
5014 * @this {SweetAlert}
5015 */
5016 constructor(...args) {
5017 /**
5018 * @type {Promise<SweetAlertResult>}
5019 */
5020 _classPrivateFieldInitSpec(this, _promise, /** @type {Promise<SweetAlertResult>} */Promise.resolve({
5021 isConfirmed: false,
5022 isDenied: false,
5023 isDismissed: true
5024 }));
5025 // Prevent run in Node env
5026 if (typeof window === 'undefined') {
5027 return;
5028 }
5029 currentInstance = this;
5030
5031 // @ts-ignore
5032 const outerParams = Object.freeze(this.constructor.argsToParams(args));
5033
5034 /** @type {Readonly<SweetAlertOptions>} */
5035 this.params = outerParams;
5036
5037 /** @type {boolean} */
5038 this.isAwaitingPromise = false;
5039 _classPrivateFieldSet2(_promise, this, this._main(currentInstance.params));
5040 }
5041
5042 /**
5043 * @param {any} userParams
5044 * @param {any} mixinParams
5045 */
5046 _main(userParams, mixinParams = {}) {
5047 showWarningsForParams(Object.assign({}, mixinParams, userParams));
5048 if (globalState.currentInstance) {
5049 const swalPromiseResolve = privateMethods.swalPromiseResolve.get(globalState.currentInstance);
5050 const {
5051 isAwaitingPromise
5052 } = globalState.currentInstance;
5053 globalState.currentInstance._destroy();
5054 if (!isAwaitingPromise) {
5055 swalPromiseResolve({
5056 isDismissed: true
5057 });
5058 }
5059 if (isModal()) {
5060 unsetAriaHidden();
5061 }
5062 }
5063 globalState.currentInstance = currentInstance;
5064 const innerParams = prepareParams(userParams, mixinParams);
5065 setParameters(innerParams);
5066 Object.freeze(innerParams);
5067
5068 // clear the previous timer
5069 if (globalState.timeout) {
5070 globalState.timeout.stop();
5071 delete globalState.timeout;
5072 }
5073
5074 // clear the restore focus timeout
5075 clearTimeout(globalState.restoreFocusTimeout);
5076 const domCache = populateDomCache(currentInstance);
5077 render(currentInstance, innerParams);
5078 privateProps.innerParams.set(currentInstance, innerParams);
5079 return swalPromise(currentInstance, domCache, innerParams);
5080 }
5081
5082 // `catch` cannot be the name of a module export, so we define our thenable methods here instead
5083 /**
5084 * @param {any} onFulfilled
5085 */
5086 then(onFulfilled) {
5087 return _classPrivateFieldGet2(_promise, this).then(onFulfilled);
5088 }
5089
5090 /**
5091 * @param {any} onFinally
5092 */
5093 finally(onFinally) {
5094 return _classPrivateFieldGet2(_promise, this).finally(onFinally);
5095 }
5096 }
5097
5098 /**
5099 * @param {SweetAlert} instance
5100 * @param {DomCache} domCache
5101 * @param {SweetAlertOptions} innerParams
5102 * @returns {Promise<SweetAlertResult>}
5103 */
5104 const swalPromise = (instance, domCache, innerParams) => {
5105 return new Promise((resolve, reject) => {
5106 // functions to handle all closings/dismissals
5107 /**
5108 * @param {DismissReason} dismiss
5109 */
5110 const dismissWith = dismiss => {
5111 instance.close({
5112 isDismissed: true,
5113 dismiss,
5114 isConfirmed: false,
5115 isDenied: false
5116 });
5117 };
5118 privateMethods.swalPromiseResolve.set(instance, resolve);
5119 privateMethods.swalPromiseReject.set(instance, reject);
5120 domCache.confirmButton.onclick = () => {
5121 handleConfirmButtonClick(instance);
5122 };
5123 domCache.denyButton.onclick = () => {
5124 handleDenyButtonClick(instance);
5125 };
5126 domCache.cancelButton.onclick = () => {
5127 handleCancelButtonClick(instance, dismissWith);
5128 };
5129 domCache.closeButton.onclick = () => {
5130 dismissWith(DismissReason.close);
5131 };
5132 handlePopupClick(innerParams, domCache, dismissWith);
5133 addKeydownHandler(globalState, innerParams, dismissWith);
5134 handleInputOptionsAndValue(instance, innerParams);
5135 openPopup(innerParams);
5136 setupTimer(globalState, innerParams, dismissWith);
5137 initFocus(domCache, innerParams);
5138
5139 // Scroll container to top on open (#1247, #1946)
5140 setTimeout(() => {
5141 domCache.container.scrollTop = 0;
5142 });
5143 });
5144 };
5145
5146 /**
5147 * @param {SweetAlertOptions} userParams
5148 * @param {SweetAlertOptions} mixinParams
5149 * @returns {SweetAlertOptions}
5150 */
5151 const prepareParams = (userParams, mixinParams) => {
5152 const templateParams = getTemplateParams(userParams);
5153 const params = Object.assign({}, defaultParams, mixinParams, templateParams, userParams); // precedence is described in #2131
5154 params.showClass = Object.assign({}, defaultParams.showClass, params.showClass);
5155 params.hideClass = Object.assign({}, defaultParams.hideClass, params.hideClass);
5156 if (params.animation === false) {
5157 params.showClass = {
5158 backdrop: 'swal2-noanimation'
5159 };
5160 params.hideClass = {};
5161 }
5162 return params;
5163 };
5164
5165 /**
5166 * @param {SweetAlert} instance
5167 * @returns {DomCache}
5168 */
5169 const populateDomCache = instance => {
5170 const domCache = /** @type {DomCache} */{
5171 popup: (/** @type {HTMLElement} */getPopup()),
5172 container: (/** @type {HTMLElement} */getContainer()),
5173 actions: (/** @type {HTMLElement} */getActions()),
5174 confirmButton: (/** @type {HTMLElement} */getConfirmButton()),
5175 denyButton: (/** @type {HTMLElement} */getDenyButton()),
5176 cancelButton: (/** @type {HTMLElement} */getCancelButton()),
5177 loader: (/** @type {HTMLElement} */getLoader()),
5178 closeButton: (/** @type {HTMLElement} */getCloseButton()),
5179 validationMessage: (/** @type {HTMLElement} */getValidationMessage()),
5180 progressSteps: (/** @type {HTMLElement} */getProgressSteps())
5181 };
5182 privateProps.domCache.set(instance, domCache);
5183 return domCache;
5184 };
5185
5186 /**
5187 * @param {GlobalState} globalState
5188 * @param {SweetAlertOptions} innerParams
5189 * @param {(dismiss: DismissReason) => void} dismissWith
5190 */
5191 const setupTimer = (globalState, innerParams, dismissWith) => {
5192 const timerProgressBar = getTimerProgressBar();
5193 hide(timerProgressBar);
5194 if (innerParams.timer) {
5195 globalState.timeout = new Timer(() => {
5196 dismissWith('timer');
5197 delete globalState.timeout;
5198 }, innerParams.timer);
5199 if (innerParams.timerProgressBar && timerProgressBar) {
5200 show(timerProgressBar);
5201 applyCustomClass(timerProgressBar, innerParams, 'timerProgressBar');
5202 setTimeout(() => {
5203 if (globalState.timeout && globalState.timeout.running) {
5204 // timer can be already stopped or unset at this point
5205 animateTimerProgressBar(/** @type {number} */innerParams.timer);
5206 }
5207 });
5208 }
5209 }
5210 };
5211
5212 /**
5213 * Initialize focus in the popup:
5214 *
5215 * 1. If `toast` is `true`, don't steal focus from the document.
5216 * 2. Else if there is an [autofocus] element, focus it.
5217 * 3. Else if `focusConfirm` is `true` and confirm button is visible, focus it.
5218 * 4. Else if `focusDeny` is `true` and deny button is visible, focus it.
5219 * 5. Else if `focusCancel` is `true` and cancel button is visible, focus it.
5220 * 6. Else focus the first focusable element in a popup (if any).
5221 *
5222 * @param {DomCache} domCache
5223 * @param {SweetAlertOptions} innerParams
5224 */
5225 const initFocus = (domCache, innerParams) => {
5226 if (innerParams.toast) {
5227 return;
5228 }
5229 // TODO: this is dumb, remove `allowEnterKey` param in the next major version
5230 if (!callIfFunction(innerParams.allowEnterKey)) {
5231 warnAboutDeprecation('allowEnterKey');
5232 blurActiveElement();
5233 return;
5234 }
5235 if (focusAutofocus(domCache)) {
5236 return;
5237 }
5238 if (focusButton(domCache, innerParams)) {
5239 return;
5240 }
5241 setFocus(-1, 1);
5242 };
5243
5244 /**
5245 * @param {DomCache} domCache
5246 * @returns {boolean}
5247 */
5248 const focusAutofocus = domCache => {
5249 const autofocusElements = Array.from(domCache.popup.querySelectorAll('[autofocus]'));
5250 for (const autofocusElement of autofocusElements) {
5251 if (autofocusElement instanceof HTMLElement && isVisible$1(autofocusElement)) {
5252 autofocusElement.focus();
5253 return true;
5254 }
5255 }
5256 return false;
5257 };
5258
5259 /**
5260 * @param {DomCache} domCache
5261 * @param {SweetAlertOptions} innerParams
5262 * @returns {boolean}
5263 */
5264 const focusButton = (domCache, innerParams) => {
5265 if (innerParams.focusDeny && isVisible$1(domCache.denyButton)) {
5266 domCache.denyButton.focus();
5267 return true;
5268 }
5269 if (innerParams.focusCancel && isVisible$1(domCache.cancelButton)) {
5270 domCache.cancelButton.focus();
5271 return true;
5272 }
5273 if (innerParams.focusConfirm && isVisible$1(domCache.confirmButton)) {
5274 domCache.confirmButton.focus();
5275 return true;
5276 }
5277 return false;
5278 };
5279 const blurActiveElement = () => {
5280 if (document.activeElement instanceof HTMLElement && typeof document.activeElement.blur === 'function') {
5281 document.activeElement.blur();
5282 }
5283 };
5284
5285 // Assign instance methods from src/instanceMethods/*.js to prototype
5286 SweetAlert.prototype.disableButtons = disableButtons;
5287 SweetAlert.prototype.enableButtons = enableButtons;
5288 SweetAlert.prototype.getInput = getInput;
5289 SweetAlert.prototype.disableInput = disableInput;
5290 SweetAlert.prototype.enableInput = enableInput;
5291 SweetAlert.prototype.hideLoading = hideLoading;
5292 SweetAlert.prototype.disableLoading = hideLoading;
5293 SweetAlert.prototype.showValidationMessage = showValidationMessage;
5294 SweetAlert.prototype.resetValidationMessage = resetValidationMessage;
5295 SweetAlert.prototype.close = close;
5296 SweetAlert.prototype.closePopup = close;
5297 SweetAlert.prototype.closeModal = close;
5298 SweetAlert.prototype.closeToast = close;
5299 SweetAlert.prototype.rejectPromise = rejectPromise;
5300 SweetAlert.prototype.update = update;
5301 SweetAlert.prototype._destroy = _destroy;
5302
5303 // Assign static methods from src/staticMethods/*.js to constructor
5304 Object.assign(SweetAlert, staticMethods);
5305
5306 // Proxy to instance methods to constructor, for now, for backwards compatibility
5307 Object.keys(instanceMethods).forEach(key => {
5308 /**
5309 * @param {...(SweetAlertOptions | string | undefined)} args
5310 * @returns {SweetAlertResult | Promise<SweetAlertResult> | undefined}
5311 */
5312 // @ts-ignore: Dynamic property assignment for backwards compatibility
5313 SweetAlert[key] = function (...args) {
5314 // @ts-ignore
5315 if (currentInstance && currentInstance[key]) {
5316 // @ts-ignore
5317 return currentInstance[key](...args);
5318 }
5319 return undefined;
5320 };
5321 });
5322 SweetAlert.DismissReason = DismissReason;
5323 SweetAlert.version = '11.26.17';
5324
5325 const Swal = SweetAlert;
5326 // @ts-ignore
5327 Swal.default = Swal;
5328
5329 return Swal;
5330
5331 }));
5332 if (typeof this !== 'undefined' && this.Sweetalert2){this.swal = this.sweetAlert = this.Swal = this.SweetAlert = this.Sweetalert2}
5333 "undefined"!=typeof document&&function(e,t){var n=e.createElement("style");if(e.getElementsByTagName("head")[0].appendChild(n),n.styleSheet)n.styleSheet.disabled||(n.styleSheet.cssText=t);else try{n.innerHTML=t}catch(e){n.innerText=t}}(document,":root{--swal2-outline: 0 0 0 3px rgba(100, 150, 200, 0.5);--swal2-container-padding: 0.625em;--swal2-backdrop: rgba(0, 0, 0, 0.4);--swal2-backdrop-transition: background-color 0.15s;--swal2-width: 32em;--swal2-padding: 0 0 1.25em;--swal2-border: none;--swal2-border-radius: 0.3125rem;--swal2-background: white;--swal2-color: #545454;--swal2-show-animation: swal2-show 0.3s;--swal2-hide-animation: swal2-hide 0.15s forwards;--swal2-icon-zoom: 1;--swal2-icon-animations: true;--swal2-title-padding: 0.8em 1em 0;--swal2-html-container-padding: 1em 1.6em 0.3em;--swal2-input-border: 1px solid #d9d9d9;--swal2-input-border-radius: 0.1875em;--swal2-input-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px transparent;--swal2-input-background: transparent;--swal2-input-transition: border-color 0.2s, box-shadow 0.2s;--swal2-input-hover-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px transparent;--swal2-input-focus-border: 1px solid #b4dbed;--swal2-input-focus-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px rgba(100, 150, 200, 0.5);--swal2-progress-step-background: #add8e6;--swal2-validation-message-background: #f0f0f0;--swal2-validation-message-color: #666;--swal2-footer-border-color: #eee;--swal2-footer-background: transparent;--swal2-footer-color: inherit;--swal2-timer-progress-bar-background: rgba(0, 0, 0, 0.3);--swal2-close-button-position: initial;--swal2-close-button-inset: auto;--swal2-close-button-font-size: 2.5em;--swal2-close-button-color: #ccc;--swal2-close-button-transition: color 0.2s, box-shadow 0.2s;--swal2-close-button-outline: initial;--swal2-close-button-box-shadow: inset 0 0 0 3px transparent;--swal2-close-button-focus-box-shadow: inset var(--swal2-outline);--swal2-close-button-hover-transform: none;--swal2-actions-justify-content: center;--swal2-actions-width: auto;--swal2-actions-margin: 1.25em auto 0;--swal2-actions-padding: 0;--swal2-actions-border-radius: 0;--swal2-actions-background: transparent;--swal2-action-button-transition: background-color 0.2s, box-shadow 0.2s;--swal2-action-button-hover: black 10%;--swal2-action-button-active: black 10%;--swal2-confirm-button-box-shadow: none;--swal2-confirm-button-border-radius: 0.25em;--swal2-confirm-button-background-color: #7066e0;--swal2-confirm-button-color: #fff;--swal2-deny-button-box-shadow: none;--swal2-deny-button-border-radius: 0.25em;--swal2-deny-button-background-color: #dc3741;--swal2-deny-button-color: #fff;--swal2-cancel-button-box-shadow: none;--swal2-cancel-button-border-radius: 0.25em;--swal2-cancel-button-background-color: #6e7881;--swal2-cancel-button-color: #fff;--swal2-toast-show-animation: swal2-toast-show 0.5s;--swal2-toast-hide-animation: swal2-toast-hide 0.1s forwards;--swal2-toast-border: none;--swal2-toast-box-shadow: 0 0 1px hsl(0deg 0% 0% / 0.075), 0 1px 2px hsl(0deg 0% 0% / 0.075), 1px 2px 4px hsl(0deg 0% 0% / 0.075), 1px 3px 8px hsl(0deg 0% 0% / 0.075), 2px 4px 16px hsl(0deg 0% 0% / 0.075)}[data-swal2-theme=dark]{--swal2-dark-theme-black: #19191a;--swal2-dark-theme-white: #e1e1e1;--swal2-background: var(--swal2-dark-theme-black);--swal2-color: var(--swal2-dark-theme-white);--swal2-footer-border-color: #555;--swal2-input-background: color-mix(in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10%);--swal2-validation-message-background: color-mix( in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10% );--swal2-validation-message-color: var(--swal2-dark-theme-white);--swal2-timer-progress-bar-background: rgba(255, 255, 255, 0.7)}@media(prefers-color-scheme: dark){[data-swal2-theme=auto]{--swal2-dark-theme-black: #19191a;--swal2-dark-theme-white: #e1e1e1;--swal2-background: var(--swal2-dark-theme-black);--swal2-color: var(--swal2-dark-theme-white);--swal2-footer-border-color: #555;--swal2-input-background: color-mix(in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10%);--swal2-validation-message-background: color-mix( in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10% );--swal2-validation-message-color: var(--swal2-dark-theme-white);--swal2-timer-progress-bar-background: rgba(255, 255, 255, 0.7)}}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown){overflow:hidden}body.swal2-height-auto{height:auto !important}body.swal2-no-backdrop .swal2-container{background-color:rgba(0,0,0,0) !important;pointer-events:none}body.swal2-no-backdrop .swal2-container .swal2-popup{pointer-events:all}body.swal2-no-backdrop .swal2-container .swal2-modal{box-shadow:0 0 10px var(--swal2-backdrop)}body.swal2-toast-shown .swal2-container{box-sizing:border-box;width:360px;max-width:100%;background-color:rgba(0,0,0,0);pointer-events:none}body.swal2-toast-shown .swal2-container.swal2-top{inset:0 auto auto 50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-top-end,body.swal2-toast-shown .swal2-container.swal2-top-right{inset:0 0 auto auto}body.swal2-toast-shown .swal2-container.swal2-top-start,body.swal2-toast-shown .swal2-container.swal2-top-left{inset:0 auto auto 0}body.swal2-toast-shown .swal2-container.swal2-center-start,body.swal2-toast-shown .swal2-container.swal2-center-left{inset:50% auto auto 0;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-center{inset:50% auto auto 50%;transform:translate(-50%, -50%)}body.swal2-toast-shown .swal2-container.swal2-center-end,body.swal2-toast-shown .swal2-container.swal2-center-right{inset:50% 0 auto auto;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-start,body.swal2-toast-shown .swal2-container.swal2-bottom-left{inset:auto auto 0 0}body.swal2-toast-shown .swal2-container.swal2-bottom{inset:auto auto 0 50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-end,body.swal2-toast-shown .swal2-container.swal2-bottom-right{inset:auto 0 0 auto}@media print{body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown){overflow-y:scroll !important}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown)>[aria-hidden=true]{display:none}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown) .swal2-container{position:static !important}}div:where(.swal2-container){display:grid;position:fixed;z-index:1060;inset:0;box-sizing:border-box;grid-template-areas:\"top-start top top-end\" \"center-start center center-end\" \"bottom-start bottom-center bottom-end\";grid-template-rows:minmax(min-content, auto) minmax(min-content, auto) minmax(min-content, auto);height:100%;padding:var(--swal2-container-padding);overflow-x:hidden;transition:var(--swal2-backdrop-transition);-webkit-overflow-scrolling:touch}div:where(.swal2-container).swal2-backdrop-show,div:where(.swal2-container).swal2-noanimation{background:var(--swal2-backdrop)}div:where(.swal2-container).swal2-backdrop-hide{background:rgba(0,0,0,0) !important}div:where(.swal2-container).swal2-top-start,div:where(.swal2-container).swal2-center-start,div:where(.swal2-container).swal2-bottom-start{grid-template-columns:minmax(0, 1fr) auto auto}div:where(.swal2-container).swal2-top,div:where(.swal2-container).swal2-center,div:where(.swal2-container).swal2-bottom{grid-template-columns:auto minmax(0, 1fr) auto}div:where(.swal2-container).swal2-top-end,div:where(.swal2-container).swal2-center-end,div:where(.swal2-container).swal2-bottom-end{grid-template-columns:auto auto minmax(0, 1fr)}div:where(.swal2-container).swal2-top-start>.swal2-popup{align-self:start}div:where(.swal2-container).swal2-top>.swal2-popup{grid-column:2;place-self:start center}div:where(.swal2-container).swal2-top-end>.swal2-popup,div:where(.swal2-container).swal2-top-right>.swal2-popup{grid-column:3;place-self:start end}div:where(.swal2-container).swal2-center-start>.swal2-popup,div:where(.swal2-container).swal2-center-left>.swal2-popup{grid-row:2;align-self:center}div:where(.swal2-container).swal2-center>.swal2-popup{grid-column:2;grid-row:2;place-self:center center}div:where(.swal2-container).swal2-center-end>.swal2-popup,div:where(.swal2-container).swal2-center-right>.swal2-popup{grid-column:3;grid-row:2;place-self:center end}div:where(.swal2-container).swal2-bottom-start>.swal2-popup,div:where(.swal2-container).swal2-bottom-left>.swal2-popup{grid-column:1;grid-row:3;align-self:end}div:where(.swal2-container).swal2-bottom>.swal2-popup{grid-column:2;grid-row:3;place-self:end center}div:where(.swal2-container).swal2-bottom-end>.swal2-popup,div:where(.swal2-container).swal2-bottom-right>.swal2-popup{grid-column:3;grid-row:3;place-self:end end}div:where(.swal2-container).swal2-grow-row>.swal2-popup,div:where(.swal2-container).swal2-grow-fullscreen>.swal2-popup{grid-column:1/4;width:100%}div:where(.swal2-container).swal2-grow-column>.swal2-popup,div:where(.swal2-container).swal2-grow-fullscreen>.swal2-popup{grid-row:1/4;align-self:stretch}div:where(.swal2-container).swal2-no-transition{transition:none !important}div:where(.swal2-container)[popover]{width:auto;border:0}div:where(.swal2-container) div:where(.swal2-popup){display:none;position:relative;box-sizing:border-box;grid-template-columns:minmax(0, 100%);width:var(--swal2-width);max-width:100%;padding:var(--swal2-padding);border:var(--swal2-border);border-radius:var(--swal2-border-radius);background:var(--swal2-background);color:var(--swal2-color);font-family:inherit;font-size:1rem;container-name:swal2-popup}div:where(.swal2-container) div:where(.swal2-popup):focus{outline:none}div:where(.swal2-container) div:where(.swal2-popup).swal2-loading{overflow-y:hidden}div:where(.swal2-container) div:where(.swal2-popup).swal2-draggable{cursor:grab}div:where(.swal2-container) div:where(.swal2-popup).swal2-draggable div:where(.swal2-icon){cursor:grab}div:where(.swal2-container) div:where(.swal2-popup).swal2-dragging{cursor:grabbing}div:where(.swal2-container) div:where(.swal2-popup).swal2-dragging div:where(.swal2-icon){cursor:grabbing}div:where(.swal2-container) h2:where(.swal2-title){position:relative;max-width:100%;margin:0;padding:var(--swal2-title-padding);color:inherit;font-size:1.875em;font-weight:600;text-align:center;text-transform:none;overflow-wrap:break-word;cursor:initial}div:where(.swal2-container) div:where(.swal2-actions){display:flex;z-index:1;box-sizing:border-box;flex-wrap:wrap;align-items:center;justify-content:var(--swal2-actions-justify-content);width:var(--swal2-actions-width);margin:var(--swal2-actions-margin);padding:var(--swal2-actions-padding);border-radius:var(--swal2-actions-border-radius);background:var(--swal2-actions-background)}div:where(.swal2-container) div:where(.swal2-loader){display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:#2778c4 rgba(0,0,0,0) #2778c4 rgba(0,0,0,0)}div:where(.swal2-container) button:where(.swal2-styled){margin:.3125em;padding:.625em 1.1em;transition:var(--swal2-action-button-transition);border:none;box-shadow:0 0 0 3px rgba(0,0,0,0);font-weight:500}div:where(.swal2-container) button:where(.swal2-styled):not([disabled]){cursor:pointer}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm){border-radius:var(--swal2-confirm-button-border-radius);background:initial;background-color:var(--swal2-confirm-button-background-color);box-shadow:var(--swal2-confirm-button-box-shadow);color:var(--swal2-confirm-button-color);font-size:1em}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm):hover{background-color:color-mix(in srgb, var(--swal2-confirm-button-background-color), var(--swal2-action-button-hover))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm):active{background-color:color-mix(in srgb, var(--swal2-confirm-button-background-color), var(--swal2-action-button-active))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny){border-radius:var(--swal2-deny-button-border-radius);background:initial;background-color:var(--swal2-deny-button-background-color);box-shadow:var(--swal2-deny-button-box-shadow);color:var(--swal2-deny-button-color);font-size:1em}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny):hover{background-color:color-mix(in srgb, var(--swal2-deny-button-background-color), var(--swal2-action-button-hover))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny):active{background-color:color-mix(in srgb, var(--swal2-deny-button-background-color), var(--swal2-action-button-active))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel){border-radius:var(--swal2-cancel-button-border-radius);background:initial;background-color:var(--swal2-cancel-button-background-color);box-shadow:var(--swal2-cancel-button-box-shadow);color:var(--swal2-cancel-button-color);font-size:1em}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel):hover{background-color:color-mix(in srgb, var(--swal2-cancel-button-background-color), var(--swal2-action-button-hover))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel):active{background-color:color-mix(in srgb, var(--swal2-cancel-button-background-color), var(--swal2-action-button-active))}div:where(.swal2-container) button:where(.swal2-styled):focus-visible{outline:none;box-shadow:var(--swal2-action-button-focus-box-shadow)}div:where(.swal2-container) button:where(.swal2-styled)[disabled]:not(.swal2-loading){opacity:.4}div:where(.swal2-container) button:where(.swal2-styled)::-moz-focus-inner{border:0}div:where(.swal2-container) div:where(.swal2-footer){margin:1em 0 0;padding:1em 1em 0;border-top:1px solid var(--swal2-footer-border-color);background:var(--swal2-footer-background);color:var(--swal2-footer-color);font-size:1em;text-align:center;cursor:initial}div:where(.swal2-container) .swal2-timer-progress-bar-container{position:absolute;right:0;bottom:0;left:0;grid-column:auto !important;overflow:hidden;border-bottom-right-radius:var(--swal2-border-radius);border-bottom-left-radius:var(--swal2-border-radius)}div:where(.swal2-container) div:where(.swal2-timer-progress-bar){width:100%;height:.25em;background:var(--swal2-timer-progress-bar-background)}div:where(.swal2-container) img:where(.swal2-image){max-width:100%;margin:2em auto 1em;cursor:initial}div:where(.swal2-container) button:where(.swal2-close){position:var(--swal2-close-button-position);inset:var(--swal2-close-button-inset);z-index:2;align-items:center;justify-content:center;width:1.2em;height:1.2em;margin-top:0;margin-right:0;margin-bottom:-1.2em;padding:0;overflow:hidden;transition:var(--swal2-close-button-transition);border:none;border-radius:var(--swal2-border-radius);outline:var(--swal2-close-button-outline);background:rgba(0,0,0,0);color:var(--swal2-close-button-color);font-family:monospace;font-size:var(--swal2-close-button-font-size);cursor:pointer;justify-self:end}div:where(.swal2-container) button:where(.swal2-close):hover{transform:var(--swal2-close-button-hover-transform);background:rgba(0,0,0,0);color:#f27474}div:where(.swal2-container) button:where(.swal2-close):focus-visible{outline:none;box-shadow:var(--swal2-close-button-focus-box-shadow)}div:where(.swal2-container) button:where(.swal2-close)::-moz-focus-inner{border:0}div:where(.swal2-container) div:where(.swal2-html-container){z-index:1;justify-content:center;margin:0;padding:var(--swal2-html-container-padding);overflow:auto;color:inherit;font-size:1.125em;font-weight:normal;line-height:normal;text-align:center;overflow-wrap:break-word;word-break:break-word;cursor:initial}div:where(.swal2-container) input:where(.swal2-input),div:where(.swal2-container) input:where(.swal2-file),div:where(.swal2-container) textarea:where(.swal2-textarea),div:where(.swal2-container) select:where(.swal2-select),div:where(.swal2-container) div:where(.swal2-radio),div:where(.swal2-container) label:where(.swal2-checkbox){margin:1em 2em 3px}div:where(.swal2-container) input:where(.swal2-input),div:where(.swal2-container) input:where(.swal2-file),div:where(.swal2-container) textarea:where(.swal2-textarea){box-sizing:border-box;width:auto;transition:var(--swal2-input-transition);border:var(--swal2-input-border);border-radius:var(--swal2-input-border-radius);background:var(--swal2-input-background);box-shadow:var(--swal2-input-box-shadow);color:inherit;font-size:1.125em}div:where(.swal2-container) input:where(.swal2-input).swal2-inputerror,div:where(.swal2-container) input:where(.swal2-file).swal2-inputerror,div:where(.swal2-container) textarea:where(.swal2-textarea).swal2-inputerror{border-color:#f27474 !important;box-shadow:0 0 2px #f27474 !important}div:where(.swal2-container) input:where(.swal2-input):hover,div:where(.swal2-container) input:where(.swal2-file):hover,div:where(.swal2-container) textarea:where(.swal2-textarea):hover{box-shadow:var(--swal2-input-hover-box-shadow)}div:where(.swal2-container) input:where(.swal2-input):focus,div:where(.swal2-container) input:where(.swal2-file):focus,div:where(.swal2-container) textarea:where(.swal2-textarea):focus{border:var(--swal2-input-focus-border);outline:none;box-shadow:var(--swal2-input-focus-box-shadow)}div:where(.swal2-container) input:where(.swal2-input)::placeholder,div:where(.swal2-container) input:where(.swal2-file)::placeholder,div:where(.swal2-container) textarea:where(.swal2-textarea)::placeholder{color:#ccc}div:where(.swal2-container) .swal2-range{margin:1em 2em 3px;background:var(--swal2-background)}div:where(.swal2-container) .swal2-range input{width:80%}div:where(.swal2-container) .swal2-range output{width:20%;color:inherit;font-weight:600;text-align:center}div:where(.swal2-container) .swal2-range input,div:where(.swal2-container) .swal2-range output{height:2.625em;padding:0;font-size:1.125em;line-height:2.625em}div:where(.swal2-container) .swal2-input{height:2.625em;padding:0 .75em}div:where(.swal2-container) .swal2-file{width:75%;margin-right:auto;margin-left:auto;background:var(--swal2-input-background);font-size:1.125em}div:where(.swal2-container) .swal2-textarea{height:6.75em;padding:.75em}div:where(.swal2-container) .swal2-select{min-width:50%;max-width:100%;padding:.375em .625em;background:var(--swal2-input-background);color:inherit;font-size:1.125em}div:where(.swal2-container) .swal2-radio,div:where(.swal2-container) .swal2-checkbox{align-items:center;justify-content:center;background:var(--swal2-background);color:inherit}div:where(.swal2-container) .swal2-radio label,div:where(.swal2-container) .swal2-checkbox label{margin:0 .6em;font-size:1.125em}div:where(.swal2-container) .swal2-radio input,div:where(.swal2-container) .swal2-checkbox input{flex-shrink:0;margin:0 .4em}div:where(.swal2-container) label:where(.swal2-input-label){display:flex;justify-content:center;margin:1em auto 0}div:where(.swal2-container) div:where(.swal2-validation-message){align-items:center;justify-content:center;margin:1em 0 0;padding:.625em;overflow:hidden;background:var(--swal2-validation-message-background);color:var(--swal2-validation-message-color);font-size:1em;font-weight:300}div:where(.swal2-container) div:where(.swal2-validation-message)::before{content:\"!\";display:inline-block;width:1.5em;min-width:1.5em;height:1.5em;margin:0 .625em;border-radius:50%;background-color:#f27474;color:#fff;font-weight:600;line-height:1.5em;text-align:center}div:where(.swal2-container) .swal2-progress-steps{flex-wrap:wrap;align-items:center;max-width:100%;margin:1.25em auto;padding:0;background:rgba(0,0,0,0);font-weight:600}div:where(.swal2-container) .swal2-progress-steps li{display:inline-block;position:relative}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:#2778c4;color:#fff;line-height:2em;text-align:center}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:#2778c4}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step{background:var(--swal2-progress-step-background);color:#fff}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step-line{background:var(--swal2-progress-step-background)}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:#2778c4}div:where(.swal2-icon){position:relative;box-sizing:content-box;justify-content:center;width:5em;height:5em;margin:2.5em auto .6em;zoom:var(--swal2-icon-zoom);border:.25em solid rgba(0,0,0,0);border-radius:50%;border-color:#000;font-family:inherit;line-height:5em;cursor:default;user-select:none}div:where(.swal2-icon) .swal2-icon-content{display:flex;align-items:center;font-size:3.75em}div:where(.swal2-icon).swal2-error{border-color:#f27474;color:#f27474}div:where(.swal2-icon).swal2-error .swal2-x-mark{position:relative;flex-grow:1}div:where(.swal2-icon).swal2-error [class^=swal2-x-mark-line]{display:block;position:absolute;top:2.3125em;width:2.9375em;height:.3125em;border-radius:.125em;background-color:#f27474}div:where(.swal2-icon).swal2-error [class^=swal2-x-mark-line][class$=left]{left:1.0625em;transform:rotate(45deg)}div:where(.swal2-icon).swal2-error [class^=swal2-x-mark-line][class$=right]{right:1em;transform:rotate(-45deg)}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-error.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-error.swal2-icon-show .swal2-x-mark{animation:swal2-animate-error-x-mark .5s}}div:where(.swal2-icon).swal2-warning{border-color:#f8bb86;color:#f8bb86}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-warning.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-warning.swal2-icon-show .swal2-icon-content{animation:swal2-animate-i-mark .5s}}div:where(.swal2-icon).swal2-info{border-color:#3fc3ee;color:#3fc3ee}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-info.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-info.swal2-icon-show .swal2-icon-content{animation:swal2-animate-i-mark .8s}}div:where(.swal2-icon).swal2-question{border-color:#87adbd;color:#87adbd}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-question.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-question.swal2-icon-show .swal2-icon-content{animation:swal2-animate-question-mark .8s}}div:where(.swal2-icon).swal2-success{border-color:#a5dc86;color:#a5dc86}div:where(.swal2-icon).swal2-success [class^=swal2-success-circular-line]{position:absolute;width:3.75em;height:7.5em;border-radius:50%}div:where(.swal2-icon).swal2-success [class^=swal2-success-circular-line][class$=left]{top:-0.4375em;left:-2.0635em;transform:rotate(-45deg);transform-origin:3.75em 3.75em;border-radius:7.5em 0 0 7.5em}div:where(.swal2-icon).swal2-success [class^=swal2-success-circular-line][class$=right]{top:-0.6875em;left:1.875em;transform:rotate(-45deg);transform-origin:0 3.75em;border-radius:0 7.5em 7.5em 0}div:where(.swal2-icon).swal2-success .swal2-success-ring{position:absolute;z-index:2;top:-0.25em;left:-0.25em;box-sizing:content-box;width:100%;height:100%;border:.25em solid rgba(165,220,134,.3);border-radius:50%}div:where(.swal2-icon).swal2-success .swal2-success-fix{position:absolute;z-index:1;top:.5em;left:1.625em;width:.4375em;height:5.625em;transform:rotate(-45deg)}div:where(.swal2-icon).swal2-success [class^=swal2-success-line]{display:block;position:absolute;z-index:2;height:.3125em;border-radius:.125em;background-color:#a5dc86}div:where(.swal2-icon).swal2-success [class^=swal2-success-line][class$=tip]{top:2.875em;left:.8125em;width:1.5625em;transform:rotate(45deg)}div:where(.swal2-icon).swal2-success [class^=swal2-success-line][class$=long]{top:2.375em;right:.5em;width:2.9375em;transform:rotate(-45deg)}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-success.swal2-icon-show .swal2-success-line-tip{animation:swal2-animate-success-line-tip .75s}div:where(.swal2-icon).swal2-success.swal2-icon-show .swal2-success-line-long{animation:swal2-animate-success-line-long .75s}div:where(.swal2-icon).swal2-success.swal2-icon-show .swal2-success-circular-line-right{animation:swal2-rotate-success-circular-line 4.25s ease-in}}[class^=swal2]{-webkit-tap-highlight-color:rgba(0,0,0,0)}.swal2-show{animation:var(--swal2-show-animation)}.swal2-hide{animation:var(--swal2-hide-animation)}.swal2-noanimation{transition:none}.swal2-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}.swal2-rtl .swal2-close{margin-right:initial;margin-left:0}.swal2-rtl .swal2-timer-progress-bar{right:0;left:auto}.swal2-toast{box-sizing:border-box;grid-column:1/4 !important;grid-row:1/4 !important;grid-template-columns:min-content auto min-content;padding:1em;overflow-y:hidden;border:var(--swal2-toast-border);background:var(--swal2-background);box-shadow:var(--swal2-toast-box-shadow);pointer-events:all}.swal2-toast>*{grid-column:2}.swal2-toast h2:where(.swal2-title){margin:.5em 1em;padding:0;font-size:1em;text-align:initial}.swal2-toast .swal2-loading{justify-content:center}.swal2-toast input:where(.swal2-input){height:2em;margin:.5em;font-size:1em}.swal2-toast .swal2-validation-message{font-size:1em}.swal2-toast div:where(.swal2-footer){margin:.5em 0 0;padding:.5em 0 0;font-size:.8em}.swal2-toast button:where(.swal2-close){grid-column:3/3;grid-row:1/99;align-self:center;width:.8em;height:.8em;margin:0;font-size:2em}.swal2-toast div:where(.swal2-html-container){margin:.5em 1em;padding:0;overflow:initial;font-size:1em;text-align:initial}.swal2-toast div:where(.swal2-html-container):empty{padding:0}.swal2-toast .swal2-loader{grid-column:1;grid-row:1/99;align-self:center;width:2em;height:2em;margin:.25em}.swal2-toast .swal2-icon{grid-column:1;grid-row:1/99;align-self:center;width:2em;min-width:2em;height:2em;margin:0 .5em 0 0}.swal2-toast .swal2-icon .swal2-icon-content{display:flex;align-items:center;font-size:1.8em;font-weight:bold}.swal2-toast .swal2-icon.swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line]{top:.875em;width:1.375em}.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:.3125em}.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:.3125em}.swal2-toast div:where(.swal2-actions){justify-content:flex-start;height:auto;margin:0;margin-top:.5em;padding:0 .5em}.swal2-toast button:where(.swal2-styled){margin:.25em .5em;padding:.4em .6em;font-size:1em}.swal2-toast .swal2-success{border-color:#a5dc86}.swal2-toast .swal2-success [class^=swal2-success-circular-line]{position:absolute;width:1.6em;height:3em;border-radius:50%}.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left]{top:-0.8em;left:-0.5em;transform:rotate(-45deg);transform-origin:2em 2em;border-radius:4em 0 0 4em}.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right]{top:-0.25em;left:.9375em;transform-origin:0 1.5em;border-radius:0 4em 4em 0}.swal2-toast .swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-toast .swal2-success .swal2-success-fix{top:0;left:.4375em;width:.4375em;height:2.6875em}.swal2-toast .swal2-success [class^=swal2-success-line]{height:.3125em}.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip]{top:1.125em;left:.1875em;width:.75em}.swal2-toast .swal2-success [class^=swal2-success-line][class$=long]{top:.9375em;right:.1875em;width:1.375em}@container swal2-popup style(--swal2-icon-animations:true){.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip{animation:swal2-toast-animate-success-line-tip .75s}.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long{animation:swal2-toast-animate-success-line-long .75s}}.swal2-toast.swal2-show{animation:var(--swal2-toast-show-animation)}.swal2-toast.swal2-hide{animation:var(--swal2-toast-hide-animation)}@keyframes swal2-show{0%{transform:translate3d(0, -50px, 0) scale(0.9);opacity:0}100%{transform:translate3d(0, 0, 0) scale(1);opacity:1}}@keyframes swal2-hide{0%{transform:translate3d(0, 0, 0) scale(1);opacity:1}100%{transform:translate3d(0, -50px, 0) scale(0.9);opacity:0}}@keyframes swal2-animate-success-line-tip{0%{top:1.1875em;left:.0625em;width:0}54%{top:1.0625em;left:.125em;width:0}70%{top:2.1875em;left:-0.375em;width:3.125em}84%{top:3em;left:1.3125em;width:1.0625em}100%{top:2.8125em;left:.8125em;width:1.5625em}}@keyframes swal2-animate-success-line-long{0%{top:3.375em;right:2.875em;width:0}65%{top:3.375em;right:2.875em;width:0}84%{top:2.1875em;right:0;width:3.4375em}100%{top:2.375em;right:.5em;width:2.9375em}}@keyframes swal2-rotate-success-circular-line{0%{transform:rotate(-45deg)}5%{transform:rotate(-45deg)}12%{transform:rotate(-405deg)}100%{transform:rotate(-405deg)}}@keyframes swal2-animate-error-x-mark{0%{margin-top:1.625em;transform:scale(0.4);opacity:0}50%{margin-top:1.625em;transform:scale(0.4);opacity:0}80%{margin-top:-0.375em;transform:scale(1.15)}100%{margin-top:0;transform:scale(1);opacity:1}}@keyframes swal2-animate-error-icon{0%{transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0deg);opacity:1}}@keyframes swal2-rotate-loading{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@keyframes swal2-animate-question-mark{0%{transform:rotateY(-360deg)}100%{transform:rotateY(0)}}@keyframes swal2-animate-i-mark{0%{transform:rotateZ(45deg);opacity:0}25%{transform:rotateZ(-25deg);opacity:.4}50%{transform:rotateZ(15deg);opacity:.8}75%{transform:rotateZ(-5deg);opacity:1}100%{transform:rotateX(0);opacity:1}}@keyframes swal2-toast-show{0%{transform:translateY(-0.625em) rotateZ(2deg)}33%{transform:translateY(0) rotateZ(-2deg)}66%{transform:translateY(0.3125em) rotateZ(2deg)}100%{transform:translateY(0) rotateZ(0deg)}}@keyframes swal2-toast-hide{100%{transform:rotateZ(1deg);opacity:0}}@keyframes swal2-toast-animate-success-line-tip{0%{top:.5625em;left:.0625em;width:0}54%{top:.125em;left:.125em;width:0}70%{top:.625em;left:-0.25em;width:1.625em}84%{top:1.0625em;left:.75em;width:.5em}100%{top:1.125em;left:.1875em;width:.75em}}@keyframes swal2-toast-animate-success-line-long{0%{top:1.625em;right:1.375em;width:0}65%{top:1.25em;right:.9375em;width:0}84%{top:.9375em;right:0;width:1.125em}100%{top:.9375em;right:.1875em;width:1.375em}}");
5334
5335 /***/ }
5336
5337 /******/ });
5338 /************************************************************************/
5339 /******/ // The module cache
5340 /******/ var __webpack_module_cache__ = {};
5341 /******/
5342 /******/ // The require function
5343 /******/ function __webpack_require__(moduleId) {
5344 /******/ // Check if module is in cache
5345 /******/ var cachedModule = __webpack_module_cache__[moduleId];
5346 /******/ if (cachedModule !== undefined) {
5347 /******/ return cachedModule.exports;
5348 /******/ }
5349 /******/ // Create a new module (and put it into the cache)
5350 /******/ var module = __webpack_module_cache__[moduleId] = {
5351 /******/ // no module.id needed
5352 /******/ // no module.loaded needed
5353 /******/ exports: {}
5354 /******/ };
5355 /******/
5356 /******/ // Execute the module function
5357 /******/ if (!(moduleId in __webpack_modules__)) {
5358 /******/ delete __webpack_module_cache__[moduleId];
5359 /******/ var e = new Error("Cannot find module '" + moduleId + "'");
5360 /******/ e.code = 'MODULE_NOT_FOUND';
5361 /******/ throw e;
5362 /******/ }
5363 /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
5364 /******/
5365 /******/ // Return the exports of the module
5366 /******/ return module.exports;
5367 /******/ }
5368 /******/
5369 /************************************************************************/
5370 /******/ /* webpack/runtime/compat get default export */
5371 /******/ (() => {
5372 /******/ // getDefaultExport function for compatibility with non-harmony modules
5373 /******/ __webpack_require__.n = (module) => {
5374 /******/ var getter = module && module.__esModule ?
5375 /******/ () => (module['default']) :
5376 /******/ () => (module);
5377 /******/ __webpack_require__.d(getter, { a: getter });
5378 /******/ return getter;
5379 /******/ };
5380 /******/ })();
5381 /******/
5382 /******/ /* webpack/runtime/define property getters */
5383 /******/ (() => {
5384 /******/ // define getter functions for harmony exports
5385 /******/ __webpack_require__.d = (exports, definition) => {
5386 /******/ for(var key in definition) {
5387 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
5388 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
5389 /******/ }
5390 /******/ }
5391 /******/ };
5392 /******/ })();
5393 /******/
5394 /******/ /* webpack/runtime/hasOwnProperty shorthand */
5395 /******/ (() => {
5396 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
5397 /******/ })();
5398 /******/
5399 /******/ /* webpack/runtime/make namespace object */
5400 /******/ (() => {
5401 /******/ // define __esModule on exports
5402 /******/ __webpack_require__.r = (exports) => {
5403 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
5404 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5405 /******/ }
5406 /******/ Object.defineProperty(exports, '__esModule', { value: true });
5407 /******/ };
5408 /******/ })();
5409 /******/
5410 /************************************************************************/
5411 var __webpack_exports__ = {};
5412 // This entry needs to be wrapped in an IIFE because it needs to be in strict mode.
5413 (() => {
5414 "use strict";
5415 /*!************************************************!*\
5416 !*** ./assets/src/js/frontend/ai-assistant.js ***!
5417 \************************************************/
5418 __webpack_require__.r(__webpack_exports__);
5419 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5420 /* harmony export */ AIAssistantWidget: () => (/* binding */ AIAssistantWidget)
5421 /* harmony export */ });
5422 /* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils.js */ "./assets/src/js/utils.js");
5423 /* harmony import */ var sweetalert2__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sweetalert2 */ "./node_modules/sweetalert2/dist/sweetalert2.all.js");
5424 /* harmony import */ var sweetalert2__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sweetalert2__WEBPACK_IMPORTED_MODULE_1__);
5425 /**
5426 * LP AI Assistant - frontend chat widget.
5427 *
5428 * LearnPress runtime implementation:
5429 * - ES6 class module.
5430 * - Delegated events via lpUtils.eventHandlers.
5431 * - Boot via lpUtils.lpOnElementReady.
5432 * - AJAX transport via window.lpAJAXG.fetchAJAX.
5433 *
5434 * @since 4.3.5
5435 * @version 1.0.0
5436 */
5437
5438
5439 class AIAssistantWidget {
5440 constructor() {
5441 this.config = null;
5442 this.root = null;
5443 this.elements = {};
5444 this.storageKey = '';
5445 this.history = [];
5446 this.activeQuizState = null;
5447 this.isRequesting = false;
5448 this.quizHookBound = false;
5449 this.lastQuizReviewSignature = '';
5450 }
5451 static selectors = {
5452 root: '#lp-ai-assistant',
5453 toggleBtn: '.lp-ai-assistant__toggle',
5454 panel: '.lp-ai-assistant__panel',
5455 closeBtn: '.lp-ai-assistant__close-btn',
5456 clearBtn: '.lp-ai-assistant__clear-btn',
5457 msgList: '.lp-ai-assistant__messages',
5458 inputEl: '.lp-ai-assistant__input',
5459 sendBtn: '.lp-ai-assistant__send-btn',
5460 quickBtn: '.lp-ai-assistant__quick-btn',
5461 quickActions: '.lp-ai-assistant__quick-actions',
5462 inputArea: '.lp-ai-assistant__input-area',
5463 smartReviewBtn: '.lp-ai-assistant__smart-review-btn',
5464 quizCard: '.lp-ai-assistant__quiz-card',
5465 quizOptionBtn: '.lp-ai-assistant__quiz-option'
5466 };
5467
5468 /**
5469 * Curriculum item types the assistant supports.
5470 *
5471 * Mirrors AIAssistantController::get_supported_item_types(). The server re-validates,
5472 * so this only avoids pointless requests.
5473 */
5474 static itemTypes = ['lp_lesson', 'lp_quiz'];
5475 init() {
5476 if (!this.validateConfig()) {
5477 return;
5478 }
5479 this.root = document.querySelector(AIAssistantWidget.selectors.root);
5480 if (!this.root) {
5481 return;
5482 }
5483 this.cacheElements();
5484 if (!this.validateDOM()) {
5485 return;
5486 }
5487 this.storageKey = `lp_ai_chat_${this.config.context}_${this.config.itemId}`;
5488 this.applyInitialState();
5489 this.loadHistory();
5490 this.renderHistoryToDOM();
5491 this.bindQuizCompletedHook();
5492 this.events();
5493 }
5494 validateConfig() {
5495 if (typeof window.lpAIAssistant !== 'object' || !window.lpAIAssistant) {
5496 return false;
5497 }
5498 this.config = window.lpAIAssistant;
5499 if (!this.config.enabled) {
5500 return false;
5501 }
5502 const requiredString = ['nonce', 'ajaxUrl'];
5503 for (const key of requiredString) {
5504 if (typeof this.config[key] !== 'string' || !this.config[key]) {
5505 return false;
5506 }
5507 }
5508 const itemId = Number.isInteger(this.config.itemId) ? this.config.itemId : this.config.lessonId;
5509 if (!Number.isInteger(itemId) || itemId <= 0) {
5510 return false;
5511 }
5512 if (!Number.isInteger(this.config.courseId) || this.config.courseId <= 0) {
5513 return false;
5514 }
5515
5516 /**
5517 * itemType is required and must come from the server-localized config. It is
5518 * never guessed from context, lessonId or the numeric ID: a course item is
5519 * identified by (courseId, itemType, itemId), and inferring one leg of that
5520 * tuple on the client would let a lesson request address a quiz record.
5521 */
5522 if (!AIAssistantWidget.itemTypes.includes(this.config.itemType)) {
5523 return false;
5524 }
5525 this.config.itemId = itemId;
5526 this.config.lessonId = itemId; // Backward compatibility for existing AJAX contract.
5527 this.config.context = this.config.context === 'quiz' ? 'quiz' : 'lesson';
5528 this.config.quizCompleted = !!this.config.quizCompleted;
5529 this.config.enabledActions = {
5530 summarize: true,
5531 explain: true,
5532 quick_quiz: true,
5533 smart_review: true,
5534 ...(this.config.enabledActions || {})
5535 };
5536 this.config.i18n = {
5537 you: this.config?.i18n?.you || 'You',
5538 assistant: this.config?.i18n?.assistant || 'AI Assistant',
5539 thinking: this.config?.i18n?.thinking || 'Thinking...',
5540 sendError: this.config?.i18n?.sendError || 'An error occurred. Please try again.',
5541 clearConfirm: this.config?.i18n?.clearConfirm || 'Clear chat history?',
5542 explainPrompt: this.config?.i18n?.explainPrompt || 'Explain a concept from this lesson.',
5543 quizPrompt: this.config?.i18n?.quizPrompt || 'Create a quick quiz from this lesson.',
5544 summarizePrompt: this.config?.i18n?.summarizePrompt || 'Summarize this lesson with key points.',
5545 smartReviewPrompt: this.config?.i18n?.smartReviewPrompt || 'Give me a smart review of my quiz results.',
5546 quizCorrectTitle: this.config?.i18n?.quizCorrectTitle || 'Correct!',
5547 quizWrongTitle: this.config?.i18n?.quizWrongTitle || 'Not correct!'
5548 };
5549 return true;
5550 }
5551 cacheElements() {
5552 this.elements.toggleBtn = document.querySelector(AIAssistantWidget.selectors.toggleBtn);
5553 this.elements.panel = this.root.querySelector(AIAssistantWidget.selectors.panel);
5554 this.elements.closeBtn = this.root.querySelector(AIAssistantWidget.selectors.closeBtn);
5555 this.elements.clearBtn = this.root.querySelector(AIAssistantWidget.selectors.clearBtn);
5556 this.elements.msgList = this.root.querySelector(AIAssistantWidget.selectors.msgList);
5557 this.elements.inputEl = this.root.querySelector(AIAssistantWidget.selectors.inputEl);
5558 this.elements.sendBtn = this.root.querySelector(AIAssistantWidget.selectors.sendBtn);
5559 this.elements.inputArea = this.root.querySelector(AIAssistantWidget.selectors.inputArea);
5560 this.elements.quickActions = this.root.querySelector(AIAssistantWidget.selectors.quickActions);
5561 this.elements.smartReviewBtn = this.root.querySelector(AIAssistantWidget.selectors.smartReviewBtn);
5562 }
5563 validateDOM() {
5564 // inputEl and sendBtn are optional — absent when free chat is disabled.
5565 return !!(this.elements.toggleBtn && this.elements.panel && this.elements.msgList);
5566 }
5567 applyInitialState() {
5568 if (this.elements.smartReviewBtn) {
5569 const showSmartReview = this.config.context === 'quiz' ? this.config.quizCompleted : !!this.config.enabledActions?.smart_review;
5570 this.elements.smartReviewBtn.hidden = !showSmartReview;
5571 }
5572 this.setQuizInputMode(false);
5573 }
5574 bindQuizCompletedHook() {
5575 if (this.config.context === 'quiz') {
5576 return;
5577 }
5578 if (this.quizHookBound || !this.elements.smartReviewBtn) {
5579 return;
5580 }
5581 const hooks = window?.wp?.hooks;
5582 if (!hooks || typeof hooks.addAction !== 'function') {
5583 return;
5584 }
5585 hooks.addAction('lp-js-quiz-answer', 'learnpress/ai-assistant-smart-review', (answered, status) => {
5586 if (String(status || '').toLowerCase() !== 'completed') {
5587 return;
5588 }
5589 this.config.quizCompleted = true;
5590 this.elements.smartReviewBtn.hidden = false;
5591 });
5592 this.quizHookBound = true;
5593 }
5594 events() {
5595 if (AIAssistantWidget._loadedEvents) {
5596 return;
5597 }
5598 AIAssistantWidget._loadedEvents = this;
5599 _utils_js__WEBPACK_IMPORTED_MODULE_0__.eventHandlers('click', [{
5600 selector: AIAssistantWidget.selectors.toggleBtn,
5601 class: this,
5602 callBack: this.handleToggleClick.name
5603 }, {
5604 selector: `${AIAssistantWidget.selectors.root} ${AIAssistantWidget.selectors.closeBtn}`,
5605 class: this,
5606 callBack: this.handleCloseClick.name
5607 }, {
5608 selector: `${AIAssistantWidget.selectors.root} ${AIAssistantWidget.selectors.clearBtn}`,
5609 class: this,
5610 callBack: this.handleClearClick.name
5611 }, {
5612 selector: `${AIAssistantWidget.selectors.root} ${AIAssistantWidget.selectors.sendBtn}`,
5613 class: this,
5614 callBack: this.handleSendClick.name
5615 }, {
5616 selector: `${AIAssistantWidget.selectors.root} ${AIAssistantWidget.selectors.quickBtn}`,
5617 class: this,
5618 callBack: this.handleQuickActionClick.name
5619 }, {
5620 selector: `${AIAssistantWidget.selectors.root} ${AIAssistantWidget.selectors.quizOptionBtn}`,
5621 class: this,
5622 callBack: this.handleQuizOptionClick.name
5623 }]);
5624 _utils_js__WEBPACK_IMPORTED_MODULE_0__.eventHandlers('keydown', [{
5625 selector: `${AIAssistantWidget.selectors.root} ${AIAssistantWidget.selectors.inputEl}`,
5626 class: this,
5627 callBack: this.handleInputKeydown.name
5628 }, {
5629 selector: 'body',
5630 class: this,
5631 callBack: this.handleEscapeKeydown.name
5632 }]);
5633 }
5634 handleToggleClick(args) {
5635 args.e.preventDefault();
5636 if (this.elements.panel.hidden) {
5637 this.openPanel();
5638 } else {
5639 this.closePanel();
5640 }
5641 }
5642 handleCloseClick(args) {
5643 args.e.preventDefault();
5644 this.closePanel();
5645 }
5646 handleClearClick(args) {
5647 args.e.preventDefault();
5648 sweetalert2__WEBPACK_IMPORTED_MODULE_1___default().fire({
5649 title: this.config.i18n.clearConfirm,
5650 icon: 'warning',
5651 showCancelButton: true,
5652 confirmButtonColor: 'var(--lp-primary-color, #ffb606)'
5653 }).then(result => {
5654 if (result.isConfirmed) {
5655 this.clearHistory();
5656 }
5657 });
5658 }
5659 handleSendClick(args) {
5660 var _this$elements$inputE;
5661 args.e.preventDefault();
5662 this.sendMessage((_this$elements$inputE = this.elements.inputEl?.value) !== null && _this$elements$inputE !== void 0 ? _this$elements$inputE : '');
5663 }
5664 handleQuickActionClick(args) {
5665 args.e.preventDefault();
5666 if (this.activeQuizState?.is_active) {
5667 return;
5668 }
5669 const btn = args.target.closest(AIAssistantWidget.selectors.quickBtn);
5670 if (!btn) {
5671 return;
5672 }
5673 const action = btn.dataset.lpAiAction;
5674 const prompts = {
5675 explain: this.config.i18n.explainPrompt,
5676 'quick-quiz': this.config.i18n.quizPrompt,
5677 summarize: this.config.i18n.summarizePrompt,
5678 'smart-review': this.config.i18n.smartReviewPrompt
5679 };
5680 const prompt = prompts[action];
5681 if (!prompt) {
5682 return;
5683 }
5684 this.openPanel();
5685 this.sendMessage(prompt, action);
5686 }
5687 handleQuizOptionClick(args) {
5688 args.e.preventDefault();
5689 if (this.isRequesting || !this.activeQuizState?.is_active) {
5690 return;
5691 }
5692 const btn = args.target.closest(AIAssistantWidget.selectors.quizOptionBtn);
5693 if (!btn) {
5694 return;
5695 }
5696 const answerText = (btn.dataset.option || btn.textContent || '').trim();
5697 if (!answerText) {
5698 return;
5699 }
5700 this.sendMessage(answerText);
5701 }
5702 handleInputKeydown(args) {
5703 if (this.activeQuizState?.is_active) {
5704 return;
5705 }
5706 if (args.e.key === 'Enter' && !args.e.shiftKey) {
5707 var _this$elements$inputE2;
5708 args.e.preventDefault();
5709 this.sendMessage((_this$elements$inputE2 = this.elements.inputEl?.value) !== null && _this$elements$inputE2 !== void 0 ? _this$elements$inputE2 : '');
5710 }
5711 }
5712 handleEscapeKeydown(args) {
5713 if (args.e.key !== 'Escape') {
5714 return;
5715 }
5716 if (this.elements.panel && !this.elements.panel.hidden) {
5717 this.closePanel();
5718 }
5719 }
5720 getAjaxHandle() {
5721 const ajaxHandle = window.lpAJAXG;
5722 if (!ajaxHandle || typeof ajaxHandle.fetchAJAX !== 'function') {
5723 return null;
5724 }
5725 return ajaxHandle;
5726 }
5727 openPanel() {
5728 this.elements.panel.hidden = false;
5729 this.root.setAttribute('aria-hidden', 'false');
5730 this.elements.toggleBtn.setAttribute('aria-expanded', 'true');
5731 this.elements.toggleBtn.classList.add('is-hidden');
5732 this.elements.inputEl?.focus();
5733 if (this.elements.msgList) {
5734 this.elements.msgList.scrollTop = this.elements.msgList.scrollHeight;
5735 }
5736 }
5737 closePanel() {
5738 this.elements.panel.hidden = true;
5739 this.root.setAttribute('aria-hidden', 'true');
5740 this.elements.toggleBtn.setAttribute('aria-expanded', 'false');
5741 this.elements.toggleBtn.classList.remove('is-hidden');
5742 this.elements.toggleBtn.focus();
5743 }
5744 setLoadingState(isLoading) {
5745 this.isRequesting = isLoading;
5746 if (this.elements.sendBtn) {
5747 this.elements.sendBtn.disabled = isLoading;
5748 }
5749 if (this.elements.inputEl) {
5750 this.elements.inputEl.disabled = isLoading;
5751 }
5752 }
5753 setQuizInputMode(isQuizActive) {
5754 if (this.elements.inputArea) {
5755 this.elements.inputArea.classList.toggle('lp-ai-assistant__input-area--hidden', isQuizActive);
5756 }
5757 if (this.elements.quickActions) {
5758 this.elements.quickActions.classList.toggle('lp-ai-assistant__quick-actions--disabled', isQuizActive);
5759 }
5760 }
5761 loadHistory() {
5762 try {
5763 const raw = localStorage.getItem(this.storageKey);
5764 this.history = raw ? JSON.parse(raw) : [];
5765 if (!Array.isArray(this.history)) {
5766 this.history = [];
5767 }
5768 const lastReview = [...this.history].reverse().find(item => item?.type === 'quiz_review' && item?.review);
5769 this.lastQuizReviewSignature = lastReview?.review ? this.getQuizReviewKey(lastReview.review) : '';
5770 } catch (_e) {
5771 this.history = [];
5772 this.lastQuizReviewSignature = '';
5773 }
5774 }
5775 saveHistory() {
5776 try {
5777 localStorage.setItem(this.storageKey, JSON.stringify(this.history));
5778 } catch (_e) {
5779 // Ignore storage errors.
5780 }
5781 }
5782 clearHistory() {
5783 this.history = [];
5784 this.activeQuizState = null;
5785 this.lastQuizReviewSignature = '';
5786 this.elements.msgList.innerHTML = '';
5787 localStorage.removeItem(this.storageKey);
5788 this.setQuizInputMode(false);
5789 }
5790
5791 /**
5792 * Create an element with a class and plain text content.
5793 *
5794 * All assistant content originates from OpenAI output, so it is built through the
5795 * DOM API only. Nothing on this path goes through innerHTML: text assigned via
5796 * textContent can never become markup, and attributes set via the DOM API can never
5797 * break out into a new attribute or event handler.
5798 *
5799 * @param {string} tag Tag name.
5800 * @param {string} className Class attribute.
5801 * @param {string} text Text content.
5802 * @return {HTMLElement} The created element.
5803 */
5804 createEl(tag, className, text = '') {
5805 const el = document.createElement(tag);
5806 if (className) {
5807 el.className = className;
5808 }
5809 if (text !== '') {
5810 el.textContent = String(text);
5811 }
5812 return el;
5813 }
5814 appendMessage(role, text) {
5815 const el = this.createEl('div', `lp-ai-assistant__msg lp-ai-assistant__msg--${role}`);
5816 const label = role === 'user' ? this.config.i18n.you : this.config.i18n.assistant;
5817 el.appendChild(this.createEl('span', 'lp-ai-assistant__msg-label', label));
5818 el.appendChild(this.createEl('p', 'lp-ai-assistant__msg-text', text));
5819 this.elements.msgList.appendChild(el);
5820 this.elements.msgList.scrollTop = this.elements.msgList.scrollHeight;
5821 return el;
5822 }
5823 renderHistoryToDOM() {
5824 this.elements.msgList.innerHTML = '';
5825 this.history.forEach(message => {
5826 if (message?.type === 'quiz_review' && message?.review) {
5827 this.appendQuizReviewCard(message.review);
5828 return;
5829 }
5830 if (!message || !['user', 'assistant'].includes(message.role)) {
5831 return;
5832 }
5833 this.appendMessage(message.role, message.content || '');
5834 });
5835 this.renderQuizState();
5836 }
5837 getQuizReviewFromState(quiz) {
5838 var _quiz$feedback$select, _quiz$feedback$correc;
5839 if (!quiz || !quiz.feedback || typeof quiz.feedback !== 'object') {
5840 return null;
5841 }
5842 const currentIndex = Number.parseInt(quiz.current_index || 0, 10);
5843 const questionIndex = Math.max(0, currentIndex - 1);
5844 const question = quiz.questions?.[questionIndex];
5845 if (!question || !Array.isArray(question.options)) {
5846 return null;
5847 }
5848 const selectedIndex = Number.parseInt((_quiz$feedback$select = quiz.feedback.selected_index) !== null && _quiz$feedback$select !== void 0 ? _quiz$feedback$select : -1, 10);
5849 const correctIndex = Number.parseInt((_quiz$feedback$correc = quiz.feedback.correct_index) !== null && _quiz$feedback$correc !== void 0 ? _quiz$feedback$correc : -1, 10);
5850 return {
5851 question_index: questionIndex,
5852 total: Number.parseInt(quiz.total || question.options.length || 0, 10),
5853 question: question.question || '',
5854 options: question.options,
5855 selected_index: selectedIndex,
5856 correct_index: correctIndex,
5857 is_correct: !!quiz.feedback.is_correct,
5858 explanation: quiz.feedback.explanation || ''
5859 };
5860 }
5861 getQuizReviewKey(review) {
5862 return [review.question_index, review.total, review.selected_index, review.correct_index, review.is_correct ? 1 : 0].join('|');
5863 }
5864 pushQuizReviewToHistory(review) {
5865 const reviewKey = this.getQuizReviewKey(review);
5866 if (reviewKey === this.lastQuizReviewSignature) {
5867 return false;
5868 }
5869 this.lastQuizReviewSignature = reviewKey;
5870 this.history.push({
5871 type: 'quiz_review',
5872 review
5873 });
5874 this.saveHistory();
5875 return true;
5876 }
5877
5878 /**
5879 * Build one quiz option button.
5880 *
5881 * @param {string} option Option text from model output.
5882 * @param {number} index Zero-based option index.
5883 * @param {string[]} extraClasses Additional state classes.
5884 * @return {HTMLButtonElement} The option button.
5885 */
5886 buildQuizOption(option, index, extraClasses = []) {
5887 const classes = ['lp-ai-assistant__quiz-option', ...extraClasses].join(' ');
5888 const letter = String.fromCharCode(65 + index);
5889 const btn = this.createEl('button', classes, `${letter}. ${String(option)}`);
5890 btn.type = 'button';
5891 return btn;
5892 }
5893 buildQuizReviewOptions(review) {
5894 const options = Array.isArray(review.options) ? review.options : [];
5895 return options.map((option, index) => {
5896 const classes = [];
5897 if (index === review.correct_index) {
5898 classes.push('is-correct-answer');
5899 }
5900 if (index === review.selected_index) {
5901 classes.push(review.is_correct ? 'is-selected-correct' : 'is-selected-wrong');
5902 }
5903 const btn = this.buildQuizOption(option, index, classes);
5904 btn.disabled = true;
5905 return btn;
5906 });
5907 }
5908 appendQuizReviewCard(review) {
5909 const card = this.createEl('div', 'lp-ai-assistant__quiz-card lp-ai-assistant__quiz-card--review');
5910 card.dataset.reviewKey = this.getQuizReviewKey(review);
5911 const optionCount = Array.isArray(review.options) ? review.options.length : 0;
5912 const total = review.total || optionCount;
5913 card.appendChild(this.createEl('div', 'lp-ai-assistant__quiz-head', `Question ${review.question_index + 1}/${total}`));
5914 card.appendChild(this.createEl('div', 'lp-ai-assistant__quiz-question', review.question || ''));
5915 const optionsEl = this.createEl('div', 'lp-ai-assistant__quiz-options');
5916 this.buildQuizReviewOptions(review).forEach(btn => optionsEl.appendChild(btn));
5917 card.appendChild(optionsEl);
5918 const feedbackClass = review.is_correct ? 'is-correct' : 'is-wrong';
5919 const feedbackEl = this.createEl('div', `lp-ai-assistant__quiz-feedback ${feedbackClass}`);
5920 feedbackEl.appendChild(this.createEl('strong', '', review.is_correct ? this.config.i18n.quizCorrectTitle : this.config.i18n.quizWrongTitle));
5921 if (review.explanation) {
5922 feedbackEl.appendChild(this.createEl('div', '', review.explanation));
5923 }
5924 card.appendChild(feedbackEl);
5925 this.elements.msgList.appendChild(card);
5926 }
5927 renderQuizState() {
5928 const oldActiveQuizCard = this.elements.msgList.querySelector('.lp-ai-assistant__quiz-card--active');
5929 if (oldActiveQuizCard) {
5930 oldActiveQuizCard.remove();
5931 }
5932 if (!this.activeQuizState || !this.activeQuizState.questions) {
5933 this.setQuizInputMode(false);
5934 return;
5935 }
5936 const quiz = this.activeQuizState;
5937 const review = this.getQuizReviewFromState(quiz);
5938 if (review) {
5939 if (this.pushQuizReviewToHistory(review)) {
5940 this.appendQuizReviewCard(review);
5941 }
5942 }
5943 if (!quiz.is_active) {
5944 this.setQuizInputMode(false);
5945 return;
5946 }
5947 const currentIndex = Number.parseInt(quiz.current_index || 0, 10);
5948 const question = quiz.questions?.[currentIndex];
5949 if (!question) {
5950 this.setQuizInputMode(false);
5951 return;
5952 }
5953 const card = this.createEl('div', 'lp-ai-assistant__quiz-card lp-ai-assistant__quiz-card--active');
5954 const options = Array.isArray(question.options) ? question.options : [];
5955 card.appendChild(this.createEl('div', 'lp-ai-assistant__quiz-head', `Question ${currentIndex + 1}/${quiz.total || options.length}`));
5956 card.appendChild(this.createEl('div', 'lp-ai-assistant__quiz-question', question.question || ''));
5957 const optionsEl = this.createEl('div', 'lp-ai-assistant__quiz-options');
5958 options.forEach((option, index) => {
5959 const btn = this.buildQuizOption(option, index);
5960
5961 // Assigned through dataset, never interpolated into an HTML attribute — model
5962 // output containing a quote cannot open a new attribute or event handler.
5963 btn.dataset.index = String(index);
5964 btn.dataset.option = String(option);
5965 optionsEl.appendChild(btn);
5966 });
5967 card.appendChild(optionsEl);
5968 this.elements.msgList.appendChild(card);
5969 this.setQuizInputMode(true);
5970 }
5971 scrollToMessageStart(messageEl) {
5972 const msgList = this.elements.msgList;
5973 if (!msgList || !messageEl || !msgList.contains(messageEl)) {
5974 return;
5975 }
5976 msgList.scrollTop = Math.max(0, messageEl.offsetTop - 8);
5977 }
5978 sendMessage(message, actionHint = '') {
5979 const text = (message || '').trim();
5980 if (this.isRequesting || !text) {
5981 return;
5982 }
5983 const ajaxHandle = this.getAjaxHandle();
5984 if (!ajaxHandle) {
5985 this.appendMessage('assistant', this.config.i18n.sendError);
5986 return;
5987 }
5988 this.appendMessage('user', text);
5989 const contextHistory = this.history.slice();
5990 this.history.push({
5991 role: 'user',
5992 content: text
5993 });
5994 this.saveHistory();
5995 if (this.elements.inputEl) {
5996 this.elements.inputEl.value = '';
5997 }
5998 const pendingEl = this.appendMessage('assistant', this.config.i18n.thinking);
5999 const pendingTextEl = pendingEl.querySelector('.lp-ai-assistant__msg-text');
6000 this.setLoadingState(true);
6001 const dataSend = {
6002 action: 'openai_assistant_chat',
6003 message: text,
6004 // Composite item identity — all three legs are required server-side.
6005 course_id: this.config.courseId,
6006 item_type: this.config.itemType,
6007 item_id: this.config.itemId,
6008 history: contextHistory,
6009 active_quiz_questions: this.activeQuizState || [],
6010 action_hint: typeof actionHint === 'string' ? actionHint : ''
6011 };
6012 const callBack = {
6013 success: response => {
6014 if (response?.status === 'success' && response?.data) {
6015 pendingTextEl.textContent = response.data.message || '';
6016 if (response?.data?.type === 'quiz') {
6017 this.activeQuizState = response?.data?.quiz || null;
6018 this.renderQuizState();
6019 const isQuizCompleted = !!this.activeQuizState?.completed || this.activeQuizState?.is_active === false;
6020 if (isQuizCompleted && this.elements.msgList?.contains(pendingEl)) {
6021 // Keep completion feedback after the last review card.
6022 this.elements.msgList.appendChild(pendingEl);
6023 }
6024 } else {
6025 this.activeQuizState = null;
6026 this.renderQuizState();
6027 }
6028 this.history.push({
6029 role: 'assistant',
6030 content: response.data.message
6031 });
6032 this.saveHistory();
6033 } else {
6034 this.activeQuizState = null;
6035 this.renderQuizState();
6036 pendingTextEl.textContent = response?.message || this.config.i18n.sendError;
6037 }
6038 },
6039 error: () => {
6040 this.activeQuizState = null;
6041 this.renderQuizState();
6042 pendingTextEl.textContent = this.config.i18n.sendError;
6043 },
6044 completed: () => {
6045 this.setLoadingState(false);
6046 this.scrollToMessageStart(pendingEl);
6047 }
6048 };
6049 ajaxHandle.fetchAJAX(dataSend, callBack);
6050 }
6051 }
6052 const aiAssistantWidget = new AIAssistantWidget();
6053 _utils_js__WEBPACK_IMPORTED_MODULE_0__.lpOnElementReady(AIAssistantWidget.selectors.root, () => {
6054 aiAssistantWidget.init();
6055 });
6056 })();
6057
6058 /******/ })()
6059 ;
6060 //# sourceMappingURL=ai-assistant.js.map