Commit eec8f49f authored by Sindre Sorhus's avatar Sindre Sorhus

Merge branch 'master' of github.com:addyosmani/todomvc

* 'master' of github.com:addyosmani/todomvc:
  Close GH-157: Updating Angular demo to v1.0 of the library.
  various readme changes. some minor, some necessary to move towards cleaner markdown
  very minor change for consistency in comment spacing/casing
  updating readme to include links, revised team notes and other tweaks
parents bcea7039 bdb644a3
# TodoMVC
#### A common demo application for popular JavaScript MV* frameworks
#### A common learning application for popular JavaScript MV* frameworks
## Introduction
Developers these days are spoiled with choice when it comes to selecting an MV* framework for structuring and organizing JavaScript web apps. Backbone, Spine, Ember.js (SproutCore 2.0), JavaScriptMVC... The list of new and stable solutions goes on and on, but just how do you decide on which to use in a sea of so many options?
Developers these days are spoiled with choice when it comes to selecting an MV* framework for structuring and organizing JavaScript web apps. Backbone, Spine, Ember.js, AngularJS, JavaScriptMVC... The list of new and stable solutions goes on and on, but just how do you decide on which to use in a sea of so many options?
To help solve this problem, TodoMVC was created - a project which offers the same Todo application implemented using MVC concepts in most of the popular JavaScript MV* frameworks of today. Solutions look and feel the same, have a common feature set, and make it easy for you to compare the syntax and structure of different frameworks, so you can select the one you feel the most comfortable with.
#### Todo apps are included for:
- Backbone.js
- Backbone.js + RequireJS (using AMD)
- Ember.js
- JavaScriptMVC
- Spine.js
- Sammy.js
- KnockoutJS (MVVM)
- Knockback.js
- Dojo
- Closure
- YUILibrary
- AngularJS
- Angular + PersistenceJS
- Ext.js
- Agility.js
- [Backbone.js](http://documentcloud.github.com/backbone/)
- [Backbone.js + RequireJS](http://requirejs.org/) (using AMD)
- [Ember.js](http://emberjs.com/)
- [JavaScriptMVC](http://javascriptmvc.com/)
- [Spine.js](http://spinejs.com/)
- [Sammy.js](http://sammyjs.org/)
- [KnockoutJS](http://knockoutjs.com/) (MVVM)
- [Knockback.js](http://kmalakoff.github.com/knockback/)
- [Dojo](http://dojotoolkit.org/)
- [Closure](http://code.google.com/closure/library/)
- [YUILibrary](http://yuilibrary.com/)
- [AngularJS](http://angularjs.org/)
- [Angular + PersistenceJS](http://persistencejs.org/)
- [Ext.js](http://www.sencha.com/products/extjs)
- [Agility.js](http://agilityjs.com/)
###### Non MV*
- jQuery
- Vanilla JS
We also have a number of in-progress applications for new frameworks (such as CanJS and Meteor) being worked on in our [Labs](http://addyosmani.github.com/todomvc/labs) site.
We also have a number of in-progress applications for new frameworks (such as [CanJS](http://canjs.us/) and [Meteor](http://meteor.com/)) being worked on in our [Labs](http://addyosmani.github.com/todomvc/labs) site.
## Live demos
......@@ -47,11 +47,13 @@ Live demos are available on the official [TodoMVC site](http://todomvc.com).
## Team
As of early 2012, I'm happy to introduce two new core committers to the project:
TodoMVC would not be possible without a strong team of [contributors](https://github.com/addyosmani/todomvc/contributors) helping push the project forward each day. In addition, we have a core project team composed of:
* [Aaron Boushley](https://github.com/boushley): Aaron is a JavaScript developer with a keen interest in architectural frameworks and will be helping both standardize existing examples and improve the project as we work on expansion.
* [Addy Osmani - Founder/Lead](http://github.com/addyosmani): Addy is a Developer Platform Engineer at Google who originally created TodoMVC. He oversees the project direction, drives expansion and helps lead core development with Sindre Sorhus (by far our most active contributor!).
* [Sindre Sorhus](https://github.com/sindresorhus): Sindre is a Web Developer who has been taking an interest in contributing to a number of open-source projects this year and will also be helping with project expansion and improvements to existing applications.
* [Sindre Sorhus - Lead Developer](https://github.com/sindresorhus): Sindre is a Web Developer who drives core development, quality control and application design for the project. His contributions have helped us ensure consistency and best practices are enforced wherever possible.
* [Aaron Boushley - on leave](https://github.com/boushley): Aaron is a JavaScript developer with a keen interest in architectural frameworks and has helped both standardize existing examples and improve the project as we worked on expansion. He is currently taking a break from his open-source contributions at this time.
## Disclaimer
......@@ -65,7 +67,7 @@ It is meant to be used as a gateway to reviewing how a basic application using a
Whilst we enjoy implementing and improving existing Todo apps, we're always interested in speaking to framework authors (and users) wishing to share Todo app implementations in their framework/solution of choice.
If you have an implementation you would like to show us or a patch you would like to send upstream, please feel free to send through a pull request after reading our [contribution guidelines](https://github.com/addyosmani/todomvc/wiki). One of us will be happy to review them and discuss any changes that may be required before they can be included.
If you have an implementation you would like to show us or a patch you would like to send upstream, please feel free to send through a pull request after reading our [contribution guidelines](https://github.com/addyosmani/todomvc/wiki) (in particular our [application specifications](https://github.com/addyosmani/todomvc/wiki/App-Specification)). One of us will be happy to review your submission and discuss any changes that may be required before they can be included.
Note that due to the current number of MVC/MVVM/MV* frameworks in circulation at the moment, it's not always possible to include each one in TodoMVC, but we'll definitely discuss the merits of any framework prior to making a decision. We hope you understand :)
......
<!doctype html>
<html xmlns:ng="http://angularjs.org/" xmlns:my="http://rx.org">
<html ng-app="todomvc">
<head>
<meta charset="utf-8">
<title>AngularJS - TodoMVC</title>
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../assets/ie.js"></script>
<![endif]-->
<meta charset="utf-8">
<title>AngularJS - TodoMVC</title>
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<div ng:controller="App.Controllers.TodoController" id="todoapp">
<header>
<h1>Todos</h1>
<form id="todo-form" ng:submit="addTodo()">
<input id="new-todo" name="newTodo" type="text" placeholder="What needs to be done?">
</form>
</header>
<section id="main" ng:show="hasTodos()">
<input id="toggle-all" type="checkbox" name="allChecked" ng:click="toggleAllStates()">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<li ng:repeat="todo in todos" my:dblclick="editTodo(todo)" ng:class="(todo.done && ' done ') + (todo.editing && ' editing ')">
<div class="view">
<input class="toggle" type="checkbox" name="todo.done">
<label>{{ todo.title }}</label>
<a class="destroy" ng:click="removeTodo(todo)"></a>
</div>
<form ng:submit="finishEditing(todo)">
<input class="edit" type="text" name="todo.title" my:focus="todo.editing" my:blur="finishEditing(todo)">
</form>
</li>
</ul>
</section>
<footer ng:show="hasTodos()">
<a id="clear-completed" ng:click="clearCompletedItems()" ng:show="hasFinishedTodos()">{{ clearItemsText() }}</a>
<div id="todo-count"><b>{{ remainingTodos() }}</b> {{ itemsLeftText() }}</div>
</footer>
</div>
<div id="instructions">
Double-click to edit a todo.
</div>
<div id="credits">
Created by <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>.
</div>
<script src="../../assets/base.js"></script>
<script src="js/booter.js"></script>
<script src="js/libs/angular/angular.min.js" ng:autobind></script>
<script src="js/controllers.js"></script>
<script src="js/directive.js"></script>
<div ng-controller="App.Controllers.TodoController" id="todoapp">
<header>
<h1>Todos</h1>
<form id="todo-form" ng-submit="addTodo()">
<input type="text" id="new-todo" name="newTodo" ng-model="newTodo" placeholder="What needs to be done?">
</form>
</header>
<section id="main" ng-show="todos.length">
<input type="checkbox" id="toggle-all" ng-click="markAllDone()" ng-checked="remainingTodos().length == 0">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<li ng-repeat="todo in todos" ng-dblclick="editTodo(todo)" ng-class="{done: todo.done, editing: todo.editing}">
<div class="view">
<input type="checkbox" class="toggle" name="todo.done" ng-model="todo.done">
<label>{{todo.title}}</label>
<a class="destroy" ng-click="removeTodo(todo)"></a>
</div>
<form ng-submit="finishEditing(todo)">
<input type="text" class="edit" name="todo.title" ng-model="todo.title" my:blur="finishEditing(todo)">
</form>
</li>
</ul>
</section>
<footer ng-show="todos.length">
<a id="clear-completed" ng-click="clearDoneTodos()" ng-show="doneTodos().length">Clear {{doneTodos().length}} items</a>
<div id="todo-count">
<ng-pluralize count="remainingTodos().length" when="todoForms"></ng-pluralize>
</div>
</footer>
</div>
<div id="instructions">
Double-click to edit a todo.
</div>
<div id="credits">
Credits: <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>, <a href="http://ericbidelman.com">Eric Bidelman</a>
</div>
<script src="../../assets/base.js"></script>
<script src="js/booter.js"></script>
<script src="js/libs/angular/angular.min.js"></script>
<script src="js/controllers.js"></script>
</body>
</html>
\ No newline at end of file
/* App Controllers */
App.Controllers.TodoController = function () {
var self = this;
self.newTodo = "";
var retrieveStore = function() {
var store = localStorage.getItem('todo-angularjs');
return ( store && JSON.parse( store ) ) || [];
};
var updateStore = function() {
var isEditing = angular.Array.count(self.todos, function(x) {
return x.editing;
});
if (!isEditing){
localStorage.setItem('todo-angularjs', JSON.stringify(self.todos));
}
};
//not sure if its intended to do so. However, we need a hook to update the store
//whenever angular changes any properties
self.$watch(updateStore);
self.todos = retrieveStore();
self.addTodo = function() {
if (self.newTodo.trim().length === 0) return;
self.todos.push({
title: self.newTodo,
done: false,
editing: false
});
self.newTodo = "";
};
self.editTodo = function(todo) {
//cancel any active editing operation
angular.forEach(self.todos, function(value) {
value.editing = false;
});
todo.editing = true;
};
self.finishEditing = function(todo) {
if (todo.title.trim().length === 0){
self.removeTodo(todo);
}
else{
todo.editing = false;
}
};
self.removeTodo = function(todo) {
angular.Array.remove(self.todos, todo);
};
var countTodos = function(done) {
return function() {
return angular.Array.count(self.todos, function(x) {
return x.done === (done === "done");
});
}
};
var pluralize = function( count, word ) {
return count === 1 ? word : word + 's';
};
self.remainingTodos = countTodos("undone");
self.finishedTodos = countTodos("done");
self.itemsLeftText = function(){
return pluralize(self.remainingTodos(), 'item') + ' left'
};
self.clearItemsText = function(){
var finishedTodos = self.finishedTodos();
return 'Clear ' + finishedTodos + ' completed ' + pluralize(finishedTodos, 'item');
};
self.clearCompletedItems = function() {
var oldTodos = self.todos;
self.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) self.todos.push(todo);
});
self.allChecked = false;
};
self.toggleAllStates = function(){
angular.forEach(self.todos, function(todo){
todo.done = self.allChecked;
})
};
self.hasFinishedTodos = function() {
return self.finishedTodos() > 0;
};
self.hasTodos = function() {
return self.todos.length > 0;
};
var todomvc = angular.module('todomvc', []);
App.Controllers.TodoController = function($scope) {
$scope.todos = retrieveStore();
// Call updateStore() whenever the todos array changes.
$scope.$watch('todos', updateStore, true);
$scope.todoForms = {
0: "You're done!",
one: '{} item left',
other: '{} items left'
};
function retrieveStore() {
var store = localStorage.getItem('todo-angularjs');
return (store && JSON.parse(store)) || [];
};
function updateStore() {
var isEditing = $scope.todos.filter(function(val) {
return val.editing;
}).length;
if (!isEditing) {
localStorage.setItem('todo-angularjs', JSON.stringify($scope.todos));
}
};
$scope.addTodo = function() {
if (this.newTodo.trim().length === 0) {
return;
}
$scope.todos.push({
title: this.newTodo,
done: false,
editing: false
});
this.newTodo = '';
};
$scope.editTodo = function(todo) {
//cancel any active editing operation
$scope.todos.forEach(function(val) {
val.editing = false;
});
todo.editing = true;
};
$scope.finishEditing = function(todo) {
if (todo.title.trim().length === 0) {
$scope.removeTodo(todo);
} else {
todo.editing = false;
}
};
$scope.removeTodo = function(todo) {
for (var i = 0, len = $scope.todos.length; i < len; ++i) {
if (todo === $scope.todos[i]) {
$scope.todos.splice(i, 1);
}
}
};
$scope.remainingTodos = function() {
return $scope.todos.filter(function(val) {
return !val.done;
});
};
$scope.doneTodos = function() {
return $scope.todos.filter(function(val) {
return val.done;
});
}
$scope.clearDoneTodos = function() {
$scope.todos = $scope.remainingTodos();
};
$scope.markAllDone = function() {
var markDone = true;
if (!$scope.remainingTodos().length) {
markDone = false;
}
$scope.todos.forEach(function(todo) {
todo.done = markDone;
});
};
};
angular.directive('my:blur', function(expression, compiledElement) {
var compiler = this;
return function(linkElement) {
var scope = this;
linkElement.bind('blur', function(event) {
scope.$apply(expression, linkElement);
event.stopPropagation();
});
};
});
angular.directive('my:dblclick', function(expression, compiledElement) {
var compiler = this;
return function(linkElement) {
var scope = this;
linkElement.bind('dblclick', function(event) {
scope.$apply(expression, linkElement);
event.stopPropagation();
});
};
});
angular.directive("my:focus", function(expression, compiledElement){
return function(element){
this.$watch(expression, function(){
if(angular.formatter.boolean.parse(expression)){
element[0].focus();
element[0].select();
}
}, element);
};
});
This source diff could not be displayed because it is too large. You can view the blob instead.
# JavaScriptMVC Todo Application
# JavaScriptMVC TodoMVC app
###Why this fork?
I began this fork of the useful JMVC Todo application as I felt it was missing a number of things including:
<ul>
<li>Setup documentation (which I've written up below)</li>
<li>A directory structure that matches what the build files originally defined require</li>
<li>Minor changes to help it fall in line with the Backbone and Spine todo apps including UI features and stylistic changes.</li>
<li>Pre-built production.js and production.css files so users could test the app right out of the box (as long as steal etc. are available)</li>
</ul>
* Setup documentation (which I've written up below)
* A directory structure that matches what the build files originally defined require
* Minor changes to help it fall in line with the Backbone and Spine todo apps including UI features and stylistic changes
* Pre-built production.js and production.css files so users could test the app right out of the box (as long as steal etc. are available)
##Getting Started
JavaScriptMVC is a jQuery-based JavaScript framework that's excellent as a comprehensive front-end solution for structuring applications using the MVC architecture pattern.
It's broken down into 4 separate projects that can be used independently of each other. These are:
<ul>
<li><b>jQueryMVC</b> - MVC extensions for jQuery</li>
<li><b>StealJS</b> - Dependency management, build process, code generators</li>
<li><b>FuncUnit</b> - A web testing framework</li>
<li><b>DocumentJS</b> - A JavaScript documentation framework</li>
</ul>
For more information on getting started with the framework as a whole, you may find Justin Meyer's documentation on it here https://gist.github.com/989117 quite helpful. It discusses classes, models, views and controllers and is an overall good read for any developers wishing to begin using JMVC.
* jQueryMVC - MVC extensions for jQuery
* StealJS - Dependency management, build process, code generators
* FuncUnit - A web testing framework
* DocumentJS - A JavaScript documentation framework
For more information on getting started with the framework as a whole, you may find Justin Meyer's [documentation](https://gist.github.com/989117) on it quite helpful. It discusses classes, models, views and controllers and is an overall good read for any developers wishing to begin using JMVC.
###Setup
......@@ -33,48 +32,49 @@ To get started with the Todo application, you'll first need to clone the reposit
The repository also contains git submodules for jQueryMVC, StealJS, FuncUnit and DocumentJS out of the box. If you would like to check-out the code for the application including all submodules you can do so as follows:
<pre>
```
git clone --recursive git://github.com/addyosmani/todo
</pre>
```
Git 1.6.5 supports this however if you're using an older version of Git you can achieve the same effect using <pre>git submodule update --init</pre>
Git 1.6.5 supports this however if you're using an older version of Git you can achieve the same effect using `git submodule update --init`
###Cloning with submodules manually
Alternatively, you can checkout the main application files and clone the dependancies manually. To checkout the app simply execute:
<pre>
```
git clone git@github.com:addyosmani/todo.git
</pre>
```
You can then either use *submodules* to checkout the submodules as their own repositories (eg. if you forked them from JMVC and wished to keep track of your own versions) or alternatively by cloning their repos locally.
###Submodules
<pre>
```
git submodule add git://github.com/jupiterjs/steal.git steal
git submodule add git://github.com/jupiterjs/jquerymx.git jquery
git submodule add git://github.com/jupiterjs/funcunit.git funcunit
git submodule add git://github.com/jupiterjs/documentjs.git documentjs
</pre>
```
Note that as per JupiterIT's original Todo application, jquerymx is actually stored in the directory called jquery in case we decide to sync up any changes made without worrying about path differences.
Note that as per Bitovi's original Todo application, jquerymx is actually stored in the directory called jquery in case we decide to sync up any changes made without worrying about path differences.
###Cloning
Cloning repositories using GitHub is a fairly straight-forward process and the todo application just requires the following commands to be executed to get you setup:
<pre>
```
git clone git://github.com/jupiterjs/steal.git
git clone git://github.com/jupiterjs/jquerymx.git
git clone git://github.com/jupiterjs/funcunit.git
git clone git://github.com/jupiterjs/documentjs.git
</pre>
```
###Structure
In order to correctly build the todo application, you'll need to ensure that you have the following directory structure, unless you decide to change the build files in the todo/scripts directory:
<pre>
```
funcunit
jquery
steal
......@@ -83,7 +83,7 @@ todo
index.html, todo.js etc.
scripts
test
</pre>
```
......@@ -91,27 +91,27 @@ todo
Within todo/todo, you'll find the main application (todo.js) as well as two additional folders. The *scripts* folder contains the build files needed to build the final production files required for the app to run, whilst the *test* folder contains the FuncUnit and QUnit files required for testing. Let's take a look at todo/todo/scripts/build.js:
<pre>
```
load("steal/rhino/steal.js");
steal.plugins('steal/build','steal/build/scripts','steal/build/styles',function(){
steal.build('todo/todo/scripts/build.html',{to: 'todo/todo'});
});
</pre>
```
In this simple build file, we're telling out build process to load steal.js from the rhino directory (for more on Rhino, see here: http://www.mozilla.org/rhino/doc.html) and then *steal* (ie. load within the context of a script loader) the steal plugins we'll be using for our build.
We finally define the build actions required, which essentially allows us to specify the build source (todo/todo/scripts/build.html) and the build target (todo/todo). In case you're wondering what build.html does, it effectively tells steal.js to build the application within the folder todo/todo as follows:
<pre>
```
&lt;script type=&#39;text/javascript&#39; src=&#39;../../../steal/steal.js?todo/todo&#39;&gt;
</pre>
```
You of course don't have to output your build files to the same directory as your application source, however as both our outputs have names which are different to the source files, I think this works fine for our example.
Finally, to perform the application build so that JMVC outputs the production-ready version of your code, execute the following command where we pass our build.js file as an argument to steal's builder.
<pre>
```
./steal/js todo/todo/scripts/build.js
</pre>
```
# jQuery • [TodoMVC](http://todomvc.com)
# jQuery TodoMVC app
## Credit
......
......@@ -5,7 +5,7 @@ Forked from https://github.com/kmalakoff/knockback-todos
## Getting started
You need [CoffeScript](http://coffeescript.org) to compile if you make changes to the files in the `src` folder.
[CoffeeScript](http://coffeescript.org) is required to compile this application if you make changes to the files in the `src` folder.
## Compile
......
Forked from https://github.com/ashish01/knockoutjs-todos
\ No newline at end of file
# Knockout.js TodoMVC app
[ashish101](https://github.com/ashish01/knockoutjs-todos) wrote the original version of this application, which was then refactored by Addy Osmani and later rewritten by TodoMVC contributors.
\ No newline at end of file
# Sammy's Todos
# Sammy.js TodoMVC app
This is a demo todo list app built on top of Sammy.js.
Original base code: [Brandon Aaron](http://brandonaaron.net). This version, refactored and rewritten: Addy Osmani
\ No newline at end of file
[Brandon Aaron](http://brandonaaron.net) wrote the original version of this application, which was then refactored and rewritten by Addy Osmani.
\ No newline at end of file
......@@ -3,7 +3,7 @@
## Getting started
You need [CoffeScript](http://coffeescript.org) to compile if you make changes to the files in the `src` folder.
[CoffeeScript](http://coffeescript.org) is required to compile this application if you make changes to the files in the `src` folder.
### Compile
......
......@@ -213,26 +213,26 @@ function redrawTodosUI() {
for ( i= 0; i < todos.length; i++ ) {
todo = todos[i];
//create checkbox
// create checkbox
checkbox = document.createElement( 'input' );
checkbox.className = 'toggle';
checkbox.setAttribute( 'data-todo-id', todo.id );
checkbox.type = 'checkbox';
checkbox.addEventListener( 'change', checkboxChangeHandler );
//create div text
// create div text
label = document.createElement( 'label' );
label.setAttribute( 'data-todo-id', todo.id );
label.appendChild( document.createTextNode( todo.title ) );
//create delete button
// create delete button
deleteLink = document.createElement( 'button' );
deleteLink.className = 'destroy';
deleteLink.setAttribute( 'data-todo-id', todo.id );
deleteLink.addEventListener( 'click', spanDeleteClickHandler );
//create divDisplay
// create divDisplay
divDisplay = document.createElement( 'div' );
divDisplay.className = 'view';
divDisplay.setAttribute( 'data-todo-id', todo.id );
......@@ -242,7 +242,7 @@ function redrawTodosUI() {
divDisplay.addEventListener( 'dblclick', todoContentHandler );
//create todo input
// create todo input
inputEditTodo = document.createElement( 'input' );
inputEditTodo.id = 'input_' + todo.id;
inputEditTodo.className = 'edit';
......@@ -251,7 +251,7 @@ function redrawTodosUI() {
inputEditTodo.addEventListener( 'blur', inputEditTodoBlurHandler );
//create li
// create li
li = document.createElement( 'li' );
li.id = 'li_' + todo.id;
li.appendChild( divDisplay );
......@@ -297,7 +297,7 @@ function drawTodoCount() {
var number,
theText,
remaining;
// Create remaining count
// create remaining count
number = document.createElement( 'strong' );
number.innerHTML = stat.todoLeft;
theText = ' item';
......
......@@ -9,7 +9,7 @@ Read the [App Specification](https://github.com/addyosmani/todomvc/wiki/App-Spec
## Need help?
Feel free to [contact me](https://github.com/sindresorhus) if you have any questions or need help with the template.
Feel free to [contact us](https://github.com/sindresorhus) if you have any questions or need help with the template.
## Credit
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment