1 // This file is part of Moodle - http://moodle.org/
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 * Lazy loaded list of items.
19 * @module core_message/message_drawer_lazy_load_list
21 * @copyright 2018 Ryan Wyllie <ryan@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 'core/custom_interaction_events'
35 ROOT: '[data-region="lazy-load-list"]',
36 LOADING_ICON_CONTAINER: '[data-region="loading-icon-container"]',
37 CONTENT_CONTAINER: '[data-region="content-container"]',
38 EMPTY_MESSAGE: '[data-region="empty-message-container"]',
39 PLACEHOLDER: '[data-region="placeholder-container"]'
43 * Flag element as loading.
45 * @param {Object} root The section container element.
47 var startLoading = function(root) {
48 root.attr('data-loading', true);
52 * Flag element as not loading.
54 * @param {Object} root The section container element.
56 var stopLoading = function(root) {
57 root.attr('data-loading', false);
61 * Check if the element is loading.
63 * @param {Object} root The section container element.
66 var isLoading = function(root) {
67 return root.attr('data-loading') === 'true';
73 * @param {Object} root The section container element.
74 * @return {Number} Logged in user id.
76 var getUserId = function(root) {
77 return root.attr('data-user-id');
81 * Get the section content container element.
83 * @param {Object} root The section container element.
84 * @return {Object} The section content container element.
86 var getContentContainer = function(root) {
87 return root.find(SELECTORS.CONTENT_CONTAINER);
91 * Get the root element.
93 * @param {Object} containerElement The container element to search in.
94 * @return {Object} The list root element.
96 var getRoot = function(containerElement) {
97 return containerElement.find(SELECTORS.ROOT);
101 * Show the loading icon.
103 * @param {Object} root The section container element.
105 var showLoadingIcon = function(root) {
106 root.find(SELECTORS.LOADING_ICON_CONTAINER).removeClass('hidden');
110 * Hide the loading icon.
112 * @param {Object} root The section container element.
114 var hideLoadingIcon = function(root) {
115 root.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');
119 * Show the empty message.
121 * @param {Object} root The section container element.
123 var showEmptyMessage = function(root) {
124 root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');
128 * Hide the empty message.
130 * @param {Object} root The section container element.
132 var hideEmptyMessage = function(root) {
133 root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');
137 * Show the placeholder element.
139 * @param {Object} root The section container element.
141 var showPlaceholder = function(root) {
142 root.find(SELECTORS.PLACEHOLDER).removeClass('hidden');
146 * Hide the placeholder element.
148 * @param {Object} root The section container element.
150 var hidePlaceholder = function(root) {
151 root.find(SELECTORS.PLACEHOLDER).addClass('hidden');
155 * Show the section content container.
157 * @param {Object} root The section container element.
159 var showContent = function(root) {
160 getContentContainer(root).removeClass('hidden');
164 * Hide the section content container.
166 * @param {Object} root The section container element.
168 var hideContent = function(root) {
169 getContentContainer(root).addClass('hidden');
173 * If the section has loaded all content.
175 * @param {Object} root The section container element.
178 var hasLoadedAll = function(root) {
179 return root.attr('data-loaded-all') == 'true';
183 * If the section has loaded all content.
185 * @param {Object} root The section container element.
186 * @param {Bool} value If all items have been loaded.
188 var setLoadedAll = function(root, value) {
189 root.attr('data-loaded-all', value);
193 * If the section can load more items.
195 * @param {Object} root The section container element.
198 var canLoadMore = function(root) {
199 return !hasLoadedAll(root) && !isLoading(root);
203 * Load all items in this container from callback and render them.
205 * @param {Object} root The section container element.
206 * @param {Function} loadCallback The callback to load items.
207 * @param {Function} renderCallback The callback to render the results.
208 * @return {Object} jQuery promise
210 var loadAndRender = function(root, loadCallback, renderCallback) {
211 var userId = getUserId(root);
214 return loadCallback(root, userId)
215 .then(function(items) {
216 if (items.length > 0) {
217 var contentContainer = getContentContainer(root);
218 return renderCallback(contentContainer, items, userId)
226 .then(function(items) {
228 root.attr('data-seen', true);
231 setLoadedAll(root, true);
238 root.attr('data-seen', true);
244 * First load of this section.
246 * @param {Object} root The section container element.
247 * @param {Function} loadCallback The callback to load items.
248 * @param {Function} renderCallback The callback to render the results.
249 * @return {Object} promise
251 var initialLoadAndRender = function(root, loadCallback, renderCallback) {
252 getContentContainer(root).empty();
253 showPlaceholder(root);
255 return loadAndRender(root, loadCallback, renderCallback)
256 .then(function(items) {
257 hidePlaceholder(root);
260 showEmptyMessage(root);
268 hidePlaceholder(root);
275 * Listen to, and handle events in this section.
277 * @param {Object} root The section container element.
278 * @param {Function} loadCallback The callback to load items.
279 * @param {Function} renderCallback The callback to render the results.
281 var registerEventListeners = function(root, loadCallback, renderCallback) {
282 CustomEvents.define(root, [
283 CustomEvents.events.scrollBottom
286 root.on(CustomEvents.events.scrollBottom, function() {
287 if (canLoadMore(root)) {
288 showLoadingIcon(root);
289 loadAndRender(root, loadCallback, renderCallback)
291 return hideLoadingIcon(root);
294 return hideLoadingIcon(root);
303 * @param {Object} root The section container element.
304 * @param {Function} loadCallback The callback to load items.
305 * @param {Function} renderCallback The callback to render the results.
307 var show = function(root, loadCallback, renderCallback) {
310 if (!root.attr('data-init')) {
311 registerEventListeners(root, loadCallback, renderCallback);
312 initialLoadAndRender(root, loadCallback, renderCallback);
313 root.attr('data-init', true);
319 getContentContainer: getContentContainer,
321 setLoadedAll: setLoadedAll,
322 showEmptyMessage: showEmptyMessage,
323 hideEmptyMessage: hideEmptyMessage,
324 showContent: showContent,
325 hideContent: hideContent