f2501cf507501a2fb5566c3b6402d8257aaf755a
[lantea.git] / js / ui.js
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/. */
4
5 // Get the best-available indexedDB object.
6 window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
7 var mainDB;
8
9 var gUIHideCountdown = 0;
10 var gWaitCounter = 0;
11
12 window.onload = function() {
13   var mSel = document.getElementById("mapSelector");
14   for (var mapStyle in gMapStyles) {
15     var opt = document.createElement("option");
16     opt.value = mapStyle;
17     opt.text = gMapStyles[mapStyle].name;
18     mSel.add(opt, null);
19   }
20
21   var areas = document.getElementsByClassName('overlayArea');
22   for (var i = 0; i <= areas.length - 1; i++) {
23     areas[i].addEventListener("mouseup", uiEvHandler, false);
24     areas[i].addEventListener("mousemove", uiEvHandler, false);
25     areas[i].addEventListener("mousedown", uiEvHandler, false);
26     areas[i].addEventListener("mouseout", uiEvHandler, false);
27
28     areas[i].addEventListener("touchstart", uiEvHandler, false);
29     areas[i].addEventListener("touchmove", uiEvHandler, false);
30     areas[i].addEventListener("touchend", uiEvHandler, false);
31     areas[i].addEventListener("touchcancel", uiEvHandler, false);
32     areas[i].addEventListener("touchleave", uiEvHandler, false);
33   }
34
35   if (navigator.platform.length == "") {
36     // For Firefox OS, don't display the "save" button.
37     // Do this by setting the debugHide class for testing in debug mode.
38     document.getElementById("saveTrackButton").classList.add("debugHide");
39   }
40
41   initDB();
42   initMap();
43
44   var loopCnt = 0;
45   var waitForInitAndDraw = function() {
46     if ((gWaitCounter <= 0) || (loopCnt > 100)) {
47       if (gWaitCounter <= 0)
48         gWaitCounter = 0;
49       else
50         document.getElementById("debug").textContent = "Loading failed (waiting for init).";
51
52       gMapPrefsLoaded = true;
53       resizeAndDraw();
54       setTracking(document.getElementById("trackCheckbox"));
55     }
56     else
57       setTimeout(waitForInitAndDraw, 100);
58     loopCnt++;
59   };
60   waitForInitAndDraw();
61 }
62
63 window.onresize = function() {
64   resizeAndDraw();
65 }
66
67 function initDB() {
68   // Open DB.
69   var request = window.indexedDB.open("MainDB", 4);
70   request.onerror = function(event) {
71     // Errors can be handled here. Error codes explain in:
72     // https://developer.mozilla.org/en/IndexedDB/IDBDatabaseException#Constants
73     //document.getElementById("debug").textContent =
74     //  "error opening mainDB: " + event.target.errorCode;
75   };
76   request.onsuccess = function(event) {
77     //document.getElementById("debug").textContent = "mainDB opened.";
78     mainDB = request.result;
79   };
80   request.onupgradeneeded = function(event) {
81     mainDB = request.result;
82     //document.getElementById("debug").textContent = "mainDB upgraded.";
83     var ver = mainDB.version || 0; // version is empty string for a new DB
84     if (ver <= 1) {
85       // Create a "prefs" objectStore.
86       var prefsStore = mainDB.createObjectStore("prefs");
87       // Create a "track" objectStore.
88       var trackStore = mainDB.createObjectStore("track", {autoIncrement: true});
89     }
90     if (ver <= 4) {
91       // Create a "tilecache" objectStore.
92       var tilecacheStore = mainDB.createObjectStore("tilecache");
93     }
94     mainDB.onversionchange = function(event) {
95       mainDB.close();
96       mainDB = undefined;
97       initDB();
98     };
99   };
100 }
101
102 function showUI() {
103   if (gUIHideCountdown <= 0) {
104     var areas = document.getElementsByClassName('overlayArea');
105     for (var i = 0; i <= areas.length - 1; i++) {
106       areas[i].classList.remove("hidden");
107     }
108     setTimeout(maybeHideUI, 1000);
109   }
110   gUIHideCountdown = 5;
111 }
112
113 function maybeHideUI() {
114   gUIHideCountdown--;
115   if (gUIHideCountdown <= 0) {
116     var areas = document.getElementsByClassName('overlayArea');
117     for (var i = 0; i <= areas.length - 1; i++) {
118       areas[i].classList.add("hidden");
119     }
120   }
121   else {
122     setTimeout(maybeHideUI, 1000);
123   }
124 }
125
126 function toggleTrackArea() {
127   var fs = document.getElementById("trackArea");
128   if (fs.style.display != "block") {
129     fs.style.display = "block";
130     showUI();
131   }
132   else {
133     fs.style.display = "none";
134   }
135 }
136
137 function toggleSettings() {
138   var fs = document.getElementById("settingsArea");
139   if (fs.style.display != "block") {
140     fs.style.display = "block";
141     showUI();
142   }
143   else {
144     fs.style.display = "none";
145   }
146 }
147
148 function toggleFullscreen() {
149   if ((document.fullScreenElement && document.fullScreenElement !== null) ||
150       (document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
151       (document.webkitFullScreenElement && document.webkitFullScreenElement !== null)) {
152     if (document.cancelFullScreen) {
153       document.cancelFullScreen();
154     } else if (document.mozCancelFullScreen) {
155       document.mozCancelFullScreen();
156     } else if (document.webkitCancelFullScreen) {
157       document.webkitCancelFullScreen();
158     }
159   }
160   else {
161     var elem = document.getElementById("body");
162     if (elem.requestFullScreen) {
163       elem.requestFullScreen();
164     } else if (elem.mozRequestFullScreen) {
165       elem.mozRequestFullScreen();
166     } else if (elem.webkitRequestFullScreen) {
167       elem.webkitRequestFullScreen();
168     }
169   }
170 }
171
172 var uiEvHandler = {
173   handleEvent: function(aEvent) {
174     var touchEvent = aEvent.type.indexOf('touch') != -1;
175
176     switch (aEvent.type) {
177       case "mousedown":
178       case "touchstart":
179       case "mousemove":
180       case "touchmove":
181       case "mouseup":
182       case "touchend":
183         showUI();
184         break;
185     }
186   }
187 };
188
189 function makeISOString(aTimestamp) {
190   // ISO time format is YYYY-MM-DDTHH:mm:ssZ
191   var tsDate = new Date(aTimestamp);
192   return tsDate.getUTCFullYear() + "-" +
193          (tsDate.getUTCMonth() < 10 ? "0" : "") + tsDate.getUTCMonth() + "-" +
194          (tsDate.getUTCDate() < 10 ? "0" : "") + tsDate.getUTCDate() + "T" +
195          (tsDate.getUTCHours() < 10 ? "0" : "") + tsDate.getUTCHours() + ":" +
196          (tsDate.getUTCMinutes() < 10 ? "0" : "") + tsDate.getUTCMinutes() + ":" +
197          (tsDate.getUTCSeconds() < 10 ? "0" : "") + tsDate.getUTCSeconds() + "Z";
198 }
199
200 function saveTrack() {
201   if (gTrack.length) {
202     var out = '<?xml version="1.0" encoding="UTF-8" ?>' + "\n\n";
203     out += '<gpx version="1.0" creator="Lantea" xmlns="http://www.topografix.com/GPX/1/0">' + "\n";
204     out += '  <trk>' + "\n";
205     out += '    <trkseg>' + "\n";
206     for (var i = 0; i < gTrack.length; i++) {
207       if (gTrack[i].beginSegment && i > 0) {
208         out += '    </trkseg>' + "\n";
209         out += '    <trkseg>' + "\n";
210       }
211       out += '      <trkpt lat="' + gTrack[i].coords.latitude + '" lon="' +
212                                     gTrack[i].coords.longitude + '">' + "\n";
213       if (gTrack[i].coords.altitude) {
214         out += '        <ele>' + gTrack[i].coords.altitude + '</ele>' + "\n";
215       }
216       out += '        <time>' + makeISOString(gTrack[i].time) + '</time>' + "\n";
217       out += '      </trkpt>' + "\n";
218     }
219     out += '    </trkseg>' + "\n";
220     out += '  </trk>' + "\n";
221     out += '</gpx>' + "\n";
222     var outDataURI = "data:application/gpx+xml," + encodeURIComponent(out);
223     window.open(outDataURI, 'GPX Track');
224   }
225 }
226
227 function saveTrackDump() {
228   if (gTrack.length) {
229     var out = JSON.stringify(gTrack);
230     var outDataURI = "data:application/json," + encodeURIComponent(out);
231     window.open(outDataURI, 'JSON dump');
232   }
233 }
234
235 var gPrefs = {
236   objStore: "prefs",
237
238   get: function(aKey, aCallback) {
239     if (!mainDB)
240       return;
241     var transaction = mainDB.transaction([this.objStore]);
242     var request = transaction.objectStore(this.objStore).get(aKey);
243     request.onsuccess = function(event) {
244       aCallback(request.result, event);
245     };
246     request.onerror = function(event) {
247       // Errors can be handled here.
248       aCallback(undefined, event);
249     };
250   },
251
252   set: function(aKey, aValue, aCallback) {
253     if (!mainDB)
254       return;
255     var success = false;
256     var transaction = mainDB.transaction([this.objStore], "readwrite");
257     var objStore = transaction.objectStore(this.objStore);
258     var request = objStore.put(aValue, aKey);
259     request.onsuccess = function(event) {
260       success = true;
261       if (aCallback)
262         aCallback(success, event);
263     };
264     request.onerror = function(event) {
265       // Errors can be handled here.
266       if (aCallback)
267         aCallback(success, event);
268     };
269   },
270
271   unset: function(aKey, aCallback) {
272     if (!mainDB)
273       return;
274     var success = false;
275     var transaction = mainDB.transaction([this.objStore], "readwrite");
276     var request = transaction.objectStore(this.objStore).delete(aKey);
277     request.onsuccess = function(event) {
278       success = true;
279       if (aCallback)
280         aCallback(success, event);
281     };
282     request.onerror = function(event) {
283       // Errors can be handled here.
284       if (aCallback)
285         aCallback(success, event);
286     }
287   }
288 };
289
290 var gTrackStore = {
291   objStore: "track",
292
293   getList: function(aCallback) {
294     if (!mainDB)
295       return;
296     var transaction = mainDB.transaction([this.objStore]);
297     var objStore = transaction.objectStore(this.objStore);
298     if (objStore.getAll) { // currently Mozilla-specific
299       objStore.getAll().onsuccess = function(event) {
300         aCallback(event.target.result);
301       };
302     }
303     else { // Use cursor (standard method).
304       var tPoints = [];
305       objStore.openCursor().onsuccess = function(event) {
306         var cursor = event.target.result;
307         if (cursor) {
308           tPoints.push(cursor.value);
309           cursor.continue();
310         }
311         else {
312           aCallback(tPoints);
313         }
314       };
315     }
316   },
317
318   push: function(aValue, aCallback) {
319     if (!mainDB)
320       return;
321     var transaction = mainDB.transaction([this.objStore], "readwrite");
322     var objStore = transaction.objectStore(this.objStore);
323     var request = objStore.add(aValue);
324     request.onsuccess = function(event) {
325       if (aCallback)
326         aCallback(request.result, event);
327     };
328     request.onerror = function(event) {
329       // Errors can be handled here.
330       if (aCallback)
331         aCallback(false, event);
332     };
333   },
334
335   clear: function(aCallback) {
336     if (!mainDB)
337       return;
338     var success = false;
339     var transaction = mainDB.transaction([this.objStore], "readwrite");
340     var request = transaction.objectStore(this.objStore).clear();
341     request.onsuccess = function(event) {
342       success = true;
343       if (aCallback)
344         aCallback(success, event);
345     };
346     request.onerror = function(event) {
347       // Errors can be handled here.
348       if (aCallback)
349         aCallback(success, event);
350     }
351   }
352 };