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/. */
5 // Get the best-available objects for indexedDB and requestAnimationFrame.
6 window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
7 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
10 var gUIHideCountdown = 0;
12 var gAction, gActionLabel;
13 var gOSMAPIURL = "http://api.openstreetmap.org/";
15 window.onload = function() {
16 gAction = document.getElementById("action");
17 gActionLabel = document.getElementById("actionlabel");
19 var mSel = document.getElementById("mapSelector");
20 for (var mapStyle in gMapStyles) {
21 var opt = document.createElement("option");
23 opt.text = gMapStyles[mapStyle].name;
27 var areas = document.getElementsByClassName("overlayArea");
28 for (var i = 0; i <= areas.length - 1; i++) {
29 areas[i].addEventListener("mouseup", uiEvHandler, false);
30 areas[i].addEventListener("mousemove", uiEvHandler, false);
31 areas[i].addEventListener("mousedown", uiEvHandler, false);
32 areas[i].addEventListener("mouseout", uiEvHandler, false);
34 areas[i].addEventListener("touchstart", uiEvHandler, false);
35 areas[i].addEventListener("touchmove", uiEvHandler, false);
36 areas[i].addEventListener("touchend", uiEvHandler, false);
37 areas[i].addEventListener("touchcancel", uiEvHandler, false);
38 areas[i].addEventListener("touchleave", uiEvHandler, false);
41 document.getElementById("body").addEventListener("keydown", uiEvHandler, false);
43 if (navigator.platform.length == "") {
44 // For Firefox OS, don't display the "save" button.
45 // Do this by setting the debugHide class for testing in debug mode.
46 document.getElementById("saveTrackButton").classList.add("debugHide");
49 // Without OAuth, the login data is useless
50 //document.getElementById("uploadSettingsArea").classList.remove("debugHide");
51 // As login data is useless for now, always enable upload button
52 document.getElementById("uploadTrackButton").disabled = false;
55 // Note that GPX upload returns an error 500 on the dev API right now.
56 gOSMAPIURL = "http://api06.dev.openstreetmap.org/";
59 gAction.addEventListener("dbinit-done", initMap, false);
60 gAction.addEventListener("mapinit-done", postInit, false);
61 console.log("starting DB init...");
65 function postInit(aEvent) {
66 gAction.removeEventListener(aEvent.type, postInit, false);
67 console.log("init done, draw map.");
68 gMapPrefsLoaded = true;
70 gActionLabel.textContent = "";
71 gAction.style.display = "none";
72 setTracking(document.getElementById("trackCheckbox"));
73 gPrefs.get(gDebug ? "osm_dev_user" : "osm_user", function(aValue) {
75 document.getElementById("uploadUser").value = aValue;
76 document.getElementById("uploadTrackButton").disabled = false;
79 gPrefs.get(gDebug ? "osm_dev_pwd" : "osm_pwd", function(aValue) {
80 var upwd = document.getElementById("uploadPwd");
82 document.getElementById("uploadPwd").value = aValue;
86 window.onresize = function() {
90 function initDB(aEvent) {
93 gAction.removeEventListener(aEvent.type, initDB, false);
94 var request = window.indexedDB.open("MainDB-lantea", 2);
95 request.onerror = function(event) {
96 // Errors can be handled here. Error codes explain in:
97 // https://developer.mozilla.org/en/IndexedDB/IDBDatabaseException#Constants
99 console.log("error opening mainDB: " + event.target.errorCode);
101 request.onsuccess = function(event) {
102 mainDB = request.result;
103 var throwEv = new CustomEvent("dbinit-done");
104 gAction.dispatchEvent(throwEv);
106 request.onupgradeneeded = function(event) {
107 mainDB = request.result;
108 var ver = mainDB.version || 0; // version is empty string for a new DB
110 console.log("mainDB has version " + ver + ", upgrade needed.");
111 if (!mainDB.objectStoreNames.contains("prefs")) {
112 // Create a "prefs" objectStore.
113 var prefsStore = mainDB.createObjectStore("prefs");
115 if (!mainDB.objectStoreNames.contains("track")) {
116 // Create a "track" objectStore.
117 var trackStore = mainDB.createObjectStore("track", {autoIncrement: true});
119 if (!mainDB.objectStoreNames.contains("tilecache")) {
120 // Create a "tilecache" objectStore.
121 var tilecacheStore = mainDB.createObjectStore("tilecache");
123 mainDB.onversionchange = function(event) {
132 if (gUIHideCountdown <= 0) {
133 var areas = document.getElementsByClassName('overlayArea');
134 for (var i = 0; i <= areas.length - 1; i++) {
135 areas[i].classList.remove("hidden");
137 setTimeout(maybeHideUI, 1000);
139 gUIHideCountdown = 5;
142 function maybeHideUI() {
144 if (gUIHideCountdown <= 0) {
145 var areas = document.getElementsByClassName('overlayArea');
146 for (var i = 0; i <= areas.length - 1; i++) {
147 areas[i].classList.add("hidden");
151 setTimeout(maybeHideUI, 1000);
155 function toggleTrackArea() {
156 var fs = document.getElementById("trackArea");
157 if (fs.style.display != "block") {
158 fs.style.display = "block";
162 fs.style.display = "none";
166 function toggleSettings() {
167 var fs = document.getElementById("settingsArea");
168 if (fs.style.display != "block") {
169 fs.style.display = "block";
173 fs.style.display = "none";
177 function toggleFullscreen() {
178 if ((document.fullScreenElement && document.fullScreenElement !== null) ||
179 (document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
180 (document.webkitFullScreenElement && document.webkitFullScreenElement !== null)) {
181 if (document.cancelFullScreen) {
182 document.cancelFullScreen();
183 } else if (document.mozCancelFullScreen) {
184 document.mozCancelFullScreen();
185 } else if (document.webkitCancelFullScreen) {
186 document.webkitCancelFullScreen();
190 var elem = document.getElementById("body");
191 if (elem.requestFullScreen) {
192 elem.requestFullScreen();
193 } else if (elem.mozRequestFullScreen) {
194 elem.mozRequestFullScreen();
195 } else if (elem.webkitRequestFullScreen) {
196 elem.webkitRequestFullScreen();
201 function showUploadDialog() {
202 var dia = document.getElementById("dialogArea");
203 var areas = dia.children;
204 for (var i = 0; i <= areas.length - 1; i++) {
205 areas[i].style.display = "none";
207 document.getElementById("uploadDialog").style.display = "block";
208 document.getElementById("uploadTrackButton").disabled = true;
209 dia.classList.remove("hidden");
212 function showGLWarningDialog() {
213 var dia = document.getElementById("dialogArea");
214 var areas = dia.children;
215 for (var i = 0; i <= areas.length - 1; i++) {
216 areas[i].style.display = "none";
218 document.getElementById("noGLwarning").style.display = "block";
219 dia.classList.remove("hidden");
222 function cancelDialog() {
223 document.getElementById("dialogArea").classList.add("hidden");
224 document.getElementById("uploadTrackButton").disabled = false;
228 handleEvent: function(aEvent) {
229 var touchEvent = aEvent.type.indexOf('touch') != -1;
231 switch (aEvent.type) {
245 function setUploadField(aField) {
248 gPrefs.set(gDebug ? "osm_dev_user" : "osm_user", aField.value);
249 document.getElementById("uploadTrackButton").disabled = !aField.value.length;
252 gPrefs.set(gDebug ? "osm_dev_pwd" : "osm_pwd", aField.value);
257 function makeISOString(aTimestamp) {
258 // ISO time format is YYYY-MM-DDTHH:mm:ssZ
259 var tsDate = new Date(aTimestamp);
260 // Note that .getUTCMonth() returns a number between 0 and 11 (0 for January)!
261 return tsDate.getUTCFullYear() + "-" +
262 (tsDate.getUTCMonth() < 9 ? "0" : "") + (tsDate.getUTCMonth() + 1 ) + "-" +
263 (tsDate.getUTCDate() < 10 ? "0" : "") + tsDate.getUTCDate() + "T" +
264 (tsDate.getUTCHours() < 10 ? "0" : "") + tsDate.getUTCHours() + ":" +
265 (tsDate.getUTCMinutes() < 10 ? "0" : "") + tsDate.getUTCMinutes() + ":" +
266 (tsDate.getUTCSeconds() < 10 ? "0" : "") + tsDate.getUTCSeconds() + "Z";
269 function convertTrack(aTargetFormat) {
271 switch (aTargetFormat) {
273 out += '<?xml version="1.0" encoding="UTF-8" ?>' + "\n\n";
274 out += '<gpx version="1.0" creator="Lantea" xmlns="http://www.topografix.com/GPX/1/0">' + "\n";
276 out += ' <trk>' + "\n";
277 out += ' <trkseg>' + "\n";
278 for (var i = 0; i < gTrack.length; i++) {
279 if (gTrack[i].beginSegment && i > 0) {
280 out += ' </trkseg>' + "\n";
281 out += ' <trkseg>' + "\n";
283 out += ' <trkpt lat="' + gTrack[i].coords.latitude + '" lon="' +
284 gTrack[i].coords.longitude + '">' + "\n";
285 if (gTrack[i].coords.altitude) {
286 out += ' <ele>' + gTrack[i].coords.altitude + '</ele>' + "\n";
288 out += ' <time>' + makeISOString(gTrack[i].time) + '</time>' + "\n";
289 out += ' </trkpt>' + "\n";
291 out += ' </trkseg>' + "\n";
292 out += ' </trk>' + "\n";
294 out += '</gpx>' + "\n";
297 out = JSON.stringify(gTrack);
305 function saveTrack() {
307 var outDataURI = "data:application/gpx+xml," +
308 encodeURIComponent(convertTrack("gpx"));
309 window.open(outDataURI, 'GPX Track');
313 function saveTrackDump() {
315 var outDataURI = "data:application/json," +
316 encodeURIComponent(convertTrack("json"));
317 window.open(outDataURI, 'JSON dump');
321 function uploadTrack() {
322 // Hide all areas in the dialog.
323 var dia = document.getElementById("dialogArea");
324 var areas = dia.children;
325 for (var i = 0; i <= areas.length - 1; i++) {
326 areas[i].style.display = "none";
328 // Reset all the fields in the status area.
329 document.getElementById("uploadStatusCloseButton").disabled = true;
330 document.getElementById("uploadInProgress").style.display = "block";
331 document.getElementById("uploadSuccess").style.display = "none";
332 document.getElementById("uploadFailed").style.display = "none";
333 document.getElementById("uploadError").style.display = "none";
334 document.getElementById("uploadErrorMsg").textContent = "";
335 // Now show the status area.
336 document.getElementById("uploadStatus").style.display = "block";
338 // See http://wiki.openstreetmap.org/wiki/Api06#Uploading_traces
339 var trackBlob = new Blob([convertTrack("gpx")],
340 { "type" : "application/gpx+xml" });
341 var formData = new FormData();
342 formData.append("file", trackBlob);
343 var desc = document.getElementById("uploadDesc").value;
344 formData.append("description",
345 desc.length ? desc : "Track recorded via Lantea Maps");
346 //formData.append("tags", "");
347 formData.append("visibility",
348 document.getElementById("uploadVisibility").value);
349 // Do an empty POST request first, so that we don't send everything,
350 // then ask for credentials, and then send again.
351 var hXHR = new XMLHttpRequest();
352 hXHR.onreadystatechange = function() {
353 if (hXHR.readyState == 4 && (hXHR.status == 200 || hXHR.status == 400)) {
354 // 400 is Bad Request, but that's expected as this was empty.
355 // So far so good, init actual upload.
356 var XHR = new XMLHttpRequest();
357 XHR.onreadystatechange = function() {
358 if (XHR.readyState == 4 && XHR.status == 200) {
359 // Everthing looks fine.
360 reportUploadStatus(true);
361 } else if (XHR.readyState == 4 && XHR.status != 200) {
362 // Fetched the wrong page or network error...
363 reportUploadStatus(false);
366 XHR.open("POST", gOSMAPIURL + "api/0.6/gpx/create", true);
367 // Cross-Origin XHR doesn't allow username/password (HTTP Auth).
368 // So, we'll ask the user for entering credentials with rather ugly UI.
369 XHR.withCredentials = true;
371 XHR.send(formData); // Send actual form data.
374 reportUploadStatus(false, e);
376 } else if (hXHR.readyState == 4 && hXHR.status != 200) {
377 // Fetched the wrong page or network error...
378 reportUploadStatus(false);
381 hXHR.open("POST", gOSMAPIURL + "api/0.6/gpx/create", true);
382 // Cross-Origin XHR doesn't allow username/password (HTTP Auth).
383 // So, we'll ask the user for entering credentials with rather ugly UI.
384 hXHR.withCredentials = true;
386 hXHR.send(); // Empty request, see above.
389 reportUploadStatus(false, e);
393 function reportUploadStatus(aSuccess, aMessage) {
394 document.getElementById("uploadStatusCloseButton").disabled = false;
395 document.getElementById("uploadInProgress").style.display = "none";
397 document.getElementById("uploadSuccess").style.display = "block";
400 document.getElementById("uploadErrorMsg").textContent = aMessage;
401 document.getElementById("uploadError").style.display = "block";
404 document.getElementById("uploadFailed").style.display = "block";
411 get: function(aKey, aCallback) {
414 var transaction = mainDB.transaction([this.objStore]);
415 var request = transaction.objectStore(this.objStore).get(aKey);
416 request.onsuccess = function(event) {
417 aCallback(request.result, event);
419 request.onerror = function(event) {
420 // Errors can be handled here.
421 aCallback(undefined, event);
425 set: function(aKey, aValue, aCallback) {
429 var transaction = mainDB.transaction([this.objStore], "readwrite");
430 var objStore = transaction.objectStore(this.objStore);
431 var request = objStore.put(aValue, aKey);
432 request.onsuccess = function(event) {
435 aCallback(success, event);
437 request.onerror = function(event) {
438 // Errors can be handled here.
440 aCallback(success, event);
444 unset: function(aKey, aCallback) {
448 var transaction = mainDB.transaction([this.objStore], "readwrite");
449 var request = transaction.objectStore(this.objStore).delete(aKey);
450 request.onsuccess = function(event) {
453 aCallback(success, event);
455 request.onerror = function(event) {
456 // Errors can be handled here.
458 aCallback(success, event);
466 getList: function(aCallback) {
469 var transaction = mainDB.transaction([this.objStore]);
470 var objStore = transaction.objectStore(this.objStore);
471 if (objStore.getAll) { // currently Mozilla-specific
472 objStore.getAll().onsuccess = function(event) {
473 aCallback(event.target.result);
476 else { // Use cursor (standard method).
478 objStore.openCursor().onsuccess = function(event) {
479 var cursor = event.target.result;
481 tPoints.push(cursor.value);
491 getListStepped: function(aCallback) {
494 var transaction = mainDB.transaction([this.objStore]);
495 var objStore = transaction.objectStore(this.objStore);
496 // Use cursor in reverse direction (so we get the most recent position first)
497 objStore.openCursor(null, "prev").onsuccess = function(event) {
498 var cursor = event.target.result;
500 aCallback(cursor.value);
509 push: function(aValue, aCallback) {
512 var transaction = mainDB.transaction([this.objStore], "readwrite");
513 var objStore = transaction.objectStore(this.objStore);
514 var request = objStore.add(aValue);
515 request.onsuccess = function(event) {
517 aCallback(request.result, event);
519 request.onerror = function(event) {
520 // Errors can be handled here.
522 aCallback(false, event);
526 clear: function(aCallback) {
530 var transaction = mainDB.transaction([this.objStore], "readwrite");
531 var request = transaction.objectStore(this.objStore).clear();
532 request.onsuccess = function(event) {
535 aCallback(success, event);
537 request.onerror = function(event) {
538 // Errors can be handled here.
540 aCallback(success, event);