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 * Fetch and render dates from timestamps.
19 * @module core/user_date
21 * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'],
25 function($, Ajax, Storage, Config) {
27 /** @var {object} promisesCache Store all promises we've seen so far. */
28 var promisesCache = {};
31 * Generate a cache key for the given request. The request should
32 * have a timestamp and format key.
34 * @param {object} request
37 var getKey = function(request) {
38 var language = $('html').attr('lang').replace(/-/g, '_');
39 return 'core_user_date/' +
41 Config.usertimezone + '/' +
42 request.timestamp + '/' +
47 * Retrieve a transformed date from the browser's storage.
52 var getFromLocalStorage = function(key) {
53 return Storage.get(key);
57 * Save the transformed date in the browser's storage.
60 * @param {string} value
62 var addToLocalStorage = function(key, value) {
63 Storage.set(key, value);
67 * Check if a key is in the module's cache.
72 var inPromisesCache = function(key) {
73 return (typeof promisesCache[key] !== 'undefined');
77 * Retrieve a promise from the module's cache.
80 * @return {object} jQuery promise
82 var getFromPromisesCache = function(key) {
83 return promisesCache[key];
87 * Save the given promise in the module's cache.
90 * @param {object} promise
92 var addToPromisesCache = function(key, promise) {
93 promisesCache[key] = promise;
97 * Send a request to the server for each of the required timestamp
98 * and format combinations.
100 * Resolves the date's deferred with the values returned from the
101 * server and saves the value in local storage.
103 * @param {array} dates
104 * @return {object} jQuery promise
106 var loadDatesFromServer = function(dates) {
107 var args = dates.map(function(data) {
109 timestamp: data.timestamp,
115 methodname: 'core_get_user_dates',
117 contextid: Config.contextid,
122 return Ajax.call([request], true, true)[0].then(function(results) {
123 results.dates.forEach(function(value, index) {
124 var date = dates[index];
125 var key = getKey(date);
127 addToLocalStorage(key, value);
128 date.deferred.resolve(value);
132 .catch(function(ex) {
133 // If we failed to retrieve the dates then reject the date's
134 // deferred objects to make sure they don't hang.
135 dates.forEach(function(date) {
136 date.deferred.reject(ex);
142 * Takes an array of request objects and returns a promise that
143 * is resolved with an array of formatted dates.
145 * The values in the returned array will be ordered the same as
148 * This function will check both the module's static promises cache
149 * and the browser's session storage to see if the user dates have
150 * already been loaded in order to avoid sending a network request
153 * Only dates not found in either cache will be sent to the server
156 * A request object must have a timestamp key and a format key.
161 * timestamp: 1293876000,
165 * timestamp: 1293876000,
166 * format: '%A, %d %B %Y, %I:%M %p'
170 * UserDate.get(request).done(function(dates) {
171 * console.log(dates[0]); // prints "1 January 2011".
172 * console.log(dates[1]); // prints "Saturday, 1 January 2011, 10:00 AM".
175 * @param {array} requests
176 * @return {object} jQuery promise
178 var get = function(requests) {
179 var ajaxRequests = [];
182 // Loop over each of the requested timestamp/format combos
183 // and add a promise to the promises array for them.
184 requests.forEach(function(request) {
185 var key = getKey(request);
187 // If we've already got a promise then use it.
188 if (inPromisesCache(key)) {
189 promises.push(getFromPromisesCache(key));
191 var deferred = $.Deferred();
192 var cached = getFromLocalStorage(key);
195 // If we were able to get the value from session storage
196 // then we can resolve the deferred with that value. No
197 // need to ask the server to transform it for us.
198 deferred.resolve(cached);
200 // Add this request to the list of ones we need to load
201 // from the server. Include the deferred so that it can
202 // be resolved when the server has responded with the
203 // transformed values.
204 request.deferred = deferred;
205 ajaxRequests.push(request);
208 // Remember this promise for next time so that we can
209 // bail out early if it is requested again.
210 addToPromisesCache(key, deferred.promise());
211 promises.push(deferred.promise());
215 // If we have any requests that we couldn't resolve from the caches
216 // then let's ask the server to get them for us.
217 if (ajaxRequests.length) {
218 loadDatesFromServer(ajaxRequests);
221 // Wait for all of the promises to resolve. Some of them may be waiting
222 // for a response from the server.
223 return $.when.apply($, promises).then(function() {
224 // This looks complicated but it's just converting an unknown
225 // length of arguments into an array for the promise to resolve
227 return arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);