Merge branch 'master' of github.com:documentcloud/underscore

This commit is contained in:
Jeremy Ashkenas
2012-04-06 16:54:29 -04:00
3 changed files with 66 additions and 28 deletions

View File

@@ -1335,7 +1335,7 @@ _.result(object, 'stuff');
=&gt; "nonsense"</pre> =&gt; "nonsense"</pre>
<p id="template"> <p id="template">
<b class="header">template</b><code>_.template(templateString, [context])</code> <b class="header">template</b><code>_.template(templateString, [data], [settings])</code>
<br /> <br />
Compiles JavaScript templates into functions that can be evaluated Compiles JavaScript templates into functions that can be evaluated
for rendering. Useful for rendering complicated bits of HTML from JSON for rendering. Useful for rendering complicated bits of HTML from JSON
@@ -1343,11 +1343,13 @@ _.result(object, 'stuff');
<tt>&lt;%= &hellip; %&gt;</tt>, as well as execute arbitrary JavaScript code, with <tt>&lt;%= &hellip; %&gt;</tt>, as well as execute arbitrary JavaScript code, with
<tt>&lt;% &hellip; %&gt;</tt>. If you wish to interpolate a value, and have <tt>&lt;% &hellip; %&gt;</tt>. If you wish to interpolate a value, and have
it be HTML-escaped, use <tt>&lt;%- &hellip; %&gt;</tt> When you evaluate a template function, pass in a it be HTML-escaped, use <tt>&lt;%- &hellip; %&gt;</tt> When you evaluate a template function, pass in a
<b>context</b> object that has properties corresponding to the template's free <b>data</b> object that has properties corresponding to the template's free
variables. If you're writing a one-off, you can pass the <b>context</b> variables. If you're writing a one-off, you can pass the <b>data</b>
object as the second parameter to <b>template</b> in order to render object as the second parameter to <b>template</b> in order to render
immediately instead of returning a template function. immediately instead of returning a template function. The <b>settings</b> argument
should be a hash containing any <tt>_.templateSettings</tt> that should be overriden.
</p> </p>
<pre> <pre>
var compiled = _.template("hello: &lt;%= name %&gt;"); var compiled = _.template("hello: &lt;%= name %&gt;");
compiled({name : 'moe'}); compiled({name : 'moe'});
@@ -1393,6 +1395,16 @@ var template = _.template("Hello {{ name }}!");
template({name : "Mustache"}); template({name : "Mustache"});
=&gt; "Hello Mustache!"</pre> =&gt; "Hello Mustache!"</pre>
<p>
By default, <b>template</b> places the values from your data in the local scope
via the <tt>with</tt> statement. However, you can specify a single variable name
with the <b>variable</b> setting.
</p>
<pre>
_.template("<%= data.hasWith %>", {hasWith: 'no'}, {variable: 'data'});
=&gt; "no"</pre>
<p> <p>
Precompiling your templates can be a big help when debugging errors you can't Precompiling your templates can be a big help when debugging errors you can't
reproduce. This is because precompiled templates can provide line numbers and reproduce. This is because precompiled templates can provide line numbers and

View File

@@ -1,9 +1,13 @@
$(document).ready(function() { $(document).ready(function() {
var templateSettings = _.templateSettings; var templateSettings;
module("Utility", { module("Utility", {
setup: function() {
templateSettings = _.clone(_.templateSettings);
},
teardown: function() { teardown: function() {
_.templateSettings = templateSettings; _.templateSettings = templateSettings;
} }
@@ -174,4 +178,12 @@ $(document).ready(function() {
strictEqual(_.result(null, 'x'), null); strictEqual(_.result(null, 'x'), null);
}); });
test('_.templateSettings.variable', function() {
var s = '<%=data.x%>';
var data = {x: 'x'};
strictEqual(_.template(s, data, {variable: 'data'}), 'x')
_.templateSettings.variable = 'data';
strictEqual(_.template(s)(data), 'x')
});
}); });

View File

@@ -951,30 +951,44 @@
// JavaScript micro-templating, similar to John Resig's implementation. // JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace, // Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code. // and correctly escapes quotes within interpolated code.
_.template = function(str, data) { _.template = function(text, data, settings) {
var settings = _.templateSettings; settings = _.extend(_.templateSettings, settings);
var source = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' + // Compile the template source, taking care to escape characters that
str // cannot be included in a string literal and then unescape them in code
.replace(escaper, function(match) { // blocks.
return '\\' + escapes[match]; var source = "__p+='" + text
}) .replace(escaper, function(match) {
.replace(settings.escape || noMatch, function(match, code) { return '\\' + escapes[match];
return "',\n_.escape(" + unescape(code) + "),\n'"; })
}) .replace(settings.escape || noMatch, function(match, code) {
.replace(settings.interpolate || noMatch, function(match, code) { return "'+\n_.escape(" + unescape(code) + ")+\n'";
return "',\n" + unescape(code) + ",\n'"; })
}) .replace(settings.interpolate || noMatch, function(match, code) {
.replace(settings.evaluate || noMatch, function(match, code) { return "'+\n(" + unescape(code) + ")+\n'";
return "');\n" + unescape(code) + "\n;__p.push('"; })
}) .replace(settings.evaluate || noMatch, function(match, code) {
+ "');\n}\nreturn __p.join('');"; return "';\n" + unescape(code) + "\n;__p+='";
var render = new Function('obj', '_', source); }) + "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __p='';" +
"var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
source + "return __p;\n";
var render = new Function(settings.variable || 'obj', '_', source);
if (data) return render(data, _); if (data) return render(data, _);
var template = function(data) { var template = function(data) {
return render.call(this, data, _); return render.call(this, data, _);
}; };
template.source = 'function(obj){\n' + source + '\n}';
// Provide the compiled function source as a convenience for build time
// precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
source + '}';
return template; return template;
}; };