2016-10-10 03:43:44 +02:00
|
|
|
/**
|
2016-11-03 01:33:53 +01:00
|
|
|
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
|
|
|
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
|
|
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
|
|
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
|
|
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
|
|
* Available under MIT license <http://lodash.com/license>
|
2016-10-10 03:43:44 +02:00
|
|
|
*/
|
2016-11-03 01:33:53 +01:00
|
|
|
var defaults = require('lodash.defaults'),
|
|
|
|
escape = require('lodash.escape'),
|
|
|
|
escapeStringChar = require('lodash._escapestringchar'),
|
2016-10-10 03:43:44 +02:00
|
|
|
keys = require('lodash.keys'),
|
2016-11-03 01:33:53 +01:00
|
|
|
reInterpolate = require('lodash._reinterpolate'),
|
|
|
|
templateSettings = require('lodash.templatesettings'),
|
|
|
|
values = require('lodash.values');
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
/** Used to match empty string literals in compiled template source */
|
2016-10-10 03:43:44 +02:00
|
|
|
var reEmptyStringLeading = /\b__p \+= '';/g,
|
|
|
|
reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
|
|
|
|
reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
|
|
|
|
|
|
|
|
/**
|
2016-11-03 01:33:53 +01:00
|
|
|
* Used to match ES6 template delimiters
|
|
|
|
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
|
2016-10-10 03:43:44 +02:00
|
|
|
*/
|
2016-11-03 01:33:53 +01:00
|
|
|
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
/** Used to ensure capturing order of template delimiters */
|
|
|
|
var reNoMatch = /($^)/;
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
/** Used to match unescaped characters in compiled string literals */
|
|
|
|
var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
|
2016-10-10 03:43:44 +02:00
|
|
|
|
|
|
|
/**
|
2016-11-03 01:33:53 +01:00
|
|
|
* A micro-templating method that handles arbitrary delimiters, preserves
|
|
|
|
* whitespace, and correctly escapes quotes within interpolated code.
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* Note: In the development build, `_.template` utilizes sourceURLs for easier
|
|
|
|
* debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* For more information on precompiling templates see:
|
|
|
|
* http://lodash.com/custom-builds
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* For more information on Chrome extension sandboxes see:
|
|
|
|
* http://developer.chrome.com/stable/extensions/sandboxingEval.html
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
|
|
|
* @static
|
|
|
|
* @memberOf _
|
2016-11-03 01:33:53 +01:00
|
|
|
* @category Utilities
|
|
|
|
* @param {string} text The template text.
|
|
|
|
* @param {Object} data The data object used to populate the text.
|
2016-10-10 03:43:44 +02:00
|
|
|
* @param {Object} [options] The options object.
|
2016-11-03 01:33:53 +01:00
|
|
|
* @param {RegExp} [options.escape] The "escape" delimiter.
|
2016-10-10 03:43:44 +02:00
|
|
|
* @param {RegExp} [options.evaluate] The "evaluate" delimiter.
|
2016-11-03 01:33:53 +01:00
|
|
|
* @param {Object} [options.imports] An object to import into the template as local variables.
|
2016-10-10 03:43:44 +02:00
|
|
|
* @param {RegExp} [options.interpolate] The "interpolate" delimiter.
|
2016-11-03 01:33:53 +01:00
|
|
|
* @param {string} [sourceURL] The sourceURL of the template's compiled source.
|
|
|
|
* @param {string} [variable] The data object variable name.
|
|
|
|
* @returns {Function|string} Returns a compiled function when no `data` object
|
|
|
|
* is given, else it returns the interpolated text.
|
2016-10-10 03:43:44 +02:00
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* // using the "interpolate" delimiter to create a compiled template
|
2016-11-03 01:33:53 +01:00
|
|
|
* var compiled = _.template('hello <%= name %>');
|
|
|
|
* compiled({ 'name': 'fred' });
|
|
|
|
* // => 'hello fred'
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* // using the "escape" delimiter to escape HTML in data property values
|
|
|
|
* _.template('<b><%- value %></b>', { 'value': '<script>' });
|
2016-10-10 03:43:44 +02:00
|
|
|
* // => '<b><script></b>'
|
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* // using the "evaluate" delimiter to generate HTML
|
|
|
|
* var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
|
|
|
|
* _.template(list, { 'people': ['fred', 'barney'] });
|
2016-10-10 03:43:44 +02:00
|
|
|
* // => '<li>fred</li><li>barney</li>'
|
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
|
|
|
|
* _.template('hello ${ name }', { 'name': 'pebbles' });
|
|
|
|
* // => 'hello pebbles'
|
|
|
|
*
|
2016-10-10 03:43:44 +02:00
|
|
|
* // using the internal `print` function in "evaluate" delimiters
|
2016-11-03 01:33:53 +01:00
|
|
|
* _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
|
2016-10-10 03:43:44 +02:00
|
|
|
* // => 'hello barney!'
|
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* // using a custom template delimiters
|
|
|
|
* _.templateSettings = {
|
|
|
|
* 'interpolate': /{{([\s\S]+?)}}/g
|
|
|
|
* };
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* _.template('hello {{ name }}!', { 'name': 'mustache' });
|
2016-10-10 03:43:44 +02:00
|
|
|
* // => 'hello mustache!'
|
|
|
|
*
|
2016-11-03 01:33:53 +01:00
|
|
|
* // using the `imports` option to import jQuery
|
|
|
|
* var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
|
|
|
|
* _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
|
2016-10-10 03:43:44 +02:00
|
|
|
* // => '<li>fred</li><li>barney</li>'
|
|
|
|
*
|
|
|
|
* // using the `sourceURL` option to specify a custom sourceURL for the template
|
2016-11-03 01:33:53 +01:00
|
|
|
* var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
|
2016-10-10 03:43:44 +02:00
|
|
|
* compiled(data);
|
|
|
|
* // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
|
|
|
|
*
|
|
|
|
* // using the `variable` option to ensure a with-statement isn't used in the compiled template
|
2016-11-03 01:33:53 +01:00
|
|
|
* var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
|
2016-10-10 03:43:44 +02:00
|
|
|
* compiled.source;
|
|
|
|
* // => function(data) {
|
2016-11-03 01:33:53 +01:00
|
|
|
* var __t, __p = '', __e = _.escape;
|
|
|
|
* __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
|
|
|
|
* return __p;
|
|
|
|
* }
|
2016-10-10 03:43:44 +02:00
|
|
|
*
|
|
|
|
* // using the `source` property to inline compiled templates for meaningful
|
|
|
|
* // line numbers in error messages and a stack trace
|
|
|
|
* fs.writeFileSync(path.join(cwd, 'jst.js'), '\
|
|
|
|
* var JST = {\
|
|
|
|
* "main": ' + _.template(mainText).source + '\
|
|
|
|
* };\
|
|
|
|
* ');
|
|
|
|
*/
|
2016-11-03 01:33:53 +01:00
|
|
|
function template(text, data, options) {
|
|
|
|
// based on John Resig's `tmpl` implementation
|
|
|
|
// http://ejohn.org/blog/javascript-micro-templating/
|
|
|
|
// and Laura Doktorova's doT.js
|
|
|
|
// https://github.com/olado/doT
|
2016-10-10 03:43:44 +02:00
|
|
|
var settings = templateSettings.imports._.templateSettings || templateSettings;
|
2016-11-03 01:33:53 +01:00
|
|
|
text = String(text || '');
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// avoid missing dependencies when `iteratorTemplate` is not defined
|
|
|
|
options = defaults({}, options, settings);
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
var imports = defaults({}, options.imports, settings.imports),
|
2016-10-10 03:43:44 +02:00
|
|
|
importsKeys = keys(imports),
|
2016-11-03 01:33:53 +01:00
|
|
|
importsValues = values(imports);
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
var isEvaluating,
|
2016-10-10 03:43:44 +02:00
|
|
|
index = 0,
|
|
|
|
interpolate = options.interpolate || reNoMatch,
|
|
|
|
source = "__p += '";
|
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// compile the regexp to match each delimiter
|
2016-10-10 03:43:44 +02:00
|
|
|
var reDelimiters = RegExp(
|
|
|
|
(options.escape || reNoMatch).source + '|' +
|
|
|
|
interpolate.source + '|' +
|
|
|
|
(interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
|
|
|
|
(options.evaluate || reNoMatch).source + '|$'
|
|
|
|
, 'g');
|
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
|
2016-10-10 03:43:44 +02:00
|
|
|
interpolateValue || (interpolateValue = esTemplateValue);
|
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// escape characters that cannot be included in string literals
|
|
|
|
source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// replace delimiters with snippets
|
2016-10-10 03:43:44 +02:00
|
|
|
if (escapeValue) {
|
|
|
|
source += "' +\n__e(" + escapeValue + ") +\n'";
|
|
|
|
}
|
|
|
|
if (evaluateValue) {
|
|
|
|
isEvaluating = true;
|
|
|
|
source += "';\n" + evaluateValue + ";\n__p += '";
|
|
|
|
}
|
|
|
|
if (interpolateValue) {
|
|
|
|
source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
|
|
|
|
}
|
|
|
|
index = offset + match.length;
|
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// the JS engine embedded in Adobe products requires returning the `match`
|
|
|
|
// string in order to produce the correct `offset` value
|
2016-10-10 03:43:44 +02:00
|
|
|
return match;
|
|
|
|
});
|
|
|
|
|
|
|
|
source += "';\n";
|
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// if `variable` is not specified, wrap a with-statement around the generated
|
|
|
|
// code to add the data object to the top of the scope chain
|
|
|
|
var variable = options.variable,
|
|
|
|
hasVariable = variable;
|
|
|
|
|
|
|
|
if (!hasVariable) {
|
|
|
|
variable = 'obj';
|
|
|
|
source = 'with (' + variable + ') {\n' + source + '\n}\n';
|
2016-10-10 03:43:44 +02:00
|
|
|
}
|
2016-11-03 01:33:53 +01:00
|
|
|
// cleanup code by stripping empty strings
|
2016-10-10 03:43:44 +02:00
|
|
|
source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
|
|
|
|
.replace(reEmptyStringMiddle, '$1')
|
|
|
|
.replace(reEmptyStringTrailing, '$1;');
|
|
|
|
|
2016-11-03 01:33:53 +01:00
|
|
|
// frame code as the function body
|
|
|
|
source = 'function(' + variable + ') {\n' +
|
|
|
|
(hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
|
|
|
|
"var __t, __p = '', __e = _.escape" +
|
2016-10-10 03:43:44 +02:00
|
|
|
(isEvaluating
|
|
|
|
? ', __j = Array.prototype.join;\n' +
|
|
|
|
"function print() { __p += __j.call(arguments, '') }\n"
|
|
|
|
: ';\n'
|
|
|
|
) +
|
|
|
|
source +
|
|
|
|
'return __p\n}';
|
|
|
|
|
|
|
|
try {
|
2016-11-03 01:33:53 +01:00
|
|
|
var result = Function(importsKeys, 'return ' + source ).apply(undefined, importsValues);
|
2016-10-10 03:43:44 +02:00
|
|
|
} catch(e) {
|
2016-11-03 01:33:53 +01:00
|
|
|
e.source = source;
|
|
|
|
throw e;
|
2016-10-10 03:43:44 +02:00
|
|
|
}
|
2016-11-03 01:33:53 +01:00
|
|
|
if (data) {
|
|
|
|
return result(data);
|
|
|
|
}
|
|
|
|
// provide the compiled function's source by its `toString` method, in
|
|
|
|
// supported environments, or the `source` property as a convenience for
|
|
|
|
// inlining compiled templates during the build process
|
|
|
|
result.source = source;
|
|
|
|
return result;
|
|
|
|
}
|
2016-10-10 03:43:44 +02:00
|
|
|
|
|
|
|
module.exports = template;
|