jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
A brief comparison of some JavaScript templating engines on a short template: 6 header tags, and 10 list items.
Note: When adding a new test, please ensure that your test returns the same HTML string (or equivalent DOM fragment) as the others.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"></script>
<!-- Milk.js -->
<script>
(function() {
var Expand, Find, Milk, Parse, TemplateCache, key;
var __slice = Array.prototype.slice;
TemplateCache = {};
Find = function(name, stack, value) {
var ctx, i, part, parts, _i, _len, _ref, _ref2, _ref3;
if (value == null) {
value = null;
}
if (name === '.') {
return stack[stack.length - 1];
}
_ref = name.split(/\./), name = _ref[0], parts = 2 <= _ref.length ? __slice.call(_ref, 1) : [];
for (i = _ref2 = stack.length - 1, _ref3 = -1; (_ref2 <= _ref3 ? i < _ref3 : i > _ref3); (_ref2 <= _ref3 ? i += 1 : i -= 1)) {
if (stack[i] == null) {
continue;
}
if (!(typeof stack[i] === 'object' && name in (ctx = stack[i]))) {
continue;
}
value = ctx[name];
break;
}
for (_i = 0, _len = parts.length; _i < _len; _i++) {
part = parts[_i];
value = Find(part, [value]);
}
if (value instanceof Function) {
value = (function(value) {
return function() {
var val;
val = value.apply(ctx, arguments);
return (val instanceof Function) && val.apply(null, arguments) || val;
};
})(value);
}
return value;
};
Expand = function() {
var args, f, obj, tmpl;
obj = arguments[0], tmpl = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
return ((function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = tmpl.length; _i < _len; _i++) {
f = tmpl[_i];
_results.push(f.call.apply(f, [obj].concat(__slice.call(args))));
}
return _results;
})()).join('');
};
Parse = function(template, delimiters, section) {
var BuildRegex, buffer, buildInterpolationTag, buildInvertedSectionTag, buildPartialTag, buildSectionTag, cache, content, contentEnd, d, error, escape, isStandalone, match, name, parseError, pos, sectionInfo, tag, tagPattern, tmpl, type, whitespace, _name, _ref, _ref2, _ref3;
if (delimiters == null) {
delimiters = ['{{', '}}'];
}
if (section == null) {
section = null;
}
cache = (TemplateCache[_name = delimiters.join(' ')] || (TemplateCache[_name] = {}));
if (template in cache) {
return cache[template];
}
buffer = [];
BuildRegex = function() {
var tagClose, tagOpen;
tagOpen = delimiters[0], tagClose = delimiters[1];
return RegExp("([\\s\\S]*?)([" + ' ' + "\\t]*)(?:" + tagOpen + "\\s*(?:(!)\\s*([\\s\\S]+?)|(=)\\s*([\\s\\S]+?)\\s*=|({)\\s*(\\w[\\S]*?)\\s*}|([^0-9a-zA-Z._!={]?)\\s*([\\w.][\\S]*?))\\s*" + tagClose + ")", "gm");
};
tagPattern = BuildRegex();
tagPattern.lastIndex = pos = (section || {
start: 0
}).start;
parseError = function(pos, msg) {
var carets, e, endOfLine, error, indent, key, lastLine, lastTag, lineNo, parsedLines, tagStart;
(endOfLine = /$/gm).lastIndex = pos;
endOfLine.exec(template);
parsedLines = template.substr(0, pos).split('\n');
lineNo = parsedLines.length;
lastLine = parsedLines[lineNo - 1];
tagStart = contentEnd + whitespace.length;
lastTag = template.substr(tagStart + 1, pos - tagStart - 1);
indent = new Array(lastLine.length - lastTag.length + 1).join(' ');
carets = new Array(lastTag.length + 1).join('^');
lastLine = lastLine + template.substr(pos, endOfLine.lastIndex - pos);
error = new Error();
for (key in e = {
"message": "" + msg + "\n\nLine " + lineNo + ":\n" + lastLine + "\n" + indent + carets,
"error": msg,
"line": lineNo,
"char": indent.length,
"tag": lastTag
}) {
error[key] = e[key];
}
return error;
};
while (match = tagPattern.exec(template)) {
_ref = match.slice(1, 3), content = _ref[0], whitespace = _ref[1];
type = match[3] || match[5] || match[7] || match[9];
tag = match[4] || match[6] || match[8] || match[10];
contentEnd = (pos + content.length) - 1;
pos = tagPattern.lastIndex;
isStandalone = (contentEnd === -1 || template.charAt(contentEnd) === '\n') && ((_ref2 = template.charAt(pos)) === void 0 || _ref2 === '' || _ref2 === '\r' || _ref2 === '\n');
if (content) {
buffer.push((function(content) {
return function() {
return content;
};
})(content));
}
if (isStandalone && (type !== '' && type !== '&' && type !== '{')) {
if (template.charAt(pos) === '\r') {
pos += 1;
}
if (template.charAt(pos) === '\n') {
pos += 1;
}
} else if (whitespace) {
buffer.push((function(whitespace) {
return function() {
return whitespace;
};
})(whitespace));
contentEnd += whitespace.length;
whitespace = '';
}
switch (type) {
case '!':
break;
case '':
case '&':
case '{':
buildInterpolationTag = function(name, is_unescaped) {
return function(context) {
var value, _ref;
if ((value = (_ref = Find(name, context)) != null ? _ref : '') instanceof Function) {
value = Expand.apply(null, [this, Parse("" + (value()))].concat(__slice.call(arguments)));
}
if (!is_unescaped) {
value = this.escape("" + value);
}
return "" + value;
};
};
buffer.push(buildInterpolationTag(tag, type));
break;
case '>':
buildPartialTag = function(name, indentation) {
return function(context, partials) {
var partial;
partial = partials(name).toString();
if (indentation) {
partial = partial.replace(/^(?=.)/gm, indentation);
}
return Expand.apply(null, [this, Parse(partial)].concat(__slice.call(arguments)));
};
};
buffer.push(buildPartialTag(tag, whitespace));
break;
case '#':
case '^':
sectionInfo = {
name: tag,
start: pos,
error: parseError(tagPattern.lastIndex, "Unclosed section '" + tag + "'!")
};
_ref3 = Parse(template, delimiters, sectionInfo), tmpl = _ref3[0], pos = _ref3[1];
sectionInfo['#'] = buildSectionTag = function(name, delims, raw) {
return function(context) {
var parsed, result, v, value;
value = Find(name, context) || [];
tmpl = value instanceof Function ? value(raw) : raw;
if (!(value instanceof Array)) {
value = [value];
}
parsed = Parse(tmpl || '', delims);
context.push(value);
result = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = value.length; _i < _len; _i++) {
v = value[_i];
context[context.length - 1] = v;
_results.push(Expand.apply(null, [this, parsed].concat(__slice.call(arguments))));
}
return _results;
}).apply(this, arguments);
context.pop();
return result.join('');
};
};
sectionInfo['^'] = buildInvertedSectionTag = function(name, delims, raw) {
return function(context) {
var value;
value = Find(name, context) || [];
if (!(value instanceof Array)) {
value = [1];
}
value = value.length === 0 ? Parse(raw, delims) : [];
return Expand.apply(null, [this, value].concat(__slice.call(arguments)));
};
};
buffer.push(sectionInfo[type](tag, delimiters, tmpl));
break;
case '/':
if (section == null) {
error = "End Section tag '" + tag + "' found, but not in section!";
} else if (tag !== (name = section.name)) {
error = "End Section tag closes '" + tag + "'; expected '" + name + "'!";
}
if (error) {
throw parseError(tagPattern.lastIndex, error);
}
template = template.slice(section.start, (contentEnd + 1) || 9e9);
cache[template] = buffer;
return [template, pos];
case '=':
if ((delimiters = tag.split(/\s+/)).length !== 2) {
error = "Set Delimiters tags should have two and only two values!";
}
if (error) {
throw parseError(tagPattern.lastIndex, error);
}
escape = /[-[\]{}()*+?.,\\^$|#]/g;
delimiters = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = delimiters.length; _i < _len; _i++) {
d = delimiters[_i];
_results.push(d.replace(escape, "\\$&"));
}
return _results;
})();
tagPattern = BuildRegex();
break;
default:
throw parseError(tagPattern.lastIndex, "Unknown tag type -- " + type);
}
tagPattern.lastIndex = pos != null ? pos : template.length;
}
if (section != null) {
throw section.error;
}
if (template.length !== pos) {
buffer.push(function() {
return template.slice(pos);
});
}
return cache[template] = buffer;
};
Milk = {
VERSION: '1.2.0',
helpers: [],
partials: null,
escape: function(value) {
var entities;
entities = {
'&': 'amp',
'"': 'quot',
'<': 'lt',
'>': 'gt'
};
return value.replace(/[&"<>]/g, function(ch) {
return "&" + entities[ch] + ";";
});
},
render: function(template, data, partials) {
var context;
if (partials == null) {
partials = null;
}
if (!((partials || (partials = this.partials || {})) instanceof Function)) {
partials = (function(partials) {
return function(name) {
if (!(name in partials)) {
throw "Unknown partial '" + name + "'!";
}
return Find(name, [partials]);
};
})(partials);
}
context = this.helpers instanceof Array ? this.helpers : [this.helpers];
return Expand(this, Parse(template), context.concat([data]), partials);
}
};
if (typeof exports != "undefined" && exports !== null) {
for (key in Milk) {
exports[key] = Milk[key];
}
} else {
this.Milk = Milk;
}
}).call(this);
</script>
<script src="http://terrainformatica.com/kite/kite.js"></script>
<script src="http://github.com/downloads/wycats/handlebars.js/handlebars-0.9.0.pre.4.js"></script>
<!-- https://gist.githubusercontent.com/jashkenas/550881/raw/29bb186167079c0b33ab6e9d50d779f37860cfa4/micro.js -->
<script>
(function() {
var cache = {};
this.tmpl = function tmpl(str, data) {
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str.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('');");
// Provide some basic currying to the user
return data ? fn(data) : fn;
};
})();
</script>
<!-- https://gist.githubusercontent.com/akorchev/860205/raw/8444586913ab249c619671b8f5054fc92dddf643/micro2.js -->
<script>
(function() {
var cache = {};
this.tmpl2 = function tmpl(str, data) {
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("data",
"var p=[];" +
// Introduce the data as local variables using with(){}
"p.push('" +
// Convert the template into pure JavaScript
str.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('');");
// Provide some basic currying to the user
return data ? fn(data) : fn;
};
})();
</script>
<!-- https://gist.githubusercontent.com/akorchev/860240/raw/cd98cacbdeee7eb2cfb2ca3ca76638dae2a5b1af/micro3.js -->
<script>
(function() {
this.tmpl3 = function tmpl(str, data) {
var value = "var out = ''; out+=" + "'" +
str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^%]*%>)/g,"\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(/<%=(.+?)%>/g, "'; out += $1; out += '")
.split("<%").join("';")
.split("%>").join("out+='")
+ "'; return out;";
return new Function("data", value);
}
})();
</script>
<!-- http://github.com/creationix/haml-js/raw/master/lib/haml.js -->
<script>
// <![CDATA[
var Haml;
(function () {
var matchers, self_close_tags, embedder, forceXML, escaperName, escapeHtmlByDefault;
function html_escape(text) {
return (text + "").
replace(/&/g, "&").
replace(/</g, "<").
replace(/>/g, ">").
replace(/\"/g, """);
}
function render_attribs(attribs) {
var key, value, result = [];
for (key in attribs) {
if (key !== '_content' && attribs.hasOwnProperty(key)) {
switch (attribs[key]) {
case 'undefined':
case 'false':
case 'null':
case '""':
break;
default:
try {
value = JSON.parse("[" + attribs[key] +"]")[0];
if (value === true) {
value = key;
} else if (typeof value === 'string' && embedder.test(value)) {
value = '" +\n' + parse_interpol(html_escape(value)) + ' +\n"';
} else {
value = html_escape(value);
}
result.push(" " + key + '=\\"' + value + '\\"');
} catch (e) {
result.push(" " + key + '=\\"" + '+escaperName+'(' + attribs[key] + ') + "\\"');
}
}
}
}
return result.join("");
}
// Parse the attribute block using a state machine
function parse_attribs(line) {
var attributes = {},
l = line.length,
i, c,
count = 1,
quote = false,
skip = false,
open, close, joiner, seperator,
pair = {
start: 1,
middle: null,
end: null
};
if (!(l > 0 && (line.charAt(0) === '{' || line.charAt(0) === '('))) {
return {
_content: line[0] === ' ' ? line.substr(1, l) : line
};
}
open = line.charAt(0);
close = (open === '{') ? '}' : ')';
joiner = (open === '{') ? ':' : '=';
seperator = (open === '{') ? ',' : ' ';
function process_pair() {
if (typeof pair.start === 'number' &&
typeof pair.middle === 'number' &&
typeof pair.end === 'number') {
var key = line.substr(pair.start, pair.middle - pair.start).trim(),
value = line.substr(pair.middle + 1, pair.end - pair.middle - 1).trim();
attributes[key] = value;
}
pair = {
start: null,
middle: null,
end: null
};
}
for (i = 1; count > 0; i += 1) {
// If we reach the end of the line, then there is a problem
if (i > l) {
throw "Malformed attribute block";
}
c = line.charAt(i);
if (skip) {
skip = false;
} else {
if (quote) {
if (c === '\\') {
skip = true;
}
if (c === quote) {
quote = false;
}
} else {
if (c === '"' || c === "'") {
quote = c;
}
if (count === 1) {
if (c === joiner) {
pair.middle = i;
}
if (c === seperator || c === close) {
pair.end = i;
process_pair();
if (c === seperator) {
pair.start = i + 1;
}
}
}
if (c === open || c === "(") {
count += 1;
}
if (c === close || (count > 1 && c === ")")) {
count -= 1;
}
}
}
}
attributes._content = line.substr(i, line.length);
return attributes;
}
// Split interpolated strings into an array of literals and code fragments.
function parse_interpol(value) {
var items = [],
pos = 0,
next = 0,
match;
while (true) {
// Match up to embedded string
next = value.substr(pos).search(embedder);
if (next < 0) {
if (pos < value.length) {
items.push(JSON.stringify(value.substr(pos)));
}
break;
}
items.push(JSON.stringify(value.substr(pos, next)));
pos += next;
// Match embedded string
match = value.substr(pos).match(embedder);
next = match[0].length;
if (next < 0) { break; }
if(match[1] === "#"){
items.push(escaperName+"("+(match[2] || match[3])+")");
}else{
//unsafe!!!
items.push(match[2] || match[3]);
}
pos += next;
}
return items.filter(function (part) { return part && part.length > 0}).join(" +\n");
}
// Used to find embedded code in interpolated strings.
embedder = /([#!])\{([^}]*)\}/;
self_close_tags = ["meta", "img", "link", "br", "hr", "input", "area", "base"];
// All matchers' regexps should capture leading whitespace in first capture
// and trailing content in last capture
matchers = [
// html tags
{
name: "html tags",
regexp: /^(\s*)((?:[.#%][a-z_\-][a-z0-9_:\-]*)+)(.*)$/i,
process: function () {
var line_beginning, tag, classes, ids, attribs, content, whitespaceSpecifier, whitespace={}, output;
line_beginning = this.matches[2];
classes = line_beginning.match(/\.([a-z_\-][a-z0-9_\-]*)/gi);
ids = line_beginning.match(/\#([a-z_\-][a-z0-9_\-]*)/gi);
tag = line_beginning.match(/\%([a-z_\-][a-z0-9_:\-]*)/gi);
// Default to <div> tag
tag = tag ? tag[0].substr(1, tag[0].length) : 'div';
attribs = this.matches[3];
if (attribs) {
attribs = parse_attribs(attribs);
if (attribs._content) {
var leader0 = attribs._content.charAt(0),
leader1 = attribs._content.charAt(1),
leaderLength = 0;
if(leader0 == "<"){
leaderLength++;
whitespace.inside = true;
if(leader1 == ">"){
leaderLength++;
whitespace.around = true;
}
}else if(leader0 == ">"){
leaderLength++;
whitespace.around = true;
if(leader1 == "<"){
leaderLength++;
whitespace.inside = true;
}
}
attribs._content = attribs._content.substr(leaderLength);
//once we've identified the tag and its attributes, the rest is content.
// this is currently trimmed for neatness.
this.contents.unshift(attribs._content.trim());
delete(attribs._content);
}
} else {
attribs = {};
}
if (classes) {
classes = classes.map(function (klass) {
return klass.substr(1, klass.length);
}).join(' ');
if (attribs['class']) {
try {
attribs['class'] = JSON.stringify(classes + " " + JSON.parse(attribs['class']));
} catch (e) {
attribs['class'] = JSON.stringify(classes + " ") + " + " + attribs['class'];
}
} else {
attribs['class'] = JSON.stringify(classes);
}
}
if (ids) {
ids = ids.map(function (id) {
return id.substr(1, id.length);
}).join(' ');
if (attribs.id) {
attribs.id = JSON.stringify(ids + " ") + attribs.id;
} else {
attribs.id = JSON.stringify(ids);
}
}
attribs = render_attribs(attribs);
content = this.render_contents();
if (content === '""') {
content = '';
}
if(whitespace.inside){
if(content.length==0){
content='" "'
}else{
try{ //remove quotes if they are there
content = '" '+JSON.parse(content)+' "';
}catch(e){
content = '" "+\n'+content+'+\n" "';
}
}
}
if (forceXML ? content.length > 0 : self_close_tags.indexOf(tag) == -1) {
output = '"<' + tag + attribs + '>"' +
(content.length > 0 ? ' + \n' + content : "") +
' + \n"</' + tag + '>"';
} else {
output = '"<' + tag + attribs + ' />"';
}
if(whitespace.around){
//output now contains '"<b>hello</b>"'
//we need to crack it open to insert whitespace.
output = '" '+output.substr(1, output.length - 2)+' "';
}
return output;
}
},
// each loops
{
name: "each loop",
regexp: /^(\s*)(?::for|:each)\s+(?:([a-z_][a-z_\-]*),\s*)?([a-z_][a-z_\-]*)\s+in\s+(.*)(\s*)$/i,
process: function () {
var ivar = this.matches[2] || '__key__', // index
vvar = this.matches[3], // value
avar = this.matches[4], // array
rvar = '__result__'; // results
if (this.matches[5]) {
this.contents.unshift(this.matches[5]);
}
return '(function () { ' +
'var ' + rvar + ' = [], ' + ivar + ', ' + vvar + '; ' +
'for (' + ivar + ' in ' + avar + ') { ' +
'if (' + avar + '.hasOwnProperty(' + ivar + ')) { ' +
vvar + ' = ' + avar + '[' + ivar + ']; ' +
rvar + '.push(\n' + (this.render_contents() || "''") + '\n); ' +
'} } return ' + rvar + '.join(""); }).call(this)';
}
},
// if statements
{
name: "if",
regexp: /^(\s*):if\s+(.*)\s*$/i,
process: function () {
var condition = this.matches[2];
this.pushIfCondition([condition]);
return '(function () { ' +
'if (' + condition + ') { ' +
'return (\n' + (this.render_contents() || '') + '\n);' +
'} else { return ""; } }).call(this)';
}
},
// else if statements
{
name: "else if",
regexp: /^(\s*):else if\s+(.*)\s*$/i,
process: function () {
var condition = this.matches[2],
conditionsArray = this.getIfConditions()[this.getIfConditions().length - 1],
ifArray = [],
ifStatement;
for (var i=0, l=conditionsArray.length; i<l; i++) {
ifArray.push('! (' + conditionsArray[i]+')');
}
conditionsArray.push(condition);
ifArray.push(condition);
ifStatement = 'if (' + ifArray.join(' && ') + ') { ';
return '(function () { ' +
ifStatement +
'return (\n' + (this.render_contents() || '') + '\n);' +
'} else { return ""; } }).call(this)';
}
},
// else statements
{
name: "else",
regexp: /^(\s*):else\s*$/i,
process: function () {
var conditionsArray = this.popIfCondition(),
ifArray = [],
ifStatement;
for (var i=0, l=conditionsArray.length; i<l; i++) {
ifArray.push('! (' + conditionsArray[i]+')');
}
ifStatement = 'if (' + ifArray.join(' && ') + ') { ';
return '(function () { ' +
ifStatement +
'return (\n' + (this.render_contents() || '') + '\n);' +
'} else { return ""; } }).call(this)';
}
},
// silent-comments
{
name: "silent-comments",
regexp: /^(\s*)-#\s*(.*)\s*$/i,
process: function () {
return '""';
}
},
//html-comments
{
name: "silent-comments",
regexp: /^(\s*)\/\s*(.*)\s*$/i,
process: function () {
this.contents.unshift(this.matches[2]);
return '"<!--'+this.contents.join('\\n').replace(/\"/g, '\\"')+'-->"';
}
},
// raw js
{
name: "rawjs",
regexp: /^(\s*)-\s*(.*)\s*$/i,
process: function () {
this.contents.unshift(this.matches[2]);
return '"";' + this.contents.join("\n")+"; _$output = _$output ";
}
},
// raw js
{
name: "pre",
regexp: /^(\s*):pre(\s+(.*)|$)/i,
process: function () {
this.contents.unshift(this.matches[2]);
return '"<pre>"+\n' + JSON.stringify(this.contents.join("\n"))+'+\n"</pre>"';
}
},
// declarations
{
name: "doctype",
regexp: /^()!!!(?:\s*(.*))\s*$/,
process: function () {
var line = '';
switch ((this.matches[2] || '').toLowerCase()) {
case '':
// XHTML 1.0 Transitional
line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
break;
case 'strict':
case '1.0':
// XHTML 1.0 Strict
line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
break;
case 'frameset':
// XHTML 1.0 Frameset
line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';
break;
case '5':
// XHTML 5
line = '<!DOCTYPE html>';
break;
case '1.1':
// XHTML 1.1
line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
break;
case 'basic':
// XHTML Basic 1.1
line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">';
break;
case 'mobile':
// XHTML Mobile 1.2
line = '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">';
break;
case 'xml':
// XML
line = "<?xml version='1.0' encoding='utf-8' ?>";
break;
case 'xml iso-8859-1':
// XML iso-8859-1
line = "<?xml version='1.0' encoding='iso-8859-1' ?>";
break;
}
return JSON.stringify(line + "\n");
}
},
// Embedded markdown. Needs to be added to exports externally.
{
name: "markdown",
regexp: /^(\s*):markdown\s*$/i,
process: function () {
return parse_interpol(exports.Markdown.encode(this.contents.join("\n")));
}
},
// script blocks
{
name: "script",
regexp: /^(\s*):(?:java)?script\s*$/,
process: function () {
return parse_interpol('\n<script>\n' +
'//<![CDATA[\n' +
this.contents.join("\n") +
"\n//]]>\n</script>\n");
}
},
// css blocks
{
name: "css",
regexp: /^(\s*):css\s*$/,
process: function () {
return JSON.stringify('<style type="text/css">\n' +
this.contents.join("\n") +
"\n</style>");
}
}
];
function compile(lines) {
var block = false,
output = [],
ifConditions = [];
// If lines is a string, turn it into an array
if (typeof lines === 'string') {
lines = lines.trim().replace(/\n\r|\r/g, '\n').split('\n');
}
lines.forEach(function(line) {
var match, found = false;
// Collect all text as raw until outdent
if (block) {
match = block.check_indent.exec(line);
if (match) {
block.contents.push(match[1] || "");
return;
} else {
output.push(block.process());
block = false;
}
}
matchers.forEach(function (matcher) {
if (!found) {
match = matcher.regexp.exec(line);
if (match) {
block = {
contents: [],
indent_level: (match[1]),
matches: match,
check_indent: new RegExp("^(?:\\s*|" + match[1] + " (.*))$"),
process: matcher.process,
getIfConditions: function() {
return ifConditions;
},
pushIfCondition: function(condition) {
ifConditions.push(condition);
},
popIfCondition: function() {
return ifConditions.pop();
},
render_contents: function () {
return compile(this.contents);
}
};
found = true;
}
}
});
// Match plain text
if (!found) {
output.push(function () {
// Escaped plain text
if (line[0] === '\\') {
return parse_interpol(line.substr(1, line.length));
}
function escapedLine(){
try {
return escaperName+'('+JSON.stringify(JSON.parse(line)) +')';
} catch (e2) {
return escaperName+'(' + line + ')';
}
}
function unescapedLine(){
try {
return parse_interpol(JSON.parse(line));
} catch (e) {
return line;
}
}
// always escaped
if((line.substr(0, 2) === "&=")) {
line = line.substr(2, line.length).trim();
return escapedLine();
}
//never escaped
if((line.substr(0, 2) === "!=")) {
line = line.substr(2, line.length).trim();
return unescapedLine();
}
// sometimes escaped
if ( (line[0] === '=')) {
line = line.substr(1, line.length).trim();
if(escapeHtmlByDefault){
return escapedLine();
}else{
return unescapedLine();
}
}
// Plain text
return parse_interpol(line);
}());
}
});
if (block) {
output.push(block.process());
}
var txt = output.filter(function (part) { return part && part.length > 0}).join(" +\n");
if(txt.length == 0){
txt = '""';
}
return txt;
};
function optimize(js) {
var new_js = [], buffer = [], part, end;
function flush() {
if (buffer.length > 0) {
new_js.push(JSON.stringify(buffer.join("")) + end);
buffer = [];
}
}
js.replace(/\n\r|\r/g, '\n').split('\n').forEach(function (line) {
part = line.match(/^(\".*\")(\s*\+\s*)?$/);
if (!part) {
flush();
new_js.push(line);
return;
}
end = part[2] || "";
part = part[1];
try {
buffer.push(JSON.parse(part));
} catch (e) {
flush();
new_js.push(line);
}
});
flush();
return new_js.join("\n");
};
function render(text, options) {
options = options || {};
text = text || "";
var js = compile(text, options);
if (options.optimize) {
js = Haml.optimize(js);
}
return execute(js, options.context || Haml, options.locals);
};
function execute(js, self, locals) {
return (function () {
with(locals || {}) {
try {
var _$output;
eval("_$output =" + js );
return _$output; //set in eval
} catch (e) {
return "\n<pre class='error'>" + html_escape(e.stack) + "</pre>\n";
}
}
}).call(self);
};
Haml = function Haml(haml, config) {
if(typeof(config) != "object"){
forceXML = config;
config = {};
}
var escaper;
if(config.customEscape){
escaper = "";
escaperName = config.customEscape;
}else{
escaper = html_escape.toString() + "\n";
escaperName = "html_escape";
}
escapeHtmlByDefault = (config.escapeHtmlByDefault || config.escapeHTML || config.escape_html);
var js = optimize(compile(haml));
var str = "with(locals || {}) {\n" +
" try {\n" +
" var _$output=" + js + ";\n return _$output;" +
" } catch (e) {\n" +
" return \"\\n<pre class='error'>\" + "+escaperName+"(e.stack) + \"</pre>\\n\";\n" +
" }\n" +
"}"
try{
var f = new Function("locals", escaper + str );
return f;
}catch(e){
if ( typeof(console) !== 'undefined' ) { console.error(str); }
throw e;
}
}
Haml.compile = compile;
Haml.optimize = optimize;
Haml.render = render;
Haml.execute = execute;
Haml.html_escape = html_escape;
}());
// Hook into module system
if (typeof module !== 'undefined') {
module.exports = Haml;
}
//]]>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.7.1/coffee-script.min.js"></script>
<!-- http://sstephenson.github.com/eco/dist/eco.js -->
<script>
/**
* Eco Compiler v1.1.0-pre
* http://github.com/sstephenson/eco
*
* Copyright (c) 2010 Sam Stephenson
* Released under the MIT License
*/
this.eco=function(j){return function g(h){var b,c={id:h,exports:{}};if(b=j[h]){b(c,g,c.exports);return c.exports}else throw"Cannot find module '"+h+"'";}}({eco:function(j,g){g("coffee-script");j.exports=g("eco/compiler")},"eco/compiler":function(j,g){(function(){var h,b,c,d,f;h=g("coffee-script");d=g("eco/util").indent;j.exports=c=function(a){(new Function("module",b(a)))(a={});return a.exports};c.preprocess=f=g("eco/preprocessor").preprocess;c.compile=b=function(a,e){var i,k;k=typeof(i=typeof e===
"undefined"||e===null?undefined:e.identifier)!=="undefined"&&i!==null?i:"module.exports";k.match(/\./)||(k="var "+k);i=h.compile(f(a),{noWrap:true});return""+k+" = function(__obj) {\n if (!__obj) __obj = {};\n var __out = [], __capture = function(callback) {\n var out = __out, result;\n __out = [];\n callback.call(this);\n result = __out.join('');\n __out = out;\n return __safe(result);\n }, __sanitize = function(value) {\n if (value && value.ecoSafe) {\n return value;\n } else if (typeof value !== 'undefined' && value != null) {\n return __escape(value);\n } else {\n return '';\n }\n }, __safe, __objSafe = __obj.safe, __escape = __obj.escape;\n __safe = __obj.safe = function(value) {\n if (value && value.ecoSafe) {\n return value;\n } else {\n if (!(typeof value !== 'undefined' && value != null)) value = '';\n var result = new String(value);\n result.ecoSafe = true;\n return result;\n }\n };\n if (!__escape) {\n __escape = __obj.escape = function(value) {\n return ('' + value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n };\n }\n (function() {\n"+
d(i,4)+"\n }).call(__obj);\n __obj.safe = __objSafe, __obj.escape = __escape;\n return __out.join('');\n};"};c.render=function(a,e){return c(a)(e)};if(g.extensions)g.extensions[".eco"]=function(a,e){var i;i=g("fs").readFileSync(e,"utf-8");return a._compile(b(i),e)};else g.registerExtension&&g.registerExtension(".eco",b)}).call(this)},"eco/preprocessor":function(j,g,h){(function(){var b,c,d,f=function(a,e){return function(){return a.apply(e,arguments)}};c=g("eco/scanner").Scanner;d=g("eco/util");
h.preprocess=function(a){return(new b(a)).preprocess()};h.Preprocessor=function(){b=function(a){this.scanner=new c(a);this.output="";this.level=0;this.options={};this.captures=[];return this};b.prototype.preprocess=function(){for(;!this.scanner.done;)this.scanner.scan(f(function(a){return this[a[0]].apply(this,a.slice(1))},this));return this.output};b.prototype.record=function(a){this.output+=d.repeat(" ",this.level);return this.output+=a+"\n"};b.prototype.printString=function(a){return a.length?
this.record("__out.push "+d.inspectString(a)):null};b.prototype.beginCode=function(a){return this.options=a};b.prototype.recordCode=function(a){return a!=="end"?this.options.print?this.options.safe?this.record("__out.push "+a):this.record("__out.push __sanitize "+a):this.record(a):null};b.prototype.indent=function(a){this.level++;if(a){this.record("__capture "+a);this.captures.unshift(this.level);return this.indent()}};b.prototype.dedent=function(){this.level--;this.level<0&&this.fail("unexpected dedent");
if(this.captures[0]===this.level){this.captures.shift();return this.dedent()}};b.prototype.fail=function(a){throw"Parse error on line "+this.scanner.lineNo+": "+a;};return b}()}).call(this)},"eco/scanner":function(j,g,h){(function(){var b,c,d,f;d=g("strscan");c=d.StringScanner;d=g("eco/util");f=d.trim;h.scan=function(a){var e;e=[];for(a=new b(a);!a.done;)a.scan(function(i){return e.push(i)});return e};h.Scanner=function(){b=function(a){this.source=a.replace(/\r\n?/g,"\n");this.scanner=new c(this.source);
this.mode="data";this.buffer="";this.lineNo=1;this.done=false;return this};b.modePatterns={data:/(.*?)(<%(([=-])?)|\n|$)/,code:/(.*?)(((:|(->|=>))\s*)?%>|\n|$)/};b.dedentablePattern=/^(end|when|else|catch|finally)(?:\W|$)/;b.prototype.scan=function(a){if(this.done)return a();else if(this.scanner.hasTerminated()){this.done=true;switch(this.mode){case "data":return a(["printString",this.flush()]);case "code":return a(["fail","unexpected end of template"])}}else{this.advance();switch(this.mode){case "data":return this.scanData(a);
case "code":return this.scanCode(a)}}};b.prototype.advance=function(){this.scanner.scanUntil(b.modePatterns[this.mode]);this.buffer+=this.scanner.getCapture(0);this.tail=this.scanner.getCapture(1);this.directive=this.scanner.getCapture(3);return this.arrow=this.scanner.getCapture(4)};b.prototype.scanData=function(a){var e;if(this.tail==="\n"){this.buffer+=this.tail;this.lineNo++;return this.scan(a)}else if(this.tail){this.mode="code";a(["printString",this.flush()]);return a(["beginCode",{print:typeof(e=
this.directive)!=="undefined"&&e!==null,safe:this.directive==="-"}])}};b.prototype.scanCode=function(a){var e;if(this.tail==="\n")return a(["fail","unexpected newline in code block"]);else if(this.tail){this.mode="data";e=f(this.flush());if(this.arrow)e+=" "+this.arrow;this.isDedentable(e)&&a(["dedent"]);a(["recordCode",e]);if(this.directive)return a(["indent",this.arrow])}};b.prototype.flush=function(){var a;a=this.buffer;this.buffer="";return a};b.prototype.isDedentable=function(a){return a.match(b.dedentablePattern)};
return b}.call(this)}).call(this)},"eco/util":function(j,g,h){(function(){var b,c;h.repeat=b=function(d,f){return Array(f+1).join(d)};h.indent=function(d,f){var a,e,i,k,l,m;m=b(" ",f);k=[];i=d.split("\n");a=0;for(e=i.length;a<e;a++){l=i[a];k.push(m+l)}return k.join("\n")};h.trim=function(d){return d.replace(/^\s+/,"").replace(/\s+$/,"")};c={"\\":"\\\\","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"};h.inspectString=function(d){return"'"+d.replace(/[\x00-\x1f\\]/g,function(f){if(f in
c)return c[f];else{f=f.charCodeAt(0).toString(16);if(f.length===1)f="0"+f;return"\\u00"+f}}).replace(/'/g,"\\'")+"'"}}).call(this)},strscan:function(j,g,h){(function(){var b;(typeof h!=="undefined"&&h!==null?h:this).StringScanner=function(){b=function(c){this.source=c.toString();this.reset();return this};b.prototype.scan=function(c){var d;return(d=c.exec(this.getRemainder()))&&d.index===0?this.setState(d,{head:this.head+d[0].length,last:this.head}):this.setState([])};b.prototype.scanUntil=function(c){if(c=
c.exec(this.getRemainder())){this.setState(c,{head:this.head+c.index+c[0].length,last:this.head});return this.source.slice(this.last,this.head)}else return this.setState([])};b.prototype.scanChar=function(){return this.scan(/./)};b.prototype.skip=function(c){if(this.scan(c))return this.match.length};b.prototype.skipUntil=function(c){if(this.scanUntil(c))return this.head-this.last};b.prototype.check=function(c){var d;return(d=c.exec(this.getRemainder()))&&d.index===0?this.setState(d):this.setState([])};
b.prototype.checkUntil=function(c){if(c=c.exec(this.getRemainder())){this.setState(c);return this.source.slice(this.head,this.head+c.index+c[0].length)}else return this.setState([])};b.prototype.peek=function(c){return this.source.substr(this.head,typeof c!=="undefined"&&c!==null?c:1)};b.prototype.getSource=function(){return this.source};b.prototype.getRemainder=function(){return this.source.slice(this.head)};b.prototype.getPosition=function(){return this.head};b.prototype.hasTerminated=function(){return this.head===
this.source.length};b.prototype.getPreMatch=function(){if(this.match)return this.source.slice(0,this.head-this.match.length)};b.prototype.getMatch=function(){return this.match};b.prototype.getPostMatch=function(){if(this.match)return this.source.slice(this.head)};b.prototype.getCapture=function(c){return this.captures[c]};b.prototype.reset=function(){return this.setState([],{head:0,last:0})};b.prototype.terminate=function(){return this.setState([],{head:this.source.length,last:this.head})};b.prototype.concat=
function(c){return this.source+=c};b.prototype.unscan=function(){if(this.match)return this.setState([],{head:this.last,last:0});else throw"nothing to unscan";};b.prototype.setState=function(c,d){var f,a;this.head=typeof(f=typeof d==="undefined"||d===null?undefined:d.head)!=="undefined"&&f!==null?f:this.head;this.last=typeof(a=typeof d==="undefined"||d===null?undefined:d.last)!=="undefined"&&a!==null?a:this.last;this.captures=c.slice(1);return this.match=c[0]};return b}()})()},"coffee-script":function(j){if(typeof CoffeeScript!==
"undefined"&&CoffeeScript!=null)j.exports=CoffeeScript;else throw"Cannot require '"+j.id+"': CoffeeScript not found";}})("eco");
</script>
<!-- https://raw.github.com/BorisMoore/jquery-tmpl/master/jquery.tmpl.min.js -->
<script>
/*
* jQuery Templates Plugin 1.0.0pre
* http://github.com/jquery/jquery-tmpl
* Requires jQuery 1.4.2
*
* Copyright 2011, Software Freedom Conservancy, Inc.
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},i=0,c=0,l=[];function g(g,d,h,e){var c={data:e||(e===0||e===false)?e:d?d.data:{},_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};g&&a.extend(c,g,{nodes:[],parent:d});if(h){c.tmpl=h;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++i;(l.length?f:b)[i]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h<m;h++){c=h;k=(h>0?this.clone(true):this).get();a(i[h])[d](k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,m,k){if(d[0]&&a.isArray(d[0])){var g=a.makeArray(arguments),h=d[0],j=h.length,i=0,f;while(i<j&&!(f=a.data(h[i++],"tmplItem")));if(f&&c)g[2]=function(b){a.tmpl.afterManip(this,b,k)};r.apply(this,g)}else r.apply(this,arguments);c=0;!e&&a.tmpl.complete(b);return this}});a.extend({tmpl:function(d,h,e,c){var i,k=!c;if(k){c=p;d=a.template[d]||a.template(null,d);f={}}else if(!d){d=c.tmpl;b[c.key]=c;c.nodes=[];c.wrapped&&n(c,c.wrapped);return a(j(c,null,c.tmpl(a,c)))}if(!d)return[];if(typeof h==="function")h=h.call(c||{});e&&e.wrapped&&n(e,e.wrapped);i=a.isArray(h)?a.map(h,function(a){return a?g(e,c,d,a):null}):[g(e,c,d,h)];return k?a(j(c,null,i)):i},tmplItem:function(b){var c;if(b instanceof a)b=b[0];while(b&&b.nodeType===1&&!(c=a.data(b,"tmplItem"))&&(b=b.parentNode));return c||p},template:function(c,b){if(b){if(typeof b==="string")b=o(b);else if(b instanceof a)b=b[0]||{};if(b.nodeType)b=a.data(b,"tmpl")||a.data(b,"tmpl",o(b.innerHTML));return typeof c==="string"?(a.template[c]=b):b}return c?typeof c!=="string"?a.template(null,c):a.template[c]||a.template(null,q.test(c)?c:a(c)):null},encode:function(a){return(""+a).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(__,$1,$2);__=[];",close:"call=$item.calls();__=call._.concat($item.wrap(call,__));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function j(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:j(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=k(c).concat(b);if(d)b=b.concat(k(d))});return b?b:k(c)}function k(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,k,g,b,c,d){var j=a.tmpl.tag[k],i,e,f;if(!j)throw"Unknown template tag: "+k;i=j._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=h(b);d=d?","+h(d)+")":c?")":"";e=c?b.indexOf(".")>-1?b+h(c):"("+b+").call($item"+d:b;f=c?e:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else f=e=i.$1||"null";g=h(g);return"');"+j[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(f).split("$1").join(e).split("$2").join(g||i.$2||"")+"__.push('"})+"');}return __;")}function n(c,b){c._wrap=j(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function h(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,h;for(e=0,p=o.length;e<p;e++){if((k=o[e]).nodeType!==1)continue;j=k.getElementsByTagName("*");for(h=j.length-1;h>=0;h--)m(j[h]);m(k)}function m(j){var p,h=j,k,e,m;if(m=j.getAttribute(d)){while(h.parentNode&&(h=h.parentNode).nodeType===1&&!(p=h.getAttribute(d)));if(p!==m){h=h.parentNode?h.nodeType===11?0:h.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[h]||f[h]);e.key=++i;b[i]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;h=a.data(j.parentNode,"tmplItem");h=h?h.key:0}if(e){k=e;while(k&&k.key!=h){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery);
</script>
<!-- https://github.com/olado/doT/raw/master/doT.js -->
<script>
// doT.js
// 2011, Laura Doktorova, https://github.com/olado/doT
// Licensed under the MIT license.
(function() {
"use strict";
var doT = {
version: '1.0.1',
templateSettings: {
evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g,
interpolate: /\{\{=([\s\S]+?)\}\}/g,
encode: /\{\{!([\s\S]+?)\}\}/g,
use: /\{\{#([\s\S]+?)\}\}/g,
useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,
define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
defineParams:/^\s*([\w$]+):([\s\S]+)/,
conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,
iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,
varname: 'it',
strip: true,
append: true,
selfcontained: false
},
template: undefined, //fn, compile template
compile: undefined //fn, for express
}, global;
if (typeof module !== 'undefined' && module.exports) {
module.exports = doT;
} else if (typeof define === 'function' && define.amd) {
define(function(){return doT;});
} else {
global = (function(){ return this || (0,eval)('this'); }());
global.doT = doT;
}
function encodeHTMLSource() {
var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/' },
matchHTML = /&(?!#?\w+;)|<|>|"|'|\//g;
return function() {
return this ? this.replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : this;
};
}
String.prototype.encodeHTML = encodeHTMLSource();
var startend = {
append: { start: "'+(", end: ")+'", endencode: "||'').toString().encodeHTML()+'" },
split: { start: "';out+=(", end: ");out+='", endencode: "||'').toString().encodeHTML();out+='"}
}, skip = /$^/;
function resolveDefs(c, block, def) {
return ((typeof block === 'string') ? block : block.toString())
.replace(c.define || skip, function(m, code, assign, value) {
if (code.indexOf('def.') === 0) {
code = code.substring(4);
}
if (!(code in def)) {
if (assign === ':') {
if (c.defineParams) value.replace(c.defineParams, function(m, param, v) {
def[code] = {arg: param, text: v};
});
if (!(code in def)) def[code]= value;
} else {
new Function("def", "def['"+code+"']=" + value)(def);
}
}
return '';
})
.replace(c.use || skip, function(m, code) {
if (c.useParams) code = code.replace(c.useParams, function(m, s, d, param) {
if (def[d] && def[d].arg && param) {
var rw = (d+":"+param).replace(/'|\\/g, '_');
def.__exp = def.__exp || {};
def.__exp[rw] = def[d].text.replace(new RegExp("(^|[^\\w$])" + def[d].arg + "([^\\w$])", "g"), "$1" + param + "$2");
return s + "def.__exp['"+rw+"']";
}
});
var v = new Function("def", "return " + code)(def);
return v ? resolveDefs(c, v, def) : v;
});
}
function unescape(code) {
return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, ' ');
}
doT.template = function(tmpl, c, def) {
c = c || doT.templateSettings;
var cse = c.append ? startend.append : startend.split, needhtmlencode, sid = 0, indv,
str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl;
str = ("var out='" + (c.strip ? str.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g,' ')
.replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,''): str)
.replace(/'|\\/g, '\\$&')
.replace(c.interpolate || skip, function(m, code) {
return cse.start + unescape(code) + cse.end;
})
.replace(c.encode || skip, function(m, code) {
needhtmlencode = true;
return cse.start + unescape(code) + cse.endencode;
})
.replace(c.conditional || skip, function(m, elsecase, code) {
return elsecase ?
(code ? "';}else if(" + unescape(code) + "){out+='" : "';}else{out+='") :
(code ? "';if(" + unescape(code) + "){out+='" : "';}out+='");
})
.replace(c.iterate || skip, function(m, iterate, vname, iname) {
if (!iterate) return "';} } out+='";
sid+=1; indv=iname || "i"+sid; iterate=unescape(iterate);
return "';var arr"+sid+"="+iterate+";if(arr"+sid+"){var "+vname+","+indv+"=-1,l"+sid+"=arr"+sid+".length-1;while("+indv+"<l"+sid+"){"
+vname+"=arr"+sid+"["+indv+"+=1];out+='";
})
.replace(c.evaluate || skip, function(m, code) {
return "';" + unescape(code) + "out+='";
})
+ "';return out;")
.replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/\r/g, '\\r')
.replace(/(\s|;|\}|^|\{)out\+='';/g, '$1').replace(/\+''/g, '')
.replace(/(\s|;|\}|^|\{)out\+=''\+/g,'$1out+=');
if (needhtmlencode && c.selfcontained) {
str = "String.prototype.encodeHTML=(" + encodeHTMLSource.toString() + "());" + str;
}
try {
return new Function(c.varname, str);
} catch (e) {
if (typeof console !== 'undefined') console.log("Could not create a template function: " + str);
throw e;
}
};
doT.compile = function(tmpl, def) {
return doT.template(tmpl, null, def);
};
}());
</script>
<!-- https://github.com/olado/doT/raw/master/doU.js -->
<script>
// doU.js
// (c) 2011, Laura Doktorova
// https://github.com/olado/doT
//
// doU is an extraction and slight modification of an excellent
// templating function from jQote2.js (jQuery plugin) by aefxx
// (http://aefxx.com/jquery-plugins/jqote2/).
//
// Modifications:
// 1. nodejs support
// 2. allow for custom template markers
// 3. only allow direct invocation of the compiled function
//
// Licensed under the MIT license.
(function() {
var doU = { version : '0.1.2' };
if (typeof module !== 'undefined' && module.exports) {
module.exports = doU;
} else {
this.doU = doU;
}
doU.templateSettings = {
begin : '{{',
end : '}}',
varname : 'it'
};
doU.template = function(tmpl, conf) {
conf = conf || doU.templateSettings;
var str = '', tb = conf.begin, te = conf.end, m, l,
arr = tmpl.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]|(\/\*[\s\S]*?\*\/)/g, '')
.split(tb).join(te +'\x1b')
.split(te);
for (m=0,l=arr.length; m < l; m++) {
str += arr[m].charAt(0) !== '\x1b' ?
"out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ?
';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ?
';out+=(' + arr[m].substr(2) + ").toString().replace(/&(?!\\w+;)/g, '&').split('<').join('<').split('>').join('>').split('" + '"' + "').join('"').split(" + '"' + "'" + '"' + ").join(''').split('/').join('/');" : ';' + arr[m].substr(1)));
}
str = ('var out="";'+str+';return out;')
.split("out+='';").join('')
.split('var out="";out+=').join('var out=');
try {
return new Function(conf.varname, str);
} catch (e) {
if (typeof console !== 'undefined') console.log("Could not create a template function: " + str);
throw e;
}
};
}());
</script>
<!-- http://github.com/aefxx/jQote2/raw/69b2053a13f5f180e696de9b3dba856a3c00678c/jquery.jqote2.js -->
<script>
/*
* jQote2 - client-side Javascript templating engine
* Copyright (C) 2010, aefxx
* http://aefxx.com/
*
* Dual licensed under the WTFPL v2 or MIT (X11) licenses
* WTFPL v2 Copyright (C) 2004, Sam Hocevar
*
* Date: Thu, Oct 21st, 2010
* Version: 0.9.7
*/
(function($) {
var JQOTE2_TMPL_UNDEF_ERROR = 'UndefinedTemplateError',
JQOTE2_TMPL_COMP_ERROR = 'TemplateCompilationError',
JQOTE2_TMPL_EXEC_ERROR = 'TemplateExecutionError';
var ARR = '[object Array]',
STR = '[object String]',
FUNC = '[object Function]';
var n = 1, tag = '%',
qreg = /^[^<]*(<[\w\W]+>)[^>]*$/,
type_of = Object.prototype.toString;
function raise(error, ext) {
throw ($.extend(error, ext), error);
}
function dotted_ns(fn) {
var ns = [];
if ( type_of.call(fn) !== ARR ) return false;
for ( var i=0,l=fn.length; i < l; i++ )
ns[i] = fn[i].jqote_id;
return ns.length ?
ns.sort().join('.').replace(/(\b\d+\b)\.(?:\1(\.|$))+/g, '$1$2') : false;
}
function lambda(tmpl, t) {
var f, fn = [], t = t || tag,
type = type_of.call(tmpl);
if ( type === FUNC )
return tmpl.jqote_id ? [tmpl] : false;
if ( type !== ARR )
return [$.jqotec(tmpl, t)];
if ( type === ARR )
for ( var i=0,l=tmpl.length; i < l; i++ )
if ( f = lambda(tmpl[i], t) ) fn.push(f[0]);
return fn.length ? fn : false;
}
$.fn.extend({
jqote: function(data, t) {
var data = type_of.call(data) === ARR ? data : [data],
dom = '';
this.each(function(i) {
var fn = $.jqotec(this, t);
for ( var j=0; j < data.length; j++ )
dom += fn.call(data[j], i, j, data, fn);
});
return dom;
}
});
$.each({app: 'append', pre: 'prepend', sub: 'html'}, function(name, method) {
$.fn['jqote'+name] = function(elem, data, t) {
var ns, regexp, str = $.jqote(elem, data, t),
$$ = !qreg.test(str) ?
function(str) {return $(document.createTextNode(str));} : $;
if ( !!(ns = dotted_ns(lambda(elem))) )
regexp = new RegExp('(^|\\.)'+ns.split('.').join('\\.(.*)?')+'(\\.|$)');
return this.each(function() {
var dom = $$(str);
$(this)[method](dom);
( dom[0].nodeType === 3 ?
$(this) : dom ).trigger('jqote.'+name, [dom, regexp]);
});
};
});
$.extend({
jqote: function(elem, data, t) {
var str = '', t = t || tag,
fn = lambda(elem);
if ( fn === false )
raise(new Error('Empty or undefined template passed to $.jqote'), {type: JQOTE2_TMPL_UNDEF_ERROR});
data = type_of.call(data) !== ARR ?
[data] : data;
for ( var i=0,l=fn.length; i < l; i++ )
for ( var j=0; j < data.length; j++ )
str += fn[i].call(data[j], i, j, data, fn[i]);
return str;
},
jqotec: function(template, t) {
var cache, elem, tmpl, t = t || tag,
type = type_of.call(template);
if ( type === STR && qreg.test(template) ) {
elem = tmpl = template;
if ( cache = $.jqotecache[template] ) return cache;
} else {
elem = type === STR || template.nodeType ?
$(template) : template instanceof jQuery ?
template : null;
if ( !elem[0] || !(tmpl = elem[0].innerHTML) && !(tmpl = elem.text()) )
raise(new Error('Empty or undefined template passed to $.jqotec'), {type: JQOTE2_TMPL_UNDEF_ERROR});
if ( cache = $.jqotecache[$.data(elem[0], 'jqote_id')] ) return cache;
}
var str = '', index,
arr = tmpl.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]/g, '')
.split('<'+t).join(t+'>\x1b')
.split(t+'>');
for ( var m=0,l=arr.length; m < l; m++ )
str += arr[m].charAt(0) !== '\x1b' ?
"out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ?
';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ?
';out+=$.jqotenc((' + arr[m].substr(2) + '));' : ';' + arr[m].substr(1)));
str = 'try{' +
('var out="";'+str+';return out;')
.split("out+='';").join('')
.split('var out="";out+=').join('var out=') +
'}catch(e){e.type="'+JQOTE2_TMPL_EXEC_ERROR+'";e.args=arguments;e.template=arguments.callee.toString();throw e;}';
try {
var fn = new Function('i, j, data, fn', str);
} catch ( e ) { raise(e, {type: JQOTE2_TMPL_COMP_ERROR}); }
index = elem instanceof jQuery ?
$.data(elem[0], 'jqote_id', n) : elem;
return $.jqotecache[index] = (fn.jqote_id = n++, fn);
},
jqotefn: function(elem) {
var type = type_of.call(elem),
index = type === STR && qreg.test(elem) ?
elem : $.data($(elem)[0], 'jqote_id');
return $.jqotecache[index] || false;
},
jqotetag: function(str) {
if ( type_of.call(str) === STR ) tag = str;
},
jqotenc: function(str) {
return s.toString()
.replace(/&(?!\w+;)/g, '&')
.split('<').join('<').split('>').join('>')
.split('"').join('"').split("'").join(''');
},
jqotecache: {}
});
$.event.special.jqote = {
add: function(obj) {
var ns, handler = obj.handler,
data = !obj.data ?
[] : type_of.call(obj.data) !== ARR ?
[obj.data] : obj.data;
if ( !obj.namespace ) obj.namespace = 'app.pre.sub';
if ( !data.length || !(ns = dotted_ns(lambda(data))) ) return;
obj.handler = function(event, dom, regexp) {
return !regexp || regexp.test(ns) ?
handler.apply(this, [event, dom]) : null;
};
}
};
})(jQuery);
</script>
<script src="http://embeddedjavascript.googlecode.com/files/ejs_0.9_alpha_1_production.js"></script>
<script src="http://github.com/pure/pure/raw/master/libs/pure.js"></script>
<div class="pure">
<h1 class='header'></h1>
<h2 class='header2'></h2>
<h3 class='header3'></h3>
<h4 class='header4'></h4>
<h5 class='header5'></h5>
<h6 class='header6'></h6>
<ul class='list'>
<li class='item'></li>
</ul>
</div>
<script src="http://github.com/douglascrockford/JSON-js/raw/master/json2.js"></script>
<script src="http://akdubya.github.com/dustjs/dist/dust-full-0.3.0.min.js"></script>
<script src="https://github.com/premasagar/tim/raw/master/tim.js"></script>
<script src="https://gist.github.com/raw/875670/d52752ead19a4eebc7237602438ae08a2541a5b5/tim-lite-cached-min.js"></script>
<script src="http://www.kuwata-lab.com/tenjin/shotenjin.js"></script>
<script src="https://github.com/QLeelulu/nTenjin/raw/master/nTenjin.js"></script>
<script>
window.mustacheTemplate = "<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#list}}<li class='item'>{{.}}</li>{{/list}}</ul></div>";
// note: exactly the same as the mustacheTemplate above.
window.kiteTemplate = "<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#list}}<li class='item'>{{.}}</li>{{/list}}</ul></div>";
window.kiteCompiledTemplate = kite(kiteTemplate); // seems like others are testing compiled versions.
window.handlebarsTemplate = Handlebars.compile("<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#each list}}<li class='item'>{{this}}</li>{{/each}}</ul></div>");
window.micro = "<div><h1 class='header'><%= header %></h1><h2 class='header2'><%= header2 %></h2><h3 class='header3'><%= header3 %></h3><h4 class='header4'><%= header4 %></h4><h5 class='header5'><%= header5 %></h5><h6 class='header6'><%= header6 %></h6><ul class='list'><% for (var i = 0, l = list.length; i < l; i++) { %><li class='item'><%= list[i] %></li><% } %></ul></div>";
window.micro2 = "<div><h1 class='header'><%= data.header %></h1><h2 class='header2'><%= data.header2 %></h2><h3 class='header3'><%= data.header3 %></h3><h4 class='header4'><%= data.header4 %></h4><h5 class='header5'><%= data.header5 %></h5><h6 class='header6'><%= data.header6 %></h6><ul class='list'><% for (var i = 0, l = data.list.length; i < l; i++) { %><li class='item'><%= data.list[i] %></li><% } %></ul></div>";
window.underscoreTemplate = _.template(micro);
window.resigTemplate = tmpl(micro);
window.resigTemplate2 = tmpl2(micro2);
window.resigTemplate3 = tmpl3(micro2);
window.sharedVariables = {
header: "Header",
header2: "Header2",
header3: "Header3",
header4: "Header4",
header5: "Header5",
header6: "Header6",
list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
};
window.jadeTemplate = "div\n h1.header!= header\n h2.header2!= header2\n h3.header3!= header3\n h4.header4!= header4\n h5.header5!= header5\n h6.header6!= header6\n ul.list\n - each item in list\n li.item!= item";
window.hamlTemplate = Haml("%div\n %h1.header= header\n %h2.header2= header2\n %h3.header3= header3\n %h4.header4= header4\n %h5.header5= header5\n %h6.header6= header6\n %ul.list\n :each item in list\n %li.item= item");
window.ecoTemplate = eco("<div><h1 class='header'><%- @header %></h1><h2 class='header2'><%- @header2 %></h2><h3 class='header3'><%- @header3 %></h3><h4 class='header4'><%- @header4 %></h4><h5 class='header5'><%- @header5 %></h5><h6 class='header6'><%- @header6 %></h6><ul class='list'><% for item in @list: %><li class='item'><%- item %></li><% end %></ul></div>");
window.jQueryTemplate = $.template(null, "<div><h1 class='header'>{{html header}}</h1><h2 class='header2'>{{html header2}}</h2><h3 class='header3'>{{html header3}}</h3><h4 class='header4'>{{html header4}}</h4><h5 class='header5'>{{html header5}}</h5><h6 class='header6'>{{html header6}}</h6><ul class='list'>{{each list}}<li class='item'>{{html $value}}</li>{{/each}}</ul></div>");
window.doTtemplate = doT.template("<div><h1 class='header'>{{= it.header }}</h1><h2 class='header2'>{{= it.header2 }}</h2><h3 class='header3'>{{= it.header3 }}</h3><h4 class='header4'>{{= it.header4 }}</h4><h5 class='header5'>{{= it.header5 }}</h5><h6 class='header6'>{{= it.header6 }}</h6><ul class='list'>{{ for (var i = 0, l = it.list.length; i < l; i++) { }}<li class='item'>{{= it.list[i] }}</li>{{ } }}</ul></div>");
window.doUtemplate = doU.template("<div><h1 class='header'>{{= it.header }}</h1><h2 class='header2'>{{= it.header2 }}</h2><h3 class='header3'>{{= it.header3 }}</h3><h4 class='header4'>{{= it.header4 }}</h4><h5 class='header5'>{{= it.header5 }}</h5><h6 class='header6'>{{= it.header6 }}</h6><ul class='list'>{{ for (var i = 0, l = it.list.length; i < l; i++) { }}<li class='item'>{{= it.list[i] }}</li>{{ } }}</ul></div>");
window.jqote2 = $.jqotec(jqote_tmpl);
window.ejs = new EJS({
text: micro
});
window.baseHtml = "<div><h1 class='header'></h1><h2 class='header2'></h2><h3 class='header3'></h3><h4 class='header4'></h4><h5 class='header5'></h5><h6 class='header6'></h6><ul class='list'><li class='item'></li></ul></div>";
window.pureTemplate = $p('div.pure').compile({
h1: 'header',
h2: 'header2',
h3: 'header3',
h4: 'header4',
h5: 'header5',
h6: 'header6',
li: {
'itm<-list': {
'.': 'itm'
}
}
});
window.hamlTemplate_2 = "%div\n %h1.header= header\n %h2.header2= header2\n %h3.header3= header3\n %h4.header4= header4\n %h5.header5= header5\n %h6.header6= header6\n %ul.list\n :each item in list\n %li.item= item";
window.hamlCompiled = Haml.compile(hamlTemplate_2);
window.hamlOptimized = Haml.optimize(hamlCompiled);
dust.loadSource(dust.compile("<div><h1 class='header'>{header}</h1><h2 class='header2'>{header2}</h2><h3 class='header3'>{header3}</h3><h4 class='header4'>{header4}</h4><h5 class='header5'>{header5}</h5><h6 class='header6'>{header6}</h6><ul class='list'>{#list}<li class='item'>{.}</li>{/list}</ul></div>", "dustTemplate"));
window.timTemplate = "<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{list}}<li class='item'>{{_content}}</li>{{/list}}</ul></div>";
window.timLiteTemplate = "<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'><li class='item'>{{list.0}}</li><li class='item'>{{list.1}}</li><li class='item'>{{list.2}}</li><li class='item'>{{list.3}}</li><li class='item'>{{list.4}}</li><li class='item'>{{list.5}}</li><li class='item'>{{list.6}}</li><li class='item'>{{list.7}}</li><li class='item'>{{list.8}}</li><li class='item'>{{list.9}}</li></ul></div>";
window.tenjinTemplate = "<div><h1 class='header'>#{ header }</h1><h2 class='header2'>#{ header2 }</h2><h3 class='header3'>#{ header3 }</h3><h4 class='header4'>#{ header4 }</h4><h5 class='header5'>#{ header5 }</h5><h6 class='header6'>#{ header6 }</h6><ul class='list'><?js for (var i = 0, l = list.length; i < l; i++) { ?><li class='item'>#{ list[i] }</li><?js } ?></ul></div>";
window.convertedTenjinTemplate = new Shotenjin.Template();
window.convertedTenjinTemplate.convert(tenjinTemplate);
window.nTenjinTemplate = "<div><h1 class='header'>#{ it.header }</h1><h2 class='header2'>#{ it.header2 }</h2><h3 class='header3'>#{ it.header3 }</h3><h4 class='header4'>#{ it.header4 }</h4><h5 class='header5'>#{ it.header5 }</h5><h6 class='header6'>#{ it.header6 }</h6><ul class='list'><?js for (var i = 0, l = it.list.length; i < l; i++) { ?><li class='item'>#{ it.list[i] }</li><?js } ?></ul></div>";
window.convertednTenjinTemplate = new nTenjin.Template();
window.convertednTenjinTemplate.convert(nTenjinTemplate);
</script>
Ready to run.
Test | Ops/sec | |
---|---|---|
Mustache.js Template |
| ready |
Resig Micro-Templating. |
| ready |
Creationix's Haml-js Template |
| ready |
Jade Template |
| ready |
Underscore.js Template |
| ready |
Eco Template |
| ready |
jQuery Templates |
| ready |
Handlebars.js |
| ready |
doT.js |
| ready |
doU.js |
| ready |
jQote2 direct |
| ready |
ejs |
| ready |
Pure |
| ready |
Creationix's Haml-js Template (compiled & optimized) |
| ready |
dust |
| ready |
KiTE |
| ready |
Resig Micro-Templating without with(obj) |
| ready |
Resig Micro-Templating with += instead of push and join |
| ready |
Milk |
| ready |
Tim |
| ready |
Tim (lite, cached) |
| ready |
jsTenjin(not converted) |
| ready |
jsTenjin(converted) |
| ready |
nTenjin(converted) |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.