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