MDL-57975 javascript: add userdate mustache js helper

Part of MDL-55611
This commit is contained in:
Ryan Wyllie 2017-02-21 05:34:33 +00:00
parent b61015cfcd
commit 0e5b3e282f
2 changed files with 90 additions and 13 deletions

File diff suppressed because one or more lines are too long

View file

@ -34,9 +34,11 @@ define(['core/mustache',
'core/localstorage', 'core/localstorage',
'core/event', 'core/event',
'core/yui', 'core/yui',
'core/log' 'core/log',
'core/user_date'
], ],
function(mustache, $, ajax, str, notification, coreurl, log, config, storage, event, Y, Log) { function(mustache, $, ajax, str, notification, coreurl, log,
config, storage, event, Y, Log, UserDate) {
// Module variables. // Module variables.
/** @var {Number} uniqInstances Count of times this constructor has been called. */ /** @var {Number} uniqInstances Count of times this constructor has been called. */
@ -56,6 +58,7 @@ define(['core/mustache',
var Renderer = function() { var Renderer = function() {
this.requiredStrings = []; this.requiredStrings = [];
this.requiredJS = []; this.requiredJS = [];
this.requiredDates = [];
this.currentThemeName = ''; this.currentThemeName = '';
}; };
// Class variables and functions. // Class variables and functions.
@ -63,6 +66,9 @@ define(['core/mustache',
/** @var {string[]} requiredStrings - Collection of strings found during the rendering of one template */ /** @var {string[]} requiredStrings - Collection of strings found during the rendering of one template */
Renderer.prototype.requiredStrings = null; 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 */ /** @var {string[]} requiredJS - Collection of js blocks found during the rendering of one template */
Renderer.prototype.requiredJS = null; Renderer.prototype.requiredJS = null;
@ -261,6 +267,32 @@ define(['core/mustache',
return '"' + content + '"'; return '"' + content + '"';
}; };
/**
* 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 + ']]';
};
/** /**
* Add some common helper functions to all context objects passed to templates. * Add some common helper functions to all context objects passed to templates.
* These helpers match exactly the helpers available in php. * These helpers match exactly the helpers available in php.
@ -287,6 +319,9 @@ define(['core/mustache',
context.quote = function() { context.quote = function() {
return this.quoteHelper.bind(this, context); return this.quoteHelper.bind(this, context);
}.bind(this); }.bind(this);
context.userdate = function() {
return this.userDateHelper.bind(this, context);
}.bind(this);
context.globals = {config: config}; context.globals = {config: config};
context.currentTheme = themeName; context.currentTheme = themeName;
}; };
@ -296,17 +331,15 @@ define(['core/mustache',
* *
* @method getJS * @method getJS
* @private * @private
* @param {string[]} strings Replacement strings.
* @return {string} * @return {string}
*/ */
Renderer.prototype.getJS = function(strings) { Renderer.prototype.getJS = function() {
var js = ''; var js = '';
if (this.requiredJS.length > 0) { if (this.requiredJS.length > 0) {
js = this.requiredJS.join(";\n"); js = this.requiredJS.join(";\n");
} }
// Re-render to get the final strings. return js;
return this.treatStringsInContent(js, strings);
}; };
/** /**
@ -378,6 +411,26 @@ define(['core/mustache',
return content; 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} strings The strings 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. * Render a template and then call the callback with the result.
* *
@ -394,23 +447,47 @@ define(['core/mustache',
return this.getTemplate('core/pix_icon').then(function() { return this.getTemplate('core/pix_icon').then(function() {
this.addHelpers(context, themeName); this.addHelpers(context, themeName);
var result = mustache.render(templateSource, context, this.partialHelper.bind(this)); 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) { if (this.requiredStrings.length > 0) {
return str.get_strings(this.requiredStrings).then(function(strings) { 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? // Why do we not do another call the render here?
// //
// Because that would expose DOS holes. E.g. // Because that would expose DOS holes. E.g.
// I create an assignment called "{{fish" which // I create an assignment called "{{fish" which
// would get inserted in the template in the first pass // would get inserted in the template in the first pass
// and cause the template to die on the second pass (unbalanced). // and cause the template to die on the second pass (unbalanced).
html = this.treatStringsInContent(html, strings);
result = this.treatStringsInContent(result, strings); js = this.treatStringsInContent(js, strings);
return $.Deferred().resolve(result, this.getJS(strings)).promise(); return $.Deferred().resolve(html, js).promise();
}.bind(this)); }.bind(this));
} else {
return $.Deferred().resolve(result.trim(), this.getJS([])).promise();
} }
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)); }.bind(this));
}; };