/ 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);
})();