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