Created
April 20, 2014 23:21
-
-
Save keeyip/11127842 to your computer and use it in GitHub Desktop.
Small, fast, compiled templating engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var tpl = (function() { | |
var slice = Array.prototype.slice; | |
function first(xs) { | |
return xs[0] | |
} | |
function rest(xs) { | |
return slice.call(xs, 1); | |
} | |
function flatten(xs) { | |
var flat = [] | |
if (!xs || !xs[0]) return flat; | |
var n = xs.length, | |
x | |
for (var i=0; i < n; i++) { | |
x = xs[i]; | |
if (x[0]) { | |
flat = flat.concat(flatten(x)); | |
break; | |
} | |
flat.push(x); | |
} | |
return flat; | |
} | |
function escapeHtml(str) { | |
return str | |
.replace(/&/g, '&') | |
.replace(/</g, '<') | |
.replace(/>/g, '>') | |
} | |
function make_tag(className) { | |
return { | |
_type: 'tag', | |
tagName: 'div', | |
className: className || '', | |
children: flatten(rest(arguments)) | |
} | |
} | |
function make_input(className) { | |
return make_tag(className, { | |
_type: 'input', | |
className: '', | |
children: flatten(rest(arguments)) | |
}) | |
} | |
function make_value(key) { | |
return { | |
_type: 'value', | |
key: key | |
} | |
} | |
function make_text(str) { | |
return { | |
_type: 'text', | |
str: escapeHtml(str) | |
} | |
} | |
function make_html(str) { | |
return { | |
_type: 'html', | |
str: str | |
} | |
} | |
function make_iterator(key) { | |
return { | |
_type: 'iterator', | |
key: key, | |
children: flatten(rest(arguments)) | |
} | |
} | |
function newOptimizer(lambdas) { | |
return { | |
lambdas: lambdas, | |
js: [], | |
currentJsString: '', | |
concat: function(str) { | |
this.currentJsString += str; | |
}, | |
flushCurrentJsString: function() { | |
if (this.js.length) this.js.push('+') | |
if (this.currentJsString) this.js.push(JSON.stringify(this.currentJsString)) | |
this.currentJsString = ''; | |
}, | |
next: function(str) { | |
this.flushCurrentJsString(); | |
if (str) { | |
if (this.js.length > 0) this.js.push('+'); | |
this.js.push(str); | |
} | |
}, | |
newLambda: function(str) { | |
this.lambdas.push(str); | |
return 'lambda' + this.lambdas.length; | |
}, | |
finalizeLambdas: function() { | |
if (this.lambdas.length === 0) return ''; | |
return 'var ' + this.lambdas.map(function(lambdaStr, i) { | |
var lambdaName = 'lambda' + (i+1); | |
return lambdaName + '=' + lambdaStr; | |
}).join(',') + ';\n'; | |
}, | |
toFunction: function(decls) { | |
return new Function('data', (decls||'') + 'return ' + this.js.join(' ')); | |
} | |
} | |
} | |
function compile(node) { | |
var optimizer = newOptimizer([]); | |
_compile(node, optimizer); | |
optimizer.flushCurrentJsString(); | |
return optimizer.toFunction(optimizer.finalizeLambdas()); | |
} | |
var COMPILE = { | |
tag: function(node, optimizer) { | |
var openTag = '<' + node.tagName + ' class="'+node.className+'">'; | |
var closeTag = '</' + node.tagName + '>'; | |
optimizer.concat(openTag); | |
var n = node.children.length; | |
for(var i=0; i < n; i++) { | |
_compile(node.children[i], optimizer); | |
} | |
optimizer.concat(closeTag); | |
}, | |
value: function(node, optimizer) { | |
optimizer.next(escapeHtml('data["'+node.key+'"]')); | |
}, | |
input: function(node, optimizer) { | |
optimizer.concat('<input class="'+node.className+'" value="') | |
var s = newOptimizer(optimizer.lambdas); | |
var n = node.children.length; | |
for(var i=0; i < n; i++) { | |
_compile(node.children[i], s); | |
} | |
s.flushCurrentJsString(); | |
var lambdaName = optimizer.newLambda(s.toFunction().toString()); | |
optimizer.next(lambdaName + '(data)'); | |
optimizer.concat('"/>') | |
}, | |
text: function(node, optimizer) { | |
optimizer.concat(node.str) | |
}, | |
html: function(node, optimizer) { | |
optimizer.concat(node.str) | |
}, | |
iterator: function(node, optimizer) { | |
var buffer = ['data["'+node.key+'"].map(']; | |
var s = newOptimizer(optimizer.lambdas); | |
var n = node.children.length; | |
for(var i=0; i < n; i++) { | |
_compile(node.children[i], s); | |
} | |
s.flushCurrentJsString(); | |
var lambdaName = optimizer.newLambda(s.toFunction().toString()); | |
buffer.push(lambdaName); | |
buffer.push(').join("")'); | |
optimizer.next(buffer.join('')) | |
} | |
} | |
function _compile(node, optimizer) { | |
COMPILE[node._type](node, optimizer); | |
} | |
return { | |
make_text: make_text, | |
make_tag: make_tag, | |
make_input: make_input, | |
make_value: make_value, | |
make_html: make_html, | |
make_iterator: make_iterator, | |
compile: compile | |
} | |
})(); | |
/* | |
var h = tpl.make_tag, | |
t = tpl.make_text, | |
v = tpl.make_value, | |
each = tpl.make_iterator; | |
var gridTemplate = | |
h("grid", | |
h("row header", | |
h("cell", t("NAME"))), | |
each('rows', | |
h("row", | |
h("cell", v("name"))))); | |
var f = tpl.compile(gridTemplate); | |
f | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment