D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
tophtucker
Full window
Github gist
Timecoder
<!DOCTYPE html> <html manifest="timecoder.appcache?v=2"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="apple-mobile-web-app-capable" content="yes"> <link rel="shortcut icon" href="favicon.png" type="image/x-icon"> <link rel="apple-touch-icon" href="favicon.png"> <title>Timecoder</title> <style> html, body { margin: 0; padding: 0; font: 14px sans-serif; } * { box-sizing: border-box; } .record { width: 100%; font: 28px sans-serif; position: fixed; top: 0; left: 0; background: white; border: 1px solid black; padding: 1em; -webkit-touch-callout: none; -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .record:hover { background: #ccc; cursor: pointer; } .record:active { color: white; background: black; } table { margin-top: 90px; width: 100%; border-collapse: collapse; } table th, table td { padding: 1em; width: 33.3%; border: 1px solid #ccc; text-align: center; } hr { border: none; border-top: 1px solid gray; } pre { margin-top: 1em; width: 100%; border-top: 1px solid gray; color: gray; font-family: monospace; } </style> <body> <button class="record">Press and hold</button> <div class="table"></div> <hr> <button class="clear">Clear times</button> <button class="epoch">Start timing from now</button> <p>CSV (in seconds after time started): </p> <pre></pre> </body> <script src="d3.min.js" charset="utf-8"></script> <script src="d3-jetpack.js" charset="utf-8"></script> <script> // simple table from // https://bl.ocks.org/gka/17ee676dc59aa752b4e6 var epoch = new Date(0); var times = []; // persist times if(typeof(Storage) !== "undefined") { if(!localStorage.getItem("times")) { save("times", times); } else { times = read("times"); } if(!localStorage.getItem("epoch")) { localStorage.setItem("epoch", epoch); } else { epoch = new Date(localStorage.getItem("epoch")); } } var columns = [ { head: 'Start', cl: 'start', html: ƒ(0, timeFormat) }, { head: 'End', cl: 'stop', html: ƒ(1, timeFormat) }, { head: 'Duration', cl: 'duration', html: diffFormat }, ]; // create table var table = d3.select('.table') .append('table'); // create table header table.append('thead').append('tr') .selectAll('th') .data(columns).enter() .append('th') .attr('class', ƒ('cl')) .text(ƒ('head')); // create table body var tbody = table.append('tbody'); d3.select(".record") .on("mousedown", start) .on("mouseup", end) .on("touchstart", touchStart) .on("touchend", touchEnd); d3.select(".clear").on("click", clear); d3.select(".epoch").on("click", setEpoch); render(); function touchStart() { d3.event.preventDefault(); start(); } function start() { times.push([newDate(), null]); save("times", times); render(); } function touchEnd() { d3.event.preventDefault(); end(); } function end() { times[times.length-1][1] = newDate(); save("times", times); render(); } function render() { // create empty rows and cells (enter selection) var update = tbody .selectAll('tr') .data(times); update.enter() .append('tr') .selectAll('td') .data(columns).enter() .append('td'); // fill cells (update selection) update.selectAll('td') .data(function(row, i) { return columns.map(function(c) { // compute cell values for this specific row var cell = {}; d3.keys(c).forEach(function(k) { cell[k] = typeof c[k] == 'function' ? c[k](row,i) : c[k]; }); return cell; }); }) .html(ƒ('html')) .attr('class', ƒ('cl')); update.exit().remove(); // render csv var csv = times.map(function(item) { return item.map(function(endpoint) { return +endpoint / 1000; }).join(",") }).join("\n"); d3.select("pre").text("start,end\n" + csv); } function clear() { if(confirm("Are you sure you want to permanently delete all your recorded times?")) { times = []; save("times", times); render(); } } function setEpoch() { if(confirm("New items will show times counting from the moment you hit 'OK'")) { epoch = new Date(); localStorage.setItem("epoch", epoch); } } function timeFormat(d) { if(d === null) return '⋯'; if(epoch - new Date(0) !== 0) { renderTime = new Date(+d + d.getTimezoneOffset()*60*1000); } else { renderTime = d; } return d3.time.format('%X')(renderTime); } function diffFormat(d) { if(d[1] === null) return '⋯'; return d3.time.format('%M:%S.%L')(new Date(d[1] - d[0])); } function save(key, value) { return localStorage.setItem(key, JSON.stringify(value)); } function read(key) { var json = JSON.parse(localStorage.getItem(key)); return json.map(function(item) { return item.map(function(endpoint) { return new Date(endpoint); }) }); } function newDate() { return new Date(new Date() - epoch); } </script> </html>