/** * Script de compte à rebours pour la page de maintenance */ /** * Convertit une date ISO 8601 en timestamp, compatible Safari. * * Safari renvoie Invalid Date pour les formats non strictement ISO * (ex. « 2025-10-11 00:00:00 » émis historiquement par PHP) : la chaîne * est donc analysée manuellement. Sans fuseau explicite, la date est * interprétée en UTC — PHP émet de toute façon toujours un décalage. * * @param {string|number} value Date ISO 8601 (ou timestamp déjà numérique) * @returns {number} Timestamp en millisecondes */ function parseTargetDate(value) { if (typeof value === 'number') { return new Date(value).getTime(); } const match = String(value).match( /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?$/ ); if (!match) { // Repli sur l'analyse native pour les formats inattendus return new Date(value).getTime(); } const [, year, month, day, hours = '0', minutes = '0', seconds = '0', offset] = match; let timestamp = Date.UTC( parseInt(year, 10), parseInt(month, 10) - 1, parseInt(day, 10), parseInt(hours, 10), parseInt(minutes, 10), parseInt(seconds, 10) ); if (offset && offset !== 'Z') { // Un décalage « +02:00 » signifie « 2 h d'avance sur UTC » : on le soustrait const sign = offset[0] === '+' ? 1 : -1; const offsetDigits = offset.slice(1).replace(':', ''); const offsetMinutes = sign * ( parseInt(offsetDigits.slice(0, 2), 10) * 60 + parseInt(offsetDigits.slice(2, 4), 10) ); timestamp -= offsetMinutes * 60 * 1000; } return timestamp; } class CountdownTimer { constructor(targetDate) { this.targetDate = parseTargetDate(targetDate); this.elements = { days: document.getElementById('countdown-days'), hours: document.getElementById('countdown-hours'), minutes: document.getElementById('countdown-minutes'), seconds: document.getElementById('countdown-seconds') }; this.start(); } start() { // Mise à jour immédiate this.updateTimer(); // Mise à jour chaque seconde this.interval = setInterval(() => { this.updateTimer(); }, 1000); } updateTimer() { const now = new Date().getTime(); const distance = this.targetDate - now; if (distance < 0) { // Le compte à rebours est terminé this.stop(); this.onComplete(); return; } const days = Math.floor(distance / (1000 * 60 * 60 * 24)); const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((distance % (1000 * 60)) / 1000); // Mise à jour des éléments du DOM if (this.elements.days) this.elements.days.textContent = this.formatNumber(days); if (this.elements.hours) this.elements.hours.textContent = this.formatNumber(hours); if (this.elements.minutes) this.elements.minutes.textContent = this.formatNumber(minutes); if (this.elements.seconds) this.elements.seconds.textContent = this.formatNumber(seconds); } formatNumber(num) { return num.toString().padStart(2, '0'); } stop() { if (this.interval) { clearInterval(this.interval); } } onComplete() { // Rediriger vers la page principale quand le compte à rebours est terminé window.location.href = '/'; } } // Animation des éléments au chargement document.addEventListener('DOMContentLoaded', function() { // Initialiser le compte à rebours si les éléments existent if (document.getElementById('countdown-days')) { // La date cible est fournie par PHP if (typeof COUNTDOWN_TARGET_DATE !== 'undefined') { new CountdownTimer(COUNTDOWN_TARGET_DATE); } } // Animation d'apparition progressive const elements = document.querySelectorAll('.countdown-container > *'); elements.forEach((element, index) => { element.style.opacity = '0'; element.style.transform = 'translateY(30px)'; setTimeout(() => { element.style.transition = 'opacity 0.8s ease, transform 0.8s ease'; element.style.opacity = '1'; element.style.transform = 'translateY(0)'; }, 100 * (index + 1)); }); }); // Gérer la fermeture propre du timer window.addEventListener('beforeunload', function() { // Arrêter le timer si il existe if (window.countdownTimer) { window.countdownTimer.stop(); } });