| (function (window) { |
| 'use strict'; |
| |
| /** |
| * Takes a model and view and acts as the controller between them |
| * |
| * @constructor |
| * @param {object} model The model instance |
| * @param {object} view The view instance |
| */ |
| function Controller(model, view) { |
| var self = this; |
| self.model = model; |
| self.view = view; |
| |
| self.view.bind('newTodo', function (title) { |
| self.addItem(title); |
| }); |
| |
| self.view.bind('itemEdit', function (item) { |
| self.editItem(item.id); |
| }); |
| |
| self.view.bind('itemEditDone', function (item) { |
| self.editItemSave(item.id, item.title); |
| }); |
| |
| self.view.bind('itemEditCancel', function (item) { |
| self.editItemCancel(item.id); |
| }); |
| |
| self.view.bind('itemRemove', function (item) { |
| self.removeItem(item.id); |
| }); |
| |
| self.view.bind('itemToggle', function (item) { |
| self.toggleComplete(item.id, item.completed); |
| }); |
| |
| self.view.bind('removeCompleted', function () { |
| self.removeCompletedItems(); |
| }); |
| |
| self.view.bind('toggleAll', function (status) { |
| self.toggleAll(status.completed); |
| }); |
| } |
| |
| /** |
| * Loads and initialises the view |
| * |
| * @param {string} '' | 'active' | 'completed' |
| */ |
| Controller.prototype.setView = function (locationHash) { |
| var route = locationHash.split('/')[1]; |
| var page = route || ''; |
| this._updateFilterState(page); |
| }; |
| |
| /** |
| * An event to fire on load. Will get all items and display them in the |
| * todo-list |
| */ |
| Controller.prototype.showAll = function () { |
| var self = this; |
| self.model.read(function (data) { |
| self.view.render('showEntries', data); |
| }); |
| }; |
| |
| /** |
| * Renders all active tasks |
| */ |
| Controller.prototype.showActive = function () { |
| var self = this; |
| self.model.read({ completed: false }, function (data) { |
| self.view.render('showEntries', data); |
| }); |
| }; |
| |
| /** |
| * Renders all completed tasks |
| */ |
| Controller.prototype.showCompleted = function () { |
| var self = this; |
| self.model.read({ completed: true }, function (data) { |
| self.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. |
| */ |
| Controller.prototype.addItem = function (title) { |
| var self = this; |
| |
| if (title.trim() === '') { |
| return; |
| } |
| |
| self.model.create(title, function () { |
| self.view.render('clearNewTodo'); |
| self._filter(true); |
| }); |
| }; |
| |
| /* |
| * Triggers the item editing mode. |
| */ |
| Controller.prototype.editItem = function (id) { |
| var self = this; |
| self.model.read(id, function (data) { |
| self.view.render('editItem', {id: id, title: data[0].title}); |
| }); |
| }; |
| |
| /* |
| * Finishes the item editing mode successfully. |
| */ |
| Controller.prototype.editItemSave = function (id, title) { |
| var self = this; |
| title = title.trim(); |
| |
| if (title.length !== 0) { |
| self.model.update(id, {title: title}, function () { |
| self.view.render('editItemDone', {id: id, title: title}); |
| }); |
| } else { |
| self.removeItem(id); |
| } |
| }; |
| |
| /* |
| * Cancels the item editing mode. |
| */ |
| Controller.prototype.editItemCancel = function (id) { |
| var self = this; |
| self.model.read(id, function (data) { |
| self.view.render('editItemDone', {id: id, title: data[0].title}); |
| }); |
| }; |
| |
| /** |
| * By giving it an ID it'll find the DOM element matching that ID, |
| * remove it from the DOM and also remove it from storage. |
| * |
| * @param {number} id The ID of the item to remove from the DOM and |
| * storage |
| */ |
| Controller.prototype.removeItem = function (id) { |
| var self = this; |
| self.model.remove(id, function () { |
| self.view.render('removeItem', id); |
| }); |
| |
| self._filter(); |
| }; |
| |
| /** |
| * Will remove all completed items from the DOM and storage. |
| */ |
| Controller.prototype.removeCompletedItems = function () { |
| var self = this; |
| self.model.read({ completed: true }, function (data) { |
| data.forEach(function (item) { |
| self.removeItem(item.id); |
| }); |
| }); |
| |
| self._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 |
| */ |
| Controller.prototype.toggleComplete = function (id, completed, silent) { |
| var self = this; |
| self.model.update(id, { completed: completed }, function () { |
| self.view.render('elementComplete', { |
| id: id, |
| completed: completed |
| }); |
| }); |
| |
| if (!silent) { |
| self._filter(); |
| } |
| }; |
| |
| /** |
| * Will toggle ALL checkboxes' on/off state and completeness of models. |
| * Just pass in the event object. |
| */ |
| Controller.prototype.toggleAll = function (completed) { |
| var self = this; |
| self.model.read({ completed: !completed }, function (data) { |
| data.forEach(function (item) { |
| self.toggleComplete(item.id, completed, true); |
| }); |
| }); |
| |
| self._filter(); |
| }; |
| |
| /** |
| * Updates the pieces of the page which change depending on the remaining |
| * number of todos. |
| */ |
| Controller.prototype._updateCount = function () { |
| var self = this; |
| self.model.getCount(function (todos) { |
| self.view.render('updateElementCount', todos.active); |
| self.view.render('clearCompletedButton', { |
| completed: todos.completed, |
| visible: todos.completed > 0 |
| }); |
| |
| self.view.render('toggleAll', {checked: todos.completed === todos.total}); |
| self.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. |
| */ |
| Controller.prototype._filter = function (force) { |
| var activeRoute = this._activeRoute.charAt(0).toUpperCase() + this._activeRoute.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 |
| */ |
| Controller.prototype._updateFilterState = function (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); |
| }; |
| |
| // Export to window |
| window.app = window.app || {}; |
| window.app.Controller = Controller; |
| })(window); |