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

C
Catouse 已提交
11 12 13

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

16
    'use strict';
C
Catouse 已提交
17
    if(typeof define === "function" && define.amd) {
C
Catouse 已提交
18 19
        // AMD. Register as an anonymous module.
        define(["jquery"], factory);
C
Catouse 已提交
20
    } else if(typeof exports === "object") {
C
Catouse 已提交
21 22 23 24
        // 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 已提交
25
    } else {
C
Catouse 已提交
26 27
        // Browser globals (root is window)
        root.bootbox = factory(root.jQuery);
C
Catouse 已提交
28 29
    }

C
Catouse 已提交
30
}(this, function init($, undefined) {
C
Catouse 已提交
31

32
    'use strict';
C
Catouse 已提交
33

C
Catouse 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
    // 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>",
C
Catouse 已提交
49
        inputs: {
C
Catouse 已提交
50 51 52 53 54 55 56 57 58 59 60
            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 已提交
61

C
Catouse 已提交
62 63
    var defaults = {
        // default language
C
Catouse 已提交
64 65 66
        locale: $.zui && $.zui.clientLang ? $.zui.clientLang() : 'zh_cn',
        // show backdrop or not. Default to static so user has to interact with dialog
        backdrop: "static",
C
Catouse 已提交
67 68 69 70 71 72 73 74 75 76 77
        // 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 已提交
78

C
Catouse 已提交
79 80
    // our public object; augmented after our private API
    var exports = {};
C
Catouse 已提交
81

C
Catouse 已提交
82 83 84
    /**
     * @private
     */
C
Catouse 已提交
85
    function _t(key) {
C
Catouse 已提交
86 87 88
        var locale = locales[defaults.locale];
        return locale ? locale[key] : locales.en[key];
    }
C
Catouse 已提交
89

C
Catouse 已提交
90
    function processCallback(e, dialog, callback) {
C
Catouse 已提交
91 92
        e.stopPropagation();
        e.preventDefault();
C
Catouse 已提交
93

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

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

C
Catouse 已提交
101
        // ... otherwise we'll bin it
C
Catouse 已提交
102
        if(!preserveDialog) {
C
Catouse 已提交
103
            dialog.modal("hide");
C
Catouse 已提交
104
        }
C
Catouse 已提交
105
    }
C
Catouse 已提交
106

C
Catouse 已提交
107
    function getKeyLength(obj) {
C
Catouse 已提交
108 109
        // @TODO defer to Object.keys(x).length if available?
        var k, t = 0;
C
Catouse 已提交
110
        for(k in obj) {
C
Catouse 已提交
111 112 113
            t++;
        }
        return t;
C
Catouse 已提交
114 115
    }

C
Catouse 已提交
116
    function each(collection, iterator) {
C
Catouse 已提交
117
        var index = 0;
C
Catouse 已提交
118
        $.each(collection, function(key, value) {
C
Catouse 已提交
119 120
            iterator(key, value, index++);
        });
C
Catouse 已提交
121 122
    }

C
Catouse 已提交
123
    function sanitize(options) {
C
Catouse 已提交
124 125
        var buttons;
        var total;
C
Catouse 已提交
126

C
Catouse 已提交
127
        if(typeof options !== "object") {
C
Catouse 已提交
128 129
            throw new Error("Please supply an object of options");
        }
C
Catouse 已提交
130

C
Catouse 已提交
131
        if(!options.message) {
C
Catouse 已提交
132 133
            throw new Error("Please specify a message");
        }
C
Catouse 已提交
134

C
Catouse 已提交
135
        // make sure any supplied options take precedence over defaults
C
Catouse 已提交
136
        options = $.extend({}, defaults, options);
C
Catouse 已提交
137

C
Catouse 已提交
138
        if(!options.buttons) {
C
Catouse 已提交
139 140
            options.buttons = {};
        }
C
Catouse 已提交
141

C
Catouse 已提交
142
        buttons = options.buttons;
C
Catouse 已提交
143

C
Catouse 已提交
144
        total = getKeyLength(buttons);
C
Catouse 已提交
145

C
Catouse 已提交
146
        each(buttons, function(key, button, index) {
C
Catouse 已提交
147

C
Catouse 已提交
148
            if($.isFunction(button)) {
C
Catouse 已提交
149 150 151 152 153 154
                // 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 已提交
155

C
Catouse 已提交
156
            // before any further checks make sure by now button is the correct type
C
Catouse 已提交
157
            if($.type(button) !== "object") {
C
Catouse 已提交
158 159
                throw new Error("button with key " + key + " must be an object");
            }
C
Catouse 已提交
160

C
Catouse 已提交
161
            if(!button.label) {
C
Catouse 已提交
162 163 164
                // the lack of an explicit label means we'll assume the key is good enough
                button.label = key;
            }
C
Catouse 已提交
165

C
Catouse 已提交
166
            if(!button.className) {
167
                if((total === 2 && (key === 'ok' || key === 'confirm')) || total === 1) {
C
Catouse 已提交
168 169
                    // always add a primary to the main option in a two-button dialog
                    button.className = "btn-primary";
C
Catouse 已提交
170
                } else {
C
Catouse 已提交
171 172 173 174 175 176 177
                    button.className = "btn-default";
                }
            }
        });

        return options;
    }
C
Catouse 已提交
178 179

    /**
C
Catouse 已提交
180 181 182 183 184 185 186
     * 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 已提交
187
     */
C
Catouse 已提交
188
    function mapArguments(args, properties) {
C
Catouse 已提交
189 190 191
        var argn = args.length;
        var options = {};

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

C
Catouse 已提交
196
        if(argn === 2 || typeof args[0] === "string") {
C
Catouse 已提交
197 198
            options[properties[0]] = args[0];
            options[properties[1]] = args[1];
C
Catouse 已提交
199
        } else {
C
Catouse 已提交
200 201
            options = args[0];
        }
C
Catouse 已提交
202

C
Catouse 已提交
203
        return options;
C
Catouse 已提交
204 205
    }

C
Catouse 已提交
206 207 208
    /**
     * merge a set of default dialog options with user supplied arguments
     */
C
Catouse 已提交
209
    function mergeArguments(defaults, args, properties) {
C
Catouse 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223
        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 已提交
224 225 226
    }

    /**
C
Catouse 已提交
227 228
     * 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 已提交
229
     */
C
Catouse 已提交
230
    function mergeDialogOptions(className, labels, properties, args) {
C
Catouse 已提交
231 232 233 234 235
        //  build up a base set of dialog properties
        var baseOptions = {
            className: "bootbox-" + className,
            buttons: createLabels.apply(null, labels)
        };
C
Catouse 已提交
236

C
Catouse 已提交
237 238 239 240 241 242 243 244 245 246 247 248
        // 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 已提交
249 250
    }

C
Catouse 已提交
251 252 253 254 255
    /**
     * 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" }
     */
C
Catouse 已提交
256
    function createLabels() {
C
Catouse 已提交
257 258
        var buttons = {};

C
Catouse 已提交
259
        for(var i = 0, j = arguments.length; i < j; i++) {
C
Catouse 已提交
260 261 262 263 264 265 266 267
            var argument = arguments[i];
            var key = argument.toLowerCase();
            var value = argument.toUpperCase();

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

C
Catouse 已提交
269
        return buttons;
C
Catouse 已提交
270 271
    }

C
Catouse 已提交
272
    function validateButtons(options, buttons) {
C
Catouse 已提交
273
        var allowedButtons = {};
C
Catouse 已提交
274
        each(buttons, function(key, value) {
C
Catouse 已提交
275 276
            allowedButtons[value] = true;
        });
C
Catouse 已提交
277

C
Catouse 已提交
278 279
        each(options.buttons, function(key) {
            if(allowedButtons[key] === undefined) {
C
Catouse 已提交
280 281 282
                throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
            }
        });
C
Catouse 已提交
283

C
Catouse 已提交
284 285
        return options;
    }
C
Catouse 已提交
286

C
Catouse 已提交
287
    exports.alert = function() {
C
Catouse 已提交
288
        var options;
C
Catouse 已提交
289

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

C
Catouse 已提交
292
        if(options.callback && !$.isFunction(options.callback)) {
C
Catouse 已提交
293 294
            throw new Error("alert requires callback property to be a function when provided");
        }
C
Catouse 已提交
295

C
Catouse 已提交
296 297 298
        /**
         * overrides
         */
C
Catouse 已提交
299 300
        options.buttons.ok.callback = options.onEscape = function() {
            if($.isFunction(options.callback)) {
C
Catouse 已提交
301
                return options.callback.call(this);
C
Catouse 已提交
302 303 304
            }
            return true;
        };
C
Catouse 已提交
305

C
Catouse 已提交
306 307
        return exports.dialog(options);
    };
C
Catouse 已提交
308

C
Catouse 已提交
309
    exports.confirm = function() {
C
Catouse 已提交
310
        var options;
C
Catouse 已提交
311

312 313 314 315
        // ZUI change begin
        options = mergeDialogOptions("confirm", ["confirm", "cancel"], ["message", "callback"], arguments);
        // OLD WAY: options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments);
        // ZUI change end
C
Catouse 已提交
316

C
Catouse 已提交
317 318 319
        /**
         * overrides; undo anything the user tried to set they shouldn't have
         */
C
Catouse 已提交
320
        options.buttons.cancel.callback = options.onEscape = function() {
C
Catouse 已提交
321
            return options.callback.call(this, false);
C
Catouse 已提交
322
        };
C
Catouse 已提交
323

C
Catouse 已提交
324
        options.buttons.confirm.callback = function() {
C
Catouse 已提交
325
            return options.callback.call(this, true);
C
Catouse 已提交
326
        };
C
Catouse 已提交
327

C
Catouse 已提交
328
        // confirm specific validation
C
Catouse 已提交
329
        if(!$.isFunction(options.callback)) {
C
Catouse 已提交
330 331
            throw new Error("confirm requires a callback");
        }
C
Catouse 已提交
332

C
Catouse 已提交
333 334
        return exports.dialog(options);
    };
C
Catouse 已提交
335

C
Catouse 已提交
336
    exports.prompt = function() {
C
Catouse 已提交
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        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",
C
Catouse 已提交
358
            buttons: createLabels("cancel", "confirm"),
C
Catouse 已提交
359 360 361
            value: "",
            inputType: "text"
        };
C
Catouse 已提交
362

C
Catouse 已提交
363
        options = validateButtons(
364 365 366 367
            // ZUI change begin
            mergeArguments(defaults, arguments, ["title", "callback"]), ["confirm", "cancel"]
            // OLD WAY: mergeArguments(defaults, arguments, ["title", "callback"]), ["cancel", "confirm"]arguments);
            // ZUI change end
C
Catouse 已提交
368 369 370 371 372 373 374 375 376 377 378
        );

        // 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;

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

C
Catouse 已提交
380
        options.buttons.cancel.callback = options.onEscape = function() {
C
Catouse 已提交
381
            return options.callback.call(this, null);
C
Catouse 已提交
382
        };
C
Catouse 已提交
383

C
Catouse 已提交
384
        options.buttons.confirm.callback = function() {
C
Catouse 已提交
385 386
            var value;

C
Catouse 已提交
387
            switch(options.inputType) {
C
Catouse 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
                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 = [];

C
Catouse 已提交
406
                    each(checkedItems, function(_, item) {
C
Catouse 已提交
407 408 409 410
                        value.push($(item).val());
                    });
                    break;
            }
C
Catouse 已提交
411

C
Catouse 已提交
412
            return options.callback.call(this, value);
C
Catouse 已提交
413
        };
C
Catouse 已提交
414

C
Catouse 已提交
415
        options.show = false;
C
Catouse 已提交
416

C
Catouse 已提交
417
        // prompt specific validation
C
Catouse 已提交
418
        if(!options.title) {
C
Catouse 已提交
419 420
            throw new Error("prompt requires a title");
        }
C
Catouse 已提交
421

C
Catouse 已提交
422
        if(!$.isFunction(options.callback)) {
C
Catouse 已提交
423 424
            throw new Error("prompt requires a callback");
        }
C
Catouse 已提交
425

C
Catouse 已提交
426
        if(!templates.inputs[options.inputType]) {
C
Catouse 已提交
427 428
            throw new Error("invalid prompt type");
        }
C
Catouse 已提交
429

C
Catouse 已提交
430 431 432
        // create the input based on the supplied type
        input = $(templates.inputs[options.inputType]);

C
Catouse 已提交
433
        switch(options.inputType) {
C
Catouse 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447
            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 || [];

C
Catouse 已提交
448 449 450 451
                if(!$.isArray(inputOptions)) {
                    throw new Error("Please pass an array of input options");
                }

C
Catouse 已提交
452
                if(!inputOptions.length) {
C
Catouse 已提交
453 454 455
                    throw new Error("prompt with select requires options");
                }

C
Catouse 已提交
456
                each(inputOptions, function(_, option) {
C
Catouse 已提交
457 458 459 460

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

C
Catouse 已提交
461
                    if(option.value === undefined || option.text === undefined) {
C
Catouse 已提交
462 463 464 465 466
                        throw new Error("given options in wrong format");
                    }

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

C
Catouse 已提交
467
                    if(option.group) {
C
Catouse 已提交
468
                        // initialise group if necessary
C
Catouse 已提交
469
                        if(!groups[option.group]) {
C
Catouse 已提交
470 471 472 473 474 475 476 477 478
                            groups[option.group] = $("<optgroup/>").attr("label", option.group);
                        }

                        elem = groups[option.group];
                    }

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

C
Catouse 已提交
479
                each(groups, function(_, group) {
C
Catouse 已提交
480 481 482 483 484 485 486 487 488 489 490
                    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 || [];

C
Catouse 已提交
491
                if(!inputOptions.length) {
C
Catouse 已提交
492 493 494
                    throw new Error("prompt with checkbox requires options");
                }

C
Catouse 已提交
495
                if(!inputOptions[0].value || !inputOptions[0].text) {
C
Catouse 已提交
496 497 498 499 500 501 502 503
                    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/>");

C
Catouse 已提交
504
                each(inputOptions, function(_, option) {
C
Catouse 已提交
505 506 507 508 509 510
                    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
C
Catouse 已提交
511 512
                    each(values, function(_, value) {
                        if(value === option.value) {
C
Catouse 已提交
513 514 515 516 517 518 519 520
                            checkbox.find("input").prop("checked", true);
                        }
                    });

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

C
Catouse 已提交
522 523
        // @TODO provide an attributes option instead
        // and simply map that as keys: vals
C
Catouse 已提交
524
        if(options.placeholder) {
C
Catouse 已提交
525 526
            input.attr("placeholder", options.placeholder);
        }
C
Catouse 已提交
527

C
Catouse 已提交
528
        if(options.pattern) {
C
Catouse 已提交
529 530
            input.attr("pattern", options.pattern);
        }
C
Catouse 已提交
531

C
Catouse 已提交
532 533 534 535
        if(options.maxlength) {
            input.attr("maxlength", options.maxlength);
        }

C
Catouse 已提交
536 537 538
        // now place it in our form
        form.append(input);

C
Catouse 已提交
539
        form.on("submit", function(e) {
C
Catouse 已提交
540 541 542 543 544 545 546
            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 已提交
547

C
Catouse 已提交
548
        dialog = exports.dialog(options);
C
Catouse 已提交
549

C
Catouse 已提交
550
        // clear the existing handler focusing the submit button...
C
Catouse 已提交
551
        dialog.off("shown.zui.modal");
C
Catouse 已提交
552

C
Catouse 已提交
553
        // ...and replace it with one focusing our input, if possible
C
Catouse 已提交
554
        dialog.on("shown.zui.modal", function() {
C
Catouse 已提交
555 556
            // need the closure here since input isn't
            // an object otherwise
C
Catouse 已提交
557 558
            input.focus();
        });
C
Catouse 已提交
559

C
Catouse 已提交
560
        if(shouldShow === true) {
C
Catouse 已提交
561 562
            dialog.modal("show");
        }
C
Catouse 已提交
563

C
Catouse 已提交
564
        return dialog;
C
Catouse 已提交
565 566
    };

C
Catouse 已提交
567
    exports.dialog = function(options) {
C
Catouse 已提交
568 569
        options = sanitize(options);

570

C
Catouse 已提交
571 572 573 574 575 576 577 578
        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 已提交
579

C
Catouse 已提交
580 581 582 583 584 585 586 587
        if($.fn.modal === undefined) {
            throw new Error(
                "$.fn.modal is not defined; please double check you have included " +
                "the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ " +
                "for more details."
            );
        }

C
Catouse 已提交
588
        each(buttons, function(key, button) {
C
Catouse 已提交
589

C
Catouse 已提交
590 591 592 593 594 595
            // @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 已提交
596

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

C
Catouse 已提交
599
        if(options.animate === true) {
C
Catouse 已提交
600 601
            dialog.addClass("fade");
        }
C
Catouse 已提交
602

C
Catouse 已提交
603
        if(options.className) {
C
Catouse 已提交
604 605
            dialog.addClass(options.className);
        }
C
Catouse 已提交
606

C
Catouse 已提交
607
        if(options.size === "large") {
C
Catouse 已提交
608
            innerDialog.addClass("modal-lg");
C
Catouse 已提交
609
        } else if(options.size === "small") {
C
Catouse 已提交
610 611
            innerDialog.addClass("modal-sm");
        }
C
Catouse 已提交
612

C
Catouse 已提交
613
        if(options.title) {
C
Catouse 已提交
614 615
            body.before(templates.header);
        }
C
Catouse 已提交
616

C
Catouse 已提交
617
        if(options.closeButton) {
C
Catouse 已提交
618
            var closeButton = $(templates.closeButton);
C
Catouse 已提交
619

C
Catouse 已提交
620
            if(options.title) {
C
Catouse 已提交
621
                dialog.find(".modal-header").prepend(closeButton);
C
Catouse 已提交
622
            } else {
C
Catouse 已提交
623 624 625
                closeButton.css("margin-top", "-10px").prependTo(body);
            }
        }
C
Catouse 已提交
626

C
Catouse 已提交
627
        if(options.title) {
C
Catouse 已提交
628 629
            dialog.find(".modal-title").html(options.title);
        }
C
Catouse 已提交
630

C
Catouse 已提交
631
        if(buttonStr.length) {
C
Catouse 已提交
632 633 634
            body.after(templates.footer);
            dialog.find(".modal-footer").html(buttonStr);
        }
C
Catouse 已提交
635 636


C
Catouse 已提交
637 638 639 640 641 642
        /**
         * Bootstrap event listeners; used handle extra
         * setup & teardown required after the underlying
         * modal has performed certain actions
         */

C
Catouse 已提交
643
        dialog.on("hidden.zui.modal", function(e) {
C
Catouse 已提交
644 645 646
            // 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
C
Catouse 已提交
647
            if(e.target === this) {
C
Catouse 已提交
648 649 650
                dialog.remove();
            }
        });
C
Catouse 已提交
651

C
Catouse 已提交
652
        /*
C
Catouse 已提交
653 654 655 656 657 658 659 660 661
        dialog.on("show.zui.modal", function() {
          // 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 已提交
662

C
Catouse 已提交
663
        dialog.on("shown.zui.modal", function() {
C
Catouse 已提交
664 665
            dialog.find(".btn-primary:first").focus();
        });
C
Catouse 已提交
666

C
Catouse 已提交
667 668 669 670 671 672
        /**
         * Bootbox event listeners; experimental and may not last
         * just an attempt to decouple some behaviours from their
         * respective triggers
         */

C
Catouse 已提交
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
        if(options.backdrop !== "static") {
            // A boolean true/false according to the Bootstrap docs
            // should show a dialog the user can dismiss by clicking on
            // the background.
            // We always only ever pass static/false to the actual
            // $.modal function because with `true` we can't trap
            // this event (the .modal-backdrop swallows it)
            // However, we still want to sort of respect true
            // and invoke the escape mechanism instead
            dialog.on("click.dismiss.zui.modal", function(e) {
                // @NOTE: the target varies in >= 3.3.x releases since the modal backdrop
                // moved *inside* the outer dialog rather than *alongside* it
                if(dialog.children(".modal-backdrop").length) {
                    e.currentTarget = dialog.children(".modal-backdrop").get(0);
                }

                if(e.target !== e.currentTarget) {
                    return;
                }

                dialog.trigger("escape.close.bb");
            });
        }

C
Catouse 已提交
697 698
        dialog.on("escape.close.bb", function(e) {
            if(callbacks.onEscape) {
C
Catouse 已提交
699 700 701
                processCallback(e, dialog, callbacks.onEscape);
            }
        });
C
Catouse 已提交
702

C
Catouse 已提交
703 704 705 706
        /**
         * Standard jQuery event listeners; used to handle user
         * interaction with our dialog
         */
C
Catouse 已提交
707

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

C
Catouse 已提交
711 712
            processCallback(e, dialog, callbacks[callbackKey]);
        });
C
Catouse 已提交
713

C
Catouse 已提交
714
        dialog.on("click", ".bootbox-close-button", function(e) {
C
Catouse 已提交
715 716 717 718 719
            // 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 已提交
720

C
Catouse 已提交
721 722
        dialog.on("keyup", function(e) {
            if(e.which === 27) {
C
Catouse 已提交
723 724 725
                dialog.trigger("escape.close.bb");
            }
        });
C
Catouse 已提交
726

C
Catouse 已提交
727 728 729 730
        // 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 已提交
731

C
Catouse 已提交
732
        $(options.container).append(dialog);
C
Catouse 已提交
733

C
Catouse 已提交
734
        dialog.modal({
C
Catouse 已提交
735
            backdrop: options.backdrop ? "static" : false,
C
Catouse 已提交
736 737 738
            keyboard: false,
            show: false
        });
C
Catouse 已提交
739

C
Catouse 已提交
740
        if(options.show) {
C
Catouse 已提交
741 742
            dialog.modal("show");
        }
C
Catouse 已提交
743

C
Catouse 已提交
744 745 746 747
        // @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 已提交
748

C
Catouse 已提交
749
        /*
C
Catouse 已提交
750 751 752 753 754 755 756 757 758 759 760 761 762
         function BBDialog(elem) {
           this.elem = elem;
         }

         BBDialog.prototype = {
           hide: function() {
             return this.elem.modal("hide");
           },
           show: function() {
             return this.elem.modal("show");
           }
         };
         */
C
Catouse 已提交
763

C
Catouse 已提交
764
        return dialog;
C
Catouse 已提交
765

C
Catouse 已提交
766
    };
C
Catouse 已提交
767

C
Catouse 已提交
768
    exports.setDefaults = function() {
C
Catouse 已提交
769
        var values = {};
C
Catouse 已提交
770

C
Catouse 已提交
771
        if(arguments.length === 2) {
C
Catouse 已提交
772 773
            // allow passing of single key/value...
            values[arguments[0]] = arguments[1];
C
Catouse 已提交
774
        } else {
C
Catouse 已提交
775 776 777
            // ... and as an object too
            values = arguments[0];
        }
C
Catouse 已提交
778

C
Catouse 已提交
779 780 781
        $.extend(defaults, values);
    };

C
Catouse 已提交
782
    exports.hideAll = function() {
C
Catouse 已提交
783
        $(".bootbox").modal("hide");
C
Catouse 已提交
784 785

        return exports;
C
Catouse 已提交
786 787 788 789 790 791 792 793
    };


    /**
     * 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 = {
C
Catouse 已提交
794
        en: {
C
Catouse 已提交
795 796 797 798
            OK: "OK",
            CANCEL: "Cancel",
            CONFIRM: "OK"
        },
C
Catouse 已提交
799
        zh_cn: {
C
Catouse 已提交
800 801 802 803
            OK: "确认",
            CANCEL: "取消",
            CONFIRM: "确认"
        },
C
Catouse 已提交
804
        zh_tw: {
C
Catouse 已提交
805 806 807 808 809
            OK: "確認",
            CANCEL: "取消",
            CONFIRM: "確認"
        }
    };
C
Catouse 已提交
810

C
Catouse 已提交
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
    exports.addLocale = function(name, values) {
        $.each(["OK", "CANCEL", "CONFIRM"], function(_, v) {
            if(!values[v]) {
                throw new Error("Please supply a translation for '" + v + "'");
            }
        });

        locales[name] = {
            OK: values.OK,
            CANCEL: values.CANCEL,
            CONFIRM: values.CONFIRM
        };

        return exports;
    };

    exports.removeLocale = function(name) {
        delete locales[name];

        return exports;
    };

    exports.setLocale = function(name) {
        return exports.setDefaults("locale", name);
    };

C
Catouse 已提交
837
    exports.init = function(_$) {
C
Catouse 已提交
838 839
        return init(_$ || $);
    };
C
Catouse 已提交
840

C
Catouse 已提交
841
    return exports;
C
Catouse 已提交
842
}));
C
Catouse 已提交
843