var Skin = Exact.Skin;
var Store = Exact.Store;
var Component = Exact.Component;
var Collection = Exact.Collection;
var id = 0;
var STATUS = { ALL: 'all', ACTIVE: 'active', COMPLETED: 'completed'
};
function filter(todos, status) { return todos.filter(function(todo) { return status === STATUS.ALL || (status === STATUS.COMPLETED ? todo.completed : !todo.completed);
});
}
var ENJ = { read: function() { var item = localStorage.getItem('todos');
if (item) { return JSON.parse(item);
}
},
save: function(todos) { localStorage.setItem('todos', JSON.stringify(todos)); }
};
ENJ.TodoAdapter = Exact.defineClass({ extend: Component,
statics: { template: Skin.query('.template .todo-adapter'),
defaults: function() { return { completed: false,
editing: false,
label: '',
text: ''
}
}
},
register: function() { Exact.help(this).bind('onToggle', 'onChange'); },
onToggle: function(event) { this.set('completed', event.target.checked); },
onChange: function(event) { this.set('text', event.target.value.trim()); },
startEditing: function() { this.set('text', this.label); this.set('editing', true); this.editor.focus();
},
doneEditing: function() { if (this.editing) { this.submit(this.text);
}
this.cancelEditing();
},
cancelEditing: function() { this.set('editing', false); },
submit: function(text) { if (text) { this.set('label', text); } else { this.destroy();
}
},
destroy: function() { this.send('destroy'); }
});
ENJ.TodoApp = Exact.defineClass({ extend: Component,
statics: { defaults: function() { return { todos: Collection.from([]),
status: STATUS.ALL,
newTodo: '',
remainingCount: 0
}
},
descriptors: { allDone: { depends: ['remainingCount'],
get: function() { return this.remainingCount === 0;
},
set: function(value) { this.todos.forEach(function(todo) { todo.set('completed', value); });
}
}
},
resources: { filter: filter,
display: function(visible) { return visible ? '' : 'none';
},
pluralize: function(num, str) { return str + (num !== 1 ? 's' : '');
},
TodoAdapter: ENJ.TodoAdapter
},
template: Skin.query('.template .todoapp') },
register: function() { Exact.help(this).bind('onEnter', 'onChange', 'onToggle'); },
ready: function() { var records = ENJ.read();
if (records) { for (var i = 0, n = records.length; i < n; ++i) { this.addTodo(Store.create(records[i]));
}
}
},
refresh: function() { var remainingCount = this.remainingCount;
this.set('remainingCount', this.todos.length - filter(this.todos, STATUS.COMPLETED).length);
if (this.status !== STATUS.ALL && this.remainingCount !== remainingCount ) { this.send('changed.todos'); }
ENJ.save(this.todos.slice(0));
},
addTodo: function(todo) { todo.on('changed.completed', this.invalidate); todo.id = ++id;
this.todos.insert(todo);
this.invalidate();
},
removeTodo: function(todo) { this.todos.remove(todo);
this.invalidate();
todo.off();
},
clearCompleted: function() { for (var i = this.todos.length - 1; i >= 0; --i) { if (this.todos[i].completed) { this.todos.splice(i, 1);
}
}
},
onToggle: function(event) { this.set('allDone', event.target.checked); },
onChange: function(event) { this.set('newTodo', event.target.value.trim()); },
onEnter: function(event) { if (this.newTodo) { this.addTodo(Store.create({ label: this.newTodo,
completed: false
}));
}
this.set('newTodo', ''); }
});
var app = Component.create(ENJ.TodoApp);
function onHashChange () { var hash = window.location.hash;
var status = STATUS[hash.replace(/#\/?/, '').toUpperCase()];
app.set('status', status || STATUS.ALL);}
onHashChange();
window.onhashchange = onHashChange;
app.attach(Skin.query('#todoapp'));