Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
{"version":3,"file":"loader.min.js","sources":["../../../src/local/templates/loader.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\nimport $ from 'jquery';\nimport ajax from 'core/ajax';\nimport * as str from 'core/str';\nimport * as config from 'core/config';\nimport mustache from 'core/mustache';\nimport storage from 'core/localstorage';\nimport {getNormalisedComponent} from 'core/utils';\n\n/**\n * Template this.\n *\n * @module     core/local/templates/loader\n * @copyright  2023 Andrew Lyons <andrew@nicols.co.uk>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since      4.3\n */\nexport default class Loader {\n    /** @var {String} themeName for the current render */\n    currentThemeName = '';\n\n    /** @var {Object[]} loadTemplateBuffer - List of templates to be loaded */\n    static loadTemplateBuffer = [];\n\n    /** @var {Bool} isLoadingTemplates - Whether templates are currently being loaded */\n    static isLoadingTemplates = false;\n\n    /** @var {Map} templateCache - Cache of already loaded template strings */\n    static templateCache = new Map();\n\n    /** @var {Promise[]} templatePromises - Cache of already loaded template promises */\n    static templatePromises = {};\n\n    /** @var {Promise[]} cachePartialPromises - Cache of already loaded template partial promises */\n    static cachePartialPromises = [];\n\n    /**\n     * A helper to get the search key\n     *\n     * @param {string} theme\n     * @param {string} templateName\n     * @returns {string}\n     */\n    static getSearchKey(theme, templateName) {\n        return `${theme}/${templateName}`;\n    }\n\n    /**\n     * Load a template.\n     *\n     * @method getTemplate\n     * @param {string} templateName - should consist of the component and the name of the template like this:\n     *                              core/menu (lib/templates/menu.mustache) or\n     *                              tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache)\n     * @param {string} [themeName=config.theme] - The theme to load the template from\n     * @return {Promise} JQuery promise object resolved when the template has been fetched.\n     */\n    static getTemplate(templateName, themeName = config.theme) {\n        const searchKey = this.getSearchKey(themeName, templateName);\n\n        // If we haven't already seen this template then buffer it.\n        const cachedPromise = this.getTemplatePromiseFromCache(searchKey);\n        if (cachedPromise) {\n            return cachedPromise;\n        }\n\n        // Check the buffer to see if this template has already been added.\n        const existingBufferRecords = this.loadTemplateBuffer.filter((record) => record.searchKey === searchKey);\n        if (existingBufferRecords.length) {\n            // This template is already in the buffer so just return the existing\n            // promise. No need to add it to the buffer again.\n            return existingBufferRecords[0].deferred.promise();\n        }\n\n        // This is the first time this has been requested so let's add it to the buffer\n        // to be loaded.\n        const parts = templateName.split('/');\n        const component = getNormalisedComponent(parts.shift());\n        const name = parts.join('/');\n        const deferred = $.Deferred();\n\n        // Add this template to the buffer to be loaded.\n        this.loadTemplateBuffer.push({\n            component,\n            name,\n            theme: themeName,\n            searchKey,\n            deferred,\n        });\n\n        // We know there is at least one thing in the buffer so kick off a processing run.\n        this.processLoadTemplateBuffer();\n        return deferred.promise();\n    }\n\n    /**\n     * Store a template in the cache.\n     *\n     * @param {string} searchKey\n     * @param {string} templateSource\n     */\n    static setTemplateInCache(searchKey, templateSource) {\n        // Cache all of the dependent templates because we'll need them to render\n        // the requested template.\n        this.templateCache.set(searchKey, templateSource);\n    }\n\n    /**\n     * Fetch a template from the cache.\n     *\n     * @param {string} searchKey\n     * @returns {string}\n     */\n    static getTemplateFromCache(searchKey) {\n        return this.templateCache.get(searchKey);\n    }\n\n    /**\n     * Check whether a template is in the cache.\n     *\n     * @param {string} searchKey\n     * @returns {bool}\n     */\n    static hasTemplateInCache(searchKey) {\n        return this.templateCache.has(searchKey);\n    }\n\n    /**\n     * Prefetch a set of templates without rendering them.\n     *\n     * @param {Array} templateNames The list of templates to fetch\n     * @param {string} themeName\n     */\n    static prefetchTemplates(templateNames, themeName) {\n        templateNames.forEach((templateName) => this.prefetchTemplate(templateName, themeName));\n    }\n\n    /**\n     * Prefetech a sginle template without rendering it.\n     *\n     * @param {string} templateName\n     * @param {string} themeName\n     */\n    static prefetchTemplate(templateName, themeName) {\n        const searchKey = this.getSearchKey(themeName, templateName);\n\n        // If we haven't already seen this template then buffer it.\n        if (this.hasTemplateInCache(searchKey)) {\n            return;\n        }\n\n        // Check the buffer to see if this template has already been added.\n        const existingBufferRecords = this.loadTemplateBuffer.filter((record) => record.searchKey === searchKey);\n\n        if (existingBufferRecords.length) {\n            // This template is already in the buffer so just return the existing promise.\n            // No need to add it to the buffer again.\n            return;\n        }\n\n        // This is the first time this has been requested so let's add it to the buffer to be loaded.\n        const parts = templateName.split('/');\n        const component = getNormalisedComponent(parts.shift());\n        const name = parts.join('/');\n\n        // Add this template to the buffer to be loaded.\n        this.loadTemplateBuffer.push({\n            component,\n            name,\n            theme: themeName,\n            searchKey,\n            deferred: $.Deferred(),\n        });\n\n        this.processLoadTemplateBuffer();\n    }\n\n    /**\n     * Load a partial from the cache or ajax.\n     *\n     * @method partialHelper\n     * @param {string} name The partial name to load.\n     * @param {string} [themeName = config.theme] The theme to load the partial from.\n     * @return {string}\n     */\n    static partialHelper(name, themeName = config.theme) {\n        const searchKey = this.getSearchKey(themeName, name);\n\n        if (!this.hasTemplateInCache(searchKey)) {\n            new Error(`Failed to pre-fetch the template: ${name}`);\n        }\n        return this.getTemplateFromCache(searchKey);\n    }\n\n    /**\n     * Scan a template source for partial tags and return a list of the found partials.\n     *\n     * @method scanForPartials\n     * @param {string} templateSource - source template to scan.\n     * @return {Array} List of partials.\n     */\n    static scanForPartials(templateSource) {\n        const tokens = mustache.parse(templateSource);\n        const partials = [];\n\n        const findPartial = (tokens, partials) => {\n            let i;\n            for (i = 0; i < tokens.length; i++) {\n                const token = tokens[i];\n                if (token[0] == '>' || token[0] == '<') {\n                    partials.push(token[1]);\n                }\n                if (token.length > 4) {\n                    findPartial(token[4], partials);\n                }\n            }\n        };\n\n        findPartial(tokens, partials);\n\n        return partials;\n    }\n\n    /**\n     * Load a template and scan it for partials. Recursively fetch the partials.\n     *\n     * @method cachePartials\n     * @param {string} templateName - should consist of the component and the name of the template like this:\n     *                              core/menu (lib/templates/menu.mustache) or\n     *                              tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache)\n     * @param {string} [themeName=config.theme]\n     * @param {Array} parentage - A list of requested partials in this render chain.\n     * @return {Promise} JQuery promise object resolved when all partials are in the cache.\n     */\n    static cachePartials(templateName, themeName = config.theme, parentage = []) {\n        const searchKey = this.getSearchKey(themeName, templateName);\n\n        if (searchKey in this.cachePartialPromises) {\n            return this.cachePartialPromises[searchKey];\n        }\n\n        // This promise will not be resolved until all child partials are also resolved and ready.\n        // We create it here to allow us to check for recursive inclusion of templates.\n        // Keep track of the requested partials in this chain.\n        if (!parentage.length) {\n            parentage.push(searchKey);\n        }\n\n        this.cachePartialPromises[searchKey] = $.Deferred();\n        this._cachePartials(templateName, themeName, parentage).catch((error) => {\n            this.cachePartialPromises[searchKey].reject(error);\n        });\n\n        return this.cachePartialPromises[searchKey];\n    }\n\n    /**\n     * Cache the template partials for the specified template.\n     *\n     * @param {string} templateName\n     * @param {string} themeName\n     * @param {array} parentage\n     * @returns {promise<string>}\n     */\n    static async _cachePartials(templateName, themeName, parentage) {\n        const searchKey = this.getSearchKey(themeName, templateName);\n        const templateSource = await this.getTemplate(templateName, themeName);\n        const partials = this.scanForPartials(templateSource);\n        const uniquePartials = partials.filter((partialName) => {\n            // Check for recursion.\n            if (parentage.indexOf(`${themeName}/${partialName}`) >= 0) {\n                // Ignore templates which include a parent template already requested in the current chain.\n                return false;\n            }\n\n            // Ignore templates that include themselves.\n            return partialName !== templateName;\n        });\n\n        // Fetch any partial which has not already been fetched.\n        const fetchThemAll = uniquePartials.map((partialName) => {\n            parentage.push(`${themeName}/${partialName}`);\n            return this.cachePartials(partialName, themeName, parentage);\n        });\n\n        await Promise.all(fetchThemAll);\n        return this.cachePartialPromises[searchKey].resolve(templateSource);\n    }\n\n    /**\n     * Take all of the templates waiting in the buffer and load them from the server\n     * or from the cache.\n     *\n     * All of the templates that need to be loaded from the server will be batched up\n     * and sent in a single network request.\n     */\n    static processLoadTemplateBuffer() {\n        if (!this.loadTemplateBuffer.length) {\n            return;\n        }\n\n        if (this.isLoadingTemplates) {\n            return;\n        }\n\n        this.isLoadingTemplates = true;\n        // Grab any templates waiting in the buffer.\n        const templatesToLoad = this.loadTemplateBuffer.slice();\n        // This will be resolved with the list of promises for the server request.\n        const serverRequestsDeferred = $.Deferred();\n        const requests = [];\n        // Get a list of promises for each of the templates we need to load.\n        const templatePromises = templatesToLoad.map((templateData) => {\n            const component = getNormalisedComponent(templateData.component);\n            const name = templateData.name;\n            const searchKey = templateData.searchKey;\n            const theme = templateData.theme;\n            const templateDeferred = templateData.deferred;\n            let promise = null;\n\n            // Double check to see if this template happened to have landed in the\n            // cache as a dependency of an earlier template.\n            if (this.hasTemplateInCache(searchKey)) {\n                // We've seen this template so immediately resolve the existing promise.\n                promise = this.getTemplatePromiseFromCache(searchKey);\n            } else {\n                // We haven't seen this template yet so we need to request it from\n                // the server.\n                requests.push({\n                    methodname: 'core_output_load_template_with_dependencies',\n                    args: {\n                        component,\n                        template: name,\n                        themename: theme,\n                        lang: config.language,\n                    }\n                });\n                // Remember the index in the requests list for this template so that\n                // we can get the appropriate promise back.\n                const index = requests.length - 1;\n\n                // The server deferred will be resolved with a list of all of the promises\n                // that were sent in the order that they were added to the requests array.\n                promise = serverRequestsDeferred.promise()\n                    .then((promises) => {\n                        // The promise for this template will be the one that matches the index\n                        // for it's entry in the requests array.\n                        //\n                        // Make sure the promise is added to the promises cache for this template\n                        // search key so that we don't request it again.\n                        templatePromises[searchKey] = promises[index].then((response) => {\n                            // Process all of the template dependencies for this template and add\n                            // them to the caches so that we don't request them again later.\n                            response.templates.forEach((data) => {\n                                data.component = getNormalisedComponent(data.component);\n                                const tempSearchKey = this.getSearchKey(\n                                    theme,\n                                    [data.component, data.name].join('/'),\n                                );\n\n                                // Cache all of the dependent templates because we'll need them to render\n                                // the requested template.\n                                this.setTemplateInCache(tempSearchKey, data.value);\n\n                                if (config.templaterev > 0) {\n                                    // The template cache is enabled - set the value there.\n                                    storage.set(`core_template/${config.templaterev}:${tempSearchKey}`, data.value);\n                                }\n                            });\n\n                            if (response.strings.length) {\n                                // If we have strings that the template needs then warm the string cache\n                                // with them now so that we don't need to re-fetch them.\n                                str.cache_strings(response.strings.map(({component, name, value}) => ({\n                                    component: getNormalisedComponent(component),\n                                    key: name,\n                                    value,\n                                })));\n                            }\n\n                            // Return the original template source that the user requested.\n                            if (this.hasTemplateInCache(searchKey)) {\n                                return this.getTemplateFromCache(searchKey);\n                            }\n\n                            return null;\n                        });\n\n                        return templatePromises[searchKey];\n                    });\n            }\n\n            return promise\n                // When we've successfully loaded the template then resolve the deferred\n                // in the buffer so that all of the calling code can proceed.\n                .then((source) => templateDeferred.resolve(source))\n                .catch((error) => {\n                    // If there was an error loading the template then reject the deferred\n                    // in the buffer so that all of the calling code can proceed.\n                    templateDeferred.reject(error);\n                    // Rethrow for anyone else listening.\n                    throw error;\n                });\n        });\n\n        if (requests.length) {\n            // We have requests to send so resolve the deferred with the promises.\n            serverRequestsDeferred.resolve(ajax.call(requests, true, false, false, 0, config.templaterev));\n        } else {\n            // Nothing to load so we can resolve our deferred.\n            serverRequestsDeferred.resolve();\n        }\n\n        // Once we've finished loading all of the templates then recurse to process\n        // any templates that may have been added to the buffer in the time that we\n        // were fetching.\n        $.when.apply(null, templatePromises)\n            .then(() => {\n                // Remove the templates we've loaded from the buffer.\n                this.loadTemplateBuffer.splice(0, templatesToLoad.length);\n                this.isLoadingTemplates = false;\n                this.processLoadTemplateBuffer();\n                return;\n            })\n            .catch(() => {\n                // Remove the templates we've loaded from the buffer.\n                this.loadTemplateBuffer.splice(0, templatesToLoad.length);\n                this.isLoadingTemplates = false;\n                this.processLoadTemplateBuffer();\n            });\n    }\n\n    /**\n     * Search the various caches for a template promise for the given search key.\n     * The search key should be in the format <theme>/<component>/<template> e.g. boost/core/modal.\n     *\n     * If the template is found in any of the caches it will populate the other caches with\n     * the same data as well.\n     *\n     * @param {String} searchKey The template search key in the format <theme>/<component>/<template> e.g. boost/core/modal\n     * @returns {Object|null} jQuery promise resolved with the template source\n     */\n    static getTemplatePromiseFromCache(searchKey) {\n        // First try the cache of promises.\n        if (searchKey in this.templatePromises) {\n            return this.templatePromises[searchKey];\n        }\n\n        // Check the module cache.\n        if (this.hasTemplateInCache(searchKey)) {\n            const templateSource = this.getTemplateFromCache(searchKey);\n            // Add this to the promises cache for future.\n            this.templatePromises[searchKey] = $.Deferred().resolve(templateSource).promise();\n            return this.templatePromises[searchKey];\n        }\n\n        if (config.templaterev <= 0) {\n            // Template caching is disabled. Do not store in persistent storage.\n            return null;\n        }\n\n        // Now try local storage.\n        const cached = storage.get(`core_template/${config.templaterev}:${searchKey}`);\n        if (cached) {\n            // Add this to the module cache for future.\n            this.setTemplateInCache(searchKey, cached);\n\n            // Add to the promises cache for future.\n            this.templatePromises[searchKey] = $.Deferred().resolve(cached).promise();\n            return this.templatePromises[searchKey];\n        }\n\n        return null;\n    }\n}\n"],"names":["Loader","theme","templateName","themeName","config","searchKey","this","getSearchKey","cachedPromise","getTemplatePromiseFromCache","existingBufferRecords","loadTemplateBuffer","filter","record","length","deferred","promise","parts","split","component","shift","name","join","$","Deferred","push","processLoadTemplateBuffer","templateSource","templateCache","set","get","has","templateNames","forEach","prefetchTemplate","hasTemplateInCache","Error","getTemplateFromCache","tokens","mustache","parse","partials","findPartial","i","token","parentage","cachePartialPromises","_cachePartials","catch","error","reject","getTemplate","fetchThemAll","scanForPartials","partialName","indexOf","map","cachePartials","Promise","all","resolve","isLoadingTemplates","templatesToLoad","slice","serverRequestsDeferred","requests","templatePromises","templateData","templateDeferred","methodname","args","template","themename","lang","language","index","then","promises","response","templates","data","tempSearchKey","setTemplateInCache","value","templaterev","strings","str","cache_strings","_ref","key","source","ajax","call","when","apply","splice","cached","storage","Map"],"mappings":"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"}