/ Delta FM — Script analytics v2.5.12 — 2026-03-27 // === GOOGLE SEARCH CONSOLE === var meta = document.createElement('meta'); meta.name = 'google-site-verification'; meta.content = 'EmC5o_uhZs1PAXpZiZRSAhJPaVZt2jsWCvWODxbSvzk'; document.head.appendChild(meta); // === VIEWPORT FIT (safe areas iPhone) === var vp = document.querySelector('meta[name="viewport"]'); if (vp && vp.content.indexOf('viewport-fit') === -1) { vp.content += ', viewport-fit=cover'; } // === GOOGLE ANALYTICS GA4 === var gtagScript = document.createElement('script'); gtagScript.async = true; gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-Y5QK9GGG17'; document.head.appendChild(gtagScript); window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-Y5QK9GGG17'); // === MOBILE : gestion des dropdowns HeaderSources (1 seul clic) === // Capture phase : intercepter AVANT Bootstrap pour éviter le double-toggle document.addEventListener('click', function(e) { // Clic sur un item dans un dropdown ouvert → fermer le dropdown var dropItem = e.target.closest('.HeaderSources .dropdown-menu .dropdown-item'); if (dropItem) { // Laisser le clic passer (l'item doit fonctionner), mais fermer après setTimeout(function() { document.querySelectorAll('.HeaderSources .dropdown-menu.show').forEach(function(m) { m.classList.remove('show'); var p = m.closest('.nav-item'); if (p) p.classList.remove('show'); }); }, 100); return; } var toggle = e.target.closest('.HeaderSources .dropdown-toggle, .HeaderSources .nav-link'); if (toggle && toggle.closest('.HeaderSources')) { var li = toggle.closest('.nav-item'); var menu = li ? li.querySelector('.dropdown-menu') : null; // Pas de dropdown → laisser le clic passer (ex: lien Podcasts) if (!menu) return; e.preventDefault(); e.stopImmediatePropagation(); var isOpen = menu.classList.contains('show'); // Fermer tous les autres d'abord document.querySelectorAll('.HeaderSources .dropdown-menu.show').forEach(function(m) { if (m !== menu) { m.classList.remove('show'); var p = m.closest('.nav-item'); if (p) p.classList.remove('show'); } }); if (isOpen) { menu.classList.remove('show'); if (li) li.classList.remove('show'); } else { menu.classList.add('show'); if (li) li.classList.add('show'); } return; } // Empêcher le scroll sur les liens # var link = e.target.closest('.HeaderSources a[href="#"]'); if (link) e.preventDefault(); }, true); // Fermer les dropdowns HeaderSources si clic en dehors OU sur un lien sans dropdown document.addEventListener('click', function(e) { var inSources = e.target.closest('.HeaderSources'); var inDropdown = e.target.closest('.HeaderSources .dropdown-menu'); var isToggle = e.target.closest('.HeaderSources .dropdown-toggle'); // Fermer si : clic en dehors, OU clic sur un nav-item sans dropdown (ex: Podcasts) if (!inSources || (!inDropdown && !isToggle)) { document.querySelectorAll('.HeaderSources .dropdown-menu.show').forEach(function(menu) { menu.classList.remove('show'); var li = menu.closest('.nav-item'); if (li) li.classList.remove('show'); }); } }); // === FORCER IsCompact sur la navbar (le CMS le retire parfois) === (function() { function forceCompact() { var header = document.querySelector('.Header'); if (header && !header.classList.contains('IsCompact')) { header.classList.add('IsCompact'); } } forceCompact(); new MutationObserver(forceCompact).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] }); })(); // === DARK MODE TOGGLE === (function() { // Restaurer la préférence sauvegardée var saved = localStorage.getItem('darkMode'); if (saved === 'true') { document.body.classList.add('mode--dark'); document.body.classList.remove('mode--light'); } function createToggle() { var container = document.querySelector('.HeaderContainer'); var headerSearch = document.querySelector('.HeaderSearch'); if (!container || !headerSearch || document.querySelector('.dark-mode-btn')) return; var btn = document.createElement('button'); btn.className = 'dark-mode-btn'; btn.type = 'button'; btn.setAttribute('aria-label', 'Mode sombre / clair'); container.insertBefore(btn, headerSearch); // Icônes Bootstrap (bi-sun-fill / bi-moon-stars-fill) en blanc var sunIcon = ''; var moonIcon = ''; function updateIcon() { var isDark = document.body.classList.contains('mode--dark'); btn.innerHTML = isDark ? sunIcon : moonIcon; } btn.addEventListener('click', function() { var isDark = !document.body.classList.contains('mode--dark'); document.body.classList.toggle('mode--dark', isDark); document.body.classList.toggle('mode--light', !isDark); localStorage.setItem('darkMode', isDark); updateIcon(); // Sync mobile var mobBtn = document.querySelector('.dark-mode-btn-mobile'); if (mobBtn) mobBtn.innerHTML = btn.innerHTML; }); updateIcon(); } // Mobile : aussi créer le bouton dans HeaderAside function createMobileToggle() { if (window.innerWidth >= 768) return; var aside = document.querySelector('.HeaderAside'); if (!aside || aside.querySelector('.dark-mode-btn-mobile')) return; var btn = document.createElement('button'); btn.className = 'dark-mode-btn-mobile'; btn.type = 'button'; btn.setAttribute('aria-label', 'Mode sombre / clair'); var sources = aside.querySelector('.HeaderSources'); if (sources) sources.parentNode.insertBefore(btn, sources.nextSibling); else aside.insertBefore(btn, aside.firstChild); var sunIcon = ''; var moonIcon = ''; function updateIcon() { var isDark = document.body.classList.contains('mode--dark'); btn.innerHTML = isDark ? sunIcon : moonIcon; } btn.addEventListener('click', function() { var isDark = !document.body.classList.contains('mode--dark'); document.body.classList.toggle('mode--dark', isDark); document.body.classList.toggle('mode--light', !isDark); localStorage.setItem('darkMode', isDark); updateIcon(); // Sync le bouton desktop aussi var deskBtn = document.querySelector('.dark-mode-btn'); if (deskBtn) { deskBtn.innerHTML = btn.innerHTML; } }); updateIcon(); } setTimeout(createToggle, 1500); setTimeout(createToggle, 4000); setTimeout(createMobileToggle, 2000); setTimeout(createMobileToggle, 5000); })(); // === DESKTOP : bouton play/pause dans la navbar === (function() { if (window.innerWidth < 768) return; function getPlayerBtn() { var btns = document.querySelectorAll('.Player .PlayerControl > button.btn, .Player .PlayerControl > div > button.btn'); var found = null; btns.forEach(function(b) { if (!b.closest('.PlayerSmall')) found = b; }); if (!found && btns.length) found = btns[0]; return found; } function setup() { // Insérer DANS le HeaderContainer, juste avant HeaderSearch var container = document.querySelector('.HeaderContainer'); var headerSearch = document.querySelector('.HeaderSearch'); if (!container || !headerSearch || document.querySelector('.navbar-play-btn')) return; var btn = document.createElement('button'); btn.className = 'navbar-play-btn'; btn.type = 'button'; btn.setAttribute('aria-label', 'Lecture / Pause'); // Insérer dans le HeaderContainer, avant HeaderSearch container.insertBefore(btn, headerSearch); // Copier le contenu du vrai bouton player function syncIcon() { var rb = getPlayerBtn(); if (!rb) return; var newHtml = rb.innerHTML; if (btn.getAttribute('data-html') !== newHtml) { btn.innerHTML = newHtml; btn.setAttribute('data-html', newHtml); } } btn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); var rb = getPlayerBtn(); if (rb) rb.click(); setTimeout(syncIcon, 200); }); syncIcon(); setInterval(syncIcon, 500); } setTimeout(setup, 1500); setTimeout(setup, 4000); })(); // === SCRIPT PUB === var adScript = document.createElement('script'); adScript.async = true; adScript.src = 'https://ads.sportslocalmedia.com/slm.prebid.deltafm.js'; document.head.appendChild(adScript); // === MASQUER LE PLAYER AU SCROLL DESCENDANT, RÉAFFICHER AU SCROLL MONTANT === (function() { var lastScrollY = window.scrollY; var hidden = false; var seeking = false; // Bloquer le scroll-hide pendant qu'on drag la progress bar document.addEventListener('mousedown', function(e) { if (e.target.closest('.PlayerProgressRange')) seeking = true; }); document.addEventListener('touchstart', function(e) { if (e.target.closest('.PlayerProgressRange')) seeking = true; }, { passive: true }); document.addEventListener('mouseup', function() { seeking = false; }); document.addEventListener('touchend', function() { seeking = false; }, { passive: true }); function getTarget() { // En mobile : le HeaderAside (contient HeaderSources + PlayerSmall) ; en desktop : le player footer if (window.innerWidth < 768) { var aside = document.querySelector('.HeaderAside'); if (aside) return aside; } var found = null; document.querySelectorAll('.Player').forEach(function(p) { if (!p.classList.contains('PlayerSmall')) found = p; }); return found; } // Réafficher le player (appelé au clic sur play) function showPlayer() { if (!hidden) return; var p = getTarget(); if (!p) return; var showTransform = window.innerWidth >= 768 ? 'translateX(-50%)' : 'none'; p.style.setProperty('transition', 'transform 0.3s ease', 'important'); p.style.setProperty('transform', showTransform, 'important'); hidden = false; } // Clic sur un bouton play (podcast, webradio, station) → réafficher le player document.addEventListener('click', function(e) { if (e.target.closest('.webradio-overlay .btn, .PlayerControl button, .btn-player, [class*="play"], .broadcast-webradio-logo, .webradio .btn')) { showPlayer(); } }); window.addEventListener('scroll', function() { if (seeking) return; var p = getTarget(); if (!p) return; var currentScrollY = window.scrollY; var delta = currentScrollY - lastScrollY; if (delta > 5 && !hidden) { // Fermer tous les dropdowns/tooltips ouverts avant de masquer document.querySelectorAll('.dropdown-menu.show').forEach(function(menu) { // Tenter la fermeture via Bootstrap (click sur le toggle) var parent = menu.closest('.nav-item, .dropdown, .dropup, .PlayerRadio, .PlayerSources, .PlayerActions'); var toggle = parent ? parent.querySelector('.dropdown-toggle, [aria-expanded="true"]') : null; if (toggle) toggle.click(); // Fallback : forcer la fermeture manuelle setTimeout(function() { menu.classList.remove('show'); if (parent) parent.classList.remove('show'); if (toggle) { toggle.classList.remove('show'); toggle.setAttribute('aria-expanded', 'false'); } }, 50); }); // Fermer le tooltip partage (Popper.js) var tooltip = document.querySelector('.PlayerActions .tooltip'); if (tooltip) { var popper = tooltip.closest('[data-popper-placement]'); if (popper) popper.style.display = 'none'; else tooltip.style.display = 'none'; } var hideTransform = (window.innerWidth >= 768 ? 'translateX(-50%) ' : '') + 'translateY(calc(100% + 30px))'; p.style.setProperty('transition', 'transform 0.3s ease', 'important'); p.style.setProperty('transform', hideTransform, 'important'); hidden = true; } else if (delta < -5 && hidden) { var showTransform = window.innerWidth >= 768 ? 'translateX(-50%)' : 'none'; p.style.setProperty('transition', 'transform 0.3s ease', 'important'); p.style.setProperty('transform', showTransform, 'important'); hidden = false; } lastScrollY = currentScrollY; }, { passive: true }); })(); // === MASQUER LES H2 VIDES (espaces uniquement) === document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.heading h2.title').forEach(function(h2) { if (!h2.textContent.trim()) { h2.style.display = 'none'; } }); }); // === FERMER LES DROPDOWNS AU CLIC === document.addEventListener('click', function(e) { // Ignorer les clics dans HeaderSources (gérés par le CMS) if (e.target.closest('.HeaderSources')) return; // Clic sur un item dans un dropdown → fermer ce dropdown var item = e.target.closest('.dropdown-menu a, .dropdown-menu button'); if (item) { var menu = item.closest('.dropdown-menu'); // Ne pas fermer les HeaderSources (gérés par le CMS) if (menu && !menu.closest('.HeaderSources')) { menu.classList.remove('show'); var li = menu.closest('.nav-item.dropdown'); if (li) li.classList.remove('show'); var toggle = li ? li.querySelector('.dropdown-toggle') : null; if (toggle) { toggle.classList.remove('show'); toggle.setAttribute('aria-expanded', 'false'); } } } // Fermer le tooltip partage (PlayerActions) au clic sur un bouton ou en dehors var tooltip = document.querySelector('.PlayerActions .tooltip'); if (tooltip) { var shareBtn = e.target.closest('.PlayerActions .tooltip button'); var triggerBtn = e.target.closest('.PlayerActions > ul > li > button'); if (shareBtn || !triggerBtn) { var popperWrapper = tooltip.closest('[data-popper-placement]'); if (popperWrapper) popperWrapper.style.display = 'none'; else tooltip.style.display = 'none'; } } // Fermer les autres dropdowns du player après un court délai // (laisser le CMS ouvrir le nouveau dropdown d'abord) var clickedToggle = e.target.closest('.dropdown-toggle, .nav-link'); if (clickedToggle) { var clickedParent = clickedToggle.closest('.nav-item, .dropdown, .dropup, .PlayerRadio, .PlayerSources'); setTimeout(function() { document.querySelectorAll('.Player .dropdown-menu.show').forEach(function(menu) { if (menu.closest('.HeaderSources')) return; var parent = menu.closest('.nav-item, .dropdown, .dropup, .PlayerRadio, .PlayerSources'); if (parent === clickedParent) return; menu.classList.remove('show'); if (parent) { parent.classList.remove('show'); var toggle = parent.querySelector('.dropdown-toggle'); if (toggle) { toggle.classList.remove('show'); toggle.setAttribute('aria-expanded', 'false'); } } }); }, 50); } }); // === FERMER DROPDOWNS PLAYER AU CLIC EN DEHORS === document.addEventListener('click', function(e) { // Si le clic est dans un dropdown ouvert ou sur un toggle, ne rien faire if (e.target.closest('.dropdown-menu')) return; if (e.target.closest('.dropdown-toggle')) return; // Fermer tous les dropdowns ouverts (player footer + PlayerBlock) var openMenus = document.querySelectorAll('.Player .dropdown-menu.show, .PlayerBlock .dropdown-menu.show'); openMenus.forEach(function(menu) { if (menu.closest('.HeaderSources')) return; // Cliquer le toggle pour déclencher la fermeture Bootstrap native var parent = menu.closest('.nav-item, .dropdown, .dropup, .PlayerRadio, .PlayerSources, .PlayerActions'); var toggle = parent ? parent.querySelector('.dropdown-toggle, [aria-expanded="true"]') : null; if (toggle) { toggle.click(); } // Fallback : forcer la fermeture manuellement setTimeout(function() { menu.classList.remove('show'); if (parent) parent.classList.remove('show'); if (toggle) { toggle.classList.remove('show'); toggle.setAttribute('aria-expanded', 'false'); } }, 100); }); }); // === PLACEHOLDER PUBS VIDES === setInterval(function() { document.querySelectorAll('.akcelo-wrapper').forEach(function(wrapper) { var slot = wrapper.querySelector('[id^="Deltafm_"]'); if (slot && !slot.querySelector('iframe, img, ins')) { wrapper.classList.add('akcelo-empty'); } else { wrapper.classList.remove('akcelo-empty'); } }); }, 2000); // === POPUP PUB 1ère PAGE PUIS TOUTES LES 3 PAGES === (function() { var count = parseInt(sessionStorage.getItem('pageCount') || '0'); function checkPopup() { count++; sessionStorage.setItem('pageCount', count); var old = document.getElementById('adPopupOverlay'); if (old) old.remove(); if (count === 1 || count % 5 === 0) { document.body.classList.add('popup-open'); var overlay = document.createElement('div'); overlay.id = 'adPopupOverlay'; overlay.innerHTML = '
' + '' + '
' + '
' + '
' + '
' + '
' + '
'; document.body.appendChild(overlay); window.slmadshb = window.slmadshb || {}; window.slmadshb.que = window.slmadshb.que || []; window.slmadshb.que.push(function() { window.slmadshb.display('Deltafm_Incontent'); }); var btn = document.getElementById('adPopupClose'); var sec = 3; btn.textContent = sec; var timer = setInterval(function() { sec--; if (sec > 0) { btn.textContent = sec; } else { clearInterval(timer); btn.textContent = '\u00D7'; btn.disabled = false; } }, 1000); function closePopup() { overlay.remove(); document.body.classList.remove('popup-open'); } btn.addEventListener('click', function() { if (!btn.disabled) closePopup(); }); overlay.addEventListener('click', function(e) { if (e.target === overlay && !btn.disabled) closePopup(); }); } } checkPopup(); var lastUrl = location.href; new MutationObserver(function() { if (location.href !== lastUrl) { lastUrl = location.href; checkPopup(); // Restaurer le logo si on quitte une page Delta+ if (window._originalLogoSrc && location.pathname.indexOf('delta-plus') === -1) { var img = document.querySelector('.HeaderLogo img'); if (img && img.src.indexOf('DPL.png') !== -1) { img.src = window._originalLogoSrc; img.alt = window._originalLogoAlt || 'Delta FM'; } } } }).observe(document.body, { childList: true, subtree: true }); })(); // === INJECTER NOM STATION SOUS LE LOGO (PlayerRadio) === (function() { function injectStationNames() { document.querySelectorAll('.PlayerRadio .dropdown-item').forEach(function(item) { if (item.querySelector('.station-name')) return; var img = item.querySelector('img'); if (!img) return; var alt = (img.alt || '').trim(); if (!alt) return; var span = document.createElement('span'); span.className = 'station-name'; span.textContent = alt; item.appendChild(span); }); } function init() { injectStationNames(); new MutationObserver(injectStationNames).observe(document.body, { childList: true, subtree: true }); } if (document.body) { init(); } else { document.addEventListener('DOMContentLoaded', init); } // Fallback : retry toutes les 2s au cas où le player se charge tard setInterval(injectStationNames, 2000); })(); // === MASQUER ZONE + RETOUR AU LIVE QUAND PAS DELTA FM === function isDeltaFMStation(name) { var n = (name || '').trim().toLowerCase(); // Delta FM principal + toutes les zones (le CMS renomme la station avec le nom de zone) var zones = ['delta fm', 'deltafm', 'dunkerque', 'saint-omer', 'st omer', 'flandres', 'boulogne', 'boulogne/mer', 'calais', 'le touquet']; return zones.indexOf(n) !== -1; } function applyRadioState(stationName) { var show = isDeltaFMStation(stationName); if (show) { document.body.classList.remove('not-delta-fm'); } else { document.body.classList.add('not-delta-fm'); } } // Détecter la station active en lisant le DOM (plus fiable que les clics) function detectActiveStation() { // 1. Priorité : lire le logo actif dans PlayerRadio (change quand on sélectionne une webradio) var radioToggle = document.querySelector('.PlayerRadio > .dropdown-toggle img, .PlayerRadio > button img'); if (radioToggle) { var alt = (radioToggle.alt || '').trim(); if (alt) return alt; } // 2. Fallback : lire le texte du bouton toggle PlayerSources (nom de zone) var toggle = document.querySelector('.PlayerSources .dropdown-toggle, .PlayerSources .nav-link'); if (toggle) { var text = toggle.textContent.trim(); if (text) return text; } return 'DELTA FM'; } // Intercepter les clics sur une station → appliquer immédiatement document.addEventListener('click', function(e) { var btn = e.target.closest('.PlayerRadio .dropdown-item'); if (!btn) return; var img = btn.querySelector('img'); var alt = img ? (img.alt || '').trim() : ''; if (!alt) { var span = btn.querySelector('.station-name'); alt = span ? span.textContent.trim() : btn.textContent.trim(); } if (alt) { // Appliquer immédiatement au clic (avant que React mette à jour le DOM) applyRadioState(alt); } }); // Surveiller en continu la station active (DOM reading) (function() { var checkTimer = null; function checkRadioState() { var station = detectActiveStation(); applyRadioState(station); } if (document.body) checkRadioState(); else document.addEventListener('DOMContentLoaded', checkRadioState); setTimeout(checkRadioState, 1000); setTimeout(checkRadioState, 3000); // Observer les changements DOM — throttle court (100ms) pour réagir vite new MutationObserver(function() { if (checkTimer) return; checkTimer = setTimeout(function() { checkTimer = null; checkRadioState(); }, 100); }).observe(document.body, { childList: true, subtree: true }); })(); // === PODCAST : seek → auto-resume play === (function() { function resumeAfterSeek() { setTimeout(function() { // Chercher le bouton play/pause var btns = document.querySelectorAll('.Player .PlayerControl button.btn'); btns.forEach(function(btn) { var svg = btn.querySelector('svg'); if (!svg) return; // Si le SVG contient un triangle (play) et pas de barres (pause) → cliquer play var paths = svg.querySelectorAll('path, polygon, rect'); var isPaused = true; paths.forEach(function(p) { // Les barres de pause ont généralement 2 rect côte à côte if (p.tagName === 'rect') isPaused = false; }); // Alternative : vérifier aria-label ou title var label = (btn.getAttribute('aria-label') || btn.getAttribute('title') || '').toLowerCase(); if (label.includes('pause') || label.includes('stop')) isPaused = false; if (isPaused) btn.click(); }); }, 300); } document.addEventListener('mouseup', function(e) { if (e.target.closest('.PlayerProgressRange')) resumeAfterSeek(); }); document.addEventListener('touchend', function(e) { if (e.target.closest('.PlayerProgressRange')) resumeAfterSeek(); }); })(); // === DURÉE PODCAST : afficher dans le player === (function() { var durationTimer = null; function setupDuration() { var bar = document.querySelector('.PlayerProgressRange'); // Signaler au CSS si la progress bar podcast existe if (bar) document.body.classList.add('has-progress-bar'); else { document.body.classList.remove('has-progress-bar'); return; } // Forcer visibilité des spans de temps var curSpan = bar.querySelector('.value .current'); var maxSpan = bar.querySelector('.value .max'); if (curSpan) curSpan.style.visibility = 'visible'; if (maxSpan) maxSpan.style.visibility = 'visible'; // Créer un élément durée dans le PlayerContainer si pas déjà fait var container = document.querySelector('.hasPlayerBroadcast .Player .PlayerContainer, .Player.hasPlayerBroadcast .PlayerContainer'); if (!container) return; if (container.querySelector('.podcast-duration')) return; var range = bar.querySelector('input[type="range"]'); if (!range) return; var dur = document.createElement('span'); dur.className = 'podcast-duration'; // Insérer avant le PlayerControl var playerControl = container.querySelector('.PlayerControl'); if (playerControl) { container.insertBefore(dur, playerControl); } else { container.appendChild(dur); } // Mettre à jour le temps affiché function formatTime(sec) { sec = Math.floor(sec); var m = Math.floor(sec / 60); var s = sec % 60; return (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s; } function updateTime() { var current = parseFloat(range.value) || 0; var max = parseFloat(range.max) || 0; dur.textContent = formatTime(current) + ' / ' + formatTime(max); // Synchroniser la barre blanche et le curseur avec la position réelle if (max > 0) { var pct = (current / max) * 100; var progress = bar.querySelector('.progress'); if (progress) progress.style.width = pct + '%'; var value = bar.querySelector('.value'); if (value) value.style.left = pct + '%'; } } range.addEventListener('input', updateTime); range.addEventListener('change', updateTime); setInterval(updateTime, 500); updateTime(); } function watchForProgressBar() { setupDuration(); new MutationObserver(function() { if (durationTimer) return; durationTimer = setTimeout(function() { durationTimer = null; setupDuration(); }, 500); }).observe(document.body, { childList: true, subtree: true }); } if (document.body) watchForProgressBar(); else document.addEventListener('DOMContentLoaded', watchForProgressBar); })(); // === DÉFILEMENT TITRAGE PLAYER (marquee texte trop long) === (function() { function setupMarquee() { var selectors = ['.Player .PlayerResume p.title', '.Player .PlayerResume p.artist']; selectors.forEach(function(sel) { document.querySelectorAll(sel).forEach(function(p) { if (p.closest('.PlayerBlock') || p.closest('.PlayerDetails')) return; var existingSpan = p.querySelector('span.marquee-inner'); var textOverflows = p.scrollWidth > p.clientWidth; if (!textOverflows) { // Pas de débordement : dé-wrapper si wrappé if (existingSpan) { p.textContent = existingSpan.textContent; p.classList.remove('has-overflow'); } return; } // Déborde : mettre à jour si déjà wrappé if (existingSpan) { var overflow = existingSpan.scrollWidth - p.clientWidth; if (overflow > 0) { existingSpan.style.setProperty('--scroll-distance', '-' + overflow + 'px'); existingSpan.style.animationDuration = Math.min(10, Math.max(4, overflow / 30)) + 's'; p.classList.add('has-overflow'); } return; } // Déborde et pas encore wrappé : wrapper var text = p.textContent; var span = document.createElement('span'); span.className = 'marquee-inner'; span.textContent = text; p.textContent = ''; p.appendChild(span); var overflow = span.scrollWidth - p.clientWidth; if (overflow > 0) { span.style.setProperty('--scroll-distance', '-' + overflow + 'px'); span.style.animationDuration = Math.min(10, Math.max(4, overflow / 30)) + 's'; p.classList.add('has-overflow'); } }); }); } // Observer les changements de titre/artiste (React met à jour le DOM) function observePlayer() { document.querySelectorAll('.Player .PlayerResume').forEach(function(resume) { if (resume.closest('.PlayerBlock') || resume.closest('.PlayerDetails')) return; if (resume.dataset.marqueeObs) return; resume.dataset.marqueeObs = 'true'; new MutationObserver(function() { setTimeout(setupMarquee, 200); }).observe(resume, { childList: true, subtree: true, characterData: true }); }); } setTimeout(function() { setupMarquee(); observePlayer(); }, 2000); setTimeout(function() { setupMarquee(); observePlayer(); }, 5000); setInterval(function() { setupMarquee(); observePlayer(); }, 10000); })();