\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 ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport SummaryModal from 'core_calendar/summary_modal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\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 }).fail();\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 */\nconst registerEventListeners = (root) => {\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, courseId, categoryId, link.dataset.day);\n e.preventDefault();\n } else if (view === 'day') {\n changeDay(root, link.href, link.dataset.year, link.dataset.month, link.dataset.day, courseId, categoryId);\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');\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');\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');\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 * @return {promise}\n */\nexport const changeMonth = (root, url, year, month, courseId, categoryId, day = 1) => {\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.monthChanged, [year, month, courseId, categoryId]);\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 *\n * @return {promise}\n */\nexport const refreshDayContent = (root, year, month, day, courseId, categoryId, target = null, template = '') => {\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 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 * @return {promise}\n */\nexport const changeDay = (root, url, year, month, day, courseId, categoryId) => {\n return refreshDayContent(root, year, month, day, courseId, categoryId)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.dayChanged, [year, month, courseId, categoryId]);\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 type: SummaryModal.TYPE,\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 ModalFactory.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\nexport const init = (root, view) => {\n prefetchStrings('calendar', ['moreevents']);\n foldDayEvents();\n registerEventListeners(root, view);\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"],"file":"view_manager.min.js"}
\ No newline at end of file
diff --git a/calendar/amd/src/calendar_view.js b/calendar/amd/src/calendar_view.js
index 7abe828a07c..e6e132b14c6 100644
--- a/calendar/amd/src/calendar_view.js
+++ b/calendar/amd/src/calendar_view.js
@@ -85,6 +85,7 @@ define([
} else {
daysWithEvent.removeClass('hidden');
}
+ CalendarViewManager.foldDayEvents(root);
});
var eventFormPromise = CalendarCrud.registerEventFormModal(root);
diff --git a/calendar/amd/src/events.js b/calendar/amd/src/events.js
index 60838a4f3ac..3c839f654ec 100644
--- a/calendar/amd/src/events.js
+++ b/calendar/amd/src/events.js
@@ -33,6 +33,7 @@ define([], function() {
monthChanged: 'calendar-events:month_changed',
moveEvent: 'calendar-events:move_event',
filterChanged: 'calendar-events:filter_changed',
+ courseChanged: 'calendar-events:course_changed',
viewUpdated: 'calendar-events:view_updated',
};
});
diff --git a/calendar/amd/src/selectors.js b/calendar/amd/src/selectors.js
index b1a96f35103..4f764e614d5 100644
--- a/calendar/amd/src/selectors.js
+++ b/calendar/amd/src/selectors.js
@@ -55,6 +55,7 @@ define([], function() {
courseSelector: 'select[name="course"]',
dateContainer: '.clickable.hasevent',
dateContent: '[data-region="day-content"]',
+ monthDetailed: '.calendarmonth.calendartable',
},
today: '.today',
day: '[data-region="day"]',
@@ -69,6 +70,7 @@ define([], function() {
containers: {
loadingIcon: '[data-region="overlay-icon-container"]',
},
+ mainCalendar: '.maincalendar .heightcontainer',
fullCalendarView: 'page-calendar-view',
};
});
diff --git a/calendar/amd/src/view_manager.js b/calendar/amd/src/view_manager.js
index 0c20c2a19aa..041c7d09778 100644
--- a/calendar/amd/src/view_manager.js
+++ b/calendar/amd/src/view_manager.js
@@ -31,7 +31,114 @@ import ModalFactory from 'core/modal_factory';
import ModalEvents from 'core/modal_events';
import SummaryModal from 'core_calendar/summary_modal';
import CustomEvents from 'core/custom_interaction_events';
+import {get_string as getString} from 'core/str';
import Pending from 'core/pending';
+import {prefetchStrings} from 'core/prefetch';
+
+/**
+ * Limit number of events per day
+ *
+ */
+const LIMIT_DAY_EVENTS = 5;
+
+/**
+ * Hide day events if more than 5.
+ *
+ */
+export const foldDayEvents = () => {
+ const root = $(CalendarSelectors.elements.monthDetailed);
+ const days = root.find(CalendarSelectors.day);
+ if (days.length === 0) {
+ return;
+ }
+ days.each(function() {
+ const dayContainer = $(this);
+ const eventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-eventtype]`;
+ const filteredEventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-filtered="true"]`;
+ const moreEventsSelector = `${CalendarSelectors.elements.dateContent} [data-action="view-more-events"]`;
+ const events = dayContainer.find(eventsSelector);
+ if (events.length === 0) {
+ return;
+ }
+
+ const filteredEvents = dayContainer.find(filteredEventsSelector);
+ const numberOfFiltered = filteredEvents.length;
+ const numberOfEvents = events.length - numberOfFiltered;
+
+ let count = 1;
+ events.each(function() {
+ const event = $(this);
+ const isNotFiltered = event.attr('data-event-filtered') !== 'true';
+ const offset = (numberOfEvents === LIMIT_DAY_EVENTS) ? 0 : 1;
+ if (isNotFiltered) {
+ if (count > LIMIT_DAY_EVENTS - offset) {
+ event.attr('data-event-folded', 'true');
+ event.hide();
+ } else {
+ event.attr('data-event-folded', 'false');
+ event.show();
+ count++;
+ }
+ } else {
+ // It's being filtered out.
+ event.attr('data-event-folded', 'false');
+ }
+ });
+
+ const moreEventsLink = dayContainer.find(moreEventsSelector);
+ if (numberOfEvents > LIMIT_DAY_EVENTS) {
+ const numberOfHiddenEvents = numberOfEvents - LIMIT_DAY_EVENTS + 1;
+ moreEventsLink.show();
+ getString('moreevents', 'calendar', numberOfHiddenEvents).then(str => {
+ const link = moreEventsLink.find('strong a');
+ moreEventsLink.attr('data-event-folded', 'false');
+ link.text(str);
+ return str;
+ }).fail();
+ } else {
+ moreEventsLink.hide();
+ }
+ });
+};
+
+/**
+ * Register and handle month calendar events.
+ *
+ * @param {string} pendingId pending id.
+ */
+export const registerEventListenersForMonthDetailed = (pendingId) => {
+ const events = `${CalendarEvents.viewUpdated}`;
+ $('body').on(events, function(e) {
+ foldDayEvents(e);
+ });
+ foldDayEvents();
+ $('body').on(CalendarEvents.filterChanged, function(e, data) {
+ const root = $(CalendarSelectors.elements.monthDetailed);
+ const pending = new Pending(pendingId);
+ const target = root.find(CalendarSelectors.eventType[data.type]);
+ const transitionPromise = $.Deferred();
+ if (data.hidden) {
+ transitionPromise.then(function() {
+ target.attr('data-event-filtered', 'true');
+ return target.hide().promise();
+ }).fail();
+ } else {
+ transitionPromise.then(function() {
+ target.attr('data-event-filtered', 'false');
+ return target.show().promise();
+ }).fail();
+ }
+
+ transitionPromise.then(function() {
+ foldDayEvents();
+ return;
+ })
+ .always(pending.resolve)
+ .fail();
+
+ transitionPromise.resolve();
+ });
+};
/**
* Register event listeners for the module.
@@ -213,7 +320,11 @@ export const reloadCurrentMonth = (root, courseId = 0, categoryId = 0) => {
courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');
categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');
- return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day);
+ return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day).
+ then((...args) => {
+ $('body').trigger(CalendarEvents.courseChanged, [year, month, courseId, categoryId]);
+ return args;
+ });
};
@@ -449,5 +560,12 @@ const renderEventSummaryModal = (eventId) => {
};
export const init = (root, view) => {
+ prefetchStrings('calendar', ['moreevents']);
+ foldDayEvents();
registerEventListeners(root, view);
+ const calendarTable = root.find(CalendarSelectors.elements.monthDetailed);
+ if (calendarTable.length) {
+ const pendingId = `month-detailed-${calendarTable.id}-filterChanged`;
+ registerEventListenersForMonthDetailed(calendarTable, pendingId);
+ }
};
diff --git a/calendar/templates/month_detailed.mustache b/calendar/templates/month_detailed.mustache
index e58fe2d295e..5bc9073303a 100644
--- a/calendar/templates/month_detailed.mustache
+++ b/calendar/templates/month_detailed.mustache
@@ -95,6 +95,7 @@
{{/underway}}
{{/events}}
+
+
+
+ {{#str}} moreevents, calendar, {{hasmoreevents}} {{/str}}
+
+
+
{{#events}}
@@ -159,42 +170,12 @@
{{#js}}
require([
'jquery',
- 'core_calendar/month_view_drag_drop',
- 'core_calendar/selectors',
- 'core_calendar/events',
+ 'core_calendar/month_view_drag_drop'
], function(
$,
- DragDrop,
- CalendarSelectors,
- CalendarEvents
+ DragDrop
) {
var root = $('#month-detailed-{{uniqid}}-{{calendarinstanceid}}');
DragDrop.init(root);
-
- $('body').on(CalendarEvents.filterChanged, function(e, data) {
- M.util.js_pending("month-detailed-{{uniqid}}-filterChanged");
- // A filter value has been changed.
- // Find all matching cells in the popover data, and hide them.
- var target = root.find(CalendarSelectors.eventType[data.type]);
-
- var transitionPromise = $.Deferred();
- if (data.hidden) {
- transitionPromise.then(function() {
- return target.slideUp('fast').promise();
- });
- } else {
- transitionPromise.then(function() {
- return target.slideDown('fast').promise();
- });
- }
-
- transitionPromise.then(function() {
- M.util.js_complete("month-detailed-{{uniqid}}-filterChanged");
-
- return;
- });
-
- transitionPromise.resolve();
- });
});
{{/js}}
diff --git a/calendar/view.php b/calendar/view.php
index 1f3f8b84093..1b7e12925ee 100644
--- a/calendar/view.php
+++ b/calendar/view.php
@@ -147,7 +147,7 @@ $calendar->add_sidecalendar_blocks($renderer, true, $view);
echo $OUTPUT->header();
echo $renderer->start_layout();
-echo html_writer::start_tag('div', array('class'=>'heightcontainer'));
+echo html_writer::start_tag('div', ['class' => 'heightcontainer', 'data-calendar-type' => 'main-block']);
diff --git a/lang/en/calendar.php b/lang/en/calendar.php
index d39b56414dd..6e4152a3788 100644
--- a/lang/en/calendar.php
+++ b/lang/en/calendar.php
@@ -187,6 +187,7 @@ $string['monthnext'] = 'Next month';
$string['monthprev'] = 'Previous month';
$string['monththis'] = 'This month';
$string['more'] = 'More';
+$string['moreevents'] = '{$a} more';
$string['namewithsource'] = '{$a->name} ({$a->source})';
$string['never'] = 'Never';
$string['newevent'] = 'New event';
diff --git a/theme/boost/scss/moodle/calendar.scss b/theme/boost/scss/moodle/calendar.scss
index 1c357f196d0..3ec2b6e4022 100644
--- a/theme/boost/scss/moodle/calendar.scss
+++ b/theme/boost/scss/moodle/calendar.scss
@@ -129,6 +129,20 @@ $calendarCurrentDateBackground: $primary;
}
}
+#region-main {
+ .maincalendar {
+ .calendarwrapper {
+ td {
+ & > div {
+ height: 11.5em;
+ overflow: hidden;
+ }
+ }
+ }
+ }
+
+}
+
.maincalendar {
vertical-align: top;
padding: 0;
@@ -154,8 +168,13 @@ $calendarCurrentDateBackground: $primary;
margin: 0;
padding: 0;
+ li[data-event-folded="true"] {
+ display: none;
+ }
+
li {
list-style-type: none;
+ line-height: 1.2em;
> a {
@include text-truncate;
@@ -171,6 +190,10 @@ $calendarCurrentDateBackground: $primary;
}
}
+ a[data-action="view-day-link"] {
+ @include text-truncate;
+ }
+
.icon {
margin-left: 0.25em;
margin-right: 0.25em;
@@ -222,7 +245,6 @@ $calendarCurrentDateBackground: $primary;
}
td {
- height: 5em;
.day-number-circle {
display: inline-block;
diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css
index e1af9fe3ccd..0b31c3a6004 100644
--- a/theme/boost/style/moodle.css
+++ b/theme/boost/style/moodle.css
@@ -13205,6 +13205,10 @@ body.dragging .dragging {
border-spacing: 2px;
width: 100%; }
+#region-main .maincalendar .calendarwrapper td > div {
+ height: 11.5em;
+ overflow: hidden; }
+
.maincalendar {
vertical-align: top;
padding: 0; }
@@ -13226,8 +13230,11 @@ body.dragging .dragging {
.maincalendar .calendarmonth ul {
margin: 0;
padding: 0; }
+ .maincalendar .calendarmonth ul li[data-event-folded="true"] {
+ display: none; }
.maincalendar .calendarmonth ul li {
- list-style-type: none; }
+ list-style-type: none;
+ line-height: 1.2em; }
.maincalendar .calendarmonth ul li > a {
overflow: hidden;
text-overflow: ellipsis;
@@ -13238,6 +13245,10 @@ body.dragging .dragging {
text-decoration: none; }
.maincalendar .calendarmonth ul li > a:hover .eventname {
text-decoration: underline; }
+ .maincalendar .calendarmonth ul li a[data-action="view-day-link"] {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap; }
.maincalendar .calendarmonth ul li .icon {
margin-left: 0.25em;
margin-right: 0.25em;
@@ -13269,22 +13280,20 @@ body.dragging .dragging {
.maincalendar .calendarmonth th {
text-align: left;
padding-left: 16px; }
- .maincalendar .calendarmonth td {
- height: 5em; }
- .maincalendar .calendarmonth td .day-number-circle {
+ .maincalendar .calendarmonth td .day-number-circle {
+ display: inline-block;
+ line-height: 0;
+ width: 30px;
+ height: 30px; }
+ .maincalendar .calendarmonth td .day-number-circle .day-number {
display: inline-block;
- line-height: 0;
- width: 30px;
- height: 30px; }
- .maincalendar .calendarmonth td .day-number-circle .day-number {
- display: inline-block;
- padding: 50% 4px;
- width: 100%;
- text-align: center; }
- .maincalendar .calendarmonth td.today .day-number-circle {
- border-radius: 50%;
- color: #fff;
- background-color: #0f6cbf; }
+ padding: 50% 4px;
+ width: 100%;
+ text-align: center; }
+ .maincalendar .calendarmonth td.today .day-number-circle {
+ border-radius: 50%;
+ color: #fff;
+ background-color: #0f6cbf; }
.maincalendar .calendarmonth .clickable:hover {
background-color: #ededed; }
.maincalendar .controls {
diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css
index a918d6f19fc..a98a7ee71d7 100644
--- a/theme/classic/style/moodle.css
+++ b/theme/classic/style/moodle.css
@@ -13205,6 +13205,10 @@ body.dragging .dragging {
border-spacing: 2px;
width: 100%; }
+#region-main .maincalendar .calendarwrapper td > div {
+ height: 11.5em;
+ overflow: hidden; }
+
.maincalendar {
vertical-align: top;
padding: 0; }
@@ -13226,8 +13230,11 @@ body.dragging .dragging {
.maincalendar .calendarmonth ul {
margin: 0;
padding: 0; }
+ .maincalendar .calendarmonth ul li[data-event-folded="true"] {
+ display: none; }
.maincalendar .calendarmonth ul li {
- list-style-type: none; }
+ list-style-type: none;
+ line-height: 1.2em; }
.maincalendar .calendarmonth ul li > a {
overflow: hidden;
text-overflow: ellipsis;
@@ -13238,6 +13245,10 @@ body.dragging .dragging {
text-decoration: none; }
.maincalendar .calendarmonth ul li > a:hover .eventname {
text-decoration: underline; }
+ .maincalendar .calendarmonth ul li a[data-action="view-day-link"] {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap; }
.maincalendar .calendarmonth ul li .icon {
margin-left: 0.25em;
margin-right: 0.25em;
@@ -13269,22 +13280,20 @@ body.dragging .dragging {
.maincalendar .calendarmonth th {
text-align: left;
padding-left: 16px; }
- .maincalendar .calendarmonth td {
- height: 5em; }
- .maincalendar .calendarmonth td .day-number-circle {
+ .maincalendar .calendarmonth td .day-number-circle {
+ display: inline-block;
+ line-height: 0;
+ width: 30px;
+ height: 30px; }
+ .maincalendar .calendarmonth td .day-number-circle .day-number {
display: inline-block;
- line-height: 0;
- width: 30px;
- height: 30px; }
- .maincalendar .calendarmonth td .day-number-circle .day-number {
- display: inline-block;
- padding: 50% 4px;
- width: 100%;
- text-align: center; }
- .maincalendar .calendarmonth td.today .day-number-circle {
- border-radius: 50%;
- color: #fff;
- background-color: #0f6cbf; }
+ padding: 50% 4px;
+ width: 100%;
+ text-align: center; }
+ .maincalendar .calendarmonth td.today .day-number-circle {
+ border-radius: 50%;
+ color: #fff;
+ background-color: #0f6cbf; }
.maincalendar .calendarmonth .clickable:hover {
background-color: #ededed; }
.maincalendar .controls {