|
|
@@ -1,138 +1,221 @@
|
|
|
"use strict";
|
|
|
|
|
|
-let p = undefined;
|
|
|
+let puzzeltocht = undefined;
|
|
|
window.addEventListener("DOMContentLoaded", () => {
|
|
|
- p = new Puzzeltocht();
|
|
|
+ puzzeltocht = new Puzzeltocht();
|
|
|
});
|
|
|
|
|
|
class Puzzeltocht {
|
|
|
|
|
|
constructor() {
|
|
|
- if ("geolocation" in navigator) {
|
|
|
- console.log("Geolocation API available");
|
|
|
- } else {
|
|
|
- console.log("Geolocation API not available");
|
|
|
- document.getElementById("unsupported").style.display = "block";
|
|
|
+ this.joinScreen = new JoinScreen();
|
|
|
+ this.location = new LocationApi();
|
|
|
+ console.log("puzzeltocht initialised");
|
|
|
+ }
|
|
|
+
|
|
|
+ startEvent(eventId, teamId) {
|
|
|
+ this.eventId = this.joinScreen.eventId;
|
|
|
+ this.teamId = teamId;
|
|
|
+ document.getElementById("join").style.display = "none";
|
|
|
+ document.getElementById("mission").style.display = "block";
|
|
|
+ document.getElementById("questionMission").style.display = "none";
|
|
|
+ this.location.enable(p => this.updateLocation(p))
|
|
|
+ }
|
|
|
+
|
|
|
+ updateLocation(position) {
|
|
|
+ let l = document.getElementById("location");
|
|
|
+ if (position.accuracy() > 20) {
|
|
|
+ l.innerText = "Onbetrouwbare locatie: " + position.string();
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (window.isSecureContext) {
|
|
|
- console.log("Secure context available");
|
|
|
- } else {
|
|
|
- console.log("Secure context not available");
|
|
|
- document.getElementById("unsupported").style.display = "block";
|
|
|
+ l.innerText = position.string();
|
|
|
+
|
|
|
+ if (this.lastUpdate === undefined || Date.now() - this.lastUpdate > 5000) {
|
|
|
+ Api.sendLocation(this.eventId, this.teamId, position)
|
|
|
+ .then(m => this.updateMission(m))
|
|
|
+ .catch(e => this.onError(e));
|
|
|
+ this.lastUpdate = Date.now();
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- Puzzeltocht.fetchEvents();
|
|
|
- document.getElementById("joinForm").addEventListener("submit", (e) => this.joinEvent(e));
|
|
|
- console.log("puzzeltocht initialised");
|
|
|
+ updateMission(m) {
|
|
|
+ this.mission = m;
|
|
|
+ document.getElementById("missionTitle").innerText = m.title + " (" + m.distanceToTarget + "m)";
|
|
|
+ document.getElementById("missionDescription").innerText = m.description;
|
|
|
}
|
|
|
|
|
|
- static fetchEvents() {
|
|
|
- fetch("/api/event", {method: "GET"})
|
|
|
- .then(r => r.json())
|
|
|
- .then(j => Puzzeltocht.showEvents(j))
|
|
|
- .catch(r => console.log(r));
|
|
|
+ onError(e) {
|
|
|
+ this.location.disable();
|
|
|
+ let l = document.getElementById("location");
|
|
|
+ l.innerText = "Error!";
|
|
|
+ console.log(e);
|
|
|
}
|
|
|
|
|
|
- static showEvents(eventJson) {
|
|
|
- let events = document.getElementById("events");
|
|
|
- events.innerHTML = "";
|
|
|
- eventJson.forEach((e) => {
|
|
|
- events.innerHTML += '<label><input name="eventId" value="' + e.id + '" type="radio">' + e.title + '</label>';
|
|
|
- });
|
|
|
- document.getElementById("joinButton").disabled = false;
|
|
|
+}
|
|
|
+
|
|
|
+class JoinScreen {
|
|
|
+
|
|
|
+ constructor() {
|
|
|
+ Api.fetchEvents()
|
|
|
+ .then(JoinScreen.showEvents)
|
|
|
+ .catch(console.log);
|
|
|
+ document.getElementById("joinForm").addEventListener("submit", (e) => this.joinEvent(e));
|
|
|
}
|
|
|
|
|
|
- joinEvent(e) {
|
|
|
- e.preventDefault();
|
|
|
+ joinEvent(submitEvent) {
|
|
|
+ submitEvent.preventDefault();
|
|
|
let form = document.getElementById("joinForm");
|
|
|
this.eventId = form.elements["eventId"].value;
|
|
|
- this.teamNaam = document.getElementById("teamName").value;
|
|
|
+ let teamName = form.elements["teamName"].value;
|
|
|
|
|
|
- if (this.eventId === "" || this.teamNaam === "") {
|
|
|
- console.log("niet gevuld");
|
|
|
+ if (this.eventId === "" || teamName === "") {
|
|
|
+ console.log("required form field empty");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
document.getElementById("joinButton").disabled = true;
|
|
|
|
|
|
- fetch("/api/event/" + encodeURIComponent(this.eventId) + "/team", {
|
|
|
- method: "POST",
|
|
|
- headers: {"Content-Type": "application/json"},
|
|
|
- body: JSON.stringify(this.teamNaam)
|
|
|
- })
|
|
|
- .then(r => r.json())
|
|
|
- .then(j => p.startEvent(j))
|
|
|
+ Api.join(this.eventId, teamName)
|
|
|
+ .then(teamId => puzzeltocht.startEvent(this.eventId, teamId))
|
|
|
.catch(r => {
|
|
|
console.log(r);
|
|
|
document.getElementById("joinButton").disabled = false;
|
|
|
});
|
|
|
-
|
|
|
- return false;
|
|
|
}
|
|
|
|
|
|
- startEvent(teamId) {
|
|
|
- this.teamId = teamId;
|
|
|
- this.mission = null;
|
|
|
- document.getElementById("join").style.display = "none";
|
|
|
- document.getElementById("mission").style.display = "block";
|
|
|
- document.getElementById("questionMission").style.display = "none";
|
|
|
- this.enableLocation()
|
|
|
+ static showEvents(eventJson) {
|
|
|
+ let events = document.getElementById("events");
|
|
|
+ events.innerHTML = "";
|
|
|
+ eventJson.forEach((e) => {
|
|
|
+ events.innerHTML += '<label><input name="eventId" value="' + e.id + '" type="radio">' + e.title + '</label>';
|
|
|
+ });
|
|
|
+ document.getElementById("joinButton").disabled = false;
|
|
|
}
|
|
|
|
|
|
- updateLocation(pos) {
|
|
|
- let l = document.getElementById("location");
|
|
|
- let ts = Date.now() - pos.timestamp;
|
|
|
- l.innerText = "Locatie (" + Puzzeltocht.lat(pos) + ", " + Puzzeltocht.lon(pos) + ") "
|
|
|
- + "(+- " + pos.coords.accuracy.toFixed(1) + "m, bijgewerkt " + ts + "s geleden)";
|
|
|
+}
|
|
|
|
|
|
- if (this.lastUpdate === undefined || Date.now() - this.lastUpdate > 5000) {
|
|
|
- this.sendLocation(pos);
|
|
|
- this.lastUpdate = Date.now();
|
|
|
- }
|
|
|
+class Api {
|
|
|
+
|
|
|
+ static fetchEvents() {
|
|
|
+ return FetchJson.get("/api/event")
|
|
|
+ }
|
|
|
+
|
|
|
+ static join(eventId, teamName) {
|
|
|
+ let url = "/api/event/" + encodeURIComponent(eventId) + "/team";
|
|
|
+ return FetchJson.post(url, teamName);
|
|
|
}
|
|
|
|
|
|
- sendLocation(pos) {
|
|
|
- let r = {
|
|
|
- missionId: this.mission == null ? null : this.mission.id,
|
|
|
- location: {latitude: Puzzeltocht.lat(pos), longitude: Puzzeltocht.lon(pos)},
|
|
|
+ static sendLocation(eventId, teamId, position) {
|
|
|
+ let body = {
|
|
|
+ location: {latitude: position.lat(), longitude: position.lon()},
|
|
|
answer: null,
|
|
|
};
|
|
|
- fetch("/api/event/" + encodeURIComponent(this.eventId) + "/team/" + encodeURIComponent(this.teamId), {
|
|
|
+ let url = "/api/event/" + encodeURIComponent(eventId) + "/team/" + encodeURIComponent(teamId);
|
|
|
+ return FetchJson.put(url, body);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+class FetchJson {
|
|
|
+
|
|
|
+ static get(url) {
|
|
|
+ return fetch(url, {method: "GET"})
|
|
|
+ .then(FetchJson.responseBody)
|
|
|
+ }
|
|
|
+
|
|
|
+ static post(url, body) {
|
|
|
+ let options = {
|
|
|
+ method: "POST",
|
|
|
+ headers: {"Content-Type": "application/json"},
|
|
|
+ body: JSON.stringify(body)
|
|
|
+ };
|
|
|
+ return fetch(url, options)
|
|
|
+ .then(FetchJson.responseBody)
|
|
|
+ }
|
|
|
+
|
|
|
+ static put(url, body) {
|
|
|
+ let options = {
|
|
|
method: "PUT",
|
|
|
headers: {"Content-Type": "application/json"},
|
|
|
- body: JSON.stringify(r)
|
|
|
- })
|
|
|
- .then(r => r.json())
|
|
|
- .then(j => p.updateMission(j))
|
|
|
- .catch(r => console.log(r));
|
|
|
+ body: JSON.stringify(body)
|
|
|
+ };
|
|
|
+ return fetch(url, options)
|
|
|
+ .then(FetchJson.responseBody)
|
|
|
}
|
|
|
|
|
|
- updateMission(m) {
|
|
|
- this.mission = m;
|
|
|
- document.getElementById("missionTitle").innerText = m.title + " (" + m.distanceToTarget + "m)";
|
|
|
- document.getElementById("missionDescription").innerText = m.description;
|
|
|
+ static responseBody(r) {
|
|
|
+ if (!r.ok) {
|
|
|
+ throw Error("HTTP " + r.status + " " + r.statusText + " " + r.url);
|
|
|
+ }
|
|
|
+ return r.json();
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+class Location {
|
|
|
+
|
|
|
+ constructor(pos) {
|
|
|
+ this.pos = pos;
|
|
|
+ }
|
|
|
+
|
|
|
+ accuracy() {
|
|
|
+ return this.pos.coords.accuracy.toFixed(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ age() {
|
|
|
+ return Date.now() - this.pos.timestamp;
|
|
|
}
|
|
|
|
|
|
- enableLocation() {
|
|
|
+ lat() {
|
|
|
+ return this.pos.coords.latitude.toFixed(5);
|
|
|
+ }
|
|
|
+
|
|
|
+ lon() {
|
|
|
+ return this.pos.coords.longitude.toFixed(5);
|
|
|
+ }
|
|
|
+
|
|
|
+ string() {
|
|
|
+ return "(" + this.lat() + ", " + this.lon() + ")"
|
|
|
+ + " +-" + this.accuracy() + "m, " + this.age() + "s geleden"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class LocationApi {
|
|
|
+
|
|
|
+ constructor() {
|
|
|
+ if ("geolocation" in navigator) {
|
|
|
+ console.log("Geolocation API available");
|
|
|
+ } else {
|
|
|
+ console.log("Geolocation API not available");
|
|
|
+ document.getElementById("unsupported").style.display = "block";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (window.isSecureContext) {
|
|
|
+ console.log("Secure context available");
|
|
|
+ } else {
|
|
|
+ console.log("Secure context not available");
|
|
|
+ document.getElementById("unsupported").style.display = "block";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ disable() {
|
|
|
+ console.log("disabling geolocation watch " + this.watchId);
|
|
|
+ navigator.geolocation.clearWatch(this.watchId);
|
|
|
+ }
|
|
|
+
|
|
|
+ enable(callback) {
|
|
|
const geoOpts = {
|
|
|
enableHighAccuracy: true,
|
|
|
maximumAge: 10000,
|
|
|
timeout: 9500
|
|
|
};
|
|
|
|
|
|
- navigator.geolocation.watchPosition(
|
|
|
- (pos) => p.updateLocation(pos),
|
|
|
+ this.watchId = navigator.geolocation.watchPosition(
|
|
|
+ (pos) => callback(new Location(pos)),
|
|
|
(err) => console.log(err),
|
|
|
geoOpts);
|
|
|
}
|
|
|
|
|
|
- static lat(pos) {
|
|
|
- return pos.coords.latitude.toFixed(5);
|
|
|
- }
|
|
|
-
|
|
|
- static lon(pos) {
|
|
|
- return pos.coords.longitude.toFixed(5);
|
|
|
- }
|
|
|
-
|
|
|
}
|