Javascript templating shootoff (extended) (v106)

Revision 106 of this benchmark created on


Description

All tests across all revisions of the original shoot off.

Please, do not remove tests when making future revisions; additions are welcome. Please do not cheat using script blocks.

Libraries: Mustache (0.3.0) jQuery (beta 1, discontinued) Kendo UI Handlebars Underscore John Resig's Micro Templates T2 jqote2 (10/21/2010)

Preparation HTML

<script src="https://www.cinsoft.net/mylib099-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/ext-core/3/ext-core.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/yui/2.9.0/build/yuiloader/yuiloader-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/mootools/1.3/mootools-yui-compressed.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/prototype/1/prototype.js"></script>
<!---------------------------------------->
<!-- EXTERNAL LIBRARIES HOSTED BY A CDN -->
<!---------------------------------------->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.3.0/mustache.min.js"></script>
<script src="https://github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js"></script>
<script src="http://cdn.kendostatic.com/2011.2.804/js/kendo.all.min.js"></script>
<script src="https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>

<!--------------------------->
<!-- MASTER TEMPLATE DATA  -->
<!--------------------------->
<script>
    window.tpl = {};
    window.tpl.vars = {
        header: "Header",
        header2: "Header2",
        header3: "Header3",
        header4: "Header4",
        header5: "Header5",
        header6: "Header6",
        list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
    };
    window.tpl.vars2 = {
        header: "Header",
        header2: "Header2",
        header3: "Header3",
        header4: "Header4",
        header5: "Header5",
        header6: "Header6",
        list: [{i: '1'}, {i: '2'}, {i: '3'}, {i: '4'}, {i: '5'}, {i: '6'}, {i: '7'}, {i: '8'}, {i: '9'}, {i: '10'}]
    };

    /****************************************
     TEMPLATE ENGINES NOT HOSTED BY A CDN
     ****************************************/
//Please minify any hard included library and note the version.

//Resig Template Function (modified to support ')
    function tmpl(a) {
        var b = "var p=[];" + "with(obj){p.push('" + a.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('');";
        return new Function("obj", b)
    }

    //Resig modified template function (no "with" block)
    function tmpl2(a) {
        var b = "var p=[];" + "p.push('" + a.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('');";
        return new Function("data", b)
    }

    //T2 Template as found on the original shootoff (origin unknown)
    window.T2template = function() {
        var a = function(b, c, d) {
            if(Object.prototype.toString.call(b) === "[object Array]") {
                var e = "";
                for(var f in b)e += a(b[f], c, d);
                return e
            } else {
                return c.replace(/\{([^\{\}]*)\}/g, function(c, e) {
                    if(e.substring(0, 1) == "@") {
                        e = e.substring(1);
                        var f = d[e], g = b[e];
                        return typeof f === "string" && Object.prototype.toString.call(g) === "[object Array]" ? a(g, f, d) : c
                    }
                    var h = b[e];
                    return typeof h === "string" || typeof h === "number" ? h : c
                })
            }
        };
        var b = function(a, b, c, d, e) {
            var f = a.split(/[\(,\)]/), g = f.shift();
            switch(g) {
                case"exists":
                    for(var h in f) {
                        if(!b[f[h]])return false
                    }
                    return true;
                case"first":
                    return d == 0;
                case"middle":
                    return d > 0 && d < e - 1;
                case"last":
                    return d == e - 1
            }
            return false
        };
        var c = function(a, d, e, f, g) {
            if(Object.prototype.toString.call(a) === "[object Array]") {
                var h = "", i = a.length;
                for(var j in a)h += c(a[j], d, e, parseInt(j, 10), i);
                return h
            } else {
                return d.replace(/\{([^\{\}]*)\}/g, function(h, i) {
                    if(i.substring(0, 1) == "@") {
                        i = i.substring(1);
                        var j = e[i], k = a[i];
                        return typeof j === "string" && Object.prototype.toString.call(k) === "[object Array]" ? c(k, j, e, f, g) : h
                    } else if(i.substring(0, 1) == "?") {
                        if(b(i.substring(1), a, d, f, g))return "{!+}"; else return "{!-}"
                    }
                    var l = a[i];
                    return typeof l === "string" || typeof l === "number" ? l : h
                }).replace(/\{\!\-\}.+?\{\/\?\}/g, "").replace(/\{\!\+\}(.+?)\{\/\?\}/g, "$1")
            }
        };
        var d = {
            compile: function(a, b) {
                var d = a._, e = a;
                return c(b, d, e, 1, 1)
            }, s_compile: function(b, c) {
                var d = b._, e = b;
                return a(c, d, e)
            }
        };
        return d
    }()

