1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
define([
'jquery',
'underscore',
'backbone',
'collections/todos',
'views/todos',
'text!templates/stats.html',
'common'
], function( $, _, Backbone, Todos, TodoView, statsTemplate, Common ) {
var AppView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: '#todoapp',
// Compile our stats template
template: _.template( statsTemplate ),
// Delegated events for creating new items, and clearing completed ones.
events: {
'keypress #new-todo': 'createOnEnter',
'click #clear-completed': 'clearCompleted',
'click #toggle-all': 'toggleAllComplete'
},
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize: function() {
this.input = this.$('#new-todo');
this.allCheckbox = this.$('#toggle-all')[0];
this.$footer = this.$('#footer');
this.$main = this.$('#main');
Todos.on( 'add', this.addAll, this );
Todos.on( 'reset', this.addAll, this );
Todos.on( 'change:completed', this.addAll, this );
Todos.on( 'all', this.render, this );
Todos.fetch();
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function() {
var completed = Todos.completed().length;
var remaining = Todos.remaining().length;
if ( Todos.length ) {
this.$main.show();
this.$footer.show();
this.$footer.html(this.template({
completed: completed,
remaining: remaining
}));
this.$('#filters li a')
.removeClass('selected')
.filter( '[href="#/' + ( Common.TodoFilter || '' ) + '"]' )
.addClass('selected');
} else {
this.$main.hide();
this.$footer.hide();
}
this.allCheckbox.checked = !remaining;
},
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
addOne: function( todo ) {
var view = new TodoView({ model: todo });
$('#todo-list').append( view.render().el );
},
// Add all items in the **Todos** collection at once.
addAll: function() {
this.$('#todo-list').html('');
switch( Common.TodoFilter ) {
case 'active':
_.each( Todos.remaining(), this.addOne );
break;
case 'completed':
_.each( Todos.completed(), this.addOne );
break;
default:
Todos.each( this.addOne, this );
break;
}
},
// Generate the attributes for a new Todo item.
newAttributes: function() {
return {
title: this.input.val().trim(),
order: Todos.nextOrder(),
completed: false
};
},
// If you hit return in the main input field, create new **Todo** model,
// persisting it to *localStorage*.
createOnEnter: function( e ) {
if ( e.which !== Common.ENTER_KEY || !this.input.val().trim() ) {
return;
}
Todos.create( this.newAttributes() );
this.input.val('');
},
// Clear all completed todo items, destroying their models.
clearCompleted: function() {
_.each( Todos.completed(), function( todo ) {
todo.destroy();
});
return false;
},
toggleAllComplete: function() {
var completed = this.allCheckbox.checked;
Todos.each(function( todo ) {
todo.save({
'completed': completed
});
});
}
});
return AppView;
});