Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/* ===========================================================
2
 * bootstrap-modalmanager.js v2.2.5
3
 * ===========================================================
4
 * Copyright 2012 Jordan Schroter.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 * ========================================================== */
18
 
19
!function ($) {
20
 
21
        "use strict"; // jshint ;_;
22
 
23
        /* MODAL MANAGER CLASS DEFINITION
24
        * ====================== */
25
 
26
        var ModalManager = function (element, options) {
27
                this.init(element, options);
28
        };
29
 
30
        ModalManager.prototype = {
31
 
32
                constructor: ModalManager,
33
 
34
                init: function (element, options) {
35
                        this.$element = $(element);
36
                        this.options = $.extend({}, $.fn.modalmanager.defaults, this.$element.data(), typeof options == 'object' && options);
37
                        this.stack = [];
38
                        this.backdropCount = 0;
39
 
40
                        if (this.options.resize) {
41
                                var resizeTimeout,
42
                                        that = this;
43
 
44
                                $(window).on('resize.modal', function(){
45
                                        resizeTimeout && clearTimeout(resizeTimeout);
46
                                        resizeTimeout = setTimeout(function(){
47
                                                for (var i = 0; i < that.stack.length; i++){
48
                                                        that.stack[i].isShown && that.stack[i].layout();
49
                                                }
50
                                        }, 10);
51
                                });
52
                        }
53
                },
54
 
55
                createModal: function (element, options) {
56
                        $(element).modal($.extend({ manager: this }, options));
57
                },
58
 
59
                appendModal: function (modal) {
60
                        this.stack.push(modal);
61
 
62
                        var that = this;
63
 
64
                        modal.$element.on('show.modalmanager', targetIsSelf(function (e) {
65
 
66
                                var showModal = function(){
67
                                        modal.isShown = true;
68
 
69
                                        var transition = $.support.transition && modal.$element.hasClass('fade');
70
 
71
                                        that.$element
72
                                                .toggleClass('modal-open', that.hasOpenModal())
73
                                                .toggleClass('page-overflow', $(window).height() < that.$element.height());
74
 
75
                                        modal.$parent = modal.$element.parent();
76
 
77
                                        modal.$container = that.createContainer(modal);
78
 
79
                                        modal.$element.appendTo(modal.$container);
80
 
81
                                        that.backdrop(modal, function () {
82
                                                modal.$element.show();
83
 
84
                                                if (transition) {      
85
                                                        //modal.$element[0].style.display = 'run-in';       
86
                                                        modal.$element[0].offsetWidth;
87
                                                        //modal.$element.one($.support.transition.end, function () { modal.$element[0].style.display = 'block' });  
88
                                                }
89
 
90
                                                modal.layout();
91
 
92
                                                modal.$element
93
                                                        .addClass('in')
94
                                                        .attr('aria-hidden', false);
95
 
96
                                                var complete = function () {
97
                                                        that.setFocus();
98
                                                        modal.$element.trigger('shown');
99
                                                };
100
 
101
                                                transition ?
102
                                                        modal.$element.one($.support.transition.end, complete) :
103
                                                        complete();
104
                                        });
105
                                };
106
 
107
                                modal.options.replace ?
108
                                        that.replace(showModal) :
109
                                        showModal();
110
                        }));
111
 
112
                        modal.$element.on('hidden.modalmanager', targetIsSelf(function (e) {
113
                                that.backdrop(modal);
114
                                // handle the case when a modal may have been removed from the dom before this callback executes
115
                                if (!modal.$element.parent().length) {
116
                                        that.destroyModal(modal);
117
                                } else if (modal.$backdrop){
118
                                        var transition = $.support.transition && modal.$element.hasClass('fade');
119
 
120
                                        // trigger a relayout due to firebox's buggy transition end event 
121
                                        if (transition) { modal.$element[0].offsetWidth; }
122
                                        $.support.transition && modal.$element.hasClass('fade') ?
123
                                                modal.$backdrop.one($.support.transition.end, function () { modal.destroy(); }) :
124
                                                modal.destroy();
125
                                } else {
126
                                        modal.destroy();
127
                                }
128
 
129
                        }));
130
 
131
                        modal.$element.on('destroyed.modalmanager', targetIsSelf(function (e) {
132
                                that.destroyModal(modal);
133
                        }));
134
                },
135
 
136
                getOpenModals: function () {
137
                        var openModals = [];
138
                        for (var i = 0; i < this.stack.length; i++){
139
                                if (this.stack[i].isShown) openModals.push(this.stack[i]);
140
                        }
141
 
142
                        return openModals;
143
                },
144
 
145
                hasOpenModal: function () {
146
                        return this.getOpenModals().length > 0;
147
                },
148
 
149
                setFocus: function () {
150
                        var topModal;
151
 
152
                        for (var i = 0; i < this.stack.length; i++){
153
                                if (this.stack[i].isShown) topModal = this.stack[i];
154
                        }
155
 
156
                        if (!topModal) return;
157
 
158
                        topModal.focus();
159
                },
160
 
161
                destroyModal: function (modal) {
162
                        modal.$element.off('.modalmanager');
163
                        if (modal.$backdrop) this.removeBackdrop(modal);
164
                        this.stack.splice(this.getIndexOfModal(modal), 1);
165
 
166
                        var hasOpenModal = this.hasOpenModal();
167
 
168
                        this.$element.toggleClass('modal-open', hasOpenModal);
169
 
170
                        if (!hasOpenModal){
171
                                this.$element.removeClass('page-overflow');
172
                        }
173
 
174
                        this.removeContainer(modal);
175
 
176
                        this.setFocus();
177
                },
178
 
179
                getModalAt: function (index) {
180
                        return this.stack[index];
181
                },
182
 
183
                getIndexOfModal: function (modal) {
184
                        for (var i = 0; i < this.stack.length; i++){
185
                                if (modal === this.stack[i]) return i;
186
                        }
187
                },
188
 
189
                replace: function (callback) {
190
                        var topModal;
191
 
192
                        for (var i = 0; i < this.stack.length; i++){
193
                                if (this.stack[i].isShown) topModal = this.stack[i];
194
                        }
195
 
196
                        if (topModal) {
197
                                this.$backdropHandle = topModal.$backdrop;
198
                                topModal.$backdrop = null;
199
 
200
                                callback && topModal.$element.one('hidden',
201
                                        targetIsSelf( $.proxy(callback, this) ));
202
 
203
                                topModal.hide();
204
                        } else if (callback) {
205
                                callback();
206
                        }
207
                },
208
 
209
                removeBackdrop: function (modal) {
210
                        modal.$backdrop.remove();
211
                        modal.$backdrop = null;
212
                },
213
 
214
                createBackdrop: function (animate, tmpl) {
215
                        var $backdrop;
216
 
217
                        if (!this.$backdropHandle) {
218
                                $backdrop = $(tmpl)
219
                                        .addClass(animate)
220
                                        .appendTo(this.$element);
221
                        } else {
222
                                $backdrop = this.$backdropHandle;
223
                                $backdrop.off('.modalmanager');
224
                                this.$backdropHandle = null;
225
                                this.isLoading && this.removeSpinner();
226
                        }
227
 
228
                        return $backdrop;
229
                },
230
 
231
                removeContainer: function (modal) {
232
                        modal.$container.remove();
233
                        modal.$container = null;
234
                },
235
 
236
                createContainer: function (modal) {
237
                        var $container;
238
 
239
                        $container = $('<div class="modal-scrollable">')
240
                                .css('z-index', getzIndex('modal', this.getOpenModals().length))
241
                                .appendTo(this.$element);
242
 
243
                        if (modal && modal.options.backdrop != 'static') {
244
                                $container.on('click.modal', targetIsSelf(function (e) {
245
                                        modal.hide();
246
                                }));
247
                        } else if (modal) {
248
                                $container.on('click.modal', targetIsSelf(function (e) {
249
                                        modal.attention();
250
                                }));
251
                        }
252
 
253
                        return $container;
254
 
255
                },
256
 
257
                backdrop: function (modal, callback) {
258
                        var animate = modal.$element.hasClass('fade') ? 'fade' : '',
259
                                showBackdrop = modal.options.backdrop &&
260
                                        this.backdropCount < this.options.backdropLimit;
261
 
262
                        if (modal.isShown && showBackdrop) {
263
                                var doAnimate = $.support.transition && animate && !this.$backdropHandle;
264
 
265
                                modal.$backdrop = this.createBackdrop(animate, modal.options.backdropTemplate);
266
 
267
                                modal.$backdrop.css('z-index', getzIndex( 'backdrop', this.getOpenModals().length ));
268
 
269
                                if (doAnimate) modal.$backdrop[0].offsetWidth; // force reflow
270
 
271
                                modal.$backdrop.addClass('in');
272
 
273
                                this.backdropCount += 1;
274
 
275
                                doAnimate ?
276
                                        modal.$backdrop.one($.support.transition.end, callback) :
277
                                        callback();
278
 
279
                        } else if (!modal.isShown && modal.$backdrop) {
280
                                modal.$backdrop.removeClass('in');
281
 
282
                                this.backdropCount -= 1;
283
 
284
                                var that = this;
285
 
286
                                $.support.transition && modal.$element.hasClass('fade')?
287
                                        modal.$backdrop.one($.support.transition.end, function () { that.removeBackdrop(modal) }) :
288
                                        that.removeBackdrop(modal);
289
 
290
                        } else if (callback) {
291
                                callback();
292
                        }
293
                },
294
 
295
                removeSpinner: function(){
296
                        this.$spinner && this.$spinner.remove();
297
                        this.$spinner = null;
298
                        this.isLoading = false;
299
                },
300
 
301
                removeLoading: function () {
302
                        this.$backdropHandle && this.$backdropHandle.remove();
303
                        this.$backdropHandle = null;
304
                        this.removeSpinner();
305
                },
306
 
307
                loading: function (callback) {
308
                        callback = callback || function () { };
309
 
310
                        this.$element
311
                                .toggleClass('modal-open', !this.isLoading || this.hasOpenModal())
312
                                .toggleClass('page-overflow', $(window).height() < this.$element.height());
313
 
314
                        if (!this.isLoading) {
315
 
316
                                this.$backdropHandle = this.createBackdrop('fade', this.options.backdropTemplate);
317
 
318
                                this.$backdropHandle[0].offsetWidth; // force reflow
319
 
320
                                var openModals = this.getOpenModals();
321
 
322
                                this.$backdropHandle
323
                                        .css('z-index', getzIndex('backdrop', openModals.length + 1))
324
                                        .addClass('in');
325
 
326
                                var $spinner = $(this.options.spinner)
327
                                        .css('z-index', getzIndex('modal', openModals.length + 1))
328
                                        .appendTo(this.$element)
329
                                        .addClass('in');
330
 
331
                                this.$spinner = $(this.createContainer())
332
                                        .append($spinner)
333
                                        .on('click.modalmanager', $.proxy(this.loading, this));
334
 
335
                                this.isLoading = true;
336
 
337
                                $.support.transition ?
338
                                        this.$backdropHandle.one($.support.transition.end, callback) :
339
                                        callback();
340
 
341
                        } else if (this.isLoading && this.$backdropHandle) {
342
                                this.$backdropHandle.removeClass('in');
343
 
344
                                var that = this;
345
                                $.support.transition ?
346
                                        this.$backdropHandle.one($.support.transition.end, function () { that.removeLoading() }) :
347
                                        that.removeLoading();
348
 
349
                        } else if (callback) {
350
                                callback(this.isLoading);
351
                        }
352
                }
353
        };
354
 
355
        /* PRIVATE METHODS
356
        * ======================= */
357
 
358
        // computes and caches the zindexes
359
        var getzIndex = (function () {
360
                var zIndexFactor,
361
                        baseIndex = {};
362
 
363
                return function (type, pos) {
364
 
365
                        if (typeof zIndexFactor === 'undefined'){
366
                                var $baseModal = $('<div class="modal hide" />').appendTo('body'),
367
                                        $baseBackdrop = $('<div class="modal-backdrop hide" />').appendTo('body');
368
 
369
                                baseIndex['modal'] = +$baseModal.css('z-index');
370
                                baseIndex['backdrop'] = +$baseBackdrop.css('z-index');
371
                                zIndexFactor = baseIndex['modal'] - baseIndex['backdrop'];
372
 
373
                                $baseModal.remove();
374
                                $baseBackdrop.remove();
375
                                $baseBackdrop = $baseModal = null;
376
                        }
377
 
378
                        return baseIndex[type] + (zIndexFactor * pos);
379
 
380
                }
381
        }());
382
 
383
        // make sure the event target is the modal itself in order to prevent
384
        // other components such as tabsfrom triggering the modal manager.
385
        // if Boostsrap namespaced events, this would not be needed.
386
        function targetIsSelf(callback){
387
                return function (e) {
388
                        if (e && this === e.target){
389
                                return callback.apply(this, arguments);
390
                        }
391
                }
392
        }
393
 
394
 
395
        /* MODAL MANAGER PLUGIN DEFINITION
396
        * ======================= */
397
 
398
        $.fn.modalmanager = function (option, args) {
399
                return this.each(function () {
400
                        var $this = $(this),
401
                                data = $this.data('modalmanager');
402
 
403
                        if (!data) $this.data('modalmanager', (data = new ModalManager(this, option)));
404
                        if (typeof option === 'string') data[option].apply(data, [].concat(args))
405
                })
406
        };
407
 
408
        $.fn.modalmanager.defaults = {
409
                backdropLimit: 999,
410
                resize: true,
411
                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>',
412
                backdropTemplate: '<div class="modal-backdrop" />'
413
        };
414
 
415
        $.fn.modalmanager.Constructor = ModalManager
416
 
417
        // ModalManager handles the modal-open class so we need 
418
        // to remove conflicting bootstrap 3 event handlers
419
        $(function () {
420
                $(document).off('show.bs.modal').off('hidden.bs.modal');
421
        });
422
 
423
}(jQuery);