sweetalert2 11.2.0 → 11.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * sweetalert2 v11.2.0
2
+ * sweetalert2 v11.3.1
3
3
  * Released under the MIT License.
4
4
  */
5
5
  (function (global, factory) {
@@ -8,14 +8,6 @@
8
8
  (global = global || self, global.Sweetalert2 = factory());
9
9
  }(this, function () { 'use strict';
10
10
 
11
- const DismissReason = Object.freeze({
12
- cancel: 'cancel',
13
- backdrop: 'backdrop',
14
- close: 'close',
15
- esc: 'esc',
16
- timer: 'timer'
17
- });
18
-
19
11
  const consolePrefix = 'SweetAlert2:';
20
12
  /**
21
13
  * Filter the unique values into a new array
@@ -97,28 +89,161 @@
97
89
  const asPromise = arg => hasToPromiseFn(arg) ? arg.toPromise() : Promise.resolve(arg);
98
90
  const isPromise = arg => arg && Promise.resolve(arg) === arg;
99
91
 
100
- const isJqueryElement = elem => typeof elem === 'object' && elem.jquery;
92
+ const defaultParams = {
93
+ title: '',
94
+ titleText: '',
95
+ text: '',
96
+ html: '',
97
+ footer: '',
98
+ icon: undefined,
99
+ iconColor: undefined,
100
+ iconHtml: undefined,
101
+ template: undefined,
102
+ toast: false,
103
+ showClass: {
104
+ popup: 'swal2-show',
105
+ backdrop: 'swal2-backdrop-show',
106
+ icon: 'swal2-icon-show'
107
+ },
108
+ hideClass: {
109
+ popup: 'swal2-hide',
110
+ backdrop: 'swal2-backdrop-hide',
111
+ icon: 'swal2-icon-hide'
112
+ },
113
+ customClass: {},
114
+ target: 'body',
115
+ color: undefined,
116
+ backdrop: true,
117
+ heightAuto: true,
118
+ allowOutsideClick: true,
119
+ allowEscapeKey: true,
120
+ allowEnterKey: true,
121
+ stopKeydownPropagation: true,
122
+ keydownListenerCapture: false,
123
+ showConfirmButton: true,
124
+ showDenyButton: false,
125
+ showCancelButton: false,
126
+ preConfirm: undefined,
127
+ preDeny: undefined,
128
+ confirmButtonText: 'OK',
129
+ confirmButtonAriaLabel: '',
130
+ confirmButtonColor: undefined,
131
+ denyButtonText: 'No',
132
+ denyButtonAriaLabel: '',
133
+ denyButtonColor: undefined,
134
+ cancelButtonText: 'Cancel',
135
+ cancelButtonAriaLabel: '',
136
+ cancelButtonColor: undefined,
137
+ buttonsStyling: true,
138
+ reverseButtons: false,
139
+ focusConfirm: true,
140
+ focusDeny: false,
141
+ focusCancel: false,
142
+ returnFocus: true,
143
+ showCloseButton: false,
144
+ closeButtonHtml: '×',
145
+ closeButtonAriaLabel: 'Close this dialog',
146
+ loaderHtml: '',
147
+ showLoaderOnConfirm: false,
148
+ showLoaderOnDeny: false,
149
+ imageUrl: undefined,
150
+ imageWidth: undefined,
151
+ imageHeight: undefined,
152
+ imageAlt: '',
153
+ timer: undefined,
154
+ timerProgressBar: false,
155
+ width: undefined,
156
+ padding: undefined,
157
+ background: undefined,
158
+ input: undefined,
159
+ inputPlaceholder: '',
160
+ inputLabel: '',
161
+ inputValue: '',
162
+ inputOptions: {},
163
+ inputAutoTrim: true,
164
+ inputAttributes: {},
165
+ inputValidator: undefined,
166
+ returnInputValueOnDeny: false,
167
+ validationMessage: undefined,
168
+ grow: false,
169
+ position: 'center',
170
+ progressSteps: [],
171
+ currentProgressStep: undefined,
172
+ progressStepsDistance: undefined,
173
+ willOpen: undefined,
174
+ didOpen: undefined,
175
+ didRender: undefined,
176
+ willClose: undefined,
177
+ didClose: undefined,
178
+ didDestroy: undefined,
179
+ scrollbarPadding: true
180
+ };
181
+ const updatableParams = ['allowEscapeKey', 'allowOutsideClick', 'background', 'buttonsStyling', 'cancelButtonAriaLabel', 'cancelButtonColor', 'cancelButtonText', 'closeButtonAriaLabel', 'closeButtonHtml', 'color', 'confirmButtonAriaLabel', 'confirmButtonColor', 'confirmButtonText', 'currentProgressStep', 'customClass', 'denyButtonAriaLabel', 'denyButtonColor', 'denyButtonText', 'didClose', 'didDestroy', 'footer', 'hideClass', 'html', 'icon', 'iconColor', 'iconHtml', 'imageAlt', 'imageHeight', 'imageUrl', 'imageWidth', 'preConfirm', 'preDeny', 'progressSteps', 'returnFocus', 'reverseButtons', 'showCancelButton', 'showCloseButton', 'showConfirmButton', 'showDenyButton', 'text', 'title', 'titleText', 'willClose'];
182
+ const deprecatedParams = {};
183
+ const toastIncompatibleParams = ['allowOutsideClick', 'allowEnterKey', 'backdrop', 'focusConfirm', 'focusDeny', 'focusCancel', 'returnFocus', 'heightAuto', 'keydownListenerCapture'];
184
+ /**
185
+ * Is valid parameter
186
+ * @param {String} paramName
187
+ */
101
188
 
102
- const isElement = elem => elem instanceof Element || isJqueryElement(elem);
189
+ const isValidParameter = paramName => {
190
+ return Object.prototype.hasOwnProperty.call(defaultParams, paramName);
191
+ };
192
+ /**
193
+ * Is valid parameter for Swal.update() method
194
+ * @param {String} paramName
195
+ */
103
196
 
104
- const argsToParams = args => {
105
- const params = {};
197
+ const isUpdatableParameter = paramName => {
198
+ return updatableParams.indexOf(paramName) !== -1;
199
+ };
200
+ /**
201
+ * Is deprecated parameter
202
+ * @param {String} paramName
203
+ */
106
204
 
107
- if (typeof args[0] === 'object' && !isElement(args[0])) {
108
- Object.assign(params, args[0]);
109
- } else {
110
- ['title', 'html', 'icon'].forEach((name, index) => {
111
- const arg = args[index];
205
+ const isDeprecatedParameter = paramName => {
206
+ return deprecatedParams[paramName];
207
+ };
112
208
 
113
- if (typeof arg === 'string' || isElement(arg)) {
114
- params[name] = arg;
115
- } else if (arg !== undefined) {
116
- error("Unexpected type of ".concat(name, "! Expected \"string\" or \"Element\", got ").concat(typeof arg));
117
- }
118
- });
209
+ const checkIfParamIsValid = param => {
210
+ if (!isValidParameter(param)) {
211
+ warn("Unknown parameter \"".concat(param, "\""));
119
212
  }
213
+ };
120
214
 
121
- return params;
215
+ const checkIfToastParamIsValid = param => {
216
+ if (toastIncompatibleParams.includes(param)) {
217
+ warn("The parameter \"".concat(param, "\" is incompatible with toasts"));
218
+ }
219
+ };
220
+
221
+ const checkIfParamIsDeprecated = param => {
222
+ if (isDeprecatedParameter(param)) {
223
+ warnAboutDeprecation(param, isDeprecatedParameter(param));
224
+ }
225
+ };
226
+ /**
227
+ * Show relevant warnings for given params
228
+ *
229
+ * @param params
230
+ */
231
+
232
+
233
+ const showWarningsForParams = params => {
234
+ if (!params.backdrop && params.allowOutsideClick) {
235
+ warn('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`');
236
+ }
237
+
238
+ for (const param in params) {
239
+ checkIfParamIsValid(param);
240
+
241
+ if (params.toast) {
242
+ checkIfToastParamIsValid(param);
243
+ }
244
+
245
+ checkIfParamIsDeprecated(param);
246
+ }
122
247
  };
123
248
 
124
249
  const swalPrefix = 'swal2-';
@@ -180,10 +305,10 @@
180
305
  return uniqueArray(focusableElementsWithTabindex.concat(otherFocusableElements)).filter(el => isVisible(el));
181
306
  };
182
307
  const isModal = () => {
183
- return !isToast() && !document.body.classList.contains(swalClasses['no-backdrop']);
308
+ return !hasClass(document.body, swalClasses['toast-shown']) && !hasClass(document.body, swalClasses['no-backdrop']);
184
309
  };
185
310
  const isToast = () => {
186
- return document.body.classList.contains(swalClasses['toast-shown']);
311
+ return getPopup() && hasClass(getPopup(), swalClasses.toast);
187
312
  };
188
313
  const isLoading = () => {
189
314
  return getPopup().hasAttribute('data-loading');
@@ -1087,7 +1212,12 @@
1087
1212
  } // Padding
1088
1213
 
1089
1214
 
1090
- applyNumericalStyle(popup, 'padding', params.padding); // Background
1215
+ applyNumericalStyle(popup, 'padding', params.padding); // Color
1216
+
1217
+ if (params.color) {
1218
+ popup.style.color = params.color;
1219
+ } // Background
1220
+
1091
1221
 
1092
1222
  if (params.background) {
1093
1223
  popup.style.background = params.background;
@@ -1139,1889 +1269,1614 @@
1139
1269
  }
1140
1270
  };
1141
1271
 
1142
- /*
1143
- * Global function to determine if SweetAlert2 popup is shown
1144
- */
1272
+ const DismissReason = Object.freeze({
1273
+ cancel: 'cancel',
1274
+ backdrop: 'backdrop',
1275
+ close: 'close',
1276
+ esc: 'esc',
1277
+ timer: 'timer'
1278
+ });
1145
1279
 
1146
- const isVisible$1 = () => {
1147
- return isVisible(getPopup());
1148
- };
1149
- /*
1150
- * Global function to click 'Confirm' button
1151
- */
1280
+ // Adding aria-hidden="true" to elements outside of the active modal dialog ensures that
1281
+ // elements not within the active modal dialog will not be surfaced if a user opens a screen
1282
+ // reader’s list of elements (headings, form controls, landmarks, etc.) in the document.
1152
1283
 
1153
- const clickConfirm = () => getConfirmButton() && getConfirmButton().click();
1154
- /*
1155
- * Global function to click 'Deny' button
1156
- */
1284
+ const setAriaHidden = () => {
1285
+ const bodyChildren = toArray(document.body.children);
1286
+ bodyChildren.forEach(el => {
1287
+ if (el === getContainer() || el.contains(getContainer())) {
1288
+ return;
1289
+ }
1157
1290
 
1158
- const clickDeny = () => getDenyButton() && getDenyButton().click();
1159
- /*
1160
- * Global function to click 'Cancel' button
1161
- */
1291
+ if (el.hasAttribute('aria-hidden')) {
1292
+ el.setAttribute('data-previous-aria-hidden', el.getAttribute('aria-hidden'));
1293
+ }
1162
1294
 
1163
- const clickCancel = () => getCancelButton() && getCancelButton().click();
1295
+ el.setAttribute('aria-hidden', 'true');
1296
+ });
1297
+ };
1298
+ const unsetAriaHidden = () => {
1299
+ const bodyChildren = toArray(document.body.children);
1300
+ bodyChildren.forEach(el => {
1301
+ if (el.hasAttribute('data-previous-aria-hidden')) {
1302
+ el.setAttribute('aria-hidden', el.getAttribute('data-previous-aria-hidden'));
1303
+ el.removeAttribute('data-previous-aria-hidden');
1304
+ } else {
1305
+ el.removeAttribute('aria-hidden');
1306
+ }
1307
+ });
1308
+ };
1164
1309
 
1165
- function fire() {
1166
- const Swal = this;
1310
+ const swalStringParams = ['swal-title', 'swal-html', 'swal-footer'];
1311
+ const getTemplateParams = params => {
1312
+ const template = typeof params.template === 'string' ? document.querySelector(params.template) : params.template;
1167
1313
 
1168
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1169
- args[_key] = arguments[_key];
1314
+ if (!template) {
1315
+ return {};
1170
1316
  }
1171
1317
 
1172
- return new Swal(...args);
1173
- }
1318
+ const templateContent = template.content;
1319
+ showWarningsForElements(templateContent);
1320
+ const result = Object.assign(getSwalParams(templateContent), getSwalButtons(templateContent), getSwalImage(templateContent), getSwalIcon(templateContent), getSwalInput(templateContent), getSwalStringParams(templateContent, swalStringParams));
1321
+ return result;
1322
+ };
1174
1323
 
1175
- /**
1176
- * Returns an extended version of `Swal` containing `params` as defaults.
1177
- * Useful for reusing Swal configuration.
1178
- *
1179
- * For example:
1180
- *
1181
- * Before:
1182
- * const textPromptOptions = { input: 'text', showCancelButton: true }
1183
- * const {value: firstName} = await Swal.fire({ ...textPromptOptions, title: 'What is your first name?' })
1184
- * const {value: lastName} = await Swal.fire({ ...textPromptOptions, title: 'What is your last name?' })
1185
- *
1186
- * After:
1187
- * const TextPrompt = Swal.mixin({ input: 'text', showCancelButton: true })
1188
- * const {value: firstName} = await TextPrompt('What is your first name?')
1189
- * const {value: lastName} = await TextPrompt('What is your last name?')
1190
- *
1191
- * @param mixinParams
1192
- */
1193
- function mixin(mixinParams) {
1194
- class MixinSwal extends this {
1195
- _main(params, priorityMixinParams) {
1196
- return super._main(params, Object.assign({}, mixinParams, priorityMixinParams));
1197
- }
1324
+ const getSwalParams = templateContent => {
1325
+ const result = {};
1326
+ toArray(templateContent.querySelectorAll('swal-param')).forEach(param => {
1327
+ showWarningsForAttributes(param, ['name', 'value']);
1328
+ const paramName = param.getAttribute('name');
1329
+ let value = param.getAttribute('value');
1198
1330
 
1199
- }
1331
+ if (typeof defaultParams[paramName] === 'boolean' && value === 'false') {
1332
+ value = false;
1333
+ }
1200
1334
 
1201
- return MixinSwal;
1202
- }
1335
+ if (typeof defaultParams[paramName] === 'object') {
1336
+ value = JSON.parse(value);
1337
+ }
1203
1338
 
1204
- /**
1205
- * Shows loader (spinner), this is useful with AJAX requests.
1206
- * By default the loader be shown instead of the "Confirm" button.
1207
- */
1339
+ result[paramName] = value;
1340
+ });
1341
+ return result;
1342
+ };
1208
1343
 
1209
- const showLoading = buttonToReplace => {
1210
- let popup = getPopup();
1344
+ const getSwalButtons = templateContent => {
1345
+ const result = {};
1346
+ toArray(templateContent.querySelectorAll('swal-button')).forEach(button => {
1347
+ showWarningsForAttributes(button, ['type', 'color', 'aria-label']);
1348
+ const type = button.getAttribute('type');
1349
+ result["".concat(type, "ButtonText")] = button.innerHTML;
1350
+ result["show".concat(capitalizeFirstLetter(type), "Button")] = true;
1211
1351
 
1212
- if (!popup) {
1213
- Swal.fire();
1214
- }
1352
+ if (button.hasAttribute('color')) {
1353
+ result["".concat(type, "ButtonColor")] = button.getAttribute('color');
1354
+ }
1215
1355
 
1216
- popup = getPopup();
1217
- const loader = getLoader();
1356
+ if (button.hasAttribute('aria-label')) {
1357
+ result["".concat(type, "ButtonAriaLabel")] = button.getAttribute('aria-label');
1358
+ }
1359
+ });
1360
+ return result;
1361
+ };
1218
1362
 
1219
- if (isToast()) {
1220
- hide(getIcon());
1221
- } else {
1222
- replaceButton(popup, buttonToReplace);
1223
- }
1363
+ const getSwalImage = templateContent => {
1364
+ const result = {};
1365
+ const image = templateContent.querySelector('swal-image');
1224
1366
 
1225
- show(loader);
1226
- popup.setAttribute('data-loading', true);
1227
- popup.setAttribute('aria-busy', true);
1228
- popup.focus();
1229
- };
1367
+ if (image) {
1368
+ showWarningsForAttributes(image, ['src', 'width', 'height', 'alt']);
1230
1369
 
1231
- const replaceButton = (popup, buttonToReplace) => {
1232
- const actions = getActions();
1233
- const loader = getLoader();
1370
+ if (image.hasAttribute('src')) {
1371
+ result.imageUrl = image.getAttribute('src');
1372
+ }
1234
1373
 
1235
- if (!buttonToReplace && isVisible(getConfirmButton())) {
1236
- buttonToReplace = getConfirmButton();
1237
- }
1374
+ if (image.hasAttribute('width')) {
1375
+ result.imageWidth = image.getAttribute('width');
1376
+ }
1238
1377
 
1239
- show(actions);
1378
+ if (image.hasAttribute('height')) {
1379
+ result.imageHeight = image.getAttribute('height');
1380
+ }
1240
1381
 
1241
- if (buttonToReplace) {
1242
- hide(buttonToReplace);
1243
- loader.setAttribute('data-button-to-replace', buttonToReplace.className);
1382
+ if (image.hasAttribute('alt')) {
1383
+ result.imageAlt = image.getAttribute('alt');
1384
+ }
1244
1385
  }
1245
1386
 
1246
- loader.parentNode.insertBefore(loader, buttonToReplace);
1247
- addClass([popup, actions], swalClasses.loading);
1387
+ return result;
1248
1388
  };
1249
1389
 
1250
- const RESTORE_FOCUS_TIMEOUT = 100;
1251
-
1252
- const globalState = {};
1390
+ const getSwalIcon = templateContent => {
1391
+ const result = {};
1392
+ const icon = templateContent.querySelector('swal-icon');
1253
1393
 
1254
- const focusPreviousActiveElement = () => {
1255
- if (globalState.previousActiveElement && globalState.previousActiveElement.focus) {
1256
- globalState.previousActiveElement.focus();
1257
- globalState.previousActiveElement = null;
1258
- } else if (document.body) {
1259
- document.body.focus();
1260
- }
1261
- }; // Restore previous active (focused) element
1394
+ if (icon) {
1395
+ showWarningsForAttributes(icon, ['type', 'color']);
1262
1396
 
1397
+ if (icon.hasAttribute('type')) {
1398
+ result.icon = icon.getAttribute('type');
1399
+ }
1263
1400
 
1264
- const restoreActiveElement = returnFocus => {
1265
- return new Promise(resolve => {
1266
- if (!returnFocus) {
1267
- return resolve();
1401
+ if (icon.hasAttribute('color')) {
1402
+ result.iconColor = icon.getAttribute('color');
1268
1403
  }
1269
1404
 
1270
- const x = window.scrollX;
1271
- const y = window.scrollY;
1272
- globalState.restoreFocusTimeout = setTimeout(() => {
1273
- focusPreviousActiveElement();
1274
- resolve();
1275
- }, RESTORE_FOCUS_TIMEOUT); // issues/900
1405
+ result.iconHtml = icon.innerHTML;
1406
+ }
1276
1407
 
1277
- window.scrollTo(x, y);
1278
- });
1408
+ return result;
1279
1409
  };
1280
1410
 
1281
- /**
1282
- * If `timer` parameter is set, returns number of milliseconds of timer remained.
1283
- * Otherwise, returns undefined.
1284
- */
1411
+ const getSwalInput = templateContent => {
1412
+ const result = {};
1413
+ const input = templateContent.querySelector('swal-input');
1285
1414
 
1286
- const getTimerLeft = () => {
1287
- return globalState.timeout && globalState.timeout.getTimerLeft();
1288
- };
1289
- /**
1290
- * Stop timer. Returns number of milliseconds of timer remained.
1291
- * If `timer` parameter isn't set, returns undefined.
1292
- */
1415
+ if (input) {
1416
+ showWarningsForAttributes(input, ['type', 'label', 'placeholder', 'value']);
1417
+ result.input = input.getAttribute('type') || 'text';
1293
1418
 
1294
- const stopTimer = () => {
1295
- if (globalState.timeout) {
1296
- stopTimerProgressBar();
1297
- return globalState.timeout.stop();
1298
- }
1299
- };
1300
- /**
1301
- * Resume timer. Returns number of milliseconds of timer remained.
1302
- * If `timer` parameter isn't set, returns undefined.
1303
- */
1419
+ if (input.hasAttribute('label')) {
1420
+ result.inputLabel = input.getAttribute('label');
1421
+ }
1304
1422
 
1305
- const resumeTimer = () => {
1306
- if (globalState.timeout) {
1307
- const remaining = globalState.timeout.start();
1308
- animateTimerProgressBar(remaining);
1309
- return remaining;
1423
+ if (input.hasAttribute('placeholder')) {
1424
+ result.inputPlaceholder = input.getAttribute('placeholder');
1425
+ }
1426
+
1427
+ if (input.hasAttribute('value')) {
1428
+ result.inputValue = input.getAttribute('value');
1429
+ }
1310
1430
  }
1311
- };
1312
- /**
1313
- * Resume timer. Returns number of milliseconds of timer remained.
1314
- * If `timer` parameter isn't set, returns undefined.
1315
- */
1316
1431
 
1317
- const toggleTimer = () => {
1318
- const timer = globalState.timeout;
1319
- return timer && (timer.running ? stopTimer() : resumeTimer());
1320
- };
1321
- /**
1322
- * Increase timer. Returns number of milliseconds of an updated timer.
1323
- * If `timer` parameter isn't set, returns undefined.
1324
- */
1432
+ const inputOptions = templateContent.querySelectorAll('swal-input-option');
1325
1433
 
1326
- const increaseTimer = n => {
1327
- if (globalState.timeout) {
1328
- const remaining = globalState.timeout.increase(n);
1329
- animateTimerProgressBar(remaining, true);
1330
- return remaining;
1434
+ if (inputOptions.length) {
1435
+ result.inputOptions = {};
1436
+ toArray(inputOptions).forEach(option => {
1437
+ showWarningsForAttributes(option, ['value']);
1438
+ const optionValue = option.getAttribute('value');
1439
+ const optionName = option.innerHTML;
1440
+ result.inputOptions[optionValue] = optionName;
1441
+ });
1331
1442
  }
1332
- };
1333
- /**
1334
- * Check if timer is running. Returns true if timer is running
1335
- * or false if timer is paused or stopped.
1336
- * If `timer` parameter isn't set, returns undefined
1337
- */
1338
1443
 
1339
- const isTimerRunning = () => {
1340
- return globalState.timeout && globalState.timeout.isRunning();
1444
+ return result;
1341
1445
  };
1342
1446
 
1343
- let bodyClickListenerAdded = false;
1344
- const clickHandlers = {};
1345
- function bindClickHandler() {
1346
- let attr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'data-swal-template';
1347
- clickHandlers[attr] = this;
1447
+ const getSwalStringParams = (templateContent, paramNames) => {
1448
+ const result = {};
1348
1449
 
1349
- if (!bodyClickListenerAdded) {
1350
- document.body.addEventListener('click', bodyClickListener);
1351
- bodyClickListenerAdded = true;
1450
+ for (const i in paramNames) {
1451
+ const paramName = paramNames[i];
1452
+ const tag = templateContent.querySelector(paramName);
1453
+
1454
+ if (tag) {
1455
+ showWarningsForAttributes(tag, []);
1456
+ result[paramName.replace(/^swal-/, '')] = tag.innerHTML.trim();
1457
+ }
1352
1458
  }
1353
- }
1354
1459
 
1355
- const bodyClickListener = event => {
1356
- for (let el = event.target; el && el !== document; el = el.parentNode) {
1357
- for (const attr in clickHandlers) {
1358
- const template = el.getAttribute(attr);
1460
+ return result;
1461
+ };
1359
1462
 
1360
- if (template) {
1361
- clickHandlers[attr].fire({
1362
- template
1363
- });
1364
- return;
1365
- }
1463
+ const showWarningsForElements = template => {
1464
+ const allowedElements = swalStringParams.concat(['swal-param', 'swal-button', 'swal-image', 'swal-icon', 'swal-input', 'swal-input-option']);
1465
+ toArray(template.children).forEach(el => {
1466
+ const tagName = el.tagName.toLowerCase();
1467
+
1468
+ if (allowedElements.indexOf(tagName) === -1) {
1469
+ warn("Unrecognized element <".concat(tagName, ">"));
1366
1470
  }
1367
- }
1471
+ });
1368
1472
  };
1369
1473
 
1370
- const defaultParams = {
1371
- title: '',
1372
- titleText: '',
1373
- text: '',
1374
- html: '',
1375
- footer: '',
1376
- icon: undefined,
1377
- iconColor: undefined,
1378
- iconHtml: undefined,
1379
- template: undefined,
1380
- toast: false,
1381
- showClass: {
1382
- popup: 'swal2-show',
1383
- backdrop: 'swal2-backdrop-show',
1384
- icon: 'swal2-icon-show'
1385
- },
1386
- hideClass: {
1387
- popup: 'swal2-hide',
1388
- backdrop: 'swal2-backdrop-hide',
1389
- icon: 'swal2-icon-hide'
1390
- },
1391
- customClass: {},
1392
- target: 'body',
1393
- backdrop: true,
1394
- heightAuto: true,
1395
- allowOutsideClick: true,
1396
- allowEscapeKey: true,
1397
- allowEnterKey: true,
1398
- stopKeydownPropagation: true,
1399
- keydownListenerCapture: false,
1400
- showConfirmButton: true,
1401
- showDenyButton: false,
1402
- showCancelButton: false,
1403
- preConfirm: undefined,
1404
- preDeny: undefined,
1405
- confirmButtonText: 'OK',
1406
- confirmButtonAriaLabel: '',
1407
- confirmButtonColor: undefined,
1408
- denyButtonText: 'No',
1409
- denyButtonAriaLabel: '',
1410
- denyButtonColor: undefined,
1411
- cancelButtonText: 'Cancel',
1412
- cancelButtonAriaLabel: '',
1413
- cancelButtonColor: undefined,
1414
- buttonsStyling: true,
1415
- reverseButtons: false,
1416
- focusConfirm: true,
1417
- focusDeny: false,
1418
- focusCancel: false,
1419
- returnFocus: true,
1420
- showCloseButton: false,
1421
- closeButtonHtml: '&times;',
1422
- closeButtonAriaLabel: 'Close this dialog',
1423
- loaderHtml: '',
1424
- showLoaderOnConfirm: false,
1425
- showLoaderOnDeny: false,
1426
- imageUrl: undefined,
1427
- imageWidth: undefined,
1428
- imageHeight: undefined,
1429
- imageAlt: '',
1430
- timer: undefined,
1431
- timerProgressBar: false,
1432
- width: undefined,
1433
- padding: undefined,
1434
- background: undefined,
1435
- input: undefined,
1436
- inputPlaceholder: '',
1437
- inputLabel: '',
1438
- inputValue: '',
1439
- inputOptions: {},
1440
- inputAutoTrim: true,
1441
- inputAttributes: {},
1442
- inputValidator: undefined,
1443
- returnInputValueOnDeny: false,
1444
- validationMessage: undefined,
1445
- grow: false,
1446
- position: 'center',
1447
- progressSteps: [],
1448
- currentProgressStep: undefined,
1449
- progressStepsDistance: undefined,
1450
- willOpen: undefined,
1451
- didOpen: undefined,
1452
- didRender: undefined,
1453
- willClose: undefined,
1454
- didClose: undefined,
1455
- didDestroy: undefined,
1456
- scrollbarPadding: true
1457
- };
1458
- const updatableParams = ['allowEscapeKey', 'allowOutsideClick', 'background', 'buttonsStyling', 'cancelButtonAriaLabel', 'cancelButtonColor', 'cancelButtonText', 'closeButtonAriaLabel', 'closeButtonHtml', 'confirmButtonAriaLabel', 'confirmButtonColor', 'confirmButtonText', 'currentProgressStep', 'customClass', 'denyButtonAriaLabel', 'denyButtonColor', 'denyButtonText', 'didClose', 'didDestroy', 'footer', 'hideClass', 'html', 'icon', 'iconColor', 'iconHtml', 'imageAlt', 'imageHeight', 'imageUrl', 'imageWidth', 'preConfirm', 'preDeny', 'progressSteps', 'returnFocus', 'reverseButtons', 'showCancelButton', 'showCloseButton', 'showConfirmButton', 'showDenyButton', 'text', 'title', 'titleText', 'willClose'];
1459
- const deprecatedParams = {};
1460
- const toastIncompatibleParams = ['allowOutsideClick', 'allowEnterKey', 'backdrop', 'focusConfirm', 'focusDeny', 'focusCancel', 'returnFocus', 'heightAuto', 'keydownListenerCapture'];
1461
- /**
1462
- * Is valid parameter
1463
- * @param {String} paramName
1464
- */
1465
-
1466
- const isValidParameter = paramName => {
1467
- return Object.prototype.hasOwnProperty.call(defaultParams, paramName);
1468
- };
1469
- /**
1470
- * Is valid parameter for Swal.update() method
1471
- * @param {String} paramName
1472
- */
1473
-
1474
- const isUpdatableParameter = paramName => {
1475
- return updatableParams.indexOf(paramName) !== -1;
1476
- };
1477
- /**
1478
- * Is deprecated parameter
1479
- * @param {String} paramName
1480
- */
1481
-
1482
- const isDeprecatedParameter = paramName => {
1483
- return deprecatedParams[paramName];
1474
+ const showWarningsForAttributes = (el, allowedAttributes) => {
1475
+ toArray(el.attributes).forEach(attribute => {
1476
+ if (allowedAttributes.indexOf(attribute.name) === -1) {
1477
+ warn(["Unrecognized attribute \"".concat(attribute.name, "\" on <").concat(el.tagName.toLowerCase(), ">."), "".concat(allowedAttributes.length ? "Allowed attributes are: ".concat(allowedAttributes.join(', ')) : 'To set the value, use HTML within the element.')]);
1478
+ }
1479
+ });
1484
1480
  };
1485
1481
 
1486
- const checkIfParamIsValid = param => {
1487
- if (!isValidParameter(param)) {
1488
- warn("Unknown parameter \"".concat(param, "\""));
1482
+ var defaultInputValidators = {
1483
+ email: (string, validationMessage) => {
1484
+ return /^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,24}$/.test(string) ? Promise.resolve() : Promise.resolve(validationMessage || 'Invalid email address');
1485
+ },
1486
+ url: (string, validationMessage) => {
1487
+ // taken from https://stackoverflow.com/a/3809435 with a small change from #1306 and #2013
1488
+ 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');
1489
1489
  }
1490
1490
  };
1491
1491
 
1492
- const checkIfToastParamIsValid = param => {
1493
- if (toastIncompatibleParams.includes(param)) {
1494
- warn("The parameter \"".concat(param, "\" is incompatible with toasts"));
1492
+ function setDefaultInputValidators(params) {
1493
+ // Use default `inputValidator` for supported input types if not provided
1494
+ if (!params.inputValidator) {
1495
+ Object.keys(defaultInputValidators).forEach(key => {
1496
+ if (params.input === key) {
1497
+ params.inputValidator = defaultInputValidators[key];
1498
+ }
1499
+ });
1495
1500
  }
1496
- };
1501
+ }
1497
1502
 
1498
- const checkIfParamIsDeprecated = param => {
1499
- if (isDeprecatedParameter(param)) {
1500
- warnAboutDeprecation(param, isDeprecatedParameter(param));
1503
+ function validateCustomTargetElement(params) {
1504
+ // Determine if the custom target element is valid
1505
+ if (!params.target || typeof params.target === 'string' && !document.querySelector(params.target) || typeof params.target !== 'string' && !params.target.appendChild) {
1506
+ warn('Target parameter is not valid, defaulting to "body"');
1507
+ params.target = 'body';
1501
1508
  }
1502
- };
1509
+ }
1503
1510
  /**
1504
- * Show relevant warnings for given params
1511
+ * Set type, text and actions on popup
1505
1512
  *
1506
1513
  * @param params
1514
+ * @returns {boolean}
1507
1515
  */
1508
1516
 
1509
1517
 
1510
- const showWarningsForParams = params => {
1511
- if (!params.backdrop && params.allowOutsideClick) {
1512
- warn('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`');
1513
- }
1514
-
1515
- for (const param in params) {
1516
- checkIfParamIsValid(param);
1517
-
1518
- if (params.toast) {
1519
- checkIfToastParamIsValid(param);
1520
- }
1521
-
1522
- checkIfParamIsDeprecated(param);
1523
- }
1524
- };
1525
-
1526
-
1527
-
1528
- var staticMethods = /*#__PURE__*/Object.freeze({
1529
- isValidParameter: isValidParameter,
1530
- isUpdatableParameter: isUpdatableParameter,
1531
- isDeprecatedParameter: isDeprecatedParameter,
1532
- argsToParams: argsToParams,
1533
- isVisible: isVisible$1,
1534
- clickConfirm: clickConfirm,
1535
- clickDeny: clickDeny,
1536
- clickCancel: clickCancel,
1537
- getContainer: getContainer,
1538
- getPopup: getPopup,
1539
- getTitle: getTitle,
1540
- getHtmlContainer: getHtmlContainer,
1541
- getImage: getImage,
1542
- getIcon: getIcon,
1543
- getInputLabel: getInputLabel,
1544
- getCloseButton: getCloseButton,
1545
- getActions: getActions,
1546
- getConfirmButton: getConfirmButton,
1547
- getDenyButton: getDenyButton,
1548
- getCancelButton: getCancelButton,
1549
- getLoader: getLoader,
1550
- getFooter: getFooter,
1551
- getTimerProgressBar: getTimerProgressBar,
1552
- getFocusableElements: getFocusableElements,
1553
- getValidationMessage: getValidationMessage,
1554
- isLoading: isLoading,
1555
- fire: fire,
1556
- mixin: mixin,
1557
- showLoading: showLoading,
1558
- enableLoading: showLoading,
1559
- getTimerLeft: getTimerLeft,
1560
- stopTimer: stopTimer,
1561
- resumeTimer: resumeTimer,
1562
- toggleTimer: toggleTimer,
1563
- increaseTimer: increaseTimer,
1564
- isTimerRunning: isTimerRunning,
1565
- bindClickHandler: bindClickHandler
1566
- });
1567
-
1568
- /**
1569
- * Hides loader and shows back the button which was hidden by .showLoading()
1570
- */
1571
-
1572
- function hideLoading() {
1573
- // do nothing if popup is closed
1574
- const innerParams = privateProps.innerParams.get(this);
1575
-
1576
- if (!innerParams) {
1577
- return;
1578
- }
1579
-
1580
- const domCache = privateProps.domCache.get(this);
1581
- hide(domCache.loader);
1582
-
1583
- if (isToast()) {
1584
- if (innerParams.icon) {
1585
- show(getIcon());
1586
- }
1587
- } else {
1588
- showRelatedButton(domCache);
1589
- }
1590
-
1591
- removeClass([domCache.popup, domCache.actions], swalClasses.loading);
1592
- domCache.popup.removeAttribute('aria-busy');
1593
- domCache.popup.removeAttribute('data-loading');
1594
- domCache.confirmButton.disabled = false;
1595
- domCache.denyButton.disabled = false;
1596
- domCache.cancelButton.disabled = false;
1597
- }
1598
-
1599
- const showRelatedButton = domCache => {
1600
- const buttonToReplace = domCache.popup.getElementsByClassName(domCache.loader.getAttribute('data-button-to-replace'));
1601
-
1602
- if (buttonToReplace.length) {
1603
- show(buttonToReplace[0], 'inline-block');
1604
- } else if (allButtonsAreHidden()) {
1605
- hide(domCache.actions);
1606
- }
1607
- };
1608
-
1609
- function getInput$1(instance) {
1610
- const innerParams = privateProps.innerParams.get(instance || this);
1611
- const domCache = privateProps.domCache.get(instance || this);
1612
-
1613
- if (!domCache) {
1614
- return null;
1615
- }
1616
-
1617
- return getInput(domCache.popup, innerParams.input);
1618
- }
1619
-
1620
- const fixScrollbar = () => {
1621
- // for queues, do not do this more than once
1622
- if (states.previousBodyPadding !== null) {
1623
- return;
1624
- } // if the body has overflow
1625
-
1626
-
1627
- if (document.body.scrollHeight > window.innerHeight) {
1628
- // add padding so the content doesn't shift after removal of scrollbar
1629
- states.previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'));
1630
- document.body.style.paddingRight = "".concat(states.previousBodyPadding + measureScrollbar(), "px");
1631
- }
1632
- };
1633
- const undoScrollbar = () => {
1634
- if (states.previousBodyPadding !== null) {
1635
- document.body.style.paddingRight = "".concat(states.previousBodyPadding, "px");
1636
- states.previousBodyPadding = null;
1637
- }
1638
- };
1639
-
1640
- /* istanbul ignore file */
1641
-
1642
- const iOSfix = () => {
1643
- const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream || navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
1644
-
1645
- if (iOS && !hasClass(document.body, swalClasses.iosfix)) {
1646
- const offset = document.body.scrollTop;
1647
- document.body.style.top = "".concat(offset * -1, "px");
1648
- addClass(document.body, swalClasses.iosfix);
1649
- lockBodyScroll();
1650
- addBottomPaddingForTallPopups(); // #1948
1651
- }
1652
- };
1653
-
1654
- const addBottomPaddingForTallPopups = () => {
1655
- const safari = !navigator.userAgent.match(/(CriOS|FxiOS|EdgiOS|YaBrowser|UCBrowser)/i);
1656
-
1657
- if (safari) {
1658
- const bottomPanelHeight = 44;
1659
-
1660
- if (getPopup().scrollHeight > window.innerHeight - bottomPanelHeight) {
1661
- getContainer().style.paddingBottom = "".concat(bottomPanelHeight, "px");
1662
- }
1663
- }
1664
- };
1665
-
1666
- const lockBodyScroll = () => {
1667
- // #1246
1668
- const container = getContainer();
1669
- let preventTouchMove;
1670
-
1671
- container.ontouchstart = e => {
1672
- preventTouchMove = shouldPreventTouchMove(e);
1673
- };
1674
-
1675
- container.ontouchmove = e => {
1676
- if (preventTouchMove) {
1677
- e.preventDefault();
1678
- e.stopPropagation();
1679
- }
1680
- };
1681
- };
1682
-
1683
- const shouldPreventTouchMove = event => {
1684
- const target = event.target;
1685
- const container = getContainer();
1686
-
1687
- if (isStylys(event) || isZoom(event)) {
1688
- return false;
1689
- }
1690
-
1691
- if (target === container) {
1692
- return true;
1693
- }
1694
-
1695
- if (!isScrollable(container) && target.tagName !== 'INPUT' && // #1603
1696
- target.tagName !== 'TEXTAREA' && // #2266
1697
- !(isScrollable(getHtmlContainer()) && // #1944
1698
- getHtmlContainer().contains(target))) {
1699
- return true;
1700
- }
1701
-
1702
- return false;
1703
- };
1704
-
1705
- const isStylys = event => {
1706
- // #1786
1707
- return event.touches && event.touches.length && event.touches[0].touchType === 'stylus';
1708
- };
1709
-
1710
- const isZoom = event => {
1711
- // #1891
1712
- return event.touches && event.touches.length > 1;
1713
- };
1714
-
1715
- const undoIOSfix = () => {
1716
- if (hasClass(document.body, swalClasses.iosfix)) {
1717
- const offset = parseInt(document.body.style.top, 10);
1718
- removeClass(document.body, swalClasses.iosfix);
1719
- document.body.style.top = '';
1720
- document.body.scrollTop = offset * -1;
1721
- }
1722
- };
1723
-
1724
- // Adding aria-hidden="true" to elements outside of the active modal dialog ensures that
1725
- // elements not within the active modal dialog will not be surfaced if a user opens a screen
1726
- // reader’s list of elements (headings, form controls, landmarks, etc.) in the document.
1727
-
1728
- const setAriaHidden = () => {
1729
- const bodyChildren = toArray(document.body.children);
1730
- bodyChildren.forEach(el => {
1731
- if (el === getContainer() || el.contains(getContainer())) {
1732
- return;
1733
- }
1734
-
1735
- if (el.hasAttribute('aria-hidden')) {
1736
- el.setAttribute('data-previous-aria-hidden', el.getAttribute('aria-hidden'));
1737
- }
1738
-
1739
- el.setAttribute('aria-hidden', 'true');
1740
- });
1741
- };
1742
- const unsetAriaHidden = () => {
1743
- const bodyChildren = toArray(document.body.children);
1744
- bodyChildren.forEach(el => {
1745
- if (el.hasAttribute('data-previous-aria-hidden')) {
1746
- el.setAttribute('aria-hidden', el.getAttribute('data-previous-aria-hidden'));
1747
- el.removeAttribute('data-previous-aria-hidden');
1748
- } else {
1749
- el.removeAttribute('aria-hidden');
1750
- }
1751
- });
1752
- };
1753
-
1754
- /**
1755
- * This module contains `WeakMap`s for each effectively-"private property" that a `Swal` has.
1756
- * For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
1757
- * This is the approach that Babel will probably take to implement private methods/fields
1758
- * https://github.com/tc39/proposal-private-methods
1759
- * https://github.com/babel/babel/pull/7555
1760
- * Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
1761
- * then we can use that language feature.
1762
- */
1763
- var privateMethods = {
1764
- swalPromiseResolve: new WeakMap(),
1765
- swalPromiseReject: new WeakMap()
1766
- };
1767
-
1768
- /*
1769
- * Instance method to close sweetAlert
1770
- */
1518
+ function setParameters(params) {
1519
+ setDefaultInputValidators(params); // showLoaderOnConfirm && preConfirm
1771
1520
 
1772
- function removePopupAndResetState(instance, container, returnFocus, didClose) {
1773
- if (isToast()) {
1774
- triggerDidCloseAndDispose(instance, didClose);
1775
- } else {
1776
- restoreActiveElement(returnFocus).then(() => triggerDidCloseAndDispose(instance, didClose));
1777
- globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, {
1778
- capture: globalState.keydownListenerCapture
1779
- });
1780
- globalState.keydownHandlerAdded = false;
1521
+ if (params.showLoaderOnConfirm && !params.preConfirm) {
1522
+ 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');
1781
1523
  }
1782
1524
 
1783
- const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); // workaround for #2088
1784
- // for some reason removing the container in Safari will scroll the document to bottom
1785
-
1786
- if (isSafari) {
1787
- container.setAttribute('style', 'display:none !important');
1788
- container.removeAttribute('class');
1789
- container.innerHTML = '';
1790
- } else {
1791
- container.remove();
1792
- }
1525
+ validateCustomTargetElement(params); // Replace newlines with <br> in title
1793
1526
 
1794
- if (isModal()) {
1795
- undoScrollbar();
1796
- undoIOSfix();
1797
- unsetAriaHidden();
1527
+ if (typeof params.title === 'string') {
1528
+ params.title = params.title.split('\n').join('<br />');
1798
1529
  }
1799
1530
 
1800
- removeBodyClasses();
1801
- }
1802
-
1803
- function removeBodyClasses() {
1804
- removeClass([document.documentElement, document.body], [swalClasses.shown, swalClasses['height-auto'], swalClasses['no-backdrop'], swalClasses['toast-shown']]);
1805
- }
1806
-
1807
- function close(resolveValue) {
1808
- resolveValue = prepareResolveValue(resolveValue);
1809
- const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this);
1810
- const didClose = triggerClosePopup(this);
1811
-
1812
- if (this.isAwaitingPromise()) {
1813
- // A swal awaiting for a promise (after a click on Confirm or Deny) cannot be dismissed anymore #2335
1814
- if (!resolveValue.isDismissed) {
1815
- handleAwaitingPromise(this);
1816
- swalPromiseResolve(resolveValue);
1817
- }
1818
- } else if (didClose) {
1819
- // Resolve Swal promise
1820
- swalPromiseResolve(resolveValue);
1821
- }
1822
- }
1823
- function isAwaitingPromise() {
1824
- return !!privateProps.awaitingPromise.get(this);
1531
+ init(params);
1825
1532
  }
1826
1533
 
1827
- const triggerClosePopup = instance => {
1828
- const popup = getPopup();
1534
+ class Timer {
1535
+ constructor(callback, delay) {
1536
+ this.callback = callback;
1537
+ this.remaining = delay;
1538
+ this.running = false;
1539
+ this.start();
1540
+ }
1829
1541
 
1830
- if (!popup) {
1831
- return false;
1542
+ start() {
1543
+ if (!this.running) {
1544
+ this.running = true;
1545
+ this.started = new Date();
1546
+ this.id = setTimeout(this.callback, this.remaining);
1547
+ }
1548
+
1549
+ return this.remaining;
1832
1550
  }
1833
1551
 
1834
- const innerParams = privateProps.innerParams.get(instance);
1552
+ stop() {
1553
+ if (this.running) {
1554
+ this.running = false;
1555
+ clearTimeout(this.id);
1556
+ this.remaining -= new Date() - this.started;
1557
+ }
1835
1558
 
1836
- if (!innerParams || hasClass(popup, innerParams.hideClass.popup)) {
1837
- return false;
1559
+ return this.remaining;
1838
1560
  }
1839
1561
 
1840
- removeClass(popup, innerParams.showClass.popup);
1841
- addClass(popup, innerParams.hideClass.popup);
1842
- const backdrop = getContainer();
1843
- removeClass(backdrop, innerParams.showClass.backdrop);
1844
- addClass(backdrop, innerParams.hideClass.backdrop);
1845
- handlePopupAnimation(instance, popup, innerParams);
1846
- return true;
1847
- };
1562
+ increase(n) {
1563
+ const running = this.running;
1848
1564
 
1849
- function rejectPromise(error) {
1850
- const rejectPromise = privateMethods.swalPromiseReject.get(this);
1851
- handleAwaitingPromise(this);
1565
+ if (running) {
1566
+ this.stop();
1567
+ }
1852
1568
 
1853
- if (rejectPromise) {
1854
- // Reject Swal promise
1855
- rejectPromise(error);
1856
- }
1857
- }
1569
+ this.remaining += n;
1858
1570
 
1859
- const handleAwaitingPromise = instance => {
1860
- if (instance.isAwaitingPromise()) {
1861
- privateProps.awaitingPromise.delete(instance); // The instance might have been previously partly destroyed, we must resume the destroy process in this case #2335
1571
+ if (running) {
1572
+ this.start();
1573
+ }
1862
1574
 
1863
- if (!privateProps.innerParams.get(instance)) {
1864
- instance._destroy();
1575
+ return this.remaining;
1576
+ }
1577
+
1578
+ getTimerLeft() {
1579
+ if (this.running) {
1580
+ this.stop();
1581
+ this.start();
1865
1582
  }
1583
+
1584
+ return this.remaining;
1866
1585
  }
1867
- };
1868
1586
 
1869
- const prepareResolveValue = resolveValue => {
1870
- // When user calls Swal.close()
1871
- if (typeof resolveValue === 'undefined') {
1872
- return {
1873
- isConfirmed: false,
1874
- isDenied: false,
1875
- isDismissed: true
1876
- };
1587
+ isRunning() {
1588
+ return this.running;
1877
1589
  }
1878
1590
 
1879
- return Object.assign({
1880
- isConfirmed: false,
1881
- isDenied: false,
1882
- isDismissed: false
1883
- }, resolveValue);
1884
- };
1591
+ }
1885
1592
 
1886
- const handlePopupAnimation = (instance, popup, innerParams) => {
1887
- const container = getContainer(); // If animation is supported, animate
1593
+ const fixScrollbar = () => {
1594
+ // for queues, do not do this more than once
1595
+ if (states.previousBodyPadding !== null) {
1596
+ return;
1597
+ } // if the body has overflow
1888
1598
 
1889
- const animationIsSupported = animationEndEvent && hasCssAnimation(popup);
1890
1599
 
1891
- if (typeof innerParams.willClose === 'function') {
1892
- innerParams.willClose(popup);
1600
+ if (document.body.scrollHeight > window.innerHeight) {
1601
+ // add padding so the content doesn't shift after removal of scrollbar
1602
+ states.previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'));
1603
+ document.body.style.paddingRight = "".concat(states.previousBodyPadding + measureScrollbar(), "px");
1893
1604
  }
1894
-
1895
- if (animationIsSupported) {
1896
- animatePopup(instance, popup, container, innerParams.returnFocus, innerParams.didClose);
1897
- } else {
1898
- // Otherwise, remove immediately
1899
- removePopupAndResetState(instance, container, innerParams.returnFocus, innerParams.didClose);
1605
+ };
1606
+ const undoScrollbar = () => {
1607
+ if (states.previousBodyPadding !== null) {
1608
+ document.body.style.paddingRight = "".concat(states.previousBodyPadding, "px");
1609
+ states.previousBodyPadding = null;
1900
1610
  }
1901
1611
  };
1902
1612
 
1903
- const animatePopup = (instance, popup, container, returnFocus, didClose) => {
1904
- globalState.swalCloseEventFinishedCallback = removePopupAndResetState.bind(null, instance, container, returnFocus, didClose);
1905
- popup.addEventListener(animationEndEvent, function (e) {
1906
- if (e.target === popup) {
1907
- globalState.swalCloseEventFinishedCallback();
1908
- delete globalState.swalCloseEventFinishedCallback;
1909
- }
1910
- });
1911
- };
1613
+ /* istanbul ignore file */
1912
1614
 
1913
- const triggerDidCloseAndDispose = (instance, didClose) => {
1914
- setTimeout(() => {
1915
- if (typeof didClose === 'function') {
1916
- didClose.bind(instance.params)();
1917
- }
1615
+ const iOSfix = () => {
1616
+ const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream || navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
1918
1617
 
1919
- instance._destroy();
1920
- });
1618
+ if (iOS && !hasClass(document.body, swalClasses.iosfix)) {
1619
+ const offset = document.body.scrollTop;
1620
+ document.body.style.top = "".concat(offset * -1, "px");
1621
+ addClass(document.body, swalClasses.iosfix);
1622
+ lockBodyScroll();
1623
+ addBottomPaddingForTallPopups(); // #1948
1624
+ }
1921
1625
  };
1922
1626
 
1923
- function setButtonsDisabled(instance, buttons, disabled) {
1924
- const domCache = privateProps.domCache.get(instance);
1925
- buttons.forEach(button => {
1926
- domCache[button].disabled = disabled;
1927
- });
1928
- }
1929
-
1930
- function setInputDisabled(input, disabled) {
1931
- if (!input) {
1932
- return false;
1933
- }
1627
+ const addBottomPaddingForTallPopups = () => {
1628
+ const safari = !navigator.userAgent.match(/(CriOS|FxiOS|EdgiOS|YaBrowser|UCBrowser)/i);
1934
1629
 
1935
- if (input.type === 'radio') {
1936
- const radiosContainer = input.parentNode.parentNode;
1937
- const radios = radiosContainer.querySelectorAll('input');
1630
+ if (safari) {
1631
+ const bottomPanelHeight = 44;
1938
1632
 
1939
- for (let i = 0; i < radios.length; i++) {
1940
- radios[i].disabled = disabled;
1633
+ if (getPopup().scrollHeight > window.innerHeight - bottomPanelHeight) {
1634
+ getContainer().style.paddingBottom = "".concat(bottomPanelHeight, "px");
1941
1635
  }
1942
- } else {
1943
- input.disabled = disabled;
1944
1636
  }
1945
- }
1637
+ };
1946
1638
 
1947
- function enableButtons() {
1948
- setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], false);
1949
- }
1950
- function disableButtons() {
1951
- setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], true);
1952
- }
1953
- function enableInput() {
1954
- return setInputDisabled(this.getInput(), false);
1955
- }
1956
- function disableInput() {
1957
- return setInputDisabled(this.getInput(), true);
1958
- }
1639
+ const lockBodyScroll = () => {
1640
+ // #1246
1641
+ const container = getContainer();
1642
+ let preventTouchMove;
1959
1643
 
1960
- function showValidationMessage(error) {
1961
- const domCache = privateProps.domCache.get(this);
1962
- const params = privateProps.innerParams.get(this);
1963
- setInnerHtml(domCache.validationMessage, error);
1964
- domCache.validationMessage.className = swalClasses['validation-message'];
1644
+ container.ontouchstart = e => {
1645
+ preventTouchMove = shouldPreventTouchMove(e);
1646
+ };
1965
1647
 
1966
- if (params.customClass && params.customClass.validationMessage) {
1967
- addClass(domCache.validationMessage, params.customClass.validationMessage);
1968
- }
1648
+ container.ontouchmove = e => {
1649
+ if (preventTouchMove) {
1650
+ e.preventDefault();
1651
+ e.stopPropagation();
1652
+ }
1653
+ };
1654
+ };
1969
1655
 
1970
- show(domCache.validationMessage);
1971
- const input = this.getInput();
1656
+ const shouldPreventTouchMove = event => {
1657
+ const target = event.target;
1658
+ const container = getContainer();
1972
1659
 
1973
- if (input) {
1974
- input.setAttribute('aria-invalid', true);
1975
- input.setAttribute('aria-describedby', swalClasses['validation-message']);
1976
- focusInput(input);
1977
- addClass(input, swalClasses.inputerror);
1660
+ if (isStylys(event) || isZoom(event)) {
1661
+ return false;
1978
1662
  }
1979
- } // Hide block with validation message
1980
-
1981
- function resetValidationMessage$1() {
1982
- const domCache = privateProps.domCache.get(this);
1983
1663
 
1984
- if (domCache.validationMessage) {
1985
- hide(domCache.validationMessage);
1664
+ if (target === container) {
1665
+ return true;
1986
1666
  }
1987
1667
 
1988
- const input = this.getInput();
1989
-
1990
- if (input) {
1991
- input.removeAttribute('aria-invalid');
1992
- input.removeAttribute('aria-describedby');
1993
- removeClass(input, swalClasses.inputerror);
1668
+ if (!isScrollable(container) && target.tagName !== 'INPUT' && // #1603
1669
+ target.tagName !== 'TEXTAREA' && // #2266
1670
+ !(isScrollable(getHtmlContainer()) && // #1944
1671
+ getHtmlContainer().contains(target))) {
1672
+ return true;
1994
1673
  }
1995
- }
1996
1674
 
1997
- function getProgressSteps$1() {
1998
- const domCache = privateProps.domCache.get(this);
1999
- return domCache.progressSteps;
2000
- }
1675
+ return false;
1676
+ };
2001
1677
 
2002
- class Timer {
2003
- constructor(callback, delay) {
2004
- this.callback = callback;
2005
- this.remaining = delay;
2006
- this.running = false;
2007
- this.start();
2008
- }
1678
+ const isStylys = event => {
1679
+ // #1786
1680
+ return event.touches && event.touches.length && event.touches[0].touchType === 'stylus';
1681
+ };
2009
1682
 
2010
- start() {
2011
- if (!this.running) {
2012
- this.running = true;
2013
- this.started = new Date();
2014
- this.id = setTimeout(this.callback, this.remaining);
2015
- }
1683
+ const isZoom = event => {
1684
+ // #1891
1685
+ return event.touches && event.touches.length > 1;
1686
+ };
2016
1687
 
2017
- return this.remaining;
1688
+ const undoIOSfix = () => {
1689
+ if (hasClass(document.body, swalClasses.iosfix)) {
1690
+ const offset = parseInt(document.body.style.top, 10);
1691
+ removeClass(document.body, swalClasses.iosfix);
1692
+ document.body.style.top = '';
1693
+ document.body.scrollTop = offset * -1;
2018
1694
  }
1695
+ };
2019
1696
 
2020
- stop() {
2021
- if (this.running) {
2022
- this.running = false;
2023
- clearTimeout(this.id);
2024
- this.remaining -= new Date() - this.started;
2025
- }
1697
+ const RESTORE_FOCUS_TIMEOUT = 100;
2026
1698
 
2027
- return this.remaining;
1699
+ const globalState = {};
1700
+
1701
+ const focusPreviousActiveElement = () => {
1702
+ if (globalState.previousActiveElement && globalState.previousActiveElement.focus) {
1703
+ globalState.previousActiveElement.focus();
1704
+ globalState.previousActiveElement = null;
1705
+ } else if (document.body) {
1706
+ document.body.focus();
2028
1707
  }
1708
+ }; // Restore previous active (focused) element
2029
1709
 
2030
- increase(n) {
2031
- const running = this.running;
2032
1710
 
2033
- if (running) {
2034
- this.stop();
1711
+ const restoreActiveElement = returnFocus => {
1712
+ return new Promise(resolve => {
1713
+ if (!returnFocus) {
1714
+ return resolve();
2035
1715
  }
2036
1716
 
2037
- this.remaining += n;
1717
+ const x = window.scrollX;
1718
+ const y = window.scrollY;
1719
+ globalState.restoreFocusTimeout = setTimeout(() => {
1720
+ focusPreviousActiveElement();
1721
+ resolve();
1722
+ }, RESTORE_FOCUS_TIMEOUT); // issues/900
2038
1723
 
2039
- if (running) {
2040
- this.start();
2041
- }
1724
+ window.scrollTo(x, y);
1725
+ });
1726
+ };
2042
1727
 
2043
- return this.remaining;
2044
- }
1728
+ const SHOW_CLASS_TIMEOUT = 10;
1729
+ /**
1730
+ * Open popup, add necessary classes and styles, fix scrollbar
1731
+ *
1732
+ * @param params
1733
+ */
2045
1734
 
2046
- getTimerLeft() {
2047
- if (this.running) {
2048
- this.stop();
2049
- this.start();
2050
- }
1735
+ const openPopup = params => {
1736
+ const container = getContainer();
1737
+ const popup = getPopup();
2051
1738
 
2052
- return this.remaining;
1739
+ if (typeof params.willOpen === 'function') {
1740
+ params.willOpen(popup);
2053
1741
  }
2054
1742
 
2055
- isRunning() {
2056
- return this.running;
2057
- }
1743
+ const bodyStyles = window.getComputedStyle(document.body);
1744
+ const initialBodyOverflow = bodyStyles.overflowY;
1745
+ addClasses$1(container, popup, params); // scrolling is 'hidden' until animation is done, after that 'auto'
2058
1746
 
2059
- }
1747
+ setTimeout(() => {
1748
+ setScrollingVisibility(container, popup);
1749
+ }, SHOW_CLASS_TIMEOUT);
2060
1750
 
2061
- var defaultInputValidators = {
2062
- email: (string, validationMessage) => {
2063
- return /^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,24}$/.test(string) ? Promise.resolve() : Promise.resolve(validationMessage || 'Invalid email address');
2064
- },
2065
- url: (string, validationMessage) => {
2066
- // taken from https://stackoverflow.com/a/3809435 with a small change from #1306 and #2013
2067
- 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');
1751
+ if (isModal()) {
1752
+ fixScrollContainer(container, params.scrollbarPadding, initialBodyOverflow);
1753
+ setAriaHidden();
2068
1754
  }
2069
- };
2070
1755
 
2071
- function setDefaultInputValidators(params) {
2072
- // Use default `inputValidator` for supported input types if not provided
2073
- if (!params.inputValidator) {
2074
- Object.keys(defaultInputValidators).forEach(key => {
2075
- if (params.input === key) {
2076
- params.inputValidator = defaultInputValidators[key];
2077
- }
2078
- });
1756
+ if (!isToast() && !globalState.previousActiveElement) {
1757
+ globalState.previousActiveElement = document.activeElement;
2079
1758
  }
2080
- }
2081
1759
 
2082
- function validateCustomTargetElement(params) {
2083
- // Determine if the custom target element is valid
2084
- if (!params.target || typeof params.target === 'string' && !document.querySelector(params.target) || typeof params.target !== 'string' && !params.target.appendChild) {
2085
- warn('Target parameter is not valid, defaulting to "body"');
2086
- params.target = 'body';
1760
+ if (typeof params.didOpen === 'function') {
1761
+ setTimeout(() => params.didOpen(popup));
2087
1762
  }
2088
- }
2089
- /**
2090
- * Set type, text and actions on popup
2091
- *
2092
- * @param params
2093
- * @returns {boolean}
2094
- */
2095
1763
 
1764
+ removeClass(container, swalClasses['no-transition']);
1765
+ };
2096
1766
 
2097
- function setParameters(params) {
2098
- setDefaultInputValidators(params); // showLoaderOnConfirm && preConfirm
1767
+ const swalOpenAnimationFinished = event => {
1768
+ const popup = getPopup();
2099
1769
 
2100
- if (params.showLoaderOnConfirm && !params.preConfirm) {
2101
- 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');
1770
+ if (event.target !== popup) {
1771
+ return;
2102
1772
  }
2103
1773
 
2104
- validateCustomTargetElement(params); // Replace newlines with <br> in title
1774
+ const container = getContainer();
1775
+ popup.removeEventListener(animationEndEvent, swalOpenAnimationFinished);
1776
+ container.style.overflowY = 'auto';
1777
+ };
2105
1778
 
2106
- if (typeof params.title === 'string') {
2107
- params.title = params.title.split('\n').join('<br />');
1779
+ const setScrollingVisibility = (container, popup) => {
1780
+ if (animationEndEvent && hasCssAnimation(popup)) {
1781
+ container.style.overflowY = 'hidden';
1782
+ popup.addEventListener(animationEndEvent, swalOpenAnimationFinished);
1783
+ } else {
1784
+ container.style.overflowY = 'auto';
2108
1785
  }
1786
+ };
2109
1787
 
2110
- init(params);
2111
- }
1788
+ const fixScrollContainer = (container, scrollbarPadding, initialBodyOverflow) => {
1789
+ iOSfix();
2112
1790
 
2113
- const swalStringParams = ['swal-title', 'swal-html', 'swal-footer'];
2114
- const getTemplateParams = params => {
2115
- const template = typeof params.template === 'string' ? document.querySelector(params.template) : params.template;
1791
+ if (scrollbarPadding && initialBodyOverflow !== 'hidden') {
1792
+ fixScrollbar();
1793
+ } // sweetalert2/issues/1247
2116
1794
 
2117
- if (!template) {
2118
- return {};
2119
- }
2120
1795
 
2121
- const templateContent = template.content;
2122
- showWarningsForElements(templateContent);
2123
- const result = Object.assign(getSwalParams(templateContent), getSwalButtons(templateContent), getSwalImage(templateContent), getSwalIcon(templateContent), getSwalInput(templateContent), getSwalStringParams(templateContent, swalStringParams));
2124
- return result;
1796
+ setTimeout(() => {
1797
+ container.scrollTop = 0;
1798
+ });
2125
1799
  };
2126
1800
 
2127
- const getSwalParams = templateContent => {
2128
- const result = {};
2129
- toArray(templateContent.querySelectorAll('swal-param')).forEach(param => {
2130
- showWarningsForAttributes(param, ['name', 'value']);
2131
- const paramName = param.getAttribute('name');
2132
- let value = param.getAttribute('value');
1801
+ const addClasses$1 = (container, popup, params) => {
1802
+ addClass(container, params.showClass.backdrop); // the workaround with setting/unsetting opacity is needed for #2019 and 2059
2133
1803
 
2134
- if (typeof defaultParams[paramName] === 'boolean' && value === 'false') {
2135
- value = false;
2136
- }
1804
+ popup.style.setProperty('opacity', '0', 'important');
1805
+ show(popup, 'grid');
1806
+ setTimeout(() => {
1807
+ // Animate popup right after showing it
1808
+ addClass(popup, params.showClass.popup); // and remove the opacity workaround
2137
1809
 
2138
- if (typeof defaultParams[paramName] === 'object') {
2139
- value = JSON.parse(value);
2140
- }
1810
+ popup.style.removeProperty('opacity');
1811
+ }, SHOW_CLASS_TIMEOUT); // 10ms in order to fix #2062
2141
1812
 
2142
- result[paramName] = value;
2143
- });
2144
- return result;
1813
+ addClass([document.documentElement, document.body], swalClasses.shown);
1814
+
1815
+ if (params.heightAuto && params.backdrop && !params.toast) {
1816
+ addClass([document.documentElement, document.body], swalClasses['height-auto']);
1817
+ }
2145
1818
  };
2146
1819
 
2147
- const getSwalButtons = templateContent => {
2148
- const result = {};
2149
- toArray(templateContent.querySelectorAll('swal-button')).forEach(button => {
2150
- showWarningsForAttributes(button, ['type', 'color', 'aria-label']);
2151
- const type = button.getAttribute('type');
2152
- result["".concat(type, "ButtonText")] = button.innerHTML;
2153
- result["show".concat(capitalizeFirstLetter(type), "Button")] = true;
1820
+ /**
1821
+ * Shows loader (spinner), this is useful with AJAX requests.
1822
+ * By default the loader be shown instead of the "Confirm" button.
1823
+ */
2154
1824
 
2155
- if (button.hasAttribute('color')) {
2156
- result["".concat(type, "ButtonColor")] = button.getAttribute('color');
2157
- }
1825
+ const showLoading = buttonToReplace => {
1826
+ let popup = getPopup();
2158
1827
 
2159
- if (button.hasAttribute('aria-label')) {
2160
- result["".concat(type, "ButtonAriaLabel")] = button.getAttribute('aria-label');
2161
- }
2162
- });
2163
- return result;
1828
+ if (!popup) {
1829
+ Swal.fire();
1830
+ }
1831
+
1832
+ popup = getPopup();
1833
+ const loader = getLoader();
1834
+
1835
+ if (isToast()) {
1836
+ hide(getIcon());
1837
+ } else {
1838
+ replaceButton(popup, buttonToReplace);
1839
+ }
1840
+
1841
+ show(loader);
1842
+ popup.setAttribute('data-loading', true);
1843
+ popup.setAttribute('aria-busy', true);
1844
+ popup.focus();
2164
1845
  };
2165
1846
 
2166
- const getSwalImage = templateContent => {
2167
- const result = {};
2168
- const image = templateContent.querySelector('swal-image');
1847
+ const replaceButton = (popup, buttonToReplace) => {
1848
+ const actions = getActions();
1849
+ const loader = getLoader();
2169
1850
 
2170
- if (image) {
2171
- showWarningsForAttributes(image, ['src', 'width', 'height', 'alt']);
1851
+ if (!buttonToReplace && isVisible(getConfirmButton())) {
1852
+ buttonToReplace = getConfirmButton();
1853
+ }
2172
1854
 
2173
- if (image.hasAttribute('src')) {
2174
- result.imageUrl = image.getAttribute('src');
2175
- }
1855
+ show(actions);
2176
1856
 
2177
- if (image.hasAttribute('width')) {
2178
- result.imageWidth = image.getAttribute('width');
2179
- }
1857
+ if (buttonToReplace) {
1858
+ hide(buttonToReplace);
1859
+ loader.setAttribute('data-button-to-replace', buttonToReplace.className);
1860
+ }
2180
1861
 
2181
- if (image.hasAttribute('height')) {
2182
- result.imageHeight = image.getAttribute('height');
2183
- }
1862
+ loader.parentNode.insertBefore(loader, buttonToReplace);
1863
+ addClass([popup, actions], swalClasses.loading);
1864
+ };
2184
1865
 
2185
- if (image.hasAttribute('alt')) {
2186
- result.imageAlt = image.getAttribute('alt');
2187
- }
1866
+ const handleInputOptionsAndValue = (instance, params) => {
1867
+ if (params.input === 'select' || params.input === 'radio') {
1868
+ handleInputOptions(instance, params);
1869
+ } else if (['text', 'email', 'number', 'tel', 'textarea'].includes(params.input) && (hasToPromiseFn(params.inputValue) || isPromise(params.inputValue))) {
1870
+ showLoading(getConfirmButton());
1871
+ handleInputValue(instance, params);
2188
1872
  }
2189
-
2190
- return result;
2191
1873
  };
1874
+ const getInputValue = (instance, innerParams) => {
1875
+ const input = instance.getInput();
2192
1876
 
2193
- const getSwalIcon = templateContent => {
2194
- const result = {};
2195
- const icon = templateContent.querySelector('swal-icon');
1877
+ if (!input) {
1878
+ return null;
1879
+ }
2196
1880
 
2197
- if (icon) {
2198
- showWarningsForAttributes(icon, ['type', 'color']);
1881
+ switch (innerParams.input) {
1882
+ case 'checkbox':
1883
+ return getCheckboxValue(input);
2199
1884
 
2200
- if (icon.hasAttribute('type')) {
2201
- result.icon = icon.getAttribute('type');
2202
- }
1885
+ case 'radio':
1886
+ return getRadioValue(input);
2203
1887
 
2204
- if (icon.hasAttribute('color')) {
2205
- result.iconColor = icon.getAttribute('color');
2206
- }
1888
+ case 'file':
1889
+ return getFileValue(input);
2207
1890
 
2208
- result.iconHtml = icon.innerHTML;
1891
+ default:
1892
+ return innerParams.inputAutoTrim ? input.value.trim() : input.value;
2209
1893
  }
2210
-
2211
- return result;
2212
1894
  };
2213
1895
 
2214
- const getSwalInput = templateContent => {
2215
- const result = {};
2216
- const input = templateContent.querySelector('swal-input');
2217
-
2218
- if (input) {
2219
- showWarningsForAttributes(input, ['type', 'label', 'placeholder', 'value']);
2220
- result.input = input.getAttribute('type') || 'text';
1896
+ const getCheckboxValue = input => input.checked ? 1 : 0;
2221
1897
 
2222
- if (input.hasAttribute('label')) {
2223
- result.inputLabel = input.getAttribute('label');
2224
- }
1898
+ const getRadioValue = input => input.checked ? input.value : null;
2225
1899
 
2226
- if (input.hasAttribute('placeholder')) {
2227
- result.inputPlaceholder = input.getAttribute('placeholder');
2228
- }
1900
+ const getFileValue = input => input.files.length ? input.getAttribute('multiple') !== null ? input.files : input.files[0] : null;
2229
1901
 
2230
- if (input.hasAttribute('value')) {
2231
- result.inputValue = input.getAttribute('value');
2232
- }
2233
- }
1902
+ const handleInputOptions = (instance, params) => {
1903
+ const popup = getPopup();
2234
1904
 
2235
- const inputOptions = templateContent.querySelectorAll('swal-input-option');
1905
+ const processInputOptions = inputOptions => populateInputOptions[params.input](popup, formatInputOptions(inputOptions), params);
2236
1906
 
2237
- if (inputOptions.length) {
2238
- result.inputOptions = {};
2239
- toArray(inputOptions).forEach(option => {
2240
- showWarningsForAttributes(option, ['value']);
2241
- const optionValue = option.getAttribute('value');
2242
- const optionName = option.innerHTML;
2243
- result.inputOptions[optionValue] = optionName;
1907
+ if (hasToPromiseFn(params.inputOptions) || isPromise(params.inputOptions)) {
1908
+ showLoading(getConfirmButton());
1909
+ asPromise(params.inputOptions).then(inputOptions => {
1910
+ instance.hideLoading();
1911
+ processInputOptions(inputOptions);
2244
1912
  });
1913
+ } else if (typeof params.inputOptions === 'object') {
1914
+ processInputOptions(params.inputOptions);
1915
+ } else {
1916
+ error("Unexpected type of inputOptions! Expected object, Map or Promise, got ".concat(typeof params.inputOptions));
2245
1917
  }
1918
+ };
2246
1919
 
2247
- return result;
1920
+ const handleInputValue = (instance, params) => {
1921
+ const input = instance.getInput();
1922
+ hide(input);
1923
+ asPromise(params.inputValue).then(inputValue => {
1924
+ input.value = params.input === 'number' ? parseFloat(inputValue) || 0 : "".concat(inputValue);
1925
+ show(input);
1926
+ input.focus();
1927
+ instance.hideLoading();
1928
+ }).catch(err => {
1929
+ error("Error in inputValue promise: ".concat(err));
1930
+ input.value = '';
1931
+ show(input);
1932
+ input.focus();
1933
+ instance.hideLoading();
1934
+ });
2248
1935
  };
2249
1936
 
2250
- const getSwalStringParams = (templateContent, paramNames) => {
2251
- const result = {};
1937
+ const populateInputOptions = {
1938
+ select: (popup, inputOptions, params) => {
1939
+ const select = getChildByClass(popup, swalClasses.select);
2252
1940
 
2253
- for (const i in paramNames) {
2254
- const paramName = paramNames[i];
2255
- const tag = templateContent.querySelector(paramName);
1941
+ const renderOption = (parent, optionLabel, optionValue) => {
1942
+ const option = document.createElement('option');
1943
+ option.value = optionValue;
1944
+ setInnerHtml(option, optionLabel);
1945
+ option.selected = isSelected(optionValue, params.inputValue);
1946
+ parent.appendChild(option);
1947
+ };
2256
1948
 
2257
- if (tag) {
2258
- showWarningsForAttributes(tag, []);
2259
- result[paramName.replace(/^swal-/, '')] = tag.innerHTML.trim();
2260
- }
2261
- }
1949
+ inputOptions.forEach(inputOption => {
1950
+ const optionValue = inputOption[0];
1951
+ const optionLabel = inputOption[1]; // <optgroup> spec:
1952
+ // https://www.w3.org/TR/html401/interact/forms.html#h-17.6
1953
+ // "...all OPTGROUP elements must be specified directly within a SELECT element (i.e., groups may not be nested)..."
1954
+ // check whether this is a <optgroup>
2262
1955
 
2263
- return result;
2264
- };
1956
+ if (Array.isArray(optionLabel)) {
1957
+ // if it is an array, then it is an <optgroup>
1958
+ const optgroup = document.createElement('optgroup');
1959
+ optgroup.label = optionValue;
1960
+ optgroup.disabled = false; // not configurable for now
2265
1961
 
2266
- const showWarningsForElements = template => {
2267
- const allowedElements = swalStringParams.concat(['swal-param', 'swal-button', 'swal-image', 'swal-icon', 'swal-input', 'swal-input-option']);
2268
- toArray(template.children).forEach(el => {
2269
- const tagName = el.tagName.toLowerCase();
1962
+ select.appendChild(optgroup);
1963
+ optionLabel.forEach(o => renderOption(optgroup, o[1], o[0]));
1964
+ } else {
1965
+ // case of <option>
1966
+ renderOption(select, optionLabel, optionValue);
1967
+ }
1968
+ });
1969
+ select.focus();
1970
+ },
1971
+ radio: (popup, inputOptions, params) => {
1972
+ const radio = getChildByClass(popup, swalClasses.radio);
1973
+ inputOptions.forEach(inputOption => {
1974
+ const radioValue = inputOption[0];
1975
+ const radioLabel = inputOption[1];
1976
+ const radioInput = document.createElement('input');
1977
+ const radioLabelElement = document.createElement('label');
1978
+ radioInput.type = 'radio';
1979
+ radioInput.name = swalClasses.radio;
1980
+ radioInput.value = radioValue;
2270
1981
 
2271
- if (allowedElements.indexOf(tagName) === -1) {
2272
- warn("Unrecognized element <".concat(tagName, ">"));
2273
- }
2274
- });
2275
- };
1982
+ if (isSelected(radioValue, params.inputValue)) {
1983
+ radioInput.checked = true;
1984
+ }
2276
1985
 
2277
- const showWarningsForAttributes = (el, allowedAttributes) => {
2278
- toArray(el.attributes).forEach(attribute => {
2279
- if (allowedAttributes.indexOf(attribute.name) === -1) {
2280
- warn(["Unrecognized attribute \"".concat(attribute.name, "\" on <").concat(el.tagName.toLowerCase(), ">."), "".concat(allowedAttributes.length ? "Allowed attributes are: ".concat(allowedAttributes.join(', ')) : 'To set the value, use HTML within the element.')]);
1986
+ const label = document.createElement('span');
1987
+ setInnerHtml(label, radioLabel);
1988
+ label.className = swalClasses.label;
1989
+ radioLabelElement.appendChild(radioInput);
1990
+ radioLabelElement.appendChild(label);
1991
+ radio.appendChild(radioLabelElement);
1992
+ });
1993
+ const radios = radio.querySelectorAll('input');
1994
+
1995
+ if (radios.length) {
1996
+ radios[0].focus();
2281
1997
  }
2282
- });
1998
+ }
2283
1999
  };
2284
-
2285
- const SHOW_CLASS_TIMEOUT = 10;
2286
2000
  /**
2287
- * Open popup, add necessary classes and styles, fix scrollbar
2288
- *
2289
- * @param params
2001
+ * Converts `inputOptions` into an array of `[value, label]`s
2002
+ * @param inputOptions
2290
2003
  */
2291
2004
 
2292
- const openPopup = params => {
2293
- const container = getContainer();
2294
- const popup = getPopup();
2005
+ const formatInputOptions = inputOptions => {
2006
+ const result = [];
2295
2007
 
2296
- if (typeof params.willOpen === 'function') {
2297
- params.willOpen(popup);
2008
+ if (typeof Map !== 'undefined' && inputOptions instanceof Map) {
2009
+ inputOptions.forEach((value, key) => {
2010
+ let valueFormatted = value;
2011
+
2012
+ if (typeof valueFormatted === 'object') {
2013
+ // case of <optgroup>
2014
+ valueFormatted = formatInputOptions(valueFormatted);
2015
+ }
2016
+
2017
+ result.push([key, valueFormatted]);
2018
+ });
2019
+ } else {
2020
+ Object.keys(inputOptions).forEach(key => {
2021
+ let valueFormatted = inputOptions[key];
2022
+
2023
+ if (typeof valueFormatted === 'object') {
2024
+ // case of <optgroup>
2025
+ valueFormatted = formatInputOptions(valueFormatted);
2026
+ }
2027
+
2028
+ result.push([key, valueFormatted]);
2029
+ });
2298
2030
  }
2299
2031
 
2300
- const bodyStyles = window.getComputedStyle(document.body);
2301
- const initialBodyOverflow = bodyStyles.overflowY;
2302
- addClasses$1(container, popup, params); // scrolling is 'hidden' until animation is done, after that 'auto'
2032
+ return result;
2033
+ };
2303
2034
 
2304
- setTimeout(() => {
2305
- setScrollingVisibility(container, popup);
2306
- }, SHOW_CLASS_TIMEOUT);
2035
+ const isSelected = (optionValue, inputValue) => {
2036
+ return inputValue && inputValue.toString() === optionValue.toString();
2037
+ };
2307
2038
 
2308
- if (isModal()) {
2309
- fixScrollContainer(container, params.scrollbarPadding, initialBodyOverflow);
2310
- setAriaHidden();
2039
+ const handleConfirmButtonClick = instance => {
2040
+ const innerParams = privateProps.innerParams.get(instance);
2041
+ instance.disableButtons();
2042
+
2043
+ if (innerParams.input) {
2044
+ handleConfirmOrDenyWithInput(instance, 'confirm');
2045
+ } else {
2046
+ confirm(instance, true);
2311
2047
  }
2048
+ };
2049
+ const handleDenyButtonClick = instance => {
2050
+ const innerParams = privateProps.innerParams.get(instance);
2051
+ instance.disableButtons();
2312
2052
 
2313
- if (!isToast() && !globalState.previousActiveElement) {
2314
- globalState.previousActiveElement = document.activeElement;
2053
+ if (innerParams.returnInputValueOnDeny) {
2054
+ handleConfirmOrDenyWithInput(instance, 'deny');
2055
+ } else {
2056
+ deny(instance, false);
2057
+ }
2058
+ };
2059
+ const handleCancelButtonClick = (instance, dismissWith) => {
2060
+ instance.disableButtons();
2061
+ dismissWith(DismissReason.cancel);
2062
+ };
2063
+
2064
+ const handleConfirmOrDenyWithInput = (instance, type
2065
+ /* 'confirm' | 'deny' */
2066
+ ) => {
2067
+ const innerParams = privateProps.innerParams.get(instance);
2068
+ const inputValue = getInputValue(instance, innerParams);
2069
+
2070
+ if (innerParams.inputValidator) {
2071
+ handleInputValidator(instance, inputValue, type);
2072
+ } else if (!instance.getInput().checkValidity()) {
2073
+ instance.enableButtons();
2074
+ instance.showValidationMessage(innerParams.validationMessage);
2075
+ } else if (type === 'deny') {
2076
+ deny(instance, inputValue);
2077
+ } else {
2078
+ confirm(instance, inputValue);
2315
2079
  }
2080
+ };
2316
2081
 
2317
- if (typeof params.didOpen === 'function') {
2318
- setTimeout(() => params.didOpen(popup));
2319
- }
2082
+ const handleInputValidator = (instance, inputValue, type
2083
+ /* 'confirm' | 'deny' */
2084
+ ) => {
2085
+ const innerParams = privateProps.innerParams.get(instance);
2086
+ instance.disableInput();
2087
+ const validationPromise = Promise.resolve().then(() => asPromise(innerParams.inputValidator(inputValue, innerParams.validationMessage)));
2088
+ validationPromise.then(validationMessage => {
2089
+ instance.enableButtons();
2090
+ instance.enableInput();
2320
2091
 
2321
- removeClass(container, swalClasses['no-transition']);
2092
+ if (validationMessage) {
2093
+ instance.showValidationMessage(validationMessage);
2094
+ } else if (type === 'deny') {
2095
+ deny(instance, inputValue);
2096
+ } else {
2097
+ confirm(instance, inputValue);
2098
+ }
2099
+ });
2322
2100
  };
2323
2101
 
2324
- const swalOpenAnimationFinished = event => {
2325
- const popup = getPopup();
2102
+ const deny = (instance, value) => {
2103
+ const innerParams = privateProps.innerParams.get(instance || undefined);
2326
2104
 
2327
- if (event.target !== popup) {
2328
- return;
2105
+ if (innerParams.showLoaderOnDeny) {
2106
+ showLoading(getDenyButton());
2329
2107
  }
2330
2108
 
2331
- const container = getContainer();
2332
- popup.removeEventListener(animationEndEvent, swalOpenAnimationFinished);
2333
- container.style.overflowY = 'auto';
2334
- };
2109
+ if (innerParams.preDeny) {
2110
+ privateProps.awaitingPromise.set(instance || undefined, true); // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesnt get destroyed until the result from this preDeny's promise is received
2335
2111
 
2336
- const setScrollingVisibility = (container, popup) => {
2337
- if (animationEndEvent && hasCssAnimation(popup)) {
2338
- container.style.overflowY = 'hidden';
2339
- popup.addEventListener(animationEndEvent, swalOpenAnimationFinished);
2112
+ const preDenyPromise = Promise.resolve().then(() => asPromise(innerParams.preDeny(value, innerParams.validationMessage)));
2113
+ preDenyPromise.then(preDenyValue => {
2114
+ if (preDenyValue === false) {
2115
+ instance.hideLoading();
2116
+ } else {
2117
+ instance.closePopup({
2118
+ isDenied: true,
2119
+ value: typeof preDenyValue === 'undefined' ? value : preDenyValue
2120
+ });
2121
+ }
2122
+ }).catch(error$$1 => rejectWith(instance || undefined, error$$1));
2340
2123
  } else {
2341
- container.style.overflowY = 'auto';
2124
+ instance.closePopup({
2125
+ isDenied: true,
2126
+ value
2127
+ });
2342
2128
  }
2343
2129
  };
2344
2130
 
2345
- const fixScrollContainer = (container, scrollbarPadding, initialBodyOverflow) => {
2346
- iOSfix();
2347
-
2348
- if (scrollbarPadding && initialBodyOverflow !== 'hidden') {
2349
- fixScrollbar();
2350
- } // sweetalert2/issues/1247
2351
-
2352
-
2353
- setTimeout(() => {
2354
- container.scrollTop = 0;
2131
+ const succeedWith = (instance, value) => {
2132
+ instance.closePopup({
2133
+ isConfirmed: true,
2134
+ value
2355
2135
  });
2356
2136
  };
2357
2137
 
2358
- const addClasses$1 = (container, popup, params) => {
2359
- addClass(container, params.showClass.backdrop); // the workaround with setting/unsetting opacity is needed for #2019 and 2059
2138
+ const rejectWith = (instance, error$$1) => {
2139
+ instance.rejectPromise(error$$1);
2140
+ };
2360
2141
 
2361
- popup.style.setProperty('opacity', '0', 'important');
2362
- show(popup, 'grid');
2363
- setTimeout(() => {
2364
- // Animate popup right after showing it
2365
- addClass(popup, params.showClass.popup); // and remove the opacity workaround
2142
+ const confirm = (instance, value) => {
2143
+ const innerParams = privateProps.innerParams.get(instance || undefined);
2366
2144
 
2367
- popup.style.removeProperty('opacity');
2368
- }, SHOW_CLASS_TIMEOUT); // 10ms in order to fix #2062
2145
+ if (innerParams.showLoaderOnConfirm) {
2146
+ showLoading();
2147
+ }
2369
2148
 
2370
- addClass([document.documentElement, document.body], swalClasses.shown);
2149
+ if (innerParams.preConfirm) {
2150
+ instance.resetValidationMessage();
2151
+ privateProps.awaitingPromise.set(instance || undefined, true); // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesnt get destroyed until the result from this preConfirm's promise is received
2371
2152
 
2372
- if (params.heightAuto && params.backdrop && !params.toast) {
2373
- addClass([document.documentElement, document.body], swalClasses['height-auto']);
2153
+ const preConfirmPromise = Promise.resolve().then(() => asPromise(innerParams.preConfirm(value, innerParams.validationMessage)));
2154
+ preConfirmPromise.then(preConfirmValue => {
2155
+ if (isVisible(getValidationMessage()) || preConfirmValue === false) {
2156
+ instance.hideLoading();
2157
+ } else {
2158
+ succeedWith(instance, typeof preConfirmValue === 'undefined' ? value : preConfirmValue);
2159
+ }
2160
+ }).catch(error$$1 => rejectWith(instance || undefined, error$$1));
2161
+ } else {
2162
+ succeedWith(instance, value);
2374
2163
  }
2375
2164
  };
2376
2165
 
2377
- const handleInputOptionsAndValue = (instance, params) => {
2378
- if (params.input === 'select' || params.input === 'radio') {
2379
- handleInputOptions(instance, params);
2380
- } else if (['text', 'email', 'number', 'tel', 'textarea'].includes(params.input) && (hasToPromiseFn(params.inputValue) || isPromise(params.inputValue))) {
2381
- showLoading(getConfirmButton());
2382
- handleInputValue(instance, params);
2166
+ const handlePopupClick = (instance, domCache, dismissWith) => {
2167
+ const innerParams = privateProps.innerParams.get(instance);
2168
+
2169
+ if (innerParams.toast) {
2170
+ handleToastClick(instance, domCache, dismissWith);
2171
+ } else {
2172
+ // Ignore click events that had mousedown on the popup but mouseup on the container
2173
+ // This can happen when the user drags a slider
2174
+ handleModalMousedown(domCache); // Ignore click events that had mousedown on the container but mouseup on the popup
2175
+
2176
+ handleContainerMousedown(domCache);
2177
+ handleModalClick(instance, domCache, dismissWith);
2383
2178
  }
2384
2179
  };
2385
- const getInputValue = (instance, innerParams) => {
2386
- const input = instance.getInput();
2387
2180
 
2388
- if (!input) {
2389
- return null;
2390
- }
2181
+ const handleToastClick = (instance, domCache, dismissWith) => {
2182
+ // Closing toast by internal click
2183
+ domCache.popup.onclick = () => {
2184
+ const innerParams = privateProps.innerParams.get(instance);
2391
2185
 
2392
- switch (innerParams.input) {
2393
- case 'checkbox':
2394
- return getCheckboxValue(input);
2186
+ if (innerParams.showConfirmButton || innerParams.showDenyButton || innerParams.showCancelButton || innerParams.showCloseButton || innerParams.timer || innerParams.input) {
2187
+ return;
2188
+ }
2395
2189
 
2396
- case 'radio':
2397
- return getRadioValue(input);
2190
+ dismissWith(DismissReason.close);
2191
+ };
2192
+ };
2398
2193
 
2399
- case 'file':
2400
- return getFileValue(input);
2194
+ let ignoreOutsideClick = false;
2401
2195
 
2402
- default:
2403
- return innerParams.inputAutoTrim ? input.value.trim() : input.value;
2404
- }
2405
- };
2196
+ const handleModalMousedown = domCache => {
2197
+ domCache.popup.onmousedown = () => {
2198
+ domCache.container.onmouseup = function (e) {
2199
+ domCache.container.onmouseup = undefined; // We only check if the mouseup target is the container because usually it doesn't
2200
+ // have any other direct children aside of the popup
2406
2201
 
2407
- const getCheckboxValue = input => input.checked ? 1 : 0;
2202
+ if (e.target === domCache.container) {
2203
+ ignoreOutsideClick = true;
2204
+ }
2205
+ };
2206
+ };
2207
+ };
2408
2208
 
2409
- const getRadioValue = input => input.checked ? input.value : null;
2209
+ const handleContainerMousedown = domCache => {
2210
+ domCache.container.onmousedown = () => {
2211
+ domCache.popup.onmouseup = function (e) {
2212
+ domCache.popup.onmouseup = undefined; // We also need to check if the mouseup target is a child of the popup
2410
2213
 
2411
- const getFileValue = input => input.files.length ? input.getAttribute('multiple') !== null ? input.files : input.files[0] : null;
2214
+ if (e.target === domCache.popup || domCache.popup.contains(e.target)) {
2215
+ ignoreOutsideClick = true;
2216
+ }
2217
+ };
2218
+ };
2219
+ };
2412
2220
 
2413
- const handleInputOptions = (instance, params) => {
2414
- const popup = getPopup();
2221
+ const handleModalClick = (instance, domCache, dismissWith) => {
2222
+ domCache.container.onclick = e => {
2223
+ const innerParams = privateProps.innerParams.get(instance);
2415
2224
 
2416
- const processInputOptions = inputOptions => populateInputOptions[params.input](popup, formatInputOptions(inputOptions), params);
2225
+ if (ignoreOutsideClick) {
2226
+ ignoreOutsideClick = false;
2227
+ return;
2228
+ }
2417
2229
 
2418
- if (hasToPromiseFn(params.inputOptions) || isPromise(params.inputOptions)) {
2419
- showLoading(getConfirmButton());
2420
- asPromise(params.inputOptions).then(inputOptions => {
2421
- instance.hideLoading();
2422
- processInputOptions(inputOptions);
2423
- });
2424
- } else if (typeof params.inputOptions === 'object') {
2425
- processInputOptions(params.inputOptions);
2426
- } else {
2427
- error("Unexpected type of inputOptions! Expected object, Map or Promise, got ".concat(typeof params.inputOptions));
2428
- }
2230
+ if (e.target === domCache.container && callIfFunction(innerParams.allowOutsideClick)) {
2231
+ dismissWith(DismissReason.backdrop);
2232
+ }
2233
+ };
2429
2234
  };
2430
2235
 
2431
- const handleInputValue = (instance, params) => {
2432
- const input = instance.getInput();
2433
- hide(input);
2434
- asPromise(params.inputValue).then(inputValue => {
2435
- input.value = params.input === 'number' ? parseFloat(inputValue) || 0 : "".concat(inputValue);
2436
- show(input);
2437
- input.focus();
2438
- instance.hideLoading();
2439
- }).catch(err => {
2440
- error("Error in inputValue promise: ".concat(err));
2441
- input.value = '';
2442
- show(input);
2443
- input.focus();
2444
- instance.hideLoading();
2445
- });
2446
- };
2236
+ /*
2237
+ * Global function to determine if SweetAlert2 popup is shown
2238
+ */
2447
2239
 
2448
- const populateInputOptions = {
2449
- select: (popup, inputOptions, params) => {
2450
- const select = getChildByClass(popup, swalClasses.select);
2240
+ const isVisible$1 = () => {
2241
+ return isVisible(getPopup());
2242
+ };
2243
+ /*
2244
+ * Global function to click 'Confirm' button
2245
+ */
2451
2246
 
2452
- const renderOption = (parent, optionLabel, optionValue) => {
2453
- const option = document.createElement('option');
2454
- option.value = optionValue;
2455
- setInnerHtml(option, optionLabel);
2456
- option.selected = isSelected(optionValue, params.inputValue);
2457
- parent.appendChild(option);
2458
- };
2247
+ const clickConfirm = () => getConfirmButton() && getConfirmButton().click();
2248
+ /*
2249
+ * Global function to click 'Deny' button
2250
+ */
2459
2251
 
2460
- inputOptions.forEach(inputOption => {
2461
- const optionValue = inputOption[0];
2462
- const optionLabel = inputOption[1]; // <optgroup> spec:
2463
- // https://www.w3.org/TR/html401/interact/forms.html#h-17.6
2464
- // "...all OPTGROUP elements must be specified directly within a SELECT element (i.e., groups may not be nested)..."
2465
- // check whether this is a <optgroup>
2252
+ const clickDeny = () => getDenyButton() && getDenyButton().click();
2253
+ /*
2254
+ * Global function to click 'Cancel' button
2255
+ */
2466
2256
 
2467
- if (Array.isArray(optionLabel)) {
2468
- // if it is an array, then it is an <optgroup>
2469
- const optgroup = document.createElement('optgroup');
2470
- optgroup.label = optionValue;
2471
- optgroup.disabled = false; // not configurable for now
2257
+ const clickCancel = () => getCancelButton() && getCancelButton().click();
2472
2258
 
2473
- select.appendChild(optgroup);
2474
- optionLabel.forEach(o => renderOption(optgroup, o[1], o[0]));
2475
- } else {
2476
- // case of <option>
2477
- renderOption(select, optionLabel, optionValue);
2478
- }
2259
+ const addKeydownHandler = (instance, globalState, innerParams, dismissWith) => {
2260
+ if (globalState.keydownTarget && globalState.keydownHandlerAdded) {
2261
+ globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, {
2262
+ capture: globalState.keydownListenerCapture
2479
2263
  });
2480
- select.focus();
2481
- },
2482
- radio: (popup, inputOptions, params) => {
2483
- const radio = getChildByClass(popup, swalClasses.radio);
2484
- inputOptions.forEach(inputOption => {
2485
- const radioValue = inputOption[0];
2486
- const radioLabel = inputOption[1];
2487
- const radioInput = document.createElement('input');
2488
- const radioLabelElement = document.createElement('label');
2489
- radioInput.type = 'radio';
2490
- radioInput.name = swalClasses.radio;
2491
- radioInput.value = radioValue;
2264
+ globalState.keydownHandlerAdded = false;
2265
+ }
2492
2266
 
2493
- if (isSelected(radioValue, params.inputValue)) {
2494
- radioInput.checked = true;
2495
- }
2267
+ if (!innerParams.toast) {
2268
+ globalState.keydownHandler = e => keydownHandler(instance, e, dismissWith);
2496
2269
 
2497
- const label = document.createElement('span');
2498
- setInnerHtml(label, radioLabel);
2499
- label.className = swalClasses.label;
2500
- radioLabelElement.appendChild(radioInput);
2501
- radioLabelElement.appendChild(label);
2502
- radio.appendChild(radioLabelElement);
2270
+ globalState.keydownTarget = innerParams.keydownListenerCapture ? window : getPopup();
2271
+ globalState.keydownListenerCapture = innerParams.keydownListenerCapture;
2272
+ globalState.keydownTarget.addEventListener('keydown', globalState.keydownHandler, {
2273
+ capture: globalState.keydownListenerCapture
2503
2274
  });
2504
- const radios = radio.querySelectorAll('input');
2275
+ globalState.keydownHandlerAdded = true;
2276
+ }
2277
+ }; // Focus handling
2505
2278
 
2506
- if (radios.length) {
2507
- radios[0].focus();
2279
+ const setFocus = (innerParams, index, increment) => {
2280
+ const focusableElements = getFocusableElements(); // search for visible elements and select the next possible match
2281
+
2282
+ if (focusableElements.length) {
2283
+ index = index + increment; // rollover to first item
2284
+
2285
+ if (index === focusableElements.length) {
2286
+ index = 0; // go to last item
2287
+ } else if (index === -1) {
2288
+ index = focusableElements.length - 1;
2508
2289
  }
2509
- }
2290
+
2291
+ return focusableElements[index].focus();
2292
+ } // no visible focusable elements, focus the popup
2293
+
2294
+
2295
+ getPopup().focus();
2510
2296
  };
2511
- /**
2512
- * Converts `inputOptions` into an array of `[value, label]`s
2513
- * @param inputOptions
2514
- */
2297
+ const arrowKeysNextButton = ['ArrowRight', 'ArrowDown'];
2298
+ const arrowKeysPreviousButton = ['ArrowLeft', 'ArrowUp'];
2515
2299
 
2516
- const formatInputOptions = inputOptions => {
2517
- const result = [];
2300
+ const keydownHandler = (instance, e, dismissWith) => {
2301
+ const innerParams = privateProps.innerParams.get(instance);
2518
2302
 
2519
- if (typeof Map !== 'undefined' && inputOptions instanceof Map) {
2520
- inputOptions.forEach((value, key) => {
2521
- let valueFormatted = value;
2303
+ if (!innerParams) {
2304
+ return; // This instance has already been destroyed
2305
+ }
2522
2306
 
2523
- if (typeof valueFormatted === 'object') {
2524
- // case of <optgroup>
2525
- valueFormatted = formatInputOptions(valueFormatted);
2526
- }
2307
+ if (innerParams.stopKeydownPropagation) {
2308
+ e.stopPropagation();
2309
+ } // ENTER
2527
2310
 
2528
- result.push([key, valueFormatted]);
2529
- });
2530
- } else {
2531
- Object.keys(inputOptions).forEach(key => {
2532
- let valueFormatted = inputOptions[key];
2533
2311
 
2534
- if (typeof valueFormatted === 'object') {
2535
- // case of <optgroup>
2536
- valueFormatted = formatInputOptions(valueFormatted);
2537
- }
2312
+ if (e.key === 'Enter') {
2313
+ handleEnter(instance, e, innerParams); // TAB
2314
+ } else if (e.key === 'Tab') {
2315
+ handleTab(e, innerParams); // ARROWS - switch focus between buttons
2316
+ } else if ([...arrowKeysNextButton, ...arrowKeysPreviousButton].includes(e.key)) {
2317
+ handleArrows(e.key); // ESC
2318
+ } else if (e.key === 'Escape') {
2319
+ handleEsc(e, innerParams, dismissWith);
2320
+ }
2321
+ };
2538
2322
 
2539
- result.push([key, valueFormatted]);
2540
- });
2323
+ const handleEnter = (instance, e, innerParams) => {
2324
+ // #720 #721
2325
+ if (e.isComposing) {
2326
+ return;
2541
2327
  }
2542
2328
 
2543
- return result;
2544
- };
2329
+ if (e.target && instance.getInput() && e.target.outerHTML === instance.getInput().outerHTML) {
2330
+ if (['textarea', 'file'].includes(innerParams.input)) {
2331
+ return; // do not submit
2332
+ }
2545
2333
 
2546
- const isSelected = (optionValue, inputValue) => {
2547
- return inputValue && inputValue.toString() === optionValue.toString();
2334
+ clickConfirm();
2335
+ e.preventDefault();
2336
+ }
2548
2337
  };
2549
2338
 
2550
- const handleConfirmButtonClick = instance => {
2551
- const innerParams = privateProps.innerParams.get(instance);
2552
- instance.disableButtons();
2339
+ const handleTab = (e, innerParams) => {
2340
+ const targetElement = e.target;
2341
+ const focusableElements = getFocusableElements();
2342
+ let btnIndex = -1;
2553
2343
 
2554
- if (innerParams.input) {
2555
- handleConfirmOrDenyWithInput(instance, 'confirm');
2344
+ for (let i = 0; i < focusableElements.length; i++) {
2345
+ if (targetElement === focusableElements[i]) {
2346
+ btnIndex = i;
2347
+ break;
2348
+ }
2349
+ }
2350
+
2351
+ if (!e.shiftKey) {
2352
+ // Cycle to the next button
2353
+ setFocus(innerParams, btnIndex, 1);
2556
2354
  } else {
2557
- confirm(instance, true);
2355
+ // Cycle to the prev button
2356
+ setFocus(innerParams, btnIndex, -1);
2558
2357
  }
2358
+
2359
+ e.stopPropagation();
2360
+ e.preventDefault();
2559
2361
  };
2560
- const handleDenyButtonClick = instance => {
2561
- const innerParams = privateProps.innerParams.get(instance);
2562
- instance.disableButtons();
2563
2362
 
2564
- if (innerParams.returnInputValueOnDeny) {
2565
- handleConfirmOrDenyWithInput(instance, 'deny');
2566
- } else {
2567
- deny(instance, false);
2363
+ const handleArrows = key => {
2364
+ const confirmButton = getConfirmButton();
2365
+ const denyButton = getDenyButton();
2366
+ const cancelButton = getCancelButton();
2367
+
2368
+ if (![confirmButton, denyButton, cancelButton].includes(document.activeElement)) {
2369
+ return;
2370
+ }
2371
+
2372
+ const sibling = arrowKeysNextButton.includes(key) ? 'nextElementSibling' : 'previousElementSibling';
2373
+ const buttonToFocus = document.activeElement[sibling];
2374
+
2375
+ if (buttonToFocus) {
2376
+ buttonToFocus.focus();
2568
2377
  }
2569
2378
  };
2570
- const handleCancelButtonClick = (instance, dismissWith) => {
2571
- instance.disableButtons();
2572
- dismissWith(DismissReason.cancel);
2379
+
2380
+ const handleEsc = (e, innerParams, dismissWith) => {
2381
+ if (callIfFunction(innerParams.allowEscapeKey)) {
2382
+ e.preventDefault();
2383
+ dismissWith(DismissReason.esc);
2384
+ }
2573
2385
  };
2574
2386
 
2575
- const handleConfirmOrDenyWithInput = (instance, type
2576
- /* 'confirm' | 'deny' */
2577
- ) => {
2578
- const innerParams = privateProps.innerParams.get(instance);
2579
- const inputValue = getInputValue(instance, innerParams);
2387
+ const isJqueryElement = elem => typeof elem === 'object' && elem.jquery;
2580
2388
 
2581
- if (innerParams.inputValidator) {
2582
- handleInputValidator(instance, inputValue, type);
2583
- } else if (!instance.getInput().checkValidity()) {
2584
- instance.enableButtons();
2585
- instance.showValidationMessage(innerParams.validationMessage);
2586
- } else if (type === 'deny') {
2587
- deny(instance, inputValue);
2389
+ const isElement = elem => elem instanceof Element || isJqueryElement(elem);
2390
+
2391
+ const argsToParams = args => {
2392
+ const params = {};
2393
+
2394
+ if (typeof args[0] === 'object' && !isElement(args[0])) {
2395
+ Object.assign(params, args[0]);
2588
2396
  } else {
2589
- confirm(instance, inputValue);
2397
+ ['title', 'html', 'icon'].forEach((name, index) => {
2398
+ const arg = args[index];
2399
+
2400
+ if (typeof arg === 'string' || isElement(arg)) {
2401
+ params[name] = arg;
2402
+ } else if (arg !== undefined) {
2403
+ error("Unexpected type of ".concat(name, "! Expected \"string\" or \"Element\", got ").concat(typeof arg));
2404
+ }
2405
+ });
2590
2406
  }
2407
+
2408
+ return params;
2591
2409
  };
2592
2410
 
2593
- const handleInputValidator = (instance, inputValue, type
2594
- /* 'confirm' | 'deny' */
2595
- ) => {
2596
- const innerParams = privateProps.innerParams.get(instance);
2597
- instance.disableInput();
2598
- const validationPromise = Promise.resolve().then(() => asPromise(innerParams.inputValidator(inputValue, innerParams.validationMessage)));
2599
- validationPromise.then(validationMessage => {
2600
- instance.enableButtons();
2601
- instance.enableInput();
2411
+ function fire() {
2412
+ const Swal = this;
2602
2413
 
2603
- if (validationMessage) {
2604
- instance.showValidationMessage(validationMessage);
2605
- } else if (type === 'deny') {
2606
- deny(instance, inputValue);
2607
- } else {
2608
- confirm(instance, inputValue);
2609
- }
2610
- });
2611
- };
2414
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
2415
+ args[_key] = arguments[_key];
2416
+ }
2612
2417
 
2613
- const deny = (instance, value) => {
2614
- const innerParams = privateProps.innerParams.get(instance || undefined);
2418
+ return new Swal(...args);
2419
+ }
2420
+
2421
+ /**
2422
+ * Returns an extended version of `Swal` containing `params` as defaults.
2423
+ * Useful for reusing Swal configuration.
2424
+ *
2425
+ * For example:
2426
+ *
2427
+ * Before:
2428
+ * const textPromptOptions = { input: 'text', showCancelButton: true }
2429
+ * const {value: firstName} = await Swal.fire({ ...textPromptOptions, title: 'What is your first name?' })
2430
+ * const {value: lastName} = await Swal.fire({ ...textPromptOptions, title: 'What is your last name?' })
2431
+ *
2432
+ * After:
2433
+ * const TextPrompt = Swal.mixin({ input: 'text', showCancelButton: true })
2434
+ * const {value: firstName} = await TextPrompt('What is your first name?')
2435
+ * const {value: lastName} = await TextPrompt('What is your last name?')
2436
+ *
2437
+ * @param mixinParams
2438
+ */
2439
+ function mixin(mixinParams) {
2440
+ class MixinSwal extends this {
2441
+ _main(params, priorityMixinParams) {
2442
+ return super._main(params, Object.assign({}, mixinParams, priorityMixinParams));
2443
+ }
2615
2444
 
2616
- if (innerParams.showLoaderOnDeny) {
2617
- showLoading(getDenyButton());
2618
2445
  }
2619
2446
 
2620
- if (innerParams.preDeny) {
2621
- privateProps.awaitingPromise.set(instance || undefined, true); // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesnt get destroyed until the result from this preDeny's promise is received
2447
+ return MixinSwal;
2448
+ }
2622
2449
 
2623
- const preDenyPromise = Promise.resolve().then(() => asPromise(innerParams.preDeny(value, innerParams.validationMessage)));
2624
- preDenyPromise.then(preDenyValue => {
2625
- if (preDenyValue === false) {
2626
- instance.hideLoading();
2627
- } else {
2628
- instance.closePopup({
2629
- isDenied: true,
2630
- value: typeof preDenyValue === 'undefined' ? value : preDenyValue
2631
- });
2632
- }
2633
- }).catch(error$$1 => rejectWith(instance || undefined, error$$1));
2634
- } else {
2635
- instance.closePopup({
2636
- isDenied: true,
2637
- value
2638
- });
2639
- }
2640
- };
2450
+ /**
2451
+ * If `timer` parameter is set, returns number of milliseconds of timer remained.
2452
+ * Otherwise, returns undefined.
2453
+ */
2641
2454
 
2642
- const succeedWith = (instance, value) => {
2643
- instance.closePopup({
2644
- isConfirmed: true,
2645
- value
2646
- });
2455
+ const getTimerLeft = () => {
2456
+ return globalState.timeout && globalState.timeout.getTimerLeft();
2647
2457
  };
2458
+ /**
2459
+ * Stop timer. Returns number of milliseconds of timer remained.
2460
+ * If `timer` parameter isn't set, returns undefined.
2461
+ */
2648
2462
 
2649
- const rejectWith = (instance, error$$1) => {
2650
- instance.rejectPromise(error$$1);
2463
+ const stopTimer = () => {
2464
+ if (globalState.timeout) {
2465
+ stopTimerProgressBar();
2466
+ return globalState.timeout.stop();
2467
+ }
2651
2468
  };
2469
+ /**
2470
+ * Resume timer. Returns number of milliseconds of timer remained.
2471
+ * If `timer` parameter isn't set, returns undefined.
2472
+ */
2652
2473
 
2653
- const confirm = (instance, value) => {
2654
- const innerParams = privateProps.innerParams.get(instance || undefined);
2655
-
2656
- if (innerParams.showLoaderOnConfirm) {
2657
- showLoading();
2474
+ const resumeTimer = () => {
2475
+ if (globalState.timeout) {
2476
+ const remaining = globalState.timeout.start();
2477
+ animateTimerProgressBar(remaining);
2478
+ return remaining;
2658
2479
  }
2480
+ };
2481
+ /**
2482
+ * Resume timer. Returns number of milliseconds of timer remained.
2483
+ * If `timer` parameter isn't set, returns undefined.
2484
+ */
2659
2485
 
2660
- if (innerParams.preConfirm) {
2661
- instance.resetValidationMessage();
2662
- privateProps.awaitingPromise.set(instance || undefined, true); // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesnt get destroyed until the result from this preConfirm's promise is received
2486
+ const toggleTimer = () => {
2487
+ const timer = globalState.timeout;
2488
+ return timer && (timer.running ? stopTimer() : resumeTimer());
2489
+ };
2490
+ /**
2491
+ * Increase timer. Returns number of milliseconds of an updated timer.
2492
+ * If `timer` parameter isn't set, returns undefined.
2493
+ */
2663
2494
 
2664
- const preConfirmPromise = Promise.resolve().then(() => asPromise(innerParams.preConfirm(value, innerParams.validationMessage)));
2665
- preConfirmPromise.then(preConfirmValue => {
2666
- if (isVisible(getValidationMessage()) || preConfirmValue === false) {
2667
- instance.hideLoading();
2668
- } else {
2669
- succeedWith(instance, typeof preConfirmValue === 'undefined' ? value : preConfirmValue);
2670
- }
2671
- }).catch(error$$1 => rejectWith(instance || undefined, error$$1));
2672
- } else {
2673
- succeedWith(instance, value);
2495
+ const increaseTimer = n => {
2496
+ if (globalState.timeout) {
2497
+ const remaining = globalState.timeout.increase(n);
2498
+ animateTimerProgressBar(remaining, true);
2499
+ return remaining;
2674
2500
  }
2675
2501
  };
2502
+ /**
2503
+ * Check if timer is running. Returns true if timer is running
2504
+ * or false if timer is paused or stopped.
2505
+ * If `timer` parameter isn't set, returns undefined
2506
+ */
2676
2507
 
2677
- const addKeydownHandler = (instance, globalState, innerParams, dismissWith) => {
2678
- if (globalState.keydownTarget && globalState.keydownHandlerAdded) {
2679
- globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, {
2680
- capture: globalState.keydownListenerCapture
2681
- });
2682
- globalState.keydownHandlerAdded = false;
2683
- }
2508
+ const isTimerRunning = () => {
2509
+ return globalState.timeout && globalState.timeout.isRunning();
2510
+ };
2684
2511
 
2685
- if (!innerParams.toast) {
2686
- globalState.keydownHandler = e => keydownHandler(instance, e, dismissWith);
2512
+ let bodyClickListenerAdded = false;
2513
+ const clickHandlers = {};
2514
+ function bindClickHandler() {
2515
+ let attr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'data-swal-template';
2516
+ clickHandlers[attr] = this;
2687
2517
 
2688
- globalState.keydownTarget = innerParams.keydownListenerCapture ? window : getPopup();
2689
- globalState.keydownListenerCapture = innerParams.keydownListenerCapture;
2690
- globalState.keydownTarget.addEventListener('keydown', globalState.keydownHandler, {
2691
- capture: globalState.keydownListenerCapture
2692
- });
2693
- globalState.keydownHandlerAdded = true;
2518
+ if (!bodyClickListenerAdded) {
2519
+ document.body.addEventListener('click', bodyClickListener);
2520
+ bodyClickListenerAdded = true;
2694
2521
  }
2695
- }; // Focus handling
2696
-
2697
- const setFocus = (innerParams, index, increment) => {
2698
- const focusableElements = getFocusableElements(); // search for visible elements and select the next possible match
2522
+ }
2699
2523
 
2700
- if (focusableElements.length) {
2701
- index = index + increment; // rollover to first item
2524
+ const bodyClickListener = event => {
2525
+ for (let el = event.target; el && el !== document; el = el.parentNode) {
2526
+ for (const attr in clickHandlers) {
2527
+ const template = el.getAttribute(attr);
2702
2528
 
2703
- if (index === focusableElements.length) {
2704
- index = 0; // go to last item
2705
- } else if (index === -1) {
2706
- index = focusableElements.length - 1;
2529
+ if (template) {
2530
+ clickHandlers[attr].fire({
2531
+ template
2532
+ });
2533
+ return;
2534
+ }
2707
2535
  }
2708
-
2709
- return focusableElements[index].focus();
2710
- } // no visible focusable elements, focus the popup
2711
-
2712
-
2713
- getPopup().focus();
2536
+ }
2714
2537
  };
2715
- const arrowKeysNextButton = ['ArrowRight', 'ArrowDown'];
2716
- const arrowKeysPreviousButton = ['ArrowLeft', 'ArrowUp'];
2717
2538
 
2718
- const keydownHandler = (instance, e, dismissWith) => {
2719
- const innerParams = privateProps.innerParams.get(instance);
2720
2539
 
2721
- if (!innerParams) {
2722
- return; // This instance has already been destroyed
2723
- }
2724
2540
 
2725
- if (innerParams.stopKeydownPropagation) {
2726
- e.stopPropagation();
2727
- } // ENTER
2541
+ var staticMethods = /*#__PURE__*/Object.freeze({
2542
+ isValidParameter: isValidParameter,
2543
+ isUpdatableParameter: isUpdatableParameter,
2544
+ isDeprecatedParameter: isDeprecatedParameter,
2545
+ argsToParams: argsToParams,
2546
+ isVisible: isVisible$1,
2547
+ clickConfirm: clickConfirm,
2548
+ clickDeny: clickDeny,
2549
+ clickCancel: clickCancel,
2550
+ getContainer: getContainer,
2551
+ getPopup: getPopup,
2552
+ getTitle: getTitle,
2553
+ getHtmlContainer: getHtmlContainer,
2554
+ getImage: getImage,
2555
+ getIcon: getIcon,
2556
+ getInputLabel: getInputLabel,
2557
+ getCloseButton: getCloseButton,
2558
+ getActions: getActions,
2559
+ getConfirmButton: getConfirmButton,
2560
+ getDenyButton: getDenyButton,
2561
+ getCancelButton: getCancelButton,
2562
+ getLoader: getLoader,
2563
+ getFooter: getFooter,
2564
+ getTimerProgressBar: getTimerProgressBar,
2565
+ getFocusableElements: getFocusableElements,
2566
+ getValidationMessage: getValidationMessage,
2567
+ isLoading: isLoading,
2568
+ fire: fire,
2569
+ mixin: mixin,
2570
+ showLoading: showLoading,
2571
+ enableLoading: showLoading,
2572
+ getTimerLeft: getTimerLeft,
2573
+ stopTimer: stopTimer,
2574
+ resumeTimer: resumeTimer,
2575
+ toggleTimer: toggleTimer,
2576
+ increaseTimer: increaseTimer,
2577
+ isTimerRunning: isTimerRunning,
2578
+ bindClickHandler: bindClickHandler
2579
+ });
2728
2580
 
2581
+ /**
2582
+ * Hides loader and shows back the button which was hidden by .showLoading()
2583
+ */
2729
2584
 
2730
- if (e.key === 'Enter') {
2731
- handleEnter(instance, e, innerParams); // TAB
2732
- } else if (e.key === 'Tab') {
2733
- handleTab(e, innerParams); // ARROWS - switch focus between buttons
2734
- } else if ([...arrowKeysNextButton, ...arrowKeysPreviousButton].includes(e.key)) {
2735
- handleArrows(e.key); // ESC
2736
- } else if (e.key === 'Escape') {
2737
- handleEsc(e, innerParams, dismissWith);
2738
- }
2739
- };
2585
+ function hideLoading() {
2586
+ // do nothing if popup is closed
2587
+ const innerParams = privateProps.innerParams.get(this);
2740
2588
 
2741
- const handleEnter = (instance, e, innerParams) => {
2742
- // #720 #721
2743
- if (e.isComposing) {
2589
+ if (!innerParams) {
2744
2590
  return;
2745
2591
  }
2746
2592
 
2747
- if (e.target && instance.getInput() && e.target.outerHTML === instance.getInput().outerHTML) {
2748
- if (['textarea', 'file'].includes(innerParams.input)) {
2749
- return; // do not submit
2750
- }
2593
+ const domCache = privateProps.domCache.get(this);
2594
+ hide(domCache.loader);
2751
2595
 
2752
- clickConfirm();
2753
- e.preventDefault();
2596
+ if (isToast()) {
2597
+ if (innerParams.icon) {
2598
+ show(getIcon());
2599
+ }
2600
+ } else {
2601
+ showRelatedButton(domCache);
2754
2602
  }
2755
- };
2756
2603
 
2757
- const handleTab = (e, innerParams) => {
2758
- const targetElement = e.target;
2759
- const focusableElements = getFocusableElements();
2760
- let btnIndex = -1;
2604
+ removeClass([domCache.popup, domCache.actions], swalClasses.loading);
2605
+ domCache.popup.removeAttribute('aria-busy');
2606
+ domCache.popup.removeAttribute('data-loading');
2607
+ domCache.confirmButton.disabled = false;
2608
+ domCache.denyButton.disabled = false;
2609
+ domCache.cancelButton.disabled = false;
2610
+ }
2761
2611
 
2762
- for (let i = 0; i < focusableElements.length; i++) {
2763
- if (targetElement === focusableElements[i]) {
2764
- btnIndex = i;
2765
- break;
2766
- }
2767
- }
2612
+ const showRelatedButton = domCache => {
2613
+ const buttonToReplace = domCache.popup.getElementsByClassName(domCache.loader.getAttribute('data-button-to-replace'));
2768
2614
 
2769
- if (!e.shiftKey) {
2770
- // Cycle to the next button
2771
- setFocus(innerParams, btnIndex, 1);
2772
- } else {
2773
- // Cycle to the prev button
2774
- setFocus(innerParams, btnIndex, -1);
2615
+ if (buttonToReplace.length) {
2616
+ show(buttonToReplace[0], 'inline-block');
2617
+ } else if (allButtonsAreHidden()) {
2618
+ hide(domCache.actions);
2775
2619
  }
2776
-
2777
- e.stopPropagation();
2778
- e.preventDefault();
2779
2620
  };
2780
2621
 
2781
- const handleArrows = key => {
2782
- const confirmButton = getConfirmButton();
2783
- const denyButton = getDenyButton();
2784
- const cancelButton = getCancelButton();
2622
+ function getInput$1(instance) {
2623
+ const innerParams = privateProps.innerParams.get(instance || this);
2624
+ const domCache = privateProps.domCache.get(instance || this);
2785
2625
 
2786
- if (![confirmButton, denyButton, cancelButton].includes(document.activeElement)) {
2787
- return;
2626
+ if (!domCache) {
2627
+ return null;
2788
2628
  }
2789
2629
 
2790
- const sibling = arrowKeysNextButton.includes(key) ? 'nextElementSibling' : 'previousElementSibling';
2791
- const buttonToFocus = document.activeElement[sibling];
2792
-
2793
- if (buttonToFocus) {
2794
- buttonToFocus.focus();
2795
- }
2796
- };
2630
+ return getInput(domCache.popup, innerParams.input);
2631
+ }
2797
2632
 
2798
- const handleEsc = (e, innerParams, dismissWith) => {
2799
- if (callIfFunction(innerParams.allowEscapeKey)) {
2800
- e.preventDefault();
2801
- dismissWith(DismissReason.esc);
2802
- }
2633
+ /**
2634
+ * This module contains `WeakMap`s for each effectively-"private property" that a `Swal` has.
2635
+ * For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
2636
+ * This is the approach that Babel will probably take to implement private methods/fields
2637
+ * https://github.com/tc39/proposal-private-methods
2638
+ * https://github.com/babel/babel/pull/7555
2639
+ * Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
2640
+ * then we can use that language feature.
2641
+ */
2642
+ var privateMethods = {
2643
+ swalPromiseResolve: new WeakMap(),
2644
+ swalPromiseReject: new WeakMap()
2803
2645
  };
2804
2646
 
2805
- const handlePopupClick = (instance, domCache, dismissWith) => {
2806
- const innerParams = privateProps.innerParams.get(instance);
2647
+ /*
2648
+ * Instance method to close sweetAlert
2649
+ */
2807
2650
 
2808
- if (innerParams.toast) {
2809
- handleToastClick(instance, domCache, dismissWith);
2651
+ function removePopupAndResetState(instance, container, returnFocus, didClose) {
2652
+ if (isToast()) {
2653
+ triggerDidCloseAndDispose(instance, didClose);
2810
2654
  } else {
2811
- // Ignore click events that had mousedown on the popup but mouseup on the container
2812
- // This can happen when the user drags a slider
2813
- handleModalMousedown(domCache); // Ignore click events that had mousedown on the container but mouseup on the popup
2814
-
2815
- handleContainerMousedown(domCache);
2816
- handleModalClick(instance, domCache, dismissWith);
2655
+ restoreActiveElement(returnFocus).then(() => triggerDidCloseAndDispose(instance, didClose));
2656
+ globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, {
2657
+ capture: globalState.keydownListenerCapture
2658
+ });
2659
+ globalState.keydownHandlerAdded = false;
2817
2660
  }
2818
- };
2819
-
2820
- const handleToastClick = (instance, domCache, dismissWith) => {
2821
- // Closing toast by internal click
2822
- domCache.popup.onclick = () => {
2823
- const innerParams = privateProps.innerParams.get(instance);
2824
-
2825
- if (innerParams.showConfirmButton || innerParams.showDenyButton || innerParams.showCancelButton || innerParams.showCloseButton || innerParams.timer || innerParams.input) {
2826
- return;
2827
- }
2828
-
2829
- dismissWith(DismissReason.close);
2830
- };
2831
- };
2832
2661
 
2833
- let ignoreOutsideClick = false;
2662
+ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); // workaround for #2088
2663
+ // for some reason removing the container in Safari will scroll the document to bottom
2834
2664
 
2835
- const handleModalMousedown = domCache => {
2836
- domCache.popup.onmousedown = () => {
2837
- domCache.container.onmouseup = function (e) {
2838
- domCache.container.onmouseup = undefined; // We only check if the mouseup target is the container because usually it doesn't
2839
- // have any other direct children aside of the popup
2665
+ if (isSafari) {
2666
+ container.setAttribute('style', 'display:none !important');
2667
+ container.removeAttribute('class');
2668
+ container.innerHTML = '';
2669
+ } else {
2670
+ container.remove();
2671
+ }
2840
2672
 
2841
- if (e.target === domCache.container) {
2842
- ignoreOutsideClick = true;
2843
- }
2844
- };
2845
- };
2846
- };
2673
+ if (isModal()) {
2674
+ undoScrollbar();
2675
+ undoIOSfix();
2676
+ unsetAriaHidden();
2677
+ }
2847
2678
 
2848
- const handleContainerMousedown = domCache => {
2849
- domCache.container.onmousedown = () => {
2850
- domCache.popup.onmouseup = function (e) {
2851
- domCache.popup.onmouseup = undefined; // We also need to check if the mouseup target is a child of the popup
2679
+ removeBodyClasses();
2680
+ }
2852
2681
 
2853
- if (e.target === domCache.popup || domCache.popup.contains(e.target)) {
2854
- ignoreOutsideClick = true;
2855
- }
2856
- };
2857
- };
2858
- };
2682
+ function removeBodyClasses() {
2683
+ removeClass([document.documentElement, document.body], [swalClasses.shown, swalClasses['height-auto'], swalClasses['no-backdrop'], swalClasses['toast-shown']]);
2684
+ }
2859
2685
 
2860
- const handleModalClick = (instance, domCache, dismissWith) => {
2861
- domCache.container.onclick = e => {
2862
- const innerParams = privateProps.innerParams.get(instance);
2686
+ function close(resolveValue) {
2687
+ resolveValue = prepareResolveValue(resolveValue);
2688
+ const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this);
2689
+ const didClose = triggerClosePopup(this);
2863
2690
 
2864
- if (ignoreOutsideClick) {
2865
- ignoreOutsideClick = false;
2866
- return;
2691
+ if (this.isAwaitingPromise()) {
2692
+ // A swal awaiting for a promise (after a click on Confirm or Deny) cannot be dismissed anymore #2335
2693
+ if (!resolveValue.isDismissed) {
2694
+ handleAwaitingPromise(this);
2695
+ swalPromiseResolve(resolveValue);
2867
2696
  }
2697
+ } else if (didClose) {
2698
+ // Resolve Swal promise
2699
+ swalPromiseResolve(resolveValue);
2700
+ }
2701
+ }
2702
+ function isAwaitingPromise() {
2703
+ return !!privateProps.awaitingPromise.get(this);
2704
+ }
2868
2705
 
2869
- if (e.target === domCache.container && callIfFunction(innerParams.allowOutsideClick)) {
2870
- dismissWith(DismissReason.backdrop);
2871
- }
2872
- };
2873
- };
2706
+ const triggerClosePopup = instance => {
2707
+ const popup = getPopup();
2874
2708
 
2875
- function _main(userParams) {
2876
- let mixinParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2877
- showWarningsForParams(Object.assign({}, mixinParams, userParams));
2709
+ if (!popup) {
2710
+ return false;
2711
+ }
2878
2712
 
2879
- if (globalState.currentInstance) {
2880
- globalState.currentInstance._destroy();
2713
+ const innerParams = privateProps.innerParams.get(instance);
2881
2714
 
2882
- if (isModal()) {
2883
- unsetAriaHidden();
2884
- }
2715
+ if (!innerParams || hasClass(popup, innerParams.hideClass.popup)) {
2716
+ return false;
2885
2717
  }
2886
2718
 
2887
- globalState.currentInstance = this;
2888
- const innerParams = prepareParams(userParams, mixinParams);
2889
- setParameters(innerParams);
2890
- Object.freeze(innerParams); // clear the previous timer
2891
-
2892
- if (globalState.timeout) {
2893
- globalState.timeout.stop();
2894
- delete globalState.timeout;
2895
- } // clear the restore focus timeout
2719
+ removeClass(popup, innerParams.showClass.popup);
2720
+ addClass(popup, innerParams.hideClass.popup);
2721
+ const backdrop = getContainer();
2722
+ removeClass(backdrop, innerParams.showClass.backdrop);
2723
+ addClass(backdrop, innerParams.hideClass.backdrop);
2724
+ handlePopupAnimation(instance, popup, innerParams);
2725
+ return true;
2726
+ };
2896
2727
 
2728
+ function rejectPromise(error) {
2729
+ const rejectPromise = privateMethods.swalPromiseReject.get(this);
2730
+ handleAwaitingPromise(this);
2897
2731
 
2898
- clearTimeout(globalState.restoreFocusTimeout);
2899
- const domCache = populateDomCache(this);
2900
- render(this, innerParams);
2901
- privateProps.innerParams.set(this, innerParams);
2902
- return swalPromise(this, domCache, innerParams);
2732
+ if (rejectPromise) {
2733
+ // Reject Swal promise
2734
+ rejectPromise(error);
2735
+ }
2903
2736
  }
2904
2737
 
2905
- const prepareParams = (userParams, mixinParams) => {
2906
- const templateParams = getTemplateParams(userParams);
2907
- const params = Object.assign({}, defaultParams, mixinParams, templateParams, userParams); // precedence is described in #2131
2738
+ const handleAwaitingPromise = instance => {
2739
+ if (instance.isAwaitingPromise()) {
2740
+ privateProps.awaitingPromise.delete(instance); // The instance might have been previously partly destroyed, we must resume the destroy process in this case #2335
2908
2741
 
2909
- params.showClass = Object.assign({}, defaultParams.showClass, params.showClass);
2910
- params.hideClass = Object.assign({}, defaultParams.hideClass, params.hideClass);
2911
- return params;
2742
+ if (!privateProps.innerParams.get(instance)) {
2743
+ instance._destroy();
2744
+ }
2745
+ }
2912
2746
  };
2913
2747
 
2914
- const swalPromise = (instance, domCache, innerParams) => {
2915
- return new Promise((resolve, reject) => {
2916
- // functions to handle all closings/dismissals
2917
- const dismissWith = dismiss => {
2918
- instance.closePopup({
2919
- isDismissed: true,
2920
- dismiss
2921
- });
2748
+ const prepareResolveValue = resolveValue => {
2749
+ // When user calls Swal.close()
2750
+ if (typeof resolveValue === 'undefined') {
2751
+ return {
2752
+ isConfirmed: false,
2753
+ isDenied: false,
2754
+ isDismissed: true
2922
2755
  };
2756
+ }
2923
2757
 
2924
- privateMethods.swalPromiseResolve.set(instance, resolve);
2925
- privateMethods.swalPromiseReject.set(instance, reject);
2758
+ return Object.assign({
2759
+ isConfirmed: false,
2760
+ isDenied: false,
2761
+ isDismissed: false
2762
+ }, resolveValue);
2763
+ };
2926
2764
 
2927
- domCache.confirmButton.onclick = () => handleConfirmButtonClick(instance);
2765
+ const handlePopupAnimation = (instance, popup, innerParams) => {
2766
+ const container = getContainer(); // If animation is supported, animate
2928
2767
 
2929
- domCache.denyButton.onclick = () => handleDenyButtonClick(instance);
2768
+ const animationIsSupported = animationEndEvent && hasCssAnimation(popup);
2930
2769
 
2931
- domCache.cancelButton.onclick = () => handleCancelButtonClick(instance, dismissWith);
2770
+ if (typeof innerParams.willClose === 'function') {
2771
+ innerParams.willClose(popup);
2772
+ }
2932
2773
 
2933
- domCache.closeButton.onclick = () => dismissWith(DismissReason.close);
2774
+ if (animationIsSupported) {
2775
+ animatePopup(instance, popup, container, innerParams.returnFocus, innerParams.didClose);
2776
+ } else {
2777
+ // Otherwise, remove immediately
2778
+ removePopupAndResetState(instance, container, innerParams.returnFocus, innerParams.didClose);
2779
+ }
2780
+ };
2934
2781
 
2935
- handlePopupClick(instance, domCache, dismissWith);
2936
- addKeydownHandler(instance, globalState, innerParams, dismissWith);
2937
- handleInputOptionsAndValue(instance, innerParams);
2938
- openPopup(innerParams);
2939
- setupTimer(globalState, innerParams, dismissWith);
2940
- initFocus(domCache, innerParams); // Scroll container to top on open (#1247, #1946)
2782
+ const animatePopup = (instance, popup, container, returnFocus, didClose) => {
2783
+ globalState.swalCloseEventFinishedCallback = removePopupAndResetState.bind(null, instance, container, returnFocus, didClose);
2784
+ popup.addEventListener(animationEndEvent, function (e) {
2785
+ if (e.target === popup) {
2786
+ globalState.swalCloseEventFinishedCallback();
2787
+ delete globalState.swalCloseEventFinishedCallback;
2788
+ }
2789
+ });
2790
+ };
2791
+
2792
+ const triggerDidCloseAndDispose = (instance, didClose) => {
2793
+ setTimeout(() => {
2794
+ if (typeof didClose === 'function') {
2795
+ didClose.bind(instance.params)();
2796
+ }
2941
2797
 
2942
- setTimeout(() => {
2943
- domCache.container.scrollTop = 0;
2944
- });
2798
+ instance._destroy();
2945
2799
  });
2946
2800
  };
2947
2801
 
2948
- const populateDomCache = instance => {
2949
- const domCache = {
2950
- popup: getPopup(),
2951
- container: getContainer(),
2952
- actions: getActions(),
2953
- confirmButton: getConfirmButton(),
2954
- denyButton: getDenyButton(),
2955
- cancelButton: getCancelButton(),
2956
- loader: getLoader(),
2957
- closeButton: getCloseButton(),
2958
- validationMessage: getValidationMessage(),
2959
- progressSteps: getProgressSteps()
2960
- };
2961
- privateProps.domCache.set(instance, domCache);
2962
- return domCache;
2963
- };
2802
+ function setButtonsDisabled(instance, buttons, disabled) {
2803
+ const domCache = privateProps.domCache.get(instance);
2804
+ buttons.forEach(button => {
2805
+ domCache[button].disabled = disabled;
2806
+ });
2807
+ }
2964
2808
 
2965
- const setupTimer = (globalState$$1, innerParams, dismissWith) => {
2966
- const timerProgressBar = getTimerProgressBar();
2967
- hide(timerProgressBar);
2809
+ function setInputDisabled(input, disabled) {
2810
+ if (!input) {
2811
+ return false;
2812
+ }
2968
2813
 
2969
- if (innerParams.timer) {
2970
- globalState$$1.timeout = new Timer(() => {
2971
- dismissWith('timer');
2972
- delete globalState$$1.timeout;
2973
- }, innerParams.timer);
2814
+ if (input.type === 'radio') {
2815
+ const radiosContainer = input.parentNode.parentNode;
2816
+ const radios = radiosContainer.querySelectorAll('input');
2974
2817
 
2975
- if (innerParams.timerProgressBar) {
2976
- show(timerProgressBar);
2977
- setTimeout(() => {
2978
- if (globalState$$1.timeout && globalState$$1.timeout.running) {
2979
- // timer can be already stopped or unset at this point
2980
- animateTimerProgressBar(innerParams.timer);
2981
- }
2982
- });
2818
+ for (let i = 0; i < radios.length; i++) {
2819
+ radios[i].disabled = disabled;
2983
2820
  }
2821
+ } else {
2822
+ input.disabled = disabled;
2984
2823
  }
2985
- };
2824
+ }
2986
2825
 
2987
- const initFocus = (domCache, innerParams) => {
2988
- if (innerParams.toast) {
2989
- return;
2990
- }
2826
+ function enableButtons() {
2827
+ setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], false);
2828
+ }
2829
+ function disableButtons() {
2830
+ setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], true);
2831
+ }
2832
+ function enableInput() {
2833
+ return setInputDisabled(this.getInput(), false);
2834
+ }
2835
+ function disableInput() {
2836
+ return setInputDisabled(this.getInput(), true);
2837
+ }
2991
2838
 
2992
- if (!callIfFunction(innerParams.allowEnterKey)) {
2993
- return blurActiveElement();
2994
- }
2839
+ function showValidationMessage(error) {
2840
+ const domCache = privateProps.domCache.get(this);
2841
+ const params = privateProps.innerParams.get(this);
2842
+ setInnerHtml(domCache.validationMessage, error);
2843
+ domCache.validationMessage.className = swalClasses['validation-message'];
2995
2844
 
2996
- if (!focusButton(domCache, innerParams)) {
2997
- setFocus(innerParams, -1, 1);
2845
+ if (params.customClass && params.customClass.validationMessage) {
2846
+ addClass(domCache.validationMessage, params.customClass.validationMessage);
2998
2847
  }
2999
- };
3000
2848
 
3001
- const focusButton = (domCache, innerParams) => {
3002
- if (innerParams.focusDeny && isVisible(domCache.denyButton)) {
3003
- domCache.denyButton.focus();
3004
- return true;
3005
- }
2849
+ show(domCache.validationMessage);
2850
+ const input = this.getInput();
3006
2851
 
3007
- if (innerParams.focusCancel && isVisible(domCache.cancelButton)) {
3008
- domCache.cancelButton.focus();
3009
- return true;
2852
+ if (input) {
2853
+ input.setAttribute('aria-invalid', true);
2854
+ input.setAttribute('aria-describedby', swalClasses['validation-message']);
2855
+ focusInput(input);
2856
+ addClass(input, swalClasses.inputerror);
3010
2857
  }
2858
+ } // Hide block with validation message
3011
2859
 
3012
- if (innerParams.focusConfirm && isVisible(domCache.confirmButton)) {
3013
- domCache.confirmButton.focus();
3014
- return true;
2860
+ function resetValidationMessage$1() {
2861
+ const domCache = privateProps.domCache.get(this);
2862
+
2863
+ if (domCache.validationMessage) {
2864
+ hide(domCache.validationMessage);
3015
2865
  }
3016
2866
 
3017
- return false;
3018
- };
2867
+ const input = this.getInput();
3019
2868
 
3020
- const blurActiveElement = () => {
3021
- if (document.activeElement && typeof document.activeElement.blur === 'function') {
3022
- document.activeElement.blur();
2869
+ if (input) {
2870
+ input.removeAttribute('aria-invalid');
2871
+ input.removeAttribute('aria-describedby');
2872
+ removeClass(input, swalClasses.inputerror);
3023
2873
  }
3024
- };
2874
+ }
2875
+
2876
+ function getProgressSteps$1() {
2877
+ const domCache = privateProps.domCache.get(this);
2878
+ return domCache.progressSteps;
2879
+ }
3025
2880
 
3026
2881
  /**
3027
2882
  * Updates popup parameters.
@@ -3132,7 +2987,6 @@
3132
2987
  showValidationMessage: showValidationMessage,
3133
2988
  resetValidationMessage: resetValidationMessage$1,
3134
2989
  getProgressSteps: getProgressSteps$1,
3135
- _main: _main,
3136
2990
  update: update,
3137
2991
  _destroy: _destroy
3138
2992
  });
@@ -3165,6 +3019,36 @@
3165
3019
  const promise = this._main(this.params);
3166
3020
 
3167
3021
  privateProps.promise.set(this, promise);
3022
+ }
3023
+
3024
+ _main(userParams) {
3025
+ let mixinParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3026
+ showWarningsForParams(Object.assign({}, mixinParams, userParams));
3027
+
3028
+ if (globalState.currentInstance) {
3029
+ globalState.currentInstance._destroy();
3030
+
3031
+ if (isModal()) {
3032
+ unsetAriaHidden();
3033
+ }
3034
+ }
3035
+
3036
+ globalState.currentInstance = this;
3037
+ const innerParams = prepareParams(userParams, mixinParams);
3038
+ setParameters(innerParams);
3039
+ Object.freeze(innerParams); // clear the previous timer
3040
+
3041
+ if (globalState.timeout) {
3042
+ globalState.timeout.stop();
3043
+ delete globalState.timeout;
3044
+ } // clear the restore focus timeout
3045
+
3046
+
3047
+ clearTimeout(globalState.restoreFocusTimeout);
3048
+ const domCache = populateDomCache(this);
3049
+ render(this, innerParams);
3050
+ privateProps.innerParams.set(this, innerParams);
3051
+ return swalPromise(this, domCache, innerParams);
3168
3052
  } // `catch` cannot be the name of a module export, so we define our thenable methods here instead
3169
3053
 
3170
3054
 
@@ -3178,7 +3062,128 @@
3178
3062
  return promise.finally(onFinally);
3179
3063
  }
3180
3064
 
3181
- } // Assign instance methods from src/instanceMethods/*.js to prototype
3065
+ }
3066
+
3067
+ const swalPromise = (instance, domCache, innerParams) => {
3068
+ return new Promise((resolve, reject) => {
3069
+ // functions to handle all closings/dismissals
3070
+ const dismissWith = dismiss => {
3071
+ instance.closePopup({
3072
+ isDismissed: true,
3073
+ dismiss
3074
+ });
3075
+ };
3076
+
3077
+ privateMethods.swalPromiseResolve.set(instance, resolve);
3078
+ privateMethods.swalPromiseReject.set(instance, reject);
3079
+
3080
+ domCache.confirmButton.onclick = () => handleConfirmButtonClick(instance);
3081
+
3082
+ domCache.denyButton.onclick = () => handleDenyButtonClick(instance);
3083
+
3084
+ domCache.cancelButton.onclick = () => handleCancelButtonClick(instance, dismissWith);
3085
+
3086
+ domCache.closeButton.onclick = () => dismissWith(DismissReason.close);
3087
+
3088
+ handlePopupClick(instance, domCache, dismissWith);
3089
+ addKeydownHandler(instance, globalState, innerParams, dismissWith);
3090
+ handleInputOptionsAndValue(instance, innerParams);
3091
+ openPopup(innerParams);
3092
+ setupTimer(globalState, innerParams, dismissWith);
3093
+ initFocus(domCache, innerParams); // Scroll container to top on open (#1247, #1946)
3094
+
3095
+ setTimeout(() => {
3096
+ domCache.container.scrollTop = 0;
3097
+ });
3098
+ });
3099
+ };
3100
+
3101
+ const prepareParams = (userParams, mixinParams) => {
3102
+ const templateParams = getTemplateParams(userParams);
3103
+ const params = Object.assign({}, defaultParams, mixinParams, templateParams, userParams); // precedence is described in #2131
3104
+
3105
+ params.showClass = Object.assign({}, defaultParams.showClass, params.showClass);
3106
+ params.hideClass = Object.assign({}, defaultParams.hideClass, params.hideClass);
3107
+ return params;
3108
+ };
3109
+
3110
+ const populateDomCache = instance => {
3111
+ const domCache = {
3112
+ popup: getPopup(),
3113
+ container: getContainer(),
3114
+ actions: getActions(),
3115
+ confirmButton: getConfirmButton(),
3116
+ denyButton: getDenyButton(),
3117
+ cancelButton: getCancelButton(),
3118
+ loader: getLoader(),
3119
+ closeButton: getCloseButton(),
3120
+ validationMessage: getValidationMessage(),
3121
+ progressSteps: getProgressSteps()
3122
+ };
3123
+ privateProps.domCache.set(instance, domCache);
3124
+ return domCache;
3125
+ };
3126
+
3127
+ const setupTimer = (globalState$$1, innerParams, dismissWith) => {
3128
+ const timerProgressBar = getTimerProgressBar();
3129
+ hide(timerProgressBar);
3130
+
3131
+ if (innerParams.timer) {
3132
+ globalState$$1.timeout = new Timer(() => {
3133
+ dismissWith('timer');
3134
+ delete globalState$$1.timeout;
3135
+ }, innerParams.timer);
3136
+
3137
+ if (innerParams.timerProgressBar) {
3138
+ show(timerProgressBar);
3139
+ setTimeout(() => {
3140
+ if (globalState$$1.timeout && globalState$$1.timeout.running) {
3141
+ // timer can be already stopped or unset at this point
3142
+ animateTimerProgressBar(innerParams.timer);
3143
+ }
3144
+ });
3145
+ }
3146
+ }
3147
+ };
3148
+
3149
+ const initFocus = (domCache, innerParams) => {
3150
+ if (innerParams.toast) {
3151
+ return;
3152
+ }
3153
+
3154
+ if (!callIfFunction(innerParams.allowEnterKey)) {
3155
+ return blurActiveElement();
3156
+ }
3157
+
3158
+ if (!focusButton(domCache, innerParams)) {
3159
+ setFocus(innerParams, -1, 1);
3160
+ }
3161
+ };
3162
+
3163
+ const focusButton = (domCache, innerParams) => {
3164
+ if (innerParams.focusDeny && isVisible(domCache.denyButton)) {
3165
+ domCache.denyButton.focus();
3166
+ return true;
3167
+ }
3168
+
3169
+ if (innerParams.focusCancel && isVisible(domCache.cancelButton)) {
3170
+ domCache.cancelButton.focus();
3171
+ return true;
3172
+ }
3173
+
3174
+ if (innerParams.focusConfirm && isVisible(domCache.confirmButton)) {
3175
+ domCache.confirmButton.focus();
3176
+ return true;
3177
+ }
3178
+
3179
+ return false;
3180
+ };
3181
+
3182
+ const blurActiveElement = () => {
3183
+ if (document.activeElement && typeof document.activeElement.blur === 'function') {
3184
+ document.activeElement.blur();
3185
+ }
3186
+ }; // Assign instance methods from src/instanceMethods/*.js to prototype
3182
3187
 
3183
3188
 
3184
3189
  Object.assign(SweetAlert.prototype, instanceMethods); // Assign static methods from src/staticMethods/*.js to constructor
@@ -3193,7 +3198,7 @@
3193
3198
  };
3194
3199
  });
3195
3200
  SweetAlert.DismissReason = DismissReason;
3196
- SweetAlert.version = '11.2.0';
3201
+ SweetAlert.version = '11.3.1';
3197
3202
 
3198
3203
  const Swal = SweetAlert;
3199
3204
  Swal.default = Swal;