Files
RoadTripsGenerator/rtg/interface/static/rtg.js
T
FabriceandClaude Opus 5.5 ada3d76f50 Administration native sur le Mac, réencodage sélectif et aide intégrée
- admin.sh : RTG lancé directement sur le Mac (plus de conteneur), nginx
  joint l'interface par host.docker.internal:8765 ; variante conteneur
  conservée (admin-docker.sh, docker-compose-conteneur.yml)
- Compression : réglages lus dans les fichiers (CRF, codec), refaire
  seulement le 1080p ou le 720p, seulement si les réglages ont changé ;
  réglages vidéo propres à chaque voyage ; « Compression actuelle » affichée
- Plus d'arrêt automatique après 2 h d'inactivité (--arret-inactif, 0 par défaut) ;
  arrêt propre qui attend la fin des encodages en cours
- Aide contextuelle dans l'interface (rtg/interface/aide) ; README centré
  sur l'installation et la mise en place
- Cœurs attribués au conteneur détectés (cgroup) et signalés

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
2026-10-19 05:32:05 +00:00

117 lines
5.1 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);
document.dispatchEvent(new CustomEvent("rtg:onglet", { detail: trouve ? nom : "" }));
}
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(); });
// --- Aide contextuelle (panneau à droite) --------------------------------
function aideOuverte(oui) {
const a = document.getElementById("aide");
if (!a) return;
a.hidden = !oui;
document.body.classList.toggle("aide-ouverte", oui);
}
function fermerAide() { aideOuverte(false); }
document.addEventListener("click", e => {
if (e.target.closest("[data-ouvrir-aide]")) aideOuverte(true);
if (e.target.closest("[data-fermer-aide]")) fermerAide();
});
// Panneau ouvert : il suit l'onglet affiché.
document.addEventListener("rtg:onglet", e => {
const a = document.getElementById("aide");
const b = document.querySelector("#onglet-" + e.detail + " [data-ouvrir-aide]");
if (a && !a.hidden && b && window.htmx) htmx.ajax("GET", b.getAttribute("hx-get"), "#aide-contenu");
});
document.addEventListener("keydown", e => { if (e.key === "Escape") fermerAide(); });
document.addEventListener("htmx:afterSwap", e => {
if (e.detail.target.id === "aide-contenu") e.detail.target.parentElement.scrollTop = 0;
});
// 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);
}
});
})();