From 6466e89946cc124ee43319cb5db1983c19b3e92d Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Thu, 12 Jul 2018 09:01:13 +0800 Subject: [PATCH] MDL-62497 javascript: rewrite core/str using ES6 syntax --- lib/amd/build/str.min.js | 2 +- lib/amd/build/str.min.js.map | 2 +- lib/amd/src/str.js | 315 +++++++++++++++-------------------- 3 files changed, 132 insertions(+), 187 deletions(-) diff --git a/lib/amd/build/str.min.js b/lib/amd/build/str.min.js index e66c6910581..45da245b37d 100644 --- a/lib/amd/build/str.min.js +++ b/lib/amd/build/str.min.js @@ -1,2 +1,2 @@ -define ("core/str",["jquery","core/ajax","core/localstorage"],function(a,b,c){var d=[];return{get_string:function get_string(a,b,c,d){var e=this.get_strings([{key:a,component:b,param:c,lang:d}]);return e.then(function(a){return a[0]})},get_strings:function get_strings(e){var f=a.Deferred(),g=[],h=0,j=!1,k;for(h=0;h.\n\n/**\n * Fetch and render language strings.\n * Hooks into the old M.str global - but can also fetch missing strings on the fly.\n *\n * @module core/str\n * @class str\n * @package core\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n// Disable no-restriced-properties because M.str is expected here:\n/* eslint-disable no-restricted-properties */\ndefine(['jquery', 'core/ajax', 'core/localstorage'], function($, ajax, storage) {\n\n var promiseCache = [];\n\n return /** @alias module:core/str */ {\n // Public variables and functions.\n /**\n * Return a promise object that will be resolved into a string eventually (maybe immediately).\n *\n * @method get_string\n * @param {string} key The language string key\n * @param {string} component The language string component\n * @param {string} param The param for variable expansion in the string.\n * @param {string} lang The users language - if not passed it is deduced.\n * @return {Promise}\n */\n // eslint-disable-next-line camelcase\n get_string: function(key, component, param, lang) {\n var request = this.get_strings([{\n key: key,\n component: component,\n param: param,\n lang: lang\n }]);\n\n return request.then(function(results) {\n return results[0];\n });\n },\n\n /**\n * Make a batch request to load a set of strings\n *\n * @method get_strings\n * @param {Object[]} requests Array of { key: key, component: component, param: param, lang: lang };\n * See get_string for more info on these args.\n * @return {Promise}\n */\n // eslint-disable-next-line camelcase\n get_strings: function(requests) {\n\n var deferred = $.Deferred();\n var results = [];\n var i = 0;\n var missing = false;\n var request;\n\n // Try from local storage. If it's there - put it in M.str and resolve it.\n\n for (i = 0; i < requests.length; i++) {\n request = requests[i];\n if (typeof request.lang === \"undefined\") {\n request.lang = $('html').attr('lang').replace(/-/g, '_');\n }\n request.cacheKey = 'core_str/' + request.key + '/' + request.component + '/' + request.lang;\n if (typeof M.str[request.component] === \"undefined\" ||\n typeof M.str[request.component][request.key] === \"undefined\") {\n // Try and revive it from local storage.\n var cached = storage.get(request.cacheKey);\n if (cached) {\n if (typeof M.str[request.component] === \"undefined\") {\n M.str[request.component] = [];\n }\n M.str[request.component][request.key] = cached;\n } else {\n // It's really not here.\n missing = true;\n }\n }\n }\n\n if (!missing) {\n // We have all the strings already.\n for (i = 0; i < requests.length; i++) {\n request = requests[i];\n\n results[i] = M.util.get_string(request.key, request.component, request.param);\n }\n deferred.resolve(results);\n } else {\n var ajaxrequests = [];\n var fetchpromises = [];\n\n // Done handler for ajax call. Must be bound to the current fetchpromise. We do this\n // to avoid creating a function in a loop.\n var doneFunc = function(str) {\n this.resolve(str);\n };\n\n var failFunc = function(reason) {\n this.reject(reason);\n };\n\n for (i = 0; i < requests.length; i++) {\n request = requests[i];\n\n // If we ever fetched this string with a promise, reuse it.\n if (typeof promiseCache[request.cacheKey] !== 'undefined') {\n fetchpromises.push(promiseCache[request.cacheKey]);\n } else {\n // Add this to the list we need to really fetch.\n var fetchpromise = $.Deferred();\n\n ajaxrequests.push({\n methodname: 'core_get_string',\n args: {\n stringid: request.key,\n component: request.component,\n lang: request.lang,\n stringparams: []\n },\n done: doneFunc.bind(fetchpromise),\n fail: failFunc.bind(fetchpromise)\n });\n\n promiseCache[request.cacheKey] = fetchpromise.promise();\n fetchpromises.push(promiseCache[request.cacheKey]);\n }\n }\n\n // Everything might already be queued so we need to check if we have real ajax requests to run.\n if (ajaxrequests.length > 0) {\n ajax.call(ajaxrequests, true, false, false, 0, M.cfg.langrev);\n }\n\n $.when.apply(null, fetchpromises).done(\n function() {\n // Turn the list of arguments (unknown length) into a real array.\n var i = 0;\n for (i = 0; i < arguments.length; i++) {\n request = requests[i];\n // Cache all the string templates.\n if (typeof M.str[request.component] === \"undefined\") {\n M.str[request.component] = [];\n }\n M.str[request.component][request.key] = arguments[i];\n storage.set('core_str/' + request.key + '/' + request.component + '/' + request.lang, arguments[i]);\n // And set the results.\n results[i] = M.util.get_string(request.key, request.component, request.param).trim();\n }\n deferred.resolve(results);\n }\n ).fail(\n function(ex) {\n deferred.reject(ex);\n }\n );\n }\n\n return deferred.promise();\n },\n /**\n * Add a list of strings to the caches.\n *\n * @method cache_strings\n * @param {Object[]} strings Array of { key: key, component: component, lang: lang, value: value }\n */\n // eslint-disable-next-line camelcase\n cache_strings: function(strings) {\n var defaultLang = $('html').attr('lang').replace(/-/g, '_');\n strings.forEach(function(string) {\n var lang = !(lang in string) ? defaultLang : string.lang;\n var key = string.key;\n var component = string.component;\n var value = string.value;\n var cacheKey = ['core_str', key, component, lang].join('/');\n\n // Check M.str caching.\n if (!(component in M.str) || !(key in M.str[component])) {\n if (!(component in M.str)) {\n M.str[component] = {};\n }\n\n M.str[component][key] = value;\n }\n\n // Check local storage.\n if (!storage.get(cacheKey)) {\n storage.set(cacheKey, value);\n }\n\n // Check the promises cache.\n if (!(cacheKey in promiseCache)) {\n promiseCache[cacheKey] = $.Deferred().resolve(value).promise();\n }\n });\n }\n };\n});\n"],"file":"str.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/str.js"],"names":["promiseCache","get_string","key","component","param","lang","get_strings","then","results","requests","requestData","pageLang","attr","replace","getCacheKey","stringPromises","map","request","cacheKey","buildReturn","promise","M","str","Promise","resolve","cached","LocalStorage","get","reject","push","methodname","args","stringid","stringparams","done","set","fail","length","Ajax","call","cfg","langrev","$","when","apply","strings","cache_strings","defaultLang","forEach","value","join","Deferred"],"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 * Fetch and render language strings.\n * Hooks into the old M.str global - but can also fetch missing strings on the fly.\n *\n * @module core/str\n * @class str\n * @package core\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\nimport $ from 'jquery';\nimport Ajax from 'core/ajax';\nimport LocalStorage from 'core/localstorage';\n\n// Module cache for the promises so that we don't make multiple\n// unnecessary requests.\nlet promiseCache = [];\n\n/**\n * Return a promise object that will be resolved into a string eventually (maybe immediately).\n *\n * @method get_string\n * @param {string} key The language string key\n * @param {string} component The language string component\n * @param {string} param The param for variable expansion in the string.\n * @param {string} lang The users language - if not passed it is deduced.\n * @return {Promise}\n */\nexport const get_string = (key, component, param, lang) => {\n return get_strings([{key, component, param, lang}])\n .then(results => results[0]);\n};\n\n/**\n * Make a batch request to load a set of strings\n *\n * @method get_strings\n * @param {Object[]} requests Array of { key: key, component: component, param: param, lang: lang };\n * See get_string for more info on these args.\n * @return {Promise}\n */\nexport const get_strings = (requests) => {\n let requestData = [];\n const pageLang = $('html').attr('lang').replace(/-/g, '_');\n // Helper function to construct the cache key.\n const getCacheKey = ({key, component, lang = pageLang}) => `core_str/${key}/${component}/${lang}`;\n\n const stringPromises = requests.map((request) => {\n const cacheKey = getCacheKey(request);\n const {component, key, lang} = request;\n // Helper function to add the promise to cache.\n const buildReturn = (promise) => {\n // Make sure the promise cache contains our promise.\n promiseCache[cacheKey] = promise;\n return promise;\n };\n\n // Check if we can serve the string straight from M.str.\n if (component in M.str && key in M.str[component]) {\n return buildReturn(new Promise((resolve) => {\n resolve(M.str[component][key]);\n }));\n }\n\n // Check if the string is in the browser's local storage.\n const cached = LocalStorage.get(cacheKey);\n if (cached) {\n M.str[component] = {...M.str[component], [key]: cached};\n return buildReturn(new Promise((resolve) => {\n resolve(M.str[component][key]);\n }));\n }\n\n // Check if we've already loaded this string from the server.\n if (cacheKey in promiseCache) {\n return buildReturn(promiseCache[cacheKey]);\n } else {\n // We're going to have to ask the server for the string so\n // add this string to the list of requests to be sent.\n return buildReturn(new Promise((resolve, reject) => {\n requestData.push({\n methodname: 'core_get_string',\n args: {\n stringid: key,\n stringparams: [],\n component,\n lang,\n },\n done: (str) => {\n // When we get the response from the server\n // we should update M.str and the browser's\n // local storage before resolving this promise.\n M.str[component] = {...M.str[component], [key]: str};\n LocalStorage.set(cacheKey, str);\n resolve(str);\n },\n fail: reject\n });\n }));\n }\n });\n\n if (requestData.length) {\n // If we need to load any strings from the server then send\n // off the request.\n Ajax.call(requestData, true, false, false, 0, M.cfg.langrev);\n }\n\n // We need to use jQuery here because some calling code uses the\n // .done handler instead of the .then handler.\n return $.when.apply($, stringPromises)\n .then((...strings) => strings);\n};\n\n/**\n * Add a list of strings to the caches.\n *\n * @method cache_strings\n * @param {Object[]} strings Array of { key: key, component: component, lang: lang, value: value }\n */\nexport const cache_strings = (strings) => {\n const defaultLang = $('html').attr('lang').replace(/-/g, '_');\n\n strings.forEach(({key, component, value, lang = defaultLang}) => {\n const cacheKey = ['core_str', key, component, lang].join('/');\n\n // Check M.str caching.\n if (!(component in M.str) || !(key in M.str[component])) {\n if (!(component in M.str)) {\n M.str[component] = {};\n }\n\n M.str[component][key] = value;\n }\n\n // Check local storage.\n if (!LocalStorage.get(cacheKey)) {\n LocalStorage.set(cacheKey, value);\n }\n\n // Check the promises cache.\n if (!(cacheKey in promiseCache)) {\n promiseCache[cacheKey] = $.Deferred().resolve(value).promise();\n }\n });\n};\n"],"file":"str.min.js"} \ No newline at end of file diff --git a/lib/amd/src/str.js b/lib/amd/src/str.js index 93ef0a18514..e0ce6eaab16 100644 --- a/lib/amd/src/str.js +++ b/lib/amd/src/str.js @@ -24,194 +24,139 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 2.9 */ -// Disable no-restriced-properties because M.str is expected here: -/* eslint-disable no-restricted-properties */ -define(['jquery', 'core/ajax', 'core/localstorage'], function($, ajax, storage) { +import $ from 'jquery'; +import Ajax from 'core/ajax'; +import LocalStorage from 'core/localstorage'; - var promiseCache = []; +// Module cache for the promises so that we don't make multiple +// unnecessary requests. +let promiseCache = []; - return /** @alias module:core/str */ { - // Public variables and functions. - /** - * Return a promise object that will be resolved into a string eventually (maybe immediately). - * - * @method get_string - * @param {string} key The language string key - * @param {string} component The language string component - * @param {string} param The param for variable expansion in the string. - * @param {string} lang The users language - if not passed it is deduced. - * @return {Promise} - */ - // eslint-disable-next-line camelcase - get_string: function(key, component, param, lang) { - var request = this.get_strings([{ - key: key, - component: component, - param: param, - lang: lang - }]); +/** + * Return a promise object that will be resolved into a string eventually (maybe immediately). + * + * @method get_string + * @param {string} key The language string key + * @param {string} component The language string component + * @param {string} param The param for variable expansion in the string. + * @param {string} lang The users language - if not passed it is deduced. + * @return {Promise} + */ +export const get_string = (key, component, param, lang) => { + return get_strings([{key, component, param, lang}]) + .then(results => results[0]); +}; - return request.then(function(results) { - return results[0]; - }); - }, +/** + * Make a batch request to load a set of strings + * + * @method get_strings + * @param {Object[]} requests Array of { key: key, component: component, param: param, lang: lang }; + * See get_string for more info on these args. + * @return {Promise} + */ +export const get_strings = (requests) => { + let requestData = []; + const pageLang = $('html').attr('lang').replace(/-/g, '_'); + // Helper function to construct the cache key. + const getCacheKey = ({key, component, lang = pageLang}) => `core_str/${key}/${component}/${lang}`; - /** - * Make a batch request to load a set of strings - * - * @method get_strings - * @param {Object[]} requests Array of { key: key, component: component, param: param, lang: lang }; - * See get_string for more info on these args. - * @return {Promise} - */ - // eslint-disable-next-line camelcase - get_strings: function(requests) { + const stringPromises = requests.map((request) => { + const cacheKey = getCacheKey(request); + const {component, key, lang} = request; + // Helper function to add the promise to cache. + const buildReturn = (promise) => { + // Make sure the promise cache contains our promise. + promiseCache[cacheKey] = promise; + return promise; + }; - var deferred = $.Deferred(); - var results = []; - var i = 0; - var missing = false; - var request; - - // Try from local storage. If it's there - put it in M.str and resolve it. - - for (i = 0; i < requests.length; i++) { - request = requests[i]; - if (typeof request.lang === "undefined") { - request.lang = $('html').attr('lang').replace(/-/g, '_'); - } - request.cacheKey = 'core_str/' + request.key + '/' + request.component + '/' + request.lang; - if (typeof M.str[request.component] === "undefined" || - typeof M.str[request.component][request.key] === "undefined") { - // Try and revive it from local storage. - var cached = storage.get(request.cacheKey); - if (cached) { - if (typeof M.str[request.component] === "undefined") { - M.str[request.component] = []; - } - M.str[request.component][request.key] = cached; - } else { - // It's really not here. - missing = true; - } - } - } - - if (!missing) { - // We have all the strings already. - for (i = 0; i < requests.length; i++) { - request = requests[i]; - - results[i] = M.util.get_string(request.key, request.component, request.param); - } - deferred.resolve(results); - } else { - var ajaxrequests = []; - var fetchpromises = []; - - // Done handler for ajax call. Must be bound to the current fetchpromise. We do this - // to avoid creating a function in a loop. - var doneFunc = function(str) { - this.resolve(str); - }; - - var failFunc = function(reason) { - this.reject(reason); - }; - - for (i = 0; i < requests.length; i++) { - request = requests[i]; - - // If we ever fetched this string with a promise, reuse it. - if (typeof promiseCache[request.cacheKey] !== 'undefined') { - fetchpromises.push(promiseCache[request.cacheKey]); - } else { - // Add this to the list we need to really fetch. - var fetchpromise = $.Deferred(); - - ajaxrequests.push({ - methodname: 'core_get_string', - args: { - stringid: request.key, - component: request.component, - lang: request.lang, - stringparams: [] - }, - done: doneFunc.bind(fetchpromise), - fail: failFunc.bind(fetchpromise) - }); - - promiseCache[request.cacheKey] = fetchpromise.promise(); - fetchpromises.push(promiseCache[request.cacheKey]); - } - } - - // Everything might already be queued so we need to check if we have real ajax requests to run. - if (ajaxrequests.length > 0) { - ajax.call(ajaxrequests, true, false, false, 0, M.cfg.langrev); - } - - $.when.apply(null, fetchpromises).done( - function() { - // Turn the list of arguments (unknown length) into a real array. - var i = 0; - for (i = 0; i < arguments.length; i++) { - request = requests[i]; - // Cache all the string templates. - if (typeof M.str[request.component] === "undefined") { - M.str[request.component] = []; - } - M.str[request.component][request.key] = arguments[i]; - storage.set('core_str/' + request.key + '/' + request.component + '/' + request.lang, arguments[i]); - // And set the results. - results[i] = M.util.get_string(request.key, request.component, request.param).trim(); - } - deferred.resolve(results); - } - ).fail( - function(ex) { - deferred.reject(ex); - } - ); - } - - return deferred.promise(); - }, - /** - * Add a list of strings to the caches. - * - * @method cache_strings - * @param {Object[]} strings Array of { key: key, component: component, lang: lang, value: value } - */ - // eslint-disable-next-line camelcase - cache_strings: function(strings) { - var defaultLang = $('html').attr('lang').replace(/-/g, '_'); - strings.forEach(function(string) { - var lang = !(lang in string) ? defaultLang : string.lang; - var key = string.key; - var component = string.component; - var value = string.value; - var cacheKey = ['core_str', key, component, lang].join('/'); - - // Check M.str caching. - if (!(component in M.str) || !(key in M.str[component])) { - if (!(component in M.str)) { - M.str[component] = {}; - } - - M.str[component][key] = value; - } - - // Check local storage. - if (!storage.get(cacheKey)) { - storage.set(cacheKey, value); - } - - // Check the promises cache. - if (!(cacheKey in promiseCache)) { - promiseCache[cacheKey] = $.Deferred().resolve(value).promise(); - } - }); + // Check if we can serve the string straight from M.str. + if (component in M.str && key in M.str[component]) { + return buildReturn(new Promise((resolve) => { + resolve(M.str[component][key]); + })); } - }; -}); + + // Check if the string is in the browser's local storage. + const cached = LocalStorage.get(cacheKey); + if (cached) { + M.str[component] = {...M.str[component], [key]: cached}; + return buildReturn(new Promise((resolve) => { + resolve(M.str[component][key]); + })); + } + + // Check if we've already loaded this string from the server. + if (cacheKey in promiseCache) { + return buildReturn(promiseCache[cacheKey]); + } else { + // We're going to have to ask the server for the string so + // add this string to the list of requests to be sent. + return buildReturn(new Promise((resolve, reject) => { + requestData.push({ + methodname: 'core_get_string', + args: { + stringid: key, + stringparams: [], + component, + lang, + }, + done: (str) => { + // When we get the response from the server + // we should update M.str and the browser's + // local storage before resolving this promise. + M.str[component] = {...M.str[component], [key]: str}; + LocalStorage.set(cacheKey, str); + resolve(str); + }, + fail: reject + }); + })); + } + }); + + if (requestData.length) { + // If we need to load any strings from the server then send + // off the request. + Ajax.call(requestData, true, false, false, 0, M.cfg.langrev); + } + + // We need to use jQuery here because some calling code uses the + // .done handler instead of the .then handler. + return $.when.apply($, stringPromises) + .then((...strings) => strings); +}; + +/** + * Add a list of strings to the caches. + * + * @method cache_strings + * @param {Object[]} strings Array of { key: key, component: component, lang: lang, value: value } + */ +export const cache_strings = (strings) => { + const defaultLang = $('html').attr('lang').replace(/-/g, '_'); + + strings.forEach(({key, component, value, lang = defaultLang}) => { + const cacheKey = ['core_str', key, component, lang].join('/'); + + // Check M.str caching. + if (!(component in M.str) || !(key in M.str[component])) { + if (!(component in M.str)) { + M.str[component] = {}; + } + + M.str[component][key] = value; + } + + // Check local storage. + if (!LocalStorage.get(cacheKey)) { + LocalStorage.set(cacheKey, value); + } + + // Check the promises cache. + if (!(cacheKey in promiseCache)) { + promiseCache[cacheKey] = $.Deferred().resolve(value).promise(); + } + }); +};