Serveur web local sans dépendance nouvelle (http.server, Jinja2, htmx et Leaflet fournis). Écrans : Mes voyages, nouveau voyage, un voyage (journées avec carte, rattachement des traces sans heures à un jour avec suggestion d'après les photos, sources avec choix de dossier, infos et récit, génération avec avancement et arrêt), réglages du site et vérification des outils. Chaîne : jours imposés ([dates_traces] dans voyage.toml), arrêt propre de la compression, suivi des étapes, état structuré (site.tableau_etat), titre du voyage repris de voyage.toml sur l'accueil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
93 lines
3.9 KiB
JavaScript
93 lines
3.9 KiB
JavaScript
// Onglets, cartes Leaflet, fenêtre de choix de dossier, journal des traitements.
|
|
(function () {
|
|
"use strict";
|
|
|
|
// --- Onglets (#journees, #sources…) --------------------------------------
|
|
function afficherOnglet() {
|
|
const liens = document.querySelectorAll(".onglets a");
|
|
if (!liens.length) return;
|
|
const nom = (location.hash || liens[0].getAttribute("href")).slice(1);
|
|
let trouve = false;
|
|
liens.forEach(a => {
|
|
const actif = a.getAttribute("href") === "#" + nom;
|
|
a.classList.toggle("actif", actif);
|
|
trouve = trouve || actif;
|
|
});
|
|
if (!trouve) { liens[0].classList.add("actif"); }
|
|
document.querySelectorAll(".onglet").forEach(s => {
|
|
s.classList.toggle("actif", s.id === "onglet-" + (trouve ? nom : liens[0].getAttribute("href").slice(1)));
|
|
});
|
|
document.querySelectorAll(".onglet.actif .carte").forEach(cadrer);
|
|
}
|
|
window.addEventListener("hashchange", afficherOnglet);
|
|
document.addEventListener("DOMContentLoaded", afficherOnglet);
|
|
|
|
// --- Cartes -----------------------------------------------------------------
|
|
// Une carte préparée dans un onglet caché n'a pas de taille : on la recadre
|
|
// la première fois qu'elle devient visible.
|
|
function cadrer(el) {
|
|
if (!el._carte || !el.offsetWidth) return;
|
|
el._carte.invalidateSize();
|
|
if (!el._cadree) {
|
|
el._cadree = true;
|
|
if (el._bornes) el._carte.fitBounds(el._bornes, { padding: [20, 20] });
|
|
else el._carte.setView([46.5, 2.5], 5);
|
|
}
|
|
}
|
|
|
|
function initCartes(racine) {
|
|
if (!window.L) return;
|
|
racine.querySelectorAll(".carte[data-couches]").forEach(el => {
|
|
if (el._carte) return;
|
|
const carte = L.map(el, { scrollWheelZoom: false });
|
|
el._carte = carte;
|
|
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
maxZoom: 18, attribution: "© OpenStreetMap"
|
|
}).addTo(carte);
|
|
const groupe = L.featureGroup().addTo(carte);
|
|
JSON.parse(el.dataset.couches).forEach(c => {
|
|
c.segments.forEach(s => {
|
|
L.polyline(s, { color: c.couleur, weight: 4, opacity: 0.85, dashArray: c.pointille ? "6 8" : null })
|
|
.bindTooltip(c.nom + (c.km ? " · " + c.km + " km" : ""), { sticky: true })
|
|
.addTo(groupe);
|
|
});
|
|
});
|
|
el._bornes = groupe.getLayers().length ? groupe.getBounds() : null;
|
|
carte.setView([46.5, 2.5], 5);
|
|
cadrer(el);
|
|
// La molette ne zoome qu'après un clic dans la carte (pas en faisant défiler la page).
|
|
carte.on("click", () => carte.scrollWheelZoom.enable());
|
|
carte.on("mouseout", () => carte.scrollWheelZoom.disable());
|
|
});
|
|
}
|
|
|
|
// --- Après chaque échange htmx --------------------------------------------
|
|
document.addEventListener("htmx:afterSettle", e => {
|
|
initCartes(e.detail.elt.parentElement || document);
|
|
document.querySelectorAll(".journal").forEach(j => { j.scrollTop = j.scrollHeight; });
|
|
});
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
initCartes(document);
|
|
document.querySelectorAll(".journal").forEach(j => { j.scrollTop = j.scrollHeight; });
|
|
});
|
|
|
|
// --- Fenêtre (choix de dossier) -------------------------------------------
|
|
function fermer() { document.getElementById("fenetre").innerHTML = ""; }
|
|
document.addEventListener("click", e => {
|
|
if (e.target.matches("[data-fermer-fond]")) fermer();
|
|
if (e.target.matches("[data-fermer]") && !e.target.hasAttribute("hx-post")) fermer();
|
|
});
|
|
document.addEventListener("htmx:afterRequest", e => {
|
|
if (e.detail.elt.matches && e.detail.elt.matches("[data-fermer]") && e.detail.successful) fermer();
|
|
});
|
|
document.addEventListener("keydown", e => { if (e.key === "Escape") fermer(); });
|
|
|
|
// Les messages (erreurs) s'effacent d'eux-mêmes.
|
|
document.addEventListener("htmx:afterSwap", e => {
|
|
if (e.detail.target.id === "messages") {
|
|
clearTimeout(window._rtgMessage);
|
|
window._rtgMessage = setTimeout(() => { e.detail.target.innerHTML = ""; }, 6000);
|
|
}
|
|
});
|
|
})();
|