// (function (window) {
'use strict';
/**
* The controller allows interaction between the {@link Model} and the {@Link View}.
*
* @constructor
* @param {object} model The {@link Model} instance
* @param {object} view The {@Link 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 {@Link View}
*
* @param {string} locationHash The hash of the actual page can have these values :
* '' | '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 with the parameter completed: false.
*/
Controller.prototype.showActive = function () {
var self = this;
self.model.read({ completed: false }, function (data) {
self.view.render('showEntries', data);
});
};
/**
* Renders all completed tasks with the parameter completed: true.
*/
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.
*
* @param {string} title The title of the item to add
*/
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.
*
* @param {number} id ID of the new model to edit.
*/
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.
*
* @param {number} id ID of the edited model to save
* @param {string} title The content of the todo.
*/
Controller.prototype.editItemSave = function (id, title) {
var self = this;
// Optimized with trim method (delete spaces)
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 of the element.
*
* @param {number} id ID of the model being edited.
*/
Controller.prototype.editItemCancel = function (id) {
var self = this;
self.model.read(id, function (data) {
self.view.render('editItemDone', {id: id, title: data[0].title});
});
};
/**
* Removes an item from the list : 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;
var items;
self.model.read(function(data) {
items = data;
});
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();
};
/**
* Updates the display of items based on their status wich depend from the checkbox 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).
*
* @param {object} checkbox The checkbox to validate the status of the element.
*/
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});
// Optimized (toggleAllVisibility for tests phasing)
self.view.render('contentBlockVisibility', {visible: todos.total > 0});
self.view.render('toggleAllVisibility', {visible: todos.total > 0});
});
};
/**
* Re-filters the todo items, based on the active route.
*
* @param {boolean|undefined} force Force item re-filtering
*/
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 routes in url
*
* @param {string} currentPage '' || active || completed.
*/
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);