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 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