blob: d2693ec20ee125bf81effe7a72b8374e1628d9a3 [file] [log] [blame]
'use strict';
class Controller {
/**
* Take a model & view, then act as controller between them
* @param {object} model The model instance
* @param {object} view The view instance
*/
constructor(model, view) {
this.model = model;
this.view = view;
this.view.bind('newTodo', title => this.addItem(title));
this.view.bind('itemEdit', item => this.editItem(item.id));
this.view.bind('itemEditDone', item => this.editItemSave(item.id, item.title));
this.view.bind('itemEditCancel', item => this.editItemCancel(item.id));
this.view.bind('itemRemove', item => this.removeItem(item.id));
this.view.bind('itemToggle', item => this.toggleComplete(item.id, item.completed));
this.view.bind('removeCompleted', () => this.removeCompletedItems());
this.view.bind('toggleAll', status => this.toggleAll(status.completed));
}
/**
* Load & Initialize the view
* @param {string} '' | 'active' | 'completed'
*/
setView(hash){
let route = hash.split('/')[1];
let page = route || '';
this._updateFilter(page);
}
/**
* Event fires on load. Gets all items & displays them
*/
showAll(){
this.model.read(data => this.view.render('showEntries', data));
}
/**
* Renders all active tasks
*/
showActive(){
this.model.read({completed: false}, data => this.view.render('showEntries', data));
}
/**
* Renders all completed tasks
*/
showCompleted(){
this.model.read({completed: true}, data => this.view.render('showEntries', data));
}
/**
* An event to fire whenever you want to add an item. Simply pass in the event
* object and it'll handle the DOM insertion and saving of the new item.
*/
addItem(title){
if (title.trim() === '') {
return;
}
this.model.create(title, () => {
this.view.render('clearNewTodo');
this._filter(true);
});
}
/*
* Triggers the item editing mode.
*/
editItem(id){
this.model.read(id, data => {
let title = data[0].title;
this.view.render('editItem', {id, title});
});
}
/*
* Finishes the item editing mode successfully.
*/
editItemSave(id, title){
title = title.trim();
if (title.length !== 0) {
this.model.update(id, {title}, () => {
this.view.render('editItemDone', {id, title});
});
} else {
this.removeItem(id);
}
}
/*
* Cancels the item editing mode.
*/
editItemCancel(id){
this.model.read(id, data => {
let title = data[0].title;
this.view.render('editItemDone', {id, title});
});
}
/**
* Find the DOM element with given ID,
* Then remove it from DOM & Storage
*/
removeItem(id){
this.model.remove(id, () => this.view.render('removeItem', id));
this._filter();
}
/**
* Will remove all completed items from the DOM and storage.
*/
removeCompletedItems(){
this.model.read({completed: true}, data => {
for (let item of data) {
this.removeItem(item.id);
}
});
this._filter();
}
/**
* Give it an ID of a model and a checkbox and it will update the item
* in storage based on the checkbox's state.
*
* @param {number} id The ID of the element to complete or uncomplete
* @param {object} checkbox The checkbox to check the state of complete
* or not
* @param {boolean|undefined} silent Prevent re-filtering the todo items
*/
toggleComplete(id, completed, silent){
this.model.update(id, {completed}, () => {
this.view.render('elementComplete', {id, completed});
});
if (!silent) {
this._filter();
}
}
/**
* Will toggle ALL checkboxes' on/off state and completeness of models.
* Just pass in the event object.
*/
toggleAll(completed){
this.model.read({completed: !completed}, data => {
for (let item of data) {
this.toggleComplete(item.id, completed, true);
}
});
this._filter();
}
/**
* Updates the pieces of the page which change depending on the remaining
* number of todos.
*/
_updateCount(){
this.model.getCount(todos => {
const completed = todos.completed;
const visible = completed > 0;
const checked = completed === todos.total;
this.view.render('updateElementCount', todos.active);
this.view.render('clearCompletedButton', {completed, visible});
this.view.render('toggleAll', {checked});
this.view.render('contentBlockVisibility', {visible: todos.total > 0});
});
}
/**
* Re-filters the todo items, based on the active route.
* @param {boolean|undefined} force forces a re-painting of todo items.
*/
_filter(force){
let active = this._activeRoute;
const activeRoute = active.charAt(0).toUpperCase() + active.substr(1);
// Update the elements on the page, which change with each completed todo
this._updateCount();
// If the last active route isn't "All", or we're switching routes, we
// re-create the todo item elements, calling:
// this.show[All|Active|Completed]()
if (force || this._lastActiveRoute !== 'All' || this._lastActiveRoute !== activeRoute) {
this['show' + activeRoute]();
}
this._lastActiveRoute = activeRoute;
}
/**
* Simply updates the filter nav's selected states
*/
_updateFilter(currentPage){
// Store a reference to the active route, allowing us to re-filter todo
// items as they are marked complete or incomplete.
this._activeRoute = currentPage;
if (currentPage === '') {
this._activeRoute = 'All';
}
this._filter();
this.view.render('setFilter', currentPage);
}
}