mstc is a minimal mustache-like template processor. It supports objects, arrays and can even call functions on them - all without using eval or new Function! Placeholders can contain any character except curly braces (that means you can have templates that render into templates).
-
-
Save tkissing/1347239 to your computer and use it in GitHub Desktop.
function(a,b) { // a: template string, b: Object or Array with values to fill in | |
// coerce a to a string | |
return(a+'').replace( | |
// search for anything with a mustache around it | |
/\{\{([^{}]+)}}/g, | |
function(c,d) { // c will be the complete placeholder, d the part between the mustaches | |
// b is optional (but if passed, must be a type that is a valid operand for "in" | |
return d in (b||{}) | |
// if d is a method of b, call it, otherwise return its value | |
? (/^f/.test(typeof b[d]) ? b[d]() :b[d] ) | |
// if d is not a member of b, don't replace anything to allow nested calls like | |
// mstc(mstc(tmpl, obj), anotherObj) | |
: c; | |
} | |
); | |
} |
function(a,b){return(a+'').replace(/\{\{([^{}]+)}}/g,function(c,d){return d in(b||{})?(/^f/.test(typeof b[d])?b[d]():b[d]):c})} |
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
Version 2, December 2004 | |
Copyright (C) 2011 Timo Kissing http://kissing.name | |
Everyone is permitted to copy and distribute verbatim or modified | |
copies of this license document, and changing it is allowed as long | |
as the name is changed. | |
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
0. You just DO WHAT THE FUCK YOU WANT TO. |
{ | |
"name": "mustacheStyleTemplatesCompressed", | |
"description": "Very minimal implementation of mustache style templating in JavaScript", | |
"keywords": [ | |
"140bytes", | |
"mustache", | |
"template" | |
] | |
} |
<!DOCTYPE html> | |
<head> | |
<title>mstc: mustache style templates - compressed </title> | |
</head> | |
<body> | |
<div>Expected value: <b>Hello @140bytes! How is the weather?</b></div> | |
<div>Actual value: <b id="ret1"></b></div> | |
<div>Expected value: <b>The array has a length of 3 and the first element is "foo". Ah, what the heck, here is all of it: foo,bar,bam</b></div> | |
<div>Actual value: <b id="ret2"></b></div> | |
<script> | |
var mstc = function(a,b){return(a+'').replace(/\{\{([^{}]+)}}/g,function(c,d){return d in(b||{})?(/^f/.test(typeof b[d])?b[d]():b[d]):c})}; | |
document.getElementById( "ret1" ).innerHTML = mstc('Hello {{w}}! How is the {{x}}?',{w:'@140bytes', x: function() {return 'weather';}}); | |
document.getElementById( "ret2" ).innerHTML = mstc('The array has a length of {{length}} and the first element is "{{0}}". Ah, what the heck, here is all of it: {{join}}', ['foo', 'bar', 'bam']); | |
</script> | |
</body> | |
</html> |
Haven't been able to test in older IE, but /^f/.test(typeof x)
works in IE9 at least...
I had an other idea: (typeof b[d]).length==8
. But it's the same length. Your approach is very cool. I checked. Works in IE6.
b[d] && b[d].call
seems to work as well - compared to the original typeof b[d] == 'function'
that's 8 bytes. Need to test in IEs though, so probably won't update the gist today.
I added this to my tests suite (abusing website visitors for compatibility tests is fun). From what I see it works as expected in all web browsers including IE6. We need to add this to Byte-saving Techniques. But don't forget this will lead to wrong results if an object contains a call
property or method.
Yeah, that limitation is important to note. I think I will go with the /^f/.test approach unless I find really good use for those additional bytes ;)
@tkissing, I'm surprised but it seems it's possible to omit all \
because {
is not followed by a number. I will add this to my tests suite.
Results from my tests suit (very few data, unfortunately): Not escaping {
fails in Chrome 8 (but works in Chrome 12+). Also fails in older versions of Safari. Works in all other browsers (IE6+, Firefox 3.6+).
Update (more data): Not escaping {
works in IE 6+ and Opera 10.6+, it works in Firefox 3.0+ but fails in 2, works in Safari 5.1+ but fails in 5.0, works in Chrome 14+ but may fail (inconsistent data) in older versions. Oh, and I added /^f/.test(typeof a)
and a&&a.call
to my tests suite. Both work as expected in all browsers (checking for call
returns true for functions and objects containing a call
method, false for numbers, strings and all other objects).
What about
function f(a,b,c){return++c?b in(c=f.z||{})?c[b].call?c[b]():c[b]:a:a.replace(/{{([^{}]+)}}/g,f,f.z=b)}
?
maettig: How did you get that idea? Would have never thought of that...
tsaniel: That is pretty darn clever! Should read :c[b]:a:a.replace
in the middle there to stay compatible with previous versions (if placeholder is not found in data, leave it untouched; your version removes the mustache), but still very nice work. Adding back the coercion of a
to a string and the /^f/
test for the function it's still 4 bytes shorter! Congratulations!
@tkissing: You're right! That was my bad.
@tsaniel: no worries, that's why I have a bunch of additonal test cases locally ;)
Will update the gist later, your little trick gave me an idea I want to test...
I went with
/\{\{([^{}]+)}}/g
- I don't want to be more restrictive as I technically required for the function to work and be predictable. I might change my mind if I could add another feature that would need those bytes :) Allowing dot-notation in placeholders would be awesome, but I don't see how that could work without adding another function and the keywords alone would bring it above 140 again.