1/** 2 * bootbox.js [v4.3.0] 3 * 4 * http://bootboxjs.com/license.txt 5 */ 6 7// @see https://github.com/makeusabrew/bootbox/issues/180 8// @see https://github.com/makeusabrew/bootbox/issues/186 9(function (root, factory) { 10 11 "use strict"; 12 if (typeof define === "function" && define.amd) { 13 // AMD. Register as an anonymous module. 14 define(["jquery"], factory); 15 } else if (typeof exports === "object") { 16 // Node. Does not work with strict CommonJS, but 17 // only CommonJS-like environments that support module.exports, 18 // like Node. 19 module.exports = factory(require("jquery")); 20 } else { 21 // Browser globals (root is window) 22 root.bootbox = factory(root.jQuery); 23 } 24 25}(this, function init($, undefined) { 26 27 "use strict"; 28 29 // the base DOM structure needed to create a modal 30 var templates = { 31 dialog: 32 "<div class='bootbox modal' tabindex='-1' role='dialog'>" + 33 "<div class='modal-dialog'>" + 34 "<div class='modal-content'>" + 35 "<div class='modal-body'><div class='bootbox-body'></div></div>" + 36 "</div>" + 37 "</div>" + 38 "</div>", 39 header: 40 "<div class='modal-header'>" + 41 "<h4 class='modal-title'></h4>" + 42 "</div>", 43 footer: 44 "<div class='modal-footer'></div>", 45 closeButton: 46 "<button type='button' class='bootbox-close-button close' data-dismiss='modal' aria-hidden='true'>×</button>", 47 form: 48 "<form class='bootbox-form'></form>", 49 inputs: { 50 text: 51 "<input class='bootbox-input bootbox-input-text form-control' autocomplete=off type=text />", 52 textarea: 53 "<textarea class='bootbox-input bootbox-input-textarea form-control'></textarea>", 54 email: 55 "<input class='bootbox-input bootbox-input-email form-control' autocomplete='off' type='email' />", 56 select: 57 "<select class='bootbox-input bootbox-input-select form-control'></select>", 58 checkbox: 59 "<div class='checkbox'><label>
59<input class='bootbox-input bootbox-input-checkbox' type='checkbox' /></label></div>", 60 date: 61 "<input class='bootbox-input bootbox-input-date form-control' autocomplete=off type='date' />", 62 time: 63 "<input class='bootbox-input bootbox-input-time form-control' autocomplete=off type='time' />", 64 number: 65 "<input class='bootbox-input bootbox-input-number form-control' autocomplete=off type='number' />", 66 password: 67 "<input class='bootbox-input bootbox-input-password form-control' autocomplete='off' type='password' />" 68 } 69 }; 70 71 var defaults = { 72 // default language 73 locale: "br", 74 // show backdrop or not 75 backdrop: true, 76 // animate the modal in/out 77 animate: true, 78 // additional class string applied to the top level dialog 79 className: null, 80 // whether or not to include a close button 81 closeButton: true, 82 // show the dialog immediately by default 83 show: true, 84 // dialog container 85 container: "body" 86 }; 87 88 // our public object; augmented after our private API 89 var exports = {}; 90 91 /** 92 * @private 93 */ 94 function _t(key) { 95 var locale = locales[defaults.locale]; 96 return locale ? locale[key] : locales.en[key]; 97 } 98 99 function processCallback(e, dialog, callback) { 100 e.stopPropagation(); 101 e.preventDefault(); 102 103 // by default we assume a callback will get rid of the dialog, 104 // although it is given the opportunity to override this 105 106 // so, if the callback can be invoked and it *explicitly returns false* 107 // then we'll set a flag to keep the dialog active... 108 var preserveDialog = $.isFunction(callback) && callback(e) === false; 109 110 // ... otherwise we'll bin it 111 if (!preserveDialog) { 112 dialog.modal("hide"); 113 } 114 } 115 116 function getKeyLength(obj) { 117 // @TODO defer to Object.keys(x).length if available? 118 var k, t = 0; 119 for (k in obj) { 120 t ++; 121 } 122 return t; 123 } 124 125 function each(collection, iterator) { 126 var index = 0; 127 $.each(collection, function(key, value) { 128 iterator(key, value, index++); 129 }); 130 } 131 132 function sanitize(options) { 133 var buttons; 134 var total; 135 136 if (typeof options !== "object") { 137 throw new Error("Please supply an object of options"); 138 } 139 140 if (!options.message) { 141 throw new Error("Please specify a message"); 142 } 143 144 // make sure any supplied options take precedence over defaults 145 options = $.extend({}, defaults, options); 146 147 if (!options.buttons) { 148 options.buttons = {}; 149 } 150 151 // we only support Bootstrap's "static" and false backdrop args 152 // supporting true would mean you could dismiss the dialog without 153 // explicitly interacting with it 154 options.backdrop = options.backdrop ? "static" : false; 155 156 buttons = options.buttons; 157 158 total = getKeyLength(buttons); 159 160 each(buttons, function(key, button, index) { 161 162 if ($.isFunction(button)) { 163 // short form, assume value is our callback. Since button 164 // isn't an object it isn't a reference either so re-assign it 165 button = buttons[key] = { 166 callback: button 167 }; 168 } 169 170 // before any further checks make sure by now button is the correct type 171 if ($.type(button) !== "object") { 172 throw new Error("button with key " + key + " must be an object"); 173 } 174 175 if (!button.label) { 176 // the lack of an explicit label means we'll assume the key is good enough 177 button.label = key; 178 } 179 180 if (!button.className) { 181 if (total <= 2 && index === total-1) { 182 // always add a primary to the main option in a two-button dialog 183 button.className = "btn-primary"; 184 } else { 185 button.className = "btn-default"; 186 } 187 } 188 }); 189 190 return options; 191 } 192 193 /** 194 * map a flexible set of arguments into a single returned object 195 * if args.length is already one just return it, otherwise 196 * use the properties argument to map the unnamed args to 197 * object properties 198 * so in the latter case: 199 * mapArguments(["foo", $.noop], ["message", "callback"]) 200 * -> { message: "foo", callback: $.noop } 201 */ 202 function mapArguments(args, properties) { 203 var argn = args.length; 204 var options = {}; 205 206 if (argn < 1 || argn > 2) { 207 throw new Error("Invalid argument length"); 208 } 209 210 if (argn === 2 || typeof args[0] === "string") { 211 options[properties[0]] = args[0]; 212 options[properties[1]] = args[1]; 213 } else { 214 options = args[0]; 215 } 216 217 return options; 218 } 219 220 /** 221 * merge a set of default dialog options with user supplied arguments 222 */ 223 function mergeArguments(defaults, args, properties) { 224 return $.extend( 225 // deep merge 226 true, 227 // ensure the target is an empty, unreferenced object 228 {}, 229 // the base options object for this type of dialog (often just buttons) 230 defaults, 231 // args could be an object or array; if it's an array properties will 232 // map it to a proper options object 233 mapArguments( 234 args, 235 properties 236 ) 237 ); 238 } 239 240 /** 241 * this entry-level method makes heavy use of composition to take a simple 242 * range of inputs and return valid options suitable for passing to bootbox.dialog 243 */ 244 function mergeDialogOptions(className, labels, properties, args) { 245 // build up a base set of dialog properties 246 var baseOptions = { 247 className: "bootbox-" + className, 248 buttons: createLabels.apply(null, labels) 249 }; 250 251 // ensure the buttons properties generated, *after* merging 252 // with user args are still valid against the supplied labels 253 return validateButtons( 254 // merge the generated base properties with user supplied arguments 255 mergeArguments( 256 baseOptions, 257 args, 258 // if args.length > 1, properties specify how each arg maps to an object key 259 properties 260 ), 261 labels 262 ); 263 } 264 265 /** 266 * from a given list of arguments return a suitable object of button labels 267 * all this does is normalise the given labels and translate them where possible 268 * e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" } 269 */ 270 function createLabels() { 271 var buttons = {}; 272 273 for (var i = 0, j = arguments.length; i < j; i++) { 274 var argument = arguments[i]; 275 var key = argument.toLowerCase(); 276 var value = argument.toUpperCase(); 277 278 buttons[key] = { 279 label: _t(value) 280 }; 281 } 282 283 return buttons; 284 } 285 286 function validateButtons(options, buttons) { 287 var allowedButtons = {}; 288 each(buttons, function(key, value) { 289 allowedButtons[value] = true; 290 }); 291 292 each(options.buttons, function(key) { 293 if (allowedButtons[key] === undefined) { 294 throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")"); 295 } 296 }); 297 298 return options; 299 } 300 301 exports.alert = function() { 302 var options; 303 304 options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments); 305 306 if (options.callback && !$.isFunction(options.callback)) { 307 throw new Error("alert requires callback property to be a function when provided"); 308 } 309 310 /** 311 * overrides 312 */ 313 options.buttons.ok.callback = options.onEscape = function() { 314 if ($.isFunction(options.callback)) { 315 return options.callback(); 316 } 317 return true; 318 }; 319 320 return exports.dialog(options); 321 }; 322 323 exports.confirm = function() { 324 var options; 325 326 options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments); 327 328 /** 329 * overrides; undo anything the user tried to set they shouldn't have 330 */ 331 options.buttons.cancel.callback = options.onEscape = function() { 332 return options.callback(false); 333 }; 334 335 options.buttons.confirm.callback = function() { 336 return options.callback(true); 337 }; 338 339 // confirm specific validation 340 if (!$.isFunction(options.callback)) { 341 throw new Error("confirm requires a callback"); 342 } 343 344 return exports.dialog(options); 345 }; 346 347 exports.prompt = function() { 348 var options; 349 var defaults; 350 var dialog; 351 var form; 352 var input; 353 var shouldShow; 354 var inputOptions; 355 356 // we have to create our form first otherwise 357 // its value is undefined when gearing up our options 358 // @TODO this could be solved by allowing message to 359 // be a function instead... 360 form = $(templates.form); 361 362 // prompt defaults are more complex than others in that 363 // users can override more defaults 364 // @TODO I don't like that prompt has to do a lot of heavy 365 // lifting which mergeDialogOptions can *almost* support already 366 // just because of 'value' and 'inputType' - can we refactor? 367 defaults = { 368 className: "bootbox-prompt", 369 buttons: createLabels("cancel", "confirm"), 370 value: "", 371 inputType: "text" 372 }; 373 374 options = validateButtons( 375 mergeArguments(defaults, arguments, ["title", "callback"]), 376 ["cancel", "confirm"] 377 ); 378 379 // capture the user's show value;
379 we always set this to false before 380 // spawning the dialog to give us a chance to attach some handlers to 381 // it, but we need to make sure we respect a preference not to show it 382 shouldShow = (options.show === undefined) ? true : options.show; 383 384 /** 385 * overrides; undo anything the user tried to set they shouldn't have 386 */ 387 options.message = form; 388 389 options.buttons.cancel.callback = options.onEscape = function() { 390 return options.callback(null); 391 }; 392 393 options.buttons.confirm.callback = function() { 394 var value; 395 396 switch (options.inputType) { 397 case "text": 398 case "textarea": 399 case "email": 400 case "select": 401 case "date": 402 case "time": 403 case "number": 404 case "password": 405 value = input.val(); 406 break; 407 408 case "checkbox": 409 var checkedItems = input.find("input:checked"); 410 411 // we assume that checkboxes are always multiple, 412 // hence we default to an empty array 413 value = []; 414 415 each(checkedItems, function(_, item) { 416 value.push($(item).val()); 417 }); 418 break; 419 } 420 421 return options.callback(value); 422 }; 423 424 options.show = false; 425 426 // prompt specific validation 427 if (!options.title) { 428 throw new Error("prompt requires a title"); 429 } 430 431 if (!$.isFunction(options.callback)) { 432 throw new Error("prompt requires a callback"); 433 } 434 435 if (!templates.inputs[options.inputType]) { 436 throw new Error("invalid prompt type"); 437 } 438 439 // create the input based on the supplied type 440 input = $(templates.inputs[options.inputType]); 441 442 switch (options.inputType) { 443 case "text": 444 case "textarea": 445 case "email": 446 case "date": 447 case "time": 448 case "number": 449 case "password": 450 input.val(options.value); 451 break; 452 453 case "select": 454 var groups = {}; 455 inputOptions = options.inputOptions || []; 456 457 if (!inputOptions.length) { 458 throw new Error("prompt with select requires options"); 459 } 460 461 each(inputOptions, function(_, option) { 462 463 // assume the element to attach to is the input... 464 var elem = input; 465 466 if (option.value === undefined || option.text === undefined) { 467 throw new Error("given options in wrong format"); 468 } 469 470 471 // ... but override that element if this option sits in a group 472 473 if (option.group) { 474 // initialise group if necessary 475 if (!groups[option.group]) { 476 groups[option.group] = $("<optgroup/>").attr("label", option.group); 477 } 478 479 elem = groups[option.group]; 480 } 481 482 elem.append("<option value='" + option.value + "'>" + option.text + "</option>"); 483 }); 484 485 each(groups, function(_, group) { 486 input.append(group); 487 }); 488 489 // safe to set a select's value as per a normal input 490 input.val(options.value); 491 break; 492 493 case "checkbox": 494 var values = $.isArray(options.value) ? options.value : [options.value]; 495 inputOptions = options.inputOptions || []; 496 497 if (!inputOptions.length) { 498 throw new Error("prompt with checkbox requires options"); 499 } 500 501 if (!inputOptions[0].value || !inputOptions[0].text) { 502 throw new Error("given options in wrong format"); 503 } 504 505 // checkboxes have to nest within a containing element, so 506 // they break the rules a bit and we end up re-assigning 507 // our 'input' element to this container instead 508 input = $("<div/>"); 509 510 each(inputOptions, function(_, option) { 511 var checkbox = $(templates.inputs[options.inputType]); 512 513 checkbox.find("input").attr("value", option.value); 514 checkbox.find("label").append(option.text); 515 516 // we've ensured values is an array so we can always iterate over it 517 each(values, function(_, value) { 518 if (value === option.value) { 519 checkbox.find("input").prop("checked", true); 520 } 521 }); 522 523 input.append(checkbox); 524 }); 525 break; 526 } 527 528 if (options.placeholder) { 529 input.attr("placeholder", options.placeholder); 530 } 531 532 if(options.pattern){ 533 input.attr("pattern", options.pattern); 534 } 535 536 // now place it in our form 537 form.append(input); 538 539 form.on("submit", function(e) { 540 e.preventDefault(); 541 // Fix for SammyJS (or similar JS routing library) hijacking the form post. 542 e.stopPropagation(); 543 // @TODO can we actually click *the* button object instead? 544 // e.g. buttons.confirm.click() or similar 545 dialog.find(".btn-primary").click(); 546 }); 547 548 dialog = exports.dialog(options); 549 550 // clear the existing handler focusing the submit button... 551 dialog.off("shown.bs.modal"); 552 553 // ...and replace it with one focusing our input, if possible 554 dialog.on("shown.bs.modal", function() { 555 input.focus(); 556 }); 557 558 if (shouldShow === true) { 559 dialog.modal("show"); 560 } 561 562 return dialog; 563 }; 564 565 exports.dialog = function(options) { 566 options = sanitize(options); 567 568 var dialog = $(templates.dialog);
569 var innerDialog = dialog.find(".modal-dialog"); 570 var body = dialog.find(".modal-body"); 571 var buttons = options.buttons; 572 var buttonStr = ""; 573 var callbacks = { 574 onEscape: options.onEscape 575 }; 576 577 each(buttons, function(key, button) { 578 579 // @TODO I don't like this string appending to itself; bit dirty. Needs reworking 580 // can we just build up button elements instead? slower but neater. Then button 581 // can just become a template too 582 buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>"; 583 callbacks[key] = button.callback; 584 }); 585 586 body.find(".bootbox-body").html(options.message); 587 588 if (options.animate === true) { 589 dialog.addClass("fade"); 590 } 591 592 if (options.className) { 593 dialog.addClass(options.className); 594 } 595 596 if (options.size === "large") { 597 innerDialog.addClass("modal-lg"); 598 } 599 600 if (options.size === "small") { 601 innerDialog.addClass("modal-sm"); 602 } 603 604 if (options.title) { 605 body.before(templates.header); 606 } 607 608 if (options.closeButton) { 609 var closeButton = $(templates.closeButton); 610 611 if (options.title) { 612 dialog.find(".modal-header").prepend(closeButton); 613 } else { 614 closeButton.css("margin-top", "-10px").prependTo(body); 615 } 616 } 617 618 if (options.title) { 619 dialog.find(".modal-title").html(options.title); 620 } 621 622 if (buttonStr.length) { 623 body.after(templates.footer); 624 dialog.find(".modal-footer").html(buttonStr); 625 } 626 627 628 /** 629 * Bootstrap event listeners; used handle extra 630 * setup & teardown required after the underlying 631 * modal has performed certain actions 632 */ 633 634 dialog.on("hidden.bs.modal", function(e) { 635 // ensure we don't accidentally intercept hidden events triggered 636 // by children of the current dialog. We shouldn't anymore now BS 637 // namespaces its events; but still worth doing 638 if (e.target === this) { 639 dialog.remove(); 640 } 641 }); 642 643 /* 644 dialog.on("show.bs.modal", function() { 645 // sadly this doesn't work; show is called *just* before 646 // the backdrop is added so we'd need a setTimeout hack or 647 // otherwise... leaving in as would be nice 648 if (options.backdrop) { 649 dialog.next(".modal-backdrop").addClass("bootbox-backdrop"); 650 } 651 }); 652 */ 653 654 dialog.on("shown.bs.modal", function() { 655 dialog.find(".btn-primary:first").focus(); 656 }); 657 658 /** 659 * Bootbox event listeners; experimental and may not last 660 * just an attempt to decouple some behaviours from their 661 * respective triggers 662 */ 663 664 dialog.on("escape.close.bb", function(e) { 665 if (callbacks.onEscape) { 666 processCallback(e, dialog, callbacks.onEscape); 667 } 668 }); 669 670 /** 671 * Standard jQuery event listeners; used to handle user 672 * interaction with our dialog 673 */ 674 675 dialog.on("click", ".modal-footer button", function(e) { 676 var callbackKey = $(this).data("bb-handler"); 677 678 processCallback(e, dialog, callbacks[callbackKey]); 679 680 }); 681 682 dialog.on("click", ".bootbox-close-button", function(e) { 683 // onEscape might be falsy but that's fine; the fact is 684 // if the user has managed to click the close button we 685 // have to close the dialog, callback or not 686 processCallback(e, dialog, callbacks.onEscape); 687 }); 688 689 dialog.on("keyup", function(e) { 690 if (e.which === 27) { 691 dialog.trigger("escape.close.bb"); 692 } 693 }); 694 695 // the remainder of this method simply deals with adding our 696 // dialogent to the DOM, augmenting it with Bootstrap's modal 697 // functionality and then giving the resulting object back 698 // to our caller 699 700 $(options.container).append(dialog); 701 702 dialog.modal({ 703 backdrop: options.backdrop, 704 keyboard: false, 705 show: false 706 }); 707 708 if (options.show) { 709 dialog.modal("show"); 710 } 711 712 // @TODO should we return the raw element here or should 713 // we wrap it in an object on which we can expose some neater 714 // methods, e.g. var d = bootbox.alert(); d.hide(); instead 715 // of d.modal("hide"); 716 717 /* 718 function BBDialog(elem) { 719 this.elem = elem; 720 } 721 722 BBDialog.prototype = { 723 hide: function() { 724 return this.elem.modal("hide"); 725 }, 726 show: function() { 727 return this.elem.modal("show"); 728 } 729 }; 730 */ 731 732 return dialog; 733 734 }; 735 736 exports.setDefaults = function() { 737 var values = {}; 738 739 if (arguments.length === 2) { 740 // allow passing of single key/value... 741 values[arguments[0]] = arguments[1]; 742 } else { 743 // ... and as an object too 744 values = arguments[0]; 745 } 746 747 $.extend(defaults, values); 748 }; 749 750 exports.hideAll = function() { 751 $(".bootbox").modal("hide"); 752 753 return exports; 754 }; 755 756 757 /** 758 * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are 759 * unlikely to be required. If this gets too large it can be split out into separate JS files. 760 */ 761 var locales = { 762 br : { 763 OK : "OK", 764 CANCEL : "Não", 765 CONFIRM : "Sim" 766 }, 767 cs : { 768 OK : "OK", 769 CANCEL : "ZruÅ¡it", 770 CONFIRM : "Potvrdit" 771 }, 772 da : { 773 OK : "OK", 774 CANCEL : "Annuller", 775 CONFIRM : "Accepter" 776 }, 777 de : { 778 OK : "OK", 779 CANCEL : "Abbrechen", 780 CONFIRM : "Akzeptieren" 781 }, 782 el : { 783 OK : "ÎνÏάξει", 784 CANCEL : "ÎκÏÏÏÏη", 785 CONFIRM : "ÎÏιβεβαίÏÏη" 786 }, 787 en : { 788 OK : "OK", 789 CANCEL : "Cancel", 790 CONFIRM : "OK" 791 }, 792 es : { 793 OK : "OK", 794 CANCEL : "Cancelar", 795 CONFIRM : "Aceptar" 796 }, 797 et : { 798 OK : "OK", 799 CANCEL : "Katkesta", 800 CONFIRM : "OK" 801 }, 802 fi : { 803 OK : "OK", 804 CANCEL : "Peruuta", 805 CONFIRM : "OK" 806 }, 807 fr : { 808 OK : "OK", 809 CANCEL : "Annuler", 810 CONFIRM : "D'accord" 811 }, 812 he : { 813 OK : "××ש×ר", 814 CANCEL : "×××××", 815 CONFIRM : "××ש×ר" 816 }, 817 id : { 818 OK : "OK", 819 CANCEL : "Batal", 820 CONFIRM : "OK" 821 }, 822 it : { 823 OK : "OK", 824 CANCEL : "Annulla", 825 CONFIRM : "Conferma" 826 }, 827 ja : { 828 OK : "OK", 829 CANCEL : "ãã£ã³ã»ã«", 830 CONFIRM : "確èª" 831 }, 832 lt : { 833 OK : "Gerai", 834 CANCEL : "AtÅ¡aukti", 835 CONFIRM : "Patvirtinti" 836 }, 837 lv : { 838 OK : "Labi", 839 CANCEL : "Atcelt", 840 CONFIRM : "ApstiprinÄt" 841 }, 842 nl : { 843 OK : "OK", 844 CANCEL : "Annuleren", 845 CONFIRM : "Accepteren" 846 }, 847 no : { 848 OK : "OK", 849 CANCEL : "Avbryt", 850 CONFIRM : "OK" 851 }, 852 pl : { 853 OK : "OK", 854 CANCEL : "Anuluj", 855 CONFIRM : "Potwierdź" 856 }, 857 pt : { 858 OK : "OK", 859 CANCEL : "Cancelar", 860 CONFIRM : "Confirmar" 861 }, 862 ru : { 863 OK : "OK", 864 CANCEL : "ÐÑмена", 865 CONFIRM : "ÐÑимениÑÑ" 866 }, 867 sv : { 868 OK : "OK", 869 CANCEL : "Avbryt", 870 CONFIRM : "OK" 871 }, 872 tr : { 873 OK : "Tamam", 874 CANCEL : "İptal", 875 CONFIRM : "Onayla" 876 }, 877 zh_CN : { 878 OK : "OK", 879 CANCEL : "åæ¶", 880 CONFIRM : "确认" 881 }, 882 zh_TW : { 883 OK : "OK", 884 CANCEL : "åæ¶", 885 CONFIRM : "確èª" 886 } 887 }; 888 889 exports.init = function(_$) { 890 return init(_$ || $); 891 }; 892 893 return exports; 894}));
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.