diff --git a/blocks/timeline/amd/build/event_list.min.js b/blocks/timeline/amd/build/event_list.min.js index 3703214d397..80f6e91d651 100644 --- a/blocks/timeline/amd/build/event_list.min.js +++ b/blocks/timeline/amd/build/event_list.min.js @@ -1,2 +1,2 @@ -define ("block_timeline/event_list",["jquery","core/notification","core/templates","core/paged_content_factory","core/str","core/user_date","block_timeline/calendar_events_repository"],function(a,b,c,d,e,f,g){var h=!1,i={EMPTY_MESSAGE:"[data-region=\"empty-message\"]",ROOT:"[data-region=\"event-list-container\"]",EVENT_LIST_CONTENT:"[data-region=\"event-list-content\"]",EVENT_LIST_LOADING_PLACEHOLDER:"[data-region=\"event-list-loading-placeholder\"]",TIMELINE_BLOCK:"[data-region=\"timeline\"]",TIMELINE_SEARCH:"[data-region=\"search-input\"]"},j={EVENT_LIST_CONTENT:"block_timeline/event-list-content"},k={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,ariaLabels:{itemsperpagecomponents:"ariaeventlistpagelimit, block_timeline"}},l=function(a){a.find(i.EVENT_LIST_CONTENT).addClass("hidden");a.find(i.EMPTY_MESSAGE).removeClass("hidden")},m=function(a){a.find(i.EVENT_LIST_CONTENT).removeClass("hidden");a.find(i.EMPTY_MESSAGE).addClass("hidden")},n=function(a){a.find(i.EVENT_LIST_CONTENT).empty()},o=function(a){var b={},c={courseview:h,eventsbyday:[]};a.forEach(function(a){var c=a.timeusermidnight;if(b[c]){b[c].push(a)}else{b[c]=[a]}});Object.keys(b).forEach(function(a){var d=b[a];c.eventsbyday.push({dayTimestamp:a,events:d})});return c},p=function(a){var b=o(a),d=j.EVENT_LIST_CONTENT;return c.render(d,b)},q=function(a,b,c,d,e,f,h){var i=d!=void 0?a+d*86400:!1,j={starttime:a+c*86400,limit:b};if(e){j.aftereventid=e}if(i){j.endtime=i}if(h){j.searchvalue=h}if(f){j.courseid=f;return g.queryByCourse(j)}else{return g.queryByTime(j)}},r=function(a,b,c,d,e,g,h,i,j){var k=a.pageNumber,l=a.limit,m=k;while(!d.hasOwnProperty(m)){m--}var n=d[m],o=null;if(e&&e.hasOwnProperty(k)){o=e[k]}else{o=q(c,l+1,h,i,n,g,j)}return o.then(function(a){if(!a.events.length){b.allItemsLoaded(k);return[]}var d=document.querySelector("[data-filtername='overdue']"),e=d&&d.getAttribute("aria-current"),g=a.events.filter(function(a){if("open"==a.eventtype||"opensubmission"==a.eventtype){var b=f.getUserMidnightForTimestamp(a.timesort,c);return b>c}return!e||a.overdue}),h=g.length<=l;if(h){b.allItemsLoaded(k)}else{g.pop()}return g})},s=function(c,f,g,h,i,j,l,m,n,o){var q={1:0},s=!1,t=a.extend({},k,n);return e.get_string("ariaeventlistpagelimit","block_timeline",a.isArray(c)?c[0].value:c).then(function(a){t.ariaLabels.itemsperpage=a;t.ariaLabels.paginationnav=m;return a}).then(function(){return d.createWithLimit(c,function(c,d){var e=[];c.forEach(function(a){var c=a.pageNumber,h=r(a,d,g,q,f,i,j,l,o).then(function(a){if(a.length){s=!0;var b=a[a.length-1].id;q[c+1]=b;return p(a)}else{return a}}).catch(b.exception);e.push(h)});a.when.apply(a,e).then(function(){h.resolve(s)}).catch(function(){h.resolve(s)});return e},t)})};return{init:function init(d,e,f,g){var j=4c}return!g||a.overdue});l=k.length<=b;if(!l){k.pop()}if(k.length){var h=k.at(-1).id;t(a,h)}return{calendarEvents:k,loadedAll:l}})},r=function(d){var e=parseInt(d.attr("data-midnight"),10),f=d.attr("data-course-id"),g=parseInt(d.attr("data-days-offset"),10),j=d.attr("data-days-limit"),k=s(d),l=d.find(h.EVENT_LIST_WRAPPER),m=d.closest(h.TIMELINE_BLOCK).find(h.TIMELINE_SEARCH).val(),o=q(d,10,e,k,f,g,j,m);o.then(function(e){if(e.calendarEvents.length){var f=n(e.calendarEvents),g=u(d);f.then(function(b,f){b=a(b);b.find("[data-timestamp=\"".concat(g,"\"]")).remove();c.appendNodeContents(l,b.html(),f);if(!e.loadedAll){c.render(i.MORE_ACTIVITIES_BUTTON,{}).then(function(a){l.append(a);v(d,e.calendarEvents.at(-1).timeusermidnight);y(d);return a}).catch(function(){return!1})}return b}).catch(b.exception)}return e}).then(function(){return x(d)}).catch(b.exception)},s=function(a){return parseInt(a.attr("data-lazyload-offset"),10)},t=function(a,b){a.attr("data-lazyload-offset",b)},u=function(a){return parseInt(a.attr("data-timestamp"),10)},v=function(a,b){a.attr("data-timestamp",b)},w=function(a){var b=a.find(h.MORE_ACTIVITIES_BUTTON);b.prop("disabled",!0);c.render(i.LOADING_ICON,{}).then(function(a){b.append(a);return a}).catch(function(){return!1})},x=function(a){var b=a.find(h.MORE_ACTIVITIES_BUTTON_CONTAINER);b.remove()},y=function(a){var b=a.find(h.MORE_ACTIVITIES_BUTTON);b.on("click",function(){w(a);r(a)})};return{init:function init(d){var e=1.\n\n/**\n * Javascript to load and render the list of calendar events for a\n * given day range.\n *\n * @module block_timeline/event_list\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/templates',\n 'core/paged_content_factory',\n 'core/str',\n 'core/user_date',\n 'block_timeline/calendar_events_repository'\n],\nfunction(\n $,\n Notification,\n Templates,\n PagedContentFactory,\n Str,\n UserDate,\n CalendarEventsRepository\n) {\n\n var SECONDS_IN_DAY = 60 * 60 * 24;\n var courseview = false;\n\n var SELECTORS = {\n EMPTY_MESSAGE: '[data-region=\"empty-message\"]',\n ROOT: '[data-region=\"event-list-container\"]',\n EVENT_LIST_CONTENT: '[data-region=\"event-list-content\"]',\n EVENT_LIST_LOADING_PLACEHOLDER: '[data-region=\"event-list-loading-placeholder\"]',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-region=\"search-input\"]'\n };\n\n var TEMPLATES = {\n EVENT_LIST_CONTENT: 'block_timeline/event-list-content'\n };\n\n // We want the paged content controls below the paged content area\n // and the controls should be ignored while data is loading.\n var DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n ariaLabels: {\n itemsperpagecomponents: 'ariaeventlistpagelimit, block_timeline',\n }\n };\n\n /**\n * Hide the content area and display the empty content message.\n *\n * @param {object} root The container element\n */\n var hideContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).addClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Show the content area and hide the empty content message.\n *\n * @param {object} root The container element\n */\n var showContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).removeClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');\n };\n\n /**\n * Empty the content area.\n *\n * @param {object} root The container element\n */\n var emptyContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).empty();\n };\n\n /**\n * Construct the template context from a list of calendar events. The events\n * are grouped by which day they are on. The day is calculated from the user's\n * midnight timestamp to ensure that the calculation is timezone agnostic.\n *\n * The return data structure will look like:\n * {\n * eventsbyday: [\n * {\n * dayTimestamp: 1533744000,\n * events: [\n * { ...event 1 data... },\n * { ...event 2 data... }\n * ]\n * },\n * {\n * dayTimestamp: 1533830400,\n * events: [\n * { ...event 3 data... },\n * { ...event 4 data... }\n * ]\n * }\n * ]\n * }\n *\n * Each day timestamp is the day's midnight in the user's timezone.\n *\n * @param {array} calendarEvents List of calendar events\n * @return {object}\n */\n var buildTemplateContext = function(calendarEvents) {\n var eventsByDay = {};\n var templateContext = {\n courseview,\n eventsbyday: []\n };\n\n calendarEvents.forEach(function(calendarEvent) {\n var dayTimestamp = calendarEvent.timeusermidnight;\n if (eventsByDay[dayTimestamp]) {\n eventsByDay[dayTimestamp].push(calendarEvent);\n } else {\n eventsByDay[dayTimestamp] = [calendarEvent];\n }\n });\n\n Object.keys(eventsByDay).forEach(function(dayTimestamp) {\n var events = eventsByDay[dayTimestamp];\n templateContext.eventsbyday.push({\n dayTimestamp: dayTimestamp,\n events: events\n });\n });\n\n return templateContext;\n };\n\n /**\n * Render the HTML for the given calendar events.\n *\n * @param {array} calendarEvents A list of calendar events\n * @return {promise} Resolved with HTML and JS strings.\n */\n var render = function(calendarEvents) {\n var templateContext = buildTemplateContext(calendarEvents);\n var templateName = TEMPLATES.EVENT_LIST_CONTENT;\n\n return Templates.render(templateName, templateContext);\n };\n\n /**\n * Retrieve a list of calendar events from the server for the given\n * constraints.\n *\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {Number} limit Limit the result set to this number of items\n * @param {Number} daysOffset How many days (from midnight) to offset the results from\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to\n * @param {int|false} lastId The ID of the last seen event (if any)\n * @param {int|undefined} courseId Course ID to restrict events to\n * @param {string|undefined} searchValue Search value\n * @return {Promise} A jquery promise\n */\n var load = function(midnight, limit, daysOffset, daysLimit, lastId, courseId, searchValue) {\n var startTime = midnight + (daysOffset * SECONDS_IN_DAY);\n var endTime = daysLimit != undefined ? midnight + (daysLimit * SECONDS_IN_DAY) : false;\n\n var args = {\n starttime: startTime,\n limit: limit,\n };\n\n if (lastId) {\n args.aftereventid = lastId;\n }\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n if (courseId) {\n // If we have a course id then we only want events from that course.\n args.courseid = courseId;\n return CalendarEventsRepository.queryByCourse(args);\n } else {\n // Otherwise we want events from any course.\n return CalendarEventsRepository.queryByTime(args);\n }\n };\n\n /**\n * Handle a single page request from the paged content. Uses the given page data to request\n * the events from the server.\n *\n * Checks the given preloadedPages before sending a request to the server to make sure we\n * don't load data unnecessarily.\n *\n * @param {object} pageData A single page data (see core/paged_content_pages for more info).\n * @param {object} actions Paged content actions (see core/paged_content_pages for more info).\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {object} lastIds The last event ID for each loaded page. Page number is key, id is value.\n * @param {object} preloadedPages An object of preloaded page data. Page number as key, data promise as value.\n * @param {int|undefined} courseId Course ID to restrict events to\n * @param {Number} daysOffset How many days (from midnight) to offset the results from\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise resolved with calendar events.\n */\n var loadEventsFromPageData = function(\n pageData,\n actions,\n midnight,\n lastIds,\n preloadedPages,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n ) {\n var pageNumber = pageData.pageNumber;\n var limit = pageData.limit;\n var lastPageNumber = pageNumber;\n\n // This is here to protect us if, for some reason, the pages\n // are loaded out of order somehow and we don't have a reference\n // to the previous page. In that case, scan back to find the most\n // recent page we've seen.\n while (!lastIds.hasOwnProperty(lastPageNumber)) {\n lastPageNumber--;\n }\n // Use the last id of the most recent page.\n var lastId = lastIds[lastPageNumber];\n var eventsPromise = null;\n\n if (preloadedPages && preloadedPages.hasOwnProperty(pageNumber)) {\n // This page has been preloaded so use that rather than load the values\n // again.\n eventsPromise = preloadedPages[pageNumber];\n } else {\n // Load one more than the given limit so that we can tell if there\n // is more content to load after this.\n eventsPromise = load(midnight, limit + 1, daysOffset, daysLimit, lastId, courseId, searchValue);\n }\n\n return eventsPromise.then(function(result) {\n if (!result.events.length) {\n // If we didn't get any events back then tell the paged content\n // that we're done loading.\n actions.allItemsLoaded(pageNumber);\n return [];\n }\n\n // Determine if the overdue filter is applied.\n const overdueFilter = document.querySelector(\"[data-filtername='overdue']\");\n const filterByOverdue = (overdueFilter && overdueFilter.getAttribute('aria-current'));\n\n var calendarEvents = result.events.filter(function(event) {\n if (event.eventtype == \"open\" || event.eventtype == \"opensubmission\") {\n var dayTimestamp = UserDate.getUserMidnightForTimestamp(event.timesort, midnight);\n return dayTimestamp > midnight;\n }\n\n // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue.\n // This means if filtering by overdue, some events fetched might not be required (eg if due later today).\n return (!filterByOverdue || event.overdue);\n });\n // We expect to receive limit + 1 events back from the server.\n // Any less means there are no more events to load.\n var loadedAll = calendarEvents.length <= limit;\n\n if (loadedAll) {\n // Tell the pagination that everything is loaded.\n actions.allItemsLoaded(pageNumber);\n } else {\n // Remove the last element from the array because it isn't\n // needed in this result set.\n calendarEvents.pop();\n }\n\n return calendarEvents;\n });\n };\n\n /**\n * Use the paged content factory to create a paged content element for showing\n * the event list. We only provide a page limit to the factory because we don't\n * know exactly how many pages we'll need. This creates a paging bar with just\n * next/previous buttons.\n *\n * This function specifies the callback for loading the event data that the user\n * is requesting.\n *\n * @param {int|array} pageLimit A single limit or list of limits as options for the paged content\n * @param {object} preloadedPages An object of preloaded page data. Page number as key, data promise as value.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {object} firstLoad A jQuery promise to be resolved after the first set of data is loaded.\n * @param {int|undefined} courseId Course ID to restrict events to\n * @param {Number} daysOffset How many days (from midnight) to offset the results from\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to\n * @param {string} paginationAriaLabel String to set as the aria label for the pagination bar.\n * @param {object} additionalConfig Additional config options to pass to pagedContentFactory\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise.\n */\n var createPagedContent = function(\n pageLimit,\n preloadedPages,\n midnight,\n firstLoad,\n courseId,\n daysOffset,\n daysLimit,\n paginationAriaLabel,\n additionalConfig,\n searchValue\n ) {\n // Remember the last event id we loaded on each page because we can't\n // use the offset value since the backend can skip events if the user doesn't\n // have the capability to see them. Instead we load the next page of events\n // based on the last seen event id.\n var lastIds = {'1': 0};\n var hasContent = false;\n var config = $.extend({}, DEFAULT_PAGED_CONTENT_CONFIG, additionalConfig);\n\n return Str.get_string(\n 'ariaeventlistpagelimit',\n 'block_timeline',\n $.isArray(pageLimit) ? pageLimit[0].value : pageLimit\n )\n .then(function(string) {\n config.ariaLabels.itemsperpage = string;\n config.ariaLabels.paginationnav = paginationAriaLabel;\n return string;\n })\n .then(function() {\n return PagedContentFactory.createWithLimit(\n pageLimit,\n function(pagesData, actions) {\n var promises = [];\n\n pagesData.forEach(function(pageData) {\n var pageNumber = pageData.pageNumber;\n // Load the page data.\n var pagePromise = loadEventsFromPageData(\n pageData,\n actions,\n midnight,\n lastIds,\n preloadedPages,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n ).then(function(calendarEvents) {\n if (calendarEvents.length) {\n // Remember that we've loaded content.\n hasContent = true;\n // Remember the last id we've seen.\n var lastEventId = calendarEvents[calendarEvents.length - 1].id;\n // Record the id that the next page will need to start from.\n lastIds[pageNumber + 1] = lastEventId;\n // Get the HTML and JS for these calendar events.\n return render(calendarEvents);\n } else {\n return calendarEvents;\n }\n })\n .catch(Notification.exception);\n\n promises.push(pagePromise);\n });\n\n $.when.apply($, promises).then(function() {\n // Tell the calling code that the first page has been loaded\n // and whether it contains any content.\n firstLoad.resolve(hasContent);\n return;\n })\n .catch(function() {\n firstLoad.resolve(hasContent);\n });\n\n return promises;\n },\n config\n );\n });\n };\n\n /**\n * Create a paged content region for the calendar events in the given root element.\n * The content of the root element are replaced with a new paged content section\n * each time this function is called.\n *\n * This function will be called each time the offset or limit values are changed to\n * reload the event list region.\n *\n * @param {object} root The event list container element\n * @param {int|array} pageLimit A single limit or list of limits as options for the paged content\n * @param {object} preloadedPages An object of preloaded page data. Page number as key, data promise as value.\n * @param {string} paginationAriaLabel String to set as the aria label for the pagination bar.\n * @param {object} additionalConfig Additional config options to pass to pagedContentFactory\n */\n var init = function(root, pageLimit, preloadedPages, paginationAriaLabel, additionalConfig = {}) {\n root = $(root);\n\n courseview = !!additionalConfig.courseview;\n\n // Create a promise that will be resolved once the first set of page\n // data has been loaded. This ensures that the loading placeholder isn't\n // hidden until we have all of the data back to prevent the page elements\n // jumping around.\n var firstLoad = $.Deferred();\n var eventListContent = root.find(SELECTORS.EVENT_LIST_CONTENT);\n var loadingPlaceholder = root.find(SELECTORS.EVENT_LIST_LOADING_PLACEHOLDER);\n var courseId = root.attr('data-course-id');\n var daysOffset = parseInt(root.attr('data-days-offset'), 10);\n var daysLimit = root.attr('data-days-limit');\n var midnight = parseInt(root.attr('data-midnight'), 10);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Make sure the content area and loading placeholder is visible.\n // This is because the init function can be called to re-initialise\n // an existing event list area.\n emptyContent(root);\n showContent(root);\n loadingPlaceholder.removeClass('hidden');\n\n // Days limit isn't mandatory.\n if (daysLimit != undefined) {\n daysLimit = parseInt(daysLimit, 10);\n }\n\n // Created the paged content element.\n return createPagedContent(pageLimit, preloadedPages, midnight, firstLoad, courseId, daysOffset, daysLimit,\n paginationAriaLabel, additionalConfig, searchValue)\n .then(function(html, js) {\n html = $(html);\n // Hide the content for now.\n html.addClass('hidden');\n // Replace existing elements with the newly created paged content.\n // If we're reinitialising an existing event list this will replace\n // the old event list (including removing any event handlers).\n Templates.replaceNodeContents(eventListContent, html, js);\n\n firstLoad.then(function(hasContent) {\n // Prevent changing page elements too much by only showing the content\n // once we've loaded some data for the first time. This allows our\n // fancy loading placeholder to shine.\n html.removeClass('hidden');\n loadingPlaceholder.addClass('hidden');\n\n if (!hasContent) {\n // If we didn't get any data then show the empty data message.\n hideContent(root);\n }\n\n return hasContent;\n })\n .catch(function() {\n return false;\n });\n\n return html;\n })\n .catch(Notification.exception);\n };\n\n return {\n init: init,\n rootSelector: SELECTORS.ROOT,\n };\n});\n"],"file":"event_list.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/event_list.js"],"names":["define","$","Notification","Templates","Str","UserDate","CalendarEventsRepository","courseview","SELECTORS","EMPTY_MESSAGE","ROOT","EVENT_LIST_CONTENT","EVENT_LIST_WRAPPER","EVENT_LIST_LOADING_PLACEHOLDER","TIMELINE_BLOCK","TIMELINE_SEARCH","MORE_ACTIVITIES_BUTTON","MORE_ACTIVITIES_BUTTON_CONTAINER","TEMPLATES","LOADING_ICON","hideContent","root","find","addClass","removeClass","showContent","emptyContent","empty","buildTemplateContext","calendarEvents","eventsByDay","templateContext","eventsbyday","forEach","calendarEvent","dayTimestamp","timeusermidnight","push","Object","keys","events","render","templateName","load","midnight","limit","daysOffset","daysLimit","lastId","courseId","searchValue","endTime","args","starttime","aftereventid","endtime","searchvalue","courseid","queryByCourse","queryByTime","createLazyLoadingContent","firstLoad","itemLimit","loadEventsForLazyLoading","then","data","length","lastEventId","at","id","lastTimeStamp","resolve","hasContent","loadedAll","catch","exception","eventsPromise","result","overdueFilter","document","querySelector","filterByOverdue","getAttribute","filter","event","eventtype","getUserMidnightForTimestamp","timesort","overdue","pop","setOffset","loadMoreEvents","parseInt","attr","getOffset","eventListWrapper","closest","val","renderPromise","lastTimestamp","getLastTimestamp","html","js","remove","appendNodeContents","append","setLastTimestamp","initEventListener","disableMoreActivitiesButtonLoading","element","offset","timestamp","enableMoreActivitiesButtonLoading","loadMoreButton","prop","loadMoreButtonContainer","on","init","additionalConfig","Deferred","eventListContent","loadingPlaceholder","replaceNodeContents","rootSelector"],"mappings":"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","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 .\n\n/**\n * Javascript to load and render the list of calendar events for a\n * given day range.\n *\n * @module block_timeline/event_list\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/templates',\n 'core/str',\n 'core/user_date',\n 'block_timeline/calendar_events_repository'\n],\nfunction(\n $,\n Notification,\n Templates,\n Str,\n UserDate,\n CalendarEventsRepository\n) {\n\n var SECONDS_IN_DAY = 60 * 60 * 24;\n var courseview = false;\n\n var SELECTORS = {\n EMPTY_MESSAGE: '[data-region=\"empty-message\"]',\n ROOT: '[data-region=\"event-list-container\"]',\n EVENT_LIST_CONTENT: '[data-region=\"event-list-content\"]',\n EVENT_LIST_WRAPPER: '[data-region=\"event-list-wrapper\"]',\n EVENT_LIST_LOADING_PLACEHOLDER: '[data-region=\"event-list-loading-placeholder\"]',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-region=\"search-input\"]',\n MORE_ACTIVITIES_BUTTON: '[data-action=\"more-events\"]',\n MORE_ACTIVITIES_BUTTON_CONTAINER: '[data-region=\"more-events-button-container\"]'\n };\n\n var TEMPLATES = {\n EVENT_LIST_CONTENT: 'block_timeline/event-list-content',\n MORE_ACTIVITIES_BUTTON: 'block_timeline/event-list-loadmore',\n LOADING_ICON: 'core/loading'\n };\n\n /** @type {number} The total items will be shown on the first load. */\n const DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD = 5;\n /** @type {number} The total items will be shown when click on the Show more activities button. */\n const DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD = 10;\n\n /**\n * Hide the content area and display the empty content message.\n *\n * @param {object} root The container element\n */\n var hideContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).addClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Show the content area and hide the empty content message.\n *\n * @param {object} root The container element\n */\n var showContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).removeClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');\n };\n\n /**\n * Empty the content area.\n *\n * @param {object} root The container element\n */\n var emptyContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).empty();\n };\n\n /**\n * Construct the template context from a list of calendar events. The events\n * are grouped by which day they are on. The day is calculated from the user's\n * midnight timestamp to ensure that the calculation is timezone agnostic.\n *\n * The return data structure will look like:\n * {\n * eventsbyday: [\n * {\n * dayTimestamp: 1533744000,\n * events: [\n * { ...event 1 data... },\n * { ...event 2 data... }\n * ]\n * },\n * {\n * dayTimestamp: 1533830400,\n * events: [\n * { ...event 3 data... },\n * { ...event 4 data... }\n * ]\n * }\n * ]\n * }\n *\n * Each day timestamp is the day's midnight in the user's timezone.\n *\n * @param {array} calendarEvents List of calendar events\n * @return {object}\n */\n var buildTemplateContext = function(calendarEvents) {\n var eventsByDay = {};\n var templateContext = {\n courseview,\n eventsbyday: []\n };\n\n calendarEvents.forEach(function(calendarEvent) {\n var dayTimestamp = calendarEvent.timeusermidnight;\n if (eventsByDay[dayTimestamp]) {\n eventsByDay[dayTimestamp].push(calendarEvent);\n } else {\n eventsByDay[dayTimestamp] = [calendarEvent];\n }\n });\n\n Object.keys(eventsByDay).forEach(function(dayTimestamp) {\n var events = eventsByDay[dayTimestamp];\n templateContext.eventsbyday.push({\n dayTimestamp: dayTimestamp,\n events: events\n });\n });\n\n return templateContext;\n };\n\n /**\n * Render the HTML for the given calendar events.\n *\n * @param {array} calendarEvents A list of calendar events\n * @return {promise} Resolved with HTML and JS strings.\n */\n var render = function(calendarEvents) {\n var templateContext = buildTemplateContext(calendarEvents);\n var templateName = TEMPLATES.EVENT_LIST_CONTENT;\n\n return Templates.render(templateName, templateContext);\n };\n\n /**\n * Retrieve a list of calendar events from the server for the given\n * constraints.\n *\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {Number} limit Limit the result set to this number of items\n * @param {Number} daysOffset How many days (from midnight) to offset the results from\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to\n * @param {int|false} lastId The ID of the last seen event (if any)\n * @param {int|undefined} courseId Course ID to restrict events to\n * @param {string|undefined} searchValue Search value\n * @return {Promise} A jquery promise\n */\n var load = function(midnight, limit, daysOffset, daysLimit, lastId, courseId, searchValue) {\n var startTime = midnight + (daysOffset * SECONDS_IN_DAY);\n var endTime = daysLimit != undefined ? midnight + (daysLimit * SECONDS_IN_DAY) : false;\n\n var args = {\n starttime: startTime,\n limit: limit,\n };\n\n if (lastId) {\n args.aftereventid = lastId;\n }\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n if (courseId) {\n // If we have a course id then we only want events from that course.\n args.courseid = courseId;\n return CalendarEventsRepository.queryByCourse(args);\n } else {\n // Otherwise we want events from any course.\n return CalendarEventsRepository.queryByTime(args);\n }\n };\n\n /**\n * Create a lazy-loading region for the calendar events in the given root element.\n *\n * @param {object} root The event list container element.\n * @param {object} additionalConfig Additional config options to pass to pagedContentFactory.\n */\n var init = function(root, additionalConfig = {}) {\n root = $(root);\n\n courseview = !!additionalConfig.courseview;\n\n // Create a promise that will be resolved once the first set of page\n // data has been loaded. This ensures that the loading placeholder isn't\n // hidden until we have all of the data back to prevent the page elements\n // jumping around.\n var firstLoad = $.Deferred();\n var eventListContent = root.find(SELECTORS.EVENT_LIST_CONTENT);\n var loadingPlaceholder = root.find(SELECTORS.EVENT_LIST_LOADING_PLACEHOLDER);\n var courseId = root.attr('data-course-id');\n var daysOffset = parseInt(root.attr('data-days-offset'), 10);\n var daysLimit = root.attr('data-days-limit');\n var midnight = parseInt(root.attr('data-midnight'), 10);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Make sure the content area and loading placeholder is visible.\n // This is because the init function can be called to re-initialise\n // an existing event list area.\n emptyContent(root);\n showContent(root);\n loadingPlaceholder.removeClass('hidden');\n\n // Days limit isn't mandatory.\n if (daysLimit != undefined) {\n daysLimit = parseInt(daysLimit, 10);\n }\n\n // Create the lazy loading content element.\n return createLazyLoadingContent(root, firstLoad,\n DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD, midnight, 0, courseId, daysOffset, daysLimit, searchValue)\n .then(function(html, js) {\n firstLoad.then(function(data) {\n if (!data.hasContent) {\n loadingPlaceholder.addClass('hidden');\n // If we didn't get any data then show the empty data message.\n return hideContent(root);\n }\n\n html = $(html);\n // Hide the content for now.\n html.addClass('hidden');\n // Replace existing elements with the newly created lazy-loading region.\n Templates.replaceNodeContents(eventListContent, html, js);\n\n // Prevent changing page elements too much by only showing the content\n // once we've loaded some data for the first time. This allows our\n // fancy loading placeholder to shine.\n html.removeClass('hidden');\n loadingPlaceholder.addClass('hidden');\n\n if (!data.loadedAll) {\n Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {}).then(function(html) {\n eventListContent.append(html);\n setLastTimestamp(root, data.lastTimeStamp);\n // Init the event handler.\n initEventListener(root);\n return html;\n }).catch(function() {\n return false;\n });\n }\n\n return data;\n })\n .catch(function() {\n return false;\n });\n\n return html;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Create a lazy-loading content element for showing the event list for the initial load.\n *\n * @param {object} root The event list container element.\n * @param {object} firstLoad A jQuery promise to be resolved after the first set of data is loaded.\n * @param {int} itemLimit Limit the number of items.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {int} lastId The last event ID for each loaded page. Page number is key, id is value.\n * @param {int|undefined} courseId Course ID to restrict events to.\n * @param {Number} daysOffset How many days (from midnight) to offset the results from.\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to.\n * @param {string|undefined} searchValue Search value.\n * @return {object} jQuery promise resolved with calendar events.\n */\n const createLazyLoadingContent = (root, firstLoad, itemLimit, midnight, lastId,\n courseId, daysOffset, daysLimit, searchValue) => {\n return loadEventsForLazyLoading(\n root,\n itemLimit,\n midnight,\n lastId,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n ).then(data => {\n if (data.calendarEvents.length) {\n const lastEventId = data.calendarEvents.at(-1).id;\n const lastTimeStamp = data.calendarEvents.at(-1).timeusermidnight;\n firstLoad.resolve({\n hasContent: true,\n lastId: lastEventId,\n lastTimeStamp: lastTimeStamp,\n loadedAll: data.loadedAll\n });\n return render(data.calendarEvents, midnight);\n } else {\n firstLoad.resolve({\n hasContent: false,\n lastId: 0,\n lastTimeStamp: 0,\n loadedAll: true\n });\n return data.calendarEvents;\n }\n }).catch(Notification.exception);\n };\n\n /**\n * Handle the request from the lazy-loading region.\n * Uses the given data like course id, offset... to request the events from the server.\n *\n * @param {object} root The event list container element.\n * @param {int} itemLimit Limit the number of items.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {int} lastId The last event ID for each loaded page.\n * @param {int|undefined} courseId Course ID to restrict events to.\n * @param {Number} daysOffset How many days (from midnight) to offset the results from.\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to.\n * @param {string|undefined} searchValue Search value.\n * @return {object} jQuery promise resolved with calendar events.\n */\n const loadEventsForLazyLoading = (root, itemLimit, midnight, lastId, courseId, daysOffset, daysLimit, searchValue) => {\n // Load one more than the given limit so that we can tell if there\n // is more content to load after this.\n const eventsPromise = load(midnight, itemLimit + 1, daysOffset, daysLimit, lastId, courseId, searchValue);\n let calendarEvents = [];\n let loadedAll = true;\n\n return eventsPromise.then(result => {\n if (!result.events.length) {\n return {calendarEvents, loadedAll};\n }\n\n // Determine if the overdue filter is applied.\n const overdueFilter = document.querySelector(\"[data-filtername='overdue']\");\n const filterByOverdue = (overdueFilter && overdueFilter.getAttribute('aria-current'));\n\n calendarEvents = result.events.filter(event => {\n if (event.eventtype == 'open' || event.eventtype == 'opensubmission') {\n const dayTimestamp = UserDate.getUserMidnightForTimestamp(event.timesort, midnight);\n return dayTimestamp > midnight;\n }\n // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue.\n // This means if filtering by overdue, some events fetched might not be required (eg if due later today).\n return (!filterByOverdue || event.overdue);\n });\n\n loadedAll = calendarEvents.length <= itemLimit;\n\n if (!loadedAll) {\n // Remove the last element from the array because it isn't\n // needed in this result set.\n calendarEvents.pop();\n }\n\n if (calendarEvents.length) {\n const lastEventId = calendarEvents.at(-1).id;\n setOffset(root, lastEventId);\n }\n\n return {calendarEvents, loadedAll};\n });\n };\n\n /**\n * Load new events and append to current list.\n *\n * @param {object} root The event list container element.\n */\n const loadMoreEvents = root => {\n const midnight = parseInt(root.attr('data-midnight'), 10);\n const courseId = root.attr('data-course-id');\n const daysOffset = parseInt(root.attr('data-days-offset'), 10);\n const daysLimit = root.attr('data-days-limit');\n const lastId = getOffset(root);\n const eventListWrapper = root.find(SELECTORS.EVENT_LIST_WRAPPER);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n const eventsPromise = loadEventsForLazyLoading(\n root,\n DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD,\n midnight,\n lastId,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n );\n eventsPromise.then(data => {\n if (data.calendarEvents.length) {\n const renderPromise = render(data.calendarEvents);\n const lastTimestamp = getLastTimestamp(root);\n renderPromise.then((html, js) => {\n html = $(html);\n\n // Remove the date heading if it has the same value as the previous one.\n html.find(`[data-timestamp=\"${lastTimestamp}\"]`).remove();\n Templates.appendNodeContents(eventListWrapper, html.html(), js);\n\n if (!data.loadedAll) {\n Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {}).then(html => {\n eventListWrapper.append(html);\n setLastTimestamp(root, data.calendarEvents.at(-1).timeusermidnight);\n // Init the event handler.\n initEventListener(root);\n\n return html;\n }).catch(() => {\n return false;\n });\n }\n\n return html;\n }).catch(Notification.exception);\n }\n\n return data;\n }).then(() => {\n return disableMoreActivitiesButtonLoading(root);\n }).catch(Notification.exception);\n };\n\n /**\n * Return the offset value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @return {Number} Offset value.\n */\n const getOffset = element => {\n return parseInt(element.attr('data-lazyload-offset'), 10);\n };\n\n /**\n * Set the offset value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @param {Number} offset Offset value.\n */\n const setOffset = (element, offset) => {\n element.attr('data-lazyload-offset', offset);\n };\n\n /**\n * Return the timestamp value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @return {Number} Timestamp value.\n */\n const getLastTimestamp = element => {\n return parseInt(element.attr('data-timestamp'), 10);\n };\n\n /**\n * Set the timestamp value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @param {Number} timestamp Timestamp value.\n */\n const setLastTimestamp = (element, timestamp) => {\n element.attr('data-timestamp', timestamp);\n };\n\n /**\n * Add the \"Show more activities\" button and remove and loading spinner.\n *\n * @param {object} root The event list container element.\n */\n const enableMoreActivitiesButtonLoading = root => {\n const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);\n loadMoreButton.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {}).then(html => {\n loadMoreButton.append(html);\n return html;\n }).catch(() => {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Remove the \"Show more activities\" button and remove and loading spinner.\n *\n * @param {object} root The event list container element.\n */\n const disableMoreActivitiesButtonLoading = root => {\n const loadMoreButtonContainer = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON_CONTAINER);\n loadMoreButtonContainer.remove();\n };\n\n /**\n * Event initialise.\n *\n * @param {object} root The event list container element.\n */\n const initEventListener = root => {\n const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);\n loadMoreButton.on('click', () => {\n enableMoreActivitiesButtonLoading(root);\n loadMoreEvents(root);\n });\n };\n\n return {\n init: init,\n rootSelector: SELECTORS.ROOT,\n };\n});\n"],"file":"event_list.min.js"} \ No newline at end of file diff --git a/blocks/timeline/amd/build/view_courses.min.js b/blocks/timeline/amd/build/view_courses.min.js index 65fccfc74b3..d751f3da32c 100644 --- a/blocks/timeline/amd/build/view_courses.min.js +++ b/blocks/timeline/amd/build/view_courses.min.js @@ -1,2 +1,2 @@ -define ("block_timeline/view_courses",["jquery","core/notification","core/custom_interaction_events","core/str","core/templates","block_timeline/event_list","core_course/repository","block_timeline/calendar_events_repository"],function(a,b,c,d,e,f,g,h){var i={MORE_COURSES_BUTTON:"[data-action=\"more-courses\"]",MORE_COURSES_BUTTON_CONTAINER:"[data-region=\"more-courses-button-container\"]",NO_COURSES_EMPTY_MESSAGE:"[data-region=\"no-courses-empty-message\"]",COURSES_LIST:"[data-region=\"courses-list\"]",COURSE_ITEMS_LOADING_PLACEHOLDER:"[data-region=\"course-items-loading-placeholder\"]",COURSE_EVENTS_CONTAINER:"[data-region=\"course-events-container\"]",COURSE_NAME:"[data-region=\"course-name\"]",LOADING_ICON:".loading-icon",TIMELINE_BLOCK:"[data-region=\"timeline\"]",TIMELINE_SEARCH:"[data-region=\"search-input\"]"},j={COURSE_ITEMS:"block_timeline/course-items",LOADING_ICON:"core/loading"},k=5,l=86400,m={courseview:!0},n=function(a){a.find(i.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass("hidden")},o=function(a){a.find(i.MORE_COURSES_BUTTON_CONTAINER).addClass("hidden")},p=function(a){a.find(i.MORE_COURSES_BUTTON_CONTAINER).removeClass("hidden")},q=function(a){var b=a.find(i.MORE_COURSES_BUTTON);b.prop("disabled",!0);e.render(j.LOADING_ICON,{}).then(function(a){b.append(a);return a}).catch(function(){return!1})},r=function(a){var b=a.find(i.MORE_COURSES_BUTTON);b.prop("disabled",!1);b.find(i.LOADING_ICON).remove()},s=function(a){a.find(i.NO_COURSES_EMPTY_MESSAGE).removeClass("hidden")},t=function(a,b){var c=a.find(i.COURSES_LIST);e.appendNodeContents(c,b,"")},u=function(a){return 0b},H=function(a,b,c,d){var e=a.map(function(a){return a.id});return D(e,b,k+1,c,d)},I=function(a,b,c,d,f){return e.render(j.COURSE_ITEMS,{courses:a,midnight:c,hasdaysoffset:!0,hasdayslimit:f!=void 0,daysoffset:d,dayslimit:f,nodayslimit:f==void 0}).then(function(a){n(b);if(a){t(b,a)}else{if(!u(b)){s(b)}}return a}).then(function(c){if(a.length<2){o(b)}else{p(b)}return c}).catch(function(){n(b)})},J=function(c){var e=v(c),h=x(c);return g.getEnrolledCoursesByTimelineClassification("inprogress",h,e,"fullname asc").then(function(b){var e=Date.now(),g=b.courses,h=b.nextoffset,j=y(c),l=z(c),n=A(c),o=B(c),p=C(c),q=c.closest(i.TIMELINE_BLOCK).find(i.TIMELINE_SEARCH).val();w(c,h);var r=H(g,o,p,q),s=I(g,c,n,j,l);return a.when(r,s).then(function(b){if(G(c,e)){return b}g.forEach(function(e){var g=e.id,h=[],i=c.find("[data-region=\"course-events-container\"][data-course-id=\""+g+"\"]"),j=i.find(f.rootSelector),l=b.groupedbycourse.filter(function(a){return a.courseid==g});if(l.length){h=l[0].events}var n=a.Deferred().resolve({events:h}).promise();d.get_string("ariaeventlistpaginationnavcourses","block_timeline",e.fullnamedisplay).then(function(a){f.init(j,k,{1:n},a,m);return a}).catch(function(){f.init(j,k,{1:n},void 0,m)})});return b})}).catch(b.exception)},K=function(c){var e=Date.now(),g=B(c),h=C(c),j=c.find(i.COURSE_EVENTS_CONTAINER),l=j.map(function(){return a(this).attr("data-course-id")}).get(),n=c.closest(i.TIMELINE_BLOCK).find(i.TIMELINE_SEARCH).val();F(c,e);return D(l,g,k+1,h,n).then(function(b){if(G(c,e)){return b}j.each(function(c,e){e=a(e);var g=e.attr("data-course-id"),h=e.find(i.COURSE_NAME).text(),j=e.find(f.rootSelector),l=a.Deferred(),n=[],o=b.groupedbycourse.filter(function(a){return a.courseid==g});if(o.length){n=o[0].events}l.resolve({events:n});d.get_string("ariaeventlistpaginationnavcourses","block_timeline",h).then(function(a){f.init(j,k,{1:l.promise()},a,m);return a}).catch(function(){f.init(j,k,{1:l.promise()},void 0,m)})});return b}).catch(b.exception)},L=function(a){c.define(a,[c.events.activate]);a.on(c.events.activate,i.MORE_COURSES_BUTTON,function(b,c){q(a);J(a).then(function(){r(a)}).catch(function(){r(a)});if(c){c.originalEvent.preventDefault();c.originalEvent.stopPropagation()}b.stopPropagation()})},M=function(a){if(!a.attr("data-seen")){if(u(a)){K(a)}else{J(a)}a.attr("data-seen",!0)}};return{init:function init(b){b=a(b);F(b,Date.now());if(b.hasClass("active")){J(b);b.attr("data-seen",!0)}L(b)},reset:function reset(a){a.removeAttr("data-seen");if(a.hasClass("active")){M(a)}},shown:M}}); +define ("block_timeline/view_courses",["jquery","core/notification","core/custom_interaction_events","core/templates","block_timeline/event_list","core_course/repository","block_timeline/calendar_events_repository"],function(a,b,c,d,e,f,g){var h={MORE_COURSES_BUTTON:"[data-action=\"more-courses\"]",MORE_COURSES_BUTTON_CONTAINER:"[data-region=\"more-courses-button-container\"]",NO_COURSES_EMPTY_MESSAGE:"[data-region=\"no-courses-empty-message\"]",COURSES_LIST:"[data-region=\"courses-list\"]",COURSE_ITEMS_LOADING_PLACEHOLDER:"[data-region=\"course-items-loading-placeholder\"]",COURSE_EVENTS_CONTAINER:"[data-region=\"course-events-container\"]",COURSE_NAME:"[data-region=\"course-name\"]",LOADING_ICON:".loading-icon",TIMELINE_BLOCK:"[data-region=\"timeline\"]",TIMELINE_SEARCH:"[data-region=\"search-input\"]"},i={COURSE_ITEMS:"block_timeline/course-items",LOADING_ICON:"core/loading"},j=5,k=86400,l={courseview:!0},m=function(a){a.find(h.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass("hidden")},n=function(a){a.find(h.MORE_COURSES_BUTTON_CONTAINER).addClass("hidden")},o=function(a){a.find(h.MORE_COURSES_BUTTON_CONTAINER).removeClass("hidden")},p=function(a){var b=a.find(h.MORE_COURSES_BUTTON);b.prop("disabled",!0);d.render(i.LOADING_ICON,{}).then(function(a){b.append(a);return a}).catch(function(){return!1})},q=function(a){var b=a.find(h.MORE_COURSES_BUTTON);b.prop("disabled",!1);b.find(h.LOADING_ICON).remove()},r=function(a){a.find(h.NO_COURSES_EMPTY_MESSAGE).removeClass("hidden")},s=function(a,b){var c=a.find(h.COURSES_LIST);d.appendNodeContents(c,b,"")},t=function(a){return 0b},G=function(a,b,c,d){var e=a.map(function(a){return a.id});return C(e,b,j+1,c,d)},H=function(a,b,c,e,f){return d.render(i.COURSE_ITEMS,{courses:a,midnight:c,hasdaysoffset:!0,hasdayslimit:f!=void 0,daysoffset:e,dayslimit:f,nodayslimit:f==void 0}).then(function(a){m(b);if(a){s(b,a)}else{if(!t(b)){r(b)}}return a}).then(function(c){if(a.length<2){n(b)}else{o(b)}return c}).catch(function(){m(b)})},I=function(c){var d=u(c),g=w(c);return f.getEnrolledCoursesByTimelineClassification("inprogress",g,d,"fullname asc").then(function(b){var d=Date.now(),f=b.courses,g=b.nextoffset,i=x(c),j=y(c),k=z(c),m=A(c),n=B(c),o=c.closest(h.TIMELINE_BLOCK).find(h.TIMELINE_SEARCH).val();v(c,g);var p=G(f,m,n,o),q=H(f,c,k,i,j);return a.when(p,q).then(function(a){if(F(c,d)){return a}f.forEach(function(a){var b=a.id,d=c.find("[data-region=\"course-events-container\"][data-course-id=\""+b+"\"]"),f=d.find(e.rootSelector);e.init(f,l)});return a})}).catch(b.exception)},J=function(c){var d=Date.now(),f=A(c),g=B(c),i=c.find(h.COURSE_EVENTS_CONTAINER),k=i.map(function(){return a(this).attr("data-course-id")}).get(),m=c.closest(h.TIMELINE_BLOCK).find(h.TIMELINE_SEARCH).val();E(c,d);return C(k,f,j+1,g,m).then(function(b){if(F(c,d)){return b}i.each(function(b,c){c=a(c);var d=c.find(e.rootSelector);e.init(d,l)});return b}).catch(b.exception)},K=function(a){c.define(a,[c.events.activate]);a.on(c.events.activate,h.MORE_COURSES_BUTTON,function(b,c){p(a);I(a).then(function(){q(a)}).catch(function(){q(a)});if(c){c.originalEvent.preventDefault();c.originalEvent.stopPropagation()}b.stopPropagation()})},L=function(a){if(!a.attr("data-seen")){if(t(a)){J(a)}else{I(a)}a.attr("data-seen",!0)}};return{init:function init(b){b=a(b);E(b,Date.now());if(b.hasClass("active")){I(b);b.attr("data-seen",!0)}K(b)},reset:function reset(a){a.removeAttr("data-seen");if(a.hasClass("active")){L(a)}},shown:L}}); //# sourceMappingURL=view_courses.min.js.map diff --git a/blocks/timeline/amd/build/view_courses.min.js.map b/blocks/timeline/amd/build/view_courses.min.js.map index 265f4df7006..e287dd96adb 100644 --- a/blocks/timeline/amd/build/view_courses.min.js.map +++ b/blocks/timeline/amd/build/view_courses.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/view_courses.js"],"names":["define","$","Notification","CustomEvents","Str","Templates","EventList","CourseRepository","EventsRepository","SELECTORS","MORE_COURSES_BUTTON","MORE_COURSES_BUTTON_CONTAINER","NO_COURSES_EMPTY_MESSAGE","COURSES_LIST","COURSE_ITEMS_LOADING_PLACEHOLDER","COURSE_EVENTS_CONTAINER","COURSE_NAME","LOADING_ICON","TIMELINE_BLOCK","TIMELINE_SEARCH","TEMPLATES","COURSE_ITEMS","COURSE_EVENT_LIMIT","SECONDS_IN_DAY","additionalConfig","courseview","hideLoadingPlaceholder","root","find","addClass","hideMoreCoursesButton","showMoreCoursesButton","removeClass","enableMoreCoursesButtonLoading","button","prop","render","then","html","append","catch","disableMoreCoursesButtonLoading","remove","showNoCoursesEmptyMessage","renderCourseItemsHTML","container","appendNodeContents","hasLoadedCourses","length","getOffset","parseInt","attr","setOffset","offset","getLimit","getDaysOffset","getDaysLimit","daysLimit","getMidnight","getStartTime","midnight","daysOffset","getEndTime","getEventsForCourseIds","courseIds","startTime","limit","endTime","searchValue","args","courseids","starttime","endtime","searchvalue","queryByCourses","getEventReloadTime","data","setEventReloadTime","time","hasReloadedEventsSince","loadEventsForCourses","courses","map","course","id","updateDisplayFromCourses","hasdaysoffset","hasdayslimit","daysoffset","dayslimit","nodayslimit","loadMoreCourses","getEnrolledCoursesByTimelineClassification","result","startEventLoadingTime","Date","now","nextOffset","nextoffset","closest","val","eventsPromise","renderPromise","when","eventsByCourse","forEach","courseId","events","courseEventsContainer","eventListRoot","rootSelector","courseGroups","groupedbycourse","filter","group","courseid","pageOnePreload","Deferred","resolve","promise","get_string","fullnamedisplay","string","init","exception","reloadCourseEvents","startReloadTime","courseEventsContainers","get","each","index","courseName","text","eventListContainer","pageDeferred","registerEventListeners","activate","on","e","originalEvent","preventDefault","stopPropagation","shown","hasClass","reset","removeAttr"],"mappings":"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","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 .\n\n/**\n * Manage the timeline courses view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/str',\n 'core/templates',\n 'block_timeline/event_list',\n 'core_course/repository',\n 'block_timeline/calendar_events_repository'\n],\nfunction(\n $,\n Notification,\n CustomEvents,\n Str,\n Templates,\n EventList,\n CourseRepository,\n EventsRepository\n) {\n\n var SELECTORS = {\n MORE_COURSES_BUTTON: '[data-action=\"more-courses\"]',\n MORE_COURSES_BUTTON_CONTAINER: '[data-region=\"more-courses-button-container\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n COURSES_LIST: '[data-region=\"courses-list\"]',\n COURSE_ITEMS_LOADING_PLACEHOLDER: '[data-region=\"course-items-loading-placeholder\"]',\n COURSE_EVENTS_CONTAINER: '[data-region=\"course-events-container\"]',\n COURSE_NAME: '[data-region=\"course-name\"]',\n LOADING_ICON: '.loading-icon',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-region=\"search-input\"]'\n };\n\n var TEMPLATES = {\n COURSE_ITEMS: 'block_timeline/course-items',\n LOADING_ICON: 'core/loading'\n };\n\n var COURSE_CLASSIFICATION = 'inprogress';\n var COURSE_SORT = 'fullname asc';\n var COURSE_EVENT_LIMIT = 5;\n var COURSE_LIMIT = 2;\n var SECONDS_IN_DAY = 60 * 60 * 24;\n\n const additionalConfig = {courseview: true};\n\n /**\n * Hide the loading placeholder elements.\n *\n * @param {object} root The rool element.\n */\n var hideLoadingPlaceholder = function(root) {\n root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass('hidden');\n };\n\n /**\n * Hide the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var hideMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Show the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var showMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Disable the \"more courses\" button and show the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var enableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {})\n .then(function(html) {\n button.append(html);\n return html;\n })\n .catch(function() {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Enable the \"more courses\" button and remove the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var disableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', false);\n button.find(SELECTORS.LOADING_ICON).remove();\n };\n\n /**\n * Display the message for when there are no courses available.\n *\n * @param {object} root The rool element.\n */\n var showNoCoursesEmptyMessage = function(root) {\n root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Render the course items HTML to the page.\n *\n * @param {object} root The rool element.\n * @param {string} html The course items HTML to render.\n */\n var renderCourseItemsHTML = function(root, html) {\n var container = root.find(SELECTORS.COURSES_LIST);\n Templates.appendNodeContents(container, html, '');\n };\n\n /**\n * Check if any courses have been loaded.\n *\n * @param {object} root The rool element.\n * @return {bool}\n */\n var hasLoadedCourses = function(root) {\n return root.find(SELECTORS.COURSE_EVENTS_CONTAINER).length > 0;\n };\n\n /**\n * Return the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getOffset = function(root) {\n return parseInt(root.attr('data-offset'), 10);\n };\n\n /**\n * Set the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @param {Number} offset Offset value.\n */\n var setOffset = function(root, offset) {\n root.attr('data-offset', offset);\n };\n\n /**\n * Return the limit value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getLimit = function(root) {\n return parseInt(root.attr('data-limit'), 10);\n };\n\n /**\n * Return the days offset value for fetching events.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getDaysOffset = function(root) {\n return parseInt(root.attr('data-days-offset'), 10);\n };\n\n /**\n * Return the days limit value for fetching events. The days\n * limit is optional so undefined will be returned if it isn't\n * set.\n *\n * @param {object} root The rool element.\n * @return {int|undefined}\n */\n var getDaysLimit = function(root) {\n var daysLimit = root.attr('data-days-limit');\n return daysLimit != undefined ? parseInt(daysLimit, 10) : undefined;\n };\n\n /**\n * Return the timestamp for the user's midnight.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getMidnight = function(root) {\n return parseInt(root.attr('data-midnight'), 10);\n };\n\n /**\n * Return the start time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getStartTime = function(root) {\n var midnight = getMidnight(root);\n var daysOffset = getDaysOffset(root);\n return midnight + (daysOffset * SECONDS_IN_DAY);\n };\n\n /**\n * Return the end time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEndTime = function(root) {\n var midnight = getMidnight(root);\n var daysLimit = getDaysLimit(root);\n return daysLimit != undefined ? midnight + (daysLimit * SECONDS_IN_DAY) : false;\n };\n\n /**\n * Get a list of events for the given course ids. Returns a promise that will\n * be resolved with the events.\n *\n * @param {array} courseIds The list of course ids to fetch events for.\n * @param {Number} startTime Timestamp to fetch events from.\n * @param {Number} limit Limit to the number of events (this applies per course, not total)\n * @param {Number} endTime Timestamp to fetch events to.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise.\n */\n var getEventsForCourseIds = function(courseIds, startTime, limit, endTime, searchValue) {\n var args = {\n courseids: courseIds,\n starttime: startTime,\n limit: limit\n };\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n return EventsRepository.queryByCourses(args);\n };\n\n /**\n * Get the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEventReloadTime = function(root) {\n return root.data('last-event-load-time');\n };\n\n /**\n * Set the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n */\n var setEventReloadTime = function(root, time) {\n root.data('last-event-load-time', time);\n };\n\n /**\n * Check if events have begun reloading since the given\n * time.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n * @return {bool}\n */\n var hasReloadedEventsSince = function(root, time) {\n return getEventReloadTime(root) > time;\n };\n\n /**\n * Send a request to the server to load the events for the courses.\n *\n * @param {array} courses List of course objects.\n * @param {Number} startTime Timestamp to load events after.\n * @param {int|undefined} endTime Timestamp to load events up until.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise resolved with the events.\n */\n var loadEventsForCourses = function(courses, startTime, endTime, searchValue) {\n var courseIds = courses.map(function(course) {\n return course.id;\n });\n\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime, searchValue);\n };\n\n /**\n * Render the courses in the DOM once the server has returned the courses.\n *\n * @param {array} courses List of course objects.\n * @param {object} root The root element\n * @param {Number} midnight The midnight timestamp in the user's timezone.\n * @param {Number} daysOffset Number of days from today to offset the events.\n * @param {Number} daysLimit Number of days from today to limit the events to.\n * @return {object} jQuery promise resolved after rendering is complete.\n */\n var updateDisplayFromCourses = function(courses, root, midnight, daysOffset, daysLimit) {\n // Render the courses template.\n return Templates.render(TEMPLATES.COURSE_ITEMS, {\n courses: courses,\n midnight: midnight,\n hasdaysoffset: true,\n hasdayslimit: daysLimit != undefined,\n daysoffset: daysOffset,\n dayslimit: daysLimit,\n nodayslimit: daysLimit == undefined,\n }).then(function(html) {\n hideLoadingPlaceholder(root);\n\n if (html) {\n // Template rendering is complete and we have the HTML so we can\n // add it to the DOM.\n renderCourseItemsHTML(root, html);\n } else {\n if (!hasLoadedCourses(root)) {\n // There were no courses to render so show the empty placeholder\n // message for the user to tell them.\n showNoCoursesEmptyMessage(root);\n }\n }\n\n return html;\n })\n .then(function(html) {\n if (courses.length < COURSE_LIMIT) {\n // We know there aren't any more courses because we got back less\n // than we asked for so hide the button to request more.\n hideMoreCoursesButton(root);\n } else {\n // Make sure the button is visible if there are more courses to load.\n showMoreCoursesButton(root);\n }\n\n return html;\n })\n .catch(function() {\n hideLoadingPlaceholder(root);\n });\n };\n\n /**\n * Find all of the visible course blocks and initialise the event\n * list module to being loading the events for the course block.\n *\n * @param {object} root The root element for the timeline courses view.\n * @return {object} jQuery promise resolved with courses and events.\n */\n var loadMoreCourses = function(root) {\n var offset = getOffset(root);\n var limit = getLimit(root);\n\n // Start loading the next set of courses.\n return CourseRepository.getEnrolledCoursesByTimelineClassification(\n COURSE_CLASSIFICATION,\n limit,\n offset,\n COURSE_SORT\n ).then(function(result) {\n var startEventLoadingTime = Date.now();\n var courses = result.courses;\n var nextOffset = result.nextoffset;\n var daysOffset = getDaysOffset(root);\n var daysLimit = getDaysLimit(root);\n var midnight = getMidnight(root);\n var startTime = getStartTime(root);\n var endTime = getEndTime(root);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Record the next offset if we want to request more courses.\n setOffset(root, nextOffset);\n // Load the events for these courses.\n var eventsPromise = loadEventsForCourses(courses, startTime, endTime, searchValue);\n // Render the courses in the DOM.\n var renderPromise = updateDisplayFromCourses(courses, root, midnight, daysOffset, daysLimit);\n\n return $.when(eventsPromise, renderPromise)\n .then(function(eventsByCourse) {\n if (hasReloadedEventsSince(root, startEventLoadingTime)) {\n // All of the events are being reloaded so ignore our results.\n return eventsByCourse;\n }\n\n // When we've got all of the courses and events we can render the events in the\n // correct course event list.\n courses.forEach(function(course) {\n var courseId = course.id;\n var events = [];\n var containerSelector = '[data-region=\"course-events-container\"][data-course-id=\"' + courseId + '\"]';\n var courseEventsContainer = root.find(containerSelector);\n var eventListRoot = courseEventsContainer.find(EventList.rootSelector);\n var courseGroups = eventsByCourse.groupedbycourse.filter(function(group) {\n return group.courseid == courseId;\n });\n\n if (courseGroups.length) {\n // Get the events for this course.\n events = courseGroups[0].events;\n }\n\n // Create a preloaded page to pass to the event list because we've already\n // loaded the first page of events.\n var pageOnePreload = $.Deferred().resolve({events: events}).promise();\n // Initialise the event list pagination area for this course.\n Str.get_string('ariaeventlistpaginationnavcourses', 'block_timeline', course.fullnamedisplay)\n .then(function(string) {\n EventList.init(eventListRoot, COURSE_EVENT_LIMIT, {'1': pageOnePreload}, string, additionalConfig);\n return string;\n })\n .catch(function() {\n // An error is ok, just render with the default string.\n EventList.init(eventListRoot, COURSE_EVENT_LIMIT, {'1': pageOnePreload}, undefined,\n additionalConfig);\n });\n });\n\n return eventsByCourse;\n });\n }).catch(Notification.exception);\n };\n\n /**\n * Reload the events for all of the visible courses. These events will be loaded\n * in a single request to the server.\n *\n * @param {object} root The root element.\n * @return {object} jQuery promise resolved with courses and events.\n */\n var reloadCourseEvents = function(root) {\n var startReloadTime = Date.now();\n var startTime = getStartTime(root);\n var endTime = getEndTime(root);\n var courseEventsContainers = root.find(SELECTORS.COURSE_EVENTS_CONTAINER);\n var courseIds = courseEventsContainers.map(function() {\n return $(this).attr('data-course-id');\n }).get();\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Record when we started our request.\n setEventReloadTime(root, startReloadTime);\n\n // Load all of the events for the given courses.\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime, searchValue)\n .then(function(eventsByCourse) {\n if (hasReloadedEventsSince(root, startReloadTime)) {\n // A new reload has begun so ignore our results.\n return eventsByCourse;\n }\n\n courseEventsContainers.each(function(index, container) {\n container = $(container);\n var courseId = container.attr('data-course-id');\n var courseName = container.find(SELECTORS.COURSE_NAME).text();\n var eventListContainer = container.find(EventList.rootSelector);\n var pageDeferred = $.Deferred();\n var events = [];\n var courseGroups = eventsByCourse.groupedbycourse.filter(function(group) {\n return group.courseid == courseId;\n });\n\n if (courseGroups.length) {\n // Get the events just for this course.\n events = courseGroups[0].events;\n }\n\n pageDeferred.resolve({events: events});\n\n // Re-initialise the events list with the preloaded events we just got from\n // the server.\n Str.get_string('ariaeventlistpaginationnavcourses', 'block_timeline', courseName)\n .then(function(string) {\n EventList.init(eventListContainer, COURSE_EVENT_LIMIT, {'1': pageDeferred.promise()},\n string, additionalConfig);\n return string;\n })\n .catch(function() {\n // Ignore a failure to load the string. Just render with the default string.\n EventList.init(eventListContainer, COURSE_EVENT_LIMIT, {'1': pageDeferred.promise()},\n undefined, additionalConfig);\n });\n });\n\n return eventsByCourse;\n }).catch(Notification.exception);\n };\n\n /**\n * Add event listeners to load more courses for the courses view.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var registerEventListeners = function(root) {\n CustomEvents.define(root, [CustomEvents.events.activate]);\n // Show more courses and load their events when the user clicks the \"more courses\"\n // button.\n root.on(CustomEvents.events.activate, SELECTORS.MORE_COURSES_BUTTON, function(e, data) {\n enableMoreCoursesButtonLoading(root);\n loadMoreCourses(root)\n .then(function() {\n disableMoreCoursesButtonLoading(root);\n return;\n })\n .catch(function() {\n disableMoreCoursesButtonLoading(root);\n });\n\n if (data) {\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n }\n e.stopPropagation();\n });\n };\n\n /**\n * Initialise the timeline courses view. Begin loading the events\n * if this view is active. Add the relevant event listeners.\n *\n * This function should only be called once per page load because it\n * is adding event listeners to the page.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n\n setEventReloadTime(root, Date.now());\n\n if (root.hasClass('active')) {\n // Only load if this is active otherwise it will be lazy loaded later.\n loadMoreCourses(root);\n root.attr('data-seen', true);\n }\n\n registerEventListeners(root);\n };\n\n /**\n * Reset the element back to it's initial state. Begin loading the events again\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n root.removeAttr('data-seen');\n if (root.hasClass('active')) {\n shown(root);\n }\n };\n\n /**\n * If this is the first time this view has been displayed then begin loading\n * the events.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen')) {\n if (hasLoadedCourses(root)) {\n // This isn't the first time this view is shown so just reload the\n // events for the courses we've already loaded.\n reloadCourseEvents(root);\n } else {\n // We haven't loaded any courses yet so do that now.\n loadMoreCourses(root);\n }\n\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"file":"view_courses.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/view_courses.js"],"names":["define","$","Notification","CustomEvents","Templates","EventList","CourseRepository","EventsRepository","SELECTORS","MORE_COURSES_BUTTON","MORE_COURSES_BUTTON_CONTAINER","NO_COURSES_EMPTY_MESSAGE","COURSES_LIST","COURSE_ITEMS_LOADING_PLACEHOLDER","COURSE_EVENTS_CONTAINER","COURSE_NAME","LOADING_ICON","TIMELINE_BLOCK","TIMELINE_SEARCH","TEMPLATES","COURSE_ITEMS","COURSE_EVENT_LIMIT","SECONDS_IN_DAY","additionalConfig","courseview","hideLoadingPlaceholder","root","find","addClass","hideMoreCoursesButton","showMoreCoursesButton","removeClass","enableMoreCoursesButtonLoading","button","prop","render","then","html","append","catch","disableMoreCoursesButtonLoading","remove","showNoCoursesEmptyMessage","renderCourseItemsHTML","container","appendNodeContents","hasLoadedCourses","length","getOffset","parseInt","attr","setOffset","offset","getLimit","getDaysOffset","getDaysLimit","daysLimit","getMidnight","getStartTime","midnight","daysOffset","getEndTime","getEventsForCourseIds","courseIds","startTime","limit","endTime","searchValue","args","courseids","starttime","endtime","searchvalue","queryByCourses","getEventReloadTime","data","setEventReloadTime","time","hasReloadedEventsSince","loadEventsForCourses","courses","map","course","id","updateDisplayFromCourses","hasdaysoffset","hasdayslimit","daysoffset","dayslimit","nodayslimit","loadMoreCourses","getEnrolledCoursesByTimelineClassification","result","startEventLoadingTime","Date","now","nextOffset","nextoffset","closest","val","eventsPromise","renderPromise","when","eventsByCourse","forEach","courseId","courseEventsContainer","eventListRoot","rootSelector","init","exception","reloadCourseEvents","startReloadTime","courseEventsContainers","get","each","index","eventListContainer","registerEventListeners","events","activate","on","e","originalEvent","preventDefault","stopPropagation","shown","hasClass","reset","removeAttr"],"mappings":"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","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 .\n\n/**\n * Manage the timeline courses view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/templates',\n 'block_timeline/event_list',\n 'core_course/repository',\n 'block_timeline/calendar_events_repository'\n],\nfunction(\n $,\n Notification,\n CustomEvents,\n Templates,\n EventList,\n CourseRepository,\n EventsRepository\n) {\n\n var SELECTORS = {\n MORE_COURSES_BUTTON: '[data-action=\"more-courses\"]',\n MORE_COURSES_BUTTON_CONTAINER: '[data-region=\"more-courses-button-container\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n COURSES_LIST: '[data-region=\"courses-list\"]',\n COURSE_ITEMS_LOADING_PLACEHOLDER: '[data-region=\"course-items-loading-placeholder\"]',\n COURSE_EVENTS_CONTAINER: '[data-region=\"course-events-container\"]',\n COURSE_NAME: '[data-region=\"course-name\"]',\n LOADING_ICON: '.loading-icon',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-region=\"search-input\"]'\n };\n\n var TEMPLATES = {\n COURSE_ITEMS: 'block_timeline/course-items',\n LOADING_ICON: 'core/loading'\n };\n\n var COURSE_CLASSIFICATION = 'inprogress';\n var COURSE_SORT = 'fullname asc';\n var COURSE_EVENT_LIMIT = 5;\n var COURSE_LIMIT = 2;\n var SECONDS_IN_DAY = 60 * 60 * 24;\n\n const additionalConfig = {courseview: true};\n\n /**\n * Hide the loading placeholder elements.\n *\n * @param {object} root The rool element.\n */\n var hideLoadingPlaceholder = function(root) {\n root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass('hidden');\n };\n\n /**\n * Hide the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var hideMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Show the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var showMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Disable the \"more courses\" button and show the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var enableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {})\n .then(function(html) {\n button.append(html);\n return html;\n })\n .catch(function() {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Enable the \"more courses\" button and remove the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var disableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', false);\n button.find(SELECTORS.LOADING_ICON).remove();\n };\n\n /**\n * Display the message for when there are no courses available.\n *\n * @param {object} root The rool element.\n */\n var showNoCoursesEmptyMessage = function(root) {\n root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Render the course items HTML to the page.\n *\n * @param {object} root The rool element.\n * @param {string} html The course items HTML to render.\n */\n var renderCourseItemsHTML = function(root, html) {\n var container = root.find(SELECTORS.COURSES_LIST);\n Templates.appendNodeContents(container, html, '');\n };\n\n /**\n * Check if any courses have been loaded.\n *\n * @param {object} root The rool element.\n * @return {bool}\n */\n var hasLoadedCourses = function(root) {\n return root.find(SELECTORS.COURSE_EVENTS_CONTAINER).length > 0;\n };\n\n /**\n * Return the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getOffset = function(root) {\n return parseInt(root.attr('data-offset'), 10);\n };\n\n /**\n * Set the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @param {Number} offset Offset value.\n */\n var setOffset = function(root, offset) {\n root.attr('data-offset', offset);\n };\n\n /**\n * Return the limit value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getLimit = function(root) {\n return parseInt(root.attr('data-limit'), 10);\n };\n\n /**\n * Return the days offset value for fetching events.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getDaysOffset = function(root) {\n return parseInt(root.attr('data-days-offset'), 10);\n };\n\n /**\n * Return the days limit value for fetching events. The days\n * limit is optional so undefined will be returned if it isn't\n * set.\n *\n * @param {object} root The rool element.\n * @return {int|undefined}\n */\n var getDaysLimit = function(root) {\n var daysLimit = root.attr('data-days-limit');\n return daysLimit != undefined ? parseInt(daysLimit, 10) : undefined;\n };\n\n /**\n * Return the timestamp for the user's midnight.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getMidnight = function(root) {\n return parseInt(root.attr('data-midnight'), 10);\n };\n\n /**\n * Return the start time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getStartTime = function(root) {\n var midnight = getMidnight(root);\n var daysOffset = getDaysOffset(root);\n return midnight + (daysOffset * SECONDS_IN_DAY);\n };\n\n /**\n * Return the end time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEndTime = function(root) {\n var midnight = getMidnight(root);\n var daysLimit = getDaysLimit(root);\n return daysLimit != undefined ? midnight + (daysLimit * SECONDS_IN_DAY) : false;\n };\n\n /**\n * Get a list of events for the given course ids. Returns a promise that will\n * be resolved with the events.\n *\n * @param {array} courseIds The list of course ids to fetch events for.\n * @param {Number} startTime Timestamp to fetch events from.\n * @param {Number} limit Limit to the number of events (this applies per course, not total)\n * @param {Number} endTime Timestamp to fetch events to.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise.\n */\n var getEventsForCourseIds = function(courseIds, startTime, limit, endTime, searchValue) {\n var args = {\n courseids: courseIds,\n starttime: startTime,\n limit: limit\n };\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n return EventsRepository.queryByCourses(args);\n };\n\n /**\n * Get the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEventReloadTime = function(root) {\n return root.data('last-event-load-time');\n };\n\n /**\n * Set the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n */\n var setEventReloadTime = function(root, time) {\n root.data('last-event-load-time', time);\n };\n\n /**\n * Check if events have begun reloading since the given\n * time.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n * @return {bool}\n */\n var hasReloadedEventsSince = function(root, time) {\n return getEventReloadTime(root) > time;\n };\n\n /**\n * Send a request to the server to load the events for the courses.\n *\n * @param {array} courses List of course objects.\n * @param {Number} startTime Timestamp to load events after.\n * @param {int|undefined} endTime Timestamp to load events up until.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise resolved with the events.\n */\n var loadEventsForCourses = function(courses, startTime, endTime, searchValue) {\n var courseIds = courses.map(function(course) {\n return course.id;\n });\n\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime, searchValue);\n };\n\n /**\n * Render the courses in the DOM once the server has returned the courses.\n *\n * @param {array} courses List of course objects.\n * @param {object} root The root element\n * @param {Number} midnight The midnight timestamp in the user's timezone.\n * @param {Number} daysOffset Number of days from today to offset the events.\n * @param {Number} daysLimit Number of days from today to limit the events to.\n * @return {object} jQuery promise resolved after rendering is complete.\n */\n var updateDisplayFromCourses = function(courses, root, midnight, daysOffset, daysLimit) {\n // Render the courses template.\n return Templates.render(TEMPLATES.COURSE_ITEMS, {\n courses: courses,\n midnight: midnight,\n hasdaysoffset: true,\n hasdayslimit: daysLimit != undefined,\n daysoffset: daysOffset,\n dayslimit: daysLimit,\n nodayslimit: daysLimit == undefined,\n }).then(function(html) {\n hideLoadingPlaceholder(root);\n\n if (html) {\n // Template rendering is complete and we have the HTML so we can\n // add it to the DOM.\n renderCourseItemsHTML(root, html);\n } else {\n if (!hasLoadedCourses(root)) {\n // There were no courses to render so show the empty placeholder\n // message for the user to tell them.\n showNoCoursesEmptyMessage(root);\n }\n }\n\n return html;\n })\n .then(function(html) {\n if (courses.length < COURSE_LIMIT) {\n // We know there aren't any more courses because we got back less\n // than we asked for so hide the button to request more.\n hideMoreCoursesButton(root);\n } else {\n // Make sure the button is visible if there are more courses to load.\n showMoreCoursesButton(root);\n }\n\n return html;\n })\n .catch(function() {\n hideLoadingPlaceholder(root);\n });\n };\n\n /**\n * Find all of the visible course blocks and initialise the event\n * list module to being loading the events for the course block.\n *\n * @param {object} root The root element for the timeline courses view.\n * @return {object} jQuery promise resolved with courses and events.\n */\n var loadMoreCourses = function(root) {\n var offset = getOffset(root);\n var limit = getLimit(root);\n\n // Start loading the next set of courses.\n return CourseRepository.getEnrolledCoursesByTimelineClassification(\n COURSE_CLASSIFICATION,\n limit,\n offset,\n COURSE_SORT\n ).then(function(result) {\n var startEventLoadingTime = Date.now();\n var courses = result.courses;\n var nextOffset = result.nextoffset;\n var daysOffset = getDaysOffset(root);\n var daysLimit = getDaysLimit(root);\n var midnight = getMidnight(root);\n var startTime = getStartTime(root);\n var endTime = getEndTime(root);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Record the next offset if we want to request more courses.\n setOffset(root, nextOffset);\n // Load the events for these courses.\n var eventsPromise = loadEventsForCourses(courses, startTime, endTime, searchValue);\n // Render the courses in the DOM.\n var renderPromise = updateDisplayFromCourses(courses, root, midnight, daysOffset, daysLimit);\n\n return $.when(eventsPromise, renderPromise)\n .then(function(eventsByCourse) {\n if (hasReloadedEventsSince(root, startEventLoadingTime)) {\n // All of the events are being reloaded so ignore our results.\n return eventsByCourse;\n }\n\n // When we've got all of the courses and events we can render the events in the\n // correct course event list.\n courses.forEach(function(course) {\n var courseId = course.id;\n var containerSelector = '[data-region=\"course-events-container\"][data-course-id=\"' + courseId + '\"]';\n var courseEventsContainer = root.find(containerSelector);\n var eventListRoot = courseEventsContainer.find(EventList.rootSelector);\n\n EventList.init(eventListRoot, additionalConfig);\n });\n\n return eventsByCourse;\n });\n }).catch(Notification.exception);\n };\n\n /**\n * Reload the events for all of the visible courses. These events will be loaded\n * in a single request to the server.\n *\n * @param {object} root The root element.\n * @return {object} jQuery promise resolved with courses and events.\n */\n var reloadCourseEvents = function(root) {\n var startReloadTime = Date.now();\n var startTime = getStartTime(root);\n var endTime = getEndTime(root);\n var courseEventsContainers = root.find(SELECTORS.COURSE_EVENTS_CONTAINER);\n var courseIds = courseEventsContainers.map(function() {\n return $(this).attr('data-course-id');\n }).get();\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Record when we started our request.\n setEventReloadTime(root, startReloadTime);\n\n // Load all of the events for the given courses.\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime, searchValue)\n .then(function(eventsByCourse) {\n if (hasReloadedEventsSince(root, startReloadTime)) {\n // A new reload has begun so ignore our results.\n return eventsByCourse;\n }\n\n courseEventsContainers.each(function(index, container) {\n container = $(container);\n var eventListContainer = container.find(EventList.rootSelector);\n\n EventList.init(eventListContainer, additionalConfig);\n });\n\n return eventsByCourse;\n }).catch(Notification.exception);\n };\n\n /**\n * Add event listeners to load more courses for the courses view.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var registerEventListeners = function(root) {\n CustomEvents.define(root, [CustomEvents.events.activate]);\n // Show more courses and load their events when the user clicks the \"more courses\"\n // button.\n root.on(CustomEvents.events.activate, SELECTORS.MORE_COURSES_BUTTON, function(e, data) {\n enableMoreCoursesButtonLoading(root);\n loadMoreCourses(root)\n .then(function() {\n disableMoreCoursesButtonLoading(root);\n return;\n })\n .catch(function() {\n disableMoreCoursesButtonLoading(root);\n });\n\n if (data) {\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n }\n e.stopPropagation();\n });\n };\n\n /**\n * Initialise the timeline courses view. Begin loading the events\n * if this view is active. Add the relevant event listeners.\n *\n * This function should only be called once per page load because it\n * is adding event listeners to the page.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n\n setEventReloadTime(root, Date.now());\n\n if (root.hasClass('active')) {\n // Only load if this is active otherwise it will be lazy loaded later.\n loadMoreCourses(root);\n root.attr('data-seen', true);\n }\n\n registerEventListeners(root);\n };\n\n /**\n * Reset the element back to it's initial state. Begin loading the events again\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n root.removeAttr('data-seen');\n if (root.hasClass('active')) {\n shown(root);\n }\n };\n\n /**\n * If this is the first time this view has been displayed then begin loading\n * the events.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen')) {\n if (hasLoadedCourses(root)) {\n // This isn't the first time this view is shown so just reload the\n // events for the courses we've already loaded.\n reloadCourseEvents(root);\n } else {\n // We haven't loaded any courses yet so do that now.\n loadMoreCourses(root);\n }\n\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"file":"view_courses.min.js"} \ No newline at end of file diff --git a/blocks/timeline/amd/build/view_dates.min.js b/blocks/timeline/amd/build/view_dates.min.js index 1fbe30bbcfd..3d1de9d06a1 100644 --- a/blocks/timeline/amd/build/view_dates.min.js +++ b/blocks/timeline/amd/build/view_dates.min.js @@ -1,2 +1,2 @@ -define ("block_timeline/view_dates",["jquery","core/str","block_timeline/event_list","core/pubsub","core/paged_content_events"],function(a,b,c,d,e){var f={EVENT_LIST_CONTAINER:"[data-region=\"event-list-container\"]"},g=[5,10,25],h=function(a){var b=parseInt(a.data("limit"),10),c=!1,d=g.map(function(a){if(b==a){c=!0}return{value:a,active:b==a}});if(!c){d[0].active=!0}return d},i=function(b,c){var f=c+e.SET_ITEMS_PER_PAGE_LIMIT;d.subscribe(f,function(c){a(b).data("limit",c)})},j=function(d){var e=d.find(f.EVENT_LIST_CONTAINER),g=a(e).attr("id")+"user_block_timeline"+Math.random();i(d,g);var j=h(d),k={persistentLimitKey:"block_timeline_user_limit_preference",eventNamespace:g};b.get_string("ariaeventlistpaginationnavdates","block_timeline").then(function(a){c.init(e,j,{},a,k);return a}).catch(function(){c.init(e,j,{},"",k)})};return{init:function init(b){b=a(b);if(b.hasClass("active")){j(b);b.attr("data-seen",!0)}},reset:function reset(a){a.removeAttr("data-seen");if(a.hasClass("active")){j(a);a.attr("data-seen",!0)}},shown:function shown(a){if(!a.attr("data-seen")){j(a);a.attr("data-seen",!0)}}}}); +define ("block_timeline/view_dates",["jquery","block_timeline/event_list","core/pubsub","core/paged_content_events"],function(a,b,c,d){var e={EVENT_LIST_CONTAINER:"[data-region=\"event-list-container\"]"},f=function(b,e){var f=e+d.SET_ITEMS_PER_PAGE_LIMIT;c.subscribe(f,function(c){a(b).data("limit",c)})},g=function(c){var d=c.find(e.EVENT_LIST_CONTAINER),g=a(d).attr("id")+"user_block_timeline"+Math.random();f(c,g);b.init(d,{persistentLimitKey:"block_timeline_user_limit_preference",eventNamespace:g})};return{init:function init(b){b=a(b);if(b.hasClass("active")){g(b);b.attr("data-seen",!0)}},reset:function reset(a){a.removeAttr("data-seen");if(a.hasClass("active")){g(a);a.attr("data-seen",!0)}},shown:function shown(a){if(!a.attr("data-seen")){g(a);a.attr("data-seen",!0)}}}}); //# sourceMappingURL=view_dates.min.js.map diff --git a/blocks/timeline/amd/build/view_dates.min.js.map b/blocks/timeline/amd/build/view_dates.min.js.map index f9058d2082b..3e6a1b9afad 100644 --- a/blocks/timeline/amd/build/view_dates.min.js.map +++ b/blocks/timeline/amd/build/view_dates.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/view_dates.js"],"names":["define","$","Str","EventList","PubSub","PagedContentEvents","SELECTORS","EVENT_LIST_CONTAINER","DEFAULT_PAGE_LIMIT","getPagingLimits","root","limitPref","parseInt","data","isDefaultSet","limits","map","value","active","registerEventListeners","namespace","event","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","load","eventListContainer","find","attr","Math","random","config","persistentLimitKey","eventNamespace","get_string","then","string","init","catch","hasClass","reset","removeAttr","shown"],"mappings":"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","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 .\n\n/**\n * Manage the timeline dates view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/str',\n 'block_timeline/event_list',\n 'core/pubsub',\n 'core/paged_content_events'\n],\nfunction(\n $,\n Str,\n EventList,\n PubSub,\n PagedContentEvents\n) {\n\n var SELECTORS = {\n EVENT_LIST_CONTAINER: '[data-region=\"event-list-container\"]',\n };\n\n var DEFAULT_PAGE_LIMIT = [5, 10, 25];\n\n /**\n * Generate a paged content array of limits taking into account user preferences\n *\n * @param {object} root The root element for the timeline dates view.\n * @return {array} Array of limit objects\n */\n var getPagingLimits = function(root) {\n var limitPref = parseInt(root.data('limit'), 10);\n var isDefaultSet = false;\n var limits = DEFAULT_PAGE_LIMIT.map(function(value) {\n if (limitPref == value) {\n isDefaultSet = true;\n }\n\n return {\n value: value,\n active: limitPref == value\n };\n });\n\n if (!isDefaultSet) {\n limits[0].active = true;\n }\n\n return limits;\n };\n\n /**\n * Setup the listeners for the timeline block\n *\n * @param {string} root view dates container\n * @param {string} namespace The namespace for the paged content\n */\n var registerEventListeners = function(root, namespace) {\n var event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, function(limit) {\n $(root).data('limit', limit);\n });\n };\n\n /**\n * Initialise the event list and being loading the events.\n *\n * @param {object} root The root element for the timeline dates view.\n */\n var load = function(root) {\n var eventListContainer = root.find(SELECTORS.EVENT_LIST_CONTAINER);\n var namespace = $(eventListContainer).attr('id') + \"user_block_timeline\" + Math.random();\n registerEventListeners(root, namespace);\n\n var limits = getPagingLimits(root);\n var config = {\n persistentLimitKey: \"block_timeline_user_limit_preference\",\n eventNamespace: namespace\n };\n Str.get_string('ariaeventlistpaginationnavdates', 'block_timeline')\n .then(function(string) {\n EventList.init(eventListContainer, limits, {}, string, config);\n return string;\n })\n .catch(function() {\n // Ignore if we can't load the string. Still init the event list.\n EventList.init(eventListContainer, limits, {}, \"\", config);\n });\n };\n\n /**\n * Initialise the timeline dates view. Begin loading the events\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n if (root.hasClass('active')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Reset the view back to it's initial state. If this view is active then\n * beging loading the events.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n root.removeAttr('data-seen');\n if (root.hasClass('active')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Load the events if this is the first time the view is displayed.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"file":"view_dates.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/view_dates.js"],"names":["define","$","EventList","PubSub","PagedContentEvents","SELECTORS","EVENT_LIST_CONTAINER","registerEventListeners","root","namespace","event","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","data","load","eventListContainer","find","attr","Math","random","init","persistentLimitKey","eventNamespace","hasClass","reset","removeAttr","shown"],"mappings":"AAsBAA,OAAM,6BACN,CACI,QADJ,CAEI,2BAFJ,CAGI,aAHJ,CAII,2BAJJ,CADM,CAON,SACIC,CADJ,CAEIC,CAFJ,CAGIC,CAHJ,CAIIC,CAJJ,CAKE,IAEMC,CAAAA,CAAS,CAAG,CACZC,oBAAoB,CAAE,wCADV,CAFlB,CAYMC,CAAsB,CAAG,SAASC,CAAT,CAAeC,CAAf,CAA0B,CACnD,GAAIC,CAAAA,CAAK,CAAGD,CAAS,CAAGL,CAAkB,CAACO,wBAA3C,CACAR,CAAM,CAACS,SAAP,CAAiBF,CAAjB,CAAwB,SAASG,CAAT,CAAgB,CACpCZ,CAAC,CAACO,CAAD,CAAD,CAAQM,IAAR,CAAa,OAAb,CAAsBD,CAAtB,CACH,CAFD,CAGH,CAjBH,CAwBME,CAAI,CAAG,SAASP,CAAT,CAAe,IAClBQ,CAAAA,CAAkB,CAAGR,CAAI,CAACS,IAAL,CAAUZ,CAAS,CAACC,oBAApB,CADH,CAElBG,CAAS,CAAGR,CAAC,CAACe,CAAD,CAAD,CAAsBE,IAAtB,CAA2B,IAA3B,EAAmC,qBAAnC,CAA2DC,IAAI,CAACC,MAAL,EAFrD,CAGtBb,CAAsB,CAACC,CAAD,CAAOC,CAAP,CAAtB,CAMAP,CAAS,CAACmB,IAAV,CAAeL,CAAf,CAJa,CACTM,kBAAkB,CAAE,sCADX,CAETC,cAAc,CAAEd,CAFP,CAIb,CACH,CAlCH,CA4EE,MAAO,CACHY,IAAI,CAnCG,QAAPA,CAAAA,IAAO,CAASb,CAAT,CAAe,CACtBA,CAAI,CAAGP,CAAC,CAACO,CAAD,CAAR,CACA,GAAIA,CAAI,CAACgB,QAAL,CAAc,QAAd,CAAJ,CAA6B,CACzBT,CAAI,CAACP,CAAD,CAAJ,CACAA,CAAI,CAACU,IAAL,CAAU,WAAV,IACH,CACJ,CA4BM,CAEHO,KAAK,CAtBG,QAARA,CAAAA,KAAQ,CAASjB,CAAT,CAAe,CACvBA,CAAI,CAACkB,UAAL,CAAgB,WAAhB,EACA,GAAIlB,CAAI,CAACgB,QAAL,CAAc,QAAd,CAAJ,CAA6B,CACzBT,CAAI,CAACP,CAAD,CAAJ,CACAA,CAAI,CAACU,IAAL,CAAU,WAAV,IACH,CACJ,CAcM,CAGHS,KAAK,CAVG,QAARA,CAAAA,KAAQ,CAASnB,CAAT,CAAe,CACvB,GAAI,CAACA,CAAI,CAACU,IAAL,CAAU,WAAV,CAAL,CAA6B,CACzBH,CAAI,CAACP,CAAD,CAAJ,CACAA,CAAI,CAACU,IAAL,CAAU,WAAV,IACH,CACJ,CAEM,CAKV,CA7FK,CAAN","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 .\n\n/**\n * Manage the timeline dates view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/event_list',\n 'core/pubsub',\n 'core/paged_content_events'\n],\nfunction(\n $,\n EventList,\n PubSub,\n PagedContentEvents\n) {\n\n var SELECTORS = {\n EVENT_LIST_CONTAINER: '[data-region=\"event-list-container\"]',\n };\n\n /**\n * Setup the listeners for the timeline block\n *\n * @param {string} root view dates container\n * @param {string} namespace The namespace for the paged content\n */\n var registerEventListeners = function(root, namespace) {\n var event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, function(limit) {\n $(root).data('limit', limit);\n });\n };\n\n /**\n * Initialise the event list and being loading the events.\n *\n * @param {object} root The root element for the timeline dates view.\n */\n var load = function(root) {\n var eventListContainer = root.find(SELECTORS.EVENT_LIST_CONTAINER);\n var namespace = $(eventListContainer).attr('id') + \"user_block_timeline\" + Math.random();\n registerEventListeners(root, namespace);\n\n var config = {\n persistentLimitKey: \"block_timeline_user_limit_preference\",\n eventNamespace: namespace\n };\n EventList.init(eventListContainer, config);\n };\n\n /**\n * Initialise the timeline dates view. Begin loading the events\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n if (root.hasClass('active')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Reset the view back to it's initial state. If this view is active then\n * beging loading the events.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n root.removeAttr('data-seen');\n if (root.hasClass('active')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Load the events if this is the first time the view is displayed.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"file":"view_dates.min.js"} \ No newline at end of file diff --git a/blocks/timeline/amd/src/event_list.js b/blocks/timeline/amd/src/event_list.js index 806a76ad69b..745ba3a8ef6 100644 --- a/blocks/timeline/amd/src/event_list.js +++ b/blocks/timeline/amd/src/event_list.js @@ -26,7 +26,6 @@ define( 'jquery', 'core/notification', 'core/templates', - 'core/paged_content_factory', 'core/str', 'core/user_date', 'block_timeline/calendar_events_repository' @@ -35,7 +34,6 @@ function( $, Notification, Templates, - PagedContentFactory, Str, UserDate, CalendarEventsRepository @@ -48,24 +46,24 @@ function( EMPTY_MESSAGE: '[data-region="empty-message"]', ROOT: '[data-region="event-list-container"]', EVENT_LIST_CONTENT: '[data-region="event-list-content"]', + EVENT_LIST_WRAPPER: '[data-region="event-list-wrapper"]', EVENT_LIST_LOADING_PLACEHOLDER: '[data-region="event-list-loading-placeholder"]', TIMELINE_BLOCK: '[data-region="timeline"]', - TIMELINE_SEARCH: '[data-region="search-input"]' + TIMELINE_SEARCH: '[data-region="search-input"]', + MORE_ACTIVITIES_BUTTON: '[data-action="more-events"]', + MORE_ACTIVITIES_BUTTON_CONTAINER: '[data-region="more-events-button-container"]' }; var TEMPLATES = { - EVENT_LIST_CONTENT: 'block_timeline/event-list-content' + EVENT_LIST_CONTENT: 'block_timeline/event-list-content', + MORE_ACTIVITIES_BUTTON: 'block_timeline/event-list-loadmore', + LOADING_ICON: 'core/loading' }; - // We want the paged content controls below the paged content area - // and the controls should be ignored while data is loading. - var DEFAULT_PAGED_CONTENT_CONFIG = { - ignoreControlWhileLoading: true, - controlPlacementBottom: true, - ariaLabels: { - itemsperpagecomponents: 'ariaeventlistpagelimit, block_timeline', - } - }; + /** @type {number} The total items will be shown on the first load. */ + const DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD = 5; + /** @type {number} The total items will be shown when click on the Show more activities button. */ + const DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD = 10; /** * Hide the content area and display the empty content message. @@ -211,219 +209,12 @@ function( }; /** - * Handle a single page request from the paged content. Uses the given page data to request - * the events from the server. + * Create a lazy-loading region for the calendar events in the given root element. * - * Checks the given preloadedPages before sending a request to the server to make sure we - * don't load data unnecessarily. - * - * @param {object} pageData A single page data (see core/paged_content_pages for more info). - * @param {object} actions Paged content actions (see core/paged_content_pages for more info). - * @param {Number} midnight The user's midnight time in unix timestamp. - * @param {object} lastIds The last event ID for each loaded page. Page number is key, id is value. - * @param {object} preloadedPages An object of preloaded page data. Page number as key, data promise as value. - * @param {int|undefined} courseId Course ID to restrict events to - * @param {Number} daysOffset How many days (from midnight) to offset the results from - * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to - * @param {string|undefined} searchValue Search value - * @return {object} jQuery promise resolved with calendar events. + * @param {object} root The event list container element. + * @param {object} additionalConfig Additional config options to pass to pagedContentFactory. */ - var loadEventsFromPageData = function( - pageData, - actions, - midnight, - lastIds, - preloadedPages, - courseId, - daysOffset, - daysLimit, - searchValue - ) { - var pageNumber = pageData.pageNumber; - var limit = pageData.limit; - var lastPageNumber = pageNumber; - - // This is here to protect us if, for some reason, the pages - // are loaded out of order somehow and we don't have a reference - // to the previous page. In that case, scan back to find the most - // recent page we've seen. - while (!lastIds.hasOwnProperty(lastPageNumber)) { - lastPageNumber--; - } - // Use the last id of the most recent page. - var lastId = lastIds[lastPageNumber]; - var eventsPromise = null; - - if (preloadedPages && preloadedPages.hasOwnProperty(pageNumber)) { - // This page has been preloaded so use that rather than load the values - // again. - eventsPromise = preloadedPages[pageNumber]; - } else { - // Load one more than the given limit so that we can tell if there - // is more content to load after this. - eventsPromise = load(midnight, limit + 1, daysOffset, daysLimit, lastId, courseId, searchValue); - } - - return eventsPromise.then(function(result) { - if (!result.events.length) { - // If we didn't get any events back then tell the paged content - // that we're done loading. - actions.allItemsLoaded(pageNumber); - return []; - } - - // Determine if the overdue filter is applied. - const overdueFilter = document.querySelector("[data-filtername='overdue']"); - const filterByOverdue = (overdueFilter && overdueFilter.getAttribute('aria-current')); - - var calendarEvents = result.events.filter(function(event) { - if (event.eventtype == "open" || event.eventtype == "opensubmission") { - var dayTimestamp = UserDate.getUserMidnightForTimestamp(event.timesort, midnight); - return dayTimestamp > midnight; - } - - // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue. - // This means if filtering by overdue, some events fetched might not be required (eg if due later today). - return (!filterByOverdue || event.overdue); - }); - // We expect to receive limit + 1 events back from the server. - // Any less means there are no more events to load. - var loadedAll = calendarEvents.length <= limit; - - if (loadedAll) { - // Tell the pagination that everything is loaded. - actions.allItemsLoaded(pageNumber); - } else { - // Remove the last element from the array because it isn't - // needed in this result set. - calendarEvents.pop(); - } - - return calendarEvents; - }); - }; - - /** - * Use the paged content factory to create a paged content element for showing - * the event list. We only provide a page limit to the factory because we don't - * know exactly how many pages we'll need. This creates a paging bar with just - * next/previous buttons. - * - * This function specifies the callback for loading the event data that the user - * is requesting. - * - * @param {int|array} pageLimit A single limit or list of limits as options for the paged content - * @param {object} preloadedPages An object of preloaded page data. Page number as key, data promise as value. - * @param {Number} midnight The user's midnight time in unix timestamp. - * @param {object} firstLoad A jQuery promise to be resolved after the first set of data is loaded. - * @param {int|undefined} courseId Course ID to restrict events to - * @param {Number} daysOffset How many days (from midnight) to offset the results from - * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to - * @param {string} paginationAriaLabel String to set as the aria label for the pagination bar. - * @param {object} additionalConfig Additional config options to pass to pagedContentFactory - * @param {string|undefined} searchValue Search value - * @return {object} jQuery promise. - */ - var createPagedContent = function( - pageLimit, - preloadedPages, - midnight, - firstLoad, - courseId, - daysOffset, - daysLimit, - paginationAriaLabel, - additionalConfig, - searchValue - ) { - // Remember the last event id we loaded on each page because we can't - // use the offset value since the backend can skip events if the user doesn't - // have the capability to see them. Instead we load the next page of events - // based on the last seen event id. - var lastIds = {'1': 0}; - var hasContent = false; - var config = $.extend({}, DEFAULT_PAGED_CONTENT_CONFIG, additionalConfig); - - return Str.get_string( - 'ariaeventlistpagelimit', - 'block_timeline', - $.isArray(pageLimit) ? pageLimit[0].value : pageLimit - ) - .then(function(string) { - config.ariaLabels.itemsperpage = string; - config.ariaLabels.paginationnav = paginationAriaLabel; - return string; - }) - .then(function() { - return PagedContentFactory.createWithLimit( - pageLimit, - function(pagesData, actions) { - var promises = []; - - pagesData.forEach(function(pageData) { - var pageNumber = pageData.pageNumber; - // Load the page data. - var pagePromise = loadEventsFromPageData( - pageData, - actions, - midnight, - lastIds, - preloadedPages, - courseId, - daysOffset, - daysLimit, - searchValue - ).then(function(calendarEvents) { - if (calendarEvents.length) { - // Remember that we've loaded content. - hasContent = true; - // Remember the last id we've seen. - var lastEventId = calendarEvents[calendarEvents.length - 1].id; - // Record the id that the next page will need to start from. - lastIds[pageNumber + 1] = lastEventId; - // Get the HTML and JS for these calendar events. - return render(calendarEvents); - } else { - return calendarEvents; - } - }) - .catch(Notification.exception); - - promises.push(pagePromise); - }); - - $.when.apply($, promises).then(function() { - // Tell the calling code that the first page has been loaded - // and whether it contains any content. - firstLoad.resolve(hasContent); - return; - }) - .catch(function() { - firstLoad.resolve(hasContent); - }); - - return promises; - }, - config - ); - }); - }; - - /** - * Create a paged content region for the calendar events in the given root element. - * The content of the root element are replaced with a new paged content section - * each time this function is called. - * - * This function will be called each time the offset or limit values are changed to - * reload the event list region. - * - * @param {object} root The event list container element - * @param {int|array} pageLimit A single limit or list of limits as options for the paged content - * @param {object} preloadedPages An object of preloaded page data. Page number as key, data promise as value. - * @param {string} paginationAriaLabel String to set as the aria label for the pagination bar. - * @param {object} additionalConfig Additional config options to pass to pagedContentFactory - */ - var init = function(root, pageLimit, preloadedPages, paginationAriaLabel, additionalConfig = {}) { + var init = function(root, additionalConfig = {}) { root = $(root); courseview = !!additionalConfig.courseview; @@ -453,31 +244,42 @@ function( daysLimit = parseInt(daysLimit, 10); } - // Created the paged content element. - return createPagedContent(pageLimit, preloadedPages, midnight, firstLoad, courseId, daysOffset, daysLimit, - paginationAriaLabel, additionalConfig, searchValue) + // Create the lazy loading content element. + return createLazyLoadingContent(root, firstLoad, + DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD, midnight, 0, courseId, daysOffset, daysLimit, searchValue) .then(function(html, js) { - html = $(html); - // Hide the content for now. - html.addClass('hidden'); - // Replace existing elements with the newly created paged content. - // If we're reinitialising an existing event list this will replace - // the old event list (including removing any event handlers). - Templates.replaceNodeContents(eventListContent, html, js); + firstLoad.then(function(data) { + if (!data.hasContent) { + loadingPlaceholder.addClass('hidden'); + // If we didn't get any data then show the empty data message. + return hideContent(root); + } + + html = $(html); + // Hide the content for now. + html.addClass('hidden'); + // Replace existing elements with the newly created lazy-loading region. + Templates.replaceNodeContents(eventListContent, html, js); - firstLoad.then(function(hasContent) { // Prevent changing page elements too much by only showing the content // once we've loaded some data for the first time. This allows our // fancy loading placeholder to shine. html.removeClass('hidden'); loadingPlaceholder.addClass('hidden'); - if (!hasContent) { - // If we didn't get any data then show the empty data message. - hideContent(root); + if (!data.loadedAll) { + Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {}).then(function(html) { + eventListContent.append(html); + setLastTimestamp(root, data.lastTimeStamp); + // Init the event handler. + initEventListener(root); + return html; + }).catch(function() { + return false; + }); } - return hasContent; + return data; }) .catch(function() { return false; @@ -488,6 +290,248 @@ function( .catch(Notification.exception); }; + /** + * Create a lazy-loading content element for showing the event list for the initial load. + * + * @param {object} root The event list container element. + * @param {object} firstLoad A jQuery promise to be resolved after the first set of data is loaded. + * @param {int} itemLimit Limit the number of items. + * @param {Number} midnight The user's midnight time in unix timestamp. + * @param {int} lastId The last event ID for each loaded page. Page number is key, id is value. + * @param {int|undefined} courseId Course ID to restrict events to. + * @param {Number} daysOffset How many days (from midnight) to offset the results from. + * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to. + * @param {string|undefined} searchValue Search value. + * @return {object} jQuery promise resolved with calendar events. + */ + const createLazyLoadingContent = (root, firstLoad, itemLimit, midnight, lastId, + courseId, daysOffset, daysLimit, searchValue) => { + return loadEventsForLazyLoading( + root, + itemLimit, + midnight, + lastId, + courseId, + daysOffset, + daysLimit, + searchValue + ).then(data => { + if (data.calendarEvents.length) { + const lastEventId = data.calendarEvents.at(-1).id; + const lastTimeStamp = data.calendarEvents.at(-1).timeusermidnight; + firstLoad.resolve({ + hasContent: true, + lastId: lastEventId, + lastTimeStamp: lastTimeStamp, + loadedAll: data.loadedAll + }); + return render(data.calendarEvents, midnight); + } else { + firstLoad.resolve({ + hasContent: false, + lastId: 0, + lastTimeStamp: 0, + loadedAll: true + }); + return data.calendarEvents; + } + }).catch(Notification.exception); + }; + + /** + * Handle the request from the lazy-loading region. + * Uses the given data like course id, offset... to request the events from the server. + * + * @param {object} root The event list container element. + * @param {int} itemLimit Limit the number of items. + * @param {Number} midnight The user's midnight time in unix timestamp. + * @param {int} lastId The last event ID for each loaded page. + * @param {int|undefined} courseId Course ID to restrict events to. + * @param {Number} daysOffset How many days (from midnight) to offset the results from. + * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to. + * @param {string|undefined} searchValue Search value. + * @return {object} jQuery promise resolved with calendar events. + */ + const loadEventsForLazyLoading = (root, itemLimit, midnight, lastId, courseId, daysOffset, daysLimit, searchValue) => { + // Load one more than the given limit so that we can tell if there + // is more content to load after this. + const eventsPromise = load(midnight, itemLimit + 1, daysOffset, daysLimit, lastId, courseId, searchValue); + let calendarEvents = []; + let loadedAll = true; + + return eventsPromise.then(result => { + if (!result.events.length) { + return {calendarEvents, loadedAll}; + } + + // Determine if the overdue filter is applied. + const overdueFilter = document.querySelector("[data-filtername='overdue']"); + const filterByOverdue = (overdueFilter && overdueFilter.getAttribute('aria-current')); + + calendarEvents = result.events.filter(event => { + if (event.eventtype == 'open' || event.eventtype == 'opensubmission') { + const dayTimestamp = UserDate.getUserMidnightForTimestamp(event.timesort, midnight); + return dayTimestamp > midnight; + } + // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue. + // This means if filtering by overdue, some events fetched might not be required (eg if due later today). + return (!filterByOverdue || event.overdue); + }); + + loadedAll = calendarEvents.length <= itemLimit; + + if (!loadedAll) { + // Remove the last element from the array because it isn't + // needed in this result set. + calendarEvents.pop(); + } + + if (calendarEvents.length) { + const lastEventId = calendarEvents.at(-1).id; + setOffset(root, lastEventId); + } + + return {calendarEvents, loadedAll}; + }); + }; + + /** + * Load new events and append to current list. + * + * @param {object} root The event list container element. + */ + const loadMoreEvents = root => { + const midnight = parseInt(root.attr('data-midnight'), 10); + const courseId = root.attr('data-course-id'); + const daysOffset = parseInt(root.attr('data-days-offset'), 10); + const daysLimit = root.attr('data-days-limit'); + const lastId = getOffset(root); + const eventListWrapper = root.find(SELECTORS.EVENT_LIST_WRAPPER); + const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val(); + const eventsPromise = loadEventsForLazyLoading( + root, + DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD, + midnight, + lastId, + courseId, + daysOffset, + daysLimit, + searchValue + ); + eventsPromise.then(data => { + if (data.calendarEvents.length) { + const renderPromise = render(data.calendarEvents); + const lastTimestamp = getLastTimestamp(root); + renderPromise.then((html, js) => { + html = $(html); + + // Remove the date heading if it has the same value as the previous one. + html.find(`[data-timestamp="${lastTimestamp}"]`).remove(); + Templates.appendNodeContents(eventListWrapper, html.html(), js); + + if (!data.loadedAll) { + Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {}).then(html => { + eventListWrapper.append(html); + setLastTimestamp(root, data.calendarEvents.at(-1).timeusermidnight); + // Init the event handler. + initEventListener(root); + + return html; + }).catch(() => { + return false; + }); + } + + return html; + }).catch(Notification.exception); + } + + return data; + }).then(() => { + return disableMoreActivitiesButtonLoading(root); + }).catch(Notification.exception); + }; + + /** + * Return the offset value for lazy loading fetching. + * + * @param {object} element The event list container element. + * @return {Number} Offset value. + */ + const getOffset = element => { + return parseInt(element.attr('data-lazyload-offset'), 10); + }; + + /** + * Set the offset value for lazy loading fetching. + * + * @param {object} element The event list container element. + * @param {Number} offset Offset value. + */ + const setOffset = (element, offset) => { + element.attr('data-lazyload-offset', offset); + }; + + /** + * Return the timestamp value for lazy loading fetching. + * + * @param {object} element The event list container element. + * @return {Number} Timestamp value. + */ + const getLastTimestamp = element => { + return parseInt(element.attr('data-timestamp'), 10); + }; + + /** + * Set the timestamp value for lazy loading fetching. + * + * @param {object} element The event list container element. + * @param {Number} timestamp Timestamp value. + */ + const setLastTimestamp = (element, timestamp) => { + element.attr('data-timestamp', timestamp); + }; + + /** + * Add the "Show more activities" button and remove and loading spinner. + * + * @param {object} root The event list container element. + */ + const enableMoreActivitiesButtonLoading = root => { + const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON); + loadMoreButton.prop('disabled', true); + Templates.render(TEMPLATES.LOADING_ICON, {}).then(html => { + loadMoreButton.append(html); + return html; + }).catch(() => { + // It's not important if this false so just do so silently. + return false; + }); + }; + + /** + * Remove the "Show more activities" button and remove and loading spinner. + * + * @param {object} root The event list container element. + */ + const disableMoreActivitiesButtonLoading = root => { + const loadMoreButtonContainer = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON_CONTAINER); + loadMoreButtonContainer.remove(); + }; + + /** + * Event initialise. + * + * @param {object} root The event list container element. + */ + const initEventListener = root => { + const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON); + loadMoreButton.on('click', () => { + enableMoreActivitiesButtonLoading(root); + loadMoreEvents(root); + }); + }; + return { init: init, rootSelector: SELECTORS.ROOT, diff --git a/blocks/timeline/amd/src/view_courses.js b/blocks/timeline/amd/src/view_courses.js index 575ebf87fb6..4d965863f61 100644 --- a/blocks/timeline/amd/src/view_courses.js +++ b/blocks/timeline/amd/src/view_courses.js @@ -25,7 +25,6 @@ define( 'jquery', 'core/notification', 'core/custom_interaction_events', - 'core/str', 'core/templates', 'block_timeline/event_list', 'core_course/repository', @@ -35,7 +34,6 @@ function( $, Notification, CustomEvents, - Str, Templates, EventList, CourseRepository, @@ -424,33 +422,11 @@ function( // correct course event list. courses.forEach(function(course) { var courseId = course.id; - var events = []; var containerSelector = '[data-region="course-events-container"][data-course-id="' + courseId + '"]'; var courseEventsContainer = root.find(containerSelector); var eventListRoot = courseEventsContainer.find(EventList.rootSelector); - var courseGroups = eventsByCourse.groupedbycourse.filter(function(group) { - return group.courseid == courseId; - }); - if (courseGroups.length) { - // Get the events for this course. - events = courseGroups[0].events; - } - - // Create a preloaded page to pass to the event list because we've already - // loaded the first page of events. - var pageOnePreload = $.Deferred().resolve({events: events}).promise(); - // Initialise the event list pagination area for this course. - Str.get_string('ariaeventlistpaginationnavcourses', 'block_timeline', course.fullnamedisplay) - .then(function(string) { - EventList.init(eventListRoot, COURSE_EVENT_LIMIT, {'1': pageOnePreload}, string, additionalConfig); - return string; - }) - .catch(function() { - // An error is ok, just render with the default string. - EventList.init(eventListRoot, COURSE_EVENT_LIMIT, {'1': pageOnePreload}, undefined, - additionalConfig); - }); + EventList.init(eventListRoot, additionalConfig); }); return eventsByCourse; @@ -488,35 +464,9 @@ function( courseEventsContainers.each(function(index, container) { container = $(container); - var courseId = container.attr('data-course-id'); - var courseName = container.find(SELECTORS.COURSE_NAME).text(); var eventListContainer = container.find(EventList.rootSelector); - var pageDeferred = $.Deferred(); - var events = []; - var courseGroups = eventsByCourse.groupedbycourse.filter(function(group) { - return group.courseid == courseId; - }); - if (courseGroups.length) { - // Get the events just for this course. - events = courseGroups[0].events; - } - - pageDeferred.resolve({events: events}); - - // Re-initialise the events list with the preloaded events we just got from - // the server. - Str.get_string('ariaeventlistpaginationnavcourses', 'block_timeline', courseName) - .then(function(string) { - EventList.init(eventListContainer, COURSE_EVENT_LIMIT, {'1': pageDeferred.promise()}, - string, additionalConfig); - return string; - }) - .catch(function() { - // Ignore a failure to load the string. Just render with the default string. - EventList.init(eventListContainer, COURSE_EVENT_LIMIT, {'1': pageDeferred.promise()}, - undefined, additionalConfig); - }); + EventList.init(eventListContainer, additionalConfig); }); return eventsByCourse; diff --git a/blocks/timeline/amd/src/view_dates.js b/blocks/timeline/amd/src/view_dates.js index 03c4ccdff1a..21a98febcae 100644 --- a/blocks/timeline/amd/src/view_dates.js +++ b/blocks/timeline/amd/src/view_dates.js @@ -23,14 +23,12 @@ define( [ 'jquery', - 'core/str', 'block_timeline/event_list', 'core/pubsub', 'core/paged_content_events' ], function( $, - Str, EventList, PubSub, PagedContentEvents @@ -40,35 +38,6 @@ function( EVENT_LIST_CONTAINER: '[data-region="event-list-container"]', }; - var DEFAULT_PAGE_LIMIT = [5, 10, 25]; - - /** - * Generate a paged content array of limits taking into account user preferences - * - * @param {object} root The root element for the timeline dates view. - * @return {array} Array of limit objects - */ - var getPagingLimits = function(root) { - var limitPref = parseInt(root.data('limit'), 10); - var isDefaultSet = false; - var limits = DEFAULT_PAGE_LIMIT.map(function(value) { - if (limitPref == value) { - isDefaultSet = true; - } - - return { - value: value, - active: limitPref == value - }; - }); - - if (!isDefaultSet) { - limits[0].active = true; - } - - return limits; - }; - /** * Setup the listeners for the timeline block * @@ -92,20 +61,11 @@ function( var namespace = $(eventListContainer).attr('id') + "user_block_timeline" + Math.random(); registerEventListeners(root, namespace); - var limits = getPagingLimits(root); var config = { persistentLimitKey: "block_timeline_user_limit_preference", eventNamespace: namespace }; - Str.get_string('ariaeventlistpaginationnavdates', 'block_timeline') - .then(function(string) { - EventList.init(eventListContainer, limits, {}, string, config); - return string; - }) - .catch(function() { - // Ignore if we can't load the string. Still init the event list. - EventList.init(eventListContainer, limits, {}, "", config); - }); + EventList.init(eventListContainer, config); }; /** diff --git a/blocks/timeline/lang/en/block_timeline.php b/blocks/timeline/lang/en/block_timeline.php index 9bbef4d835b..f5f624e7ff9 100644 --- a/blocks/timeline/lang/en/block_timeline.php +++ b/blocks/timeline/lang/en/block_timeline.php @@ -25,14 +25,12 @@ $string['ariadayfilter'] = 'Filter timeline by date'; $string['ariadayfilteroption'] = '{$a} filter option'; $string['ariaeventlistitem'] = '{$a->name} activity in {$a->course} is due on {$a->date}'; -$string['ariaeventlistpagelimit'] = 'Show {$a} activities per page'; -$string['ariaeventlistpaginationnavdates'] = 'Timeline activities pagination'; -$string['ariaeventlistpaginationnavcourses'] = 'Timeline activities for course {$a} pagination'; $string['ariaviewselector'] = 'Sort timeline items'; $string['ariaviewselectoroption'] = '{$a} sort option'; $string['clearsearch'] = 'Clear search'; $string['duedate'] = 'Due date'; -$string['morecourses'] = 'More courses'; +$string['moreactivities'] = 'Show more activities'; +$string['morecourses'] = 'Show more courses'; $string['timeline:myaddinstance'] = 'Add a new timeline block to Dashboard'; $string['nocoursesinprogress'] = 'No in-progress courses'; $string['noevents'] = 'No activities require action'; @@ -51,3 +49,8 @@ $string['viewcourse'] = 'View course'; $string['privacy:metadata:timelinesortpreference'] = 'The user sort preference for the timeline block.'; $string['privacy:metadata:timelinefilterpreference'] = 'The user day filter preference for the timeline block.'; $string['privacy:metadata:timelinelimitpreference'] = 'The user page limit preference for the timeline block.'; + +// Deprecated since Moodle 4.0. +$string['ariaeventlistpagelimit'] = 'Show {$a} activities per page'; +$string['ariaeventlistpaginationnavdates'] = 'Timeline activities pagination'; +$string['ariaeventlistpaginationnavcourses'] = 'Timeline activities for course {$a} pagination'; diff --git a/blocks/timeline/lang/en/deprecated.txt b/blocks/timeline/lang/en/deprecated.txt new file mode 100644 index 00000000000..6c46d756af6 --- /dev/null +++ b/blocks/timeline/lang/en/deprecated.txt @@ -0,0 +1,3 @@ +ariaeventlistpagelimit,block_timeline +ariaeventlistpaginationnavdates,block_timeline +ariaeventlistpaginationnavcourses,block_timeline diff --git a/blocks/timeline/templates/event-list-content.mustache b/blocks/timeline/templates/event-list-content.mustache index e3df62add1d..1e6cb5dc375 100644 --- a/blocks/timeline/templates/event-list-content.mustache +++ b/blocks/timeline/templates/event-list-content.mustache @@ -63,9 +63,9 @@ ] } }} -
+
{{#eventsbyday}} -
+
{{#userdate}} {{dayTimestamp}}, {{#str}} strftimedaydate, core_langconfig {{/str}} {{/userdate}}
{{> block_timeline/event-list-items }} diff --git a/blocks/timeline/templates/event-list-loadmore.mustache b/blocks/timeline/templates/event-list-loadmore.mustache new file mode 100644 index 00000000000..365c6120b19 --- /dev/null +++ b/blocks/timeline/templates/event-list-loadmore.mustache @@ -0,0 +1,31 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template block_timeline/event-list-loadmore + + This template renders a show more activities button. + + Example context (json): + { + + } +}} +
+ +
diff --git a/blocks/timeline/templates/view-courses.mustache b/blocks/timeline/templates/view-courses.mustache index 7073e64d3b6..1749d4018ca 100644 --- a/blocks/timeline/templates/view-courses.mustache +++ b/blocks/timeline/templates/view-courses.mustache @@ -30,8 +30,8 @@
    -