CanJS EJS Live Binding Performance

JavaScript performance comparison

Revision 5 of this test case created by Curtis Cummings

Preparation code

<script src="http://underscorejs.org/underscore-min.js">
</script>
<script src="http://yui.yahooapis.com/3.5.0pr2/build/yui/yui-min.js"></script>
<script type='text/javascript' src='http://donejs.com/examples/todo/zepto/zepto.0.8-focusblur.js'></script>
<script>
(function(d,w,j){function Z(a,b){var c=a[G],c=c&&H[c];return b===j?c||aa(a):c&&c[b]||za.call($(a),b)}function aa(a,b,c){a=a[G]||(a[G]=++ba);a=H[a]||(H[a]={});b!==j&&(a[b]=c);return a}var H={},za=$.fn.data,ba=$.uuid=+new Date,G=$.expando="Zepto"+ba;$.fn.data=function(a,b){return b===j?0==this.length?j:Z(this[0],a):this.each(function(c){aa(this,a,$.isFunction(b)?b.call(this,c,Z(this,a)):b)})};$.cleanData=function(a){for(var b=0,c;(c=a[b])!==j;b++)d.trigger(c,"destroyed",[],!1),delete H[c[G]]};d.addEvent=
function(a,b){this.__bindEvents||(this.__bindEvents={});var c=a.split(".")[0];this.__bindEvents[c]||(this.__bindEvents[c]=[]);this.__bindEvents[c].push({handler:b,name:a});return this};d.removeEvent=function(a,b){if(this.__bindEvents){for(var c=0,d=this.__bindEvents[a.split(".")[0]],e;c<d.length;)e=d[c],b&&e.handler===b||!b&&e.name===a?d.splice(c,1):c++;return this}};d.dispatch=function(a){if(this.__bindEvents){var b=this.__bindEvents[a.type.split(".")[0]]||[],c=this,f=[a].concat(a.data||[]);d.each(b,
function(b,d){a.data=f.slice(1);d.handler.apply(c,f)})}};var o=document.createElement("table"),C=document.createElement("tr"),ca={tr:document.createElement("tbody"),tbody:o,thead:o,tfoot:o,td:C,th:C,"*":document.createElement("div")},Aa=/^\s*<(\w+)[^>]*>/,Ba=function(a,b){b===j&&(b=Aa.test(a)&&RegExp.$1);b in ca||(b="*");var c=ca[b];if("tr"===b){var d=document.createElement("div");d.innerHTML="<table><tbody>"+a+"</tbody></table>";c=d.firstChild.firstChild}else c.innerHTML=""+a;d={};c=c.childNodes;
d.length=c.length;for(var e=0;e<c.length;e++)d[e]=c[e];return[].slice.call(d)};d.buildFragment=function(a){var a=Ba(a[0]),b=document.createDocumentFragment();a.forEach(function(a){b.appendChild(a)});return{fragment:b}};$.extend(d,Zepto);d.trigger=function(a,b,c,f){a.trigger?a.trigger(b,c):a[0]&&a[0].dispatchEvent||a.dispatchEvent?!1===f?$([a]).triggerHandler(b,c):$([a]).trigger(b,c):("string"==typeof b&&(b={type:b}),b.target=b.target||a,b.data=c,d.dispatch.call(a,b))};d.$=Zepto;d.bind=function(a,
b){this.bind?this.bind(a,b):this[0]&&this[0].addEventListener||this.addEventListener?$([this]).bind(a,b):d.addEvent.call(this,a,b);return this};d.unbind=function(a,b){this.unbind?this.unbind(a,b):this[0]&&this[0].addEventListener||this.addEventListener?$([this]).unbind(a,b):d.removeEvent.call(this,a,b);return this};d.delegate=function(a,b,c){this.delegate?this.delegate(a,b,c):$([this]).delegate(a,b,c)};d.undelegate=function(a,b,c){this.undelegate?this.undelegate(a,b,c):$([this]).undelegate(a,b,c)};
$.each(["append","filter","addClass","remove","data"],function(a,b){d[b]=function(a){return a[b].apply(a,d.makeArray(arguments).slice(1))}});d.makeArray=function(a){var b=[];d.each(a,function(a,d){b[a]=d});return b};d.inArray=function(a,b){return b.indexOf(a)};d.proxy=function(a,b){return function(){return a.apply(b,arguments)}};var Ca=$.ajaxSettings.xhr;$.ajaxSettings.xhr=function(){var a=Ca(),b=a.open;a.open=function(a,d){b.call(this,a,d,I===j?!0:I)};return a};var I,Da=$.ajax,M=function(a,b){for(var c in a)b[c]=
"function"==typeof b[c]?function(){a[c].apply(a,arguments)}:c[a]};d.ajax=function(a){var b=a.success,c=a.error,f=d.Deferred();a.success=function(){M(e,f);f.resolve.apply(f,arguments);b&&b.apply(this,arguments)};a.error=function(){M(e,f);f.reject.apply(f,arguments);c&&c.apply(this,arguments)};!1===a.async&&(I=!1);var e=Da(a);I=j;M(e,f);return f};$.fn.empty=function(){return this.each(function(){$.cleanData(this.getElementsByTagName("*"));this.innerHTML=""})};$.fn.remove=function(){$.cleanData(this);
this.each(function(){null!=this.parentNode&&(this.getElementsByTagName&&$.cleanData(this.getElementsByTagName("*")),this.parentNode.removeChild(this))});return this};d.trim=function(a){return a.trim()};d.isEmptyObject=function(a){for(var b in a);return b!==j};d.extend=function(a){if(!0===a){var b=d.makeArray(arguments);b.shift();return $.extend.apply($,b)}return $.extend.apply($,arguments)};d.get=function(a,b){return a[b]};var v=function(a){if(!(this instanceof v))return new v;this._doneFuncs=[];
this._failFuncs=[];this._resultArgs=null;this._status="";a&&a.call(this,this)};d.Deferred=v;d.when=v.when=function(){var a=d.makeArray(arguments);if(2>a.length){var b=a[0];return b&&d.isFunction(b.isResolved)&&d.isFunction(b.isRejected)?b:v().resolve(b)}var c=v(),f=0,e=[];d.each(a,function(b,d){d.done(function(){e[b]=2>arguments.length?arguments[0]:arguments;++f==a.length&&c.resolve.apply(c,e)}).fail(function(){c.reject(arguments)})});return c};o=function(a,b){return function(c){var d=this._resultArgs=
1<arguments.length?arguments[1]:[];return this.exec(c,this[a],d,b)}};C=function(a,b){return function(){var c=this;d.each(Array.prototype.slice.call(arguments),function(d,e,g){e&&(e.constructor===Array?g.callee.apply(c,e):(c._status===b&&e.apply(c,c._resultArgs||[]),c[a].push(e)))});return this}};d.extend(v.prototype,{pipe:function(a,b){var c=d.Deferred();this.done(function(){c.resolve(a.apply(this,arguments))});this.fail(function(){b?c.reject(b.apply(this,arguments)):c.reject.apply(c,arguments)});
return c},resolveWith:o("_doneFuncs","rs"),rejectWith:o("_failFuncs","rj"),done:C("_doneFuncs","rs"),fail:C("_failFuncs","rj"),always:function(){var a=d.makeArray(arguments);a.length&&a[0]&&this.done(a[0]).fail(a[0]);return this},then:function(){var a=d.makeArray(arguments);1<a.length&&a[1]&&this.fail(a[1]);a.length&&a[0]&&this.done(a[0]);return this},isResolved:function(){return"rs"===this._status},isRejected:function(){return"rj"===this._status},reject:function(){return this.rejectWith(this,arguments)},
resolve:function(){return this.resolveWith(this,arguments)},exec:function(a,b,c,f){if(""!==this._status)return this;this._status=f;d.each(b,function(b,d){d.apply(a,c)});return this}});var Ea=/==/,Fa=/([A-Z]+)([A-Z][a-z])/g,Ga=/([a-z\d])([A-Z])/g,Ha=/([a-z\d])([A-Z])/g,da=/\{([^\}]+)\}/g,s=/"/g,Ia=/'/g;d.extend(d,{esc:function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(s,"&#34;").replace(Ia,"&#39;")},getObject:function(a,b,c){var a=a?a.split("."):[],f=a.length,
e,g=0,h,i,b=d.isArray(b)?b:[b||w];if(!f)return b[0];for(;e=b[g++];){for(i=0;i<f-1&&/^f|^o/.test(typeof e);i++)e=a[i]in e?e[a[i]]:c&&(e[a[i]]={});if(/^f|^o/.test(typeof e)&&(h=a[i]in e?e[a[i]]:c&&(e[a[i]]={}),h!==j))return!1===c&&delete e[a[i]],h}},capitalize:function(a){return a.charAt(0).toUpperCase()+a.slice(1)},underscore:function(a){return a.replace(Ea,"/").replace(Fa,"$1_$2").replace(Ga,"$1_$2").replace(Ha,"_").toLowerCase()},sub:function(a,b,c){var f=[];f.push(a.replace(da,function(a,g){var h=
d.getObject(g,b,c);return/^f|^o/.test(typeof h)?(f.push(h),""):""+h}));return 1>=f.length?f[0]:f},replacer:da,undHash:/_|-/});var N=0;d.Construct=function(){if(arguments.length)return d.Construct.extend.apply(d.Construct,arguments)};d.extend(d.Construct,{newInstance:function(){var a=this.instance(),b;a.setup&&(b=a.setup.apply(a,arguments));a.init&&a.init.apply(a,b||arguments);return a},_inherit:function(a,b,c){d.extend(c||a,a||{})},setup:function(a){this.defaults=d.extend(!0,{},a.defaults,this.defaults)},
instance:function(){N=1;var a=new this;N=0;return a},extend:function(a,b,c){function f(){if(!N)return this.constructor!==f&&arguments.length?arguments.callee.extend.apply(arguments.callee,arguments):this.constructor.newInstance.apply(this.constructor,arguments)}"string"!=typeof a&&(c=b,b=a,a=null);c||(c=b,b=null);var c=c||{},e=this.prototype,g,h,i,p;p=this.instance();this._inherit(c,e,p);for(g in this)this.hasOwnProperty(g)&&(f[g]=this[g]);this._inherit(b,this,f);if(a){i=a.split(".");h=i.pop();i=
e=d.getObject(i.join("."),w,!0);var j=d.underscore(a.replace(/\./g,"_")),J=d.underscore(h);e[h]=f}d.extend(f,{constructor:f,prototype:p,namespace:i,shortName:h,_shortName:J,fullName:a,_fullName:j});f.prototype.constructor=f;h=[this].concat(d.makeArray(arguments));p=f.setup.apply(f,h);f.init&&f.init.apply(f,p||h);return f}});var r=function(a){return a&&"object"===typeof a&&!(a instanceof Date)},O=function(a,b){return d.each(a,function(a,d){d&&d.unbind&&d.unbind("change"+b)})},P=function(a,b,c){a instanceof
x?O([a],c._namespace):a=d.isArray(a)?new x.List(a):new x(a);a.bind("change"+c._namespace,function(f,e){var g=d.makeArray(arguments),f=g.shift();g[0]="*"===b?c.indexOf(a)+"."+g[0]:b+"."+g[0];d.trigger(c,f,g);d.trigger(c,g[0],g)});return a},ea=0,y=j,fa=function(){if(!y)return y=[],!0},l=function(a,b,c){if(!a._init)if(y)y.push([a,{type:b,batchNum:ga},c]);else return d.trigger(a,b,c)},ga=1,ha=function(){var a=y.slice(0);y=j;ga++;d.each(a,function(a,c){d.trigger.apply(d,c)})},K=function(a,b,c){a.each(function(a,
e){c[a]=r(e)&&d.isFunction(e[b])?e[b]():e});return c},o=function(a){return function(){return d[a].apply(this,arguments)}},D=o("addEvent"),o=o("removeEvent"),Q=function(a){return d.isArray(a)?a:(""+a).split(".")},x=d.Construct("can.Observe",{setup:function(){d.Construct.setup.apply(this,arguments)},bind:D,unbind:o,id:"id"},{setup:function(a){this._data={};this._namespace=".observe"+ ++ea;this._init=1;this.attr(a);delete this._init},attr:function(a,b){if(~"ns".indexOf((typeof a).charAt(0))){if(b===
j)return x.__reading&&x.__reading(this,a),this._get(a);this._set(a,b);return this}return this._attrs(a,b)},each:function(){return d.each.apply(j,[this.__get()].concat(d.makeArray(arguments)))},removeAttr:function(a){var a=Q(a),b=a.shift(),c=this._data[b];if(a.length)return c.removeAttr(a);delete this._data[b];b in this.constructor.prototype||delete this[b];l(this,"change",[b,"remove",j,c]);l(this,b,j,c);return c},_get:function(a){var a=Q(a),b=this.__get(a.shift());return a.length?b?b._get(a):j:b},
__get:function(a){return a?this._data[a]:this._data},_set:function(a,b){var c=Q(a),d=c.shift(),e=this.__get(d);if(r(e)&&c.length)e._set(c,b);else{if(c.length)throw"can.Observe: Object does not exist";this.__convert&&(b=this.__convert(d,b));this.__set(d,b,e)}},__set:function(a,b,c){if(b!==c){var d=this.__get().hasOwnProperty(a)?"set":"add";this.___set(a,r(b)?P(b,a,this):b);l(this,"change",[a,d,b,c]);l(this,a,b,c);c&&O([c],this._namespace)}},___set:function(a,b){this._data[a]=b;a in this.constructor.prototype||
(this[a]=b)},bind:D,unbind:o,serialize:function(){return K(this,"serialize",{})},_attrs:function(a,b){if(a===j)return K(this,"attr",{});var a=d.extend(!0,{},a),c,f=fa(),e=this,g;this.each(function(c,d){g=a[c];g===j?b&&e.removeAttr(c):(r(d)&&r(g)?d.attr(g,b):d!=g&&e._set(c,g),delete a[c])});for(c in a)g=a[c],this._set(c,g);f&&ha();return this}}),Ja=[].splice,R=x("can.Observe.List",{setup:function(a,b){this.length=0;this._namespace=".observe"+ ++ea;this._init=1;this.bind("change",d.proxy(this._changes,
this));this.push.apply(this,d.makeArray(a||[]));d.extend(this,b);delete this._init},_changes:function(a,b,c,d,e){~b.indexOf(".")||("add"===c?(l(this,c,[d,+b]),l(this,"length",[this.length])):"remove"===c?(l(this,c,[e,+b]),l(this,"length",[this.length])):l(this,c,[d,+b]))},__get:function(a){return a?this[a]:this},___set:function(a,b){this[a]=b;+a>=this.length&&(this.length=+a+1)},serialize:function(){return K(this,"serialize",[])},splice:function(a,b){var c=d.makeArray(arguments),f;for(f=2;f<c.length;f++){var e=
c[f];r(e)&&(c[f]=P(e,"*",this))}b===j&&(b=c[1]=this.length-a);f=Ja.apply(this,c);0<b&&(l(this,"change",[""+a,"remove",j,f]),O(f,this._namespace));2<c.length&&l(this,"change",[""+a,"add",c.slice(2),f]);return f},_attrs:function(a,b){if(a===j)return K(this,"attr",[]);var a=a.slice(0),c=Math.min(a.length,this.length),d=fa(),e;for(e=0;e<c;e++){var g=this[e],h=a[e];r(g)&&r(h)?g.attr(h,b):g!=h&&this._set(e,h)}a.length>this.length?this.push(a.slice(this.length)):a.length<this.length&&b&&this.splice(a.length);
d&&ha()}});d.each({push:"length",unshift:0},function(a,b){R.prototype[a]=function(){for(var c=arguments[0]&&d.isArray(arguments[0])?arguments[0]:d.makeArray(arguments),f=b?this.length:0,e=0;e<c.length;e++){var g=c[e];r(g)&&(c[e]=P(g,"*",this))}e=[][a].apply(this,c);(!this.comparator||!c.length)&&l(this,"change",[""+f,"add",c,j]);return e}});d.each({pop:"length",shift:0},function(a,b){R.prototype[a]=function(){var c=arguments[0]&&d.isArray(arguments[0])?arguments[0]:d.makeArray(arguments),f=b&&this.length?
this.length-1:0,c=[][a].apply(this,c);l(this,"change",[""+f,"remove",j,[c]]);c&&c.unbind&&c.unbind("change"+this._namespace);return c}});R.prototype.indexOf=[].indexOf||function(a){return d.inArray(a,this)};var Ka=function(a,b,c){var f=new d.Deferred;a.then(function(){arguments[0]=b[c](arguments[0]);f.resolve.apply(f,arguments)},function(){f.resolveWith.apply(this,arguments)});return f},La=0,ia=/change.observe\d+/,ja=function(a,b,c,d,e){var g;g=[a.serialize()];var h=a.constructor,i;"destroy"==b&&
g.shift();"create"!==b&&g.unshift(a[a.constructor.id]);i=h[b].apply(h,g);g=i.pipe(function(c){a[e||b+"d"](c,i);return a});i.abort&&(g.abort=function(){i.abort()});return g.then(c,d)},Ma={create:{url:"_shortName",type:"post"},update:{data:function(a,b){var b=b||{},c=this.id;b[c]&&b[c]!==a&&(b["new"+d.capitalize(a)]=b[c],delete b[c]);b[c]=a;return b},type:"put"},destroy:{type:"delete",data:function(a){return{}[this.id]=a}},findAll:{url:"_shortName"},findOne:{}},Na=function(a,b){return function(c){var c=
a.data?a.data.apply(this,arguments):c,f=b||this[a.url||"_url"],e=c,g=a.type||"get";if("string"==typeof f){var h=f.split(" "),f={url:h.pop()};h.length&&(f.type=h.pop())}f.data="object"==typeof e&&!d.isArray(e)?d.extend(f.data||{},e):e;f.url=d.sub(f.url,f.data,!0);return d.ajax(d.extend({type:g||"post",dataType:"json",success:void 0,error:void 0},f))}};d.Observe("can.Model",{setup:function(){d.Observe.apply(this,arguments);if(this!==d.Model){var a=this;d.each(Ma,function(b,f){d.isFunction(a[b])||(a[b]=
Na(f,a[b]))});var b=d.proxy(this._clean,a);d.each({findAll:"models",findOne:"model"},function(c,d){var e=a[c];a[c]=function(c,h,i){a._reqs++;return Ka(e.call(a,c),a,d).then(h,i).then(b,b)}});"can.Model"==a.fullName&&(a.fullName="Model"+ ++La);this.store={};this._reqs=0;this._url=this._shortName+"/{"+this.id+"}"}},_clean:function(){this._reqs--;if(!this._reqs)for(var a in this.store)this.store[a]._bindings||delete this.store[a]},models:function(a){if(a){var b=this,c=new (b.List||ka),f=d.isArray(a),
e=a instanceof ka,e=f?a:e?a.serialize():a.data;d.each(e,function(a,d){c.push(b.model(d))});f||d.each(a,function(a,b){"data"!==a&&(c[a]=b)});return c}},model:function(a){if(a){a instanceof this&&(a=a.serialize());var b=this.store[a.id]||new this(a);this._reqs&&(this.store[a.id]=b);return b}}},{isNew:function(){var a=this[this.constructor.id];return!(a||0===a)},save:function(a,b){return ja(this,this.isNew()?"create":"update",a,b)},destroy:function(a,b){return ja(this,"destroy",a,b,"destroyed")},bind:function(a){ia.test(a)||
(this._bindings||(this.constructor.store[this[this.constructor.id]]=this,this._bindings=0),this._bindings++);return d.Observe.prototype.bind.apply(this,arguments)},unbind:function(a){ia.test(a)||(this._bindings--,this._bindings||delete this.constructor.store[this[this.constructor.id]]);return d.Observe.prototype.unbind.apply(this,arguments)},___set:function(a,b){d.Observe.prototype.___set.call(this,a,b);a===this.constructor.id&&this._bindings&&(this.constructor.store[this[this.constructor.id]]=this)}});
d.each(["created","updated","destroyed"],function(a,b){d.Model.prototype[b]=function(a){var f=this.constructor;a&&"object"==typeof a&&this.attr(a.attr?a.attr():a);d.trigger(this,b);d.trigger(this,"change",b);d.trigger(f,b,this)}});var ka=d.Observe.List("can.Model.List",{setup:function(){d.Observe.List.prototype.setup.apply(this,arguments);var a=this;this.bind("change",function(b,c){/\w+\.destroyed/.test(c)&&a.splice(a.indexOf(b.target),1)})}}),Oa=/^\d+$/,Pa=/([^\[\]]+)|(\[\])/g,Qa=/([^?#]*)(#.*)?$/,
la=function(a){return decodeURIComponent(a.replace(/\+/g," "))};d.extend(d,{deparam:function(a){var b={};a&&Qa.test(a)&&(a=a.split("&"),d.each(a,function(a,d){var e=d.split("="),g=la(e.shift()),h=la(e.join("="));current=b;for(var e=g.match(Pa),g=0,i=e.length-1;g<i;g++)current[e[g]]||(current[e[g]]=Oa.test(e[g+1])||"[]"==e[g+1]?[]:{}),current=current[e[g]];lastPart=e.pop();"[]"==lastPart?current.push(h):current[lastPart]=h}));return b}});var ma=/\:([\w\.]+)/g,na=/^(?:&[^=]+=[^&]*)+/,Ra=function(a){return d.map(a,
function(a,c){return("className"===c?"class":c)+'="'+d.esc(a)+'"'}).join(" ")},oa=!0,S=w.location,t=d.each,m=d.extend;d.route=function(a,b){var c=[],f=a.replace(ma,function(a,b){c.push(b);return"([^\\/\\&]*)"});d.route.routes[a]={test:RegExp("^"+f+"($|&)"),route:a,names:c,defaults:b||{},length:a.split("/").length};return d.route};m(d.route,{param:function(a){delete a.route;var b,c=0,f,e=a.route;(!e||!(b=d.route.routes[e]))&&t(d.route.routes,function(d,e){a:{for(var g=0,h=0;h<e.names.length;h++){if(!a.hasOwnProperty(e.names[h])){f=
-1;break a}g++}f=g}f>c&&(b=e,c=f)});if(b){var g=m({},a),e=b.route.replace(ma,function(c,d){delete g[d];return a[d]===b.defaults[d]?"":encodeURIComponent(a[d])}),h;t(b.defaults,function(a,b){g[a]===b&&delete g[a]});h=d.param(g);return e+(h?"&"+h:"")}return d.isEmptyObject(a)?"":"&"+d.param(a)},deparam:function(a){var b={length:-1};t(d.route.routes,function(c,d){d.test.test(a)&&d.length>b.length&&(b=d)});if(-1<b.length){var c=a.match(b.test),f=c.shift(),e=(f=a.substr(f.length-("&"===c[c.length-1]?1:
0)))&&na.test(f)?d.deparam(f.slice(1)):{},e=m(!0,{},b.defaults,e);t(c,function(a,c){c&&"&"!==c&&(e[b.names[a]]=decodeURIComponent(c))});e.route=b.route;return e}"&"!==a.charAt(0)&&(a="&"+a);return na.test(a)?d.deparam(a.slice(1)):{}},data:new d.Observe({}),routes:{},ready:function(a){!1===a&&(oa=a);(!0===a||!0===oa)&&pa();return d.route},url:function(a,b){b&&(a=m({},T,a));return"#!"+d.route.param(a)},link:function(a,b,c,f){return"<a "+Ra(m({href:d.route.url(b,f)},c))+">"+a+"</a>"},current:function(a){return S.hash==
"#!"+d.route.param(a)}});t("bind,unbind,delegate,undelegate,attr,removeAttr".split(","),function(a,b){d.route[b]=function(){return d.route.data[b].apply(d.route.data,arguments)}});var qa,T,pa=function(){T=d.route.deparam(S.hash.split(/#!?/).pop()||"");d.route.attr(T,!0)};d.bind.call(w,"hashchange",pa);d.route.bind("change",function(){clearTimeout(qa);qa=setTimeout(function(){S.hash="#!"+d.route.param(d.route.data.serialize())},1)});d.bind.call(document,"ready",d.route.ready);var D=function(a,b,c){d.bind.call(a,
b,c);return function(){d.unbind.call(a,b,c)}},z=d.isFunction,m=d.extend,t=d.each,Sa=[].slice,Ta=d.getObject("$.event.special")||{},ra=function(a,b,c,f){d.delegate.call(a,b,c,f);return function(){d.undelegate.call(a,b,c,f)}},U=function(a,b){var c="string"==typeof b?a[b]:b;return function(){a.called=b;return c.apply(a,[this.nodeName?d.$(this):this].concat(Sa.call(arguments,0)))}},V;d.Construct("can.Control",{setup:function(){d.Construct.setup.apply(this,arguments);if(this!==d.Control){var a;this.actions=
{};for(a in this.prototype)"constructor"!=a&&z(this.prototype[a])&&this._isAction(a)&&(this.actions[a]=this._action(a))}},_isAction:function(a){return!(!Ta[a]&&!W[a]&&!/[^\w]/.test(a))},_action:function(a,b){if(b||!/\{([^\}]+)\}/g.test(a)){var c=b?d.sub(a,[b,w]):a,f=d.isArray(c),e=(f?c[1]:c).match(/^(?:(.*?)\s)?([\w\.\:>]+)$/);return{processor:W[e[2]]||V,parts:e,delegate:f?c[0]:j}}},processors:{},defaults:{}},{setup:function(a,b){var c=this.constructor,f=c.pluginName||c._fullName;this.element=d.$(a);
f&&"can_control"!==f&&this.element.addClass(f);(f=d.data(this.element,"controls"))||d.data(this.element,"controls",f=[]);f.push(this);this.options=m({},c.defaults,b);this.on();return[this.element,this.options]},on:function(a,b,c,f){if(!a){this.off();var a=this.constructor,b=this._bindings,c=a.actions,f=this.element,e=U(this,"destroy"),g;for(g in c)c.hasOwnProperty(g)&&(ready=c[g]||a._action(g,this.options),b.push(ready.processor(ready.delegate||f,ready.parts[2],ready.parts[1],g,this)));d.bind.call(f,
"destroyed",e);b.push(function(a){d.unbind.call(a,"destroyed",e)});return b.length}"string"==typeof a&&(f=c,c=b,b=a,a=this.element);"string"==typeof f&&(f=U(this,f));this._bindings.push(b?ra(a,d.trim(b),c,f):D(a,c,f));return this._bindings.length},off:function(){var a=this.element[0];t(this._bindings||[],function(b,c){c(a)});this._bindings=[]},destroy:function(){var a=this.constructor,a=a.pluginName||a._fullName;this.off();a&&"can_control"!==a&&this.element.removeClass(a);a=d.data(this.element,"controls");
a.splice(d.inArray(this,a),1);d.trigger(this,"destroyed");this.element=null}});var W=d.Control.processors;V=function(a,b,c,f,e){f=U(e,f);return c?ra(a,d.trim(c),b,f):D(a,b,f)};t("change,click,contextmenu,dblclick,keydown,keyup,keypress,mousedown,mousemove,mouseout,mouseover,mouseup,reset,resize,scroll,select,submit,focusin,focusout,mouseenter,mouseleave".split(","),function(a,b){W[b]=V});d.Control.processors.route=function(a,b,c,f,e){d.route(c||"");var g,h=function(a){if(d.route.attr("route")===(c||
"")&&(a.batchNum===j||a.batchNum!==g))g=a.batchNum,a=d.route.attr(),delete a.route,e[f](a)};d.route.bind("change",h);return function(){d.route.unbind("change",h)}};var z=d.isFunction,Ua=d.makeArray,sa=1,k=d.view=function(a,b,c,f){a=k.render(a,b,c,f);return d.isDeferred(a)?a.pipe(function(a){return k.frag(a)}):k.frag(a)};d.extend(k,{frag:function(a){a=d.buildFragment([a],[document.body]).fragment;a.childNodes.length||a.appendChild(document.createTextNode(""));return k.hookup(a)},hookup:function(a){var b=
[],c,f,e,g=0;for(d.each(a.childNodes?d.makeArray(a.childNodes):a,function(a,c){1===c.nodeType&&(b.push(c),b.push.apply(b,d.makeArray(c.getElementsByTagName("*"))))});e=b[g++];)if(e.getAttribute&&(c=e.getAttribute("data-view-id"))&&(f=k.hookups[c]))f(e,c),delete k.hookups[c],e.removeAttribute("data-view-id");return a},hookups:{},hook:function(a){k.hookups[++sa]=a;return" data-view-id='"+sa+"'"},cached:{},cache:!0,register:function(a){this.types["."+a.suffix]=a},types:{},ext:".ejs",registerScript:function(){},
preload:function(){},render:function(a,b,c,f){z(c)&&(f=c,c=j);var e=Va(b);if(e.length){var g=new d.Deferred;e.push(ta(a,!0));d.when.apply(d,e).then(function(a){var e=Ua(arguments),h=e.pop();if(d.isDeferred(b))b=ua(a);else for(var J in b)d.isDeferred(b[J])&&(b[J]=ua(e.shift()));e=h(b,c);g.resolve(e);f&&f(e)});return g}var h,e=z(f),g=ta(a,e);e?(h=g,g.then(function(a){f(a(b,c))})):g.then(function(a){h=a(b,c)});return h}});d.isDeferred=function(a){return a&&z(a.then)&&z(a.pipe)};var va=function(a,b){if(!a.length)throw"can.view: No template or empty template:"+
b;},ta=function(a,b){var c=a.match(/\.[\w\d]+$/),f,e,g,h=function(a){var a=f.renderer(g,a),b=new d.Deferred;b.resolve(a);k.cache&&(k.cached[g]=b);return b};if(e=document.getElementById(a))c="."+e.type.match(/\/(x\-)?(.+)/)[2];c||(a+=c=k.ext);d.isArray(c)&&(c=c[0]);g=a.split(/\/|\./g).join("_");if(a.match(/^\/\//))var i=a.substr(2),a=!w.steal?"/"+i:steal.root.mapJoin(i);f=k.types[c];if(k.cached[g])return k.cached[g];if(e)return h(e.innerHTML);var j=new d.Deferred;d.ajax({async:b,url:a,dataType:"text",
error:function(b){va("",a);j.reject(b)},success:function(b){va(b,a);j.resolve(f.renderer(g,b));k.cache&&(k.cached[g]=j)}});return j},Va=function(a){var b=[];if(d.isDeferred(a))return[a];for(var c in a)d.isDeferred(a[c])&&b.push(a[c]);return b},ua=function(a){return d.isArray(a)&&"success"===a[1]?a[0]:a},Wa=function(a){eval(a)},m=d.extend,wa=/\s*\(([\$\w]+)\)\s*->([^\n]*)/,xa=/([^\s]+)=$/,Xa=/(\r|\n)+/g,Ya=/__!!__/g,Za={"":"span",table:"tr",tr:"td",ol:"li",ul:"li",tbody:"tr",thead:"tr",tfoot:"tr"},
E={"class":"className"},ya=d.each(["checked","disabled","readonly","required"],function(a,b){E[b]=b}),X=function(a,b,c){"style"===b?d.each(c.split(";"),function(b,c){var d=c.split(":");a.style[d[0]]=d[1]||""}):E[b]?a[E[b]]=-1<d.inArray(b,ya)?!0:c:a.setAttribute(b,c)},u=function(a,b,c,f){var e=f.matched===j;f.matched=!f.matched;d.each(a,function(a,b){f[b.obj._namespace+"|"+b.attr]?f[b.obj._namespace+"|"+b.attr].matched=f.matched:(b.matched=f.matched,f[b.obj._namespace+"|"+b.attr]=b,b.obj.bind(b.attr,
c))});for(var g in f)a=f[g],"matched"!==g&&a.matched!==f.matched&&(a.obj.unbind(a.attr),delete f[g]);e&&d.bind.call(b,"destroyed",function(){d.each(f,function(a,b){"boolean"!==typeof b&&b.obj.unbind(b.attr,c)})})},$a=function(a){return"string"==typeof a||"number"==typeof a?d.esc(a):Y(a)},Y=function(a){if("string"==typeof a)return a;if(!a&&0!=a)return"";var b=a.hookup&&function(b,d){a.hookup.call(a,b,d)}||"function"==typeof a&&a;return b?(A.push(b),""):""+a},F=function(a,b){d.Observe&&(d.Observe.__reading=
function(a,b){c.push({obj:a,attr:b})});var c=[],f=a.call(b);d.Observe&&delete d.Observe.__reading;return{value:f,observed:c}},q=function(a){if(this.constructor!=q){var b=new q(a);return function(a,d){return b.render(a,d)}}"function"==typeof a?this.template={fn:a}:(m(this,a),this.template=ab(this.text,this.name))};d.EJS=q;q.prototype.render=function(a,b){a=a||{};return this.template.fn.call(a,a,new q.Helpers(a,b||{}))};m(q,{txt:function(a,b,c,f,e){var g=F(e,f),h=g.observed,i=g.value,p={},b=Za[b]||
"span";if(!h.length)return(a||0!==c?$a:Y)(i);if(0==c)return"<"+b+d.view.hook(a?function(a){var b=a.parentNode,c=document.createTextNode(i),d=function(){var a=F(e,f);c.nodeValue=""+a.value;u(a.observed,b,d,p)};b.insertBefore(c,a);b.removeChild(a);u(h,b,d,p)}:function(a){var b=function(a,b){var c=d.view.frag(a),e=d.map(c.childNodes,function(a){return a}),f=b[b.length-1];f.nextSibling?f.parentNode.insertBefore(c,f.nextSibling):f.parentNode.appendChild(c);d.remove(d.$(b));return e},c=b(i,[a]),g=function(){var d=
F(e,f);c=b(d.value,c);u(d.observed,a.parentNode,g,p)};u(h,a.parentNode,g,p)})+"></"+b+">";if(1===c){var k=i.replace(/['"]/g,"").split("=")[0];A.push(function(a){var b=function(){var c=F(e,f),g=(c.value||"").replace(/['"]/g,"").split("="),h=g[0];if(h!=k&&k){var i=k;-1<d.inArray(i,ya)?a[i]=!1:a.removeAttribute(i)}h&&(X(a,h,g[1]),k=h);u(c.observed,a,b,p)};u(h,a,b,p)});return i}A.push(function(a){var b=d.$(a),g;(g=d.data(b,"hooks"))||d.data(b,"hooks",g={});var k=E[c]?a[E[c]]:a.getAttribute(c),b=k.split("__!!__"),
n,l=function(b){if(b.batchNum===j||b.batchNum!==n.batchNum)n.batchNum=b.batchNum,X(a,c,n.render())};g[c]?g[c].funcs.push({func:e,old:p}):g[c]={render:function(){var b=0;return k.replace(Ya,function(){var c=F(n.funcs[b].func,f);u(c.observed,a,l,n.funcs[b++].old);return Y(c.value)})},funcs:[{func:e,old:p}],batchNum:j};n=g[c];b.splice(1,0,i);X(a,c,b.join(""));u(h,a,l,p)});return"__!!__"},pending:function(){if(A.length){var a=A.slice(0);A=[];return d.view.hook(function(b){d.each(a,function(a,d){d(b)})})}return""}});
var bb=RegExp("(<%%|%%>|<%==|<%=|<%#|<%|%>|<|>|\"|')","g"),B=null,L=s=null,A=[],ab=function(a,b){var c=[],d=0,a=a.replace(Xa,"\n");a.replace(bb,function(b,e,g){g>d&&c.push(a.substring(d,g));c.push(e);d=g+e.length});0===d&&c.push(a);var e="",g=["var ___v1ew = [];"],h=function(a,b){g.push("___v1ew.push(",'"',a.split("\\").join("\\\\").split("\n").join("\\n").split('"').join('\\"').split("\t").join("\\t"),'"'+(b||"")+");")},i=[],k,l=null,o=!1,m="",q=[],r=0,n;for(B=s=L=null;(n=c[r++])!==j;){if(null===
l)switch(n){case "<%":case "<%=":case "<%==":o=1;case "<%#":l=n;e.length&&h(e);e="";break;case "<%%":e+="<%";break;case "<":0!==c[r].indexOf("!--")&&(B=1,o=0);e+=n;break;case ">":B=0;o?(h(e,',can.EJS.pending(),">"'),e=""):e+=n;break;case "'":case '"':B&&(s&&s===n?s=null:null===s&&(s=n,L=k));default:"<"===k&&(m=n.split(" ")[0],0===m.indexOf("/")&&q.pop()===m.substr(1)?m=q[q.length-1]||m.substr(1):q.push(m)),e+=n}else switch(n){case "%>":switch(l){case "<%":k=--e.split("{").length- --e.split("}").length;
1==k?(g.push("___v1ew.push(","can.EJS.txt(0,'"+m+"',"+(s?"'"+L.match(xa)[1]+"'":B?1:0)+",this,function(){","var ___v1ew = [];",e),i.push({before:"",after:"return ___v1ew.join('')}));"})):(d=i.length&&-1==k?i.pop():{after:";"},d.before&&g.push(d.before),g.push(e,";",d.after));break;case "<%=":case "<%==":(k=--e.split("{").length- --e.split("}").length)&&i.push({before:"return ___v1ew.join('')",after:"}));"}),wa.test(e)&&(e=e.match(wa),e="function(__){var "+e[1]+"=can.$(__);"+e[2]+"}"),g.push("___v1ew.push(",
"can.EJS.txt("+("<%="===l?1:0)+",'"+m+"',"+(s?"'"+L.match(xa)[1]+"'":B?1:0)+",this,function(){ return ",e,k?"var ___v1ew = [];":"}));")}l=null;e="";break;case "<%%":e+="<%";break;default:e+=n}k=n}e.length&&h(e);g.push(";");h={out:"with(_VIEW) { with (_CONTEXT) {"+g.join("")+" return ___v1ew.join('')}}"};Wa.call(h,"this.fn = (function(_CONTEXT,_VIEW){"+h.out+"});\r\n//@ sourceURL="+b+".js");return h};q.Helpers=function(a,b){this._data=a;this._extras=b;m(this,b)};q.Helpers.prototype={list:function(a,
b){a.attr("length");for(var c=0,d=a.length;c<d;c++)b(a[c],c,a)}};d.view.register({suffix:"ejs",script:function(a,b){return"can.EJS(function(_CONTEXT,_VIEW) { "+(new q({text:b,name:a})).template.out+" })"},renderer:function(a,b){return q({text:b,name:a})}})})(can={},this);

</script>
<script src="http://backbonejs.org/backbone-min.js">
</script>
<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.0.0.js">
</script>


<style>
p {
  font: 12px/16px Arial;
  margin: 10px 10px 15px;    
}

button {
  font: bold 14px/14px Arial;  
  margin-left: 10px;
}

#grid {
  margin: 10px;  
}

