Sindbad~EG File Manager
{"version":3,"file":"view_courses.min.js","sources":["../src/view_courses.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Manage the timeline courses view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie <ryan@moodle.com>\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 };\n\n var TEMPLATES = {\n COURSE_ITEMS: 'block_timeline/course-items',\n LOADING_ICON: 'core/loading'\n };\n\n var COURSE_CLASSIFICATION = 'all';\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 /**\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 * @return {object} jQuery promise.\n */\n var getEventsForCourseIds = function(courseIds, startTime, limit, endTime) {\n var args = {\n courseids: courseIds,\n starttime: startTime,\n limit: limit\n };\n\n if (endTime) {\n args.endtime = endTime;\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 * @return {object} jQuery promise resolved with the events.\n */\n var loadEventsForCourses = function(courses, startTime, endTime) {\n var courseIds = courses.map(function(course) {\n return course.id;\n });\n\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime);\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 * @param {string} noEventsURL URL for the image to display for no events.\n * @return {object} jQuery promise resolved after rendering is complete.\n */\n var updateDisplayFromCourses = function(courses, root, midnight, daysOffset, daysLimit, noEventsURL) {\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 urls: {\n noevents: noEventsURL\n }\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 var noEventsURL = root.attr('data-no-events-url');\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);\n // Render the courses in the DOM.\n var renderPromise = updateDisplayFromCourses(courses, root, midnight, daysOffset, daysLimit, noEventsURL);\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);\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});\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\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)\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()}, string);\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 });\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"],"names":["define","$","Notification","CustomEvents","Str","Templates","EventList","CourseRepository","EventsRepository","SELECTORS","TEMPLATES","hideLoadingPlaceholder","root","find","addClass","disableMoreCoursesButtonLoading","button","prop","remove","hasLoadedCourses","length","getDaysOffset","parseInt","attr","getDaysLimit","daysLimit","undefined","getMidnight","getStartTime","getEndTime","midnight","getEventsForCourseIds","courseIds","startTime","limit","endTime","args","courseids","starttime","endtime","queryByCourses","setEventReloadTime","time","data","hasReloadedEventsSince","getEventReloadTime","updateDisplayFromCourses","courses","daysOffset","noEventsURL","render","hasdaysoffset","hasdayslimit","daysoffset","dayslimit","nodayslimit","urls","noevents","then","html","container","appendNodeContents","renderCourseItemsHTML","removeClass","showNoCoursesEmptyMessage","hideMoreCoursesButton","showMoreCoursesButton","catch","loadMoreCourses","offset","getOffset","getLimit","getEnrolledCoursesByTimelineClassification","result","startEventLoadingTime","Date","now","nextOffset","nextoffset","setOffset","eventsPromise","map","course","id","COURSE_EVENT_LIMIT","loadEventsForCourses","renderPromise","when","eventsByCourse","forEach","courseId","events","containerSelector","eventListRoot","rootSelector","courseGroups","groupedbycourse","filter","group","courseid","pageOnePreload","Deferred","resolve","promise","get_string","fullnamedisplay","string","init","exception","registerEventListeners","activate","on","e","append","enableMoreCoursesButtonLoading","originalEvent","preventDefault","stopPropagation","shown","startReloadTime","courseEventsContainers","this","get","each","index","courseName","text","eventListContainer","pageDeferred","reloadCourseEvents","hasClass","reset","removeAttr"],"mappings":"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"}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists