Refonte : templates, balades, photos web, aperçus, carte globale
- Mise en page sortie du Python : Scripts/templates/ (Jinja2 + CSS/JS communs copiés dans assets/ de chaque voyage) - Voyages rangés dans un sous-dossier (Balades/…) : rubriques sur l'accueil, 2 plus récents par rubrique, pages voyages.html et balades.html - prepare_web.py : photos allégées (photos_web/) et images d'aperçu des vidéos (apercus/) - Voyage d'une seule journée : la page du voyage est la carte du jour - Carte globale de l'accueil (OpenStreetMap) : voyages du site + cartes Google My Maps récupérées (mymaps.txt, _mymaps/), moto par voyage (moto.txt) - KML par voyage pour My Maps - compress.sh : réencode une sortie illisible (encodage interrompu) ; repli automatique sur l'autre qualité dans la page - Corrections : CSV vide (voyage sans photo), racine de genere_site.sh, fichiers ._ de macOS - README réécrit, commentaires allégés Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
This commit is contained in:
1 parent
5a04f159ca
commit
97b936fd3e
17 files changed
+2042
-829
No files matched your search
@@ -0,0 +1,37 @@
|
||||
{#- Page d'accueil : une rubrique par section (Voyages, Balades…), avec les
|
||||
voyages les plus récents et un lien vers la liste complète. -#}
|
||||
{% extends "base_accueil.html" %}
|
||||
{% from "base_accueil.html" import carte %}
|
||||
|
||||
{% block sous_titre %}{{ sous_titre }}{% endblock %}
|
||||
|
||||
{% block contenu %}
|
||||
{% if not rubriques %}
|
||||
<p class="vide">Aucun voyage pour l'instant.<br>Lance <code>genere_site.sh</code> pour en générer.</p>
|
||||
{% else %}
|
||||
<div class="rubriques">
|
||||
{% for r in rubriques %}
|
||||
<section>
|
||||
<div class="rubrique-titre">
|
||||
<h2>{{ r.titre }}</h2>
|
||||
<a href="{{ r.page }}">Tout voir ({{ r.voyages|length }}) →</a>
|
||||
</div>
|
||||
<div class="grid compact">
|
||||
{% for v in r.voyages[:nb_recents] %}
|
||||
{{ carte(v) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if carte_globale %}
|
||||
<section class="carte-externe" id="carte">
|
||||
<div class="rubrique-titre">
|
||||
<h2>La carte de nos principaux voyages</h2>
|
||||
<a href="{{ carte_globale }}">Ouvrir en grand ↗</a>
|
||||
</div>
|
||||
<iframe src="{{ carte_globale }}" loading="lazy" title="Carte de nos principaux voyages"></iframe>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,90 @@
|
||||
{#- Gabarit commun à la page d'accueil (accueil.html) et aux listes complètes
|
||||
(liste.html). Les pages filles remplissent les blocs titre et contenu. -#}
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block titre %}{{ titre_site }}{% endblock %}</title>
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: #f4f4f9; margin: 0; padding: 20px; color: #222;
|
||||
}
|
||||
header { text-align: center; margin-bottom: 30px; }
|
||||
h1 { margin: 0 0 6px; font-size: clamp(1.6rem, 4vw, 2.4rem); }
|
||||
.sous-titre { color: #777; font-size: 0.9rem; }
|
||||
main { max-width: 1200px; margin: 0 auto; }
|
||||
a.retour { display: inline-block; margin-bottom: 10px; color: #3498db; text-decoration: none; font-weight: bold; }
|
||||
|
||||
/* Rubriques de l'accueil, côte à côte ; empilées quand la place manque. */
|
||||
.rubriques {
|
||||
display: grid; gap: 40px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(480px, 100%), 1fr));
|
||||
}
|
||||
.rubrique-titre {
|
||||
display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
|
||||
margin: 0 0 16px; padding-bottom: 6px; border-bottom: 2px solid #ddd;
|
||||
}
|
||||
.rubrique-titre h2 { margin: 0; font-size: 1.3rem; }
|
||||
.rubrique-titre a, a.tout-voir { color: #3498db; text-decoration: none; font-weight: bold; font-size: 0.9rem; white-space: nowrap; }
|
||||
a.tout-voir { display: block; margin-top: 14px; text-align: right; }
|
||||
|
||||
/* min() : sur un écran étroit, une colonne fixe déborderait. */
|
||||
.grid { display: grid; gap: 24px; grid-template-columns: repeat(auto-fill, minmax(min(var(--carte, 400px), 100%), 1fr)); }
|
||||
.compact { --carte: 220px; }
|
||||
.card {
|
||||
background: #fff; border-radius: 15px; overflow: hidden;
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||
transition: transform .2s, box-shadow .2s;
|
||||
}
|
||||
.card:hover { transform: translateY(-4px); box-shadow: 0 14px 28px rgba(0,0,0,0.15); }
|
||||
/* Toute la carte est cliquable ; l'iframe ne capte pas les clics. */
|
||||
.card > a { display: block; color: inherit; text-decoration: none; }
|
||||
.map-container { width: 100%; height: 300px; border: none; pointer-events: none; display: block; }
|
||||
.compact .map-container { height: 190px; }
|
||||
.card-content { padding: 16px 20px 20px; text-align: center; }
|
||||
.card h3 { text-transform: capitalize; margin: 0 0 4px; font-size: 1.2rem; }
|
||||
.compact .card h3 { font-size: 1.05rem; }
|
||||
.periode { color: #888; font-size: 0.85rem; }
|
||||
.carte-externe { margin-top: 50px; scroll-margin-top: 20px; }
|
||||
.carte-externe iframe {
|
||||
display: block; width: 100%; height: min(600px, 75vh); border: none;
|
||||
border-radius: 15px; box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
.vide { text-align: center; color: #777; margin-top: 60px; line-height: 1.7; }
|
||||
code { background: #e8e8ef; padding: 2px 6px; border-radius: 4px; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #16161a; color: #e8e8ea; }
|
||||
.card { background: #232329; box-shadow: 0 10px 20px rgba(0,0,0,0.4); }
|
||||
.sous-titre, .vide, .periode { color: #9a9aa2; }
|
||||
.rubrique-titre { border-color: #333; }
|
||||
code { background: #2e2e36; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{% block entete %}🌍 {{ titre_site }}{% endblock %}</h1>
|
||||
<div class="sous-titre">{% block sous_titre %}{% endblock %}</div>
|
||||
</header>
|
||||
<main>
|
||||
{% block contenu %}{% endblock %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{% macro carte(v) -%}
|
||||
<article class="card">
|
||||
<a href="{{ v.chemin }}/index.html">
|
||||
{# loading="lazy" : sinon chaque miniature charge une carte Leaflet complète dès l'ouverture #}
|
||||
<iframe class="map-container" src="{{ v.chemin }}/mini.html" loading="lazy" title="Carte de {{ v.titre }}" tabindex="-1"></iframe>
|
||||
<div class="card-content">
|
||||
<h3>{{ v.titre }}</h3>
|
||||
{% if v.periode %}<div class="periode">{{ v.periode }}</div>{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,131 @@
|
||||
/* =============================================================================
|
||||
Pages d'un voyage : carte générale (index.html) et cartes du jour (html/).
|
||||
Copié dans <voyage>/assets/ à chaque génération.
|
||||
============================================================================= */
|
||||
|
||||
/* --- Contrôles Leaflet : zoom en bas à gauche, la barre latérale occupe le haut */
|
||||
.leaflet-top.leaflet-left { top: auto !important; bottom: 20px !important; left: 15px !important; }
|
||||
.leaflet-popup-content-wrapper { border-radius: 12px; max-width: 95vw !important; }
|
||||
|
||||
/* --- Épingles des médias --------------------------------------------------- */
|
||||
.pin { position: relative; height: 40px; width: 24px; }
|
||||
.pin-tete {
|
||||
position: absolute; top: 0; z-index: 2;
|
||||
width: 24px; height: 24px; border-radius: 50%;
|
||||
background: var(--c); border: 2px solid #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.4);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; font-size: 12px;
|
||||
}
|
||||
.pin-tige {
|
||||
position: absolute; top: 22px; left: 10.5px; z-index: 1;
|
||||
width: 3px; height: 18px; background: var(--c);
|
||||
}
|
||||
|
||||
/* --- Boutons flottants ----------------------------------------------------- */
|
||||
.btn-flottant {
|
||||
position: fixed; right: 15px; z-index: 10001; display: block;
|
||||
padding: 12px 20px; border: 2px solid #fff; border-radius: 50px;
|
||||
color: #fff; text-decoration: none; font: bold 14px sans-serif;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.4); cursor: pointer;
|
||||
}
|
||||
.btn-journal { bottom: 25px; z-index: 10000; background: #FFD700; color: #000; }
|
||||
.btn-gpx { bottom: 85px; background: #28a745; }
|
||||
.btn-accueil {
|
||||
top: 15px; left: 15px; right: auto; border-radius: 10px;
|
||||
background: #333; box-shadow: 0 4px 10px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* --- Barre latérale de la carte générale ------------------------------------ */
|
||||
.sidebar {
|
||||
position: fixed; top: 15px; left: 15px; z-index: 9999;
|
||||
width: 160px; max-height: 85vh; overflow-y: auto; padding: 12px;
|
||||
background: rgba(255,255,255,0.9); border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.2); font-family: sans-serif;
|
||||
}
|
||||
.sidebar a {
|
||||
display: block; margin-bottom: 8px; padding: 12px; border-radius: 8px;
|
||||
color: #fff; text-decoration: none; font-weight: bold; text-align: center;
|
||||
}
|
||||
.sidebar .btn-retour { margin-bottom: 15px; background: #444; border: 2px solid #666; }
|
||||
.sidebar .jour { background: var(--c); }
|
||||
.sidebar-titre {
|
||||
border-top: 1px solid #ccc; margin-bottom: 10px; padding-top: 10px;
|
||||
font-size: 12px; font-weight: bold; color: #666; text-align: center;
|
||||
}
|
||||
|
||||
/* --- Popup d'une étape (clic sur la trace) ---------------------------------- */
|
||||
.popup-trace { text-align: center; font-family: sans-serif; }
|
||||
.popup-trace .btn-jour {
|
||||
display: inline-block; margin-top: 14px; padding: 8px 12px; border-radius: 4px;
|
||||
background: var(--c); color: #fff; text-decoration: none; font-weight: bold;
|
||||
}
|
||||
|
||||
/* --- Popup vidéo ------------------------------------------------------------ */
|
||||
.popup-video { width: calc(85vw - 40px); max-width: 1000px; text-align: center; }
|
||||
.popup-video video {
|
||||
display: block; width: 100%; max-height: 70vh;
|
||||
border-radius: 8px; background: #000;
|
||||
}
|
||||
.header-row {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
background: #f0f0f0; padding: 5px 10px; border-radius: 4px; margin-bottom: 5px;
|
||||
}
|
||||
.file-name {
|
||||
font: bold 11px monospace; color: #333; max-width: 60%;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.quality-controls { display: flex; gap: 6px; }
|
||||
.quality-btn {
|
||||
padding: 3px 8px; border: 1px solid #ccc; border-radius: 12px; background: #fff;
|
||||
font: bold 10px sans-serif; text-transform: uppercase;
|
||||
cursor: pointer; opacity: 0.6; transition: 0.2s;
|
||||
}
|
||||
.quality-btn:hover { opacity: 1; background: #eee; }
|
||||
.active-720 { background: #2ECC71 !important; color: #fff; border-color: #27ae60 !important; opacity: 1 !important; }
|
||||
.active-1080 { background: #007AFF !important; color: #fff; border-color: #0056b3 !important; opacity: 1 !important; }
|
||||
.qbtn-disabled, .qbtn-disabled:hover {
|
||||
opacity: 0.35 !important; background: #eee !important;
|
||||
cursor: not-allowed !important; text-decoration: line-through;
|
||||
}
|
||||
.hevc-note {
|
||||
display: none; margin-top: 6px; padding: 6px 10px; border-radius: 6px;
|
||||
font: 11px sans-serif; color: #8a6d3b; background: #fcf8e3; text-align: left;
|
||||
}
|
||||
|
||||
/* --- Popup photos (diaporama) ----------------------------------------------- */
|
||||
.slider-container { position: relative; width: calc(85vw - 60px); max-width: 800px; min-height: 200px; text-align: center; }
|
||||
.file-header { font: bold 11px monospace; color: #555; margin-bottom: 4px; }
|
||||
.slide img { display: block; margin: auto; max-width: 100%; max-height: 65vh; border-radius: 8px; }
|
||||
.nav-btn {
|
||||
position: absolute; top: 50%; transform: translateY(-50%); z-index: 10;
|
||||
padding: 12px; border: 2px solid #fff; border-radius: 50%;
|
||||
background: rgba(0,0,0,0.6); color: #fff; font-size: 18px; cursor: pointer;
|
||||
}
|
||||
.prev { left: -10px; }
|
||||
.next { right: -10px; }
|
||||
.slide-counter { margin-top: 8px; font: bold 13px sans-serif; color: #333; }
|
||||
|
||||
/* --- Carnet de route -------------------------------------------------------- */
|
||||
#journal-panel {
|
||||
position: fixed; top: 0; right: -100%; z-index: 10005;
|
||||
width: 400px; height: 100%; padding: 25px; overflow-y: auto;
|
||||
background: #fff; box-shadow: -5px 0 15px rgba(0,0,0,0.2);
|
||||
font-family: sans-serif; transition: 0.4s;
|
||||
}
|
||||
#journal-panel.open { right: 0; }
|
||||
@media (max-width: 600px) { #journal-panel { width: 85%; } }
|
||||
.close-btn {
|
||||
position: absolute; top: 15px; right: 15px;
|
||||
border: none; background: none; font-size: 24px; cursor: pointer;
|
||||
}
|
||||
.journal-content a.lien-jour {
|
||||
display: inline-block; margin: 4px 0 10px; padding: 5px 12px; border-radius: 14px;
|
||||
background: #007AFF; color: #fff !important; text-decoration: none;
|
||||
font-size: 12px; font-weight: bold;
|
||||
}
|
||||
.journal-content a.lien-jour:hover { background: #0056b3; }
|
||||
.journal-content a.lien-jour.jour-actif { background: #2ECC71; box-shadow: 0 0 0 3px rgba(46,204,113,0.3); }
|
||||
.journal-content a.lien-media {
|
||||
color: #007AFF; text-decoration: underline;
|
||||
text-decoration-style: dotted; text-underline-offset: 2px;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/* =============================================================================
|
||||
Pages d'un voyage : vidéos, diaporamas photo et carnet de route.
|
||||
Copié dans <voyage>/assets/ à chaque génération.
|
||||
|
||||
Les gestionnaires sont appelés par des onclick="" dans les popups : Leaflet
|
||||
bloque la propagation des clics hors des popups, une délégation au niveau
|
||||
du document ne les verrait pas.
|
||||
============================================================================= */
|
||||
|
||||
// --- Vidéos ------------------------------------------------------------------
|
||||
// Le 720p (H.264) est chargé par défaut et se lit partout. Le 1080p est en
|
||||
// HEVC : si le navigateur ne le décode pas (Firefox surtout), on grise le bouton.
|
||||
var HEVC_OK = (function () {
|
||||
try {
|
||||
var t = document.createElement('video').canPlayType('video/mp4; codecs="hvc1.1.6.L120.B0"');
|
||||
return t === 'probably' || t === 'maybe';
|
||||
} catch (e) { return false; }
|
||||
})();
|
||||
|
||||
function applyCodecSupport(popup) {
|
||||
if (HEVC_OK) return;
|
||||
var btns = popup.querySelectorAll('.quality-btn[data-hevc="1"]');
|
||||
for (var i = 0; i < btns.length; i++) {
|
||||
btns[i].classList.add('qbtn-disabled');
|
||||
btns[i].title = "Le 1080p est encodé en HEVC, que ce navigateur ne sait pas décoder. Essaie Safari, Chrome ou Edge.";
|
||||
}
|
||||
var note = popup.querySelector('.hevc-note');
|
||||
if (note) note.style.display = 'block';
|
||||
}
|
||||
|
||||
function changeQuality(btn) {
|
||||
if (btn.classList.contains('qbtn-disabled')) return;
|
||||
var box = btn.closest('.popup-video');
|
||||
var video = box.querySelector('video');
|
||||
var newSrc = btn.getAttribute('data-src');
|
||||
|
||||
// video.src est une URL absolue, data-src est relatif : on compare les noms.
|
||||
var nomActuel = decodeURIComponent(video.src).split('/').pop();
|
||||
var nomVoulu = decodeURIComponent(newSrc).split('/').pop();
|
||||
if (nomActuel === nomVoulu) return;
|
||||
|
||||
var savedTime = video.currentTime;
|
||||
var wasPlaying = !video.paused;
|
||||
video.src = newSrc;
|
||||
video.onloadedmetadata = function () {
|
||||
this.currentTime = savedTime;
|
||||
if (wasPlaying) {
|
||||
var p = this.play();
|
||||
if (p !== undefined) p.catch(function () { console.log("Lecture bloquée"); });
|
||||
}
|
||||
this.onloadedmetadata = null;
|
||||
};
|
||||
video.load();
|
||||
|
||||
var buttons = box.querySelectorAll('.quality-btn');
|
||||
for (var i = 0; i < buttons.length; i++) buttons[i].classList.remove('active-720', 'active-1080');
|
||||
btn.classList.add(btn.getAttribute('data-hevc') === '1' ? 'active-1080' : 'active-720');
|
||||
}
|
||||
|
||||
// À l'ouverture d'une popup vidéo : codec, image d'aperçu, puis lecture.
|
||||
// L'aperçu n'est posé qu'à ce moment-là : en attribut poster="" direct, le
|
||||
// navigateur chargerait d'un coup les aperçus de toutes les vidéos du jour.
|
||||
function nomFichier(u) { return decodeURIComponent(u || '').split('/').pop(); }
|
||||
|
||||
// Filet de sécurité : si une version ne se lit pas (1080p HEVC malgré la
|
||||
// détection, fichier abîmé ou absent), on essaie l'autre.
|
||||
function surErreurVideo() {
|
||||
var video = this, box = video.closest('.popup-video');
|
||||
video._essais = video._essais || {};
|
||||
video._essais[nomFichier(video.src)] = true;
|
||||
var candidats = [video.getAttribute('data-fallback')];
|
||||
var hd = box.querySelector('.quality-btn[data-hevc="1"]');
|
||||
if (hd && HEVC_OK) candidats.push(hd.getAttribute('data-src'));
|
||||
for (var i = 0; i < candidats.length; i++) {
|
||||
var c = candidats[i];
|
||||
if (c && !video._essais[nomFichier(c)]) {
|
||||
var btn = box.querySelector('.quality-btn[data-src="' + c + '"]');
|
||||
if (btn) changeQuality(btn); else { video.src = c; video.load(); }
|
||||
var p = video.play();
|
||||
if (p !== undefined) p.catch(function () {});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function preparePopup(node) {
|
||||
applyCodecSupport(node);
|
||||
var video = node.querySelector('video');
|
||||
if (!video) return;
|
||||
// Leaflet réinsère la même popup à chaque ouverture : une seule initialisation.
|
||||
if (!video._pret) {
|
||||
video._pret = true;
|
||||
var apercu = video.getAttribute('data-poster');
|
||||
if (apercu) video.setAttribute('poster', apercu);
|
||||
video.addEventListener('error', surErreurVideo);
|
||||
}
|
||||
var p = video.play();
|
||||
if (p !== undefined) p.catch(function (e) { console.log("Autoplay bloqué par le navigateur", e); });
|
||||
}
|
||||
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (m) {
|
||||
m.addedNodes.forEach(function (node) {
|
||||
if (node.nodeType === 1 && node.classList && node.classList.contains('leaflet-popup')) {
|
||||
preparePopup(node);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
|
||||
// --- Diaporama photos ----------------------------------------------------------
|
||||
function moveSlide(btn, step) {
|
||||
var slides = btn.parentElement.querySelectorAll('.slides .slide');
|
||||
var actif = Array.prototype.findIndex.call(slides, function (s) { return s.style.display !== 'none'; });
|
||||
if (actif === -1) actif = 0;
|
||||
slides[actif].style.display = 'none';
|
||||
var suivant = (actif + step + slides.length) % slides.length;
|
||||
slides[suivant].style.display = 'block';
|
||||
btn.parentElement.querySelector('.slide-counter').innerText = (suivant + 1) + '/' + slides.length;
|
||||
}
|
||||
|
||||
// --- Carnet de route -----------------------------------------------------------
|
||||
// localStorage lève une exception sur file:// dans certains navigateurs
|
||||
// (Safari, navigation privée) : on l'enveloppe.
|
||||
function memLire(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }
|
||||
function memEcrire(k, v) { try { localStorage.setItem(k, v); } catch (e) {} }
|
||||
|
||||
// Journée à afficher : celle de l'URL (#jour-AAAA-MM-JJ) ou celle de la page.
|
||||
function journalCible() {
|
||||
var m = /#jour-(\d{4}-\d{2}-\d{2})/.exec(window.location.hash || '');
|
||||
return m ? m[1] : (window.JOUR_COURANT || null);
|
||||
}
|
||||
|
||||
function journalDefile(jour) {
|
||||
var panel = document.getElementById('journal-panel');
|
||||
if (!panel || !jour) return false;
|
||||
var el = document.getElementById('ancre-' + jour);
|
||||
if (!el) return false;
|
||||
var r = el.getBoundingClientRect(), pr = panel.getBoundingClientRect();
|
||||
panel.scrollTop += (r.top - pr.top) - 70; // laisse voir le titre du jour
|
||||
var actif = panel.querySelector('.jour-actif');
|
||||
if (actif) actif.classList.remove('jour-actif');
|
||||
el.classList.add('jour-actif');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rejoué plusieurs fois : les CSS distantes (Leaflet, Font Awesome) arrivent
|
||||
// après DOMContentLoaded et décalent les hauteurs.
|
||||
function journalRecale() {
|
||||
var j = journalCible();
|
||||
journalDefile(j);
|
||||
[150, 500, 1200].forEach(function (d) { setTimeout(function () { journalDefile(j); }, d); });
|
||||
}
|
||||
|
||||
function toggleJournal(open) {
|
||||
var panel = document.getElementById('journal-panel');
|
||||
if (!panel) return;
|
||||
panel.classList.toggle('open', open);
|
||||
memEcrire('journalOpen', open ? 'true' : 'false');
|
||||
if (open) journalRecale();
|
||||
}
|
||||
|
||||
function journalInit() {
|
||||
var panel = document.getElementById('journal-panel');
|
||||
if (!panel) return;
|
||||
var vientDuCarnet = /#jour-\d{4}-\d{2}-\d{2}/.test(window.location.hash || '');
|
||||
if (memLire('journalOpen') === 'true' || vientDuCarnet) {
|
||||
panel.classList.add('open');
|
||||
if (vientDuCarnet) memEcrire('journalOpen', 'true');
|
||||
}
|
||||
journalRecale();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
window.addEventListener('DOMContentLoaded', journalInit);
|
||||
} else {
|
||||
journalInit();
|
||||
}
|
||||
window.addEventListener('load', journalRecale);
|
||||
window.addEventListener('hashchange', function () { toggleJournal(true); });
|
||||
@@ -0,0 +1,95 @@
|
||||
{#- =============================================================================
|
||||
Morceaux de HTML insérés par genere_carte.py dans les pages folium.
|
||||
Échappement automatique : seules les variables marquées |safe passent telles
|
||||
quelles. Les chemins arrivent déjà encodés pour les URL (quote()).
|
||||
Les styles sont dans carte.css, le comportement dans carte.js.
|
||||
============================================================================= -#}
|
||||
|
||||
{# Feuille de style et script communs, avec un numéro de version pour que les
|
||||
navigateurs ne gardent pas une ancienne copie en cache. #}
|
||||
{% macro entete(prefixe, version) -%}
|
||||
<link rel="stylesheet" href="{{ prefixe }}assets/carte.css?v={{ version }}">
|
||||
<script src="{{ prefixe }}assets/carte.js?v={{ version }}"></script>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{% macro epingle(couleur, icone) -%}
|
||||
<div class="pin" style="--c:{{ couleur }}"><div class="pin-tete"><i class="fa fa-{{ icone }}"></i></div><div class="pin-tige"></div></div>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# Popup affichée au clic sur la trace d'une journée (carte générale). #}
|
||||
{% macro popup_trace(jour, couleur, lien) -%}
|
||||
<div class="popup-trace"><b>📅 {{ jour }}</b><br><a class="btn-jour" style="--c:{{ couleur }}" href="{{ lien }}" target="_top">Détails</a></div>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# photos : liste de {nom, src (version web), href (original)} #}
|
||||
{% macro popup_photos(photos) -%}
|
||||
<div class="slider-container">
|
||||
<div class="slides">
|
||||
{%- for p in photos %}
|
||||
<div class="slide"{% if not loop.first %} style="display:none"{% endif %}>
|
||||
<div class="file-header">{{ p.nom }}</div>
|
||||
<a href="{{ p.href }}" target="_blank"><img src="{{ p.src }}" alt="{{ p.nom }}" loading="lazy"></a>
|
||||
</div>
|
||||
{%- endfor %}
|
||||
</div>
|
||||
{%- if photos|length > 1 %}
|
||||
<button class="nav-btn prev" onclick="moveSlide(this, -1)">❮</button>
|
||||
<button class="nav-btn next" onclick="moveSlide(this, 1)">❯</button>
|
||||
<div class="slide-counter">1/{{ photos|length }}</div>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# 720p H.264 chargé par défaut (lisible partout), 1080p HEVC à la demande.
|
||||
preload="none" : rien n'est téléchargé avant l'ouverture de la popup. #}
|
||||
{% macro popup_video(nom, mobile, hd, apercu) -%}
|
||||
<div class="popup-video">
|
||||
<div class="header-row">
|
||||
<span class="file-name" title="{{ nom }}">{{ nom }}</span>
|
||||
<div class="quality-controls">
|
||||
<span class="quality-btn active-720" data-src="{{ mobile }}" onclick="changeQuality(this)">720p</span>
|
||||
<span class="quality-btn" data-hevc="1" data-src="{{ hd }}" onclick="changeQuality(this)">1080p</span>
|
||||
</div>
|
||||
</div>
|
||||
<video controls playsinline webkit-playsinline preload="none" src="{{ mobile }}" data-fallback="{{ mobile }}"
|
||||
{%- if apercu %} data-poster="{{ apercu }}"{% endif %}></video>
|
||||
<div class="hevc-note">Le 1080p est encodé en H.265/HEVC, que ce navigateur ne sait pas décoder.
|
||||
La lecture en 720p fonctionne normalement ; pour la haute définition, utilise Safari, Chrome ou Edge.</div>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# contenu : HTML du récit (voyage.md converti), déjà prêt. #}
|
||||
{% macro journal(contenu) -%}
|
||||
<div id="journal-panel">
|
||||
<button class="close-btn" onclick="toggleJournal(false)">×</button>
|
||||
<div class="journal-content">{{ contenu|safe }}</div>
|
||||
</div>
|
||||
<button class="btn-flottant btn-journal" onclick="toggleJournal(true)">📖 Journal</button>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{% macro bouton_accueil(lien) -%}
|
||||
<a class="btn-flottant btn-accueil" href="{{ lien }}" target="_top">🏠 Accueil</a>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{% macro bouton_gpx(lien, libelle) -%}
|
||||
<a class="btn-flottant btn-gpx" href="{{ lien }}" download>{{ libelle }}</a>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# jours : liste de {libelle, lien, couleur} #}
|
||||
{% macro barre_jours(retour, jours) -%}
|
||||
<nav class="sidebar">
|
||||
<a class="btn-retour" href="{{ retour }}" target="_top">⬅ Retour</a>
|
||||
<div class="sidebar-titre">PAR JOURS</div>
|
||||
{%- for j in jours %}
|
||||
<a class="jour" style="--c:{{ j.couleur }}" href="{{ j.lien }}" target="_top">{{ j.libelle }}</a>
|
||||
{%- endfor %}
|
||||
</nav>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,41 @@
|
||||
{#- Morceaux de HTML de la carte globale (genere_carte_globale.py).
|
||||
Les liens visent target="_top" : la carte est affichée dans un cadre
|
||||
de la page d'accueil, c'est toute la page qui doit changer. -#}
|
||||
|
||||
{% macro popup_voyage(titre, periode, km, lien) -%}
|
||||
<div style="font-family:sans-serif; text-align:center; min-width:160px">
|
||||
<b style="font-size:14px; text-transform:capitalize">{{ titre }}</b><br>
|
||||
<span style="color:#777; font-size:12px">{{ periode }}{% if km %} · {{ "%.0f"|format(km) }} km{% endif %}</span><br>
|
||||
<a href="{{ lien }}" target="_top" style="display:inline-block; margin-top:10px; padding:7px 14px; border-radius:16px; background:#3498db; color:#fff; text-decoration:none; font-weight:bold">Voir le voyage</a>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{% macro popup_mymaps(nom, description) -%}
|
||||
<div style="font-family:sans-serif; max-height:240px; overflow:auto">
|
||||
<b style="font-size:14px">{{ nom }}</b>
|
||||
{%- if description %}<div style="margin-top:6px; font-size:12px; color:#444">{{ description|liens_et_lignes }}</div>{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# Visible seulement quand la carte est ouverte seule (voir script_page). #}
|
||||
{% macro bouton_accueil() -%}
|
||||
<a id="retour-accueil" href="index.html" style="display:none; position:fixed; bottom:25px; left:15px; z-index:1000; padding:10px 16px; border-radius:10px; background:#333; color:#fff; font:bold 14px sans-serif; text-decoration:none; box-shadow:0 4px 10px rgba(0,0,0,.4)">🏠 Accueil</a>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# Exécuté au chargement complet : folium crée la carte plus bas dans la page. #}
|
||||
{% macro script_page(carte) -%}
|
||||
window.addEventListener('load', function () {
|
||||
if (window.self !== window.top) return;
|
||||
{{ carte }}.scrollWheelZoom.enable();
|
||||
document.getElementById('retour-accueil').style.display = 'block';
|
||||
});
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
{# Libellé d'un voyage dans la légende (sélecteur de calques). #}
|
||||
{% macro legende(titre, couleur) -%}
|
||||
<span style="display:inline-block; width:18px; height:4px; margin:0 6px 3px 0; border-radius:2px; vertical-align:middle; background:{{ couleur }}"></span><span style="text-transform:capitalize">{{ titre }}</span>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,16 @@
|
||||
{#- Liste complète d'une rubrique (voyages.html, balades.html…). -#}
|
||||
{% extends "base_accueil.html" %}
|
||||
{% from "base_accueil.html" import carte %}
|
||||
|
||||
{% block titre %}{{ r.titre }} — {{ titre_site }}{% endblock %}
|
||||
{% block entete %}{{ r.titre }}{% endblock %}
|
||||
{% block sous_titre %}{{ r.voyages|length }} {{ r.unite }}{% endblock %}
|
||||
|
||||
{% block contenu %}
|
||||
<a class="retour" href="index.html">← {{ titre_site }}</a>
|
||||
<div class="grid">
|
||||
{% for v in r.voyages %}
|
||||
{{ carte(v) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user