"use strict";
import "../styles/main.scss";
import { initTheme } from "./theme.js";
import { state } from "./state.js";
import { initWeather } from "./weather.js";
import { fetchPOIs, normalizePOIs, groupPOIsByCategory, limitPOIsPerCategory, getDistanceFromRouteKm, sortPOIsAlongRoute } from "./poi.js";
import { initMap, drawRoute, drawPOIs, showPOIOnMap } from "./map.js";
import { geocodePlace, fetchRoute } from "./route.js";
import { renderStopsGroups, initPOIModalEvents } from "./ui.js";
import { registerSW } from "virtual:pwa-register";
registerSW({ immediate: true });
/* Startar kartan direkt när sidan laddas */
initMap();
initWeather();
initTheme();
/* Hämtar element som JS ska jobba med */
const form = document.getElementById("route-form");
const startInput = document.getElementById("start-input");
const destinationInput = document.getElementById("destination-input");
const statusMessage = document.getElementById("status-message");
const visibleCountSelect = document.getElementById("visible-count");
const useLocationBtn = document.getElementById("use-location-btn");
/* Sparar alla normaliserade POI från senaste sökningen */
let currentPOIs = [];
/* Hämtar filter-checkboxar */
const filterToilets = document.getElementById("filter-toilets");
const filterFood = document.getElementById("filter-food");
const filterFuel = document.getElementById("filter-fuel");
const filterCamping = document.getElementById("filter-camping");
const filterViewpoints = document.getElementById("filter-viewpoints");
/**
* Läser av vilka filter i 'Visa stopp längs vägen' som är aktiva just nu.
* @returns {object}
*/
function getActiveFilters() {
return {
toilets: filterToilets?.checked ?? false,
food: filterFood?.checked ?? false,
fuel: filterFuel?.checked ?? false,
camping: filterCamping?.checked ?? false,
viewpoints: filterViewpoints?.checked ?? false
};
}
/**
* Filtrerar POI utifrån vilka checkboxar som är aktiva.
* @param {object[]} pois
* @param {object} filters
* @returns {object[]}
*/
function filterPOIs(pois, filters) {
return pois.filter((poi) => {
if (poi.type === "toilets") {
return filters.toilets;
}
if (["restaurant", "cafe", "fast_food"].includes(poi.type)) {
return filters.food;
}
if (poi.type === "fuel") {
return filters.fuel;
}
if (["camp_site", "caravan_site"].includes(poi.type)) {
return filters.camping;
}
if (poi.type === "viewpoint") {
return filters.viewpoints;
}
/* Övriga typer visas som standard */
return true;
});
}
/**
* Uppdaterar karta och stopplista utifrån aktiva filter.
*/
function updateFilteredPOIView() {
const activeFilters = getActiveFilters();
const filteredPOIs = filterPOIs(currentPOIs, activeFilters);
const groupedPOIs = groupPOIsByCategory(filteredPOIs);
const maxPerCategory = Number(visibleCountSelect?.value || 10);
const limitedGroupedPOIs = limitPOIsPerCategory(groupedPOIs, maxPerCategory);
const limitedPOIs = Object.values(limitedGroupedPOIs).flat();
drawPOIs(limitedPOIs);
renderStopsGroups(limitedGroupedPOIs);
}
/* Hämtar användarens position när man klickar på knappen */
useLocationBtn?.addEventListener("click", () => {
/* Kontrollerar att geolocation finns */
if (!navigator.geolocation) {
statusMessage.textContent = "Din webbläsare stödjer inte geolocation.";
return;
}
statusMessage.textContent = "Hämtar din position...";
navigator.geolocation.getCurrentPosition(
(position) => {
/* Hämta lat/lon från webbläsaren */
const lat = position.coords.latitude;
const lon = position.coords.longitude;
/* Spara i state i formatet [lon, lat] */
state.userPosition = [lon, lat];
/* Skriv in i startfältet så användaren ser vad som valts */
startInput.value = "Min position";
statusMessage.textContent = "Din position hämtades.";
},
() => {
statusMessage.textContent = "Kunde inte hämta din position.";
}
);
});
/* Om formuläret finns - lyssna på när det skickas, callbacken är async eftersom den ska använda await */
form?.addEventListener("submit", async (event) => {
/* Stoppar sidan från att laddas om automatiskt */
event.preventDefault();
/* Läser in vad användaren skrivit och tar bort onödiga mellanslag i början och slutet */
const start = startInput.value.trim();
const destination = destinationInput.value.trim();
/* Validering - om något fält är tomt: visa meddelande och avbryt funktionen */
if (!start || !destination) {
statusMessage.textContent = "Fyll i startplats och destination.";
return;
}
try {
statusMessage.textContent = "Hämtar rutt...";
const startCoords = start === "Min position" && state.userPosition
? state.userPosition
: await geocodePlace(start);
const endCoords = await geocodePlace(destination);
/* Hämtar rutten mellan de två punkterna */
const routeCoordinates = await fetchRoute(startCoords, endCoords);
/* Skickar datan vidare till map.js som ritar rutten på kartan */
drawRoute(routeCoordinates);
/* Hämta stopp */
const rawPOIs = await fetchPOIs(routeCoordinates);
/* Normalisera stopp */
const normalizedPOIs = normalizePOIs(rawPOIs);
/* Lägg till avstånd från rutten på varje stopp */
const normalizedPOIsWithDistance = normalizedPOIs.map((poi) => ({
...poi,
/* Sparar kortaste avståndet till rutten i km */
distanceFromRouteKm: getDistanceFromRouteKm(poi, routeCoordinates)
}));
/* Sortera stoppen i ruttens ordning så urvalet blir mer utspritt */
const sortedPOIsAlongRoute = sortPOIsAlongRoute(
normalizedPOIsWithDistance,
routeCoordinates
);
/* Spara alla stopp från senaste sökningen */
currentPOIs = sortedPOIsAlongRoute;
/* Uppdatera karta och stopplista utifrån valda filter */
updateFilteredPOIView();
/* Koppla klick i stopplistan till modalen med POI som redan har ruttavstånd */
initPOIModalEvents(sortedPOIsAlongRoute, showPOIOnMap);
statusMessage.textContent = "Rutt och stopp hämtade.";
} catch (error) {
statusMessage.textContent = error.message || "Något gick fel.";
}
});
/* När användaren ändrar filter ska kartan och listan uppdateras direkt */
[
filterToilets,
filterFood,
filterFuel,
filterCamping,
filterViewpoints
].forEach((checkbox) => {
checkbox?.addEventListener("change", () => {
updateFilteredPOIView();
});
});
/* Ändra antal visningar i listan */
visibleCountSelect?.addEventListener("change", () => {
updateFilteredPOIView();
});