Файловый менеджер - Редактировать - /home/harasnat/www/learning/lib/amd/src/local/templates/loader.js
Назад
// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. import $ from 'jquery'; import ajax from 'core/ajax'; import * as str from 'core/str'; import * as config from 'core/config'; import mustache from 'core/mustache'; import storage from 'core/localstorage'; import {getNormalisedComponent} from 'core/utils'; /** * Template this. * * @module core/local/templates/loader * @copyright 2023 Andrew Lyons <andrew@nicols.co.uk> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.3 */ export default class Loader { /** @var {String} themeName for the current render */ currentThemeName = ''; /** @var {Object[]} loadTemplateBuffer - List of templates to be loaded */ static loadTemplateBuffer = []; /** @var {Bool} isLoadingTemplates - Whether templates are currently being loaded */ static isLoadingTemplates = false; /** @var {Map} templateCache - Cache of already loaded template strings */ static templateCache = new Map(); /** @var {Promise[]} templatePromises - Cache of already loaded template promises */ static templatePromises = {}; /** @var {Promise[]} cachePartialPromises - Cache of already loaded template partial promises */ static cachePartialPromises = []; /** * A helper to get the search key * * @param {string} theme * @param {string} templateName * @returns {string} */ static getSearchKey(theme, templateName) { return `${theme}/${templateName}`; } /** * Load a template. * * @method getTemplate * @param {string} templateName - should consist of the component and the name of the template like this: * core/menu (lib/templates/menu.mustache) or * tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache) * @param {string} [themeName=config.theme] - The theme to load the template from * @return {Promise} JQuery promise object resolved when the template has been fetched. */ static getTemplate(templateName, themeName = config.theme) { const searchKey = this.getSearchKey(themeName, templateName); // If we haven't already seen this template then buffer it. const cachedPromise = this.getTemplatePromiseFromCache(searchKey); if (cachedPromise) { return cachedPromise; } // Check the buffer to see if this template has already been added. const existingBufferRecords = this.loadTemplateBuffer.filter((record) => record.searchKey === searchKey); if (existingBufferRecords.length) { // This template is already in the buffer so just return the existing // promise. No need to add it to the buffer again. return existingBufferRecords[0].deferred.promise(); } // This is the first time this has been requested so let's add it to the buffer // to be loaded. const parts = templateName.split('/'); const component = getNormalisedComponent(parts.shift()); const name = parts.join('/'); const deferred = $.Deferred(); // Add this template to the buffer to be loaded. this.loadTemplateBuffer.push({ component, name, theme: themeName, searchKey, deferred, }); // We know there is at least one thing in the buffer so kick off a processing run. this.processLoadTemplateBuffer(); return deferred.promise(); } /** * Store a template in the cache. * * @param {string} searchKey * @param {string} templateSource */ static setTemplateInCache(searchKey, templateSource) { // Cache all of the dependent templates because we'll need them to render // the requested template. this.templateCache.set(searchKey, templateSource); } /** * Fetch a template from the cache. * * @param {string} searchKey * @returns {string} */ static getTemplateFromCache(searchKey) { return this.templateCache.get(searchKey); } /** * Check whether a template is in the cache. * * @param {string} searchKey * @returns {bool} */ static hasTemplateInCache(searchKey) { return this.templateCache.has(searchKey); } /** * Prefetch a set of templates without rendering them. * * @param {Array} templateNames The list of templates to fetch * @param {string} themeName */ static prefetchTemplates(templateNames, themeName) { templateNames.forEach((templateName) => this.prefetchTemplate(templateName, themeName)); } /** * Prefetech a sginle template without rendering it. * * @param {string} templateName * @param {string} themeName */ static prefetchTemplate(templateName, themeName) { const searchKey = this.getSearchKey(themeName, templateName); // If we haven't already seen this template then buffer it. if (this.hasTemplateInCache(searchKey)) { return; } // Check the buffer to see if this template has already been added. const existingBufferRecords = this.loadTemplateBuffer.filter((record) => record.searchKey === searchKey); if (existingBufferRecords.length) { // This template is already in the buffer so just return the existing promise. // No need to add it to the buffer again. return; } // This is the first time this has been requested so let's add it to the buffer to be loaded. const parts = templateName.split('/'); const component = getNormalisedComponent(parts.shift()); const name = parts.join('/'); // Add this template to the buffer to be loaded. this.loadTemplateBuffer.push({ component, name, theme: themeName, searchKey, deferred: $.Deferred(), }); this.processLoadTemplateBuffer(); } /** * Load a partial from the cache or ajax. * * @method partialHelper * @param {string} name The partial name to load. * @param {string} [themeName = config.theme] The theme to load the partial from. * @return {string} */ static partialHelper(name, themeName = config.theme) { const searchKey = this.getSearchKey(themeName, name); if (!this.hasTemplateInCache(searchKey)) { new Error(`Failed to pre-fetch the template: ${name}`); } return this.getTemplateFromCache(searchKey); } /** * Scan a template source for partial tags and return a list of the found partials. * * @method scanForPartials * @param {string} templateSource - source template to scan. * @return {Array} List of partials. */ static scanForPartials(templateSource) { const tokens = mustache.parse(templateSource); const partials = []; const findPartial = (tokens, partials) => { let i; for (i = 0; i < tokens.length; i++) { const token = tokens[i]; if (token[0] == '>' || token[0] == '<') { partials.push(token[1]); } if (token.length > 4) { findPartial(token[4], partials); } } }; findPartial(tokens, partials); return partials; } /** * Load a template and scan it for partials. Recursively fetch the partials. * * @method cachePartials * @param {string} templateName - should consist of the component and the name of the template like this: * core/menu (lib/templates/menu.mustache) or * tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache) * @param {string} [themeName=config.theme] * @param {Array} parentage - A list of requested partials in this render chain. * @return {Promise} JQuery promise object resolved when all partials are in the cache. */ static cachePartials(templateName, themeName = config.theme, parentage = []) { const searchKey = this.getSearchKey(themeName, templateName); if (searchKey in this.cachePartialPromises) { return this.cachePartialPromises[searchKey]; } // This promise will not be resolved until all child partials are also resolved and ready. // We create it here to allow us to check for recursive inclusion of templates. // Keep track of the requested partials in this chain. if (!parentage.length) { parentage.push(searchKey); } this.cachePartialPromises[searchKey] = $.Deferred(); this._cachePartials(templateName, themeName, parentage).catch((error) => { this.cachePartialPromises[searchKey].reject(error); }); return this.cachePartialPromises[searchKey]; } /** * Cache the template partials for the specified template. * * @param {string} templateName * @param {string} themeName * @param {array} parentage * @returns {promise<string>} */ static async _cachePartials(templateName, themeName, parentage) { const searchKey = this.getSearchKey(themeName, templateName); const templateSource = await this.getTemplate(templateName, themeName); const partials = this.scanForPartials(templateSource); const uniquePartials = partials.filter((partialName) => { // Check for recursion. if (parentage.indexOf(`${themeName}/${partialName}`) >= 0) { // Ignore templates which include a parent template already requested in the current chain. return false; } // Ignore templates that include themselves. return partialName !== templateName; }); // Fetch any partial which has not already been fetched. const fetchThemAll = uniquePartials.map((partialName) => { parentage.push(`${themeName}/${partialName}`); return this.cachePartials(partialName, themeName, parentage); }); await Promise.all(fetchThemAll); return this.cachePartialPromises[searchKey].resolve(templateSource); } /** * Take all of the templates waiting in the buffer and load them from the server * or from the cache. * * All of the templates that need to be loaded from the server will be batched up * and sent in a single network request. */ static processLoadTemplateBuffer() { if (!this.loadTemplateBuffer.length) { return; } if (this.isLoadingTemplates) { return; } this.isLoadingTemplates = true; // Grab any templates waiting in the buffer. const templatesToLoad = this.loadTemplateBuffer.slice(); // This will be resolved with the list of promises for the server request. const serverRequestsDeferred = $.Deferred(); const requests = []; // Get a list of promises for each of the templates we need to load. const templatePromises = templatesToLoad.map((templateData) => { const component = getNormalisedComponent(templateData.component); const name = templateData.name; const searchKey = templateData.searchKey; const theme = templateData.theme; const templateDeferred = templateData.deferred; let promise = null; // Double check to see if this template happened to have landed in the // cache as a dependency of an earlier template. if (this.hasTemplateInCache(searchKey)) { // We've seen this template so immediately resolve the existing promise. promise = this.getTemplatePromiseFromCache(searchKey); } else { // We haven't seen this template yet so we need to request it from // the server. requests.push({ methodname: 'core_output_load_template_with_dependencies', args: { component, template: name, themename: theme, lang: $('html').attr('lang').replace(/-/g, '_') } }); // Remember the index in the requests list for this template so that // we can get the appropriate promise back. const index = requests.length - 1; // The server deferred will be resolved with a list of all of the promises // that were sent in the order that they were added to the requests array. promise = serverRequestsDeferred.promise() .then((promises) => { // The promise for this template will be the one that matches the index // for it's entry in the requests array. // // Make sure the promise is added to the promises cache for this template // search key so that we don't request it again. templatePromises[searchKey] = promises[index].then((response) => { // Process all of the template dependencies for this template and add // them to the caches so that we don't request them again later. response.templates.forEach((data) => { data.component = getNormalisedComponent(data.component); const tempSearchKey = this.getSearchKey( theme, [data.component, data.name].join('/'), ); // Cache all of the dependent templates because we'll need them to render // the requested template. this.setTemplateInCache(tempSearchKey, data.value); if (config.templaterev > 0) { // The template cache is enabled - set the value there. storage.set(`core_template/${config.templaterev}:${tempSearchKey}`, data.value); } }); if (response.strings.length) { // If we have strings that the template needs then warm the string cache // with them now so that we don't need to re-fetch them. str.cache_strings(response.strings.map(({component, name, value}) => ({ component: getNormalisedComponent(component), key: name, value, }))); } // Return the original template source that the user requested. if (this.hasTemplateInCache(searchKey)) { return this.getTemplateFromCache(searchKey); } return null; }); return templatePromises[searchKey]; }); } return promise // When we've successfully loaded the template then resolve the deferred // in the buffer so that all of the calling code can proceed. .then((source) => templateDeferred.resolve(source)) .catch((error) => { // If there was an error loading the template then reject the deferred // in the buffer so that all of the calling code can proceed. templateDeferred.reject(error); // Rethrow for anyone else listening. throw error; }); }); if (requests.length) { // We have requests to send so resolve the deferred with the promises. serverRequestsDeferred.resolve(ajax.call(requests, true, false, false, 0, config.templaterev)); } else { // Nothing to load so we can resolve our deferred. serverRequestsDeferred.resolve(); } // Once we've finished loading all of the templates then recurse to process // any templates that may have been added to the buffer in the time that we // were fetching. $.when.apply(null, templatePromises) .then(() => { // Remove the templates we've loaded from the buffer. this.loadTemplateBuffer.splice(0, templatesToLoad.length); this.isLoadingTemplates = false; this.processLoadTemplateBuffer(); return; }) .catch(() => { // Remove the templates we've loaded from the buffer. this.loadTemplateBuffer.splice(0, templatesToLoad.length); this.isLoadingTemplates = false; this.processLoadTemplateBuffer(); }); } /** * Search the various caches for a template promise for the given search key. * The search key should be in the format <theme>/<component>/<template> e.g. boost/core/modal. * * If the template is found in any of the caches it will populate the other caches with * the same data as well. * * @param {String} searchKey The template search key in the format <theme>/<component>/<template> e.g. boost/core/modal * @returns {Object|null} jQuery promise resolved with the template source */ static getTemplatePromiseFromCache(searchKey) { // First try the cache of promises. if (searchKey in this.templatePromises) { return this.templatePromises[searchKey]; } // Check the module cache. if (this.hasTemplateInCache(searchKey)) { const templateSource = this.getTemplateFromCache(searchKey); // Add this to the promises cache for future. this.templatePromises[searchKey] = $.Deferred().resolve(templateSource).promise(); return this.templatePromises[searchKey]; } if (config.templaterev <= 0) { // Template caching is disabled. Do not store in persistent storage. return null; } // Now try local storage. const cached = storage.get(`core_template/${config.templaterev}:${searchKey}`); if (cached) { // Add this to the module cache for future. this.setTemplateInCache(searchKey, cached); // Add to the promises cache for future. this.templatePromises[searchKey] = $.Deferred().resolve(cached).promise(); return this.templatePromises[searchKey]; } return null; } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка