moodle/calendar/amd/build/view_manager.min.js.map
Meirza b19b9cb8bf MDL-81644 calendar: The behaviour of event dates in the block calendar
There is a difference in the behaviour of the calendar block when it is on the calendar page and when it is not.
On the calendar page, when the user clicks on the date or link next/previous month or day name in the calendar block,
it will have the effect of changing the URL, which should not happen.

The patch also includes hiding popover after the user clicks the day number. When the user clicks on the date or
is focused on the date and then presses enter on the keyboard, the popover does not automatically close.
To fix this, I added an event type, "click", to be attached to the hidePopover function and added conditioning to
the target element when there was a "click" event.

An additional step was added to the Behat calendar for the click event to ease the testing process.

For the popover, I set the "animation" to false to avoid the random Behat failure caused by the slow animation.
2024-06-07 14:46:44 +07:00

1 line
33 KiB
Plaintext

{"version":3,"file":"view_manager.min.js","sources":["../src/view_manager.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * A javascript module to handler calendar view changes.\n *\n * @module core_calendar/view_manager\n * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport CalendarEvents from 'core_calendar/events';\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport ModalEvents from 'core/modal_events';\nimport SummaryModal from 'core_calendar/summary_modal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport Url from 'core/url';\nimport Config from 'core/config';\n\n/**\n * Limit number of events per day\n *\n */\nconst LIMIT_DAY_EVENTS = 5;\n\n/**\n * Hide day events if more than 5.\n *\n */\nexport const foldDayEvents = () => {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const days = root.find(CalendarSelectors.day);\n if (days.length === 0) {\n return;\n }\n days.each(function() {\n const dayContainer = $(this);\n const eventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-eventtype]`;\n const filteredEventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-filtered=\"true\"]`;\n const moreEventsSelector = `${CalendarSelectors.elements.dateContent} [data-action=\"view-more-events\"]`;\n const events = dayContainer.find(eventsSelector);\n if (events.length === 0) {\n return;\n }\n\n const filteredEvents = dayContainer.find(filteredEventsSelector);\n const numberOfFiltered = filteredEvents.length;\n const numberOfEvents = events.length - numberOfFiltered;\n\n let count = 1;\n events.each(function() {\n const event = $(this);\n const isNotFiltered = event.attr('data-event-filtered') !== 'true';\n const offset = (numberOfEvents === LIMIT_DAY_EVENTS) ? 0 : 1;\n if (isNotFiltered) {\n if (count > LIMIT_DAY_EVENTS - offset) {\n event.attr('data-event-folded', 'true');\n event.hide();\n } else {\n event.attr('data-event-folded', 'false');\n event.show();\n count++;\n }\n } else {\n // It's being filtered out.\n event.attr('data-event-folded', 'false');\n }\n });\n\n const moreEventsLink = dayContainer.find(moreEventsSelector);\n if (numberOfEvents > LIMIT_DAY_EVENTS) {\n const numberOfHiddenEvents = numberOfEvents - LIMIT_DAY_EVENTS + 1;\n moreEventsLink.show();\n getString('moreevents', 'calendar', numberOfHiddenEvents).then(str => {\n const link = moreEventsLink.find('strong a');\n moreEventsLink.attr('data-event-folded', 'false');\n link.text(str);\n return str;\n }).catch(Notification.exception);\n } else {\n moreEventsLink.hide();\n }\n });\n};\n\n/**\n * Register and handle month calendar events.\n *\n * @param {string} pendingId pending id.\n */\nexport const registerEventListenersForMonthDetailed = (pendingId) => {\n const events = `${CalendarEvents.viewUpdated}`;\n $('body').on(events, function(e) {\n foldDayEvents(e);\n });\n foldDayEvents();\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const pending = new Pending(pendingId);\n const target = root.find(CalendarSelectors.eventType[data.type]);\n const transitionPromise = $.Deferred();\n if (data.hidden) {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'true');\n return target.hide().promise();\n }).fail();\n } else {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'false');\n return target.show().promise();\n }).fail();\n }\n\n transitionPromise.then(function() {\n foldDayEvents();\n return;\n })\n .always(pending.resolve)\n .fail();\n\n transitionPromise.resolve();\n });\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {object} root The root element.\n * @param {boolean} isCalendarBlock - A flag indicating whether this is a calendar block.\n */\nconst registerEventListeners = (root, isCalendarBlock) => {\n root = $(root);\n\n // Bind click events to event links.\n root.on('click', CalendarSelectors.links.eventLink, (e) => {\n const target = e.target;\n let eventLink = null;\n let eventId = null;\n const pendingPromise = new Pending('core_calendar/view_manager:eventLink:click');\n\n if (target.matches(CalendarSelectors.actions.viewEvent)) {\n eventLink = target;\n } else {\n eventLink = target.closest(CalendarSelectors.actions.viewEvent);\n }\n\n if (eventLink) {\n eventId = eventLink.dataset.eventId;\n } else {\n eventId = target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId;\n }\n\n if (eventId) {\n // A link was found. Show the modal.\n\n e.preventDefault();\n // We've handled the event so stop it from bubbling\n // and causing the day click handler to fire.\n e.stopPropagation();\n\n renderEventSummaryModal(eventId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n root.on('click', CalendarSelectors.links.navLink, (e) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const view = wrapper.data('view');\n const courseId = wrapper.data('courseid');\n const categoryId = wrapper.data('categoryid');\n const link = e.currentTarget;\n\n if (view === 'month' || view === 'monthblock') {\n changeMonth(root, link.href, link.dataset.year, link.dataset.month,\n courseId, categoryId, link.dataset.day, isCalendarBlock);\n e.preventDefault();\n } else if (view === 'day') {\n changeDay(root, link.href, link.dataset.year, link.dataset.month, link.dataset.day,\n courseId, categoryId, isCalendarBlock);\n e.preventDefault();\n }\n });\n\n const viewSelector = root.find(CalendarSelectors.viewSelector);\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(\n CustomEvents.events.activate,\n (e) => {\n e.preventDefault();\n\n const option = e.target;\n if (option.classList.contains('active')) {\n return;\n }\n\n const view = option.dataset.view,\n year = option.dataset.year,\n month = option.dataset.month,\n day = option.dataset.day,\n courseId = option.dataset.courseid,\n categoryId = option.dataset.categoryid;\n\n if (view == 'month') {\n refreshMonthContent(root, year, month, courseId, categoryId, root, 'core_calendar/calendar_month', day)\n .then(() => {\n updateUrl('?view=month&course=' + courseId);\n return;\n }).fail(Notification.exception);\n } else if (view == 'day') {\n refreshDayContent(root, year, month, day, courseId, categoryId, root, 'core_calendar/calendar_day')\n .then(() => {\n updateUrl('?view=day&course=' + courseId);\n return;\n }).fail(Notification.exception);\n } else if (view == 'upcoming') {\n reloadCurrentUpcoming(root, courseId, categoryId, root, 'core_calendar/calendar_upcoming')\n .then(() => {\n updateUrl('?view=upcoming&course=' + courseId);\n return;\n }).fail(Notification.exception);\n }\n }\n );\n};\n\n/**\n * Refresh the month content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const refreshMonthContent = (root, year, month, courseId, categoryId, target = null, template = '', day = 1) => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, courseId].join('-'));\n const includenavigation = root.data('includenavigation');\n const mini = root.data('mini');\n const viewMode = target.data('view');\n return CalendarRepository.getCalendarMonthData(year, month, courseId, categoryId, includenavigation, mini, day, viewMode)\n .then(context => {\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, courseId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The container element\n * @param {string} url The calendar url to be shown\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {number} day Day (optional)\n * @param {boolean} [isCalendarBlock=false] - A flag indicating whether this is a calendar block.\n * @return {promise}\n */\nexport const changeMonth = (root, url, year, month, courseId, categoryId, day = 1, isCalendarBlock = false) => {\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day)\n .then((...args) => {\n if (url.length && url !== '#' && !isCalendarBlock) {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.monthChanged, [year, month, courseId, categoryId, day, isCalendarBlock]);\n return args;\n });\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentMonth = (root, courseId = 0, categoryId = 0) => {\n const year = root.find(CalendarSelectors.wrapper).data('year');\n const month = root.find(CalendarSelectors.wrapper).data('month');\n const day = root.find(CalendarSelectors.wrapper).data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day).\n then((...args) => {\n $('body').trigger(CalendarEvents.courseChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n\n/**\n * Refresh the day content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @param {boolean} isCalendarBlock - A flag indicating whether this is a calendar block.\n *\n * @return {promise}\n */\nexport const refreshDayContent = (root, year, month, day, courseId, categoryId,\n target = null, template = '', isCalendarBlock = false) => {\n startLoading(root);\n\n if (!target || target.length == 0){\n target = root.find(CalendarSelectors.wrapper);\n }\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n const includenavigation = root.data('includenavigation');\n return CalendarRepository.getCalendarDayData(year, month, day, courseId, categoryId, includenavigation)\n .then((context) => {\n context.viewingday = true;\n context.showviewselector = true;\n context.iscalendarblock = isCalendarBlock;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Reload the current day view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentDay = (root, courseId = 0, categoryId = 0) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const year = wrapper.data('year');\n const month = wrapper.data('month');\n const day = wrapper.data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshDayContent(root, year, month, day, courseId, categoryId);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The root element.\n * @param {String} url The calendar url to be shown\n * @param {Number} year Year\n * @param {Number} month Month\n * @param {Number} day Day\n * @param {Number} courseId The id of the course whose events are shown\n * @param {Number} categoryId The id of the category whose events are shown\n * @param {boolean} [isCalendarBlock=false] - A flag indicating whether this is a calendar block.\n * @return {promise}\n */\nexport const changeDay = (root, url, year, month, day, courseId, categoryId, isCalendarBlock = false) => {\n return refreshDayContent(root, year, month, day, courseId, categoryId, null, '', isCalendarBlock)\n .then((...args) => {\n if (url.length && url !== '#' && !isCalendarBlock) {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.dayChanged, [year, month, courseId, categoryId, isCalendarBlock]);\n return args;\n });\n};\n\n/**\n * Update calendar URL.\n *\n * @param {String} url The calendar url to be updated.\n */\nexport const updateUrl = (url) => {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n\n // We want to update the url only if the user is viewing the full calendar.\n if (viewingFullCalendar) {\n window.history.pushState({}, '', url);\n }\n};\n\n/**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\nconst startLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n};\n\n/**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\nconst stopLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @return {promise}\n */\nexport const reloadCurrentUpcoming = (root, courseId = 0, categoryId = 0, target = null, template = '') => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return CalendarRepository.getCalendarUpcomingData(courseId, categoryId)\n .then((context) => {\n context.viewingupcoming = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(function() {\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Get the CSS class to apply for the given event type.\n *\n * @param {string} eventType The calendar event type\n * @return {string}\n */\nconst getEventTypeClassFromType = (eventType) => {\n return 'calendar_event_' + eventType;\n};\n\n/**\n * Render the event summary modal.\n *\n * @param {Number} eventId The calendar event id.\n * @returns {Promise}\n */\nconst renderEventSummaryModal = (eventId) => {\n const pendingPromise = new Pending('core_calendar/view_manager:renderEventSummaryModal');\n\n // Calendar repository promise.\n return CalendarRepository.getEventById(eventId)\n .then((getEventResponse) => {\n if (!getEventResponse.event) {\n throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);\n }\n\n return getEventResponse.event;\n })\n .then(eventData => {\n // Build the modal parameters from the event data.\n const modalParams = {\n title: eventData.name,\n body: Templates.render('core_calendar/event_summary_body', eventData),\n templateContext: {\n canedit: eventData.canedit,\n candelete: eventData.candelete,\n headerclasses: getEventTypeClassFromType(eventData.normalisedeventtype),\n isactionevent: eventData.isactionevent,\n url: eventData.url,\n action: eventData.action\n }\n };\n\n // Create the modal.\n return SummaryModal.create(modalParams);\n })\n .then(modal => {\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Finally, render the modal!\n modal.show();\n\n return modal;\n })\n .then(modal => {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Initializes the calendar component by prefetching strings, folding day events,\n * and registering event listeners.\n *\n * @param {HTMLElement} root - The root element where the calendar view manager and event listeners will be attached.\n * @param {string} view - A flag indicating whether this is a calendar block.\n * @param {boolean} isCalendarBlock - A flag indicating whether this is a calendar block.\n */\nexport const init = (root, view, isCalendarBlock) => {\n prefetchStrings('calendar', ['moreevents']);\n foldDayEvents();\n registerEventListeners(root, isCalendarBlock);\n const calendarTable = root.find(CalendarSelectors.elements.monthDetailed);\n if (calendarTable.length) {\n const pendingId = `month-detailed-${calendarTable.id}-filterChanged`;\n registerEventListenersForMonthDetailed(calendarTable, pendingId);\n }\n};\n\n/**\n * Handles the change of course and updates the relevant elements on the page.\n *\n * @param {integer} courseId - The ID of the new course.\n * @param {string} courseName - The name of the new course.\n * @returns {Promise<void>} - A promise that resolves after the updates are applied.\n */\nexport const handleCourseChange = async(courseId, courseName) => {\n // Select the <ul> element inside the data-region=\"view-selector\".\n const ulElement = document.querySelector(CalendarSelectors.viewSelector + ' ul');\n // Select all <li><a> elements within the <ul>.\n const liElements = ulElement.querySelectorAll('li a');\n // Loop through the selected elements and update the courseid.\n liElements.forEach(element => {\n element.setAttribute('data-courseid', courseId);\n });\n\n const calendar = await getString('calendar', 'calendar');\n const pageHeaderHeadingsElement = document.querySelector(CalendarSelectors.pageHeaderHeadings);\n const courseUrl = Url.relativeUrl('/course/view.php', {id: courseId});\n // Apply the page header text.\n if (courseId !== Config.siteId) {\n pageHeaderHeadingsElement.innerHTML = calendar + ': <a href=\"' + courseUrl + '\">' + courseName + '</a>';\n } else {\n pageHeaderHeadingsElement.innerHTML = calendar;\n }\n};"],"names":["foldDayEvents","days","CalendarSelectors","elements","monthDetailed","find","day","length","each","dayContainer","this","eventsSelector","dateContent","filteredEventsSelector","moreEventsSelector","events","numberOfFiltered","numberOfEvents","count","event","attr","hide","show","moreEventsLink","numberOfHiddenEvents","then","str","link","text","catch","Notification","exception","registerEventListenersForMonthDetailed","pendingId","CalendarEvents","viewUpdated","on","e","filterChanged","data","root","pending","Pending","target","eventType","type","transitionPromise","$","Deferred","hidden","promise","fail","always","resolve","refreshMonthContent","year","month","courseId","categoryId","template","startLoading","wrapper","M","util","js_pending","get","join","includenavigation","mini","viewMode","CalendarRepository","getCalendarMonthData","context","Templates","render","html","js","replaceNode","document","querySelector","dispatchEvent","CustomEvent","js_complete","stopLoading","changeMonth","url","isCalendarBlock","updateUrl","args","trigger","monthChanged","courseChanged","refreshDayContent","getCalendarDayData","viewingday","showviewselector","iscalendarblock","changeDay","dayChanged","getElementById","fullCalendarView","window","history","pushState","containers","loadingIcon","removeClass","addClass","reloadCurrentUpcoming","getCalendarUpcomingData","viewingupcoming","renderEventSummaryModal","eventId","pendingPromise","getEventById","getEventResponse","Error","eventData","modalParams","title","name","body","templateContext","canedit","candelete","headerclasses","normalisedeventtype","isactionevent","action","SummaryModal","create","modal","getRoot","ModalEvents","destroy","view","links","eventLink","matches","actions","viewEvent","closest","dataset","preventDefault","stopPropagation","navLink","currentTarget","href","viewSelector","define","CustomEvents","activate","option","classList","contains","courseid","categoryid","registerEventListeners","calendarTable","id","async","courseName","querySelectorAll","forEach","element","setAttribute","calendar","pageHeaderHeadingsElement","pageHeaderHeadings","courseUrl","Url","relativeUrl","Config","siteId","innerHTML"],"mappings":"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"}