todomvc
Version:
> Helping you select an MV\* framework
236 lines (201 loc) • 5.93 kB
JavaScript
/*global YUI*/
YUI.add('todo-app', function (Y) {
'use strict';
// Dependencies from MVC namespace.
var TodoApp;
var TodoList = Y.TodoMVC.TodoList;
var TodoView = Y.TodoMVC.TodoView;
// -- Main Application --------------
TodoApp = Y.Base.create('todoApp', Y.App, [], {
// Set container to bind to the existing '#todoapp' element
containerTemplate: '#todoapp',
// Compile statistics template with Handlebars.
template: Y.Handlebars.compile(Y.one('#stats-template').getHTML()),
// DOM events for creating new Todos and clearing out old ones.
events: {
'#new-todo': {
keypress: 'enterCreate'
},
'#clear-completed': {
click: 'clearCompleted'
},
'#toggle-all': {
click: 'completeAll'
}
},
// Initialize our TodoList, and bind any events that occur
// when new Todos are added, changed, or removed within it.
// Also, fetch any Todos that are found within localStorage.
initializer: function () {
this.set('todoList', new TodoList());
var list = this.get('todoList');
Y.Handlebars.registerHelper('pluralize', function (context, word) {
return (context === 1) ? word : word + 's';
});
list.after(['add', 'remove', 'reset', 'todo:completedChange'],
this.render, this);
list.load();
// Keep our filters on refresh by immediately dispatching route.
this.once('ready', function () {
if (this.hasRoute(this.getPath())) {
this.dispatch();
}
});
},
// Render our application with the statistics from our TodoList,
// and various other stylistic elements.
render: function () {
var todoList = this.get('todoList');
var completed = todoList.completed().size();
var remaining = todoList.remaining().size();
var main = this.get('main');
var footer = this.get('footer');
// If we have Todos in our TodoList, show them with statistics.
if (todoList.size()) {
main.show();
footer.show();
footer.setHTML(this.template({
completed: completed,
remaining: remaining
}));
// Highlights for filters at the bottom of our Todo application.
footer.one('#filters li a').removeClass('selected');
footer.all('#filters li a')
.filter('[href="#/' + (this.get('filter') || '') + '"]')
.addClass('selected');
} else {
main.hide();
footer.hide();
}
// Set the checkbox only if all Todos have been completed.
this.get('allCheckbox').set('checked', !remaining);
this.addViews();
},
// Add Todo views to the DOM simultaneously, triggered when
// the application initially loads, or we switch filters.
addViews: function () {
var models;
var fragment = Y.one(Y.config.doc.createDocumentFragment());
var todoList = this.get('todoList');
// An Array of models is passed through when the 'reset'
// event is triggered through syncing through load().
switch (this.get('filter')) {
case 'active':
models = todoList.remaining();
break;
case 'completed':
models = todoList.completed();
break;
default:
models = todoList;
break;
}
// Iterate through the (filtered) ModelList.
models.each(function (model) {
var view = new TodoView({model: model});
fragment.append(view.render().get('container'));
});
this.get('container').one('#todo-list').setContent(fragment);
},
// Create and save a new Todo from the inputted value when the
// Enter key is pressed down.
enterCreate: function (e) {
var ENTER_KEY = 13;
var todoList = this.get('todoList');
var inputNode = this.get('inputNode');
var value = inputNode.get('value');
if (e.keyCode !== ENTER_KEY || !value) {
return;
}
todoList.create({
title: value
});
inputNode.set('value', '');
},
// Clear all completed Todos from the TodoList. This removes the models
// from the list, as well as deletes them from localStorage.
clearCompleted: function () {
var todoList = this.get('todoList');
var completed = todoList.completed();
todoList.remove(completed);
completed.each(function (todo) {
todo.clear();
});
},
// Complete all non-complete Todos, or reset them all if they are
// all already complete.
completeAll: function () {
var todoList = this.get('todoList');
var allCheckbox = this.get('allCheckbox');
var completed = allCheckbox.get('checked');
todoList.each(function (todo) {
todo.save({completed: completed});
});
},
// Set the filter for our application from the route that is passed
// in (see below).
handleFilter: function (req) {
this.set('filter', req.params.filter);
this.get('todoList').load();
}
}, {
ATTRS: {
// Significant DOM elements that relate to our application that
// we would like to keep as attributes.
container: {
valueFn: function () {
return Y.one('#todoapp');
}
},
inputNode: {
valueFn: function () {
return Y.one('#new-todo');
}
},
allCheckbox: {
valueFn: function () {
return Y.one('#toggle-all');
}
},
main: {
valueFn: function () {
return Y.one('#main');
}
},
footer: {
valueFn: function () {
return Y.one('#footer');
}
},
// This can be set to fall back on server-side routing when
// HTML5 pushState is not available. For this application,
// we are only using hash-based URLs though.
serverRouting: {
value: false
},
// Our initial filter for the application.
filter: {
value: null
},
// Routing for the application, to determine the filter.
// The callback takes a request object, Express-style.
routes: {
value: [
{
path: '/:filter',
callback: 'handleFilter'
}
]
}
}
});
// Namespace this application under our custom Y.MVC namespace.
Y.namespace('TodoMVC').TodoApp = TodoApp;
}, '@VERSION@', {
requires: [
'app-base',
'todo-list',
'todo-view',
'node'
]
});