Tajmkiper, part 2
One important feature that I want for Tajmkiper ("time keeper" written phonetically in Swedish) is that everything should run
locally, including data storage. The data model is really simple: I'll write a class called Project
with only three
properties: name
, totalFinishedTime
and startedOn
Project.name
: The name of the project.Project.totalFinishedTime
: Number of seconds the project has been running in completed chunks. If this project's time clock is currently running, thetotalFinishedTime
does not include the number of seconds on the running clock.Project.startedOn
: The Unix timestamp (Date.getTime()
) for when this project's time clock started, ornull
if the time clock is not started.
This way the project objects don't need to be constantly update every second. To present the currently elapsed time on the clock, the
user interface simply adds totalFinishedTime
to the number of seconds that passed since startedOn
. Assuming most people don't fiddle
around with the system clock, this will also make it perfectly possible to close the browser and reopen it any amount of time later, and
the time clock will remember when it was started.
// First version implementation
function Project(name, totalFinishedTime, startedOn) {
this.name = name;
this.totalFinishedTime = totalFinishedTime;
this.startedOn = startedOn;
}
Project.prototype.getTotalTime = function() {
if (this.startedOn) {
// Bitwise or with zero forces the result to be an integer
return this.totalFinishedTime + (Date.now() - this.startedOn) / 1000 | 0;
} else {
return this.totalFinishedTime;
}
};
Project.prototype.stop = function() {
this.totalFinishedTime = this.getTotalTime();
this.startedOn = null;
};
Project.prototype.start = function() {
this.totalFinishedTime = this.getTotalTime();
this.startedOn = Date.now();
};
var allProjects = [];
function saveProjects() {
var json = JSON.stringify(allProjects);
localStorage["projects"] = json;
}
function loadProjects() {
allProjects = [];
var json = localStorage["projects"];
if (json) {
var temp = JSON.parse(json);
temp.forEach(function(item) {
allProjects.push(new Projects(item.name, item.totalFinishedTime, item.startedOn));
});
}
}
The next step is to connect the Project objects up to some HTML generation. I could do this using a jQuery templating plugin, but i choose to do it myself, just for the hell of it.
// Second version implementation
function createProjectsHtml() {
// First, clear any existing content of the #projects UL element
var ul = document.getElementById("projects");
ul.innerHTML = "";
// Go through all projects, create html elements for each, and add them to the UL
allProjects.forEach(function(project) {
var li = createProjectElement(project);
ul.appendChild(li);
});
// TODO: Add the #total LI element
}
function createProjectElement(project) {
// Create an LI element, and store a reference to it in the Project object for later use
var li = document.createElement("LI");
project.element = li;
// Store a reference to the Project object in the clickable link
var a = document.createElement("A");
a.href = "#";
a.projectReference = project;
li.appendChild(a);
a.addEventListener("click", onProjectClick, false);
var header = document.createElement("HEADER");
header.textContent = project.name;
a.appendChild(header);
var timeSpan = document.createElement("SPAN");
timeSpan.className = "time";
a.appendChild(timeSpan);
updateProjectElement(project);
return li;
}
function updateProjectElement(project) {
var li = project.element;
var timeSpan = li.querySelector("span.time");
var totalTime = project.getTotalTime();
var timeText = formatTime(totalTime);
timeSpan.textContent = timeText;
li.className = (project.startedOn) ? "running" : "";
}
// TODO: Write a formatTime function
var running = null;
function onProjectClick(e) {
e.preventDefault();
// Stop the currently running project first
if (running) {
running.stop();
updateProjectElement(project);
}
// Start the clicked project
var project = this.projectReference;
project.start();
updateProjectElement(project);
running = project;
saveProjects();
}
Now, the projects are clickable, and the html gets updated at each click. What is still missing is a timer function to repeatedly update the running project's time display. Also, now that a reference to the LI element is stored in the Project object, we need to modify the saveProjects function, so that is doesn't try to store any HTML elements.
// Third version implementation
function saveProjects() {
// Filter out properties to only include array indices, and those properties that need storing
var json = JSON.stringify(array, function(key, value) {
if (key == "name" || key == "totalFinishedTime" || key == "startedOn" || key >= 0) {
// Only certain properties of Project are saved
return value;
}
return undefined;
});
localStorage["projects"] = json;
}
function timerFunc() {
if (running) {
updateProjectElement(running);
}
// Wait for the next second on the clock
var now = Date.now();
var millis = now % 1000;
var wait = 1000 - millis;
window.setTimeout(timerFunc, wait);
}
document.addEventListener("DOMContentLoaded", function() {
loadProjects();
createProjectsHtml();
timerFunc();
}, false);
Next step
There you have it. What is still missing?
- Export to CSV
- Working stop button
- Clear all timers
- Remove all projects
EDIT: Tajmkiper is now publicly available to anyone who wants to use it.
Try it: tajmkiper.com
Posted by
Category JavaScript Labels
Tweet this