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