bootbox.js 26.0 KB
Newer Older
C
Catouse 已提交
1
/* ========================================================================
C
Catouse 已提交
2 3
 * bootbox.js [master branch]
 * http://bootboxjs.com/license.txt
C
Catouse 已提交
4 5 6 7 8 9
 * ========================================================================
 * Updates in ZUI:
 * 1. Determine client language and apply setting automatically.
 * 2. Changed button position.
 * ======================================================================== */

C
Catouse 已提交
10 11 12

// @see https://github.com/makeusabrew/bootbox/issues/180
// @see https://github.com/makeusabrew/bootbox/issues/186
C
Catouse 已提交
13 14
(function(root, factory){

15
    'use strict';
C
Catouse 已提交
16 17 18 19
    if (typeof define === "function" && define.amd)
    {
        // AMD. Register as an anonymous module.
        define(["jquery"], factory);
C
Catouse 已提交
20
    }
C
Catouse 已提交
21 22 23 24 25 26
    else if (typeof exports === "object")
    {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory(require("jquery"));
C
Catouse 已提交
27
    }
C
Catouse 已提交
28 29 30 31
    else
    {
        // Browser globals (root is window)
        root.bootbox = factory(root.jQuery);
C
Catouse 已提交
32 33
    }

C
Catouse 已提交
34 35
}(this, function init($, undefined)
{
C
Catouse 已提交
36

37
    'use strict';
C
Catouse 已提交
38

C
Catouse 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    // the base DOM structure needed to create a modal
    var templates = {
        dialog: "<div class='bootbox modal' tabindex='-1' role='dialog'>" +
            "<div class='modal-dialog'>" +
            "<div class='modal-content'>" +
            "<div class='modal-body'><div class='bootbox-body'></div></div>" +
            "</div>" +
            "</div>" +
            "</div>",
        header: "<div class='modal-header'>" +
            "<h4 class='modal-title'></h4>" +
            "</div>",
        footer: "<div class='modal-footer'></div>",
        closeButton: "<button type='button' class='bootbox-close-button close' data-dismiss='modal' aria-hidden='true'>&times;</button>",
        form: "<form class='bootbox-form'></form>",
        inputs:
        {
            text: "<input class='bootbox-input bootbox-input-text form-control' autocomplete=off type=text />",
            textarea: "<textarea class='bootbox-input bootbox-input-textarea form-control'></textarea>",
            email: "<input class='bootbox-input bootbox-input-email form-control' autocomplete='off' type='email' />",
            select: "<select class='bootbox-input bootbox-input-select form-control'></select>",
            checkbox: "<div class='checkbox'><label><input class='bootbox-input bootbox-input-checkbox' type='checkbox' /></label></div>",
            date: "<input class='bootbox-input bootbox-input-date form-control' autocomplete=off type='date' />",
            time: "<input class='bootbox-input bootbox-input-time form-control' autocomplete=off type='time' />",
            number: "<input class='bootbox-input bootbox-input-number form-control' autocomplete=off type='number' />",
            password: "<input class='bootbox-input bootbox-input-password form-control' autocomplete='off' type='password' />"
        }
    };
C
Catouse 已提交
67

C
Catouse 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    var defaults = {
        // default language
        locale: judgeClientLang(),
        // show backdrop or not
        backdrop: true,
        // animate the modal in/out
        animate: true,
        // additional class string applied to the top level dialog
        className: null,
        // whether or not to include a close button
        closeButton: true,
        // show the dialog immediately by default
        show: true,
        // dialog container
        container: "body"
    };
C
Catouse 已提交
84

C
Catouse 已提交
85 86
    // our public object; augmented after our private API
    var exports = {};
C
Catouse 已提交
87

C
Catouse 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100
    function judgeClientLang()
    {
        var lang;
        if (typeof(config) != 'undefined' && config.clientLang)
        {
            lang = config.clientLang;
        }
        else
        {
            var hl = $('html').attr('lang');
            lang = hl ? hl : 'en';
        }
        return lang.replace('-', '_').toLowerCase();
C
Catouse 已提交
101 102
    }

C
Catouse 已提交
103 104 105 106 107 108 109 110
    /**
     * @private
     */
    function _t(key)
    {
        var locale = locales[defaults.locale];
        return locale ? locale[key] : locales.en[key];
    }
C
Catouse 已提交
111

C
Catouse 已提交
112 113 114 115
    function processCallback(e, dialog, callback)
    {
        e.stopPropagation();
        e.preventDefault();
C
Catouse 已提交
116

C
Catouse 已提交
117 118
        // by default we assume a callback will get rid of the dialog,
        // although it is given the opportunity to override this
C
Catouse 已提交
119

C
Catouse 已提交
120 121 122
        // so, if the callback can be invoked and it *explicitly returns false*
        // then we'll set a flag to keep the dialog active...
        var preserveDialog = $.isFunction(callback) && callback(e) === false;
C
Catouse 已提交
123

C
Catouse 已提交
124 125 126 127
        // ... otherwise we'll bin it
        if (!preserveDialog)
        {
            dialog.modal("hide");
C
Catouse 已提交
128
        }
C
Catouse 已提交
129
    }
C
Catouse 已提交
130

C
Catouse 已提交
131 132 133 134 135 136 137 138 139
    function getKeyLength(obj)
    {
        // @TODO defer to Object.keys(x).length if available?
        var k, t = 0;
        for (k in obj)
        {
            t++;
        }
        return t;
C
Catouse 已提交
140 141
    }

C
Catouse 已提交
142 143 144 145 146 147 148
    function each(collection, iterator)
    {
        var index = 0;
        $.each(collection, function(key, value)
        {
            iterator(key, value, index++);
        });
C
Catouse 已提交
149 150
    }

C
Catouse 已提交
151 152 153 154
    function sanitize(options)
    {
        var buttons;
        var total;
C
Catouse 已提交
155

C
Catouse 已提交
156 157 158 159
        if (typeof options !== "object")
        {
            throw new Error("Please supply an object of options");
        }
C
Catouse 已提交
160

C
Catouse 已提交
161 162 163 164
        if (!options.message)
        {
            throw new Error("Please specify a message");
        }
C
Catouse 已提交
165

C
Catouse 已提交
166 167 168
        // make sure any supplied options take precedence over defaults
        options = $.extend(
        {}, defaults, options);
C
Catouse 已提交
169

C
Catouse 已提交
170 171 172 173
        if (!options.buttons)
        {
            options.buttons = {};
        }
C
Catouse 已提交
174

C
Catouse 已提交
175 176 177 178
        // we only support Bootstrap's "static" and false backdrop args
        // supporting true would mean you could dismiss the dialog without
        // explicitly interacting with it
        options.backdrop = options.backdrop ? "static" : false;
C
Catouse 已提交
179

C
Catouse 已提交
180
        buttons = options.buttons;
C
Catouse 已提交
181

C
Catouse 已提交
182
        total = getKeyLength(buttons);
C
Catouse 已提交
183

C
Catouse 已提交
184 185
        each(buttons, function(key, button, index)
        {
C
Catouse 已提交
186

C
Catouse 已提交
187 188 189 190 191 192 193 194
            if ($.isFunction(button))
            {
                // short form, assume value is our callback. Since button
                // isn't an object it isn't a reference either so re-assign it
                button = buttons[key] = {
                    callback: button
                };
            }
C
Catouse 已提交
195

C
Catouse 已提交
196 197 198 199 200
            // before any further checks make sure by now button is the correct type
            if ($.type(button) !== "object")
            {
                throw new Error("button with key " + key + " must be an object");
            }
C
Catouse 已提交
201

C
Catouse 已提交
202 203 204 205 206
            if (!button.label)
            {
                // the lack of an explicit label means we'll assume the key is good enough
                button.label = key;
            }
C
Catouse 已提交
207

C
Catouse 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
            if (!button.className)
            {
                if (total == 1 || (total >= 2 && key === 'confirm'))
                {
                    // always add a primary to the main option in a two-button dialog
                    button.className = "btn-primary";
                }
                else
                {
                    button.className = "btn-default";
                }
            }
        });

        return options;
    }
C
Catouse 已提交
224 225

    /**
C
Catouse 已提交
226 227 228 229 230 231 232
     * map a flexible set of arguments into a single returned object
     * if args.length is already one just return it, otherwise
     * use the properties argument to map the unnamed args to
     * object properties
     * so in the latter case:
     * mapArguments(["foo", $.noop], ["message", "callback"])
     * -> { message: "foo", callback: $.noop }
C
Catouse 已提交
233
     */
C
Catouse 已提交
234 235 236 237 238 239 240 241 242
    function mapArguments(args, properties)
    {
        var argn = args.length;
        var options = {};

        if (argn < 1 || argn > 2)
        {
            throw new Error("Invalid argument length");
        }
C
Catouse 已提交
243

C
Catouse 已提交
244 245 246 247 248 249 250 251 252
        if (argn === 2 || typeof args[0] === "string")
        {
            options[properties[0]] = args[0];
            options[properties[1]] = args[1];
        }
        else
        {
            options = args[0];
        }
C
Catouse 已提交
253

C
Catouse 已提交
254
        return options;
C
Catouse 已提交
255 256
    }

C
Catouse 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
    /**
     * merge a set of default dialog options with user supplied arguments
     */
    function mergeArguments(defaults, args, properties)
    {
        return $.extend(
            // deep merge
            true,
            // ensure the target is an empty, unreferenced object
            {},
            // the base options object for this type of dialog (often just buttons)
            defaults,
            // args could be an object or array; if it's an array properties will
            // map it to a proper options object
            mapArguments(
                args,
                properties
            )
        );
C
Catouse 已提交
276 277 278
    }

    /**
C
Catouse 已提交
279 280
     * this entry-level method makes heavy use of composition to take a simple
     * range of inputs and return valid options suitable for passing to bootbox.dialog
C
Catouse 已提交
281
     */
C
Catouse 已提交
282 283 284 285 286 287 288
    function mergeDialogOptions(className, labels, properties, args)
    {
        //  build up a base set of dialog properties
        var baseOptions = {
            className: "bootbox-" + className,
            buttons: createLabels.apply(null, labels)
        };
C
Catouse 已提交
289

C
Catouse 已提交
290 291 292 293 294 295 296 297 298 299 300 301
        // ensure the buttons properties generated, *after* merging
        // with user args are still valid against the supplied labels
        return validateButtons(
            // merge the generated base properties with user supplied arguments
            mergeArguments(
                baseOptions,
                args,
                // if args.length > 1, properties specify how each arg maps to an object key
                properties
            ),
            labels
        );
C
Catouse 已提交
302 303
    }

C
Catouse 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
    /**
     * from a given list of arguments return a suitable object of button labels
     * all this does is normalise the given labels and translate them where possible
     * e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" }
     */
    function createLabels()
    {
        var buttons = {};

        for (var i = 0, j = arguments.length; i < j; i++)
        {
            var argument = arguments[i];
            var key = argument.toLowerCase();
            var value = argument.toUpperCase();

            buttons[key] = {
                label: _t(value)
            };
        }
C
Catouse 已提交
323

C
Catouse 已提交
324
        return buttons;
C
Catouse 已提交
325 326
    }

C
Catouse 已提交
327 328 329 330 331 332 333
    function validateButtons(options, buttons)
    {
        var allowedButtons = {};
        each(buttons, function(key, value)
        {
            allowedButtons[value] = true;
        });
C
Catouse 已提交
334

C
Catouse 已提交
335 336 337 338 339 340 341
        each(options.buttons, function(key)
        {
            if (allowedButtons[key] === undefined)
            {
                throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
            }
        });
C
Catouse 已提交
342

C
Catouse 已提交
343 344
        return options;
    }
C
Catouse 已提交
345

C
Catouse 已提交
346 347 348
    exports.alert = function()
    {
        var options;
C
Catouse 已提交
349

C
Catouse 已提交
350
        options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments);
C
Catouse 已提交
351

C
Catouse 已提交
352 353 354 355
        if (options.callback && !$.isFunction(options.callback))
        {
            throw new Error("alert requires callback property to be a function when provided");
        }
C
Catouse 已提交
356

C
Catouse 已提交
357 358 359 360 361 362 363 364 365 366 367
        /**
         * overrides
         */
        options.buttons.ok.callback = options.onEscape = function()
        {
            if ($.isFunction(options.callback))
            {
                return options.callback();
            }
            return true;
        };
C
Catouse 已提交
368

C
Catouse 已提交
369 370
        return exports.dialog(options);
    };
C
Catouse 已提交
371

C
Catouse 已提交
372 373 374
    exports.confirm = function()
    {
        var options;
C
Catouse 已提交
375

C
Catouse 已提交
376
        options = mergeDialogOptions("confirm", ["confirm", "cancel"], ["message", "callback"], arguments);
C
Catouse 已提交
377

C
Catouse 已提交
378 379 380 381 382 383 384
        /**
         * overrides; undo anything the user tried to set they shouldn't have
         */
        options.buttons.cancel.callback = options.onEscape = function()
        {
            return options.callback(false);
        };
C
Catouse 已提交
385

C
Catouse 已提交
386 387 388 389
        options.buttons.confirm.callback = function()
        {
            return options.callback(true);
        };
C
Catouse 已提交
390

C
Catouse 已提交
391 392 393 394 395
        // confirm specific validation
        if (!$.isFunction(options.callback))
        {
            throw new Error("confirm requires a callback");
        }
C
Catouse 已提交
396

C
Catouse 已提交
397 398
        return exports.dialog(options);
    };
C
Catouse 已提交
399

C
Catouse 已提交
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
    exports.prompt = function()
    {
        var options;
        var defaults;
        var dialog;
        var form;
        var input;
        var shouldShow;
        var inputOptions;

        // we have to create our form first otherwise
        // its value is undefined when gearing up our options
        // @TODO this could be solved by allowing message to
        // be a function instead...
        form = $(templates.form);

        // prompt defaults are more complex than others in that
        // users can override more defaults
        // @TODO I don't like that prompt has to do a lot of heavy
        // lifting which mergeDialogOptions can *almost* support already
        // just because of 'value' and 'inputType' - can we refactor?
        defaults = {
            className: "bootbox-prompt",
            buttons: createLabels("confirm", "cancel"),
            value: "",
            inputType: "text"
        };
C
Catouse 已提交
427

C
Catouse 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
        options = validateButtons(
            mergeArguments(defaults, arguments, ["title", "callback"]), ["cancel", "confirm"]
        );

        // capture the user's show value; we always set this to false before
        // spawning the dialog to give us a chance to attach some handlers to
        // it, but we need to make sure we respect a preference not to show it
        shouldShow = (options.show === undefined) ? true : options.show;

        // check if the browser supports the option.inputType
        var html5inputs = ["date", "time", "number"];
        var i = document.createElement("input");
        i.setAttribute("type", options.inputType);
        if (html5inputs[options.inputType])
        {
            options.inputType = i.type;
C
Catouse 已提交
444 445
        }

C
Catouse 已提交
446 447 448 449
        /**
         * overrides; undo anything the user tried to set they shouldn't have
         */
        options.message = form;
C
Catouse 已提交
450

C
Catouse 已提交
451 452 453 454
        options.buttons.cancel.callback = options.onEscape = function()
        {
            return options.callback(null);
        };
C
Catouse 已提交
455

C
Catouse 已提交
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
        options.buttons.confirm.callback = function()
        {
            var value;

            switch (options.inputType)
            {
                case "text":
                case "textarea":
                case "email":
                case "select":
                case "date":
                case "time":
                case "number":
                case "password":
                    value = input.val();
                    break;

                case "checkbox":
                    var checkedItems = input.find("input:checked");

                    // we assume that checkboxes are always multiple,
                    // hence we default to an empty array
                    value = [];

                    each(checkedItems, function(_, item)
                    {
                        value.push($(item).val());
                    });
                    break;
            }
C
Catouse 已提交
486

C
Catouse 已提交
487 488
            return options.callback(value);
        };
C
Catouse 已提交
489

C
Catouse 已提交
490
        options.show = false;
C
Catouse 已提交
491

C
Catouse 已提交
492 493 494 495 496
        // prompt specific validation
        if (!options.title)
        {
            throw new Error("prompt requires a title");
        }
C
Catouse 已提交
497

C
Catouse 已提交
498 499 500 501
        if (!$.isFunction(options.callback))
        {
            throw new Error("prompt requires a callback");
        }
C
Catouse 已提交
502

C
Catouse 已提交
503 504 505 506
        if (!templates.inputs[options.inputType])
        {
            throw new Error("invalid prompt type");
        }
C
Catouse 已提交
507

C
Catouse 已提交
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
        // create the input based on the supplied type
        input = $(templates.inputs[options.inputType]);

        switch (options.inputType)
        {
            case "text":
            case "textarea":
            case "email":
            case "date":
            case "time":
            case "number":
            case "password":
                input.val(options.value);
                break;

            case "select":
                var groups = {};
                inputOptions = options.inputOptions || [];

                if (!inputOptions.length)
                {
                    throw new Error("prompt with select requires options");
                }

                each(inputOptions, function(_, option)
                {

                    // assume the element to attach to is the input...
                    var elem = input;

                    if (option.value === undefined || option.text === undefined)
                    {
                        throw new Error("given options in wrong format");
                    }


                    // ... but override that element if this option sits in a group

                    if (option.group)
                    {
                        // initialise group if necessary
                        if (!groups[option.group])
                        {
                            groups[option.group] = $("<optgroup/>").attr("label", option.group);
                        }

                        elem = groups[option.group];
                    }

                    elem.append("<option value='" + option.value + "'>" + option.text + "</option>");
                });

                each(groups, function(_, group)
                {
                    input.append(group);
                });

                // safe to set a select's value as per a normal input
                input.val(options.value);
                break;

            case "checkbox":
                var values = $.isArray(options.value) ? options.value : [options.value];
                inputOptions = options.inputOptions || [];

                if (!inputOptions.length)
                {
                    throw new Error("prompt with checkbox requires options");
                }

                if (!inputOptions[0].value || !inputOptions[0].text)
                {
                    throw new Error("given options in wrong format");
                }

                // checkboxes have to nest within a containing element, so
                // they break the rules a bit and we end up re-assigning
                // our 'input' element to this container instead
                input = $("<div/>");

                each(inputOptions, function(_, option)
                {
                    var checkbox = $(templates.inputs[options.inputType]);

                    checkbox.find("input").attr("value", option.value);
                    checkbox.find("label").append(option.text);

                    // we've ensured values is an array so we can always iterate over it
                    each(values, function(_, value)
                    {
                        if (value === option.value)
                        {
                            checkbox.find("input").prop("checked", true);
                        }
                    });

                    input.append(checkbox);
                });
                break;
        }
C
Catouse 已提交
608

C
Catouse 已提交
609 610 611 612
        if (options.placeholder)
        {
            input.attr("placeholder", options.placeholder);
        }
C
Catouse 已提交
613

C
Catouse 已提交
614 615 616 617
        if (options.pattern)
        {
            input.attr("pattern", options.pattern);
        }
C
Catouse 已提交
618

C
Catouse 已提交
619 620 621 622 623 624 625 626 627 628 629 630
        // now place it in our form
        form.append(input);

        form.on("submit", function(e)
        {
            e.preventDefault();
            // Fix for SammyJS (or similar JS routing library) hijacking the form post.
            e.stopPropagation();
            // @TODO can we actually click *the* button object instead?
            // e.g. buttons.confirm.click() or similar
            dialog.find(".btn-primary").click();
        });
C
Catouse 已提交
631

C
Catouse 已提交
632
        dialog = exports.dialog(options);
C
Catouse 已提交
633

C
Catouse 已提交
634
        // clear the existing handler focusing the submit button...
C
Catouse 已提交
635
        dialog.off("shown.zui.modal");
C
Catouse 已提交
636

C
Catouse 已提交
637
        // ...and replace it with one focusing our input, if possible
C
Catouse 已提交
638
        dialog.on("shown.zui.modal", function()
C
Catouse 已提交
639 640 641
        {
            input.focus();
        });
C
Catouse 已提交
642

C
Catouse 已提交
643 644 645 646
        if (shouldShow === true)
        {
            dialog.modal("show");
        }
C
Catouse 已提交
647

C
Catouse 已提交
648
        return dialog;
C
Catouse 已提交
649 650
    };

C
Catouse 已提交
651 652 653 654 655 656 657 658 659 660 661 662
    exports.dialog = function(options)
    {
        options = sanitize(options);

        var dialog = $(templates.dialog);
        var innerDialog = dialog.find(".modal-dialog");
        var body = dialog.find(".modal-body");
        var buttons = options.buttons;
        var buttonStr = "";
        var callbacks = {
            onEscape: options.onEscape
        };
C
Catouse 已提交
663

C
Catouse 已提交
664 665
        each(buttons, function(key, button)
        {
C
Catouse 已提交
666

C
Catouse 已提交
667 668 669 670 671 672
            // @TODO I don't like this string appending to itself; bit dirty. Needs reworking
            // can we just build up button elements instead? slower but neater. Then button
            // can just become a template too
            buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>";
            callbacks[key] = button.callback;
        });
C
Catouse 已提交
673

C
Catouse 已提交
674
        body.find(".bootbox-body").html(options.message);
C
Catouse 已提交
675

C
Catouse 已提交
676 677 678 679
        if (options.animate === true)
        {
            dialog.addClass("fade");
        }
C
Catouse 已提交
680

C
Catouse 已提交
681 682 683 684
        if (options.className)
        {
            dialog.addClass(options.className);
        }
C
Catouse 已提交
685

C
Catouse 已提交
686 687 688 689
        if (options.size === "large")
        {
            innerDialog.addClass("modal-lg");
        }
C
Catouse 已提交
690

C
Catouse 已提交
691 692 693 694
        if (options.size === "small")
        {
            innerDialog.addClass("modal-sm");
        }
C
Catouse 已提交
695

C
Catouse 已提交
696 697 698 699
        if (options.title)
        {
            body.before(templates.header);
        }
C
Catouse 已提交
700

C
Catouse 已提交
701 702 703
        if (options.closeButton)
        {
            var closeButton = $(templates.closeButton);
C
Catouse 已提交
704

C
Catouse 已提交
705 706 707 708 709 710 711 712 713
            if (options.title)
            {
                dialog.find(".modal-header").prepend(closeButton);
            }
            else
            {
                closeButton.css("margin-top", "-10px").prependTo(body);
            }
        }
C
Catouse 已提交
714

C
Catouse 已提交
715 716 717 718
        if (options.title)
        {
            dialog.find(".modal-title").html(options.title);
        }
C
Catouse 已提交
719

C
Catouse 已提交
720 721 722 723 724
        if (buttonStr.length)
        {
            body.after(templates.footer);
            dialog.find(".modal-footer").html(buttonStr);
        }
C
Catouse 已提交
725 726


C
Catouse 已提交
727 728 729 730 731 732
        /**
         * Bootstrap event listeners; used handle extra
         * setup & teardown required after the underlying
         * modal has performed certain actions
         */

C
Catouse 已提交
733
        dialog.on("hidden.zui.modal", function(e)
C
Catouse 已提交
734 735 736 737 738 739 740 741 742
        {
            // ensure we don't accidentally intercept hidden events triggered
            // by children of the current dialog. We shouldn't anymore now BS
            // namespaces its events; but still worth doing
            if (e.target === this)
            {
                dialog.remove();
            }
        });
C
Catouse 已提交
743

C
Catouse 已提交
744
        /*
C
Catouse 已提交
745
    dialog.on("show.zui.modal", function() {
C
Catouse 已提交
746 747 748 749 750 751 752 753 754
      // sadly this doesn't work; show is called *just* before
      // the backdrop is added so we'd need a setTimeout hack or
      // otherwise... leaving in as would be nice
      if (options.backdrop) {
        dialog.next(".modal-backdrop").addClass("bootbox-backdrop");
      }
    });
    */

C
Catouse 已提交
755
        dialog.on("shown.zui.modal", function()
C
Catouse 已提交
756 757 758
        {
            dialog.find(".btn-primary:first").focus();
        });
C
Catouse 已提交
759

C
Catouse 已提交
760 761 762 763 764 765 766 767 768 769 770 771 772
        /**
         * Bootbox event listeners; experimental and may not last
         * just an attempt to decouple some behaviours from their
         * respective triggers
         */

        dialog.on("escape.close.bb", function(e)
        {
            if (callbacks.onEscape)
            {
                processCallback(e, dialog, callbacks.onEscape);
            }
        });
C
Catouse 已提交
773

C
Catouse 已提交
774 775 776 777
        /**
         * Standard jQuery event listeners; used to handle user
         * interaction with our dialog
         */
C
Catouse 已提交
778

C
Catouse 已提交
779 780 781
        dialog.on("click", ".modal-footer button", function(e)
        {
            var callbackKey = $(this).data("bb-handler");
C
Catouse 已提交
782

C
Catouse 已提交
783
            processCallback(e, dialog, callbacks[callbackKey]);
C
Catouse 已提交
784

C
Catouse 已提交
785
        });
C
Catouse 已提交
786

C
Catouse 已提交
787 788 789 790 791 792 793
        dialog.on("click", ".bootbox-close-button", function(e)
        {
            // onEscape might be falsy but that's fine; the fact is
            // if the user has managed to click the close button we
            // have to close the dialog, callback or not
            processCallback(e, dialog, callbacks.onEscape);
        });
C
Catouse 已提交
794

C
Catouse 已提交
795 796 797 798 799 800 801
        dialog.on("keyup", function(e)
        {
            if (e.which === 27)
            {
                dialog.trigger("escape.close.bb");
            }
        });
C
Catouse 已提交
802

C
Catouse 已提交
803 804 805 806
        // the remainder of this method simply deals with adding our
        // dialogent to the DOM, augmenting it with Bootstrap's modal
        // functionality and then giving the resulting object back
        // to our caller
C
Catouse 已提交
807

C
Catouse 已提交
808
        $(options.container).append(dialog);
C
Catouse 已提交
809

C
Catouse 已提交
810 811 812 813 814 815
        dialog.modal(
        {
            backdrop: options.backdrop,
            keyboard: false,
            show: false
        });
C
Catouse 已提交
816

C
Catouse 已提交
817 818 819 820
        if (options.show)
        {
            dialog.modal("show");
        }
C
Catouse 已提交
821

C
Catouse 已提交
822 823 824 825
        // @TODO should we return the raw element here or should
        // we wrap it in an object on which we can expose some neater
        // methods, e.g. var d = bootbox.alert(); d.hide(); instead
        // of d.modal("hide");
C
Catouse 已提交
826

C
Catouse 已提交
827
        /*
C
Catouse 已提交
828 829 830 831 832 833 834 835 836 837 838 839 840 841
    function BBDialog(elem) {
      this.elem = elem;
    }

    BBDialog.prototype = {
      hide: function() {
        return this.elem.modal("hide");
      },
      show: function() {
        return this.elem.modal("show");
      }
    };
    */

C
Catouse 已提交
842
        return dialog;
C
Catouse 已提交
843

C
Catouse 已提交
844
    };
C
Catouse 已提交
845

C
Catouse 已提交
846 847 848
    exports.setDefaults = function()
    {
        var values = {};
C
Catouse 已提交
849

C
Catouse 已提交
850 851 852 853 854 855 856 857 858 859
        if (arguments.length === 2)
        {
            // allow passing of single key/value...
            values[arguments[0]] = arguments[1];
        }
        else
        {
            // ... and as an object too
            values = arguments[0];
        }
C
Catouse 已提交
860

C
Catouse 已提交
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
        $.extend(defaults, values);
    };

    exports.hideAll = function()
    {
        $(".bootbox").modal("hide");
    };


    /**
     * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
     * unlikely to be required. If this gets too large it can be split out into separate JS files.
     */
    var locales = {
        en:
        {
            OK: "OK",
            CANCEL: "Cancel",
            CONFIRM: "OK"
        },
        zh_cn:
        {
            OK: "确认",
            CANCEL: "取消",
            CONFIRM: "确认"
        },
        zh_tw:
        {
            OK: "確認",
            CANCEL: "取消",
            CONFIRM: "確認"
        }
    };
C
Catouse 已提交
894

C
Catouse 已提交
895 896 897 898
    exports.init = function(_$)
    {
        return init(_$ || $);
    };
C
Catouse 已提交
899

C
Catouse 已提交
900
    return exports;
C
Catouse 已提交
901
}));