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