2
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
3
* Build: `lodash modularize exports="node" -o ./compat/`
4
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
5
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
6
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7
* Available under MIT license <http://lodash.com/license>
9
var defaults = require('../objects/defaults'),
10
escape = require('./escape'),
11
escapeStringChar = require('../internals/escapeStringChar'),
12
keys = require('../objects/keys'),
13
reInterpolate = require('../internals/reInterpolate'),
14
templateSettings = require('./templateSettings'),
15
values = require('../objects/values');
17
/** Used to match empty string literals in compiled template source */
18
var reEmptyStringLeading = /\b__p \+= '';/g,
19
reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
20
reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
23
* Used to match ES6 template delimiters
24
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
26
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
28
/** Used to ensure capturing order of template delimiters */
29
var reNoMatch = /($^)/;
31
/** Used to match unescaped characters in compiled string literals */
32
var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
35
* A micro-templating method that handles arbitrary delimiters, preserves
36
* whitespace, and correctly escapes quotes within interpolated code.
38
* Note: In the development build, `_.template` utilizes sourceURLs for easier
39
* debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
41
* For more information on precompiling templates see:
42
* http://lodash.com/custom-builds
44
* For more information on Chrome extension sandboxes see:
45
* http://developer.chrome.com/stable/extensions/sandboxingEval.html
50
* @param {string} text The template text.
51
* @param {Object} data The data object used to populate the text.
52
* @param {Object} [options] The options object.
53
* @param {RegExp} [options.escape] The "escape" delimiter.
54
* @param {RegExp} [options.evaluate] The "evaluate" delimiter.
55
* @param {Object} [options.imports] An object to import into the template as local variables.
56
* @param {RegExp} [options.interpolate] The "interpolate" delimiter.
57
* @param {string} [sourceURL] The sourceURL of the template's compiled source.
58
* @param {string} [variable] The data object variable name.
59
* @returns {Function|string} Returns a compiled function when no `data` object
60
* is given, else it returns the interpolated text.
63
* // using the "interpolate" delimiter to create a compiled template
64
* var compiled = _.template('hello <%= name %>');
65
* compiled({ 'name': 'fred' });
68
* // using the "escape" delimiter to escape HTML in data property values
69
* _.template('<b><%- value %></b>', { 'value': '<script>' });
70
* // => '<b><script></b>'
72
* // using the "evaluate" delimiter to generate HTML
73
* var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
74
* _.template(list, { 'people': ['fred', 'barney'] });
75
* // => '<li>fred</li><li>barney</li>'
77
* // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
78
* _.template('hello ${ name }', { 'name': 'pebbles' });
79
* // => 'hello pebbles'
81
* // using the internal `print` function in "evaluate" delimiters
82
* _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
83
* // => 'hello barney!'
85
* // using a custom template delimiters
86
* _.templateSettings = {
87
* 'interpolate': /{{([\s\S]+?)}}/g
90
* _.template('hello {{ name }}!', { 'name': 'mustache' });
91
* // => 'hello mustache!'
93
* // using the `imports` option to import jQuery
94
* var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
95
* _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
96
* // => '<li>fred</li><li>barney</li>'
98
* // using the `sourceURL` option to specify a custom sourceURL for the template
99
* var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
101
* // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
103
* // using the `variable` option to ensure a with-statement isn't used in the compiled template
104
* var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
106
* // => function(data) {
107
* var __t, __p = '', __e = _.escape;
108
* __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
112
* // using the `source` property to inline compiled templates for meaningful
113
* // line numbers in error messages and a stack trace
114
* fs.writeFileSync(path.join(cwd, 'jst.js'), '\
116
* "main": ' + _.template(mainText).source + '\
120
function template(text, data, options) {
121
// based on John Resig's `tmpl` implementation
122
// http://ejohn.org/blog/javascript-micro-templating/
123
// and Laura Doktorova's doT.js
124
// https://github.com/olado/doT
125
var settings = templateSettings.imports._.templateSettings || templateSettings;
126
text = String(text || '');
128
// avoid missing dependencies when `iteratorTemplate` is not defined
129
options = defaults({}, options, settings);
131
var imports = defaults({}, options.imports, settings.imports),
132
importsKeys = keys(imports),
133
importsValues = values(imports);
137
interpolate = options.interpolate || reNoMatch,
140
// compile the regexp to match each delimiter
141
var reDelimiters = RegExp(
142
(options.escape || reNoMatch).source + '|' +
143
interpolate.source + '|' +
144
(interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
145
(options.evaluate || reNoMatch).source + '|$'
148
text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
149
interpolateValue || (interpolateValue = esTemplateValue);
151
// escape characters that cannot be included in string literals
152
source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
154
// replace delimiters with snippets
156
source += "' +\n__e(" + escapeValue + ") +\n'";
160
source += "';\n" + evaluateValue + ";\n__p += '";
162
if (interpolateValue) {
163
source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
165
index = offset + match.length;
167
// the JS engine embedded in Adobe products requires returning the `match`
168
// string in order to produce the correct `offset` value
174
// if `variable` is not specified, wrap a with-statement around the generated
175
// code to add the data object to the top of the scope chain
176
var variable = options.variable,
177
hasVariable = variable;
181
source = 'with (' + variable + ') {\n' + source + '\n}\n';
183
// cleanup code by stripping empty strings
184
source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
185
.replace(reEmptyStringMiddle, '$1')
186
.replace(reEmptyStringTrailing, '$1;');
188
// frame code as the function body
189
source = 'function(' + variable + ') {\n' +
190
(hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
191
"var __t, __p = '', __e = _.escape" +
193
? ', __j = Array.prototype.join;\n' +
194
"function print() { __p += __j.call(arguments, '') }\n"
201
var result = Function(importsKeys, 'return ' + source ).apply(undefined, importsValues);
209
// provide the compiled function's source by its `toString` method, in
210
// supported environments, or the `source` property as a convenience for
211
// inlining compiled templates during the build process
212
result.source = source;
216
module.exports = template;