Add todoapp benchmark
This commit is contained in:
parent
479db8c2cc
commit
02e7b40c07
218 changed files with 63168 additions and 0 deletions
2
bench/.gitignore
vendored
Normal file
2
bench/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
elm-stuff
|
||||||
|
node_modules
|
||||||
27
bench/LICENCE
Normal file
27
bench/LICENCE
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2016, Evan Czaplicki
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the {organization} nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
15
bench/elm-package.json
Normal file
15
bench/elm-package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"summary": "helpful summary of your project, less than 80 characters",
|
||||||
|
"repository": "https://github.com/user/project.git",
|
||||||
|
"license": "BSD3",
|
||||||
|
"source-directories": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"exposed-modules": [],
|
||||||
|
"dependencies": {
|
||||||
|
"elm-lang/core": "4.0.5 <= v < 5.0.0",
|
||||||
|
"elm-lang/html": "1.1.0 <= v < 2.0.0"
|
||||||
|
},
|
||||||
|
"elm-version": "0.17.1 <= v < 0.18.0"
|
||||||
|
}
|
||||||
1
bench/implementations/angular-1.5.8-optimized/build.min.js
vendored
Executable file
1
bench/implementations/angular-1.5.8-optimized/build.min.js
vendored
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
(function(){"use strict";angular.module("todoCtrl",[]).controller("TodoCtrl",function TodoCtrl($scope,$location){var TC=this;var todos=TC.todos=[];TC.ESCAPE_KEY=27;TC.editedTodo={};TC.updateRemainingCount=function(){TC.remainingCount=todos.filter(function(todo){return!todo.completed}).length;TC.allChecked=TC.remainingCount===0};function resetTodo(){TC.newTodo={title:"",completed:false}}resetTodo();if($location.path()===""){$location.path("/")}TC.location=$location;$scope.$watch("TC.location.path()",function(path){TC.statusFilter={"/active":{completed:false},"/completed":{completed:true}}[path]});TC.addTodo=function(){var newTitle=TC.newTodo.title=TC.newTodo.title.trim();if(newTitle.length===0){return}todos.push(TC.newTodo);resetTodo();TC.updateRemainingCount()};TC.editTodo=function(todo){TC.editedTodo=todo;TC.originalTodo=angular.copy(todo)};TC.doneEditing=function(todo,index){TC.editedTodo={};todo.title=todo.title.trim();if(!todo.title){TC.removeTodo(index)}TC.updateRemainingCount()};TC.revertEditing=function(index){TC.editedTodo={};todos[index]=TC.originalTodo;TC.updateRemainingCount()};TC.removeTodo=function(index){todos.splice(index,1);TC.updateRemainingCount()};TC.clearCompletedTodos=function(){TC.todos=todos=todos.filter(function(val){return!val.completed});TC.updateRemainingCount()};TC.markAll=function(completed){todos.forEach(function(todo){todo.completed=completed});TC.updateRemainingCount()}});angular.module("todoFocus",[]).directive("todoFocus",function($timeout){return function(scope,elem,attrs){scope.$watch(attrs.todoFocus,function(newVal){if(newVal){$timeout(function(){elem[0].focus()},0,false)}})}});angular.module("todomvc",["todoCtrl","todoFocus"])})();
|
||||||
77
bench/implementations/angular-1.5.8-optimized/index.html
Executable file
77
bench/implementations/angular-1.5.8-optimized/index.html
Executable file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" ng-app="todomvc" data-framework="angularjs">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>AngularJS • TodoMVC</title>
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
|
||||||
|
<style>
|
||||||
|
[ng-cloak] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<section class="todoapp" ng-controller="TodoCtrl as TC">
|
||||||
|
<header class="header">
|
||||||
|
<h1>todos</h1>
|
||||||
|
<form id="todo-form">
|
||||||
|
<input class="new-todo" placeholder="What needs to be done?" ng-model="TC.newTodo.title" autofocus ng-keydown="($event.keyCode === 13) && TC.addTodo()">
|
||||||
|
</form>
|
||||||
|
</header>
|
||||||
|
<section class="main" ng-show="TC.todos.length" ng-cloak>
|
||||||
|
<input class="toggle-all" type="checkbox" ng-model="TC.allChecked" ng-click="TC.markAll(TC.allChecked)">
|
||||||
|
<label for="toggle-all">Mark all as complete</label>
|
||||||
|
<ul class="todo-list">
|
||||||
|
<li ng-repeat="todo in TC.todos | filter:TC.statusFilter" ng-class="{completed: todo.completed, editing: todo === TC.editedTodo}">
|
||||||
|
<div class="view">
|
||||||
|
<input class="toggle" type="checkbox" ng-model="todo.completed" ng-change="TC.updateAndStore()">
|
||||||
|
<label ng-dblclick="TC.editTodo(todo)">{{todo.title}}</label>
|
||||||
|
<button class="destroy" ng-click="TC.removeTodo($index)"></button>
|
||||||
|
</div>
|
||||||
|
<form ng-submit="TC.doneEditing(todo, $index)">
|
||||||
|
<input class="edit" ng-trim="false" ng-model="todo.title" ng-blur="TC.doneEditing(todo, $index)" ng-keydown="($event.keyCode === TC.ESCAPE_KEY) && TC.revertEditing($index)" todo-focus="todo === TC.editedTodo">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<footer class="footer" ng-show="TC.todos.length" ng-cloak>
|
||||||
|
<span class="todo-count"><strong>{{TC.remainingCount}}</strong>
|
||||||
|
<ng-pluralize count="TC.remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize>
|
||||||
|
</span>
|
||||||
|
<ul class="filters">
|
||||||
|
<li>
|
||||||
|
<a ng-class="{selected: TC.location.path() == '/'} " href="#/">All</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a ng-class="{selected: TC.location.path() == '/active'}" href="#/active">Active</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a ng-class="{selected: TC.location.path() == '/completed'}" href="#/completed">Completed</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button class="clear-completed" ng-click="TC.clearCompletedTodos()" ng-show="TC.remainingCount < TC.todos.length">Clear completed</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
<footer class="info">
|
||||||
|
<p>Double-click to edit a todo</p>
|
||||||
|
<p>Credits:
|
||||||
|
<a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>,
|
||||||
|
<a href="http://ericbidelman.com">Eric Bidelman</a>,
|
||||||
|
<a href="http://jacobmumm.com">Jacob Mumm</a> and
|
||||||
|
<a href="http://blog.igorminar.com">Igor Minar</a>
|
||||||
|
</p>
|
||||||
|
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
window.requestAnimationFrame = window.webkitRequestAnimationFrame = function(callback) {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="node_modules/angular/angular.js"></script>
|
||||||
|
<script src="build.min.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
118
bench/implementations/angular-1.5.8-optimized/js/app.js
Executable file
118
bench/implementations/angular-1.5.8-optimized/js/app.js
Executable file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/* jshint undef: true, unused: true */
|
||||||
|
/*global angular */
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('todoCtrl', [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main controller for the app. The controller:
|
||||||
|
* - retrieves and persists the model via the todoStorage service
|
||||||
|
* - exposes the model to the template and provides event handlers
|
||||||
|
*/
|
||||||
|
.controller('TodoCtrl', function TodoCtrl($scope, $location) {
|
||||||
|
var TC = this;
|
||||||
|
var todos = TC.todos = [];
|
||||||
|
|
||||||
|
TC.ESCAPE_KEY = 27;
|
||||||
|
TC.editedTodo = {};
|
||||||
|
|
||||||
|
TC.updateRemainingCount = function () {
|
||||||
|
TC.remainingCount = todos.filter(function (todo) { return !todo.completed; }).length;
|
||||||
|
TC.allChecked = (TC.remainingCount === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTodo() {
|
||||||
|
TC.newTodo = {title: '', completed: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTodo();
|
||||||
|
|
||||||
|
if ($location.path() === '') {
|
||||||
|
$location.path('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
TC.location = $location;
|
||||||
|
|
||||||
|
$scope.$watch('TC.location.path()', function (path) {
|
||||||
|
TC.statusFilter = { '/active': {completed: false}, '/completed': {completed: true} }[path];
|
||||||
|
});
|
||||||
|
|
||||||
|
TC.addTodo = function () {
|
||||||
|
var newTitle = TC.newTodo.title = TC.newTodo.title.trim();
|
||||||
|
if (newTitle.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
todos.push(TC.newTodo);
|
||||||
|
resetTodo();
|
||||||
|
TC.updateRemainingCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.editTodo = function (todo) {
|
||||||
|
TC.editedTodo = todo;
|
||||||
|
|
||||||
|
// Clone the original todo to restore it on demand.
|
||||||
|
TC.originalTodo = angular.copy(todo);
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.doneEditing = function (todo, index) {
|
||||||
|
TC.editedTodo = {};
|
||||||
|
todo.title = todo.title.trim();
|
||||||
|
|
||||||
|
if (!todo.title) {
|
||||||
|
TC.removeTodo(index);
|
||||||
|
}
|
||||||
|
TC.updateRemainingCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.revertEditing = function (index) {
|
||||||
|
TC.editedTodo = {};
|
||||||
|
todos[index] = TC.originalTodo;
|
||||||
|
TC.updateRemainingCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.removeTodo = function (index) {
|
||||||
|
todos.splice(index, 1);
|
||||||
|
TC.updateRemainingCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.clearCompletedTodos = function () {
|
||||||
|
TC.todos = todos = todos.filter(function (val) {
|
||||||
|
return !val.completed;
|
||||||
|
});
|
||||||
|
TC.updateRemainingCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.markAll = function (completed) {
|
||||||
|
todos.forEach(function (todo) {
|
||||||
|
todo.completed = completed;
|
||||||
|
});
|
||||||
|
TC.updateRemainingCount();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('todoFocus', [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true
|
||||||
|
*/
|
||||||
|
.directive('todoFocus', function ($timeout) {
|
||||||
|
return function (scope, elem, attrs) {
|
||||||
|
scope.$watch(attrs.todoFocus, function (newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
$timeout(function () {
|
||||||
|
elem[0].focus();
|
||||||
|
}, 0, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* The main TodoMVC app module that pulls all dependency modules declared in same named files
|
||||||
|
*
|
||||||
|
* @type {angular.Module}
|
||||||
|
*/
|
||||||
|
angular.module('todomvc', ['todoCtrl', 'todoFocus']);
|
||||||
|
})();
|
||||||
14
bench/implementations/angular-1.5.8-optimized/package.json
Normal file
14
bench/implementations/angular-1.5.8-optimized/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"make": "uglifyjs js/app.js -o build.min.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"angular": "^1.5.8",
|
||||||
|
"todomvc-app-css": "^2.0.6",
|
||||||
|
"todomvc-common": "^1.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"uglify-js": "^2.6.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
bench/implementations/angular-1.5.8-optimized/readme.md
Executable file
25
bench/implementations/angular-1.5.8-optimized/readme.md
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Angular TodoMVC for Benchmarking
|
||||||
|
|
||||||
|
This is an update of the
|
||||||
|
_[Angular TodoMVC example](https://github.com/tastejs/todomvc/tree/gh-pages/examples/angularjs-perf)_
|
||||||
|
with the following changes:
|
||||||
|
|
||||||
|
* Upgrade to Angular 1.5.7
|
||||||
|
* Update todomvc-app-css to 2.0.6 to match other examples in this benchmark
|
||||||
|
* Remove localStorage functionality - not relevant to measuring render performance
|
||||||
|
* Concat JS into one file
|
||||||
|
* Minify with uglify
|
||||||
|
|
||||||
|
and additional performance optimizations:
|
||||||
|
|
||||||
|
#### Removed the deep $watch in favor of direct function calls
|
||||||
|
The original example uses $scope.$watch with object equality enabled to track changes to the array of todos and perform actions when changes are detected. This is not a good practice as it causes a deep array comparison to happen every digest cycle, which can significantly degrade performance when working with large collections. Instead we can perform the required actions directly whenever todos are modified, since we control all points of possible modification.
|
||||||
|
|
||||||
|
#### Removed "track by $index" from the ng-repeat directive
|
||||||
|
The original example uses the ng-repeat directive with a "track by $index" clause to display the list of todos. _[Refer to section "Tracking and Duplicates" here for information on using "track by".](https://code.angularjs.org/1.5.7/docs/api/ng/directive/ngRepeat)_ In this particular case it is not necessary to use it, as long as care is taken not to persist the "$$hashKey" property added by Angular to local storage, which would cause duplicates to appear when the collection is loaded and new items are added, triggering an exception. The version without the "track by" clause could reasonably be written by someone implementing this from scratch and demonstrates better performance.
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
1. `npm install`
|
||||||
|
2. `npm run make`
|
||||||
|
3. Open a local server (e.g. with `npm install -g http-server`) and open index.html
|
||||||
1
bench/implementations/angular-1.5.8/build.min.js
vendored
Normal file
1
bench/implementations/angular-1.5.8/build.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(function(){"use strict";angular.module("todoCtrl",[]).controller("TodoCtrl",function TodoCtrl($scope,$location){var TC=this;var todos=TC.todos=[];TC.ESCAPE_KEY=27;TC.editedTodo={};function resetTodo(){TC.newTodo={title:"",completed:false}}resetTodo();if($location.path()===""){$location.path("/")}TC.location=$location;$scope.$watch("TC.location.path()",function(path){TC.statusFilter={"/active":{completed:false},"/completed":{completed:true}}[path]});$scope.$watch("TC.todos",function(){TC.remainingCount=todos.filter(function(todo){return!todo.completed}).length;TC.allChecked=TC.remainingCount===0},true);TC.addTodo=function(){var newTitle=TC.newTodo.title=TC.newTodo.title.trim();if(newTitle.length===0){return}todos.push(TC.newTodo);resetTodo()};TC.editTodo=function(todo){TC.editedTodo=todo;TC.originalTodo=angular.copy(todo)};TC.doneEditing=function(todo,index){TC.editedTodo={};todo.title=todo.title.trim();if(!todo.title){TC.removeTodo(index)}};TC.revertEditing=function(index){TC.editedTodo={};todos[index]=TC.originalTodo};TC.removeTodo=function(index){todos.splice(index,1)};TC.clearCompletedTodos=function(){TC.todos=todos=todos.filter(function(val){return!val.completed})};TC.markAll=function(completed){todos.forEach(function(todo){todo.completed=completed})}});angular.module("todoFocus",[]).directive("todoFocus",function($timeout){return function(scope,elem,attrs){scope.$watch(attrs.todoFocus,function(newVal){if(newVal){$timeout(function(){elem[0].focus()},0,false)}})}});angular.module("todomvc",["todoCtrl","todoFocus"])})();
|
||||||
77
bench/implementations/angular-1.5.8/index.html
Normal file
77
bench/implementations/angular-1.5.8/index.html
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" ng-app="todomvc" data-framework="angularjs">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>AngularJS • TodoMVC</title>
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
|
||||||
|
<style>
|
||||||
|
[ng-cloak] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<section class="todoapp" ng-controller="TodoCtrl as TC">
|
||||||
|
<header class="header">
|
||||||
|
<h1>todos</h1>
|
||||||
|
<form id="todo-form">
|
||||||
|
<input class="new-todo" placeholder="What needs to be done?" ng-model="TC.newTodo.title" autofocus ng-keydown="($event.keyCode === 13) && TC.addTodo()">
|
||||||
|
</form>
|
||||||
|
</header>
|
||||||
|
<section class="main" ng-show="TC.todos.length" ng-cloak>
|
||||||
|
<input class="toggle-all" type="checkbox" ng-model="TC.allChecked" ng-click="TC.markAll(TC.allChecked)">
|
||||||
|
<label for="toggle-all">Mark all as complete</label>
|
||||||
|
<ul class="todo-list">
|
||||||
|
<li ng-repeat="todo in TC.todos | filter:TC.statusFilter track by $index" ng-class="{completed: todo.completed, editing: todo === TC.editedTodo}">
|
||||||
|
<div class="view">
|
||||||
|
<input class="toggle" type="checkbox" ng-model="todo.completed">
|
||||||
|
<label ng-dblclick="TC.editTodo(todo)">{{todo.title}}</label>
|
||||||
|
<button class="destroy" ng-click="TC.removeTodo($index)"></button>
|
||||||
|
</div>
|
||||||
|
<form ng-submit="TC.doneEditing(todo, $index)">
|
||||||
|
<input class="edit" ng-trim="false" ng-model="todo.title" ng-blur="TC.doneEditing(todo, $index)" ng-keydown="($event.keyCode === TC.ESCAPE_KEY) && TC.revertEditing($index)" todo-focus="todo === TC.editedTodo">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<footer class="footer" ng-show="TC.todos.length" ng-cloak>
|
||||||
|
<span class="todo-count"><strong>{{TC.remainingCount}}</strong>
|
||||||
|
<ng-pluralize count="TC.remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize>
|
||||||
|
</span>
|
||||||
|
<ul class="filters">
|
||||||
|
<li>
|
||||||
|
<a ng-class="{selected: TC.location.path() == '/'} " href="#/">All</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a ng-class="{selected: TC.location.path() == '/active'}" href="#/active">Active</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a ng-class="{selected: TC.location.path() == '/completed'}" href="#/completed">Completed</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button class="clear-completed" ng-click="TC.clearCompletedTodos()" ng-show="TC.remainingCount < TC.todos.length">Clear completed</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
<footer class="info">
|
||||||
|
<p>Double-click to edit a todo</p>
|
||||||
|
<p>Credits:
|
||||||
|
<a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>,
|
||||||
|
<a href="http://ericbidelman.com">Eric Bidelman</a>,
|
||||||
|
<a href="http://jacobmumm.com">Jacob Mumm</a> and
|
||||||
|
<a href="http://blog.igorminar.com">Igor Minar</a>
|
||||||
|
</p>
|
||||||
|
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
window.requestAnimationFrame = window.webkitRequestAnimationFrame = function(callback) {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="node_modules/angular/angular.js"></script>
|
||||||
|
<script src="build.min.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
113
bench/implementations/angular-1.5.8/js/app.js
Normal file
113
bench/implementations/angular-1.5.8/js/app.js
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
/* jshint undef: true, unused: true */
|
||||||
|
/*global angular */
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('todoCtrl', [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main controller for the app. The controller:
|
||||||
|
* - retrieves and persists the model via the todoStorage service
|
||||||
|
* - exposes the model to the template and provides event handlers
|
||||||
|
*/
|
||||||
|
.controller('TodoCtrl', function TodoCtrl($scope, $location) {
|
||||||
|
var TC = this;
|
||||||
|
var todos = TC.todos = [];
|
||||||
|
|
||||||
|
TC.ESCAPE_KEY = 27;
|
||||||
|
TC.editedTodo = {};
|
||||||
|
|
||||||
|
function resetTodo() {
|
||||||
|
TC.newTodo = {title: '', completed: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTodo();
|
||||||
|
|
||||||
|
if ($location.path() === '') {
|
||||||
|
$location.path('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
TC.location = $location;
|
||||||
|
|
||||||
|
$scope.$watch('TC.location.path()', function (path) {
|
||||||
|
TC.statusFilter = { '/active': {completed: false}, '/completed': {completed: true} }[path];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3rd argument `true` for deep object watching
|
||||||
|
$scope.$watch('TC.todos', function () {
|
||||||
|
TC.remainingCount = todos.filter(function (todo) { return !todo.completed; }).length;
|
||||||
|
TC.allChecked = (TC.remainingCount === 0);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
TC.addTodo = function () {
|
||||||
|
var newTitle = TC.newTodo.title = TC.newTodo.title.trim();
|
||||||
|
if (newTitle.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
todos.push(TC.newTodo);
|
||||||
|
resetTodo();
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.editTodo = function (todo) {
|
||||||
|
TC.editedTodo = todo;
|
||||||
|
|
||||||
|
// Clone the original todo to restore it on demand.
|
||||||
|
TC.originalTodo = angular.copy(todo);
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.doneEditing = function (todo, index) {
|
||||||
|
TC.editedTodo = {};
|
||||||
|
todo.title = todo.title.trim();
|
||||||
|
|
||||||
|
if (!todo.title) {
|
||||||
|
TC.removeTodo(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.revertEditing = function (index) {
|
||||||
|
TC.editedTodo = {};
|
||||||
|
todos[index] = TC.originalTodo;
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.removeTodo = function (index) {
|
||||||
|
todos.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.clearCompletedTodos = function () {
|
||||||
|
TC.todos = todos = todos.filter(function (val) {
|
||||||
|
return !val.completed;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TC.markAll = function (completed) {
|
||||||
|
todos.forEach(function (todo) {
|
||||||
|
todo.completed = completed;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
angular.module('todoFocus', [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true
|
||||||
|
*/
|
||||||
|
.directive('todoFocus', function ($timeout) {
|
||||||
|
return function (scope, elem, attrs) {
|
||||||
|
scope.$watch(attrs.todoFocus, function (newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
$timeout(function () {
|
||||||
|
elem[0].focus();
|
||||||
|
}, 0, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* The main TodoMVC app module that pulls all dependency modules declared in same named files
|
||||||
|
*
|
||||||
|
* @type {angular.Module}
|
||||||
|
*/
|
||||||
|
angular.module('todomvc', ['todoCtrl', 'todoFocus']);
|
||||||
|
})();
|
||||||
14
bench/implementations/angular-1.5.8/package.json
Normal file
14
bench/implementations/angular-1.5.8/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"make": "uglifyjs js/app.js -o build.min.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"angular": "^1.5.8",
|
||||||
|
"todomvc-app-css": "^2.0.6",
|
||||||
|
"todomvc-common": "^1.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"uglify-js": "^2.6.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
bench/implementations/angular-1.5.8/readme.md
Normal file
19
bench/implementations/angular-1.5.8/readme.md
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Angular TodoMVC for Benchmarking
|
||||||
|
|
||||||
|
This is an update of the
|
||||||
|
_[Angular TodoMVC example](https://github.com/tastejs/todomvc/tree/gh-pages/examples/angularjs-perf)_
|
||||||
|
with the following changes:
|
||||||
|
|
||||||
|
* Upgrade to Angular 1.5.7
|
||||||
|
* Update todomvc-app-css to 2.0.6 to match other examples in this benchmark
|
||||||
|
* Remove localStorage functionality - not relevant to measuring render performance
|
||||||
|
* Concat JS into one file
|
||||||
|
* Minify with uglify
|
||||||
|
|
||||||
|
This example is listed as "performance optimized" in the TodoMVC repository, but doesn't appear to make any performance improvements to the basic example other than excluding all features not mentioned in the app specification. Therefore, it should count as having no optimizations for the purposes of this benchmark.
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
1. `npm install`
|
||||||
|
2. `npm run make`
|
||||||
|
3. Open a local server (e.g. with `npm install -g http-server`) and open index.html
|
||||||
28
bench/implementations/angular-2-optimized/.gitignore
vendored
Normal file
28
bench/implementations/angular-2-optimized/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
node_modules/.bin
|
||||||
|
|
||||||
|
# ignore the source / documentation of each node module, but retain the
|
||||||
|
# distribution builds
|
||||||
|
|
||||||
|
node_modules/systemjs
|
||||||
|
!node_modules/systemjs/dist/system.src.js
|
||||||
|
|
||||||
|
node_modules/todomvc-app-css
|
||||||
|
!node_modules/todomvc-app-css/index.css
|
||||||
|
|
||||||
|
node_modules/todomvc-common
|
||||||
|
!node_modules/todomvc-common/base.css
|
||||||
|
|
||||||
|
node_modules/angular2
|
||||||
|
!node_modules/angular2/bundles/angular2-polyfills.js
|
||||||
|
!node_modules/angular2/bundles/angular2.dev.js
|
||||||
|
|
||||||
|
node_modules/rxjs
|
||||||
|
!node_modules/rxjs/bundles/Rx.js
|
||||||
|
|
||||||
|
# Ignore development dependencies
|
||||||
|
node_modules/tsd
|
||||||
|
node_modules/typescript
|
||||||
|
|
||||||
|
*.js
|
||||||
|
*.js.map
|
||||||
|
typings
|
||||||
23
bench/implementations/angular-2-optimized/app/app.html
Normal file
23
bench/implementations/angular-2-optimized/app/app.html
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<section class="todoapp">
|
||||||
|
<header class="header">
|
||||||
|
<h1>todos</h1>
|
||||||
|
<input class="new-todo" placeholder="What needs to be done?" autofocus="" [(ngModel)]="newTodoText" (keydown.enter)="addTodo()">
|
||||||
|
</header>
|
||||||
|
<section class="main" *ngIf="todoStore.todos.length > 0">
|
||||||
|
<input class="toggle-all" type="checkbox" *ngIf="todoStore.todos.length" #toggleall [checked]="todoStore.allCompleted()" (click)="todoStore.setAllTo(toggleall.checked)">
|
||||||
|
<ul class="todo-list">
|
||||||
|
<li *ngFor="#todo of todoStore.todos; trackBy:identify" [class.completed]="todo.completed" [class.editing]="todo.editing">
|
||||||
|
<div class="view">
|
||||||
|
<input class="toggle" type="checkbox" (click)="toggleCompletion(todo)" [checked]="todo.completed">
|
||||||
|
<label (dblclick)="editTodo(todo)">{{todo.title}}</label>
|
||||||
|
<button class="destroy" (click)="remove(todo)"></button>
|
||||||
|
</div>
|
||||||
|
<input class="edit" *ngIf="todo.editing" [value]="todo.title" #editedtodo (blur)="stopEditing(todo, editedtodo.value)" (keydown.enter)="updateEditingTodo(todo, editedtodo.value)" (keydown.escape)="cancelEditingTodo(todo)">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<footer class="footer" *ngIf="todoStore.todos.length > 0">
|
||||||
|
<span class="todo-count"><strong>{{todoStore.getRemaining().length}}</strong> {{todoStore.getRemaining().length == 1 ? 'item' : 'items'}} left</span>
|
||||||
|
<button class="clear-completed" *ngIf="todoStore.getCompleted().length > 0" (click)="removeCompleted()">Clear completed</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
62
bench/implementations/angular-2-optimized/app/app.ts
Normal file
62
bench/implementations/angular-2-optimized/app/app.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import {Component} from 'angular2/core';
|
||||||
|
import {TodoStore, Todo} from './services/store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'todo-app',
|
||||||
|
templateUrl: 'app/app.html'
|
||||||
|
})
|
||||||
|
export default class TodoApp {
|
||||||
|
todoStore: TodoStore;
|
||||||
|
newTodoText = '';
|
||||||
|
|
||||||
|
constructor(todoStore: TodoStore) {
|
||||||
|
this.todoStore = todoStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
identify(index: number) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEditing(todo: Todo, editedTitle: string) {
|
||||||
|
todo.title = editedTitle;
|
||||||
|
todo.editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelEditingTodo(todo: Todo) {
|
||||||
|
todo.editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEditingTodo(todo: Todo, editedTitle: string) {
|
||||||
|
editedTitle = editedTitle.trim();
|
||||||
|
todo.editing = false;
|
||||||
|
|
||||||
|
if (editedTitle.length === 0) {
|
||||||
|
return this.todoStore.remove(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
todo.title = editedTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
editTodo(todo: Todo) {
|
||||||
|
todo.editing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCompleted() {
|
||||||
|
this.todoStore.removeCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCompletion(todo: Todo) {
|
||||||
|
this.todoStore.toggleCompletion(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(todo: Todo){
|
||||||
|
this.todoStore.remove(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
addTodo() {
|
||||||
|
if (this.newTodoText.trim().length) {
|
||||||
|
this.todoStore.add(this.newTodoText);
|
||||||
|
this.newTodoText = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
///<reference path="../node_modules/angular2/typings/browser.d.ts"/>
|
||||||
|
|
||||||
|
import {bootstrap} from 'angular2/platform/browser';
|
||||||
|
import TodoApp from './app'
|
||||||
|
import {TodoStore} from './services/store';
|
||||||
|
import {enableProdMode} from 'angular2/core';
|
||||||
|
|
||||||
|
enableProdMode();
|
||||||
|
bootstrap(TodoApp, [TodoStore]);
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
export class Todo {
|
||||||
|
completed: Boolean;
|
||||||
|
editing: Boolean;
|
||||||
|
|
||||||
|
private _title: String;
|
||||||
|
get title() {
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
set title(value: String) {
|
||||||
|
this._title = value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(title: String) {
|
||||||
|
this.completed = false;
|
||||||
|
this.editing = false;
|
||||||
|
this.title = title.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TodoStore {
|
||||||
|
todos: Array<Todo>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.todos = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private getWithCompleted(completed: Boolean) {
|
||||||
|
return this.todos.filter((todo: Todo) => todo.completed === completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
allCompleted() {
|
||||||
|
return this.todos.length === this.getCompleted().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAllTo(completed: Boolean) {
|
||||||
|
this.todos.forEach((t: Todo) => t.completed = completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCompleted() {
|
||||||
|
this.todos = this.getWithCompleted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRemaining() {
|
||||||
|
return this.getWithCompleted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCompleted() {
|
||||||
|
return this.getWithCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCompletion(todo: Todo) {
|
||||||
|
todo.completed = !todo.completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(todo: Todo) {
|
||||||
|
this.todos.splice(this.todos.indexOf(todo), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(title: String) {
|
||||||
|
this.todos.push(new Todo(title));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
bench/implementations/angular-2-optimized/index.html
Normal file
35
bench/implementations/angular-2-optimized/index.html
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" data-framework="angular2">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Angular2 • TodoMVC</title>
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
|
||||||
|
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
||||||
|
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||||
|
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||||
|
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<todo-app></todo-app>
|
||||||
|
<footer class="info">
|
||||||
|
<p>Double-click to edit a todo</p>
|
||||||
|
<p>
|
||||||
|
Created by <a href="http://github.com/samccone">Sam Saccone</a> and <a href="http://github.com/colineberhardt">Colin Eberhardt</a>
|
||||||
|
using <a href="http://angular.io">Angular2</a>
|
||||||
|
</p>
|
||||||
|
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
System.config({
|
||||||
|
packages: {
|
||||||
|
'app': {
|
||||||
|
format: 'cjs',
|
||||||
|
defaultExtension: 'js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
System.import('app/bootstrap');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
bench/implementations/angular-2-optimized/package.json
Normal file
19
bench/implementations/angular-2-optimized/package.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^1.8.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"angular2": "^2.0.0-beta.17",
|
||||||
|
"es6-shim": "^0.35.1",
|
||||||
|
"reflect-metadata": "0.1.2",
|
||||||
|
"rxjs": "5.0.0-beta.6",
|
||||||
|
"systemjs": "0.19.6",
|
||||||
|
"todomvc-app-css": "^2.0.0",
|
||||||
|
"todomvc-common": "^1.0.1",
|
||||||
|
"zone.js": "^0.6.17"
|
||||||
|
}
|
||||||
|
}
|
||||||
32
bench/implementations/angular-2-optimized/readme.md
Normal file
32
bench/implementations/angular-2-optimized/readme.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Angular 2 • [TodoMVC](http://todomvc.com)
|
||||||
|
|
||||||
|
> Angular is a development platform for creating applications using modern web standards. Angular includes a wealth of essential features such as mobile gestures, animations, filtering, routing, data binding, security, internationalization, and beautiful UI components. It's extremely modular, lightweight, and easy to learn.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Website](https://angular.io/)
|
||||||
|
- [Documentation](https://angular.io/docs/ts/latest/)
|
||||||
|
|
||||||
|
### Articles
|
||||||
|
|
||||||
|
- [Angular 2 Beta Announcement](http://angularjs.blogspot.co.uk/2015/12/angular-2-beta.html)
|
||||||
|
|
||||||
|
### Support
|
||||||
|
|
||||||
|
- [StackOverflow](http://stackoverflow.com/questions/tagged/angular2)
|
||||||
|
- [Google Groups](https://groups.google.com/forum/#!forum/angular)
|
||||||
|
- [Twitter](http://twitter.com/angularjs)
|
||||||
|
- [Google+](https://plus.sandbox.google.com/+AngularJS/posts)
|
||||||
|
|
||||||
|
*Let us [know](https://github.com/tastejs/todomvc/issues) if you discover anything worth sharing.*
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
This app was built using TypeScript and Angular 2 beta. To make changes simply
|
||||||
|
|
||||||
|
* `npm i`
|
||||||
|
* `npm run dev`
|
||||||
|
|
||||||
|
## Credit
|
||||||
|
|
||||||
|
Created by [Sam Saccone](http://github.com/samccone)
|
||||||
15
bench/implementations/angular-2-optimized/tsconfig.json
Normal file
15
bench/implementations/angular-2-optimized/tsconfig.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"removeComments": false,
|
||||||
|
"noImplicitAny": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"app/bootstrap.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
28
bench/implementations/angular-2/.gitignore
vendored
Executable file
28
bench/implementations/angular-2/.gitignore
vendored
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
node_modules/.bin
|
||||||
|
|
||||||
|
# ignore the source / documentation of each node module, but retain the
|
||||||
|
# distribution builds
|
||||||
|
|
||||||
|
node_modules/systemjs
|
||||||
|
!node_modules/systemjs/dist/system.src.js
|
||||||
|
|
||||||
|
node_modules/todomvc-app-css
|
||||||
|
!node_modules/todomvc-app-css/index.css
|
||||||
|
|
||||||
|
node_modules/todomvc-common
|
||||||
|
!node_modules/todomvc-common/base.css
|
||||||
|
|
||||||
|
node_modules/angular2
|
||||||
|
!node_modules/angular2/bundles/angular2-polyfills.js
|
||||||
|
!node_modules/angular2/bundles/angular2.dev.js
|
||||||
|
|
||||||
|
node_modules/rxjs
|
||||||
|
!node_modules/rxjs/bundles/Rx.js
|
||||||
|
|
||||||
|
# Ignore development dependencies
|
||||||
|
node_modules/tsd
|
||||||
|
node_modules/typescript
|
||||||
|
|
||||||
|
*.js
|
||||||
|
*.js.map
|
||||||
|
typings
|
||||||
23
bench/implementations/angular-2/app/app.html
Executable file
23
bench/implementations/angular-2/app/app.html
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<section class="todoapp">
|
||||||
|
<header class="header">
|
||||||
|
<h1>todos</h1>
|
||||||
|
<input class="new-todo" placeholder="What needs to be done?" autofocus="" [(ngModel)]="newTodoText" (keydown.enter)="addTodo()">
|
||||||
|
</header>
|
||||||
|
<section class="main" *ngIf="todoStore.todos.length > 0">
|
||||||
|
<input class="toggle-all" type="checkbox" *ngIf="todoStore.todos.length" #toggleall [checked]="todoStore.allCompleted()" (click)="todoStore.setAllTo(toggleall.checked)">
|
||||||
|
<ul class="todo-list">
|
||||||
|
<li *ngFor="#todo of todoStore.todos" [class.completed]="todo.completed" [class.editing]="todo.editing">
|
||||||
|
<div class="view">
|
||||||
|
<input class="toggle" type="checkbox" (click)="toggleCompletion(todo)" [checked]="todo.completed">
|
||||||
|
<label (dblclick)="editTodo(todo)">{{todo.title}}</label>
|
||||||
|
<button class="destroy" (click)="remove(todo)"></button>
|
||||||
|
</div>
|
||||||
|
<input class="edit" *ngIf="todo.editing" [value]="todo.title" #editedtodo (blur)="stopEditing(todo, editedtodo.value)" (keydown.enter)="updateEditingTodo(todo, editedtodo.value)" (keydown.escape)="cancelEditingTodo(todo)">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<footer class="footer" *ngIf="todoStore.todos.length > 0">
|
||||||
|
<span class="todo-count"><strong>{{todoStore.getRemaining().length}}</strong> {{todoStore.getRemaining().length == 1 ? 'item' : 'items'}} left</span>
|
||||||
|
<button class="clear-completed" *ngIf="todoStore.getCompleted().length > 0" (click)="removeCompleted()">Clear completed</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
58
bench/implementations/angular-2/app/app.ts
Executable file
58
bench/implementations/angular-2/app/app.ts
Executable file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import {Component} from 'angular2/core';
|
||||||
|
import {TodoStore, Todo} from './services/store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'todo-app',
|
||||||
|
templateUrl: 'app/app.html'
|
||||||
|
})
|
||||||
|
export default class TodoApp {
|
||||||
|
todoStore: TodoStore;
|
||||||
|
newTodoText = '';
|
||||||
|
|
||||||
|
constructor(todoStore: TodoStore) {
|
||||||
|
this.todoStore = todoStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEditing(todo: Todo, editedTitle: string) {
|
||||||
|
todo.title = editedTitle;
|
||||||
|
todo.editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelEditingTodo(todo: Todo) {
|
||||||
|
todo.editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEditingTodo(todo: Todo, editedTitle: string) {
|
||||||
|
editedTitle = editedTitle.trim();
|
||||||
|
todo.editing = false;
|
||||||
|
|
||||||
|
if (editedTitle.length === 0) {
|
||||||
|
return this.todoStore.remove(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
todo.title = editedTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
editTodo(todo: Todo) {
|
||||||
|
todo.editing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCompleted() {
|
||||||
|
this.todoStore.removeCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCompletion(todo: Todo) {
|
||||||
|
this.todoStore.toggleCompletion(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(todo: Todo){
|
||||||
|
this.todoStore.remove(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
addTodo() {
|
||||||
|
if (this.newTodoText.trim().length) {
|
||||||
|
this.todoStore.add(this.newTodoText);
|
||||||
|
this.newTodoText = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
bench/implementations/angular-2/app/bootstrap.ts
Executable file
9
bench/implementations/angular-2/app/bootstrap.ts
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
///<reference path="../node_modules/angular2/typings/browser.d.ts"/>
|
||||||
|
|
||||||
|
import {bootstrap} from 'angular2/platform/browser';
|
||||||
|
import TodoApp from './app'
|
||||||
|
import {TodoStore} from './services/store';
|
||||||
|
import {enableProdMode} from 'angular2/core';
|
||||||
|
|
||||||
|
enableProdMode();
|
||||||
|
bootstrap(TodoApp, [TodoStore]);
|
||||||
62
bench/implementations/angular-2/app/services/store.ts
Executable file
62
bench/implementations/angular-2/app/services/store.ts
Executable file
|
|
@ -0,0 +1,62 @@
|
||||||
|
export class Todo {
|
||||||
|
completed: Boolean;
|
||||||
|
editing: Boolean;
|
||||||
|
|
||||||
|
private _title: String;
|
||||||
|
get title() {
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
set title(value: String) {
|
||||||
|
this._title = value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(title: String) {
|
||||||
|
this.completed = false;
|
||||||
|
this.editing = false;
|
||||||
|
this.title = title.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TodoStore {
|
||||||
|
todos: Array<Todo>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.todos = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private getWithCompleted(completed: Boolean) {
|
||||||
|
return this.todos.filter((todo: Todo) => todo.completed === completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
allCompleted() {
|
||||||
|
return this.todos.length === this.getCompleted().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAllTo(completed: Boolean) {
|
||||||
|
this.todos.forEach((t: Todo) => t.completed = completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCompleted() {
|
||||||
|
this.todos = this.getWithCompleted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRemaining() {
|
||||||
|
return this.getWithCompleted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCompleted() {
|
||||||
|
return this.getWithCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCompletion(todo: Todo) {
|
||||||
|
todo.completed = !todo.completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(todo: Todo) {
|
||||||
|
this.todos.splice(this.todos.indexOf(todo), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(title: String) {
|
||||||
|
this.todos.push(new Todo(title));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
bench/implementations/angular-2/index.html
Executable file
35
bench/implementations/angular-2/index.html
Executable file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" data-framework="angular2">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Angular2 • TodoMVC</title>
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
|
||||||
|
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
|
||||||
|
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
||||||
|
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||||
|
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||||
|
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<todo-app></todo-app>
|
||||||
|
<footer class="info">
|
||||||
|
<p>Double-click to edit a todo</p>
|
||||||
|
<p>
|
||||||
|
Created by <a href="http://github.com/samccone">Sam Saccone</a> and <a href="http://github.com/colineberhardt">Colin Eberhardt</a>
|
||||||
|
using <a href="http://angular.io">Angular2</a>
|
||||||
|
</p>
|
||||||
|
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
System.config({
|
||||||
|
packages: {
|
||||||
|
'app': {
|
||||||
|
format: 'cjs',
|
||||||
|
defaultExtension: 'js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
System.import('app/bootstrap');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
bench/implementations/angular-2/package.json
Executable file
19
bench/implementations/angular-2/package.json
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^1.8.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"angular2": "^2.0.0-beta.17",
|
||||||
|
"es6-shim": "^0.35.1",
|
||||||
|
"reflect-metadata": "0.1.2",
|
||||||
|
"rxjs": "5.0.0-beta.6",
|
||||||
|
"systemjs": "0.19.6",
|
||||||
|
"todomvc-app-css": "^2.0.0",
|
||||||
|
"todomvc-common": "^1.0.1",
|
||||||
|
"zone.js": "^0.6.17"
|
||||||
|
}
|
||||||
|
}
|
||||||
32
bench/implementations/angular-2/readme.md
Executable file
32
bench/implementations/angular-2/readme.md
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Angular 2 • [TodoMVC](http://todomvc.com)
|
||||||
|
|
||||||
|
> Angular is a development platform for creating applications using modern web standards. Angular includes a wealth of essential features such as mobile gestures, animations, filtering, routing, data binding, security, internationalization, and beautiful UI components. It's extremely modular, lightweight, and easy to learn.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Website](https://angular.io/)
|
||||||
|
- [Documentation](https://angular.io/docs/ts/latest/)
|
||||||
|
|
||||||
|
### Articles
|
||||||
|
|
||||||
|
- [Angular 2 Beta Announcement](http://angularjs.blogspot.co.uk/2015/12/angular-2-beta.html)
|
||||||
|
|
||||||
|
### Support
|
||||||
|
|
||||||
|
- [StackOverflow](http://stackoverflow.com/questions/tagged/angular2)
|
||||||
|
- [Google Groups](https://groups.google.com/forum/#!forum/angular)
|
||||||
|
- [Twitter](http://twitter.com/angularjs)
|
||||||
|
- [Google+](https://plus.sandbox.google.com/+AngularJS/posts)
|
||||||
|
|
||||||
|
*Let us [know](https://github.com/tastejs/todomvc/issues) if you discover anything worth sharing.*
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
This app was built using TypeScript and Angular 2 beta. To make changes simply
|
||||||
|
|
||||||
|
* `npm i`
|
||||||
|
* `npm run dev`
|
||||||
|
|
||||||
|
## Credit
|
||||||
|
|
||||||
|
Created by [Sam Saccone](http://github.com/samccone)
|
||||||
15
bench/implementations/angular-2/tsconfig.json
Executable file
15
bench/implementations/angular-2/tsconfig.json
Executable file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"removeComments": false,
|
||||||
|
"noImplicitAny": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"app/bootstrap.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
339
bench/implementations/elm-0.16-optimized/Todo.elm
Executable file
339
bench/implementations/elm-0.16-optimized/Todo.elm
Executable file
|
|
@ -0,0 +1,339 @@
|
||||||
|
module Todo where
|
||||||
|
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
|
||||||
|
|
||||||
|
This application is broken up into four distinct parts:
|
||||||
|
|
||||||
|
1. Model - a full definition of the application's state
|
||||||
|
2. Update - a way to step the application state forward
|
||||||
|
3. View - a way to visualize our application state with HTML
|
||||||
|
4. Inputs - the signals necessary to manage events
|
||||||
|
|
||||||
|
This clean division of concerns is a core part of Elm. You can read more about
|
||||||
|
this in the Pong tutorial: http://elm-lang.org/blog/making-pong
|
||||||
|
|
||||||
|
This program is not particularly large, so definitely see the following
|
||||||
|
for notes on structuring more complex GUIs with Elm:
|
||||||
|
https://github.com/evancz/elm-architecture-tutorial/
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (..)
|
||||||
|
import Html.Lazy exposing (lazy2, lazy3)
|
||||||
|
import Json.Decode as Json
|
||||||
|
import Signal exposing (Signal, Address)
|
||||||
|
import String
|
||||||
|
import Window
|
||||||
|
|
||||||
|
|
||||||
|
---- MODEL ----
|
||||||
|
|
||||||
|
-- The full application state of our todo app.
|
||||||
|
type alias Model =
|
||||||
|
{ tasks : List Task
|
||||||
|
, field : String
|
||||||
|
, uid : Int
|
||||||
|
, visibility : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Task =
|
||||||
|
{ description : String
|
||||||
|
, completed : Bool
|
||||||
|
, editing : Bool
|
||||||
|
, id : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
newTask : String -> Int -> Task
|
||||||
|
newTask desc id =
|
||||||
|
{ description = desc
|
||||||
|
, completed = False
|
||||||
|
, editing = False
|
||||||
|
, id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
{ tasks = []
|
||||||
|
, visibility = "All"
|
||||||
|
, field = ""
|
||||||
|
, uid = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
---- UPDATE ----
|
||||||
|
|
||||||
|
-- A description of the kinds of actions that can be performed on the model of
|
||||||
|
-- our application. See the following for more info on this pattern and
|
||||||
|
-- some alternatives: https://github.com/evancz/elm-architecture-tutorial/
|
||||||
|
type Action
|
||||||
|
= NoOp
|
||||||
|
| UpdateField String
|
||||||
|
| EditingTask Int Bool
|
||||||
|
| UpdateTask Int String
|
||||||
|
| Add
|
||||||
|
| Delete Int
|
||||||
|
| DeleteComplete
|
||||||
|
| Check Int Bool
|
||||||
|
| CheckAll Bool
|
||||||
|
| ChangeVisibility String
|
||||||
|
|
||||||
|
|
||||||
|
-- How we update our Model on a given Action?
|
||||||
|
update : Action -> Model -> Model
|
||||||
|
update action model =
|
||||||
|
case action of
|
||||||
|
NoOp -> model
|
||||||
|
|
||||||
|
Add ->
|
||||||
|
{ model |
|
||||||
|
uid = model.uid + 1,
|
||||||
|
field = "",
|
||||||
|
tasks =
|
||||||
|
if String.isEmpty model.field
|
||||||
|
then model.tasks
|
||||||
|
else model.tasks ++ [newTask model.field model.uid]
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateField str ->
|
||||||
|
{ model | field = str }
|
||||||
|
|
||||||
|
EditingTask id isEditing ->
|
||||||
|
let updateTask t = if t.id == id then { t | editing = isEditing } else t
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
UpdateTask id task ->
|
||||||
|
let updateTask t = if t.id == id then { t | description = task } else t
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
Delete id ->
|
||||||
|
{ model | tasks = List.filter (\t -> t.id /= id) model.tasks }
|
||||||
|
|
||||||
|
DeleteComplete ->
|
||||||
|
{ model | tasks = List.filter (not << .completed) model.tasks }
|
||||||
|
|
||||||
|
Check id isCompleted ->
|
||||||
|
let updateTask t = if t.id == id then { t | completed = isCompleted } else t
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
CheckAll isCompleted ->
|
||||||
|
let updateTask t = { t | completed = isCompleted }
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
ChangeVisibility visibility ->
|
||||||
|
{ model | visibility = visibility }
|
||||||
|
|
||||||
|
|
||||||
|
---- VIEW ----
|
||||||
|
|
||||||
|
view : Address Action -> Model -> Html
|
||||||
|
view address model =
|
||||||
|
div
|
||||||
|
[ class "todomvc-wrapper"
|
||||||
|
, style [ ("visibility", "hidden") ]
|
||||||
|
]
|
||||||
|
[ section
|
||||||
|
[ class "todoapp" ]
|
||||||
|
[ lazy2 taskEntry address model.field
|
||||||
|
, lazy3 taskList address model.visibility model.tasks
|
||||||
|
, lazy3 controls address model.visibility model.tasks
|
||||||
|
]
|
||||||
|
, infoFooter
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
onEnter : Address a -> a -> Attribute
|
||||||
|
onEnter address value =
|
||||||
|
on "keydown"
|
||||||
|
(Json.customDecoder keyCode is13)
|
||||||
|
(\_ -> Signal.message address value)
|
||||||
|
|
||||||
|
|
||||||
|
is13 : Int -> Result String ()
|
||||||
|
is13 code =
|
||||||
|
if code == 13 then Ok () else Err "not the right key code"
|
||||||
|
|
||||||
|
|
||||||
|
taskEntry : Address Action -> String -> Html
|
||||||
|
taskEntry address task =
|
||||||
|
header
|
||||||
|
[ class "header" ]
|
||||||
|
[ h1 [] [ text "todos" ]
|
||||||
|
, input
|
||||||
|
[ class "new-todo"
|
||||||
|
, placeholder "What needs to be done?"
|
||||||
|
, autofocus True
|
||||||
|
, value task
|
||||||
|
, name "newTodo"
|
||||||
|
, on "input" targetValue (Signal.message address << UpdateField)
|
||||||
|
, onEnter address Add
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
taskList : Address Action -> String -> List Task -> Html
|
||||||
|
taskList address visibility tasks =
|
||||||
|
let isVisible todo =
|
||||||
|
case visibility of
|
||||||
|
"Completed" -> todo.completed
|
||||||
|
"Active" -> not todo.completed
|
||||||
|
_ -> True
|
||||||
|
|
||||||
|
allCompleted = List.all .completed tasks
|
||||||
|
|
||||||
|
cssVisibility = if List.isEmpty tasks then "hidden" else "visible"
|
||||||
|
in
|
||||||
|
section
|
||||||
|
[ class "main"
|
||||||
|
, style [ ("visibility", cssVisibility) ]
|
||||||
|
]
|
||||||
|
[ input
|
||||||
|
[ class "toggle-all"
|
||||||
|
, type' "checkbox"
|
||||||
|
, name "toggle"
|
||||||
|
, checked allCompleted
|
||||||
|
, onClick address (CheckAll (not allCompleted))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ for "toggle-all" ]
|
||||||
|
[ text "Mark all as complete" ]
|
||||||
|
, ul
|
||||||
|
[ class "todo-list" ]
|
||||||
|
(List.map (todoItem address) (List.filter isVisible tasks))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
todoItem : Address Action -> Task -> Html
|
||||||
|
todoItem address todo =
|
||||||
|
li
|
||||||
|
[ classList [ ("completed", todo.completed), ("editing", todo.editing) ]
|
||||||
|
, key (toString todo.id)
|
||||||
|
]
|
||||||
|
[ div
|
||||||
|
[ class "view" ]
|
||||||
|
[ input
|
||||||
|
[ class "toggle"
|
||||||
|
, type' "checkbox"
|
||||||
|
, checked todo.completed
|
||||||
|
, onClick address (Check todo.id (not todo.completed))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ onDoubleClick address (EditingTask todo.id True) ]
|
||||||
|
[ text todo.description ]
|
||||||
|
, button
|
||||||
|
[ class "destroy"
|
||||||
|
, onClick address (Delete todo.id)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, input
|
||||||
|
[ class "edit"
|
||||||
|
, value todo.description
|
||||||
|
, name "title"
|
||||||
|
, id ("todo-" ++ toString todo.id)
|
||||||
|
, on "input" targetValue (Signal.message address << UpdateTask todo.id)
|
||||||
|
, onBlur address (EditingTask todo.id False)
|
||||||
|
, onEnter address (EditingTask todo.id False)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
controls : Address Action -> String -> List Task -> Html
|
||||||
|
controls address visibility tasks =
|
||||||
|
let tasksCompleted = List.length (List.filter .completed tasks)
|
||||||
|
tasksLeft = List.length tasks - tasksCompleted
|
||||||
|
item_ = if tasksLeft == 1 then " item" else " items"
|
||||||
|
in
|
||||||
|
footer
|
||||||
|
[ class "footer"
|
||||||
|
, hidden (List.isEmpty tasks)
|
||||||
|
]
|
||||||
|
[ span
|
||||||
|
[ class "todo-count" ]
|
||||||
|
[ strong [] [ text (toString tasksLeft) ]
|
||||||
|
, text (item_ ++ " left")
|
||||||
|
]
|
||||||
|
, ul
|
||||||
|
[ class "filters" ]
|
||||||
|
[ visibilitySwap address "#/" "All" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap address "#/active" "Active" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap address "#/completed" "Completed" visibility
|
||||||
|
]
|
||||||
|
, button
|
||||||
|
[ class "clear-completed"
|
||||||
|
, hidden (tasksCompleted == 0)
|
||||||
|
, onClick address DeleteComplete
|
||||||
|
]
|
||||||
|
[ text ("Clear completed (" ++ toString tasksCompleted ++ ")") ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
visibilitySwap : Address Action -> String -> String -> String -> Html
|
||||||
|
visibilitySwap address uri visibility actualVisibility =
|
||||||
|
li
|
||||||
|
[ onClick address (ChangeVisibility visibility) ]
|
||||||
|
[ a [ href uri, classList [("selected", visibility == actualVisibility)] ] [ text visibility ] ]
|
||||||
|
|
||||||
|
|
||||||
|
infoFooter : Html
|
||||||
|
infoFooter =
|
||||||
|
footer [ class "info" ]
|
||||||
|
[ p [] [ text "Double-click to edit a todo" ]
|
||||||
|
, p []
|
||||||
|
[ text "Written by "
|
||||||
|
, a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
|
||||||
|
]
|
||||||
|
, p []
|
||||||
|
[ text "Part of "
|
||||||
|
, a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
---- INPUTS ----
|
||||||
|
|
||||||
|
-- wire the entire application together
|
||||||
|
main : Signal Html
|
||||||
|
main =
|
||||||
|
Signal.map (view actions.address) model
|
||||||
|
|
||||||
|
|
||||||
|
-- manage the model of our application over time
|
||||||
|
model : Signal Model
|
||||||
|
model =
|
||||||
|
Signal.foldp update emptyModel actions.signal
|
||||||
|
|
||||||
|
|
||||||
|
-- actions from user input
|
||||||
|
actions : Signal.Mailbox Action
|
||||||
|
actions =
|
||||||
|
Signal.mailbox NoOp
|
||||||
|
|
||||||
|
|
||||||
|
port focus : Signal String
|
||||||
|
port focus =
|
||||||
|
let needsFocus act =
|
||||||
|
case act of
|
||||||
|
EditingTask id bool -> bool
|
||||||
|
_ -> False
|
||||||
|
|
||||||
|
toSelector act =
|
||||||
|
case act of
|
||||||
|
EditingTask id _ -> "#todo-" ++ toString id
|
||||||
|
_ -> ""
|
||||||
|
in
|
||||||
|
actions.signal
|
||||||
|
|> Signal.filter needsFocus (EditingTask 0 True)
|
||||||
|
|> Signal.map toSelector
|
||||||
15
bench/implementations/elm-0.16-optimized/elm-package.json
Normal file
15
bench/implementations/elm-0.16-optimized/elm-package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"summary": "TodoMVC created with Elm and elm-html",
|
||||||
|
"repository": "https://github.com/evancz/elm-todomvc.git",
|
||||||
|
"license": "BSD3",
|
||||||
|
"source-directories": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"exposed-modules": [],
|
||||||
|
"dependencies": {
|
||||||
|
"elm-lang/core": "3.0.0 <= v < 4.0.0",
|
||||||
|
"evancz/elm-html": "4.0.2 <= v < 5.0.0"
|
||||||
|
},
|
||||||
|
"elm-version": "0.16.0 <= v < 0.17.0"
|
||||||
|
}
|
||||||
10560
bench/implementations/elm-0.16-optimized/elm.js
Executable file
10560
bench/implementations/elm-0.16-optimized/elm.js
Executable file
File diff suppressed because it is too large
Load diff
26
bench/implementations/elm-0.16-optimized/index.html
Normal file
26
bench/implementations/elm-0.16-optimized/index.html
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Elm • TodoMVC</title>
|
||||||
|
<script type="text/javascript" src="elm.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var todomvc = Elm.fullscreen(Elm.Todo);
|
||||||
|
todomvc.ports.focus.subscribe(function(selector) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var nodes = document.querySelectorAll(selector);
|
||||||
|
if (nodes.length === 1 && document.activeElement !== nodes[0]) {
|
||||||
|
nodes[0].focus();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
382
bench/implementations/elm-0.16-optimized/style.css
Normal file
382
bench/implementations/elm-0.16-optimized/style.css
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todomvc-wrapper {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.4em;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #4d4d4d;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="checkbox"] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp {
|
||||||
|
background: #fff;
|
||||||
|
margin: 130px 0 40px 0;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-webkit-input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-moz-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp h1 {
|
||||||
|
position: absolute;
|
||||||
|
top: -155px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(175, 47, 47, 0.15);
|
||||||
|
-webkit-text-rendering: optimizeLegibility;
|
||||||
|
-moz-text-rendering: optimizeLegibility;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo,
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: 1.4em;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo {
|
||||||
|
padding: 16px 16px 16px 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(0, 0, 0, 0.003);
|
||||||
|
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='toggle-all'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
position: absolute;
|
||||||
|
top: -55px;
|
||||||
|
left: -12px;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:before {
|
||||||
|
content: '❯';
|
||||||
|
font-size: 22px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
padding: 10px 27px 10px 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:checked:before {
|
||||||
|
color: #737373;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li {
|
||||||
|
position: relative;
|
||||||
|
font-size: 24px;
|
||||||
|
border-bottom: 1px solid #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .edit {
|
||||||
|
display: block;
|
||||||
|
width: 506px;
|
||||||
|
padding: 13px 17px 12px 17px;
|
||||||
|
margin: 0 0 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
text-align: center;
|
||||||
|
width: 40px;
|
||||||
|
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||||
|
height: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto 0;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:checked:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li label {
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 15px 60px 15px 15px;
|
||||||
|
margin-left: 45px;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.completed label {
|
||||||
|
color: #d9d9d9;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #cc9a9a;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:after {
|
||||||
|
content: '×';
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing:last-child {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
color: #777;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 0 -3px #f6f6f6,
|
||||||
|
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0 16px 0 -6px #f6f6f6,
|
||||||
|
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count strong {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a {
|
||||||
|
color: inherit;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected,
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed,
|
||||||
|
html .clear-completed:active {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin: 65px auto 0;
|
||||||
|
color: #bfbfbf;
|
||||||
|
font-size: 10px;
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hack to remove background from Mobile Safari.
|
||||||
|
Can't use it globally since it destroys checkboxes in Firefox
|
||||||
|
*/
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
.toggle-all,
|
||||||
|
.todo-list li .toggle {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
.footer {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
336
bench/implementations/elm-0.16/Todo.elm
Normal file
336
bench/implementations/elm-0.16/Todo.elm
Normal file
|
|
@ -0,0 +1,336 @@
|
||||||
|
module Todo where
|
||||||
|
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
|
||||||
|
|
||||||
|
This application is broken up into four distinct parts:
|
||||||
|
|
||||||
|
1. Model - a full definition of the application's state
|
||||||
|
2. Update - a way to step the application state forward
|
||||||
|
3. View - a way to visualize our application state with HTML
|
||||||
|
4. Inputs - the signals necessary to manage events
|
||||||
|
|
||||||
|
This clean division of concerns is a core part of Elm. You can read more about
|
||||||
|
this in the Pong tutorial: http://elm-lang.org/blog/making-pong
|
||||||
|
|
||||||
|
This program is not particularly large, so definitely see the following
|
||||||
|
for notes on structuring more complex GUIs with Elm:
|
||||||
|
https://github.com/evancz/elm-architecture-tutorial/
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (..)
|
||||||
|
import Json.Decode as Json
|
||||||
|
import Signal exposing (Signal, Address)
|
||||||
|
import String
|
||||||
|
import Window
|
||||||
|
|
||||||
|
|
||||||
|
---- MODEL ----
|
||||||
|
|
||||||
|
-- The full application state of our todo app.
|
||||||
|
type alias Model =
|
||||||
|
{ tasks : List Task
|
||||||
|
, field : String
|
||||||
|
, uid : Int
|
||||||
|
, visibility : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Task =
|
||||||
|
{ description : String
|
||||||
|
, completed : Bool
|
||||||
|
, editing : Bool
|
||||||
|
, id : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
newTask : String -> Int -> Task
|
||||||
|
newTask desc id =
|
||||||
|
{ description = desc
|
||||||
|
, completed = False
|
||||||
|
, editing = False
|
||||||
|
, id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
{ tasks = []
|
||||||
|
, visibility = "All"
|
||||||
|
, field = ""
|
||||||
|
, uid = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
---- UPDATE ----
|
||||||
|
|
||||||
|
-- A description of the kinds of actions that can be performed on the model of
|
||||||
|
-- our application. See the following for more info on this pattern and
|
||||||
|
-- some alternatives: https://github.com/evancz/elm-architecture-tutorial/
|
||||||
|
type Action
|
||||||
|
= NoOp
|
||||||
|
| UpdateField String
|
||||||
|
| EditingTask Int Bool
|
||||||
|
| UpdateTask Int String
|
||||||
|
| Add
|
||||||
|
| Delete Int
|
||||||
|
| DeleteComplete
|
||||||
|
| Check Int Bool
|
||||||
|
| CheckAll Bool
|
||||||
|
| ChangeVisibility String
|
||||||
|
|
||||||
|
|
||||||
|
-- How we update our Model on a given Action?
|
||||||
|
update : Action -> Model -> Model
|
||||||
|
update action model =
|
||||||
|
case action of
|
||||||
|
NoOp -> model
|
||||||
|
|
||||||
|
Add ->
|
||||||
|
{ model |
|
||||||
|
uid = model.uid + 1,
|
||||||
|
field = "",
|
||||||
|
tasks =
|
||||||
|
if String.isEmpty model.field
|
||||||
|
then model.tasks
|
||||||
|
else model.tasks ++ [newTask model.field model.uid]
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateField str ->
|
||||||
|
{ model | field = str }
|
||||||
|
|
||||||
|
EditingTask id isEditing ->
|
||||||
|
let updateTask t = if t.id == id then { t | editing = isEditing } else t
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
UpdateTask id task ->
|
||||||
|
let updateTask t = if t.id == id then { t | description = task } else t
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
Delete id ->
|
||||||
|
{ model | tasks = List.filter (\t -> t.id /= id) model.tasks }
|
||||||
|
|
||||||
|
DeleteComplete ->
|
||||||
|
{ model | tasks = List.filter (not << .completed) model.tasks }
|
||||||
|
|
||||||
|
Check id isCompleted ->
|
||||||
|
let updateTask t = if t.id == id then { t | completed = isCompleted } else t
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
CheckAll isCompleted ->
|
||||||
|
let updateTask t = { t | completed = isCompleted }
|
||||||
|
in
|
||||||
|
{ model | tasks = List.map updateTask model.tasks }
|
||||||
|
|
||||||
|
ChangeVisibility visibility ->
|
||||||
|
{ model | visibility = visibility }
|
||||||
|
|
||||||
|
|
||||||
|
---- VIEW ----
|
||||||
|
|
||||||
|
view : Address Action -> Model -> Html
|
||||||
|
view address model =
|
||||||
|
div
|
||||||
|
[ class "todomvc-wrapper"
|
||||||
|
, style [ ("visibility", "hidden") ]
|
||||||
|
]
|
||||||
|
[ section
|
||||||
|
[ class "todoapp" ]
|
||||||
|
[ taskEntry address model.field
|
||||||
|
, taskList address model.visibility model.tasks
|
||||||
|
, controls address model.visibility model.tasks
|
||||||
|
]
|
||||||
|
, infoFooter
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
onEnter : Address a -> a -> Attribute
|
||||||
|
onEnter address value =
|
||||||
|
on "keydown"
|
||||||
|
(Json.customDecoder keyCode is13)
|
||||||
|
(\_ -> Signal.message address value)
|
||||||
|
|
||||||
|
|
||||||
|
is13 : Int -> Result String ()
|
||||||
|
is13 code =
|
||||||
|
if code == 13 then Ok () else Err "not the right key code"
|
||||||
|
|
||||||
|
|
||||||
|
taskEntry : Address Action -> String -> Html
|
||||||
|
taskEntry address task =
|
||||||
|
header
|
||||||
|
[ class "header" ]
|
||||||
|
[ h1 [] [ text "todos" ]
|
||||||
|
, input
|
||||||
|
[ class "new-todo"
|
||||||
|
, placeholder "What needs to be done?"
|
||||||
|
, autofocus True
|
||||||
|
, value task
|
||||||
|
, name "newTodo"
|
||||||
|
, on "input" targetValue (Signal.message address << UpdateField)
|
||||||
|
, onEnter address Add
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
taskList : Address Action -> String -> List Task -> Html
|
||||||
|
taskList address visibility tasks =
|
||||||
|
let isVisible todo =
|
||||||
|
case visibility of
|
||||||
|
"Completed" -> todo.completed
|
||||||
|
"Active" -> not todo.completed
|
||||||
|
_ -> True
|
||||||
|
|
||||||
|
allCompleted = List.all .completed tasks
|
||||||
|
|
||||||
|
cssVisibility = if List.isEmpty tasks then "hidden" else "visible"
|
||||||
|
in
|
||||||
|
section
|
||||||
|
[ class "main"
|
||||||
|
, style [ ("visibility", cssVisibility) ]
|
||||||
|
]
|
||||||
|
[ input
|
||||||
|
[ class "toggle-all"
|
||||||
|
, type' "checkbox"
|
||||||
|
, name "toggle"
|
||||||
|
, checked allCompleted
|
||||||
|
, onClick address (CheckAll (not allCompleted))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ for "toggle-all" ]
|
||||||
|
[ text "Mark all as complete" ]
|
||||||
|
, ul
|
||||||
|
[ class "todo-list" ]
|
||||||
|
(List.map (todoItem address) (List.filter isVisible tasks))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
todoItem : Address Action -> Task -> Html
|
||||||
|
todoItem address todo =
|
||||||
|
li
|
||||||
|
[ classList [ ("completed", todo.completed), ("editing", todo.editing) ] ]
|
||||||
|
[ div
|
||||||
|
[ class "view" ]
|
||||||
|
[ input
|
||||||
|
[ class "toggle"
|
||||||
|
, type' "checkbox"
|
||||||
|
, checked todo.completed
|
||||||
|
, onClick address (Check todo.id (not todo.completed))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ onDoubleClick address (EditingTask todo.id True) ]
|
||||||
|
[ text todo.description ]
|
||||||
|
, button
|
||||||
|
[ class "destroy"
|
||||||
|
, onClick address (Delete todo.id)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, input
|
||||||
|
[ class "edit"
|
||||||
|
, value todo.description
|
||||||
|
, name "title"
|
||||||
|
, id ("todo-" ++ toString todo.id)
|
||||||
|
, on "input" targetValue (Signal.message address << UpdateTask todo.id)
|
||||||
|
, onBlur address (EditingTask todo.id False)
|
||||||
|
, onEnter address (EditingTask todo.id False)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
controls : Address Action -> String -> List Task -> Html
|
||||||
|
controls address visibility tasks =
|
||||||
|
let tasksCompleted = List.length (List.filter .completed tasks)
|
||||||
|
tasksLeft = List.length tasks - tasksCompleted
|
||||||
|
item_ = if tasksLeft == 1 then " item" else " items"
|
||||||
|
in
|
||||||
|
footer
|
||||||
|
[ class "footer"
|
||||||
|
, hidden (List.isEmpty tasks)
|
||||||
|
]
|
||||||
|
[ span
|
||||||
|
[ class "todo-count" ]
|
||||||
|
[ strong [] [ text (toString tasksLeft) ]
|
||||||
|
, text (item_ ++ " left")
|
||||||
|
]
|
||||||
|
, ul
|
||||||
|
[ class "filters" ]
|
||||||
|
[ visibilitySwap address "#/" "All" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap address "#/active" "Active" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap address "#/completed" "Completed" visibility
|
||||||
|
]
|
||||||
|
, button
|
||||||
|
[ class "clear-completed"
|
||||||
|
, hidden (tasksCompleted == 0)
|
||||||
|
, onClick address DeleteComplete
|
||||||
|
]
|
||||||
|
[ text ("Clear completed (" ++ toString tasksCompleted ++ ")") ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
visibilitySwap : Address Action -> String -> String -> String -> Html
|
||||||
|
visibilitySwap address uri visibility actualVisibility =
|
||||||
|
li
|
||||||
|
[ onClick address (ChangeVisibility visibility) ]
|
||||||
|
[ a [ href uri, classList [("selected", visibility == actualVisibility)] ] [ text visibility ] ]
|
||||||
|
|
||||||
|
|
||||||
|
infoFooter : Html
|
||||||
|
infoFooter =
|
||||||
|
footer [ class "info" ]
|
||||||
|
[ p [] [ text "Double-click to edit a todo" ]
|
||||||
|
, p []
|
||||||
|
[ text "Written by "
|
||||||
|
, a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
|
||||||
|
]
|
||||||
|
, p []
|
||||||
|
[ text "Part of "
|
||||||
|
, a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
---- INPUTS ----
|
||||||
|
|
||||||
|
-- wire the entire application together
|
||||||
|
main : Signal Html
|
||||||
|
main =
|
||||||
|
Signal.map (view actions.address) model
|
||||||
|
|
||||||
|
|
||||||
|
-- manage the model of our application over time
|
||||||
|
model : Signal Model
|
||||||
|
model =
|
||||||
|
Signal.foldp update emptyModel actions.signal
|
||||||
|
|
||||||
|
|
||||||
|
-- actions from user input
|
||||||
|
actions : Signal.Mailbox Action
|
||||||
|
actions =
|
||||||
|
Signal.mailbox NoOp
|
||||||
|
|
||||||
|
|
||||||
|
port focus : Signal String
|
||||||
|
port focus =
|
||||||
|
let needsFocus act =
|
||||||
|
case act of
|
||||||
|
EditingTask id bool -> bool
|
||||||
|
_ -> False
|
||||||
|
|
||||||
|
toSelector act =
|
||||||
|
case act of
|
||||||
|
EditingTask id _ -> "#todo-" ++ toString id
|
||||||
|
_ -> ""
|
||||||
|
in
|
||||||
|
actions.signal
|
||||||
|
|> Signal.filter needsFocus (EditingTask 0 True)
|
||||||
|
|> Signal.map toSelector
|
||||||
15
bench/implementations/elm-0.16/elm-package.json
Normal file
15
bench/implementations/elm-0.16/elm-package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"summary": "TodoMVC created with Elm and elm-html",
|
||||||
|
"repository": "https://github.com/evancz/elm-todomvc.git",
|
||||||
|
"license": "BSD3",
|
||||||
|
"source-directories": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"exposed-modules": [],
|
||||||
|
"dependencies": {
|
||||||
|
"elm-lang/core": "3.0.0 <= v < 4.0.0",
|
||||||
|
"evancz/elm-html": "4.0.2 <= v < 5.0.0"
|
||||||
|
},
|
||||||
|
"elm-version": "0.16.0 <= v < 0.17.0"
|
||||||
|
}
|
||||||
10534
bench/implementations/elm-0.16/elm.js
Normal file
10534
bench/implementations/elm-0.16/elm.js
Normal file
File diff suppressed because it is too large
Load diff
26
bench/implementations/elm-0.16/index.html
Normal file
26
bench/implementations/elm-0.16/index.html
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Elm • TodoMVC</title>
|
||||||
|
<script type="text/javascript" src="elm.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var todomvc = Elm.fullscreen(Elm.Todo);
|
||||||
|
todomvc.ports.focus.subscribe(function(selector) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var nodes = document.querySelectorAll(selector);
|
||||||
|
if (nodes.length === 1 && document.activeElement !== nodes[0]) {
|
||||||
|
nodes[0].focus();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
382
bench/implementations/elm-0.16/style.css
Normal file
382
bench/implementations/elm-0.16/style.css
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todomvc-wrapper {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.4em;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #4d4d4d;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="checkbox"] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp {
|
||||||
|
background: #fff;
|
||||||
|
margin: 130px 0 40px 0;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-webkit-input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-moz-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp h1 {
|
||||||
|
position: absolute;
|
||||||
|
top: -155px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(175, 47, 47, 0.15);
|
||||||
|
-webkit-text-rendering: optimizeLegibility;
|
||||||
|
-moz-text-rendering: optimizeLegibility;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo,
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: 1.4em;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo {
|
||||||
|
padding: 16px 16px 16px 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(0, 0, 0, 0.003);
|
||||||
|
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='toggle-all'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
position: absolute;
|
||||||
|
top: -55px;
|
||||||
|
left: -12px;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:before {
|
||||||
|
content: '❯';
|
||||||
|
font-size: 22px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
padding: 10px 27px 10px 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:checked:before {
|
||||||
|
color: #737373;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li {
|
||||||
|
position: relative;
|
||||||
|
font-size: 24px;
|
||||||
|
border-bottom: 1px solid #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .edit {
|
||||||
|
display: block;
|
||||||
|
width: 506px;
|
||||||
|
padding: 13px 17px 12px 17px;
|
||||||
|
margin: 0 0 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
text-align: center;
|
||||||
|
width: 40px;
|
||||||
|
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||||
|
height: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto 0;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:checked:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li label {
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 15px 60px 15px 15px;
|
||||||
|
margin-left: 45px;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.completed label {
|
||||||
|
color: #d9d9d9;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #cc9a9a;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:after {
|
||||||
|
content: '×';
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing:last-child {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
color: #777;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 0 -3px #f6f6f6,
|
||||||
|
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0 16px 0 -6px #f6f6f6,
|
||||||
|
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count strong {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a {
|
||||||
|
color: inherit;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected,
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed,
|
||||||
|
html .clear-completed:active {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin: 65px auto 0;
|
||||||
|
color: #bfbfbf;
|
||||||
|
font-size: 10px;
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hack to remove background from Mobile Safari.
|
||||||
|
Can't use it globally since it destroys checkboxes in Firefox
|
||||||
|
*/
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
.toggle-all,
|
||||||
|
.todo-list li .toggle {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
.footer {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
381
bench/implementations/elm-0.17-optimized/Todo.elm
Executable file
381
bench/implementations/elm-0.17-optimized/Todo.elm
Executable file
|
|
@ -0,0 +1,381 @@
|
||||||
|
port module Todo exposing (main)
|
||||||
|
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
|
||||||
|
|
||||||
|
This application is broken up into three key parts:
|
||||||
|
|
||||||
|
1. Model - a full definition of the application's state
|
||||||
|
2. Update - a way to step the application state forward
|
||||||
|
3. View - a way to visualize our application state with HTML
|
||||||
|
|
||||||
|
This clean division of concerns is a core part of Elm. You can read more about
|
||||||
|
this in <http://guide.elm-lang.org/architecture/index.html>
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.App as App
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (..)
|
||||||
|
import Html.Keyed as Keyed
|
||||||
|
import Html.Lazy exposing (lazy, lazy2)
|
||||||
|
import Json.Decode as Json
|
||||||
|
import String
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
main : Program Never
|
||||||
|
main =
|
||||||
|
App.program
|
||||||
|
{ init = init
|
||||||
|
, view = view
|
||||||
|
, update = update
|
||||||
|
, subscriptions = \_ -> Sub.none
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
port focus : String -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
-- The full application state of our todo app.
|
||||||
|
type alias Model =
|
||||||
|
{ entries : List Entry
|
||||||
|
, field : String
|
||||||
|
, uid : Int
|
||||||
|
, visibility : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Entry =
|
||||||
|
{ description : String
|
||||||
|
, completed : Bool
|
||||||
|
, editing : Bool
|
||||||
|
, id : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
{ entries = []
|
||||||
|
, visibility = "All"
|
||||||
|
, field = ""
|
||||||
|
, uid = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
newEntry : String -> Int -> Entry
|
||||||
|
newEntry desc id =
|
||||||
|
{ description = desc
|
||||||
|
, completed = False
|
||||||
|
, editing = False
|
||||||
|
, id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : ( Model, Cmd Msg )
|
||||||
|
init =
|
||||||
|
emptyModel ! []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
{-| Users of our app can trigger messages by clicking and typing. These
|
||||||
|
messages are fed into the `update` function as they occur, letting us react
|
||||||
|
to them.
|
||||||
|
-}
|
||||||
|
type Msg
|
||||||
|
= NoOp
|
||||||
|
| UpdateField String
|
||||||
|
| EditingEntry Int Bool
|
||||||
|
| UpdateEntry Int String
|
||||||
|
| Add
|
||||||
|
| Delete Int
|
||||||
|
| DeleteComplete
|
||||||
|
| Check Int Bool
|
||||||
|
| CheckAll Bool
|
||||||
|
| ChangeVisibility String
|
||||||
|
|
||||||
|
|
||||||
|
-- How we update our Model on a given Msg?
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
NoOp ->
|
||||||
|
model ! []
|
||||||
|
|
||||||
|
Add ->
|
||||||
|
{ model
|
||||||
|
| uid = model.uid + 1
|
||||||
|
, field = ""
|
||||||
|
, entries =
|
||||||
|
if String.isEmpty model.field then
|
||||||
|
model.entries
|
||||||
|
else
|
||||||
|
model.entries ++ [newEntry model.field model.uid]
|
||||||
|
}
|
||||||
|
! []
|
||||||
|
|
||||||
|
UpdateField str ->
|
||||||
|
{ model | field = str }
|
||||||
|
! []
|
||||||
|
|
||||||
|
EditingEntry id isEditing ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
if t.id == id then { t | editing = isEditing } else t
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! [ focus ("#todo-" ++ toString id) ]
|
||||||
|
|
||||||
|
UpdateEntry id task ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
if t.id == id then { t | description = task } else t
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
Delete id ->
|
||||||
|
{ model | entries = List.filter (\t -> t.id /= id) model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
DeleteComplete ->
|
||||||
|
{ model | entries = List.filter (not << .completed) model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
Check id isCompleted ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
if t.id == id then { t | completed = isCompleted } else t
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
CheckAll isCompleted ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
{ t | completed = isCompleted }
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
ChangeVisibility visibility ->
|
||||||
|
{ model | visibility = visibility }
|
||||||
|
! []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view model =
|
||||||
|
div
|
||||||
|
[ class "todomvc-wrapper"
|
||||||
|
, style [ ("visibility", "hidden") ]
|
||||||
|
]
|
||||||
|
[ section
|
||||||
|
[ class "todoapp" ]
|
||||||
|
[ lazy viewInput model.field
|
||||||
|
, lazy2 viewEntries model.visibility model.entries
|
||||||
|
, lazy2 viewControls model.visibility model.entries
|
||||||
|
]
|
||||||
|
, infoFooter
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewInput : String -> Html Msg
|
||||||
|
viewInput task =
|
||||||
|
header
|
||||||
|
[ class "header" ]
|
||||||
|
[ h1 [] [ text "todos" ]
|
||||||
|
, input
|
||||||
|
[ class "new-todo"
|
||||||
|
, placeholder "What needs to be done?"
|
||||||
|
, autofocus True
|
||||||
|
, value task
|
||||||
|
, name "newTodo"
|
||||||
|
, onInput UpdateField
|
||||||
|
, onEnter Add
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
onEnter : Msg -> Attribute Msg
|
||||||
|
onEnter msg =
|
||||||
|
let
|
||||||
|
tagger code =
|
||||||
|
if code == 13 then msg else NoOp
|
||||||
|
in
|
||||||
|
on "keydown" (Json.map tagger keyCode)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW ALL ENTRIES
|
||||||
|
|
||||||
|
|
||||||
|
viewEntries : String -> List Entry -> Html Msg
|
||||||
|
viewEntries visibility entries =
|
||||||
|
let
|
||||||
|
isVisible todo =
|
||||||
|
case visibility of
|
||||||
|
"Completed" -> todo.completed
|
||||||
|
"Active" -> not todo.completed
|
||||||
|
_ -> True
|
||||||
|
|
||||||
|
allCompleted =
|
||||||
|
List.all .completed entries
|
||||||
|
|
||||||
|
cssVisibility =
|
||||||
|
if List.isEmpty entries then "hidden" else "visible"
|
||||||
|
in
|
||||||
|
section
|
||||||
|
[ class "main"
|
||||||
|
, style [ ("visibility", cssVisibility) ]
|
||||||
|
]
|
||||||
|
[ input
|
||||||
|
[ class "toggle-all"
|
||||||
|
, type' "checkbox"
|
||||||
|
, name "toggle"
|
||||||
|
, checked allCompleted
|
||||||
|
, onClick (CheckAll (not allCompleted))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ for "toggle-all" ]
|
||||||
|
[ text "Mark all as complete" ]
|
||||||
|
, Keyed.ul [ class "todo-list" ] <|
|
||||||
|
List.map viewKeyedEntry (List.filter isVisible entries)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW INDIVIDUAL ENTRIES
|
||||||
|
|
||||||
|
|
||||||
|
viewKeyedEntry : Entry -> (String, Html Msg)
|
||||||
|
viewKeyedEntry todo =
|
||||||
|
( toString todo.id, lazy viewEntry todo )
|
||||||
|
|
||||||
|
|
||||||
|
viewEntry : Entry -> Html Msg
|
||||||
|
viewEntry todo =
|
||||||
|
li
|
||||||
|
[ classList [ ("completed", todo.completed), ("editing", todo.editing) ] ]
|
||||||
|
[ div
|
||||||
|
[ class "view" ]
|
||||||
|
[ input
|
||||||
|
[ class "toggle"
|
||||||
|
, type' "checkbox"
|
||||||
|
, checked todo.completed
|
||||||
|
, onClick (Check todo.id (not todo.completed))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ onDoubleClick (EditingEntry todo.id True) ]
|
||||||
|
[ text todo.description ]
|
||||||
|
, button
|
||||||
|
[ class "destroy"
|
||||||
|
, onClick (Delete todo.id)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, input
|
||||||
|
[ class "edit"
|
||||||
|
, value todo.description
|
||||||
|
, name "title"
|
||||||
|
, id ("todo-" ++ toString todo.id)
|
||||||
|
, onInput (UpdateEntry todo.id)
|
||||||
|
, onBlur (EditingEntry todo.id False)
|
||||||
|
, onEnter (EditingEntry todo.id False)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW CONTROLS AND FOOTER
|
||||||
|
|
||||||
|
|
||||||
|
viewControls : String -> List Entry -> Html Msg
|
||||||
|
viewControls visibility entries =
|
||||||
|
let
|
||||||
|
entriesCompleted =
|
||||||
|
List.length (List.filter .completed entries)
|
||||||
|
|
||||||
|
entriesLeft =
|
||||||
|
List.length entries - entriesCompleted
|
||||||
|
in
|
||||||
|
footer
|
||||||
|
[ class "footer"
|
||||||
|
, hidden (List.isEmpty entries)
|
||||||
|
]
|
||||||
|
[ lazy viewControlsCount entriesLeft
|
||||||
|
, lazy viewControlsFilters visibility
|
||||||
|
, lazy viewControlsClear entriesCompleted
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewControlsCount : Int -> Html Msg
|
||||||
|
viewControlsCount entriesLeft =
|
||||||
|
let
|
||||||
|
item_ =
|
||||||
|
if entriesLeft == 1 then " item" else " items"
|
||||||
|
in
|
||||||
|
span
|
||||||
|
[ class "todo-count" ]
|
||||||
|
[ strong [] [ text (toString entriesLeft) ]
|
||||||
|
, text (item_ ++ " left")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewControlsFilters : String -> Html Msg
|
||||||
|
viewControlsFilters visibility =
|
||||||
|
ul
|
||||||
|
[ class "filters" ]
|
||||||
|
[ visibilitySwap "#/" "All" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap "#/active" "Active" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap "#/completed" "Completed" visibility
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
visibilitySwap : String -> String -> String -> Html Msg
|
||||||
|
visibilitySwap uri visibility actualVisibility =
|
||||||
|
li
|
||||||
|
[ onClick (ChangeVisibility visibility) ]
|
||||||
|
[ a [ href uri, classList [("selected", visibility == actualVisibility)] ]
|
||||||
|
[ text visibility ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewControlsClear : Int -> Html Msg
|
||||||
|
viewControlsClear entriesCompleted =
|
||||||
|
button
|
||||||
|
[ class "clear-completed"
|
||||||
|
, hidden (entriesCompleted == 0)
|
||||||
|
, onClick DeleteComplete
|
||||||
|
]
|
||||||
|
[ text ("Clear completed (" ++ toString entriesCompleted ++ ")")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
infoFooter : Html msg
|
||||||
|
infoFooter =
|
||||||
|
footer [ class "info" ]
|
||||||
|
[ p [] [ text "Double-click to edit a todo" ]
|
||||||
|
, p []
|
||||||
|
[ text "Written by "
|
||||||
|
, a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
|
||||||
|
]
|
||||||
|
, p []
|
||||||
|
[ text "Part of "
|
||||||
|
, a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
15
bench/implementations/elm-0.17-optimized/elm-package.json
Normal file
15
bench/implementations/elm-0.17-optimized/elm-package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"summary": "TodoMVC created with Elm and elm-html",
|
||||||
|
"repository": "https://github.com/evancz/elm-todomvc.git",
|
||||||
|
"license": "BSD3",
|
||||||
|
"source-directories": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"exposed-modules": [],
|
||||||
|
"dependencies": {
|
||||||
|
"elm-lang/core": "4.0.0 <= v < 5.0.0",
|
||||||
|
"elm-lang/html": "1.0.0 <= v < 2.0.0"
|
||||||
|
},
|
||||||
|
"elm-version": "0.17.0 <= v < 0.18.0"
|
||||||
|
}
|
||||||
8282
bench/implementations/elm-0.17-optimized/elm.js
Executable file
8282
bench/implementations/elm-0.17-optimized/elm.js
Executable file
File diff suppressed because it is too large
Load diff
29
bench/implementations/elm-0.17-optimized/index.html
Normal file
29
bench/implementations/elm-0.17-optimized/index.html
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Elm • TodoMVC</title>
|
||||||
|
<script type="text/javascript" src="elm.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var todomvc = Elm.Todo.fullscreen();
|
||||||
|
todomvc.ports.focus.subscribe(function(selector)
|
||||||
|
{
|
||||||
|
setTimeout(function()
|
||||||
|
{
|
||||||
|
var nodes = document.querySelectorAll(selector);
|
||||||
|
if (nodes.length === 1 && document.activeElement !== nodes[0])
|
||||||
|
{
|
||||||
|
nodes[0].focus();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
382
bench/implementations/elm-0.17-optimized/style.css
Normal file
382
bench/implementations/elm-0.17-optimized/style.css
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todomvc-wrapper {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.4em;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #4d4d4d;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="checkbox"] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp {
|
||||||
|
background: #fff;
|
||||||
|
margin: 130px 0 40px 0;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-webkit-input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-moz-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp h1 {
|
||||||
|
position: absolute;
|
||||||
|
top: -155px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(175, 47, 47, 0.15);
|
||||||
|
-webkit-text-rendering: optimizeLegibility;
|
||||||
|
-moz-text-rendering: optimizeLegibility;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo,
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: 1.4em;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo {
|
||||||
|
padding: 16px 16px 16px 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(0, 0, 0, 0.003);
|
||||||
|
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='toggle-all'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
position: absolute;
|
||||||
|
top: -55px;
|
||||||
|
left: -12px;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:before {
|
||||||
|
content: '❯';
|
||||||
|
font-size: 22px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
padding: 10px 27px 10px 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:checked:before {
|
||||||
|
color: #737373;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li {
|
||||||
|
position: relative;
|
||||||
|
font-size: 24px;
|
||||||
|
border-bottom: 1px solid #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .edit {
|
||||||
|
display: block;
|
||||||
|
width: 506px;
|
||||||
|
padding: 13px 17px 12px 17px;
|
||||||
|
margin: 0 0 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
text-align: center;
|
||||||
|
width: 40px;
|
||||||
|
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||||
|
height: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto 0;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:checked:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li label {
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 15px 60px 15px 15px;
|
||||||
|
margin-left: 45px;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.completed label {
|
||||||
|
color: #d9d9d9;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #cc9a9a;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:after {
|
||||||
|
content: '×';
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing:last-child {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
color: #777;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 0 -3px #f6f6f6,
|
||||||
|
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0 16px 0 -6px #f6f6f6,
|
||||||
|
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count strong {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a {
|
||||||
|
color: inherit;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected,
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed,
|
||||||
|
html .clear-completed:active {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin: 65px auto 0;
|
||||||
|
color: #bfbfbf;
|
||||||
|
font-size: 10px;
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hack to remove background from Mobile Safari.
|
||||||
|
Can't use it globally since it destroys checkboxes in Firefox
|
||||||
|
*/
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
.toggle-all,
|
||||||
|
.todo-list li .toggle {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
.footer {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
374
bench/implementations/elm-0.17/Todo.elm
Normal file
374
bench/implementations/elm-0.17/Todo.elm
Normal file
|
|
@ -0,0 +1,374 @@
|
||||||
|
port module Todo exposing (main)
|
||||||
|
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
|
||||||
|
|
||||||
|
This application is broken up into three key parts:
|
||||||
|
|
||||||
|
1. Model - a full definition of the application's state
|
||||||
|
2. Update - a way to step the application state forward
|
||||||
|
3. View - a way to visualize our application state with HTML
|
||||||
|
|
||||||
|
This clean division of concerns is a core part of Elm. You can read more about
|
||||||
|
this in <http://guide.elm-lang.org/architecture/index.html>
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.App as App
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (..)
|
||||||
|
import Json.Decode as Json
|
||||||
|
import String
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
main : Program Never
|
||||||
|
main =
|
||||||
|
App.program
|
||||||
|
{ init = init
|
||||||
|
, view = view
|
||||||
|
, update = update
|
||||||
|
, subscriptions = \_ -> Sub.none
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
port focus : String -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
-- The full application state of our todo app.
|
||||||
|
type alias Model =
|
||||||
|
{ entries : List Entry
|
||||||
|
, field : String
|
||||||
|
, uid : Int
|
||||||
|
, visibility : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Entry =
|
||||||
|
{ description : String
|
||||||
|
, completed : Bool
|
||||||
|
, editing : Bool
|
||||||
|
, id : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
{ entries = []
|
||||||
|
, visibility = "All"
|
||||||
|
, field = ""
|
||||||
|
, uid = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
newEntry : String -> Int -> Entry
|
||||||
|
newEntry desc id =
|
||||||
|
{ description = desc
|
||||||
|
, completed = False
|
||||||
|
, editing = False
|
||||||
|
, id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : ( Model, Cmd Msg )
|
||||||
|
init =
|
||||||
|
emptyModel ! []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
{-| Users of our app can trigger messages by clicking and typing. These
|
||||||
|
messages are fed into the `update` function as they occur, letting us react
|
||||||
|
to them.
|
||||||
|
-}
|
||||||
|
type Msg
|
||||||
|
= NoOp
|
||||||
|
| UpdateField String
|
||||||
|
| EditingEntry Int Bool
|
||||||
|
| UpdateEntry Int String
|
||||||
|
| Add
|
||||||
|
| Delete Int
|
||||||
|
| DeleteComplete
|
||||||
|
| Check Int Bool
|
||||||
|
| CheckAll Bool
|
||||||
|
| ChangeVisibility String
|
||||||
|
|
||||||
|
|
||||||
|
-- How we update our Model on a given Msg?
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
NoOp ->
|
||||||
|
model ! []
|
||||||
|
|
||||||
|
Add ->
|
||||||
|
{ model
|
||||||
|
| uid = model.uid + 1
|
||||||
|
, field = ""
|
||||||
|
, entries =
|
||||||
|
if String.isEmpty model.field then
|
||||||
|
model.entries
|
||||||
|
else
|
||||||
|
model.entries ++ [newEntry model.field model.uid]
|
||||||
|
}
|
||||||
|
! []
|
||||||
|
|
||||||
|
UpdateField str ->
|
||||||
|
{ model | field = str }
|
||||||
|
! []
|
||||||
|
|
||||||
|
EditingEntry id isEditing ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
if t.id == id then { t | editing = isEditing } else t
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! [ focus ("#todo-" ++ toString id) ]
|
||||||
|
|
||||||
|
UpdateEntry id task ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
if t.id == id then { t | description = task } else t
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
Delete id ->
|
||||||
|
{ model | entries = List.filter (\t -> t.id /= id) model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
DeleteComplete ->
|
||||||
|
{ model | entries = List.filter (not << .completed) model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
Check id isCompleted ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
if t.id == id then { t | completed = isCompleted } else t
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
CheckAll isCompleted ->
|
||||||
|
let
|
||||||
|
updateEntry t =
|
||||||
|
{ t | completed = isCompleted }
|
||||||
|
in
|
||||||
|
{ model | entries = List.map updateEntry model.entries }
|
||||||
|
! []
|
||||||
|
|
||||||
|
ChangeVisibility visibility ->
|
||||||
|
{ model | visibility = visibility }
|
||||||
|
! []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view model =
|
||||||
|
div
|
||||||
|
[ class "todomvc-wrapper"
|
||||||
|
, style [ ("visibility", "hidden") ]
|
||||||
|
]
|
||||||
|
[ section
|
||||||
|
[ class "todoapp" ]
|
||||||
|
[ viewInput model.field
|
||||||
|
, viewEntries model.visibility model.entries
|
||||||
|
, viewControls model.visibility model.entries
|
||||||
|
]
|
||||||
|
, infoFooter
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewInput : String -> Html Msg
|
||||||
|
viewInput task =
|
||||||
|
header
|
||||||
|
[ class "header" ]
|
||||||
|
[ h1 [] [ text "todos" ]
|
||||||
|
, input
|
||||||
|
[ class "new-todo"
|
||||||
|
, placeholder "What needs to be done?"
|
||||||
|
, autofocus True
|
||||||
|
, value task
|
||||||
|
, name "newTodo"
|
||||||
|
, onInput UpdateField
|
||||||
|
, onEnter Add
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
onEnter : Msg -> Attribute Msg
|
||||||
|
onEnter msg =
|
||||||
|
let
|
||||||
|
tagger code =
|
||||||
|
if code == 13 then msg else NoOp
|
||||||
|
in
|
||||||
|
on "keydown" (Json.map tagger keyCode)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW ALL ENTRIES
|
||||||
|
|
||||||
|
|
||||||
|
viewEntries : String -> List Entry -> Html Msg
|
||||||
|
viewEntries visibility entries =
|
||||||
|
let
|
||||||
|
isVisible todo =
|
||||||
|
case visibility of
|
||||||
|
"Completed" -> todo.completed
|
||||||
|
"Active" -> not todo.completed
|
||||||
|
_ -> True
|
||||||
|
|
||||||
|
allCompleted =
|
||||||
|
List.all .completed entries
|
||||||
|
|
||||||
|
cssVisibility =
|
||||||
|
if List.isEmpty entries then "hidden" else "visible"
|
||||||
|
in
|
||||||
|
section
|
||||||
|
[ class "main"
|
||||||
|
, style [ ("visibility", cssVisibility) ]
|
||||||
|
]
|
||||||
|
[ input
|
||||||
|
[ class "toggle-all"
|
||||||
|
, type' "checkbox"
|
||||||
|
, name "toggle"
|
||||||
|
, checked allCompleted
|
||||||
|
, onClick (CheckAll (not allCompleted))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ for "toggle-all" ]
|
||||||
|
[ text "Mark all as complete" ]
|
||||||
|
, ul [ class "todo-list" ] <|
|
||||||
|
List.map viewEntry (List.filter isVisible entries)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW INDIVIDUAL ENTRIES
|
||||||
|
|
||||||
|
|
||||||
|
viewEntry : Entry -> Html Msg
|
||||||
|
viewEntry todo =
|
||||||
|
li
|
||||||
|
[ classList [ ("completed", todo.completed), ("editing", todo.editing) ] ]
|
||||||
|
[ div
|
||||||
|
[ class "view" ]
|
||||||
|
[ input
|
||||||
|
[ class "toggle"
|
||||||
|
, type' "checkbox"
|
||||||
|
, checked todo.completed
|
||||||
|
, onClick (Check todo.id (not todo.completed))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label
|
||||||
|
[ onDoubleClick (EditingEntry todo.id True) ]
|
||||||
|
[ text todo.description ]
|
||||||
|
, button
|
||||||
|
[ class "destroy"
|
||||||
|
, onClick (Delete todo.id)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, input
|
||||||
|
[ class "edit"
|
||||||
|
, value todo.description
|
||||||
|
, name "title"
|
||||||
|
, id ("todo-" ++ toString todo.id)
|
||||||
|
, onInput (UpdateEntry todo.id)
|
||||||
|
, onBlur (EditingEntry todo.id False)
|
||||||
|
, onEnter (EditingEntry todo.id False)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW CONTROLS AND FOOTER
|
||||||
|
|
||||||
|
|
||||||
|
viewControls : String -> List Entry -> Html Msg
|
||||||
|
viewControls visibility entries =
|
||||||
|
let
|
||||||
|
entriesCompleted =
|
||||||
|
List.length (List.filter .completed entries)
|
||||||
|
|
||||||
|
entriesLeft =
|
||||||
|
List.length entries - entriesCompleted
|
||||||
|
in
|
||||||
|
footer
|
||||||
|
[ class "footer"
|
||||||
|
, hidden (List.isEmpty entries)
|
||||||
|
]
|
||||||
|
[ viewControlsCount entriesLeft
|
||||||
|
, viewControlsFilters visibility
|
||||||
|
, viewControlsClear entriesCompleted
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewControlsCount : Int -> Html Msg
|
||||||
|
viewControlsCount entriesLeft =
|
||||||
|
let
|
||||||
|
item_ =
|
||||||
|
if entriesLeft == 1 then " item" else " items"
|
||||||
|
in
|
||||||
|
span
|
||||||
|
[ class "todo-count" ]
|
||||||
|
[ strong [] [ text (toString entriesLeft) ]
|
||||||
|
, text (item_ ++ " left")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewControlsFilters : String -> Html Msg
|
||||||
|
viewControlsFilters visibility =
|
||||||
|
ul
|
||||||
|
[ class "filters" ]
|
||||||
|
[ visibilitySwap "#/" "All" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap "#/active" "Active" visibility
|
||||||
|
, text " "
|
||||||
|
, visibilitySwap "#/completed" "Completed" visibility
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
visibilitySwap : String -> String -> String -> Html Msg
|
||||||
|
visibilitySwap uri visibility actualVisibility =
|
||||||
|
li
|
||||||
|
[ onClick (ChangeVisibility visibility) ]
|
||||||
|
[ a [ href uri, classList [("selected", visibility == actualVisibility)] ]
|
||||||
|
[ text visibility ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewControlsClear : Int -> Html Msg
|
||||||
|
viewControlsClear entriesCompleted =
|
||||||
|
button
|
||||||
|
[ class "clear-completed"
|
||||||
|
, hidden (entriesCompleted == 0)
|
||||||
|
, onClick DeleteComplete
|
||||||
|
]
|
||||||
|
[ text ("Clear completed (" ++ toString entriesCompleted ++ ")")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
infoFooter : Html msg
|
||||||
|
infoFooter =
|
||||||
|
footer [ class "info" ]
|
||||||
|
[ p [] [ text "Double-click to edit a todo" ]
|
||||||
|
, p []
|
||||||
|
[ text "Written by "
|
||||||
|
, a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
|
||||||
|
]
|
||||||
|
, p []
|
||||||
|
[ text "Part of "
|
||||||
|
, a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
15
bench/implementations/elm-0.17/elm-package.json
Normal file
15
bench/implementations/elm-0.17/elm-package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"summary": "TodoMVC created with Elm and elm-html",
|
||||||
|
"repository": "https://github.com/evancz/elm-todomvc.git",
|
||||||
|
"license": "BSD3",
|
||||||
|
"source-directories": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"exposed-modules": [],
|
||||||
|
"dependencies": {
|
||||||
|
"elm-lang/core": "4.0.0 <= v < 5.0.0",
|
||||||
|
"elm-lang/html": "1.0.0 <= v < 2.0.0"
|
||||||
|
},
|
||||||
|
"elm-version": "0.17.0 <= v < 0.18.0"
|
||||||
|
}
|
||||||
8512
bench/implementations/elm-0.17/elm.js
Normal file
8512
bench/implementations/elm-0.17/elm.js
Normal file
File diff suppressed because it is too large
Load diff
29
bench/implementations/elm-0.17/index.html
Normal file
29
bench/implementations/elm-0.17/index.html
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Elm • TodoMVC</title>
|
||||||
|
<script type="text/javascript" src="elm.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var todomvc = Elm.Todo.fullscreen();
|
||||||
|
todomvc.ports.focus.subscribe(function(selector)
|
||||||
|
{
|
||||||
|
setTimeout(function()
|
||||||
|
{
|
||||||
|
var nodes = document.querySelectorAll(selector);
|
||||||
|
if (nodes.length === 1 && document.activeElement !== nodes[0])
|
||||||
|
{
|
||||||
|
nodes[0].focus();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
382
bench/implementations/elm-0.17/style.css
Normal file
382
bench/implementations/elm-0.17/style.css
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todomvc-wrapper {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.4em;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #4d4d4d;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="checkbox"] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp {
|
||||||
|
background: #fff;
|
||||||
|
margin: 130px 0 40px 0;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-webkit-input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-moz-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp h1 {
|
||||||
|
position: absolute;
|
||||||
|
top: -155px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(175, 47, 47, 0.15);
|
||||||
|
-webkit-text-rendering: optimizeLegibility;
|
||||||
|
-moz-text-rendering: optimizeLegibility;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo,
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: 1.4em;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo {
|
||||||
|
padding: 16px 16px 16px 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(0, 0, 0, 0.003);
|
||||||
|
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='toggle-all'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
position: absolute;
|
||||||
|
top: -55px;
|
||||||
|
left: -12px;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:before {
|
||||||
|
content: '❯';
|
||||||
|
font-size: 22px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
padding: 10px 27px 10px 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:checked:before {
|
||||||
|
color: #737373;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li {
|
||||||
|
position: relative;
|
||||||
|
font-size: 24px;
|
||||||
|
border-bottom: 1px solid #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .edit {
|
||||||
|
display: block;
|
||||||
|
width: 506px;
|
||||||
|
padding: 13px 17px 12px 17px;
|
||||||
|
margin: 0 0 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
text-align: center;
|
||||||
|
width: 40px;
|
||||||
|
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||||
|
height: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto 0;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:checked:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li label {
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 15px 60px 15px 15px;
|
||||||
|
margin-left: 45px;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.completed label {
|
||||||
|
color: #d9d9d9;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #cc9a9a;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:after {
|
||||||
|
content: '×';
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing:last-child {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
color: #777;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 0 -3px #f6f6f6,
|
||||||
|
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0 16px 0 -6px #f6f6f6,
|
||||||
|
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count strong {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a {
|
||||||
|
color: inherit;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected,
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed,
|
||||||
|
html .clear-completed:active {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin: 65px auto 0;
|
||||||
|
color: #bfbfbf;
|
||||||
|
font-size: 10px;
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hack to remove background from Mobile Safari.
|
||||||
|
Can't use it globally since it destroys checkboxes in Firefox
|
||||||
|
*/
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
.toggle-all,
|
||||||
|
.todo-list li .toggle {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
.footer {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
bench/implementations/ember-2.6.3/.gitignore
vendored
Normal file
16
bench/implementations/ember-2.6.3/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
/tmp
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/bower_components
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage/*
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
testem.log
|
||||||
15
bench/implementations/ember-2.6.3/README.md
Executable file
15
bench/implementations/ember-2.6.3/README.md
Executable file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Ember.js TodoMVC for Benchmarking
|
||||||
|
|
||||||
|
This is an update of the
|
||||||
|
_[Ember.js TodoMVC example](https://github.com/tastejs/todomvc/tree/gh-pages/examples/emberjs)_
|
||||||
|
with the following changes:
|
||||||
|
|
||||||
|
* Upgrade to Ember 2.6.2
|
||||||
|
* Configure router to support relative URLs
|
||||||
|
* Remove localStorage functionality - not relevant to measuring render performance
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
1. `npm install`
|
||||||
|
2. `npm run make`
|
||||||
|
3. `open dist/index.html`
|
||||||
18
bench/implementations/ember-2.6.3/app/app.js
Executable file
18
bench/implementations/ember-2.6.3/app/app.js
Executable file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import Resolver from './resolver';
|
||||||
|
import loadInitializers from 'ember-load-initializers';
|
||||||
|
import config from './config/environment';
|
||||||
|
|
||||||
|
let App;
|
||||||
|
|
||||||
|
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||||
|
|
||||||
|
App = Ember.Application.extend({
|
||||||
|
modulePrefix: config.modulePrefix,
|
||||||
|
podModulePrefix: config.podModulePrefix,
|
||||||
|
Resolver
|
||||||
|
});
|
||||||
|
|
||||||
|
loadInitializers(App, config.modulePrefix);
|
||||||
|
|
||||||
|
export default App;
|
||||||
48
bench/implementations/ember-2.6.3/app/components/todo-item.js
Executable file
48
bench/implementations/ember-2.6.3/app/components/todo-item.js
Executable file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
repo: Ember.inject.service(),
|
||||||
|
tagName: 'li',
|
||||||
|
editing: false,
|
||||||
|
classNameBindings: ['todo.completed', 'editing'],
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
startEditing() {
|
||||||
|
this.get('onStartEdit')();
|
||||||
|
this.set('editing', true);
|
||||||
|
Ember.run.scheduleOnce('afterRender', this, 'focusInput');
|
||||||
|
},
|
||||||
|
|
||||||
|
doneEditing(todoTitle) {
|
||||||
|
if (!this.get('editing')) { return; }
|
||||||
|
if (Ember.isBlank(todoTitle)) {
|
||||||
|
this.send('removeTodo');
|
||||||
|
} else {
|
||||||
|
this.set('todo.title', todoTitle.trim());
|
||||||
|
this.set('editing', false);
|
||||||
|
this.get('onEndEdit')();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleKeydown(e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
e.target.blur();
|
||||||
|
} else if (e.keyCode === 27) {
|
||||||
|
this.set('editing', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleCompleted(e) {
|
||||||
|
let todo = this.get('todo');
|
||||||
|
Ember.set(todo, 'completed', e.target.checked);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTodo() {
|
||||||
|
this.get('repo').delete(this.get('todo'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
focusInput() {
|
||||||
|
this.element.querySelector('input.edit').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
26
bench/implementations/ember-2.6.3/app/components/todo-list.js
Executable file
26
bench/implementations/ember-2.6.3/app/components/todo-list.js
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
repo: Ember.inject.service(),
|
||||||
|
tagName: 'section',
|
||||||
|
elementId: 'main',
|
||||||
|
canToggle: true,
|
||||||
|
allCompleted: Ember.computed('todos.@each.completed', function () {
|
||||||
|
return this.get('todos').isEvery('completed');
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
enableToggle() {
|
||||||
|
this.set('canToggle', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
disableToggle() {
|
||||||
|
this.set('canToggle', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleAll() {
|
||||||
|
let allCompleted = this.get('allCompleted');
|
||||||
|
this.get('todos').forEach(todo => Ember.set(todo, 'completed', !allCompleted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
5
bench/implementations/ember-2.6.3/app/controllers/active.js
Executable file
5
bench/implementations/ember-2.6.3/app/controllers/active.js
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
todos: Ember.computed.filterBy('model', 'completed', false)
|
||||||
|
});
|
||||||
19
bench/implementations/ember-2.6.3/app/controllers/application.js
Executable file
19
bench/implementations/ember-2.6.3/app/controllers/application.js
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
repo: Ember.inject.service(),
|
||||||
|
remaining: Ember.computed.filterBy('model', 'completed', false),
|
||||||
|
completed: Ember.computed.filterBy('model', 'completed'),
|
||||||
|
actions: {
|
||||||
|
createTodo(e) {
|
||||||
|
if (e.keyCode === 13 && !Ember.isBlank(e.target.value)) {
|
||||||
|
this.get('repo').add({ title: e.target.value.trim(), completed: false });
|
||||||
|
e.target.value = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clearCompleted() {
|
||||||
|
this.get('model').removeObjects(this.get('completed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
5
bench/implementations/ember-2.6.3/app/controllers/completed.js
Executable file
5
bench/implementations/ember-2.6.3/app/controllers/completed.js
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
todos: Ember.computed.filterBy('model', 'completed', true)
|
||||||
|
});
|
||||||
7
bench/implementations/ember-2.6.3/app/helpers/gt.js
Executable file
7
bench/implementations/ember-2.6.3/app/helpers/gt.js
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export function gt([n1, n2]/*, hash*/) {
|
||||||
|
return n1 > n2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Ember.Helper.helper(gt);
|
||||||
8
bench/implementations/ember-2.6.3/app/helpers/pluralize.js
Executable file
8
bench/implementations/ember-2.6.3/app/helpers/pluralize.js
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import { pluralize } from 'ember-inflector';
|
||||||
|
|
||||||
|
export function pluralizeHelper([singular, count]/*, hash*/) {
|
||||||
|
return count === 1 ? singular : pluralize(singular);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Ember.Helper.helper(pluralizeHelper);
|
||||||
24
bench/implementations/ember-2.6.3/app/index.html
Executable file
24
bench/implementations/ember-2.6.3/app/index.html
Executable file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>Todomvc</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
{{content-for "head"}}
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/vendor.css">
|
||||||
|
<link rel="stylesheet" href="assets/todomvc.css">
|
||||||
|
|
||||||
|
{{content-for "head-footer"}}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{content-for "body"}}
|
||||||
|
|
||||||
|
<script src="assets/vendor.js"></script>
|
||||||
|
<script src="assets/todomvc.js"></script>
|
||||||
|
|
||||||
|
{{content-for "body-footer"}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
bench/implementations/ember-2.6.3/app/resolver.js
Executable file
3
bench/implementations/ember-2.6.3/app/resolver.js
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Resolver from 'ember-resolver';
|
||||||
|
|
||||||
|
export default Resolver;
|
||||||
13
bench/implementations/ember-2.6.3/app/router.js
Executable file
13
bench/implementations/ember-2.6.3/app/router.js
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import config from './config/environment';
|
||||||
|
|
||||||
|
const Router = Ember.Router.extend({
|
||||||
|
location: config.locationType
|
||||||
|
});
|
||||||
|
|
||||||
|
Router.map(function () {
|
||||||
|
this.route('active');
|
||||||
|
this.route('completed');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Router;
|
||||||
8
bench/implementations/ember-2.6.3/app/routes/application.js
Executable file
8
bench/implementations/ember-2.6.3/app/routes/application.js
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
repo: Ember.inject.service(),
|
||||||
|
model() {
|
||||||
|
return this.get('repo').findAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
19
bench/implementations/ember-2.6.3/app/services/repo.js
Executable file
19
bench/implementations/ember-2.6.3/app/services/repo.js
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Service.extend({
|
||||||
|
lastId: 0,
|
||||||
|
data: null,
|
||||||
|
findAll() {
|
||||||
|
return this.get('data') || this.set('data', []);
|
||||||
|
},
|
||||||
|
|
||||||
|
add(attrs) {
|
||||||
|
let todo = Object.assign({ id: this.incrementProperty('lastId') }, attrs);
|
||||||
|
this.get('data').pushObject(todo);
|
||||||
|
return todo;
|
||||||
|
},
|
||||||
|
|
||||||
|
delete(todo) {
|
||||||
|
this.get('data').removeObject(todo);
|
||||||
|
}
|
||||||
|
});
|
||||||
0
bench/implementations/ember-2.6.3/app/styles/app.css
Executable file
0
bench/implementations/ember-2.6.3/app/styles/app.css
Executable file
1
bench/implementations/ember-2.6.3/app/templates/active.hbs
Executable file
1
bench/implementations/ember-2.6.3/app/templates/active.hbs
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
{{todo-list todos=todos}}
|
||||||
29
bench/implementations/ember-2.6.3/app/templates/application.hbs
Executable file
29
bench/implementations/ember-2.6.3/app/templates/application.hbs
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<section id="todoapp">
|
||||||
|
<header id="header">
|
||||||
|
<h1>todos</h1>
|
||||||
|
<input type="text" id="new-todo" class="new-todo" onkeydown={{action 'createTodo'}} placeholder="What needs to be done?" autofocus>
|
||||||
|
</header>
|
||||||
|
{{outlet}}
|
||||||
|
{{#if (gt model.length 0)}}
|
||||||
|
<footer id="footer">
|
||||||
|
<span id="todo-count"><strong>{{remaining.length}}</strong> {{pluralize 'item' remaining.length}} left</span>
|
||||||
|
<ul id="filters">
|
||||||
|
<li>{{#link-to "index" activeClass="selected"}}All{{/link-to}}</li>
|
||||||
|
<li>{{#link-to "active" activeClass="selected"}}Active{{/link-to}}</li>
|
||||||
|
<li>{{#link-to "completed" activeClass="selected"}}Completed{{/link-to}}</li>
|
||||||
|
</ul>
|
||||||
|
{{#if completed.length}}
|
||||||
|
<button id="clear-completed" onclick={{action 'clearCompleted'}}>Clear completed</button>
|
||||||
|
{{/if}}
|
||||||
|
</footer>
|
||||||
|
{{/if}}
|
||||||
|
</section>
|
||||||
|
<footer id="info">
|
||||||
|
<p>Double-click to edit a todo</p>
|
||||||
|
<p>
|
||||||
|
Created by
|
||||||
|
<a href="http://github.com/cibernox">Miguel Camba</a>,
|
||||||
|
<a href="http://github.com/addyosmani">Addy Osmani</a>
|
||||||
|
</p>
|
||||||
|
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||||
|
</footer>
|
||||||
1
bench/implementations/ember-2.6.3/app/templates/completed.hbs
Executable file
1
bench/implementations/ember-2.6.3/app/templates/completed.hbs
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
{{todo-list todos=todos}}
|
||||||
6
bench/implementations/ember-2.6.3/app/templates/components/todo-item.hbs
Executable file
6
bench/implementations/ember-2.6.3/app/templates/components/todo-item.hbs
Executable file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="view">
|
||||||
|
<input type="checkbox" class="toggle" checked={{todo.completed}} onchange={{action 'toggleCompleted'}}>
|
||||||
|
<label ondblclick={{action 'startEditing'}}>{{todo.title}}</label>
|
||||||
|
<button onclick={{action 'removeTodo'}} class="destroy"></button>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="edit" value={{todo.title}} onblur={{action 'doneEditing' value='target.value'}} onkeydown={{action 'handleKeydown'}} autofocus>
|
||||||
10
bench/implementations/ember-2.6.3/app/templates/components/todo-list.hbs
Executable file
10
bench/implementations/ember-2.6.3/app/templates/components/todo-list.hbs
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{{#if todos.length}}
|
||||||
|
{{#if canToggle}}
|
||||||
|
<input type="checkbox" id="toggle-all" checked={{allCompleted}} onchange={{action 'toggleAll'}}>
|
||||||
|
{{/if}}
|
||||||
|
<ul id="todo-list" class="todo-list">
|
||||||
|
{{#each todos as |todo|}}
|
||||||
|
{{todo-item todo=todo onStartEdit=(action 'disableToggle') onEndEdit=(action 'enableToggle')}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
3
bench/implementations/ember-2.6.3/app/templates/index.hbs
Executable file
3
bench/implementations/ember-2.6.3/app/templates/index.hbs
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{{#if model.length}}
|
||||||
|
{{todo-list todos=model}}
|
||||||
|
{{/if}}
|
||||||
9
bench/implementations/ember-2.6.3/bower.json
Executable file
9
bench/implementations/ember-2.6.3/bower.json
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "todomvc",
|
||||||
|
"dependencies": {
|
||||||
|
"ember": "~2.6.2",
|
||||||
|
"ember-cli-shims": "0.1.1",
|
||||||
|
"ember-cli-test-loader": "0.2.2",
|
||||||
|
"ember-qunit-notifications": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
47
bench/implementations/ember-2.6.3/config/environment.js
Executable file
47
bench/implementations/ember-2.6.3/config/environment.js
Executable file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* jshint node: true */
|
||||||
|
|
||||||
|
module.exports = function (environment) {
|
||||||
|
var ENV = {
|
||||||
|
modulePrefix: 'todomvc',
|
||||||
|
environment: environment,
|
||||||
|
baseURL: null,
|
||||||
|
locationType: 'none',
|
||||||
|
EmberENV: {
|
||||||
|
FEATURES: {
|
||||||
|
// Here you can enable experimental features on an ember canary build
|
||||||
|
// e.g. 'with-controller': true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
APP: {
|
||||||
|
// Here you can pass flags/options to your application instance
|
||||||
|
// when it is created
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (environment === 'development') {
|
||||||
|
// ENV.APP.LOG_RESOLVER = true;
|
||||||
|
// ENV.APP.LOG_ACTIVE_GENERATION = true;
|
||||||
|
// ENV.APP.LOG_TRANSITIONS = true;
|
||||||
|
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
|
||||||
|
// ENV.APP.LOG_VIEW_LOOKUPS = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (environment === 'test') {
|
||||||
|
// Testem prefers this...
|
||||||
|
ENV.baseURL = '/';
|
||||||
|
ENV.locationType = 'none';
|
||||||
|
|
||||||
|
// keep test console output quieter
|
||||||
|
ENV.APP.LOG_ACTIVE_GENERATION = false;
|
||||||
|
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||||
|
|
||||||
|
ENV.APP.rootElement = '#ember-testing';
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (environment === 'production') {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
return ENV;
|
||||||
|
};
|
||||||
1
bench/implementations/ember-2.6.3/dist/assets/todomvc-b88bd0267dade133c112b65a28b90916.js
vendored
Normal file
1
bench/implementations/ember-2.6.3/dist/assets/todomvc-b88bd0267dade133c112b65a28b90916.js
vendored
Normal file
File diff suppressed because one or more lines are too long
0
bench/implementations/ember-2.6.3/dist/assets/todomvc-d41d8cd98f00b204e9800998ecf8427e.css
vendored
Normal file
0
bench/implementations/ember-2.6.3/dist/assets/todomvc-d41d8cd98f00b204e9800998ecf8427e.css
vendored
Normal file
18
bench/implementations/ember-2.6.3/dist/assets/vendor-01de4edc8253c2828fa6f36d3b1ca849.js
vendored
Normal file
18
bench/implementations/ember-2.6.3/dist/assets/vendor-01de4edc8253c2828fa6f36d3b1ca849.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
bench/implementations/ember-2.6.3/dist/assets/vendor-7b5c98520910afa58d74e05ec86cd873.css
vendored
Normal file
1
bench/implementations/ember-2.6.3/dist/assets/vendor-7b5c98520910afa58d74e05ec86cd873.css
vendored
Normal file
File diff suppressed because one or more lines are too long
15
bench/implementations/ember-2.6.3/dist/crossdomain.xml
vendored
Executable file
15
bench/implementations/ember-2.6.3/dist/crossdomain.xml
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
|
||||||
|
<cross-domain-policy>
|
||||||
|
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
|
||||||
|
|
||||||
|
<!-- Most restrictive policy: -->
|
||||||
|
<site-control permitted-cross-domain-policies="none"/>
|
||||||
|
|
||||||
|
<!-- Least restrictive policy: -->
|
||||||
|
<!--
|
||||||
|
<site-control permitted-cross-domain-policies="all"/>
|
||||||
|
<allow-access-from domain="*" to-ports="*" secure="false"/>
|
||||||
|
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
|
||||||
|
-->
|
||||||
|
</cross-domain-policy>
|
||||||
25
bench/implementations/ember-2.6.3/dist/index.html
vendored
Normal file
25
bench/implementations/ember-2.6.3/dist/index.html
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>Todomvc</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<meta name="todomvc/config/environment" content="%7B%22modulePrefix%22%3A%22todomvc%22%2C%22environment%22%3A%22production%22%2C%22baseURL%22%3Anull%2C%22locationType%22%3A%22none%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%7D%2C%22APP%22%3A%7B%22name%22%3A%22todomvc%22%2C%22version%22%3A%220.0.0+9b29a440%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/vendor-7b5c98520910afa58d74e05ec86cd873.css" integrity="sha256-bsagGHduhay9QPLUFpddcZFq7Kmr2ScM3VKnWhdX8oM= sha512-eNsGN2aLecWPvoqNVH8oXK8o/IJ7rO5ti0zgS8lF8LiwmKUHdEIuFduwcDL1VLAt2r+3YjgDzoSNYK6c57pJzw==" >
|
||||||
|
<link rel="stylesheet" href="assets/todomvc-d41d8cd98f00b204e9800998ecf8427e.css" integrity="sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" >
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="assets/vendor-01de4edc8253c2828fa6f36d3b1ca849.js" integrity="sha256-lBRb2M2secp4ojcJ0xM1d9YkRX7hKTz8KeKZlAWyh6Y= sha512-MfYfzPGAnzELmWDdNiIq7OMBhv2gLn5rwfQ9QtIcxibX0kdu21LLbuEv6RV7eK75XS5WHxksS0R2Xhghfl767A==" ></script>
|
||||||
|
<script src="assets/todomvc-b88bd0267dade133c112b65a28b90916.js" integrity="sha256-KmQwQXUtlnv5yK0MUO7F9qBoMauvZRkmuFO7VuiGoso= sha512-gtby61qJDGevaGdmHBxy1zA40OietcAdgZbUWgEuWhLqnnbkdcEbmkcx1VtYCVrGO63n2ehSzqnl0uFOiSsCqg==" ></script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
bench/implementations/ember-2.6.3/dist/robots.txt
vendored
Executable file
3
bench/implementations/ember-2.6.3/dist/robots.txt
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# http://www.robotstxt.org
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
26
bench/implementations/ember-2.6.3/ember-cli-build.js
Executable file
26
bench/implementations/ember-2.6.3/ember-cli-build.js
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*jshint node:true*/
|
||||||
|
/* global require, module */
|
||||||
|
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||||
|
|
||||||
|
module.exports = function (defaults) {
|
||||||
|
var app = new EmberApp(defaults, {
|
||||||
|
// Add options here
|
||||||
|
});
|
||||||
|
|
||||||
|
app.import('vendor/base.css');
|
||||||
|
app.import('vendor/index.css');
|
||||||
|
// Use `app.import` to add additional libraries to the generated
|
||||||
|
// output files.
|
||||||
|
//
|
||||||
|
// If you need to use different assets in different
|
||||||
|
// environments, specify an object as the first parameter. That
|
||||||
|
// object's keys should be the environment name and the values
|
||||||
|
// should be the asset to use in that environment.
|
||||||
|
//
|
||||||
|
// If the library that you are including contains AMD or ES6
|
||||||
|
// modules that you would like to import into your application
|
||||||
|
// please specify an object with the list of modules as keys
|
||||||
|
// along with the exports of each module as its value.
|
||||||
|
|
||||||
|
return app.toTree();
|
||||||
|
};
|
||||||
44
bench/implementations/ember-2.6.3/package.json
Executable file
44
bench/implementations/ember-2.6.3/package.json
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "todomvc",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Small description for todomvc goes here",
|
||||||
|
"private": true,
|
||||||
|
"directories": {
|
||||||
|
"doc": "doc",
|
||||||
|
"test": "tests"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "ember build",
|
||||||
|
"start": "ember server",
|
||||||
|
"test": "ember test",
|
||||||
|
"make": "bower install && ember build -prod"
|
||||||
|
},
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.0"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"bower": "*",
|
||||||
|
"broccoli-asset-rev": "^2.4.2",
|
||||||
|
"ember-ajax": "^2.0.1",
|
||||||
|
"ember-cli": "2.6.3",
|
||||||
|
"ember-cli-app-version": "^1.0.0",
|
||||||
|
"ember-cli-babel": "^5.1.6",
|
||||||
|
"ember-cli-dependency-checker": "^1.2.0",
|
||||||
|
"ember-cli-htmlbars": "^1.0.3",
|
||||||
|
"ember-cli-htmlbars-inline-precompile": "^0.3.1",
|
||||||
|
"ember-cli-inject-live-reload": "^1.4.0",
|
||||||
|
"ember-cli-jshint": "^1.0.0",
|
||||||
|
"ember-cli-qunit": "^1.4.0",
|
||||||
|
"ember-cli-release": "^0.2.9",
|
||||||
|
"ember-cli-sri": "^2.1.0",
|
||||||
|
"ember-cli-uglify": "^1.2.0",
|
||||||
|
"ember-export-application-global": "^1.0.5",
|
||||||
|
"ember-inflector": "1.9.4",
|
||||||
|
"ember-load-initializers": "^0.5.1",
|
||||||
|
"ember-resolver": "^2.0.3",
|
||||||
|
"loader.js": "^4.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
bench/implementations/ember-2.6.3/public/crossdomain.xml
Executable file
15
bench/implementations/ember-2.6.3/public/crossdomain.xml
Executable file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
|
||||||
|
<cross-domain-policy>
|
||||||
|
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
|
||||||
|
|
||||||
|
<!-- Most restrictive policy: -->
|
||||||
|
<site-control permitted-cross-domain-policies="none"/>
|
||||||
|
|
||||||
|
<!-- Least restrictive policy: -->
|
||||||
|
<!--
|
||||||
|
<site-control permitted-cross-domain-policies="all"/>
|
||||||
|
<allow-access-from domain="*" to-ports="*" secure="false"/>
|
||||||
|
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
|
||||||
|
-->
|
||||||
|
</cross-domain-policy>
|
||||||
3
bench/implementations/ember-2.6.3/public/robots.txt
Executable file
3
bench/implementations/ember-2.6.3/public/robots.txt
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# http://www.robotstxt.org
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
13
bench/implementations/ember-2.6.3/testem.js
Executable file
13
bench/implementations/ember-2.6.3/testem.js
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*jshint node:true*/
|
||||||
|
module.exports = {
|
||||||
|
framework: 'qunit',
|
||||||
|
test_page: 'tests/index.html?hidepassed',
|
||||||
|
disable_watching: true,
|
||||||
|
launch_in_ci: [
|
||||||
|
'PhantomJS'
|
||||||
|
],
|
||||||
|
launch_in_dev: [
|
||||||
|
'PhantomJS',
|
||||||
|
'Chrome'
|
||||||
|
]
|
||||||
|
};
|
||||||
141
bench/implementations/ember-2.6.3/vendor/base.css
vendored
Executable file
141
bench/implementations/ember-2.6.3/vendor/base.css
vendored
Executable file
|
|
@ -0,0 +1,141 @@
|
||||||
|
hr {
|
||||||
|
margin: 20px 0;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed #c5c5c5;
|
||||||
|
border-bottom: 1px dashed #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn a {
|
||||||
|
font-weight: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #b83f45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #787e7e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h3,
|
||||||
|
.learn h4,
|
||||||
|
.learn h5 {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 30px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn li {
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn p {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#issue-count {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
border: none;
|
||||||
|
margin: 20px 0 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote p {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote p:before {
|
||||||
|
content: '“';
|
||||||
|
font-size: 50px;
|
||||||
|
opacity: .15;
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote p:after {
|
||||||
|
content: '”';
|
||||||
|
font-size: 50px;
|
||||||
|
opacity: .15;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -42px;
|
||||||
|
right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -40px;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote footer img {
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote footer a {
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.speech-bubble {
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
background: rgba(0, 0, 0, .04);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.speech-bubble:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 30px;
|
||||||
|
border: 13px solid transparent;
|
||||||
|
border-top-color: rgba(0, 0, 0, .04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn-bar > .learn {
|
||||||
|
position: absolute;
|
||||||
|
width: 272px;
|
||||||
|
top: 8px;
|
||||||
|
left: -300px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: rgba(255, 255, 255, .6);
|
||||||
|
transition-property: left;
|
||||||
|
transition-duration: 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 899px) {
|
||||||
|
.learn-bar {
|
||||||
|
width: auto;
|
||||||
|
padding-left: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn-bar > .learn {
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
378
bench/implementations/ember-2.6.3/vendor/index.css
vendored
Executable file
378
bench/implementations/ember-2.6.3/vendor/index.css
vendored
Executable file
|
|
@ -0,0 +1,378 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.4em;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #4d4d4d;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="checkbox"] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todoapp {
|
||||||
|
background: #fff;
|
||||||
|
margin: 130px 0 40px 0;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#todoapp input::-webkit-input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todoapp input::-moz-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todoapp input::input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todoapp h1 {
|
||||||
|
position: absolute;
|
||||||
|
top: -155px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(175, 47, 47, 0.15);
|
||||||
|
-webkit-text-rendering: optimizeLegibility;
|
||||||
|
-moz-text-rendering: optimizeLegibility;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
#new-todo,
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: 1.4em;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
#new-todo {
|
||||||
|
padding: 16px 16px 16px 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(0, 0, 0, 0.003);
|
||||||
|
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='toggle-all'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-all {
|
||||||
|
position: absolute;
|
||||||
|
top: -55px;
|
||||||
|
left: -12px;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-all:before {
|
||||||
|
content: '❯';
|
||||||
|
font-size: 22px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
padding: 10px 27px 10px 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-all:checked:before {
|
||||||
|
color: #737373;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li {
|
||||||
|
position: relative;
|
||||||
|
font-size: 24px;
|
||||||
|
border-bottom: 1px solid #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li.editing {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li.editing .edit {
|
||||||
|
display: block;
|
||||||
|
width: 506px;
|
||||||
|
padding: 13px 17px 12px 17px;
|
||||||
|
margin: 0 0 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li.editing .view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .toggle {
|
||||||
|
text-align: center;
|
||||||
|
width: 40px;
|
||||||
|
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||||
|
height: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto 0;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .toggle:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .toggle:checked:after {
|
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li label {
|
||||||
|
white-space: pre;
|
||||||
|
word-break: break-word;
|
||||||
|
padding: 15px 60px 15px 15px;
|
||||||
|
margin-left: 45px;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li.completed label {
|
||||||
|
color: #d9d9d9;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #cc9a9a;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .destroy:after {
|
||||||
|
content: '×';
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li.editing:last-child {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
color: #777;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 0 -3px #f6f6f6,
|
||||||
|
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0 16px 0 -6px #f6f6f6,
|
||||||
|
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-count {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-count strong {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters li a {
|
||||||
|
color: inherit;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters li a.selected,
|
||||||
|
#filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-completed,
|
||||||
|
html #clear-completed:active {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-completed:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info {
|
||||||
|
margin: 65px auto 0;
|
||||||
|
color: #bfbfbf;
|
||||||
|
font-size: 10px;
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hack to remove background from Mobile Safari.
|
||||||
|
Can't use it globally since it destroys checkboxes in Firefox
|
||||||
|
*/
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
#toggle-all,
|
||||||
|
#todo-list li .toggle {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todo-list li .toggle {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-all {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
#footer {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters {
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
bench/implementations/nim-karax-comp-local/docs/docs.rst
Normal file
58
bench/implementations/nim-karax-comp-local/docs/docs.rst
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
|
||||||
|
Virtual DOM
|
||||||
|
===========
|
||||||
|
|
||||||
|
The virtual dom is in the ``vdom`` module.
|
||||||
|
|
||||||
|
The ``VNodeKind`` enum describes every tag that the HTML 5 spec includes
|
||||||
|
and some extensions that allow for an efficient component system.
|
||||||
|
|
||||||
|
These extensions are:
|
||||||
|
|
||||||
|
``VNodeKind.int``
|
||||||
|
The node havs a single integer field.
|
||||||
|
|
||||||
|
``VNodeKind.bool``
|
||||||
|
The node has a single boolean field.
|
||||||
|
|
||||||
|
``VNodeKint.vthunk``
|
||||||
|
The node is a `virtual thunk`:idx:. This means there is a
|
||||||
|
function attached to it that produces the ``VNode`` structure
|
||||||
|
on demand.
|
||||||
|
|
||||||
|
``VNodeKint.dthunk``
|
||||||
|
The node is a `DOM thunk`:idx:. This means there is a
|
||||||
|
function attached to it that produces the ``Node`` DOM structure
|
||||||
|
on demand.
|
||||||
|
|
||||||
|
For convenience of the resulting Nim DSL these tags have enum names
|
||||||
|
that differ from their HTML equivalent:
|
||||||
|
|
||||||
|
================= =======================================================
|
||||||
|
Enum value HTML
|
||||||
|
================= =======================================================
|
||||||
|
``tdiv`` ``div`` (changed because ``div`` is a keyword in Nim)
|
||||||
|
``italic`` ``i``
|
||||||
|
``bold`` ``b``
|
||||||
|
``strikethrough`` ``s``
|
||||||
|
``underlined`` ``u``
|
||||||
|
================= =======================================================
|
||||||
|
|
||||||
|
|
||||||
|
A virtual dom node also has a special field set called ``key``, an integer
|
||||||
|
that can be used as a data model specific key/id. It can be accessed in event
|
||||||
|
handlers to change the data model. See the todoapp for an example of how to
|
||||||
|
use it.
|
||||||
|
|
||||||
|
**Note**: This is not a hint for the DOM diff algorithm, multipe nodes can
|
||||||
|
all have the same key value.
|
||||||
|
|
||||||
|
|
||||||
|
Event system
|
||||||
|
============
|
||||||
|
|
||||||
|
Karax does not abstract over the event system the DOM offers much: The same
|
||||||
|
``Event`` structure is used. Every callback has the
|
||||||
|
signature ``proc (ev: Event; n: VNode)``.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
import karax, karaxdsl, vdom, dom, components
|
||||||
|
|
||||||
|
const
|
||||||
|
Play = 0
|
||||||
|
Big = 1
|
||||||
|
Small = 2
|
||||||
|
Normal = 3
|
||||||
|
|
||||||
|
proc paused(n: Node): bool {.importcpp: "#.paused".}
|
||||||
|
proc play(n: Node) {.importcpp.}
|
||||||
|
proc pause(n: Node) {.importcpp.}
|
||||||
|
proc `width=`(n: Node, w: int) {.importcpp: "#.width = #".}
|
||||||
|
|
||||||
|
proc mplayer*(id, resource: cstring): VNode {.component.} =
|
||||||
|
proc handler(ev: Event; n: VNode) =
|
||||||
|
let myVideo = document.getElementById(id)
|
||||||
|
case n.key
|
||||||
|
of Play:
|
||||||
|
if myVideo.paused:
|
||||||
|
myVideo.play()
|
||||||
|
else:
|
||||||
|
myVideo.pause()
|
||||||
|
of Big: myVideo.width = 560
|
||||||
|
of Small: myVideo.width = 320
|
||||||
|
of Normal: myVideo.width = 420
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
result = buildHtml(tdiv):
|
||||||
|
button(onclick=handler, key=Play):
|
||||||
|
text "Play/Pause"
|
||||||
|
button(onclick=handler, key=Big):
|
||||||
|
text "Big"
|
||||||
|
button(onclick=handler, key=Small):
|
||||||
|
text "Small"
|
||||||
|
button(onclick=handler, key=Normal):
|
||||||
|
text "Normal"
|
||||||
|
br()
|
||||||
|
br()
|
||||||
|
video(id=id, width="420"):
|
||||||
|
source(src=resource, `type`="video/mp4"):
|
||||||
|
text "Your browser does not support HTML5 video."
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Player app</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="body">
|
||||||
|
|
||||||
|
<div id="ROOT" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="nimcache/playerapp.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
## Example app that shows how to write and embed a custom component.
|
||||||
|
|
||||||
|
include karaxprelude
|
||||||
|
import mediaplayer
|
||||||
|
|
||||||
|
const url = "https://www.w3schools.com/html/mov_bbb.mp4"
|
||||||
|
|
||||||
|
proc createDom(): VNode =
|
||||||
|
result = buildHtml(table):
|
||||||
|
tr:
|
||||||
|
td:
|
||||||
|
mplayer("vid1", url)
|
||||||
|
td:
|
||||||
|
mplayer("vid2", url)
|
||||||
|
tr:
|
||||||
|
td:
|
||||||
|
mplayer("vid3", url)
|
||||||
|
td:
|
||||||
|
mplayer("vid4", url)
|
||||||
|
|
||||||
|
setRenderer createDom
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
--path: "../../src"
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Todo app</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="body">
|
||||||
|
|
||||||
|
<div id="ROOT" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="nimcache/scrollapp.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
## Example that shows how to accomplish an "infinitely scrolling" app.
|
||||||
|
|
||||||
|
include karaxprelude
|
||||||
|
import jstrutils, dom
|
||||||
|
|
||||||
|
var entries: seq[cstring] = @[]
|
||||||
|
for i in 1..500:
|
||||||
|
entries.add(cstring("Entry ") & &i)
|
||||||
|
|
||||||
|
proc scrollEvent(ev: Event; n: VNode) =
|
||||||
|
let d = n.dom
|
||||||
|
if d != nil and inViewport(d.lastChild):
|
||||||
|
# "load" more data:
|
||||||
|
for i in 1..50:
|
||||||
|
entries.add(cstring("Loaded Entry ") & &i)
|
||||||
|
|
||||||
|
proc createDom(): VNode =
|
||||||
|
result = buildHtml():
|
||||||
|
tdiv(onscroll=scrollEvent, style="height: 400px; overflow: scroll"):
|
||||||
|
for x in entries:
|
||||||
|
tdiv:
|
||||||
|
text x
|
||||||
|
|
||||||
|
setRenderer createDom
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
--path: "../../src"
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Todo app</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="body">
|
||||||
|
|
||||||
|
<div id="ROOT" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="nimcache/todoapp.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,378 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todomvc-wrapper {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.4em;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #4d4d4d;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="checkbox"] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp {
|
||||||
|
background: #fff;
|
||||||
|
margin: 130px 0 40px 0;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||||
|
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-webkit-input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::-moz-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp input::input-placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoapp h1 {
|
||||||
|
position: absolute;
|
||||||
|
top: -155px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(175, 47, 47, 0.15);
|
||||||
|
-webkit-text-rendering: optimizeLegibility;
|
||||||
|
-moz-text-rendering: optimizeLegibility;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo,
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: 1.4em;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-font-smoothing: antialiased;
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-todo {
|
||||||
|
padding: 16px 16px 16px 60px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(0, 0, 0, 0.003);
|
||||||
|
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='toggle-all'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
position: absolute;
|
||||||
|
top: -55px;
|
||||||
|
left: -12px;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:before {
|
||||||
|
content: '>';
|
||||||
|
font-size: 22px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
padding: 10px 27px 10px 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all:checked:before {
|
||||||
|
color: #737373;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li {
|
||||||
|
position: relative;
|
||||||
|
font-size: 24px;
|
||||||
|
border-bottom: 1px solid #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .edit {
|
||||||
|
display: block;
|
||||||
|
width: 506px;
|
||||||
|
padding: 13px 17px 12px 17px;
|
||||||
|
margin: 0 0 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing .view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
text-align: center;
|
||||||
|
width: 40px;
|
||||||
|
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||||
|
height: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto 0;
|
||||||
|
border: none; /* Mobile Safari */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:after {
|
||||||
|
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle:checked:after {
|
||||||
|
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li label {
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 15px 60px 15px 15px;
|
||||||
|
margin-left: 45px;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.completed label {
|
||||||
|
color: #d9d9d9;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #cc9a9a;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .destroy:after {
|
||||||
|
content: 'x';
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li.editing:last-child {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
color: #777;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 8px 0 -3px #f6f6f6,
|
||||||
|
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0 16px 0 -6px #f6f6f6,
|
||||||
|
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-count strong {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a {
|
||||||
|
color: inherit;
|
||||||
|
margin: 3px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected,
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed,
|
||||||
|
html .clear-completed:active {
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-completed:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin: 65px auto 0;
|
||||||
|
color: #bfbfbf;
|
||||||
|
/* font-size: 10px; */
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hack to remove background from Mobile Safari.
|
||||||
|
Can't use it globally since it destroys checkboxes in Firefox
|
||||||
|
*/
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
.toggle-all,
|
||||||
|
.todo-list li .toggle {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list li .toggle {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-all {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
.footer {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
|
||||||
|
import vdom, karax, karaxdsl, jstrutils, components
|
||||||
|
|
||||||
|
type
|
||||||
|
Filter = enum
|
||||||
|
all, active, completed
|
||||||
|
|
||||||
|
var
|
||||||
|
selectedEntry = -1
|
||||||
|
filter: Filter
|
||||||
|
entriesLen: int
|
||||||
|
|
||||||
|
proc getItem(key : cstring ): cstring {.importc: "localStorage.getItem"}
|
||||||
|
proc setItem(key, value : cstring ) {.importc: "localStorage.setItem"}
|
||||||
|
proc clearEntries() {.importc: "localStorage.clear"}
|
||||||
|
|
||||||
|
proc getEntryCstring(pos: int): cstring =
|
||||||
|
result = getItem(&pos & cstring"cstring")
|
||||||
|
if result == cstring"null":
|
||||||
|
result = nil
|
||||||
|
|
||||||
|
proc isCompleted(pos: int): bool =
|
||||||
|
var value = getItem(cstring($pos & "bool"))
|
||||||
|
result = value == cstring"true"
|
||||||
|
|
||||||
|
proc setEntryCstring(pos: int, value: cstring) =
|
||||||
|
setItem(cstring($pos & "cstring"), value)
|
||||||
|
|
||||||
|
proc markAsCompleted(pos: int, value : bool) =
|
||||||
|
var val = cstring"true"
|
||||||
|
if not value:
|
||||||
|
val = cstring"false"
|
||||||
|
setItem(cstring($pos & "bool"), val)
|
||||||
|
|
||||||
|
proc addEntry(str: cstring, bval : bool) =
|
||||||
|
setEntryCstring(entriesLen, str)
|
||||||
|
markAsCompleted(entriesLen, bval)
|
||||||
|
inc entriesLen
|
||||||
|
|
||||||
|
proc updateEntry(pos: int, str: cstring, bval: bool) =
|
||||||
|
setEntryCstring(pos, str)
|
||||||
|
markAsCompleted(pos, bval)
|
||||||
|
|
||||||
|
proc onTodoEnter(ev: Event; n: VNode) =
|
||||||
|
addEntry(n.value, false)
|
||||||
|
n.value = ""
|
||||||
|
|
||||||
|
proc removeHandler(ev: Event; n: VNode) =
|
||||||
|
updateEntry(n.key, cstring(nil), false)
|
||||||
|
|
||||||
|
proc editHandler(ev: Event; n: VNode) =
|
||||||
|
selectedEntry = n.key
|
||||||
|
|
||||||
|
proc focusLost(ev: Event; n: VNode) = selectedEntry = -1
|
||||||
|
|
||||||
|
proc editEntry(ev: Event; n: VNode) =
|
||||||
|
setEntryCstring(n.key, n.value)
|
||||||
|
selectedEntry = -1
|
||||||
|
|
||||||
|
proc toggleEntry(ev: Event; n: VNode) =
|
||||||
|
let id = n.key
|
||||||
|
markAsCompleted(id, not isCompleted(id))
|
||||||
|
|
||||||
|
proc onAllDone(ev: Event; n: VNode) =
|
||||||
|
clearEntries()
|
||||||
|
selectedEntry = -1
|
||||||
|
|
||||||
|
proc clearCompleted(ev: Event, n: VNode) =
|
||||||
|
for i in 0..<entriesLen:
|
||||||
|
if isCompleted(i): setEntryCstring(i, nil)
|
||||||
|
|
||||||
|
proc toClass(completed: bool): cstring =
|
||||||
|
(if completed: cstring"completed" else: cstring(nil))
|
||||||
|
|
||||||
|
proc toChecked(checked: bool): cstring =
|
||||||
|
(if checked: cstring"checked" else: cstring(nil))
|
||||||
|
|
||||||
|
proc selected(v: Filter): cstring =
|
||||||
|
(if filter == v: cstring"selected" else: cstring(nil))
|
||||||
|
|
||||||
|
proc createEntry(id: int; d: cstring; completed, selected: bool): VNode {.component.} =
|
||||||
|
result = buildHtml(tr):
|
||||||
|
li(class=toClass(completed)):
|
||||||
|
if not selected:
|
||||||
|
tdiv(class = "view"):
|
||||||
|
input(class = "toggle", `type` = "checkbox", checked = toChecked(completed),
|
||||||
|
onclick=toggleEntry, key=id)
|
||||||
|
label(onDblClick=editHandler, key=id):
|
||||||
|
text d
|
||||||
|
button(class = "destroy", key=id, onclick=removeHandler)
|
||||||
|
else:
|
||||||
|
input(class = "edit", name = "title", key=id,
|
||||||
|
onblur = focusLost,
|
||||||
|
onkeyupenter = editEntry, value = d, setFocus)
|
||||||
|
|
||||||
|
proc makeFooter(entriesCount, completedCount: int): VNode {.component.} =
|
||||||
|
result = buildHtml(footer(class = "footer")):
|
||||||
|
span(class = "todo-count"):
|
||||||
|
strong:
|
||||||
|
text(&entriesCount)
|
||||||
|
text cstring" item" & &(if entriesCount != 1: "s left" else: " left")
|
||||||
|
ul(class = "filters"):
|
||||||
|
li:
|
||||||
|
a(class = selected(all), href = "#/"):
|
||||||
|
text "All"
|
||||||
|
li:
|
||||||
|
a(class = selected(active), href = "#/active"):
|
||||||
|
text "Active"
|
||||||
|
li:
|
||||||
|
a(class = selected(completed), href = "#/completed"):
|
||||||
|
text "Completed"
|
||||||
|
button(class = "clear-completed", onclick = clearCompleted):
|
||||||
|
text "Clear completed (" & &completedCount & ")"
|
||||||
|
|
||||||
|
proc makeHeader(): VNode {.component.} =
|
||||||
|
result = buildHtml(header(class = "header")):
|
||||||
|
h1:
|
||||||
|
text "todos"
|
||||||
|
input(class = "new-todo", placeholder="What needs to be done?", name = "newTodo",
|
||||||
|
onkeyupenter = onTodoEnter, setFocus)
|
||||||
|
|
||||||
|
proc createDom(): VNode =
|
||||||
|
result = buildHtml(tdiv(class="todomvc-wrapper")):
|
||||||
|
section(class = "todoapp"):
|
||||||
|
makeHeader()
|
||||||
|
section(class = "main"):
|
||||||
|
input(class = "toggle-all", `type` = "checkbox", name = "toggle")
|
||||||
|
label(`for` = "toggle-all", onclick = onAllDone):
|
||||||
|
text "Mark all as complete"
|
||||||
|
var entriesCount = 0
|
||||||
|
var completedCount = 0
|
||||||
|
ul(class = "todo-list"):
|
||||||
|
#for i, d in pairs(entries):
|
||||||
|
for i in 0..entriesLen-1:
|
||||||
|
|
||||||
|
var d0 = getEntryCstring(i)
|
||||||
|
var d1 = isCompleted(i)
|
||||||
|
if d0 != nil:
|
||||||
|
let b = case filter
|
||||||
|
of all: true
|
||||||
|
of active: not d1
|
||||||
|
of completed: d1
|
||||||
|
if b:
|
||||||
|
createEntry(i, d0, d1, i == selectedEntry)
|
||||||
|
inc completedCount, ord(d1)
|
||||||
|
inc entriesCount
|
||||||
|
makeFooter(entriesCount, completedCount)
|
||||||
|
|
||||||
|
setOnHashChange(proc(hash: cstring) =
|
||||||
|
if hash == "#/": filter = all
|
||||||
|
elif hash == "#/completed": filter = completed
|
||||||
|
elif hash == "#/active": filter = active
|
||||||
|
)
|
||||||
|
entriesLen = 0
|
||||||
|
setRenderer createDom
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue