Demonstration release of the principles underpinning krsd.
[krsd] / demo / todo / js / app.js
1 (function() {
2   'use strict';
3
4   var stat = {},
5     ENTER_KEY = 13;
6
7   window.addEventListener( 'load', windowLoadHandler, false );
8
9   function Stat() {
10     this.todoLeft = 0;
11     this.todoCompleted = 0;
12     this.totalTodo = 0;
13   }
14
15   function windowLoadHandler() {
16     remoteStorage.access.claim('tasks', 'rw');
17     remoteStorage.displayWidget();
18     remoteStorage.tasks.init();
19     paintAll();
20     addEventListeners();
21
22     remoteStorage.tasks.onChange(paintAll);
23     remoteStorage.on('disconnect', paintAll);
24   }
25
26   function addEventListeners() {
27     document.getElementById('new-todo').addEventListener( 'keypress', newTodoKeyPressHandler, false );
28     document.getElementById('toggle-all').addEventListener( 'change', toggleAllChangeHandler, false );
29   }
30
31   function inputEditTodoKeyPressHandler( event ) {
32     var inputEditTodo = event.target,
33       trimmedText = inputEditTodo.value.trim(),
34       todoId = event.target.id.slice( 6 );
35
36     if ( trimmedText ) {
37       if ( event.keyCode === ENTER_KEY ) {
38         remoteStorage.tasks.setTodoText( todoId, trimmedText );
39       }
40     } else {
41       remoteStorage.tasks.removeTodo( todoId );
42     }
43   }
44
45   function inputEditTodoBlurHandler( event ) {
46     var inputEditTodo = event.target,
47       todoId = event.target.id.slice( 6 );
48
49     remoteStorage.tasks.setTodoText( todoId, inputEditTodo.value );
50   }
51
52   function newTodoKeyPressHandler( event ) {
53     var trimmedText = document.getElementById('new-todo').value.trim();
54     if ( event.keyCode === ENTER_KEY && trimmedText ) {
55       remoteStorage.tasks.addTodo( trimmedText );
56     }
57   }
58
59   function toggleAllChangeHandler( event ) {
60     remoteStorage.tasks.setAllTodosCompleted( event.target.checked );
61   }
62
63   function spanDeleteClickHandler( event ) {
64     remoteStorage.tasks.removeTodo( event.target.getAttribute('data-todo-id') );
65   }
66
67   function hrefClearClickHandler() {
68     remoteStorage.tasks.removeAllCompletedTodos();
69   }
70
71   function todoContentHandler( event ) {
72     var todoId = event.target.getAttribute('data-todo-id'),
73       div = document.getElementById( 'li_' + todoId ),
74       inputEditTodo = document.getElementById( 'input_' + todoId );
75
76     div.className = 'editing';
77     inputEditTodo.focus();
78   }
79
80   function checkboxChangeHandler( event ) {
81     var checkbox = event.target;
82     remoteStorage.tasks.setTodoCompleted( checkbox.getAttribute('data-todo-id'), checkbox.checked );
83   }
84
85   function paintAll() {
86     remoteStorage.tasks.getTodos().then( function( todosMap ) {
87       var todosArr = [], i;
88       for( i in  todosMap ) {
89         todosArr.push( todosMap[i] );
90       }
91       computeStats( todosArr );
92       redrawTodosUI( todosArr );
93       redrawStatsUI( todosArr );
94       changeToggleAllCheckboxState( todosArr );
95     }, function(err) {
96       console.log('error during getTodos', err);
97     });
98   }
99
100   function computeStats( todos ) {
101     var i, l;
102
103     stat = new Stat();
104     stat.totalTodo = todos.length;
105
106     for ( i = 0, l = todos.length; i < l; i++ ) {
107       if ( todos[ i ].completed ) {
108         stat.todoCompleted++;
109       }
110     }
111
112     stat.todoLeft = stat.totalTodo - stat.todoCompleted;
113   }
114
115
116   function redrawTodosUI( todos ) {
117
118     var todo, checkbox, label, deleteLink, divDisplay, inputEditTodo, li, i, l,
119       ul = document.getElementById('todo-list');
120
121     document.getElementById('main').style.display = todos.length ? 'block' : 'none';
122
123     ul.innerHTML = '';
124     document.getElementById('new-todo').value = '';
125
126     for ( i = 0, l = todos.length; i < l; i++ ) {
127       todo = todos[ i ];
128
129       // create checkbox
130       checkbox = document.createElement('input');
131       checkbox.className = 'toggle';
132       checkbox.setAttribute( 'data-todo-id', todo.id );
133       checkbox.type = 'checkbox';
134       checkbox.addEventListener( 'change', checkboxChangeHandler );
135
136       // create div text
137       label = document.createElement('label');
138       label.setAttribute( 'data-todo-id', todo.id );
139       label.appendChild( document.createTextNode( todo.title ) );
140       label.addEventListener( 'dblclick', todoContentHandler );
141
142
143       // create delete button
144       deleteLink = document.createElement('button');
145       deleteLink.className = 'destroy';
146       deleteLink.setAttribute( 'data-todo-id', todo.id );
147       deleteLink.addEventListener( 'click', spanDeleteClickHandler );
148
149       // create divDisplay
150       divDisplay = document.createElement('div');
151       divDisplay.className = 'view';
152       divDisplay.setAttribute( 'data-todo-id', todo.id );
153       divDisplay.appendChild( checkbox );
154       divDisplay.appendChild( label );
155       divDisplay.appendChild( deleteLink );
156
157       // create todo input
158       inputEditTodo = document.createElement('input');
159       inputEditTodo.id = 'input_' + todo.id;
160       inputEditTodo.className = 'edit';
161       inputEditTodo.value = todo.title;
162       inputEditTodo.addEventListener( 'keypress', inputEditTodoKeyPressHandler );
163       inputEditTodo.addEventListener( 'blur', inputEditTodoBlurHandler );
164
165
166       // create li
167       li = document.createElement('li');
168       li.id = 'li_' + todo.id;
169       li.appendChild( divDisplay );
170       li.appendChild( inputEditTodo );
171
172
173       if ( todo.completed ) {
174         li.className += 'complete';
175         checkbox.checked = true;
176       }
177
178       ul.appendChild( li );
179     }
180   }
181
182   function changeToggleAllCheckboxState( todos ) {
183     var toggleAll = document.getElementById('toggle-all');
184
185     toggleAll.checked = stat.todoCompleted === todos.length;
186   }
187
188   function redrawStatsUI( todos ) {
189     removeChildren( document.getElementsByTagName('footer')[0] );
190     document.getElementById('footer').style.display = todos.length ? 'block' : 'none';
191
192     if ( stat.todoCompleted ) {
193       drawTodoClear();
194     }
195
196     if ( stat.totalTodo ) {
197       drawTodoCount();
198     }
199   }
200
201   function drawTodoCount() {
202     var number = document.createElement('strong'),
203       remaining = document.createElement('span'),
204       text = ' ' + ( stat.todoLeft === 1 ? 'item' : 'items' ) + ' left';
205
206     // create remaining count
207     number.innerHTML = stat.todoLeft;
208
209     remaining.id = 'todo-count';
210     remaining.appendChild( number );
211     remaining.appendChild( document.createTextNode( text ) );
212
213     document.getElementsByTagName('footer')[0].appendChild( remaining );
214   }
215
216   function drawTodoClear() {
217     var buttonClear = document.createElement('button');
218
219     buttonClear.id = 'clear-completed';
220     buttonClear.addEventListener( 'click', hrefClearClickHandler );
221     buttonClear.innerHTML = 'Clear completed (' + stat.todoCompleted + ')';
222
223     document.getElementsByTagName('footer')[0].appendChild( buttonClear );
224   }
225
226   function removeChildren( node ) {
227     node.innerHTML = '';
228   }
229 })();