(function () { if (window.passlordForumsEnhancementsBootstrapped) { return; } window.passlordForumsEnhancementsBootstrapped = true; function storageGet(key) { try { return window.localStorage ? window.localStorage.getItem(key) : null; } catch (error) { return null; } } function storageSet(key, value) { try { if (window.localStorage) { window.localStorage.setItem(key, value); } } catch (error) { } } function clipText(text, maxLength) { if (!text || text.length <= maxLength) { return text; } return text.slice(0, maxLength).replace(/\s+\S*$/, '') + '...'; } function markCardAsRead(card, persist) { var topicKey = card.getAttribute('data-topic-key'); if (!topicKey) { return; } card.classList.add('is-read'); if (persist) { storageSet('passlord-read-' + topicKey, '1'); } } function hydratePreview(card) { var previewBody = card.querySelector('[data-preview-body]'); var previewPanel = card.querySelector('[data-preview-panel]'); var previewUrl = card.getAttribute('data-preview-url'); var currentState = card.getAttribute('data-preview-state'); if (!previewBody || !previewPanel || !previewUrl || currentState === 'loading' || currentState === 'loaded') { return; } card.setAttribute('data-preview-state', 'loading'); previewPanel.removeAttribute('hidden'); previewBody.textContent = 'Chargement de l\'apercu...'; var request = new XMLHttpRequest(); request.open('GET', previewUrl, true); request.onreadystatechange = function () { if (request.readyState !== 4) { return; } var previewText = 'Aucun apercu disponible.'; if (request.status >= 200 && request.status < 300) { try { var payload = JSON.parse(request.responseText); var post = payload && payload.data && payload.data.length ? payload.data[0] : null; if (post && post.body_raw) { previewText = clipText(post.body_raw.replace(/\s+/g, ' ').trim(), 220); } card.setAttribute('data-preview-state', 'loaded'); } catch (error) { card.setAttribute('data-preview-state', 'error'); } } else { card.setAttribute('data-preview-state', 'error'); } previewBody.textContent = previewText; }; request.send(null); } function togglePreview(card, forceOpen) { var previewPanel = card.querySelector('[data-preview-panel]'); if (!previewPanel) { return; } var isOpen = card.getAttribute('data-preview-open') === '1'; var nextState = (typeof forceOpen === 'boolean') ? forceOpen : !isOpen; if (nextState) { card.setAttribute('data-preview-open', '1'); previewPanel.removeAttribute('hidden'); hydratePreview(card); } else { card.removeAttribute('data-preview-open'); previewPanel.setAttribute('hidden', 'hidden'); } } function setupSmartSidebar(sidebar) { if (!sidebar) { return; } var desktopQuery = window.matchMedia('(min-width: 1080px)'); var edgeGap = 24; var topGap = 112; var state = { lastScrollY: 0, ticking: false }; function getScrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; } function getHeaderOffset() { var topbarShell = document.querySelector('.forums-topbar-shell'); if (!topbarShell) { return topGap; } var shellRect = topbarShell.getBoundingClientRect(); return Math.max(Math.round(shellRect.bottom + 14), 96); } function resetSidebarState() { sidebar.classList.remove('is-stuck-bottom'); sidebar.style.removeProperty('--forums-sidebar-top'); sidebar.style.removeProperty('--forums-sidebar-bottom-gap'); } function updateSidebarMode() { state.ticking = false; if (!desktopQuery.matches) { resetSidebarState(); state.lastScrollY = getScrollY(); return; } topGap = getHeaderOffset(); var viewportHeight = window.innerHeight || document.documentElement.clientHeight || 1; var sidebarHeight = sidebar.offsetHeight || 0; var scrollY = getScrollY(); var delta = scrollY - state.lastScrollY; var availableHeight = viewportHeight - topGap - edgeGap; sidebar.style.setProperty('--forums-sidebar-top', topGap + 'px'); sidebar.style.setProperty('--forums-sidebar-bottom-gap', edgeGap + 'px'); if (scrollY <= 0 || sidebarHeight <= availableHeight) { sidebar.classList.remove('is-stuck-bottom'); } else if (delta > 0) { sidebar.classList.add('is-stuck-bottom'); } else if (delta < 0) { sidebar.classList.remove('is-stuck-bottom'); } state.lastScrollY = scrollY; } function queueSidebarModeUpdate() { if (state.ticking) { return; } state.ticking = true; window.requestAnimationFrame(updateSidebarMode); } function handleViewportChange() { state.lastScrollY = getScrollY(); queueSidebarModeUpdate(); } state.lastScrollY = getScrollY(); queueSidebarModeUpdate(); window.addEventListener('scroll', queueSidebarModeUpdate, { passive: true }); window.addEventListener('resize', handleViewportChange); if (desktopQuery.addEventListener) { desktopQuery.addEventListener('change', handleViewportChange); } else if (desktopQuery.addListener) { desktopQuery.addListener(handleViewportChange); } } document.addEventListener('DOMContentLoaded', function () { var threadProgressBar = document.querySelector('[data-thread-progress-bar]'); if (threadProgressBar) { var threadProgressRoot = document.querySelector('[data-thread-progress-root]') || document.body; var updateThreadProgress = function () { var rootRect = threadProgressRoot.getBoundingClientRect(); var viewportHeight = window.innerHeight || document.documentElement.clientHeight || 1; var totalScrollable = Math.max(threadProgressRoot.offsetHeight - viewportHeight * 0.45, 1); var consumed = Math.min(Math.max((viewportHeight * 0.35) - rootRect.top, 0), totalScrollable); var ratio = Math.max(0, Math.min(1, consumed / totalScrollable)); threadProgressBar.style.transform = 'scaleX(' + ratio + ')'; }; updateThreadProgress(); window.addEventListener('scroll', updateThreadProgress, { passive: true }); window.addEventListener('resize', updateThreadProgress); } var forumsSidebar = document.querySelector('.forums-sidebar'); if (forumsSidebar) { setupSmartSidebar(forumsSidebar); } var cards = document.querySelectorAll('.forum-card[data-topic-key]'); for (var i = 0; i < cards.length; i++) { (function (card) { var topicKey = card.getAttribute('data-topic-key'); if (topicKey && storageGet('passlord-read-' + topicKey) === '1') { card.classList.add('is-read'); } var links = card.querySelectorAll('a[href]'); for (var linkIndex = 0; linkIndex < links.length; linkIndex++) { links[linkIndex].addEventListener('click', function () { markCardAsRead(card, true); }); } var previewButton = card.querySelector('[data-preview-toggle]'); if (previewButton) { previewButton.addEventListener('click', function () { var shouldOpen = card.getAttribute('data-preview-open') !== '1'; togglePreview(card, shouldOpen); }); } // Auto-preview disabled : hover, focus and long-press triggers // were too intrusive and confused navigation. The preview is // still accessible explicitly via the "Aperçu rapide" link in // the card's dropdown menu (handled by previewButton above). })(cards[i]); } }); })();