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