//jQote2 (10/21/2010)
    (function($) {
        var _ = false, E1 = "UndefinedTemplateError", E2 = "TemplateCompilationError", E3 = "TemplateExecutionError", A = "[object Array]", S = "[object String]", F = "[object Function]", n = 1, c = "%", q = /^[^<]*(<[\w\W]+>)[^>]*$/, ts = Object.prototype.toString;

        function r(e, x) {
            throw ($.extend(e, x), e)
        }

        function dns(f) {
            var a = [];
            if(ts.call(f) !== A)return _;
            for(var i = 0, l = f.length; i < l; i++)a[i] = f[i].jqote_id;
            return a.length ? a.sort().join('.').replace(/(\b\d+\b)\.(?:\1(\.|$))+/g, "$1$2") : _
        }

        function l(s, t) {
            var f, g = [], t = t || c, x = ts.call(s);
            if(x === F)return s.jqote_id ? [s] : _;
            if(x !== A)return [$.jqotec(s, t)];
            if(x === A)for(var i = 0, l = s.length; i < l; i++)return g.length ? g : _
        }

        $.fn.extend({
            jqote: function(x, y) {
                var x = ts.call(x) === A ? x : [x], d = "";
                this.each(function(i) {
                    var f = $.jqotec(this, y);
                    for(var j = 0; j < x.length; j++)d += f.call(x[j], i, j, x, f)
                });
                return d
            }
        });
        $.each({app: "append", pre: "prepend", sub: "html"}, function(x, y) {
            $.fn["jqote" + x] = function(e, d, t) {
                var p, r, s = $.jqote(e, d, t), $$ = !q.test(s) ? function(s) {
                    return $(document.createTextNode(s))
                } : $;
                if(!!(p = dns(l(e))))r = new RegExp("(^|\\.)" + p.split(".").join("\\.(.*)?") + "(\\.|$)");
                return this.each(function() {
                    var z = $$(s);
                    $(this)[y](z);
                    (z[0].nodeType === 3 ? $(this) : z).trigger("jqote." + x, [z, r])
                })
            }
        });
        $.extend({
            jqote: function(e, d, t) {
                var s = "", t = t || c, f = l(e);
                if(f === _)r(new Error("Empty or undefined template passed to $.jqote"), {type: E1});
                d = ts.call(d) !== A ? [d] : d;
                for(var i = 0, m = f.length; i < m; i++)for(var j = 0; j < d.length; j++)s += f[i].call(d[j], i, j, d, f[i]);
                return s
            }, jqotec: function(x, t) {
                var h, e, y, t = t || c, z = ts.call(x);
                if(z === S && q.test(x)) {
                    e = y = x;
                    if(h = $.jqotecache[x])return h
                } else {
                    e = z === S || x.nodeType ? $(x) : x instanceof jQuery ? x : null;
                    if(!e[0] || !(y = e[0].innerHTML) && !(y = e.text()))r(new Error("Empty or undefined template passed to $.jqotec"), {type: E1});
                    if(h = $.jqotecache[$.data(e[0], "jqote_id")])return h
                }
                var s = "", i, a = y.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]/g, "").split("<" + t).join(t + ">\x1b").split(t + ">");
                for(var m = 0, k = a.length; m < k; m++)s += a[m].charAt(0) !== "\x1b" ? "out+='" + a[m].replace(/(\\|["'])/g, "\\$1") + "'" : (a[m].charAt(1) === "=" ? ";out+=(" + a[m].substr(2) + ");" : (a[m].charAt(1) === "!" ? ";out+=$.jqotenc((" + a[m].substr(2) + "));" : ";" + a[m].substr(1)));
                s = "try{" + ('var out="";' + s + ";return out;").split("out+='';").join("").split('var out="";out+=').join("var out=") + '}catch(e){e.type="' + E3 + '";e.args=arguments;e.template=arguments.callee.toString();throw e;}';
                try {
                    var f = new Function("i, j, data, fn", s)
                } catch(e) {
                    r(e, {type: E2})
                }
                i = e instanceof jQuery ? $.data(e[0], "jqote_id", n) : e;
                return $.jqotecache[i] = (f.jqote_id = n++, f)
            },
            jqotefn: function(e) {
                var t = ts.call(e), i = t === S && q.test(e) ? e : $.data($(e)[0], "jqote_id");
                return $.jqotecache[i] || _
            }, jqotetag: function(s) {
                if(ts.call(s) === S)c = s
            }, jqotenc: function(s) {
                return s.toString().replace(/&(?!\w+;)/g, '&#38;').split('<').join('&#60;').split('>').join('&#62;').split('"').join('&#34;').split("'").join('&#39;')
            }, jqotecache: {}
        });
        $.event.special.jqote = {
            add: function(o) {
                var n, h = o.handler, d = !o.data ? [] : ts.call(o.data) !== A ? [o.data] : o.data;
                if(!o.namespace)o.namespace = "app.pre.sub";
                if(!d.length || !(n = dns(l(d))))return;
                o.handler = function(e, m, r) {
                    return !r || r.test(n) ? h.apply(this, [e, m]) : null
                }
            }
        }
    })(jQuery);

    /********************************
     TEMPLATE DEFINITIONS
     ********************************/
// jQuery - Beta 1 (discontinued)
    window.tpl.jQuery = $.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>");

    // Underscore
    window.tpl.underscore = _.template("<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>");

    // Mustache
    window.tpl.mustache = "<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>";

    // Handlebars
    window.tpl.handlebars = 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>");

    // Kendo
    window.tpl.kendo = kendo.template("<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>", {useWithBlock: true});

    window.tpl.kendo2 = kendo.template("<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>", {useWithBlock: false});

    //John Resig's approach
    window.tpl.resig = tmpl("<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.tpl.resig2 = tmpl2("<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>");

    //T2 Template
    window.tpl.T2 = {
        _: "<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}</ul></div>",
        list: "<li class='item'>{i}</li>"
    };

    //jQuote2
    window.tpl.jqote = $.jqotec("<div><h1 class='header'><%= this.header %></h1><h2 class='header2'><%= this.header2 %></h2><h3 class='header3'><%= this.header3 %></h3><h4 class='header4'><%= this.header4 %></h4><h5 class='header5'><%= this.header5 %></h5><h6 class='header6'><%= this.header6 %></h6><ul class='list'><% for (var n = 0, l = this.list.length; n < l; n++) { %><li class='item'><%= this.list[n] %></li><% } %></ul></div>");


</script>

Test runner

Ready to run.

Testing in
TestOps/sec
Mustache 0.3.0
Mustache.to_html(tpl.mustache, tpl.vars);
ready
jQuery (beta 1, discontinued)
tpl.jQuery($, {
  data: tpl.vars
}).join("");
ready
Kendo UI
tpl.kendo(tpl.vars);
ready
Kendo UI (no "with" block)
tpl.kendo2(tpl.vars);
ready
Handlebars
tpl.handlebars(tpl.vars)
ready
Underscore
tpl.handlebars(tpl.vars);
ready
Resig Micro Templates
tpl.resig(tpl.vars)
ready
Resig Micro Templates (No "with" block)
tpl.resig2(tpl.vars)
ready
T2
T2template.s_compile(tpl.T2, tpl.vars2)
ready
T2 (logic)
T2template.compile(tpl.T2, tpl.vars2)
ready
jqote2 (10/21/2010)
jqote2.call(tpl.vars, 0, 0, [tpl.vars], jqote2);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.