calendar/amd/src/calendar.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/>.
  15. /**
  16. * This module is the highest level module for the calendar. It is
  17. * responsible for initialising all of the components required for
  18. * the calendar to run. It also coordinates the interaction between
  19. * components by listening for and responding to different events
  20. * triggered within the calendar UI.
  21. *
  22. * @module core_calendar/calendar
  23. * @copyright 2017 Simey Lameze <simey@moodle.com>
  24. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25. */
  26. define([
  27. 'jquery',
  28. 'core/ajax',
  29. 'core/str',
  30. 'core/templates',
  31. 'core/notification',
  32. 'core/custom_interaction_events',
  33. 'core/modal_events',
  34. 'core/modal_factory',
  35. 'core_calendar/modal_event_form',
  36. 'core_calendar/summary_modal',
  37. 'core_calendar/repository',
  38. 'core_calendar/events',
  39. 'core_calendar/view_manager',
  40. 'core_calendar/crud',
  41. 'core_calendar/selectors',
  42. 'core/config',
  43. ],
  44. function(
  45. $,
  46. Ajax,
  47. Str,
  48. Templates,
  49. Notification,
  50. CustomEvents,
  51. ModalEvents,
  52. ModalFactory,
  53. ModalEventForm,
  54. SummaryModal,
  55. CalendarRepository,
  56. CalendarEvents,
  57. CalendarViewManager,
  58. CalendarCrud,
  59. CalendarSelectors,
  60. Config,
  61. ) {
  62. var SELECTORS = {
  63. ROOT: "[data-region='calendar']",
  64. DAY: "[data-region='day']",
  65. NEW_EVENT_BUTTON: "[data-action='new-event-button']",
  66. DAY_CONTENT: "[data-region='day-content']",
  67. LOADING_ICON: '.loading-icon',
  68. VIEW_DAY_LINK: "[data-action='view-day-link']",
  69. CALENDAR_MONTH_WRAPPER: ".calendarwrapper",
  70. TODAY: '.today',
  71. DAY_NUMBER_CIRCLE: '.day-number-circle',
  72. DAY_NUMBER: '.day-number'
  73. };
  74. /**
  75. * Handler for the drag and drop move event. Provides a loading indicator
  76. * while the request is sent to the server to update the event start date.
  77. *
  78. * Triggers a eventMoved calendar javascript event if the event was successfully
  79. * updated.
  80. *
  81. * @param {event} e The calendar move event
  82. * @param {int} eventId The event id being moved
  83. * @param {object|null} originElement The jQuery element for where the event is moving from
  84. * @param {object} destinationElement The jQuery element for where the event is moving to
  85. */
  86. var handleMoveEvent = function(e, eventId, originElement, destinationElement) {
  87. var originTimestamp = null;
  88. var destinationTimestamp = destinationElement.attr('data-day-timestamp');
  89. if (originElement) {
  90. originTimestamp = originElement.attr('data-day-timestamp');
  91. }
  92. // If the event has actually changed day.
  93. if (!originElement || originTimestamp != destinationTimestamp) {
  94. Templates.render('core/loading', {})
  95. .then(function(html, js) {
  96. // First we show some loading icons in each of the days being affected.
  97. destinationElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
  98. Templates.appendNodeContents(destinationElement, html, js);
  99. if (originElement) {
  100. originElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');
  101. Templates.appendNodeContents(originElement, html, js);
  102. }
  103. return;
  104. })
  105. .then(function() {
  106. // Send a request to the server to make the change.
  107. return CalendarRepository.updateEventStartDay(eventId, destinationTimestamp);
  108. })
  109. .then(function() {
  110. // If the update was successful then broadcast an event letting the calendar
  111. // know that an event has been moved.
  112. $('body').trigger(CalendarEvents.eventMoved, [eventId, originElement, destinationElement]);
  113. return;
  114. })
  115. .always(function() {
  116. // Always remove the loading icons regardless of whether the update
  117. // request was successful or not.
  118. var destinationLoadingElement = destinationElement.find(SELECTORS.LOADING_ICON);
  119. destinationElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
  120. Templates.replaceNode(destinationLoadingElement, '', '');
  121. if (originElement) {
  122. var originLoadingElement = originElement.find(SELECTORS.LOADING_ICON);
  123. originElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');
  124. Templates.replaceNode(originLoadingElement, '', '');
  125. }
  126. return;
  127. })
  128. .fail(Notification.exception);
  129. }
  130. };
  131. /**
  132. * Listen to and handle any calendar events fired by the calendar UI.
  133. *
  134. * @method registerCalendarEventListeners
  135. * @param {object} root The calendar root element
  136. * @param {object} eventFormModalPromise A promise reolved with the event form modal
  137. */
  138. var registerCalendarEventListeners = function(root, eventFormModalPromise) {
  139. var body = $('body');
  140. body.on(CalendarEvents.created, function() {
  141. CalendarViewManager.reloadCurrentMonth(root);
  142. });
  143. body.on(CalendarEvents.deleted, function() {
  144. CalendarViewManager.reloadCurrentMonth(root);
  145. });
  146. body.on(CalendarEvents.updated, function() {
  147. CalendarViewManager.reloadCurrentMonth(root);
  148. });
  149. body.on(CalendarEvents.editActionEvent, function(e, url) {
  150. // Action events needs to be edit directly on the course module.
  151. window.location.assign(url);
  152. });
  153. // Handle the event fired by the drag and drop code.
  154. body.on(CalendarEvents.moveEvent, handleMoveEvent);
  155. // When an event is successfully moved we should updated the UI.
  156. body.on(CalendarEvents.eventMoved, function() {
  157. CalendarViewManager.reloadCurrentMonth(root);
  158. });
  159. CalendarCrud.registerEditListeners(root, eventFormModalPromise);
  160. };
  161. /**
  162. * Register event listeners for the module.
  163. *
  164. * @param {object} root The calendar root element
  165. */
  166. var registerEventListeners = function(root) {
  167. const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);
  168. // Listen the click on the day link to render the day view.
  169. root.on('click', SELECTORS.VIEW_DAY_LINK, function(e) {
  170. var dayLink = $(e.target).closest(SELECTORS.VIEW_DAY_LINK);
  171. var year = dayLink.data('year'),
  172. month = dayLink.data('month'),
  173. day = dayLink.data('day'),
  174. courseId = dayLink.data('courseid'),
  175. categoryId = dayLink.data('categoryid');
  176. const url = '?view=day&time=' + dayLink.data('timestamp');
  177. if (viewingFullCalendar) {
  178. CalendarViewManager.refreshDayContent(root, year, month, day, courseId, categoryId, root,
  179. 'core_calendar/calendar_day').then(function() {
  180. e.preventDefault();
  181. return CalendarViewManager.updateUrl(url);
  182. }).fail(Notification.exception);
  183. } else {
  184. window.location.assign(Config.wwwroot + '/calendar/view.php' + url);
  185. }
  186. });
  187. root.on('change', CalendarSelectors.elements.courseSelector, function() {
  188. var selectElement = $(this);
  189. var courseId = selectElement.val();
  190. CalendarViewManager.reloadCurrentMonth(root, courseId, null)
  191. .then(function() {
  192. // We need to get the selector again because the content has changed.
  193. return root.find(CalendarSelectors.elements.courseSelector).val(courseId);
  194. })
  195. .fail(Notification.exception);
  196. });
  197. var eventFormPromise = CalendarCrud.registerEventFormModal(root),
  198. contextId = $(SELECTORS.CALENDAR_MONTH_WRAPPER).data('context-id');
  199. registerCalendarEventListeners(root, eventFormPromise);
  200. if (contextId) {
  201. // Bind click events to calendar days.
  202. root.on('click', SELECTORS.DAY, function(e) {
  203. var target = $(e.target);
  204. const displayingSmallBlockCalendar = root.parents('aside').data('blockregion') === 'side-pre';
  205. if (!viewingFullCalendar && displayingSmallBlockCalendar) {
  206. const dateContainer = target.closest(SELECTORS.DAY);
  207. const url = '?view=day&time=' + dateContainer.data('day-timestamp');
  208. window.location.assign(Config.wwwroot + '/calendar/view.php' + url);
  209. } else {
  210. const hasViewDayLink = target.closest(SELECTORS.VIEW_DAY_LINK).length;
  211. const shouldShowNewEventModal = !hasViewDayLink;
  212. if (shouldShowNewEventModal) {
  213. var startTime = $(this).attr('data-new-event-timestamp');
  214. eventFormPromise.then(function(modal) {
  215. var wrapper = target.closest(CalendarSelectors.wrapper);
  216. modal.setCourseId(wrapper.data('courseid'));
  217. var categoryId = wrapper.data('categoryid');
  218. if (typeof categoryId !== 'undefined') {
  219. modal.setCategoryId(categoryId);
  220. }
  221. modal.setContextId(wrapper.data('contextId'));
  222. modal.setStartTime(startTime);
  223. modal.show();
  224. return;
  225. }).fail(Notification.exception);
  226. }
  227. }
  228. e.preventDefault();
  229. });
  230. }
  231. };
  232. return {
  233. init: function(root) {
  234. root = $(root);
  235. CalendarViewManager.init(root);
  236. registerEventListeners(root);
  237. }
  238. };
  239. });