--- a/src/main/resources/scripts/tracker.js Tue Aug 23 10:48:47 2011 -0400
+++ b/src/main/resources/scripts/tracker.js Tue Aug 23 16:39:44 2011 -0400
@@ -1,19 +1,73 @@
+var Const = {
+ KM : "km",
+ KM_UNIT : 1,
+ MILE_UNIT : 0.621371192,
+ MILE : "mile",
-function send(user, data) {
+ // wait for at least 2 consecutive GPS data before declaring the GPS ready
+ GPS_MAX_ERRORS : 2,
+};
+
+var Util = {
+ store : {
+ enabled : (function() {
+ try {
+ return !!window.localStorage.getItem;
+ } catch (e) {
+ return false;
+ }
+ })(),
+ set : function (key, value) {
+ try {
+ window.localStorage.setItem(key, JSON.stringify(value));
+ return true;
+ } catch (e) { };
+ return false;
+ },
+ get : function (key) {
+ try {
+ var value = window.localStorage.getItem(key);
+ return ((value != null)? JSON.parse(value) : null);
+ } catch (e) { };
+ return null;
+ }
+ },
+
+ log : function (msg) {
+ var element = document.getElementById("log");
+ if (element != null) element.textContent = msg;
+ },
+
+ // pretty formatting of numbers
+ formatNumber : function (n) {
+ return (n < 10)? "0" + n : n;
+ },
+
+ // pretty formatting of time
+ formatTime : function (time) {
+ t = parseInt(time);
+ hours = parseInt(t / 3600);
+ minutes = parseInt(t / 60);
+ seconds = t - ((hours * 3600) + (minutes * 60));
+ return ((hours > 0)? Util.formatNumber(hours) + ":": "") + Util.formatNumber(minutes) + ":" + Util.formatNumber(seconds);
+ },
+
+ // send information to the server as JSON
+ sendAsJSON : function (uri, data) {
var xhr = new XMLHttpRequest;
try {
- xhr.open("POST", "/user/" + user + "/run", true);
+ xhr.open("POST", uri, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status != 200) { // this needs to be 201
- log("xhr failed? [" + xhr.status + "] " + xhr.statusText);
+ Util.log("xhr failed? [" + xhr.status + "] " + xhr.statusText);
} else {
- log("resource created at " + xhr.getResponseHeader("Location"));
+ Util.log("resource created at " + xhr.getResponseHeader("Location"));
}
} else {
- log("xhr readyState is " + xhr.readyState);
+ Util.log("xhr readyState is " + xhr.readyState);
}
};
xhr.setRequestHeader("Content-Type", "application/json");
@@ -22,133 +76,139 @@
xhr.send(JSON.stringify(data));
} catch (e) {
- log("Failed to send to server " + e);
+ Util.log("Failed to send to server " + 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 2 consecutive GPS data before declaring the GPS ready
-var MAX_ERRORS = 2;
-var got_error = MAX_ERRORS;
+ },
-var discarded = 0;
-var locations = 0;
-
-var activity = { activity: "http://dbpedia.org/resource/Running", events: []};
-
-var dst_unit = 1;
-
-function formatNumber(n) {
- return (n < 10)? "0" + n : n;
-}
+ geo : {
+ // calculate the distance in km between two GPS locations
+ calculateDistance : function (lat1, lon1, lat2, lon2) {
+ 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);
+ // 6371 km is average earth radius
+ return 6371 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
+ },
+
+ calculateDistanceCoordinates : function (events) {
+ var result = 0;
+ if (events.length > 0) {
+ var i = 1;
+ while (i < events.length) {
+ var position1 = events[i-1].c;
+ var position2 = events[i].c;
+ result += Util.geo.calculateDistance(position1[1], position1[0], position2[1], position2[0]);
+ i++;
+ }
+ }
+ return result;
+ },
+ // calculate the duration between geolocation coordinates
+ calculateDurationCoordinates : function (events) {
+ var result = 0;
+ if (events.length > 0) {
+ var i = 1;
+ while (i < events.length) {
+ result += (events[i].t - events[i-1].t);
+ i++;
+ }
+ }
+ return result;
+ }
+ }
+};
-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);
-}
+var getUser = function () {
+ function dummyuser() {
+ this.name = "dummy";
+ this.weight = "73";
+ this.height = "180";
+ this.distance = "1";
+ this.prefs = { weight : "kg", height : "cm", distance : Const.KM };
+ }
+ var n = Util.store.get("track_gps_user");
+ if (n === null) n = new dummyuser();
+ n.getPostRunURI = function() { return "/user/" + this.name + "/run"; }
+ n.getProfileURI = function() { return "/user/" + this.name + "/profile"; }
+ n.setKm = function() { this.distance = Const.KM_UNIT; this.prefs.distance = Const.KM; }
+ n.setMile = function() { this.distance = Const.MILE_UNIT; this.prefs.distance = Const.MILE; }
+ n.save = function() {
+ Util.store.set("track_gps_user", this);
+ };
+
+ return n;
+};
-
+var app = {
+ user : getUser(),
-function coordinates_event(position) {
+ // identifier for geolocation watch operation
+ watchId : 0,
+
+ // is an activity ongoing?
+ inActivity : false,
+
+ // total real time duration
+ duration : 0,
+ // start_time since start or pause
+ start_time : 0,
+
+ // identifier for setInterval timer operation
+ intervalId : 0,
+
+ // got_errors counts how many we've got
+ got_error : Const.GPS_MAX_ERRORS,
+
+ // total of geolocation data discarded
+ discarded : 0,
+
+ // total of geolocation data accepted
+ locations : 0,
+
+ // this will store all the geolocation data and pause
+ currentActivity : { activity: "http://dbpedia.org/resource/Running", events: []},
+
+};
+
+// add a geolocation position into the current activity
+app.coordinates_event = function (activity, position) {
var events = activity.events;
events[events.length] =
{ t:position.timestamp, c: [position.coords.longitude, position.coords.latitude, position.coords.altitude] };
}
-function pause_event() {
+// add a pause event into the current activity
+app.pause_event = function (activity) {
var events = activity.events;
events[events.length] = { t:Date.now(), s: [ "pause" ] };
}
-
-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 events = activity.events;
- var result = 0;
- if (events.length > 0) {
- var i = 1;
- while (i < events.length) {
- var position1 = events[i-1].c;
- var position2 = events[i].c;
- result += calculateDistance(position1[1], position1[0], position2[1], position2[0]);
- i++;
- }
- }
- return result;
-}
-
-function calculateDurationCoordinates() {
- var events = activity.events;
- var result = 0;
- if (events.length > 0) {
- var i = 1;
- while (i < events.length) {
- var position1 = events[i-1].t;
- var position2 = events[i].t;
- result += (position2 - position1);
- 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);
+// simply refresh the current screen data
+app.repaint = function () {
+ if (document.visibilityState && app.inActivity) {
+ document.getElementById("locations").textContent = String(app.locations);
+ var d = Util.geo.calculateDistanceCoordinates(app.currentActivity.events) * app.user.distance;
+ var t = app.duration + (Date.now() - app.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 (activity.events.length > 0) {
+ if (app.currentActivity.events.length > 0) {
document.getElementById("gps_text").style.display = "none";
document.getElementById("gps_data").style.display = "inline";
}
}
}
-
-function log(msg) {
- document.getElementById("log").textContent = msg;
-}
-
-function logGPS(position, startMsg) {
- log(
+// Display a GPS location in the page
+app.logGPS = function (position, startMsg) {
+ Util.log(
startMsg + position.timestamp
+ ": " + position.coords.latitude
+ ", " + position.coords.longitude
@@ -159,31 +219,33 @@
+ ", " + position.coords.speed);
}
-function handleSuccess(position) {
- if (running) {
- logGPS(position, "GPS: ");
- // discard positions that have no altitude since they're likely inacurate
+// when watchPosition is successful at returning a GPS coordinate
+app.handleSuccess = function (position) {
+ if (app.inActivity) {
+ app.logGPS(position, "GPS: ");
+
+ // discard positions with no altitude since they're likely inaccurate
if (position.coords.altitude != null) {
- coordinates_event(position);
- locations++;
- repaint();
+ app.coordinates_event(app.currentActivity, position);
+ app.locations++;
+ app.repaint();
} else {
- document.getElementById("discarded").textContent = String(discarded++);
+ document.getElementById("discarded").textContent = String(app.discarded++);
}
} else {
if (position.coords.altitude == null) {
- got_error++;
- if (got_error > MAX_ERRORS) got_error = MAX_ERRORS;
- logGPS(position, "[ERROR] GPS: ");
+ app.got_error++;
+ if (app.got_error > Util.GPS_MAX_ERRORS) app.got_error = Util.GPS_MAX_ERRORS;
+ app.logGPS(position, "[ERROR] GPS: ");
} else {
- got_error--;
- logGPS(position, "GPS: ");
+ app.got_error--;
+ app.logGPS(position, "GPS: ");
}
- if (got_error <= 0) {
- got_error = 0;
+ if (app.got_error <= 0) {
+ app.got_error = 0;
document.getElementById("gps_text").textContent = "ready";
document.getElementById("waves").style.display = "block";
} else {
@@ -193,7 +255,8 @@
}
}
-function handleError(error) {
+// when watchPosition is not successful at returning a GPS coordinate
+app.handleError = function (error) {
var code = "unknown";
switch(error.code) {
case error.TIMEOUT:
@@ -206,68 +269,68 @@
code = "position unavailable";
break;
}
- if (running) {
- document.getElementById("errors").textContent = String(++got_error);
- log("[ERROR] GPS: " + code);
+ if (app.inActivity) {
+ document.getElementById("errors").textContent = String(++app.got_error);
+ Util.log("[ERROR] GPS: " + code);
} else {
document.getElementById("gps_text").textContent = code;
// wait for MAX_ERRORS non-consecutive errors
- got_error = MAX_ERRORS;
+ app.got_error = Util.GPS_MAX_ERRORS;
}
}
-function timer() {
- if (document.visibilityState == true) {
+// Called by setInterval to display the real time
+app.timer = function () {
+ if (document.visibilityState) {
document.getElementById("realtime").textContent =
- formatTime(Math.round((duration+(Date.now()-start_time))/1000));
+ Util.formatTime(Math.round((app.duration+(Date.now()-app.start_time))/1000));
}
}
-function start() {
- if (!running) {
- running = true;
- start_time = Date.now();
- intervalId = setInterval(timer, 1000);
+// The Start / Pause button
+app.start = function () {
+ if (!app.inActivity) {
+ // start an activity
+ app.inActivity = true;
+ app.start_time = Date.now();
+ app.intervalId = setInterval(app.timer, 1000);
document.getElementById("stop").disabled = false;
document.getElementById("start").textContent = "Pause";
} else {
- running = false;
- clearInterval(intervalId);
- duration += (Date.now() - start_time);
+ // pause an activity
+ app.inActivity = false;
+ clearInterval(app.intervalId);
+ app.duration += (Date.now() - app.start_time);
document.getElementById("start").textContent = "Restart";
- pause_event();
+ app.pause_event(app.activity);
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;
+// The Stop button
+app.stop = function () {
+ app.inActivity = false;
+ navigator.geolocation.clearWatch(app.watchId);
+ clearInterval(app.intervalId);
+ app.got_error = 0;
+ app.locations = 0;
+ app.discarded = 0;
+ app.duration = 0;
document.getElementById("start").textContent = "Start";
document.getElementById("stop").disabled = true;
- log("");
- if (storage) {
- try {
- window.localStorage.setItem("track_gps_app", JSON.stringify(activity));
- } catch (e) {
- log("[ERROR] Can't store the value " + e.code);
+ Util.log("");
+ if (!Util.sendAsJSON(app.user.getPostRunURI(),app.currentActivity)) {
+ if (!Util.store.set("track_gps_app", app.currentActivity)) {
+ Util.log("[ERROR] Can't store the value " + e.code);
}
}
- if (!send("teole",activity)) {
- // do something!
- }
- activity.events = [];
+ app.currentActivity.events = [];
}
-function init() {
+app.init = function () {
if (!!document.visibilityState)
- document.addEventListener("visibilitychange", repaint, false);
+ document.addEventListener("visibilitychange", app.repaint, false);
else
document.visibilityState = true;
@@ -275,18 +338,13 @@
// return false;
// @@ 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();
+ if (Util.store.set("track_gps_app", "")) {
+ app.setUnit();
} else {
- log("[ERROR] no local storage");
+ Util.log("[ERROR] no local storage");
}
if (!!navigator.geolocation) {
- watchId = navigator.geolocation.watchPosition(handleSuccess, handleError, {enableHighAccuracy:true, maximumAge:0, timeout:1000});
+ app.watchId = navigator.geolocation.watchPosition(app.handleSuccess, app.handleError, {enableHighAccuracy:true, maximumAge:0, timeout:1000});
} else {
document.getElementById("gps_text").textContent = "not supported";
document.getElementById("start").disabled = true;
@@ -294,31 +352,26 @@
}
-window.addEventListener("load", init, false);
+window.addEventListener("load", app.init, false);
-function km() {
- if (storage) {
- window.localStorage.setItem("track_gps_app_dst_unit", "km");
- }
- dst_unit = 1;
- setUnit();
- repaint();
+app.km = function () {
+ app.user.setKm();
+ app.user.save();
+ app.setUnit();
+ app.repaint();
}
-function mile() {
- if (storage) {
- window.localStorage.setItem("track_gps_app_dst_unit", "mile");
- }
- dst_unit = 0.621371192;
- setUnit();
- repaint();
+app.mile = function () {
+ app.user.setMile();
+ app.user.save();
+ app.setUnit();
+ app.repaint();
}
-function setUnit() {
- var text = (dst_unit == 0.621371192)? "mile" : "km";
- var nodes = document.getElementsByClassName("dst_unit");
+app.setUnit = function () {
+ var nodes = document.getElementsByClassName("distance_unit");
i = 0;
do {
- nodes[i].textContent = text;
+ nodes[i].textContent = app.user.prefs.distance;
} while (++i < nodes.length);
}