MDL-59940 calendar: Fix eslint errors
[moodle.git] / calendar / amd / src / calendar.js
CommitLineData
3b0738f0
SL
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/**
aa091225
RW
17 * This module is the highest level module for the calendar. It is
18 * responsible for initialising all of the components required for
19 * the calendar to run. It also coordinates the interaction between
20 * components by listening for and responding to different events
21 * triggered within the calendar UI.
3b0738f0
SL
22 *
23 * @module core_calendar/calendar
24 * @package core_calendar
25 * @copyright 2017 Simey Lameze <simey@moodle.com>
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 */
aa091225
RW
28define([
29 'jquery',
30 'core/ajax',
31 'core/str',
32 'core/templates',
33 'core/notification',
34 'core/custom_interaction_events',
35 'core/modal_events',
36 'core/modal_factory',
37 'core_calendar/modal_event_form',
38 'core_calendar/summary_modal',
39 'core_calendar/repository',
695c5726
AN
40 'core_calendar/events',
41 'core_calendar/view_manager'
aa091225
RW
42 ],
43 function(
44 $,
45 Ajax,
46 Str,
47 Templates,
48 Notification,
49 CustomEvents,
50 ModalEvents,
51 ModalFactory,
52 ModalEventForm,
53 SummaryModal,
54 CalendarRepository,
695c5726
AN
55 CalendarEvents,
56 CalendarViewManager
aa091225
RW
57 ) {
58
59 var SELECTORS = {
60 ROOT: "[data-region='calendar']",
f6e8cc83
RW
61 DAY: "[data-region='day']",
62 EVENT_ITEM: "[data-region='event-item']",
aa091225 63 EVENT_LINK: "[data-action='view-event']",
5ca142dc
RW
64 NEW_EVENT_BUTTON: "[data-action='new-event-button']",
65 DAY_CONTENT: "[data-region='day-content']",
66 LOADING_ICON: '.loading-icon',
29158c8b 67 VIEW_DAY_LINK: "[data-action='view-day-link']",
afa8c3da 68 CALENDAR_MONTH_WRAPPER: ".calendarwrapper",
bc2cca3a
AN
69 COURSE_SELECTOR: 'select[name="course"]',
70 TODAY: '.today',
aa091225
RW
71 };
72
73 /**
74 * Get the event type lang string.
75 *
76 * @param {String} eventType The event type.
77 * @return {promise} The lang string promise.
78 */
79 var getEventType = function(eventType) {
80 var lang = 'type' + eventType;
81 return Str.get_string(lang, 'core_calendar').then(function(langStr) {
82 return langStr;
83 });
84 };
85
aa091225
RW
86 /**
87 * Render the event summary modal.
88 *
89 * @param {Number} eventId The calendar event id.
90 */
91 var renderEventSummaryModal = function(eventId) {
92 // Calendar repository promise.
93 CalendarRepository.getEventById(eventId).then(function(getEventResponse) {
94 if (!getEventResponse.event) {
95 throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);
96 }
97 var eventData = getEventResponse.event;
aa091225 98
8afe9f8a 99 return getEventType(eventData.eventtype).then(function(eventType) {
aa091225
RW
100 eventData.eventtype = eventType;
101 return eventData;
102 });
aa091225
RW
103 }).then(function(eventData) {
104 // Build the modal parameters from the event data.
105 var modalParams = {
106 title: eventData.name,
107 type: SummaryModal.TYPE,
4df03a27
SL
108 body: Templates.render('core_calendar/event_summary_body', eventData),
109 templateContext: {
110 canedit: eventData.canedit,
4757780a
SL
111 candelete: eventData.candelete,
112 isactionevent: eventData.isactionevent,
113 url: eventData.url
4df03a27 114 }
aa091225 115 };
4df03a27 116
aa091225
RW
117 // Create the modal.
118 return ModalFactory.create(modalParams);
119
120 }).done(function(modal) {
121 // Handle hidden event.
122 modal.getRoot().on(ModalEvents.hidden, function() {
123 // Destroy when hidden.
124 modal.destroy();
125 });
126
127 // Finally, render the modal!
128 modal.show();
129
130 }).fail(Notification.exception);
131 };
132
5ca142dc
RW
133 /**
134 * Handler for the drag and drop move event. Provides a loading indicator
135 * while the request is sent to the server to update the event start date.
136 *
137 * Triggers a eventMoved calendar javascript event if the event was successfully
138 * updated.
139 *
140 * @param {event} e The calendar move event
aefb2950
RW
141 * @param {int} eventId The event id being moved
142 * @param {object|null} originElement The jQuery element for where the event is moving from
5ca142dc
RW
143 * @param {object} destinationElement The jQuery element for where the event is moving to
144 */
aefb2950
RW
145 var handleMoveEvent = function(e, eventId, originElement, destinationElement) {
146 var originTimestamp = null;
5ca142dc
RW
147 var destinationTimestamp = destinationElement.attr('data-day-timestamp');
148
aefb2950
RW
149 if (originElement) {
150 originTimestamp = originElement.attr('data-day-timestamp');
151 }
152
5ca142dc 153 // If the event has actually changed day.
aefb2950 154 if (!originElement || originTimestamp != destinationTimestamp) {
5ca142dc
RW
155 Templates.render('core/loading', {})
156 .then(function(html, js) {
157 // First we show some loading icons in each of the days being affected.
5ca142dc 158 destinationElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
5ca142dc 159 Templates.appendNodeContents(destinationElement, html, js);
aefb2950
RW
160
161 if (originElement) {
162 originElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
163 Templates.appendNodeContents(originElement, html, js);
164 }
5ca142dc
RW
165 return;
166 })
167 .then(function() {
168 // Send a request to the server to make the change.
169 return CalendarRepository.updateEventStartDay(eventId, destinationTimestamp);
170 })
171 .then(function() {
172 // If the update was successful then broadcast an event letting the calendar
173 // know that an event has been moved.
aefb2950 174 $('body').trigger(CalendarEvents.eventMoved, [eventId, originElement, destinationElement]);
5ca142dc
RW
175 return;
176 })
177 .always(function() {
178 // Always remove the loading icons regardless of whether the update
179 // request was successful or not.
5ca142dc 180 var destinationLoadingElement = destinationElement.find(SELECTORS.LOADING_ICON);
5ca142dc 181 destinationElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
5ca142dc 182 Templates.replaceNode(destinationLoadingElement, '', '');
aefb2950
RW
183
184 if (originElement) {
185 var originLoadingElement = originElement.find(SELECTORS.LOADING_ICON);
186 originElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
187 Templates.replaceNode(originLoadingElement, '', '');
188 }
5ca142dc
RW
189 return;
190 })
191 .fail(Notification.exception);
192 }
193 };
194
aa091225
RW
195 /**
196 * Create the event form modal for creating new events and
197 * editing existing events.
198 *
199 * @method registerEventFormModal
200 * @param {object} root The calendar root element
201 * @return {object} The create modal promise
202 */
203 var registerEventFormModal = function(root) {
204 var newEventButton = root.find(SELECTORS.NEW_EVENT_BUTTON);
205 var contextId = newEventButton.attr('data-context-id');
206
207 return ModalFactory.create(
208 {
209 type: ModalEventForm.TYPE,
210 large: true,
211 templateContext: {
212 contextid: contextId
3d67e83f 213 }
bc2cca3a 214 }
aa091225
RW
215 );
216 };
a43ec9eb 217
aa091225
RW
218 /**
219 * Listen to and handle any calendar events fired by the calendar UI.
220 *
221 * @method registerCalendarEventListeners
222 * @param {object} root The calendar root element
223 * @param {object} eventFormModalPromise A promise reolved with the event form modal
224 */
225 var registerCalendarEventListeners = function(root, eventFormModalPromise) {
29158c8b
SL
226 var body = $('body'),
227 courseId = $(root).find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('courseid');
aa091225 228
d6942fb5 229 body.on(CalendarEvents.created, function() {
b31a1695 230 CalendarViewManager.reloadCurrentMonth(root);
d6942fb5
RW
231 });
232 body.on(CalendarEvents.deleted, function() {
b31a1695 233 CalendarViewManager.reloadCurrentMonth(root);
d6942fb5
RW
234 });
235 body.on(CalendarEvents.updated, function() {
b31a1695 236 CalendarViewManager.reloadCurrentMonth(root);
d6942fb5 237 });
c34e2002
SL
238 body.on(CalendarEvents.editActionEvent, function(e, url) {
239 // Action events needs to be edit directly on the course module.
240 window.location.assign(url);
241 });
5ca142dc
RW
242 // Handle the event fired by the drag and drop code.
243 body.on(CalendarEvents.moveEvent, handleMoveEvent);
244 // When an event is successfully moved we should updated the UI.
245 body.on(CalendarEvents.eventMoved, function() {
aefb2950 246 CalendarViewManager.reloadCurrentMonth(root);
5ca142dc 247 });
a43ec9eb 248
aa091225
RW
249 eventFormModalPromise.then(function(modal) {
250 // When something within the calendar tells us the user wants
251 // to edit an event then show the event form modal.
252 body.on(CalendarEvents.editEvent, function(e, eventId) {
253 modal.setEventId(eventId);
254 modal.show();
3b0738f0 255 });
29158c8b 256 modal.setCourseId(courseId);
aa091225 257 return;
abf00dad
AN
258 })
259 .fail(Notification.exception);
aa091225
RW
260 };
261
262 /**
263 * Register event listeners for the module.
abf00dad
AN
264 *
265 * @param {object} root The calendar root element
aa091225 266 */
6397ec54 267 var registerEventListeners = function(root) {
aa091225 268 // Bind click events to event links.
f6e8cc83 269 root.on('click', SELECTORS.EVENT_ITEM, function(e) {
aa091225 270 e.preventDefault();
f6e8cc83
RW
271 // We've handled the event so stop it from bubbling
272 // and causing the day click handler to fire.
273 e.stopPropagation();
274
275 var target = $(e.target);
276 var eventId = null;
277
278 if (target.is(SELECTORS.EVENT_LINK)) {
279 eventId = target.attr('data-event-id');
280 } else {
281 eventId = target.find(SELECTORS.EVENT_LINK).attr('data-event-id');
282 }
283
aa091225
RW
284 renderEventSummaryModal(eventId);
285 });
286
afa8c3da
SL
287 root.on('change', SELECTORS.COURSE_SELECTOR, function() {
288 var selectElement = $(this);
289 var courseId = selectElement.val();
290 CalendarViewManager.reloadCurrentMonth(root, courseId)
291 .then(function() {
292 // We need to get the selector again because the content has changed.
293 return root.find(SELECTORS.COURSE_SELECTOR).val(courseId);
294 })
295 .fail(Notification.exception);
296 });
297
aa091225
RW
298 var eventFormPromise = registerEventFormModal(root);
299 registerCalendarEventListeners(root, eventFormPromise);
f6e8cc83 300
bc2cca3a
AN
301 // Bind click event on the new event button.
302 root.on('click', SELECTORS.NEW_EVENT_BUTTON, function(e) {
303 eventFormPromise.then(function(modal) {
304 // Attempt to find the cell for today.
305 // If it can't be found, then use the start time of the first day on the calendar.
306 var today = root.find(SELECTORS.TODAY);
307 if (!today.length) {
308 modal.setStartTime(root.find(SELECTORS.DAY).attr('data-new-event-timestamp'));
309 }
310
311 modal.show();
312 return;
313 })
314 .fail(Notification.exception);
315
316 e.preventDefault();
317 });
318
f6e8cc83
RW
319 // Bind click events to calendar days.
320 root.on('click', SELECTORS.DAY, function(e) {
321 var target = $(e.target);
322
323 if (!target.is(SELECTORS.VIEW_DAY_LINK)) {
324 var startTime = $(this).attr('data-new-event-timestamp');
325 eventFormPromise.then(function(modal) {
326 modal.setStartTime(startTime);
327 modal.show();
328 return;
abf00dad
AN
329 })
330 .fail(Notification.exception);
f6e8cc83
RW
331
332 e.preventDefault();
333 }
334 });
aa091225
RW
335 };
336
337 return {
6397ec54
AN
338 init: function(root) {
339 root = $(root);
340
341 CalendarViewManager.init(root);
342 registerEventListeners(root);
aa091225
RW
343 }
344 };
345});