(function () { 'use strict'; var card = null; var hideTimer = null; var currentUrl = null; /* ── Build the floating card once ── */ function buildCard() { if (card) return; card = document.createElement('div'); card.id = 'apc-card'; card.innerHTML = '
' + '
' + '
' + '
Chargement…
' + '
' + '' + '
'; document.body.appendChild(card); } /* ── Position card near cursor, avoiding viewport edges ── */ function place(e) { if (!card) return; var W = 360, H = 300; var x = e.clientX + 24; var y = e.clientY - 60; if (x + W > window.innerWidth - 16) x = e.clientX - W - 24; if (y + H > window.innerHeight - 16) y = window.innerHeight - H - 16; if (y < 8) y = 8; card.style.left = x + 'px'; card.style.top = y + 'px'; } /* ── Fetch OG data and populate card ── */ async function loadPreview(url, sourceFallback, titleFallback) { try { var resp = await fetch('/api/preview?url=' + encodeURIComponent(url)); var data = await resp.json(); if (currentUrl !== url) return; // stale var imgEl = document.getElementById('apc-img'); var titleEl = document.getElementById('apc-title'); var descEl = document.getElementById('apc-desc'); var siteEl = document.getElementById('apc-site'); var placeholder = card.querySelector('.apc-img-placeholder'); siteEl.textContent = data.site_name || sourceFallback || ''; titleEl.textContent = data.title || titleFallback || 'Article'; descEl.textContent = data.description || ''; if (data.image) { imgEl.onload = function () { imgEl.style.display = 'block'; placeholder.style.display = 'none'; }; imgEl.onerror = function () { imgEl.style.display = 'none'; placeholder.style.display = 'flex'; }; imgEl.src = data.image; } else { imgEl.style.display = 'none'; placeholder.style.display = 'flex'; } card.classList.remove('apc-loading'); card.classList.add('apc-visible'); } catch (err) { if (currentUrl !== url) return; document.getElementById('apc-title').textContent = titleFallback || 'Aperçu indisponible'; card.classList.remove('apc-loading'); card.classList.add('apc-visible'); } } /* ── Show card on hover ── */ function onEnter(e) { var el = this; var url = el.dataset.previewUrl || el.getAttribute('href') || ''; if (!url || url === '#') return; buildCard(); clearTimeout(hideTimer); var titleFb = el.dataset.title || ''; var sourceFb = el.dataset.source || ''; if (currentUrl === url) { place(e); card.style.display = 'flex'; card.classList.add('apc-visible'); return; } currentUrl = url; card.classList.remove('apc-visible'); card.classList.add('apc-loading'); card.style.display = 'flex'; place(e); /* reset content */ document.getElementById('apc-title').textContent = 'Chargement…'; document.getElementById('apc-desc').textContent = ''; document.getElementById('apc-site').textContent = sourceFb; var imgEl = document.getElementById('apc-img'); imgEl.src = ''; imgEl.style.display = 'none'; card.querySelector('.apc-img-placeholder').style.display = 'flex'; loadPreview(url, sourceFb, titleFb); } function onMove(e) { place(e); } function onLeave() { hideTimer = setTimeout(function () { if (card) card.classList.remove('apc-visible'); }, 180); } /* ── Attach listeners to all unbound news links ── */ function attach(root) { var links = (root || document).querySelectorAll('.analyse-news-link:not([data-apc-bound])'); links.forEach(function (el) { el.setAttribute('data-apc-bound', '1'); el.addEventListener('mouseenter', onEnter); el.addEventListener('mousemove', onMove); el.addEventListener('mouseleave', onLeave); }); } /* ── Bootstrap ── */ document.addEventListener('DOMContentLoaded', function () { attach(); }); /* Re-attach when Dash re-renders the news feed */ var observer = new MutationObserver(function (mutations) { var needAttach = false; mutations.forEach(function (m) { if (m.addedNodes.length) needAttach = true; }); if (needAttach) attach(); }); observer.observe(document.body, { childList: true, subtree: true }); })();