MDL-59784 core: Modal factory should listen before fetching templates
[moodle.git] / lib / amd / src / modal_factory.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * Create a modal.
18  *
19  * @module     core/modal_factory
20  * @class      modal_factory
21  * @package    core
22  * @copyright  2016 Ryan Wyllie <ryan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
25 define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
26         'core/modal_save_cancel', 'core/modal_confirm', 'core/modal_cancel',
27         'core/templates', 'core/notification', 'core/custom_interaction_events'],
28     function($, ModalEvents, ModalRegistry, Modal, ModalSaveCancel, ModalConfirm,
29         ModalCancel, Templates, Notification, CustomEvents) {
31     // The templates for each type of modal.
32     var TEMPLATES = {
33         DEFAULT: 'core/modal',
34         SAVE_CANCEL: 'core/modal_save_cancel',
35         CONFIRM: 'core/modal_confirm',
36         CANCEL: 'core/modal_cancel',
37     };
39     // The available types of modals.
40     var TYPES = {
41         DEFAULT: 'DEFAULT',
42         SAVE_CANCEL: 'SAVE_CANCEL',
43         CONFIRM: 'CONFIRM',
44         CANCEL: 'CANCEL',
45     };
47     // Register the common set of modals.
48     ModalRegistry.register(TYPES.DEFAULT, Modal, TEMPLATES.DEFAULT);
49     ModalRegistry.register(TYPES.SAVE_CANCEL, ModalSaveCancel, TEMPLATES.SAVE_CANCEL);
50     ModalRegistry.register(TYPES.CONFIRM, ModalConfirm, TEMPLATES.CONFIRM);
51     ModalRegistry.register(TYPES.CANCEL, ModalCancel, TEMPLATES.CANCEL);
53     /**
54      * Set up the events required to show the modal and return focus when the modal
55      * is closed.
56      *
57      * @method setUpTrigger
58      * @param {Promise} modalPromise The modal instance
59      * @param {object} triggerElement The jQuery element to open the modal
60      */
61     var setUpTrigger = function(modalPromise, triggerElement) {
62         if (typeof triggerElement != 'undefined') {
63             if (Array.isArray(triggerElement)) {
64                 var selector = triggerElement[1];
65                 triggerElement = triggerElement[0];
67                 CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
68                 triggerElement.on(CustomEvents.events.activate, selector, function(e, data) {
69                     modalPromise.then(function(modal) {
70                         modal.show();
72                         return modal;
73                     });
74                     data.originalEvent.preventDefault();
75                 });
76             } else {
77                 CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
78                 triggerElement.on(CustomEvents.events.activate, function(e, data) {
79                     modalPromise.then(function(modal) {
80                         modal.show();
82                         return modal;
83                     });
84                     data.originalEvent.preventDefault();
85                 });
86             }
88             modalPromise.then(function(modal) {
89                 modal.getRoot().on(ModalEvents.hidden, function() {
90                     triggerElement.focus();
91                 });
93                 return modal;
94             });
95         }
96     };
98     /**
99      * Create the correct instance of a modal based on the givem type. Sets up
100      * the trigger between the modal and the trigger element.
101      *
102      * @method createFromElement
103      * @param {object} registryConf A config from the ModalRegistry
104      * @param {object} modalElement The modal HTML jQuery object
105      * @param {object} triggerElement The trigger HTML jQuery object
106      * @return {object} Modal instance
107      */
108     var createFromElement = function(registryConf, modalElement) {
109         modalElement = $(modalElement);
110         var module = registryConf.module;
111         var modal = new module(modalElement);
113         return modal;
114     };
116     /**
117      * Create the correct modal instance for the given type, including loading
118      * the correct template and setting up the trigger relationship with the
119      * trigger element.
120      *
121      * @method createFromType
122      * @param {object} registryConf A config from the ModalRegistry
123      * @param {object} triggerElement The trigger HTML jQuery object
124      * @return {promise} Resolved with a Modal instance
125      */
126     var createFromType = function(registryConf, templateContext, triggerElement) {
127         var templateName = registryConf.template;
129         var modalPromise = Templates.render(templateName, templateContext)
130             .then(function(html) {
131                 var modalElement = $(html);
132                 return createFromElement(registryConf, modalElement);
133             })
134             .fail(Notification.exception);
136         setUpTrigger(modalPromise, triggerElement);
138         return modalPromise;
139     };
141     /**
142      * Create a Modal instance.
143      *
144      * @method create
145      * @param {object} modalConfig The configuration to create the modal instance
146      * @param {object} triggerElement The trigger HTML jQuery object
147      * @return {promise} Resolved with a Modal instance
148      */
149     var create = function(modalConfig, triggerElement) {
150         var type = modalConfig.type || TYPES.DEFAULT;
151         var isLarge = modalConfig.large ? true : false;
152         var registryConf = null;
153         var templateContext = {};
155         registryConf = ModalRegistry.get(type);
157         if (!registryConf) {
158             Notification.exception({message: 'Unable to find modal of type: ' + type});
159         }
161         if (typeof modalConfig.templateContext != 'undefined') {
162             templateContext = modalConfig.templateContext;
163         }
165         return createFromType(registryConf, templateContext, triggerElement)
166             .then(function(modal) {
167                 if (typeof modalConfig.title != 'undefined') {
168                     modal.setTitle(modalConfig.title);
169                 }
171                 if (typeof modalConfig.body != 'undefined') {
172                     modal.setBody(modalConfig.body);
173                 }
175                 if (typeof modalConfig.footer != 'undefined') {
176                     modal.setFooter(modalConfig.footer);
177                 }
179                 if (isLarge) {
180                     modal.setLarge();
181                 }
183                 return modal;
184             });
185     };
187     return {
188         create: create,
189         types: TYPES,
190     };
191 });