From 7d9e603be862a66a093c0062a580d5fc8c95ce1b Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 18 Jan 2010 12:45:04 -0500 Subject: [PATCH] Underscore 0.5.6, with custom template delimiters --- index.html | 53 ++++++++++++++++++++++++++++------------------- test/utility.js | 33 +++++++++++++++++------------ underscore-min.js | 9 ++++---- underscore.js | 27 ++++++++++++------------ 4 files changed, 71 insertions(+), 51 deletions(-) diff --git a/index.html b/index.html index 70490a641..1a03be19f 100644 --- a/index.html +++ b/index.html @@ -111,11 +111,11 @@

- + - +
Development Version (0.5.5)Development Version (0.5.6) 22kb, Uncompressed with Comments
Production Version (0.5.5)Production Version (0.5.6) 3kb, Packed and Gzipped
@@ -979,8 +979,7 @@ result;

 _.uniqueId('contact_');
-=> 'contact_104'
-
+=> 'contact_104'

template_.template(templateString, [context]) @@ -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
- <%= … %>, as well as execute arbitrary JavaScript code, with - <% … %>. When you evaluate a template function, pass in a + <%= … %>, as well as execute arbitrary JavaScript code, with + <% … %>. When you evaluate a template function, pass in a context object that has properties corresponding to the template's free variables. If you're writing a one-off, you can pass the context object as the second parameter to template in order to render immediately instead of returning a template function. -
- If the <% … %> 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.

 var compiled = _.template("hello: <%= name %>");
@@ -1007,17 +1001,28 @@ compiled({name : 'moe'});
 
 var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <% }); %>";
 _.template(list, {people : ['moe', 'curly', 'larry']});
-=> "<li>moe</li><li>curly</li><li>larry</li>"
+=> "<li>moe</li><li>curly</li><li>larry</li>"
-var custom = "{{ _.each(people, function(name) { }} <li>{{= name }}</li> {{ }); }}"; -_.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']}); -=> "<li>moe</li><li>curly</li><li>larry</li>" - +

+ 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 start and end tokens, and an interpolate regex + to match expressions that should be evaluated and inserted. For example, + to perform + Mustache.js + style templating: +

+ +
+_.templateSettings = {
+  start       : '{{',
+  end         : '}}',
+  interpolate : /\{\{(.+?)\}\}/g
+};
+        
+var template = _.template("Hello {{ name }}!");
+template({name : "Mustache"});
+=> "Hello Mustache!"

Chaining

@@ -1079,6 +1084,12 @@ _([1, 2, 3]).value();

Change Log

+

+ 0.5.6
+ Customizable delimiters for _.template, contributed by + Noah Sloan. +

+

0.5.5
Fix for a bug in MobileSafari's OOP-wrapper, with the arguments object. diff --git a/test/utility.js b/test/utility.js index 93263c9c6..763a48036 100644 --- a/test/utility.js +++ b/test/utility.js @@ -39,23 +39,30 @@ $(document).ready(function() { result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); equals(result, "

", 'can run arbitrary javascript in templates'); - var custom = _.template({ - template: "", - start: "{{", end: "}}", - interpolate: /\{\{=(.+?)\}\}/g - }); - result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); - equals(result, "", '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(""); + result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); + equals(result, "", '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"); }); }); diff --git a/underscore-min.js b/underscore-min.js index 9146e0860..2c8b49988 100644 --- a/underscore-min.js +++ b/underscore-min.js @@ -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)/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}})(); diff --git a/underscore.js b/underscore.js index c0f901651..cb620b4fd 100644 --- a/underscore.js +++ b/underscore.js @@ -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; };