Commit 0fd9c99b authored by Robert Mosolgo's avatar Robert Mosolgo

add npm script watch

data-yield header in markup
use data-context todo
move view-related methods to view
reorder model definition, use @Alfred
simplify which tab is active
clean up routes
put run in HTML
save when completed changes
When done editing, trim the title and save the record, if no title, delete record
change form binding, make views for other controller actions
let binding save checked todos, rename keypress listener
parent e247d4e9
class Alfred extends Batman.App
@root 'todos#all'
@root 'todos#index', as: "all"
@route '/completed', 'todos#completed'
@route '/active', 'todos#active'
......@@ -9,32 +9,27 @@ class Alfred.TodosController extends Batman.Controller
@set('newTodo', new Alfred.Todo(completed: false))
routingKey: 'todos'
currentTodoSet: 'all'
@accessor 'currentTodos', -> Alfred.Todo.get(@get('currentTodoSet'))
all: ->
index: ->
@set('currentTodoSet', 'all')
completed: ->
@set 'currentTodoSet', 'completed'
@render(source: 'todos/all')
@render(source: 'todos/index') # uses the same template
active: ->
@set 'currentTodoSet', 'active'
@render(source: 'todos/all')
createTodo: ->
@get('newTodo').save (err, todo) =>
if err
throw err unless err instanceof Batman.ErrorsSet
else
@set 'newTodo', new Alfred.Todo(completed: false, title: "")
todoDoneChanged: (node, event, context) ->
todo = context.get('todo')
todo.save (err) ->
throw err if err && !err instanceof Batman.ErrorsSet
@render(source: 'todos/index')
class Alfred.TodosIndexView extends Batman.View
createTodo: (node, event, context) ->
event.preventDefault()
title = node[0].value
if title
newTodo = new Alfred.Todo(title: title, completed: false)
newTodo.save (err) ->
if not err
node[0].value = null
destroyTodo: (node, event, context) ->
todo = context.get('todo')
......@@ -43,8 +38,6 @@ class Alfred.TodosController extends Batman.Controller
toggleAll: (node, context) ->
Alfred.Todo.get('all').forEach (todo) ->
todo.set('completed', !!node.checked)
todo.save (err) ->
throw err if err && !err instanceof Batman.ErrorsSet
clearCompleted: ->
Alfred.Todo.get('completed').forEach (todo) ->
......@@ -56,31 +49,40 @@ class Alfred.TodosController extends Batman.Controller
if editing
input = document.getElementById("todo-input-#{todo.get('id')}")
input.focus()
else
if todo.get('title')?.length > 0
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) ->
disableEditingOnEnter: (node, event, context) ->
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
@encode 'title', 'completed'
@persist Batman.LocalStorage
@validate 'title', presence: true
@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', ->
@get('all').filter (todo) -> !todo.get('completed')
@classAccessor 'completed', ->
@get('all').filter (todo) -> todo.get('completed')
@wrapAccessor 'title', (core) ->
set: (key, value) -> core.set.call(@, key, value?.trim())
window.Alfred = Alfred
Alfred.run()
@Alfred = Alfred
// Generated by CoffeeScript 1.6.2
// Generated by CoffeeScript 1.6.3
(function() {
var Alfred, _ref, _ref1,
var Alfred, _ref, _ref1, _ref2, _ref3,
__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; };
......@@ -12,7 +12,9 @@
return _ref;
}
Alfred.root('todos#all');
Alfred.root('todos#index', {
as: "all"
});
Alfred.route('/completed', 'todos#completed');
......@@ -34,61 +36,55 @@
TodosController.prototype.routingKey = 'todos';
TodosController.prototype.currentTodoSet = 'all';
TodosController.accessor('currentTodos', function() {
return Alfred.Todo.get(this.get('currentTodoSet'));
});
TodosController.prototype.all = function() {
TodosController.prototype.index = function() {
return this.set('currentTodoSet', 'all');
};
TodosController.prototype.completed = function() {
this.set('currentTodoSet', 'completed');
return this.render({
source: 'todos/all'
source: 'todos/index'
});
};
TodosController.prototype.active = function() {
this.set('currentTodoSet', 'active');
return this.render({
source: 'todos/all'
source: 'todos/index'
});
};
TodosController.prototype.createTodo = function() {
var _this = this;
return TodosController;
return this.get('newTodo').save(function(err, todo) {
if (err) {
if (!(err instanceof Batman.ErrorsSet)) {
throw err;
}
} else {
return _this.set('newTodo', new Alfred.Todo({
completed: false,
title: ""
}));
}
});
};
})(Batman.Controller);
TodosController.prototype.todoDoneChanged = function(node, event, context) {
var todo;
Alfred.TodosIndexView = (function(_super) {
__extends(TodosIndexView, _super);
todo = context.get('todo');
return todo.save(function(err) {
if (err && !err instanceof Batman.ErrorsSet) {
throw err;
}
});
function TodosIndexView() {
_ref1 = TodosIndexView.__super__.constructor.apply(this, arguments);
return _ref1;
}
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;
todo = context.get('todo');
return todo.destroy(function(err) {
if (err) {
......@@ -97,18 +93,13 @@
});
};
TodosController.prototype.toggleAll = function(node, context) {
TodosIndexView.prototype.toggleAll = function(node, context) {
return Alfred.Todo.get('all').forEach(function(todo) {
todo.set('completed', !!node.checked);
return todo.save(function(err) {
if (err && !err instanceof Batman.ErrorsSet) {
throw err;
}
});
return todo.set('completed', !!node.checked);
});
};
TodosController.prototype.clearCompleted = function() {
TodosIndexView.prototype.clearCompleted = function() {
return Alfred.Todo.get('completed').forEach(function(todo) {
return todo.destroy(function(err) {
if (err) {
......@@ -118,59 +109,81 @@
});
};
TodosController.prototype.toggleEditing = function(node, event, context) {
var editing, input, todo, _ref1;
TodosIndexView.prototype.toggleEditing = function(node, event, context) {
var editing, input, todo;
todo = context.get('todo');
editing = todo.set('editing', !todo.get('editing'));
if (editing) {
input = document.getElementById("todo-input-" + (todo.get('id')));
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)) {
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) {
__extends(Todo, _super);
Todo.persist(Batman.LocalStorage);
Todo.storageKey = 'todos-batman';
function Todo() {
_ref1 = Todo.__super__.constructor.apply(this, arguments);
return _ref1;
Todo.__super__.constructor.apply(this, arguments);
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.persist(Batman.LocalStorage);
Todo.validate('title', {
presence: true
});
Todo.storageKey = 'todos-batman';
Todo.classAccessor('active', function() {
return this.get('all').filter(function(todo) {
return !todo.get('completed');
......@@ -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;
})(Batman.Model);
window.Alfred = Alfred;
Alfred.run();
this.Alfred = Alfred;
}).call(this);
......@@ -7,35 +7,49 @@
<link rel="stylesheet" href="bower_components/todomvc-common/base.css">
</head>
<body>
<div data-yield="main"></div>
<div data-defineview="todos/all">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<form data-formfor-todo="newTodo" data-event-submit="createTodo">
<input id="new-todo" type="text" placeholder="What needs to be completed?" autofocus data-bind="todo.title">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<div data-yield="header"></div>
</header>
<div data-yield="main"></div>
</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>
</header>
</div>
<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">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<li data-foreach-todo="currentTodos"
data-addclass-completed="todo.completed"
data-addclass-editing="todo.editing" >
<div class="view" data-hideif="todo.editing">
<input class="toggle" type="checkbox" data-bind="todo.completed" data-event-change="todoDoneChanged">
<label data-bind="todo.title" data-event-doubleclick="toggleEditing"></label>
<li data-foreach-todo="Todo[currentTodoSet]"
data-context="todo"
data-addclass-completed="completed"
data-addclass-editing="editing" >
<div class="view" data-hideif="editing">
<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>
</div>
<input class="edit" type="text"
data-bind="todo.title"
data-bind-id="'todo-input-' | append todo.id"
data-bind="title"
data-bind-id="'todo-input-' | append id"
data-event-blur="toggleEditing"
data-event-keypress="disableEditingUponSubmit">
data-event-keypress="disableEditingOnEnter">
</li>
</ul>
</section>
<footer id="footer" class="hidden" data-removeclass-hidden="Todo.all.length">
<span id="todo-count">
<strong data-bind="Todo.active.length"></strong>
......@@ -43,28 +57,26 @@
</span>
<ul id="filters">
<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>
<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>
<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>
</ul>
<button id="clear-completed" data-event-click="clearCompleted" data-showif="Todo.completed.length">
Clear completed (<span data-bind="Todo.completed.length"></span>)
</button>
</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>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/batman/lib/dist/batman.js"></script>
<script src="app.js"></script>
<script>
Alfred.run()
</script>
</body>
</html>
......@@ -7,7 +7,8 @@
"coffee-script": "~1.6.2"
},
"scripts": {
"compile": "coffee -c app.coffee"
"compile": "coffee -c app.coffee",
"watch": "coffee -wc app.coffee"
},
"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