├── todomvc ├── mithril │ ├── css │ │ └── app.css │ ├── bower_components │ │ ├── mithril │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── background.html │ │ │ ├── .bower.json │ │ │ ├── package.json │ │ │ ├── deploy │ │ │ │ └── cdnjs-package.json │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── mithril.d.ts │ │ └── todomvc-common │ │ │ ├── bower.json │ │ │ ├── bg.png │ │ │ ├── readme.md │ │ │ └── .bower.json │ ├── bower.json │ ├── js │ │ ├── app.js │ │ ├── models │ │ │ └── todo.js │ │ ├── views │ │ │ ├── single-view.js │ │ │ ├── footer-view.js │ │ │ └── main-view.js │ │ └── controllers │ │ │ └── todo.js │ ├── LICENSE │ ├── index.html │ └── README.md ├── vanilla-es6 │ ├── src │ │ ├── .jshintrc │ │ ├── app.js │ │ ├── helpers.js │ │ ├── template.js │ │ ├── model.js │ │ └── store.js │ ├── .gitignore │ ├── package.json │ ├── README.md │ ├── index.html │ ├── dist │ │ ├── helpers.js │ │ ├── app.js │ │ ├── template.js │ │ └── store.js │ └── node_modules │ │ └── todomvc-common │ │ └── base.css ├── elm17 │ ├── bg.png │ ├── index.html │ └── elm-package.json ├── preact │ ├── bower_components │ │ └── todomvc-common │ │ │ ├── bower.json │ │ │ ├── bg.png │ │ │ ├── readme.md │ │ │ └── .bower.json │ ├── src │ │ ├── index.js │ │ ├── util.js │ │ ├── header.js │ │ ├── footer.js │ │ ├── model.js │ │ ├── item.js │ │ └── app.js │ ├── index.html │ └── package.json ├── om │ ├── bower_components │ │ ├── todomvc-common │ │ │ └── bg.png │ │ └── director │ │ │ ├── .gitignore │ │ │ ├── lib │ │ │ ├── director.js │ │ │ └── director │ │ │ │ ├── http │ │ │ │ ├── methods.js │ │ │ │ └── responses.js │ │ │ │ └── cli.js │ │ │ ├── .bower.json │ │ │ ├── LICENSE │ │ │ └── package.json │ ├── bower.json │ ├── readme.md │ ├── index.html │ └── project.clj ├── backbone │ ├── bower_components │ │ └── todomvc-common │ │ │ └── bg.png │ ├── js │ │ ├── app.js │ │ ├── routers │ │ │ └── router.js │ │ ├── models │ │ │ └── todo.js │ │ ├── collections │ │ │ └── todos.js │ │ └── views │ │ │ ├── app-view.js │ │ │ └── todo-view.js │ ├── bower.json │ ├── readme.md │ └── index.html ├── emberjs │ ├── bower_components │ │ ├── todomvc-common │ │ │ └── bg.png │ │ └── ember-localstorage-adapter │ │ │ └── localstorage_adapter.js │ ├── js │ │ ├── models │ │ │ └── todo.js │ │ ├── app.js │ │ ├── views │ │ │ ├── todos_view.js │ │ │ └── edit_todo_view.js │ │ ├── helpers │ │ │ └── pluralize.js │ │ ├── router.js │ │ └── controllers │ │ │ ├── todos_controller.js │ │ │ └── todo_controller.js │ ├── bower.json │ ├── readme.md │ └── index.html ├── ractive │ ├── bower_components │ │ └── todomvc-common │ │ │ └── bg.png │ ├── bower.json │ ├── css │ │ └── app.css │ ├── js │ │ ├── routes.js │ │ ├── persistence.js │ │ └── app.js │ ├── readme.md │ └── index.html ├── knockoutjs │ ├── package.json │ ├── .gitignore │ ├── readme.md │ ├── node_modules │ │ └── todomvc-common │ │ │ └── base.css │ └── index.html ├── angularjs-perf │ ├── package.json │ ├── js │ │ ├── app.js │ │ ├── directives │ │ │ └── todoFocus.js │ │ ├── services │ │ │ └── todoStorage.js │ │ └── controllers │ │ │ └── todoCtrl.js │ ├── node_modules │ │ └── todomvc-common │ │ │ └── base.css │ ├── readme.md │ └── index.html ├── vue │ ├── package.json │ ├── .gitignore │ ├── js │ │ ├── store.js │ │ ├── routes.js │ │ └── app.js │ ├── readme.md │ ├── index.html │ └── node_modules │ │ └── todomvc-common │ │ └── base.css ├── react │ ├── package.json │ ├── .gitignore │ ├── todomvc-app-css │ │ ├── readme.md │ │ └── package.json │ ├── js │ │ ├── utils.js │ │ ├── footer.jsx │ │ ├── footer.js │ │ ├── todoModel.js │ │ ├── todoItem.jsx │ │ ├── todoItem.js │ │ ├── app.jsx │ │ └── app.js │ ├── index.html │ ├── readme.md │ └── todomvc-common │ │ └── base.css ├── mercury │ ├── lib │ │ ├── do-mutable-focus.js │ │ └── raf-listen.js │ ├── input.js │ ├── state.js │ ├── browser.js │ ├── update.js │ └── render.js └── choo │ ├── index.html │ └── dist │ └── base.css ├── .gitignore ├── img ├── 2016-05-18.png ├── 2016-06-25.png └── 2016-07-08.png ├── README.md ├── package.json ├── LICENCE └── index.html /todomvc/mithril/css/app.css: -------------------------------------------------------------------------------- 1 | /* base.css overrides */ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *elm-stuff 2 | *node_modules 3 | *.log 4 | .DS_Store -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /todomvc/vanilla-es6/src/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /img/2016-05-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/img/2016-05-18.png -------------------------------------------------------------------------------- /img/2016-06-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/img/2016-06-25.png -------------------------------------------------------------------------------- /img/2016-07-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/img/2016-07-08.png -------------------------------------------------------------------------------- /todomvc/elm17/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/elm17/bg.png -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9" 4 | } 5 | -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9" 4 | } 5 | -------------------------------------------------------------------------------- /todomvc/om/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/om/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/preact/src/index.js: -------------------------------------------------------------------------------- 1 | import { h, render } from 'preact'; 2 | import App from './app'; 3 | 4 | render(, document.getElementById('todoapp')); 5 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | script: 6 | - grunt test 7 | - grunt teste2e 8 | -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | node_modules 3 | npm-debug.log 4 | .DS_Store 5 | 6 | /test/browser/browserified-bundle.js 7 | -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/preact/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/backbone/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/backbone/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/emberjs/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/emberjs/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/mithril/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/ractive/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-perf/HEAD/todomvc/ractive/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/readme.md: -------------------------------------------------------------------------------- 1 | # todomvc-common 2 | 3 | > Bower component for some common utilities we use in every app 4 | 5 | 6 | ## License 7 | 8 | MIT 9 | -------------------------------------------------------------------------------- /todomvc/ractive/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-ractive", 3 | "dependencies": { 4 | "todomvc-common": "~0.1.4", 5 | "ractive": "~0.3.5", 6 | "director": "~1.2.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/models/todo.js: -------------------------------------------------------------------------------- 1 | /*global Todos, DS */ 2 | 'use strict'; 3 | 4 | Todos.Todo = DS.Model.extend({ 5 | title: DS.attr('string'), 6 | isCompleted: DS.attr('boolean') 7 | }); 8 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/readme.md: -------------------------------------------------------------------------------- 1 | # todomvc-common 2 | 3 | > Bower component for some common utilities we use in every app 4 | 5 | 6 | ## License 7 | 8 | MIT 9 | -------------------------------------------------------------------------------- /todomvc/om/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-om", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "todomvc-common": "~0.1.7", 6 | "director": "~1.2.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /todomvc/knockoutjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "todomvc-app-css": "^1.0.0", 4 | "knockout": "^3.1.0", 5 | "todomvc-common": "^1.0.1", 6 | "director": "^1.2.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "angular": "1.5.3", 5 | "todomvc-common": "^1.0.1", 6 | "todomvc-app-css": "^1.0.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/app.js: -------------------------------------------------------------------------------- 1 | /*global Ember, DS, Todos:true */ 2 | window.Todos = Ember.Application.create(); 3 | 4 | Todos.ApplicationAdapter = DS.LSAdapter.extend({ 5 | namespace: 'todos-emberjs' 6 | }); 7 | -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/lib/director.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | exports.Router = require('./director/router').Router; 5 | exports.http = require('./director/http'); 6 | exports.cli = require('./director/cli'); 7 | -------------------------------------------------------------------------------- /todomvc/ractive/css/app.css: -------------------------------------------------------------------------------- 1 | input[type="checkbox"] { 2 | outline: none; 3 | } 4 | 5 | label { 6 | -webkit-user-select: none; 7 | -moz-user-select: none; 8 | -ms-user-select: none; 9 | user-select: none; 10 | } 11 | -------------------------------------------------------------------------------- /todomvc/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "director": "^1.2.8", 5 | "vue": "^1.0.24", 6 | "todomvc-common": "^1.0.2", 7 | "todomvc-app-css": "^2.0.6" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/views/todos_view.js: -------------------------------------------------------------------------------- 1 | /*global Todos, Ember */ 2 | 'use strict'; 3 | 4 | Todos.TodosView = Ember.View.extend({ 5 | focusInput: function () { 6 | this.$('#new-todo').focus(); 7 | }.on('didInsertElement') 8 | }); 9 | -------------------------------------------------------------------------------- /todomvc/mithril/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-mithril", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "mithril": "git://github.com/lhorie/mithril.js.git", 6 | "todomvc-common": "~0.1.4" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /todomvc/backbone/js/app.js: -------------------------------------------------------------------------------- 1 | /*global $ */ 2 | /*jshint unused:false */ 3 | var app = app || {}; 4 | var ENTER_KEY = 13; 5 | var ESC_KEY = 27; 6 | 7 | $(function () { 8 | 'use strict'; 9 | 10 | // kick things off by creating the `App` 11 | window.appView = new app.AppView(); 12 | }); 13 | -------------------------------------------------------------------------------- /todomvc/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "classnames": "^2.1.5", 5 | "director": "^1.2.0", 6 | "react": "^15.0.0", 7 | "react-dom": "^15.0.2", 8 | "todomvc-app-css": "^2.0.0", 9 | "todomvc-common": "^1.0.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /todomvc/backbone/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-backbone", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "backbone": "~1.1.0", 6 | "underscore": "~1.5.0", 7 | "jquery": "~2.0.0", 8 | "todomvc-common": "~0.1.4", 9 | "backbone.localStorage": "~1.1.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/.bin 2 | node_modules/babel-core 3 | node_modules/babel-preset-es2015 4 | node_modules/browserify 5 | 6 | node_modules/todomvc-app-css/* 7 | !node_modules/todomvc-app-css/index.css 8 | 9 | node_modules/todomvc-common/* 10 | !node_modules/todomvc-common/base.css 11 | -------------------------------------------------------------------------------- /todomvc/mithril/js/app.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function( window ) { 4 | 'use strict'; 5 | 6 | app.ENTER_KEY = 13; 7 | app.ESC_KEY = 27; 8 | 9 | m.route(document.getElementById('todoapp'), '/', { 10 | '/': app, 11 | '/:filter': app 12 | }); 13 | 14 | })(window); 15 | -------------------------------------------------------------------------------- /todomvc/emberjs/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-emberjs", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "todomvc-common": "~0.1.4", 6 | "jquery": "~2.1.0", 7 | "handlebars": "~1.3.0", 8 | "ember": "~1.3.1", 9 | "ember-data": "1.0.0-beta.6", 10 | "ember-localstorage-adapter": "latest" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/helpers/pluralize.js: -------------------------------------------------------------------------------- 1 | /*global Todos, Ember */ 2 | 'use strict'; 3 | 4 | Ember.Handlebars.helper('pluralize', function (singular, count) { 5 | /* From Ember-Data */ 6 | var inflector = new Ember.Inflector(Ember.Inflector.defaultRules); 7 | 8 | return count === 1 ? singular : inflector.pluralize(singular); 9 | }); 10 | -------------------------------------------------------------------------------- /todomvc/vue/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/todomvc-app-css 2 | !node_modules/todomvc-app-css/index.css 3 | 4 | node_modules/todomvc-common 5 | !node_modules/todomvc-common/base.js 6 | !node_modules/todomvc-common/base.css 7 | 8 | node_modules/director/** 9 | !node_modules/director/build/director.js 10 | 11 | node_modules/vue/** 12 | !node_modules/vue/dist/vue.js 13 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/js/app.js: -------------------------------------------------------------------------------- 1 | /* jshint undef: true, unused: true */ 2 | /*global angular */ 3 | (function () { 4 | 'use strict'; 5 | 6 | /** 7 | * The main TodoMVC app module that pulls all dependency modules declared in same named files 8 | * 9 | * @type {angular.Module} 10 | */ 11 | angular.module('todomvc', ['todoCtrl', 'todoFocus', 'todoStorage']); 12 | })(); 13 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/background.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /todomvc/elm17/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm • TodoMVC 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /todomvc/preact/index.html: -------------------------------------------------------------------------------- 1 | Preact • TodoMVC
-------------------------------------------------------------------------------- /todomvc/emberjs/js/views/edit_todo_view.js: -------------------------------------------------------------------------------- 1 | /*global Todos, Ember */ 2 | 'use strict'; 3 | 4 | Todos.EditTodoView = Ember.TextField.extend({ 5 | focusOnInsert: function () { 6 | // Re-set input value to get rid of a reduntant text selection 7 | this.$().val(this.$().val()); 8 | this.$().focus(); 9 | }.on('didInsertElement') 10 | }); 11 | 12 | Ember.Handlebars.helper('edit-todo', Todos.EditTodoView); 13 | -------------------------------------------------------------------------------- /todomvc/mithril/js/models/todo.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | // Todo Model 7 | app.Todo = function(data) { 8 | this.title = m.prop(data.title); 9 | this.completed = m.prop(false); 10 | }; 11 | 12 | // List of Todos 13 | var list = []; 14 | app.TodoList = function() { 15 | return list; 16 | }; 17 | 18 | })(); 19 | -------------------------------------------------------------------------------- /todomvc/react/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/director 2 | !node_modules/director/build/director.js 3 | 4 | node_modules/react 5 | !node_modules/react/dist/react-with-addons.js 6 | !node_modules/react/dist/JSXTransformer.js 7 | node_modules/todomvc-app-css 8 | !node_modules/todomvc-app-css/index.css 9 | 10 | node_modules/todomvc-common 11 | !node_modules/todomvc-common/base.css 12 | !node_modules/todomvc-common/base.js 13 | -------------------------------------------------------------------------------- /todomvc/vue/js/store.js: -------------------------------------------------------------------------------- 1 | /*jshint unused:false */ 2 | 3 | (function (exports) { 4 | 5 | 'use strict'; 6 | 7 | var STORAGE_KEY = 'todos-vuejs'; 8 | 9 | exports.todoStorage = { 10 | fetch: function () { 11 | return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); 12 | }, 13 | save: function (todos) { 14 | localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)); 15 | } 16 | }; 17 | 18 | })(window); 19 | -------------------------------------------------------------------------------- /todomvc/mercury/lib/do-mutable-focus.js: -------------------------------------------------------------------------------- 1 | var document = require("global/document") 2 | 3 | module.exports = MutableFocusHook 4 | 5 | function MutableFocusHook() { 6 | if (!(this instanceof MutableFocusHook)) { 7 | return new MutableFocusHook() 8 | } 9 | } 10 | 11 | MutableFocusHook.prototype.hook = function (node, property) { 12 | if (document.activeElement !== node) { 13 | node.focus(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /todomvc/preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preact-todomvc", 3 | "version": "0.2.0", 4 | "scripts": { 5 | "build": "preact build --template index.html && cp -r build/{bundle.js,bundle.js.map,index.html} ./ && rm -rf build" 6 | }, 7 | "dependencies": { 8 | "linkstate": "^1.0.1", 9 | "preact": "^8.1.0", 10 | "preact-router": "^2.0.0" 11 | }, 12 | "devDependencies": { 13 | "preact-cli": "^1.3.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "director", 3 | "homepage": "https://github.com/flatiron/director", 4 | "version": "1.2.0", 5 | "_release": "1.2.0", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v1.2.0", 9 | "commit": "538dee97b0d57163d682a397de674f36af4d16a1" 10 | }, 11 | "_source": "git://github.com/flatiron/director.git", 12 | "_target": "~1.2.0", 13 | "_originalSource": "director" 14 | } -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril", 3 | "homepage": "https://github.com/lhorie/mithril.js", 4 | "version": "0.1.19", 5 | "_release": "0.1.19", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v0.1.19", 9 | "commit": "c8e0d917f7962a6a2e1b78ce191f74cf1478a4d3" 10 | }, 11 | "_source": "git://github.com/lhorie/mithril.js.git", 12 | "_target": "*", 13 | "_originalSource": "git://github.com/lhorie/mithril.js.git" 14 | } -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9", 4 | "homepage": "https://github.com/tastejs/todomvc-common", 5 | "_release": "0.1.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v0.1.9", 9 | "commit": "7dd61b0ebf56c020e719a69444442cc7ae7242ff" 10 | }, 11 | "_source": "git://github.com/tastejs/todomvc-common.git", 12 | "_target": "~0.1.4", 13 | "_originalSource": "todomvc-common" 14 | } -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9", 4 | "homepage": "https://github.com/tastejs/todomvc-common", 5 | "_release": "0.1.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v0.1.9", 9 | "commit": "7dd61b0ebf56c020e719a69444442cc7ae7242ff" 10 | }, 11 | "_source": "git://github.com/tastejs/todomvc-common.git", 12 | "_target": "~0.1.4", 13 | "_originalSource": "todomvc-common" 14 | } -------------------------------------------------------------------------------- /todomvc/vanilla-es6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "compile": "babel src --presets es2015 --out-dir=dist && browserify dist/app.js > dist/bundle.js", 5 | "prepublish": "npm run compile" 6 | }, 7 | "dependencies": { 8 | "todomvc-app-css": "^2.0.1", 9 | "todomvc-common": "^1.0.2" 10 | }, 11 | "devDependencies": { 12 | "babel-core": "^6.1.0", 13 | "babel-preset-es2015": "^6.1.18", 14 | "browserify": "^12.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TodoMVC Performance Benchmark 2 | 3 | 4 | 5 | _(Results for Chrome 51 + OSX 10.10.5 on a Macbook Pro)_ 6 | 7 | # Try 8 | https://developit.github.io/preact-perf 9 | 10 | # Setup 11 | ``` 12 | npm run install 13 | ``` 14 | # Develop 15 | ``` 16 | npm run start 17 | ``` 18 | # TODO 19 | - [ ] Collect client info. 20 | - [ ] Average test. 21 | - [ ] Size comparison. 22 | - [ ] Add more framework. 23 | - [ ] DRY test. 24 | - [ ] Better UI/UX. 25 | -------------------------------------------------------------------------------- /todomvc/elm17/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "TodoMVC created with Elm and elm-html", 4 | "repository": "https://github.com/evancz/elm-todomvc.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "4.0.0 <= v < 5.0.0", 12 | "elm-lang/html": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "elm-version": "0.17.0 <= v < 0.18.0" 15 | } 16 | -------------------------------------------------------------------------------- /todomvc/vue/js/routes.js: -------------------------------------------------------------------------------- 1 | /*global app, Router */ 2 | 3 | (function (app, Router) { 4 | 5 | 'use strict'; 6 | 7 | var router = new Router(); 8 | 9 | ['all', 'active', 'completed'].forEach(function (visibility) { 10 | router.on(visibility, function () { 11 | app.visibility = visibility; 12 | }); 13 | }); 14 | 15 | router.configure({ 16 | notfound: function () { 17 | window.location.hash = ''; 18 | app.visibility = 'all'; 19 | } 20 | }); 21 | 22 | router.init(); 23 | 24 | })(app, Router); 25 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "test": "grunt test" 6 | }, 7 | "devDependencies": { 8 | "grunt-cli": "*", 9 | "grunt-contrib-copy": "*", 10 | "grunt-contrib-uglify": "*", 11 | "grunt-contrib-clean": "*", 12 | "grunt-contrib-concat": "*", 13 | "grunt-contrib-watch": "*", 14 | "grunt-execute": "*", 15 | "grunt-md2html": "*", 16 | "grunt-replace": "*", 17 | "grunt-contrib-qunit": "*", 18 | "grunt-contrib-connect": "*", 19 | "grunt-zip": "*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todomvc/om/readme.md: -------------------------------------------------------------------------------- 1 | # Om TodoMVC Example 2 | 3 | > Om is a ClojureScript UI component library over React. 4 | 5 | > _[Om - github.com/swannodette/om](http://github.com/swannodette/om)_ 6 | 7 | ## Running 8 | 9 | Install ClojureScript from master by cloning and running 10 | `script/build`. You may need to tweak the `project.clj` so that the 11 | ClojureScript version matches what gets install into your `.m2` 12 | directory. Install Om by cloning and running `lein install`. Then 13 | clone this repo and run `lein cljsbuild once release`. Open 14 | `index.html` in your favorite browser. 15 | -------------------------------------------------------------------------------- /todomvc/knockoutjs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/director/* 2 | node_modules/director/build/* 3 | !node_modules/director/build 4 | !node_modules/director/build/director.js 5 | 6 | node_modules/knockout/* 7 | !node_modules/knockout/build 8 | node_modules/knockout/build/* 9 | !node_modules/knockout/build/output/ 10 | node_modules/knockout/build/output/* 11 | !node_modules/knockout/build/output/knockout-latest.js 12 | 13 | node_modules/todomvc-app-css/* 14 | !node_modules/todomvc-app-css/index.css 15 | 16 | node_modules/todomvc-common/* 17 | !node_modules/todomvc-common/base.css 18 | !node_modules/todomvc-common/base.js 19 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/js/directives/todoFocus.js: -------------------------------------------------------------------------------- 1 | /* jshint undef: true, unused: true */ 2 | /*global angular */ 3 | (function () { 4 | 'use strict'; 5 | 6 | angular.module('todoFocus', []) 7 | 8 | /** 9 | * Directive that places focus on the element it is applied to when the expression it binds to evaluates to true 10 | */ 11 | .directive('todoFocus', function ($timeout) { 12 | return function (scope, elem, attrs) { 13 | scope.$watch(attrs.todoFocus, function (newVal) { 14 | if (newVal) { 15 | $timeout(function () { 16 | elem[0].focus(); 17 | }, 0, false); 18 | } 19 | }); 20 | }; 21 | }); 22 | })(); 23 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/js/services/todoStorage.js: -------------------------------------------------------------------------------- 1 | /* jshint undef: true, unused: true */ 2 | /*global angular */ 3 | (function () { 4 | 'use strict'; 5 | 6 | angular.module('todoStorage', []) 7 | 8 | /** 9 | * Services that persists and retrieves TODOs from localStorage 10 | */ 11 | .factory('todoStorage', function () { 12 | var STORAGE_ID = 'todos-angularjs-perf'; 13 | 14 | return { 15 | get: function () { 16 | return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]'); 17 | }, 18 | 19 | put: function (todos) { 20 | localStorage.setItem(STORAGE_ID, JSON.stringify(todos)); 21 | } 22 | }; 23 | }); 24 | })(); 25 | -------------------------------------------------------------------------------- /todomvc/backbone/js/routers/router.js: -------------------------------------------------------------------------------- 1 | /*global Backbone */ 2 | var app = app || {}; 3 | 4 | (function () { 5 | 'use strict'; 6 | 7 | // Todo Router 8 | // ---------- 9 | var TodoRouter = Backbone.Router.extend({ 10 | routes: { 11 | '*filter': 'setFilter' 12 | }, 13 | 14 | setFilter: function (param) { 15 | // Set the current filter to be used 16 | app.TodoFilter = param || ''; 17 | 18 | // Trigger a collection filter event, causing hiding/unhiding 19 | // of Todo view items 20 | app.todos.trigger('filter'); 21 | } 22 | }); 23 | 24 | app.TodoRouter = new TodoRouter(); 25 | Backbone.history.start(); 26 | })(); 27 | -------------------------------------------------------------------------------- /todomvc/ractive/js/routes.js: -------------------------------------------------------------------------------- 1 | /*global window, Router, todoList */ 2 | (function (window, Router, todoList) { 3 | 'use strict'; 4 | 5 | // We're using https://github.com/flatiron/director for routing 6 | 7 | var router = new Router({ 8 | '/active': function () { 9 | todoList.set('currentFilter', 'active'); 10 | }, 11 | '/completed': function () { 12 | todoList.set('currentFilter', 'completed'); 13 | } 14 | }); 15 | 16 | router.configure({ 17 | notfound: function () { 18 | window.location.hash = ''; 19 | todoList.set('currentFilter', 'all'); 20 | } 21 | }); 22 | 23 | router.init(); 24 | 25 | })(window, Router, todoList); 26 | -------------------------------------------------------------------------------- /todomvc/backbone/js/models/todo.js: -------------------------------------------------------------------------------- 1 | /*global Backbone */ 2 | var app = app || {}; 3 | 4 | (function () { 5 | 'use strict'; 6 | 7 | // Todo Model 8 | // ---------- 9 | 10 | // Our basic **Todo** model has `title`, `order`, and `completed` attributes. 11 | app.Todo = Backbone.Model.extend({ 12 | // Default attributes for the todo 13 | // and ensure that each todo created has `title` and `completed` keys. 14 | defaults: { 15 | title: '', 16 | completed: false 17 | }, 18 | 19 | // Toggle the `completed` state of this todo item. 20 | toggle: function () { 21 | this.save({ 22 | completed: !this.get('completed') 23 | }); 24 | } 25 | }); 26 | })(); 27 | -------------------------------------------------------------------------------- /todomvc/preact/src/util.js: -------------------------------------------------------------------------------- 1 | export function uuid() { 2 | let uuid = ''; 3 | for (let i=0; i<32; i++) { 4 | let random = Math.random() * 16 | 0; 5 | if (i === 8 || i === 12 || i === 16 || i === 20) { 6 | uuid += '-'; 7 | } 8 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16); 9 | } 10 | return uuid; 11 | } 12 | 13 | export function pluralize(count, word) { 14 | return count === 1 ? word : word + 's'; 15 | } 16 | 17 | export function store(namespace, data) { 18 | if (data) { 19 | return localStorage.setItem(namespace, JSON.stringify(data)); 20 | } 21 | 22 | let store = localStorage.getItem(namespace); 23 | return (store && JSON.parse(store)) || []; 24 | } 25 | -------------------------------------------------------------------------------- /todomvc/mercury/lib/raf-listen.js: -------------------------------------------------------------------------------- 1 | var raf = require('raf/polyfill'); 2 | 3 | module.exports = rafListen; 4 | 5 | function rafListen(observ, fn) { 6 | var sending = false; 7 | var currValue; 8 | 9 | return observ(onvalue); 10 | 11 | function onvalue(value) { 12 | currValue = value; 13 | if (sending) { 14 | return; 15 | } 16 | 17 | sending = true; 18 | raf(send); 19 | } 20 | 21 | function send() { 22 | var oldValue = currValue; 23 | fn(currValue); 24 | sending = false; 25 | 26 | if (oldValue !== currValue) { 27 | sending = true; 28 | raf(send); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /todomvc/mithril/js/views/single-view.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | // Single todo view 7 | app.single = function(ctrl, task, index) { 8 | return m('li', { class: task.completed() ? 'completed' : ''}, [ 9 | m('.view', [ 10 | m('input.toggle[type=checkbox]', { 11 | onclick: m.withAttr('checked', task.completed), 12 | checked: task.completed() 13 | }), 14 | m('label', task.title()), 15 | m('button.destroy', { onclick: ctrl.remove.bind(ctrl, index)}) 16 | ]), 17 | m('input.edit') 18 | ]); 19 | }; 20 | 21 | })(); 22 | -------------------------------------------------------------------------------- /todomvc/mercury/input.js: -------------------------------------------------------------------------------- 1 | var window = require("global/window") 2 | var HashRouter = require("hash-router") 3 | var Event = require("geval") 4 | 5 | var mercury = require("../../index.js") 6 | 7 | module.exports = createInput 8 | 9 | function createInput() { 10 | var events = mercury.input([ 11 | "toggleAll", "add", "setTodoField", "toggle", "destroy", 12 | "startEdit", "finishEdit", "cancelEdit", "clearCompleted" 13 | ]) 14 | 15 | events.setRoute = EventRouter() 16 | 17 | return events 18 | } 19 | 20 | function EventRouter() { 21 | var router = HashRouter() 22 | window.addEventListener("hashchange", router) 23 | 24 | return Event(function (emit) { 25 | router.on("hash", emit) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /todomvc/preact/src/header.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import linkState from 'linkstate'; 3 | 4 | const ENTER_KEY = 13; 5 | 6 | export default class TodoHeader extends Component { 7 | handleKey = e => { 8 | if (e.keyCode!==ENTER_KEY) return; 9 | e.preventDefault(); 10 | 11 | let text = this.state.text.trim(); 12 | if (text) { 13 | this.props.addTodo(text); 14 | this.setState({ text: '' }); 15 | } 16 | }; 17 | 18 | render({ }, { text }) { 19 | return ( 20 | 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /todomvc/choo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | choo • TodoMVC 7 | 8 | 9 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/src/app.js: -------------------------------------------------------------------------------- 1 | import Controller from './controller'; 2 | import * as helpers from './helpers'; 3 | import Template from './template'; 4 | import Store from './store'; 5 | import Model from './model'; 6 | import View from './view'; 7 | 8 | const $on = helpers.$on; 9 | const setView = () => todo.controller.setView(document.location.hash); 10 | 11 | class Todo { 12 | /** 13 | * Init new Todo List 14 | * @param {string} The name of your list 15 | */ 16 | constructor(name) { 17 | this.storage = new Store(name); 18 | this.model = new Model(this.storage); 19 | 20 | this.template = new Template(); 21 | this.view = new View(this.template); 22 | 23 | this.controller = new Controller(this.model, this.view); 24 | } 25 | } 26 | 27 | const todo = new Todo('todos-vanillajs'); 28 | 29 | $on(window, 'load', setView); 30 | $on(window, 'hashchange', setView); 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-perf-comparison", 3 | "version": "0.0.1", 4 | "description": "Speed FTW", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "opener http://localhost:3000 && http-server -p 3000", 8 | "deploy": "gh-pages -d .", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/rabbots/todomvc-perf-comparison.git" 14 | }, 15 | "keywords": [ 16 | "pref", 17 | "speed", 18 | "todomvc" 19 | ], 20 | "author": "katopz", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/rabbots/todomvc-perf-comparison/issues" 24 | }, 25 | "homepage": "https://github.com/rabbots/todomvc-perf-comparison#readme", 26 | "devDependencies": { 27 | "gh-pages": "^0.11.0", 28 | "http-server": "^0.9.0", 29 | "opener": "^1.4.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/deploy/cdnjs-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril", 3 | "npmName": "mithril", 4 | "version": "$version", 5 | "filename": "mithril.min.js", 6 | "description": "A Javascript Framework for building brilliant applications", 7 | "homepage": "http://lhorie.github.io/mithril", 8 | "license": "MIT", 9 | "main": "mithril", 10 | "keywords": [ 11 | "mvc", 12 | "browser" 13 | ], 14 | "author": "Leo Horie (http://lhorie.blogspot.com/)", 15 | "contributors": [ 16 | "Leo Horie (http://lhorie.blogspot.com/)" 17 | ], 18 | "bugs": "https://github.com/lhorie/mithril.js/issues", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/lhorie/mithril.js.git" 22 | }, 23 | "npmFileMap": [ 24 | { 25 | "basePath": "/", 26 | "files": [ 27 | "mithril.js", 28 | "mithril.min.js", 29 | "mithril.min.map" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /todomvc/preact/src/footer.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import { pluralize } from './util'; 3 | 4 | const ALL_TODOS = 'all'; 5 | const ACTIVE_TODOS = 'active'; 6 | const COMPLETED_TODOS = 'completed'; 7 | 8 | export default ({ nowShowing, count, completedCount, onClearCompleted }) => ( 9 |
10 | 11 | {count} {pluralize(count, 'item')} left 12 | 13 | 26 | { completedCount > 0 ? ( 27 | 30 | ) : null } 31 |
32 | ); 33 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-app-css/readme.md: -------------------------------------------------------------------------------- 1 | # todomvc-app-css 2 | 3 | > CSS for TodoMVC apps 4 | 5 | ![](screenshot.png) 6 | 7 | 8 | ## Install 9 | 10 | 11 | ``` 12 | $ npm install --save todomvc-app-css 13 | ``` 14 | 15 | 16 | ## Getting started 17 | 18 | ```html 19 | 20 | ``` 21 | 22 | See the [TodoMVC app template](https://github.com/tastejs/todomvc-app-template). 23 | 24 | 25 | 26 | ## License 27 | 28 | Creative Commons License
This work by Sindre Sorhus is licensed under a Creative Commons Attribution 4.0 International License. 29 | -------------------------------------------------------------------------------- /todomvc/vue/readme.md: -------------------------------------------------------------------------------- 1 | # Vue.js TodoMVC Example 2 | 3 | > Vue.js is a library for building interactive web interfaces. 4 | It provides data-driven, nestable view components with a simple and flexible API. 5 | 6 | > _[Vue.js - vuejs.org](http://vuejs.org)_ 7 | 8 | ## Learning Vue.js 9 | 10 | The [Vue.js website](http://vuejs.org/) is a great resource to get started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Official Guide](http://vuejs.org/guide/) 15 | * [API Reference](http://vuejs.org/api/) 16 | * [Examples](http://vuejs.org/examples/) 17 | * [Building Larger Apps with Vue.js](http://vuejs.org/guide/application.html) 18 | 19 | Get help from other Vue.js users: 20 | 21 | * [Vue.js on Twitter](https://twitter.com/vuejs) 22 | * [Vue.js on Gitter](https://gitter.im/vuejs/vue) 23 | * [Vue.js Forum](http://forum.vuejs.org) 24 | 25 | _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)._ 26 | 27 | ## Credit 28 | 29 | This TodoMVC application was created by [Evan You](http://evanyou.me). 30 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Matt Esch. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/router.js: -------------------------------------------------------------------------------- 1 | /*global Ember, Todos */ 2 | 'use strict'; 3 | 4 | Todos.Router.map(function () { 5 | this.resource('todos', { path: '/' }, function () { 6 | this.route('active'); 7 | this.route('completed'); 8 | }); 9 | }); 10 | 11 | Todos.TodosRoute = Ember.Route.extend({ 12 | model: function () { 13 | return this.store.find('todo'); 14 | } 15 | }); 16 | 17 | Todos.TodosIndexRoute = Ember.Route.extend({ 18 | setupController: function () { 19 | this.controllerFor('todos').set('filteredTodos', this.modelFor('todos')); 20 | } 21 | }); 22 | 23 | Todos.TodosActiveRoute = Ember.Route.extend({ 24 | setupController: function () { 25 | var todos = this.store.filter('todo', function (todo) { 26 | return !todo.get('isCompleted'); 27 | }); 28 | 29 | this.controllerFor('todos').set('filteredTodos', todos); 30 | } 31 | }); 32 | 33 | Todos.TodosCompletedRoute = Ember.Route.extend({ 34 | setupController: function () { 35 | var todos = this.store.filter('todo', function (todo) { 36 | return todo.get('isCompleted'); 37 | }); 38 | 39 | this.controllerFor('todos').set('filteredTodos', todos); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /todomvc/mercury/state.js: -------------------------------------------------------------------------------- 1 | var cuid = require("cuid") 2 | var extend = require("xtend") 3 | var mercury = require("../../index.js") 4 | 5 | var TodoApp = { 6 | todos: [], 7 | route: "all", 8 | field: { 9 | text: "" 10 | } 11 | } 12 | 13 | var TodoItem = { 14 | id: null, 15 | title: "", 16 | editing: false, 17 | completed: false 18 | } 19 | 20 | module.exports = { 21 | todoApp: todoApp, 22 | todoItem: todoItem 23 | } 24 | 25 | function todoApp(events, initialState) { 26 | var state = extend(TodoApp, initialState) 27 | 28 | return mercury.hash({ 29 | todos: mercury.array(state.todos.map(todoItem)), 30 | route: mercury.value(state.route), 31 | field: mercury.hash({ 32 | text :mercury.value(state.field.text) 33 | }), 34 | events: events 35 | }) 36 | } 37 | 38 | function todoItem(item) { 39 | var state = extend(TodoItem, item) 40 | 41 | return mercury.hash({ 42 | id: cuid(), 43 | title: mercury.value(state.title), 44 | editing: mercury.value(state.editing), 45 | completed: mercury.value(state.completed) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /todomvc/mithril/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jean-Philippe Monette 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Nodejitsu Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "director", 3 | "description": "A client Side/Server Side Router", 4 | "author": "Nodejitsu Inc. ", 5 | "version": "1.2.0", 6 | "maintainers": [ 7 | "hij1nx ", 8 | "indexzero " 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "http://github.com/flatiron/director.git" 13 | }, 14 | "keywords": [ 15 | "URL", 16 | "router", 17 | "http", 18 | "cli", 19 | "flatiron", 20 | "client side", 21 | "ender" 22 | ], 23 | "devDependencies": { 24 | "codesurgeon": "https://github.com/hij1nx/codesurgeon/tarball/master", 25 | "colors": "0.5.x", 26 | "api-easy": "0.3.x", 27 | "uglify-js": "1.0.6", 28 | "request": "2.9.x", 29 | "qunitjs": "1.9.x", 30 | "vows": "0.6.x" 31 | }, 32 | "ender": "./build/ender.js", 33 | "browserify": "./build/director", 34 | "main": "./lib/director", 35 | "engines": { 36 | "node": ">= 0.4.0" 37 | }, 38 | "scripts": { 39 | "test": "vows test/server/*/*-test.js --spec" 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Leo Horie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /todomvc/om/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Om • TodoMVC 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /todomvc/react/js/utils.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | app.Utils = { 7 | uuid: function () { 8 | /*jshint bitwise:false */ 9 | var i, random; 10 | var uuid = ''; 11 | 12 | for (i = 0; i < 32; i++) { 13 | random = Math.random() * 16 | 0; 14 | if (i === 8 || i === 12 || i === 16 || i === 20) { 15 | uuid += '-'; 16 | } 17 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)) 18 | .toString(16); 19 | } 20 | 21 | return uuid; 22 | }, 23 | 24 | pluralize: function (count, word) { 25 | return count === 1 ? word : word + 's'; 26 | }, 27 | 28 | store: function (namespace, data) { 29 | if (data) { 30 | return localStorage.setItem(namespace, JSON.stringify(data)); 31 | } 32 | 33 | var store = localStorage.getItem(namespace); 34 | return (store && JSON.parse(store)) || []; 35 | }, 36 | 37 | extend: function () { 38 | var newObj = {}; 39 | for (var i = 0; i < arguments.length; i++) { 40 | var obj = arguments[i]; 41 | for (var key in obj) { 42 | if (obj.hasOwnProperty(key)) { 43 | newObj[key] = obj[key]; 44 | } 45 | } 46 | } 47 | return newObj; 48 | } 49 | }; 50 | })(); 51 | -------------------------------------------------------------------------------- /todomvc/mithril/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mithril • TodoMVC 6 | 7 | 8 | 9 | 10 |
11 |
12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /todomvc/om/project.clj: -------------------------------------------------------------------------------- 1 | (defproject todomvc "0.1.0-SNAPSHOT" 2 | :description "FIXME: write this!" 3 | :url "http://example.com/FIXME" 4 | 5 | :jvm-opts ^:replace ["-Xmx1g" "-server"] 6 | 7 | :dependencies [[org.clojure/clojure "1.5.1"] 8 | [org.clojure/clojurescript "0.0-2173"] 9 | [org.clojure/core.async "0.1.267.0-0d7780-alpha"] 10 | [secretary "0.4.0"] 11 | [om "0.5.0"]] 12 | 13 | :plugins [[lein-cljsbuild "1.0.2"]] 14 | 15 | :source-paths ["src"] 16 | 17 | :cljsbuild { 18 | :builds [{:id "dev" 19 | :source-paths ["src"] 20 | :compiler { 21 | :output-to "app.js" 22 | :output-dir "out" 23 | :optimizations :none 24 | :source-map true}} 25 | {:id "release" 26 | :source-paths ["src"] 27 | :compiler { 28 | :output-to "app.js" 29 | :optimizations :advanced 30 | :elide-asserts true 31 | :pretty-print false 32 | :output-wrapper false 33 | :preamble ["react/react.min.js"] 34 | :externs ["react/externs/react.js"]}}]}) 35 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/controllers/todos_controller.js: -------------------------------------------------------------------------------- 1 | /*global Todos, Ember */ 2 | 'use strict'; 3 | 4 | Todos.TodosController = Ember.ArrayController.extend({ 5 | actions: { 6 | createTodo: function () { 7 | var title, todo; 8 | 9 | // Get the todo title set by the "New Todo" text field 10 | title = this.get('newTitle').trim(); 11 | if (!title) { 12 | return; 13 | } 14 | 15 | // Create the new Todo model 16 | todo = this.store.createRecord('todo', { 17 | title: title, 18 | isCompleted: false 19 | }); 20 | todo.save(); 21 | 22 | // Clear the "New Todo" text field 23 | this.set('newTitle', ''); 24 | }, 25 | 26 | clearCompleted: function () { 27 | var completed = this.get('completed'); 28 | completed.invoke('deleteRecord'); 29 | completed.invoke('save'); 30 | }, 31 | }, 32 | 33 | /* properties */ 34 | 35 | remaining: Ember.computed.filterBy('content', 'isCompleted', false), 36 | completed: Ember.computed.filterBy('content', 'isCompleted', true), 37 | 38 | allAreDone: function (key, value) { 39 | if (value !== undefined) { 40 | this.setEach('isCompleted', value); 41 | return value; 42 | } else { 43 | var length = this.get('length'); 44 | var completedLength = this.get('completed.length'); 45 | 46 | return length > 0 && length === completedLength; 47 | } 48 | }.property('length', 'completed.length') 49 | }); 50 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/README.md: -------------------------------------------------------------------------------- 1 | # Vanilla ES6 (ES2015) • [TodoMVC](http://todomvc.com) 2 | 3 | > An exact port of the [Vanilla JS Example](http://todomvc.com/examples/vanillajs/), but translated into ES6, also known as ES2015. 4 | 5 | ## Learning ES6 6 | 7 | - [ES6 Features](https://github.com/lukehoban/es6features) 8 | - [Learning Resources](https://github.com/ericdouglas/ES6-Learning) 9 | - [Babel's ES6 Guide](https://babeljs.io/docs/learn-es2015/) 10 | - [Babel Compiler](https://babeljs.io/) 11 | 12 | ## Installation 13 | 14 | To get started with this example, navigate into the example folder and install the NPM modules. 15 | ```bash 16 | cd todomvc/examples/vanilla-es6 17 | npm install 18 | ``` 19 | 20 | ## Compiling ES6 to ES5 21 | 22 | After NPM modules have been installed, use the pre-defined Babel script to convert the `src` files. Browserify is also used so that `module.exports` and `require()` can be run in your browser. 23 | 24 | ```bash 25 | npm run compile 26 | ``` 27 | 28 | ## Support 29 | 30 | - [Twitter](http://twitter.com/lukeed05) 31 | 32 | *Let us [know](https://github.com/tastejs/todomvc/issues) if you discover anything worth sharing.* 33 | 34 | 35 | ## Implementation 36 | 37 | Uses [Babel JS](https://babeljs.io/) to compile ES6 code to ES5, which is then readable by all browsers. 38 | 39 | 40 | ## Credit 41 | 42 | Created by [Luke Edwards](http://www.lukeed.com) 43 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ES6 • TodoMVC 6 | 7 | 8 | 9 | 10 |
11 |
12 |

todos

13 | 14 |
15 | 16 |
17 | 18 | 19 |
    20 |
    21 | 22 | 31 |
    32 | 33 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /todomvc/emberjs/readme.md: -------------------------------------------------------------------------------- 1 | # Ember.js TodoMVC Example 2 | 3 | > A framework for creating ambitious web applications. 4 | 5 | > _[Ember.js - emberjs.com](http://emberjs.com)_ 6 | 7 | 8 | ## Learning Ember.js 9 | 10 | The [Ember.js website](http://emberjs.com) is a great resource for getting started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Guides](http://emberjs.com/guides) 15 | * [API Reference](http://emberjs.com/api) 16 | * [Screencast - Building an App with Ember.js](https://www.youtube.com/watch?v=Ga99hMi7wfY) 17 | * [Applications built with Ember.js](http://emberjs.com/ember-users) 18 | * [Blog](http://emberjs.com/blog) 19 | 20 | Articles and guides from the community: 21 | 22 | * [Getting Into Ember.js](http://net.tutsplus.com/tutorials/javascript-ajax/getting-into-ember-js) 23 | * [EmberWatch](http://emberwatch.com) 24 | * [CodeSchool course Warming Up With Ember.js](https://www.codeschool.com/courses/warming-up-with-emberjs) 25 | 26 | Get help from other Ember.js users: 27 | 28 | * [Ember.js on StackOverflow](http://stackoverflow.com/questions/tagged/ember.js) 29 | * [Ember.js on Twitter](http://twitter.com/emberjs) 30 | * [Ember.js on Google +](https://plus.google.com/communities/106387049790387471205) 31 | 32 | _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)._ 33 | -------------------------------------------------------------------------------- /todomvc/mithril/js/views/footer-view.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | app.footer = function(ctrl) { 7 | return m('footer#footer', [ 8 | m('span#todo-count', [ 9 | m('strong', ctrl.list.length), ' item' + (ctrl.list.length > 1 ? 's' : '') + ' left' 10 | ]), 11 | m('ul#filters', [ 12 | m('li', [ 13 | m('a[href=/]', { 14 | config: m.route, 15 | class: ctrl.filter() == '' ? 'selected' : '' 16 | }, 'All') 17 | ]), 18 | m('li', [ 19 | m('a[href=/active]', { 20 | config: m.route, 21 | class: ctrl.filter() == 'active' ? 'selected' : '' 22 | }, 'Active') 23 | ]), 24 | m('li', [ 25 | m('a[href=/completed]', { 26 | config: m.route, 27 | class: ctrl.filter() == 'completed' ? 'selected' : '' 28 | }, 'Completed') 29 | ]) 30 | ]), 31 | ctrl.amountCompleted() == 0 ? '' : m('button#clear-completed', { 32 | onclick: ctrl.clearCompleted.bind(ctrl) 33 | }, 'Clear completed (' + ctrl.amountCompleted() + ')') 34 | ]); 35 | } 36 | 37 | })(); 38 | -------------------------------------------------------------------------------- /todomvc/preact/src/model.js: -------------------------------------------------------------------------------- 1 | import { uuid, store } from './util'; 2 | 3 | // note: commented out localStorage persistence as it mucks up tests. 4 | 5 | export default () => { 6 | let onChanges = []; 7 | 8 | function inform() { 9 | for (let i=onChanges.length; i--; ) { 10 | onChanges[i](model); 11 | } 12 | } 13 | 14 | let model = { 15 | todos: [], 16 | 17 | onChanges: [], 18 | 19 | subscribe(fn) { 20 | onChanges.push(fn); 21 | }, 22 | 23 | addTodo(title) { 24 | model.todos = model.todos.concat({ 25 | id: uuid(), 26 | title, 27 | completed: false 28 | }); 29 | inform(); 30 | }, 31 | 32 | toggleAll(completed) { 33 | model.todos = model.todos.map( 34 | todo => ({ ...todo, completed }) 35 | ); 36 | inform(); 37 | }, 38 | 39 | toggle(todoToToggle) { 40 | model.todos = model.todos.map( todo => ( 41 | todo !== todoToToggle ? todo : ({ ...todo, completed: !todo.completed }) 42 | ) ); 43 | inform(); 44 | }, 45 | 46 | destroy(todo) { 47 | model.todos = model.todos.filter( t => t !== todo ); 48 | inform(); 49 | }, 50 | 51 | save(todoToSave, title) { 52 | model.todos = model.todos.map( todo => ( 53 | todo !== todoToSave ? todo : ({ ...todo, title }) 54 | )); 55 | inform(); 56 | }, 57 | 58 | clearCompleted() { 59 | model.todos = model.todos.filter( todo => !todo.completed ); 60 | inform(); 61 | } 62 | }; 63 | 64 | return model; 65 | }; 66 | -------------------------------------------------------------------------------- /todomvc/ractive/js/persistence.js: -------------------------------------------------------------------------------- 1 | /*global window, todoList */ 2 | (function (window, todoList) { 3 | 'use strict'; 4 | 5 | // In Ractive, 'models' are usually just POJOs - plain old JavaScript objects. 6 | // Our todo list is simply an array of objects, which is handy for fetching 7 | // and persisting from/to localStorage 8 | 9 | var items, localStorage, removeEditingState; 10 | 11 | // Firefox throws a SecurityError if you try to access localStorage while 12 | // cookies are disabled 13 | try { 14 | localStorage = window.localStorage; 15 | } catch (err) { 16 | todoList.set('items', []); 17 | return; 18 | } 19 | 20 | if (localStorage) { 21 | items = JSON.parse(localStorage.getItem('todos-ractive')) || []; 22 | 23 | // Editing state should not be persisted, so we remove it 24 | // (https://github.com/tastejs/todomvc/blob/gh-pages/app-spec.md#persistence) 25 | removeEditingState = function (item) { 26 | return { 27 | description: item.description, 28 | completed: item.completed 29 | }; 30 | }; 31 | 32 | // Whenever the model changes (including child properties like 33 | // `items[1].completed`)... 34 | todoList.observe('items', function (items) { 35 | 36 | // ...we persist it to localStorage 37 | localStorage.setItem('todos-ractive', JSON.stringify(items.map(removeEditingState))); 38 | }); 39 | } else { 40 | items = []; 41 | } 42 | 43 | todoList.set('items', items); 44 | 45 | })(window, todoList); 46 | -------------------------------------------------------------------------------- /todomvc/backbone/js/collections/todos.js: -------------------------------------------------------------------------------- 1 | /*global Backbone */ 2 | var app = app || {}; 3 | 4 | (function () { 5 | 'use strict'; 6 | 7 | // Todo Collection 8 | // --------------- 9 | 10 | // The collection of todos is backed by *localStorage* instead of a remote 11 | // server. 12 | var Todos = Backbone.Collection.extend({ 13 | // Reference to this collection's model. 14 | model: app.Todo, 15 | 16 | // Save all of the todo items under the `"todos"` namespace. 17 | localStorage: new Backbone.LocalStorage('todos-backbone'), 18 | 19 | // Filter down the list of all todo items that are finished. 20 | completed: function () { 21 | return this.filter(function (todo) { 22 | return todo.get('completed'); 23 | }); 24 | }, 25 | 26 | // Filter down the list to only todo items that are still not finished. 27 | remaining: function () { 28 | return this.without.apply(this, this.completed()); 29 | }, 30 | 31 | // We keep the Todos in sequential order, despite being saved by unordered 32 | // GUID in the database. This generates the next order number for new items. 33 | nextOrder: function () { 34 | if (!this.length) { 35 | return 1; 36 | } 37 | return this.last().get('order') + 1; 38 | }, 39 | 40 | // Todos are sorted by their original insertion order. 41 | comparator: function (todo) { 42 | return todo.get('order'); 43 | } 44 | }); 45 | 46 | // Create our global collection of **Todos**. 47 | app.todos = new Todos(); 48 | })(); 49 | -------------------------------------------------------------------------------- /todomvc/backbone/readme.md: -------------------------------------------------------------------------------- 1 | # Backbone.js TodoMVC Example 2 | 3 | > 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. 4 | 5 | > _[Backbone.js - backbonejs.org](http://backbonejs.org)_ 6 | 7 | 8 | ## Learning Backbone.js 9 | 10 | The [Backbone.js website](http://backbonejs.org) is a great resource for getting started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Annotated source code](http://backbonejs.org/docs/backbone.html) 15 | * [Applications built with Backbone.js](http://backbonejs.org/#examples) 16 | * [FAQ](http://backbonejs.org/#faq) 17 | 18 | Articles and guides from the community: 19 | 20 | * [Developing Backbone.js Applications](http://addyosmani.github.io/backbone-fundamentals) 21 | * [Collection of tutorials, blog posts, and example sites](https://github.com/documentcloud/backbone/wiki/Tutorials%2C-blog-posts-and-example-sites) 22 | 23 | Get help from other Backbone.js users: 24 | 25 | * [Backbone.js on StackOverflow](http://stackoverflow.com/questions/tagged/backbone.js) 26 | * [Google Groups mailing list](https://groups.google.com/forum/#!forum/backbonejs) 27 | * [Backbone.js on Twitter](http://twitter.com/documentcloud) 28 | 29 | _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)._ 30 | -------------------------------------------------------------------------------- /todomvc/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React • TodoMVC 6 | 7 | 8 | 9 | 10 |
    11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /todomvc/react/js/footer.jsx: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | /*global React */ 6 | var app = app || {}; 7 | 8 | (function () { 9 | 'use strict'; 10 | 11 | app.TodoFooter = React.createClass({ 12 | render: function () { 13 | var activeTodoWord = app.Utils.pluralize(this.props.count, 'item'); 14 | var clearButton = null; 15 | 16 | if (this.props.completedCount > 0) { 17 | clearButton = ( 18 | 23 | ); 24 | } 25 | 26 | var nowShowing = this.props.nowShowing; 27 | return ( 28 | 59 | ); 60 | } 61 | }); 62 | })(); 63 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/src/helpers.js: -------------------------------------------------------------------------------- 1 | // Allow for looping on nodes by chaining: 2 | // qsa('.foo').forEach(function () {}) 3 | NodeList.prototype.forEach = Array.prototype.forEach; 4 | 5 | // Get element(s) by CSS selector: 6 | export function qs(selector, scope) { 7 | return (scope || document).querySelector(selector); 8 | } 9 | 10 | export function qsa(selector, scope) { 11 | return (scope || document).querySelectorAll(selector); 12 | } 13 | 14 | // addEventListener wrapper: 15 | export function $on(target, type, callback, useCapture) { 16 | target.addEventListener(type, callback, !!useCapture); 17 | } 18 | 19 | // Attach a handler to event for all elements that match the selector, 20 | // now or in the future, based on a root element 21 | export function $delegate(target, selector, type, handler) { 22 | const dispatchEvent = event => { 23 | const targetElement = event.target; 24 | const potentialElements = qsa(selector, target); 25 | const hasMatch = Array.from(potentialElements).includes(targetElement); 26 | 27 | if (hasMatch) { 28 | handler.call(targetElement, event); 29 | } 30 | }; 31 | 32 | // https://developer.mozilla.org/en-US/docs/Web/Events/blur 33 | const useCapture = type === 'blur' || type === 'focus'; 34 | 35 | $on(target, type, dispatchEvent, useCapture); 36 | } 37 | 38 | // Find the element's parent with the given tag name: 39 | // $parent(qs('a'), 'div') 40 | export function $parent(element, tagName) { 41 | if (!element.parentNode) { 42 | return; 43 | } 44 | 45 | if (element.parentNode.tagName.toLowerCase() === tagName.toLowerCase()) { 46 | return element.parentNode; 47 | } 48 | 49 | return $parent(element.parentNode, tagName); 50 | } 51 | -------------------------------------------------------------------------------- /todomvc/emberjs/js/controllers/todo_controller.js: -------------------------------------------------------------------------------- 1 | /*global Todos, Ember */ 2 | 'use strict'; 3 | 4 | Todos.TodoController = Ember.ObjectController.extend({ 5 | isEditing: false, 6 | 7 | // We use the bufferedTitle to store the original value of 8 | // the model's title so that we can roll it back later in the 9 | // `cancelEditing` action. 10 | bufferedTitle: Ember.computed.oneWay('title'), 11 | 12 | actions: { 13 | editTodo: function () { 14 | this.set('isEditing', true); 15 | }, 16 | 17 | doneEditing: function () { 18 | var bufferedTitle = this.get('bufferedTitle').trim(); 19 | 20 | if (Ember.isEmpty(bufferedTitle)) { 21 | // The `doneEditing` action gets sent twice when the user hits 22 | // enter (once via 'insert-newline' and once via 'focus-out'). 23 | // 24 | // We debounce our call to 'removeTodo' so that it only gets 25 | // made once. 26 | Ember.run.debounce(this, 'removeTodo', 0); 27 | } else { 28 | var todo = this.get('model'); 29 | todo.set('title', bufferedTitle); 30 | todo.save(); 31 | } 32 | 33 | // Re-set our newly edited title to persist its trimmed version 34 | this.set('bufferedTitle', bufferedTitle); 35 | this.set('isEditing', false); 36 | }, 37 | 38 | cancelEditing: function () { 39 | this.set('bufferedTitle', this.get('title')); 40 | this.set('isEditing', false); 41 | }, 42 | 43 | removeTodo: function () { 44 | this.removeTodo(); 45 | } 46 | }, 47 | 48 | removeTodo: function () { 49 | var todo = this.get('model'); 50 | 51 | todo.deleteRecord(); 52 | todo.save(); 53 | }, 54 | 55 | saveWhenCompleted: function () { 56 | this.get('model').save(); 57 | }.observes('isCompleted') 58 | }); 59 | -------------------------------------------------------------------------------- /todomvc/mercury/browser.js: -------------------------------------------------------------------------------- 1 | var mercury = require("../../index.js") 2 | var TimeTravel = require("../../time-travel.js") 3 | var document = require("global/document") 4 | var window = require("global/window") 5 | var rafListen = require("./lib/raf-listen.js") 6 | var localStorage = window.localStorage 7 | 8 | var Input = require("./input.js") 9 | var State = require("./state.js") 10 | var Render = require("./render.js") 11 | var Update = require("./update.js") 12 | 13 | module.exports = createApp 14 | 15 | var state = createApp() 16 | window.undo = TimeTravel(state) 17 | mercury.app(document.body, state, Render) 18 | 19 | function createApp() { 20 | // load from localStorage 21 | var storedState = localStorage.getItem("todos-mercury") 22 | var initialState = storedState ? JSON.parse(storedState) : null 23 | 24 | var events = Input() 25 | var state = window.state = State.todoApp(events, initialState) 26 | 27 | wireUpEvents(state, events) 28 | 29 | rafListen(state, function (value) { 30 | localStorage.setItem("todos-mercury", JSON.stringify(value)) 31 | }) 32 | 33 | return state 34 | } 35 | 36 | function wireUpEvents(state, events) { 37 | events.toggleAll(Update.toggleAll.bind(null, state)) 38 | events.add(Update.add.bind(null, state)) 39 | events.setTodoField(Update.setTodoField.bind(null, state)) 40 | events.toggle(Update.toggle.bind(null, state)) 41 | events.destroy(Update.destroy.bind(null, state)) 42 | events.startEdit(Update.startEdit.bind(null, state)) 43 | events.finishEdit(Update.finishEdit.bind(null, state)) 44 | events.cancelEdit(Update.cancelEdit.bind(null, state)) 45 | events.clearCompleted(Update.clearCompleted.bind(null, state)) 46 | events.setRoute(Update.setRoute.bind(null, state)) 47 | } 48 | -------------------------------------------------------------------------------- /todomvc/knockoutjs/readme.md: -------------------------------------------------------------------------------- 1 | # Knockout.js TodoMVC Example 2 | 3 | > Knockout.js helps you simplify dynamic JavaScript UIs using the Model-View-ViewModel (MVVM) pattern. 4 | 5 | > _[Knockout.js - knockoutjs.com](http://knockoutjs.com)_ 6 | 7 | 8 | ## Learning Knockout.js 9 | 10 | The [Knockout.js website](http://knockoutjs.com) is a great resource for getting started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Documentation](http://knockoutjs.com/documentation/introduction.html) 15 | * [Tutorials](http://learn.knockoutjs.com) 16 | * [Live examples](http://knockoutjs.com/examples) 17 | 18 | Articles and guides from the community: 19 | 20 | * [Getting Started with Knockout.js](http://www.adobe.com/devnet/html5/articles/getting-started-with-knockoutjs.html) 21 | * [Into the Ring with Knockout.js](http://net.tutsplus.com/tutorials/javascript-ajax/into-the-ring-with-knockout-js) 22 | * [Beginners Guide to Knockout.js](http://www.sitepoint.com/beginners-guide-to-knockoutjs-part-1) 23 | 24 | Get help from other Knockout.js users: 25 | 26 | * [Knockout.js on StackOverflow](http://stackoverflow.com/questions/tagged/knockout) 27 | * [Mailing list on Google Groups](http://groups.google.com/group/knockoutjs) 28 | * [Knockout.js on Twitter](http://twitter.com/knockoutjs) 29 | * [Knockout.js on Google +](https://plus.google.com/communities/106789046312204355684/stream/c5bfcfdf-3690-44a6-b015-35aad4f4e42e) 30 | 31 | _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)._ 32 | 33 | 34 | ## Credit 35 | 36 | This TodoMVC application was originally created by [ashish101](https://github.com/ashish01/knockoutjs-todos), then refactored by Addy Osmani and later rewritten by TodoMVC contributors. 37 | -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/lib/director/http/methods.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Express - router - methods 3 | * Copyright(c) 2010 TJ Holowaychuk 4 | * MIT Licensed 5 | * 6 | * Adapted for SS 7 | * (C) 2011 Nodejitsu Inc. 8 | * 9 | */ 10 | 11 | /** 12 | * Hypertext Transfer Protocol -- HTTP/1.1 13 | * http://www.ietf.org/rfc/rfc2616.txt 14 | */ 15 | var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']; 16 | 17 | /** 18 | * HTTP Extensions for Distributed Authoring -- WEBDAV 19 | * http://www.ietf.org/rfc/rfc2518.txt 20 | */ 21 | var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK']; 22 | 23 | /** 24 | * Versioning Extensions to WebDAV 25 | * http://www.ietf.org/rfc/rfc3253.txt 26 | */ 27 | var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY']; 28 | 29 | /** 30 | * Ordered Collections Protocol (WebDAV) 31 | * http://www.ietf.org/rfc/rfc3648.txt 32 | */ 33 | var RFC3648 = ['ORDERPATCH']; 34 | 35 | /** 36 | * Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol 37 | * http://www.ietf.org/rfc/rfc3744.txt 38 | */ 39 | var RFC3744 = ['ACL']; 40 | 41 | /** 42 | * Web Distributed Authoring and Versioning (WebDAV) SEARCH 43 | * http://www.ietf.org/rfc/rfc5323.txt 44 | */ 45 | var RFC5323 = ['SEARCH']; 46 | 47 | /** 48 | * PATCH Method for HTTP 49 | * http://www.ietf.org/rfc/rfc5789.txt 50 | */ 51 | var RFC5789 = ['PATCH']; 52 | 53 | /** 54 | * Expose the methods. 55 | */ 56 | module.exports = [].concat( 57 | RFC2616, 58 | RFC2518, 59 | RFC3253, 60 | RFC3648, 61 | RFC3744, 62 | RFC5323, 63 | RFC5789 64 | ).map(function (method) { 65 | return method.toLowerCase(); 66 | }); -------------------------------------------------------------------------------- /todomvc/vanilla-es6/dist/helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.qs = qs; 7 | exports.qsa = qsa; 8 | exports.$on = $on; 9 | exports.$delegate = $delegate; 10 | exports.$parent = $parent; 11 | // Allow for looping on nodes by chaining: 12 | // qsa('.foo').forEach(function () {}) 13 | NodeList.prototype.forEach = Array.prototype.forEach; 14 | 15 | // Get element(s) by CSS selector: 16 | function qs(selector, scope) { 17 | return (scope || document).querySelector(selector); 18 | } 19 | 20 | function qsa(selector, scope) { 21 | return (scope || document).querySelectorAll(selector); 22 | } 23 | 24 | // addEventListener wrapper: 25 | function $on(target, type, callback, useCapture) { 26 | target.addEventListener(type, callback, !!useCapture); 27 | } 28 | 29 | // Attach a handler to event for all elements that match the selector, 30 | // now or in the future, based on a root element 31 | function $delegate(target, selector, type, handler) { 32 | var dispatchEvent = function dispatchEvent(event) { 33 | var targetElement = event.target; 34 | var potentialElements = qsa(selector, target); 35 | var hasMatch = Array.from(potentialElements).includes(targetElement); 36 | 37 | if (hasMatch) { 38 | handler.call(targetElement, event); 39 | } 40 | }; 41 | 42 | // https://developer.mozilla.org/en-US/docs/Web/Events/blur 43 | var useCapture = type === 'blur' || type === 'focus'; 44 | 45 | $on(target, type, dispatchEvent, useCapture); 46 | } 47 | 48 | // Find the element's parent with the given tag name: 49 | // $parent(qs('a'), 'div') 50 | function $parent(element, tagName) { 51 | if (!element.parentNode) { 52 | return; 53 | } 54 | 55 | if (element.parentNode.tagName.toLowerCase() === tagName.toLowerCase()) { 56 | return element.parentNode; 57 | } 58 | 59 | return $parent(element.parentNode, tagName); 60 | } -------------------------------------------------------------------------------- /todomvc/react/js/footer.js: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | /*global React */ 6 | var app = app || {}; 7 | 8 | (function () { 9 | 'use strict'; 10 | 11 | app.TodoFooter = React.createClass({ 12 | render: function () { 13 | var activeTodoWord = app.Utils.pluralize(this.props.count, 'item'); 14 | var clearButton = null; 15 | 16 | if (this.props.completedCount > 0) { 17 | clearButton = ( 18 | React.createElement('button', { 19 | className: "clear-completed", 20 | onClick: this.props.onClearCompleted}, [ 21 | "Clear completed" 22 | ]) 23 | ); 24 | } 25 | 26 | var nowShowing = this.props.nowShowing; 27 | return ( 28 | React.createElement('footer', {className: "footer"}, [ 29 | React.createElement('span', {className: "todo-count"}, [ 30 | React.createElement('strong', null, [this.props.count])," ", activeTodoWord, " left" 31 | ]), 32 | React.createElement('ul', {className: "filters"}, [ 33 | React.createElement('li', null, [ 34 | React.createElement('a', { 35 | href: "#/", 36 | className: classNames({selected: nowShowing === app.ALL_TODOS})}, [ 37 | "All" 38 | ]) 39 | ]), 40 | ' ', 41 | React.createElement('li', null, [ 42 | React.createElement('a', { 43 | href: "#/active", 44 | className: classNames({selected: nowShowing === app.ACTIVE_TODOS})}, [ 45 | "Active" 46 | ]) 47 | ]), 48 | ' ', 49 | React.createElement('li', null, [ 50 | React.createElement('a', { 51 | href: "#/completed", 52 | className: classNames({selected: nowShowing === app.COMPLETED_TODOS})}, [ 53 | "Completed" 54 | ]) 55 | ]) 56 | ]), 57 | clearButton 58 | ]) 59 | ); 60 | } 61 | }); 62 | })(); 63 | -------------------------------------------------------------------------------- /todomvc/mithril/js/controllers/todo.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | app.controller = function() { 7 | 8 | this.list = new app.TodoList(); // Todo collection 9 | this.title = m.prop(''); // Temp title placeholder 10 | this.filter = m.prop(m.route.param('filter') || ''); // TodoList filter 11 | 12 | // Add a Todo 13 | this.add = function(title) { 14 | if(this.title()) { 15 | this.list.push(new app.Todo({title: title()})); 16 | this.title(''); 17 | } 18 | }; 19 | 20 | //check whether a todo is visible 21 | this.isVisible = function(todo) { 22 | if(this.filter() == '') 23 | return true; 24 | if (this.filter() == 'active') 25 | return !todo.completed(); 26 | if (this.filter() == 'completed') 27 | return todo.completed(); 28 | } 29 | 30 | this.clearTitle = function() { 31 | this.title('') 32 | } 33 | 34 | // Removing a Todo from the list 35 | this.remove = function(key) { 36 | this.list.splice(key, 1) 37 | } 38 | 39 | // Remove all Todos where Completed == true 40 | this.clearCompleted = function() { 41 | for(var i = 0; i < this.list.length; i++) { 42 | if(this.list[i].completed()) 43 | this.list.splice(i, 1) 44 | } 45 | } 46 | 47 | // Total amount of Todos completed 48 | this.amountCompleted = function() { 49 | var amount = 0; 50 | 51 | for(var i = 0; i < this.list.length; i++) 52 | if(this.list[i].completed()) 53 | amount++; 54 | 55 | return amount; 56 | } 57 | }; 58 | 59 | })(); 60 | -------------------------------------------------------------------------------- /todomvc/react/readme.md: -------------------------------------------------------------------------------- 1 | # React TodoMVC Example 2 | 3 | > React is a JavaScript library for creating user interfaces. Its core principles are declarative code, efficiency, and flexibility. Simply specify what your component looks like and React will keep it up-to-date when the underlying data changes. 4 | 5 | > _[React - facebook.github.io/react](http://facebook.github.io/react)_ 6 | 7 | 8 | ## Learning React 9 | 10 | The [React getting started documentation](http://facebook.github.io/react/docs/getting-started.html) is a great way to get started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Documentation](http://facebook.github.io/react/docs/getting-started.html) 15 | * [API Reference](http://facebook.github.io/react/docs/reference.html) 16 | * [Blog](http://facebook.github.io/react/blog/) 17 | * [React on GitHub](https://github.com/facebook/react) 18 | * [Support](http://facebook.github.io/react/support.html) 19 | 20 | Articles and guides from the community: 21 | 22 | * [How is Facebook's React JavaScript library](http://www.quora.com/React-JS-Library/How-is-Facebooks-React-JavaScript-library) 23 | * [React: Under the hood](http://www.quora.com/Pete-Hunt/Posts/React-Under-the-Hood) 24 | 25 | Get help from other React users: 26 | 27 | * [React on StackOverflow](http://stackoverflow.com/questions/tagged/reactjs) 28 | * [Discussion Forum](https://discuss.reactjs.org/) 29 | 30 | _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)._ 31 | 32 | 33 | ## Running 34 | 35 | The app is built with [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) and compiled at runtime for a lighter and more fun code reading experience. As stated in the link, JSX is not mandatory. 36 | 37 | To run the app, spin up an HTTP server (e.g. `python -m SimpleHTTPServer`) and visit http://localhost/.../myexample/. 38 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/dist/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _controller = require('./controller'); 4 | 5 | var _controller2 = _interopRequireDefault(_controller); 6 | 7 | var _helpers = require('./helpers'); 8 | 9 | var helpers = _interopRequireWildcard(_helpers); 10 | 11 | var _template = require('./template'); 12 | 13 | var _template2 = _interopRequireDefault(_template); 14 | 15 | var _store = require('./store'); 16 | 17 | var _store2 = _interopRequireDefault(_store); 18 | 19 | var _model = require('./model'); 20 | 21 | var _model2 = _interopRequireDefault(_model); 22 | 23 | var _view = require('./view'); 24 | 25 | var _view2 = _interopRequireDefault(_view); 26 | 27 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 28 | 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 30 | 31 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 32 | 33 | var $on = helpers.$on; 34 | var setView = function setView() { 35 | return todo.controller.setView(document.location.hash); 36 | }; 37 | 38 | var Todo = 39 | /** 40 | * Init new Todo List 41 | * @param {string} The name of your list 42 | */ 43 | function Todo(name) { 44 | _classCallCheck(this, Todo); 45 | 46 | this.storage = new _store2.default(name); 47 | this.model = new _model2.default(this.storage); 48 | 49 | this.template = new _template2.default(); 50 | this.view = new _view2.default(this.template); 51 | 52 | this.controller = new _controller2.default(this.model, this.view); 53 | }; 54 | 55 | var todo = new Todo('todos-vanillajs'); 56 | 57 | $on(window, 'load', setView); 58 | $on(window, 'hashchange', setView); -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/lhorie/mithril.js.svg?branch=master)](https://travis-ci.org/lhorie/mithril.js) 2 | 3 | # Mithril 4 | 5 | A Javascript Framework for Building Brilliant Applications 6 | 7 | See the [website](http://lhorie.github.io/mithril) for documentation 8 | 9 | There's also a [blog](http://lhorie.github.io/mithril-blog) and a [mailing list](https://groups.google.com/forum/#!forum/mithriljs) 10 | 11 | --- 12 | 13 | ## What is Mithril? 14 | 15 | Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain. 16 | 17 | ### Light-weight 18 | 19 | - Only 4kb gzipped, no dependencies 20 | - Small API, small learning curve 21 | 22 | ### Robust 23 | 24 | - Safe-by-default templates 25 | - Hierarchical MVC via components 26 | 27 | ### Fast 28 | 29 | - Virtual DOM diffing and compilable templates 30 | - Intelligent auto-redrawing system 31 | 32 | --- 33 | 34 | ## Sample code 35 | 36 | ```javascript 37 | //namespace 38 | var app = {}; 39 | 40 | //model 41 | app.PageList = function() { 42 | return m.request({method: "GET", url: "pages.json"}); 43 | }; 44 | 45 | //controller 46 | app.controller = function() { 47 | this.pages = app.PageList(); 48 | 49 | this.rotate = function() { 50 | this.pages().push(this.pages().shift()) 51 | }.bind(this) 52 | }; 53 | 54 | //view 55 | app.view = function(ctrl) { 56 | return [ 57 | ctrl.pages().map(function(page) { 58 | return m("a", {href: page.url}, page.title); 59 | }), 60 | m("a", {onclick: ctrl.rotate}, "Rotate links") 61 | ]; 62 | }; 63 | 64 | //initialize 65 | m.module(document.getElementById("example"), app); 66 | ``` 67 | 68 | --- 69 | 70 | ### Learn more 71 | 72 | - [Tutorial](http://lhorie.github.io/mithril/getting-started.html) 73 | - [Differences from Other MVC Frameworks](http://lhorie.github.io/mithril/comparison.html) 74 | - [Benchmarks](http://lhorie.github.io/mithril/benchmarks.html) -------------------------------------------------------------------------------- /todomvc/mithril/js/views/main-view.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | //view utility 7 | app.watchInput = function(ontype, onenter, onescape) { 8 | return function(e) { 9 | ontype(e) 10 | if (e.keyCode == app.ENTER_KEY) onenter() 11 | if (e.keyCode == app.ESC_KEY) onescape() 12 | } 13 | }; 14 | 15 | 16 | 17 | app.view = function(ctrl) { 18 | return [ 19 | m('header#header', [ 20 | m('h1', 'todos'), 21 | m('input#new-todo[placeholder="What needs to be done?"]', { 22 | onkeypress: app.watchInput( 23 | m.withAttr('value', ctrl.title), 24 | ctrl.add.bind(ctrl, ctrl.title), 25 | ctrl.clearTitle.bind(ctrl) 26 | ), 27 | value: ctrl.title() 28 | }) 29 | ]), 30 | m('section#main', [ 31 | m('input#toggle-all[type=checkbox]'), 32 | m('ul#todo-list', [ 33 | ctrl.list.filter(ctrl.isVisible.bind(ctrl)).map(function(task, index) { 34 | return m('li', { class: task.completed() ? 'completed' : ''}, [ 35 | m('.view', [ 36 | m('input.toggle[type=checkbox]', { 37 | onclick: m.withAttr('checked', task.completed), 38 | checked: task.completed() 39 | }), 40 | m('label', task.title()), 41 | m('button.destroy', { onclick: ctrl.remove.bind(ctrl, index)}) 42 | ]), 43 | m('input.edit') 44 | ]) 45 | }) 46 | ]) 47 | ]), 48 | ctrl.list.length == 0 ? '' : app.footer(ctrl) 49 | ]; 50 | }; 51 | 52 | })(); 53 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TodoMVC Benchmark 5 | 6 | 7 | 8 | 24 | 25 | 26 |
    27 | 29 |

    TodoMVC Benchmark v1.0.0

    30 |

    31 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /todomvc/ractive/readme.md: -------------------------------------------------------------------------------- 1 | # Ractive.js TodoMVC app 2 | 3 | > Ractive.js solves some of the biggest headaches in web development – data binding, efficient DOM updates, event handling – and does so with almost no learning curve. 4 | 5 | > _[Ractive.js - ractivejs.org](http://ractivejs.org)_ 6 | 7 | 8 | ## Learning Ractive.js 9 | 10 | [Try the 60 second setup](https://github.com/Rich-Harris/Ractive/wiki/60-second-setup), or [follow the interactive tutorials](http://learn.ractivejs.org). 11 | 12 | You can find the [API documentation on GitHub](https://github.com/Rich-Harris/Ractive/wiki). 13 | 14 | If you have questions, try [Stack Overflow](http://stackoverflow.com/questions/tagged/ractivejs) or [@RactiveJS on Twitter](http://twitter.com/RactiveJS). 15 | 16 | _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)._ 17 | 18 | 19 | ## Implementation 20 | 21 | Ractive.js isn't an MVC framework in the traditional sense. There are no Model classes, just a plain old array of task objects, and there is only one view object. The app lives in a single file, with two tiny extra files to handle persistence and routing. 22 | 23 | This is because Ractive is optimised for developer sanity (as well as performance!). It was initially developed to create interactive news apps at [theguardian.com](http://theguardian.com), which have to be built against punishing deadlines and moving goalposts. Because it embraces reactive programming principles, the developer has less to worry about. Ractive's API is totally straightforward - you can learn it in 5 minutes and master it in a few hours. 24 | 25 | It has a number of innovative features: animations, easier event handling, declarative transitions, first-class SVG support, logical expressions in templates with sophisticated dependency tracking. For many developers, it hits the sweet spot between the flexibility of a library like Backbone and the power of a framework like Angular. 26 | 27 | 28 | ## Credit 29 | 30 | This TodoMVC application was created by [Rich Harris](http://rich-harris.co.uk). 31 | -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/lib/director/cli.js: -------------------------------------------------------------------------------- 1 | 2 | var util = require('util'), 3 | director = require('../director'); 4 | 5 | var Router = exports.Router = function (routes) { 6 | director.Router.call(this, routes); 7 | this.recurse = false; 8 | }; 9 | 10 | // 11 | // Inherit from `director.Router`. 12 | // 13 | util.inherits(Router, director.Router); 14 | 15 | // 16 | // ### function configure (options) 17 | // #### @options {Object} **Optional** Options to configure this instance with 18 | // Configures this instance with the specified `options`. 19 | // 20 | Router.prototype.configure = function (options) { 21 | options = options || {}; 22 | director.Router.prototype.configure.call(this, options); 23 | 24 | // 25 | // Delimiter must always be `\s` in CLI routing. 26 | // e.g. `jitsu users create` 27 | // 28 | this.delimiter = '\\s'; 29 | return this; 30 | }; 31 | 32 | // 33 | // ### function dispatch (method, path) 34 | // #### @method {string} Method to dispatch 35 | // #### @path {string} Path to dispatch 36 | // Finds a set of functions on the traversal towards 37 | // `method` and `path` in the core routing table then 38 | // invokes them based on settings in this instance. 39 | // 40 | Router.prototype.dispatch = function (method, path, tty, callback) { 41 | // 42 | // Prepend a single space onto the path so that the traversal 43 | // algorithm will recognize it. This is because we always assume 44 | // that the `path` begins with `this.delimiter`. 45 | // 46 | path = ' ' + path; 47 | var fns = this.traverse(method, path, this.routes, ''); 48 | if (!fns || fns.length === 0) { 49 | if (typeof this.notfound === 'function') { 50 | this.notfound.call({ tty: tty, cmd: path }, callback); 51 | } 52 | else if (callback) { 53 | callback(new Error('Could not find path: ' + path)); 54 | } 55 | 56 | return false; 57 | } 58 | 59 | if (this.recurse === 'forward') { 60 | fns = fns.reverse(); 61 | } 62 | 63 | this.invoke(this.runlist(fns), { tty: tty, cmd: path }, callback); 64 | return true; 65 | }; 66 | -------------------------------------------------------------------------------- /todomvc/preact/src/item.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import linkState from 'linkstate'; 3 | 4 | const ESCAPE_KEY = 27; 5 | const ENTER_KEY = 13; 6 | 7 | export default class TodoItem extends Component { 8 | handleSubmit = () => { 9 | let val = this.state.editText.trim(), 10 | { todo, onSave, onDestroy } = this.props; 11 | if (val) { 12 | onSave(todo, val); 13 | this.setState({ editText: val }); 14 | } 15 | else { 16 | onDestroy(todo); 17 | } 18 | }; 19 | 20 | handleEdit = () => { 21 | let { todo, onEdit } = this.props; 22 | onEdit(todo); 23 | this.setState({ editText: todo.title }); 24 | }; 25 | 26 | toggle = e => { 27 | let { todo, onToggle } = this.props; 28 | onToggle(todo); 29 | e.preventDefault(); 30 | }; 31 | 32 | handleKeyDown = e => { 33 | let { todo, onCancel } = this.props; 34 | if (e.which===ESCAPE_KEY) { 35 | this.setState({ editText: todo.title }); 36 | onCancel(todo); 37 | } 38 | else if (e.which===ENTER_KEY) { 39 | this.handleSubmit(todo); 40 | } 41 | }; 42 | 43 | destroy = () => { 44 | let { todo, onDestroy } = this.props; 45 | onDestroy(todo); 46 | }; 47 | 48 | focus(c) { 49 | if (c) setTimeout(() => c.focus(), 1); 50 | } 51 | 52 | // componentDidUpdate({ editing }) { 53 | // let node = editing && this.base && this.base.querySelector('.edit'); 54 | // if (node) node.focus(); 55 | // } 56 | 57 | render({ todo:{ title, completed }, editing }, { editText }) { 58 | return ( 59 |
  • 60 |
    61 | 67 | 68 |
    70 | { editing && ( 71 | 79 | ) } 80 |
  • 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/mithril.d.ts: -------------------------------------------------------------------------------- 1 | //Mithril type definitions for Typescript 2 | 3 | interface MithrilStatic { 4 | (selector: string, attributes: Object, children?: any): MithrilVirtualElement; 5 | (selector: string, children?: any): MithrilVirtualElement; 6 | prop(value?: any): (value?: any) => any; 7 | withAttr(property: string, callback: (value: any) => void): (e: Event) => any; 8 | module(rootElement: Element, module: MithrilModule): void; 9 | trust(html: string): String; 10 | render(rootElement: Element, children?: any): void; 11 | render(rootElement: HTMLDocument, children?: any): void; 12 | redraw(): void; 13 | route(rootElement: Element, defaultRoute: string, routes: { [key: string]: MithrilModule }): void; 14 | route(rootElement: HTMLDocument, defaultRoute: string, routes: { [key: string]: MithrilModule }): void; 15 | route(path: string, params?: any, shouldReplaceHistory?: boolean): void; 16 | route(): string; 17 | route(element: Element, isInitialized: boolean): void; 18 | request(options: MithrilXHROptions): MithrilPromise; 19 | deferred(): MithrilDeferred; 20 | sync(promises: MithrilPromise[]): MithrilPromise; 21 | startComputation(): void; 22 | endComputation(): void; 23 | } 24 | 25 | interface MithrilVirtualElement { 26 | tag: string; 27 | attrs: Object; 28 | children: any; 29 | } 30 | 31 | interface MithrilModule { 32 | controller: Function; 33 | view: Function; 34 | } 35 | 36 | interface MithrilDeferred { 37 | resolve(value?: any): void; 38 | reject(value?: any): void; 39 | promise: MithrilPromise; 40 | } 41 | 42 | interface MithrilPromise { 43 | (value?: any): any; 44 | then(successCallback?: (value: any) => any, errorCallback?: (value: any) => any): MithrilPromise; 45 | } 46 | 47 | interface MithrilXHROptions { 48 | method: string; 49 | url: string; 50 | user?: string; 51 | password?: string; 52 | data?: any; 53 | background?: boolean; 54 | unwrapSuccess?(data: any): any; 55 | unwrapError?(data: any): any; 56 | serialize?(dataToSerialize: any): string; 57 | deserialize?(dataToDeserialize: string): any; 58 | extract?(xhr: XMLHttpRequest, options: MithrilXHROptions); 59 | type?(data: Object): void; 60 | config?(xhr: XMLHttpRequest, options: MithrilXHROptions) 61 | } 62 | 63 | declare var Mithril: MithrilStatic; 64 | declare var m: MithrilStatic; -------------------------------------------------------------------------------- /todomvc/om/bower_components/director/lib/director/http/responses.js: -------------------------------------------------------------------------------- 1 | // 2 | // HTTP Error objectst 3 | // 4 | var util = require('util'); 5 | 6 | exports.NotModified = function () { 7 | this.status = 304; 8 | this.options = { 9 | removeContentHeaders: true 10 | }; 11 | }; 12 | 13 | util.inherits(exports.NotModified, Error); 14 | 15 | exports.BadRequest = function (msg) { 16 | msg = msg || 'Bad request'; 17 | 18 | this.status = 400; 19 | this.headers = {}; 20 | this.message = msg; 21 | this.body = { error: msg }; 22 | }; 23 | 24 | util.inherits(exports.BadRequest, Error); 25 | 26 | exports.NotAuthorized = function (msg) { 27 | msg = msg || 'Not Authorized'; 28 | 29 | this.status = 401; 30 | this.headers = {}; 31 | this.message = msg; 32 | this.body = { error: msg }; 33 | }; 34 | 35 | util.inherits(exports.NotAuthorized, Error); 36 | 37 | exports.Forbidden = function (msg) { 38 | msg = msg || 'Not Authorized'; 39 | 40 | this.status = 403; 41 | this.headers = {}; 42 | this.message = msg; 43 | this.body = { error: msg }; 44 | }; 45 | 46 | util.inherits(exports.Forbidden, Error); 47 | 48 | exports.NotFound = function (msg) { 49 | msg = msg || 'Not Found'; 50 | 51 | this.status = 404; 52 | this.headers = {}; 53 | this.message = msg; 54 | this.body = { error: msg }; 55 | }; 56 | 57 | util.inherits(exports.NotFound, Error); 58 | 59 | exports.MethodNotAllowed = function (allowed) { 60 | var msg = 'method not allowed.'; 61 | 62 | this.status = 405; 63 | this.headers = { allow: allowed }; 64 | this.message = msg; 65 | this.body = { error: msg }; 66 | }; 67 | 68 | util.inherits(exports.MethodNotAllowed, Error); 69 | 70 | exports.NotAcceptable = function (accept) { 71 | var msg = 'cannot generate "' + accept + '" response'; 72 | 73 | this.status = 406; 74 | this.headers = {}; 75 | this.message = msg; 76 | this.body = { 77 | error: msg, 78 | only: 'application/json' 79 | }; 80 | }; 81 | 82 | util.inherits(exports.NotAcceptable, Error); 83 | 84 | exports.NotImplemented = function (msg) { 85 | msg = msg || 'Not Implemented'; 86 | 87 | this.status = 501; 88 | this.headers = {}; 89 | this.message = msg; 90 | this.body = { error: msg }; 91 | }; 92 | 93 | util.inherits(exports.NotImplemented, Error); 94 | -------------------------------------------------------------------------------- /todomvc/mithril/README.md: -------------------------------------------------------------------------------- 1 | # Mithril TodoMVC Example 2 | 3 | > Mithril is a client-side Javascript MVC framework, i.e. it's a tool to make application code divided into a data layer (called "Model"), a UI layer (called View), and a glue layer (called Controller) 4 | 5 | > _[Mithril](http://lhorie.github.io/mithril/)_ 6 | 7 | 8 | ## Learning Mithril 9 | 10 | The [Mithril website](http://lhorie.github.io/mithril/) is a great resource for getting started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Getting Started](http://lhorie.github.io/mithril/getting-started.html) 15 | * [API Reference](http://lhorie.github.io/mithril/mithril.html) 16 | * [Mithril on Github](https://github.com/lhorie/mithril.js) 17 | 18 | _If you have other helpful links to share, or find any of the links above no longer work, please [let me know](https://github.com/jpmonette/todomvc-mithril/issues)._ 19 | 20 | ## Running 21 | 22 | 1. Clone the repo 23 | 2. Execute `bower install` in the repo (make sure yo install `npm`) 24 | 3. Open `index.html`! 25 | 26 | ## TODO 27 | 28 | * Proper routing support (fixed, waiting for latest Mithril version (v0.1.1)) 29 | * Select all 30 | * Fix items left behaviour 31 | 32 | ## Credit 33 | 34 | This TodoMVC application was created by [Jean-Philippe Monette](http://blogue.jpmonette.net/) 35 | 36 | ## License 37 | 38 | Copyright (C) 2013, Jean-Philippe Monette 39 | 40 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 45 | -------------------------------------------------------------------------------- /todomvc/backbone/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Backbone.js • TodoMVC 7 | 8 | 9 | 10 |
    11 | 15 |
    16 | 17 | 18 |
      19 |
      20 |
      21 |
      22 | 27 | 35 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/src/template.js: -------------------------------------------------------------------------------- 1 | const htmlEscapes = { 2 | '&': '&', 3 | '<': '<', 4 | '>': '>', 5 | '"': '"', 6 | '\'': ''', 7 | '`': '`' 8 | }; 9 | 10 | const reUnescapedHtml = /[&<>"'`]/g; 11 | const reHasUnescapedHtml = new RegExp(reUnescapedHtml.source); 12 | 13 | const escape = str => (str && reHasUnescapedHtml.test(str)) ? str.replace(reUnescapedHtml, escapeHtmlChar) : str; 14 | const escapeHtmlChar = chr => htmlEscapes[chr]; 15 | 16 | export default class Template { 17 | constructor() { 18 | this.defaultTemplate = ` 19 |
    • 20 |
      21 | 22 | 23 | 24 |
      25 |
    • 26 | `; 27 | } 28 | 29 | /** 30 | * Creates an
    • HTML string and returns it for placement in your app. 31 | * 32 | * NOTE: In real life you should be using a templating engine such as Mustache 33 | * or Handlebars, however, this is a vanilla JS example. 34 | * 35 | * @param {object} data The object containing keys you want to find in the 36 | * template to replace. 37 | * @returns {string} HTML String of an
    • element 38 | * 39 | * @example 40 | * view.show({ 41 | * id: 1, 42 | * title: "Hello World", 43 | * completed: 0, 44 | * }) 45 | */ 46 | show(data){ 47 | const view = data.map(d => { 48 | const template = this.defaultTemplate; 49 | const completed = d.completed ? 'completed' : ''; 50 | const checked = d.completed ? 'checked' : ''; 51 | 52 | return this.defaultTemplate 53 | .replace('{{id}}', d.id) 54 | .replace('{{title}}', escape(d.title)) 55 | .replace('{{completed}}', completed) 56 | .replace('{{checked}}', checked); 57 | }); 58 | 59 | return view.join(''); 60 | } 61 | 62 | /** 63 | * Displays a counter of how many to dos are left to complete 64 | * 65 | * @param {number} activeTodos The number of active todos. 66 | * @returns {string} String containing the count 67 | */ 68 | itemCounter(activeTodos){ 69 | const plural = activeTodos === 1 ? '' : 's'; 70 | return `${activeTodos} item${plural} left`; 71 | } 72 | 73 | /** 74 | * Updates the text within the "Clear completed" button 75 | * 76 | * @param {[type]} completedTodos The number of completed todos. 77 | * @returns {string} String containing the count 78 | */ 79 | clearCompletedButton(completedTodos){ 80 | return (completedTodos > 0) ? 'Clear completed' : ''; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /todomvc/react/js/todoModel.js: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | var app = app || {}; 6 | 7 | (function () { 8 | 'use strict'; 9 | 10 | var Utils = app.Utils; 11 | // Generic "model" object. You can use whatever 12 | // framework you want. For this application it 13 | // may not even be worth separating this logic 14 | // out, but we do this to demonstrate one way to 15 | // separate out parts of your application. 16 | app.TodoModel = function (key) { 17 | this.key = key; 18 | this.todos = Utils.store(key); 19 | this.onChanges = []; 20 | }; 21 | 22 | app.TodoModel.prototype.subscribe = function (onChange) { 23 | this.onChanges.push(onChange); 24 | }; 25 | 26 | app.TodoModel.prototype.inform = function () { 27 | Utils.store(this.key, this.todos); 28 | this.onChanges.forEach(function (cb) { cb(); }); 29 | }; 30 | 31 | app.TodoModel.prototype.addTodo = function (title) { 32 | this.todos = this.todos.concat({ 33 | id: Utils.uuid(), 34 | title: title, 35 | completed: false 36 | }); 37 | 38 | this.inform(); 39 | }; 40 | 41 | app.TodoModel.prototype.toggleAll = function (checked) { 42 | // Note: it's usually better to use immutable data structures since they're 43 | // easier to reason about and React works very well with them. That's why 44 | // we use map() and filter() everywhere instead of mutating the array or 45 | // todo items themselves. 46 | this.todos = this.todos.map(function (todo) { 47 | return Utils.extend({}, todo, {completed: checked}); 48 | }); 49 | 50 | this.inform(); 51 | }; 52 | 53 | app.TodoModel.prototype.toggle = function (todoToToggle) { 54 | this.todos = this.todos.map(function (todo) { 55 | return todo !== todoToToggle ? 56 | todo : 57 | Utils.extend({}, todo, {completed: !todo.completed}); 58 | }); 59 | 60 | this.inform(); 61 | }; 62 | 63 | app.TodoModel.prototype.destroy = function (todo) { 64 | this.todos = this.todos.filter(function (candidate) { 65 | return candidate !== todo; 66 | }); 67 | 68 | this.inform(); 69 | }; 70 | 71 | app.TodoModel.prototype.save = function (todoToSave, text) { 72 | this.todos = this.todos.map(function (todo) { 73 | return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text}); 74 | }); 75 | 76 | this.inform(); 77 | }; 78 | 79 | app.TodoModel.prototype.clearCompleted = function () { 80 | this.todos = this.todos.filter(function (todo) { 81 | return !todo.completed; 82 | }); 83 | 84 | this.inform(); 85 | }; 86 | 87 | })(); 88 | -------------------------------------------------------------------------------- /todomvc/choo/dist/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js • TodoMVC 6 | 7 | 8 | 9 | 10 | 11 |
      12 |
      13 |

      todos

      14 | 15 |
      16 |
      17 | 18 |
        19 |
      • 20 |
        21 | 22 | 23 | 24 |
        25 | 26 |
      • 27 |
      28 |
      29 |
      30 | 31 | {{remaining | pluralize 'item'}} left 32 | 33 | 38 | 41 |
      42 |
      43 |
      44 |

      Double-click to edit a todo

      45 |

      Written by Evan You

      46 |

      Part of TodoMVC

      47 |
      48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /todomvc/vue/node_modules/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/knockoutjs/node_modules/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/mercury/update.js: -------------------------------------------------------------------------------- 1 | var State = require("./state.js") 2 | 3 | module.exports = { 4 | setRoute: setRoute, 5 | toggleAll: toggleAll, 6 | add: add, 7 | setTodoField: setTodoField, 8 | toggle: toggle, 9 | destroy: destroy, 10 | startEdit: startEdit, 11 | finishEdit: finishEdit, 12 | cancelEdit: cancelEdit, 13 | clearCompleted: clearCompleted 14 | } 15 | 16 | function setRoute(state, route) { 17 | state.route.set(route.substr(2) || "all") 18 | } 19 | 20 | function toggleAll(state, value) { 21 | state.todos.forEach(function (todo) { 22 | todo.completed.set(value.toggle) 23 | }) 24 | } 25 | 26 | function add(state, data) { 27 | if (data.newTodo.trim() === "") { 28 | return 29 | } 30 | 31 | state.todos.push(State.todoItem({ 32 | title: data.newTodo.trim() 33 | })) 34 | state.field.text.set("") 35 | } 36 | 37 | 38 | function setTodoField(state, data) { 39 | state.field.text.set(data.newTodo) 40 | } 41 | 42 | function toggle(state, data) { 43 | var item = find(state.todos, data.id) 44 | item.completed.set(data.completed) 45 | } 46 | 47 | function startEdit(state, data) { 48 | var item = find(state.todos, data.id) 49 | item.editing.set(true) 50 | } 51 | 52 | function destroy(state, data) { 53 | var index = findIndex(state.todos, data.id) 54 | state.todos.splice(index, 1) 55 | } 56 | 57 | function finishEdit(state, data) { 58 | var item = find(state.todos, data.id) 59 | if (!item || item.editing === false) return 60 | 61 | item.editing.set(false) 62 | item.title.set(data.title) 63 | 64 | if (data.title.trim() === "") { 65 | destroy(state, data) 66 | } 67 | } 68 | 69 | function cancelEdit(state, data) { 70 | var item = find(state.todos, data.id) 71 | 72 | item.editing.set(false) 73 | } 74 | 75 | function clearCompleted(state) { 76 | state.todos().forEach(function (todo, index) { 77 | if (todo.completed) { 78 | destroy(state, todo) 79 | } 80 | }) 81 | } 82 | 83 | function find(list, id) { 84 | for (var i = 0; i < list.getLength(); i++) { 85 | var item = list.get(i) 86 | if (item.id === id) { 87 | return item 88 | } 89 | } 90 | 91 | return null 92 | } 93 | 94 | function findIndex(list, id) { 95 | for (var i = 0; i < list.getLength(); i++) { 96 | var item = list.get(i) 97 | if (item.id === id) { 98 | return i 99 | } 100 | } 101 | 102 | return null 103 | } 104 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/node_modules/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/node_modules/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/readme.md: -------------------------------------------------------------------------------- 1 | # AngularJS (Performance Optimized) TodoMVC Example 2 | 3 | > HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop. 4 | 5 | > _[AngularJS - angularjs.org](http://angularjs.org)_ 6 | 7 | 8 | ## Learning AngularJS 9 | The [AngularJS website](http://angularjs.org) is a great resource for getting started. 10 | 11 | Here are some links you may find helpful: 12 | 13 | * [Tutorial](http://docs.angularjs.org/tutorial) 14 | * [API Reference](http://docs.angularjs.org/api) 15 | * [Developer Guide](http://docs.angularjs.org/guide) 16 | * [Applications built with AngularJS](https://www.madewithangular.com/) 17 | * [Blog](http://blog.angularjs.org) 18 | * [FAQ](http://docs.angularjs.org/misc/faq) 19 | * [AngularJS Meetups](http://www.youtube.com/angularjs) 20 | 21 | Articles and guides from the community: 22 | 23 | * [Code School AngularJS course](https://www.codeschool.com/courses/shaping-up-with-angular-js) 24 | * [5 Awesome AngularJS Features](http://net.tutsplus.com/tutorials/javascript-ajax/5-awesome-angularjs-features) 25 | * [Using Yeoman with AngularJS](http://briantford.com/blog/angular-yeoman.html) 26 | * [me&ngular - an introduction to MVW](http://stephenplusplus.github.io/meangular) 27 | 28 | Get help from other AngularJS users: 29 | 30 | * [Walkthroughs and Tutorials on YouTube](http://www.youtube.com/playlist?list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz) 31 | * [Google Groups mailing list](https://groups.google.com/forum/?fromgroups#!forum/angular) 32 | * [angularjs on Stack Overflow](http://stackoverflow.com/questions/tagged/angularjs) 33 | * [AngularJS on Twitter](https://twitter.com/angularjs) 34 | * [AngularjS on Google +](https://plus.google.com/+AngularJS/posts) 35 | 36 | _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)._ 37 | 38 | 39 | ## Implementation 40 | The normal AngularJS TodoMVC implementation performs deep watching of the todos array object. This means that it keeps an in-memory copy of the complete array that is used for dirty checking in order to detect model mutations. For smaller applications such as TodoMVC, this is completely fine as one trades off a little memory and performance for the sake of simplicity. 41 | 42 | In larger more complex applications however, where one might be working with 100s or 1000s of large objects one definitely should avoid using this approach. This implementation of the AngularJS app demonstrates the correct way to approach this problem when working in larger apps. 43 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-app-css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "todomvc-app-css@^2.0.0", 5 | "/Users/lorenzo/Projects/todomvc-perf-comparison/todomvc/react" 6 | ] 7 | ], 8 | "_from": "todomvc-app-css@>=2.0.0 <3.0.0", 9 | "_id": "todomvc-app-css@2.0.4", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/todomvc-app-css", 13 | "_nodeVersion": "4.2.4", 14 | "_npmUser": { 15 | "email": "sindresorhus@gmail.com", 16 | "name": "sindresorhus" 17 | }, 18 | "_npmVersion": "2.14.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "todomvc-app-css", 22 | "raw": "todomvc-app-css@^2.0.0", 23 | "rawSpec": "^2.0.0", 24 | "scope": null, 25 | "spec": ">=2.0.0 <3.0.0", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_resolved": "https://registry.npmjs.org/todomvc-app-css/-/todomvc-app-css-2.0.4.tgz", 32 | "_shasum": "a1b62664d1e3ade62140b6c40a5bb92adea0f0ff", 33 | "_shrinkwrap": null, 34 | "_spec": "todomvc-app-css@^2.0.0", 35 | "_where": "/Users/lorenzo/Projects/todomvc-perf-comparison/todomvc/react", 36 | "author": { 37 | "email": "sindresorhus@gmail.com", 38 | "name": "Sindre Sorhus", 39 | "url": "sindresorhus.com" 40 | }, 41 | "bugs": { 42 | "url": "https://github.com/tastejs/todomvc-app-css/issues" 43 | }, 44 | "dependencies": {}, 45 | "description": "CSS for TodoMVC apps", 46 | "devDependencies": {}, 47 | "directories": {}, 48 | "dist": { 49 | "shasum": "a1b62664d1e3ade62140b6c40a5bb92adea0f0ff", 50 | "tarball": "https://registry.npmjs.org/todomvc-app-css/-/todomvc-app-css-2.0.4.tgz" 51 | }, 52 | "files": [ 53 | "index.css" 54 | ], 55 | "gitHead": "47337a0da5727cbca2672c3055411405bd43bde4", 56 | "homepage": "https://github.com/tastejs/todomvc-app-css", 57 | "keywords": [ 58 | "app", 59 | "css", 60 | "style", 61 | "stylesheet", 62 | "tastejs", 63 | "template", 64 | "todo", 65 | "todomvc" 66 | ], 67 | "license": "CC-BY-4.0", 68 | "maintainers": [ 69 | { 70 | "name": "sindresorhus", 71 | "email": "sindresorhus@gmail.com" 72 | }, 73 | { 74 | "name": "addyosmani", 75 | "email": "addyosmani@gmail.com" 76 | }, 77 | { 78 | "name": "passy", 79 | "email": "phartig@rdrei.net" 80 | }, 81 | { 82 | "name": "stephenplusplus", 83 | "email": "sawchuk@gmail.com" 84 | } 85 | ], 86 | "name": "todomvc-app-css", 87 | "optionalDependencies": {}, 88 | "readme": "ERROR: No README data found!", 89 | "repository": { 90 | "type": "git", 91 | "url": "git+https://github.com/tastejs/todomvc-app-css.git" 92 | }, 93 | "scripts": {}, 94 | "style": "index.css", 95 | "version": "2.0.4" 96 | } 97 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/js/controllers/todoCtrl.js: -------------------------------------------------------------------------------- 1 | /* jshint undef: true, unused: true */ 2 | /*global angular */ 3 | 4 | /* 5 | * Line below lets us save `this` as `TC` 6 | * to make properties look exactly the same as in the template 7 | */ 8 | //jscs:disable safeContextKeyword 9 | (function () { 10 | 'use strict'; 11 | 12 | angular.module('todoCtrl', []) 13 | 14 | /** 15 | * The main controller for the app. The controller: 16 | * - retrieves and persists the model via the todoStorage service 17 | * - exposes the model to the template and provides event handlers 18 | */ 19 | .controller('TodoCtrl', function TodoCtrl($scope, $location, todoStorage) { 20 | var TC = this; 21 | var todos = TC.todos = todoStorage.get(); 22 | 23 | TC.ESCAPE_KEY = 27; 24 | TC.editedTodo = {}; 25 | 26 | function resetTodo() { 27 | TC.newTodo = {title: '', completed: false}; 28 | } 29 | 30 | resetTodo(); 31 | 32 | if ($location.path() === '') { 33 | $location.path('/'); 34 | } 35 | 36 | TC.location = $location; 37 | 38 | $scope.$watch('TC.location.path()', function (path) { 39 | TC.statusFilter = { '/active': {completed: false}, '/completed': {completed: true} }[path]; 40 | }); 41 | 42 | // 3rd argument `true` for deep object watching 43 | $scope.$watch('TC.todos', function () { 44 | TC.remainingCount = todos.filter(function (todo) { return !todo.completed; }).length; 45 | TC.allChecked = (TC.remainingCount === 0); 46 | 47 | // Save any changes to localStorage 48 | todoStorage.put(todos); 49 | }, true); 50 | 51 | TC.addTodo = function () { 52 | var newTitle = TC.newTodo.title = TC.newTodo.title.trim(); 53 | if (newTitle.length === 0) { 54 | return; 55 | } 56 | 57 | todos.push(TC.newTodo); 58 | resetTodo(); 59 | }; 60 | 61 | TC.editTodo = function (todo) { 62 | TC.editedTodo = todo; 63 | 64 | // Clone the original todo to restore it on demand. 65 | TC.originalTodo = angular.copy(todo); 66 | }; 67 | 68 | TC.doneEditing = function (todo, index) { 69 | TC.editedTodo = {}; 70 | todo.title = todo.title.trim(); 71 | 72 | if (!todo.title) { 73 | TC.removeTodo(index); 74 | } 75 | }; 76 | 77 | TC.revertEditing = function (index) { 78 | TC.editedTodo = {}; 79 | todos[index] = TC.originalTodo; 80 | }; 81 | 82 | TC.removeTodo = function (index) { 83 | todos.splice(index, 1); 84 | }; 85 | 86 | TC.clearCompletedTodos = function () { 87 | TC.todos = todos = todos.filter(function (val) { 88 | return !val.completed; 89 | }); 90 | }; 91 | 92 | TC.markAll = function (completed) { 93 | todos.forEach(function (todo) { 94 | todo.completed = completed; 95 | }); 96 | }; 97 | }); 98 | })(); 99 | //jscs:enable 100 | -------------------------------------------------------------------------------- /todomvc/knockoutjs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Knockout.js • TodoMVC 6 | 7 | 8 | 9 | 10 |
      11 | 15 |
      16 | 17 | 18 |
        19 |
      • 20 |
        21 | 22 | 23 | 24 |
        25 | 26 |
      • 27 |
      28 |
      29 |
      30 | 31 | 0 32 | left 33 | 34 | 45 | 46 |
      47 |
      48 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /todomvc/preact/src/app.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import linkState from 'linkstate'; 3 | import Router from 'preact-router'; 4 | import createTodoModel from './model'; 5 | import TodoHeader from './header'; 6 | import TodoFooter from './footer'; 7 | import TodoItem from './item'; 8 | 9 | const ALL_TODOS = 'all'; 10 | const ACTIVE_TODOS = 'active'; 11 | const COMPLETED_TODOS = 'completed'; 12 | 13 | const FILTERS = { 14 | [ALL_TODOS]: todo => true, 15 | [ACTIVE_TODOS]: todo => !todo.completed, 16 | [COMPLETED_TODOS]: todo => todo.completed 17 | }; 18 | 19 | export default class App extends Component { 20 | model = createTodoModel(); 21 | 22 | state = { 23 | todos: this.model.todos, 24 | nowShowing: ALL_TODOS 25 | }; 26 | 27 | handleRoute = ({ url }) => { 28 | let nowShowing = url.replace(/\/$/,'').split('/').pop(); 29 | if (!FILTERS[nowShowing]) { 30 | nowShowing = ALL_TODOS; 31 | } 32 | this.setState({ nowShowing }); 33 | }; 34 | 35 | toggleAll = e => { 36 | this.model.toggleAll(e.target.checked); 37 | }; 38 | 39 | save = (todo, text) => { 40 | this.model.save(todo, text); 41 | this.reset(); 42 | }; 43 | 44 | reset = () => { 45 | this.setState({ editing: null }); 46 | }; 47 | 48 | componentWillMount() { 49 | this.model.subscribe( state => { 50 | this.setState({ todos: state.todos }); 51 | }); 52 | } 53 | 54 | render({ }, { nowShowing=ALL_TODOS, todos, newTodo, editing }) { 55 | let shownTodos = todos.filter( FILTERS[nowShowing] ), 56 | activeTodoCount = todos.reduce( (a, todo) => a + (todo.completed ? 0 : 1), 0), 57 | completedCount = todos.length - activeTodoCount; 58 | 59 | return ( 60 |
      61 | 62 | 63 | 64 | 65 | 66 | 67 | { todos.length ? ( 68 |
      69 | 75 |
        76 | { shownTodos.map( todo => ( 77 | 87 | )) } 88 |
      89 |
      90 | ) : null } 91 | 92 | { (activeTodoCount || completedCount) ? ( 93 | 99 | ) : null } 100 |
      101 | ); 102 | } 103 | } 104 | 105 | 106 | // just a fake component we can feed to router. yay. 107 | const Noop = () => null; 108 | -------------------------------------------------------------------------------- /todomvc/vue/js/app.js: -------------------------------------------------------------------------------- 1 | /*global Vue, todoStorage */ 2 | 3 | (function (exports) { 4 | 5 | 'use strict'; 6 | 7 | var filters = { 8 | all: function (todos) { 9 | return todos; 10 | }, 11 | active: function (todos) { 12 | return todos.filter(function (todo) { 13 | return !todo.completed; 14 | }); 15 | }, 16 | completed: function (todos) { 17 | return todos.filter(function (todo) { 18 | return todo.completed; 19 | }); 20 | } 21 | }; 22 | 23 | exports.app = new Vue({ 24 | 25 | // the root element that will be compiled 26 | el: '.todoapp', 27 | 28 | // app initial state 29 | data: { 30 | todos: todoStorage.fetch(), 31 | newTodo: '', 32 | editedTodo: null, 33 | visibility: 'all' 34 | }, 35 | 36 | // watch todos change for localStorage persistence 37 | watch: { 38 | todos: { 39 | deep: true, 40 | handler: todoStorage.save 41 | } 42 | }, 43 | 44 | // computed properties 45 | // http://vuejs.org/guide/computed.html 46 | computed: { 47 | filteredTodos: function () { 48 | return filters[this.visibility](this.todos); 49 | }, 50 | remaining: function () { 51 | return filters.active(this.todos).length; 52 | }, 53 | allDone: { 54 | get: function () { 55 | return this.remaining === 0; 56 | }, 57 | set: function (value) { 58 | this.todos.forEach(function (todo) { 59 | todo.completed = value; 60 | }); 61 | } 62 | } 63 | }, 64 | 65 | // methods that implement data logic. 66 | // note there's no DOM manipulation here at all. 67 | methods: { 68 | 69 | addTodo: function () { 70 | var value = this.newTodo && this.newTodo.trim(); 71 | if (!value) { 72 | return; 73 | } 74 | this.todos.push({ title: value, completed: false }); 75 | this.newTodo = ''; 76 | }, 77 | 78 | removeTodo: function (todo) { 79 | this.todos.$remove(todo); 80 | }, 81 | 82 | editTodo: function (todo) { 83 | this.beforeEditCache = todo.title; 84 | this.editedTodo = todo; 85 | }, 86 | 87 | doneEdit: function (todo) { 88 | if (!this.editedTodo) { 89 | return; 90 | } 91 | this.editedTodo = null; 92 | todo.title = todo.title.trim(); 93 | if (!todo.title) { 94 | this.removeTodo(todo); 95 | } 96 | }, 97 | 98 | cancelEdit: function (todo) { 99 | this.editedTodo = null; 100 | todo.title = this.beforeEditCache; 101 | }, 102 | 103 | removeCompleted: function () { 104 | this.todos = filters.active(this.todos); 105 | } 106 | }, 107 | 108 | // a custom directive to wait for the DOM to be updated 109 | // before focusing on the input field. 110 | // http://vuejs.org/guide/custom-directive.html 111 | directives: { 112 | 'todo-focus': function (value) { 113 | if (!value) { 114 | return; 115 | } 116 | var el = this.el; 117 | Vue.nextTick(function () { 118 | el.focus(); 119 | }); 120 | } 121 | } 122 | }); 123 | 124 | })(window); 125 | -------------------------------------------------------------------------------- /todomvc/emberjs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ember.js • TodoMVC 6 | 7 | 8 | 9 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/src/model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a new Model instance and hooks up the storage. 3 | * @constructor 4 | * @param {object} storage A reference to the client side storage class 5 | */ 6 | export default class Model { 7 | constructor(storage) { 8 | this.storage = storage; 9 | } 10 | 11 | /** 12 | * Creates a new todo model 13 | * 14 | * @param {string} [title] The title of the task 15 | * @param {function} [callback] The callback to fire after the model is created 16 | */ 17 | create(title, callback){ 18 | title = title || ''; 19 | 20 | const newItem = { 21 | title: title.trim(), 22 | completed: false 23 | }; 24 | 25 | this.storage.save(newItem, callback); 26 | } 27 | 28 | /** 29 | * Finds and returns a model in storage. If no query is given it'll simply 30 | * return everything. If you pass in a string or number it'll look that up as 31 | * the ID of the model to find. Lastly, you can pass it an object to match 32 | * against. 33 | * 34 | * @param {string|number|object} [query] A query to match models against 35 | * @param {function} [callback] The callback to fire after the model is found 36 | * 37 | * @example 38 | * model.read(1, func) // Will find the model with an ID of 1 39 | * model.read('1') // Same as above 40 | * //Below will find a model with foo equalling bar and hello equalling world. 41 | * model.read({ foo: 'bar', hello: 'world' }) 42 | */ 43 | read(query, callback){ 44 | const queryType = typeof query; 45 | 46 | if (queryType === 'function') { 47 | this.storage.findAll(query); 48 | } else if (queryType === 'string' || queryType === 'number') { 49 | query = parseInt(query, 10); 50 | this.storage.find({id: query}, callback); 51 | } else { 52 | this.storage.find(query, callback); 53 | } 54 | } 55 | 56 | /** 57 | * Updates a model by giving it an ID, data to update, and a callback to fire when 58 | * the update is complete. 59 | * 60 | * @param {number} id The id of the model to update 61 | * @param {object} data The properties to update and their new value 62 | * @param {function} callback The callback to fire when the update is complete. 63 | */ 64 | update(id, data, callback){ 65 | this.storage.save(data, callback, id); 66 | } 67 | 68 | /** 69 | * Removes a model from storage 70 | * 71 | * @param {number} id The ID of the model to remove 72 | * @param {function} callback The callback to fire when the removal is complete. 73 | */ 74 | remove(id, callback){ 75 | this.storage.remove(id, callback); 76 | } 77 | 78 | /** 79 | * WARNING: Will remove ALL data from storage. 80 | * 81 | * @param {function} callback The callback to fire when the storage is wiped. 82 | */ 83 | removeAll(callback){ 84 | this.storage.drop(callback); 85 | } 86 | 87 | /** 88 | * Returns a count of all todos 89 | */ 90 | getCount(callback){ 91 | const todos = { 92 | active: 0, 93 | completed: 0, 94 | total: 0 95 | }; 96 | 97 | this.storage.findAll(data => { 98 | for (let todo of data) { 99 | if (todo.completed) { 100 | todos.completed++; 101 | } else { 102 | todos.active++; 103 | } 104 | 105 | todos.total++; 106 | } 107 | 108 | callback(todos); 109 | }); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /todomvc/angularjs-perf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularJS • TodoMVC 6 | 7 | 8 | 9 | 10 | 11 |
      12 | 18 |
      19 | 20 | 21 |
        22 |
      • 24 |
        25 | 26 | 27 | 28 |
        29 |
        30 | 36 |
        37 |
      • 38 |
      39 |
      40 |
      41 | {{TC.remainingCount}} 42 | 43 | 44 | 55 | 56 |
      57 |
      58 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /todomvc/react/js/todoItem.jsx: -------------------------------------------------------------------------------- 1 | /*jshint quotmark: false */ 2 | /*jshint white: false */ 3 | /*jshint trailing: false */ 4 | /*jshint newcap: false */ 5 | /*global React */ 6 | var app = app || {}; 7 | 8 | (function () { 9 | 'use strict'; 10 | 11 | var ESCAPE_KEY = 27; 12 | var ENTER_KEY = 13; 13 | 14 | app.TodoItem = React.createClass({ 15 | handleSubmit: function (event) { 16 | var val = this.state.editText.trim(); 17 | if (val) { 18 | this.props.onSave(val); 19 | this.setState({editText: val}); 20 | } else { 21 | this.props.onDestroy(); 22 | } 23 | }, 24 | 25 | handleEdit: function () { 26 | this.props.onEdit(); 27 | this.setState({editText: this.props.todo.title}); 28 | }, 29 | 30 | handleKeyDown: function (event) { 31 | if (event.which === ESCAPE_KEY) { 32 | this.setState({editText: this.props.todo.title}); 33 | this.props.onCancel(event); 34 | } else if (event.which === ENTER_KEY) { 35 | this.handleSubmit(event); 36 | } 37 | }, 38 | 39 | handleChange: function (event) { 40 | if (this.props.editing) { 41 | this.setState({editText: event.target.value}); 42 | } 43 | }, 44 | 45 | getInitialState: function () { 46 | return {editText: this.props.todo.title}; 47 | }, 48 | 49 | /** 50 | * This is a completely optional performance enhancement that you can 51 | * implement on any React component. If you were to delete this method 52 | * the app would still work correctly (and still be very performant!), we 53 | * just use it as an example of how little code it takes to get an order 54 | * of magnitude performance improvement. 55 | */ 56 | shouldComponentUpdate: function (nextProps, nextState) { 57 | return ( 58 | nextProps.todo !== this.props.todo || 59 | nextProps.editing !== this.props.editing || 60 | nextState.editText !== this.state.editText 61 | ); 62 | }, 63 | 64 | /** 65 | * Safely manipulate the DOM after updating the state when invoking 66 | * `this.props.onEdit()` in the `handleEdit` method above. 67 | * For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate 68 | * and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate 69 | */ 70 | componentDidUpdate: function (prevProps) { 71 | if (!prevProps.editing && this.props.editing) { 72 | var node = React.findDOMNode(this.refs.editField); 73 | node.focus(); 74 | node.setSelectionRange(node.value.length, node.value.length); 75 | } 76 | }, 77 | 78 | render: function () { 79 | return ( 80 |
    • 84 |
      85 | 91 | 94 |
      96 | 104 |
    • 105 | ); 106 | } 107 | }); 108 | })(); 109 | -------------------------------------------------------------------------------- /todomvc/react/js/todoItem.js: -------------------------------------------------------------------------------- 1 | /*jshint quotmark: false */ 2 | /*jshint white: false */ 3 | /*jshint trailing: false */ 4 | /*jshint newcap: false */ 5 | /*global React */ 6 | var app = app || {}; 7 | 8 | (function () { 9 | 'use strict'; 10 | 11 | var ESCAPE_KEY = 27; 12 | var ENTER_KEY = 13; 13 | 14 | app.TodoItem = React.createClass({ 15 | handleSubmit: function (event) { 16 | var val = this.state.editText.trim(); 17 | if (val) { 18 | this.props.onSave(val); 19 | this.setState({editText: val}); 20 | } else { 21 | this.props.onDestroy(); 22 | } 23 | }, 24 | 25 | handleEdit: function () { 26 | this.props.onEdit(); 27 | this.setState({editText: this.props.todo.title}); 28 | }, 29 | 30 | handleKeyDown: function (event) { 31 | if (event.which === ESCAPE_KEY) { 32 | this.setState({editText: this.props.todo.title}); 33 | this.props.onCancel(event); 34 | } else if (event.which === ENTER_KEY) { 35 | this.handleSubmit(event); 36 | } 37 | }, 38 | 39 | handleChange: function (event) { 40 | if (this.props.editing) { 41 | this.setState({editText: event.target.value}); 42 | } 43 | }, 44 | 45 | getInitialState: function () { 46 | return {editText: this.props.todo.title}; 47 | }, 48 | 49 | /** 50 | * This is a completely optional performance enhancement that you can 51 | * implement on any React component. If you were to delete this method 52 | * the app would still work correctly (and still be very performant!), we 53 | * just use it as an example of how little code it takes to get an order 54 | * of magnitude performance improvement. 55 | */ 56 | shouldComponentUpdate: function (nextProps, nextState) { 57 | return ( 58 | nextProps.todo !== this.props.todo || 59 | nextProps.editing !== this.props.editing || 60 | nextState.editText !== this.state.editText 61 | ); 62 | }, 63 | 64 | /** 65 | * Safely manipulate the DOM after updating the state when invoking 66 | * `this.props.onEdit()` in the `handleEdit` method above. 67 | * For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate 68 | * and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate 69 | */ 70 | componentDidUpdate: function (prevProps) { 71 | if (!prevProps.editing && this.props.editing) { 72 | var node = React.findDOMNode(this.refs.editField); 73 | node.focus(); 74 | node.setSelectionRange(node.value.length, node.value.length); 75 | } 76 | }, 77 | 78 | render: function () { 79 | return ( 80 | React.createElement('li', {className: classNames({ 81 | completed: this.props.todo.completed, 82 | editing: this.props.editing 83 | })}, [ 84 | React.createElement('div', {className: "view"}, [ 85 | React.createElement('input', { 86 | className: "toggle", 87 | type: "checkbox", 88 | checked: this.props.todo.completed, 89 | onChange: this.props.onToggle} 90 | ), 91 | React.createElement('label', {onDoubleClick: this.handleEdit}, [ 92 | this.props.todo.title 93 | ]), 94 | React.createElement('button', {className: "destroy", onClick: this.props.onDestroy}) 95 | ]), 96 | React.createElement('input', { 97 | ref: "editField", 98 | className: "edit", 99 | value: this.state.editText, 100 | onBlur: this.handleSubmit, 101 | onChange: this.handleChange, 102 | onKeyDown: this.handleKeyDown} 103 | ) 104 | ]) 105 | ); 106 | } 107 | }); 108 | })(); 109 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/dist/template.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | 9 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 10 | 11 | var htmlEscapes = { 12 | '&': '&', 13 | '<': '<', 14 | '>': '>', 15 | '"': '"', 16 | '\'': ''', 17 | '`': '`' 18 | }; 19 | 20 | var reUnescapedHtml = /[&<>"'`]/g; 21 | var reHasUnescapedHtml = new RegExp(reUnescapedHtml.source); 22 | 23 | var escape = function escape(str) { 24 | return str && reHasUnescapedHtml.test(str) ? str.replace(reUnescapedHtml, escapeHtmlChar) : str; 25 | }; 26 | var escapeHtmlChar = function escapeHtmlChar(chr) { 27 | return htmlEscapes[chr]; 28 | }; 29 | 30 | var Template = function () { 31 | function Template() { 32 | _classCallCheck(this, Template); 33 | 34 | this.defaultTemplate = '\n\t\t\t
    • \n\t\t\t\t
      \n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
      \n\t\t\t
    • \n\t\t'; 35 | } 36 | 37 | /** 38 | * Creates an
    • HTML string and returns it for placement in your app. 39 | * 40 | * NOTE: In real life you should be using a templating engine such as Mustache 41 | * or Handlebars, however, this is a vanilla JS example. 42 | * 43 | * @param {object} data The object containing keys you want to find in the 44 | * template to replace. 45 | * @returns {string} HTML String of an
    • element 46 | * 47 | * @example 48 | * view.show({ 49 | * id: 1, 50 | * title: "Hello World", 51 | * completed: 0, 52 | * }) 53 | */ 54 | 55 | _createClass(Template, [{ 56 | key: 'show', 57 | value: function show(data) { 58 | var _this = this; 59 | 60 | var view = data.map(function (d) { 61 | var template = _this.defaultTemplate; 62 | var completed = d.completed ? 'completed' : ''; 63 | var checked = d.completed ? 'checked' : ''; 64 | 65 | return _this.defaultTemplate.replace('{{id}}', d.id).replace('{{title}}', escape(d.title)).replace('{{completed}}', completed).replace('{{checked}}', checked); 66 | }); 67 | 68 | return view.join(''); 69 | } 70 | 71 | /** 72 | * Displays a counter of how many to dos are left to complete 73 | * 74 | * @param {number} activeTodos The number of active todos. 75 | * @returns {string} String containing the count 76 | */ 77 | 78 | }, { 79 | key: 'itemCounter', 80 | value: function itemCounter(activeTodos) { 81 | var plural = activeTodos === 1 ? '' : 's'; 82 | return '' + activeTodos + ' item' + plural + ' left'; 83 | } 84 | 85 | /** 86 | * Updates the text within the "Clear completed" button 87 | * 88 | * @param {[type]} completedTodos The number of completed todos. 89 | * @returns {string} String containing the count 90 | */ 91 | 92 | }, { 93 | key: 'clearCompletedButton', 94 | value: function clearCompletedButton(completedTodos) { 95 | return completedTodos > 0 ? 'Clear completed' : ''; 96 | } 97 | }]); 98 | 99 | return Template; 100 | }(); 101 | 102 | exports.default = Template; -------------------------------------------------------------------------------- /todomvc/emberjs/bower_components/ember-localstorage-adapter/localstorage_adapter.js: -------------------------------------------------------------------------------- 1 | /*global Ember*/ 2 | /*global DS*/ 3 | 'use strict'; 4 | 5 | DS.LSAdapter = DS.Adapter.extend(Ember.Evented, { 6 | 7 | init: function () { 8 | this._loadData(); 9 | }, 10 | 11 | generateIdForRecord: function () { 12 | return Math.random().toString(32).slice(2).substr(0, 5); 13 | }, 14 | 15 | find: function (store, type, id) { 16 | var namespace = this._namespaceForType(type); 17 | return Ember.RSVP.resolve(Ember.copy(namespace.records[id])); 18 | }, 19 | 20 | findMany: function (store, type, ids) { 21 | var namespace = this._namespaceForType(type); 22 | var results = []; 23 | for (var i = 0; i < ids.length; i++) { 24 | results.push(Ember.copy(namespace.records[ids[i]])); 25 | } 26 | return Ember.RSVP.resolve(results); 27 | }, 28 | 29 | // Supports queries that look like this: 30 | // 31 | // { 32 | // : , 33 | // ... 34 | // } 35 | // 36 | // Every property added to the query is an "AND" query, not "OR" 37 | // 38 | // Example: 39 | // 40 | // match records with "complete: true" and the name "foo" or "bar" 41 | // 42 | // { complete: true, name: /foo|bar/ } 43 | findQuery: function (store, type, query, recordArray) { 44 | var namespace = this._namespaceForType(type); 45 | var results = this.query(namespace.records, query); 46 | return Ember.RSVP.resolve(results); 47 | }, 48 | 49 | query: function (records, query) { 50 | var results = []; 51 | var id, record, property, test, push; 52 | for (id in records) { 53 | record = records[id]; 54 | for (property in query) { 55 | test = query[property]; 56 | push = false; 57 | if (Object.prototype.toString.call(test) === '[object RegExp]') { 58 | push = test.test(record[property]); 59 | } else { 60 | push = record[property] === test; 61 | } 62 | } 63 | if (push) { 64 | results.push(record); 65 | } 66 | } 67 | return results; 68 | }, 69 | 70 | findAll: function (store, type) { 71 | var namespace = this._namespaceForType(type); 72 | var results = []; 73 | for (var id in namespace.records) { 74 | results.push(Ember.copy(namespace.records[id])); 75 | } 76 | return Ember.RSVP.resolve(results); 77 | }, 78 | 79 | createRecord: function (store, type, record) { 80 | var namespace = this._namespaceForType(type); 81 | this._addRecordToNamespace(namespace, record); 82 | this._saveData(); 83 | return Ember.RSVP.resolve(); 84 | }, 85 | 86 | updateRecord: function (store, type, record) { 87 | var namespace = this._namespaceForType(type); 88 | var id = record.get('id'); 89 | namespace.records[id] = record.toJSON({ includeId: true }); 90 | this._saveData(); 91 | return Ember.RSVP.resolve(); 92 | }, 93 | 94 | deleteRecord: function (store, type, record) { 95 | var namespace = this._namespaceForType(type); 96 | var id = record.get('id'); 97 | delete namespace.records[id]; 98 | this._saveData(); 99 | return Ember.RSVP.resolve(); 100 | }, 101 | 102 | // private 103 | 104 | _getNamespace: function () { 105 | return this.namespace || 'DS.LSAdapter'; 106 | }, 107 | 108 | _loadData: function () { 109 | var storage = localStorage.getItem(this._getNamespace()); 110 | this._data = storage ? JSON.parse(storage) : {}; 111 | }, 112 | 113 | _saveData: function () { 114 | localStorage.setItem(this._getNamespace(), JSON.stringify(this._data)); 115 | }, 116 | 117 | _namespaceForType: function (type) { 118 | var namespace = type.url || type.toString(); 119 | return this._data[namespace] || ( 120 | this._data[namespace] = {records: {}} 121 | ); 122 | }, 123 | 124 | _addRecordToNamespace: function (namespace, record) { 125 | var data = record.serialize({includeId: true}); 126 | namespace.records[data.id] = data; 127 | } 128 | }); 129 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/src/store.js: -------------------------------------------------------------------------------- 1 | /*jshint eqeqeq:false */ 2 | 3 | /** 4 | * Creates a new client side storage object and will create an empty 5 | * collection if no collection already exists. 6 | * 7 | * @param {string} name The name of our DB we want to use 8 | * @param {function} callback Our fake DB uses callbacks because in 9 | * real life you probably would be making AJAX calls 10 | */ 11 | export default class Store { 12 | constructor(name, callback) { 13 | this._dbName = name; 14 | 15 | if (!localStorage[name]) { 16 | const data = { 17 | todos: [] 18 | }; 19 | 20 | localStorage[name] = JSON.stringify(data); 21 | } 22 | 23 | if (callback) { 24 | callback.call(this, JSON.parse(localStorage[name])); 25 | } 26 | } 27 | 28 | /** 29 | * Finds items based on a query given as a JS object 30 | * 31 | * @param {object} query The query to match against (i.e. {foo: 'bar'}) 32 | * @param {function} callback The callback to fire when the query has 33 | * completed running 34 | * 35 | * @example 36 | * db.find({foo: 'bar', hello: 'world'}, function (data) { 37 | * // data will return any items that have foo: bar and 38 | * // hello: world in their properties 39 | * }) 40 | */ 41 | find(query, callback){ 42 | const todos = JSON.parse(localStorage[this._dbName]).todos; 43 | 44 | callback.call(this, todos.filter(todo => { 45 | for (let q in query) { 46 | if (query[q] !== todo[q]) { 47 | return false; 48 | } 49 | } 50 | return true; 51 | })); 52 | } 53 | 54 | /** 55 | * Will retrieve all data from the collection 56 | * 57 | * @param {function} callback The callback to fire upon retrieving data 58 | */ 59 | findAll(callback){ 60 | if (callback) { 61 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 62 | } 63 | } 64 | 65 | /** 66 | * Will save the given data to the DB. If no item exists it will create a new 67 | * item, otherwise it'll simply update an existing item's properties 68 | * 69 | * @param {object} updateData The data to save back into the DB 70 | * @param {function} callback The callback to fire after saving 71 | * @param {number} id An optional param to enter an ID of an item to update 72 | */ 73 | save(updateData, callback, id){ 74 | const data = JSON.parse(localStorage[this._dbName]); 75 | const todos = data.todos; 76 | const len = todos.length; 77 | 78 | // If an ID was actually given, find the item and update each property 79 | if (id) { 80 | for (let i = 0; i < len; i++) { 81 | if (todos[i].id === id) { 82 | for (let key in updateData) { 83 | todos[i][key] = updateData[key]; 84 | } 85 | break; 86 | } 87 | } 88 | 89 | localStorage[this._dbName] = JSON.stringify(data); 90 | 91 | if (callback) { 92 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 93 | } 94 | } else { 95 | // Generate an ID 96 | updateData.id = new Date().getTime(); 97 | 98 | todos.push(updateData); 99 | localStorage[this._dbName] = JSON.stringify(data); 100 | 101 | if (callback) { 102 | callback.call(this, [updateData]); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Will remove an item from the Store based on its ID 109 | * 110 | * @param {number} id The ID of the item you want to remove 111 | * @param {function} callback The callback to fire after saving 112 | */ 113 | remove(id, callback){ 114 | const data = JSON.parse(localStorage[this._dbName]); 115 | const todos = data.todos; 116 | const len = todos.length; 117 | 118 | for (let i = 0; i < todos.length; i++) { 119 | if (todos[i].id == id) { 120 | todos.splice(i, 1); 121 | break; 122 | } 123 | } 124 | 125 | localStorage[this._dbName] = JSON.stringify(data); 126 | 127 | if (callback) { 128 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 129 | } 130 | } 131 | 132 | /** 133 | * Will drop all storage and start fresh 134 | * 135 | * @param {function} callback The callback to fire after dropping the data 136 | */ 137 | drop(callback){ 138 | localStorage[this._dbName] = JSON.stringify({todos: []}); 139 | 140 | if (callback) { 141 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /todomvc/backbone/js/views/app-view.js: -------------------------------------------------------------------------------- 1 | /*global Backbone, jQuery, _, ENTER_KEY */ 2 | var app = app || {}; 3 | 4 | (function ($) { 5 | 'use strict'; 6 | 7 | // The Application 8 | // --------------- 9 | 10 | // Our overall **AppView** is the top-level piece of UI. 11 | app.AppView = Backbone.View.extend({ 12 | 13 | // Instead of generating a new element, bind to the existing skeleton of 14 | // the App already present in the HTML. 15 | el: '#todoapp', 16 | 17 | // Our template for the line of statistics at the bottom of the app. 18 | statsTemplate: _.template($('#stats-template').html()), 19 | 20 | // Delegated events for creating new items, and clearing completed ones. 21 | events: { 22 | 'keypress #new-todo': 'createOnEnter', 23 | 'click #clear-completed': 'clearCompleted', 24 | 'click #toggle-all': 'toggleAllComplete' 25 | }, 26 | 27 | // At initialization we bind to the relevant events on the `Todos` 28 | // collection, when items are added or changed. Kick things off by 29 | // loading any preexisting todos that might be saved in *localStorage*. 30 | initialize: function () { 31 | this.allCheckbox = this.$('#toggle-all')[0]; 32 | this.$input = this.$('#new-todo'); 33 | this.$footer = this.$('#footer'); 34 | this.$main = this.$('#main'); 35 | this.$list = $('#todo-list'); 36 | 37 | this.listenTo(app.todos, 'add', this.addOne); 38 | this.listenTo(app.todos, 'reset', this.addAll); 39 | this.listenTo(app.todos, 'change:completed', this.filterOne); 40 | this.listenTo(app.todos, 'filter', this.filterAll); 41 | this.listenTo(app.todos, 'all', this.render); 42 | 43 | // Suppresses 'add' events with {reset: true} and prevents the app view 44 | // from being re-rendered for every model. Only renders when the 'reset' 45 | // event is triggered at the end of the fetch. 46 | app.todos.fetch({reset: true}); 47 | }, 48 | 49 | // Re-rendering the App just means refreshing the statistics -- the rest 50 | // of the app doesn't change. 51 | render: function () { 52 | var completed = app.todos.completed().length; 53 | var remaining = app.todos.remaining().length; 54 | 55 | if (app.todos.length) { 56 | this.$main.show(); 57 | this.$footer.show(); 58 | 59 | this.$footer.html(this.statsTemplate({ 60 | completed: completed, 61 | remaining: remaining 62 | })); 63 | 64 | this.$('#filters li a') 65 | .removeClass('selected') 66 | .filter('[href="#/' + (app.TodoFilter || '') + '"]') 67 | .addClass('selected'); 68 | } else { 69 | this.$main.hide(); 70 | this.$footer.hide(); 71 | } 72 | 73 | this.allCheckbox.checked = !remaining; 74 | }, 75 | 76 | // Add a single todo item to the list by creating a view for it, and 77 | // appending its element to the `
        `. 78 | addOne: function (todo) { 79 | var view = new app.TodoView({ model: todo }); 80 | this.$list.append(view.render().el); 81 | }, 82 | 83 | // Add all items in the **Todos** collection at once. 84 | addAll: function () { 85 | this.$list.html(''); 86 | app.todos.each(this.addOne, this); 87 | }, 88 | 89 | filterOne: function (todo) { 90 | todo.trigger('visible'); 91 | }, 92 | 93 | filterAll: function () { 94 | app.todos.each(this.filterOne, this); 95 | }, 96 | 97 | // Generate the attributes for a new Todo item. 98 | newAttributes: function () { 99 | return { 100 | title: this.$input.val().trim(), 101 | order: app.todos.nextOrder(), 102 | completed: false 103 | }; 104 | }, 105 | 106 | // If you hit return in the main input field, create new **Todo** model, 107 | // persisting it to *localStorage*. 108 | createOnEnter: function (e) { 109 | if (e.which === ENTER_KEY && this.$input.val().trim()) { 110 | app.todos.create(this.newAttributes()); 111 | this.$input.val(''); 112 | } 113 | }, 114 | 115 | // Clear all completed todo items, destroying their models. 116 | clearCompleted: function () { 117 | _.invoke(app.todos.completed(), 'destroy'); 118 | return false; 119 | }, 120 | 121 | toggleAllComplete: function () { 122 | var completed = this.allCheckbox.checked; 123 | 124 | app.todos.each(function (todo) { 125 | todo.save({ 126 | 'completed': completed 127 | }); 128 | }); 129 | } 130 | }); 131 | })(jQuery); 132 | -------------------------------------------------------------------------------- /todomvc/backbone/js/views/todo-view.js: -------------------------------------------------------------------------------- 1 | /*global Backbone, jQuery, _, ENTER_KEY, ESC_KEY */ 2 | var app = app || {}; 3 | 4 | (function ($) { 5 | 'use strict'; 6 | 7 | // Todo Item View 8 | // -------------- 9 | 10 | // The DOM element for a todo item... 11 | app.TodoView = Backbone.View.extend({ 12 | //... is a list tag. 13 | tagName: 'li', 14 | 15 | // Cache the template function for a single item. 16 | template: _.template($('#item-template').html()), 17 | 18 | // The DOM events specific to an item. 19 | events: { 20 | 'click .toggle': 'toggleCompleted', 21 | 'dblclick label': 'edit', 22 | 'click .destroy': 'clear', 23 | 'keypress .edit': 'updateOnEnter', 24 | 'keydown .edit': 'revertOnEscape', 25 | 'blur .edit': 'close' 26 | }, 27 | 28 | // The TodoView listens for changes to its model, re-rendering. Since there's 29 | // a one-to-one correspondence between a **Todo** and a **TodoView** in this 30 | // app, we set a direct reference on the model for convenience. 31 | initialize: function () { 32 | this.listenTo(this.model, 'change', this.render); 33 | this.listenTo(this.model, 'destroy', this.remove); 34 | this.listenTo(this.model, 'visible', this.toggleVisible); 35 | }, 36 | 37 | // Re-render the titles of the todo item. 38 | render: function () { 39 | // Backbone LocalStorage is adding `id` attribute instantly after creating a model. 40 | // This causes our TodoView to render twice. Once after creating a model and once on `id` change. 41 | // We want to filter out the second redundant render, which is caused by this `id` change. 42 | // It's known Backbone LocalStorage bug, therefore we've to create a workaround. 43 | // https://github.com/tastejs/todomvc/issues/469 44 | if (this.model.changed.id !== undefined) { 45 | return; 46 | } 47 | 48 | this.$el.html(this.template(this.model.toJSON())); 49 | this.$el.toggleClass('completed', this.model.get('completed')); 50 | this.toggleVisible(); 51 | this.$input = this.$('.edit'); 52 | return this; 53 | }, 54 | 55 | toggleVisible: function () { 56 | this.$el.toggleClass('hidden', this.isHidden()); 57 | }, 58 | 59 | isHidden: function () { 60 | var isCompleted = this.model.get('completed'); 61 | return (// hidden cases only 62 | (!isCompleted && app.TodoFilter === 'completed') || 63 | (isCompleted && app.TodoFilter === 'active') 64 | ); 65 | }, 66 | 67 | // Toggle the `"completed"` state of the model. 68 | toggleCompleted: function () { 69 | this.model.toggle(); 70 | }, 71 | 72 | // Switch this view into `"editing"` mode, displaying the input field. 73 | edit: function () { 74 | this.$el.addClass('editing'); 75 | this.$input.focus(); 76 | }, 77 | 78 | // Close the `"editing"` mode, saving changes to the todo. 79 | close: function () { 80 | var value = this.$input.val(); 81 | var trimmedValue = value.trim(); 82 | 83 | // We don't want to handle blur events from an item that is no 84 | // longer being edited. Relying on the CSS class here has the 85 | // benefit of us not having to maintain state in the DOM and the 86 | // JavaScript logic. 87 | if (!this.$el.hasClass('editing')) { 88 | return; 89 | } 90 | 91 | if (trimmedValue) { 92 | this.model.save({ title: trimmedValue }); 93 | 94 | if (value !== trimmedValue) { 95 | // Model values changes consisting of whitespaces only are not causing change to be triggered 96 | // Therefore we've to compare untrimmed version with a trimmed one to chech whether anything changed 97 | // And if yes, we've to trigger change event ourselves 98 | this.model.trigger('change'); 99 | } 100 | } else { 101 | this.clear(); 102 | } 103 | 104 | this.$el.removeClass('editing'); 105 | }, 106 | 107 | // If you hit `enter`, we're through editing the item. 108 | updateOnEnter: function (e) { 109 | if (e.which === ENTER_KEY) { 110 | this.close(); 111 | } 112 | }, 113 | 114 | // If you're pressing `escape` we revert your change by simply leaving 115 | // the `editing` state. 116 | revertOnEscape: function (e) { 117 | if (e.which === ESC_KEY) { 118 | this.$el.removeClass('editing'); 119 | } 120 | }, 121 | 122 | // Remove the item, destroy the model from *localStorage* and delete its view. 123 | clear: function () { 124 | this.model.destroy(); 125 | } 126 | }); 127 | })(jQuery); 128 | -------------------------------------------------------------------------------- /todomvc/ractive/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ractive.js • TodoMVC 7 | 8 | 9 | 10 | 11 |
        12 | 17 | 18 | 19 | 72 | 73 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /todomvc/react/js/app.jsx: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | /*global React, Router, ReactDOM*/ 6 | var app = app || {}; 7 | 8 | (function () { 9 | 'use strict'; 10 | 11 | app.ALL_TODOS = 'all'; 12 | app.ACTIVE_TODOS = 'active'; 13 | app.COMPLETED_TODOS = 'completed'; 14 | var TodoFooter = app.TodoFooter; 15 | var TodoItem = app.TodoItem; 16 | 17 | var ENTER_KEY = 13; 18 | 19 | var TodoApp = React.createClass({ 20 | getInitialState: function () { 21 | return { 22 | nowShowing: app.ALL_TODOS, 23 | editing: null, 24 | newTodo: '' 25 | }; 26 | }, 27 | 28 | componentDidMount: function () { 29 | var setState = this.setState; 30 | var router = Router({ 31 | '/': setState.bind(this, {nowShowing: app.ALL_TODOS}), 32 | '/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}), 33 | '/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS}) 34 | }); 35 | router.init('/'); 36 | }, 37 | 38 | handleChange: function (event) { 39 | this.setState({newTodo: event.target.value}); 40 | }, 41 | 42 | handleNewTodoKeyDown: function (event) { 43 | if (event.keyCode !== ENTER_KEY) { 44 | return; 45 | } 46 | 47 | event.preventDefault(); 48 | 49 | var val = this.state.newTodo.trim(); 50 | 51 | if (val) { 52 | this.props.model.addTodo(val); 53 | this.setState({newTodo: ''}); 54 | } 55 | }, 56 | 57 | toggleAll: function (event) { 58 | var checked = event.target.checked; 59 | this.props.model.toggleAll(checked); 60 | }, 61 | 62 | toggle: function (todoToToggle) { 63 | this.props.model.toggle(todoToToggle); 64 | }, 65 | 66 | destroy: function (todo) { 67 | this.props.model.destroy(todo); 68 | }, 69 | 70 | edit: function (todo) { 71 | this.setState({editing: todo.id}); 72 | }, 73 | 74 | save: function (todoToSave, text) { 75 | this.props.model.save(todoToSave, text); 76 | this.setState({editing: null}); 77 | }, 78 | 79 | cancel: function () { 80 | this.setState({editing: null}); 81 | }, 82 | 83 | clearCompleted: function () { 84 | this.props.model.clearCompleted(); 85 | }, 86 | 87 | render: function () { 88 | var footer; 89 | var main; 90 | var todos = this.props.model.todos; 91 | 92 | var shownTodos = todos.filter(function (todo) { 93 | switch (this.state.nowShowing) { 94 | case app.ACTIVE_TODOS: 95 | return !todo.completed; 96 | case app.COMPLETED_TODOS: 97 | return todo.completed; 98 | default: 99 | return true; 100 | } 101 | }, this); 102 | 103 | var todoItems = shownTodos.map(function (todo) { 104 | return ( 105 | 115 | ); 116 | }, this); 117 | 118 | var activeTodoCount = todos.reduce(function (accum, todo) { 119 | return todo.completed ? accum : accum + 1; 120 | }, 0); 121 | 122 | var completedCount = todos.length - activeTodoCount; 123 | 124 | if (activeTodoCount || completedCount) { 125 | footer = 126 | ; 132 | } 133 | 134 | if (todos.length) { 135 | main = ( 136 |
        137 | 143 |
          144 | {todoItems} 145 |
        146 |
        147 | ); 148 | } 149 | 150 | return ( 151 |
        152 |
        153 |

        todos

        154 | 162 |
        163 | {main} 164 | {footer} 165 |
        166 | ); 167 | } 168 | }); 169 | 170 | var model = new app.TodoModel('react-todos'); 171 | 172 | function render() { 173 | ReactDOM.render( 174 | , 175 | document.getElementsByClassName('todoapp')[0] 176 | ); 177 | } 178 | 179 | model.subscribe(render); 180 | render(); 181 | })(); 182 | -------------------------------------------------------------------------------- /todomvc/react/js/app.js: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | /*global React, Router, ReactDOM*/ 6 | var app = app || {}; 7 | 8 | (function () { 9 | 'use strict'; 10 | 11 | app.ALL_TODOS = 'all'; 12 | app.ACTIVE_TODOS = 'active'; 13 | app.COMPLETED_TODOS = 'completed'; 14 | var TodoFooter = React.createFactory(app.TodoFooter); 15 | var TodoItem = React.createFactory(app.TodoItem); 16 | 17 | var ENTER_KEY = 13; 18 | 19 | var TodoApp = React.createClass({ 20 | getInitialState: function () { 21 | return { 22 | nowShowing: app.ALL_TODOS, 23 | editing: null, 24 | newTodo: '' 25 | }; 26 | }, 27 | 28 | componentDidMount: function () { 29 | var setState = this.setState; 30 | var router = Router({ 31 | '/': setState.bind(this, {nowShowing: app.ALL_TODOS}), 32 | '/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}), 33 | '/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS}) 34 | }); 35 | router.init('/'); 36 | }, 37 | 38 | handleChange: function (event) { 39 | this.setState({newTodo: event.target.value}); 40 | }, 41 | 42 | handleNewTodoKeyDown: function (event) { 43 | if (event.keyCode !== ENTER_KEY) { 44 | return; 45 | } 46 | 47 | event.preventDefault(); 48 | 49 | var val = this.state.newTodo.trim(); 50 | 51 | if (val) { 52 | this.props.model.addTodo(val); 53 | this.setState({newTodo: ''}); 54 | } 55 | }, 56 | 57 | toggleAll: function (event) { 58 | var checked = event.target.checked; 59 | this.props.model.toggleAll(checked); 60 | }, 61 | 62 | toggle: function (todoToToggle) { 63 | this.props.model.toggle(todoToToggle); 64 | }, 65 | 66 | destroy: function (todo) { 67 | this.props.model.destroy(todo); 68 | }, 69 | 70 | edit: function (todo) { 71 | this.setState({editing: todo.id}); 72 | }, 73 | 74 | save: function (todoToSave, text) { 75 | this.props.model.save(todoToSave, text); 76 | this.setState({editing: null}); 77 | }, 78 | 79 | cancel: function () { 80 | this.setState({editing: null}); 81 | }, 82 | 83 | clearCompleted: function () { 84 | this.props.model.clearCompleted(); 85 | }, 86 | 87 | render: function () { 88 | var footer; 89 | var main; 90 | var todos = this.props.model.todos; 91 | 92 | var shownTodos = todos.filter(function (todo) { 93 | switch (this.state.nowShowing) { 94 | case app.ACTIVE_TODOS: 95 | return !todo.completed; 96 | case app.COMPLETED_TODOS: 97 | return todo.completed; 98 | default: 99 | return true; 100 | } 101 | }, this); 102 | 103 | var todoItems = shownTodos.map(function (todo) { 104 | return ( 105 | TodoItem({ 106 | key: todo.id, 107 | todo: todo, 108 | onToggle: this.toggle.bind(this, todo), 109 | onDestroy: this.destroy.bind(this, todo), 110 | onEdit: this.edit.bind(this, todo), 111 | editing: this.state.editing === todo.id, 112 | onSave: this.save.bind(this, todo), 113 | onCancel: this.cancel} 114 | ) 115 | ); 116 | }, this); 117 | 118 | var activeTodoCount = todos.reduce(function (accum, todo) { 119 | return todo.completed ? accum : accum + 1; 120 | }, 0); 121 | 122 | var completedCount = todos.length - activeTodoCount; 123 | 124 | if (activeTodoCount || completedCount) { 125 | footer = 126 | TodoFooter({ 127 | count: activeTodoCount, 128 | completedCount: completedCount, 129 | nowShowing: this.state.nowShowing, 130 | onClearCompleted: this.clearCompleted} 131 | ); 132 | } 133 | 134 | if (todos.length) { 135 | main = ( 136 | React.createElement('section', {className: "main"}, [ 137 | React.createElement('input', { 138 | className: "toggle-all", 139 | type: "checkbox", 140 | onChange: this.toggleAll, 141 | checked: activeTodoCount === 0} 142 | ), 143 | React.createElement('ul', {className: "todo-list"}, [ 144 | todoItems 145 | ]) 146 | ]) 147 | ); 148 | } 149 | 150 | return ( 151 | React.createElement('div', null, [ 152 | React.createElement('header', {className: "header"}, [ 153 | React.createElement('h1', null, ["todos"]), 154 | React.createElement('input', { 155 | className: "new-todo", 156 | placeholder: "What needs to be done?", 157 | value: this.state.newTodo, 158 | onKeyDown: this.handleNewTodoKeyDown, 159 | onChange: this.handleChange, 160 | autoFocus: true} 161 | ) 162 | ]), 163 | main, 164 | footer 165 | ]) 166 | ); 167 | } 168 | }); 169 | 170 | var model = new app.TodoModel('react-todos'); 171 | 172 | function render() { 173 | ReactDOM.render( 174 | React.createFactory(TodoApp)({model: model}), 175 | document.getElementsByClassName('todoapp')[0] 176 | ); 177 | } 178 | 179 | window.Utils = app.Utils 180 | model.subscribe(render); 181 | render(); 182 | })(); 183 | -------------------------------------------------------------------------------- /todomvc/ractive/js/app.js: -------------------------------------------------------------------------------- 1 | /*global window, Ractive */ 2 | (function (window, Ractive) { 3 | 'use strict'; 4 | 5 | // Create some filter functions, which we'll need later 6 | var filters = { 7 | completed: function (item) { return item.completed; }, 8 | active: function (item) { return !item.completed; } 9 | }; 10 | 11 | // The keycode for the 'enter' and 'escape' keys 12 | var ENTER_KEY = 13; 13 | var ESCAPE_KEY = 27; 14 | 15 | // Create our Ractive instance 16 | var todoList = new Ractive({ 17 | // Specify a target element - an ID, a CSS selector, or the element itself 18 | el: 'todoapp', 19 | 20 | // Specify a template, or the ID of a script tag containing the template 21 | template: '#main', 22 | 23 | // This is our viewmodel - as well as our list of tasks (which gets added 24 | // later from localStorage - see persistence.js), it includes helper 25 | // functions and computed values 26 | data: { 27 | filter: function (item) { 28 | // Because we're doing `this.get('currentFilter')`, Ractive understands 29 | // that this function needs to be re-executed reactively when the value of 30 | // `currentFilter` changes 31 | var currentFilter = this.get('currentFilter'); 32 | 33 | if (currentFilter === 'all') { 34 | return true; 35 | } 36 | 37 | return filters[currentFilter](item); 38 | }, 39 | 40 | // completedTasks() and activeTasks() are computed values, that will update 41 | // our app view reactively whenever `items` changes (including changes to 42 | // child properties like `items[1].completed`) 43 | completedTasks: function () { 44 | return this.get('items').filter(filters.completed); 45 | }, 46 | 47 | activeTasks: function () { 48 | return this.get('items').filter(filters.active); 49 | }, 50 | 51 | // By default, show all tasks. This value changes when the route changes 52 | // (see routes.js) 53 | currentFilter: 'all' 54 | }, 55 | 56 | // We can define custom events. Here, we're defining an `enter` event, 57 | // and an `escape` event, which fire when the user hits those keys while 58 | // an input is focused: 59 | // 60 | // 61 | events: (function () { 62 | var makeCustomEvent = function (keyCode) { 63 | return function (node, fire) { 64 | var keydownHandler = function (event) { 65 | if (event.which === keyCode) { 66 | fire({ 67 | node: node, 68 | original: event 69 | }); 70 | } 71 | }; 72 | 73 | node.addEventListener('keydown', keydownHandler, false); 74 | 75 | return { 76 | teardown: function () { 77 | node.removeEventListener('keydown', keydownHandler, false); 78 | } 79 | }; 80 | }; 81 | }; 82 | 83 | return { 84 | enter: makeCustomEvent(ENTER_KEY), 85 | escape: makeCustomEvent(ESCAPE_KEY) 86 | }; 87 | }()) 88 | }); 89 | 90 | 91 | // Here, we're defining how to respond to user interactions. Unlike many 92 | // libraries, where you use CSS selectors to dictate what event corresponds 93 | // to what action, in Ractive the 'meaning' of the event is baked into the 94 | // template itself (e.g. ). 95 | todoList.on({ 96 | 97 | // Removing a todo is as straightforward as splicing it out of the array - 98 | // Ractive intercepts calls to array mutator methods and updates the view 99 | // accordingly. The DOM is updated in the most efficient manner possible. 100 | remove: function (event, index) { 101 | this.get('items').splice(index, 1); 102 | }, 103 | 104 | // The `event` object contains useful properties for (e.g.) retrieving 105 | // data from the DOM 106 | newTodo: function (event) { 107 | var description = event.node.value.trim(); 108 | 109 | if (!description) { 110 | return; 111 | } 112 | 113 | this.get('items').push({ 114 | description: description, 115 | completed: false 116 | }); 117 | 118 | event.node.value = ''; 119 | }, 120 | 121 | edit: function (event) { 122 | this.set(event.keypath + '.editing', true); 123 | this.nodes.edit.value = event.context.description; 124 | }, 125 | 126 | submit: function (event) { 127 | var description = event.node.value.trim(); 128 | 129 | if (!description) { 130 | this.get('items').splice(event.index.i, 1); 131 | return; 132 | } 133 | 134 | this.set(event.keypath + '.description', description); 135 | this.set(event.keypath + '.editing', false); 136 | }, 137 | 138 | cancel: function (event) { 139 | this.set(event.keypath + '.editing', false); 140 | }, 141 | 142 | clearCompleted: function () { 143 | var items = this.get('items'); 144 | var i = items.length; 145 | 146 | while (i--) { 147 | if (items[i].completed) { 148 | items.splice(i, 1); 149 | } 150 | } 151 | }, 152 | 153 | toggleAll: function (event) { 154 | var i = this.get('items').length; 155 | var completed = event.node.checked; 156 | var changeHash = {}; 157 | 158 | while (i--) { 159 | changeHash['items[' + i + '].completed'] = completed; 160 | } 161 | 162 | this.set(changeHash); 163 | } 164 | }); 165 | 166 | window.todoList = todoList; 167 | 168 | })(window, Ractive); 169 | -------------------------------------------------------------------------------- /todomvc/mercury/render.js: -------------------------------------------------------------------------------- 1 | var mercury = require("../../index.js") 2 | var h = require("../../index.js").h 3 | 4 | var doMutableFocus = require("./lib/do-mutable-focus.js") 5 | 6 | var ESCAPE = 27 7 | var footer = infoFooter() 8 | 9 | module.exports = render 10 | 11 | function render(state) { 12 | return h(".todomvc-wrapper", [ 13 | h("link", { 14 | rel: "stylesheet", 15 | href: "https://rawgithub.com/raynos/mercury/master/examples/todomvc/style.css" 16 | }), 17 | h("section#todoapp.todoapp", [ 18 | mercury.partial(header, state.field, state.events), 19 | mainSection(state.todos, state.route, state.events), 20 | mercury.partial(statsSection, state.todos, state.route, state.events) 21 | ]), 22 | footer 23 | ]) 24 | } 25 | 26 | function header(field, events) { 27 | return h("header#header.header", { 28 | "data-event": [ 29 | mercury.changeEvent(events.setTodoField), 30 | mercury.submitEvent(events.add) 31 | ] 32 | }, [ 33 | h("h1", "Todos"), 34 | h("input#new-todo.new-todo", { 35 | placeholder: "What needs to be done?", 36 | autofocus: true, 37 | value: field.text, 38 | name: "newTodo" 39 | }) 40 | ]) 41 | } 42 | 43 | function mainSection(todos, route, events) { 44 | var allCompleted = todos.every(function (todo) { 45 | return todo.completed 46 | }) 47 | var visibleTodos = todos.filter(function (todo) { 48 | return route === "completed" && todo.completed || 49 | route === "active" && !todo.completed || 50 | route === "all" 51 | }) 52 | 53 | return h("section#main.main", { hidden: !todos.length }, [ 54 | h("input#toggle-all.toggle-all", { 55 | type: "checkbox", 56 | name: "toggle", 57 | checked: allCompleted, 58 | "data-change": mercury.valueEvent(events.toggleAll) 59 | }), 60 | h("label", { htmlFor: "toggle-all" }, "Mark all as complete"), 61 | h("ul#todo-list.todolist", visibleTodos.map(function (todo) { 62 | return todoItem(todo, events) 63 | })) 64 | ]) 65 | } 66 | 67 | function todoItem(todo, events) { 68 | var className = (todo.completed ? "completed " : "") + 69 | (todo.editing ? "editing" : "") 70 | 71 | return h("li", { className: className, key: todo.id }, [ 72 | h(".view", [ 73 | h("input.toggle", { 74 | type: "checkbox", 75 | checked: todo.completed, 76 | "data-change": mercury.event(events.toggle, { 77 | id: todo.id, 78 | completed: !todo.completed 79 | }) 80 | }), 81 | h("label", { 82 | "data-dblclick": mercury.event(events.startEdit, { id: todo.id }) 83 | }, todo.title), 84 | h("button.destroy", { 85 | "data-click": mercury.event(events.destroy, { id: todo.id }) 86 | }) 87 | ]), 88 | h("input.edit", { 89 | value: todo.title, 90 | name: "title", 91 | // when we need an RPC invocation we add a 92 | // custom mutable operation into the tree to be 93 | // invoked at patch time 94 | "data-focus": todo.editing ? doMutableFocus() : null, 95 | "data-keydown": mercury.keyEvent(events.cancelEdit, ESCAPE, { id: todo.id }), 96 | "data-event": mercury.submitEvent(events.finishEdit, { id: todo.id }), 97 | "data-blur": mercury.valueEvent(events.finishEdit, { id: todo.id }) 98 | }) 99 | ]) 100 | } 101 | 102 | function statsSection(todos, route, events) { 103 | var todosLeft = todos.filter(function (todo) { 104 | return !todo.completed 105 | }).length 106 | var todosCompleted = todos.length - todosLeft 107 | 108 | return h("footer#footer.footer", { hidden: !todos.length }, [ 109 | h("span#todo-count.todo-count", [ 110 | h("strong", String(todosLeft)), 111 | todosLeft === 1 ? " item" : " items", 112 | " left" 113 | ]), 114 | h("ul#filters.filters", [ 115 | link("#/", "All", route === "all"), 116 | link("#/active", "Active", route === "active"), 117 | link("#/completed", "Completed", route === "completed") 118 | ]), 119 | h("button.clear-completed#clear-completed", { 120 | hidden: todosCompleted === 0, 121 | "data-click": mercury.event(events.clearCompleted) 122 | }, "Clear completed (" + String(todosCompleted) + ")") 123 | ]) 124 | } 125 | 126 | function link(uri, text, isSelected) { 127 | return h("li", [ 128 | h("a", { className: isSelected ? "selected" : "", href: uri }, text) 129 | ]) 130 | } 131 | 132 | function infoFooter() { 133 | return h("footer#info.info", [ 134 | h("p", "Double-click to edit a todo"), 135 | h("p", [ 136 | "Written by ", 137 | h("a", { href: "https://github.com/Raynos" }, "Raynos") 138 | ]), 139 | h("p", [ 140 | "Part of ", 141 | h("a", { href: "http://todomvc.com" }, "TodoMVC") 142 | ]) 143 | ]) 144 | } 145 | -------------------------------------------------------------------------------- /todomvc/vanilla-es6/dist/store.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | 9 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 10 | 11 | /*jshint eqeqeq:false */ 12 | 13 | /** 14 | * Creates a new client side storage object and will create an empty 15 | * collection if no collection already exists. 16 | * 17 | * @param {string} name The name of our DB we want to use 18 | * @param {function} callback Our fake DB uses callbacks because in 19 | * real life you probably would be making AJAX calls 20 | */ 21 | 22 | var Store = function () { 23 | function Store(name, callback) { 24 | _classCallCheck(this, Store); 25 | 26 | this._dbName = name; 27 | 28 | if (!localStorage[name]) { 29 | var data = { 30 | todos: [] 31 | }; 32 | 33 | localStorage[name] = JSON.stringify(data); 34 | } 35 | 36 | if (callback) { 37 | callback.call(this, JSON.parse(localStorage[name])); 38 | } 39 | } 40 | 41 | /** 42 | * Finds items based on a query given as a JS object 43 | * 44 | * @param {object} query The query to match against (i.e. {foo: 'bar'}) 45 | * @param {function} callback The callback to fire when the query has 46 | * completed running 47 | * 48 | * @example 49 | * db.find({foo: 'bar', hello: 'world'}, function (data) { 50 | * // data will return any items that have foo: bar and 51 | * // hello: world in their properties 52 | * }) 53 | */ 54 | 55 | _createClass(Store, [{ 56 | key: "find", 57 | value: function find(query, callback) { 58 | var todos = JSON.parse(localStorage[this._dbName]).todos; 59 | 60 | callback.call(this, todos.filter(function (todo) { 61 | for (var q in query) { 62 | if (query[q] !== todo[q]) { 63 | return false; 64 | } 65 | } 66 | return true; 67 | })); 68 | } 69 | 70 | /** 71 | * Will retrieve all data from the collection 72 | * 73 | * @param {function} callback The callback to fire upon retrieving data 74 | */ 75 | 76 | }, { 77 | key: "findAll", 78 | value: function findAll(callback) { 79 | if (callback) { 80 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 81 | } 82 | } 83 | 84 | /** 85 | * Will save the given data to the DB. If no item exists it will create a new 86 | * item, otherwise it'll simply update an existing item's properties 87 | * 88 | * @param {object} updateData The data to save back into the DB 89 | * @param {function} callback The callback to fire after saving 90 | * @param {number} id An optional param to enter an ID of an item to update 91 | */ 92 | 93 | }, { 94 | key: "save", 95 | value: function save(updateData, callback, id) { 96 | var data = JSON.parse(localStorage[this._dbName]); 97 | var todos = data.todos; 98 | var len = todos.length; 99 | 100 | // If an ID was actually given, find the item and update each property 101 | if (id) { 102 | for (var i = 0; i < len; i++) { 103 | if (todos[i].id === id) { 104 | for (var key in updateData) { 105 | todos[i][key] = updateData[key]; 106 | } 107 | break; 108 | } 109 | } 110 | 111 | localStorage[this._dbName] = JSON.stringify(data); 112 | 113 | if (callback) { 114 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 115 | } 116 | } else { 117 | // Generate an ID 118 | updateData.id = new Date().getTime(); 119 | 120 | todos.push(updateData); 121 | localStorage[this._dbName] = JSON.stringify(data); 122 | 123 | if (callback) { 124 | callback.call(this, [updateData]); 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * Will remove an item from the Store based on its ID 131 | * 132 | * @param {number} id The ID of the item you want to remove 133 | * @param {function} callback The callback to fire after saving 134 | */ 135 | 136 | }, { 137 | key: "remove", 138 | value: function remove(id, callback) { 139 | var data = JSON.parse(localStorage[this._dbName]); 140 | var todos = data.todos; 141 | var len = todos.length; 142 | 143 | for (var i = 0; i < todos.length; i++) { 144 | if (todos[i].id == id) { 145 | todos.splice(i, 1); 146 | break; 147 | } 148 | } 149 | 150 | localStorage[this._dbName] = JSON.stringify(data); 151 | 152 | if (callback) { 153 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 154 | } 155 | } 156 | 157 | /** 158 | * Will drop all storage and start fresh 159 | * 160 | * @param {function} callback The callback to fire after dropping the data 161 | */ 162 | 163 | }, { 164 | key: "drop", 165 | value: function drop(callback) { 166 | localStorage[this._dbName] = JSON.stringify({ todos: [] }); 167 | 168 | if (callback) { 169 | callback.call(this, JSON.parse(localStorage[this._dbName]).todos); 170 | } 171 | } 172 | }]); 173 | 174 | return Store; 175 | }(); 176 | 177 | exports.default = Store; --------------------------------------------------------------------------------