Underscore 0.5.6, with custom template delimiters

This commit is contained in:
Jeremy Ashkenas
2010-01-18 12:45:04 -05:00
parent 94195e661d
commit 7d9e603be8
4 changed files with 71 additions and 51 deletions

View File

@@ -111,11 +111,11 @@
<p>
<table>
<tr>
<td><a href="underscore.js">Development Version (0.5.5)</a></td>
<td><a href="underscore.js">Development Version (0.5.6)</a></td>
<td><i>22kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="underscore-min.js">Production Version (0.5.5)</a></td>
<td><a href="underscore-min.js">Production Version (0.5.6)</a></td>
<td><i>3kb, Packed and Gzipped</i></td>
</tr>
</table>
@@ -979,8 +979,7 @@ result;
</p>
<pre>
_.uniqueId('contact_');
=&gt; 'contact_104'
</pre>
=&gt; 'contact_104'</pre>
<p id="template">
<b class="header">template</b><code>_.template(templateString, [context])</code>
@@ -988,17 +987,12 @@ _.uniqueId('contact_');
Compiles JavaScript templates into functions that can be evaluated
for rendering. Useful for rendering complicated bits of HTML from JSON
data sources. Template functions can both interpolate variables, using<br />
<i>&lt;%= &hellip; %&gt;</i>, as well as execute arbitrary JavaScript code, with
<i>&lt;% &hellip; %&gt;</i>. When you evaluate a template function, pass in a
<tt>&lt;%= &hellip; %&gt;</tt>, as well as execute arbitrary JavaScript code, with
<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
variables. If you're writing a one-off, you can pass the <b>context</b>
object as the second parameter to <b>template</b> in order to render
immediately instead of returning a template function.
<br />
If the <i>&lt;% &hellip; %&gt;</i> syntax is not convenient because
your templating languages assigns special meaning to it, the delimeters
can be customized by passing an object as the first argument. See the
code sample below for options.
</p>
<pre>
var compiled = _.template("hello: &lt;%= name %&gt;");
@@ -1007,17 +1001,28 @@ compiled({name : 'moe'});
var list = "&lt;% _.each(people, function(name) { %&gt; &lt;li&gt;&lt;%= name %&gt;&lt;/li&gt; &lt;% }); %&gt;";
_.template(list, {people : ['moe', 'curly', 'larry']});
=&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;"
=&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;"</pre>
var custom = "{{ _.each(people, function(name) { }} &lt;li&gt;{{= name }}&lt;/li&gt; {{ }); }}";
_.template({
template: custom,
start: '{{', // the code start delimeter
end: '}}', // the code end delimeter
interpolate: /\{\{=(.+?)\}\}/g // a regex with 1 capture group for the var name
}, {people : ['moe', 'curly', 'larry']});
=&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;"
</pre>
<p>
If ERB-style delimiters aren't your cup of tea, you can change Underscore's
template settings to use different symbols to set off interpolated code.
Define <b>start</b> and <b>end</b> tokens, and an <b>interpolate</b> regex
to match expressions that should be evaluated and inserted. For example,
to perform
<a href="http://github.com/janl/mustache.js#readme">Mustache.js</a>
style templating:
</p>
<pre>
_.templateSettings = {
start : '{{',
end : '}}',
interpolate : /\{\{(.+?)\}\}/g
};
var template = _.template("Hello {{ name }}!");
template({name : "Mustache"});
=&gt; "Hello Mustache!"</pre>
<h2>Chaining</h2>
@@ -1079,6 +1084,12 @@ _([1, 2, 3]).value();
<h2>Change Log</h2>
<p>
<b class="header">0.5.6</b><br />
Customizable delimiters for <tt>_.template</tt>, contributed by
<a href="http://github.com/iamnoah">Noah Sloan</a>.
</p>
<p>
<b class="header">0.5.5</b><br />
Fix for a bug in MobileSafari's OOP-wrapper, with the arguments object.

View File

@@ -39,23 +39,30 @@ $(document).ready(function() {
result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
equals(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
var custom = _.template({
template: "<ul>{{ for (key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>",
start: "{{", end: "}}",
interpolate: /\{\{=(.+?)\}\}/g
});
result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
equals(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
var quoteTemplate = _.template("It's its, not it's");
equals(quoteTemplate({}), "It's its, not it's");
var customQuote = _.template({
template: "It's its, not it's",
start: "{{", end: "}}",
interpolate: /\{\{=(.+?)\}\}/g
});
_.templateSettings = {
start : '{{',
end : '}}',
interpolate : /\{\{=(.+?)\}\}/g
};
var custom = _.template("<ul>{{ for (key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>");
result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
equals(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates');
var customQuote = _.template("It's its, not it's");
equals(customQuote({}), "It's its, not it's");
_.templateSettings = {
start : '{{',
end : '}}',
interpolate : /\{\{(.+?)\}\}/g
};
var mustache = _.template("Hello {{planet}}!");
equals(mustache({planet : "World"}), "Hello World!", "can mimic mustache.js");
});
});

9
underscore-min.js vendored
View File

@@ -1,4 +1,4 @@
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.5";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.6";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
@@ -11,6 +11,7 @@ return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(func
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!b.isArray(a)&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&
a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.template=function(a,c){a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g,
" ").replace(/'(?=[^%]*%>)/g,"\t").split("'").join("\\'").split("\t").join("'").replace(/<%=(.+?)%>/g,"',$1,'").split("<%").join("');").split("%>").join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);
o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();
a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;a=new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;b.methods=b.functions;
var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){return l(c.apply(this._wrapped,
arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();

View File

@@ -38,7 +38,7 @@
propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
// Current version.
_.VERSION = '0.5.5';
_.VERSION = '0.5.6';
// ------------------------ Collection Functions: ---------------------------
@@ -559,28 +559,29 @@
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
start : '<%',
end : '%>',
interpolate : /<%=(.+?)%>/g
};
// JavaScript templating a-la ERB, pilfered from John Resig's
// "Secrets of the JavaScript Ninja", page 83.
// Single-quote fix from Rick Strahl's version.
_.template = function(str, data) {
var start = '<%', end = '%>',
interpolate = /<%=(.+?)%>/g;
if(str && !_.isString(str)) {
start = str.start || start;
end = str.end || end;
interpolate = str.interpolate || interpolate;
str = str.template;
}
var c = _.templateSettings;
var fn = new Function('obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
str.replace(/[\r\t\n]/g, " ")
.replace(new RegExp("'(?=[^"+end[0]+"]*"+end+")","g"),"\t")
.replace(new RegExp("'(?=[^"+c.end[0]+"]*"+c.end+")","g"),"\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(interpolate, "',$1,'")
.split(start).join("');")
.split(end).join("p.push('")
.replace(c.interpolate, "',$1,'")
.split(c.start).join("');")
.split(c.end).join("p.push('")
+ "');}return p.join('');");
return data ? fn(data) : fn;
};