*[YUI on StackOverflow](http://stackoverflow.com/questions/tagged/yui)
*[Forms](http://yuilibrary.com/forum)
*[YUI on Twitter](http://twitter.com/yuilibrary)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._
<htmllang="en"><head><metacharset="utf-8"><title>TodoMVC</title><metaname="description"content="Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS, Spine and many more"><metaname="viewport"content="width=device-width,initial-scale=1"><metaname="twitter:card"content="summary"><metaname="twitter:title"content="TodoMVC"><metaname="twitter:site"content="@TasteJS"><metaname="twitter:description"content="Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS and many more"><metaname="twitter:image"content="https://raw.githubusercontent.com/tastejs/todomvc/gh-pages/site-assets/screenshot.png"><metaproperty="og:url"content="http://todomvc.com"><metaproperty="og:title"content="TodoMVC"><metaproperty="og:image"content="https://raw.github.com/tastejs/todomvc/gh-pages/site-assets/screenshot.png"><metaproperty="og:description"content="Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS, Spine and many more"><linkrel="shortcut icon"href="site-assets/favicon.ico"><script src="bower_components/platform/platform.js"></script><linkrel="stylesheet"href="site-assets/main.min.css"><divhidden><style shim-shadowdom="">html/deep/[layout][horizontal],html/deep/[layout][vertical]{display:-ms-flexbox;display:-webkit-flex;display:flex}html/deep/[layout][horizontal][inline],html/deep/[layout][vertical][inline]{display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}html/deep/[layout][horizontal]{-ms-flex-direction:row;-webkit-flex-direction:row;flex-direction:row}html/deep/[layout][horizontal][reverse]{-ms-flex-direction:row-reverse;-webkit-flex-direction:row-reverse;flex-direction:row-reverse}html/deep/[layout][vertical]{-ms-flex-direction:column;-webkit-flex-direction:column;flex-direction:column}html/deep/[layout][vertical][reverse]{-ms-flex-direction:column-reverse;-webkit-flex-direction:column-reverse;flex-direction:column-reverse}html/deep/[layout][wrap]{-ms-flex-wrap:wrap;-webkit-flex-wrap:wrap;flex-wrap:wrap}html/deep/[layout][wrap-reverse]{-ms-flex-wrap:wrap-reverse;-webkit-flex-wrap:wrap-reverse;flex-wrap:wrap-reverse}html/deep/[flex]{-ms-flex:110;-webkit-flex:1;flex:1;-webkit-flex-basis:0;flex-basis:0}html/deep/[vertical][layout]>[flex][auto-vertical],html/deep/[vertical][layout]::shadow[flex][auto-vertical]{-ms-flex:11auto;-webkit-flex-basis:auto;flex-basis:auto}html/deep/[flex][auto]{-ms-flex:11auto;-webkit-flex-basis:auto;flex-basis:auto}html/deep/[flex][none]{-ms-flex:none;-webkit-flex:none;flex:none}html/deep/[flex][one]{-ms-flex:1;-webkit-flex:1;flex:1}html/deep/[flex][two]{-ms-flex:2;-webkit-flex:2;flex:2}html/deep/[flex][three]{-ms-flex:3;-webkit-flex:3;flex:3}html/deep/[flex][four]{-ms-flex:4;-webkit-flex:4;flex:4}html/deep/[flex][five]{-ms-flex:5;-webkit-flex:5;flex:5}html/deep/[flex][six]{-ms-flex:6;-webkit-flex:6;flex:6}html/deep/[flex][seven]{-ms-flex:7;-webkit-flex:7;flex:7}html/deep/[flex][eight]{-ms-flex:8;-webkit-flex:8;flex:8}html/deep/[flex][nine]{-ms-flex:9;-webkit-flex:9;flex:9}html/deep/[flex][ten]{-ms-flex:10;-webkit-flex:10;flex:10}html/deep/[flex][eleven]{-ms-flex:11;-webkit-flex:11;flex:11}html/deep/[flex][twelve]{-ms-flex:12;-webkit-flex:12;flex:12}html/deep/[layout][start]{-ms-flex-align:start;-webkit-align-items:flex-start;align-items:flex-start}html/deep/[layout][center],html/deep/[layout][center-center]{-ms-flex-align:center;-webkit-align-items:center;align-items:center}html/deep/[layout][end]{-ms-flex-align:end;-webkit-align-items:flex-end;align-items:flex-end}html/deep/[layout][start-justified]{-ms-flex-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start}html/deep/[layout][center-justified],html/deep/[layout][center-center]{-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center}html/deep/[layout][end-justified]{-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end}html/deep/[layout][around-justified]{-ms-flex-pack:distribute;-webkit-justify-content:space-around;justify-content:space-around}html/deep/[layout][justified]{-ms-flex-pack:justify;-webkit-justify-content:space-between;justify-content:space-between}html/deep/[self-start]{-ms-align-self:flex-start;-webkit-align-self:flex-start;align-self:flex-start}html/deep/[self-center]{-ms-align-self:center;-webkit-align-self:center;align-self:center}html/deep/[self-end]{-ms-align-self:flex-end;-webkit-align-self:flex-end;align-self:flex-end}html/deep/[self-stretch]{-ms-align-self:stretch;-webkit-align-self:stretch;align-self:stretch}html/deep/[block]{display:block}html/deep/[hidden]{display:none!important}html/deep/[relative]{position:relative}html/deep/[fit]{position:absolute;top:0;right:0;bottom:0;left:0}body[fullbleed]{margin:0;height:100vh}html/deep/[segment],html/deep/segment{display:block;position:relative;-webkit-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;margin:1em.5em;padding:1em;background-color:#fff;-webkit-box-shadow:0001pxrgba(0,0,0,.1);box-shadow:0001pxrgba(0,0,0,.1);border-radius:5px5px5px5px}</style><script src="bower_components/polymer/polymer.js"></script><polymer-elementname="core-selection"attributes="multi"hiddenassetpath="bower_components/core-selection/"><script>Polymer("core-selection",{multi:false,ready:function(){this.clear()},clear:function(){this.selection=[]},getSelection:function(){returnthis.multi?this.selection:this.selection[0]},isSelected:function(item){returnthis.selection.indexOf(item)>=0},setItemSelected:function(item,isSelected){if(item!==undefined&&item!==null){if(isSelected){this.selection.push(item)}else{vari=this.selection.indexOf(item);if(i>=0){this.selection.splice(i,1)}}this.fire("core-select",{isSelected:isSelected,item:item})}},select:function(item){if(this.multi){this.toggle(item)}elseif(this.getSelection()!==item){this.setItemSelected(this.getSelection(),false);this.setItemSelected(item,true)}},toggle:function(item){this.setItemSelected(item,!this.isSelected(item))}});</script></polymer-element><polymer-elementname="core-selector"attributes="selected multi valueattr selectedClass selectedProperty selectedAttribute selectedItem selectedModel selectedIndex notap excludedLocalNames target itemsSelector activateEvent"assetpath="bower_components/core-selector/"><template><core-selectionid="selection"multi="{{multi}}"on-core-select="{{selectionSelect}}"></core-selection><contentid="items"select="*"></content></template><script>Polymer("core-selector",{selected:null,multi:false,valueattr:"name",selectedClass:"core-selected",selectedProperty:"",selectedAttribute:"active",selectedItem:null,selectedModel:null,selectedIndex:-1,excludedLocalNames:"",target:null,itemsSelector:"",activateEvent:"tap",notap:false,defaultExcludedLocalNames:"template",ready:function(){this.activateListener=this.activateHandler.bind(this);this.itemFilter=this.filterItem.bind(this);this.excludedLocalNamesChanged();this.observer=newMutationObserver(this.updateSelected.bind(this));if(!this.target){this.target=this}},getitems(){if(!this.target){return[]}varnodes=this.target!==this?this.itemsSelector?this.target.querySelectorAll(this.itemsSelector):this.target.children:this.$.items.getDistributedNodes();returnArray.prototype.filter.call(nodes,this.itemFilter)},filterItem:function(node){return!this._excludedNames[node.localName]},excludedLocalNamesChanged:function(){this._excludedNames={};vars=this.defaultExcludedLocalNames;if(this.excludedLocalNames){s+=""+this.excludedLocalNames}s.split(/\s+/g).forEach(function(n){this._excludedNames[n]=1},this)},targetChanged:function(old){if(old){this.removeListener(old);this.observer.disconnect();this.clearSelection()}if(this.target){this.addListener(this.target);this.observer.observe(this.target,{childList:true});this.updateSelected()}},addListener:function(node){Polymer.addEventListener(node,this.activateEvent,this.activateListener)},removeListener:function(node){Polymer.removeEventListener(node,this.activateEvent,this.activateListener)},getselection(){returnthis.$.selection.getSelection()},selectedChanged:function(){this.updateSelected()},updateSelected:function(){this.validateSelected();if(this.multi){this.clearSelection();this.selected&&this.selected.forEach(function(s){this.valueToSelection(s)},this)}else{this.valueToSelection(this.selected)}},validateSelected:function(){if(this.multi&&!Array.isArray(this.selected)&&this.selected!==null&&this.selected!==undefined){this.selected=[this.selected]}},clearSelection:function(){if(this.multi){this.selection.slice().forEach(function(s){this.$.selection.setItemSelected(s,false)},this)}else{this.$.selection.setItemSelected(this.selection,false)}this.selectedItem=null;this.$.selection.clear()},valueToSelection:function(value){varitem=value===null||value===undefined?null:this.items[this.valueToIndex(value)];this.$.selection.select(item)},updateSelectedItem:function(){this.selectedItem=this.selection},selectedItemChanged:function(){if(this.selectedItem){vart=this.selectedItem.templateInstance;this.selectedModel=t?t.model:undefined}else{this.selectedModel=null}this.selectedIndex=this.selectedItem?parseInt(this.valueToIndex(this.selected)):-1},valueToIndex:function(value){for(vari=0,items=this.items,c;c=items[i];i++){if(this.valueForNode(c)==value){returni}}returnvalue},valueForNode:function(node){returnnode[this.valueattr]||node.getAttribute(this.valueattr)},selectionSelect:function(e,detail){this.updateSelectedItem();if(detail.item){this.applySelection(detail.item,detail.isSelected)}},applySelection:function(item,isSelected){if(this.selectedClass){item.classList.toggle(this.selectedClass,isSelected)}if(this.selectedProperty){item[this.selectedProperty]=isSelected}if(this.selectedAttribute&&item.setAttribute){if(isSelected){item.setAttribute(this.selectedAttribute,"")}else{item.removeAttribute(this.selectedAttribute)}}},activateHandler:function(e){if(!this.notap){vari=this.findDistributedTarget(e.target,this.items);if(i>=0){varitem=this.items[i];vars=this.valueForNode(item)||i;if(this.multi){if(this.selected){this.addRemoveSelected(s)}else{this.selected=[s]}}else{this.selected=s}this.asyncFire("core-activate",{item:item})}}},addRemoveSelected:function(value){vari=this.selected.indexOf(value);if(i>=0){this.selected.splice(i,1)}else{this.selected.push(value)}this.valueToSelection(value)},findDistributedTarget:function(target,nodes){while(target&&target!=this){vari=Array.prototype.indexOf.call(nodes,target);if(i>=0){returni}target=target.parentNode}},selectIndex:function(index){varitem=this.items[index];if(item){this.selected=this.valueForNode(item)||index;returnitem}},selectPrevious:function(wrap){vari=wrap&&!this.selectedIndex?this.items.length-1:this.selectedIndex-1;returnthis.selectIndex(i)},selectNext:function(wrap){vari=wrap&&this.selectedIndex>=this.items.length-1?0:this.selectedIndex+1;returnthis.selectIndex(i)}});</script></polymer-element><polymer-elementname="paper-ripple"attributes="initialOpacity opacityDecayVelocity"assetpath="bower_components/paper-ripple/"><template><style>:host{display:block;position:relative}:host-context([noink]){pointer-events:none}#bg,#waves,.wave-container,.wave{pointer-events:none;position:absolute;top:0;left:0;width:100%;height:100%}#bg,.wave{opacity:0}#waves,.wave{overflow:hidden}.wave-container,.wave{border-radius:50%}:host(.circle)#bg,:host(.circle)#waves{border-radius:50%}:host(.circle).wave-container{overflow:hidden}</style><divid="bg"></div><divid="waves"></div></template><script>(function(){varwaveMaxRadius=150;functionwaveRadiusFn(touchDownMs,touchUpMs,anim){vartouchDown=touchDownMs/1e3;vartouchUp=touchUpMs/1e3;vartotalElapsed=touchDown+touchUp;varww=anim.width,hh=anim.height;varwaveRadius=Math.min(Math.sqrt(ww*ww+hh*hh),waveMaxRadius)*1.1+5;varduration=1.1-.2*(waveRadius/waveMaxRadius);vartt=totalElapsed/duration;varsize=waveRadius*(1-Math.pow(80,-tt));returnMath.abs(size)}functionwaveOpacityFn(td,tu,anim){vartouchDown=td/1e3;vartouchUp=tu/1e3;vartotalElapsed=touchDown+touchUp;if(tu<=0){returnanim.initialOpacity}returnMath.max(0,anim.initialOpacity-touchUp*anim.opacityDecayVelocity)}functionwaveOuterOpacityFn(td,tu,anim){vartouchDown=td/1e3;vartouchUp=tu/1e3;varouterOpacity=touchDown*.3;varwaveOpacity=waveOpacityFn(td,tu,anim);returnMath.max(0,Math.min(outerOpacity,waveOpacity))}functionwaveDidFinish(wave,radius,anim){varwaveOpacity=waveOpacityFn(wave.tDown,wave.tUp,anim);returnwaveOpacity<.01&&radius>=Math.min(wave.maxRadius,waveMaxRadius)}functionwaveAtMaximum(wave,radius,anim){varwaveOpacity=waveOpacityFn(wave.tDown,wave.tUp,anim);returnwaveOpacity>=anim.initialOpacity&&radius>=Math.min(wave.maxRadius,waveMaxRadius)}functiondrawRipple(ctx,x,y,radius,innerAlpha,outerAlpha){if(outerAlpha!==undefined){ctx.bg.style.opacity=outerAlpha}ctx.wave.style.opacity=innerAlpha;vars=radius/(ctx.containerSize/2);vardx=x-ctx.containerWidth/2;vardy=y-ctx.containerHeight/2;ctx.wc.style.webkitTransform="translate3d("+dx+"px,"+dy+"px,0)";ctx.wc.style.transform="translate3d("+dx+"px,"+dy+"px,0)";ctx.wave.style.webkitTransform="scale("+s+","+s+")";ctx.wave.style.transform="scale3d("+s+","+s+",1)"}functioncreateWave(elem){varelementStyle=window.getComputedStyle(elem);varfgColor=elementStyle.color;varinner=document.createElement("div");inner.style.backgroundColor=fgColor;inner.classList.add("wave");varouter=document.createElement("div");outer.classList.add("wave-container");outer.appendChild(inner);varcontainer=elem.$.waves;container.appendChild(outer);elem.$.bg.style.backgroundColor=fgColor;varwave={bg:elem.$.bg,wc:outer,wave:inner,waveColor:fgColor,maxRadius:0,isMouseDown:false,mouseDownStart:0,mouseUpStart:0,tDown:0,tUp:0};returnwave}functionremoveWaveFromScope(scope,wave){if(scope.waves){varpos=scope.waves.indexOf(wave);scope.waves.splice(pos,1);wave.wc.remove()}}varpow=Math.pow;varnow=Date.now;if(window.performance&&performance.now){now=performance.now.bind(performance)}functioncssColorWithAlpha(cssColor,alpha){varparts=cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);if(typeofalpha=="undefined"){alpha=1}if(!parts){return"rgba(255, 255, 255, "+alpha+")"}return"rgba("+parts[1]+", "+parts[2]+", "+parts[3]+", "+alpha+")"}functiondist(p1,p2){returnMath.sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2))}functiondistanceFromPointToFurthestCorner(point,size){vartl_d=dist(point,{x:0,y:0});vartr_d=dist(point,{x:size.w,y:0});varbl_d=dist(point,{x:0,y:size.h});varbr_d=dist(point,{x:size.w,y:size.h});returnMath.max(tl_d,tr_d,bl_d,br_d)}Polymer("paper-ripple",{initialOpacity:.25,opacityDecayVelocity:.8,backgroundFill:true,pixelDensity:2,eventDelegates:{down:"downAction",up:"upAction"},ready:function(){this.waves=[]},downAction:function(e){varwave=createWave(this);this.cancelled=false;wave.isMouseDown=true;wave.tDown=0;wave.tUp=0;wave.mouseUpStart=0;wave.mouseDownStart=now();varrect=this.getBoundingClientRect();varwidth=rect.width;varheight=rect.height;vartouchX=e.x-rect.left;vartouchY=e.y-rect.top;wave.startPosition={x:touchX,y:touchY};if(this.classList.contains("recenteringTouch")){wave.endPosition={x:width/2,y:height/2};wave.slideDistance=dist(wave.startPosition,wave.endPosition)}wave.containerSize=Math.max(width,height);wave.containerWidth=width;wave.containerHeight=height;wave.maxRadius=distanceFromPointToFurthestCorner(wave.startPosition,{w:width,h:height});wave.wc.style.top=(wave.containerHeight-wave.containerSize)/2+"px";wave.wc.style.left=(wave.containerWidth-wave.containerSize)/2+"px";wave.wc.style.width=wave.containerSize+"px";wave.wc.style.height=wave.containerSize+"px";this.waves.push(wave);if(!this._loop){this._loop=this.animate.bind(this,{width:width,height:height});requestAnimationFrame(this._loop)}},upAction:function(){for(vari=0;i<this.waves.length;i++){varwave=this.waves[i];if(wave.isMouseDown){wave.isMouseDown=false;wave.mouseUpStart=now();wave.mouseDownStart=0;wave.tUp=0;break}}this._loop&&requestAnimationFrame(this._loop)},cancel:function(){this.cancelled=true},animate:function(ctx){varshouldRenderNextFrame=false;vardeleteTheseWaves=[];varlongestTouchDownDuration=0;varlongestTouchUpDuration=0;varlastWaveColor=null;varanim={initialOpacity:this.initialOpacity,opacityDecayVelocity:this.opacityDecayVelocity,height:ctx.height,width:ctx.width};for(vari=0;i<this.waves.length;i++){varwave=this.waves[i];if(wave.mouseDownStart>0){wave.tDown=now()-wave.mouseDownStart}if(wave.mouseUpStart>0){wave.tUp=now()-wave.mouseUpStart}vartUp=wave.tUp;vartDown=wave.tDown;longestTouchDownDuration=Math.max(longestTouchDownDuration,tDown);longestTouchUpDuration=Math.max(longestTouchUpDuration,tUp);varradius=waveRadiusFn(tDown,tUp,anim);varwaveAlpha=waveOpacityFn(tDown,tUp,anim);varwaveColor=cssColorWithAlpha(wave.waveColor,waveAlpha);lastWaveColor=wave.waveColor;varx=wave.startPosition.x;vary=wave.startPosition.y;if(wave.endPosition){vartranslateFraction=Math.min(1,radius/wave.containerSize*2/Math.sqrt(2));x+=translateFraction*(wave.endPosition.x-wave.startPosition.x);y+=translateFraction*(wave.endPosition.y-wave.startPosition.y)}varbgFillColor=null;if(this.backgroundFill){varbgFillAlpha=waveOuterOpacityFn(tDown,tUp,anim);bgFillColor=cssColorWithAlpha(wave.waveColor,bgFillAlpha)}drawRipple(wave,x,y,radius,waveAlpha,bgFillAlpha);varmaximumWave=waveAtMaximum(wave,radius,anim);varwaveDissipated=waveDidFinish(wave,radius,anim);varshouldKeepWave=!waveDissipated||maximumWave;varshouldRenderWaveAgain=!waveDissipated&&!maximumWave;shouldRenderNextFrame=shouldRenderNextFrame||shouldRenderWaveAgain;if(!shouldKeepWave||this.cancelled){deleteTheseWaves.push(wave)}}if(shouldRenderNextFrame){requestAnimationFrame(this._loop)}for(vari=0;i<deleteTheseWaves.length;++i){varwave=deleteTheseWaves[i];removeWaveFromScope(this,wave)}if(!this.waves.length&&this._loop){this.$.bg.style.backgroundColor=null;this._loop=null;this.fire("core-transitionend")}}})})();</script></polymer-element><polymer-elementname="paper-tab"attributes="noink"role="tab"assetpath="bower_components/paper-tabs/"><template><style>:host{display:block;position:relative;overflow:hidden}#tabContainer{position:absolute;top:0;right:0;bottom:0;left:0}.tab-content{transition:opacity.1scubic-bezier(0.4,0,1,1),color.1scubic-bezier(0.4,0,1,1);cursor:default;pointer-events:none}:host(:not(.core-selected)).tab-content{opacity:.6}#ink{position:absolute;top:0;right:0;bottom:0;left:0;color:#ffff8d}:host[noink]#ink{pointer-events:none}:host-context(paper-tabs[noink])#ink{pointer-events:none}</style><divid="tabContainer"center-justified=""center=""horizontal=""layout=""><divclass="tab-content"><content></content></div><paper-rippleid="ink"initialopacity="0.95"opacitydecayvelocity="0.98"></paper-ripple></div></template><script>Polymer("paper-tab",{noink:false});</script></polymer-element><polymer-elementname="paper-tabs"extends="core-selector"attributes="noink nobar"role="tablist"assetpath="bower_components/paper-tabs/"><template><style>:host{display:block;position:relative;font-size:14px;font-weight:500;height:48px;overflow:hidden}#tabsContainer{position:absolute;top:0;right:0;bottom:0;left:0;white-space:nowrap}#selectionBar{position:absolute;height:2px;bottom:0;left:0;width:0;background-color:#ffff8d;transition:width,left}#selectionBar[hidden]{display:hidden}#selectionBar.expand{transition-duration:.15s;transition-timing-function:cubic-bezier(0.4,0,1,1)}#selectionBar.contract{transition-duration:.18s;transition-timing-function:cubic-bezier(0,0,.2,1)}polyfill-next-selector{content:'#tabsContainer > *:not(#selectionBar)'}::content>*{-ms-flex:1;-webkit-flex:1;flex:1}</style><divid="tabsContainer"horizontal=""layout=""><shadow></shadow><divid="selectionBar"hidden?="{{nobar}}"on-transitionend="{{barTransitionEnd}}"></div></div></template><script>Polymer("paper-tabs",{noink:false,nobar:false,activateEvent:"down",nostretch:false,selectedIndexChanged:function(old){vars=this.$.selectionBar.style;if(!this.selectedItem){s.width=0;s.left=0;return}varw=100/this.items.length;if(this.nostretch||old===null||old===-1){s.width=w+"%";s.left=this.selectedIndex*w+"%";return}varm=5;this.$.selectionBar.classList.add("expand");if(old<this.selectedIndex){s.width=w+w*(this.selectedIndex-old)-m+"%";this._transitionCounter=1}else{s.width=w+w*(old-this.selectedIndex)-m+"%";s.left=this.selectedIndex*w+m+"%";this._transitionCounter=2}},barTransitionEnd:function(e){this._transitionCounter--;varcl=this.$.selectionBar.classList;if(cl.contains("expand")&&!this._transitionCounter){cl.remove("expand");cl.add("contract");vars=this.$.selectionBar.style;varw=100/this.items.length;s.width=w+"%";s.left=this.selectedIndex*w+"%"}elseif(cl.contains("contract")){cl.remove("contract")}}});</script></polymer-element></div></head><body><divclass="container"><headerclass="row"><divclass="col-md-8"><imgclass="logo"src="site-assets/logo.svg"width="500"height="86"alt="TodoMVC"><p>Helping you <strong>select</strong> an MV* framework</p><nav><ahref="https://github.com/tastejs/todomvc/archive/master.zip"class="zocial red">Download</a><ahref="https://github.com/tastejs/todomvc"class="zocial ltgray">View on GitHub</a><ahref="http://blog.tastejs.com/"class="zocial ltgray">Blog</a></nav></div><divclass="col-md-4"><imgclass="logo-icon"src="site-assets/logo-icon.png"width="330"height="330"alt="TodoMVC"></div></header><divclass="row"><divclass="col-md-4"><h2>Introduction</h2><p>Developers these days are spoiled with choice when it comes to <ahref="http://coding.smashingmagazine.com/2012/07/27/journey-through-the-javascript-mvc-jungle/">selecting</a> an <strong>MV* framework</strong> for structuring and organizing their JavaScript web apps.</p><p>Backbone, Ember, AngularJS… the list of new and stable solutions continues to grow, but just how do you decide on which to use in a sea of so many options?</p><p>To help solve this problem, we created <ahref="https://github.com/tastejs/todomvc">TodoMVC</a> - a project which offers the same Todo application implemented using MV* concepts in most of the popular JavaScript MV* frameworks of today.</p><ahref="https://twitter.com/tastejs"class="twitter-follow-button"data-show-count="false"data-show-screen-name="false"></a><ahref="https://twitter.com/share"class="twitter-share-button"data-via="tastejs"data-url="http://todomvc.com"data-text="TodoMVC - Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS, and more"></a><divclass="g-plusone"data-size="medium"data-annotation="none"data-href="http://todomvc.com"></div></div><divclass="col-md-8"><h2>Examples</h2><style shim-shadowdom="">paper-tabs,core-toolbar{color:#353535;background-color:#f4f4f4;font-family:'Helvetica Neue',Helvetica,Arial;box-shadow:03px2pxrgba(0,0,0,.2)}paper-tabs::shadow#selectionBar{background-color:#b12d2b}paper-tab::shadowpaper-ripple#ink{color:#b12d2b}</style><paper-tabsselected="js"class="js-app-tabs"><paper-tabname="js">JavaScript</paper-tab><paper-tabname="ctojs">Compile-to-JS</paper-tab><paper-tabname="labs">Labs</paper-tab></paper-tabs><divclass="app-lists"><divclass="js-app-list"data-app-list="js"><pclass="applist-intro">
<htmllang="en"><head><metacharset="utf-8"><title>TodoMVC</title><metaname="description"content="Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS, Spine and many more"><metaname="viewport"content="width=device-width,initial-scale=1"><metaname="twitter:card"content="summary"><metaname="twitter:title"content="TodoMVC"><metaname="twitter:site"content="@TasteJS"><metaname="twitter:description"content="Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS and many more"><metaname="twitter:image"content="https://raw.githubusercontent.com/tastejs/todomvc/gh-pages/site-assets/screenshot.png"><metaproperty="og:url"content="http://todomvc.com"><metaproperty="og:title"content="TodoMVC"><metaproperty="og:image"content="https://raw.github.com/tastejs/todomvc/gh-pages/site-assets/screenshot.png"><metaproperty="og:description"content="Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS, Spine and many more"><linkrel="shortcut icon"href="site-assets/favicon.ico"><script src="bower_components/platform/platform.js"></script><linkrel="stylesheet"href="site-assets/main.min.css"><divhidden><style shim-shadowdom="">html/deep/[layout][horizontal],html/deep/[layout][vertical]{display:-ms-flexbox;display:-webkit-flex;display:flex}html/deep/[layout][horizontal][inline],html/deep/[layout][vertical][inline]{display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}html/deep/[layout][horizontal]{-ms-flex-direction:row;-webkit-flex-direction:row;flex-direction:row}html/deep/[layout][horizontal][reverse]{-ms-flex-direction:row-reverse;-webkit-flex-direction:row-reverse;flex-direction:row-reverse}html/deep/[layout][vertical]{-ms-flex-direction:column;-webkit-flex-direction:column;flex-direction:column}html/deep/[layout][vertical][reverse]{-ms-flex-direction:column-reverse;-webkit-flex-direction:column-reverse;flex-direction:column-reverse}html/deep/[layout][wrap]{-ms-flex-wrap:wrap;-webkit-flex-wrap:wrap;flex-wrap:wrap}html/deep/[layout][wrap-reverse]{-ms-flex-wrap:wrap-reverse;-webkit-flex-wrap:wrap-reverse;flex-wrap:wrap-reverse}html/deep/[flex]{-ms-flex:110;-webkit-flex:1;flex:1;-webkit-flex-basis:0;flex-basis:0}html/deep/[vertical][layout]>[flex][auto-vertical],html/deep/[vertical][layout]::shadow[flex][auto-vertical]{-ms-flex:11auto;-webkit-flex-basis:auto;flex-basis:auto}html/deep/[flex][auto]{-ms-flex:11auto;-webkit-flex-basis:auto;flex-basis:auto}html/deep/[flex][none]{-ms-flex:none;-webkit-flex:none;flex:none}html/deep/[flex][one]{-ms-flex:1;-webkit-flex:1;flex:1}html/deep/[flex][two]{-ms-flex:2;-webkit-flex:2;flex:2}html/deep/[flex][three]{-ms-flex:3;-webkit-flex:3;flex:3}html/deep/[flex][four]{-ms-flex:4;-webkit-flex:4;flex:4}html/deep/[flex][five]{-ms-flex:5;-webkit-flex:5;flex:5}html/deep/[flex][six]{-ms-flex:6;-webkit-flex:6;flex:6}html/deep/[flex][seven]{-ms-flex:7;-webkit-flex:7;flex:7}html/deep/[flex][eight]{-ms-flex:8;-webkit-flex:8;flex:8}html/deep/[flex][nine]{-ms-flex:9;-webkit-flex:9;flex:9}html/deep/[flex][ten]{-ms-flex:10;-webkit-flex:10;flex:10}html/deep/[flex][eleven]{-ms-flex:11;-webkit-flex:11;flex:11}html/deep/[flex][twelve]{-ms-flex:12;-webkit-flex:12;flex:12}html/deep/[layout][start]{-ms-flex-align:start;-webkit-align-items:flex-start;align-items:flex-start}html/deep/[layout][center],html/deep/[layout][center-center]{-ms-flex-align:center;-webkit-align-items:center;align-items:center}html/deep/[layout][end]{-ms-flex-align:end;-webkit-align-items:flex-end;align-items:flex-end}html/deep/[layout][start-justified]{-ms-flex-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start}html/deep/[layout][center-justified],html/deep/[layout][center-center]{-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center}html/deep/[layout][end-justified]{-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end}html/deep/[layout][around-justified]{-ms-flex-pack:distribute;-webkit-justify-content:space-around;justify-content:space-around}html/deep/[layout][justified]{-ms-flex-pack:justify;-webkit-justify-content:space-between;justify-content:space-between}html/deep/[self-start]{-ms-align-self:flex-start;-webkit-align-self:flex-start;align-self:flex-start}html/deep/[self-center]{-ms-align-self:center;-webkit-align-self:center;align-self:center}html/deep/[self-end]{-ms-align-self:flex-end;-webkit-align-self:flex-end;align-self:flex-end}html/deep/[self-stretch]{-ms-align-self:stretch;-webkit-align-self:stretch;align-self:stretch}html/deep/[block]{display:block}html/deep/[hidden]{display:none!important}html/deep/[relative]{position:relative}html/deep/[fit]{position:absolute;top:0;right:0;bottom:0;left:0}body[fullbleed]{margin:0;height:100vh}html/deep/[segment],html/deep/segment{display:block;position:relative;-webkit-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;margin:1em.5em;padding:1em;background-color:#fff;-webkit-box-shadow:0001pxrgba(0,0,0,.1);box-shadow:0001pxrgba(0,0,0,.1);border-radius:5px5px5px5px}</style><script src="bower_components/polymer/polymer.js"></script><polymer-elementname="core-selection"attributes="multi"hiddenassetpath="bower_components/core-selection/"><script>Polymer("core-selection",{multi:false,ready:function(){this.clear()},clear:function(){this.selection=[]},getSelection:function(){returnthis.multi?this.selection:this.selection[0]},isSelected:function(item){returnthis.selection.indexOf(item)>=0},setItemSelected:function(item,isSelected){if(item!==undefined&&item!==null){if(isSelected){this.selection.push(item)}else{vari=this.selection.indexOf(item);if(i>=0){this.selection.splice(i,1)}}this.fire("core-select",{isSelected:isSelected,item:item})}},select:function(item){if(this.multi){this.toggle(item)}elseif(this.getSelection()!==item){this.setItemSelected(this.getSelection(),false);this.setItemSelected(item,true)}},toggle:function(item){this.setItemSelected(item,!this.isSelected(item))}});</script></polymer-element><polymer-elementname="core-selector"attributes="selected multi valueattr selectedClass selectedProperty selectedAttribute selectedItem selectedModel selectedIndex notap excludedLocalNames target itemsSelector activateEvent"assetpath="bower_components/core-selector/"><template><core-selectionid="selection"multi="{{multi}}"on-core-select="{{selectionSelect}}"></core-selection><contentid="items"select="*"></content></template><script>Polymer("core-selector",{selected:null,multi:false,valueattr:"name",selectedClass:"core-selected",selectedProperty:"",selectedAttribute:"active",selectedItem:null,selectedModel:null,selectedIndex:-1,excludedLocalNames:"",target:null,itemsSelector:"",activateEvent:"tap",notap:false,defaultExcludedLocalNames:"template",ready:function(){this.activateListener=this.activateHandler.bind(this);this.itemFilter=this.filterItem.bind(this);this.excludedLocalNamesChanged();this.observer=newMutationObserver(this.updateSelected.bind(this));if(!this.target){this.target=this}},getitems(){if(!this.target){return[]}varnodes=this.target!==this?this.itemsSelector?this.target.querySelectorAll(this.itemsSelector):this.target.children:this.$.items.getDistributedNodes();returnArray.prototype.filter.call(nodes,this.itemFilter)},filterItem:function(node){return!this._excludedNames[node.localName]},excludedLocalNamesChanged:function(){this._excludedNames={};vars=this.defaultExcludedLocalNames;if(this.excludedLocalNames){s+=""+this.excludedLocalNames}s.split(/\s+/g).forEach(function(n){this._excludedNames[n]=1},this)},targetChanged:function(old){if(old){this.removeListener(old);this.observer.disconnect();this.clearSelection()}if(this.target){this.addListener(this.target);this.observer.observe(this.target,{childList:true});this.updateSelected()}},addListener:function(node){Polymer.addEventListener(node,this.activateEvent,this.activateListener)},removeListener:function(node){Polymer.removeEventListener(node,this.activateEvent,this.activateListener)},getselection(){returnthis.$.selection.getSelection()},selectedChanged:function(){this.updateSelected()},updateSelected:function(){this.validateSelected();if(this.multi){this.clearSelection();this.selected&&this.selected.forEach(function(s){this.valueToSelection(s)},this)}else{this.valueToSelection(this.selected)}},validateSelected:function(){if(this.multi&&!Array.isArray(this.selected)&&this.selected!==null&&this.selected!==undefined){this.selected=[this.selected]}},clearSelection:function(){if(this.multi){this.selection.slice().forEach(function(s){this.$.selection.setItemSelected(s,false)},this)}else{this.$.selection.setItemSelected(this.selection,false)}this.selectedItem=null;this.$.selection.clear()},valueToSelection:function(value){varitem=value===null||value===undefined?null:this.items[this.valueToIndex(value)];this.$.selection.select(item)},updateSelectedItem:function(){this.selectedItem=this.selection},selectedItemChanged:function(){if(this.selectedItem){vart=this.selectedItem.templateInstance;this.selectedModel=t?t.model:undefined}else{this.selectedModel=null}this.selectedIndex=this.selectedItem?parseInt(this.valueToIndex(this.selected)):-1},valueToIndex:function(value){for(vari=0,items=this.items,c;c=items[i];i++){if(this.valueForNode(c)==value){returni}}returnvalue},valueForNode:function(node){returnnode[this.valueattr]||node.getAttribute(this.valueattr)},selectionSelect:function(e,detail){this.updateSelectedItem();if(detail.item){this.applySelection(detail.item,detail.isSelected)}},applySelection:function(item,isSelected){if(this.selectedClass){item.classList.toggle(this.selectedClass,isSelected)}if(this.selectedProperty){item[this.selectedProperty]=isSelected}if(this.selectedAttribute&&item.setAttribute){if(isSelected){item.setAttribute(this.selectedAttribute,"")}else{item.removeAttribute(this.selectedAttribute)}}},activateHandler:function(e){if(!this.notap){vari=this.findDistributedTarget(e.target,this.items);if(i>=0){varitem=this.items[i];vars=this.valueForNode(item)||i;if(this.multi){if(this.selected){this.addRemoveSelected(s)}else{this.selected=[s]}}else{this.selected=s}this.asyncFire("core-activate",{item:item})}}},addRemoveSelected:function(value){vari=this.selected.indexOf(value);if(i>=0){this.selected.splice(i,1)}else{this.selected.push(value)}this.valueToSelection(value)},findDistributedTarget:function(target,nodes){while(target&&target!=this){vari=Array.prototype.indexOf.call(nodes,target);if(i>=0){returni}target=target.parentNode}},selectIndex:function(index){varitem=this.items[index];if(item){this.selected=this.valueForNode(item)||index;returnitem}},selectPrevious:function(wrap){vari=wrap&&!this.selectedIndex?this.items.length-1:this.selectedIndex-1;returnthis.selectIndex(i)},selectNext:function(wrap){vari=wrap&&this.selectedIndex>=this.items.length-1?0:this.selectedIndex+1;returnthis.selectIndex(i)}});</script></polymer-element><polymer-elementname="paper-ripple"attributes="initialOpacity opacityDecayVelocity"assetpath="bower_components/paper-ripple/"><template><style>:host{display:block;position:relative}:host-context([noink]){pointer-events:none}#bg,#waves,.wave-container,.wave{pointer-events:none;position:absolute;top:0;left:0;width:100%;height:100%}#bg,.wave{opacity:0}#waves,.wave{overflow:hidden}.wave-container,.wave{border-radius:50%}:host(.circle)#bg,:host(.circle)#waves{border-radius:50%}:host(.circle).wave-container{overflow:hidden}</style><divid="bg"></div><divid="waves"></div></template><script>(function(){varwaveMaxRadius=150;functionwaveRadiusFn(touchDownMs,touchUpMs,anim){vartouchDown=touchDownMs/1e3;vartouchUp=touchUpMs/1e3;vartotalElapsed=touchDown+touchUp;varww=anim.width,hh=anim.height;varwaveRadius=Math.min(Math.sqrt(ww*ww+hh*hh),waveMaxRadius)*1.1+5;varduration=1.1-.2*(waveRadius/waveMaxRadius);vartt=totalElapsed/duration;varsize=waveRadius*(1-Math.pow(80,-tt));returnMath.abs(size)}functionwaveOpacityFn(td,tu,anim){vartouchDown=td/1e3;vartouchUp=tu/1e3;vartotalElapsed=touchDown+touchUp;if(tu<=0){returnanim.initialOpacity}returnMath.max(0,anim.initialOpacity-touchUp*anim.opacityDecayVelocity)}functionwaveOuterOpacityFn(td,tu,anim){vartouchDown=td/1e3;vartouchUp=tu/1e3;varouterOpacity=touchDown*.3;varwaveOpacity=waveOpacityFn(td,tu,anim);returnMath.max(0,Math.min(outerOpacity,waveOpacity))}functionwaveDidFinish(wave,radius,anim){varwaveOpacity=waveOpacityFn(wave.tDown,wave.tUp,anim);returnwaveOpacity<.01&&radius>=Math.min(wave.maxRadius,waveMaxRadius)}functionwaveAtMaximum(wave,radius,anim){varwaveOpacity=waveOpacityFn(wave.tDown,wave.tUp,anim);returnwaveOpacity>=anim.initialOpacity&&radius>=Math.min(wave.maxRadius,waveMaxRadius)}functiondrawRipple(ctx,x,y,radius,innerAlpha,outerAlpha){if(outerAlpha!==undefined){ctx.bg.style.opacity=outerAlpha}ctx.wave.style.opacity=innerAlpha;vars=radius/(ctx.containerSize/2);vardx=x-ctx.containerWidth/2;vardy=y-ctx.containerHeight/2;ctx.wc.style.webkitTransform="translate3d("+dx+"px,"+dy+"px,0)";ctx.wc.style.transform="translate3d("+dx+"px,"+dy+"px,0)";ctx.wave.style.webkitTransform="scale("+s+","+s+")";ctx.wave.style.transform="scale3d("+s+","+s+",1)"}functioncreateWave(elem){varelementStyle=window.getComputedStyle(elem);varfgColor=elementStyle.color;varinner=document.createElement("div");inner.style.backgroundColor=fgColor;inner.classList.add("wave");varouter=document.createElement("div");outer.classList.add("wave-container");outer.appendChild(inner);varcontainer=elem.$.waves;container.appendChild(outer);elem.$.bg.style.backgroundColor=fgColor;varwave={bg:elem.$.bg,wc:outer,wave:inner,waveColor:fgColor,maxRadius:0,isMouseDown:false,mouseDownStart:0,mouseUpStart:0,tDown:0,tUp:0};returnwave}functionremoveWaveFromScope(scope,wave){if(scope.waves){varpos=scope.waves.indexOf(wave);scope.waves.splice(pos,1);wave.wc.remove()}}varpow=Math.pow;varnow=Date.now;if(window.performance&&performance.now){now=performance.now.bind(performance)}functioncssColorWithAlpha(cssColor,alpha){varparts=cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);if(typeofalpha=="undefined"){alpha=1}if(!parts){return"rgba(255, 255, 255, "+alpha+")"}return"rgba("+parts[1]+", "+parts[2]+", "+parts[3]+", "+alpha+")"}functiondist(p1,p2){returnMath.sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2))}functiondistanceFromPointToFurthestCorner(point,size){vartl_d=dist(point,{x:0,y:0});vartr_d=dist(point,{x:size.w,y:0});varbl_d=dist(point,{x:0,y:size.h});varbr_d=dist(point,{x:size.w,y:size.h});returnMath.max(tl_d,tr_d,bl_d,br_d)}Polymer("paper-ripple",{initialOpacity:.25,opacityDecayVelocity:.8,backgroundFill:true,pixelDensity:2,eventDelegates:{down:"downAction",up:"upAction"},ready:function(){this.waves=[]},downAction:function(e){varwave=createWave(this);this.cancelled=false;wave.isMouseDown=true;wave.tDown=0;wave.tUp=0;wave.mouseUpStart=0;wave.mouseDownStart=now();varrect=this.getBoundingClientRect();varwidth=rect.width;varheight=rect.height;vartouchX=e.x-rect.left;vartouchY=e.y-rect.top;wave.startPosition={x:touchX,y:touchY};if(this.classList.contains("recenteringTouch")){wave.endPosition={x:width/2,y:height/2};wave.slideDistance=dist(wave.startPosition,wave.endPosition)}wave.containerSize=Math.max(width,height);wave.containerWidth=width;wave.containerHeight=height;wave.maxRadius=distanceFromPointToFurthestCorner(wave.startPosition,{w:width,h:height});wave.wc.style.top=(wave.containerHeight-wave.containerSize)/2+"px";wave.wc.style.left=(wave.containerWidth-wave.containerSize)/2+"px";wave.wc.style.width=wave.containerSize+"px";wave.wc.style.height=wave.containerSize+"px";this.waves.push(wave);if(!this._loop){this._loop=this.animate.bind(this,{width:width,height:height});requestAnimationFrame(this._loop)}},upAction:function(){for(vari=0;i<this.waves.length;i++){varwave=this.waves[i];if(wave.isMouseDown){wave.isMouseDown=false;wave.mouseUpStart=now();wave.mouseDownStart=0;wave.tUp=0;break}}this._loop&&requestAnimationFrame(this._loop)},cancel:function(){this.cancelled=true},animate:function(ctx){varshouldRenderNextFrame=false;vardeleteTheseWaves=[];varlongestTouchDownDuration=0;varlongestTouchUpDuration=0;varlastWaveColor=null;varanim={initialOpacity:this.initialOpacity,opacityDecayVelocity:this.opacityDecayVelocity,height:ctx.height,width:ctx.width};for(vari=0;i<this.waves.length;i++){varwave=this.waves[i];if(wave.mouseDownStart>0){wave.tDown=now()-wave.mouseDownStart}if(wave.mouseUpStart>0){wave.tUp=now()-wave.mouseUpStart}vartUp=wave.tUp;vartDown=wave.tDown;longestTouchDownDuration=Math.max(longestTouchDownDuration,tDown);longestTouchUpDuration=Math.max(longestTouchUpDuration,tUp);varradius=waveRadiusFn(tDown,tUp,anim);varwaveAlpha=waveOpacityFn(tDown,tUp,anim);varwaveColor=cssColorWithAlpha(wave.waveColor,waveAlpha);lastWaveColor=wave.waveColor;varx=wave.startPosition.x;vary=wave.startPosition.y;if(wave.endPosition){vartranslateFraction=Math.min(1,radius/wave.containerSize*2/Math.sqrt(2));x+=translateFraction*(wave.endPosition.x-wave.startPosition.x);y+=translateFraction*(wave.endPosition.y-wave.startPosition.y)}varbgFillColor=null;if(this.backgroundFill){varbgFillAlpha=waveOuterOpacityFn(tDown,tUp,anim);bgFillColor=cssColorWithAlpha(wave.waveColor,bgFillAlpha)}drawRipple(wave,x,y,radius,waveAlpha,bgFillAlpha);varmaximumWave=waveAtMaximum(wave,radius,anim);varwaveDissipated=waveDidFinish(wave,radius,anim);varshouldKeepWave=!waveDissipated||maximumWave;varshouldRenderWaveAgain=!waveDissipated&&!maximumWave;shouldRenderNextFrame=shouldRenderNextFrame||shouldRenderWaveAgain;if(!shouldKeepWave||this.cancelled){deleteTheseWaves.push(wave)}}if(shouldRenderNextFrame){requestAnimationFrame(this._loop)}for(vari=0;i<deleteTheseWaves.length;++i){varwave=deleteTheseWaves[i];removeWaveFromScope(this,wave)}if(!this.waves.length&&this._loop){this.$.bg.style.backgroundColor=null;this._loop=null;this.fire("core-transitionend")}}})})();</script></polymer-element><polymer-elementname="paper-tab"attributes="noink"role="tab"assetpath="bower_components/paper-tabs/"><template><style>:host{display:block;position:relative;overflow:hidden}#tabContainer{position:absolute;top:0;right:0;bottom:0;left:0}.tab-content{transition:opacity.1scubic-bezier(0.4,0,1,1),color.1scubic-bezier(0.4,0,1,1);cursor:default;pointer-events:none}:host(:not(.core-selected)).tab-content{opacity:.6}#ink{position:absolute;top:0;right:0;bottom:0;left:0;color:#ffff8d}:host[noink]#ink{pointer-events:none}:host-context(paper-tabs[noink])#ink{pointer-events:none}</style><divid="tabContainer"center-justified=""center=""horizontal=""layout=""><divclass="tab-content"><content></content></div><paper-rippleid="ink"initialopacity="0.95"opacitydecayvelocity="0.98"></paper-ripple></div></template><script>Polymer("paper-tab",{noink:false});</script></polymer-element><polymer-elementname="paper-tabs"extends="core-selector"attributes="noink nobar"role="tablist"assetpath="bower_components/paper-tabs/"><template><style>:host{display:block;position:relative;font-size:14px;font-weight:500;height:48px;overflow:hidden}#tabsContainer{position:absolute;top:0;right:0;bottom:0;left:0;white-space:nowrap}#selectionBar{position:absolute;height:2px;bottom:0;left:0;width:0;background-color:#ffff8d;transition:width,left}#selectionBar[hidden]{display:hidden}#selectionBar.expand{transition-duration:.15s;transition-timing-function:cubic-bezier(0.4,0,1,1)}#selectionBar.contract{transition-duration:.18s;transition-timing-function:cubic-bezier(0,0,.2,1)}polyfill-next-selector{content:'#tabsContainer > *:not(#selectionBar)'}::content>*{-ms-flex:1;-webkit-flex:1;flex:1}</style><divid="tabsContainer"horizontal=""layout=""><shadow></shadow><divid="selectionBar"hidden?="{{nobar}}"on-transitionend="{{barTransitionEnd}}"></div></div></template><script>Polymer("paper-tabs",{noink:false,nobar:false,activateEvent:"down",nostretch:false,selectedIndexChanged:function(old){vars=this.$.selectionBar.style;if(!this.selectedItem){s.width=0;s.left=0;return}varw=100/this.items.length;if(this.nostretch||old===null||old===-1){s.width=w+"%";s.left=this.selectedIndex*w+"%";return}varm=5;this.$.selectionBar.classList.add("expand");if(old<this.selectedIndex){s.width=w+w*(this.selectedIndex-old)-m+"%";this._transitionCounter=1}else{s.width=w+w*(old-this.selectedIndex)-m+"%";s.left=this.selectedIndex*w+m+"%";this._transitionCounter=2}},barTransitionEnd:function(e){this._transitionCounter--;varcl=this.$.selectionBar.classList;if(cl.contains("expand")&&!this._transitionCounter){cl.remove("expand");cl.add("contract");vars=this.$.selectionBar.style;varw=100/this.items.length;s.width=w+"%";s.left=this.selectedIndex*w+"%"}elseif(cl.contains("contract")){cl.remove("contract")}}});</script></polymer-element></div></head><body><divclass="container"><headerclass="row"><divclass="col-md-8"><imgclass="logo"src="site-assets/logo.svg"width="500"height="86"alt="TodoMVC"><p>Helping you <strong>select</strong> an MV* framework</p><nav><ahref="https://github.com/tastejs/todomvc/archive/master.zip"class="zocial red">Download</a><ahref="https://github.com/tastejs/todomvc"class="zocial ltgray">View on GitHub</a><ahref="http://blog.tastejs.com/"class="zocial ltgray">Blog</a></nav></div><divclass="col-md-4"><imgclass="logo-icon"src="site-assets/logo-icon.png"width="330"height="330"alt="TodoMVC"></div></header><divclass="row"><divclass="col-md-4"><h2>Introduction</h2><p>Developers these days are spoiled with choice when it comes to <ahref="http://coding.smashingmagazine.com/2012/07/27/journey-through-the-javascript-mvc-jungle/">selecting</a> an <strong>MV* framework</strong> for structuring and organizing their JavaScript web apps.</p><p>Backbone, Ember, AngularJS… the list of new and stable solutions continues to grow, but just how do you decide on which to use in a sea of so many options?</p><p>To help solve this problem, we created <ahref="https://github.com/tastejs/todomvc">TodoMVC</a> - a project which offers the same Todo application implemented using MV* concepts in most of the popular JavaScript MV* frameworks of today.</p><ahref="https://twitter.com/tastejs"class="twitter-follow-button"data-show-count="false"data-show-screen-name="false"></a><ahref="https://twitter.com/share"class="twitter-share-button"data-via="tastejs"data-url="http://todomvc.com"data-text="TodoMVC - Helping you select an MV* framework - Todo apps for Backbone.js, Ember.js, AngularJS, and more"></a><divclass="g-plusone"data-size="medium"data-annotation="none"data-href="http://todomvc.com"></div></div><divclass="col-md-8"><h2>Examples</h2><style shim-shadowdom="">paper-tabs,core-toolbar{color:#353535;background-color:#f4f4f4;font-family:'Helvetica Neue',Helvetica,Arial;box-shadow:03px2pxrgba(0,0,0,.2)}paper-tabs::shadow#selectionBar{background-color:#b12d2b}paper-tab::shadowpaper-ripple#ink{color:#b12d2b}</style><paper-tabsselected="js"class="js-app-tabs"><paper-tabname="js">JavaScript</paper-tab><paper-tabname="ctojs">Compile-to-JS</paper-tab><paper-tabname="labs">Labs</paper-tab></paper-tabs><divclass="app-lists"><divclass="js-app-list"data-app-list="js"><pclass="applist-intro">
These are examples written in pure JavaScript.
These are examples written in pure JavaScript.
</p><ulclass="js-app-list-inner applist js"><liclass="routing"><ahref="examples/backbone"data-source="http://documentcloud.github.com/backbone/"data-content="Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.">Backbone.js</a></li><liclass="routing"><ahref="examples/angularjs"data-source="http://angularjs.org"data-content="What HTML would have been had it been designed for web apps">AngularJS</a></li><liclass="routing"><ahref="examples/emberjs"data-source="http://emberjs.com"data-content="Ember is a JavaScript framework for creating ambitious web applications that eliminates boilerplate and provides a standard application architecture.">Ember.js</a></li><liclass="routing"><ahref="examples/knockoutjs"data-source="http://knockoutjs.com"data-content="Simplify dynamic JavaScript UIs by applying the Model-View-View Model (MVVM) pattern">KnockoutJS</a></li><liclass="routing"><ahref="examples/dojo"data-source="http://dojotoolkit.org"data-content="Dojo saves you time and scales with your development process, using web standards as its platform. It’s the toolkit experienced developers turn to for building high quality desktop and mobile web applications.">Dojo</a></li><liclass="routing"><ahref="examples/yui"data-source="http://yuilibrary.com"data-content="YUI's lightweight core and modular architecture make it scalable, fast, and robust. Built by frontend engineers at Yahoo!, YUI powers the most popular websites in the world.">YUI</a></li><liclass="routing"><ahref="examples/knockback"data-source="http://kmalakoff.github.com/knockback/"data-content="Knockback.js provides Knockout.js magic for Backbone.js Models and Collections.">Knockback.js</a></li><liclass="routing"><ahref="examples/canjs"data-source="http://canjs.us"data-content="CanJS with jQuery. CanJS is a client-side, JavaScript framework that makes building rich web applications easy. It provides can.Model (for connecting to RESTful JSON interfaces), can.View (for template loading and caching), can.Observe (for key-value binding), can.EJS (live binding templates), can.Control (declarative event bindings) and can.route (routing support).">CanJS</a></li><liclass="routing"><ahref="examples/polymer/index.html"data-source="http://polymer-project.org"data-content="Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers. It is comprised of core platform features (e.g Shadow DOM, Custom Elements, MDV) enabled with polyfills and a next generation web application framework built on these technologies.">Polymer</a></li><liclass="routing"><ahref="examples/react"data-source="http://facebook.github.io/react/"data-content="React is a JavaScript library for building user interfaces.">React</a></li><liclass="routing"><ahref="examples/mithril"data-source="http://lhorie.github.io/mithril/"data-content="Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain.">Mithril</a></li><liclass="routing"><ahref="examples/ampersand"data-source="http://ampersandjs.com"data-content="A highly modular, loosely coupled, non-frameworky framework for building advanced JavaScript apps.">Ampersand</a></li><liclass="routing"><ahref="examples/flight"data-source="http://twitter.github.com/flight"data-content="Flight is a lightweight, component-based JavaScript framework that maps behavior to DOM nodes. Twitter uses it for their web applications.">Flight</a></li><liclass="routing"><ahref="examples/vue"data-source="http://vuejs.org"data-content="Vue.js provides the benefits of MVVM data binding and a composable component system with an extremely simple and flexible API.">Vue.js</a></li><liclass="routing"><ahref="examples/backbone_marionette"data-source="http://marionettejs.com"data-content="Backbone.Marionette is a composite application library for Backbone.js that aims to simplify the construction of large scale JavaScript applications.">MarionetteJS</a></li><liclass="routing"><ahref="examples/troopjs_require"data-source="https://github.com/troopjs/"data-content="TroopJS attempts to package popular front-end technologies and bind them with minimal effort for the developer. It includes jQuery for DOM manipulation, When.js for promises, RequireJS for modularity and Has.js for feature detection. On top, it includes Pub/Sub support, templating, weaving (widgets to DOM) and auto-wiring.">TroopJS + RequireJS</a></li></ul></div><divclass="js-app-list"data-app-list="ctojs"><pclass="applist-intro">
</p><ulclass="js-app-list-inner applist js"><liclass="routing"><ahref="examples/backbone"data-source="http://documentcloud.github.com/backbone/"data-content="Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.">Backbone.js</a></li><liclass="routing"><ahref="examples/angularjs"data-source="http://angularjs.org"data-content="What HTML would have been had it been designed for web apps">AngularJS</a></li><liclass="routing"><ahref="examples/emberjs"data-source="http://emberjs.com"data-content="Ember is a JavaScript framework for creating ambitious web applications that eliminates boilerplate and provides a standard application architecture.">Ember.js</a></li><liclass="routing"><ahref="examples/knockoutjs"data-source="http://knockoutjs.com"data-content="Simplify dynamic JavaScript UIs by applying the Model-View-View Model (MVVM) pattern">KnockoutJS</a></li><liclass="routing"><ahref="examples/dojo"data-source="http://dojotoolkit.org"data-content="Dojo saves you time and scales with your development process, using web standards as its platform. It’s the toolkit experienced developers turn to for building high quality desktop and mobile web applications.">Dojo</a></li><liclass="routing"><ahref="examples/knockback"data-source="http://kmalakoff.github.com/knockback/"data-content="Knockback.js provides Knockout.js magic for Backbone.js Models and Collections.">Knockback.js</a></li><liclass="routing"><ahref="examples/canjs"data-source="http://canjs.us"data-content="CanJS with jQuery. CanJS is a client-side, JavaScript framework that makes building rich web applications easy. It provides can.Model (for connecting to RESTful JSON interfaces), can.View (for template loading and caching), can.Observe (for key-value binding), can.EJS (live binding templates), can.Control (declarative event bindings) and can.route (routing support).">CanJS</a></li><liclass="routing"><ahref="examples/polymer/index.html"data-source="http://polymer-project.org"data-content="Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers. It is comprised of core platform features (e.g Shadow DOM, Custom Elements, MDV) enabled with polyfills and a next generation web application framework built on these technologies.">Polymer</a></li><liclass="routing"><ahref="examples/react"data-source="http://facebook.github.io/react/"data-content="React is a JavaScript library for building user interfaces.">React</a></li><liclass="routing"><ahref="examples/mithril"data-source="http://lhorie.github.io/mithril/"data-content="Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain.">Mithril</a></li><liclass="routing"><ahref="examples/ampersand"data-source="http://ampersandjs.com"data-content="A highly modular, loosely coupled, non-frameworky framework for building advanced JavaScript apps.">Ampersand</a></li><liclass="routing"><ahref="examples/flight"data-source="http://twitter.github.com/flight"data-content="Flight is a lightweight, component-based JavaScript framework that maps behavior to DOM nodes. Twitter uses it for their web applications.">Flight</a></li><liclass="routing"><ahref="examples/vue"data-source="http://vuejs.org"data-content="Vue.js provides the benefits of MVVM data binding and a composable component system with an extremely simple and flexible API.">Vue.js</a></li><liclass="routing"><ahref="examples/backbone_marionette"data-source="http://marionettejs.com"data-content="Backbone.Marionette is a composite application library for Backbone.js that aims to simplify the construction of large scale JavaScript applications.">MarionetteJS</a></li><liclass="routing"><ahref="examples/troopjs_require"data-source="https://github.com/troopjs/"data-content="TroopJS attempts to package popular front-end technologies and bind them with minimal effort for the developer. It includes jQuery for DOM manipulation, When.js for promises, RequireJS for modularity and Has.js for feature detection. On top, it includes Pub/Sub support, templating, weaving (widgets to DOM) and auto-wiring.">TroopJS + RequireJS</a></li></ul></div><divclass="js-app-list"data-app-list="ctojs"><pclass="applist-intro">
These are applications written in programming
These are applications written in programming
languages that compile to JavaScript.
languages that compile to JavaScript.
</p><ulclass="js-app-list-inner applist js"><liclass="routing"><ahref="examples/spine"data-source="http://spinejs.com"data-content="Spine is a lightweight framework for building JavaScript web applications. Spine gives you an MVC structure and then gets out of your way, allowing you to concentrate on the fun stuff, building awesome web applications.">Spine</a></li><liclass="routing"><ahref="examples/vanilladart/build/web"data-source="http://dartlang.org"data-content="Dart firstly targets the development of modern and large scale browser-side web apps. It's an object oriented language with a C-style syntax. It has two run modes : it can be compiled to JS, and will later run in native VM in compliant browsers (just in a dedicated Chromium provided with Dart SDK for the moment).">Dart</a></li><liclass="routing"><ahref="examples/gwt"data-source="https://developers.google.com/web-toolkit/"data-content="Google Web Toolkit (GWT) is an MVP development toolkit for building and optimizing complex browser-based applications. GWT is used by many products at Google, including Google AdWords.">GWT</a></li><liclass="routing"><ahref="examples/closure"data-source="http://code.google.com/closure/library/"data-content="The Closure Library is a broad, well-tested, modular, and cross-browser JavaScript library. You can pull just what you need from a large set of reusable UI widgets and controls, and from lower-level utilities for DOM manipulation, server communication, animation, data structures, unit testing, rich-text editing, and more.">Closure</a></li><liclass="routing"><ahref="examples/elm"data-source="http://elm-lang.org"data-content="A functional reactive language for interactive applications">Elm</a></li><li><ahref="examples/angular-dart/web"data-source="https://github.com/angular/angular.dart"data-content="Dart firstly targets the development of modern and large scale browser-side web apps. It's an object oriented language with a C-style syntax. AngularDart is a port of Angular to Dart.">AngularDart</a></li><li><ahref="examples/typescript-backbone"data-source="http://typescriptlang.org"data-content="TypeScript is a language for application-scale JavaScript development. It offers classes, modules, interfaces and type-checking at compile time to help you build robust components.">TypeScript <br>+ Backbone.js</a></li><li><ahref="examples/typescript-angular"data-source="http://typescriptlang.org"data-content="An AngularJS + TypeScript implementation of TodoMVC. The only significant difference between this and the vanilla Angular app is that dependency injection is done via annotated constructors, which allows minification of JavaScript.">TypeScript <br>+ AngularJS</a></li><li><ahref="examples/serenadejs"data-source="https://github.com/elabs/serenade.js"data-content="Serenade.js is yet another MVC client side JavaScript framework. Why do we indulge in recreating the wheel? We believe that Serenade.js more closely follows the ideas of classical MVC than competing frameworks.">Serenade.js</a></li><liclass="routing"><ahref="examples/reagent"data-source="https://reagent-project.github.io/"data-content="Reagent provides a minimalistic interface between ClojureScript and React.">Reagent</a></li></ul></div><divclass="js-app-list"data-app-list="labs"><pclass="applist-intro">
</p><ulclass="js-app-list-inner applist js"><liclass="routing"><ahref="examples/spine"data-source="http://spinejs.com"data-content="Spine is a lightweight framework for building JavaScript web applications. Spine gives you an MVC structure and then gets out of your way, allowing you to concentrate on the fun stuff, building awesome web applications.">Spine</a></li><liclass="routing"><ahref="examples/vanilladart/build/web"data-source="http://dartlang.org"data-content="Dart firstly targets the development of modern and large scale browser-side web apps. It's an object oriented language with a C-style syntax. It has two run modes : it can be compiled to JS, and will later run in native VM in compliant browsers (just in a dedicated Chromium provided with Dart SDK for the moment).">Dart</a></li><liclass="routing"><ahref="examples/gwt"data-source="https://developers.google.com/web-toolkit/"data-content="Google Web Toolkit (GWT) is an MVP development toolkit for building and optimizing complex browser-based applications. GWT is used by many products at Google, including Google AdWords.">GWT</a></li><liclass="routing"><ahref="examples/closure"data-source="http://code.google.com/closure/library/"data-content="The Closure Library is a broad, well-tested, modular, and cross-browser JavaScript library. You can pull just what you need from a large set of reusable UI widgets and controls, and from lower-level utilities for DOM manipulation, server communication, animation, data structures, unit testing, rich-text editing, and more.">Closure</a></li><liclass="routing"><ahref="examples/elm"data-source="http://elm-lang.org"data-content="A functional reactive language for interactive applications">Elm</a></li><li><ahref="examples/angular-dart/web"data-source="https://github.com/angular/angular.dart"data-content="Dart firstly targets the development of modern and large scale browser-side web apps. It's an object oriented language with a C-style syntax. AngularDart is a port of Angular to Dart.">AngularDart</a></li><li><ahref="examples/typescript-backbone"data-source="http://typescriptlang.org"data-content="TypeScript is a language for application-scale JavaScript development. It offers classes, modules, interfaces and type-checking at compile time to help you build robust components.">TypeScript <br>+ Backbone.js</a></li><li><ahref="examples/typescript-angular"data-source="http://typescriptlang.org"data-content="An AngularJS + TypeScript implementation of TodoMVC. The only significant difference between this and the vanilla Angular app is that dependency injection is done via annotated constructors, which allows minification of JavaScript.">TypeScript <br>+ AngularJS</a></li><li><ahref="examples/serenadejs"data-source="https://github.com/elabs/serenade.js"data-content="Serenade.js is yet another MVC client side JavaScript framework. Why do we indulge in recreating the wheel? We believe that Serenade.js more closely follows the ideas of classical MVC than competing frameworks.">Serenade.js</a></li><liclass="routing"><ahref="examples/reagent"data-source="https://reagent-project.github.io/"data-content="Reagent provides a minimalistic interface between ClojureScript and React.">Reagent</a></li></ul></div><divclass="js-app-list"data-app-list="labs"><pclass="applist-intro">
"description":"WebRx is a browser-based MVVM-Framework written in Typescript that combines functional-reactive programming with declarative Data-Binding and Templating.",
"description":"WebRx is a browser-based MVVM-Framework written in Typescript that combines functional-reactive programming with declarative Data-Binding and Templating.",