PageSourceSearch

https://dmlp.lpndole.com/node_modules/bootbox/bootbox.all.js

js lpndole.com collected 2026-09-24 14:47:13 UTC 45,353 bytes, 1,444 lines download raw bytes

1/*! @preserve
2 * bootbox.js
3 * version: 5.5.2
4 * author: Nick Payne <[email protected]>
5 * license: MIT
6 * http://bootboxjs.com/
7 */
8(function (root, factory) {
9  'use strict';
10  if (typeof define === 'function' && define.amd) {
11    // AMD
12    define(['jquery'], factory);
13  } else if (typeof exports === 'object') {
14    // Node, CommonJS-like
15    module.exports = factory(require('jquery'));
16  } else {
17    // Browser globals (root is window)
18    root.bootbox = factory(root.jQuery);
19  }
20}(this, function init($, undefined) {
21  'use strict';
22
23  //  Polyfills Object.keys, if necessary.
24  //  @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
25  if (!Object.keys) {
26    Object.keys = (function () {
27      var hasOwnProperty = Object.prototype.hasOwnProperty,
28        hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
29        dontEnums = [
30          'toString',
31          'toLocaleString',
32          'valueOf',
33          'hasOwnProperty',
34          'isPrototypeOf',
35          'propertyIsEnumerable',
36          'constructor'
37        ],
38        dontEnumsLength = dontEnums.length;
39
40      return function (obj) {
41        if (typeof obj !== 'function' && (typeof obj !== 'object' || obj === null)) {
42          throw new TypeError('Object.keys called on non-object');
43        }
44
45        var result = [], prop, i;
46
47        for (prop in obj) {
48          if (hasOwnProperty.call(obj, prop)) {
49            result.push(prop);
50          }
51        }
52
53        if (hasDontEnumBug) {
54          for (i = 0; i < dontEnumsLength; i++) {
55            if (hasOwnProperty.call(obj, dontEnums[i])) {
56              result.push(dontEnums[i]);
57            }
58          }
59        }
60
61        return result;
62      };
63    }());
64  }
65
66  var exports = {};
67
68  var VERSION = '5.5.2';
69  exports.VERSION = VERSION;
70
71  var locales = {
72      ar : {
73        OK      : 'موافق',
74        CANCEL  : 'الغاء',
75        CONFIRM : 'تأكيد'
76      },
77      bg_BG : {
78        OK      : 'Ок',
79        CANCEL  : 'Отказ',
80        CONFIRM : 'Потвърждавам'
81      },
82      br : {
83        OK      : 'OK',
84        CANCEL  : 'Cancelar',
85        CONFIRM : 'Sim'
86      },
87      cs : {
88        OK      : 'OK',
89        CANCEL  : 'Zrušit',
90        CONFIRM : 'Potvrdit'
91      },
92      da : {
93        OK      : 'OK',
94        CANCEL  : 'Annuller',
95        CONFIRM : 'Accepter'
96      },
97      de : {
98        OK      : 'OK',
99        CANCEL  : 'Abbrechen',
100        CONFIRM : 'Akzeptieren'
101      },
102      el : {
103        OK      : 'Εντάξει',
104        CANCEL  : 'Ακύρωση',
105        CONFIRM : 'Επιβεβαίωση'
106      },
107      en : {
108        OK      : 'OK',
109        CANCEL  : 'Cancel',
110        CONFIRM : 'OK'
111      },
112      es : {
113        OK      : 'OK',
114        CANCEL  : 'Cancelar',
115        CONFIRM : 'Aceptar'
116      },
117      eu : {
118        OK      : 'OK',
119        CANCEL  : 'Ezeztatu',
120        CONFIRM : 'Onartu'
121      },
122      et : {
123        OK      : 'OK',
124        CANCEL  : 'Katkesta',
125        CONFIRM : 'OK'
126      },
127      fa : {
128        OK      : 'قبول',
129        CANCEL  : 'لغو',
130        CONFIRM : 'تایید'
131      },
132      fi : {
133        OK      : 'OK',
134        CANCEL  : 'Peruuta',
135        CONFIRM : 'OK'
136      },
137      fr : {
138        OK      : 'OK',
139        CANCEL  : 'Annuler',
140        CONFIRM : 'Confirmer'
141      },
142      he : {
143        OK      : 'אישור',
144        CANCEL  : 'ביטול',
145        CONFIRM : 'אישור'
146      },
147      hu : {
148        OK      : 'OK',
149        CANCEL  : 'Mégsem',
150        CONFIRM : 'Megerősít'
151      },
152      hr : {
153        OK      : 'OK',
154        CANCEL  : 'Odustani',
155        CONFIRM : 'Potvrdi'
156      },
157      id : {
158        OK      : 'OK',
159        CANCEL  : 'Batal',
160        CONFIRM : 'OK'
161      },
162      it : {
163        OK      : 'OK',
164        CANCEL  : 'Annulla',
165        CONFIRM : 'Conferma'
166      },
167      ja : {
168        OK      : 'OK',
169        CANCEL  : 'キャンセル',
170        CONFIRM : '確認'
171      },
172      ka : {
173        OK: 'OK',
174        CANCEL: 'გაუქმება',
175        CONFIRM: 'დადასტურება'
176      },
177      ko : {
178        OK: 'OK',
179        CANCEL: '취소',
180        CONFIRM: '확인'
181      },
182      lt : {
183        OK      : 'Gerai',
184        CANCEL  : 'Atšaukti',
185        CONFIRM : 'Patvirtinti'
186      },
187      lv : {
188        OK      : 'Labi',
189        CANCEL  : 'Atcelt',
190        CONFIRM : 'Apstiprināt'
191      },
192      nl : {
193        OK      : 'OK',
194        CANCEL  : 'Annuleren',
195        CONFIRM : 'Accepteren'
196      },
197      no : {
198        OK      : 'OK',
199        CANCEL  : 'Avbryt',
200        CONFIRM : 'OK'
201      },
202      pl : {
203        OK      : 'OK',
204        CANCEL  : 'Anuluj',
205        CONFIRM : 'Potwierdź'
206      },
207      pt : {
208        OK      : 'OK',
209        CANCEL  : 'Cancelar',
210        CONFIRM : 'Confirmar'
211      },
212      ru : {
213        OK      : 'OK',
214        CANCEL  : 'Отмена',
215        CONFIRM : 'Применить'
216      },
217      sk : {
218        OK      : 'OK',
219        CANCEL  : 'Zrušiť',
220        CONFIRM : 'Potvrdiť'
221      },
222      sl : {
223        OK      : 'OK',
224        CANCEL  : 'Prekliči',
225        CONFIRM : 'Potrdi'
226      },
227      sq : {
228        OK      : 'OK',
229        CANCEL  : 'Anulo',
230        CONFIRM : 'Prano'
231      },
232      sv : {
233        OK      : 'OK',
234        CANCEL  : 'Avbryt',
235        CONFIRM : 'OK'
236      },
237      sw: {
238        OK      : 'Sawa',
239        CANCEL  : 'Ghairi',
240        CONFIRM: 'Thibitisha'
241      },
242      ta:{
243        OK      : 'சரி',
244        CANCEL  : 'ரத்து செய்',
245        CONFIRM : 'உறுதி செய்'
246      },
247      th : {
248        OK      : 'ตกลง',
249        CANCEL  : 'ยกเลิก',
250        CONFIRM : 'ยืนยัน'
251      },
252      tr : {
253        OK      : 'Tamam',
254        CANCEL  : 'İptal',
255        CONFIRM : 'Onayla'
256      },
257      uk : {
258        OK      : 'OK',
259        CANCEL  : 'Відміна',
260        CONFIRM : 'Прийняти'
261      },
262      vi : {
263        OK      : 'OK',
264        CANCEL  : 'Hủy bỏ',
265        CONFIRM : 'Xác nhận'
266      },
267      zh_CN : {
268        OK      : 'OK',
269        CANCEL  : '取消',
270        CONFIRM : '确认'
271      },
272      zh_TW : {
273        OK      : 'OK',
274        CANCEL  : '取消',
275        CONFIRM : '確認'
276      }
277  };
278
279  var templates = {
280    dialog:
281    '<div class="bootbox modal" tabindex="-1" role="dialog" aria-hidden="true">' +
282    '<div class="modal-dialog">' +
283    '<div class="modal-content">' +
284    '<div class="modal-body"><div class="bootbox-body"></div></div>' +
285    '</div>' +
286    '</div>' +
287    '</div>',
288    header:
289    '<div class="modal-header">' +
290    '<h5 class="modal-title"></h5>' +
291    '</div>',
292    footer:
293    '<div class="modal-footer"></div>',
294    closeButton:
295    '<button type="button" class="bootbox-close-button close" aria-hidden="true">&times;</button>',
296    form:
297    '<form class="bootbox-form"></form>',
298    button:
299    '<button type="button" class="btn"></button>',
300    option:
301    '<option></option>',
302    promptMessage:
303    '<div class="bootbox-prompt-message"></div>',
304    inputs: {
305      text:
306      '<input class="bootbox-input bootbox-input-text form-control" autocomplete="off" type="text" />',
307      textarea:
308      '<textarea class="bootbox-input bootbox-input-textarea form-control"></textarea>',
309      email:
310      '<input class="bootbox-input bootbox-input-email form-control" autocomplete="off" type="email" />',
311      select:
312      '<select class="bootbox-input bootbox-input-select form-control"></select>',
313      checkbox:
314      '<div class="form-check checkbox"><label class="form-check-label">
314<input class="form-check-input bootbox-input bootbox-input-checkbox" type="checkbox" /></label></div>',
315      radio:
316      '<div class="form-check radio"><label class="form-check-label"><input class="form-check-input bootbox-input bootbox-input-radio" type="radio" name="bootbox-radio" /></label></div>',
317      date:
318      '<input class="bootbox-input bootbox-input-date form-control" autocomplete="off" type="date" />',
319      time:
320      '<input class="bootbox-input bootbox-input-time form-control" autocomplete="off" type="time" />',
321      number:
322      '<input class="bootbox-input bootbox-input-number form-control" autocomplete="off" type="number" />',
323      password:
324      '<input class="bootbox-input bootbox-input-password form-control" autocomplete="off" type="password" />',
325      range:
326      '<input class="bootbox-input bootbox-input-range form-control-range" autocomplete="off" type="range" />'
327    }
328  };
329
330
331  var defaults = {
332    // default language
333    locale: 'en',
334    // show backdrop or not. Default to static so user has to interact with dialog
335    backdrop: 'static',
336    // animate the modal in/out
337    animate: true,
338    // additional class string applied to the top level dialog
339    className: null,
340    // whether or not to include a close button
341    closeButton: true,
342    // show the dialog immediately by default
343    show: true,
344    // dialog container
345    container: 'body',
346    // default value (used by the prompt helper)
347    value: '',
348    // default input type (used by the prompt helper)
349    inputType: 'text',
350    // switch button order from cancel/confirm (default) to confirm/cancel
351    swapButtonOrder: false,
352    // center modal vertically in page
353    centerVertical: false,
354    // Append "multiple" property to the select when using the "prompt" helper
355    multiple: false,
356    // Automatically scroll modal content when height exceeds viewport height
357    scrollable: false,
358    // whether or not to destroy the modal on hide
359    reusable: false
360  };
361
362
363  // PUBLIC FUNCTIONS
364  // *************************************************************************************************************
365
366  // Return all currently registered locales, or a specific locale if "name" is defined
367  exports.locales = function (name) {
368    return name ? locales[name] : locales;
369  };
370
371
372  // Register localized strings for the OK, CONFIRM, and CANCEL buttons
373  exports.addLocale = function (name, values) {
374    $.each(['OK', 'CANCEL', 'CONFIRM'], function (_, v) {
375      if (!values[v]) {
376        throw new Error('Please supply a translation for "' + v + '"');
377      }
378    });
379
380    locales[name] = {
381      OK: values.OK,
382      CANCEL: values.CANCEL,
383      CONFIRM: values.CONFIRM
384    };
385
386    return exports;
387  };
388
389
390  // Remove a previously-registered locale
391  exports.removeLocale = function (name) {
392    if (name !== 'en') {
393      delete locales[name];
394    }
395    else {
396      throw new Error('"en" is used as the default and fallback locale and cannot be removed.');
397    }
398
399    return exports;
400  };
401
402
403  // Set the default locale
404  exports.setLocale = function (name) {
405    return exports.setDefaults('locale', name);
406  };
407
408
409  // Override default value(s) of Bootbox.
410  exports.setDefaults = function () {
411    var values = {};
412
413    if (arguments.length === 2) {
414      // allow passing of single key/value...
415      values[arguments[0]] = arguments[1];
416    } else {
417      // ... and as an object too
418      values = arguments[0];
419    }
420
421    $.extend(defaults, values);
422
423    return exports;
424  };
425
426
427  // Hides all currently active Bootbox modals
428  exports.hideAll = function () {
429    $('.bootbox').modal('hide');
430
431    return exports;
432  };
433
434
435  // Allows the base init() function to be overridden
436  exports.init = function (_$) {
437    return init(_$ || $);
438  };
439
440
441  // CORE HELPER FUNCTIONS
442  // *************************************************************************************************************
443
444  // Core dialog function
445  exports.dialog = function (options) {
446    if ($.fn.modal === undefined) {
447      throw new Error(
448        '"$.fn.modal" is not defined; please double check you have included ' +
449        'the Bootstrap JavaScript library. See https://getbootstrap.com/docs/4.4/getting-started/javascript/ ' +
450        'for more details.'
451      );
452    }
453
454    options = sanitize(options);
455
456    if ($.fn.modal.Constructor.VERSION) {
457      options.fullBootstrapVersion = $.fn.modal.Constructor.VERSION;
458      var i = options.fullBootstrapVersion.indexOf('.');
459      options.bootstrap = options.fullBootstrapVersion.substring(0, i);
460    }
461    else {
462      // Assuming version 2.3.2, as that was the last "supported" 2.x version
463      options.bootstrap = '2';
464      options.fullBootstrapVersion = '2.3.2';
465      console.warn('Bootbox will *mostly* work with Bootstrap 2, but we do not officially support it. Please upgrade, if possible.');
466    }
467
468    var dialog = $(templates.dialog);
469    var innerDialog = dialog.find('.modal-dialog');
470    var body = dialog.find('.modal-body');
471    var header = $(templates.header);
472    var footer = $(templates.footer);
473    var buttons = options.buttons;
474
475    var callbacks = {
476      onEscape: options.onEscape
477    };
478
479    body.find('.bootbox-body').html(options.message);
480
481    // Only attempt to create buttons if at least one has 
482    // been defined in the options object
483    if (getKeyLength(options.buttons) > 0) {
484      each(buttons, function (key, b) {
485        var button = $(templates.button);
486        button.data('bb-handler', key);
487        button.addClass(b.className);
488
489        switch (key) {
490          case 'ok':
491          case 'confirm':
492            button.addClass('bootbox-accept');
493            break;
494
495          case 'cancel':
496            button.addClass('bootbox-cancel');
497            break;
498        }
499
500        button.html(b.label);
501        footer.append(button);
502
503        callbacks[key] = b.callback;
504      });
505
506      body.after(footer);
507    }
508
509    if (options.animate === true) {
510      dialog.addClass('fade');
511    }
512
513    if (options.className) {
514      dialog.addClass(options.className);
515    }
516
517    if (options.size) {
518      // Requires Bootstrap 3.1.0 or higher
519      if (options.fullBootstrapVersion.substring(0, 3) < '3.1') {
520        console.warn('"size" requires Bootstrap 3.1.0 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.');
521      }
522
523      switch (options.size) {
524        case 'small':
525        case 'sm':
526          innerDialog.addClass('modal-sm');
527          break;
528
529        case 'large':
530        case 'lg':
531          innerDialog.addClass('modal-lg');
532          break;
533
534        case 'extra-large':
535        case 'xl':
536          innerDialog.addClass('modal-xl');
537
538          // Requires Bootstrap 4.2.0 or higher
539          if (options.fullBootstrapVersion.substring(0, 3) < '4.2') {
540            console.warn('Using size "xl"/"extra-large" requires Bootstrap 4.2.0 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.');
541          }
542          break;
543      }
544    }
545
546    if (options.scrollable) {
547      innerDialog.addClass('modal-dialog-scrollable');
548
549      // Requires Bootstrap 4.3.0 or higher
550      if (options.fullBootstrapVersion.substring(0, 3) < '4.3') {
551        console.warn('Using "scrollable" requires Bootstrap 4.3.0 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.');
552      }
553    }
554
555    if (options.title) {
556      body.before(header);
557      dialog.find('.modal-title').html(options.title);
558    }
559
560    if (options.closeButton) {
561      var closeButton = $(templates.closeButton);
562
563      if (options.title) {
564        if (options.bootstrap > 3) {
565          dialog.find('.modal-header').append(closeButton);
566        }
567        else {
568          dialog.find('.modal-header').prepend(closeButton);
569        }
570      } else {
571        closeButton.prependTo(body);
572      }
573    }
574
575    if (options.centerVertical) {
576      innerDialog.addClass('modal-dialog-centered');
577
578      // Requires Bootstrap 4.0.0-beta.3 or higher
579      if (options.fullBootstrapVersion < '4.0.0') {
580        console.warn('"centerVertical" requires Bootstrap 4.0.0-beta.3 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.');
581      }
582    }
583
584    // Bootstrap event listeners; these handle extra
585    // setup & teardown required after the underlying
586    // modal has performed certain actions.
587
588    if(!options.reusable) {
589      // make sure we unbind any listeners once the dialog has definitively been dismissed
590      dialog.one('hide.bs.modal', { dialog: dialog }, unbindModal);
591    }
592
593    if (options.onHide) {
594      if ($.isFunction(options.onHide)) {
595        dialog.on('hide.bs.modal', options.onHide);
596      }
597      else {
598        throw new Error('Argument supplied to "onHide" must be a function');
599      }
600    }
601
602    if(!options.reusable) {
603      dialog.one('hidden.bs.modal', { dialog: dialog }, destroyModal);
604    }
605
606    if (options.onHidden) {
607      if ($.isFunction(options.onHidden)) {
608        dialog.on('hidden.bs.modal', options.onHidden);
609      }
610      else {
611        throw new Error('Argument supplied to "onHidden" must be a function');
612      }
613    }
614
615    if (options.onShow) {
616      if ($.isFunction(options.onShow)) {
617        dialog.on('show.bs.modal', options.onShow);
618      }
619      else {
620        throw new Error('Argument supplied to "onShow" must be a function');
621      }
622    }
623
624    dialog.one('shown.bs.modal', { dialog: dialog }, focusPrimaryButton);
625
626    if (options.onShown) {
627      if ($.isFunction(options.onShown)) {
628        dialog.on('shown.bs.modal', options.onShown);
629      }
630      else {
631        throw new Error('Argument supplied to "onShown" must be a function');
632      }
633    }
634
635    // Bootbox event listeners; used to decouple some
636    // behaviours from their respective triggers
637
638    if (options.backdrop === true) {
639      // A boolean true/false according to the Bootstrap docs
640      // should show a dialog the user can dismiss by clicking on
641      // the background.
642      // We always only ever pass static/false to the actual
643      // $.modal function because with "true" we can't trap
644      // this event (the .modal-backdrop swallows it)
645      // However, we still want to sort-of respect true
646      // and invoke the escape mechanism instead
647      dialog.on('click.dismiss.bs.modal', function (e) {
648        // @NOTE: the target varies in >= 3.3.x releases since the modal backdrop
649        // moved *inside* the outer dialog rather than *alongside* it
650        if (dialog.children('.modal-backdrop').length) {
651          e.currentTarget = dialog.children('.modal-backdrop').get(0);
652        }
653
654        if (e.target !== e.currentTarget) {
655          return;
656        }
657
658        dialog.trigger('escape.close.bb');
659      });
660    }
661
662    dialog.on('escape.close.bb', function (e) {
663      // the if statement looks redundant but it isn't; without it
664      // if we *didn't* have an onEscape handler then processCallback
665      // would automatically dismiss the dialog
666      if (callbacks.onEscape) {
667        processCallback(e, dialog, callbacks.onEscape);
668      }
669    });
670
671
672    dialog.on('click', '.modal-footer button:not(.disabled)', function (e) {
673      var callbackKey = $(this).data('bb-handler');
674
675      if (callbackKey !== undefined) {
676        // Only process callbacks for buttons we recognize:
677        processCallback(e, dialog, callbacks[callbackKey]);
678      }
679    });
680
681    dialog.on('click', '.bootbox-close-button', function (e) {
682      // onEscape might be falsy but that's fine; the fact is
683      // if the user has managed to click the close button we
684      // have to close the dialog, callback or not
685      processCallback(e, dialog, callbacks.onEscape);
686    });
687
688    dialog.on('keyup', function (e) {
689      if (e.which === 27) {
690        dialog.trigger('escape.close.bb');
691      }
692    });
693
694    // the remainder of this method simply deals with adding our
695    // dialog element to the DOM, augmenting it with Bootstrap's modal
696    // functionality and then giving the resulting object back
697    // to our caller
698
699    $(options.container).append(dialog);
700
701    dialog.modal({
702      backdrop: options.backdrop,
703      keyboard: false,
704      show: false
705    });
706
707    if (options.show) {
708      dialog.modal('show');
709    }
710
711    return dialog;
712  };
713
714
715  // Helper function to simulate the native alert() behavior. **NOTE**: This is non-blocking, so any
716  // code that must happen after the alert is dismissed should be placed within the callback function 
717  // for this alert.
718  exports.alert = function () {
719    var options;
720
721    options = mergeDialogOptions('alert', ['ok'], ['message', 'callback'], arguments);
722
723    // @TODO: can this move inside exports.dialog when we're iterating over each
724    // button and checking its button.callback value instead?
725    if (options.callback && !$.isFunction(options.callback)) {
726      throw new Error('alert requires the "callback" property to be a function when provided');
727    }
728
729    // override the ok and escape callback to make sure they just invoke
730    // the single user-supplied one (if provided)
731    options.buttons.ok.callback = options.onEscape = function () {
732      if ($.isFunction(options.callback)) {
733        return options.callback.call(this);
734      }
735
736      return true;
737    };
738
739    return exports.dialog(options);
740  };
741
742
743  // Helper function to simulate the native confirm() behavior. **NOTE**: This is non-blocking, so any
744  // code that must happen after the confirm is dismissed should be placed within the callback function 
745  // for this confirm.
746  exports.confirm = function () {
747    var options;
748
749    options = mergeDialogOptions('confirm', ['cancel', 'confirm'], ['message', 'callback'], arguments);
750
751    // confirm specific validation; they don't make sense without a callback so make
752    // sure it's present
753    if (!$.isFunction(options.callback)) {
754      throw new Error('confirm requires a callback');
755    }
756
757    // overrides; undo anything the user tried to set they shouldn't have
758    options.buttons.cancel.callback = options.onEscape = function () {
759      return options.callback.call(this, false);
760    };
761
762    options.buttons.confirm.callback = function () {
763      return options.callback.call(this, true);
764    };
765
766    return exports.dialog(options);
767  };
768
769
770  // Helper function to simulate the native prompt() behavior. **NOTE**: This is non-blocking, so any
771  // code that must happen after the prompt is dismissed should be placed within the callback function 
772  // for this prompt.
773  exports.prompt = function () {
774    var options;
775    var promptDialog;
776    var form;
777    var input;
778    var shouldShow;
779    var inputOptions;
780
781    // we have to create our form first otherwise
782    // its value is undefined when gearing up our options
783    // @TODO this could be solved by allowing message to
784    // be a function instead...
785    form = $(templates.form);
786
787    // prompt defaults are more complex than others in that
788    // users can override more defaults
789    options = mergeDialogOptions('prompt', ['cancel', 'confirm'], ['title', 'callback'], arguments);
790
791    if (!options.value) {
792      options.value = defaults.value;
793    }
794
795    if (!options.inputType) {
796      options.inputType = defaults.inputType;
797    }
798
799    // capture the user's show value;
vendor: 21,530 bytes, lines 799-1444
799 we always set this to false before
800    // spawning the dialog to give us a chance to attach some handlers to
801    // it, but we need to make sure we respect a preference not to show it
802    shouldShow = (options.show === undefined) ? defaults.show : options.show;
803
804    // This is required prior to calling the dialog builder below - we need to 
805    // add an event handler just before the prompt is shown
806    options.show = false;
807
808    // Handles the 'cancel' action
809    options.buttons.cancel.callback = options.onEscape = function () {
810      return options.callback.call(this, null);
811    };
812
813    // Prompt submitted - extract the prompt value. This requires a bit of work, 
814    // given the different input types available.
815    options.buttons.confirm.callback = function () {
816      var value;
817
818      if (options.inputType === 'checkbox') {
819        value = input.find('input:checked').map(function () {
820          return $(this).val();
821        }).get();
822      } else if (options.inputType === 'radio') {
823        value = input.find('input:checked').val();
824      }
825      else {
826        if (input[0].checkValidity && !input[0].checkValidity()) {
827          // prevents button callback from being called
828          return false;
829        } else {
830          if (options.inputType === 'select' && options.multiple === true) {
831            value = input.find('option:selected').map(function () {
832              return $(this).val();
833            }).get();
834          }
835          else {
836            value = input.val();
837          }
838        }
839      }
840
841      return options.callback.call(this, value);
842    };
843
844    // prompt-specific validation
845    if (!options.title) {
846      throw new Error('prompt requires a title');
847    }
848
849    if (!$.isFunction(options.callback)) {
850      throw new Error('prompt requires a callback');
851    }
852
853    if (!templates.inputs[options.inputType]) {
854      throw new Error('Invalid prompt type');
855    }
856
857    // create the input based on the supplied type
858    input = $(templates.inputs[options.inputType]);
859
860    switch (options.inputType) {
861      case 'text':
862      case 'textarea':
863      case 'email':
864      case 'password':
865        input.val(options.value);
866
867        if (options.placeholder) {
868          input.attr('placeholder', options.placeholder);
869        }
870
871        if (options.pattern) {
872          input.attr('pattern', options.pattern);
873        }
874
875        if (options.maxlength) {
876          input.attr('maxlength', options.maxlength);
877        }
878
879        if (options.required) {
880          input.prop({ 'required': true });
881        }
882
883        if (options.rows && !isNaN(parseInt(options.rows))) {
884          if (options.inputType === 'textarea') {
885            input.attr({ 'rows': options.rows });
886          }
887        }
888
889        break;
890
891
892      case 'date':
893      case 'time':
894      case 'number':
895      case 'range':
896        input.val(options.value);
897
898        if (options.placeholder) {
899          input.attr('placeholder', options.placeholder);
900        }
901
902        if (options.pattern) {
903          input.attr('pattern', options.pattern);
904        }
905
906        if (options.required) {
907          input.prop({ 'required': true });
908        }
909
910        // These input types have extra attributes which affect their input validation.
911        // Warning: For most browsers, date inputs are buggy in their implementation of 'step', so 
912        // this attribute will have no effect. Therefore, we don't set the attribute for date inputs.
913        // @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date#Setting_maximum_and_minimum_dates
914        if (options.inputType !== 'date') {
915          if (options.step) {
916            if (options.step === 'any' || (!isNaN(options.step) && parseFloat(options.step) > 0)) {
917              input.attr('step', options.step);
918            }
919            else {
920              throw new Error('"step" must be a valid positive number or the value "any". See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-step for more information.');
921            }
922          }
923        }
924
925        if (minAndMaxAreValid(options.inputType, options.min, options.max)) {
926          if (options.min !== undefined) {
927            input.attr('min', options.min);
928          }
929          if (options.max !== undefined) {
930            input.attr('max', options.max);
931          }
932        }
933
934        break;
935
936
937      case 'select':
938        var groups = {};
939        inputOptions = options.inputOptions || [];
940
941        if (!$.isArray(inputOptions)) {
942          throw new Error('Please pass an array of input options');
943        }
944
945        if (!inputOptions.length) {
946          throw new Error('prompt with "inputType" set to "select" requires at least one option');
947        }
948
949        // placeholder is not actually a valid attribute for select,
950        // but we'll allow it, assuming it might be used for a plugin
951        if (options.placeholder) {
952          input.attr('placeholder', options.placeholder);
953        }
954
955        if (options.required) {
956          input.prop({ 'required': true });
957        }
958
959        if (options.multiple) {
960          input.prop({ 'multiple': true });
961        }
962
963        each(inputOptions, function (_, option) {
964          // assume the element to attach to is the input...
965          var elem = input;
966
967          if (option.value === undefined || option.text === undefined) {
968            throw new Error('each option needs a "value" property and a "text" property');
969          }
970
971          // ... but override that element if this option sits in a group
972
973          if (option.group) {
974            // initialise group if necessary
975            if (!groups[option.group]) {
976              groups[option.group] = $('<optgroup />').attr('label', option.group);
977            }
978
979            elem = groups[option.group];
980          }
981
982          var o = $(templates.option);
983          o.attr('value', option.value).text(option.text);
984          elem.append(o);
985        });
986
987        each(groups, function (_, group) {
988          input.append(group);
989        });
990
991        // safe to set a select's value as per a normal input
992        input.val(options.value);
993
994        break;
995
996
997      case 'checkbox':
998        var checkboxValues = $.isArray(options.value) ? options.value : [options.value];
999        inputOptions = options.inputOptions || [];
1000
1001        if (!inputOptions.length) {
1002          throw new Error('prompt with "inputType" set to "checkbox" requires at least one option');
1003        }
1004
1005        // checkboxes have to nest within a containing element, so
1006        // they break the rules a bit and we end up re-assigning
1007        // our 'input' element to this container instead
1008        input = $('<div class="bootbox-checkbox-list"></div>');
1009
1010        each(inputOptions, function (_, option) {
1011          if (option.value === undefined || option.text === undefined) {
1012            throw new Error('each option needs a "value" property and a "text" property');
1013          }
1014
1015          var checkbox = $(templates.inputs[options.inputType]);
1016
1017          checkbox.find('input').attr('value', option.value);
1018          checkbox.find('label').append('\n' + option.text);
1019
1020          // we've ensured values is an array so we can always iterate over it
1021          each(checkboxValues, function (_, value) {
1022            if (value === option.value) {
1023              checkbox.find('input').prop('checked', true);
1024            }
1025          });
1026
1027          input.append(checkbox);
1028        });
1029        break;
1030
1031
1032      case 'radio':
1033        // Make sure that value is not an array (only a single radio can ever be checked)
1034        if (options.value !== undefined && $.isArray(options.value)) {
1035          throw new Error('prompt with "inputType" set to "radio" requires a single, non-array value for "value"');
1036        }
1037
1038        inputOptions = options.inputOptions || [];
1039
1040        if (!inputOptions.length) {
1041          throw new Error('prompt with "inputType" set to "radio" requires at least one option');
1042        }
1043
1044        // Radiobuttons have to nest within a containing element, so
1045        // they break the rules a bit and we end up re-assigning
1046        // our 'input' element to this container instead
1047        input = $('<div class="bootbox-radiobutton-list"></div>');
1048
1049        // Radiobuttons should always have an initial checked input checked in a "group".
1050        // If value is undefined or doesn't match an input option, select the first radiobutton
1051        var checkFirstRadio = true;
1052
1053        each(inputOptions, function (_, option) {
1054          if (option.value === undefined || option.text === undefined) {
1055            throw new Error('each option needs a "value" property and a "text" property');
1056          }
1057
1058          var radio = $(templates.inputs[options.inputType]);
1059
1060          radio.find('input').attr('value', option.value);
1061          radio.find('label').append('\n' + option.text);
1062
1063          if (options.value !== undefined) {
1064            if (option.value === options.value) {
1065              radio.find('input').prop('checked', true);
1066              checkFirstRadio = false;
1067            }
1068          }
1069
1070          input.append(radio);
1071        });
1072
1073        if (checkFirstRadio) {
1074          input.find('input[type="radio"]').first().prop('checked', true);
1075        }
1076        break;
1077    }
1078
1079    // now place it in our form
1080    form.append(input);
1081
1082    form.on('submit', function (e) {
1083      e.preventDefault();
1084      // Fix for SammyJS (or similar JS routing library) hijacking the form post.
1085      e.stopPropagation();
1086
1087      // @TODO can we actually click *the* button object instead?
1088      // e.g. buttons.confirm.click() or similar
1089      promptDialog.find('.bootbox-accept').trigger('click');
1090    });
1091
1092    if ($.trim(options.message) !== '') {
1093      // Add the form to whatever content the user may have added.
1094      var message = $(templates.promptMessage).html(options.message);
1095      form.prepend(message);
1096      options.message = form;
1097    }
1098    else {
1099      options.message = form;
1100    }
1101
1102    // Generate the dialog
1103    promptDialog = exports.dialog(options);
1104
1105    // clear the existing handler focusing the submit button...
1106    promptDialog.off('shown.bs.modal', focusPrimaryButton);
1107
1108    // ...and replace it with one focusing our input, if possible
1109    promptDialog.on('shown.bs.modal', function () {
1110      // need the closure here since input isn't
1111      // an object otherwise
1112      input.focus();
1113    });
1114
1115    if (shouldShow === true) {
1116      promptDialog.modal('show');
1117    }
1118
1119    return promptDialog;
1120  };
1121
1122
1123  // INTERNAL FUNCTIONS
1124  // *************************************************************************************************************
1125
1126  // Map a flexible set of arguments into a single returned object
1127  // If args.length is already one just return it, otherwise
1128  // use the properties argument to map the unnamed args to
1129  // object properties.
1130  // So in the latter case:
1131  //  mapArguments(["foo", $.noop], ["message", "callback"])
1132  //  -> { message: "foo", callback: $.noop }
1133  function mapArguments(args, properties) {
1134    var argn = args.length;
1135    var options = {};
1136
1137    if (argn < 1 || argn > 2) {
1138      throw new Error('Invalid argument length');
1139    }
1140
1141    if (argn === 2 || typeof args[0] === 'string') {
1142      options[properties[0]] = args[0];
1143      options[properties[1]] = args[1];
1144    } else {
1145      options = args[0];
1146    }
1147
1148    return options;
1149  }
1150
1151
1152  //  Merge a set of default dialog options with user supplied arguments
1153  function mergeArguments(defaults, args, properties) {
1154    return $.extend(
1155      // deep merge
1156      true,
1157      // ensure the target is an empty, unreferenced object
1158      {},
1159      // the base options object for this type of dialog (often just buttons)
1160      defaults,
1161      // args could be an object or array; if it's an array properties will
1162      // map it to a proper options object
1163      mapArguments(
1164        args,
1165        properties
1166      )
1167    );
1168  }
1169
1170
1171  //  This entry-level method makes heavy use of composition to take a simple
1172  //  range of inputs and return valid options suitable for passing to bootbox.dialog
1173  function mergeDialogOptions(className, labels, properties, args) {
1174    var locale;
1175    if (args && args[0]) {
1176      locale = args[0].locale || defaults.locale;
1177      var swapButtons = args[0].swapButtonOrder || defaults.swapButtonOrder;
1178
1179      if (swapButtons) {
1180        labels = labels.reverse();
1181      }
1182    }
1183
1184    //  build up a base set of dialog properties
1185    var baseOptions = {
1186      className: 'bootbox-' + className,
1187      buttons: createLabels(labels, locale)
1188    };
1189
1190    // Ensure the buttons properties generated, *after* merging
1191    // with user args are still valid against the supplied labels
1192    return validateButtons(
1193      // merge the generated base properties with user supplied arguments
1194      mergeArguments(
1195        baseOptions,
1196        args,
1197        // if args.length > 1, properties specify how each arg maps to an object key
1198        properties
1199      ),
1200      labels
1201    );
1202  }
1203
1204
1205  //  Checks each button object to see if key is valid. 
1206  //  This function will only be called by the alert, confirm, and prompt helpers. 
1207  function validateButtons(options, buttons) {
1208    var allowedButtons = {};
1209    each(buttons, function (key, value) {
1210      allowedButtons[value] = true;
1211    });
1212
1213    each(options.buttons, function (key) {
1214      if (allowedButtons[key] === undefined) {
1215        throw new Error('button key "' + key + '" is not allowed (options are ' + buttons.join(' ') + ')');
1216      }
1217    });
1218
1219    return options;
1220  }
1221
1222
1223
1224  //  From a given list of arguments, return a suitable object of button labels.
1225  //  All this does is normalise the given labels and translate them where possible.
1226  //  e.g. "ok", "confirm" -> { ok: "OK", cancel: "Annuleren" }
1227  function createLabels(labels, locale) {
1228    var buttons = {};
1229
1230    for (var i = 0, j = labels.length; i < j; i++) {
1231      var argument = labels[i];
1232      var key = argument.toLowerCase();
1233      var value = argument.toUpperCase();
1234
1235      buttons[key] = {
1236        label: getText(value, locale)
1237      };
1238    }
1239
1240    return buttons;
1241  }
1242
1243
1244
1245  //  Get localized text from a locale. Defaults to 'en' locale if no locale 
1246  //  provided or a non-registered locale is requested
1247  function getText(key, locale) {
1248    var labels = locales[locale];
1249
1250    return labels ? labels[key] : locales.en[key];
1251  }
1252
1253
1254
1255  //  Filter and tidy up any user supplied parameters to this dialog.
1256  //  Also looks for any shorthands used and ensures that the options
1257  //  which are returned are all normalized properly
1258  function sanitize(options) {
1259    var buttons;
1260    var total;
1261
1262    if (typeof options !== 'object') {
1263      throw new Error('Please supply an object of options');
1264    }
1265
1266    if (!options.message) {
1267      throw new Error('"message" option must not be null or an empty string.');
1268    }
1269
1270    // make sure any supplied options take precedence over defaults
1271    options = $.extend({}, defaults, options);
1272
1273    //make sure backdrop is either true, false, or 'static'
1274    if (!options.backdrop) {
1275      options.backdrop = (options.backdrop === false || options.backdrop === 0) ? false : 'static';
1276    } else {
1277      options.backdrop = typeof options.backdrop === 'string' && options.backdrop.toLowerCase() === 'static' ? 'static' : true;
1278    } 
1279
1280    // no buttons is still a valid dialog but it's cleaner to always have
1281    // a buttons object to iterate over, even if it's empty
1282    if (!options.buttons) {
1283      options.buttons = {};
1284    }
1285    
1286    buttons = options.buttons;
1287
1288    total = getKeyLength(buttons);
1289
1290    each(buttons, function (key, button, index) {
1291      if ($.isFunction(button)) {
1292        // short form, assume value is our callback. Since button
1293        // isn't an object it isn't a reference either so re-assign it
1294        button = buttons[key] = {
1295          callback: button
1296        };
1297      }
1298
1299      // before any further checks make sure by now button is the correct type
1300      if ($.type(button) !== 'object') {
1301        throw new Error('button with key "' + key + '" must be an object');
1302      }
1303
1304      if (!button.label) {
1305        // the lack of an explicit label means we'll assume the key is good enough
1306        button.label = key;
1307      }
1308
1309      if (!button.className) {
1310        var isPrimary = false;
1311        if (options.swapButtonOrder) {
1312          isPrimary = index === 0;
1313        }
1314        else {
1315          isPrimary = index === total - 1;
1316        }
1317
1318        if (total <= 2 && isPrimary) {
1319          // always add a primary to the main option in a one or two-button dialog
1320          button.className = 'btn-primary';
1321        } else {
1322          // adding both classes allows us to target both BS3 and BS4 without needing to check the version
1323          button.className = 'btn-secondary btn-default';
1324        }
1325      }
1326    });
1327
1328    return options;
1329  }
1330
1331
1332  //  Returns a count of the properties defined on the object
1333  function getKeyLength(obj) {
1334    return Object.keys(obj).length;
1335  }
1336
1337
1338  //  Tiny wrapper function around jQuery.each; just adds index as the third parameter
1339  function each(collection, iterator) {
1340    var index = 0;
1341    $.each(collection, function (key, value) {
1342      iterator(key, value, index++);
1343    });
1344  }
1345
1346
1347  function focusPrimaryButton(e) {
1348    e.data.dialog.find('.bootbox-accept').first().trigger('focus');
1349  }
1350
1351
1352  function destroyModal(e) {
1353    // ensure we don't accidentally intercept hidden events triggered
1354    // by children of the current dialog. We shouldn't need to handle this anymore, 
1355    // now that Bootstrap namespaces its events, but still worth doing.
1356    if (e.target === e.data.dialog[0]) {
1357      e.data.dialog.remove();
1358    }
1359  }
1360
1361
1362  function unbindModal(e) {
1363    if (e.target === e.data.dialog[0]) {
1364      e.data.dialog.off('escape.close.bb');
1365      e.data.dialog.off('click');
1366    }
1367  }
1368
1369
1370  //  Handle the invoked dialog callback
1371  function processCallback(e, dialog, callback) {
1372    e.stopPropagation();
1373    e.preventDefault();
1374
1375    // by default we assume a callback will get rid of the dialog,
1376    // although it is given the opportunity to override this
1377
1378    // so, if the callback can be invoked and it *explicitly returns false*
1379    // then we'll set a flag to keep the dialog active...
1380    var preserveDialog = $.isFunction(callback) && callback.call(dialog, e) === false;
1381
1382    // ... otherwise we'll bin it
1383    if (!preserveDialog) {
1384      dialog.modal('hide');
1385    }
1386  }
1387
1388  // Validate `min` and `max` values based on the current `inputType` value
1389  function minAndMaxAreValid(type, min, max) {
1390    var result = false;
1391    var minValid = true;
1392    var maxValid = true;
1393
1394    if (type === 'date') {
1395      if (min !== undefined && !(minValid = dateIsValid(min))) {
1396        console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your min value may not be enforced by this browser.');
1397      }
1398      else if (max !== undefined && !(maxValid = dateIsValid(max))) {
1399        console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your max value may not be enforced by this browser.');
1400      }
1401    }
1402    else if (type === 'time') {
1403      if (min !== undefined && !(minValid = timeIsValid(min))) {
1404        throw new Error('"min" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.');
1405      }
1406      else if (max !== undefined && !(maxValid = timeIsValid(max))) {
1407        throw new Error('"max" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.');
1408      }
1409    }
1410    else {
1411      if (min !== undefined && isNaN(min)) {
1412        minValid = false;
1413        throw new Error('"min" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-min for more information.');
1414      }
1415
1416      if (max !== undefined && isNaN(max)) {
1417        maxValid = false;
1418        throw new Error('"max" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max for more information.');
1419      }
1420    }
1421
1422    if (minValid && maxValid) {
1423      if (max <= min) {
1424        throw new Error('"max" must be greater than "min". See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max for more information.');
1425      }
1426      else {
1427        result = true;
1428      }
1429    }
1430
1431    return result;
1432  }
1433
1434  function timeIsValid(value) {
1435    return /([01][0-9]|2[0-3]):[0-5][0-9]?:[0-5][0-9]/.test(value);
1436  }
1437
1438  function dateIsValid(value) {
1439    return /(\d{4})-(\d{2})-(\d{2})/.test(value);
1440  }
1441
1442  //  The Bootbox object
1443  return exports;
1444}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.