Commit 7c5e9bee authored by Addy Osmani's avatar Addy Osmani

Merge pull request #699 from rmosolgo/rm/rewrite-batman

Redo Batman App
parents e247d4e9 0fd9c99b
class Alfred extends Batman.App class Alfred extends Batman.App
@root 'todos#all' @root 'todos#index', as: "all"
@route '/completed', 'todos#completed' @route '/completed', 'todos#completed'
@route '/active', 'todos#active' @route '/active', 'todos#active'
...@@ -9,32 +9,27 @@ class Alfred.TodosController extends Batman.Controller ...@@ -9,32 +9,27 @@ class Alfred.TodosController extends Batman.Controller
@set('newTodo', new Alfred.Todo(completed: false)) @set('newTodo', new Alfred.Todo(completed: false))
routingKey: 'todos' routingKey: 'todos'
currentTodoSet: 'all'
@accessor 'currentTodos', -> Alfred.Todo.get(@get('currentTodoSet')) index: ->
all: ->
@set('currentTodoSet', 'all') @set('currentTodoSet', 'all')
completed: -> completed: ->
@set 'currentTodoSet', 'completed' @set 'currentTodoSet', 'completed'
@render(source: 'todos/all') @render(source: 'todos/index') # uses the same template
active: -> active: ->
@set 'currentTodoSet', 'active' @set 'currentTodoSet', 'active'
@render(source: 'todos/all') @render(source: 'todos/index')
createTodo: -> class Alfred.TodosIndexView extends Batman.View
@get('newTodo').save (err, todo) => createTodo: (node, event, context) ->
if err event.preventDefault()
throw err unless err instanceof Batman.ErrorsSet title = node[0].value
else if title
@set 'newTodo', new Alfred.Todo(completed: false, title: "") newTodo = new Alfred.Todo(title: title, completed: false)
newTodo.save (err) ->
todoDoneChanged: (node, event, context) -> if not err
todo = context.get('todo') node[0].value = null
todo.save (err) ->
throw err if err && !err instanceof Batman.ErrorsSet
destroyTodo: (node, event, context) -> destroyTodo: (node, event, context) ->
todo = context.get('todo') todo = context.get('todo')
...@@ -43,8 +38,6 @@ class Alfred.TodosController extends Batman.Controller ...@@ -43,8 +38,6 @@ class Alfred.TodosController extends Batman.Controller
toggleAll: (node, context) -> toggleAll: (node, context) ->
Alfred.Todo.get('all').forEach (todo) -> Alfred.Todo.get('all').forEach (todo) ->
todo.set('completed', !!node.checked) todo.set('completed', !!node.checked)
todo.save (err) ->
throw err if err && !err instanceof Batman.ErrorsSet
clearCompleted: -> clearCompleted: ->
Alfred.Todo.get('completed').forEach (todo) -> Alfred.Todo.get('completed').forEach (todo) ->
...@@ -56,31 +49,40 @@ class Alfred.TodosController extends Batman.Controller ...@@ -56,31 +49,40 @@ class Alfred.TodosController extends Batman.Controller
if editing if editing
input = document.getElementById("todo-input-#{todo.get('id')}") input = document.getElementById("todo-input-#{todo.get('id')}")
input.focus() input.focus()
else
if todo.get('title')?.length > 0 disableEditingOnEnter: (node, event, context) ->
todo.save (err, todo) ->
throw err if err && !err instanceof Batman.ErrorsSet
else
todo.destroy (err, todo) ->
throw err if err
disableEditingUponSubmit: (node, event, context) ->
node.blur() if Batman.DOM.events.isEnter(event) node.blur() if Batman.DOM.events.isEnter(event)
# These views get instantiated by name. Extend TodosIndex so they have the same methods.
class Alfred.TodosActiveView extends Alfred.TodosIndexView
class Alfred.TodosCompletedView extends Alfred.TodosIndexView
class Alfred.Todo extends Batman.Model class Alfred.Todo extends Batman.Model
@encode 'title', 'completed'
@persist Batman.LocalStorage @persist Batman.LocalStorage
@validate 'title', presence: true
@storageKey: 'todos-batman' @storageKey: 'todos-batman'
constructor: ->
super # instantiate the record
# set up some observers on the record's attributes:
@observe 'completed', (newValue, oldValue) ->
@save()
@observe 'editing', (newValue, oldValue) ->
if newValue == false
if @get('title').length > 0
@set('title', @get('title').trim())
@save()
else
@destroy()
@
@encode 'title', 'completed'
@validate 'title', presence: true
@classAccessor 'active', -> @classAccessor 'active', ->
@get('all').filter (todo) -> !todo.get('completed') @get('all').filter (todo) -> !todo.get('completed')
@classAccessor 'completed', -> @classAccessor 'completed', ->
@get('all').filter (todo) -> todo.get('completed') @get('all').filter (todo) -> todo.get('completed')
@wrapAccessor 'title', (core) -> @Alfred = Alfred
set: (key, value) -> core.set.call(@, key, value?.trim())
window.Alfred = Alfred
Alfred.run()
// Generated by CoffeeScript 1.6.2 // Generated by CoffeeScript 1.6.3
(function() { (function() {
var Alfred, _ref, _ref1, var Alfred, _ref, _ref1, _ref2, _ref3,
__hasProp = {}.hasOwnProperty, __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
return _ref; return _ref;
} }
Alfred.root('todos#all'); Alfred.root('todos#index', {
as: "all"
});
Alfred.route('/completed', 'todos#completed'); Alfred.route('/completed', 'todos#completed');
...@@ -34,61 +36,55 @@ ...@@ -34,61 +36,55 @@
TodosController.prototype.routingKey = 'todos'; TodosController.prototype.routingKey = 'todos';
TodosController.prototype.currentTodoSet = 'all'; TodosController.prototype.index = function() {
TodosController.accessor('currentTodos', function() {
return Alfred.Todo.get(this.get('currentTodoSet'));
});
TodosController.prototype.all = function() {
return this.set('currentTodoSet', 'all'); return this.set('currentTodoSet', 'all');
}; };
TodosController.prototype.completed = function() { TodosController.prototype.completed = function() {
this.set('currentTodoSet', 'completed'); this.set('currentTodoSet', 'completed');
return this.render({ return this.render({
source: 'todos/all' source: 'todos/index'
}); });
}; };
TodosController.prototype.active = function() { TodosController.prototype.active = function() {
this.set('currentTodoSet', 'active'); this.set('currentTodoSet', 'active');
return this.render({ return this.render({
source: 'todos/all' source: 'todos/index'
}); });
}; };
TodosController.prototype.createTodo = function() { return TodosController;
var _this = this;
return this.get('newTodo').save(function(err, todo) { })(Batman.Controller);
if (err) {
if (!(err instanceof Batman.ErrorsSet)) {
throw err;
}
} else {
return _this.set('newTodo', new Alfred.Todo({
completed: false,
title: ""
}));
}
});
};
TodosController.prototype.todoDoneChanged = function(node, event, context) { Alfred.TodosIndexView = (function(_super) {
var todo; __extends(TodosIndexView, _super);
todo = context.get('todo'); function TodosIndexView() {
return todo.save(function(err) { _ref1 = TodosIndexView.__super__.constructor.apply(this, arguments);
if (err && !err instanceof Batman.ErrorsSet) { return _ref1;
throw err; }
}
}); TodosIndexView.prototype.createTodo = function(node, event, context) {
var newTodo, title;
event.preventDefault();
title = node[0].value;
if (title) {
newTodo = new Alfred.Todo({
title: title,
completed: false
});
return newTodo.save(function(err) {
if (!err) {
return node[0].value = null;
}
});
}
}; };
TodosController.prototype.destroyTodo = function(node, event, context) { TodosIndexView.prototype.destroyTodo = function(node, event, context) {
var todo; var todo;
todo = context.get('todo'); todo = context.get('todo');
return todo.destroy(function(err) { return todo.destroy(function(err) {
if (err) { if (err) {
...@@ -97,18 +93,13 @@ ...@@ -97,18 +93,13 @@
}); });
}; };
TodosController.prototype.toggleAll = function(node, context) { TodosIndexView.prototype.toggleAll = function(node, context) {
return Alfred.Todo.get('all').forEach(function(todo) { return Alfred.Todo.get('all').forEach(function(todo) {
todo.set('completed', !!node.checked); return todo.set('completed', !!node.checked);
return todo.save(function(err) {
if (err && !err instanceof Batman.ErrorsSet) {
throw err;
}
});
}); });
}; };
TodosController.prototype.clearCompleted = function() { TodosIndexView.prototype.clearCompleted = function() {
return Alfred.Todo.get('completed').forEach(function(todo) { return Alfred.Todo.get('completed').forEach(function(todo) {
return todo.destroy(function(err) { return todo.destroy(function(err) {
if (err) { if (err) {
...@@ -118,59 +109,81 @@ ...@@ -118,59 +109,81 @@
}); });
}; };
TodosController.prototype.toggleEditing = function(node, event, context) { TodosIndexView.prototype.toggleEditing = function(node, event, context) {
var editing, input, todo, _ref1; var editing, input, todo;
todo = context.get('todo'); todo = context.get('todo');
editing = todo.set('editing', !todo.get('editing')); editing = todo.set('editing', !todo.get('editing'));
if (editing) { if (editing) {
input = document.getElementById("todo-input-" + (todo.get('id'))); input = document.getElementById("todo-input-" + (todo.get('id')));
return input.focus(); return input.focus();
} else {
if (((_ref1 = todo.get('title')) != null ? _ref1.length : void 0) > 0) {
return todo.save(function(err, todo) {
if (err && !err instanceof Batman.ErrorsSet) {
throw err;
}
});
} else {
return todo.destroy(function(err, todo) {
if (err) {
throw err;
}
});
}
} }
}; };
TodosController.prototype.disableEditingUponSubmit = function(node, event, context) { TodosIndexView.prototype.disableEditingOnEnter = function(node, event, context) {
if (Batman.DOM.events.isEnter(event)) { if (Batman.DOM.events.isEnter(event)) {
return node.blur(); return node.blur();
} }
}; };
return TodosController; return TodosIndexView;
})(Batman.Controller); })(Batman.View);
Alfred.TodosActiveView = (function(_super) {
__extends(TodosActiveView, _super);
function TodosActiveView() {
_ref2 = TodosActiveView.__super__.constructor.apply(this, arguments);
return _ref2;
}
return TodosActiveView;
})(Alfred.TodosIndexView);
Alfred.TodosCompletedView = (function(_super) {
__extends(TodosCompletedView, _super);
function TodosCompletedView() {
_ref3 = TodosCompletedView.__super__.constructor.apply(this, arguments);
return _ref3;
}
return TodosCompletedView;
})(Alfred.TodosIndexView);
Alfred.Todo = (function(_super) { Alfred.Todo = (function(_super) {
__extends(Todo, _super); __extends(Todo, _super);
Todo.persist(Batman.LocalStorage);
Todo.storageKey = 'todos-batman';
function Todo() { function Todo() {
_ref1 = Todo.__super__.constructor.apply(this, arguments); Todo.__super__.constructor.apply(this, arguments);
return _ref1; this.observe('completed', function(newValue, oldValue) {
return this.save();
});
this.observe('editing', function(newValue, oldValue) {
if (newValue === false) {
if (this.get('title').length > 0) {
this.set('title', this.get('title').trim());
return this.save();
} else {
return this.destroy();
}
}
});
this;
} }
Todo.encode('title', 'completed'); Todo.encode('title', 'completed');
Todo.persist(Batman.LocalStorage);
Todo.validate('title', { Todo.validate('title', {
presence: true presence: true
}); });
Todo.storageKey = 'todos-batman';
Todo.classAccessor('active', function() { Todo.classAccessor('active', function() {
return this.get('all').filter(function(todo) { return this.get('all').filter(function(todo) {
return !todo.get('completed'); return !todo.get('completed');
...@@ -183,20 +196,10 @@ ...@@ -183,20 +196,10 @@
}); });
}); });
Todo.wrapAccessor('title', function(core) {
return {
set: function(key, value) {
return core.set.call(this, key, value != null ? value.trim() : void 0);
}
};
});
return Todo; return Todo;
})(Batman.Model); })(Batman.Model);
window.Alfred = Alfred; this.Alfred = Alfred;
Alfred.run();
}).call(this); }).call(this);
...@@ -7,35 +7,49 @@ ...@@ -7,35 +7,49 @@
<link rel="stylesheet" href="bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="bower_components/todomvc-common/base.css">
</head> </head>
<body> <body>
<div data-yield="main"></div> <section id="todoapp">
<div data-defineview="todos/all"> <header id="header">
<section id="todoapp"> <h1>todos</h1>
<header id="header"> <div data-yield="header"></div>
<h1>todos</h1> </header>
<form data-formfor-todo="newTodo" data-event-submit="createTodo"> <div data-yield="main"></div>
<input id="new-todo" type="text" placeholder="What needs to be completed?" autofocus data-bind="todo.title"> </section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://batmanjs.org">Harry Brundage</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<div data-defineview="todos/index">
<div data-contentfor="header">
<form data-event-submit="createTodo">
<input id="new-todo" type="text" placeholder="What needs to be completed?" autofocus>
</form> </form>
</header> </div>
<section id="main" class="hidden" data-removeclass-hidden="Todo.all.length"> <section id="main" class="hidden" data-removeclass-hidden="Todo.all.length">
<input id="toggle-all" type="checkbox" data-event-change="toggleAll" data-source="Todo.completed.length | equals Todo.all.length"> <input id="toggle-all" type="checkbox" data-event-change="toggleAll" data-source="Todo.completed.length | equals Todo.all.length">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"> <ul id="todo-list">
<li data-foreach-todo="currentTodos" <li data-foreach-todo="Todo[currentTodoSet]"
data-addclass-completed="todo.completed" data-context="todo"
data-addclass-editing="todo.editing" > data-addclass-completed="completed"
<div class="view" data-hideif="todo.editing"> data-addclass-editing="editing" >
<input class="toggle" type="checkbox" data-bind="todo.completed" data-event-change="todoDoneChanged"> <div class="view" data-hideif="editing">
<label data-bind="todo.title" data-event-doubleclick="toggleEditing"></label> <input class="toggle" type="checkbox" data-bind="completed">
<label data-bind="title" data-event-doubleclick="toggleEditing"></label>
<button class="destroy" data-event-click="destroyTodo"></button> <button class="destroy" data-event-click="destroyTodo"></button>
</div> </div>
<input class="edit" type="text" <input class="edit" type="text"
data-bind="todo.title" data-bind="title"
data-bind-id="'todo-input-' | append todo.id" data-bind-id="'todo-input-' | append id"
data-event-blur="toggleEditing" data-event-blur="toggleEditing"
data-event-keypress="disableEditingUponSubmit"> data-event-keypress="disableEditingOnEnter">
</li> </li>
</ul> </ul>
</section> </section>
<footer id="footer" class="hidden" data-removeclass-hidden="Todo.all.length"> <footer id="footer" class="hidden" data-removeclass-hidden="Todo.all.length">
<span id="todo-count"> <span id="todo-count">
<strong data-bind="Todo.active.length"></strong> <strong data-bind="Todo.active.length"></strong>
...@@ -43,28 +57,26 @@ ...@@ -43,28 +57,26 @@
</span> </span>
<ul id="filters"> <ul id="filters">
<li> <li>
<a data-addclass-selected="currentRoute.action | equals 'all'" data-route="'/'">All</a> <a data-addclass-selected="currentTodoSet | equals 'all'" data-route="routes.all">All</a>
</li> </li>
<li> <li>
<a data-addclass-selected="currentRoute.action | equals 'active'" data-route="'/active'">Active</a> <a data-addclass-selected="currentTodoSet | equals 'active'" data-route="routes.active">Active</a>
</li> </li>
<li> <li>
<a data-addclass-selected="currentRoute.action | equals 'completed'" data-route="'/completed'">Completed</a> <a data-addclass-selected="currentTodoSet | equals 'completed'" data-route="routes.completed">Completed</a>
</li> </li>
</ul> </ul>
<button id="clear-completed" data-event-click="clearCompleted" data-showif="Todo.completed.length"> <button id="clear-completed" data-event-click="clearCompleted" data-showif="Todo.completed.length">
Clear completed (<span data-bind="Todo.completed.length"></span>) Clear completed (<span data-bind="Todo.completed.length"></span>)
</button> </button>
</footer> </footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://batmanjs.org">Harry Brundage</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div> </div>
<script src="bower_components/todomvc-common/base.js"></script> <script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/batman/lib/dist/batman.js"></script> <script src="bower_components/batman/lib/dist/batman.js"></script>
<script src="app.js"></script> <script src="app.js"></script>
<script>
Alfred.run()
</script>
</body> </body>
</html> </html>
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
"coffee-script": "~1.6.2" "coffee-script": "~1.6.2"
}, },
"scripts": { "scripts": {
"compile": "coffee -c app.coffee" "compile": "coffee -c app.coffee",
"watch": "coffee -wc app.coffee"
}, },
"private": true "private": true
} }
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