.box-view {
  width: 20px; height: 20px;
  float: left;
  position: relative;
  margin: 8px;    
}

.box {
  border-radius: 100px;
  width: 20px; height: 10px;
  padding: 5px 0;
  color: #fff;
  font: 10px/10px Arial;
  text-align: center;
  position: absolute;
}  

</style>
<script type="text/ejs" id="ejs-template">
  <% list(boxes, function( box ) { %>
    <div class="box-view">
      <div class="box" id="box-<%= box.count  %>" style="<%= box.style() %>">
        <%= box.content() %>
      </div>
    </div>
  <% }) %>
</script>

<script type="x-template" id="underscore-template">
  <div class="box" id="box-<%= number %>" style="top: <%= top %>px; left: <%= left %>px; background: rgb(0,0,<%= color %>);">
    <%= content %>
  </div>
</script>

<script type="text/x-handlebars" id="handlebars-template" data-template-name="box">
  <div class="box" {{bindAttr id="model.number" style="model.style"}}>
    {{ model.content }}
  </div>
</script>

<script type="text/x-template" id="knockout-template">
  <div data-bind="foreach: boxes">
    <div class="box-view">
      <div class="box" data-bind="style: {top: top(), left: left(), background: color()}, text: content()"></div>
    </div>
  </div>
</script>
<script type="text/x-yui-handlebars" id="yui-handlebars-template">
    <div class="box" id="box-{{number}}" style="top: {{top}}px; left: {{left}}px; background: rgb(0,0,{{color}});">
        {{content}}
    </div>
</script>
<div id="can-grid"></div>
<div id="bb-grid"></div>
<div id="ko-grid"></div>
<div id="yui-grid"></div>
<script>
    window.N = 100;
   
    (function(){
      var Box = function() {
          return {
            count: 0,
            top: function() {
              return (Math.sin(this.attr('count') / 10) * 10) + 'px';
            },
            left: function() {
              return (Math.cos(this.attr('count') / 10) * 10) + 'px';
            },
            color: function() {
              return 'rgb(0,0,' + (this.attr('count')) % 255 + ')';
            },
            content: function() {
              return this.attr('count') % 100;
            },
            tick: function() {
              this.attr('count', (this.attr('count') + 1));
            },
            style: function() {
              this.attr('count');
              return 'top: ' + this.top() + '; left: ' + this.left() + '; background: ' + this.color() + ';';
            }
          };
        };
        var boxes;
        window.canInit = function() {
          boxes = new can.Observe.List();
          for (var i = 0; i < N; i++) {
            boxes.push(new Box());
          }
          $('#can-grid').append(can.view('ejs-template', {
            boxes: boxes
          }));
          };
          var canAnimate = function() {
            for (var i = 0; i < N; i++) {
              boxes[i].tick();
            }
          }
      window.runCan = function() {
        canAnimate();
      };
    })();
   
    (function() {
   
      var Box = Backbone.Model.extend({
   
        defaults: {
          top: 0,
          left: 0,
          color: 0,
          content: 0
        },
   
        initialize: function() {
          this.count = 0;
        },
   
        tick: function() {
          var count = this.count += 1;
          this.set({
            top: Math.sin(count / 10) * 10,
            left: Math.cos(count / 10) * 10,
            color: (count) % 255,
            content: count % 100
          });
        }
   
      });
   
   
      var BoxView = Backbone.View.extend({
   
        className: 'box-view',
   
        template: _.template($('#underscore-template').html()),
   
        initialize: function() {
          this.model.bind('change', this.render, this);
        },
   
        render: function() {
          this.$el.html(this.template(this.model.attributes));
          return this;
        }
   
      });
   
      var boxes;
   
      window.backboneInit = function() {
          boxes = _.map(_.range(N), function(i) {
            var box = new Box({
              number: i
            });
            var view = new BoxView({
              model: box
            });
            $('#bb-grid').append(view.render().el);
            return box;
          });
          };
   
      var backboneAnimate = function() {
            for (var i = 0, l = boxes.length; i < l; i++) {
              boxes[i].tick();
            }
          };
   
      window.runBackbone = function() {
        backboneAnimate();
      };
    })();
   
    (function() {
   
      var Box = function() {
          this.count = ko.observable(0);
          this.top = function() {
            return (Math.sin(this.count() / 10) * 10) + 'px';
          };
          this.left = function() {
            return (Math.cos(this.count() / 10) * 10) + 'px';
          };
          this.color = function() {
            return 'rgb(0,0,' + (this.count()) % 255 + ')';
          };
          this.content = function() {
            return this.count() % 100;
          };
   
   
          this.tick = function() {
            this.count(this.count() + 1);
          }
          }
         
         
         
         
         
      var ViewModel = function(num) {
          this.num = num;
          this.boxes = ko.observableArray();
          for (var i = 0; i < num; i++) {
            this.boxes.push(new Box())
          }
          }
         
         
         
         
         
      var knockoutAnimate = function() {
            for (var i = 0, l = vm.boxes().length; i < l; i++) {
              vm.boxes()[i].tick();
            }
      }
         
         
          window.knockoutInit = function(){
            $('#ko-grid').html($('#knockout-template').html());
            vm = new ViewModel(N)
            ko.applyBindings(vm, $('#ko-grid')[0]);
          };
         
         
      window.runKnockout = function() {
        knockoutAnimate();
      };
   
    })();
   
YUI().use('model', 'view', 'handlebars', function (Y) {

Y.Box = Y.Base.create('box', Y.Model, [], {
    initializer: function () {
        this.count = 0;
    },
   
    tick: function () {
        var count = this.count += 1;
       
        this.setAttrs({
            top    : Math.sin(count / 10) * 10,
            left   : Math.cos(count / 10) * 10,
            color  : (count) % 255,
            content: count % 100
        });
    }        
}, {
    ATTRS: {
        top    : {value: 0},
        left   : {value: 0},
        color  : {value: 0},
        content: {value: 0},
        number : {}
    }
});
   
Y.BoxView = Y.Base.create('boxView', Y.View, [], {
    template: Y.Handlebars.compile(Y.one('#yui-handlebars-template').getContent()),
   
    initializer: function () {
        this.get('model').after('change', this.render, this);
    },
   
    create: function () {
        return Y.Node.create('<div class="box-view" />');
    },
   
    render: function () {
        var content = this.template(this.get('model').toJSON());
        this.get('container').setContent(content);
        return this;
    }
});

var boxes;

var yuiAnimate = function() {
    for (var i = 0, l = boxes.length; i < l; i++) {
      boxes[i].tick();  
    }
};

window.yuiInit = function () {
    boxes = _.map(_.range(N), function (i) {
        var box  = new Y.Box({number: i});
        var view = new Y.BoxView({model: box});
        Y.one('#yui-grid').append(view.render().get('container'));
        return box;
    });
};

window.runYUI = function() {
  yuiAnimate();    
};
   
});
    </script>
<script>
Benchmark.prototype.setup = function() {
    window.canInit();
        window.backboneInit();
        window.knockoutInit();
    window.yuiInit();
};

Benchmark.prototype.teardown = function() {
    $('#can-grid').empty();
    $('#bb-grid').empty();
    $('#ko-grid').empty();
    $('#yui-grid').empty();
};
</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
CanJS
window.runCan()
pending…
Backbone
window.runBackbone();
pending…
Knockout
window.runKnockout();
pending…
YUI
window.runYUI();
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