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">×</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.