shorten the update message
[lantea.git] / js / ui.js
CommitLineData
a7393a71
RK
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
23cd2dcc 4
41e2dba2 5// Get the best-available objects for indexedDB and requestAnimationFrame.
3d99a6fd 6window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
41e2dba2 7window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
993fd081 8
41e2dba2 9var mainDB;
16e4f664 10var gAppInitDone = false;
e1c3d337 11var firstRun = false;
7a549148 12var gUIHideCountdown = 0;
4b1d0915 13var gWaitCounter = 0;
026c4f46 14var gTrackUpdateInterval;
68afcd96 15var gAction, gActionLabel;
fd6126e2 16var authData = null, userData = null;
662078d1 17var gBackendURL = "https://backend.lantea.kairo.at/";
8f9306c5 18var gAuthClientID = "lantea";
7a549148 19
b47b4a65 20window.onload = function() {
68afcd96
RK
21 gAction = document.getElementById("action");
22 gActionLabel = document.getElementById("actionlabel");
23
b47b4a65
RK
24 var mSel = document.getElementById("mapSelector");
25 for (var mapStyle in gMapStyles) {
26 var opt = document.createElement("option");
27 opt.value = mapStyle;
28 opt.text = gMapStyles[mapStyle].name;
29 mSel.add(opt, null);
30 }
23cd2dcc 31
4018d25a 32 var areas = document.getElementsByClassName("autoFade");
7a549148
RK
33 for (var i = 0; i <= areas.length - 1; i++) {
34 areas[i].addEventListener("mouseup", uiEvHandler, false);
35 areas[i].addEventListener("mousemove", uiEvHandler, false);
36 areas[i].addEventListener("mousedown", uiEvHandler, false);
37 areas[i].addEventListener("mouseout", uiEvHandler, false);
38
39 areas[i].addEventListener("touchstart", uiEvHandler, false);
40 areas[i].addEventListener("touchmove", uiEvHandler, false);
41 areas[i].addEventListener("touchend", uiEvHandler, false);
42 areas[i].addEventListener("touchcancel", uiEvHandler, false);
43 areas[i].addEventListener("touchleave", uiEvHandler, false);
44 }
45
1222624d
RK
46 document.getElementById("body").addEventListener("keydown", uiEvHandler, false);
47
8e901dce 48 if (navigator.platform.length == "") {
b91b74a7
RK
49 // For Firefox OS, don't display the "save" button.
50 // Do this by setting the debugHide class for testing in debug mode.
51 document.getElementById("saveTrackButton").classList.add("debugHide");
c4d0569c
RK
52 }
53
8f9306c5
RK
54 // Set backend URL in a way that it works for testing on localhost as well as
55 // both the lantea.kairo.at and lantea-dev.kairo.at deployments.
56 if (window.location.host == "localhost") {
57 gBackendURL = window.location.protocol + '//' + window.location.host + "/lantea-backend/";
58 }
59 else {
60 gBackendURL = window.location.protocol + '//' + "backend." + window.location.host + "/";
61 }
62 // Make sure to use a different login client ID for the -dev setup.
63 if (/\-dev\./.test(window.location.host)) {
64 gAuthClientID += "-dev";
65 }
66
867ee0af
RK
67 document.getElementById("libCloseButton").onclick = hideLibrary;
68
8f9306c5
RK
69 // Set up the login area.
70 document.getElementById("loginbtn").onclick = startLogin;
71 document.getElementById("logoutbtn").onclick = doLogout;
fd6126e2
RK
72 // Put in a logged-out state by default.
73 // Opening the track drawer will update this correctly.
74 displayLogout();
43255174 75
582d50fc
RK
76 gAction.addEventListener("dbinit-done", initMap, false);
77 gAction.addEventListener("mapinit-done", postInit, false);
78 console.log("starting DB init...");
993fd081 79 initDB();
582d50fc 80}
4b1d0915 81
582d50fc
RK
82function postInit(aEvent) {
83 gAction.removeEventListener(aEvent.type, postInit, false);
84 console.log("init done, draw map.");
85 gMapPrefsLoaded = true;
16e4f664 86 gAppInitDone = true;
14acbcf7
RK
87 //gMap.resizeAndDraw(); <-- HACK: This triggers bug 1001853, work around with a delay.
88 window.setTimeout(gMap.resizeAndDraw, 100);
582d50fc
RK
89 gActionLabel.textContent = "";
90 gAction.style.display = "none";
91 setTracking(document.getElementById("trackCheckbox"));
65946832 92 gPrefs.get("devicename", function(aValue) {
582d50fc 93 if (aValue) {
65946832 94 document.getElementById("uploadDevName").value = aValue;
4b1d0915 95 }
582d50fc 96 });
e1c3d337
RK
97 if (firstRun) {
98 showFirstRunDialog();
99 }
100 else {
101 gPrefs.get("lastInfoShown", function(aValue) {
102 if (!aValue || !parseInt(aValue) || parseInt(aValue) < 1) {
103 showInfoDialog();
104 }
105 });
106 }
107 gPrefs.set("lastInfoShown", 1);
b47b4a65
RK
108}
109
110window.onresize = function() {
ac6286bd 111 gMap.resizeAndDraw();
b47b4a65
RK
112}
113
8f9306c5 114function startLogin() {
662078d1
RK
115 var logerr = document.getElementById("loginerror");
116 logerr.classList.add("hidden");
117 logerr.title = "";
fd6126e2
RK
118 if (!authData || !authData["state"]) {
119 // We have no oAuth state, try to fetch it and call ourselves again if it worked.
120 prepareLoginButton(function() {
121 if (authData && authData["state"]) {
122 startLogin();
123 }
124 else if (!userData) {
125 // Only warn if we didn't actually end up being logged in.
126 console.log("No OAuth state and fetching fails, client or server may be offline.");
662078d1
RK
127 logerr.classList.remove("hidden");
128 logerr.title = "Client or server may be offline.";
fd6126e2
RK
129 }
130 });
131 return;
132 }
8f9306c5
RK
133 var authURL = authData["url"] + "authorize?response_type=code&client_id=" + gAuthClientID + "&scope=email" +
134 "&state=" + authData["state"] + "&redirect_uri=" + encodeURIComponent(getRedirectURI());
135 if (window.open(authURL, "KaiRoAuth", 'height=450,width=600')) {
136 console.log("Sign In window open.");
137 }
138 else {
139 console.log("Opening Sign In window failed.");
662078d1
RK
140 logerr.classList.remove("hidden");
141 logerr.title = "Opening Sign-In window failed.";
8f9306c5
RK
142 }
143}
144
145function getRedirectURI() {
67aadbe8 146 return window.location.protocol + '//' + window.location.host + window.location.pathname.replace("index.html", "") + "login.html";
8f9306c5
RK
147}
148
149function doLogout() {
150 fetchBackend("logout", "GET", null,
151 function(aResult, aStatus) {
152 if (aStatus < 400) {
153 prepareLoginButton();
154 }
155 else {
156 console.log("Backend issue trying to log out.");
157 }
158 },
159 {}
160 );
161}
162
163function prepareLoginButton(aCallback) {
164 fetchBackend("oauth_state", "GET", null,
165 function(aResult, aStatus) {
166 if (aStatus == 200) {
167 if (aResult["logged_in"]) {
168 userData = {
169 "email": aResult["email"],
170 "permissions": aResult["permissions"],
171 };
172 authData = null;
173 displayLogin();
174 }
175 else {
176 authData = {"state": aResult["state"], "url": aResult["url"]};
177 userData = null;
178 displayLogout();
179 }
180 }
181 else {
3c30699f 182 console.log("Backend error " + aStatus + " fetching OAuth state: " + aResult["message"]);
8f9306c5
RK
183 }
184 if (aCallback) { aCallback(); }
185 },
186 {}
187 );
188}
189
8f9306c5
RK
190function finishLogin(aCode, aState) {
191 if (aState == authData["state"]) {
192 fetchBackend("login?code=" + aCode + "&state=" + aState + "&redirect_uri=" + encodeURIComponent(getRedirectURI()), "GET", null,
193 function(aResult, aStatus) {
194 if (aStatus == 200) {
195 userData = {
196 "email": aResult["email"],
197 "permissions": aResult["permissions"],
198 };
199 displayLogin();
200 }
201 else {
202 console.log("Login error " + aStatus + ": " + aResult["message"]);
203 prepareLoginButton();
204 }
205 },
206 {}
207 );
208 }
209 else {
210 console.log("Login state did not match, not continuing with login.");
211 }
212}
213
214function displayLogin() {
215 document.getElementById("loginbtn").classList.add("hidden");
216 document.getElementById("logindesc").classList.add("hidden");
217 document.getElementById("username").classList.remove("hidden");
218 document.getElementById("username").textContent = userData.email;
219 document.getElementById("uploadTrackButton").disabled = false;
867ee0af 220 document.getElementById("libraryShowLine").classList.remove("hidden");
8f9306c5
RK
221 document.getElementById("logoutbtn").classList.remove("hidden");
222}
223
224function displayLogout() {
225 document.getElementById("logoutbtn").classList.add("hidden");
226 document.getElementById("username").classList.add("hidden");
227 document.getElementById("username").textContent = "";
228 document.getElementById("uploadTrackButton").disabled = true;
867ee0af 229 document.getElementById("libraryShowLine").classList.add("hidden");
8f9306c5
RK
230 document.getElementById("loginbtn").classList.remove("hidden");
231 document.getElementById("logindesc").classList.remove("hidden");
232}
233
582d50fc 234function initDB(aEvent) {
993fd081 235 // Open DB.
582d50fc
RK
236 if (aEvent)
237 gAction.removeEventListener(aEvent.type, initDB, false);
1222624d 238 var request = window.indexedDB.open("MainDB-lantea", 2);
993fd081
RK
239 request.onerror = function(event) {
240 // Errors can be handled here. Error codes explain in:
241 // https://developer.mozilla.org/en/IndexedDB/IDBDatabaseException#Constants
915d4271
RK
242 if (gDebug)
243 console.log("error opening mainDB: " + event.target.errorCode);
993fd081
RK
244 };
245 request.onsuccess = function(event) {
993fd081 246 mainDB = request.result;
582d50fc
RK
247 var throwEv = new CustomEvent("dbinit-done");
248 gAction.dispatchEvent(throwEv);
993fd081
RK
249 };
250 request.onupgradeneeded = function(event) {
251 mainDB = request.result;
a8634d37 252 var ver = mainDB.version || 0; // version is empty string for a new DB
915d4271
RK
253 if (gDebug)
254 console.log("mainDB has version " + ver + ", upgrade needed.");
255 if (!mainDB.objectStoreNames.contains("prefs")) {
a8634d37
RK
256 // Create a "prefs" objectStore.
257 var prefsStore = mainDB.createObjectStore("prefs");
e1c3d337 258 firstRun = true;
915d4271
RK
259 }
260 if (!mainDB.objectStoreNames.contains("track")) {
a8634d37
RK
261 // Create a "track" objectStore.
262 var trackStore = mainDB.createObjectStore("track", {autoIncrement: true});
263 }
915d4271 264 if (!mainDB.objectStoreNames.contains("tilecache")) {
a8634d37
RK
265 // Create a "tilecache" objectStore.
266 var tilecacheStore = mainDB.createObjectStore("tilecache");
267 }
993fd081
RK
268 mainDB.onversionchange = function(event) {
269 mainDB.close();
270 mainDB = undefined;
271 initDB();
272 };
273 };
274}
275
7a549148
RK
276function showUI() {
277 if (gUIHideCountdown <= 0) {
4018d25a 278 var areas = document.getElementsByClassName('autoFade');
7a549148
RK
279 for (var i = 0; i <= areas.length - 1; i++) {
280 areas[i].classList.remove("hidden");
281 }
282 setTimeout(maybeHideUI, 1000);
283 }
284 gUIHideCountdown = 5;
285}
286
287function maybeHideUI() {
288 gUIHideCountdown--;
289 if (gUIHideCountdown <= 0) {
4018d25a 290 var areas = document.getElementsByClassName('autoFade');
7a549148
RK
291 for (var i = 0; i <= areas.length - 1; i++) {
292 areas[i].classList.add("hidden");
293 }
294 }
295 else {
296 setTimeout(maybeHideUI, 1000);
297 }
298}
299
026c4f46
RK
300function updateTrackInfo() {
301 document.getElementById("trackLengthNum").textContent = calcTrackLength().toFixed(1);
302 var duration = calcTrackDuration();
303 var durationM = Math.round(duration/60);
304 var durationH = Math.floor(durationM/60); durationM = durationM - durationH * 60;
305 document.getElementById("trackDurationH").style.display = durationH ? "inline" : "none";
306 document.getElementById("trackDurationHNum").textContent = durationH;
307 document.getElementById("trackDurationMNum").textContent = durationM;
308}
309
993fd081
RK
310function toggleTrackArea() {
311 var fs = document.getElementById("trackArea");
123b3142 312 if (fs.classList.contains("hidden")) {
4018d25a 313 prepareLoginButton();
123b3142 314 fs.classList.remove("hidden");
7a549148 315 showUI();
026c4f46 316 gTrackUpdateInterval = setInterval(updateTrackInfo, 1000);
993fd081
RK
317 }
318 else {
026c4f46 319 clearInterval(gTrackUpdateInterval);
123b3142 320 fs.classList.add("hidden");
993fd081
RK
321 }
322}
323
b47b4a65 324function toggleSettings() {
993fd081 325 var fs = document.getElementById("settingsArea");
123b3142
RK
326 if (fs.classList.contains("hidden")) {
327 fs.classList.remove("hidden");
7a549148 328 showUI();
b47b4a65
RK
329 }
330 else {
123b3142 331 fs.classList.add("hidden");
b47b4a65
RK
332 }
333}
99631a75 334
c5378747
RK
335function toggleFullscreen() {
336 if ((document.fullScreenElement && document.fullScreenElement !== null) ||
337 (document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
338 (document.webkitFullScreenElement && document.webkitFullScreenElement !== null)) {
339 if (document.cancelFullScreen) {
340 document.cancelFullScreen();
341 } else if (document.mozCancelFullScreen) {
342 document.mozCancelFullScreen();
343 } else if (document.webkitCancelFullScreen) {
344 document.webkitCancelFullScreen();
345 }
346 }
347 else {
348 var elem = document.getElementById("body");
349 if (elem.requestFullScreen) {
350 elem.requestFullScreen();
351 } else if (elem.mozRequestFullScreen) {
352 elem.mozRequestFullScreen();
353 } else if (elem.webkitRequestFullScreen) {
354 elem.webkitRequestFullScreen();
355 }
356 }
357}
358
43255174 359function showUploadDialog() {
6201b112 360 var dia = document.getElementById("trackDialogArea");
43255174
RK
361 var areas = dia.children;
362 for (var i = 0; i <= areas.length - 1; i++) {
363 areas[i].style.display = "none";
364 }
365 document.getElementById("uploadDialog").style.display = "block";
366 document.getElementById("uploadTrackButton").disabled = true;
367 dia.classList.remove("hidden");
368}
369
e1c3d337
RK
370function cancelTrackDialog() {
371 document.getElementById("trackDialogArea").classList.add("hidden");
372 document.getElementById("uploadTrackButton").disabled = false;
373}
374
ecde0af2
RK
375function showGLWarningDialog() {
376 var dia = document.getElementById("dialogArea");
377 var areas = dia.children;
378 for (var i = 0; i <= areas.length - 1; i++) {
379 areas[i].style.display = "none";
380 }
381 document.getElementById("noGLwarning").style.display = "block";
382 dia.classList.remove("hidden");
383}
384
e1c3d337
RK
385function showFirstRunDialog() {
386 var dia = document.getElementById("dialogArea");
387 var areas = dia.children;
388 for (var i = 0; i <= areas.length - 1; i++) {
389 areas[i].style.display = "none";
390 }
391 document.getElementById("firstRunIntro").style.display = "block";
392 dia.classList.remove("hidden");
393}
394
395function closeDialog() {
396 document.getElementById("dialogArea").classList.add("hidden");
397}
398
399function showInfoDialog() {
400 var dia = document.getElementById("dialogArea");
401 var areas = dia.children;
402 for (var i = 0; i <= areas.length - 1; i++) {
403 areas[i].style.display = "none";
404 }
405 document.getElementById("infoDialog").style.display = "block";
406 dia.classList.remove("hidden");
43255174
RK
407}
408
7a549148
RK
409var uiEvHandler = {
410 handleEvent: function(aEvent) {
411 var touchEvent = aEvent.type.indexOf('touch') != -1;
412
413 switch (aEvent.type) {
414 case "mousedown":
415 case "touchstart":
416 case "mousemove":
417 case "touchmove":
418 case "mouseup":
419 case "touchend":
1222624d 420 case "keydown":
7a549148
RK
421 showUI();
422 break;
423 }
424 }
425};
426
8389557a
RK
427function setUploadField(aField) {
428 switch (aField.id) {
65946832
RK
429 case "uploadDevName":
430 gPrefs.set("devicename", aField.value);
8389557a
RK
431 break;
432 }
433}
434
99631a75
RK
435function makeISOString(aTimestamp) {
436 // ISO time format is YYYY-MM-DDTHH:mm:ssZ
437 var tsDate = new Date(aTimestamp);
362a6833 438 // Note that .getUTCMonth() returns a number between 0 and 11 (0 for January)!
99631a75 439 return tsDate.getUTCFullYear() + "-" +
362a6833 440 (tsDate.getUTCMonth() < 9 ? "0" : "") + (tsDate.getUTCMonth() + 1 ) + "-" +
99631a75
RK
441 (tsDate.getUTCDate() < 10 ? "0" : "") + tsDate.getUTCDate() + "T" +
442 (tsDate.getUTCHours() < 10 ? "0" : "") + tsDate.getUTCHours() + ":" +
443 (tsDate.getUTCMinutes() < 10 ? "0" : "") + tsDate.getUTCMinutes() + ":" +
444 (tsDate.getUTCSeconds() < 10 ? "0" : "") + tsDate.getUTCSeconds() + "Z";
445}
446
8389557a
RK
447function convertTrack(aTargetFormat) {
448 var out = "";
449 switch (aTargetFormat) {
450 case "gpx":
451 out += '<?xml version="1.0" encoding="UTF-8" ?>' + "\n\n";
452 out += '<gpx version="1.0" creator="Lantea" xmlns="http://www.topografix.com/GPX/1/0">' + "\n";
453 if (gTrack.length) {
454 out += ' <trk>' + "\n";
993fd081 455 out += ' <trkseg>' + "\n";
8389557a
RK
456 for (var i = 0; i < gTrack.length; i++) {
457 if (gTrack[i].beginSegment && i > 0) {
458 out += ' </trkseg>' + "\n";
459 out += ' <trkseg>' + "\n";
460 }
461 out += ' <trkpt lat="' + gTrack[i].coords.latitude + '" lon="' +
462 gTrack[i].coords.longitude + '">' + "\n";
463 if (gTrack[i].coords.altitude) {
464 out += ' <ele>' + gTrack[i].coords.altitude + '</ele>' + "\n";
465 }
466 out += ' <time>' + makeISOString(gTrack[i].time) + '</time>' + "\n";
467 out += ' </trkpt>' + "\n";
468 }
469 out += ' </trkseg>' + "\n";
470 out += ' </trk>' + "\n";
993fd081 471 }
8389557a
RK
472 out += '</gpx>' + "\n";
473 break;
474 case "json":
475 out = JSON.stringify(gTrack);
476 break;
477 default:
478 break;
479 }
480 return out;
481}
482
483function saveTrack() {
484 if (gTrack.length) {
485 var outDataURI = "data:application/gpx+xml," +
486 encodeURIComponent(convertTrack("gpx"));
99631a75
RK
487 window.open(outDataURI, 'GPX Track');
488 }
489}
993fd081 490
4b12da3a
RK
491function saveTrackDump() {
492 if (gTrack.length) {
8389557a
RK
493 var outDataURI = "data:application/json," +
494 encodeURIComponent(convertTrack("json"));
4b12da3a
RK
495 window.open(outDataURI, 'JSON dump');
496 }
497}
498
8389557a 499function uploadTrack() {
6ddefbf9 500 // Hide all areas in the dialog.
6201b112 501 var dia = document.getElementById("trackDialogArea");
43255174
RK
502 var areas = dia.children;
503 for (var i = 0; i <= areas.length - 1; i++) {
504 areas[i].style.display = "none";
505 }
6ddefbf9
RK
506 // Reset all the fields in the status area.
507 document.getElementById("uploadStatusCloseButton").disabled = true;
508 document.getElementById("uploadInProgress").style.display = "block";
509 document.getElementById("uploadSuccess").style.display = "none";
d0c62ee0 510 document.getElementById("uploadFailed").style.display = "none";
6ddefbf9 511 document.getElementById("uploadError").style.display = "none";
d0c62ee0 512 document.getElementById("uploadErrorMsg").textContent = "";
6ddefbf9 513 // Now show the status area.
43255174
RK
514 document.getElementById("uploadStatus").style.display = "block";
515
6201b112 516 // Assemble field to post to the backend.
8389557a 517 var formData = new FormData();
6201b112 518 formData.append("jsondata", convertTrack("json"));
43255174 519 var desc = document.getElementById("uploadDesc").value;
6201b112 520 formData.append("comment",
43255174 521 desc.length ? desc : "Track recorded via Lantea Maps");
65946832
RK
522 formData.append("devicename",
523 document.getElementById("uploadDevName").value);
6201b112
RK
524 formData.append("public",
525 document.getElementById("uploadPublic").value);
526
527 fetchBackend("save_track", "POST", formData,
528 function(aResult, aStatusCode) {
529 if (aStatusCode >= 400) {
530 reportUploadStatus(false, aResult);
da6dad24 531 }
5e9f4a24 532 else if (aResult["id"]) {
da6dad24
RK
533 reportUploadStatus(true);
534 }
5e9f4a24
RK
535 else { // If no ID is returned, we assume a general error.
536 reportUploadStatus(false);
537 }
da6dad24
RK
538 }
539 );
8389557a
RK
540}
541
5e9f4a24 542function reportUploadStatus(aSuccess, aResponse) {
43255174
RK
543 document.getElementById("uploadStatusCloseButton").disabled = false;
544 document.getElementById("uploadInProgress").style.display = "none";
545 if (aSuccess) {
546 document.getElementById("uploadSuccess").style.display = "block";
547 }
b5291db1 548 else if (aResponse && aResponse["message"]) {
5e9f4a24
RK
549 document.getElementById("uploadErrorMsg").textContent = aResponse["message"];
550 if (aResponse["errortype"]) {
551 document.getElementById("uploadErrorMsg").textContent += " (" + aResponse["errortype"] + ")";
552 }
553 document.getElementById("uploadError").style.display = "block";
554 }
555 else if (aResponse) {
556 document.getElementById("uploadErrorMsg").textContent = aResponse;
43255174
RK
557 document.getElementById("uploadError").style.display = "block";
558 }
559 else {
560 document.getElementById("uploadFailed").style.display = "block";
561 }
8389557a
RK
562}
563
993fd081
RK
564var gPrefs = {
565 objStore: "prefs",
566
567 get: function(aKey, aCallback) {
568 if (!mainDB)
569 return;
570 var transaction = mainDB.transaction([this.objStore]);
571 var request = transaction.objectStore(this.objStore).get(aKey);
572 request.onsuccess = function(event) {
573 aCallback(request.result, event);
574 };
575 request.onerror = function(event) {
576 // Errors can be handled here.
577 aCallback(undefined, event);
578 };
579 },
580
581 set: function(aKey, aValue, aCallback) {
582 if (!mainDB)
583 return;
584 var success = false;
d4ccddb8 585 var transaction = mainDB.transaction([this.objStore], "readwrite");
993fd081 586 var objStore = transaction.objectStore(this.objStore);
3610c22d 587 var request = objStore.put(aValue, aKey);
993fd081
RK
588 request.onsuccess = function(event) {
589 success = true;
590 if (aCallback)
591 aCallback(success, event);
592 };
593 request.onerror = function(event) {
594 // Errors can be handled here.
595 if (aCallback)
596 aCallback(success, event);
597 };
598 },
599
600 unset: function(aKey, aCallback) {
601 if (!mainDB)
602 return;
603 var success = false;
d4ccddb8 604 var transaction = mainDB.transaction([this.objStore], "readwrite");
993fd081
RK
605 var request = transaction.objectStore(this.objStore).delete(aKey);
606 request.onsuccess = function(event) {
607 success = true;
608 if (aCallback)
609 aCallback(success, event);
610 };
611 request.onerror = function(event) {
612 // Errors can be handled here.
613 if (aCallback)
614 aCallback(success, event);
615 }
616 }
617};
618
619var gTrackStore = {
620 objStore: "track",
621
622 getList: function(aCallback) {
623 if (!mainDB)
624 return;
625 var transaction = mainDB.transaction([this.objStore]);
626 var objStore = transaction.objectStore(this.objStore);
627 if (objStore.getAll) { // currently Mozilla-specific
628 objStore.getAll().onsuccess = function(event) {
629 aCallback(event.target.result);
630 };
631 }
632 else { // Use cursor (standard method).
633 var tPoints = [];
634 objStore.openCursor().onsuccess = function(event) {
635 var cursor = event.target.result;
636 if (cursor) {
637 tPoints.push(cursor.value);
638 cursor.continue();
639 }
640 else {
641 aCallback(tPoints);
642 }
643 };
644 }
645 },
646
6ddefbf9
RK
647 getListStepped: function(aCallback) {
648 if (!mainDB)
649 return;
650 var transaction = mainDB.transaction([this.objStore]);
651 var objStore = transaction.objectStore(this.objStore);
652 // Use cursor in reverse direction (so we get the most recent position first)
653 objStore.openCursor(null, "prev").onsuccess = function(event) {
654 var cursor = event.target.result;
655 if (cursor) {
656 aCallback(cursor.value);
657 cursor.continue();
658 }
659 else {
660 aCallback(null);
661 }
662 };
663 },
664
993fd081
RK
665 push: function(aValue, aCallback) {
666 if (!mainDB)
667 return;
d4ccddb8 668 var transaction = mainDB.transaction([this.objStore], "readwrite");
993fd081
RK
669 var objStore = transaction.objectStore(this.objStore);
670 var request = objStore.add(aValue);
671 request.onsuccess = function(event) {
672 if (aCallback)
673 aCallback(request.result, event);
674 };
675 request.onerror = function(event) {
676 // Errors can be handled here.
677 if (aCallback)
678 aCallback(false, event);
679 };
680 },
681
682 clear: function(aCallback) {
683 if (!mainDB)
684 return;
685 var success = false;
d4ccddb8 686 var transaction = mainDB.transaction([this.objStore], "readwrite");
993fd081
RK
687 var request = transaction.objectStore(this.objStore).clear();
688 request.onsuccess = function(event) {
689 success = true;
690 if (aCallback)
691 aCallback(success, event);
692 };
693 request.onerror = function(event) {
694 // Errors can be handled here.
695 if (aCallback)
696 aCallback(success, event);
697 }
698 }
699};
8f9306c5
RK
700
701function fetchBackend(aEndpoint, aMethod, aSendData, aCallback, aCallbackForwards) {
702 var XHR = new XMLHttpRequest();
703 XHR.onreadystatechange = function() {
704 if (XHR.readyState == 4) {
705 // State says we are fully loaded.
706 var result = {};
707 if (XHR.getResponseHeader("Content-Type") == "application/json") {
708 // Got a JSON object, see if we have success.
65946832
RK
709 try {
710 result = JSON.parse(XHR.responseText);
711 }
712 catch (e) {
713 console.log(e);
714 result = {"error": e,
715 "message": XHR.responseText};
716 }
8f9306c5
RK
717 }
718 else {
719 result = XHR.responseText;
720 }
721 aCallback(result, XHR.status, aCallbackForwards);
722 }
723 };
724 XHR.open(aMethod, gBackendURL + aEndpoint, true);
87be1057 725 XHR.withCredentials = "true";
8f9306c5
RK
726 //XHR.setRequestHeader("Accept", "application/json");
727 try {
728 XHR.send(aSendData); // Send actual form data.
729 }
730 catch (e) {
731 aCallback(e, 500, aCallbackForwards);
732 }
733}