JavaScript template engine compare

JavaScript performance comparison

Revision 88 of this test case created

Info

Preparation code

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<script src="http://rawgithub.com/spmbt/doT/master/doT.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src="http://borismoore.github.com/jsrender/jsrender.js"></script>
<script src="http://github.com/janl/mustache.js/raw/master/mustache.js"></script>
<script src="https://rawgithub.com/atmajs/MaskJS/master/lib/mask.min.js"></script>
<script src="http://ysugimoto.github.io/Retriever/build/retriever.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0-alpha.2/handlebars.min.js"></script>

<script type="text/javascript">
(function(window,undefined){if(!Array.isArray)Array.isArray=function(v){return Object.prototype.toString.call(v)==="[object Array]"};if(!String.prototype.endsWith)Object.defineProperty(String.prototype,"endsWith",{enumerable:false,configurable:false,writable:false,value:function(searchString,position){position=position||this.length;position=position-searchString.length;var lastIndex=this.lastIndexOf(searchString);return lastIndex!==-1&&lastIndex===position}});var _Penguin=function(){};var _Config=
{templateDataAttribute:"data-template",templateMarkDataAttribute:"data-id",scriptMimeType:"text/penguin-template",scriptFor:"data-for",cleanDomAfterCompilation:true,cleanTemplates:false,isBuffering:false,jQueryCheckTimeout:5};var _jQueryDetected=false;var _Original$$=undefined;var _PrecompiledTemplates={};var _ReadyList=[];var _TemplateBuffer=[];function _BindGlobals(){if(typeof window.$$!=="undefined")_Original$$=window.$$;window.$$=_Penguin;window.Penguin=_Penguin}function _CallReadyList(){for(var i=
0,l=_ReadyList.length;i<l;++i)_ReadyList[i].call(this)}function _DefineJqueryPlugins(){function __define(){_jQueryDetected=typeof window.jQuery==="function";if(!_jQueryDetected){window.setTimeout(__define,_Config.jQueryCheckTimeout*=1.01);return}jQuery.fn.extend({appendTemplates:function(){var args=Array.prototype.slice.call(arguments);for(var i=0,l=args.length;i<l;++i)if(args[i]instanceof Template||args[i]instanceof TemplateCollection)args[i]=args[i].Node;return this.append(args)}})}__define()}function _DomNodeToString(n){if("outerHTML"in
n)return n.outerHTML;var div=document.createElement("div");div.appendChild(n.cloneNode(true));return div.innerHTML}function _GetElementsByAttribute(attr){var arr=[];var walker=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT,{acceptNode:function(n){return n.hasAttribute(attr)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});while(walker.nextNode())arr.push(walker.currentNode);return arr}function _GetMarkedElements(el){var marks={};var walker=document.createTreeWalker(el,NodeFilter.SHOW_ELEMENT,
{acceptNode:function(n){return n.hasAttribute(_Config.templateMarkDataAttribute)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});while(walker.nextNode()){marks[walker.currentNode.getAttribute(_Config.templateMarkDataAttribute)]=walker.currentNode;if(_Config.cleanTemplates)walker.currentNode.removeAttribute(_Config.templateMarkDataAttribute)}return marks}var _HTMLtoFragment=function(){function makeMap(str){var obj={};var items=str.split(",");for(var i=0;i<items.length;++i)obj[items[i]]=true;return obj}
function HTMLParser(html,handler){function parseEndTag(tag,tagName){var pos;if(!tagName)pos=0;else for(pos=stack.length-1;pos>=0;--pos)if(stack[pos]===tagName)break;if(pos>=0){for(var i=stack.length-1;i>=pos;--i)if(handler.end)handler.end(stack[i]);stack.length=pos}}function parseStartTag(tag,tagName,rest,unary){var attrs=[];tagName=tagName.toLowerCase();if(block[tagName])while(stack.last()&&inline[stack.last()])parseEndTag("",stack.last());if(closeSelf[tagName]&&stack.last()===tagName)parseEndTag("",
tagName);unary=empty[tagName]||!!unary;if(!unary)stack.push(tagName);if(handler.start){attrs.length=0;rest.replace(attr,function(match,name){var value=arguments[2]?arguments[2]:arguments[3]?arguments[3]:arguments[4]?arguments[4]:fillAttrs[name]?name:"";attrs.push({name:name,value:value,escaped:value.replace(/(^|[^\\])"/g,'$1\\"')})});if(handler.start)handler.start(tagName,attrs,unary)}}var index;var chars;var match;var stack=[];var last=html;stack.last=function(){return this[this.length-1]};while(html){chars=
true;if(!stack.last()||!special[stack.last()]){if(html.indexOf("\x3c!--")===0){index=html.indexOf("--\x3e");if(index>=0){if(handler.comment)handler.comment(html.substring(4,index));html=html.substring(index+3);chars=false}}else if(html.indexOf("</")===0){match=html.match(endTag);if(match){html=html.substring(match[0].length);match[0].replace(endTag,parseEndTag);chars=false}}else if(html.indexOf("<")===0){match=html.match(startTag);if(match){html=html.substring(match[0].length);match[0].replace(startTag,
parseStartTag);chars=false}}if(chars){index=html.indexOf("<");var text=index<0?html:html.substring(0,index);html=index<0?"":html.substring(index);if(handler.chars)handler.chars(text)}}else{html=html.replace(new RegExp("(.*)</"+stack.last()+"[^>]*>"),function(all,text){text=text.replace(/\x3c!--(.*?)--\x3e/g,"$1").replace(/<!\[CDATA\[(.*?)]]\x3e/g,"$1");if(handler.chars)handler.chars(text);return""});parseEndTag("",stack.last())}if(html===last)throw"Parse Error: "+html;last=html}parseEndTag()}var startTag=
/^<([-A-Za-z0-9_]+)((?:\s+[\w-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;var endTag=/^<\/([-A-Za-z0-9_]+)[^>]*>/;var attr=/([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;var empty=makeMap("area,base,basefont,br,col,embed,frame,hr,img,input,isindex,keygen,link,menuitem,meta,param,source,track,wbr,embed");var block=makeMap("address,applet,article,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video");
var inline=makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");var closeSelf=makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");var fillAttrs=makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");var special=makeMap("script,style");return function(html){var frag=document.createDocumentFragment();
var elems=[];var curParentNode=frag;elems[0]=frag;HTMLParser(html,{start:function(tagName,attrs,unary){var elem=document.createElement(tagName);for(var attr in attrs)elem.setAttribute(attrs[attr].name,attrs[attr].value);curParentNode.appendChild(elem);if(!unary){elems.push(elem);curParentNode=elem}},end:function(tag){elems.length-=1;if(!empty[tag])curParentNode=elems[elems.length-1]},chars:function(text){curParentNode.appendChild(document.createTextNode(text))},comment:function(text){var comm=document.createComment(text);
if(curParentNode)curParentNode.appendChild(comm);else elems.push(comm)}});return frag}}();function _Initialise(){function __ready(){_PrecompileTemplates();_DefineJqueryPlugins();_CallReadyList()}_jQueryDetected=typeof window.jQuery==="function";if(_jQueryDetected)jQuery(document).ready(__ready);else document.addEventListener("DOMContentLoaded",__ready,false)}function _IsElement(o){return typeof HTMLElement==="object"?o instanceof HTMLElement:o&&typeof o==="object"&&o!==null&&o.nodeType===Node.ELEMENT_NODE&&
typeof o.nodeName==="string"}function _NOP(){}function _ParseHTML(html){return _HTMLtoFragment(html)}function _PrecompileTemplates(){var elems=_GetElementsByAttribute(_Config.templateDataAttribute);var scripts=function(){var o={};var sourceArr=[];var scriptElems=Array.prototype.slice.call(document.scripts).filter(function(s){return s.hasAttribute(_Config.scriptFor)&&s.type.toLowerCase()===_Config.scriptMimeType});sourceArr.length=scriptElems.length;for(var i=0,l=scriptElems.length;i<l;++i){sourceArr[i]=
scriptElems[i].textContent.replace(/(?!})\s*;\s*$/,"");o[i]=scriptElems[i].getAttribute(_Config.scriptFor);if(_Config.cleanDomAfterCompilation)scriptElems[i].parentNode.removeChild(scriptElems[i])}eval("sourceArr=["+sourceArr.join(",")+"]");for(var i=0,l=sourceArr.length;i<l;++i)o[o[i]]=sourceArr[i];return o}();var name;var model;for(var i=0,l=elems.length;i<l;++i){name=elems[i].getAttribute(_Config.templateDataAttribute);if(elems[i].nodeName.toLowerCase()==="template")if("content"in elems[i])model=
elems[i].content;else model=elems[i].innerHTML;else model=elems[i];_PrecompiledTemplates[name]=_Penguin.Create(model,name in scripts?scripts[name]:null);if(_Config.cleanDomAfterCompilation)elems[i].parentNode.removeChild(elems[i])}}function Template(o){this._original=o.Model;this._node=o.Model.cloneNode(true);this._marks=_GetMarkedElements(this._node);this._func=typeof o.Logic==="function"?o.Logic:null;if(_Config.cleanTemplates&&!(this._node instanceof DocumentFragment))this._node.removeAttribute(_Config.templateDataAttribute);
if(_Config.isBuffering)_TemplateBuffer[_TemplateBuffer.length]=this}Object.defineProperties(Template.prototype,{_original:{enumerable:false,writable:true},_node:{writable:true,configurable:false},_marks:{writable:true,enumerable:false},_func:{writable:true,enumerable:false},AppendTo:{value:function(n){if(typeof n==="string")n=document.getElementById(n);n.appendChild(this._node);return this},enumerable:true},Copy:{value:function(){return new Template({Type:"copy",Model:this._original,Logic:this._func})},
enumerable:true},DataBind:{value:function(){if(typeof this._func==="function")this._func.apply(this,arguments);return this},enumerable:true},DataBindCollection:{value:function(coll){var t;var arr=[];arr.length=coll.length;for(var i=0,l=coll.length;i<l;++i){t=this.Copy();t.DataBind.apply(t,[coll[i],i,coll]);arr[i]=t}return new TemplateCollection(arr)},enumerable:true},Node:{get:function(){return this._node},enumerable:true},Get:{value:function(name){return this._marks[name]},enumerable:true},Logic:{value:function(f){if(typeof f===
"function"){this._func=f;return this}else return this._func},enumerable:true},Set:{value:function(a,b,c){var mods=[];var modType;var markedElem;if(arguments.length===1)if(typeof a==="function")a.call(this);else{if(typeof a==="object")for(var mark in a){markedElem=this._marks[mark];if(typeof a[mark]==="object")for(var subMark in a[mark])mods.push({Mark:markedElem,Property:subMark,Value:a[mark][subMark]});else mods.push({Mark:markedElem,Property:null,Value:a[mark]})}}else if(arguments.length===2){markedElem=
this._marks[a];if(typeof b==="function")b.call(markedElem,this);else if(typeof b==="object"&&Array.isArray(b)===false)for(var prop in b)mods.push({Mark:markedElem,Property:prop,Value:b[prop]});else mods.push({Mark:markedElem,Property:null,Value:b})}else if(arguments.length===3)mods.push({Mark:this._marks[a],Property:b,Value:c});else console.log("Incorrect number of parameters");for(var i=0;i<mods.length;++i){if(mods[i].Mark===undefined){console.log("Marked element not found");continue}modType=typeof mods[i].Value;
if(mods[i].Property===null)if(modType==="function")mods[i].Value.call(mods[i].Mark,this);else if(modType==="string"||modType==="boolean"||modType==="number")mods[i].Mark.textContent=mods[i].Value;else if(Array.isArray(mods[i].Value))mods[i].Mark.textContent=mods[i].Value.join(", ");else console.log("Modification type of element not supported: "+modType);else if(mods[i].Property in mods[i].Mark)mods[i].Mark[mods[i].Property]=mods[i].Value;else mods[i].Mark.setAttribute(mods[i].Property,mods[i].Value)}return this},
enumerable:true},toJSON:{value:function(){return{Type:"json",Model:_DomNodeToString(this._original),Logic:typeof this._func==="function"?this._func.toString():null}},enumerable:true}});function TemplateCollection(arr){this._Arr=arr}Object.defineProperties(TemplateCollection.prototype,{AppendTo:{value:function(n){if(typeof n==="string")n=document.getElementById(n);n.appendChild(this.Node);return this},enumerable:true},AppendToAsync:{value:function(n,ival,cb){if(typeof n==="string")n=document.getElementById(n);
if(typeof ival!=="number")ival=0;if(typeof cb!=="function")cb=_NOP;var _self=this;var f=document.createDocumentFragment();var i=0;var l=_self._Arr.length;var chunkSize=Math.ceil(l*.1);var _process=function(){var max=Math.min(i+chunkSize,l);for(;i<max;++i)f.appendChild(_self._Arr[i].Node);n.appendChild(f);if(i<l){f=document.createDocumentFragment();window.setTimeout(_process,ival)}else cb.call(_self,null)};_process();return _self},enumerable:true},Node:{get:function(){var f=document.createDocumentFragment();
for(var i=0,l=this._Arr.length;i<l;++i)f.appendChild(this._Arr[i].Node);return f},enumerable:true},Item:{value:function(i){return this._Arr[i]},enumerable:true},Length:{get:function(){return this._Arr.length},enumerable:true},Push:{value:function(t){if(t instanceof Template)this._Arr.push(t);return this},enumerable:true}});Object.defineProperties(_Penguin,{Buffer:{value:{},enumerable:true},Create:{value:function(){var elem;var func=null;if(arguments.length===1)if(typeof arguments[0]==="object"){elem=
arguments[0].Model;func=arguments[0].Logic}else elem=arguments[0];else if(arguments.length===2){elem=arguments[0];func=arguments[1]}if(typeof elem==="string")elem=_ParseHTML(elem);else if(elem.nodeName.toLowerCase()==="template")if("content"in elem)elem=elem.content;else elem=_ParseHTML(elem.innerHTML);if(typeof func==="string")eval("func="+func);return new Template({Type:"new",Model:elem,Logic:func})},enumerable:true},Get:{value:function(name){return _PrecompiledTemplates[name].Copy()},enumerable:true},
jQuerySupport:{value:function(){_DefineJqueryPlugins();return _Penguin},enumerable:true},Original$$:{get:function(){return _Original$$},enumerable:true},Ready:{value:function(f){if(typeof f==="function")_ReadyList.push(f);return _Penguin},enumerable:true}});Object.defineProperties(_Penguin.Buffer,{Clear:{value:function(){_TemplateBuffer.length=0;return _Penguin},enumerable:true},Disable:{value:function(){_Config.isBuffering=false;return _Penguin},enumerable:true},Enable:{value:function(){_Config.isBuffering=
true;return _Penguin},enumerable:true},Enabled:{get:function(){return _Config.isBuffering},set:function(v){_Config.isBuffering=v?true:false;return _Config.isBuffering},enumerable:true},Collection:{get:function(){var arr=[];arr.length=_TemplateBuffer.length;for(var i=0,l=_TemplateBuffer.length;i<l;++i)arr[i]=_TemplateBuffer[i];return new TemplateCollection(arr)},enumerable:true},Length:{get:function(){return _TemplateBuffer.length},enumerable:true},Push:{value:function(t){if(t instanceof Template)_TemplateBuffer.push(t);
return _Penguin},enumerable:true}});_BindGlobals();_Initialise()})(window);</script>

<!--External Template Definitions-->
<script>
  window.sharedVariables = {
   header0: "Test render template engine ",
   header2: "Header2",
   header3: "Header3",
   header4: "Header4",
   header5: "Header5",
   header6: "Header6",
   list: ['1000000', '2000000', '3000000', '4000000', '5000000', '6000000', '7000000', '8000000', '9000000', '10000000']
  };

window.doTtemplateRaw = "<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.doTtemplate = doT.compile(doTtemplateRaw);

 
window.underscoreTemplateRaw = "<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.underscoreTemplate = _.template(underscoreTemplateRaw);
 
window.underscoreTemplateNoWithRaw = "<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.underscoreTemplateNoWith = _.template(underscoreTemplateNoWithRaw, null, {variable: 'data'});

  //JsRender compiled template (no encoding)
window.jsRenderTemplateRaw = "<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 list}}<li class='item'>{{:#data}}</li>{{/for}}</ul></div>";
window.jsRenderTemplate = $.templates(jsRenderTemplateRaw);
 
window.mustacheTemplateRaw = "<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.retrieverTemplateRaw = "<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'>{{loop list}}<li class='item'>{{@%data}}</li>{{/loop}}</ul></div>";
window.retrieverTemplate = Retriever.make(retrieverTemplateRaw).compile();

window.HandlebarsTemplateRaw = "<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.HandlebarsTemplate = Handlebars.compile(HandlebarsTemplateRaw);

window.maskTemplateRaw = "div { \
        h1.header > '~[header]'\
        h2.header2 > '~[header2]'\
        h3.header3 > '~[header3]'\
        h4.header4 > '~[header4]'\
        h5.header5 > '~[header5]'\
        h6.header6 > '~[header6]'\
        ul > each (list) > li > '~[.]'\
    }"
;
  window.maskDom = mask.compile(maskTemplateRaw)
</script>

<div style="display:none">
<!-- Begin Penguin -->

<div data-template="content"><h1 data-id="header"></h1><h2 data-id="header2"></h2><h3 data-id="header3"></h3><h4 data-id="header4"></h4><h5 data-id="header5"></h5><h6 data-id="header6"></h6><ul data-id="list"></ul></div>

<li data-template="listItem" data-id="text"></li>

<script type="text/penguin-template" data-for="content">
function(vars){
 this.Set({
  header: vars.header0,
  header2: vars.header2,
  header3: vars.header3,
  header4: vars.header4,
  header5: vars.header5,
  header6: vars.header6
 });
 this.Set("list", function(){
  $$.Get("listItem").DataBindCollection(vars.list).AppendTo(this);
 });
}
</script>

<script type="text/penguin-template" data-for="listItem">
function(i){
 this.Set("text", i);
}
</script>

<!-- End Penguin -->
</div>


<div id="template" style=""></div>
<script>
Benchmark.prototype.setup = function() {
    template = $('#template').get(0);
};
</script>

Preparation code output

  • Test runner

    Warning! For accurate results, please disable Firebug before running the tests. (Why?)

    Java applet disabled.

    Testing in unknown unknown
    Test Ops/sec
    1.doT.js
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'doT.js';
    template.innerHTML = doTtemplate(sharedVariables);
    ++sharedVariables.list[0];
    pending…
    2.Underscore
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'Underscore';
    template.innerHTML = underscoreTemplate(sharedVariables);
    ++sharedVariables.list[1];
    pending…
    3.Underscore (no "with")
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'Underscore (no "with")';
    template.innerHTML = underscoreTemplateNoWith(sharedVariables);
    ++sharedVariables.list[2];
    pending…
    4.JsRender
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'JsRender';
    template.innerHTML = jsRenderTemplate.render(sharedVariables);
    ++sharedVariables.list[3];
    pending…
    6.MaskJs
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'MaskJs';
    template.appendChild(mask.render(maskTemplateRaw, sharedVariables));
    ++sharedVariables.list[5];
    pending…
    7.doT Compile
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'doT Compile';
    template.innerHTML = doT.compile(doTtemplateRaw)(sharedVariables);
    ++sharedVariables.list[6];
    pending…
    8. Retriever
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'Retriever';
    template.innerHTML = retrieverTemplate.parse(sharedVariables);
    ++sharedVariables.list[7];
    pending…
    9.Handlebars(compiled)
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'Handlebars';
    template.innerHTML = HandlebarsTemplate(sharedVariables);
    ++sharedVariables.list[8];
    pending…
    10. Penguin
    template.innerHTML = '';
    sharedVariables.header = sharedVariables.header0 +'Penguin';
    template.appendChild($$.Get("content").DataBind(sharedVariables).Node);
    ++sharedVariables.list[9];
    pending…

    Compare results of other browsers

    Revisions

    You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

    0 comments

    Add a comment