--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/templates/geolocation.ssp Mon Aug 22 10:52:48 2011 -0400
@@ -0,0 +1,538 @@
+<!DOCTYPE html>
+<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
+<!-- manifest='geolocation.manifest'> -->
+<head>
+ <meta charset='utf-8' />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width,initial-scale=1.0" />
+ <link rel='icon' type='image/png' href='geolocation-icon.png'/>
+ <title>HTML5 Track</title>
+ <script>
+<![CDATA[
+
+function buildMessage(data, boundary) {
+ var CRLF = "\r\n";
+ var parts = [];
+ var iter = 0;
+
+ while (iter < data.length) {
+ var part = "";
+ var type = "TEXT";
+
+ part = 'Content-Disposition: form-data; ';
+ part += 'name="' + data[iter][0] + '"' + CRLF + CRLF;
+
+ part += data[iter][1] + CRLF;
+
+ parts.push(part);
+ iter++;
+ }
+
+ var request = "--" + boundary + CRLF;
+ request+= parts.join("--" + boundary + CRLF);
+ request+= "--" + boundary + "--" + CRLF;
+
+ return request;
+}
+
+function send(user, data) {
+ var boundary = "AJAX-----------------------" + Date.now();
+ var xhr = new XMLHttpRequest;
+
+ if (!("withCredentials" in xhr)) {
+ return false;
+ }
+
+ try {
+ xhr.open("POST", "http://teole.jfouffa.org/2011/07/html5track.php", true);
+ xhr.setRequestHeader('X-HTML5TRACK', 'true');
+ xhr.onreadystatechange = function() {
+ switch (xhr.readyState) {
+ case 4:
+ document.getElementById("log").textContent = "xhr done " + xhr.status + " " + xhr.statusText;
+ break;
+ case 0:
+ document.getElementById("log").textContent = "xhr unsent";
+ break;
+ case 1:
+ document.getElementById("log").textContent = "xhr opened";
+ break;
+ case 2:
+ document.getElementById("log").textContent = "xhr headers_received";
+ break;
+ case 3:
+ document.getElementById("log").textContent = "xhr loading";
+ break;
+ }
+ };
+ var contentType = "multipart/form-data; boundary=" + boundary;
+ xhr.setRequestHeader("Content-Type", contentType);
+
+ var data = buildMessage([ ["user", user], ["gps_coords", data] ], boundary);
+
+ xhr.sendAsBinary(data);
+
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
+
+var storage = (function() {
+ try {
+ return !!window.localStorage.getItem;
+ } catch (e) {
+ return false;
+ }
+})();
+
+
+
+var watchId = 0;
+var running = false;
+
+var duration = 0;
+var start_time = 0;
+var intervalId = 0;
+
+// wait for at least 5 consecutive GPS data before declaring the GPS ready
+var MAX_ERRORS = 2;
+var got_error = MAX_ERRORS;
+
+var discarded = 0;
+var locations = 0;
+
+var coordinates = [];
+
+var dst_unit = 1;
+
+function formatNumber(n) {
+ return (n < 10)? "0" + n : n;
+}
+
+function formatTime(time) {
+ t = parseInt(time);
+ hours = parseInt(t / 3600);
+ minutes = parseInt(t / 60);
+ seconds = t - ((hours * 3600) + (minutes * 60));
+ return ((hours > 0)? formatNumber(hours) + ":": "") + formatNumber(minutes) + ":" + formatNumber(seconds);
+}
+
+
+
+
+function pushCoordinates(position) {
+ coordinates[coordinates.length] =
+ [ position.timestamp, position.coords.longitude, position.coords.latitude, position.coords.altitude, position.coords.speed ];
+// [ position.timestamp, position.coords.longitude, position.coords.latitude, position.coords.altitude ];
+}
+
+function pause() {
+ coordinates[coordinates.length] = [ Date.now(), "pause", 0, 0, 0, 0 ];
+}
+
+function getLastCoordinates() {
+ return (coordinates.length > 0)? coordinates[coordinates.length - 1] : null;
+}
+
+function getCoordinates() {
+ if (coordinates.length > 0 && !running) {
+ var result = "";
+ var i = 0;
+ while (i < coordinates.length) {
+ var position = coordinates[i];
+ var j = 1;
+ result += position[0];
+ while (j < position.length) result += "," + position[j++];
+ result += " ";
+ i++;
+ }
+ return result;
+ }
+ return null;
+}
+
+function calculateDistance(lat1, lon1, lat2, lon2) {
+ var R = 6371; // km
+ var dLat = (lat2-lat1) * Math.PI / 180;
+ var dLon = (lon2-lon1) * Math.PI / 180;
+ var lat1 = lat1 * Math.PI / 180;
+ var lat2 = lat2 * Math.PI / 180;
+ var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
+ Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
+ var d = R * c;
+ return d;
+}
+
+function calculateDistanceCoordinates() {
+ var result = 0;
+ if (coordinates.length > 0) {
+ var i = 1;
+ while (i < coordinates.length) {
+ var position1 = coordinates[i-1];
+ var position2 = coordinates[i];
+ result += calculateDistance(position1[2], position1[1], position2[2], position2[1]);
+ i++;
+ }
+ }
+ return result;
+}
+
+function calculateDurationCoordinates() {
+ var result = 0;
+ if (coordinates.length > 0) {
+ var i = 1;
+ while (i < coordinates.length) {
+ var position1 = coordinates[i-1];
+ var position2 = coordinates[i];
+ result += (position2[0]- position1[0]);
+ i++;
+ }
+ }
+ return result;
+}
+
+function repaint() {
+ if (document.visibilityState == true && running) {
+ document.getElementById("locations").textContent = String(locations);
+ var d = calculateDistanceCoordinates() * dst_unit;
+ var t = duration + (Date.now() - start_time);
+ document.getElementById("distance").textContent = String(Math.round(d*100)/100);
+ if (t != 0 && d != 0) {
+ document.getElementById("speed").textContent = String(Math.round( (d / (t / 3600000)) * 100) / 100);
+ document.getElementById("pace").textContent = String(Math.round( ((t / 60000)/d) * 100) / 100);
+ }
+ if (coordinates.length > 0) {
+ document.getElementById("gps_text").style.display = "none";
+ document.getElementById("gps_data").style.display = "inline";
+ }
+ }
+}
+
+function logGPS(position, startMsg) {
+ document.getElementById("log").textContent =
+ startMsg + position.timestamp
+ + ": " + position.coords.latitude
+ + ", " + position.coords.longitude
+ + ", " + position.coords.altitude
+ + ", " + position.coords.accuracy
+ + ", " + position.coords.altitudeAccuracy
+ + ", " + position.coords.heading
+ + ", " + position.coords.speed;
+}
+
+function handleSuccess(position) {
+ if (running) {
+ logGPS(position, "GPS: ");
+
+ // discard positions that have no altitude since they're likely inacurate
+ if (position.coords.altitude != null) {
+ pushCoordinates(position);
+ locations++;
+ repaint();
+ } else {
+ document.getElementById("discarded").textContent = String(discarded++);
+ }
+ } else {
+
+
+ if (position.coords.altitude == null) {
+ got_error++;
+ if (got_error > MAX_ERRORS) got_error = MAX_ERRORS;
+ logGPS(position, "[ERROR] GPS: ");
+ } else {
+ got_error--;
+ logGPS(position, "GPS: ");
+ }
+ if (got_error <= 0) {
+ got_error = 0;
+ document.getElementById("gps_text").textContent = "ready";
+ document.getElementById("waves").style.display = "block";
+ } else {
+ document.getElementById("gps_text").textContent = "not ready";
+ document.getElementById("waves").style.display = "none";
+ }
+ }
+}
+
+function handleError(error) {
+ var code = "unknown";
+ switch(error.code) {
+ case error.TIMEOUT:
+ code = "timeout";
+ break;
+ case error.PERMISSION_DENIED:
+ code = "permission denied";
+ break;
+ case error.POSITION_UNAVAILABLE:
+ code = "position unavailable";
+ break;
+ }
+ if (running) {
+ document.getElementById("errors").textContent = String(++got_error);
+ document.getElementById("log").textContent = "[ERROR] GPS: " + code;
+ } else {
+ document.getElementById("gps_text").textContent = code;
+ // wait for 5 non-consecutive errors
+ got_error = MAX_ERRORS;
+ }
+}
+
+function timer() {
+ if (document.visibilityState == true) {
+ document.getElementById("realtime").textContent =
+ formatTime(Math.round((duration+(Date.now()-start_time))/1000));
+ }
+}
+
+function start() {
+ if (!running) {
+ running = true;
+ start_time = Date.now();
+ intervalId = setInterval(timer, 1000);
+ document.getElementById("stop").disabled = false;
+ document.getElementById("start").textContent = "Pause";
+ } else {
+ running = false;
+ clearInterval(intervalId);
+ duration += (Date.now() - start_time);
+ document.getElementById("start").textContent = "Restart";
+ pause();
+ document.getElementById("gps_text").style.display = "inline";
+ document.getElementById("gps_data").style.display = "none";
+ }
+}
+
+function stop() {
+ // navigator.geolocation.clearWatch(watchId);
+ running = false;
+ clearInterval(intervalId);
+ got_error = 0;
+ locations = 0;
+ discarded = 0;
+ duration = 0;
+ document.getElementById("start").textContent = "Start";
+ document.getElementById("stop").disabled = true;
+ document.getElementById("log").textContent = "";
+ var coords = getCoordinates();
+ if (coords != null && storage) {
+ try {
+ window.localStorage.setItem("track_gps_app", coords);
+ } catch (e) {
+ document.getDocumentById("log").textContent = "[ERROR] Can't store the value " + e.code;
+ }
+ }
+ if (coords != null) {
+ if (!send("teole",coords)) {
+ document.getElementById("gps_coords").value = coords;
+ document.getElementById("xhr_failed").style.display = "inline";
+ document.getElementById("xhr_failed").disabled = false;
+ }
+ }
+ coordinates = [];
+}
+
+function init() {
+ if (!!document.visibilityState)
+ document.addEventListener("visibilitychange", repaint, false);
+ else
+ document.visibilityState = true;
+
+ // @@ in the future, make sure this is empty
+ if (storage) {
+ window.localStorage.setItem("track_gps_app", "");
+
+ if (window.localStorage.getItem("track_gps_app_dst_unit") === "mile") {
+ dst_unit = 0.621371192;
+ }
+ setUnit();
+ } else {
+ document.getElementById("log").textContent = "[ERROR] no local storage";
+ }
+ if (!!navigator.geolocation) {
+ watchId = navigator.geolocation.watchPosition(handleSuccess, handleError, {enableHighAccuracy:true, maximumAge:0, timeout:1000});
+ } else {
+ document.getElementById("gps_text").textContent = "not supported";
+ document.getElementById("start").disabled = true;
+ }
+
+ var xhr = new XMLHttpRequest;
+ if (!!xhr && !("withCredentials" in xhr)) {
+ document.getElementById("xhr_failed").style.display = "inline";
+ document.getElementById("xhr_failed").disabled = true;
+ }
+}
+
+window.addEventListener("load", init, false);
+
+function km() {
+ if (storage) {
+ window.localStorage.setItem("track_gps_app_dst_unit", "km");
+ }
+ dst_unit = 1;
+ setUnit();
+ repaint();
+}
+
+function mile() {
+ if (storage) {
+ window.localStorage.setItem("track_gps_app_dst_unit", "mile");
+ }
+ dst_unit = 0.621371192;
+ setUnit();
+ repaint();
+}
+
+function setUnit() {
+ var text = (dst_unit == 0.621371192)? "mile" : "km";
+ var nodes = document.getElementsByClassName("dst_unit");
+ i = 0;
+ do {
+ nodes[i].textContent = text;
+ } while (++i < nodes.length);
+}
+
+]]>
+ </script>
+<style>
+body { background-color: #6699ff; }
+section table { margin: auto; }
+section p { text-align: center;}
+.text { font-size: 150%; }
+button {
+ font-size: 175%;
+ padding-left: 1ex;
+ padding-right: 1ex;
+ margin: 0;
+}
+#log { font-size: 75%; }
+#errors { color: red }
+#discarded { color: #f88 }
+table { border-collapse: collapse; }
+th { text-align: left; border-right: 1px solid #ccc; padding-right: 0.5ex;}
+td:nth-child(2) { text-align: right; width: 8ex; padding-right: 0.5ex;}
+h1 { text-align: right; padding-right: 1ex; font-family: cursive; margin: 0;}
+#gps_data { display: none }
+
+nav #atab0 { color: #ccc; cursor: default; }
+#vtab0:target ~ #tab0 { display: block }
+#vtab0:target ~ #tab1 { display: none }
+#vtab0:target ~ nav #atab1 { color: #6699ff; cursor: pointer; }
+#vtab0:target ~ nav #atab0 { color: #ccc; cursor: default; }
+
+#tab1 { display: none; }
+#vtab1:target ~ #tab0 { display: none }
+#vtab1:target ~ #tab1 { display: block }
+#vtab1:target ~ nav #atab0 { color: #6699ff; cursor: pointer; }
+#vtab1:target ~ nav #atab1 { color: #ccc; cursor: default; }
+
+section.tab_anchor { display: none; }
+
+nav {
+ border-bottom: 1px solid white;
+ padding-bottom: 2px;
+ margin-bottom: 5px;
+}
+nav a {
+ border: 1px solid white;
+ background: white;
+ color: #6699ff;
+ padding: 2px;
+}
+</style>
+</head>
+<body>
+<section id='vtab0' class='tab_anchor'></section>
+<section id='vtab1' class='tab_anchor'></section>
+
+<h1>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 236.6261 299.95209" width="36" height="40">
+ <g transform="translate(-317.72197,-178.00957)">
+ <g transform="matrix(2.8428417,1.4908353,-1.4908353,2.8428417,85.009337,-2057.1417)">
+
+ <g id='waves' style='display: none'>
+ <g transform="matrix(0.83109018,-0.84335752,0.84335752,0.83109018,-425.72131,478.52845)">
+ <path style="fill:#E54B26" d="m 443.65385,563.57293 c -1.05193,-0.0129 -2.10701,0.0598 -3.12744,0.18048 l 0.06,2.75094 c 0.99036,-0.12733 2.00989,-0.19417 3.0337,-0.18162 12.83055,0.15709 23.11001,11.57671 22.93914,25.53274 -0.002,0.17643 -0.001,0.35569 -0.007,0.53121 l 2.50057,-0.0319 c 0.004,-0.15699 0.004,-0.31113 0.006,-0.46872 0.18944,-15.473 -11.18011,-28.13898 -25.40528,-28.31315 z"/>
+ <path style="fill:#E54B26" d="m 442.89836,570.53348 c -0.79783,-0.01 -1.59802,0.0454 -2.37195,0.13688 l 0.0455,2.52833 c 0.75111,-0.0966 1.52436,-0.14725 2.30084,-0.13774 9.73108,0.11913 16.95911,8.33817 16.82952,18.92284 -0.002,0.1338 -10e-4,0.26976 -0.005,0.40289 l 2.46472,-0.0242 c 0.003,-0.11907 0.003,-0.23598 0.004,-0.35547 0.14368,-11.73519 -8.47933,-21.34145 -19.26811,-21.47354 z"/>
+ <path style="fill:#E54B26" d="m 442.20847,576.88946 c -0.56577,-0.007 -1.13323,0.0322 -1.68206,0.0971 l 0.0323,2.42658 c 0.53265,-0.0685 1.081,-0.10442 1.63165,-0.0977 6.90079,0.46331 10.91426,5.09 11.13804,12.78552 -10e-4,0.0949 -7.7e-4,0.19129 -0.003,0.28572 l 2.54446,-0.0171 c 0.002,-0.0845 0.002,-0.16735 0.003,-0.25208 0.10189,-8.32201 -6.01312,-15.13429 -13.66399,-15.22797 z"/>
+ </g>
+ </g>
+ <g transform="translate(4.3272904,0)">
+ <path style="fill:#E54B26" d="m 428.82958,632.50381 c 1.51739,8.39514 3.18843,16.70366 3.25039,25.86381 13.43389,-7.07008 16.24573,-8.60045 29.67957,-15.67062 -4.84405,-3.37683 -9.59166,-7.39353 -13.56307,-11.22903 -6.14118,1.98174 -12.37667,2.63171 -19.36689,1.03584 z m 3.03559,3.5588 2.90054,0.34654 c 1.65734,7.68989 1.24429,15.11973 -0.19508,18.63703 -0.5999,-6.11993 -0.9108,-10.44519 -2.70546,-18.98357 z"/>
+ <path style="fill:#E54B26" d="m 398.11826,602.8218 c 5.46403,15.3042 20.05405,26.26585 37.2409,26.32057 17.18753,0.0547 31.81943,-10.80285 37.38029,-26.07291 l -74.62119,-0.24766 z m 6.11343,4.37701 6.55964,0.18844 c 4.52018,8.23085 9.05564,11.77782 15.40988,16.82483 -9.13544,-2.75636 -16.94711,-7.80772 -21.96952,-17.01327 z"/>
+ <g transform="translate(2.1996971,2)">
+ <rect y="594.76306" x="431.58765" height="8.0812206" width="3.2829957" style="fill:#E54B26"/>
+ <path d="m 436.63842,594.00549 c 0,2.0921 -1.52638,3.78808 -3.40926,3.78808 -1.88289,0 -3.40927,-1.69598 -3.40927,-3.78808 0,-2.09209 1.52638,-3.78807 3.40927,-3.78807 1.88288,0 3.40926,1.69598 3.40926,3.78807 z" style="fill:#E54B26"/>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
+ HTML5 Track</h1>
+
+<nav>
+<a id='atab0' href='#vtab0'>Tracker</a>
+<a id='atab1' href='#vtab1'>Settings</a>
+</nav>
+
+
+<section id='tab0'>
+<table class='text'>
+<tbody>
+<tr>
+<th>Distance</th> <td id='distance'>0</td><td class='dst_unit'>km</td>
+</tr>
+<tr>
+<th>Time</th><td id='realtime'>00:00</td><td></td>
+</tr>
+<tr>
+<th>Speed</th><td id='speed'>0</td><td><span class='dst_unit'>km</span>/h</td>
+</tr>
+<tr>
+<th>Pace</th><td id='pace'>0</td><td>min/<span class='dst_unit'>km</span></td>
+</tr>
+<tr>
+<th>GPS</th><td colspan='2'><span id='gps_text'>not started</span><span id='gps_data'><span id='errors'>0</span>/<span id='discarded'>0</span>/<span id='locations'>0</span></span></td>
+</tr>
+</tbody>
+</table>
+
+<section>
+<p>
+<button type='button' id='start'
+ onclick='start()'>Start</button>
+
+<button type='button' id='stop' disabled='disabled'
+ onclick='stop()'>Stop</button>
+
+<form action='http://teole.jfouffa.org/2011/07/html5track.php' method='post'>
+ <input type='hidden' name='gps_coords' id='gps_coords' value='13,-71,42,-20 '/>
+ <input type='hidden' name='user' value='teole'/>
+
+ <button type='submit' id='xhr_failed' style='display:none'>Send</button>
+</form>
+
+</p>
+
+</section>
+
+</section>
+
+<section id='tab1'>
+
+<p class='text'>Current unit: <span class='dst_unit'>km</span></p>
+
+<p>
+<button type='button' id='start' onclick='km()'>KM</button>
+
+<button type='button' id='stop' onclick='mile()'>MILE</button>
+</p>
+
+</section>
+
+<p id='log'>
+</p>
+
+</body>
+</html>