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