Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"version":3,"file":"event_list.min.js","sources":["../src/event_list.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 * 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 <ryan@moodle.com>\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    'core/pending'\n],\nfunction(\n    $,\n    Notification,\n    Templates,\n    Str,\n    UserDate,\n    CalendarEventsRepository,\n    Pending\n) {\n\n    var SECONDS_IN_DAY = 60 * 60 * 24;\n    var courseview = false;\n\n    var SELECTORS = {\n        EMPTY_MESSAGE: '[data-region=\"no-events-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-action=\"search\"]',\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    /** @property {number} The total items will be shown on the first load. */\n    const DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD = 5;\n    /** @property {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        const pendingPromise = new Pending('block/timeline:event-init');\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, {courseview}).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            }).then(() => {\n                return pendingPromise.resolve();\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"],"names":["define","$","Notification","Templates","Str","UserDate","CalendarEventsRepository","Pending","courseview","SELECTORS","TEMPLATES","hideContent","root","find","addClass","removeClass","showContent","emptyContent","empty","render","calendarEvents","templateContext","eventsByDay","eventsbyday","forEach","calendarEvent","dayTimestamp","timeusermidnight","push","Object","keys","events","buildTemplateContext","templateName","createLazyLoadingContent","firstLoad","itemLimit","midnight","lastId","courseId","daysOffset","daysLimit","searchValue","loadEventsForLazyLoading","then","data","length","lastEventId","at","id","lastTimeStamp","resolve","hasContent","loadedAll","catch","exception","eventsPromise","limit","endTime","undefined","args","starttime","aftereventid","endtime","searchvalue","courseid","queryByCourse","queryByTime","load","result","overdueFilter","document","querySelector","filterByOverdue","getAttribute","filter","event","eventtype","getUserMidnightForTimestamp","timesort","overdue","pop","setOffset","getOffset","element","parseInt","attr","offset","getLastTimestamp","setLastTimestamp","timestamp","disableMoreActivitiesButtonLoading","remove","initEventListener","on","loadMoreButton","prop","html","append","enableMoreActivitiesButtonLoading","eventListWrapper","closest","val","renderPromise","lastTimestamp","js","appendNodeContents","loadMoreEvents","init","additionalConfig","pendingPromise","Deferred","eventListContent","loadingPlaceholder","replaceNodeContents","rootSelector"],"mappings":"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"}