/* ============================================================================= Pages d'un voyage : vidéos, diaporamas photo et carnet de route. Copié dans /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); });