MDL-59382 calendar: add modal to create and update events
[moodle.git] / calendar / amd / src / event_form.js
CommitLineData
aa091225
RW
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/>.
15
16/**
17 * A javascript module to enhance the event form.
18 *
19 * @module core_calendar/event_form
20 * @package core_calendar
21 * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24define(['jquery', 'core/templates'], function($, Templates) {
25
26 var SELECTORS = {
27 EVENT_TYPE: '[name="eventtype"]',
28 EVENT_COURSE_ID: '[name="courseid"]',
29 EVENT_GROUP_COURSE_ID: '[name="groupcourseid"]',
30 EVENT_GROUP_ID: '[name="groupid"]',
31 FORM_GROUP: '.form-group',
32 SELECT_OPTION: 'option',
33 ADVANCED_ELEMENT: '.fitem.advanced',
34 FIELDSET_ADVANCED_ELEMENTS: 'fieldset.containsadvancedelements',
35 MORELESS_TOGGLE: '.moreless-actions'
36 };
37
38 var EVENT_TYPES = {
39 USER: 'user',
40 SITE: 'site',
41 COURSE: 'course',
42 GROUP: 'group'
43 };
44
45 var EVENTS = {
46 SHOW_ADVANCED: 'event_form-show-advanced',
47 HIDE_ADVANCED: 'event_form-hide-advanced',
48 ADVANCED_SHOWN: 'event_form-advanced-shown',
49 ADVANCED_HIDDEN: 'event_form-advanced-hidden',
50 };
51
52 /**
53 * Find the old show more / show less toggle added by the mform and destroy it.
54 * We are handling the visibility of the advanced fields with the more/less button
55 * in the footer of the modal that this form is rendered within.
56 *
57 * @method destroyOldMoreLessToggle
58 * @param {object} formElement The root form element
59 */
60 var destroyOldMoreLessToggle = function(formElement) {
61 formElement.find(SELECTORS.FIELDSET_ADVANCED_ELEMENTS).removeClass('containsadvancedelements');
62 var element = formElement.find(SELECTORS.MORELESS_TOGGLE);
63 Templates.replaceNode(element, '', '');
64 };
65
66 /**
67 * Find each of the advanced form elements and make them visible.
68 *
69 * This function triggers the ADVANCED_SHOWN event for any other
70 * component to handle (e.g. the event form modal).
71 *
72 * @method destroyOldMoreLessToggle
73 * @param {object} formElement The root form element
74 */
75 var showAdvancedElements = function(formElement) {
76 formElement.find(SELECTORS.ADVANCED_ELEMENT).removeClass('hidden');
77 formElement.trigger(EVENTS.ADVANCED_SHOWN);
78 };
79
80 /**
81 * Find each of the advanced form elements and hide them.
82 *
83 * This function triggers the ADVANCED_HIDDEN event for any other
84 * component to handle (e.g. the event form modal).
85 *
86 * @method hideAdvancedElements
87 * @param {object} formElement The root form element
88 */
89 var hideAdvancedElements = function(formElement) {
90 formElement.find(SELECTORS.ADVANCED_ELEMENT).addClass('hidden');
91 formElement.trigger(EVENTS.ADVANCED_HIDDEN);
92 };
93
94 /**
95 * Listen for any events telling this module to show or hide it's
96 * advanced elements.
97 *
98 * This function listens for SHOW_ADVANCED and HIDE_ADVANCED.
99 *
100 * @method listenForShowHideEvents
101 * @param {object} formElement The root form element
102 */
103 var listenForShowHideEvents = function(formElement) {
104 formElement.on(EVENTS.SHOW_ADVANCED, function() {
105 showAdvancedElements(formElement);
106 });
107
108 formElement.on(EVENTS.HIDE_ADVANCED, function() {
109 hideAdvancedElements(formElement);
110 });
111 };
112
113 /**
114 * Parse the group id select element in the event form and pull out
115 * the course id from the value to allow us to toggle other select
116 * elements based on the course id for the group a user selects.
117 *
118 * This is a little hacky but I couldn't find a better way to pass
119 * the course id for each group id with the limitations of mforms.
120 *
121 * The group id options are rendered with a value like:
122 * "<courseid>-<groupid>"
123 * E.g.
124 * For a group with id 10 in a course with id 3 the value of the
125 * option will be 3-10.
126 *
127 * @method parseGroupSelect
128 * @param {object} formElement The root form element
129 */
130 var parseGroupSelect = function(formElement) {
131 formElement.find(SELECTORS.EVENT_GROUP_ID)
132 .find(SELECTORS.SELECT_OPTION)
133 .each(function(index, element) {
134 var element = $(element);
135 var value = element.attr('value');
136 var splits = value.split('-');
137 var courseId = splits[0];
138
139 element.attr('data-course-id', courseId);
140 });
141 };
142
143 /**
144 * Toggle the visibility of the secondary select elements based on
145 * the event type the user has selected.
146 *
147 * There are 3 secondary select elements within the form:
148 * - course: a list of all courses a user can add course events to
149 * - group course: a list of all courses a user can add group events to.
150 * this list can be different from the course list above.
151 * - group: a list of all groups a user can add an event to. This list will
152 * be filtered further based on the group course selected.
153 *
154 * There are 4 event types:
155 * - user: none of the secondary selects should be visible.
156 * - site: none of the secondary selects should be visible.
157 * - course: "course" select should be visible and both "group course"
158 * and "group" should be hidden.
159 * - group: "group course" and "group" should be visible and "course"
160 * should be hidden.
161 *
162 * @method hideTypeSubSelects
163 * @param {object} formElement The root form element
164 */
165 var hideTypeSubSelects = function(formElement) {
166 var typeSelect = formElement.find(SELECTORS.EVENT_TYPE);
167 var eventType = typeSelect.val();
168 var courseIdSelect = formElement.find(SELECTORS.EVENT_COURSE_ID)
169 .closest(SELECTORS.FORM_GROUP)
170 .removeClass('hidden');
171 var groupCourseIdSelect = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID)
172 .closest(SELECTORS.FORM_GROUP)
173 .removeClass('hidden');
174 var groupIdSelect = formElement.find(SELECTORS.EVENT_GROUP_ID)
175 .closest(SELECTORS.FORM_GROUP)
176 .removeClass('hidden');
177
178 // Hide the unreleated selectors for the given event type.
179 switch (eventType) {
180 case EVENT_TYPES.COURSE:
181 groupCourseIdSelect.addClass('hidden');
182 groupIdSelect.addClass('hidden');
183 break;
184 case EVENT_TYPES.GROUP:
185 courseIdSelect.addClass('hidden');
186 break;
187 default:
188 courseIdSelect.addClass('hidden');
189 groupCourseIdSelect.addClass('hidden');
190 groupIdSelect.addClass('hidden');
191 }
192 };
193
194 /**
195 * Listen for when the user changes the event type select in the
196 * form and then toggle the visibility of the appropriate secondary
197 * select elements.
198 *
199 * See: hideTypeSubSelects.
200 *
201 * @method addTypeSelectListeners
202 * @param {object} formElement The root form element
203 */
204 var addTypeSelectListeners = function(formElement) {
205 var typeSelect = formElement.find(SELECTORS.EVENT_TYPE);
206
207 typeSelect.on('change', function() {
208 hideTypeSubSelects(formElement);
209 });
210 };
211
212 /**
213 * Listen for when the user changes the group course when configuring
214 * a group event and filter the options in the group select to only
215 * show the groups available within the course the user has selected.
216 *
217 * @method addCourseGroupSelectListeners
218 * @param {object} formElement The root form element
219 */
220 var addCourseGroupSelectListeners = function(formElement) {
221 var courseGroupSelect = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID);
222 var groupSelect = formElement.find(SELECTORS.EVENT_GROUP_ID);
223 var groupSelectOptions = groupSelect.find(SELECTORS.SELECT_OPTION);
224 var filterGroupSelectOptions = function() {
225 var selectedCourseId = courseGroupSelect.val();
226 var selectedIndex = null;
227
228 groupSelectOptions.each(function(index, element) {
229 element = $(element);
230
231 if (element.attr('data-course-id') == selectedCourseId) {
232 element.removeClass('hidden');
233 element.prop('disabled', false);
234
235 if (selectedIndex === null) {
236 selectedIndex = index;
237 }
238 } else {
239 element.addClass('hidden');
240 element.prop('disabled', true);
241 }
242 });
243
244 groupSelect.prop('selectedIndex', selectedIndex);
245 };
246
247 courseGroupSelect.on('change', filterGroupSelectOptions);
248 filterGroupSelectOptions();
249 };
250
251 /**
252 * Initialise all of the form enhancementds.
253 *
254 * @method init
255 * @param {string} formId The value of the form's id attribute
256 * @param {bool} hasError If the form has errors rendered form the server.
257 */
258 var init = function(formId, hasError) {
259 var formElement = $('#' + formId);
260
261 listenForShowHideEvents(formElement);
262 destroyOldMoreLessToggle(formElement);
263 hideTypeSubSelects(formElement);
264 parseGroupSelect(formElement);
265 addTypeSelectListeners(formElement);
266 addCourseGroupSelectListeners(formElement);
267
268 // If we know that the form has been rendered with server side
269 // errors then we need to display all of the elements in the form
270 // in case one of those elements has the error.
271 if (hasError) {
272 showAdvancedElements(formElement);
273 } else {
274 hideAdvancedElements(formElement);
275 }
276 };
277
278 return {
279 init: init,
280 events: EVENTS,
281 };
282});