packages/123done/static/js/state.js (136 lines of code) (raw):
/*
* This javascript file implements functions that store state for 123done.
* It exposes three functions:
*
* State.save() - saves todolist to local storage and to the server
* if the user is logged in.
* State.load() - loads todolist items from local storage or the server
* if the user is logged in.
* State.merge() - merges local todolist items with the server, and saves
* this back to the server.
*
* To determine whether the user is authenticated, code in this file uses
* the global variable window.loggedInEmail.
*/
/* eslint-disable */
// enable experimental API features
(function () {
var todo = $('#todolist'),
form = $('#addform'),
field = $('#newitem');
var hasSomething = /\S/;
var lastSync = localStorage.lastSync
? parseInt(localStorage.lastSync, 10)
: 0;
function udpateLastSync() {
var ls = new Date().getTime().toString();
localStorage.lastSync = lastSync = ls;
}
function setSyncStatus(status) {
$('#dataState > div').css('display', 'none');
$('#dataState > div.' + status).css('display', 'block');
}
// upon form submission to add a new element, add it to the list
form.on('submit', function (e) {
e.preventDefault();
if (!field.val().match(hasSomething)) {
return field.val('').focus();
}
// create a new element, set its value, and append it
todo.prepend(
$('<li>').attr('when', new Date().getTime()).text(field.val())
);
todo.children(':first-child').hide().slideDown(200);
// clear and refocus the input field
field.val('').focus();
State.save();
});
// when a todolist item is clicked on, it is marked done. if it was already
// done, it's removed.
todo.click(function (ev) {
var t = $(ev.target);
if (t.is('li')) {
if (t.hasClass('done')) {
t.slideUp(200);
setTimeout(function () {
t.remove();
showHideDone();
}, 200);
} else {
done(t);
$('#donelistwrapper h3').show();
}
State.save();
}
ev.preventDefault();
});
//sort done items
function done(t) {
t.prependTo('ul#donelist');
t.addClass('done');
t.mouseout(function () {
t.addClass('can-delete');
});
}
function showHideDone() {
var num = $('#donelist li').length;
if (num > 0) {
$('#donelistwrapper h3').show();
} else {
$('#donelistwrapper h3').hide();
}
console.log(num);
}
// state mangement:
// * when you are not signed in, we use local storage
// * when you sign in with added todo items, we merge them with your items on the server
// * when you sign out, we leave your todo items local
// * when you change items and you're signed in, we blast those items to the server
function savestate() {
localStorage.todolist = todo.html();
setSyncStatus('outofdate');
if (loggedInEmail) {
setSyncStatus('inprogress');
// let's extract the state from the dom
var l = [];
$('#todolist > li').each(function (e) {
var self = $(this);
l.push({
v: self.text(),
done: self.hasClass('done'),
});
});
// now store it to the server
$.ajax({
type: 'POST',
url: '/api/todos/save',
data: JSON.stringify(l),
contentType: 'application/json',
success: function () {
setSyncStatus('saved');
udpateLastSync();
},
error: function () {
setSyncStatus('outofdate');
},
});
}
}
function updateDomWithArray(a) {
for (var i = 0; i < a.length; i++) {
var li = $('<li/>').text(a[i].v);
if (a[i].done) li.addClass('done');
todo.prepend(li);
}
}
// retrieve todolist from localstorage
function loadstate() {
if (loggedInEmail) {
setSyncStatus('inprogress');
$.get('/api/todos/get', function (data) {
if (!data || !data.length) data = [];
updateDomWithArray(data);
setSyncStatus('saved');
});
} else if (localStorage.todolist) {
setSyncStatus('outofdate');
todo.html(localStorage.todolist);
}
}
// merge local state with the server, this is a poor man's sync
function mergestate() {
// this is only meaningful when we're connected
if (!loggedInEmail) return;
setSyncStatus('inprogress');
var l = [];
// first let's get the list of todo items from the server
$.get('/api/todos/get', function (data) {
if (!data || !data.length) data = [];
for (var i = 0; i < data.length; i++) l.push(data[i]);
// now let's that list with local items added since the last sync
$('#todolist > li').each(function (e) {
var self = $(this);
var when = self.attr('when') || 0;
if (when <= lastSync) return;
l.push({
v: self.text(),
done: self.hasClass('done'),
});
});
// clear items from the dom
$('#todolist > li').remove();
// update the dom with our new merged set
updateDomWithArray(l);
// and store that set of items to the server
State.save();
});
}
window.State = {
save: savestate,
load: loadstate,
merge: mergestate,
};
})();