1 /* ===========================================================
2 * bootstrap-modal.js v2.2.5
3 * ===========================================================
4 * Copyright 2012 Jordan Schroter
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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 * ========================================================== */
22 "use strict"; // jshint ;_;
24 /* MODAL CLASS DEFINITION
25 * ====================== */
27 var Modal = function (element, options) {
28 this.init(element, options);
35 init: function (element, options) {
38 this.options = options;
40 this.$element = $(element)
41 .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this));
43 this.options.remote && this.$element.find('.modal-body').load(this.options.remote, function () {
44 var e = $.Event('loaded');
45 that.$element.trigger(e);
48 var manager = typeof this.options.manager === 'function' ?
49 this.options.manager.call(this) : this.options.manager;
51 manager = manager.appendModal ?
52 manager : $(manager).modalmanager().data('modalmanager');
54 manager.appendModal(this);
58 return this[!this.isShown ? 'show' : 'hide']();
62 var e = $.Event('show');
64 if (this.isShown) return;
66 this.$element.trigger(e);
68 if (e.isDefaultPrevented()) return;
74 this.options.loading && this.loading();
78 e && e.preventDefault();
82 this.$element.trigger(e);
84 if (!this.isShown || e.isDefaultPrevented()) return;
92 this.isLoading && this.loading();
94 $(document).off('focusin.modal');
98 .removeClass('animated')
99 .removeClass(this.options.attentionAnimation)
100 .removeClass('modal-overflow')
101 .attr('aria-hidden', true);
103 $.support.transition && this.$element.hasClass('fade') ?
104 this.hideWithTransition() :
108 layout: function () {
109 var prop = this.options.height ? 'height' : 'max-height',
110 value = this.options.height || this.options.maxHeight;
112 if (this.options.width){
113 this.$element.css('width', this.options.width);
116 this.$element.css('margin-left', function () {
117 if (/%/ig.test(that.options.width)){
118 return -(parseInt(that.options.width) / 2) + '%';
120 return -($(this).width() / 2) + 'px';
124 this.$element.css('width', '');
125 this.$element.css('margin-left', '');
128 this.$element.find('.modal-body')
133 this.$element.find('.modal-body')
134 .css('overflow', 'auto')
138 var modalOverflow = $(window).height() - 10 < this.$element.height();
140 if (modalOverflow || this.options.modalOverflow) {
142 .css('margin-top', 0)
143 .addClass('modal-overflow');
146 .css('margin-top', 0 - this.$element.height() / 2)
147 .removeClass('modal-overflow');
154 if (this.isShown && this.options.consumeTab) {
155 this.$element.on('keydown.tabindex.modal', '[data-tabindex]', function (e) {
156 if (e.keyCode && e.keyCode == 9){
158 tabindex = Number($(this).data('tabindex'));
160 that.$element.find('[data-tabindex]:enabled:visible:not([readonly])').each(function (ev) {
161 elements.push(Number($(this).data('tabindex')));
163 elements.sort(function(a,b){return a-b});
165 var arrayPos = $.inArray(tabindex, elements);
167 arrayPos < elements.length-1 ?
168 that.$element.find('[data-tabindex='+elements[arrayPos+1]+']').focus() :
169 that.$element.find('[data-tabindex='+elements[0]+']').focus();
172 that.$element.find('[data-tabindex='+elements[elements.length-1]+']').focus() :
173 that.$element.find('[data-tabindex='+elements[arrayPos-1]+']').focus();
179 } else if (!this.isShown) {
180 this.$element.off('keydown.tabindex.modal');
184 escape: function () {
186 if (this.isShown && this.options.keyboard) {
187 if (!this.$element.attr('tabindex')) this.$element.attr('tabindex', -1);
189 this.$element.on('keyup.dismiss.modal', function (e) {
190 e.which == 27 && that.hide();
192 } else if (!this.isShown) {
193 this.$element.off('keyup.dismiss.modal')
197 hideWithTransition: function () {
199 , timeout = setTimeout(function () {
200 that.$element.off($.support.transition.end);
204 this.$element.one($.support.transition.end, function () {
205 clearTimeout(timeout);
210 hideModal: function () {
211 var prop = this.options.height ? 'height' : 'max-height';
212 var value = this.options.height || this.options.maxHeight;
215 this.$element.find('.modal-body')
225 removeLoading: function () {
226 this.$loading.remove();
227 this.$loading = null;
228 this.isLoading = false;
231 loading: function (callback) {
232 callback = callback || function () {};
234 var animate = this.$element.hasClass('fade') ? 'fade' : '';
236 if (!this.isLoading) {
237 var doAnimate = $.support.transition && animate;
239 this.$loading = $('<div class="loading-mask ' + animate + '">')
240 .append(this.options.spinner)
241 .appendTo(this.$element);
243 if (doAnimate) this.$loading[0].offsetWidth; // force reflow
245 this.$loading.addClass('in');
247 this.isLoading = true;
250 this.$loading.one($.support.transition.end, callback) :
253 } else if (this.isLoading && this.$loading) {
254 this.$loading.removeClass('in');
257 $.support.transition && this.$element.hasClass('fade')?
258 this.$loading.one($.support.transition.end, function () { that.removeLoading() }) :
259 that.removeLoading();
261 } else if (callback) {
262 callback(this.isLoading);
267 var $focusElem = this.$element.find(this.options.focusOn);
269 $focusElem = $focusElem.length ? $focusElem : this.$element;
274 attention: function (){
275 // NOTE: transitionEnd with keyframes causes odd behaviour
277 if (this.options.attentionAnimation){
279 .removeClass('animated')
280 .removeClass(this.options.attentionAnimation);
284 setTimeout(function () {
286 .addClass('animated')
287 .addClass(that.options.attentionAnimation);
296 destroy: function () {
297 var e = $.Event('destroy');
299 this.$element.trigger(e);
301 if (e.isDefaultPrevented()) return;
307 .attr('aria-hidden', true);
309 if (this.$parent !== this.$element.parent()) {
310 this.$element.appendTo(this.$parent);
311 } else if (!this.$parent.length) {
312 // modal is not part of the DOM so remove it.
313 this.$element.remove();
314 this.$element = null;
317 this.$element.trigger('destroyed');
322 /* MODAL PLUGIN DEFINITION
323 * ======================= */
325 $.fn.modal = function (option, args) {
326 return this.each(function () {
328 data = $this.data('modal'),
329 options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option);
331 if (!data) $this.data('modal', (data = new Modal(this, options)));
332 if (typeof option == 'string') data[option].apply(data, [].concat(args));
333 else if (options.show) data.show()
337 $.fn.modal.defaults = {
345 modalOverflow: false,
350 attentionAnimation: 'shake',
352 spinner: '<div class="loading-spinner" style="width: 200px; margin-left: -100px;"><div class="progress progress-striped active"><div class="bar" style="width: 100%;"></div></div></div>',
353 backdropTemplate: '<div class="modal-backdrop" />'
356 $.fn.modal.Constructor = Modal;
363 $(document).off('click.modal').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
365 href = $this.attr('href'),
366 $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))), //strip for ie7
367 option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data());
372 .one('hide', function () {