提交 | 用户 | 时间
|
58d006
|
1 |
/** |
A |
2 |
* bootbox.js [v4.4.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><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: "en", |
|
74 |
// show backdrop or not. Default to static so user has to interact with dialog |
|
75 |
backdrop: "static", |
|
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.call(dialog, 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 |
buttons = options.buttons; |
|
152 |
|
|
153 |
total = getKeyLength(buttons); |
|
154 |
|
|
155 |
each(buttons, function(key, button, index) { |
|
156 |
|
|
157 |
if ($.isFunction(button)) { |
|
158 |
// short form, assume value is our callback. Since button |
|
159 |
// isn't an object it isn't a reference either so re-assign it |
|
160 |
button = buttons[key] = { |
|
161 |
callback: button |
|
162 |
}; |
|
163 |
} |
|
164 |
|
|
165 |
// before any further checks make sure by now button is the correct type |
|
166 |
if ($.type(button) !== "object") { |
|
167 |
throw new Error("button with key " + key + " must be an object"); |
|
168 |
} |
|
169 |
|
|
170 |
if (!button.label) { |
|
171 |
// the lack of an explicit label means we'll assume the key is good enough |
|
172 |
button.label = key; |
|
173 |
} |
|
174 |
|
|
175 |
if (!button.className) { |
|
176 |
if (total <= 2 && index === total-1) { |
|
177 |
// always add a primary to the main option in a two-button dialog |
|
178 |
button.className = "btn-primary"; |
|
179 |
} else { |
|
180 |
button.className = "btn-default"; |
|
181 |
} |
|
182 |
} |
|
183 |
}); |
|
184 |
|
|
185 |
return options; |
|
186 |
} |
|
187 |
|
|
188 |
/** |
|
189 |
* map a flexible set of arguments into a single returned object |
|
190 |
* if args.length is already one just return it, otherwise |
|
191 |
* use the properties argument to map the unnamed args to |
|
192 |
* object properties |
|
193 |
* so in the latter case: |
|
194 |
* mapArguments(["foo", $.noop], ["message", "callback"]) |
|
195 |
* -> { message: "foo", callback: $.noop } |
|
196 |
*/ |
|
197 |
function mapArguments(args, properties) { |
|
198 |
var argn = args.length; |
|
199 |
var options = {}; |
|
200 |
|
|
201 |
if (argn < 1 || argn > 2) { |
|
202 |
throw new Error("Invalid argument length"); |
|
203 |
} |
|
204 |
|
|
205 |
if (argn === 2 || typeof args[0] === "string") { |
|
206 |
options[properties[0]] = args[0]; |
|
207 |
options[properties[1]] = args[1]; |
|
208 |
} else { |
|
209 |
options = args[0]; |
|
210 |
} |
|
211 |
|
|
212 |
return options; |
|
213 |
} |
|
214 |
|
|
215 |
/** |
|
216 |
* merge a set of default dialog options with user supplied arguments |
|
217 |
*/ |
|
218 |
function mergeArguments(defaults, args, properties) { |
|
219 |
return $.extend( |
|
220 |
// deep merge |
|
221 |
true, |
|
222 |
// ensure the target is an empty, unreferenced object |
|
223 |
{}, |
|
224 |
// the base options object for this type of dialog (often just buttons) |
|
225 |
defaults, |
|
226 |
// args could be an object or array; if it's an array properties will |
|
227 |
// map it to a proper options object |
|
228 |
mapArguments( |
|
229 |
args, |
|
230 |
properties |
|
231 |
) |
|
232 |
); |
|
233 |
} |
|
234 |
|
|
235 |
/** |
|
236 |
* this entry-level method makes heavy use of composition to take a simple |
|
237 |
* range of inputs and return valid options suitable for passing to bootbox.dialog |
|
238 |
*/ |
|
239 |
function mergeDialogOptions(className, labels, properties, args) { |
|
240 |
// build up a base set of dialog properties |
|
241 |
var baseOptions = { |
|
242 |
className: "bootbox-" + className, |
|
243 |
buttons: createLabels.apply(null, labels) |
|
244 |
}; |
|
245 |
|
|
246 |
// ensure the buttons properties generated, *after* merging |
|
247 |
// with user args are still valid against the supplied labels |
|
248 |
return validateButtons( |
|
249 |
// merge the generated base properties with user supplied arguments |
|
250 |
mergeArguments( |
|
251 |
baseOptions, |
|
252 |
args, |
|
253 |
// if args.length > 1, properties specify how each arg maps to an object key |
|
254 |
properties |
|
255 |
), |
|
256 |
labels |
|
257 |
); |
|
258 |
} |
|
259 |
|
|
260 |
/** |
|
261 |
* from a given list of arguments return a suitable object of button labels |
|
262 |
* all this does is normalise the given labels and translate them where possible |
|
263 |
* e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" } |
|
264 |
*/ |
|
265 |
function createLabels() { |
|
266 |
var buttons = {}; |
|
267 |
|
|
268 |
for (var i = 0, j = arguments.length; i < j; i++) { |
|
269 |
var argument = arguments[i]; |
|
270 |
var key = argument.toLowerCase(); |
|
271 |
var value = argument.toUpperCase(); |
|
272 |
|
|
273 |
buttons[key] = { |
|
274 |
label: _t(value) |
|
275 |
}; |
|
276 |
} |
|
277 |
|
|
278 |
return buttons; |
|
279 |
} |
|
280 |
|
|
281 |
function validateButtons(options, buttons) { |
|
282 |
var allowedButtons = {}; |
|
283 |
each(buttons, function(key, value) { |
|
284 |
allowedButtons[value] = true; |
|
285 |
}); |
|
286 |
|
|
287 |
each(options.buttons, function(key) { |
|
288 |
if (allowedButtons[key] === undefined) { |
|
289 |
throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")"); |
|
290 |
} |
|
291 |
}); |
|
292 |
|
|
293 |
return options; |
|
294 |
} |
|
295 |
|
|
296 |
exports.alert = function() { |
|
297 |
var options; |
|
298 |
|
|
299 |
options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments); |
|
300 |
|
|
301 |
if (options.callback && !$.isFunction(options.callback)) { |
|
302 |
throw new Error("alert requires callback property to be a function when provided"); |
|
303 |
} |
|
304 |
|
|
305 |
/** |
|
306 |
* overrides |
|
307 |
*/ |
|
308 |
options.buttons.ok.callback = options.onEscape = function() { |
|
309 |
if ($.isFunction(options.callback)) { |
|
310 |
return options.callback.call(this); |
|
311 |
} |
|
312 |
return true; |
|
313 |
}; |
|
314 |
|
|
315 |
return exports.dialog(options); |
|
316 |
}; |
|
317 |
|
|
318 |
exports.confirm = function() { |
|
319 |
var options; |
|
320 |
|
|
321 |
options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments); |
|
322 |
|
|
323 |
/** |
|
324 |
* overrides; undo anything the user tried to set they shouldn't have |
|
325 |
*/ |
|
326 |
options.buttons.cancel.callback = options.onEscape = function() { |
|
327 |
return options.callback.call(this, false); |
|
328 |
}; |
|
329 |
|
|
330 |
options.buttons.confirm.callback = function() { |
|
331 |
return options.callback.call(this, true); |
|
332 |
}; |
|
333 |
|
|
334 |
// confirm specific validation |
|
335 |
if (!$.isFunction(options.callback)) { |
|
336 |
throw new Error("confirm requires a callback"); |
|
337 |
} |
|
338 |
|
|
339 |
return exports.dialog(options); |
|
340 |
}; |
|
341 |
|
|
342 |
exports.prompt = function() { |
|
343 |
var options; |
|
344 |
var defaults; |
|
345 |
var dialog; |
|
346 |
var form; |
|
347 |
var input; |
|
348 |
var shouldShow; |
|
349 |
var inputOptions; |
|
350 |
|
|
351 |
// we have to create our form first otherwise |
|
352 |
// its value is undefined when gearing up our options |
|
353 |
// @TODO this could be solved by allowing message to |
|
354 |
// be a function instead... |
|
355 |
form = $(templates.form); |
|
356 |
|
|
357 |
// prompt defaults are more complex than others in that |
|
358 |
// users can override more defaults |
|
359 |
// @TODO I don't like that prompt has to do a lot of heavy |
|
360 |
// lifting which mergeDialogOptions can *almost* support already |
|
361 |
// just because of 'value' and 'inputType' - can we refactor? |
|
362 |
defaults = { |
|
363 |
className: "bootbox-prompt", |
|
364 |
buttons: createLabels("cancel", "confirm"), |
|
365 |
value: "", |
|
366 |
inputType: "text" |
|
367 |
}; |
|
368 |
|
|
369 |
options = validateButtons( |
|
370 |
mergeArguments(defaults, arguments, ["title", "callback"]), |
|
371 |
["cancel", "confirm"] |
|
372 |
); |
|
373 |
|
|
374 |
// capture the user's show value; we always set this to false before |
|
375 |
// spawning the dialog to give us a chance to attach some handlers to |
|
376 |
// it, but we need to make sure we respect a preference not to show it |
|
377 |
shouldShow = (options.show === undefined) ? true : options.show; |
|
378 |
|
|
379 |
/** |
|
380 |
* overrides; undo anything the user tried to set they shouldn't have |
|
381 |
*/ |
|
382 |
options.message = form; |
|
383 |
|
|
384 |
options.buttons.cancel.callback = options.onEscape = function() { |
|
385 |
return options.callback.call(this, null); |
|
386 |
}; |
|
387 |
|
|
388 |
options.buttons.confirm.callback = function() { |
|
389 |
var value; |
|
390 |
|
|
391 |
switch (options.inputType) { |
|
392 |
case "text": |
|
393 |
case "textarea": |
|
394 |
case "email": |
|
395 |
case "select": |
|
396 |
case "date": |
|
397 |
case "time": |
|
398 |
case "number": |
|
399 |
case "password": |
|
400 |
value = input.val(); |
|
401 |
break; |
|
402 |
|
|
403 |
case "checkbox": |
|
404 |
var checkedItems = input.find("input:checked"); |
|
405 |
|
|
406 |
// we assume that checkboxes are always multiple, |
|
407 |
// hence we default to an empty array |
|
408 |
value = []; |
|
409 |
|
|
410 |
each(checkedItems, function(_, item) { |
|
411 |
value.push($(item).val()); |
|
412 |
}); |
|
413 |
break; |
|
414 |
} |
|
415 |
|
|
416 |
return options.callback.call(this, value); |
|
417 |
}; |
|
418 |
|
|
419 |
options.show = false; |
|
420 |
|
|
421 |
// prompt specific validation |
|
422 |
if (!options.title) { |
|
423 |
throw new Error("prompt requires a title"); |
|
424 |
} |
|
425 |
|
|
426 |
if (!$.isFunction(options.callback)) { |
|
427 |
throw new Error("prompt requires a callback"); |
|
428 |
} |
|
429 |
|
|
430 |
if (!templates.inputs[options.inputType]) { |
|
431 |
throw new Error("invalid prompt type"); |
|
432 |
} |
|
433 |
|
|
434 |
// create the input based on the supplied type |
|
435 |
input = $(templates.inputs[options.inputType]); |
|
436 |
|
|
437 |
switch (options.inputType) { |
|
438 |
case "text": |
|
439 |
case "textarea": |
|
440 |
case "email": |
|
441 |
case "date": |
|
442 |
case "time": |
|
443 |
case "number": |
|
444 |
case "password": |
|
445 |
input.val(options.value); |
|
446 |
break; |
|
447 |
|
|
448 |
case "select": |
|
449 |
var groups = {}; |
|
450 |
inputOptions = options.inputOptions || []; |
|
451 |
|
|
452 |
if (!$.isArray(inputOptions)) { |
|
453 |
throw new Error("Please pass an array of input options"); |
|
454 |
} |
|
455 |
|
|
456 |
if (!inputOptions.length) { |
|
457 |
throw new Error("prompt with select requires options"); |
|
458 |
} |
|
459 |
|
|
460 |
each(inputOptions, function(_, option) { |
|
461 |
|
|
462 |
// assume the element to attach to is the input... |
|
463 |
var elem = input; |
|
464 |
|
|
465 |
if (option.value === undefined || option.text === undefined) { |
|
466 |
throw new Error("given options in wrong format"); |
|
467 |
} |
|
468 |
|
|
469 |
// ... but override that element if this option sits in a group |
|
470 |
|
|
471 |
if (option.group) { |
|
472 |
// initialise group if necessary |
|
473 |
if (!groups[option.group]) { |
|
474 |
groups[option.group] = $("<optgroup/>").attr("label", option.group); |
|
475 |
} |
|
476 |
|
|
477 |
elem = groups[option.group]; |
|
478 |
} |
|
479 |
|
|
480 |
elem.append("<option value='" + option.value + "'>" + option.text + "</option>"); |
|
481 |
}); |
|
482 |
|
|
483 |
each(groups, function(_, group) { |
|
484 |
input.append(group); |
|
485 |
}); |
|
486 |
|
|
487 |
// safe to set a select's value as per a normal input |
|
488 |
input.val(options.value); |
|
489 |
break; |
|
490 |
|
|
491 |
case "checkbox": |
|
492 |
var values = $.isArray(options.value) ? options.value : [options.value]; |
|
493 |
inputOptions = options.inputOptions || []; |
|
494 |
|
|
495 |
if (!inputOptions.length) { |
|
496 |
throw new Error("prompt with checkbox requires options"); |
|
497 |
} |
|
498 |
|
|
499 |
if (!inputOptions[0].value || !inputOptions[0].text) { |
|
500 |
throw new Error("given options in wrong format"); |
|
501 |
} |
|
502 |
|
|
503 |
// checkboxes have to nest within a containing element, so |
|
504 |
// they break the rules a bit and we end up re-assigning |
|
505 |
// our 'input' element to this container instead |
|
506 |
input = $("<div/>"); |
|
507 |
|
|
508 |
each(inputOptions, function(_, option) { |
|
509 |
var checkbox = $(templates.inputs[options.inputType]); |
|
510 |
|
|
511 |
checkbox.find("input").attr("value", option.value); |
|
512 |
checkbox.find("label").append(option.text); |
|
513 |
|
|
514 |
// we've ensured values is an array so we can always iterate over it |
|
515 |
each(values, function(_, value) { |
|
516 |
if (value === option.value) { |
|
517 |
checkbox.find("input").prop("checked", true); |
|
518 |
} |
|
519 |
}); |
|
520 |
|
|
521 |
input.append(checkbox); |
|
522 |
}); |
|
523 |
break; |
|
524 |
} |
|
525 |
|
|
526 |
// @TODO provide an attributes option instead |
|
527 |
// and simply map that as keys: vals |
|
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 |
if (options.maxlength) { |
|
537 |
input.attr("maxlength", options.maxlength); |
|
538 |
} |
|
539 |
|
|
540 |
// now place it in our form |
|
541 |
form.append(input); |
|
542 |
|
|
543 |
form.on("submit", function(e) { |
|
544 |
e.preventDefault(); |
|
545 |
// Fix for SammyJS (or similar JS routing library) hijacking the form post. |
|
546 |
e.stopPropagation(); |
|
547 |
// @TODO can we actually click *the* button object instead? |
|
548 |
// e.g. buttons.confirm.click() or similar |
|
549 |
dialog.find(".btn-primary").click(); |
|
550 |
}); |
|
551 |
|
|
552 |
dialog = exports.dialog(options); |
|
553 |
|
|
554 |
// clear the existing handler focusing the submit button... |
|
555 |
dialog.off("shown.bs.modal"); |
|
556 |
|
|
557 |
// ...and replace it with one focusing our input, if possible |
|
558 |
dialog.on("shown.bs.modal", function() { |
|
559 |
// need the closure here since input isn't |
|
560 |
// an object otherwise |
|
561 |
input.focus(); |
|
562 |
}); |
|
563 |
|
|
564 |
if (shouldShow === true) { |
|
565 |
dialog.modal("show"); |
|
566 |
} |
|
567 |
|
|
568 |
return dialog; |
|
569 |
}; |
|
570 |
|
|
571 |
exports.dialog = function(options) { |
|
572 |
options = sanitize(options); |
|
573 |
|
|
574 |
var dialog = $(templates.dialog); |
|
575 |
var innerDialog = dialog.find(".modal-dialog"); |
|
576 |
var body = dialog.find(".modal-body"); |
|
577 |
var buttons = options.buttons; |
|
578 |
var buttonStr = ""; |
|
579 |
var callbacks = { |
|
580 |
onEscape: options.onEscape |
|
581 |
}; |
|
582 |
|
|
583 |
if ($.fn.modal === undefined) { |
|
584 |
throw new Error( |
|
585 |
"$.fn.modal is not defined; please double check you have included " + |
|
586 |
"the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ " + |
|
587 |
"for more details." |
|
588 |
); |
|
589 |
} |
|
590 |
|
|
591 |
each(buttons, function(key, button) { |
|
592 |
|
|
593 |
// @TODO I don't like this string appending to itself; bit dirty. Needs reworking |
|
594 |
// can we just build up button elements instead? slower but neater. Then button |
|
595 |
// can just become a template too |
|
596 |
buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>"; |
|
597 |
callbacks[key] = button.callback; |
|
598 |
}); |
|
599 |
|
|
600 |
body.find(".bootbox-body").html(options.message); |
|
601 |
|
|
602 |
if (options.animate === true) { |
|
603 |
dialog.addClass("fade"); |
|
604 |
} |
|
605 |
|
|
606 |
if (options.className) { |
|
607 |
dialog.addClass(options.className); |
|
608 |
} |
|
609 |
|
|
610 |
if (options.size === "large") { |
|
611 |
innerDialog.addClass("modal-lg"); |
|
612 |
} else if (options.size === "small") { |
|
613 |
innerDialog.addClass("modal-sm"); |
|
614 |
} |
|
615 |
|
|
616 |
if (options.title) { |
|
617 |
body.before(templates.header); |
|
618 |
} |
|
619 |
|
|
620 |
if (options.closeButton) { |
|
621 |
var closeButton = $(templates.closeButton); |
|
622 |
|
|
623 |
if (options.title) { |
|
624 |
dialog.find(".modal-header").prepend(closeButton); |
|
625 |
} else { |
|
626 |
closeButton.css("margin-top", "-10px").prependTo(body); |
|
627 |
} |
|
628 |
} |
|
629 |
|
|
630 |
if (options.title) { |
|
631 |
dialog.find(".modal-title").html(options.title); |
|
632 |
} |
|
633 |
|
|
634 |
if (buttonStr.length) { |
|
635 |
body.after(templates.footer); |
|
636 |
dialog.find(".modal-footer").html(buttonStr); |
|
637 |
} |
|
638 |
|
|
639 |
|
|
640 |
/** |
|
641 |
* Bootstrap event listeners; used handle extra |
|
642 |
* setup & teardown required after the underlying |
|
643 |
* modal has performed certain actions |
|
644 |
*/ |
|
645 |
|
|
646 |
dialog.on("hidden.bs.modal", function(e) { |
|
647 |
// ensure we don't accidentally intercept hidden events triggered |
|
648 |
// by children of the current dialog. We shouldn't anymore now BS |
|
649 |
// namespaces its events; but still worth doing |
|
650 |
if (e.target === this) { |
|
651 |
dialog.remove(); |
|
652 |
} |
|
653 |
}); |
|
654 |
|
|
655 |
/* |
|
656 |
dialog.on("show.bs.modal", function() { |
|
657 |
// sadly this doesn't work; show is called *just* before |
|
658 |
// the backdrop is added so we'd need a setTimeout hack or |
|
659 |
// otherwise... leaving in as would be nice |
|
660 |
if (options.backdrop) { |
|
661 |
dialog.next(".modal-backdrop").addClass("bootbox-backdrop"); |
|
662 |
} |
|
663 |
}); |
|
664 |
*/ |
|
665 |
|
|
666 |
dialog.on("shown.bs.modal", function() { |
|
667 |
dialog.find(".btn-primary:first").focus(); |
|
668 |
}); |
|
669 |
|
|
670 |
/** |
|
671 |
* Bootbox event listeners; experimental and may not last |
|
672 |
* just an attempt to decouple some behaviours from their |
|
673 |
* respective triggers |
|
674 |
*/ |
|
675 |
|
|
676 |
if (options.backdrop !== "static") { |
|
677 |
// A boolean true/false according to the Bootstrap docs |
|
678 |
// should show a dialog the user can dismiss by clicking on |
|
679 |
// the background. |
|
680 |
// We always only ever pass static/false to the actual |
|
681 |
// $.modal function because with `true` we can't trap |
|
682 |
// this event (the .modal-backdrop swallows it) |
|
683 |
// However, we still want to sort of respect true |
|
684 |
// and invoke the escape mechanism instead |
|
685 |
dialog.on("click.dismiss.bs.modal", function(e) { |
|
686 |
// @NOTE: the target varies in >= 3.3.x releases since the modal backdrop |
|
687 |
// moved *inside* the outer dialog rather than *alongside* it |
|
688 |
if (dialog.children(".modal-backdrop").length) { |
|
689 |
e.currentTarget = dialog.children(".modal-backdrop").get(0); |
|
690 |
} |
|
691 |
|
|
692 |
if (e.target !== e.currentTarget) { |
|
693 |
return; |
|
694 |
} |
|
695 |
|
|
696 |
dialog.trigger("escape.close.bb"); |
|
697 |
}); |
|
698 |
} |
|
699 |
|
|
700 |
dialog.on("escape.close.bb", function(e) { |
|
701 |
if (callbacks.onEscape) { |
|
702 |
processCallback(e, dialog, callbacks.onEscape); |
|
703 |
} |
|
704 |
}); |
|
705 |
|
|
706 |
/** |
|
707 |
* Standard jQuery event listeners; used to handle user |
|
708 |
* interaction with our dialog |
|
709 |
*/ |
|
710 |
|
|
711 |
dialog.on("click", ".modal-footer button", function(e) { |
|
712 |
var callbackKey = $(this).data("bb-handler"); |
|
713 |
|
|
714 |
processCallback(e, dialog, callbacks[callbackKey]); |
|
715 |
}); |
|
716 |
|
|
717 |
dialog.on("click", ".bootbox-close-button", function(e) { |
|
718 |
// onEscape might be falsy but that's fine; the fact is |
|
719 |
// if the user has managed to click the close button we |
|
720 |
// have to close the dialog, callback or not |
|
721 |
processCallback(e, dialog, callbacks.onEscape); |
|
722 |
}); |
|
723 |
|
|
724 |
dialog.on("keyup", function(e) { |
|
725 |
if (e.which === 27) { |
|
726 |
dialog.trigger("escape.close.bb"); |
|
727 |
} |
|
728 |
}); |
|
729 |
|
|
730 |
// the remainder of this method simply deals with adding our |
|
731 |
// dialogent to the DOM, augmenting it with Bootstrap's modal |
|
732 |
// functionality and then giving the resulting object back |
|
733 |
// to our caller |
|
734 |
|
|
735 |
$(options.container).append(dialog); |
|
736 |
|
|
737 |
dialog.modal({ |
|
738 |
backdrop: options.backdrop ? "static": false, |
|
739 |
keyboard: false, |
|
740 |
show: false |
|
741 |
}); |
|
742 |
|
|
743 |
if (options.show) { |
|
744 |
dialog.modal("show"); |
|
745 |
} |
|
746 |
|
|
747 |
// @TODO should we return the raw element here or should |
|
748 |
// we wrap it in an object on which we can expose some neater |
|
749 |
// methods, e.g. var d = bootbox.alert(); d.hide(); instead |
|
750 |
// of d.modal("hide"); |
|
751 |
|
|
752 |
/* |
|
753 |
function BBDialog(elem) { |
|
754 |
this.elem = elem; |
|
755 |
} |
|
756 |
|
|
757 |
BBDialog.prototype = { |
|
758 |
hide: function() { |
|
759 |
return this.elem.modal("hide"); |
|
760 |
}, |
|
761 |
show: function() { |
|
762 |
return this.elem.modal("show"); |
|
763 |
} |
|
764 |
}; |
|
765 |
*/ |
|
766 |
|
|
767 |
return dialog; |
|
768 |
|
|
769 |
}; |
|
770 |
|
|
771 |
exports.setDefaults = function() { |
|
772 |
var values = {}; |
|
773 |
|
|
774 |
if (arguments.length === 2) { |
|
775 |
// allow passing of single key/value... |
|
776 |
values[arguments[0]] = arguments[1]; |
|
777 |
} else { |
|
778 |
// ... and as an object too |
|
779 |
values = arguments[0]; |
|
780 |
} |
|
781 |
|
|
782 |
$.extend(defaults, values); |
|
783 |
}; |
|
784 |
|
|
785 |
exports.hideAll = function() { |
|
786 |
$(".bootbox").modal("hide"); |
|
787 |
|
|
788 |
return exports; |
|
789 |
}; |
|
790 |
|
|
791 |
|
|
792 |
/** |
|
793 |
* standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are |
|
794 |
* unlikely to be required. If this gets too large it can be split out into separate JS files. |
|
795 |
*/ |
|
796 |
var locales = { |
|
797 |
bg_BG : { |
|
798 |
OK : "Ок", |
|
799 |
CANCEL : "Отказ", |
|
800 |
CONFIRM : "Потвърждавам" |
|
801 |
}, |
|
802 |
br : { |
|
803 |
OK : "OK", |
|
804 |
CANCEL : "Cancelar", |
|
805 |
CONFIRM : "Sim" |
|
806 |
}, |
|
807 |
cs : { |
|
808 |
OK : "OK", |
|
809 |
CANCEL : "Zrušit", |
|
810 |
CONFIRM : "Potvrdit" |
|
811 |
}, |
|
812 |
da : { |
|
813 |
OK : "OK", |
|
814 |
CANCEL : "Annuller", |
|
815 |
CONFIRM : "Accepter" |
|
816 |
}, |
|
817 |
de : { |
|
818 |
OK : "OK", |
|
819 |
CANCEL : "Abbrechen", |
|
820 |
CONFIRM : "Akzeptieren" |
|
821 |
}, |
|
822 |
el : { |
|
823 |
OK : "Εντάξει", |
|
824 |
CANCEL : "Ακύρωση", |
|
825 |
CONFIRM : "Επιβεβαίωση" |
|
826 |
}, |
|
827 |
en : { |
|
828 |
OK : "OK", |
|
829 |
CANCEL : "Cancel", |
|
830 |
CONFIRM : "OK" |
|
831 |
}, |
|
832 |
es : { |
|
833 |
OK : "OK", |
|
834 |
CANCEL : "Cancelar", |
|
835 |
CONFIRM : "Aceptar" |
|
836 |
}, |
|
837 |
et : { |
|
838 |
OK : "OK", |
|
839 |
CANCEL : "Katkesta", |
|
840 |
CONFIRM : "OK" |
|
841 |
}, |
|
842 |
fa : { |
|
843 |
OK : "قبول", |
|
844 |
CANCEL : "لغو", |
|
845 |
CONFIRM : "تایید" |
|
846 |
}, |
|
847 |
fi : { |
|
848 |
OK : "OK", |
|
849 |
CANCEL : "Peruuta", |
|
850 |
CONFIRM : "OK" |
|
851 |
}, |
|
852 |
fr : { |
|
853 |
OK : "OK", |
|
854 |
CANCEL : "Annuler", |
|
855 |
CONFIRM : "D'accord" |
|
856 |
}, |
|
857 |
he : { |
|
858 |
OK : "אישור", |
|
859 |
CANCEL : "ביטול", |
|
860 |
CONFIRM : "אישור" |
|
861 |
}, |
|
862 |
hu : { |
|
863 |
OK : "OK", |
|
864 |
CANCEL : "Mégsem", |
|
865 |
CONFIRM : "Megerősít" |
|
866 |
}, |
|
867 |
hr : { |
|
868 |
OK : "OK", |
|
869 |
CANCEL : "Odustani", |
|
870 |
CONFIRM : "Potvrdi" |
|
871 |
}, |
|
872 |
id : { |
|
873 |
OK : "OK", |
|
874 |
CANCEL : "Batal", |
|
875 |
CONFIRM : "OK" |
|
876 |
}, |
|
877 |
it : { |
|
878 |
OK : "OK", |
|
879 |
CANCEL : "Annulla", |
|
880 |
CONFIRM : "Conferma" |
|
881 |
}, |
|
882 |
ja : { |
|
883 |
OK : "OK", |
|
884 |
CANCEL : "キャンセル", |
|
885 |
CONFIRM : "確認" |
|
886 |
}, |
|
887 |
lt : { |
|
888 |
OK : "Gerai", |
|
889 |
CANCEL : "Atšaukti", |
|
890 |
CONFIRM : "Patvirtinti" |
|
891 |
}, |
|
892 |
lv : { |
|
893 |
OK : "Labi", |
|
894 |
CANCEL : "Atcelt", |
|
895 |
CONFIRM : "Apstiprināt" |
|
896 |
}, |
|
897 |
nl : { |
|
898 |
OK : "OK", |
|
899 |
CANCEL : "Annuleren", |
|
900 |
CONFIRM : "Accepteren" |
|
901 |
}, |
|
902 |
no : { |
|
903 |
OK : "OK", |
|
904 |
CANCEL : "Avbryt", |
|
905 |
CONFIRM : "OK" |
|
906 |
}, |
|
907 |
pl : { |
|
908 |
OK : "OK", |
|
909 |
CANCEL : "Anuluj", |
|
910 |
CONFIRM : "Potwierdź" |
|
911 |
}, |
|
912 |
pt : { |
|
913 |
OK : "OK", |
|
914 |
CANCEL : "Cancelar", |
|
915 |
CONFIRM : "Confirmar" |
|
916 |
}, |
|
917 |
ru : { |
|
918 |
OK : "OK", |
|
919 |
CANCEL : "Отмена", |
|
920 |
CONFIRM : "Применить" |
|
921 |
}, |
|
922 |
sq : { |
|
923 |
OK : "OK", |
|
924 |
CANCEL : "Anulo", |
|
925 |
CONFIRM : "Prano" |
|
926 |
}, |
|
927 |
sv : { |
|
928 |
OK : "OK", |
|
929 |
CANCEL : "Avbryt", |
|
930 |
CONFIRM : "OK" |
|
931 |
}, |
|
932 |
th : { |
|
933 |
OK : "ตกลง", |
|
934 |
CANCEL : "ยกเลิก", |
|
935 |
CONFIRM : "ยืนยัน" |
|
936 |
}, |
|
937 |
tr : { |
|
938 |
OK : "Tamam", |
|
939 |
CANCEL : "İptal", |
|
940 |
CONFIRM : "Onayla" |
|
941 |
}, |
|
942 |
zh_CN : { |
|
943 |
OK : "OK", |
|
944 |
CANCEL : "取消", |
|
945 |
CONFIRM : "确认" |
|
946 |
}, |
|
947 |
zh_TW : { |
|
948 |
OK : "OK", |
|
949 |
CANCEL : "取消", |
|
950 |
CONFIRM : "確認" |
|
951 |
} |
|
952 |
}; |
|
953 |
|
|
954 |
exports.addLocale = function(name, values) { |
|
955 |
$.each(["OK", "CANCEL", "CONFIRM"], function(_, v) { |
|
956 |
if (!values[v]) { |
|
957 |
throw new Error("Please supply a translation for '" + v + "'"); |
|
958 |
} |
|
959 |
}); |
|
960 |
|
|
961 |
locales[name] = { |
|
962 |
OK: values.OK, |
|
963 |
CANCEL: values.CANCEL, |
|
964 |
CONFIRM: values.CONFIRM |
|
965 |
}; |
|
966 |
|
|
967 |
return exports; |
|
968 |
}; |
|
969 |
|
|
970 |
exports.removeLocale = function(name) { |
|
971 |
delete locales[name]; |
|
972 |
|
|
973 |
return exports; |
|
974 |
}; |
|
975 |
|
|
976 |
exports.setLocale = function(name) { |
|
977 |
return exports.setDefaults("locale", name); |
|
978 |
}; |
|
979 |
|
|
980 |
exports.init = function(_$) { |
|
981 |
return init(_$ || $); |
|
982 |
}; |
|
983 |
|
|
984 |
return exports; |
|
985 |
})); |