1 /* ===========================================================
2 * bootstrap-modal.js v2.1
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) {
36 this.options = options;
38 this.$element = $(element)
39 .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this));
41 this.options.remote && this.$element.find('.modal-body').load(this.options.remote);
43 var manager = typeof this.options.manager === 'function' ?
44 this.options.manager.call(this) : this.options.manager;
46 manager = manager.appendModal ?
47 manager : $(manager).modalmanager().data('modalmanager');
49 manager.appendModal(this);
53 return this[!this.isShown ? 'show' : 'hide']();
57 var e = $.Event('show');
59 if (this.isShown) return;
61 this.$element.trigger(e);
63 if (e.isDefaultPrevented()) return;
69 this.options.loading && this.loading();
73 e && e.preventDefault();
77 this.$element.trigger(e);
79 if (!this.isShown || e.isDefaultPrevented()) return (this.isShown = false);
87 this.isLoading && this.loading();
89 $(document).off('focusin.modal');
93 .removeClass('animated')
94 .removeClass(this.options.attentionAnimation)
95 .removeClass('modal-overflow')
96 .attr('aria-hidden', true);
98 $.support.transition && this.$element.hasClass('fade') ?
99 this.hideWithTransition() :
103 layout: function () {
104 var prop = this.options.height ? 'height' : 'max-height',
105 value = this.options.height || this.options.maxHeight;
107 if (this.options.width){
108 this.$element.css('width', this.options.width);
111 this.$element.css('margin-left', function () {
112 if (/%/ig.test(that.options.width)){
113 return -(parseInt(that.options.width) / 2) + '%';
115 return -($(this).width() / 2) + 'px';
119 this.$element.css('width', '');
120 this.$element.css('margin-left', '');
123 this.$element.find('.modal-body')
127 var modalOverflow = $(window).height() - 10 < this.$element.height();
130 this.$element.find('.modal-body')
131 .css('overflow', 'auto')
135 if (modalOverflow || this.options.modalOverflow) {
137 .css('margin-top', 0)
138 .addClass('modal-overflow');
141 .css('margin-top', 0 - this.$element.height() / 2)
142 .removeClass('modal-overflow');
149 if (this.isShown && this.options.consumeTab) {
150 this.$element.on('keydown.tabindex.modal', '[data-tabindex]', function (e) {
151 if (e.keyCode && e.keyCode == 9){
155 that.$element.find('[data-tabindex]:enabled:not([readonly])').each(function (e) {
157 $next = $next.data('tabindex') < $(this).data('tabindex') ?
161 $next = $next.data('tabindex') > $(this).data('tabindex') ?
167 $next[0] !== $(this)[0] ?
168 $next.focus() : $rollover.focus();
173 } else if (!this.isShown) {
174 this.$element.off('keydown.tabindex.modal');
178 escape: function () {
180 if (this.isShown && this.options.keyboard) {
181 if (!this.$element.attr('tabindex')) this.$element.attr('tabindex', -1);
183 this.$element.on('keyup.dismiss.modal', function (e) {
184 e.which == 27 && that.hide();
186 } else if (!this.isShown) {
187 this.$element.off('keyup.dismiss.modal')
191 hideWithTransition: function () {
193 , timeout = setTimeout(function () {
194 that.$element.off($.support.transition.end);
198 this.$element.one($.support.transition.end, function () {
199 clearTimeout(timeout);
204 hideModal: function () {
209 var prop = this.options.height ? 'height' : 'max-height';
210 var value = this.options.height || this.options.maxHeight;
213 this.$element.find('.modal-body')
220 removeLoading: function () {
221 this.$loading.remove();
222 this.$loading = null;
223 this.isLoading = false;
226 loading: function (callback) {
227 callback = callback || function () {};
229 var animate = this.$element.hasClass('fade') ? 'fade' : '';
231 if (!this.isLoading) {
232 var doAnimate = $.support.transition && animate;
234 this.$loading = $('<div class="loading-mask ' + animate + '">')
235 .append(this.options.spinner)
236 .appendTo(this.$element);
238 if (doAnimate) this.$loading[0].offsetWidth; // force reflow
240 this.$loading.addClass('in');
242 this.isLoading = true;
245 this.$loading.one($.support.transition.end, callback) :
248 } else if (this.isLoading && this.$loading) {
249 this.$loading.removeClass('in');
252 $.support.transition && this.$element.hasClass('fade')?
253 this.$loading.one($.support.transition.end, function () { that.removeLoading() }) :
254 that.removeLoading();
256 } else if (callback) {
257 callback(this.isLoading);
262 var $focusElem = this.$element.find(this.options.focusOn);
264 $focusElem = $focusElem.length ? $focusElem : this.$element;
269 attention: function (){
270 // NOTE: transitionEnd with keyframes causes odd behaviour
272 if (this.options.attentionAnimation){
274 .removeClass('animated')
275 .removeClass(this.options.attentionAnimation);
279 setTimeout(function () {
281 .addClass('animated')
282 .addClass(that.options.attentionAnimation);
291 destroy: function () {
292 var e = $.Event('destroy');
293 this.$element.trigger(e);
294 if (e.isDefaultPrevented()) return;
299 teardown: function () {
300 if (!this.$parent.length){
301 this.$element.remove();
302 this.$element = null;
306 if (this.$parent !== this.$element.parent()){
307 this.$element.appendTo(this.$parent);
310 this.$element.off('.modal');
311 this.$element.removeData('modal');
314 .attr('aria-hidden', true);
319 /* MODAL PLUGIN DEFINITION
320 * ======================= */
322 $.fn.modal = function (option, args) {
323 return this.each(function () {
325 data = $this.data('modal'),
326 options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option);
328 if (!data) $this.data('modal', (data = new Modal(this, options)));
329 if (typeof option == 'string') data[option].apply(data, [].concat(args));
330 else if (options.show) data.show()
334 $.fn.modal.defaults = {
342 modalOverflow: false,
347 attentionAnimation: 'shake',
349 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>'
352 $.fn.modal.Constructor = Modal;
359 $(document).off('click.modal').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
361 href = $this.attr('href'),
362 $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))), //strip for ie7
363 option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data());
368 .one('hide', function () {