Naprawa lokalizacji - odczytu

This commit is contained in:
2026-06-10 20:31:48 +02:00
parent 04aaa6e321
commit 79a83d4d73
16 changed files with 1826 additions and 477 deletions

View File

@@ -7,6 +7,9 @@ window.kitchenAnimations = [
];
window.selectedAnimationHtml = null;
const MENU_ASSET_VERSION =
window.MENU_ASSET_VERSION || window.APP_ASSET_VERSION || "1";
const params = new URLSearchParams(location.search);
let hashParam = (params.get("h") || "").trim();
const isStaffPreview = params.get("preview") === "staff";
@@ -454,7 +457,10 @@ function updateNavAccessState() {
if (el) el.classList.toggle("nav-locked", locked);
});
const banner = document.getElementById("menuOnlyBanner");
if (banner) banner.classList.toggle("hidden", appAccessLevel !== "menu");
if (banner) {
banner.classList.remove("hidden");
banner.classList.toggle("is-hidden", appAccessLevel !== "menu");
}
}
function showBottomNav() {
@@ -479,46 +485,156 @@ async function resolveTableLabel() {
}
}
function isIOSDevice() {
return /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
}
const GEO_GATE_LABELS = {
status: "status zamówienia",
waiter: "wezwanie kelnera",
bill: "prośbę o rachunek",
};
const GEO_DEFAULT_LEAD =
"Przeglądaj menu od razu — albo potwierdź, że jesteś u nas, aby wezwać kelnera, śledzić zamówienie i poprosić o rachunek.";
function setGeoLead(html) {
const el = document.getElementById("geoLead");
if (el) el.innerHTML = html;
}
function setGeoStatus(html, { error = false, info = false } = {}) {
const el = document.getElementById("geoMsg");
if (!el) return;
el.innerHTML = html || "";
el.classList.toggle("is-error", error);
el.classList.toggle("is-info", info);
}
function showGeoInstructions(html) {
const el = document.getElementById("geoInstructions");
if (!el) return;
el.innerHTML = html || "";
el.classList.toggle("hidden", !html);
}
function hideGeoInstructions() {
showGeoInstructions("");
}
function setGeoActionBusy(busy) {
const btn = document.getElementById("geoActionBtn");
if (!btn) return;
btn.disabled = false;
btn.setAttribute("aria-busy", busy ? "true" : "false");
if (busy) {
setGeoActionLabel("Sprawdzanie…");
}
}
function isGeoPermissionDenied(error) {
return Number(error?.code) === 1;
}
function setGeoActionLabel(text) {
const btn = document.getElementById("geoActionBtn");
if (!btn) return;
const main = btn.querySelector(".geo-btn-main");
if (main) main.textContent = text;
else btn.textContent = text;
}
function getGeoPermissionInstructions() {
if (isIOSDevice()) {
return `<b>iPhone (Safari):</b><br>
1. Kliknij <b>aA</b> po lewej stronie paska adresu.<br>
2. Wybierz <b>Ustawienia witryny</b>.<br>
3. Ustaw <b>Położenie</b> na „Zapytaj” lub „Pozwalaj”.<br>
4. Odśwież stronę.<br><br>
<i>Lokalizacja działa tylko przez bezpieczne <b>https://</b>.</i>`;
}
return `<b>Android / Chrome:</b><br>
1. Kliknij ikonę <b>kłódki</b> obok adresu strony.<br>
2. W <b>Uprawnieniach</b> zmień Lokalizację na „Zezwalaj”.<br>
3. Odśwież stronę.`;
}
let geoMenuButtonMode = "menu_only";
window.handleGeoMenuClick = function () {
if (geoMenuButtonMode === "back_to_menu") {
document.getElementById("geoScreen")?.classList.add("hidden");
return;
}
enterMenuOnlyMode();
};
window.retryGeolocation = function () {
if (shouldBypassGeolocationHost()) {
bypassGeolocation("trusted_host", { host: window.location.hostname });
return;
}
initGeolocationAfterBypassChecks({ userInitiated: true }).catch((err) => {
console.error("[GEO] retry failed", err);
showGeoPermissionBlockedState();
});
};
function bindGeoScreenButtons() {
document.getElementById("geoMenuOnlyBtn")?.addEventListener("click", (event) => {
event.preventDefault();
handleGeoMenuClick();
});
document.getElementById("geoActionBtn")?.addEventListener("click", (event) => {
event.preventDefault();
retryGeolocation();
});
}
function configureGeoSecondaryButton(mode) {
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
if (!menuOnlyBtn) return;
geoMenuButtonMode = mode;
const mainEl = menuOnlyBtn.querySelector(".geo-btn-main");
const subEl = menuOnlyBtn.querySelector(".geo-btn-sub");
if (mode === "back_to_menu") {
menuOnlyBtn.style.display = "";
menuOnlyBtn.textContent = "Wróć do menu";
menuOnlyBtn.onclick = () => {
document.getElementById("geoScreen")?.classList.add("hidden");
};
if (mainEl) mainEl.textContent = "Wróć do menu";
if (subEl) {
subEl.textContent = "";
subEl.style.display = "none";
}
return;
}
menuOnlyBtn.style.display = "";
menuOnlyBtn.textContent = "Przeglądaj menu bez lokalizacji";
menuOnlyBtn.onclick = () => enterMenuOnlyMode();
if (mainEl) mainEl.textContent = "Przejdź do menu";
if (subEl) {
subEl.textContent = "bez lokalizacji";
subEl.style.display = "";
}
}
function showGeoGateForAction(action) {
const geoScreen = document.getElementById("geoScreen");
const loadingScreen = document.getElementById("loadingScreen");
const geoMsg = document.getElementById("geoMsg");
const geoActionBtn = document.getElementById("geoActionBtn");
if (loadingScreen) loadingScreen.classList.add("hidden");
if (geoScreen) geoScreen.classList.remove("hidden");
const feature = GEO_GATE_LABELS[action] || "tę funkcję";
if (geoMsg) {
geoMsg.innerHTML = `Aby skorzystać z <b>${feature}</b>, potwierdź, że jesteś w restauracji.<br><br>Prosimy o zgodę na dostęp do lokalizacji.`;
}
setGeoLead(`Menu masz już otwarte. Aby skorzystać z <b>${feature}</b>, potwierdź krótko, że jesteś w restauracji.`);
setGeoStatus("");
hideGeoInstructions();
if (geoActionBtn) {
geoActionBtn.disabled = false;
geoActionBtn.textContent = "Sprawdź lokalizację";
setGeoActionBusy(false);
setGeoActionLabel("Sprawdź lokalizację");
}
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
}
@@ -1322,7 +1438,7 @@ window.fetchGUS = async function () {
// --- DYNAMIC MENU LOADING ---
async function loadMenu() {
try {
const response = await fetch('menu.json');
const response = await fetch(`menu.json?v=${encodeURIComponent(MENU_ASSET_VERSION)}`);
if (!response.ok) throw new Error('Nie udało się załadować menu');
const menuData = await response.json();
window.menuDataRaw = menuData;
@@ -1467,9 +1583,13 @@ window.confirmInvoice = async function () {
};
// --- GEOLOCATION LOGIC ---
const RESTAURANT_LAT = 50.5624963;
const RESTAURANT_LNG = 22.0608059;
const MAX_DISTANCE_METERS = 200;
// Dwa punkty odniesienia: OSM (adres budynku) i pin Google Maps (z nim porównują goście w Maps).
const RESTAURANT_LOCATIONS = [
{ lat: 50.5622609, lng: 22.0606303, source: "osm" },
{ lat: 50.567953, lng: 22.061045, source: "google_maps" },
];
const MAX_DISTANCE_METERS = 300;
const MAX_ACCURACY_BONUS = 150;
function haversineDistance(lat1, lon1, lat2, lon2) {
const R = 6371e3;
@@ -1485,6 +1605,76 @@ function haversineDistance(lat1, lon1, lat2, lon2) {
return R * c;
}
function distanceToRestaurant(lat, lng) {
return Math.min(
...RESTAURANT_LOCATIONS.map(({ lat: rLat, lng: rLng }) =>
haversineDistance(rLat, rLng, lat, lng)
)
);
}
function isInsideRestaurantGeofence(distanceMeters, accuracyMeters) {
const accuracyBonus = Math.min(Math.max(Number(accuracyMeters) || 0, 0), MAX_ACCURACY_BONUS);
return distanceMeters <= MAX_DISTANCE_METERS + accuracyBonus;
}
function requestRestaurantGeolocation() {
const geoOptions = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 0,
};
return new Promise((resolve, reject) => {
let bestSample = null;
let watchId = null;
let settled = false;
const finish = (result) => {
if (settled) return;
settled = true;
if (watchId != null) navigator.geolocation.clearWatch(watchId);
clearTimeout(timeoutId);
resolve(result);
};
const fail = (error) => {
if (settled) return;
settled = true;
if (watchId != null) navigator.geolocation.clearWatch(watchId);
clearTimeout(timeoutId);
reject(error);
};
const handlePosition = (position) => {
const dist = distanceToRestaurant(position.coords.latitude, position.coords.longitude);
const accuracy = position.coords.accuracy;
const sample = { position, dist, accuracy };
if (!bestSample || dist < bestSample.dist) {
bestSample = sample;
}
if (isInsideRestaurantGeofence(dist, accuracy)) {
finish({ passed: true, ...sample });
}
};
const timeoutId = setTimeout(() => {
if (bestSample) {
finish({
passed: isInsideRestaurantGeofence(bestSample.dist, bestSample.accuracy),
...bestSample,
});
return;
}
fail({ code: 3, message: "Geolocation timeout" });
}, 12000);
watchId = navigator.geolocation.watchPosition(handlePosition, fail, geoOptions);
});
}
function startApp() {
appAccessLevel = "full";
updateNavAccessState();
@@ -1512,28 +1702,30 @@ function startApp() {
function showGeoConsentScreen() {
const geoScreen = document.getElementById("geoScreen");
const loadingScreen = document.getElementById("loadingScreen");
const geoMsg = document.getElementById("geoMsg");
const geoActionBtn = document.getElementById("geoActionBtn");
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
loadingScreen.classList.add("hidden");
geoScreen.classList.remove("hidden");
if (geoMsg) {
geoMsg.innerHTML = `Aby zapewnić bezpieczeństwo Twojego zamówienia, musimy upewnić się, że znajdujesz się na terenie restauracji.<br><br>Prosimy o udzielenie zgody na dostęp do lokalizacji w przeglądarce.`;
}
setGeoLead(GEO_DEFAULT_LEAD);
setGeoStatus("");
hideGeoInstructions();
if (geoActionBtn) {
geoActionBtn.disabled = false;
geoActionBtn.textContent = "Udziel zgody / Sprawdź";
}
if (menuOnlyBtn) {
configureGeoSecondaryButton("menu_only");
setGeoActionBusy(false);
setGeoActionLabel("Zgoda, sprawdź lokalizację");
}
configureGeoSecondaryButton("menu_only");
}
function isIOSDevice() {
return /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
function showGeoPermissionBlockedState() {
setGeoStatus("Przeglądarka zablokowała dostęp do lokalizacji.", { error: true });
showGeoInstructions(
`${getGeoPermissionInstructions()}<br><br>Po zmianie ustawień <b>odśwież stronę</b>, a potem kliknij „Spróbuj ponownie”.`
);
setGeoActionBusy(false);
setGeoActionLabel("Spróbuj ponownie");
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
}
function shouldBypassGeolocationHost() {
@@ -1541,135 +1733,189 @@ function shouldBypassGeolocationHost() {
return bypassHosts.includes(window.location.hostname);
}
window.initGeolocation = function () {
if (shouldBypassGeolocationHost()) {
console.warn("Bypassing geolocation for trusted host.");
trackEvent("geo_bypass_host", { host: window.location.hostname, reason: "trusted_host" });
async function checkGeoBypassByClientIp() {
try {
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
const timeoutId = controller
? setTimeout(() => controller.abort(), 4000)
: null;
const res = await fetch("../api/geo_bypass.php", {
credentials: "same-origin",
cache: "no-store",
signal: controller?.signal,
});
if (timeoutId) clearTimeout(timeoutId);
const data = await res.json();
return data.status === "success" && data.bypassGeo === true;
} catch {
return false;
}
}
function bypassGeolocation(reason, extra = {}) {
trackEvent('geo_bypass_host', { reason, ...extra });
if (appAccessLevel === 'menu') {
unlockFullApp();
} else {
startApp();
}
}
async function queryGeolocationPermissionState() {
if (!navigator.permissions?.query) {
return "unknown";
}
try {
const status = await navigator.permissions.query({ name: "geolocation" });
return status.state;
} catch {
return "unknown";
}
}
async function bootstrapGeolocation() {
if (shouldBypassGeolocationHost()) {
bypassGeolocation('trusted_host', { host: window.location.hostname });
return;
}
if (await checkGeoBypassByClientIp()) {
bypassGeolocation('trusted_ip');
return;
}
showGeoConsentScreen();
}
window.initGeolocation = function () {
if (shouldBypassGeolocationHost()) {
console.warn("Bypassing geolocation for trusted host.");
bypassGeolocation("trusted_host", { host: window.location.hostname });
return;
}
checkGeoBypassByClientIp().then((bypassByIp) => {
if (bypassByIp) {
console.warn("Bypassing geolocation for trusted client IP.");
bypassGeolocation("trusted_ip");
return;
}
initGeolocationAfterBypassChecks();
});
};
async function initGeolocationAfterBypassChecks(options = {}) {
const geoScreen = document.getElementById("geoScreen");
const loadingScreen = document.getElementById("loadingScreen");
const geoMsg = document.getElementById("geoMsg");
const geoActionBtn = document.getElementById("geoActionBtn");
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
const bypassHosts = ['localhost', '127.0.0.1', '192.168.20.84'];
if (window.location.protocol === 'http:' && bypassHosts.includes(window.location.hostname)) {
console.warn("Bypassing geolocation on local HTTP environment.");
trackEvent("geo_bypass_host", { host: window.location.hostname, reason: "local_http" });
unlockFullApp();
bypassGeolocation("local_http", { host: window.location.hostname });
return;
}
if (!window.isSecureContext) {
geoMsg.innerHTML = `<b style="color: #ff6b6b;">Ta strona nie jest uruchomiona w bezpiecznym trybie HTTPS.</b><br><br>
Przeglądarki mobilne blokują geolokalizację bez pytania, jeśli adres nie zaczyna się od <b>https://</b>.<br><br>
Otwórz aplikację przez HTTPS i spróbuj ponownie.<br><br>
<b>Masz problem z lokalizacją?</b> Połącz się z <b>HotSpot Karczmy</b>, a wtedy wejdziesz do aplikacji bez geolokalizacji.<br>
Hasło: <b>karczmabiesiada</b>`;
if (geoActionBtn) {
geoActionBtn.disabled = false;
geoActionBtn.textContent = "Spróbuj ponownie";
}
if (menuOnlyBtn) configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
return;
}
loadingScreen.classList.add("hidden");
geoScreen.classList.remove("hidden");
loadingScreen?.classList.add("hidden");
geoScreen?.classList.remove("hidden");
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
if (!navigator.geolocation) {
geoMsg.innerHTML = "Twoja przeglądarka nie wspiera geolokalizacji. Aplikacja wymaga nowszej przeglądarki.";
if (!window.isSecureContext) {
setGeoLead(GEO_DEFAULT_LEAD);
setGeoStatus("Ta strona wymaga bezpiecznego połączenia HTTPS.", { error: true });
showGeoInstructions("Przeglądarki mobilne blokują geolokalizację bez <b>https://</b>. Otwórz aplikację przez HTTPS i spróbuj ponownie.");
setGeoActionBusy(false);
setGeoActionLabel("Spróbuj ponownie");
return;
}
geoMsg.innerHTML = "Sprawdzamy Twoją lokalizację...";
trackEvent("geo_check_started");
if (geoActionBtn) {
geoActionBtn.disabled = true;
geoActionBtn.textContent = "Sprawdzanie...";
if (!navigator.geolocation) {
setGeoLead(GEO_DEFAULT_LEAD);
setGeoStatus("Twoja przeglądarka nie wspiera geolokalizacji.", { error: true });
hideGeoInstructions();
setGeoActionBusy(false);
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
const dist = haversineDistance(
RESTAURANT_LAT, RESTAURANT_LNG,
position.coords.latitude, position.coords.longitude
);
setGeoLead(GEO_DEFAULT_LEAD);
setGeoStatus("Sprawdzamy Twoją lokalizację…", { info: true });
hideGeoInstructions();
setGeoActionBusy(true);
const accuracy = position.coords.accuracy;
console.log(`[GEO] Lat: ${position.coords.latitude}, Lng: ${position.coords.longitude}, Dist: ${dist}m, Accuracy: ${accuracy}m`);
const permissionState = await queryGeolocationPermissionState();
if (permissionState === "denied") {
showGeoPermissionBlockedState();
return;
}
if (dist <= MAX_DISTANCE_METERS) {
trackEvent("geo_check_passed", { distanceMeters: Math.round(dist), accuracyMeters: Math.round(accuracy) });
unlockFullApp();
// setTimeout(() => showToast(`Lokalizacja zweryfikowana (Dystans: ${Math.round(dist)}m, Dokładność: ${Math.round(accuracy)}m)`), 2000);
} else {
trackEvent("geo_check_failed", { reason: "outside_restaurant", distanceMeters: Math.round(dist), accuracyMeters: Math.round(accuracy) });
if (geoActionBtn) {
geoActionBtn.disabled = false;
geoActionBtn.textContent = "Spróbuj ponownie";
}
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
geoMsg.innerHTML = `Wydaje się, że jesteś poza restauracją (ok. ${Math.round(dist)}m od nas).<br>Nasza aplikacja działa tylko na miejscu.<br><br>
<small style="color: #888;">Debug: Twoja odległość: ${Math.round(dist)}m, Dokładność sygnału: ${Math.round(accuracy)}m</small><br><br>
Jeśli to błąd GPS lub słaby sygnał, spróbuj ponownie za chwilę.`;
}
},
(error) => {
trackEvent("geo_check_failed", { reason: "browser_error", code: error.code || null, message: String(error.message || "") });
if (geoActionBtn) {
geoActionBtn.disabled = false;
geoActionBtn.textContent = "Spróbuj ponownie";
}
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
const deniedBecauseInsecure = /secure origins|only secure|https/i.test(String(error.message || ""));
if (deniedBecauseInsecure) {
geoMsg.innerHTML = `<b style="color: #ff6b6b;">Przeglądarka zablokowała lokalizację z powodu braku HTTPS.</b><br><br>
Geolokalizacja działa tylko na bezpiecznym adresie <b>https://</b> (lub localhost).<br>
Otwórz aplikację przez HTTPS i spróbuj ponownie.<br><br>
<b>Masz problem z lokalizacją?</b> Połącz się z <b>HotSpot Karczmy</b>, a wtedy wejdziesz do aplikacji bez geolokalizacji.<br>
Hasło: <b>karczmabiesiada</b>`;
} else if (error.code === error.PERMISSION_DENIED) {
const isIOS = isIOSDevice();
let instructions = '';
if (isIOS) {
instructions = `<b>Instrukcja dla iPhone (Safari):</b><br>
1. Kliknij ikonę <b>"aA"</b> po lewej stronie paska adresu.<br>
2. Wybierz <b>"Ustawienia witryny"</b> (Website Settings).<br>
3. Zmień opcję <b>"Położenie"</b> (Location) na "Zapytaj" lub "Pozwalaj".<br>
4. Odśwież stronę.<br><br>
<i>Uwaga: Na urządzeniach Apple lokalizacja działa WYŁĄCZNIE, gdy adres strony zaczyna się od bezpiecznego <b>https://</b>. Jeżeli jesteś na http://, system zablokuje to automatycznie.</i><br><br>
<b>Masz problem z lokalizacją?</b> Połącz się z <b>HotSpot Karczmy</b>, a wtedy wejdziesz do aplikacji bez geolokalizacji.<br>
Hasło: <b>karczmabiesiada</b>`;
} else {
instructions = `<b>Instrukcja dla Android / Chrome:</b><br>
1. Kliknij ikonkę <b>kłódki / ustawień</b> 🔒 obok adresu strony na górze przeglądarki.<br>
2. Znajdź <b>Uprawnienia</b> (Lokalizacja) i zmień z "Zablokuj" na "Zezwalaj".<br>
3. Odśwież stronę.<br><br>
<b>Masz problem z lokalizacją?</b> Połącz się z <b>HotSpot Karczmy</b>, a wtedy wejdziesz do aplikacji bez geolokalizacji.<br>
Hasło: <b>karczmabiesiada</b>`;
}
trackEvent("geo_check_started");
geoMsg.innerHTML = `<b style="color: #ff6b6b;">Przeglądarka zablokowała dostęp do lokalizacji.</b><br><br>
Bez tego nie możemy zweryfikować, czy jesteś w restauracji.<br><br>
${instructions}`;
} else {
geoMsg.innerHTML = "Nie udało się pobrać lokalizacji. Sprawdź zasięg lub włącz GPS i spróbuj ponownie.";
}
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 0 }
);
try {
const result = await requestRestaurantGeolocation();
const dist = result.dist;
const accuracy = result.accuracy;
console.log(
`[GEO] Lat: ${result.position.coords.latitude}, Lng: ${result.position.coords.longitude}, ` +
`MinDist: ${Math.round(dist)}m, Accuracy: ${Math.round(accuracy)}m`
);
if (result.passed) {
trackEvent("geo_check_passed", {
distanceMeters: Math.round(dist),
accuracyMeters: Math.round(accuracy),
});
unlockFullApp();
return;
}
trackEvent("geo_check_failed", {
reason: "outside_restaurant",
distanceMeters: Math.round(dist),
accuracyMeters: Math.round(accuracy),
});
setGeoActionBusy(false);
setGeoActionLabel("Spróbuj ponownie");
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
setGeoStatus(
`Wygląda na to, że jesteś poza restauracją (ok. ${Math.round(dist)} m, dokładność GPS: ±${Math.round(accuracy)} m).`,
{ error: true }
);
showGeoInstructions(
"Przeglądarka często podaje inną lokalizację niż aplikacja Map Google. " +
"Spróbuj ponownie na zewnątrz lub bliżej okna — albo przejdź do menu bez lokalizacji."
);
} catch (error) {
trackEvent("geo_check_failed", {
reason: "browser_error",
code: error.code || null,
message: String(error.message || ""),
});
setGeoActionBusy(false);
setGeoActionLabel("Spróbuj ponownie");
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
const deniedBecauseInsecure = /secure origins|only secure|https/i.test(String(error.message || ""));
if (deniedBecauseInsecure) {
setGeoStatus("Geolokalizacja wymaga HTTPS.", { error: true });
showGeoInstructions("Otwórz aplikację przez bezpieczny adres <b>https://</b> i spróbuj ponownie.");
} else if (isGeoPermissionDenied(error)) {
showGeoPermissionBlockedState();
} else {
setGeoStatus("Nie udało się pobrać lokalizacji.", { error: true });
showGeoInstructions("Sprawdź zasięg, włącz GPS i spróbuj ponownie.");
}
}
};
bindGeoScreenButtons();
if (shouldBypassGeolocationHost()) {
startApp();
} else if (isIOSDevice()) {
showGeoConsentScreen();
bypassGeolocation("trusted_host", { host: window.location.hostname });
} else {
initGeolocation();
bootstrapGeolocation();
}