source: pro-violet-viettel/docs/Space/assets/js/uncompressed/bootbox.js @ 290

Last change on this file since 290 was 290, checked in by lamdt, 11 years ago
File size: 14.9 KB
Line 
1/**
2 * bootbox.js v4.0.0
3 *
4 * http://bootboxjs.com/license.txt
5 */
6// @see https://github.com/makeusabrew/bootbox/issues/71
7window.bootbox = window.bootbox || (function init($, undefined) {
8  "use strict";
9
10  // the base DOM structure needed to create a modal
11  var templates = {
12    dialog:
13      "<div class='bootbox modal' tabindex='-1' role='dialog'>" +
14        "<div class='modal-dialog'>" +
15          "<div class='modal-content'>" +
16            "<div class='modal-body'><div class='bootbox-body'></div></div>" +
17          "</div>" +
18        "</div>" +
19      "</div>",
20    header:
21      "<div class='modal-header'>" +
22        "<h4 class='modal-title'></h4>" +
23      "</div>",
24    footer:
25      "<div class='modal-footer'></div>",
26    closeButton:
27      "<button type='button' class='bootbox-close-button close'>&times;</button>",
28    form:
29      "<form class='bootbox-form'></form>",
30    inputs: {
31      text:
32        "<input class='bootbox-input form-control' autocomplete=off type=text />"
33    }
34  };
35
36  // cache a reference to the jQueryfied body element
37  var appendTo = $("body");
38
39  var defaults = {
40    // default language
41    locale: "en",
42    // show backdrop or not
43    backdrop: true,
44    // animate the modal in/out
45    animate: true,
46    // additional class string applied to the top level dialog
47    className: null,
48    // whether or not to include a close button
49    closeButton: true,
50    // show the dialog immediately by default
51    show: true
52  };
53
54  // our public object; augmented after our private API
55  var exports = {};
56
57  /**
58   * @private
59   */
60  function _t(key) {
61    var locale = locales[defaults.locale];
62    return locale ? locale[key] : locales.en[key];
63  }
64
65  function processCallback(e, dialog, callback) {
66    e.preventDefault();
67
68    // by default we assume a callback will get rid of the dialog,
69    // although it is given the opportunity to override this
70
71    // so, if the callback can be invoked and it *explicitly returns false*
72    // then we'll set a flag to keep the dialog active...
73    var preserveDialog = $.isFunction(callback) && callback(e) === false;
74
75    // ... otherwise we'll bin it
76    if (!preserveDialog) {
77      dialog.modal("hide");
78    }
79  }
80
81  function getKeyLength(obj) {
82    // @TODO defer to Object.keys(x).length if available?
83    var k, t = 0;
84    for (k in obj) {
85      t ++;
86    }
87    return t;
88  }
89
90  function each(collection, iterator) {
91    var index = 0;
92    $.each(collection, function(key, value) {
93      iterator(key, value, index++);
94    });
95  }
96
97  function sanitize(options) {
98    var buttons;
99    var total;
100
101
102    if (typeof options !== "object") {
103      throw new Error("Please supply an object of options");
104    }
105
106    if (!options.message) {
107      throw new Error("Please specify a message");
108    }
109
110    // make sure any supplied options take precedence over defaults
111    options = $.extend({}, defaults, options);
112
113    if (!options.buttons) {
114      options.buttons = {};
115    }
116
117    // we only support Bootstrap's "static" and false backdrop args
118    // supporting true would mean you could dismiss the dialog without
119    // explicitly interacting with it
120    options.backdrop = options.backdrop ? "static" : false;
121
122    buttons = options.buttons;
123
124    total = getKeyLength(buttons);
125
126    each(buttons, function(key, button, index) {
127
128      if ($.isFunction(button)) {
129        // short form, assume value is our callback. Since button
130        // isn't an object it isn't a reference either so re-assign it
131        button = buttons[key] = {
132          callback: button
133        };
134      }
135
136      // before any further checks make sure by now button is the correct type
137      if ($.type(button) !== "object") {
138        throw new Error("button with key " + key + " must be an object");
139      }
140
141      if (!button.label) {
142        // the lack of an explicit label means we'll assume the key is good enough
143        button.label = key;
144      }
145
146      if (!button.className) {
147        if (total <= 2 && index === total-1) {
148          // always add a primary to the main option in a two-button dialog
149          button.className = "btn-primary";
150        } else {
151          button.className = "btn-default";
152        }
153      }
154    });
155
156    return options;
157  }
158
159  function mapArguments(args, properties) {
160    var argn = args.length;
161    var options = {};
162
163    if (argn < 1 || argn > 2) {
164      throw new Error("Invalid argument length");
165    }
166
167    if (argn === 2 || typeof args[0] === "string") {
168      options[properties[0]] = args[0];
169      options[properties[1]] = args[1];
170    } else {
171      options = args[0];
172    }
173
174    return options;
175  }
176
177  function mergeArguments(defaults, args, properties) {
178    return $.extend(true, {}, defaults, mapArguments(args, properties));
179  }
180
181  function mergeButtons(labels, args, properties) {
182    return validateButtons(
183      mergeArguments(createButtons.apply(null, labels), args, properties),
184      labels
185    );
186  }
187
188  function createLabels() {
189    var buttons = {};
190
191    for (var i = 0, j = arguments.length; i < j; i++) {
192      var argument = arguments[i];
193      var key = argument.toLowerCase();
194      var value = argument.toUpperCase();
195
196      buttons[key] = {
197        label: _t(value)
198      };
199    }
200
201    return buttons;
202  }
203
204  function createButtons() {
205    return {
206      buttons: createLabels.apply(null, arguments)
207    };
208  }
209
210  function validateButtons(options, buttons) {
211    var allowedButtons = {};
212    each(buttons, function(key, value) {
213      allowedButtons[value] = true;
214    });
215
216    each(options.buttons, function(key) {
217      if (allowedButtons[key] === undefined) {
218        throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
219      }
220    });
221
222    return options;
223  }
224
225  exports.alert = function() {
226    var options;
227
228    options = mergeButtons(["ok"], arguments, ["message", "callback"]);
229
230    if (options.callback && !$.isFunction(options.callback)) {
231      throw new Error("alert requires callback property to be a function when provided");
232    }
233
234    /**
235     * overrides
236     */
237    options.buttons.ok.callback = options.onEscape = function() {
238      if ($.isFunction(options.callback)) {
239        return options.callback();
240      }
241      return true;
242    };
243
244    return exports.dialog(options);
245  };
246
247  exports.confirm = function() {
248    var options;
249
250    options = mergeButtons(["cancel", "confirm"], arguments, ["message", "callback"]);
251
252    /**
253     * overrides; undo anything the user tried to set they shouldn't have
254     */
255    options.buttons.cancel.callback = options.onEscape = function() {
256      return options.callback(false);
257    };
258
259    options.buttons.confirm.callback = function() {
260      return options.callback(true);
261    };
262
263    // confirm specific validation
264    if (!$.isFunction(options.callback)) {
265      throw new Error("confirm requires a callback");
266    }
267
268    return exports.dialog(options);
269  };
270
271  exports.prompt = function() {
272    var options;
273    var defaults;
274    var dialog;
275    var form;
276    var input;
277    var shouldShow;
278
279    // we have to create our form first otherwise
280    // its value is undefined when gearing up our options
281    // @TODO this could be solved by allowing message to
282    // be a function instead...
283    form = $(templates.form);
284
285    defaults = {
286      buttons: createLabels("cancel", "confirm"),
287      value: ""
288    };
289
290    options = validateButtons(
291      mergeArguments(defaults, arguments, ["title", "callback"]),
292      ["cancel", "confirm"]
293    );
294
295    // capture the user's show value; we always set this to false before
296    // spawning the dialog to give us a chance to attach some handlers to
297    // it, but we need to make sure we respect a preference not to show it
298    shouldShow = (options.show === undefined) ? true : options.show;
299
300    /**
301     * overrides; undo anything the user tried to set they shouldn't have
302     */
303    options.message = form;
304
305    options.buttons.cancel.callback = options.onEscape = function() {
306      return options.callback(null);
307    };
308
309    options.buttons.confirm.callback = function() {
310      return options.callback(input.val());
311    };
312
313    options.show = false;
314
315    // prompt specific validation
316    if (!options.title) {
317      throw new Error("prompt requires a title");
318    }
319
320    if (!$.isFunction(options.callback)) {
321      throw new Error("prompt requires a callback");
322    }
323
324    // create the input
325    input = $(templates.inputs.text);
326    input.val(options.value);
327
328    // now place it in our form
329    form.append(input);
330
331    form.on("submit", function(e) {
332      e.preventDefault();
333      // @TODO can we actually click *the* button object instead?
334      // e.g. buttons.confirm.click() or similar
335      dialog.find(".btn-primary").click();
336    });
337
338    dialog = exports.dialog(options);
339
340    // clear the existing handler focusing the submit button...
341    dialog.off("shown.bs.modal");
342
343    // ...and replace it with one focusing our input, if possible
344    dialog.on("shown.bs.modal", function() {
345      input.focus();
346    });
347
348    if (shouldShow === true) {
349      dialog.modal("show");
350    }
351
352    return dialog;
353  };
354
355  exports.dialog = function(options) {
356    options = sanitize(options);
357
358    var dialog = $(templates.dialog);
359    var body = dialog.find(".modal-body");
360    var buttons = options.buttons;
361    var buttonStr = "";
362    var callbacks = {
363      onEscape: options.onEscape
364    };
365
366    each(buttons, function(key, button) {
367
368      // @TODO I don't like this string appending to itself; bit dirty. Needs reworking
369      // can we just build up button elements instead? slower but neater. Then button
370      // can just become a template too
371      buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>";
372      callbacks[key] = button.callback;
373    });
374
375    body.find(".bootbox-body").html(options.message);
376
377    if (options.animate === true) {
378      dialog.addClass("fade");
379    }
380
381    if (options.className) {
382      dialog.addClass(options.className);
383    }
384
385    if (options.title) {
386      body.before(templates.header);
387    }
388
389    if (options.closeButton) {
390      var closeButton = $(templates.closeButton);
391
392      if (options.title) {
393        dialog.find(".modal-header").prepend(closeButton);
394      } else {
395        closeButton.css("margin-top", "-10px").prependTo(body);
396      }
397    }
398
399    if (options.title) {
400      dialog.find(".modal-title").html(options.title);
401    }
402
403    if (buttonStr.length) {
404      body.after(templates.footer);
405      dialog.find(".modal-footer").html(buttonStr);
406    }
407
408
409    /**
410     * Bootstrap event listeners; used handle extra
411     * setup & teardown required after the underlying
412     * modal has performed certain actions
413     */
414
415    dialog.on("hidden.bs.modal", function(e) {
416      // ensure we don't accidentally intercept hidden events triggered
417      // by children of the current dialog. We shouldn't anymore now BS
418      // namespaces its events; but still worth doing
419      if (e.target === this) {
420        dialog.remove();
421      }
422    });
423
424    /*
425    dialog.on("show.bs.modal", function() {
426      // sadly this doesn't work; show is called *just* before
427      // the backdrop is added so we'd need a setTimeout hack or
428      // otherwise... leaving in as would be nice
429      if (options.backdrop) {
430        dialog.next(".modal-backdrop").addClass("bootbox-backdrop");
431      }
432    });
433    */
434
435    dialog.on("shown.bs.modal", function() {
436      dialog.find(".btn-primary:first").focus();
437    });
438
439    /**
440     * Bootbox event listeners; experimental and may not last
441     * just an attempt to decouple some behaviours from their
442     * respective triggers
443     */
444
445    dialog.on("escape.close.bb", function(e) {
446      if (callbacks.onEscape) {
447        processCallback(e, dialog, callbacks.onEscape);
448      }
449    });
450
451    /**
452     * Standard jQuery event listeners; used to handle user
453     * interaction with our dialog
454     */
455
456    dialog.on("click", ".modal-footer button", function(e) {
457      var callbackKey = $(this).data("bb-handler");
458
459      processCallback(e, dialog, callbacks[callbackKey]);
460
461    });
462
463    dialog.on("click", ".bootbox-close-button", function(e) {
464      // onEscape might be falsy but that's fine; the fact is
465      // if the user has managed to click the close button we
466      // have to close the dialog, callback or not
467      processCallback(e, dialog, callbacks.onEscape);
468    });
469
470    dialog.on("keyup", function(e) {
471      if (e.which === 27) {
472        dialog.trigger("escape.close.bb");
473      }
474    });
475
476    // the remainder of this method simply deals with adding our
477    // dialogent to the DOM, augmenting it with Bootstrap's modal
478    // functionality and then giving the resulting object back
479    // to our caller
480
481    appendTo.append(dialog);
482
483    dialog.modal({
484      backdrop: options.backdrop,
485      keyboard: false,
486      show: false
487    });
488
489    if (options.show) {
490      dialog.modal("show");
491    }
492
493    // @TODO should we return the raw element here or should
494    // we wrap it in an object on which we can expose some neater
495    // methods, e.g. var d = bootbox.alert(); d.hide(); instead
496    // of d.modal("hide");
497
498   /*
499    function BBDialog(elem) {
500      this.elem = elem;
501    }
502
503    BBDialog.prototype = {
504      hide: function() {
505        return this.elem.modal("hide");
506      },
507      show: function() {
508        return this.elem.modal("show");
509      }
510    };
511    */
512
513    return dialog;
514
515  };
516
517  exports.setDefaults = function(values) {
518    $.extend(defaults, values);
519  };
520
521  exports.hideAll = function() {
522    $(".bootbox").modal("hide");
523  };
524
525
526  /**
527   * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
528   * unlikely to be required. If this gets too large it can be split out into separate JS files.
529   */
530  var locales = {
531    br : {
532      OK      : "OK",
533      CANCEL  : "Cancelar",
534      CONFIRM : "Sim"
535    },
536    da : {
537      OK      : "OK",
538      CANCEL  : "Annuller",
539      CONFIRM : "Accepter"
540    },
541    de : {
542      OK      : "OK",
543      CANCEL  : "Abbrechen",
544      CONFIRM : "Akzeptieren"
545    },
546    en : {
547      OK      : "OK",
548      CANCEL  : "Cancel",
549      CONFIRM : "OK"
550    },
551    es : {
552      OK      : "OK",
553      CANCEL  : "Cancelar",
554      CONFIRM : "Aceptar"
555    },
556    fi : {
557      OK      : "OK",
558      CANCEL  : "Peruuta",
559      CONFIRM : "OK"
560    },
561    fr : {
562      OK      : "OK",
563      CANCEL  : "Annuler",
564      CONFIRM : "D'accord"
565    },
566    it : {
567      OK      : "OK",
568      CANCEL  : "Annulla",
569      CONFIRM : "Conferma"
570    },
571    nl : {
572      OK      : "OK",
573      CANCEL  : "Annuleren",
574      CONFIRM : "Accepteren"
575    },
576    pl : {
577      OK      : "OK",
578      CANCEL  : "Anuluj",
579      CONFIRM : "Potwierdź"
580    },
581    ru : {
582      OK      : "OK",
583      CANCEL  : "ОтЌеМа",
584      CONFIRM : "ПрОЌеМОть"
585    },
586    zh_CN : {
587      OK      : "OK",
588      CANCEL  : "取消",
589      CONFIRM : "确讀"
590    },
591    zh_TW : {
592      OK      : "OK",
593      CANCEL  : "取消",
594      CONFIRM : "確認"
595    }
596  };
597
598  exports.init = function(_$) {
599    window.bootbox = init(_$ || $);
600  };
601
602  return exports;
603
604}(window.jQuery));
Note: See TracBrowser for help on using the repository browser.