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