Modal.js 8.34 KB
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

define(function(require) {

    var Backbone = require('backbone');
    var template = require('hbs!tmpl/common/modal');

    var Modal = Backbone.View.extend({

        className: 'modal',

        events: {
            'click .close': function(event) {
                event.preventDefault();

                this.trigger('closeModal');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('closeModal', this);
                }
            },
            'click .cancel': function(event) {
                event.preventDefault();

                this.trigger('closeModal');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('closeModal', this);
                }
            },
            'click .ok': function(event) {
                event.preventDefault();

                this.trigger('ok');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('ok', this);
                }

                if (this.options.okCloses) {
                    this.close();
                }
            }
        },

        /**
         * Creates an instance of a Bootstrap Modal
         *
         * @see http://twitter.github.com/bootstrap/javascript.html#modals
         *
         * @param {Object} options
         * @param {String|View} [options.content] Modal content. Default: none
         * @param {String} [options.title]        Title. Default: none
         * @param {String} [options.okText]       Text for the OK button. Default: 'OK'
         * @param {String} [options.cancelText]   Text for the cancel button. Default: 'Cancel'. If passed a falsey value, the button will be removed
         * @param {Boolean} [options.allowCancel  Whether the modal can be closed, other than by pressing OK. Default: true
         * @param {Boolean} [options.escape]      Whether the 'esc' key can dismiss the modal. Default: true, but false if options.cancellable is true
         * @param {Boolean} [options.animate]     Whether to animate in/out. Default: false
         * @param {Function} [options.template]   Compiled underscore template to override the default one
         */
        initialize: function(options) {
            this.options = _.extend({
                title: null,
                okText: 'OK',
                focusOk: true,
                okCloses: true,
                cancelText: 'Cancel',
                allowCancel: false,
                showFooter: true,
                escape: true,
                animate: true,
                contentWithFooter: false,
                template: template
            }, options);
        },

        /**
         * Creates the DOM element
         *
         * @api private
         */
        render: function() {
            var $el = this.$el,
                options = this.options,
                content = options.content;

            //Create the modal container
            $el.html(options.template(options));

            // var $content = this.$content = $el.find('.modal-body');

            //Insert the main content if it's a view
            if (content && content.$el) {
                content.render();
                if (options.contentWithFooter) {
                    $el.find('.modal-content').append(content.$el);
                } else {
                    $el.find('.modal-body').html(content.$el);
                }
            } else {
                if (options.htmlContent) {
                    $el.find('.modal-body').append(options.htmlContent);
                }

            }

            // if (options.mainClass) $el.addClass(options.mainClass);

            if (options.animate) $el.addClass('fade');

            this.isRendered = true;

            return this;
        },
        onClose: function() {
            alert('close');
        },
        /**
         * Renders and shows the modal
         *
         * @param {Function} [cb]     Optional callback that runs only when OK is pressed.
         */
        open: function(cb) {
            if (!this.isRendered) this.render();

            var self = this,
                $el = this.$el;

            //Create it
            $el.modal(_.extend({
                keyboard: this.options.allowCancel,
                backdrop: this.options.allowCancel ? true : 'static'
            }, this.options.modalOptions));

            //Focus OK button
            $el.one('shown', function() {
                if (self.options.focusOk) {
                    $el.find('.btn.ok').focus();
                }

                if (self.options.content && self.options.content.trigger) {
                    self.options.content.trigger('shown', self);
                }

                self.trigger('shown');
            });

            //Adjust the modal and backdrop z-index; for dealing with multiple modals
            var numModals = Modal.count,
                $backdrop = $('.modal-backdrop:eq(' + numModals + ')'),
                backdropIndex = parseInt($backdrop.css('z-index'), 10),
                elIndex = parseInt($backdrop.css('z-index'), 10);

            $backdrop.css('z-index', backdropIndex + numModals);
            this.$el.css('z-index', elIndex + numModals);

            if (this.options.allowCancel) {
                $backdrop.one('click', function() {
                    if (self.options.content && self.options.content.trigger) {
                        self.options.content.trigger('closeModal', self);
                    }

                    self.trigger('closeModal');
                });

                $(document).one('keyup.dismiss.modal', function(e) {
                    e.which == 27 && self.trigger('closeModal');

                    if (self.options.content && self.options.content.trigger) {
                        e.which == 27 && self.options.content.trigger('shown', self);
                    }
                });
            }

            this.on('cancel', function() {
                self.close();
            });

            Modal.count++;

            //Run callback on OK if provided
            if (cb) {
                self.on('ok', cb);
            }
            $el.one('shown.bs.modal', function() {
                self.trigger('shownModal');
            });
            return this;
        },

        /**
         * Closes the modal
         */
        close: function() {
            var self = this,
                $el = this.$el;

            //Check if the modal should stay open
            if (this._preventClose) {
                this._preventClose = false;
                return;
            }

            $el.one('hidden.bs.modal', function onHidden(e) {
                // Ignore events propagated from interior objects, like bootstrap tooltips
                if (e.target !== e.currentTarget) {
                    return $el.one('hidden.bs.modal', onHidden);
                }
                self.remove();

                if (self.options.content && self.options.content.trigger) {
                    self.options.content.trigger('hidden.bs.modal', self);
                }

                self.trigger('hidden.bs.modal');
            });

            $el.modal('hide');

            Modal.count--;
        },

        /**
         * Stop the modal from closing.
         * Can be called from within a 'close' or 'ok' event listener.
         */
        preventClose: function() {
            this._preventClose = true;
        }
    }, {
        //STATICS

        //The number of modals on display
        count: 0
    });

    return Modal;
});