16 * limitations under the License. |
16 * limitations under the License. |
17 * ========================================================== */ |
17 * ========================================================== */ |
18 |
18 |
19 !function ($) { |
19 !function ($) { |
20 |
20 |
21 "use strict"; // jshint ;_; |
21 "use strict"; // jshint ;_; |
22 |
22 |
23 /* MODAL MANAGER CLASS DEFINITION |
23 /* MODAL MANAGER CLASS DEFINITION |
24 * ====================== */ |
24 * ====================== */ |
25 |
25 |
26 var ModalManager = function (element, options) { |
26 var ModalManager = function (element, options) { |
31 |
31 |
32 constructor: ModalManager, |
32 constructor: ModalManager, |
33 |
33 |
34 init: function (element, options) { |
34 init: function (element, options) { |
35 this.$element = $(element); |
35 this.$element = $(element); |
36 this.options = $.extend({}, $.fn.modalmanager.defaults, this.$element.data(), typeof options == 'object' && options); |
36 this.options = $.extend({}, $.fn.modalmanager.defaults, this.$element.data(), typeof options === 'object' && options); |
37 this.stack = []; |
37 this.stack = []; |
38 this.backdropCount = 0; |
38 this.backdropCount = 0; |
39 |
39 |
40 if (this.options.resize) { |
40 if (this.options.resize) { |
41 var resizeTimeout, |
41 var resizeTimeout, |
42 that = this; |
42 that = this; |
43 |
43 |
44 $(window).on('resize.modal', function () { |
44 $(window).on('resize.modal', function () { |
45 resizeTimeout && clearTimeout(resizeTimeout); |
45 if (resizeTimeout) { |
|
46 clearTimeout(resizeTimeout); |
|
47 } |
46 resizeTimeout = setTimeout(function () { |
48 resizeTimeout = setTimeout(function () { |
47 for (var i = 0; i < that.stack.length; i++) { |
49 for (var i = 0; i < that.stack.length; i++) { |
48 that.stack[i].isShown && that.stack[i].layout(); |
50 if (that.stack[i].isShown) { |
|
51 that.stack[i].layout(); |
|
52 } |
49 } |
53 } |
50 }, 10); |
54 }, 10); |
51 }); |
55 }); |
52 } |
56 } |
53 }, |
57 }, |
80 |
84 |
81 that.backdrop(modal, function () { |
85 that.backdrop(modal, function () { |
82 modal.$element.show(); |
86 modal.$element.show(); |
83 |
87 |
84 if (transition) { |
88 if (transition) { |
85 //modal.$element[0].style.display = 'run-in'; |
89 //modal.$element[0].style.display = 'run-in'; |
86 modal.$element[0].offsetWidth; |
90 modal.$element[0].offsetWidth; |
87 //modal.$element.one($.support.transition.end, function () { modal.$element[0].style.display = 'block' }); |
91 //modal.$element.one($.support.transition.end, function () { modal.$element[0].style.display = 'block' }); |
88 } |
92 } |
89 |
93 |
90 modal.layout(); |
94 modal.layout(); |
91 |
95 |
92 modal.$element |
96 modal.$element |
96 var complete = function () { |
100 var complete = function () { |
97 that.setFocus(); |
101 that.setFocus(); |
98 modal.$element.trigger('shown'); |
102 modal.$element.trigger('shown'); |
99 }; |
103 }; |
100 |
104 |
101 transition ? |
105 if (transition) { |
102 modal.$element.one($.support.transition.end, complete) : |
106 modal.$element.one($.support.transition.end, complete); |
|
107 } else { |
103 complete(); |
108 complete(); |
|
109 } |
104 }); |
110 }); |
105 }; |
111 }; |
106 |
112 |
107 modal.options.replace ? |
113 if (modal.options.replace) { |
108 that.replace(showModal) : |
114 that.replace(showModal); |
|
115 } else { |
109 showModal(); |
116 showModal(); |
|
117 } |
110 })); |
118 })); |
111 |
119 |
112 modal.$element.on('hidden.modalmanager', targetIsSelf(function (e) { |
120 modal.$element.on('hidden.modalmanager', targetIsSelf(function (e) { |
113 that.backdrop(modal); |
121 that.backdrop(modal); |
114 // handle the case when a modal may have been removed from the dom before this callback executes |
122 // handle the case when a modal may have been removed from the dom before this callback executes |
115 if (!modal.$element.parent().length) { |
123 if (!modal.$element.parent().length) { |
116 that.destroyModal(modal); |
124 that.destroyModal(modal); |
117 } else if (modal.$backdrop) { |
125 } else if (modal.$backdrop) { |
118 var transition = $.support.transition && modal.$element.hasClass('fade'); |
126 var transition = $.support.transition && modal.$element.hasClass('fade'); |
119 |
127 |
120 // trigger a relayout due to firebox's buggy transition end event |
128 // trigger a relayout due to firefox's buggy transition end event |
121 if (transition) |
129 if (transition) { |
122 modal.$element[0].offsetWidth; |
130 modal.$element[0].offsetWidth; |
123 $.support.transition && modal.$element.hasClass('fade') ? |
131 } |
|
132 if ($.support.transition && modal.$element.hasClass('fade')) { |
124 modal.$backdrop.one($.support.transition.end, function () { |
133 modal.$backdrop.one($.support.transition.end, function () { |
125 modal.destroy(); |
134 modal.destroy(); |
126 }) : |
135 }); |
|
136 } else { |
127 modal.destroy(); |
137 modal.destroy(); |
|
138 } |
128 } else { |
139 } else { |
129 modal.destroy(); |
140 modal.destroy(); |
130 } |
141 } |
131 |
142 |
132 })); |
143 })); |
151 |
164 |
152 setFocus: function () { |
165 setFocus: function () { |
153 var topModal; |
166 var topModal; |
154 |
167 |
155 for (var i = 0; i < this.stack.length; i++) { |
168 for (var i = 0; i < this.stack.length; i++) { |
156 if (this.stack[i].isShown) topModal = this.stack[i]; |
169 if (this.stack[i].isShown) { |
157 } |
170 topModal = this.stack[i]; |
158 |
171 } |
159 if (!topModal) return; |
172 } |
|
173 |
|
174 if (!topModal) { |
|
175 return; |
|
176 } |
160 |
177 |
161 topModal.focus(); |
178 topModal.focus(); |
162 }, |
179 }, |
163 |
180 |
164 destroyModal: function (modal) { |
181 destroyModal: function (modal) { |
165 modal.$element.off('.modalmanager'); |
182 modal.$element.off('.modalmanager'); |
166 if (modal.$backdrop) this.removeBackdrop(modal); |
183 if (modal.$backdrop) { |
|
184 this.removeBackdrop(modal); |
|
185 } |
167 this.stack.splice(this.getIndexOfModal(modal), 1); |
186 this.stack.splice(this.getIndexOfModal(modal), 1); |
168 |
187 |
169 var hasOpenModal = this.hasOpenModal(); |
188 var hasOpenModal = this.hasOpenModal(); |
170 |
189 |
171 this.$element.toggleClass('modal-open', hasOpenModal); |
190 this.$element.toggleClass('modal-open', hasOpenModal); |
183 return this.stack[index]; |
202 return this.stack[index]; |
184 }, |
203 }, |
185 |
204 |
186 getIndexOfModal: function (modal) { |
205 getIndexOfModal: function (modal) { |
187 for (var i = 0; i < this.stack.length; i++) { |
206 for (var i = 0; i < this.stack.length; i++) { |
188 if (modal === this.stack[i]) return i; |
207 if (modal === this.stack[i]) { |
|
208 return i; |
|
209 } |
189 } |
210 } |
190 }, |
211 }, |
191 |
212 |
192 replace: function (callback) { |
213 replace: function (callback) { |
193 var topModal; |
214 var topModal; |
194 |
215 |
195 for (var i = 0; i < this.stack.length; i++) { |
216 for (var i = 0; i < this.stack.length; i++) { |
196 if (this.stack[i].isShown) topModal = this.stack[i]; |
217 if (this.stack[i].isShown) { |
|
218 topModal = this.stack[i]; |
|
219 } |
197 } |
220 } |
198 |
221 |
199 if (topModal) { |
222 if (topModal) { |
200 this.$backdropHandle = topModal.$backdrop; |
223 this.$backdropHandle = topModal.$backdrop; |
201 topModal.$backdrop = null; |
224 topModal.$backdrop = null; |
202 |
225 |
203 callback && topModal.$element.one('hidden', |
226 if (callback) { |
204 targetIsSelf($.proxy(callback, this))); |
227 topModal.$element.one('hidden', |
|
228 targetIsSelf($.proxy(callback, this))); |
|
229 } |
205 |
230 |
206 topModal.hide(); |
231 topModal.hide(); |
207 } else if (callback) { |
232 } else if (callback) { |
208 callback(); |
233 callback(); |
209 } |
234 } |
223 .appendTo(this.$element); |
248 .appendTo(this.$element); |
224 } else { |
249 } else { |
225 $backdrop = this.$backdropHandle; |
250 $backdrop = this.$backdropHandle; |
226 $backdrop.off('.modalmanager'); |
251 $backdrop.off('.modalmanager'); |
227 this.$backdropHandle = null; |
252 this.$backdropHandle = null; |
228 this.isLoading && this.removeSpinner(); |
253 if (this.isLoading) { |
|
254 this.removeSpinner(); |
|
255 } |
229 } |
256 } |
230 |
257 |
231 return $backdrop; |
258 return $backdrop; |
232 }, |
259 }, |
233 |
260 |
241 |
268 |
242 $container = $('<div class="modal-scrollable">') |
269 $container = $('<div class="modal-scrollable">') |
243 .css('z-index', getzIndex('modal', this.getOpenModals().length)) |
270 .css('z-index', getzIndex('modal', this.getOpenModals().length)) |
244 .appendTo(this.$element); |
271 .appendTo(this.$element); |
245 |
272 |
246 if (modal && modal.options.backdrop != 'static') { |
273 if (modal && modal.options.backdrop === 'hide') { |
247 $container.on('click.modal', targetIsSelf(function (e) { |
274 $container.on('click.modal', targetIsSelf(function (e) { |
248 modal.hide(); |
275 modal.hide(); |
249 })); |
276 })); |
250 } else if (modal) { |
277 } else if (modal && modal.options.backdrop === 'attention') { |
251 $container.on('click.modal', targetIsSelf(function (e) { |
278 $container.on('click.modal', targetIsSelf(function (e) { |
252 modal.attention(); |
279 modal.attention(); |
253 })); |
280 })); |
254 } |
281 } |
255 |
282 |
267 |
294 |
268 modal.$backdrop = this.createBackdrop(animate, modal.options.backdropTemplate); |
295 modal.$backdrop = this.createBackdrop(animate, modal.options.backdropTemplate); |
269 |
296 |
270 modal.$backdrop.css('z-index', getzIndex('backdrop', this.getOpenModals().length)); |
297 modal.$backdrop.css('z-index', getzIndex('backdrop', this.getOpenModals().length)); |
271 |
298 |
272 if (doAnimate) modal.$backdrop[0].offsetWidth; // force reflow |
299 if (doAnimate) { |
|
300 modal.$backdrop[0].offsetWidth; |
|
301 } // force reflow |
273 |
302 |
274 modal.$backdrop.addClass('in'); |
303 modal.$backdrop.addClass('in'); |
275 |
304 |
276 this.backdropCount += 1; |
305 this.backdropCount += 1; |
277 |
306 |
278 doAnimate ? |
307 if (doAnimate) { |
279 modal.$backdrop.one($.support.transition.end, callback) : |
308 modal.$backdrop.one($.support.transition.end, callback); |
|
309 } else { |
280 callback(); |
310 callback(); |
|
311 } |
281 |
312 |
282 } else if (!modal.isShown && modal.$backdrop) { |
313 } else if (!modal.isShown && modal.$backdrop) { |
283 modal.$backdrop.removeClass('in'); |
314 modal.$backdrop.removeClass('in'); |
284 |
315 |
285 this.backdropCount -= 1; |
316 this.backdropCount -= 1; |
286 |
317 |
287 var that = this; |
318 var that = this; |
288 |
319 |
289 $.support.transition && modal.$element.hasClass('fade') ? |
320 if ($.support.transition && modal.$element.hasClass('fade')) { |
290 modal.$backdrop.one($.support.transition.end, function () { |
321 modal.$backdrop.one($.support.transition.end, function () { |
291 that.removeBackdrop(modal) |
322 that.removeBackdrop(modal); |
292 }) : |
323 }); |
|
324 } else { |
293 that.removeBackdrop(modal); |
325 that.removeBackdrop(modal); |
|
326 } |
294 |
327 |
295 } else if (callback) { |
328 } else if (callback) { |
296 callback(); |
329 callback(); |
297 } |
330 } |
298 }, |
331 }, |
299 |
332 |
300 removeSpinner: function () { |
333 removeSpinner: function () { |
301 this.$spinner && this.$spinner.remove(); |
334 if (this.$spinner) { |
|
335 this.$spinner.remove(); |
|
336 } |
302 this.$spinner = null; |
337 this.$spinner = null; |
303 this.isLoading = false; |
338 this.isLoading = false; |
304 }, |
339 }, |
305 |
340 |
306 removeLoading: function () { |
341 removeLoading: function () { |
307 this.$backdropHandle && this.$backdropHandle.remove(); |
342 if (this.$backdropHandle) { |
|
343 this.$backdropHandle.remove(); |
|
344 } |
308 this.$backdropHandle = null; |
345 this.$backdropHandle = null; |
309 this.removeSpinner(); |
346 this.removeSpinner(); |
310 }, |
347 }, |
311 |
348 |
312 loading: function (callback) { |
349 loading: function (callback) { |
337 .append($spinner) |
374 .append($spinner) |
338 .on('click.modalmanager', $.proxy(this.loading, this)); |
375 .on('click.modalmanager', $.proxy(this.loading, this)); |
339 |
376 |
340 this.isLoading = true; |
377 this.isLoading = true; |
341 |
378 |
342 $.support.transition ? |
379 if ($.support.transition) { |
343 this.$backdropHandle.one($.support.transition.end, callback) : |
380 this.$backdropHandle.one($.support.transition.end, callback); |
|
381 } else { |
344 callback(); |
382 callback(); |
|
383 } |
345 |
384 |
346 } else if (this.isLoading && this.$backdropHandle) { |
385 } else if (this.isLoading && this.$backdropHandle) { |
347 this.$backdropHandle.removeClass('in'); |
386 this.$backdropHandle.removeClass('in'); |
348 |
387 |
349 var that = this; |
388 var that = this; |
350 $.support.transition ? |
389 if ($.support.transition) { |
351 this.$backdropHandle.one($.support.transition.end, function () { |
390 this.$backdropHandle.one($.support.transition.end, function () { |
352 that.removeLoading() |
391 that.removeLoading(); |
353 }) : |
392 }); |
|
393 } else { |
354 that.removeLoading(); |
394 that.removeLoading(); |
|
395 } |
355 |
396 |
356 } else if (callback) { |
397 } else if (callback) { |
357 callback(this.isLoading); |
398 callback(this.isLoading); |
358 } |
399 } |
359 } |
400 } |
371 |
412 |
372 if (typeof zIndexFactor === 'undefined') { |
413 if (typeof zIndexFactor === 'undefined') { |
373 var $baseModal = $('<div class="modal hide" />').appendTo('body'), |
414 var $baseModal = $('<div class="modal hide" />').appendTo('body'), |
374 $baseBackdrop = $('<div class="modal-backdrop hide" />').appendTo('body'); |
415 $baseBackdrop = $('<div class="modal-backdrop hide" />').appendTo('body'); |
375 |
416 |
376 baseIndex['modal'] = +$baseModal.css('z-index'); |
417 baseIndex.modal = +$baseModal.css('z-index'); |
377 baseIndex['backdrop'] = +$baseBackdrop.css('z-index'); |
418 baseIndex.backdrop = +$baseBackdrop.css('z-index'); |
378 zIndexFactor = baseIndex['modal'] - baseIndex['backdrop']; |
419 zIndexFactor = baseIndex.modal - baseIndex.backdrop; |
379 |
420 |
380 $baseModal.remove(); |
421 $baseModal.remove(); |
381 $baseBackdrop.remove(); |
422 $baseBackdrop.remove(); |
382 $baseBackdrop = $baseModal = null; |
423 $baseBackdrop = $baseModal = null; |
383 } |
424 } |
384 |
425 |
385 return baseIndex[type] + (zIndexFactor * pos); |
426 return baseIndex[type] + (zIndexFactor * pos); |
386 |
427 |
387 } |
428 }; |
388 }()); |
429 }()); |
389 |
430 |
390 // make sure the event target is the modal itself in order to prevent |
431 // make sure the event target is the modal itself in order to prevent |
391 // other components such as tabsfrom triggering the modal manager. |
432 // other components such as tabsfrom triggering the modal manager. |
392 // if Boostsrap namespaced events, this would not be needed. |
433 // if Boostsrap namespaced events, this would not be needed. |
405 $.fn.modalmanager = function (option, args) { |
446 $.fn.modalmanager = function (option, args) { |
406 return this.each(function () { |
447 return this.each(function () { |
407 var $this = $(this), |
448 var $this = $(this), |
408 data = $this.data('modalmanager'); |
449 data = $this.data('modalmanager'); |
409 |
450 |
410 if (!data) $this.data('modalmanager', (data = new ModalManager(this, option))); |
451 if (!data) { |
411 if (typeof option === 'string') data[option].apply(data, [].concat(args)) |
452 $this.data('modalmanager', (data = new ModalManager(this, option))); |
412 }) |
453 } |
|
454 if (typeof option === 'string') { |
|
455 data[option].apply(data, [].concat(args)); |
|
456 } |
|
457 }); |
413 }; |
458 }; |
414 |
459 |
415 $.fn.modalmanager.defaults = { |
460 $.fn.modalmanager.defaults = { |
416 backdropLimit: 999, |
461 backdropLimit: 999, |
417 resize: true, |
462 resize: true, |
418 spinner: '<div class="loading-spinner fade" style="width: 200px; margin-left: -100px;"><div class="progress progress-striped active"><div class="bar" style="width: 100%;"></div></div></div>', |
463 spinner: '<div class="loading-spinner fade" style="width: 200px; margin-left: -100px;"><div class="progress progress-striped active"><div class="bar" style="width: 100%;"></div></div></div>', |
419 backdropTemplate: '<div class="modal-backdrop" />' |
464 backdropTemplate: '<div class="modal-backdrop" />' |
420 }; |
465 }; |
421 |
466 |
422 $.fn.modalmanager.Constructor = ModalManager |
467 $.fn.modalmanager.Constructor = ModalManager; |
423 |
468 |
424 // ModalManager handles the modal-open class so we need |
469 // ModalManager handles the modal-open class so we need |
425 // to remove conflicting bootstrap 3 event handlers |
470 // to remove conflicting bootstrap 3 event handlers |
426 $(function () { |
471 $(function () { |
427 $(document).off('show.bs.modal').off('hidden.bs.modal'); |
472 $(document).off('show.bs.modal').off('hidden.bs.modal'); |