| '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); |
| } |
| } |