Commit 6f3db0f0 authored by Florian Fesseler's avatar Florian Fesseler Committed by Sindre Sorhus

Close GH-109: Update VanillaJS app

parent 341bbfeb
This diff is collapsed.
<!DOCTYPE html>
<html>
<head>
<link href="css/todos.css" rel="stylesheet" type="text/css">
<script src="js/todo.js"></script>
<script src="js/json2.js"></script>
</head>
<body onload="bodyLoadHandler()">
<div id="todoapp">
<div class="title">
<h1>Todos</h1>
</div>
<div class="content">
<div id="create-todo">
<input id="new-todo" placeholder="What needs to be done?" onkeypress="newTodoKeyPressHandler(event)">
<span class="ui-tooltip-top" style="display:none;">Press Enter to save this task</span>
</div>
<div id="todos">
<ul id="todo-list">
</ul>
</div>
<div id="todo-stats">
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
<head>
<meta charset="utf-8">
<title>VanillaJS • TodoMVC</title>
<link rel="stylesheet" href="../../assets/base.css">
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>Todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus>
</header>
<section id="main">
<input id="toggle-all" type="checkbox" >
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
</ul>
</section>
<footer id="footer">
<span id="todo-count"></span>
<button id="clear-completed">Clear completed</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo.</p>
<p>Created by <a href="http://twitter.com/ffesseler">Florian Fesseler</a></p>
<p>Cleanup, edits: <a href="http://github.com/boushley">Aaron Boushley</a>.</p>
</footer>
<!-- scripts here -->
<script src="js/app.js"></script>
</body>
</html>
var todos = [],
stat = {},
ENTER_KEY = 13;
window.addEventListener( "load", windowLoadHandler, false );
function Todo( title, completed ) {
this.id = getUuid();
this.title = title;
this.completed = completed;
}
function Stat() {
this.todoLeft = 0;
this.todoCompleted = 0;
this.totalTodo = 0;
}
function windowLoadHandler() {
loadTodos();
refreshData();
addEventListeners();
}
function addEventListeners() {
document.getElementById( 'new-todo' ).addEventListener( "keypress", newTodoKeyPressHandler, false );
document.getElementById( 'toggle-all' ).addEventListener( "change", toggleAllChangeHandler, false );
}
function inputEditTodoKeyPressHandler( event ) {
var inputEditTodo,
trimmedText,
todoId;
inputEditTodo = event.target;
trimmedText = inputEditTodo.value.trim();
todoId = event.target.id.slice( 6 );
if ( trimmedText ) {
if ( event.keyCode === ENTER_KEY ) {
editTodo( todoId, trimmedText );
}
}
else {
removeTodoById( todoId );
refreshData();
}
}
function inputEditTodoBlurHandler( event ) {
var inputEditTodo,
todoId;
inputEditTodo = event.target;
todoId = event.target.id.slice( 6 );
editTodo( todoId, inputEditTodo.value );
}
function newTodoKeyPressHandler( event ) {
if ( event.keyCode === ENTER_KEY ) {
addTodo( document.getElementById( 'new-todo' ).value );
}
}
function toggleAllChangeHandler( event ) {
for ( var i in todos ) {
todos[i].completed = event.target.checked;
}
refreshData();
}
function spanDeleteClickHandler( event ) {
removeTodoById( event.target.getAttribute( 'data-todo-id' ) );
refreshData();
}
function hrefClearClickHandler() {
removeTodosCompleted();
refreshData();
}
function todoContentHandler( event ) {
var todoId,
div,
inputEditTodo;
todoId = event.target.getAttribute( 'data-todo-id' );
div = document.getElementById( 'li_'+todoId );
div.className = 'editing';
inputEditTodo = document.getElementById( 'input_' + todoId );
inputEditTodo.focus();
}
function checkboxChangeHandler( event ) {
var checkbox,
todo;
checkbox = event.target;
todo = getTodoById( checkbox.getAttribute( 'data-todo-id' ) );
todo.completed = checkbox.checked;
refreshData();
}
function loadTodos() {
if ( !localStorage.getItem( 'todos-vanillajs' ) ) {
localStorage.setItem( 'todos-vanillajs', JSON.stringify( [] ) );
}
todos = JSON.parse( localStorage.getItem( 'todos-vanillajs' ) );
}
function addTodo( text ) {
var trimmedText = text.trim();
if ( trimmedText ) {
var todo = new Todo( trimmedText, false );
todos.push( todo );
refreshData();
}
}
function editTodo( todoId, text ) {
var i;
for ( i=0; i < todos.length; i++ ) {
if ( todos[i].id === todoId ) {
todos[i].title = text;
}
}
refreshData();
}
function removeTodoById( id ) {
var i;
for ( i=0; i < todos.length; i++ ) {
if ( todos[i].id === id ) {
todos.splice( i, 1 );
}
}
}
function removeTodosCompleted() {
var i = todos.length-1;
while ( i >= 0 ) {
if ( todos[i].completed ) {
todos.splice( i, 1 );
}
--i;
}
}
function getTodoById( id ) {
var i;
for ( i=0; i < todos.length; i++ ) {
if ( todos[i].id === id ) {
return todos[i];
}
}
}
function refreshData() {
saveTodos();
computeStats();
redrawTodosUI();
redrawStatsUI();
changeToggleAllCheckboxState();
}
function saveTodos() {
localStorage.setItem( 'todos-vanillajs', JSON.stringify( todos ) );
}
function computeStats() {
var i;
stat = new Stat();
stat.totalTodo = todos.length;
for ( i=0; i < todos.length; i++ ) {
if ( todos[i].completed ) {
stat.todoCompleted += 1;
}
}
stat.todoLeft = stat.totalTodo - stat.todoCompleted;
}
function redrawTodosUI() {
var ul,
todo,
checkbox,
label,
deleteLink,
divDisplay,
inputEditTodo,
li,
i;
ul = document.getElementById( 'todo-list' );
document.getElementById( 'main' ).style.display = todos.length ? 'block' : 'none';
ul.innerHTML = "";
document.getElementById( 'new-todo' ).value = '';
for ( i= 0; i < todos.length; i++ ) {
todo = todos[i];
//create checkbox
checkbox = document.createElement( 'input' );
checkbox.className = 'toggle';
checkbox.setAttribute( 'data-todo-id', todo.id );
checkbox.type = 'checkbox';
checkbox.addEventListener( 'change', checkboxChangeHandler );
//create div text
label = document.createElement( 'label' );
label.setAttribute( 'data-todo-id', todo.id );
label.appendChild( document.createTextNode( todo.title ) );
//create delete button
deleteLink = document.createElement( 'button' );
deleteLink.className = 'destroy';
deleteLink.setAttribute( 'data-todo-id', todo.id );
deleteLink.addEventListener( 'click', spanDeleteClickHandler );
//create divDisplay
divDisplay = document.createElement( 'div' );
divDisplay.className = 'view';
divDisplay.setAttribute( 'data-todo-id', todo.id );
divDisplay.appendChild( checkbox );
divDisplay.appendChild( label );
divDisplay.appendChild( deleteLink );
divDisplay.addEventListener( 'dblclick', todoContentHandler );
//create todo input
inputEditTodo = document.createElement( 'input' );
inputEditTodo.id = 'input_' + todo.id;
inputEditTodo.className = 'edit';
inputEditTodo.value = todo.title;
inputEditTodo.addEventListener( 'keypress', inputEditTodoKeyPressHandler );
inputEditTodo.addEventListener( 'blur', inputEditTodoBlurHandler );
//create li
li = document.createElement( 'li' );
li.id = 'li_' + todo.id;
li.appendChild( divDisplay );
li.appendChild( inputEditTodo );
if ( todo.completed )
{
li.className += 'complete';
checkbox.checked = true;
}
ul.appendChild( li );
}
}
function changeToggleAllCheckboxState() {
var toggleAll = document.getElementById( 'toggle-all' );
if ( stat.todoCompleted === todos.length ) {
toggleAll.checked = true;
} else {
toggleAll.checked = false;
}
}
function redrawStatsUI() {
removeChildren( document.getElementsByTagName( 'footer' )[ 0 ] );
document.getElementById( 'footer' ).style.display = todos.length ? 'block' : 'none';
if ( stat.todoCompleted > 0 ) {
drawTodoClear();
}
if ( stat.totalTodo > 0 ) {
drawTodoCount();
}
}
function drawTodoCount() {
var number,
theText,
remaining;
// Create remaining count
number = document.createElement( 'strong' );
number.innerHTML = stat.todoLeft;
theText = ' item';
if ( stat.todoLeft !== 1 ) {
theText += 's';
}
theText += ' left';
remaining = document.createElement( 'span' );
remaining.id = 'todo-count';
remaining.appendChild( number );
remaining.appendChild( document.createTextNode( theText ) );
document.getElementsByTagName( 'footer' )[ 0 ].appendChild( remaining );
}
function drawTodoClear() {
var buttonClear = document.createElement( 'button' );
buttonClear.id = 'clear-completed';
buttonClear.addEventListener( 'click', hrefClearClickHandler );
buttonClear.innerHTML = 'Clear completed (' + stat.todoCompleted + ')';
document.getElementsByTagName( 'footer' )[ 0 ].appendChild( buttonClear );
}
function removeChildren( node ) {
while ( node.firstChild ) {
node.removeChild( node.firstChild );
}
}
function getUuid() {
var uuid = '',
i,
random;
for ( i = 0; i < 32; i++ ) {
random = Math.random() * 16 | 0;
if ( i === 8 || i === 12 || i === 16 || i === 20 ) {
uuid += '-';
}
uuid += ( i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random) ).toString( 16 );
}
return uuid;
}
/* json2.js
* 2008-01-17
* Public Domain
* No warranty expressed or implied. Use at your own risk.
* See http://www.JSON.org/js.html
*/
if(!this.JSON){JSON=function(){function f(n){return n<10?'0'+n:n;}
Date.prototype.toJSON=function(){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function stringify(value,whitelist){var a,i,k,l,r=/["\\\x00-\x1f\x7f-\x9f]/g,v;switch(typeof value){case'string':return r.test(value)?'"'+value.replace(r,function(a){var c=m[a];if(c){return c;}
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+
(c%16).toString(16);})+'"':'"'+value+'"';case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
if(typeof value.toJSON==='function'){return stringify(value.toJSON());}
a=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){l=value.length;for(i=0;i<l;i+=1){a.push(stringify(value[i],whitelist)||'null');}
return'['+a.join(',')+']';}
if(whitelist){l=whitelist.length;for(i=0;i<l;i+=1){k=whitelist[i];if(typeof k==='string'){v=stringify(value[k],whitelist);if(v){a.push(stringify(k)+':'+v);}}}}else{for(k in value){if(typeof k==='string'){v=stringify(value[k],whitelist);if(v){a.push(stringify(k)+':'+v);}}}}
return'{'+a.join(',')+'}';}}
return{stringify:stringify,parse:function(text,filter){var j;function walk(k,v){var i,n;if(v&&typeof v==='object'){for(i in v){if(Object.prototype.hasOwnProperty.apply(v,[i])){n=walk(i,v[i]);if(n!==undefined){v[i]=n;}}}}
return filter(k,v);}
if(/^[\],:{}\s]*$/.test(text.replace(/\\./g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof filter==='function'?walk('',j):j;}
throw new SyntaxError('parseJSON');}};}();}
\ No newline at end of file
var tasks = [];
var stat;
/**************************/
/* MODEL /
/**************************/
function Todo(name, done) {
this.id = uuid();
this.name = name;
this.done = done;
}
function Stat() {
this.todoLeft = 0;
this.todoCompleted = 0;
this.totalTodo = 0;
}
/**************************/
/* EVENT HANDLERS /
/**************************/
function bodyLoadHandler() {
loadTasks();
refreshData();
}
function inputEditTodoKeyPressHandler(event) {
var inputEditTodo = event.target;
var taskId = event.target.id.slice(6);
if (event.keyCode === 13) {
editTask(taskId, inputEditTodo.value);
}
}
function newTodoKeyPressHandler(event) {
if (event.keyCode === 13) {
addTask(document.getElementById("new-todo").value);
}
}
function spanDeleteClickHandler(event) {
removeTaskById(event.target.id);
refreshData();
}
function hrefClearClickHandler() {
removeTasksDone();
refreshData();
}
function todoContentHandler(event) {
var taskId = event.target.id;
var div = document.getElementById("li_"+taskId);
div.className = "editing";
var inputEditTodo = document.getElementById("input_"+taskId);
inputEditTodo.focus();
}
function checkboxChangeHandler(event) {
var checkbox = event.target;
var todo = getTodoById(checkbox.id);
todo.done = checkbox.checked;
refreshData();
}
/**************************/
/* ACTIONS /
/**************************/
function loadTasks() {
if (!localStorage.todo) {
localStorage.todo = JSON.stringify([]);
}
tasks = JSON.parse(localStorage['todo']);
}
function addTask(text) {
var todo = new Todo(text, false);
tasks.push(todo);
refreshData();
}
function editTask(taskId, text) {
for (var i=0; i < tasks.length; i++) {
if (tasks[i].id === taskId) {
tasks[i].name = text;
}
}
refreshData();
}
function removeTaskById(id) {
for (var i=0; i < tasks.length; i++) {
if (tasks[i].id === id) {
tasks.splice(i, 1);
}
}
}
function removeTasksDone() {
for (var i=tasks.length-1; i >= 0; --i) {
if (tasks[i].done) {
tasks.splice(i, 1);
}
}
}
function getTodoById(id) {
for (var i=0; i < tasks.length; i++) {
if (tasks[i].id === id) {
return tasks[i];
}
}
}
function refreshData() {
saveTasks();
computeStats();
redrawTasksUI();
redrawStatsUI();
}
function saveTasks() {
localStorage['todo'] = JSON.stringify(tasks);
}
function computeStats() {
stat = new Stat();
stat.totalTodo = tasks.length;
for (var i=0; i < tasks.length; i++) {
if (tasks[i].done) {
stat.todoCompleted += 1;
}
}
stat.todoLeft = stat.totalTodo - stat.todoCompleted;
}
/**************************/
/* DRAWING /
/**************************/
function redrawTasksUI() {
var ul = document.getElementById("todo-list");
var todo;
removeChildren(ul);
document.getElementById("new-todo").value = "";
for (var i= 0; i < tasks.length; i++) {
todo = tasks[i];
//create checkbox
var checkbox = document.createElement("input");
checkbox.className = "check";
checkbox.id = todo.id;
checkbox.type = "checkbox";
checkbox.addEventListener("change", checkboxChangeHandler);
//create div text
var divText = document.createElement("div");
divText.className = "todo-content";
divText.id = todo.id;
divText.appendChild(document.createTextNode(todo.name));
divText.addEventListener("dblclick", todoContentHandler);
//create delete button
var spanDelete = document.createElement("span");
spanDelete.className = "todo-destroy";
spanDelete.id = todo.id;
spanDelete.addEventListener("click", spanDeleteClickHandler);
//create divDisplay
var divDisplay = document.createElement("div");
divDisplay.className = "display";
divDisplay.appendChild(checkbox);
divDisplay.appendChild(divText);
divDisplay.appendChild(spanDelete);
//create div todo
var divTodo = document.createElement("div");
divTodo.className = "todo ";
divTodo.appendChild(divDisplay);
//create todo input
var inputEditTodo = document.createElement("input");
inputEditTodo.id = "input_" + todo.id;
inputEditTodo.type = "text";
inputEditTodo.className = "todo-input";
inputEditTodo.value = todo.name;
inputEditTodo.addEventListener("keypress", inputEditTodoKeyPressHandler);
//create div edit
var divEdit = document.createElement("div");
divEdit.className = "edit";
divEdit.appendChild(inputEditTodo);
//create li
var li = document.createElement("li");
li.id = "li_" + todo.id;
li.appendChild(divTodo);
li.appendChild(divEdit);
if (todo.done)
{
divTodo.className += "done";
checkbox.checked = true;
}
ul.appendChild(li);
}
}
function redrawStatsUI() {
removeChildren(document.getElementById("todo-stats"))
if (stat.totalTodo > 0) {
drawTodoCount();
}
if (stat.todoCompleted > 0) {
drawTodoClear();
}
}
function drawTodoCount() {
//create span number
var spanNumber = document.createElement("span");
spanNumber.className = "number";
spanNumber.innerHTML = stat.todoLeft;
//create span word
var spanWord = document.createElement("span");
spanWord.className = "word";
spanWord.innerHTML = " item";
if (stat.todoLeft > 1) {
spanWord.innerHTML += "s";
}
var spanTodoCount = document.createElement("span");
spanTodoCount.className = "todo-count";
spanTodoCount.appendChild(spanNumber);
spanTodoCount.appendChild(spanWord);
spanTodoCount.innerHTML += " left.";
document.getElementById("todo-stats").appendChild(spanTodoCount);
}
function drawTodoClear() {
//create a href
var hrefClear = document.createElement("a");
hrefClear.href = "#";
hrefClear.addEventListener("click", hrefClearClickHandler);
hrefClear.innerHTML = "Clear ";
//create span number done
var spanNumberDone = document.createElement("span");
spanNumberDone.className = "number-done";
spanNumberDone.innerHTML = stat.todoCompleted;
hrefClear.appendChild(spanNumberDone);
hrefClear.innerHTML += " completed ";
//create span word
var spanWordDone = document.createElement("span");
spanWordDone.className = "word-done";
spanWordDone.innerHTML = " item";
if (stat.todoCompleted > 1) {
spanWordDone.innerHTML += "s";
}
hrefClear.appendChild(spanWordDone);
var spanTodoClear = document.createElement("span");
spanTodoClear.className = "todo-clear";
spanTodoClear.appendChild(hrefClear);
document.getElementById("todo-stats").appendChild(spanTodoClear);
}
function removeChildren(node) {
while (node.hasChildNodes()) {
node.removeChild(node.firstChild);
}
}
/**************************/
/* UTILS /
/**************************/
function uuid() {
var uuid = "", i, random;
for (i = 0; i < 32; i++) {
random = Math.random() * 16 | 0;
if (i == 8 || i == 12 || i == 16 || i == 20) {
uuid += "-"
}
uuid += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16);
}
return uuid;
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment