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