Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

/* ===========================================================
 * bootstrap-modalmanager.js v2.2.5
 * ===========================================================
 * Copyright 2012 Jordan Schroter.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function ($) {

        "use strict"; // jshint ;_;

        /* MODAL MANAGER CLASS DEFINITION
        * ====================== */

        var ModalManager = function (element, options) {
                this.init(element, options);
        };

        ModalManager.prototype = {

                constructor: ModalManager,

                init: function (element, options) {
                        this.$element = $(element);
                        this.options = $.extend({}, $.fn.modalmanager.defaults, this.$element.data(), typeof options == 'object' && options);
                        this.stack = [];
                        this.backdropCount = 0;

                        if (this.options.resize) {
                                var resizeTimeout,
                                        that = this;

                                $(window).on('resize.modal', function(){
                                        resizeTimeout && clearTimeout(resizeTimeout);
                                        resizeTimeout = setTimeout(function(){
                                                for (var i = 0; i < that.stack.length; i++){
                                                        that.stack[i].isShown && that.stack[i].layout();
                                                }
                                        }, 10);
                                });
                        }
                },

                createModal: function (element, options) {
                        $(element).modal($.extend({ manager: this }, options));
                },

                appendModal: function (modal) {
                        this.stack.push(modal);

                        var that = this;

                        modal.$element.on('show.modalmanager', targetIsSelf(function (e) {

                                var showModal = function(){
                                        modal.isShown = true;

                                        var transition = $.support.transition && modal.$element.hasClass('fade');

                                        that.$element
                                                .toggleClass('modal-open', that.hasOpenModal())
                                                .toggleClass('page-overflow', $(window).height() < that.$element.height());

                                        modal.$parent = modal.$element.parent();

                                        modal.$container = that.createContainer(modal);

                                        modal.$element.appendTo(modal.$container);

                                        that.backdrop(modal, function () {
                                                modal.$element.show();

                                                if (transition) {       
                                                        //modal.$element[0].style.display = 'run-in';       
                                                        modal.$element[0].offsetWidth;
                                                        //modal.$element.one($.support.transition.end, function () { modal.$element[0].style.display = 'block' });  
                                                }
                                                
                                                modal.layout();

                                                modal.$element
                                                        .addClass('in')
                                                        .attr('aria-hidden', false);

                                                var complete = function () {
                                                        that.setFocus();
                                                        modal.$element.trigger('shown');
                                                };

                                                transition ?
                                                        modal.$element.one($.support.transition.end, complete) :
                                                        complete();
                                        });
                                };

                                modal.options.replace ?
                                        that.replace(showModal) :
                                        showModal();
                        }));

                        modal.$element.on('hidden.modalmanager', targetIsSelf(function (e) {
                                that.backdrop(modal);
                                // handle the case when a modal may have been removed from the dom before this callback executes
                                if (!modal.$element.parent().length) {
                                        that.destroyModal(modal);
                                } else if (modal.$backdrop){
                                        var transition = $.support.transition && modal.$element.hasClass('fade');

                                        // trigger a relayout due to firebox's buggy transition end event 
                                        if (transition) { modal.$element[0].offsetWidth; }
                                        $.support.transition && modal.$element.hasClass('fade') ?
                                                modal.$backdrop.one($.support.transition.end, function () { modal.destroy(); }) :
                                                modal.destroy();
                                } else {
                                        modal.destroy();
                                }

                        }));

                        modal.$element.on('destroyed.modalmanager', targetIsSelf(function (e) {
                                that.destroyModal(modal);
                        }));
                },

                getOpenModals: function () {
                        var openModals = [];
                        for (var i = 0; i < this.stack.length; i++){
                                if (this.stack[i].isShown) openModals.push(this.stack[i]);
                        }

                        return openModals;
                },

                hasOpenModal: function () {
                        return this.getOpenModals().length > 0;
                },

                setFocus: function () {
                        var topModal;

                        for (var i = 0; i < this.stack.length; i++){
                                if (this.stack[i].isShown) topModal = this.stack[i];
                        }

                        if (!topModal) return;

                        topModal.focus();
                },

                destroyModal: function (modal) {
                        modal.$element.off('.modalmanager');
                        if (modal.$backdrop) this.removeBackdrop(modal);
                        this.stack.splice(this.getIndexOfModal(modal), 1);

                        var hasOpenModal = this.hasOpenModal();

                        this.$element.toggleClass('modal-open', hasOpenModal);

                        if (!hasOpenModal){
                                this.$element.removeClass('page-overflow');
                        }

                        this.removeContainer(modal);

                        this.setFocus();
                },

                getModalAt: function (index) {
                        return this.stack[index];
                },

                getIndexOfModal: function (modal) {
                        for (var i = 0; i < this.stack.length; i++){
                                if (modal === this.stack[i]) return i;
                        }
                },

                replace: function (callback) {
                        var topModal;

                        for (var i = 0; i < this.stack.length; i++){
                                if (this.stack[i].isShown) topModal = this.stack[i];
                        }

                        if (topModal) {
                                this.$backdropHandle = topModal.$backdrop;
                                topModal.$backdrop = null;

                                callback && topModal.$element.one('hidden',
                                        targetIsSelf( $.proxy(callback, this) ));

                                topModal.hide();
                        } else if (callback) {
                                callback();
                        }
                },

                removeBackdrop: function (modal) {
                        modal.$backdrop.remove();
                        modal.$backdrop = null;
                },

                createBackdrop: function (animate, tmpl) {
                        var $backdrop;

                        if (!this.$backdropHandle) {
                                $backdrop = $(tmpl)
                                        .addClass(animate)
                                        .appendTo(this.$element);
                        } else {
                                $backdrop = this.$backdropHandle;
                                $backdrop.off('.modalmanager');
                                this.$backdropHandle = null;
                                this.isLoading && this.removeSpinner();
                        }

                        return $backdrop;
                },

                removeContainer: function (modal) {
                        modal.$container.remove();
                        modal.$container = null;
                },

                createContainer: function (modal) {
                        var $container;

                        $container = $('<div class="modal-scrollable">')
                                .css('z-index', getzIndex('modal', this.getOpenModals().length))
                                .appendTo(this.$element);

                        if (modal && modal.options.backdrop != 'static') {
                                $container.on('click.modal', targetIsSelf(function (e) {
                                        modal.hide();
                                }));
                        } else if (modal) {
                                $container.on('click.modal', targetIsSelf(function (e) {
                                        modal.attention();
                                }));
                        }

                        return $container;

                },

                backdrop: function (modal, callback) {
                        var animate = modal.$element.hasClass('fade') ? 'fade' : '',
                                showBackdrop = modal.options.backdrop &&
                                        this.backdropCount < this.options.backdropLimit;

                        if (modal.isShown && showBackdrop) {
                                var doAnimate = $.support.transition && animate && !this.$backdropHandle;

                                modal.$backdrop = this.createBackdrop(animate, modal.options.backdropTemplate);

                                modal.$backdrop.css('z-index', getzIndex( 'backdrop', this.getOpenModals().length ));

                                if (doAnimate) modal.$backdrop[0].offsetWidth; // force reflow

                                modal.$backdrop.addClass('in');

                                this.backdropCount += 1;

                                doAnimate ?
                                        modal.$backdrop.one($.support.transition.end, callback) :
                                        callback();

                        } else if (!modal.isShown && modal.$backdrop) {
                                modal.$backdrop.removeClass('in');

                                this.backdropCount -= 1;

                                var that = this;

                                $.support.transition && modal.$element.hasClass('fade')?
                                        modal.$backdrop.one($.support.transition.end, function () { that.removeBackdrop(modal) }) :
                                        that.removeBackdrop(modal);

                        } else if (callback) {
                                callback();
                        }
                },

                removeSpinner: function(){
                        this.$spinner && this.$spinner.remove();
                        this.$spinner = null;
                        this.isLoading = false;
                },

                removeLoading: function () {
                        this.$backdropHandle && this.$backdropHandle.remove();
                        this.$backdropHandle = null;
                        this.removeSpinner();
                },

                loading: function (callback) {
                        callback = callback || function () { };

                        this.$element
                                .toggleClass('modal-open', !this.isLoading || this.hasOpenModal())
                                .toggleClass('page-overflow', $(window).height() < this.$element.height());

                        if (!this.isLoading) {

                                this.$backdropHandle = this.createBackdrop('fade', this.options.backdropTemplate);

                                this.$backdropHandle[0].offsetWidth; // force reflow

                                var openModals = this.getOpenModals();

                                this.$backdropHandle
                                        .css('z-index', getzIndex('backdrop', openModals.length + 1))
                                        .addClass('in');

                                var $spinner = $(this.options.spinner)
                                        .css('z-index', getzIndex('modal', openModals.length + 1))
                                        .appendTo(this.$element)
                                        .addClass('in');

                                this.$spinner = $(this.createContainer())
                                        .append($spinner)
                                        .on('click.modalmanager', $.proxy(this.loading, this));

                                this.isLoading = true;

                                $.support.transition ?
                                        this.$backdropHandle.one($.support.transition.end, callback) :
                                        callback();

                        } else if (this.isLoading && this.$backdropHandle) {
                                this.$backdropHandle.removeClass('in');

                                var that = this;
                                $.support.transition ?
                                        this.$backdropHandle.one($.support.transition.end, function () { that.removeLoading() }) :
                                        that.removeLoading();

                        } else if (callback) {
                                callback(this.isLoading);
                        }
                }
        };

        /* PRIVATE METHODS
        * ======================= */

        // computes and caches the zindexes
        var getzIndex = (function () {
                var zIndexFactor,
                        baseIndex = {};

                return function (type, pos) {

                        if (typeof zIndexFactor === 'undefined'){
                                var $baseModal = $('<div class="modal hide" />').appendTo('body'),
                                        $baseBackdrop = $('<div class="modal-backdrop hide" />').appendTo('body');

                                baseIndex['modal'] = +$baseModal.css('z-index');
                                baseIndex['backdrop'] = +$baseBackdrop.css('z-index');
                                zIndexFactor = baseIndex['modal'] - baseIndex['backdrop'];

                                $baseModal.remove();
                                $baseBackdrop.remove();
                                $baseBackdrop = $baseModal = null;
                        }

                        return baseIndex[type] + (zIndexFactor * pos);

                }
        }());

        // make sure the event target is the modal itself in order to prevent
        // other components such as tabsfrom triggering the modal manager.
        // if Boostsrap namespaced events, this would not be needed.
        function targetIsSelf(callback){
                return function (e) {
                        if (e && this === e.target){
                                return callback.apply(this, arguments);
                        }
                }
        }


        /* MODAL MANAGER PLUGIN DEFINITION
        * ======================= */

        $.fn.modalmanager = function (option, args) {
                return this.each(function () {
                        var $this = $(this),
                                data = $this.data('modalmanager');

                        if (!data) $this.data('modalmanager', (data = new ModalManager(this, option)));
                        if (typeof option === 'string') data[option].apply(data, [].concat(args))
                })
        };

        $.fn.modalmanager.defaults = {
                backdropLimit: 999,
                resize: true,
                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>',
                backdropTemplate: '<div class="modal-backdrop" />'
        };

        $.fn.modalmanager.Constructor = ModalManager

        // ModalManager handles the modal-open class so we need 
        // to remove conflicting bootstrap 3 event handlers
        $(function () {
                $(document).off('show.bs.modal').off('hidden.bs.modal');
        });

}(jQuery);