I promised myself I'd show parity with <a href="https://gist.github.com/3346253#file_t_js_parity.js" rel="nofollow">https://gist.github.com/3346253#file_t_js_parity.js</a> at <a href="http://news.ycombinator.com/item?id=4378847" rel="nofollow">http://news.ycombinator.com/item?id=4378847</a>, but comments eventually go non-editable.<p>I haven't properly updated the gist fork yet, but as follows are the semi-minified additions [of `safe` and `map` (iter)] and parity tests.<p><pre><code> function t(a,b){
return function(c,d){
return a.replace(/#{([^}]*)}/g, function(a,e){
function safe(v){return new Option(v).innerHTML;}
function map(o, f){
var k, v = [];
for(k in o) v.push(f(k, o[k]));
return v;
}
return Function("x",safe+map+"with(x)return "+e).call(c,d||b||{})
})
}
}
function l(v){
console.log(v);
}
// Simple interpolation: {{=value}}
l(t("Hello, #{this.name}!")({name: "Mike"}));
// Scrubbed interpolation: {{%unsafe_value}}
l(t("Saved from #{safe(this.xss)}!")({xss: "<script>alert('xss')<\/script>"}));
// Name-spaced variables: {{=User.address.city}}
l(t("Hello1, #{this.user.name}!")({user: {name: "Mike"}}));
// If/else blocks: {{value}} <<markup>> {{:value}} <<alternate markup>> {{/value}}
l(t("Hello2, #{this.name || 'World'}!")({name: "Mike"}));
l(t("Hello2, #{this.name || 'World'}!")());
l(t("Hello2, #{this.u ? this.u.name : 'World'}!")({u: {name: "Mike"}}));
// If not blocks: {{!value}} <<markup>> {{/value}}
l(t("Hello3, #{!this.u ? 'World' : ''}!")({user: {name: "Mike"}}));
// Object/Array iteration: {{@object_value}} {{=_key}}:{{=_val}} {{/@object_value}}
l(t("Hello4, #{map(this.users, print_user).join(', ')}!", {
print_user: function(k, user){return user;}
})({users: ["Mike", "John", "Bill"]}));
// Maybe you have one of those new-fangled browsers w/ builtin map
l(t("Hello4, #{this.users.map(print_user).join(', ')}!", {
print_user: function(user){return user;}
})({users: ["Mike", "John", "Bill"]}));
// Let's go deeper...
l(t("Hello4, #{map(this.users, user).join(', ')}!", {
user: function(_, user){
return t("#{safe(this.name)}")(user);
}
})({users:[{name: "Mike"}, {name: "John<3"}, {name: "Bill"}]}));
// Render the same template multiple times with different data
hello = t("Hello5, and #{this.goodbye}!");
l(hello({goodbye: "goodbye"}));
l(hello({goodbye: "good night"}));</code></pre>