Skip to content

Instantly share code, notes, and snippets.

@OscarGodson
Created July 27, 2012 01:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OscarGodson/3185561 to your computer and use it in GitHub Desktop.
Save OscarGodson/3185561 to your computer and use it in GitHub Desktop.
// We'll wrap our app in the "app" namespace
app = {
model: {}
, view: {}
, controller: {}
};
// Setup our data if this is the first time opening our app
app.data = {
init: function(){
var data, dbName;
this._dbName = 'todos-vanillajs';
dbName = this._dbName;
if(!localStorage[dbName]){
data = {
tasks: {}
, id: 0
}
localStorage[dbName] = JSON.stringify(data);
}
this._data = localStorage[dbName];
}
// NOTE: In a real app this would be like an AJAX GET request
, payload: function(){
return JSON.parse(localStorage[this._dbName]);
}
// NOTE: In a real app this would be like an AJAX POST request
, sync: function(data){
localStorage[this._dbName] = JSON.stringify(data);
}
}
// OK, init the data... in a real app this would be a
// DB connection or something
app.data.init();
// Defined our model
app.model.Tasks = {
create: function(task){
if(task == undefined){
throw new Error('No task was given');
}
var data = app.data.payload();
data.tasks[data.id] = { "task": task, status: 0 };
data.id = data.id + 1;
app.data.sync(data);
return this;
}
, read: function(id){
var data = app.data.payload();
if(id == undefined){
return data.tasks;
}
else if(!data.tasks[id]){
throw new Error('No task found with that ID');
}
else{
return data.tasks[id];
}
}
, update: function(id, newData){
var data = app.data.payload();
if(id == undefined || data.tasks[id] == undefined){
throw new Error('No task found with that ID');
}
else{
for(x in newData){
data.tasks[id][x] = newData[x];
}
app.data.sync(data);
}
return this;
}
, destroy: function(id){
var data = app.data.payload();
if(id == undefined){
data.tasks = {};
}
else if(data.tasks[id] == undefined){
throw new Error('No task found with that ID');
}
else{
delete data.tasks[id];
}
app.data.sync(data);
return this;
}
}
// Define our view
app.view.Tasks = {
task: function(data){
var template, output, html, task, done;
template = '' +
'<li id="task-{{id}}" data-task-id="{{id}}">' +
'<div class="todo {{done}}">' +
'<div class="display">' +
'<input {{checked}} class="check" type="checkbox" data-task-id="{{id}}">' +
'<div class="todo-content">{{task}}</div>' +
'<span class="todo-destroy" data-task-id="{{id}}"></span>' +
'</div>' +
'</div>' +
'</li>';
for(x in data){
// Clone our template or...
if(output == undefined){
output = template;
}
// Add to our already created clone
else{
output = output+template;
}
task = data[x];
// Add the "checked" attribute if it's been done
if(task.status == '1'){
checked = 'checked';
done = 'done';
}
else{
checked = '';
done = '';
}
// Replace our tags with data
// NOTE: This _should_ use something like Mustache.js in the real world
output = output.replace(/{{id}}/g,x);
output = output.replace(/{{task}}/g,task.task);
output = output.replace(/{{checked}}/g,checked);
output = output.replace(/{{done}}/g,done);
}
//If there is no output return a blank string
return output ? output : '';
}
, stats: function(data){
var template
, todo = []
, done = []
, todoPlural = ''
, donePlural = '';
todoTemplate = '' +
'<div class="todo-count">' +
'<span class="number">{{todo-count}}</span> ' +
'<span class="word">item{{todo-plural}}</span>' +
'</div>';
doneTemplate = '' +
'<div class="todo-clear">' +
'<a href="#">' +
'Clear ' +
'<span class="number-done">{{done-count}}</span>' +
' completed ' +
'<span>item{{done-plural}}</span>' +
'</a>' +
'</div>';
for(x in data){
if(data[x].status == 1){
done.push(x);
}
else{
todo.push(x);
}
}
if(done.length !== 1){
donePlural = 's';
}
if(todo.length !== 1){
todoPlural = 's';
}
todoTemplate = todoTemplate.replace(/{{todo-count}}/, todo.length);
todoTemplate = todoTemplate.replace(/{{todo-plural}}/, todoPlural);
if(done.length > 0){
doneTemplate = doneTemplate.replace(/{{done-count}}/, done.length);
doneTemplate = doneTemplate.replace(/{{done-plural}}/, donePlural);
}
else{
doneTemplate = '';
}
return todoTemplate+doneTemplate;
}
}
// Define our controller
app.controller.Tasks = {
create: function(e){
if(e.keyCode == 13){
app.model.Tasks.create(this.value);
app.controller.Tasks.render();
this.value = '';
}
}
, update: function(e){
var done = 0
, data = app.data.payload().tasks;
if(e.target.className == 'check'){
if(e.target.checked){
done = 1;
}
app.model.Tasks.update(e.target.dataset.taskId, { status: done });
}
if(e.target.className == 'todo-destroy'){
app.model.Tasks.destroy(e.target.dataset.taskId);
}
console.log(e.target.nodeName);
//while(e.target.nodeName !== 'DIV'){
if(e.target.className == 'todo-clear'){
for(x in data){
if(data[x].status == 1){
app.model.Tasks.destroy(x);
}
}
}
//}
//Refresh the view
app.controller.Tasks.render();
}
, render: function(){
var data = app.data.payload().tasks;
document.getElementById('todo-list').innerHTML = app.view.Tasks.task(data);
document.getElementById('todo-stats').innerHTML = app.view.Tasks.stats(data);
}
}
// When a user hits enter on the input
document.getElementById('new-todo').addEventListener('keyup', app.controller.Tasks.create);
// Delegated event that contains events for everything in the <li>s like delete and update
document.getElementById('todoapp').addEventListener('click', app.controller.Tasks.update, true);
// Initial view load
document.addEventListener("DOMContentLoaded", app.controller.Tasks.render, false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment