src/pyams_skin/resources/js/ext/bootstrap-modalmanager.js
changeset 0 bb4aabe07487
child 108 e49d9bb000ae
equal deleted inserted replaced
-1:000000000000 0:bb4aabe07487
       
     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)
       
   122 						modal.$element[0].offsetWidth;
       
   123 					$.support.transition && modal.$element.hasClass('fade') ?
       
   124 						modal.$backdrop.one($.support.transition.end, function () {
       
   125 							modal.destroy();
       
   126 						}) :
       
   127 						modal.destroy();
       
   128 				} else {
       
   129 					modal.destroy();
       
   130 				}
       
   131 
       
   132 			}));
       
   133 
       
   134 			modal.$element.on('destroyed.modalmanager', targetIsSelf(function (e) {
       
   135 				that.destroyModal(modal);
       
   136 			}));
       
   137 		},
       
   138 
       
   139 		getOpenModals: function () {
       
   140 			var openModals = [];
       
   141 			for (var i = 0; i < this.stack.length; i++) {
       
   142 				if (this.stack[i].isShown) openModals.push(this.stack[i]);
       
   143 			}
       
   144 
       
   145 			return openModals;
       
   146 		},
       
   147 
       
   148 		hasOpenModal: function () {
       
   149 			return this.getOpenModals().length > 0;
       
   150 		},
       
   151 
       
   152 		setFocus: function () {
       
   153 			var topModal;
       
   154 
       
   155 			for (var i = 0; i < this.stack.length; i++) {
       
   156 				if (this.stack[i].isShown) topModal = this.stack[i];
       
   157 			}
       
   158 
       
   159 			if (!topModal) return;
       
   160 
       
   161 			topModal.focus();
       
   162 		},
       
   163 
       
   164 		destroyModal: function (modal) {
       
   165 			modal.$element.off('.modalmanager');
       
   166 			if (modal.$backdrop) this.removeBackdrop(modal);
       
   167 			this.stack.splice(this.getIndexOfModal(modal), 1);
       
   168 
       
   169 			var hasOpenModal = this.hasOpenModal();
       
   170 
       
   171 			this.$element.toggleClass('modal-open', hasOpenModal);
       
   172 
       
   173 			if (!hasOpenModal) {
       
   174 				this.$element.removeClass('page-overflow');
       
   175 			}
       
   176 
       
   177 			this.removeContainer(modal);
       
   178 
       
   179 			this.setFocus();
       
   180 		},
       
   181 
       
   182 		getModalAt: function (index) {
       
   183 			return this.stack[index];
       
   184 		},
       
   185 
       
   186 		getIndexOfModal: function (modal) {
       
   187 			for (var i = 0; i < this.stack.length; i++) {
       
   188 				if (modal === this.stack[i]) return i;
       
   189 			}
       
   190 		},
       
   191 
       
   192 		replace: function (callback) {
       
   193 			var topModal;
       
   194 
       
   195 			for (var i = 0; i < this.stack.length; i++) {
       
   196 				if (this.stack[i].isShown) topModal = this.stack[i];
       
   197 			}
       
   198 
       
   199 			if (topModal) {
       
   200 				this.$backdropHandle = topModal.$backdrop;
       
   201 				topModal.$backdrop = null;
       
   202 
       
   203 				callback && topModal.$element.one('hidden',
       
   204 												  targetIsSelf($.proxy(callback, this)));
       
   205 
       
   206 				topModal.hide();
       
   207 			} else if (callback) {
       
   208 				callback();
       
   209 			}
       
   210 		},
       
   211 
       
   212 		removeBackdrop: function (modal) {
       
   213 			modal.$backdrop.remove();
       
   214 			modal.$backdrop = null;
       
   215 		},
       
   216 
       
   217 		createBackdrop: function (animate, tmpl) {
       
   218 			var $backdrop;
       
   219 
       
   220 			if (!this.$backdropHandle) {
       
   221 				$backdrop = $(tmpl)
       
   222 					.addClass(animate)
       
   223 					.appendTo(this.$element);
       
   224 			} else {
       
   225 				$backdrop = this.$backdropHandle;
       
   226 				$backdrop.off('.modalmanager');
       
   227 				this.$backdropHandle = null;
       
   228 				this.isLoading && this.removeSpinner();
       
   229 			}
       
   230 
       
   231 			return $backdrop;
       
   232 		},
       
   233 
       
   234 		removeContainer: function (modal) {
       
   235 			modal.$container.remove();
       
   236 			modal.$container = null;
       
   237 		},
       
   238 
       
   239 		createContainer: function (modal) {
       
   240 			var $container;
       
   241 
       
   242 			$container = $('<div class="modal-scrollable">')
       
   243 				.css('z-index', getzIndex('modal', this.getOpenModals().length))
       
   244 				.appendTo(this.$element);
       
   245 
       
   246 			if (modal && modal.options.backdrop != 'static') {
       
   247 				$container.on('click.modal', targetIsSelf(function (e) {
       
   248 					modal.hide();
       
   249 				}));
       
   250 			} else if (modal) {
       
   251 				$container.on('click.modal', targetIsSelf(function (e) {
       
   252 					modal.attention();
       
   253 				}));
       
   254 			}
       
   255 
       
   256 			return $container;
       
   257 
       
   258 		},
       
   259 
       
   260 		backdrop: function (modal, callback) {
       
   261 			var animate = modal.$element.hasClass('fade') ? 'fade' : '',
       
   262 				showBackdrop = modal.options.backdrop &&
       
   263 					this.backdropCount < this.options.backdropLimit;
       
   264 
       
   265 			if (modal.isShown && showBackdrop) {
       
   266 				var doAnimate = $.support.transition && animate && !this.$backdropHandle;
       
   267 
       
   268 				modal.$backdrop = this.createBackdrop(animate, modal.options.backdropTemplate);
       
   269 
       
   270 				modal.$backdrop.css('z-index', getzIndex('backdrop', this.getOpenModals().length));
       
   271 
       
   272 				if (doAnimate) modal.$backdrop[0].offsetWidth; // force reflow
       
   273 
       
   274 				modal.$backdrop.addClass('in');
       
   275 
       
   276 				this.backdropCount += 1;
       
   277 
       
   278 				doAnimate ?
       
   279 					modal.$backdrop.one($.support.transition.end, callback) :
       
   280 					callback();
       
   281 
       
   282 			} else if (!modal.isShown && modal.$backdrop) {
       
   283 				modal.$backdrop.removeClass('in');
       
   284 
       
   285 				this.backdropCount -= 1;
       
   286 
       
   287 				var that = this;
       
   288 
       
   289 				$.support.transition && modal.$element.hasClass('fade') ?
       
   290 					modal.$backdrop.one($.support.transition.end, function () {
       
   291 						that.removeBackdrop(modal)
       
   292 					}) :
       
   293 					that.removeBackdrop(modal);
       
   294 
       
   295 			} else if (callback) {
       
   296 				callback();
       
   297 			}
       
   298 		},
       
   299 
       
   300 		removeSpinner: function () {
       
   301 			this.$spinner && this.$spinner.remove();
       
   302 			this.$spinner = null;
       
   303 			this.isLoading = false;
       
   304 		},
       
   305 
       
   306 		removeLoading: function () {
       
   307 			this.$backdropHandle && this.$backdropHandle.remove();
       
   308 			this.$backdropHandle = null;
       
   309 			this.removeSpinner();
       
   310 		},
       
   311 
       
   312 		loading: function (callback) {
       
   313 			callback = callback || function () {};
       
   314 
       
   315 			this.$element
       
   316 				.toggleClass('modal-open', !this.isLoading || this.hasOpenModal())
       
   317 				.toggleClass('page-overflow', $(window).height() < this.$element.height());
       
   318 
       
   319 			if (!this.isLoading) {
       
   320 
       
   321 				this.$backdropHandle = this.createBackdrop('fade', this.options.backdropTemplate);
       
   322 
       
   323 				this.$backdropHandle[0].offsetWidth; // force reflow
       
   324 
       
   325 				var openModals = this.getOpenModals();
       
   326 
       
   327 				this.$backdropHandle
       
   328 					.css('z-index', getzIndex('backdrop', openModals.length + 1))
       
   329 					.addClass('in');
       
   330 
       
   331 				var $spinner = $(this.options.spinner)
       
   332 					.css('z-index', getzIndex('modal', openModals.length + 1))
       
   333 					.appendTo(this.$element)
       
   334 					.addClass('in');
       
   335 
       
   336 				this.$spinner = $(this.createContainer())
       
   337 					.append($spinner)
       
   338 					.on('click.modalmanager', $.proxy(this.loading, this));
       
   339 
       
   340 				this.isLoading = true;
       
   341 
       
   342 				$.support.transition ?
       
   343 					this.$backdropHandle.one($.support.transition.end, callback) :
       
   344 					callback();
       
   345 
       
   346 			} else if (this.isLoading && this.$backdropHandle) {
       
   347 				this.$backdropHandle.removeClass('in');
       
   348 
       
   349 				var that = this;
       
   350 				$.support.transition ?
       
   351 					this.$backdropHandle.one($.support.transition.end, function () {
       
   352 						that.removeLoading()
       
   353 					}) :
       
   354 					that.removeLoading();
       
   355 
       
   356 			} else if (callback) {
       
   357 				callback(this.isLoading);
       
   358 			}
       
   359 		}
       
   360 	};
       
   361 
       
   362 	/* PRIVATE METHODS
       
   363 	 * ======================= */
       
   364 
       
   365 	// computes and caches the zindexes
       
   366 	var getzIndex = (function () {
       
   367 		var zIndexFactor,
       
   368 			baseIndex = {};
       
   369 
       
   370 		return function (type, pos) {
       
   371 
       
   372 			if (typeof zIndexFactor === 'undefined') {
       
   373 				var $baseModal = $('<div class="modal hide" />').appendTo('body'),
       
   374 					$baseBackdrop = $('<div class="modal-backdrop hide" />').appendTo('body');
       
   375 
       
   376 				baseIndex['modal'] = +$baseModal.css('z-index');
       
   377 				baseIndex['backdrop'] = +$baseBackdrop.css('z-index');
       
   378 				zIndexFactor = baseIndex['modal'] - baseIndex['backdrop'];
       
   379 
       
   380 				$baseModal.remove();
       
   381 				$baseBackdrop.remove();
       
   382 				$baseBackdrop = $baseModal = null;
       
   383 			}
       
   384 
       
   385 			return baseIndex[type] + (zIndexFactor * pos);
       
   386 
       
   387 		}
       
   388 	}());
       
   389 
       
   390 	// make sure the event target is the modal itself in order to prevent
       
   391 	// other components such as tabsfrom triggering the modal manager.
       
   392 	// if Boostsrap namespaced events, this would not be needed.
       
   393 	function targetIsSelf(callback) {
       
   394 		return function (e) {
       
   395 			if (e && this === e.target){
       
   396 				return callback.apply(this, arguments);
       
   397 			}
       
   398 		}
       
   399 	}
       
   400 
       
   401 
       
   402 	/* MODAL MANAGER PLUGIN DEFINITION
       
   403 	 * ======================= */
       
   404 
       
   405 	$.fn.modalmanager = function (option, args) {
       
   406 		return this.each(function () {
       
   407 			var $this = $(this),
       
   408 				data = $this.data('modalmanager');
       
   409 
       
   410 			if (!data) $this.data('modalmanager', (data = new ModalManager(this, option)));
       
   411 			if (typeof option === 'string') data[option].apply(data, [].concat(args))
       
   412 		})
       
   413 	};
       
   414 
       
   415 	$.fn.modalmanager.defaults = {
       
   416 		backdropLimit: 999,
       
   417 		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>',
       
   419 		backdropTemplate: '<div class="modal-backdrop" />'
       
   420 	};
       
   421 
       
   422 	$.fn.modalmanager.Constructor = ModalManager
       
   423 
       
   424 	// ModalManager handles the modal-open class so we need 
       
   425 	// to remove conflicting bootstrap 3 event handlers
       
   426 	$(function () {
       
   427 		$(document).off('show.bs.modal').off('hidden.bs.modal');
       
   428 	});
       
   429 
       
   430 }(jQuery);