/** * This is a base modal implementation that provides common utilities. * * A modal implementation should override the following methods: * * getTitle(): * returns the title for the modal. * getHTMLContent(): * returns the HTML content to be shown inside the modal. * * A modal implementation should also provide the following options: * * modalName: A string identifying the modal. * modalType: A string identifying the type of the modal. * modalSize: A string, either 'sm', 'med', or 'lg' indicating the * size of the modal. * viewSpecificClasses: A string of CSS classes to be attached to * the modal window. * addPrimaryActionButton: A boolean indicating whether to include a primary action * button on the modal. * primaryActionButtonType: A string to be used as type for primary action button. * primaryActionButtonTitle: A string to be used as title for primary action button. * showEditorModeButtons: Whether to show editor mode button in the modal header. */ define(['jquery', 'underscore', 'gettext', 'js/views/baseview'], function($, _, gettext, BaseView) { var BaseModal = BaseView.extend({ events: { 'click .action-cancel': 'cancel' }, options: _.extend({}, BaseView.prototype.options, { type: 'prompt', closeIcon: false, icon: false, modalName: 'basic', modalType: 'generic', modalSize: 'lg', title: '', modalWindowClass: '.modal-window', // A list of class names, separated by space. viewSpecificClasses: '', addPrimaryActionButton: false, primaryActionButtonType: 'save', primaryActionButtonTitle: gettext('Save'), showEditorModeButtons: true }), initialize: function() { var parent = this.options.parent, parentElement = this.options.parentElement; this.modalTemplate = this.loadTemplate('basic-modal'); this.buttonTemplate = this.loadTemplate('modal-button'); if (parent) { parentElement = parent.$el; } else if (!parentElement) { parentElement = this.$el.closest(this.options.modalWindowClass); if (parentElement.length === 0) { parentElement = $('body'); } } this.parentElement = parentElement; }, render: function() { this.$el.html(this.modalTemplate({ name: this.options.modalName, type: this.options.modalType, size: this.options.modalSize, title: this.getTitle(), modalSRTitle: this.options.modalSRTitle, showEditorModeButtons: this.options.showEditorModeButtons, viewSpecificClasses: this.options.viewSpecificClasses })); this.addActionButtons(); this.renderContents(); this.parentElement.append(this.$el); }, getTitle: function() { return this.options.title; }, renderContents: function() { var contentHtml = this.getContentHtml(); this.$('.modal-content').html(contentHtml); }, /** * Returns the content to be shown in the modal. */ getContentHtml: function() { return ''; }, show: function(focusModal) { var focusModalWindow = focusModal === undefined; this.render(); this.resize(); $(window).resize(_.bind(this.resize, this)); // child may want to have its own focus management if (focusModalWindow) { // after showing and resizing, send focus this.$el.find(this.options.modalWindowClass).focus(); } }, hide: function() { // Completely remove the modal from the DOM this.undelegateEvents(); this.$el.html(''); }, cancel: function(event) { if (event) { event.preventDefault(); event.stopPropagation(); // Make sure parent modals don't see the click } this.hide(); }, /** * Adds the action buttons to the modal. */ addActionButtons: function() { if (this.options.addPrimaryActionButton) { this.addActionButton( this.options.primaryActionButtonType, this.options.primaryActionButtonTitle, true ); } this.addActionButton('cancel', gettext('Cancel')); }, /** * Adds a new action button to the modal. * @param type The type of the action. * @param name The action's name. * @param isPrimary True if this button is the primary one. */ addActionButton: function(type, name, isPrimary) { var html = this.buttonTemplate({ type: type, name: name, isPrimary: isPrimary }); this.getActionBar().find('ul').append(html); }, /** * Returns the action bar that contains the modal's action buttons. */ getActionBar: function() { return this.$(this.options.modalWindowClass + ' > div > .modal-actions'); }, /** * Returns the action button of the specified type. */ getActionButton: function(type) { return this.getActionBar().find('.action-' + type); }, enableActionButton: function(type) { this.getActionBar().find('.action-' + type).prop('disabled', false).removeClass('is-disabled'); }, disableActionButton: function(type) { this.getActionBar().find('.action-' + type).prop('disabled', true).addClass('is-disabled'); }, resize: function() { var top, left, modalWindow, modalWidth, modalHeight, availableWidth, availableHeight, maxWidth, maxHeight; modalWindow = this.$el.find(this.options.modalWindowClass); availableWidth = $(window).width(); availableHeight = $(window).height(); maxWidth = availableWidth * 0.80; maxHeight = availableHeight * 0.80; modalWidth = Math.min(modalWindow.outerWidth(), maxWidth); modalHeight = Math.min(modalWindow.outerHeight(), maxHeight); left = (availableWidth - modalWidth) / 2; top = (availableHeight - modalHeight) / 2; modalWindow.css({ top: top + $(window).scrollTop(), left: left + $(window).scrollLeft() }); } }); return BaseModal; });