Menu de configuration avancé, Réponse rapide dans le tiroir, Favoris intelligents, Auto-refresh, Scroll infini, Mode Galerie, sauvegarde des pavés et panneau latéral des topics actifs.
// ==UserScript==
// @name JVC Nexus
// @namespace https://www.jeuxvideo.com/
// @version 1.0.7
// @description Menu de configuration avancé, Réponse rapide dans le tiroir, Favoris intelligents, Auto-refresh, Scroll infini, Mode Galerie, sauvegarde des pavés et panneau latéral des topics actifs.
// @author Smelly
// @match *://www.jeuxvideo.com/forums/*
// @match *://www.jeuxvideo.com/recherche/forums/*
// @match file:///*
// @license MIT
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
// --- 1. OVERRIDES DE BASE (Anti-iframes JVC) ---
const _origCreate = document.createElement.bind(document);
document.createElement = function (tag, opts) {
const el = _origCreate(tag, opts);
if (tag.toLowerCase() === 'iframe') {
Object.defineProperty(el, 'src', {
configurable: true,
get() { return this.getAttribute('src') || ''; },
set(val) {
if (!val) return;
try {
// Sécurité anti "xoilac" ou autres redirections douteuses
if (val.includes('xoilac')) {
this.dataset.blockedSrc = "blocked-malicious";
return;
}
const u = new URL(val, location.origin);
const host = u.hostname.replace('www.', ''); // Nettoyage du www.
// Liste blanche élargie pour les embeds
const allowed = [
'jeuxvideo.com', 'jvc.gg', 'risibank.fr',
'youtube.com', 'youtube-nocookie.com',
'twitter.com', 'x.com', 'platform.twitter.com',
'streamable.com', 'dailymotion.com',
'tiktok.com', 'instagram.com', 'redditmedia.com', 'reddit.com', 'embed.reddit.com'
];
if (!allowed.some(a => host === a || host.endsWith('.' + a))) {
this.dataset.blockedSrc = String(val);
return;
}
this.setAttribute('src', val);
} catch (e) {
// En cas d'erreur de parsing d'URL, on bloque par sécurité
this.dataset.blockedSrc = String(val);
}
}
});
}
return el;
};
// --- 2. CONFIGURATION ET SETTINGS MULTIPLES ---
const defaultSettings = {
width: 1600,
imgMaxSizePx: 800,
topicPadding: 0.5,
accent: '#ee5c28',
featQuickR: true,
featInfinite: false,
featAutoRefresh: false,
autoRefreshDelay: 35,
featBookmarks: true,
featBypass: true,
featEmbeds: true,
featX: true,
featGallery: true,
featReplyHistory: true,
replyHistoryMax: 50,
featZebra: true,
featSidebar: true,
showNativeNavbar: true,
featVanillaTheme: false,
highlightWords: 'Alerte,🚨,🔴,Officiel,Drama'
};
const loadSettings = () => {
try {
const stored = JSON.parse(localStorage.getItem('MidnightArchivistSettings_v10'));
return stored ? { ...defaultSettings, ...stored } : defaultSettings;
} catch (e) { return defaultSettings; }
};
const saveSettings = (newSet) => {
localStorage.setItem('MidnightArchivistSettings_v10', JSON.stringify(newSet));
};
let MA_CONFIG = loadSettings();
// --- 3. INJECTION DU MOTEUR CSS ---
const injectCSS = () => {
if (document.getElementById('ma-custom-style')) return;
const fontLnk = document.createElement('link');
fontLnk.rel = 'stylesheet';
fontLnk.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Manrope:wght@600;700;800&display=swap';
(document.head || document.documentElement).appendChild(fontLnk);
const iconLnk = document.createElement('link');
iconLnk.rel = 'stylesheet';
iconLnk.href = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200';
(document.head || document.documentElement).appendChild(iconLnk);
if (document.getElementById('ma-custom-style')) return;
const style = document.createElement('style');
style.id = 'ma-custom-style';
style.textContent = `
/* -- Variables Midnight Archivist -- */
:root {
--ma-bg: #0f131a;
--ma-surface: #151921;
--ma-surface-low: #0a0c10;
--ma-border-solid: #2a2e37;
--ma-border-light: #1f232b;
--ma-text: #e2e8f0;
--ma-text-dim: #94a3b8;
--ma-outline: #475569;
--ma-yellow: #f59e0b;
--ma-radius: 10px;
--ma-font-body: 'Inter', -apple-system, sans-serif;
--ma-font-sh: 'Manrope', sans-serif;
--ma-accent: ${MA_CONFIG.accent};
--ma-accent-hov: ${MA_CONFIG.accent}dd;
--ma-accent-dim: ${MA_CONFIG.accent}20;
--ma-max-width: ${MA_CONFIG.width}px;
--ma-img-max: ${MA_CONFIG.imgMaxSizePx}px;
}
${MA_CONFIG.featVanillaTheme ? '' : `
body { background-color: var(--ma-bg) !important; color: var(--ma-text) !important; font-family: var(--ma-font-body) !important; overflow-x: hidden !important; }
html { background-color: var(--ma-bg) !important; color: var(--ma-text) !important; }
#layout, .layout, .container, .layout__contentMain, #page-topics, .layout--forum { background-color: transparent !important; overflow: visible !important; }
`}
*, *::before, *::after { box-sizing: border-box; }
.material-symbols-outlined { font-family: 'Material Symbols Outlined' !important; font-weight: normal; font-style: normal; font-size: 24px; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; font-feature-settings: 'liga'; font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; vertical-align: middle; }
/* Nettoyage JVC (Ads & Junk only) */
ins, .layout__adHeader, .dfp__atf, .nosticky, [id^="gpt-"], [class*="pub-"],
.layout__videoFooter, .video-footer-container, #js-video-footer,
iframe[data-blocked-src],
iframe[src^="about:blank"]:not([name="__tcfapiLocator"]) {
display: none !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; border: none !important;
}
/* Bouton Paramètres Flottant */
#ma-floating-settings { position: fixed; bottom: 24px; left: 24px; width: 44px; height: 44px; background: var(--ma-accent); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 15px rgba(0,0,0,0.4); z-index: 5500; cursor: pointer; transition: all 0.2s; border: 2px solid rgba(255,255,255,0.1); }
#ma-floating-settings:hover { transform: scale(1.1) rotate(30deg); background: var(--ma-accent-hov); }
#ma-floating-settings span { font-size: 24px !important; }
${MA_CONFIG.featVanillaTheme ? '' : `
.container__pagination { display: flex !important; justify-content: center; gap: 4px; margin: 24px 0 !important; background: transparent !important; min-height: 3em !important; height: 3em !important; align-items: center; }
.pagination__item, .pagi-item { border: 1px solid var(--ma-border-solid) !important; background: var(--ma-surface-low) !important; color: var(--ma-text-dim) !important; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 8px; text-decoration: none; font-weight: 700; font-size: 13px; transition: all 0.2s; }
.pagination__item.active, .pagi-item.active { background: var(--ma-accent) !important; color: white !important; border-color: var(--ma-accent) !important; }
.pagination__item:hover:not(.active), .pagi-item:hover:not(.active) { background: var(--ma-surface) !important; color: var(--ma-accent) !important; }
.pagination__dropdown { order: unset !important; }
/* Padding special pour les LI dans la liste forum */
.tablesForum li, .container__messages li { padding: var(--ma-topic-padding, 0.5em) 0em var(--ma-topic-padding, 0.5em) 3em !important; transition: background 0.1s ease; }
.ma-zebra-active .tablesForum__bodyRow:nth-child(even) { background: rgba(255,255,255,0.02) !important; }
.tablesForum__bodyRow:hover { background: rgba(255,255,255,0.05) !important; }
`}
/* Media Gallery Grid */
.ma-gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; padding: 24px 0; }
.ma-gallery-item { position: relative; border-radius: 8px; overflow: hidden; background: var(--ma-surface-low); border: 1px solid var(--ma-border-solid); aspect-ratio: 16/9; cursor: pointer; transition: transform 0.2s, border-color 0.2s; display: flex; align-items: center; justify-content: center; }
.ma-gallery-item:hover { transform: translateY(-4px); border-color: var(--ma-accent); }
.ma-gallery-item img { width: 100%; height: 100%; object-fit: cover; }
.ma-gallery-item .ma-media-badge { position: absolute; top: 8px; right: 8px; background: rgba(0,0,0,0.7); color: white; padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: 800; text-transform: uppercase; z-index: 5; }
.ma-gallery-item .ma-play-icon { position: absolute; font-size: 48px !important; color: white; opacity: 0.8; text-shadow: 0 4px 12px rgba(0,0,0,0.5); z-index: 5; pointer-events: none; }
.ma-gallery-load-more { width: 100%; padding: 16px; text-align: center; background: var(--ma-surface); border: 1px solid var(--ma-border-solid); color: var(--ma-text-dim); cursor: pointer; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; border-radius: 8px; margin-top: 24px; transition: all 0.2s; }
.ma-gallery-load-more:hover { background: var(--ma-accent-dim); color: var(--ma-accent); border-color: var(--ma-accent); }
.ma-gallery-filters { display: flex; gap: 8px; align-items: center; padding: 16px 0 8px; flex-wrap: wrap; }
.ma-gf-btn { padding: 5px 14px; border-radius: 20px; border: 1px solid var(--ma-border-solid); background: var(--ma-surface-low); color: var(--ma-text-dim); font-size: 11px; font-weight: 700; cursor: pointer; text-transform: uppercase; letter-spacing: 0.05em; transition: all 0.2s; }
.ma-gf-btn.active { background: var(--ma-accent); color: white; border-color: var(--ma-accent); }
.ma-gf-btn:hover:not(.active) { background: var(--ma-surface); color: var(--ma-text); }
.ma-gf-sticker-label { margin-left: auto; font-size: 12px; color: var(--ma-text-dim); display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
.ma-gf-sticker-label input { accent-color: var(--ma-accent); cursor: pointer; }
/* Lightbox */
.ma-lightbox { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.92); backdrop-filter: blur(8px); z-index: 9000; display: none; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s; }
.ma-lightbox.open { display: flex; opacity: 1; }
.ma-lightbox-content { max-width: 90%; max-height: 90%; position: relative; display: flex; align-items: center; justify-content: center; }
.ma-lightbox-img { max-width: 100%; max-height: 90vh; border-radius: 4px; box-shadow: 0 0 50px rgba(0,0,0,0.5); }
.ma-lightbox-video { width: 80vw; aspect-ratio: 16/9; border: none; border-radius: 8px; box-shadow: 0 0 50px rgba(0,0,0,0.5); }
.ma-lightbox-close { position: absolute; top: -48px; right: 0; color: white; cursor: pointer; transition: color 0.15s; z-index: 100; }
.ma-lightbox-close:hover { color: var(--ma-accent); }
.ma-lightbox-nav { position: absolute; top: 50%; left: 0; width: 100%; display: flex; justify-content: space-between; padding: 0 24px; transform: translateY(-50%); pointer-events: none; z-index: 90; }
.ma-lb-arrow { width: 48px; height: 48px; background: rgba(255,255,255,0.08); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; pointer-events: auto; transition: all 0.2s; user-select: none; backdrop-filter: blur(4px); }
.ma-lb-arrow:hover { background: var(--ma-accent); transform: scale(1.1); }
.ma-lb-arrow.hidden { opacity: 0; pointer-events: none; }
${MA_CONFIG.featVanillaTheme ? '' : `
body { padding-top: 64px !important; }
/* Custom Navbar - REMOVED */
#ma-top-navbar { display: none !important; }
body { padding-top: 0 !important; }
/* LAYOUT PRINCIPAL — colonne unique (aside masqué) centré */
body > .layout--forum, #page-topics, #page-topic, .layout {
display: block !important; max-width: var(--ma-max-width, 1600px) !important; width: 96% !important; margin: 32px auto 64px auto !important; padding: 0 !important; background: transparent !important; transition: max-width 0.3s ease;
}
body .layout__contentMain { display: block !important; width: 100% !important; max-width: none !important; margin: 0 !important; overflow: visible !important; }
body .layout__contentAside { display: none !important; }
body .layout__row:not(.layout__contentMain):not(.layout__contentAside):not(.buttonsNavbar__sticky):not(#js-list-message-tools-actions) { display: none !important; }
/* JVC Native Sticky Navbar (buttonsNavbar) - FORCE STICKY */
.buttonsNavbar__sticky, #js-list-message-tools-actions { display: block !important; position: sticky !important; top: 0px !important; z-index: 5000 !important; visibility: visible !important; opacity: 1 !important; }
.ma-hide-native-nav .buttonsNavbar__sticky { display: none !important; }
body #forum-main-col, body .container__main { max-width: none !important; width: 100% !important; display: flex !important; flex-direction: column !important; margin: 0 !important; }
`}
/* WIDTH SLIDER — toujours actif, même en thème vanilla */
/* Override CSS grid variable sur TOUS les conteneurs de page JVC (list + topic + autres) */
[id^="page-"] { --grid-template-columns: 1fr minmax(0, var(--ma-max-width, 1600px)) 1fr !important; transition: all 0.3s ease; }
/* En thème vanilla, max-width: 100% pour que le grid s'étale — PAS en thème Nexus (ça casserait le max-width du slider) */
${MA_CONFIG.featVanillaTheme ? '[id^="page-"] { max-width: 100% !important; }' : ''}
body > .layout--forum, .layout { max-width: var(--ma-max-width, 1600px) !important; margin-left: auto !important; margin-right: auto !important; transition: max-width 0.3s ease; }
/* Modale Settings - NOUVEAU MENU COMPLET ET USER FRIENDLY */
.ma-modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.6); backdrop-filter: blur(4px); z-index: 6000; display: none; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s; }
.ma-modal-overlay.open { display: flex; opacity: 1; }
.ma-modal { background: var(--ma-surface); border: 1px solid var(--ma-border-solid); border-radius: 8px; width: 600px; max-width: 90%; box-shadow: 0 20px 40px rgba(0,0,0,0.4); display: flex; flex-direction: column; overflow: hidden; }
.ma-modal-header { padding: 24px; border-bottom: 1px solid var(--ma-border-solid); display: flex; justify-content: space-between; align-items: center; background: var(--ma-surface-low); }
.ma-modal-header h2 { margin: 0; font-family: var(--ma-font-sh); font-size: 20px; color: var(--ma-text); }
.ma-modal-close { cursor: pointer; color: var(--ma-outline); transition: color 0.15s; }
.ma-modal-close:hover { color: var(--ma-accent); }
.ma-modal-tabs { display: flex; background: var(--ma-surface-low); border-bottom: 1px solid var(--ma-border-solid); }
.ma-modal-tab { flex: 1; padding: 12px; text-align: center; cursor: pointer; font-size: 13px; font-weight: 700; color: var(--ma-text-dim); text-transform: uppercase; letter-spacing: 0.05em; transition: all 0.2s; border-bottom: 2px solid transparent; }
.ma-modal-tab.active { color: var(--ma-accent); border-bottom-color: var(--ma-accent); background: var(--ma-surface); }
.ma-modal-body { padding: 24px; max-height: 60vh; overflow-y: auto; scrollbar-width: thin; }
.ma-modal-pane { display: none; flex-direction: column; gap: 20px; }
.ma-modal-pane.active { display: flex; }
.ma-setting-group { display: flex; flex-direction: column; gap: 8px; }
.ma-setting-group label { display: flex; justify-content: space-between; align-items: center; font-size: 14px; font-weight: 500; color: var(--ma-text); }
.ma-setting-desc { font-size: 12px; color: var(--ma-outline); margin-top: 4px; line-height: 1.4; }
.ma-setting-group input[type="range"] { accent-color: var(--ma-accent); flex: 1; margin-left: 16px; }
.ma-setting-group input[type="color"] { border: none; width: 40px; height: 30px; cursor: pointer; padding: 0; border-radius: 4px; }
.ma-setting-group input[type="text"] { background: var(--ma-bg); border: 1px solid var(--ma-border-solid); color: var(--ma-text); padding: 8px 12px; border-radius: 4px; font-size: 13px; width: 100%; margin-top: 8px; }
/* Toggle Switch pour les Options */
.ma-switch { position: relative; display: inline-block; width: 44px; height: 24px; flex-shrink: 0; }
.ma-switch input { opacity: 0; width: 0; height: 0; }
.ma-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: var(--ma-border-solid); transition: .2s; border-radius: 24px; }
.ma-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 4px; bottom: 4px; background-color: white; transition: .2s; border-radius: 50%; }
input:checked + .ma-slider { background-color: var(--ma-accent); }
input:checked + .ma-slider:before { transform: translateX(20px); }
.ma-modal-footer { padding: 16px 24px; border-top: 1px solid var(--ma-border-solid); display: flex; justify-content: flex-end; gap: 16px; background: var(--ma-surface-low); }
.ma-btn { padding: 8px 24px; border-radius: 4px; font-weight: 700; cursor: pointer; font-size: 14px; transition: all 0.2s; border: none; }
.ma-btn-ghost { background: transparent; color: var(--ma-text); border: 1px solid var(--ma-border-solid); }
.ma-btn-ghost:hover { background: var(--ma-surface); }
.ma-btn-primary { background: var(--ma-accent); color: white; box-shadow: 0 4px 12px var(--ma-accent-dim); }
.ma-btn-primary:hover { background: var(--ma-accent-hov); transform: translateY(-1px); }
/* Titre + Nouveau Sujet */
.ma-header-flex { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 24px; width: 100%; box-sizing: border-box; }
.ma-header-flex nav { font-size: 10px; font-weight: 700; color: var(--ma-accent); text-transform: uppercase; letter-spacing: .1em; margin-bottom: 4px; display: block; }
.ma-header-flex h1 { font-family: var(--ma-font-sh); font-size: 32px; font-weight: 800; color: var(--ma-text); margin: 0; letter-spacing: -0.02em; }
.ma-btn-new { background: var(--ma-accent); color: #fff; font-weight: 700; font-size: 14px; padding: 10px 24px; border-radius: var(--ma-radius); box-shadow: 0 4px 12px var(--ma-accent-dim); transition: all .15s; border: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
.ma-btn-new:hover { background: var(--ma-accent-hov); transform: translateY(-1px); }
/* Barre Filtres */
.ma-filter-bar { background: var(--ma-surface); height: 48px; display: flex; align-items: center; justify-content: space-between; padding: 0 16px; border-radius: var(--ma-radius) var(--ma-radius) 0 0; width: 100%; box-sizing: border-box; }
.ma-sticky-bar .ma-filter-bar { position: sticky; top: 0; z-index: 4000; box-shadow: 0 10px 30px rgba(0,0,0,0.2); }
.ma-filter-tabs { display: flex; gap: 16px; height: 100%; align-items: center; }
.ma-filter-tab { font-size: 12px; font-weight: 700; height: 100%; display: flex; align-items: center; cursor: pointer; text-transform: uppercase; letter-spacing: .05em; border-bottom: 2px solid transparent; }
.ma-filter-tab.active { color: var(--ma-accent); border-bottom: 2px solid var(--ma-accent); }
.ma-filter-tab:not(.active) { color: var(--ma-text-dim); }
.ma-filter-tab:not(.active):hover { color: var(--ma-text); }
.ma-toast-refresh { display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; background: var(--ma-accent-dim); color: var(--ma-accent); font-size: 10px; font-weight: 800; border-radius: 4px; text-transform: uppercase; margin-left: auto; opacity: 0; transition: opacity 0.3s; }
.ma-toast-refresh.show { opacity: 1; }
${MA_CONFIG.featVanillaTheme ? '' : `
/* TABLEAU DES SUJETS (Correction PADDING 6px 25px) */
.tablesForum { display: flex !important; flex-direction: column !important; background: var(--ma-surface) !important; width: 100% !important; margin: 0 !important; padding: 0 !important; border-radius: 0 !important; }
.tablesForum__headRow { display: grid !important; grid-template-columns: minmax(0, 1fr) 130px 90px 110px !important; align-items: center !important; width: 100% !important; padding: 0 !important; background: var(--ma-surface-low) !important; border-bottom: 1px solid var(--ma-border-solid) !important; }
.tablesForum__rowSujets, .tablesForum__rowParticipants, .tablesForum__rowNbMessages, .tablesForum__rowLastMsg { font-size: 10px !important; font-weight: 700 !important; text-transform: uppercase !important; letter-spacing: .05em !important; color: var(--ma-text-dim) !important; padding: 12px 16px !important; background: transparent !important; border: none !important; }
.tablesForum__rowNbMessages { text-align: center !important; }
.tablesForum__rowLastMsg { text-align: right !important; }
.tablesForum__rowSujets { padding-left: 66px !important; }
.tablesForum__bodyRow {
display: grid !important;
grid-template-columns: minmax(0, 1fr) 130px 90px 110px !important;
align-items: center !important;
width: 100% !important;
border-bottom: 1px solid var(--ma-border-light) !important;
transition: background .15s !important;
padding: var(--ma-topic-padding-v, 6px) 25px var(--ma-topic-padding-v, 6px) 25px !important;
background: var(--ma-surface) !important;
}
.tablesForum__bodyRow:hover { background: var(--ma-surface-low) !important; }
.tablesForum__bodyRow.ma-hidden-row { display: none !important; }
.tablesForum__bodyRow.ma-new-refresh { animation: pulseBg 2s infinite; }
@keyframes pulseBg { 0% { background: var(--ma-accent-dim); } 50% { background: var(--ma-surface); } 100% { background: var(--ma-accent-dim); } }
.tablesForum__cellSubject {
display: flex !important; align-items: center !important; padding: 0 !important; gap: 16px !important; text-decoration: none !important; min-width: 0 !important; max-width: 100% !important;
}
.ma-custom-icon { display: inline-flex !important; align-items: center !important; justify-content: center !important; width: 24px !important; min-width: 24px !important; height: 24px !important; flex: 0 0 24px !important; background: transparent !important; overflow: visible !important; margin-right: 8px !important; }
.ma-custom-icon span { font-size: 20px !important; line-height: 1 !important; color: inherit; display: inline-block !important; width: 100% !important; text-align: center !important; overflow: visible !important; }
.tablesForum__subjectMarkerIcon { display: none !important; }
.tablesForum__subjectText { font-size: 14px !important; font-weight: 500 !important; color: var(--ma-text) !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; transition: color .15s !important; flex: 1 1 auto !important; }
.tablesForum__bodyRow:hover .tablesForum__subjectText { color: var(--ma-accent) !important; }
.tablesForum__subjectText .ma-highlight { font-weight: 800; color: var(--ma-accent); }
.ma-badge-pin { display: inline-block; background: var(--ma-accent-dim); color: var(--ma-accent); font-size: 9px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; padding: 2px 6px; border-radius: var(--ma-radius); margin-right: 8px; vertical-align: 2px; }
@keyframes ma-flash {
0% { opacity: 1; transform: scale(1); }
80% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.9); }
}
.ma-badge-new {
display: inline-block;
background: var(--ma-accent);
color: white;
font-size: 9px;
font-weight: 800;
letter-spacing: .05em;
text-transform: uppercase;
padding: 2px 6px;
border-radius: var(--ma-radius);
margin-right: 8px;
vertical-align: 2px;
animation: ma-flash 3s ease-in;
pointer-events: none;
}
/* Bouton Favoris Star sur Hover */
.ma-star-btn { position: static !important; margin-left: auto !important; color: var(--ma-outline); cursor: pointer; opacity: 0; transition: all .2s; z-index: 10; padding: 0 8px; display: flex; align-items: center; justify-content: center; transform: none !important; }
.tablesForum__bodyRow:hover .ma-star-btn { opacity: 0.5; }
.ma-star-btn:hover { color: var(--ma-yellow) !important; opacity: 1 !important; transform: translateY(-50%) scale(1.2); }
.ma-star-btn.active, .ma-star-btn.bookmarked { opacity: 1 !important; color: var(--ma-yellow) !important; }
.tablesForum__cellParticipants { padding: 4px 16px !important; display: flex !important; gap: 4px !important; overflow: hidden !important; align-items: center !important; }
.avatar__image { border-radius: 50% !important; border: 1px solid var(--ma-border-solid) !important; }
.tablesForum__remainingAvatars { background: var(--ma-surface-low) !important; color: var(--ma-text) !important; border: 1px solid var(--ma-border-solid) !important; border-radius: 99px !important; font-size: 10px !important; padding: 2px 6px !important; height: 20px !important; min-width: 24px !important; display: flex !important; align-items: center !important; justify-content: center !important; font-weight: 700 !important; }
.tablesForum__cellText--msg { padding: 4px 16px !important; color: var(--ma-outline) !important; font-size: 14px !important; font-weight: 500 !important; text-align: center !important; display: block !important; }
.tablesForum__cellLink { padding: 4px 16px !important; color: var(--ma-outline) !important; font-size: 12px !important; text-align: right !important; text-decoration: none !important; display: block !important; white-space: nowrap !important; cursor: help !important; }
.tablesForum__cellLink:hover { color: var(--ma-accent) !important; text-decoration: underline !important; }
/* PAGINATION & INFINITE SCROLL */
.container__pagination { background: transparent !important; display: flex !important; justify-content: center !important; padding: 16px 0 !important; width: 100% !important; position: relative; margin: 24px 0 !important; }
.ma-infinite-loader { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: var(--ma-bg); display: flex; align-items: center; justify-content: center; color: var(--ma-accent); font-weight: 700; font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; opacity: 0; pointer-events: none; transition: opacity 0.2s; z-index: 10; }
.ma-infinite-loader.loading { opacity: 1; pointer-events: auto; }
.pagination { margin: 0 !important; width: 100% !important; display: flex !important; justify-content: center !important; overflow: visible !important; }
.pagination__navigation { display: flex !important; gap: 6px !important; justify-content: center !important; align-items: center !important; flex-wrap: wrap !important; background: transparent !important; overflow: visible !important; }
/* Masquer les éléments inutiles de JVC */
.pagination__dropdown {
position: relative !important;
order: 0 !important;
display: inline-flex !important;
}
.pagination__dropdown > .pagination__button { display: inline-flex !important; }
/* Mode Simple (<= 10 pages) : On déballe tout et on cache le bouton ... */
.pagination__navigation[data-ma-pagi="simple"] .pagination__dropdown { display: contents !important; }
.pagination__navigation[data-ma-pagi="simple"] .pagination__dropdown > .pagination__button { display: none !important; }
.pagination__navigation[data-ma-pagi="simple"] .pagination__dropdownListWrapper { display: contents !important; }
/* Mode Dropdown (> 10 pages) : On cache la liste et on montre ... */
.pagination__navigation[data-ma-pagi="dropdown"] .pagination__dropdownListWrapper {
display: none !important;
position: absolute !important;
top: 100% !important;
left: 50% !important;
transform: translateX(-50%) !important;
background: #111 !important; /* Retour à un fond très sombre pour la lisibilité */
border: 1px solid rgba(255,255,255,0.1) !important;
border-radius: 12px !important;
padding: 16px !important;
box-shadow: 0 15px 50px rgba(0,0,0,0.9) !important;
z-index: 100000 !important;
width: 450px !important;
max-width: 90vw !important;
max-height: 400px !important;
overflow-y: auto !important;
}
/* Ouverture au clic (classe ma-open ajoutée par JS) */
.pagination__navigation[data-ma-pagi="dropdown"] .pagination__dropdownListWrapper.ma-open {
display: block !important;
}
.pagination__dropdownList {
display: flex !important;
gap: 6px !important;
justify-content: center !important;
flex-wrap: wrap !important;
padding: 0 !important;
margin: 0 !important;
background: none !important;
}
.pagination__break { display: none !important; }
/* Style global des boutons de pagination */
.pagination__button, .pagination__item, .pagi-item {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
min-width: 36px !important;
height: 36px !important;
padding: 0 8px !important;
border-radius: 8px !important;
font-size: 13px !important;
font-weight: 700 !important;
background: var(--ma-surface) !important;
color: var(--ma-text-dim) !important;
border: 1px solid var(--ma-border-solid) !important;
transition: all 0.2s !important;
cursor: pointer !important;
text-decoration: none !important;
box-sizing: border-box !important;
}
.pagination__item--first, .pagination__button--first { order: -2 !important; }
.pagination__item--prev, .pagination__button--prev { order: -1 !important; }
.pagination__button--next { order: 10 !important; }
.pagination__button--last { order: 11 !important; }
/* Nettoyage des icônes dans les boutons */
/* Nettoyage du fond gris des boutons dans le popup */
.pagination__dropdownList .pagination__button,
.pagination__dropdownList .pagination__item,
.pagination__dropdownList .pagi-item {
background: transparent !important;
border-color: rgba(255,255,255,0.05) !important;
box-shadow: none !important;
}
.pagination__dropdownList .pagination__item--current {
background: var(--ma-accent) !important;
color: white !important;
}
.pagination__icon { font-size: 14px !important; margin: 0 !important; }
.pagination__item--disabled { opacity: 0.3 !important; cursor: not-allowed !important; pointer-events: none !important; }
.pagination__item--isHideMobile i.icon-more { font-style: normal !important; font-family: var(--ma-font-body) !important; }
.pagination__item--isHideMobile i.icon-more::before { content: "..." !important; }
/* États Hover et Actif */
.pagination__button:hover:not(.pagination__item--current):not(.pagination__item--disabled),
.pagination__item:hover:not(.pagination__item--current):not(.pagination__item--disabled) {
background: var(--ma-surface-low) !important;
color: var(--ma-accent) !important;
border-color: var(--ma-accent) !important;
transform: translateY(-2px);
}
.pagination__item--current, .pagi-item.active {
background: var(--ma-accent) !important;
color: white !important;
border-color: var(--ma-accent) !important;
box-shadow: 0 4px 12px var(--ma-accent-dim) !important;
}
/* Simple mode : --first et --last sont redondants avec les pages déjà visibles */
.pagination__navigation[data-ma-pagi="simple"] .pagination__button--first,
.pagination__navigation[data-ma-pagi="simple"] .pagination__button--last { display: none !important; }
`}
/* Users online badge (injected by JS near filter bar) */
.ma-users-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 700; color: var(--ma-text-dim); padding: 3px 10px; background: rgba(255,255,255,0.04); border: 1px solid var(--ma-border-solid); border-radius: 20px; margin-right: 8px; }
.ma-users-badge .material-symbols-outlined { font-size: 14px !important; }
/* Blacklist */
.ma-bl-btn { opacity: 0; cursor: pointer; padding: 0 4px; display: inline-flex; align-items: center; justify-content: center; font-size: 16px !important; color: #64748b; transition: opacity .15s, color .15s; flex-shrink: 0; vertical-align: middle; }
.tablesForum__bodyRow:hover .ma-bl-btn { opacity: 0.5; }
.ma-bl-btn:hover { color: #ef4444 !important; opacity: 1 !important; }
.ma-bl-menu { position: fixed; background: var(--ma-surface, #151921); border: 1px solid var(--ma-border-solid, #2a2e37); border-radius: 8px; padding: 6px 0; z-index: 9999; min-width: 180px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); }
.ma-bl-menu-item { padding: 8px 14px; font-size: 13px; cursor: pointer; display: flex; align-items: center; gap: 8px; color: var(--ma-text, #e2e8f0); transition: background .1s; white-space: nowrap; }
.ma-bl-menu-item:hover { background: rgba(239,68,68,0.12); color: #ef4444; }
.ma-bl-menu-item .material-symbols-outlined { font-size: 16px !important; }
${MA_CONFIG.featVanillaTheme ? '' : `
/* ASIDE CLEANUP & REDESIGN */
.layout__contentAside { display: flex !important; flex-direction: column !important; gap: 24px !important; }
.sideCardForum {
display: none !important;
background: var(--ma-surface) !important;
border-radius: var(--ma-radius) !important;
border: 1px solid var(--ma-border-solid) !important;
padding: 0 !important;
overflow: hidden !important;
box-shadow: 0 4px 20px rgba(0,0,0,0.2) !important;
transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s !important;
}
.sideCardForum:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0,0,0,0.3) !important; border-color: var(--ma-accent-dim) !important; }
.js-side-module-forum-info, .js-side-module-forum-favorite { display: flex !important; flex-direction: column !important; }
.sideCardForum__header { padding: 18px 20px !important; background: var(--ma-surface-low) !important; border-bottom: 1px solid var(--ma-border-solid) !important; display: flex !important; align-items: center !important; gap: 12px !important; }
.sideCardForum__header::before { font-family: 'Material Symbols Outlined' !important; font-size: 22px !important; color: var(--ma-accent) !important; font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24 !important; }
.js-side-module-forum-info .sideCardForum__header::before { content: 'info'; }
.js-side-module-forum-favorite .sideCardForum__header::before { content: 'star'; }
.sideCardForum__headerTitle { font-family: var(--ma-font-sh) !important; font-size: 13px !important; font-weight: 800 !important; color: var(--ma-text) !important; text-transform: uppercase !important; letter-spacing: 0.1em !important; margin: 0 !important; }
.sideCardForum__body { padding: 20px !important; display: flex !important; flex-direction: column !important; gap: 14px !important; }
.sideCardForum__subtitle { font-size: 10px !important; font-weight: 900 !important; color: var(--ma-accent) !important; text-transform: uppercase !important; letter-spacing: 0.08em !important; display: block !important; margin-bottom: 4px !important; opacity: 0.6; }
.sideCardForum__listItem { padding: 0 !important; list-style: none !important; border: none !important; }
.sideCardForum__listItemLink { color: var(--ma-text-dim) !important; text-decoration: none !important; font-size: 13px !important; font-weight: 600 !important; transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1) !important; display: flex !important; align-items: center !important; gap: 10px !important; background: rgba(255,255,255,0.03) !important; padding: 10px 14px !important; border-radius: 10px !important; margin-bottom: 8px !important; border: 1px solid transparent !important; }
.sideCardForum__listItemLink:hover { color: white !important; background: var(--ma-accent) !important; box-shadow: 0 4px 15px var(--ma-accent-dim) !important; transform: translateX(6px); border-color: rgba(255,255,255,0.1) !important; }
.sideCardForum__headerExtra { display: none !important; }
.sideCardForum__empty { color: var(--ma-outline) !important; font-size: 12px !important; padding: 16px !important; text-align: center !important; background: rgba(0,0,0,0.1) !important; border-radius: 8px !important; border: 1px dashed var(--ma-border-solid) !important; }
`}
/* TIROIR SPLIT VIEW (DRAWER) DE LECTURE */
.ma-drawer-reader {
position: fixed; top: 64px; right: -650px; width: 650px; height: calc(100vh - 64px); background: var(--ma-surface); border-left: 1px solid var(--ma-border-solid); box-shadow: -10px 0 30px rgba(0,0,0,0.5); z-index: 4500; transition: right 0.3s cubic-bezier(0.16, 1, 0.3, 1); display: flex; flex-direction: column;
}
.ma-drawer-reader.open { right: 0; }
.ma-drawer-header { display: flex; justify-content: space-between; align-items: center; padding: 24px; border-bottom: 1px solid var(--ma-border-solid); background: var(--ma-surface-low); }
.ma-drawer-infos { display: flex; flex-direction: column; gap: 4px; }
.ma-drawer-author { font-weight: 800; color: var(--ma-accent); font-size: 16px; font-family: var(--ma-font-sh); }
.ma-drawer-date { font-size: 12px; color: var(--ma-outline); }
.ma-drawer-badge { display: inline-block; background: var(--ma-accent-dim); color: var(--ma-accent); font-size: 10px; font-weight: 800; padding: 4px 8px; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.05em; align-self: flex-start; margin-bottom: 8px; }
.ma-drawer-content { padding: 32px 24px; font-size: 15px; line-height: 1.7; color: var(--ma-text); overflow-y: auto; scrollbar-width: thin; flex: 1; min-height: 0; }
.ma-drawer-content img { max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; border: 1px solid var(--ma-border-light); }
.ma-drawer-content .message__blockquote { background: var(--ma-surface-low); padding: 16px; border-left: 4px solid var(--ma-accent); margin: 16px 0; font-size: 13px; color: var(--ma-text-dim); border-radius: 0 8px 8px 0; }
.ma-drawer-loader { padding: 48px; text-align: center; color: var(--ma-outline); font-style: italic; }
/* QUICK REPLY DRAWER (Native Import Full Editor) */
.ma-drawer-quickreply { border-top: 1px solid var(--ma-border-solid); flex-shrink: 0; max-height: 60vh; overflow-y: auto; scrollbar-width: none; background: var(--ma-bg); }
.ma-drawer-quickreply form { margin: 0 !important; }
.ma-drawer-quickreply .messageEditor__containerEdit { padding: 16px 24px !important; display: flex !important; flex-direction: column !important; background: transparent !important; border: none !important; box-shadow: none !important; }
.ma-drawer-quickreply .messageEditor__containerPreview { background: transparent !important; border: none !important; }
.ma-drawer-quickreply .messageEditor__edit { height: 160px !important; width: 100% !important; border: 1px solid var(--ma-border-light) !important; background: var(--ma-surface) !important; color: var(--ma-text) !important; padding: 12px !important; border-radius: 0 !important; resize: vertical !important; margin-bottom: 0 !important; font-family: inherit !important; font-size: 14px !important; border-bottom: none !important; outline: none !important; }
.ma-drawer-quickreply .messageEditor__edit:focus { border-color: var(--ma-accent) !important; }
.ma-drawer-quickreply .messageEditor__buttonEdit { background: var(--ma-surface-low) !important; border: 1px solid var(--ma-border-light) !important; border-radius: 0 !important; padding: 6px 12px !important; }
.ma-drawer-quickreply .buttonsEditor { display: flex !important; flex-wrap: wrap !important; gap: 8px !important; border: none !important; background: transparent !important; }
.ma-drawer-quickreply .buttonsEditor__group { display: flex !important; gap: 4px !important; align-items: center !important; border-right: 1px solid var(--ma-border-solid) !important; padding-right: 8px !important; }
.ma-drawer-quickreply .buttonsEditor__group:last-child { border-right: none !important; }
.ma-drawer-quickreply .buttonsEditor__button { background: transparent !important; border: none !important; color: var(--ma-text-dim) !important; cursor: pointer !important; padding: 6px !important; transition: all 0.15s !important; border-radius: 4px !important; display: flex !important; align-items: center !important; justify-content: center !important; }
.ma-drawer-quickreply .buttonsEditor__button:hover { color: var(--ma-accent) !important; background: var(--ma-accent-dim) !important; }
.ma-drawer-quickreply #ma-drawer-btn-post { margin: 12px 24px 24px; background: var(--ma-accent); color: #fff; border: none; padding: 12px 24px; border-radius: 0; font-weight: 700; cursor: pointer; width: calc(100% - 48px); font-size: 14px; transition: all 0.2s; text-transform: uppercase; letter-spacing: 0.05em; }
.ma-drawer-quickreply #ma-drawer-btn-post:hover { background: var(--ma-accent-hov); transform: translateY(-1px); }
.ma-drawer-quickreply #ma-drawer-btn-post:active { transform: translateY(0); }
.ma-drawer-quickreply #ma-drawer-btn-post:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
/* Bouton star drawer */
#drawer-star-btn { background: transparent; border: 1px solid var(--ma-border-solid); border-radius: 6px; color: var(--ma-outline); cursor: pointer; padding: 6px 8px; display: flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 700; transition: all .2s; flex-shrink: 0; }
#drawer-star-btn:hover { border-color: var(--ma-yellow); color: var(--ma-yellow); }
#drawer-star-btn.bookmarked { border-color: var(--ma-yellow); color: var(--ma-yellow); background: rgba(245,158,11,0.1); }
/* Star btn en mode vanilla (liste de sujets) */
${MA_CONFIG.featVanillaTheme ? `
.ma-star-btn { position: static !important; margin-left: 8px !important; color: #94a3b8; cursor: pointer; opacity: 0; transition: opacity .2s, color .2s; z-index: 10; padding: 0 4px; display: inline-flex !important; align-items: center; justify-content: center; font-size: 18px !important; vertical-align: middle; }
.tablesForum__bodyRow:hover .ma-star-btn { opacity: 0.5; }
.ma-star-btn:hover { color: #f59e0b !important; opacity: 1 !important; }
.ma-star-btn.bookmarked { opacity: 1 !important; color: #f59e0b !important; }
` : ''}
${MA_CONFIG.featVanillaTheme ? '' : `
/* INTERIEUR DES TOPICS */
body .container__messages { background: transparent !important; }
.ma-gallery-mode .container__messages { display: grid !important; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)) !important; gap: 16px !important; }
.ma-gallery-mode .messageUser { margin-bottom: 0 !important; }
/* --- Blocs de message (topic) --- */
body .messageUser { display: flex !important; flex-direction: column !important; background: var(--ma-surface) !important; border-radius: 12px !important; margin-bottom: 10px !important; border: 1px solid var(--ma-border-solid) !important; box-shadow: none !important; overflow: hidden !important; }
body .messageUser:hover { background: var(--ma-surface) !important; }
body .messageUser__card { padding: 18px 20px 10px !important; width: 100% !important; background: transparent !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; }
body .messageUser__msg { border: none !important; border-radius: 0 !important; background: transparent !important; box-shadow: none !important; }
body .messageUser__col-left, body .messageUser__col-right, body .messageUser__infos, body .messageUser__pseudo-mp { border: none !important; background: transparent !important; }
/* Header */
body .messageUser__header { display: flex !important; gap: 12px !important; align-items: flex-start !important; margin-bottom: 14px !important; }
body .messageUser__avatar { flex-shrink: 0 !important; }
body .messageUser__avatar img, body .messageUser__avatar .avatar__image { width: 38px !important; height: 38px !important; border-radius: 50% !important; border: 2px solid var(--ma-border-solid) !important; object-fit: cover !important; display: block !important; }
body .messageUser__profil { display: flex !important; flex-direction: column !important; gap: 1px !important; min-width: 0 !important; flex-wrap: nowrap !important; }
body .messageUser__label { font-weight: 700 !important; font-size: 14px !important; color: var(--ma-accent) !important; text-decoration: none !important; line-height: 1.3 !important; }
body .messageUser__level { font-size: 11px !important; color: var(--ma-text-dim) !important; background: transparent !important; padding: 0 !important; line-height: 1.3 !important; }
body .messageUser__date { font-size: 11px !important; color: var(--ma-outline) !important; text-decoration: none !important; white-space: nowrap !important; }
/* Corps */
body .messageUser__main { font-size: 14px !important; line-height: 1.65 !important; color: var(--ma-text) !important; }
body .messageUser__main img { max-width: var(--ma-img-max, 100%) !important; height: auto !important; border-radius: 6px; }
body .message__p { margin-bottom: 10px !important; }
body .message__blockquote { background: var(--ma-surface-low) !important; border-left: 3px solid var(--ma-accent) !important; padding: 10px 14px !important; margin-bottom: 12px !important; border-radius: 0 8px 8px 0 !important; color: var(--ma-text-dim) !important; font-size: 13px !important; }
body .messageUser__msgEdited { font-size: 11px !important; color: var(--ma-outline) !important; font-style: italic !important; margin-top: 8px !important; display: block !important; }
/* Formulaires */
body .messageEditor__containerEdit { background-color: transparent !important; box-shadow: none !important; transition: none !important; border: none !important; }
body .titre-bloc-bloc-forum { background: var(--ma-surface-low) !important; color: var(--ma-text) !important; border: none !important; border-bottom: 1px solid var(--ma-border-solid) !important; border-radius: 0 !important; padding: 14px 20px !important; font-family: var(--ma-font-sh) !important; font-weight: 800 !important; font-size: 13px !important; text-transform: uppercase !important; letter-spacing: 0.08em !important; margin: 0 !important; }
body .form-post-topic, body .form-post-message { background: var(--ma-surface) !important; border: none !important; border-radius: 0 !important; padding: 0 !important; }
input[type="text"]:focus, textarea:focus { outline: none !important; border-color: var(--ma-accent) !important; }
body .btn-poster-msg { background: var(--ma-accent) !important; color: #fff !important; font-weight: 700 !important; border-radius: var(--ma-radius) !important; padding: 12px 32px !important; border: none !important; cursor: pointer !important; text-transform: uppercase !important; box-shadow: 0 4px 12px var(--ma-accent-dim) !important; }
/* Tous les Blocs Formulaires (Nouveau sujet + Réponse) - Haute Visibilité */
body .bloc-form-reponse,
body .reponse-area,
body .bloc-form-ecrire-sujet,
body .form-post-topic,
body .form-post-message,
body .container-form-ecrire {
background: #1c212c !important; /* Gris bleuté clair */
border-radius: 12px !important;
border: 1px solid rgba(255,255,255,0.08) !important;
border-top: 3px solid var(--ma-accent) !important; /* Barre orange bien épaisse */
padding: 20px !important;
margin-top: 32px !important;
margin-bottom: 32px !important;
overflow: hidden !important;
box-shadow: 0 20px 50px rgba(0,0,0,0.6) !important;
transition: all 0.3s ease !important;
display: block !important;
}
body .bloc-form-reponse:hover, body .reponse-area:hover, body .bloc-form-ecrire-sujet:hover { border-color: var(--ma-accent) !important; }
body .bloc-form-reponse .reponse-area, body .bloc-form-reponse form { background: transparent !important; border: none !important; margin: 0 !important; padding: 0 !important; }
body .bloc-form-reponse .bl-alert, body .bloc-form-reponse .bl-content-message-forum,
body .bloc-form-reponse [class*="alert"], body .bloc-form-reponse [class*="bl-info"],
body .bloc-form-reponse .bloc-harcelement-msg { background: rgba(255,255,255,0.03) !important; border: none !important; border-bottom: 1px solid var(--ma-border-solid) !important; color: var(--ma-text-dim) !important; padding: 12px 24px !important; font-size: 13px !important; border-radius: 0 !important; margin: 0 !important; }
body .bloc-form-reponse .bl-alert a, body .bloc-form-reponse .bloc-harcelement-msg a { color: var(--ma-accent) !important; }
body .bloc-form-reponse .messageEditor__containerEdit { background: transparent !important; border: none !important; box-shadow: none !important; padding: 20px 24px !important; display: flex !important; flex-direction: column !important; gap: 16px !important; }
body .bloc-form-reponse .buttonsEditor, body .bloc-form-reponse .messageEditor__buttonEdit { background-color: #3032362b !important; border: 1px solid var(--ma-border-solid) !important; border-radius: 10px !important; padding: 10px 14px !important; display: flex !important; flex-wrap: wrap !important; align-items: center !important; gap: 6px !important; }
body .bloc-form-reponse .buttonsEditor__button, body .bloc-form-reponse .messageEditor__buttonEdit button, body .bloc-form-reponse .messageEditor__buttonEdit a { background: rgba(255,255,255,0.06) !important; border: 1px solid rgba(255,255,255,0.10) !important; border-radius: 6px !important; color: var(--ma-text-dim) !important; padding: 6px 10px !important; font-size: 13px !important; cursor: pointer !important; transition: all 0.15s !important; text-decoration: none !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; line-height: 1 !important; }
body .bloc-form-reponse .buttonsEditor__button:hover, body .bloc-form-reponse .messageEditor__buttonEdit button:hover { background: var(--ma-accent-dim) !important; border-color: var(--ma-accent) !important; color: var(--ma-accent) !important; }
body .bloc-form-reponse .buttonsEditor__separator { width: 1px !important; height: 20px !important; background: var(--ma-border-solid) !important; margin: 0 4px !important; }
body .bloc-form-reponse .buttonsEditor__previewToggle, body .bloc-form-reponse [class*="preview"] { margin-left: auto !important; font-size: 12px !important; color: var(--ma-text-dim) !important; }
body .messageEditor__editArea, body .messageEditor__edit,
body .bloc-form-reponse textarea, body .reponse-area textarea,
body .bloc-form-ecrire-sujet textarea, body textarea {
background: #0d1117 !important; /* Fond bien sombre pour trancher avec le container */
color: #fff !important;
border: 1px solid rgba(255,255,255,0.05) !important;
border-radius: 8px !important;
padding: 20px !important;
font-size: 15px !important;
font-family: var(--ma-font-body) !important;
line-height: 1.6 !important;
resize: vertical !important;
width: 100% !important;
box-sizing: border-box !important;
min-height: 180px !important;
outline: none !important;
transition: all 0.2s ease !important;
box-shadow: inset 0 2px 10px rgba(0,0,0,0.5) !important;
}
body .bloc-form-reponse .messageEditor__editArea:focus-within, body .bloc-form-reponse textarea:focus, body .reponse-area textarea:focus {
border-color: var(--ma-accent) !important;
background: #090c10 !important;
box-shadow: inset 0 2px 10px rgba(0,0,0,0.7), 0 0 15px var(--ma-accent-dim) !important;
}
body .bloc-form-reponse .messageEditor__submit, body .bloc-form-reponse .form-actions, body .bloc-form-reponse .bloc-form-submit { background: transparent !important; border: none !important; padding: 0 !important; display: flex !important; align-items: center !important; gap: 12px !important; }
body .bloc-form-reponse .btn-poster-msg, body .bloc-form-reponse button[type="submit"] { background: var(--ma-accent) !important; color: #fff !important; font-weight: 700 !important; border-radius: var(--ma-radius) !important; padding: 12px 32px !important; border: none !important; cursor: pointer !important; text-transform: uppercase !important; font-size: 13px !important; letter-spacing: 0.05em !important; box-shadow: 0 4px 12px var(--ma-accent-dim) !important; transition: opacity 0.2s !important; display: inline-flex !important; align-items: center !important; gap: 8px !important; }
body .bloc-form-reponse .btn-poster-msg:hover, body .bloc-form-reponse button[type="submit"]:hover { opacity: 0.85 !important; }
body .bloc-form-reponse input[type="text"], body .topicTitle__input, body .bloc-form-ecrire-sujet input[type="text"] { background: #0a0505 !important; color: var(--ma-text) !important; border: 1px solid var(--ma-border-solid) !important; border-radius: 8px !important; padding: 10px 14px !important; font-size: 14px !important; width: 100% !important; box-sizing: border-box !important; outline: none !important; transition: border-color 0.2s !important; margin-bottom: 20px !important; height: 44px !important; box-shadow: inset 0 2px 8px rgba(0,0,0,0.4) !important; }
body .bloc-form-reponse input[type="text"]:focus, body .topicTitle__input:focus { border-color: var(--ma-accent) !important; }
`}
/* Bouton blacklist auteur dans les topics */
.ma-bl-author-btn { order: 99 !important; width: 28px !important; height: 28px !important; padding: 0 !important; border-radius: 6px !important; border: 1px solid rgba(128,128,128,0.2) !important; cursor: pointer !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; font-size: 16px !important; gap: 0 !important; opacity: 0; transition: opacity 0.15s, background 0.15s, border-color 0.15s, color 0.15s !important; color: #64748b; background: transparent; flex-shrink: 0; }
.messageUser:hover .ma-bl-author-btn { opacity: 1 !important; }
.ma-bl-author-btn:hover { color: #ef4444 !important; border-color: rgba(239,68,68,0.4) !important; background: rgba(239,68,68,0.08) !important; opacity: 1 !important; }
/* Footer actions — commun aux deux thèmes (déplacé dans le header par JS) */
body .messageUser__footer:not(.ma-header-actions) { display: none !important; }
body .messageUser__footer.ma-header-actions { display: flex !important; border-top: none !important; border: none !important; padding: 0 !important; margin: 0 0 0 auto !important; gap: 4px !important; align-self: flex-start !important; flex-shrink: 0 !important; background: transparent !important; }
body .messageUser__action { width: 28px !important; height: 28px !important; padding: 0 !important; border-radius: 6px !important; border: 1px solid rgba(128,128,128,0.2) !important; cursor: pointer !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; font-size: 12px !important; gap: 0 !important; opacity: 0; transition: opacity 0.15s, background 0.15s, border-color 0.15s, color 0.15s !important; }
body .messageUser:hover .messageUser__action { opacity: 1 !important; }
body .messageUser__action:hover { opacity: 1 !important; }
body .messageUser__action .messageUser__actionText { display: none !important; }
.ma-draft-badge { font-size: 11px; font-weight: bold; color: var(--ma-yellow); float: right; margin-top: 4px; }
/* Videos Embeds */
/* Videos Embeds */
.ma-video-embed {
border-radius: 8px;
overflow: hidden;
margin: 12px 0;
border: 1px solid var(--ma-border-light);
width: 100%;
max-width: 600px;
/* On met un ratio par défaut pour YouTube/Streamable, mais min-height pour éviter qu'il soit écrasé */
aspect-ratio: 16/9;
}
/* Embeds natifs (TikTok, Instagram, Reddit) — pas de ratio fixe, le widget gère */
.ma-tiktok-embed, .ma-instagram-embed, .ma-reddit-embed { margin: 12px 0; width: 100%; display: block; }
.ma-tiktok-embed blockquote, .ma-instagram-embed blockquote, .ma-reddit-embed blockquote { margin-left: 0 !important; margin-right: 0 !important; max-width: 100% !important; min-width: 0 !important; }
/* Tweet loading indicator */
.ma-tweet-loading { display: flex; align-items: center; padding: 12px 16px; margin: 8px 0; background: rgba(255,255,255,0.04); border: 1px solid var(--ma-border-light); border-radius: 12px; color: var(--ma-text-dim); font-size: 13px; animation: ma-pulse 1.4s ease-in-out infinite; }
@keyframes ma-pulse { 0%,100% { opacity: 0.5; } 50% { opacity: 1; } }
/* Fade-in pour les nouveaux éléments injectés */
.ma-fade-in { opacity: 0; transform: translateY(10px); transition: none; }
.ma-fade-in.ma-visible { opacity: 1; transform: translateY(0); transition: opacity 0.4s ease, transform 0.4s ease; }
/* ===== PANNEAU LATERAL TOPICS ACTIFS ===== */
#ma-topics-sidebar {
position: fixed; top: 0; left: -340px; width: 320px; height: 100vh;
background: var(--ma-surface); border-right: 1px solid var(--ma-border-solid);
box-shadow: 4px 0 24px rgba(0,0,0,0.5); z-index: 4400;
transition: left 0.3s cubic-bezier(0.16, 1, 0.3, 1);
display: flex; flex-direction: column; overflow: hidden;
}
#ma-topics-sidebar.ma-sb-open { left: 0; }
#ma-sidebar-toggle {
position: fixed; top: 50%; left: 0; transform: translateY(-50%);
width: 22px; height: 72px; background: var(--ma-accent); color: #fff;
border-radius: 0 8px 8px 0; display: flex; align-items: center; justify-content: center;
cursor: pointer; z-index: 4401; transition: left 0.3s cubic-bezier(0.16, 1, 0.3, 1);
box-shadow: 2px 0 10px rgba(0,0,0,0.4);
}
#ma-sidebar-toggle.ma-sb-open { left: 320px; }
#ma-sidebar-toggle span { font-size: 16px !important; }
.ma-sb-header {
padding: 12px 16px; border-bottom: 1px solid var(--ma-border-solid);
background: var(--ma-surface-low); display: flex; align-items: center;
justify-content: space-between; flex-shrink: 0; gap: 8px;
}
.ma-sb-title {
font-family: var(--ma-font-sh); font-size: 12px; font-weight: 800;
color: var(--ma-text); text-transform: uppercase; letter-spacing: 0.08em; flex: 1;
}
.ma-sb-refresh-btn, .ma-sb-live-btn {
cursor: pointer; color: var(--ma-text-dim); transition: color 0.15s, transform 0.4s;
display: flex; align-items: center;
}
.ma-sb-refresh-btn span, .ma-sb-live-btn span { font-size: 20px !important; }
.ma-sb-refresh-btn:hover { color: var(--ma-accent); }
.ma-sb-live-btn:hover { color: var(--ma-accent); }
.ma-sb-live-btn.paused { color: var(--ma-outline); }
.ma-sb-refresh-btn.spinning span { animation: ma-sb-spin 0.7s linear infinite; }
@keyframes ma-sb-spin { to { transform: rotate(360deg); } }
.ma-sb-list {
flex: 1; overflow-y: auto; scrollbar-width: thin;
scrollbar-color: var(--ma-border-solid) transparent;
}
.ma-sb-topic {
display: block; padding: 9px 14px; border-bottom: 1px solid var(--ma-border-light);
text-decoration: none; transition: background 0.1s; cursor: pointer;
border-left: 3px solid transparent;
}
.ma-sb-topic:hover { background: var(--ma-surface-low); border-left-color: var(--ma-accent); }
.ma-sb-topic.ma-sb-current { border-left-color: var(--ma-accent); background: var(--ma-accent-dim); }
.ma-sb-cat {
font-size: 9px; font-weight: 800; text-transform: uppercase;
letter-spacing: 0.07em; color: var(--ma-accent); margin-bottom: 2px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-sb-ttitle {
font-size: 12px; font-weight: 500; color: var(--ma-text);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px;
line-height: 1.3; display: flex; align-items: center;
}
.ma-sb-topic:hover .ma-sb-ttitle { color: var(--ma-accent); }
.ma-sb-meta {
display: flex; align-items: center; gap: 8px; font-size: 10px; color: var(--ma-text-dim);
}
.ma-sb-meta-item { display: flex; align-items: center; gap: 2px; }
.ma-sb-meta-item span { font-size: 11px !important; }
.ma-sb-meta-time { margin-left: auto; font-size: 10px; color: var(--ma-outline); }
.ma-sb-meta-author { display: flex; align-items: center; gap: 2px; font-size: 10px; color: var(--ma-text-dim); }
.ma-sb-meta-author span { font-size: 11px !important; }
.ma-sb-icon { font-size: 13px !important; vertical-align: middle; margin-right: 3px; }
.ma-sb-icon--pin { color: #f59e0b; }
.ma-sb-icon--hot { color: var(--ma-accent); }
.ma-sb-icon--normal { color: #6b7280; }
.ma-sb-footer {
padding: 6px 14px; border-top: 1px solid var(--ma-border-light); flex-shrink: 0;
display: flex; align-items: center; justify-content: space-between;
font-size: 10px; color: var(--ma-outline); background: var(--ma-surface-low);
}
.ma-sb-footer-timer { font-weight: 700; }
.ma-sb-empty {
padding: 32px 16px; text-align: center; color: var(--ma-text-dim); font-size: 12px;
}
/* Bouton Retour en Haut */
#ma-scroll-top { position: fixed; bottom: 76px; right: 24px; width: 44px; height: 44px; background: var(--ma-surface); color: var(--ma-text-dim); border-radius: 50%; display: none; align-items: center; justify-content: center; box-shadow: 0 4px 15px rgba(0,0,0,0.4); z-index: 5400; cursor: pointer; transition: all 0.2s; border: 1px solid var(--ma-border-solid); }
#ma-scroll-top.visible { display: flex; }
#ma-scroll-top:hover { color: var(--ma-accent); border-color: var(--ma-accent); transform: translateY(-2px); }
`;
// Inject custom root CSS variables into document so JS updating them reflects live
const rootStyles = `
:root { --ma-accent: ${MA_CONFIG.accent}; --ma-accent-hov: ${MA_CONFIG.accent}dd; --ma-accent-dim: ${MA_CONFIG.accent}20; --ma-max-width: ${MA_CONFIG.width}px; --ma-img-max: ${MA_CONFIG.imgSize}%; --ma-topic-padding: ${MA_CONFIG.topicPadding}em; --ma-topic-padding-v: ${MA_CONFIG.topicPadding * 12}px; }
`;
let rLnk = document.getElementById('ma-dynamic-root');
if (!rLnk) {
rLnk = document.createElement('style');
rLnk.id = "ma-dynamic-root";
(document.head || document.documentElement).appendChild(rLnk);
}
rLnk.textContent = rootStyles;
if (!document.getElementById('ma-custom-style')) {
(document.head || document.documentElement).appendChild(style);
}
};
if (document.head) injectCSS();
else new MutationObserver((muts, obs) => { if (document.head) { injectCSS(); obs.disconnect(); } }).observe(document, { childList: true, subtree: true });
// Utilitaire : limite l'exécution d'une fonction à une fois par `delay` ms
const throttle = (fn, delay) => {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= delay) { last = now; fn(...args); }
};
};
document.addEventListener('DOMContentLoaded', () => {
// --- Initialisation Pagination ---
const setupPagination = () => {
document.querySelectorAll('.pagination__navigation').forEach(nav => {
const list = nav.querySelector('.pagination__dropdownList');
let uniquePages;
if (list) {
const itemsList = Array.from(list.querySelectorAll('.pagination__item, .pagination__button'));
uniquePages = new Set(itemsList.map(i => i.textContent.trim()).filter(t => t && !isNaN(t)));
} else {
// Pas de dropdown : liens de pages directs (ex: topic à peu de pages)
const directItems = Array.from(nav.querySelectorAll(
'.pagination__item:not(.pagination__button--first):not(.pagination__button--prev):not(.pagination__button--next):not(.pagination__button--last),' +
'.pagination__button:not(.pagination__button--first):not(.pagination__button--prev):not(.pagination__button--next):not(.pagination__button--last)'
));
uniquePages = new Set(directItems.map(i => i.textContent.trim()).filter(t => t && !isNaN(t)));
}
nav.setAttribute('data-ma-pagi', uniquePages.size <= 10 ? 'simple' : 'dropdown');
});
};
// Gestion globale des clics pagination
document.addEventListener('click', (e) => {
const dotBtn = e.target.closest('.pagination__dropdown > .pagination__button');
if (dotBtn) {
e.preventDefault(); e.stopPropagation();
const nav = dotBtn.closest('.pagination__navigation');
const wrapper = nav?.querySelector('.pagination__dropdownListWrapper');
if (wrapper) {
const wasOpen = wrapper.classList.contains('ma-open');
document.querySelectorAll('.pagination__dropdownListWrapper.ma-open').forEach(w => w.classList.remove('ma-open'));
if (!wasOpen) wrapper.classList.add('ma-open');
}
return;
}
// Fermer si clic ailleurs
document.querySelectorAll('.pagination__dropdownListWrapper.ma-open').forEach(w => w.classList.remove('ma-open'));
});
setupPagination(); // Premier appel au chargement
// Observer pour le scroll infini / refresh live
const pagiObs = new MutationObserver(() => setupPagination());
const pagiContainer = document.querySelector('.container__main') || document.body;
pagiObs.observe(pagiContainer, { childList: true, subtree: true });
if (!MA_CONFIG.featVanillaTheme) document.documentElement.classList.add('theme-dark');
if (MA_CONFIG.featZebra && !MA_CONFIG.featVanillaTheme) document.body.classList.add('ma-zebra-active');
if (!MA_CONFIG.showNativeNavbar && !MA_CONFIG.featVanillaTheme) document.body.classList.add('ma-hide-native-nav');
// --- 4. DATA LOGIC (Bookmarks, History & Keywords) ---
const getBookmarks = () => JSON.parse(localStorage.getItem('MidnightArchivistBookmarks')) || [];
const saveBookmark = (id, title, url) => {
let bks = getBookmarks();
if (!bks.find(b => b.id === id)) {
const cleanTitle = title.replace(/^(star_border|folder_special|folder|whatshot|star|reply)\s+/i, '');
bks.push({ id, title: cleanTitle, url });
localStorage.setItem('MidnightArchivistBookmarks', JSON.stringify(bks));
}
};
const removeBookmark = (id) => {
let bks = getBookmarks();
bks = bks.filter(b => b.id !== id);
localStorage.setItem('MidnightArchivistBookmarks', JSON.stringify(bks));
};
const getBlacklist = () => { try { return JSON.parse(localStorage.getItem('MA_Blacklist')) || { authors: [], topics: [] }; } catch (e) { return { authors: [], topics: [] }; } };
const saveBlacklist = (bl) => localStorage.setItem('MA_Blacklist', JSON.stringify(bl));
const blacklistAuthor = (name) => { const bl = getBlacklist(); if (!bl.authors.includes(name)) { bl.authors.push(name); saveBlacklist(bl); } };
const blacklistTopic = (id, title, url) => { const bl = getBlacklist(); if (!bl.topics.find(t => t.id === id)) { bl.topics.push({ id, title, url: url || null }); saveBlacklist(bl); } };
const unblacklistAuthor = (name) => { const bl = getBlacklist(); bl.authors = bl.authors.filter(a => a !== name); saveBlacklist(bl); };
const unblacklistTopic = (id) => { const bl = getBlacklist(); bl.topics = bl.topics.filter(t => t.id !== id); saveBlacklist(bl); };
const isBlacklisted = (row) => { const bl = getBlacklist(); const tid = row.id.replace('topic-', ''); if (bl.topics.find(t => t.id === tid)) return true; const authorLinkEl = row.querySelector('.tablesForum__cellAuthor .tablesForum__authorLink'); let author = authorLinkEl ? (authorLinkEl.getAttribute('title') || authorLinkEl.textContent.trim()) : null; if (!author) { const firstAvatar = row.querySelector('.tablesForum__cellParticipants .tablesForum__firstAvatar'); if (firstAvatar) author = firstAvatar.getAttribute('title') || firstAvatar.querySelector('img')?.getAttribute('alt') || null; } if (author && bl.authors.some(a => author.toLowerCase().includes(a.toLowerCase()))) return true; return false; };
const getReplyHistory = () => JSON.parse(localStorage.getItem('MA_ReplyHistory')) || [];
const saveReplyHistory = (h) => localStorage.setItem('MA_ReplyHistory', JSON.stringify(h));
const addToReplyHistory = (topicId, forumId, title, url, msgCount) => {
let h = getReplyHistory();
h = h.filter(x => x.topicId !== topicId); // remove old entry
const cleanTitle = title.replace(/^(star_border|folder_special|folder|whatshot|star|reply)\s+/i, '');
h.unshift({ topicId, forumId, title: cleanTitle, url, msgCount, repliedAt: Date.now(), lastChecked: Date.now(), newReplies: 0 });
if (h.length > MA_CONFIG.replyHistoryMax) h = h.slice(0, MA_CONFIG.replyHistoryMax);
saveReplyHistory(h);
};
const highlightTopic = (titleEl) => {
if (!MA_CONFIG.highlightWords) return;
const words = MA_CONFIG.highlightWords.split(',').map(w => w.trim()).filter(Boolean);
if (!words.length || titleEl.dataset.highlighted) return;
let html = titleEl.innerHTML;
words.forEach(w => {
let escaped = w;
['\\\\', '[', ']', '(', ')', '{', '}', '*', '+', '?', '.', '^', '$', '|'].forEach(c => {
escaped = escaped.split(c).join('\\\\' + c);
});
const regex = new RegExp("(" + escaped + ")", "gi");
html = html.replace(regex, '<span class="ma-highlight">$1</span>');
});
titleEl.innerHTML = html;
titleEl.dataset.highlighted = "true";
};
const transformEmbeds = (container) => {
if (!MA_CONFIG.featEmbeds) return;
// 1. On sélectionne uniquement les liens qui n'ont pas encore été traités (:not(.ma-embedded))
container.querySelectorAll('a:not(.ma-embedded)').forEach(a => {
// Sécurité : on ignore si le lien est déjà dans un de nos conteneurs
if (a.closest('.ma-video-embed') || a.closest('.ma-twitter-embed')) return;
// 2. On marque le lien comme traité IMMÉDIATEMENT pour stopper la boucle infinie
a.classList.add('ma-embedded');
const url = a.href;
let src = '';
if (url.includes('youtube.com/watch?v=') || url.includes('youtu.be/')) {
let vid = url.split('v=')[1] || url.split('youtu.be/')[1];
if (vid) src = `https://www.youtube.com/embed/${vid.split('&')[0]}`;
} else if (url.includes('streamable.com/')) {
const parts = url.split('streamable.com/');
if (parts.length > 1) {
const sid = parts[1].split('/')[0].split('?')[0];
if (sid && sid.length >= 4) src = `https://streamable.com/e/${sid}`;
}
} else if (url.includes('tiktok.com/') && url.includes('/video/')) {
const match = url.match(/tiktok\.com\/@([\w.-]+)\/video\/(\d+)/);
if (match) {
const cleanUrl = `https://www.tiktok.com/@${match[1]}/video/${match[2]}`;
const w = document.createElement('div');
w.className = 'ma-tiktok-embed';
w.dataset.originalUrl = url;
w.innerHTML = `<blockquote class="tiktok-embed" cite="${cleanUrl}" data-video-id="${match[2]}"><section></section></blockquote>`;
a.parentNode.replaceChild(w, a);
if (!window.maTikTokScriptAdded) {
window.maTikTokScriptAdded = true;
const s = document.createElement('script'); s.src = 'https://www.tiktok.com/embed.js'; s.async = true; document.head.appendChild(s);
} else if (window.tiktokEmbed) {
setTimeout(() => window.tiktokEmbed.init(), 0);
}
return;
}
} else if (url.includes('instagram.com/')) {
const match = url.match(/instagram\.com\/(?:p|reel)\/([\w-]+)/);
if (match) {
const cleanUrl = `https://www.instagram.com/p/${match[1]}/`;
const w = document.createElement('div');
w.className = 'ma-instagram-embed';
w.dataset.originalUrl = url;
w.innerHTML = `<blockquote class="instagram-media" data-instgrm-permalink="${cleanUrl}" data-instgrm-version="14"></blockquote>`;
a.parentNode.replaceChild(w, a);
if (!window.maInstagramScriptAdded) {
window.maInstagramScriptAdded = true;
const s = document.createElement('script'); s.src = 'https://www.instagram.com/embed.js'; s.async = true; document.head.appendChild(s);
} else if (window.instgrm && window.instgrm.Embeds) {
setTimeout(() => window.instgrm.Embeds.process(), 0);
}
return;
}
} else if (url.includes('reddit.com/r/') && url.includes('/comments/')) {
const match = url.match(/reddit\.com\/(r\/[\w]+\/comments\/[\w]+)/);
if (match) {
const w = document.createElement('div');
w.className = 'ma-reddit-embed';
w.dataset.originalUrl = url;
w.innerHTML = `<blockquote class="reddit-embed-bq" data-embed-theme="dark" data-embed-height="316"><a href="${url}"></a></blockquote>`;
a.parentNode.replaceChild(w, a);
if (!window.maRedditScriptAdded) {
window.maRedditScriptAdded = true;
const s = document.createElement('script'); s.src = 'https://embed.reddit.com/widgets.js'; s.async = true; document.head.appendChild(s);
}
return;
}
} else if (MA_CONFIG.featX && (url.includes('twitter.com/') || url.includes('x.com/'))) {
const match = url.match(/(?:twitter\.com|x\.com)\/([\w.-]+)\/status\/(\d+)/);
if (match) {
// Si le nom d'utilisateur est "i" (format x.com/i/status/...), on utilise "x" par défaut pour que l'API Twitter ne plante pas
const username = match[1] === 'i' ? 'x' : match[1];
const tweetId = match[2];
// L'API widgets.js a TOUJOURS besoin d'une URL formatée en twitter.com pour l'attribut href !
const cleanTwitterUrl = `https://twitter.com/${username}/status/${tweetId}`;
// On crée un conteneur spécifique pour Twitter (pas le ma-video-embed classique)
const w = document.createElement('div');
w.className = 'ma-twitter-embed';
w.dataset.originalUrl = url;
w.style.display = 'flex';
// On injecte le Blockquote officiel avec un indicateur de chargement
w.innerHTML = `<div class="ma-tweet-loading"><span class="material-symbols-outlined" style="font-size:20px;margin-right:8px;opacity:0.6;">hourglass_top</span>Chargement du tweet…</div><blockquote class="twitter-tweet" data-theme="dark" data-dnt="true"><a href="${cleanTwitterUrl}"></a></blockquote>`;
// On l'insère D'ABORD dans le DOM
a.parentNode.replaceChild(w, a);
// Observer: retire le placeholder dès qu'un iframe apparaît (= tweet rendu par widgets.js)
const tweetLoadObs = new MutationObserver(() => {
if (w.querySelector('iframe')) {
const loader = w.querySelector('.ma-tweet-loading');
if (loader) loader.remove();
tweetLoadObs.disconnect();
}
});
tweetLoadObs.observe(w, { childList: true, subtree: true });
// On gère le script de Twitter
if (!window.maTwitterScriptAdded) {
window.maTwitterScriptAdded = true;
const script = document.createElement('script');
script.id = 'twitter-wjs';
script.src = 'https://platform.twitter.com/widgets.js';
script.async = true;
document.head.appendChild(script);
} else if (window.twttr && window.twttr.widgets) {
setTimeout(() => { window.twttr.widgets.load(w); }, 0);
}
return;
}
}
// Pour les autres intégrations (YouTube, Streamable)
if (src) {
const w = document.createElement('div');
w.className = 'ma-video-embed';
w.dataset.originalUrl = url;
w.innerHTML = `<iframe src="${src}" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`;
a.parentNode.replaceChild(w, a);
}
});
};
/* --- 5. UI PRINCIPALE --- */
const floatingBtn = document.createElement('div');
floatingBtn.id = 'ma-floating-settings';
floatingBtn.title = "Paramètres Midnight Archivist";
floatingBtn.innerHTML = "<span class='material-symbols-outlined'>settings</span>";
document.body.appendChild(floatingBtn);
// Modal Settings
const modalOverlay = document.createElement('div');
modalOverlay.className = 'ma-modal-overlay';
modalOverlay.innerHTML = `
<div class="ma-modal" id="ma-modal-box">
<div class="ma-modal-header">
<h2>JVC Nexus</h2>
<span class="material-symbols-outlined ma-modal-close">close</span>
</div>
<div class="ma-modal-tabs">
<div class="ma-modal-tab active" data-target="pane-ui">Interface</div>
<div class="ma-modal-tab" data-target="pane-feats">Fonctionnalités</div>
<div class="ma-modal-tab" data-target="pane-blacklist">Blacklist</div>
</div>
<div class="ma-modal-body">
<div class="ma-modal-pane active" id="pane-ui">
<div class="ma-setting-group">
<label>Thème JVC d'origine <label class="ma-switch"><input type="checkbox" id="set-vanilla-topics" ${MA_CONFIG.featVanillaTheme ? 'checked' : ''}><span class="ma-slider"></span></label></label>
<div class="ma-setting-desc">Désactive tout le redesign visuel pour retrouver l'apparence JVC native. Les fonctionnalités restent actives.</div>
</div>
<div class="ma-setting-group">
<label>Couleur d'Accentuation Principale <input type="color" id="set-accent" value="${MA_CONFIG.accent}"></label>
</div>
<div class="ma-setting-group">
<label>Largeur maximale du site <span id="val-width">${MA_CONFIG.width}px</span></label>
<input type="range" id="set-width" min="1000" max="1920" step="50" value="${MA_CONFIG.width}">
</div>
<div class="ma-setting-group">
<label>Taille maximale des images <span id="val-img">${MA_CONFIG.imgMaxSizePx}px</span></label>
<input type="range" id="set-img" min="100" max="1600" step="50" value="${MA_CONFIG.imgMaxSizePx}">
<div class="ma-setting-desc">Limite la largeur des images (ne réduit pas les plus petites).</div>
</div>
<div class="ma-setting-group">
<label>Hauteur des topics <span id="val-topic-padding">${MA_CONFIG.topicPadding}em</span></label>
<input type="range" id="set-topic-padding" min="0.1" max="2" step="0.1" value="${MA_CONFIG.topicPadding}">
<div class="ma-setting-desc">Espace vertical entre les topics.</div>
</div>
<div class="ma-setting-group">
<label>Alternance des lignes (Zebra) <label class="ma-switch"><input type="checkbox" id="set-zebra" ${MA_CONFIG.featZebra ? 'checked' : ''}><span class="ma-slider"></span></label></label>
<div class="ma-setting-desc">Colore une ligne sur deux pour plus de lisibilité.</div>
</div>
<div class="ma-setting-group">
<label>Mot-clés à mettre en surbrillance (séparés par virgule)</label>
<input type="text" id="set-kw" value="${MA_CONFIG.highlightWords}">
<div class="ma-setting-desc">Les topics contenant ces mots brilleront dans la liste.</div>
</div>
</div>
<div class="ma-modal-pane" id="pane-feats">
<div class="ma-setting-group"><label>Tiroir intelligent (Aperçu & Réponse rapide) <label class="ma-switch"><input type="checkbox" id="set-qr" ${MA_CONFIG.featQuickR ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Affiche un volet latéral au survol pour lire les messages et y répondre sans changer de page.</div></div>
<div class="ma-setting-group"><label>Défilement Infini (Sujets) <label class="ma-switch"><input type="checkbox" id="set-inf" ${MA_CONFIG.featInfinite ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Charge la page suivante automatiquement quand vous atteignez le bas.</div></div>
<div class="ma-setting-group"><label>Actualisation Automatique (Live) <label class="ma-switch"><input type="checkbox" id="set-live" ${MA_CONFIG.featAutoRefresh ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Vérifie discrètement s'il y a de nouveaux sujets périodiquement.</div></div>
<div class="ma-setting-group">
<label>Fréquence d'actualisation <span id="val-live-delay">${MA_CONFIG.autoRefreshDelay}s</span></label>
<input type="range" id="set-live-delay" min="4" max="120" step="1" value="${MA_CONFIG.autoRefreshDelay}">
</div>
<div class="ma-setting-group"><label>Favoris Intelligents <label class="ma-switch"><input type="checkbox" id="set-bkm" ${MA_CONFIG.featBookmarks ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Bouton étoile pour garder les sujets à lire plus tard.</div></div>
<div class="ma-setting-group"><label>Sauvegarde des pavés (Bypass Erreurs) <label class="ma-switch"><input type="checkbox" id="set-byp" ${MA_CONFIG.featBypass ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Sauvegarde votre texte en local pendant la frappe pour éviter de le perdre sur une erreur JVC.</div></div>
<div class="ma-setting-group"><label>Mini-Lecteur Vidéo Intégré <label class="ma-switch"><input type="checkbox" id="set-vid" ${MA_CONFIG.featEmbeds ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Transforme les liens bruts (ex: YouTube) en vidéos jouables directement dans l'aperçu.</div></div>
<div class="ma-setting-group"><label>Intégration Twitter/X <label class="ma-switch"><input type="checkbox" id="set-featX" ${MA_CONFIG.featX ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Active la prévisualisation des tweets via Twitframe.</div></div>
<div class="ma-setting-group"><label>Barre JVC Native <label class="ma-switch"><input type="checkbox" id="set-native-nav" ${MA_CONFIG.showNativeNavbar ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Affiche la barre d'outils native de JVC (Répondre, Actualiser, etc).</div></div>
<div class="ma-setting-group"><label>Mode Galerie Topics <label class="ma-switch"><input type="checkbox" id="set-featGallery" ${MA_CONFIG.featGallery ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Affiche l'onglet Galerie pour explorer les médias d'un topic.</div></div>
<div class="ma-setting-group"><label>Panneau Topics Actifs <label class="ma-switch"><input type="checkbox" id="set-featSidebar" ${MA_CONFIG.featSidebar !== false ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Affiche le panneau latéral gauche dans les topics pour voir les sujets actifs du forum.</div></div>
<div class="ma-setting-group"><label>Historique des Réponses <label class="ma-switch"><input type="checkbox" id="set-hist" ${MA_CONFIG.featReplyHistory ? 'checked' : ''}><span class="ma-slider"></span></label></label><div class="ma-setting-desc">Garde en mémoire les topics auxquels vous avez répondu et vérifie les nouvelles réponses.</div></div>
<div class="ma-setting-group"><label>Nombre max dans l'historique <span id="val-hist">${MA_CONFIG.replyHistoryMax}</span></label><input type="range" id="set-hist-max" min="10" max="200" step="10" value="${MA_CONFIG.replyHistoryMax}"></div>
</div>
<div class="ma-modal-pane" id="pane-blacklist">
<div class="ma-setting-group">
<label style="font-size:13px;font-weight:700;">Auteurs blacklistés</label>
<div id="bl-authors-list" style="display:flex;flex-direction:column;gap:6px;margin-top:8px;max-height:160px;overflow-y:auto;"></div>
<div style="display:flex;gap:8px;margin-top:8px;">
<input type="text" id="bl-author-input" placeholder="Pseudo à blacklister…" style="flex:1;">
<button class="ma-btn ma-btn-primary" id="bl-author-add" style="padding:8px 14px;font-size:13px;">Ajouter</button>
</div>
</div>
<div class="ma-setting-group">
<label style="font-size:13px;font-weight:700;">Topics masqués</label>
<div id="bl-topics-list" style="display:flex;flex-direction:column;gap:6px;margin-top:8px;max-height:160px;overflow-y:auto;"></div>
</div>
<div style="margin-top:8px;">
<button class="ma-btn ma-btn-ghost" id="bl-clear-all" style="color:#ef4444;border-color:rgba(239,68,68,0.3);font-size:13px;">Vider toute la blacklist</button>
</div>
</div>
</div>
<div class="ma-modal-footer">
<button class="ma-btn ma-btn-ghost" id="ma-modal-reset" style="margin-right:auto; color:#cc4b4b; border-color:rgba(204,75,75,0.2);">Réinitialiser</button>
<button class="ma-btn ma-btn-ghost" id="ma-modal-export" title="Exporter la configuration en JSON" style="padding:6px 12px;font-size:13px;"><span class="material-symbols-outlined" style="font-size:15px;vertical-align:middle;margin-right:4px;">download</span>Exporter</button>
<label class="ma-btn ma-btn-ghost" id="ma-modal-import-label" title="Importer une configuration JSON" style="cursor:pointer;padding:6px 12px;font-size:13px;"><span class="material-symbols-outlined" style="font-size:15px;vertical-align:middle;margin-right:4px;">upload</span>Importer<input type="file" id="ma-modal-import-input" accept=".json" style="display:none;"></label>
<button class="ma-btn ma-btn-ghost" id="ma-modal-cancel">Annuler</button>
<button class="ma-btn ma-btn-primary" id="ma-modal-save">Sauvegarder & Appliquer</button>
</div>
</div>
`;
document.body.appendChild(modalOverlay);
const docSt = document.getElementById('ma-dynamic-root').sheet.cssRules[0].style;
const renderBlacklistPane = () => {
const bl = getBlacklist();
const authList = modalOverlay.querySelector('#bl-authors-list');
const topicList = modalOverlay.querySelector('#bl-topics-list');
if (!authList || !topicList) return;
authList.innerHTML = '';
if (!bl.authors.length) {
authList.innerHTML = '<span style="color:var(--ma-outline);font-size:12px;font-style:italic;">Aucun auteur blacklisté.</span>';
} else {
bl.authors.forEach(name => {
const item = document.createElement('div');
item.style.cssText = 'display:flex;align-items:center;justify-content:space-between;background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);border-radius:6px;padding:6px 10px;font-size:13px;';
const profileUrl = `https://www.jeuxvideo.com/profil/${encodeURIComponent(name)}?mode=page_perso`;
item.innerHTML = `<a href="${profileUrl}" target="_blank" rel="noopener noreferrer" style="display:flex;align-items:center;gap:6px;color:inherit;text-decoration:none;flex:1;min-width:0;overflow:hidden;" title="Voir le profil de ${name}"><span class="material-symbols-outlined" style="font-size:14px;color:#ef4444;flex-shrink:0;">person_off</span><span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${name}</span></a><span class="material-symbols-outlined" style="font-size:16px;cursor:pointer;color:var(--ma-outline);flex-shrink:0;margin-left:8px;" data-rm-author="${name}">close</span>`;
item.querySelector('[data-rm-author]').addEventListener('click', () => { unblacklistAuthor(name); renderBlacklistPane(); });
authList.appendChild(item);
});
}
topicList.innerHTML = '';
if (!bl.topics.length) {
topicList.innerHTML = '<span style="color:var(--ma-outline);font-size:12px;font-style:italic;">Aucun topic masqué.</span>';
} else {
bl.topics.forEach(t => {
const item = document.createElement('div');
item.style.cssText = 'display:flex;align-items:center;justify-content:space-between;background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);border-radius:6px;padding:6px 10px;font-size:13px;';
const titleHtml = t.url
? `<a href="${t.url}" target="_blank" rel="noopener noreferrer" style="color:inherit;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;" title="Ouvrir le topic">${t.title || t.id}</a>`
: `<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;">${t.title || t.id}</span>`;
item.innerHTML = `${titleHtml}<span class="material-symbols-outlined" style="font-size:16px;cursor:pointer;color:var(--ma-outline);flex-shrink:0;margin-left:8px;" data-rm-topic="${t.id}">close</span>`;
item.querySelector('[data-rm-topic]').addEventListener('click', () => { unblacklistTopic(t.id); renderBlacklistPane(); });
topicList.appendChild(item);
});
}
};
floatingBtn.addEventListener('click', () => { modalOverlay.classList.add('open'); renderBlacklistPane(); });
modalOverlay.querySelector('.ma-modal-close').addEventListener('click', () => modalOverlay.classList.remove('open'));
modalOverlay.querySelector('#ma-modal-cancel').addEventListener('click', () => modalOverlay.classList.remove('open'));
// Onglets Modal
modalOverlay.querySelectorAll('.ma-modal-tab').forEach(t => t.addEventListener('click', (e) => {
modalOverlay.querySelectorAll('.ma-modal-tab').forEach(x => x.classList.remove('active'));
modalOverlay.querySelectorAll('.ma-modal-pane').forEach(x => x.classList.remove('active'));
e.target.classList.add('active');
modalOverlay.querySelector('#' + e.target.dataset.target).classList.add('active');
}));
// Save Modal
modalOverlay.querySelector('#ma-modal-save').addEventListener('click', () => {
MA_CONFIG.accent = modalOverlay.querySelector('#set-accent').value;
MA_CONFIG.width = parseInt(modalOverlay.querySelector('#set-width').value);
MA_CONFIG.imgMaxSizePx = parseInt(modalOverlay.querySelector('#set-img').value);
MA_CONFIG.topicPadding = parseFloat(modalOverlay.querySelector('#set-topic-padding').value);
MA_CONFIG.highlightWords = modalOverlay.querySelector('#set-kw').value;
MA_CONFIG.featQuickR = document.getElementById('set-qr').checked;
MA_CONFIG.featInfinite = document.getElementById('set-inf').checked;
MA_CONFIG.featAutoRefresh = document.getElementById('set-live').checked;
MA_CONFIG.autoRefreshDelay = parseInt(document.getElementById('set-live-delay').value);
MA_CONFIG.featBookmarks = document.getElementById('set-bkm').checked;
MA_CONFIG.featBypass = document.getElementById('set-byp').checked;
MA_CONFIG.featEmbeds = document.getElementById('set-vid').checked;
MA_CONFIG.featX = document.getElementById('set-featX').checked;
MA_CONFIG.featGallery = document.getElementById('set-featGallery').checked;
MA_CONFIG.featSidebar = document.getElementById('set-featSidebar').checked;
MA_CONFIG.featReplyHistory = document.getElementById('set-hist').checked;
MA_CONFIG.replyHistoryMax = parseInt(document.getElementById('set-hist-max').value);
MA_CONFIG.featZebra = document.getElementById('set-zebra').checked;
MA_CONFIG.showNativeNavbar = document.getElementById('set-native-nav').checked;
MA_CONFIG.featVanillaTheme = document.getElementById('set-vanilla-topics').checked;
saveSettings(MA_CONFIG);
const ds = document.getElementById('ma-dynamic-root').sheet.cssRules[0].style;
ds.setProperty('--ma-accent', MA_CONFIG.accent);
ds.setProperty('--ma-accent-hov', MA_CONFIG.accent + 'dd');
ds.setProperty('--ma-accent-dim', MA_CONFIG.accent + '20');
ds.setProperty('--ma-max-width', MA_CONFIG.width + 'px');
ds.setProperty('--ma-img-max', MA_CONFIG.imgMaxSizePx + 'px');
ds.setProperty('--ma-topic-padding', MA_CONFIG.topicPadding + 'em');
ds.setProperty('--ma-topic-padding-v', (MA_CONFIG.topicPadding * 12) + 'px');
modalOverlay.classList.remove('open');
setTimeout(() => window.location.reload(), 100);
});
// Reset Modal
modalOverlay.querySelector('#ma-modal-reset').addEventListener('click', () => {
if (confirm("Réinitialiser tous les paramètres aux valeurs d'usine ? (Vos favoris et historique seront conservés)")) {
localStorage.removeItem('MidnightArchivistSettings_v10');
window.location.reload();
}
});
// Export config
modalOverlay.querySelector('#ma-modal-export').addEventListener('click', () => {
const exportData = JSON.stringify({
settings: MA_CONFIG,
bookmarks: getBookmarks(),
blacklist: getBlacklist(),
replyHistory: getReplyHistory()
}, null, 2);
const blob = new Blob([exportData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'jvc-nexus-config.json';
a.click();
URL.revokeObjectURL(url);
});
// Import config
modalOverlay.querySelector('#ma-modal-import-input').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (ev) => {
try {
const parsed = JSON.parse(ev.target.result);
// Support ancien format (export direct de MA_CONFIG) et nouveau format
if (parsed.settings) {
saveSettings({ ...defaultSettings, ...parsed.settings });
if (parsed.bookmarks) localStorage.setItem('MidnightArchivistBookmarks', JSON.stringify(parsed.bookmarks));
if (parsed.blacklist) saveBlacklist(parsed.blacklist);
if (parsed.replyHistory) saveReplyHistory(parsed.replyHistory);
} else {
// Ancien format : juste les settings
saveSettings({ ...defaultSettings, ...parsed });
}
alert('Configuration importée ! La page va se recharger.');
window.location.reload();
} catch (err) {
alert('Fichier JSON invalide.');
}
};
reader.readAsText(file);
e.target.value = '';
});
// Sync Sliders visually and real-time preview (Robust Method)
modalOverlay.querySelector('#set-width').addEventListener('input', e => {
const val = e.target.value;
modalOverlay.querySelector('#val-width').textContent = val + 'px';
document.documentElement.style.setProperty('--ma-max-width', val + 'px');
});
modalOverlay.querySelector('#set-topic-padding').addEventListener('input', e => {
const val = e.target.value;
modalOverlay.querySelector('#val-topic-padding').textContent = val + 'em';
document.documentElement.style.setProperty('--ma-topic-padding', val + 'em');
document.documentElement.style.setProperty('--ma-topic-padding-v', (val * 12) + 'px');
});
modalOverlay.querySelector('#set-img').addEventListener('input', e => {
const val = e.target.value;
modalOverlay.querySelector('#val-img').textContent = val + 'px';
document.documentElement.style.setProperty('--ma-img-max', val + 'px');
});
modalOverlay.querySelector('#set-live-delay').addEventListener('input', e => {
modalOverlay.querySelector('#val-live-delay').textContent = e.target.value + 's';
});
modalOverlay.querySelector('#set-hist-max').addEventListener('input', e => modalOverlay.querySelector('#val-hist').textContent = e.target.value);
// Blacklist modal controls
modalOverlay.querySelector('#bl-author-add').addEventListener('click', () => {
const input = modalOverlay.querySelector('#bl-author-input');
const name = input.value.trim();
if (!name) return;
blacklistAuthor(name);
input.value = '';
renderBlacklistPane();
});
modalOverlay.querySelector('#bl-author-input').addEventListener('keydown', (e) => {
if (e.key === 'Enter') modalOverlay.querySelector('#bl-author-add').click();
});
modalOverlay.querySelector('#bl-clear-all').addEventListener('click', () => {
if (confirm('Vider toute la blacklist (auteurs + topics) ?')) {
saveBlacklist({ authors: [], topics: [] });
renderBlacklistPane();
}
});
/* --- 6. FORUMS & TOPICS (Core UI Builders) --- */
const forumCol = document.getElementById('forum-main-col');
const titleBox = document.querySelector('.titleMessagesUsers');
const isTopic = titleBox && document.getElementById('listMessages');
if (forumCol) {
const extractedTitle = titleBox ? titleBox.querySelector('h2, h1')?.textContent?.trim() || 'Forum Blabla 18-25 ans' : 'Forum Blabla 18-25 ans';
const flexHeader = document.createElement('div');
flexHeader.className = 'ma-header-flex';
flexHeader.innerHTML = "<div><nav>" + (isTopic ? "Topic" : "JVC Nexus") + "</nav><h1>" + extractedTitle + "</h1></div>" +
"<button class='ma-btn-new'><span class='material-symbols-outlined'>" + (isTopic ? "reply" : "add") + "</span>" + (isTopic ? "Répondre" : "Nouveau Sujet") + "</button>";
const filterBar = document.createElement('div');
filterBar.className = 'ma-filter-bar';
let tabsHtml = "<div class='ma-filter-tab active' id='tab-main'>" + (isTopic ? "Messages" : "Sujets") + "</div>";
if (isTopic && MA_CONFIG.featGallery) {
tabsHtml += `<div class='ma-filter-tab' id='tab-gallery'>Galerie</div>`;
}
if (!isTopic && MA_CONFIG.featBookmarks) {
tabsHtml += `<div class='ma-filter-tab' id='tab-favs'>Favoris <span style='font-size:10px;font-weight:700;background:var(--ma-border-solid);padding:2px 6px;border-radius:10px;margin-left:4px'>${getBookmarks().length}</span></div>`;
}
if (!isTopic && MA_CONFIG.featReplyHistory) {
tabsHtml += `<div class='ma-filter-tab' id='tab-history'>Réponses <span style='font-size:10px;font-weight:700;background:var(--ma-border-solid);padding:2px 6px;border-radius:10px;margin-left:4px'>${getReplyHistory().length}</span></div>`;
}
filterBar.innerHTML = "<div class='ma-filter-tabs'>" + tabsHtml + "</div>" +
"<div class='ma-filter-actions'><span class='ma-toast-refresh' id='ma-toast-nb'></span><span class='material-symbols-outlined' id='ma-refresh' title='Actualiser' style='cursor:pointer;font-size:20px;color:var(--ma-text-dim);transition:color .15s'>refresh</span></div>";
filterBar.querySelector('#ma-refresh').addEventListener('click', () => { window.location.reload(); });
const targetTable = document.querySelector('.tablesForum') || document.getElementById('forums-topic-survey') || document.querySelector('.container__messages');
if (targetTable && targetTable.parentElement) {
targetTable.parentElement.insertBefore(flexHeader, targetTable);
targetTable.parentElement.insertBefore(filterBar, targetTable);
}
// Mode Galerie : accessible via les raccourcis clavier (touche 'G') ou les futurs réglages, bouton navbar supprimé.
// Feature: Smart Bookmarks Toggles
const allFilterTabs = () => filterBar.querySelectorAll('.ma-filter-tab');
const resetTabs = () => {
allFilterTabs().forEach(t => t.classList.remove('active'));
// Restore EVERYTHING
document.querySelectorAll('.tablesForum__bodyRow, .messageUser, .container__navTop, .container__navBottom, .container__messages > *:not(.ma-gallery-view), .bloc-form-reponse').forEach(r => {
r.classList.remove('ma-hidden-row');
r.style.setProperty('display', '', 'important');
});
document.querySelectorAll('.ma-history-row, .ma-virtual-row, .ma-gallery-view').forEach(r => r.remove());
};
const updateBadges = () => {
const bks = getBookmarks();
const hist = getReplyHistory();
const favTab = filterBar.querySelector('#tab-favs');
const histTab = filterBar.querySelector('#tab-history');
if (favTab) favTab.querySelector('span').textContent = bks.length;
if (histTab) histTab.querySelector('span').textContent = hist.length;
};
if (isTopic && MA_CONFIG.featGallery) {
const btnGallery = filterBar.querySelector('#tab-gallery');
let nextPageUrl = null;
let allGalleryMedia = [];
let currentLbIndex = -1;
const scrapeMedia = (container) => {
const media = [];
const seen = new Set();
const normalize = (u) => {
if (!u) return "";
let clean = u.replace('http:', 'https:').split('?')[0].split('#')[0];
if (clean.includes('noelshack.com')) {
const parts = clean.split('/');
if (parts.length > 4) return "ns-" + parts.slice(4).join('/');
}
return clean;
};
const processUrl = (url, imgEl = null) => {
if (!url) return;
const norm = normalize(url);
if (seen.has(norm)) return;
// YouTube
const ytMatch = url.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=|shorts\/)|youtu\.be\/)([^"&?\/\s]{11})/);
if (ytMatch && ytMatch[1]) {
const id = ytMatch[1];
const key = `yt-${id}`;
if (!seen.has(key)) {
media.push({ type: 'video', category: 'video', url: `https://www.youtube.com/embed/${id}`, thumb: `https://img.youtube.com/vi/${id}/mqdefault.jpg`, label: 'YouTube' });
seen.add(key); seen.add(norm);
}
return;
}
// Twitter/X
if (url.includes('twitter.com/') || url.includes('x.com/')) {
if (url.includes('/status/')) {
media.push({ type: 'link', category: 'social', url: url, thumb: '', label: 'Tweet' });
seen.add(norm);
return;
}
}
// Streamable
if (url.includes('streamable.com/')) {
const sid = url.split('/').pop().split('?')[0];
if (sid && sid.length > 3) {
media.push({ type: 'video', category: 'video', url: `https://streamable.com/e/${sid}`, thumb: `https://cdn-cf-east.streamable.com/image/${sid}.jpg`, label: 'Streamable' });
seen.add(norm);
return;
}
}
// TikTok
if (url.includes('tiktok.com/') && url.includes('/video/')) {
const tm = url.match(/tiktok\.com\/@([\w.-]+)\/video\/(\d+)/);
if (tm) {
media.push({ type: 'video', category: 'social', url: `https://www.tiktok.com/embed/v2/${tm[2]}`, thumb: '', label: 'TikTok' });
seen.add(norm);
return;
}
}
// Instagram
if (url.includes('instagram.com/')) {
const im = url.match(/instagram\.com\/(?:p|reel)\/([\w-]+)/);
if (im) {
media.push({ type: 'video', category: 'social', url: `https://www.instagram.com/p/${im[1]}/embed/`, thumb: '', label: 'Instagram' });
seen.add(norm);
return;
}
}
// Reddit
if (url.includes('reddit.com/r/') && url.includes('/comments/')) {
const rm = url.match(/reddit\.com\/r\/([\w]+)\/comments\/([\w]+)/);
if (rm) {
media.push({ type: 'video', category: 'social', url: `https://www.redditmedia.com/r/${rm[1]}/comments/${rm[2]}/?ref_source=embed&embed=true&theme=dark`, thumb: '', label: 'Reddit' });
seen.add(norm);
return;
}
}
// Direct Images & NoelShack
if (url.match(/\.(jpg|jpeg|png|gif|webp)$/i) || url.includes('noelshack.com')) {
let finalUrl = norm.startsWith('ns-') ? "https://image.noelshack.com/fichiers/" + norm.substring(3) : url;
// Sticker detection: img element with explicit small dimensions (≤ 300px)
let isSticker = false;
if (imgEl) {
const w = parseInt(imgEl.getAttribute('width') || imgEl.style.width || '9999');
const h = parseInt(imgEl.getAttribute('height') || imgEl.style.height || '9999');
if ((w <= 300 || h <= 300) && (w > 0 || h > 0)) isSticker = true;
}
media.push({ type: 'image', category: 'image', url: finalUrl, thumb: finalUrl, isSticker });
seen.add(norm);
}
};
container.querySelectorAll('.messageUser__main').forEach(post => {
post.querySelectorAll('.ma-video-embed, .ma-tiktok-embed, .ma-instagram-embed, .ma-reddit-embed').forEach(w => {
if (w.dataset.originalUrl) processUrl(w.dataset.originalUrl);
else { const ifr = w.querySelector('iframe'); if (ifr) processUrl(ifr.src); }
});
post.querySelectorAll('a').forEach(a => processUrl(a.href));
post.querySelectorAll('img').forEach(img => {
if (img.src && !img.src.includes('smiley') && !img.src.includes('sticker')) processUrl(img.src, img);
});
});
return media;
};
const openLightbox = (index) => {
const item = allGalleryMedia[index];
if (!item) return;
if (item.type === 'link') {
window.open(item.url, '_blank', 'noopener,noreferrer');
return;
}
currentLbIndex = index;
let lb = document.querySelector('.ma-lightbox');
if (!lb) {
lb = document.createElement('div');
lb.className = 'ma-lightbox';
lb.innerHTML = `
<div class="ma-lightbox-content">
<span class="material-symbols-outlined ma-lightbox-close">close</span>
<div class="ma-lightbox-nav">
<div class="ma-lb-arrow" id="ma-lb-prev"><span class="material-symbols-outlined">chevron_left</span></div>
<div class="ma-lb-arrow" id="ma-lb-next"><span class="material-symbols-outlined">chevron_right</span></div>
</div>
<div id="ma-lb-body"></div>
</div>`;
const closeLB = () => {
lb.classList.remove('open');
setTimeout(() => { if (!lb.classList.contains('open')) lb.querySelector('#ma-lb-body').innerHTML = ''; }, 300);
};
lb.querySelector('.ma-lightbox-close').onclick = closeLB;
lb.onclick = (e) => { if (e.target === lb) closeLB(); };
lb.querySelector('#ma-lb-prev').onclick = (e) => { e.stopPropagation(); navigateLB(-1); };
lb.querySelector('#ma-lb-next').onclick = (e) => { e.stopPropagation(); navigateLB(1); };
document.addEventListener('keydown', (e) => {
if (!lb.classList.contains('open')) return;
if (e.key === 'ArrowLeft') navigateLB(-1);
if (e.key === 'ArrowRight') navigateLB(1);
if (e.key === 'Escape') closeLB();
});
document.body.appendChild(lb);
}
const navigateLB = (dir) => {
let nextIdx = currentLbIndex + dir;
// On cherche le prochain ou précédent qui n'est pas un lien
while (nextIdx >= 0 && nextIdx < allGalleryMedia.length) {
if (allGalleryMedia[nextIdx].type !== 'link') {
openLightbox(nextIdx);
return;
}
nextIdx += dir;
}
};
const body = lb.querySelector('#ma-lb-body');
body.innerHTML = '';
if (item.type === 'image') {
const img = document.createElement('img'); img.src = item.url; img.className = 'ma-lightbox-img'; body.appendChild(img);
} else {
const ifr = document.createElement('iframe'); ifr.src = item.url; ifr.className = 'ma-lightbox-video'; ifr.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"; ifr.allowFullscreen = true; body.appendChild(ifr);
}
// Maj de la visibilité des flèches
const prevArrow = lb.querySelector('#ma-lb-prev');
const nextArrow = lb.querySelector('#ma-lb-next');
let hasPrev = false;
for (let i = index - 1; i >= 0; i--) if (allGalleryMedia[i].type !== 'link') { hasPrev = true; break; }
let hasNext = false;
for (let i = index + 1; i < allGalleryMedia.length; i++) if (allGalleryMedia[i].type !== 'link') { hasNext = true; break; }
prevArrow.classList.toggle('hidden', !hasPrev);
nextArrow.classList.toggle('hidden', !hasNext);
lb.classList.add('open');
};
const renderGallery = (container, media, baseIdx = 0) => {
media.forEach((item, i) => {
const el = document.createElement('div');
el.className = 'ma-gallery-item';
el.onclick = () => openLightbox(baseIdx + i);
if (item.thumb) {
const img = document.createElement('img');
img.src = item.thumb;
img.loading = 'lazy';
el.appendChild(img);
} else {
const isTweet = item.type === 'link' && item.label === 'Tweet';
const placeholder = document.createElement('div');
placeholder.style.cssText = 'height:100%;width:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--ma-text-dim);font-size:12px;font-weight:700;' + (isTweet ? 'background:linear-gradient(135deg, #0d1b2a, #0a1929);' : 'background:linear-gradient(135deg, #1e293b, #0f172a);');
placeholder.innerHTML = isTweet
? `<svg style="margin-bottom:10px;width:36px;height:36px;fill:#1d9bf0;" viewBox="0 0 24 24"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.747l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>Tweet`
: `<span class="material-symbols-outlined" style="margin-bottom:8px;font-size:32px;">video_library</span>${item.label || 'Média'}`;
el.appendChild(placeholder);
}
if (item.type === 'link') {
const badge = document.createElement('div');
badge.className = 'ma-media-badge';
badge.textContent = item.label || 'LIEN';
el.appendChild(badge);
const extIcon = document.createElement('span');
extIcon.className = 'material-symbols-outlined ma-play-icon';
extIcon.textContent = 'open_in_new';
el.appendChild(extIcon);
} else if (item.type === 'video') {
const badge = document.createElement('div');
badge.className = 'ma-media-badge';
badge.textContent = item.label || 'VIDEO';
el.appendChild(badge);
const play = document.createElement('span');
play.className = 'material-symbols-outlined ma-play-icon';
play.textContent = 'play_circle';
el.appendChild(play);
}
container.appendChild(el);
});
};
btnGallery.addEventListener('click', () => {
resetTabs();
btnGallery.classList.add('active');
const messagesBox = document.querySelector('.container__messages');
document.querySelectorAll('.messageUser, .container__navTop, .container__navBottom, .bloc-form-reponse').forEach(m => {
m.classList.add('ma-hidden-row');
m.style.setProperty('display', 'none', 'important');
});
// --- Pagination via URL pattern (plus fiable que les sélecteurs CSS) ---
const parsePageFromUrl = (url) => {
const m = url.match(/\/forums\/[\d]+-[\d]+-[\d]+-(\d+)-/);
return m ? parseInt(m[1]) : 1;
};
const buildPageUrl = (baseUrl, page) => {
return baseUrl.replace(/(\/forums\/[\d]+-[\d]+-[\d]+-)(\d+)(-[\d]+-[\d]+-[\d]+-)/, `$1${page}$3`);
};
const getMaxPage = (doc) => {
let max = 1;
doc.querySelectorAll('a[href]').forEach(a => {
const href = a.getAttribute('href');
if (!href) return;
const m = href.match(/\/forums\/[\d]+-[\d]+-[\d]+-(\d+)-/);
if (m) { const n = parseInt(m[1]); if (n > max) max = n; }
});
return max;
};
const topicBaseUrl = location.href.split('?')[0].split('#')[0];
let currentGalleryPage = parsePageFromUrl(topicBaseUrl);
const maxGalleryPage = getMaxPage(document);
nextPageUrl = currentGalleryPage < maxGalleryPage
? buildPageUrl(topicBaseUrl, currentGalleryPage + 1)
: null;
// --- Filtre état ---
let activeFilter = 'all';
let hideStickers = false;
const applyGalleryFilter = () => {
grid.querySelectorAll('.ma-gallery-item').forEach(el => {
const cat = el.dataset.gcat;
const sticker = el.dataset.sticker === 'true';
const hidden = (activeFilter !== 'all' && cat !== activeFilter) || (hideStickers && sticker);
el.style.display = hidden ? 'none' : '';
});
};
// --- UI Galerie ---
const galleryView = document.createElement('div');
galleryView.className = 'ma-gallery-view';
// Barre de filtres
const filterRow = document.createElement('div');
filterRow.className = 'ma-gallery-filters';
filterRow.innerHTML = `
<button class="ma-gf-btn active" data-gf="all">Tout</button>
<button class="ma-gf-btn" data-gf="image">Images</button>
<button class="ma-gf-btn" data-gf="video">Vidéos</button>
<button class="ma-gf-btn" data-gf="social">Réseaux sociaux</button>
<label class="ma-gf-sticker-label"><input type="checkbox" id="ma-gf-hide-stickers"> Masquer stickers</label>
`;
galleryView.appendChild(filterRow);
filterRow.querySelectorAll('.ma-gf-btn').forEach(btn => {
btn.addEventListener('click', () => {
filterRow.querySelectorAll('.ma-gf-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
activeFilter = btn.dataset.gf;
applyGalleryFilter();
});
});
filterRow.querySelector('#ma-gf-hide-stickers').addEventListener('change', (e) => {
hideStickers = e.target.checked;
applyGalleryFilter();
});
const grid = document.createElement('div');
grid.className = 'ma-gallery-grid';
galleryView.appendChild(grid);
// Wrapper qui appelle renderGallery et tague chaque élément pour le filtrage
const renderGalleryFiltered = (container, mediaItems) => {
mediaItems.forEach(item => {
const absIndex = allGalleryMedia.length;
allGalleryMedia.push(item);
renderGallery(container, [item], absIndex);
const el = container.children[container.children.length - 1];
if (el) {
el.dataset.gcat = item.category || 'image';
el.dataset.sticker = item.isSticker ? 'true' : 'false';
// Appliquer le filtre courant immédiatement
const cat = el.dataset.gcat;
const sticker = el.dataset.sticker === 'true';
const hidden = (activeFilter !== 'all' && cat !== activeFilter) || (hideStickers && sticker);
if (hidden) el.style.display = 'none';
}
});
};
const media = scrapeMedia(messagesBox);
renderGalleryFiltered(grid, media);
// Bouton charger plus
const loadMore = document.createElement('div');
loadMore.className = 'ma-gallery-load-more';
if (maxGalleryPage > 1) {
loadMore.textContent = nextPageUrl
? `Charger plus de médias (page ${currentGalleryPage + 1} / ${maxGalleryPage})`
: "Tout est chargé";
if (!nextPageUrl) { loadMore.style.opacity = '0.4'; loadMore.style.pointerEvents = 'none'; }
} else {
loadMore.style.display = 'none';
}
loadMore.onclick = async () => {
if (!nextPageUrl) return;
const pageToLoad = parsePageFromUrl(nextPageUrl);
loadMore.textContent = `Chargement page ${pageToLoad} / ${maxGalleryPage}…`;
loadMore.style.pointerEvents = 'none';
try {
const fetchUrl = nextPageUrl;
const res = await fetch(fetchUrl);
const rawText = await res.text();
const freshDoc = new DOMParser().parseFromString(rawText, 'text/html');
const freshMedia = scrapeMedia(freshDoc);
renderGalleryFiltered(grid, freshMedia);
currentGalleryPage = pageToLoad;
nextPageUrl = currentGalleryPage < maxGalleryPage
? buildPageUrl(topicBaseUrl, currentGalleryPage + 1)
: null;
if (!nextPageUrl) {
loadMore.textContent = "Tout est chargé";
loadMore.style.opacity = '0.4';
loadMore.style.pointerEvents = 'none';
} else {
loadMore.textContent = `Charger plus de médias (page ${currentGalleryPage + 1} / ${maxGalleryPage})`;
loadMore.style.pointerEvents = '';
}
} catch (e) {
loadMore.textContent = "Erreur — réessayer";
loadMore.style.pointerEvents = '';
}
};
galleryView.appendChild(loadMore);
messagesBox.prepend(galleryView);
});
}
if (!isTopic && MA_CONFIG.featBookmarks) {
const btnFavs = filterBar.querySelector('#tab-favs');
btnFavs.addEventListener('click', () => {
resetTabs(); btnFavs.classList.add('active');
const bks = getBookmarks();
const table = document.querySelector('.tablesForum');
// Hide ALL regular rows
document.querySelectorAll('.tablesForum__bodyRow').forEach(r => { r.classList.add('ma-hidden-row'); r.style.display = 'none'; });
if (!bks.length) {
const empty = document.createElement('div'); empty.className = 'ma-virtual-row'; empty.style.cssText = 'padding:32px;text-align:center;color:var(--ma-outline);font-style:italic;'; empty.textContent = 'Aucun favori.'; table.appendChild(empty);
return;
}
// Clear All button
const clearBtn = document.createElement('div');
clearBtn.className = 'ma-virtual-row';
clearBtn.style.cssText = 'padding:15px; text-align:center; border-bottom:1px solid var(--ma-border-light); cursor:pointer; color:var(--ma-outline); font-size:12px; font-weight:700; text-transform:uppercase;';
clearBtn.textContent = "Vider les favoris";
clearBtn.onclick = () => { if (confirm("Vider les favoris ?")) { localStorage.setItem('MidnightArchivistBookmarks', '[]'); resetTabs(); updateBadges(); } };
table.appendChild(clearBtn);
bks.forEach(entry => {
// If entry doesn't have a title (old format), we might need to handle it or just use 'Favori'
const title = entry.title || (entry.html && entry.html !== 'saved' ? entry.html : 'Topic #' + entry.id);
const cleanTitle = title.replace(/^(star_border|folder_special|folder|whatshot|star)\s+/i, '');
const row = document.createElement('div');
row.className = 'ma-virtual-row';
row.style.cssText = 'display:grid; grid-template-columns: minmax(0,1fr) 40px; align-items:center; padding:12px 25px; border-bottom:1px solid var(--ma-border-light); transition: background .15s; cursor:pointer;';
row.innerHTML = `
<div style="display:flex;align-items:center;gap:12px;min-width:0;"><span class="material-symbols-outlined" style="font-size:18px;color:var(--ma-yellow);flex-shrink:0;">star</span><a href="https://www.jeuxvideo.com/forums/0-51-0-1-0-1-0-0.htm" data-id="${entry.id}" class="ma-bks-link" style="color:var(--ma-text);text-decoration:none;font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${cleanTitle}</a></div>
<div class="ma-delete-bks" style="text-align:right;"><span class="material-symbols-outlined" style="font-size:18px;color:var(--ma-outline);cursor:pointer;">delete</span></div>`;
// In a real scenario, we'd want the actual URL. For now we use a placeholder or try to find it if we saved it.
// Since old format didn't save title/url, we'll try to find if it exists in the current page
const existingRow = document.getElementById('topic-' + entry.id);
if (existingRow) {
const link = existingRow.querySelector('a.tablesForum__cellSubject');
if (link) { row.querySelector('.ma-bks-link').href = link.href; row.querySelector('.ma-bks-link').textContent = link.querySelector('.tablesForum__subjectText')?.textContent || link.textContent; }
}
row.querySelector('.ma-delete-bks').addEventListener('click', (e) => { e.stopPropagation(); removeBookmark(entry.id); row.remove(); updateBadges(); });
row.addEventListener('mouseenter', () => row.style.background = 'var(--ma-surface-low)');
row.addEventListener('mouseleave', () => row.style.background = '');
table.appendChild(row);
});
});
}
const btnMain = filterBar.querySelector('#tab-main');
if (btnMain) btnMain.addEventListener('click', () => { resetTabs(); btnMain.classList.add('active'); });
// Feature: Reply History UI
if (!isTopic && MA_CONFIG.featReplyHistory) {
const btnHist = filterBar.querySelector('#tab-history');
const hist = getReplyHistory();
if (hist.length > 0 && hist.some(e => e.newReplies > 0)) {
const totalNew = hist.reduce((sum, e) => sum + (e.newReplies || 0), 0);
btnHist.insertAdjacentHTML('beforeend', `<span class="ma-hist-badge" style="background:var(--ma-accent);color:#fff;font-size:10px;font-weight:800;padding:2px 6px;border-radius:99px;margin-left:6px;">${totalNew}</span>`);
}
if (btnHist) {
btnHist.addEventListener('click', () => {
resetTabs(); btnHist.classList.add('active');
const badge = btnHist.querySelector('.ma-hist-badge'); if (badge) badge.remove();
document.querySelectorAll('.tablesForum__bodyRow').forEach(r => { r.classList.add('ma-hidden-row'); r.style.display = 'none'; });
const hist = getReplyHistory();
const table = document.querySelector('.tablesForum');
if (!hist.length) {
const empty = document.createElement('div'); empty.className = 'ma-history-row'; empty.style.cssText = 'padding:32px;text-align:center;color:var(--ma-outline);font-style:italic;'; empty.textContent = 'Aucune réponse enregistrée.'; table.appendChild(empty);
return;
}
// Mark All as Read button
const markReadBtn = document.createElement('div');
markReadBtn.className = 'ma-history-row';
markReadBtn.style.cssText = 'padding:15px; text-align:center; border-bottom:1px solid var(--ma-border-light); cursor:pointer; color:var(--ma-accent); font-size:12px; font-weight:700; text-transform:uppercase; background:var(--ma-accent-dim);';
markReadBtn.textContent = "Marquer comme lus";
markReadBtn.onclick = () => {
hist.forEach(e => {
if (e.newReplies > 0) {
e.msgCount += e.newReplies;
e.newReplies = 0;
}
});
saveReplyHistory(hist);
localStorage.setItem('MA_SeenTopics', '[]');
document.querySelectorAll('.ma-hist-badge').forEach(b => b.remove());
document.querySelectorAll('.ma-history-row').forEach(r => r.querySelector('span')?.style?.setProperty('display', 'none'));
btnHist.querySelectorAll('span').forEach(s => { if (s.textContent.match(/^\+\d+$/) || s.textContent.match(/^\d+$/)) s.remove(); });
window.location.reload();
};
table.appendChild(markReadBtn);
// Clear All button
const clearBtn = document.createElement('div');
clearBtn.className = 'ma-history-row';
clearBtn.style.cssText = 'padding:15px; text-align:center; border-bottom:1px solid var(--ma-border-light); cursor:pointer; color:var(--ma-outline); font-size:12px; font-weight:700; text-transform:uppercase;';
clearBtn.textContent = "Vider l'historique";
clearBtn.onclick = () => { if (confirm("Vider l'historique des réponses ?")) { localStorage.setItem('MA_ReplyHistory', '[]'); resetTabs(); updateBadges(); } };
table.appendChild(clearBtn);
hist.forEach(entry => {
const cleanTitle = entry.title.replace(/^(star_border|folder_special|folder|whatshot|star)\s+/i, '');
const row = document.createElement('div');
row.className = 'ma-history-row';
row.style.cssText = 'display:grid; grid-template-columns: minmax(0,1fr) 120px 110px 40px; align-items:center; padding:10px 25px; border-bottom:1px solid var(--ma-border-light); transition: background .15s; cursor:pointer;';
const newBadge = entry.newReplies > 0 ? `<span style="background:var(--ma-accent);color:#fff;font-size:11px;font-weight:800;padding:2px 8px;border-radius:99px;margin-left:8px;">+${entry.newReplies}</span>` : '';
row.innerHTML = `
<div style="display:flex;align-items:center;gap:12px;min-width:0;"><span class="material-symbols-outlined" style="font-size:18px;color:var(--ma-accent);flex-shrink:0;">reply</span><a href="${entry.url}" style="color:var(--ma-text);text-decoration:none;font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${cleanTitle}</a>${newBadge}</div>
<div style="color:var(--ma-outline);font-size:12px;text-align:center;">${entry.msgCount} msgs</div>
<div style="color:var(--ma-outline);font-size:11px;text-align:right;">${new Date(entry.repliedAt).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}</div>
<div class="ma-delete-hist" style="text-align:right;"><span class="material-symbols-outlined" style="font-size:18px;color:var(--ma-outline);cursor:pointer;">delete</span></div>`;
row.addEventListener('click', () => { entry.newReplies = 0; saveReplyHistory(hist); });
row.querySelector('.ma-delete-hist').addEventListener('click', (e) => { e.stopPropagation(); let h = getReplyHistory().filter(x => x.topicId !== entry.topicId); saveReplyHistory(h); row.remove(); updateBadges(); });
row.addEventListener('mouseenter', () => row.style.background = 'var(--ma-surface-low)');
row.addEventListener('mouseleave', () => row.style.background = '');
table.appendChild(row);
});
});
}
// Auto-check for new replies every 5 minutes
const checkReplyHistory = async () => {
let h = getReplyHistory();
let updated = false;
for (const entry of h.slice(0, 10)) { // check top 10 only per cycle
try {
const res = await fetch(entry.url);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
// Get current msg count from the pager or count messages
const rows = doc.querySelectorAll('.messageUser');
const scripts = doc.querySelectorAll('script');
let currentCount = entry.msgCount;
for (const s of scripts) {
const m = s.textContent.match(/forumsAppPayload\s*=\s*"([^"]+)"/);
if (m && m[1]) {
try {
const p = JSON.parse(atob(m[1]));
if (p.listMessage) currentCount = p.listMessage.length + (((p.pagerView?.currentPage || 1) - 1) * 20);
} catch (e) { }
break;
}
}
if (currentCount > entry.msgCount) {
entry.newReplies = currentCount - entry.msgCount;
updated = true;
}
entry.lastChecked = Date.now();
} catch (e) { }
}
if (updated) {
saveReplyHistory(h);
// Update badge on history tab
const totalNew = h.reduce((sum, e) => sum + (e.newReplies || 0), 0);
if (btnHist && totalNew > 0) {
const existing = btnHist.querySelector('.ma-hist-badge');
if (existing) existing.textContent = totalNew;
else btnHist.insertAdjacentHTML('beforeend', `<span class="ma-hist-badge" style="background:var(--ma-accent);color:#fff;font-size:10px;font-weight:800;padding:2px 6px;border-radius:99px;margin-left:6px;">${totalNew}</span>`);
}
}
};
// Initial check after 10s, then every 5 minutes
setTimeout(checkReplyHistory, 10000);
setInterval(checkReplyHistory, 300000);
}
// Feature: Quick Scroll Form
flexHeader.querySelector('.ma-btn-new').addEventListener('click', (e) => {
e.preventDefault();
const editor = document.getElementById('forums-post-topic-editor') || document.querySelector('.form-post-message');
if (editor) { editor.scrollIntoView({ behavior: 'smooth', block: 'center' }); const input = editor.querySelector('input[name="titre"], textarea'); if (input) setTimeout(() => input.focus(), 300); }
});
}
// Helper partagé : enregistre la réponse dans l'historique depuis la page du topic
const trackReplyFromTopic = (btn = null) => {
if (!MA_CONFIG.featReplyHistory) return;
try {
let tId, fId;
// Méthode 1 : Depuis le formulaire (très fiable au moment du clic)
if (btn) {
const form = btn.closest('form');
if (form) {
tId = form.querySelector('[name="topicId"]')?.value;
fId = form.querySelector('[name="forumId"]')?.value;
}
}
// Méthode 2 : Repli via l'URL et le DOM si Méthode 1 échoue
if (!tId) {
const scripts = document.querySelectorAll('script');
for (const s of scripts) {
const m = s.textContent.match(/forumsAppPayload\s*=\s*"([^"]+)"/);
if (m && m[1]) {
const p = JSON.parse(atob(m[1]));
tId = p.topicId; fId = p.forumId;
break;
}
}
}
if (tId) {
const titleBox = document.querySelector('.titleMessagesUsers');
const extractedTitle = titleBox ? titleBox.querySelector('h2, h1')?.textContent?.trim() : (document.title.split(' sur le forum')[0] || 'Sans titre');
const msgCount = document.querySelectorAll('.messageUser').length + 1;
let cleanUrl = location.href.split('#')[0];
cleanUrl = cleanUrl.replace(/-[0-9]+-([0-9]+-[0-9]+-[0-9]+-[^.]+)\.htm/i, '-1-$1.htm');
if (typeof addToReplyHistory === 'function') {
addToReplyHistory(tId, fId, extractedTitle, cleanUrl, msgCount);
updateBadges();
}
}
} catch (e) { console.warn('[MA] Trace history failed', e); }
};
// Feature: Auto Backup (Bypass Captcha/Erreur)
if (MA_CONFIG.featBypass) {
const textarea = document.querySelector('textarea[name="message_topic"], textarea[name="message"]');
if (textarea) {
const draft = localStorage.getItem('MidnightArchivistDraft');
if (draft && !textarea.value) {
textarea.value = draft;
const draftPill = document.createElement('span'); draftPill.className = 'ma-draft-badge'; draftPill.textContent = "Brouillon restauré";
textarea.parentNode.insertBefore(draftPill, textarea.nextSibling);
}
textarea.addEventListener('keyup', () => localStorage.setItem('MidnightArchivistDraft', textarea.value));
const form = textarea.closest('form');
if (form) {
form.dataset.maTracked = '1';
form.addEventListener('submit', () => {
localStorage.removeItem('MidnightArchivistDraft');
trackReplyFromTopic();
});
}
}
}
// Suivi de l'historique des réponses même sans featBypass
if (MA_CONFIG.featReplyHistory && isTopic) {
// Détection par clic sur le bouton "Poster" (très fiable)
document.addEventListener('click', (e) => {
const btn = e.target.closest('.btn-poster-msg, .js-post-message, button[type="submit"]');
if (btn) {
localStorage.removeItem('MidnightArchivistDraft');
trackReplyFromTopic(btn);
}
});
// Détection de secours par soumission de formulaire
const form = document.querySelector('textarea[name="message_topic"], textarea[name="message"]')?.closest('form');
if (form && !form.dataset.maTracked) {
form.dataset.maTracked = '1';
form.addEventListener('submit', () => trackReplyFromTopic());
}
}
/* --- 7. DRAWER SPLIT VIEW ET QUICK REPLY --- */
let drawerPanel = null;
let actionFormHtml = ""; // Quick reply secret form storage
let activeTopicId = null;
let qrDrafts = {}; // Stockage temporaire des brouillons de réponse rapide
if (!isTopic) {
// Extraction du formulaire native pur AJAX POST, au cas où sur la liste des sujets y'en a pas,
// On ne gère le Quick Reply que si on fetch la page du topic
drawerPanel = document.createElement('div');
drawerPanel.className = 'ma-drawer-reader';
drawerPanel.innerHTML = `
<div class="ma-drawer-header">
<div class="ma-drawer-infos" style="flex:1;min-width:0;">
<span class="ma-drawer-badge" id="drawer-badge">Aperçu</span>
<div id="drawer-topic-title" style="font-size:14px; font-weight:700; color:var(--ma-text); margin:6px 0 4px 0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"></div>
<div style="display:flex; gap:12px; align-items:center;">
<span class="ma-drawer-author" id="drawer-author"></span>
<span class="ma-drawer-date" id="drawer-date"></span>
</div>
</div>
${MA_CONFIG.featBookmarks ? `<button id="drawer-star-btn" title="Ajouter aux favoris"><span class="material-symbols-outlined" style="font-size:18px;">star_border</span></button>` : ''}
</div>
<div class="ma-drawer-content" id="drawer-content"></div>
${MA_CONFIG.featQuickR ? '<div class="ma-drawer-quickreply" id="drawer-qr" style="display:none"></div>' : ''}
`;
document.body.appendChild(drawerPanel);
let hideDrawerTimeout;
let activeDrawerRow = null;
const fetchPreviewInfos = async (url, isLastNode, topicTitle) => {
try {
drawerPanel.querySelector('#drawer-author').textContent = '';
drawerPanel.querySelector('#drawer-date').textContent = '';
drawerPanel.querySelector('#drawer-badge').textContent = isLastNode ? 'Dernier Message' : 'Premier Message';
drawerPanel.querySelector('#drawer-topic-title').textContent = topicTitle || '';
drawerPanel.querySelector('#drawer-content').innerHTML = `<div class="ma-drawer-loader">Extraction de la matrice d'obsidienne...</div>`;
if (MA_CONFIG.featQuickR) drawerPanel.querySelector('#drawer-qr').style.display = 'none';
const _resetStar = drawerPanel.querySelector('#drawer-star-btn');
if (_resetStar) { _resetStar.classList.remove('bookmarked'); const _si = _resetStar.querySelector('.material-symbols-outlined'); if (_si) _si.textContent = 'star_border'; _resetStar.onclick = null; }
const res = await fetch(url);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const messages = doc.querySelectorAll('.messageUser');
const targetMsg = isLastNode ? messages[messages.length - 1] : messages[0];
// Extract CSRF tokens from forumsAppPayload for posting
let formSession = null;
let ajaxToken = null;
let topicId = null;
let forumId = null;
try {
const scripts = doc.querySelectorAll('script');
for (const s of scripts) {
const match = s.textContent.match(/forumsAppPayload\s*=\s*"([^"]+)"/);
if (match && match[1]) {
const payload = JSON.parse(atob(match[1]));
formSession = payload.formSession || null;
ajaxToken = payload.ajaxToken || null;
topicId = payload.topicId || null;
forumId = payload.forumId || null;
break;
}
}
} catch (parseErr) { console.warn('[MA] Failed to parse forumsAppPayload', parseErr); }
if (targetMsg) {
drawerPanel.querySelector('#drawer-author').textContent = targetMsg.querySelector('.messageUser__label')?.textContent || 'Anonyme';
drawerPanel.querySelector('#drawer-date').textContent = targetMsg.querySelector('.messageUser__date')?.textContent || '';
drawerPanel.querySelector('#drawer-content').innerHTML = targetMsg.querySelector('.messageUser__msg')?.innerHTML || '';
// Wire up drawer bookmark button
if (MA_CONFIG.featBookmarks) {
const drawerStarBtn = drawerPanel.querySelector('#drawer-star-btn');
if (drawerStarBtn) {
const tid = topicId || url.match(/forums\/\d+-\d+-\d+-\d+-(\d+)-/)?.[1] || '';
const bks = getBookmarks();
const isStarred = tid && bks.some(b => b.id === tid);
const starIcon = drawerStarBtn.querySelector('.material-symbols-outlined');
drawerStarBtn.classList.toggle('bookmarked', isStarred);
if (starIcon) starIcon.textContent = isStarred ? 'star' : 'star_border';
drawerStarBtn.onclick = () => {
const nowStarred = drawerStarBtn.classList.contains('bookmarked');
if (nowStarred) {
removeBookmark(tid);
drawerStarBtn.classList.remove('bookmarked');
if (starIcon) starIcon.textContent = 'star_border';
// Sync the row star if visible
const rowStar = document.querySelector(`[id="topic-${tid}"] .ma-star-btn`);
if (rowStar) { rowStar.classList.remove('bookmarked'); rowStar.textContent = 'star_border'; }
} else {
const cleanTitle = (topicTitle || '').replace(/^(star_border|folder_special|folder|whatshot|star|reply)\s+/i, '');
saveBookmark(tid, cleanTitle, url);
drawerStarBtn.classList.add('bookmarked');
if (starIcon) starIcon.textContent = 'star';
// Sync the row star if visible
const rowStar = document.querySelector(`[id="topic-${tid}"] .ma-star-btn`);
if (rowStar) { rowStar.classList.add('bookmarked'); rowStar.textContent = 'star'; }
}
updateBadges();
};
}
}
// Setup Quick Reply with native editor from fetched page
if (MA_CONFIG.featQuickR) {
const qrBox = drawerPanel.querySelector('#drawer-qr');
// Inject manual full editor snippet with native IDs so that toolbar buttons (smileys, images) might work if JVC JS hooks via event delegation.
qrBox.innerHTML = `
<div style="padding:16px 24px 0; font-weight:700; font-size:12px; color:var(--ma-outline); text-transform:uppercase; letter-spacing:0.05em;">Réponse Rapide</div>
<div class="messageEditor__containerEdit">
<textarea name="message_reponse" id="message_reponse" class="messageEditor__edit js-textarea-message-editor" placeholder="Pour que les discussions restent agréables, merci de rester poli..."></textarea>
<div class="messageEditor__buttonEdit">
<div class="buttonsEditor">
<div class="buttonsEditor__group">
<button class="buttonsEditor__button" type="button" title="Gras"><span class="buttonsEditor__icon jvcode-bold"></span></button>
<button class="buttonsEditor__button" type="button" title="Italique"><span class="buttonsEditor__icon jvcode-italic"></span></button>
<button class="buttonsEditor__button" type="button" title="Souligné"><span class="buttonsEditor__icon jvcode-underline"></span></button>
<button class="buttonsEditor__button" type="button" title="Barré"><span class="buttonsEditor__icon jvcode-strike"></span></button>
</div>
<div class="buttonsEditor__group">
<button class="buttonsEditor__button" type="button" title="Liste"><span class="buttonsEditor__icon jvcode-list"></span></button>
<button class="buttonsEditor__button" type="button" title="Listes numérotées"><span class="buttonsEditor__icon jvcode-numbered-list"></span></button>
<button class="buttonsEditor__button" type="button" title="Citation"><span class="buttonsEditor__icon jvcode-quotes-right"></span></button>
<button class="buttonsEditor__button" type="button" title="Code"><span class="buttonsEditor__icon jvcode-embed"></span></button>
<button class="buttonsEditor__button" type="button" title="Spoiler"><span class="buttonsEditor__icon jvcode-eye-blocked"></span></button>
</div>
<div class="buttonsEditor__group">
<button class="buttonsEditor__button" type="button" title="Smileys"><span class="buttonsEditor__icon jvcode-smiley"></span></button>
<button class="buttonsEditor__button" type="button" title="Image"><span class="buttonsEditor__icon jvcode-image"></span></button>
<button class="buttonsEditor__button" type="button" title="Vidéo"><span class="buttonsEditor__icon jvcode-film"></span></button>
</div>
</div>
</div>
</div>
<div id="ma-drawer-qr-status" style="font-size:12px; margin:0 24px 8px; color:var(--ma-outline); display:none;"></div>
<button id="ma-drawer-btn-post" type="button">Réponse rapide</button>
`;
qrBox.style.display = 'block';
const btnPost = qrBox.querySelector('#ma-drawer-btn-post');
const textarea = qrBox.querySelector('#ma_qr_message_reponse') || qrBox.querySelector('textarea');
const statusEl = qrBox.querySelector('#ma-drawer-qr-status');
// Restauration du brouillon si présent pour ce topic
if (topicId && qrDrafts[topicId] && textarea) {
textarea.value = qrDrafts[topicId];
}
// Sauvegarde automatique du brouillon à la saisie
if (textarea) {
textarea.addEventListener('input', () => {
if (topicId) qrDrafts[topicId] = textarea.value;
});
}
// Implémentation manuelle des boutons de formatage car JVC app.js n'est pas bindé ici
const wrapText = (before, after) => {
if (!textarea) return;
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const sel = textarea.value.substring(start, end);
textarea.value = textarea.value.substring(0, start) + before + sel + after + textarea.value.substring(end);
textarea.selectionStart = start + before.length;
textarea.selectionEnd = end + before.length;
textarea.focus();
};
const insertLine = (prefix) => {
if (!textarea) return;
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
let sel = textarea.value.substring(start, end);
if (!sel) sel = "texte";
const lines = sel.split('\n').map(l => prefix + l).join('\n');
textarea.value = textarea.value.substring(0, start) + lines + textarea.value.substring(end);
textarea.selectionStart = start + lines.length;
textarea.selectionEnd = start + lines.length;
textarea.focus();
};
qrBox.querySelectorAll('.buttonsEditor__button').forEach(btn => {
btn.addEventListener('click', () => {
const icon = btn.querySelector('.buttonsEditor__icon');
if (!icon) return;
if (icon.classList.contains('jvcode-bold')) wrapText("'''", "'''");
else if (icon.classList.contains('jvcode-italic')) wrapText("''", "''");
else if (icon.classList.contains('jvcode-underline')) wrapText("<u>", "</u>");
else if (icon.classList.contains('jvcode-strike')) wrapText("<s>", "</s>");
else if (icon.classList.contains('jvcode-list')) insertLine("* ");
else if (icon.classList.contains('jvcode-numbered-list')) insertLine("# ");
else if (icon.classList.contains('jvcode-quotes-right')) insertLine("> ");
else if (icon.classList.contains('jvcode-embed')) wrapText("<code>", "</code>");
else if (icon.classList.contains('jvcode-eye-blocked')) wrapText("<spoil>", "</spoil>");
else if (icon.classList.contains('jvcode-smiley')) { alert("Astuce : tapez vos smileys manuellement (ex: :hap:, :noel:, :pf:)"); textarea.focus(); }
else if (icon.classList.contains('jvcode-image') || icon.classList.contains('jvcode-film')) { alert("Veuillez transférer vos images/vidéos manuellement ou via NoelShack dans un autre onglet pour le moment."); textarea.focus(); }
});
});
btnPost.addEventListener('click', async () => {
const msg = textarea ? textarea.value.trim() : '';
if (!msg) { if (textarea) textarea.style.borderColor = '#e74c3c'; return; }
if (textarea) textarea.style.borderColor = 'var(--ma-border-light)';
btnPost.disabled = true;
btnPost.style.opacity = '0.6';
btnPost.textContent = 'Envoi en cours...';
statusEl.style.display = 'none';
try {
// Build FormData matching JVC's actual API
const fd = new FormData();
fd.append('text', msg);
fd.append('topicId', topicId || '');
fd.append('forumId', forumId || '');
fd.append('group', '1');
fd.append('messageId', 'undefined');
if (formSession) {
for (const [key, value] of Object.entries(formSession)) {
fd.append(key, String(value));
}
}
if (ajaxToken) {
fd.append('ajax_hash', ajaxToken);
}
fd.append('resetFormAfterSuccess', 'false');
const postRes = await fetch('https://www.jeuxvideo.com/forums/message/add', {
method: 'POST',
body: fd,
credentials: 'include',
headers: {
'accept': 'application/json',
'x-requested-with': 'XMLHttpRequest'
}
});
if (postRes.ok || postRes.redirected) {
// Verify the message was actually posted by re-fetching
const verifyRes = await fetch(url);
const verifyText = await verifyRes.text();
// Save to reply history
try {
const verifyDoc = new DOMParser().parseFromString(verifyText, 'text/html');
const msgCount = verifyDoc.querySelectorAll('.messageUser').length;
if (typeof addToReplyHistory === 'function') addToReplyHistory(topicId, forumId, topicTitle || 'Sans titre', url, msgCount);
} catch (e) { }
btnPost.textContent = '✔ Posté !';
btnPost.style.background = '#27ae60';
if (textarea) textarea.value = '';
if (topicId) delete qrDrafts[topicId]; // Effacer le brouillon après succès
statusEl.textContent = 'Message envoyé avec succès.';
statusEl.style.color = '#27ae60';
statusEl.style.display = 'block';
setTimeout(() => {
btnPost.textContent = 'Poster';
btnPost.style.background = 'var(--ma-accent)';
btnPost.style.opacity = '1';
btnPost.disabled = false;
}, 2500);
} else {
throw new Error('HTTP ' + postRes.status);
}
} catch (err) {
btnPost.textContent = '❌ Erreur - Réessayer';
btnPost.style.background = '#e74c3c';
statusEl.textContent = 'Échec: ' + err.message + '. Vérifiez que vous êtes connecté.';
statusEl.style.color = '#e74c3c';
statusEl.style.display = 'block';
btnPost.disabled = false;
btnPost.style.opacity = '1';
setTimeout(() => {
btnPost.textContent = 'Poster';
btnPost.style.background = 'var(--ma-accent)';
}, 3000);
}
});
}
} else {
drawerPanel.querySelector('#drawer-content').innerHTML = `<div class="ma-drawer-loader">Sujet introuvable ou supprimé.</div>`;
}
} catch (e) { drawerPanel.querySelector('#drawer-content').innerHTML = `<div class="ma-drawer-loader">Erreur de connexion spatio-temporelle.</div>`; }
};
document.addEventListener('mouseover', (e) => {
if (!MA_CONFIG.featQuickR) return;
const row = e.target.closest('.tablesForum__bodyRow');
if (row) {
const link = row.querySelector('a.tablesForum__cellSubject');
if (!link || !link.href) return;
const isLast = !!e.target.closest('a.tablesForum__cellLink');
const targetUrl = isLast ? e.target.closest('a.tablesForum__cellLink').href : link.href;
const titleNode = link.querySelector('.tablesForum__subjectText');
const topicTitle = titleNode ? titleNode.textContent.trim() : link.textContent.trim();
// Clear history badge if hovered
const tid = row.id.replace('topic-', '');
let hist = getReplyHistory();
let histFound = hist.find(x => x.topicId === tid);
if (histFound && histFound.newReplies > 0) {
histFound.newReplies = 0;
saveReplyHistory(hist);
updateBadges();
}
clearTimeout(hideDrawerTimeout);
row._previewOpenTimeout = setTimeout(() => {
if (row.matches(':hover') || drawerPanel.matches(':hover')) {
if (drawerPanel._currentUrl !== targetUrl) {
drawerPanel._currentUrl = targetUrl;
fetchPreviewInfos(targetUrl, isLast, topicTitle);
}
// Highlight active row
if (activeDrawerRow && activeDrawerRow !== row) {
activeDrawerRow.style.background = '';
}
activeDrawerRow = row;
row.style.background = 'rgba(var(--ma-accent-rgb, 100,100,255), 0.08)';
drawerPanel.classList.add('open');
}
}, 300);
}
});
document.addEventListener('mouseout', (e) => {
const row = e.target.closest('.tablesForum__bodyRow');
if (row) { clearTimeout(row._previewOpenTimeout); }
// On vérifie si la souris n'est pas allée ni dans le Drawer ni sur une autre ligne
if (!e.relatedTarget?.closest('.ma-drawer-reader') && !e.relatedTarget?.closest('.tablesForum__bodyRow')) {
hideDrawerTimeout = setTimeout(() => {
drawerPanel.classList.remove('open');
drawerPanel._currentUrl = null;
if (activeDrawerRow) { activeDrawerRow.style.background = ''; activeDrawerRow = null; }
}, 350);
}
});
drawerPanel.addEventListener('mouseenter', () => clearTimeout(hideDrawerTimeout));
drawerPanel.addEventListener('mouseleave', () => { hideDrawerTimeout = setTimeout(() => { drawerPanel.classList.remove('open'); drawerPanel._currentUrl = null; }, 200); });
}
/* --- 8. ROW PROCESSOR (Icones, Highlight, Bookmarks) --- */
const processRows = (container = document) => {
const bks = getBookmarks().map(b => b.id);
document.querySelectorAll('.tablesForum__bodyRow:not(.ma-processed-row)').forEach(row => {
const bks = getBookmarks().map(b => b.id);
row.classList.add('ma-processed-row');
// Blacklist check — masquer les rows blacklistés
if (!isTopic && isBlacklisted(row)) {
row.classList.add('ma-hidden-row');
row.style.setProperty('display', 'none', 'important');
return;
}
// Hide if we are currently in a filtered tab (Bookmarks or History)
const activeTab = document.querySelector('.ma-filter-tab.active');
if (activeTab && activeTab.id !== 'tab-main') {
row.classList.add('ma-hidden-row');
row.style.setProperty('display', 'none', 'important');
}
// Icones Replacement (Nexus theme only)
if (!MA_CONFIG.featVanillaTheme) {
const iconEl = row.querySelector('i[class*="icon-topic"], .tablesForum__subjectMarkerIcon');
if (iconEl) {
const isPinned = iconEl.classList.contains('icon-topic-pin');
const isHot = iconEl.classList.contains('tablesForum__iconTopicRed') || iconEl.classList.contains('icon-topic-fire');
const newIcon = document.createElement('i');
newIcon.className = 'ma-custom-icon';
const span = document.createElement('span');
span.className = 'material-symbols-outlined';
const titleEl = iconEl.parentElement.querySelector('.tablesForum__subjectText');
if (isPinned) {
span.textContent = 'folder_special'; span.style.color = 'var(--ma-yellow)';
if (titleEl && !titleEl.dataset.pinned) { titleEl.dataset.pinned = 'true'; titleEl.insertAdjacentHTML('afterbegin', '<span class="ma-badge-pin">Épinglé</span>'); }
}
else if (isHot) { span.textContent = 'whatshot'; span.style.color = 'var(--ma-accent)'; }
else { span.textContent = 'folder'; span.style.color = 'var(--ma-yellow)'; }
newIcon.appendChild(span);
iconEl.parentNode.replaceChild(newIcon, iconEl);
// Highlight Keywords
if (titleEl) highlightTopic(titleEl);
}
} else {
// Vanilla mode: highlight keywords on native title element
const titleEl = row.querySelector('.tablesForum__subjectText');
if (titleEl) highlightTopic(titleEl);
}
// Bookmark Button Injection
if (!isTopic && MA_CONFIG.featBookmarks) {
const tid = row.id.replace('topic-', '');
const subjCell = row.querySelector('.tablesForum__cellSubject');
if (subjCell) {
const star = document.createElement('span');
star.className = 'material-symbols-outlined ma-star-btn ' + (bks.includes(tid) ? 'bookmarked' : '');
star.textContent = bks.includes(tid) ? 'star' : 'star_border';
star.title = "Ajouter aux favoris";
subjCell.appendChild(star);
// Clic géré par délégation sur .tablesForum (voir bas du fichier)
}
}
// Blacklist button injection
if (!isTopic) {
const tid = row.id.replace('topic-', '');
const subjCell = row.querySelector('.tablesForum__cellSubject');
if (subjCell) {
const blBtn = document.createElement('span');
blBtn.className = 'material-symbols-outlined ma-bl-btn';
blBtn.textContent = 'block';
blBtn.title = 'Blacklister';
subjCell.appendChild(blBtn);
}
}
});
// Topic Embeds + Move action buttons to header top-right
if (isTopic) {
container.querySelectorAll('.messageUser__main').forEach(transformEmbeds);
// Move .messageUser__footer (action buttons) into .messageUser__header top-right
container.querySelectorAll('.messageUser:not(.ma-actions-moved)').forEach(msg => {
msg.classList.add('ma-actions-moved');
const header = msg.querySelector('.messageUser__header');
const footer = msg.querySelector('.messageUser__footer');
if (header && footer) {
footer.classList.add('ma-header-actions');
// Inject blacklist author button into the footer (now in header)
const authorEl = msg.querySelector('.messageUser__label');
const authorName = authorEl ? authorEl.textContent.trim() : null;
if (authorName) {
const blBtn = document.createElement('span');
blBtn.className = 'material-symbols-outlined ma-bl-author-btn';
blBtn.textContent = 'person_off';
blBtn.title = `Blacklister ${authorName}`;
blBtn.dataset.author = authorName;
footer.appendChild(blBtn);
}
header.appendChild(footer);
}
});
}
};
// --- IntersectionObserver : apparition fluide des éléments ---
const fadeObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('ma-visible');
fadeObserver.unobserve(entry.target);
}
});
}, { threshold: 0.05 });
const observeFadeIn = (el) => {
el.classList.add('ma-fade-in');
fadeObserver.observe(el);
};
// --- Bouton Retour en Haut ---
const scrollTopBtn = document.createElement('div');
scrollTopBtn.id = 'ma-scroll-top';
scrollTopBtn.title = "Retourner en haut";
scrollTopBtn.innerHTML = "<span class='material-symbols-outlined'>arrow_upward</span>";
document.body.appendChild(scrollTopBtn);
scrollTopBtn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
window.addEventListener('scroll', throttle(() => {
scrollTopBtn.classList.toggle('visible', window.scrollY > window.innerHeight * 2);
}, 200));
processRows();
const _throttledProcessRows = throttle(processRows, 300);
new MutationObserver((muts) => { if (muts.some(m => m.addedNodes.length)) _throttledProcessRows(); }).observe(document.body, { childList: true, subtree: true });
// Affiche le nombre de connectés (extrait du sideCardForum masqué) dans la barre de filtres
const showUserCount = () => {
const numEl = document.querySelector('.userCount__number, [class*="userCount"] .number, .js-user-count');
const count = numEl ? numEl.textContent.trim() : null;
if (!count) return;
const actions = document.querySelector('.ma-filter-actions');
if (actions && !actions.querySelector('.ma-users-badge')) {
const badge = document.createElement('span');
badge.className = 'ma-users-badge';
badge.innerHTML = `<span class="material-symbols-outlined">group</span>${count} connectés`;
actions.insertBefore(badge, actions.firstChild);
}
};
setTimeout(showUserCount, 800); // petit délai pour que JVC hydrate le DOM
// --- Délégation : bouton blacklist ---
if (!isTopic) {
let blMenu = null;
const closeBlMenu = () => { if (blMenu) { blMenu.remove(); blMenu = null; } };
document.addEventListener('click', closeBlMenu, true);
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeBlMenu(); });
const forumTableBl = document.querySelector('.tablesForum');
if (forumTableBl) {
forumTableBl.addEventListener('click', (e) => {
const btn = e.target.closest('.ma-bl-btn');
if (!btn) return;
e.preventDefault(); e.stopPropagation();
closeBlMenu();
const row = btn.closest('.tablesForum__bodyRow');
if (!row) return;
const tid = row.id.replace('topic-', '');
const titleNode = row.querySelector('.tablesForum__subjectText');
const topicTitle = titleNode ? titleNode.textContent.trim().replace(/^(Épinglé\s*)/i, '') : 'Topic';
const topicLinkEl = row.querySelector('a.tablesForum__cellSubject');
const topicUrl = topicLinkEl ? topicLinkEl.href : null;
const authorLinkEl = row.querySelector('.tablesForum__cellAuthor .tablesForum__authorLink');
let authorName = authorLinkEl ? (authorLinkEl.getAttribute('title') || authorLinkEl.textContent.trim()) : null;
if (!authorName) {
const firstAvatar = row.querySelector('.tablesForum__cellParticipants .tablesForum__firstAvatar');
if (firstAvatar) authorName = firstAvatar.getAttribute('title') || firstAvatar.querySelector('img')?.getAttribute('alt') || null;
}
blMenu = document.createElement('div');
blMenu.className = 'ma-bl-menu';
blMenu.innerHTML = `
<div class="ma-bl-menu-item" id="bl-topic"><span class="material-symbols-outlined">topic</span>Masquer ce topic</div>
${authorName ? `<div class="ma-bl-menu-item" id="bl-author"><span class="material-symbols-outlined">person_off</span>Blacklister "${authorName}"</div>` : ''}
`;
blMenu.style.top = e.clientY + 'px';
blMenu.style.left = Math.min(e.clientX, window.innerWidth - 200) + 'px';
document.body.appendChild(blMenu);
blMenu.querySelector('#bl-topic')?.addEventListener('click', (ev) => {
ev.stopPropagation();
blacklistTopic(tid, topicTitle, topicUrl);
row.classList.add('ma-hidden-row');
row.style.setProperty('display', 'none', 'important');
closeBlMenu();
});
blMenu.querySelector('#bl-author')?.addEventListener('click', (ev) => {
ev.stopPropagation();
blacklistAuthor(authorName);
// Masquer toutes les rows de cet auteur
document.querySelectorAll('.tablesForum__bodyRow').forEach(r => {
const rAuthorEl = r.querySelector('.tablesForum__cellAuthor .tablesForum__authorLink');
let rAuthor = rAuthorEl ? (rAuthorEl.getAttribute('title') || rAuthorEl.textContent.trim()) : null;
if (!rAuthor) {
const rAvatar = r.querySelector('.tablesForum__cellParticipants .tablesForum__firstAvatar');
if (rAvatar) rAuthor = rAvatar.getAttribute('title') || rAvatar.querySelector('img')?.getAttribute('alt') || null;
}
if (rAuthor && rAuthor === authorName) {
r.classList.add('ma-hidden-row');
r.style.setProperty('display', 'none', 'important');
}
});
closeBlMenu();
});
}, true);
}
}
// --- Délégation : blacklist auteur depuis un topic ---
if (isTopic) {
let blTopicMenu = null;
const closeBlTopicMenu = () => { if (blTopicMenu) { blTopicMenu.remove(); blTopicMenu = null; } };
document.addEventListener('click', closeBlTopicMenu, true);
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeBlTopicMenu(); });
document.addEventListener('click', (e) => {
const btn = e.target.closest('.ma-bl-author-btn');
if (!btn) return;
e.preventDefault(); e.stopPropagation();
closeBlTopicMenu();
const authorName = btn.dataset.author;
if (!authorName) return;
blTopicMenu = document.createElement('div');
blTopicMenu.className = 'ma-bl-menu';
blTopicMenu.innerHTML = `<div class="ma-bl-menu-item" id="blt-author"><span class="material-symbols-outlined">person_off</span>Blacklister "${authorName}"</div>`;
blTopicMenu.style.top = e.clientY + 'px';
blTopicMenu.style.left = Math.min(e.clientX, window.innerWidth - 200) + 'px';
document.body.appendChild(blTopicMenu);
blTopicMenu.querySelector('#blt-author').addEventListener('click', (ev) => {
ev.stopPropagation();
blacklistAuthor(authorName);
// Griser visuellement les messages de cet auteur dans le topic
document.querySelectorAll('.messageUser').forEach(msg => {
const lbl = msg.querySelector('.messageUser__label');
if (lbl && lbl.textContent.trim() === authorName) {
msg.style.opacity = '0.3';
msg.style.pointerEvents = 'none';
msg.title = `Messages de ${authorName} masqués (blacklisté)`;
}
});
closeBlTopicMenu();
});
}, true);
}
// --- Délégation d'événements : boutons étoile (Bookmarks) ---
if (!isTopic && MA_CONFIG.featBookmarks) {
const forumTable = document.querySelector('.tablesForum');
if (forumTable) {
forumTable.addEventListener('click', (e) => {
const star = e.target.closest('.ma-star-btn');
if (!star) return;
e.preventDefault(); e.stopPropagation();
const row = star.closest('.tablesForum__bodyRow');
if (!row) return;
const tid = row.id.replace('topic-', '');
if (star.classList.contains('bookmarked')) {
star.classList.remove('bookmarked'); star.textContent = 'star_border';
removeBookmark(tid);
} else {
star.classList.add('bookmarked'); star.textContent = 'star';
const titleNode = row.querySelector('.tablesForum__subjectText');
const linkNode = row.querySelector('a.tablesForum__cellSubject');
saveBookmark(tid, titleNode ? titleNode.textContent.trim() : linkNode.textContent.trim(), linkNode.href);
}
updateBadges();
}, true);
}
}
/* --- 9. INFINITE SCROLL & LIVE REFRESH --- */
if (!isTopic && MA_CONFIG.featInfinite) {
let isFetchingNext = false;
let pagesLoaded = 0;
const MAX_AUTO_PAGES = 3;
const loaderUi = document.createElement('div');
loaderUi.className = 'ma-infinite-loader'; loaderUi.textContent = 'Extraction des archives suivantes...';
const paginationWrap = document.querySelector('.container__pagination');
if (paginationWrap) paginationWrap.appendChild(loaderUi);
window.addEventListener('scroll', throttle(async () => {
if (isFetchingNext) return;
if ((window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight - 1000) {
if (pagesLoaded >= MAX_AUTO_PAGES) {
if (loaderUi) {
loaderUi.textContent = "Extraction manuelle (Cliquer pour charger la suite)";
loaderUi.style.cursor = "pointer";
loaderUi.style.pointerEvents = "auto";
loaderUi.style.opacity = "1";
loaderUi.onclick = () => { pagesLoaded = 0; loaderUi.onclick = null; loaderUi.textContent = 'Extraction des archives suivantes...'; loaderUi.style.cursor = ''; loaderUi.style.pointerEvents = ''; };
}
return;
}
const nextBtn = document.querySelector('a.pagination__button--next[href], .pagination__button[title*="suivante" i], a.pagi-suivant[href]');
if (nextBtn && nextBtn.href) {
isFetchingNext = true;
if (loaderUi) loaderUi.classList.add('loading');
try {
const res = await fetch(nextBtn.href);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const newRows = doc.querySelectorAll('.tablesForum__bodyRow');
const mainList = document.querySelector('.tablesForum');
newRows.forEach(r => { const clone = r.cloneNode(true); mainList.appendChild(clone); observeFadeIn(clone); });
// Modifier le bouton current pagination pour le prochain scroll
const nextPagi = doc.querySelector('.container__pagination');
if (nextPagi && paginationWrap) {
paginationWrap.innerHTML = nextPagi.innerHTML;
paginationWrap.appendChild(loaderUi); // remettre le loader
} else { nextBtn.style.display = 'none'; } // plus de page dispo
processRows(mainList);
pagesLoaded++;
} catch (e) { }
if (loaderUi) loaderUi.classList.remove('loading');
setTimeout(() => { isFetchingNext = false; }, 1000); // 1s cooldown
}
}
}, 150));
}
if (!isTopic) {
const getLiveDelay = () => {
try { return JSON.parse(localStorage.getItem('MidnightArchivistSettings_v10'))?.autoRefreshDelay || 5; }
catch (e) { return 5; }
};
const getSeenTopics = () => {
try { return JSON.parse(localStorage.getItem('MA_SeenTopics')) || []; }
catch (e) { return []; }
};
const markTopicSeen = (id) => {
const seen = getSeenTopics();
if (!seen.includes(id)) {
seen.push(id);
localStorage.setItem('MA_SeenTopics', JSON.stringify(seen));
}
};
const runRefresh = async () => {
const scheduleNext = () => setTimeout(runRefresh, getLiveDelay() * 1000);
if (window.scrollY > 300 || !document.getElementById('set-live')?.checked) {
scheduleNext();
return;
}
try {
const res = await fetch(location.href);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const newRows = Array.from(doc.querySelectorAll('.tablesForum__bodyRow'));
const table = document.querySelector('.tablesForum');
const currentIds = Array.from(table.querySelectorAll('.tablesForum__bodyRow')).map(r => r.id);
const seenTopics = getSeenTopics();
let added = 0;
const allRows = Array.from(table.querySelectorAll('.tablesForum__bodyRow'));
const pinnedRows = allRows.filter(r => r.querySelector('[data-pinned="true"]'));
const lastPinned = pinnedRows.length > 0 ? pinnedRows[pinnedRows.length - 1] : null;
const newRowsToAdd = [];
newRows.reverse().forEach(row => {
if (!currentIds.includes(row.id)) {
row.classList.add('ma-new-refresh');
const subjectText = row.querySelector('.tablesForum__subjectText');
if (subjectText && !seenTopics.includes(row.id)) {
const newBadge = document.createElement('span');
newBadge.className = 'ma-badge-new';
newBadge.textContent = 'Nouveau';
subjectText.insertBefore(newBadge, subjectText.firstChild);
}
markTopicSeen(row.id);
newRowsToAdd.push(row);
added++;
}
});
if (newRowsToAdd.length > 0) {
const insertPoint = lastPinned ? lastPinned.nextSibling : table.querySelector('.tablesForum__headRow').nextSibling;
newRowsToAdd.forEach(row => {
table.insertBefore(row, insertPoint);
observeFadeIn(row);
// Nettoyage forcé du badge Nouveau après 3s (une fois le row inséré et stable)
setTimeout(() => {
row.querySelectorAll('.ma-badge-new').forEach(b => b.remove());
}, 3000);
});
}
if (added > 0) {
processRows(table);
const toast = document.getElementById('ma-toast-nb');
if (toast) {
toast.textContent = `+${added} nouve${added > 1 ? 'aux' : 'au'}`;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 5000);
}
}
} catch (e) { }
scheduleNext();
};
setTimeout(runRefresh, getLiveDelay() * 1000);
}
/* --- PANNEAU LATERAL TOPICS ACTIFS (uniquement dans les topics) --- */
if (isTopic && MA_CONFIG.featSidebar !== false) {
// Trouver l'URL de la liste des topics
const findForumListUrl = () => {
// URL topic : /forums/{family}-{forumId}-{topicId}-{page}-..htm
// Le forum parent = /forums/0-{forumId}-0-1-0-1-0-{slug}.htm
// On remplace juste le segment 1 (family) par 0
const topicMatch = location.href.match(/\/forums\/(\d+)-(\d+)-(\d+)/);
if (topicMatch) {
const forumId = topicMatch[2]; // ex: 51 (pas 42 qui est la family)
// Chercher un lien existant dans la page avec ce forumId spécifique
const allLinks = document.querySelectorAll('a[href]');
for (const l of allLinks) {
if (new RegExp(`/forums/0-${forumId}-`).test(l.href)) return l.href;
}
// Fallback : construire l'URL directement depuis l'URL courante
// /forums/42-51-76897910-1-0-1-0-slug.htm → /forums/0-51-0-1-0-1-0-slug.htm
const slugMatch = location.href.match(/\/forums\/\d+-\d+-\d+-\d+-\d+-\d+-\d+-([^/]+\.htm)/);
if (slugMatch) return `https://www.jeuxvideo.com/forums/0-${forumId}-0-1-0-1-0-${slugMatch[1]}`;
return `https://www.jeuxvideo.com/forums/0-${forumId}-0-1-0-1-0-forum.htm`;
}
// Fallback hardcodé Blabla 18-25 ans
return 'https://www.jeuxvideo.com/forums/0-51-0-1-0-1-0-blabla-18-25-ans.htm';
};
const forumListUrl = findForumListUrl();
// Créer le panneau
const sidebar = document.createElement('div');
sidebar.id = 'ma-topics-sidebar';
sidebar.innerHTML = `
<div class="ma-sb-header">
<span class="ma-sb-title">Topics actifs</span>
<div class="ma-sb-live-btn" id="ma-sb-live" title="Désactiver l'actualisation automatique">
<span class="material-symbols-outlined">pause_circle</span>
</div>
<div class="ma-sb-refresh-btn" id="ma-sb-refresh" title="Actualiser maintenant">
<span class="material-symbols-outlined">refresh</span>
</div>
</div>
<div class="ma-sb-list" id="ma-sb-list">
<div class="ma-sb-empty">Chargement…</div>
</div>
<div class="ma-sb-footer">
<span>Topics actifs</span>
<span class="ma-sb-footer-timer" id="ma-sb-timer">5s</span>
</div>
`;
const toggleBtn = document.createElement('div');
toggleBtn.id = 'ma-sidebar-toggle';
toggleBtn.title = 'Topics actifs';
toggleBtn.innerHTML = `<span class="material-symbols-outlined">view_list</span>`;
document.body.appendChild(sidebar);
document.body.appendChild(toggleBtn);
// Décaler le panneau sous le header JVC natif (qui peut masquer le haut du panneau)
const applySidebarOffset = () => {
let offset = 0;
// Chercher le header natif JVC dans l'ordre de priorité
for (const sel of ['header[role="banner"]', '.header-main', '#header-main', 'header']) {
const el = document.querySelector(sel);
if (el && el.offsetHeight > 30) { offset = el.offsetHeight; break; }
}
// Fallback : utiliser la barre sticky (Répondre, Actualiser…)
if (!offset) {
const sticky = document.querySelector('.buttonsNavbar__sticky, #js-list-message-tools-actions');
if (sticky && sticky.offsetHeight > 10) offset = sticky.offsetHeight;
}
if (offset > 0) {
sidebar.style.top = offset + 'px';
sidebar.style.height = `calc(100vh - ${offset}px)`;
}
};
// Petit délai pour laisser le DOM se stabiliser
setTimeout(applySidebarOffset, 200);
let sbOpen = false;
let sbLiveEnabled = localStorage.getItem('MA_SbLiveEnabled') !== 'false';
const currentHref = location.href;
let sbCountdown = 5;
let sbCountInterval = null;
const toggleSb = () => {
sbOpen = !sbOpen;
sidebar.classList.toggle('ma-sb-open', sbOpen);
toggleBtn.classList.toggle('ma-sb-open', sbOpen);
if (sbOpen) {
// Charger immédiatement à l'ouverture, puis démarrer le compte à rebours si live actif
fetchTopics().then(() => { if (sbLiveEnabled) resetSbCountdown(); });
} else {
// Mettre en pause quand le panneau est fermé
clearInterval(sbCountInterval);
const timerEl = document.getElementById('ma-sb-timer');
if (timerEl) timerEl.textContent = '⏸';
}
};
toggleBtn.addEventListener('click', toggleSb);
const fetchTopics = async () => {
const refreshBtn = document.getElementById('ma-sb-refresh');
if (refreshBtn) refreshBtn.classList.add('spinning');
const listEl = document.getElementById('ma-sb-list');
if (!forumListUrl) {
if (listEl) listEl.innerHTML = `<div class="ma-sb-empty">Lien du forum introuvable.<br>Navigue d'abord vers la liste des topics.</div>`;
if (refreshBtn) refreshBtn.classList.remove('spinning');
return;
}
try {
const res = await fetch(forumListUrl);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const rows = doc.querySelectorAll('.tablesForum__bodyRow');
if (!listEl) return;
if (!rows.length) {
listEl.innerHTML = `<div class="ma-sb-empty">Aucun topic trouvé.</div>`;
if (refreshBtn) refreshBtn.classList.remove('spinning');
return;
}
listEl.innerHTML = '';
rows.forEach(row => {
const linkEl = row.querySelector('a.tablesForum__cellSubject');
if (!linkEl) return;
const titleEl = row.querySelector('.tablesForum__subjectText');
const title = titleEl ? titleEl.textContent.trim() : linkEl.textContent.trim();
// Catégorie (ex: "PHILO 18-25", "SOCIETÉ", etc.)
const iconEl = row.querySelector('.ma-custom-icon, .tablesForum__subjectMarkerIcon');
const catEl = row.querySelector('[class*="category"], [class*="forum-name"], .tablesForum__cellSubject .ma-custom-icon + span');
let catText = '';
// Essayer de lire le tag de catégorie depuis le titre ou icon title
const subjectCell = row.querySelector('.tablesForum__cellSubject');
if (subjectCell) {
const firstSpan = subjectCell.querySelector('span:not(.tablesForum__subjectText):not(.ma-custom-icon)');
if (firstSpan && firstSpan.textContent.trim().length < 40) catText = firstSpan.textContent.trim();
}
// Nombre de messages
const msgEl = row.querySelector('.tablesForum__cellText--msg');
const msgCount = msgEl ? msgEl.textContent.trim() : '';
// Dernier message (temps)
const timeEl = row.querySelector('.tablesForum__cellLink');
const timeText = timeEl ? timeEl.textContent.trim() : '';
// Auteur du topic
const authorEl = row.querySelector('.tablesForum__authorLink, .author-link');
const authorText = authorEl ? authorEl.textContent.trim() : '';
// Icône du topic (épinglé, hot, normal…)
const topicIconEl = row.querySelector('i[class*="icon-topic"]');
let topicIconHtml = '';
if (topicIconEl) {
const cls = topicIconEl.className;
if (/pinned|stick|epingle/i.test(cls)) {
topicIconHtml = `<span class="material-symbols-outlined ma-sb-icon ma-sb-icon--pin">push_pin</span>`;
} else if (/hot|fire|chaud/i.test(cls)) {
topicIconHtml = `<span class="material-symbols-outlined ma-sb-icon ma-sb-icon--hot">local_fire_department</span>`;
} else {
topicIconHtml = `<span class="material-symbols-outlined ma-sb-icon ma-sb-icon--normal">forum</span>`;
}
}
// Topic courant ?
const rowTopicId = row.id ? row.id.replace('topic-', '') : '';
const isCurrent = currentHref.includes(rowTopicId) && rowTopicId.length > 3;
const item = document.createElement('a');
item.className = 'ma-sb-topic' + (isCurrent ? ' ma-sb-current' : '');
item.href = linkEl.href;
item.title = title;
item.innerHTML = `
${catText ? `<div class="ma-sb-cat">${catText}</div>` : ''}
<div class="ma-sb-ttitle">${topicIconHtml}${title}</div>
<div class="ma-sb-meta">
${authorText ? `<span class="ma-sb-meta-author"><span class="material-symbols-outlined">person</span>${authorText}</span>` : ''}
${msgCount ? `<span class="ma-sb-meta-item"><span class="material-symbols-outlined">reply</span>${msgCount}</span>` : ''}
${timeText ? `<span class="ma-sb-meta-time">${timeText}</span>` : ''}
</div>
`;
listEl.appendChild(item);
});
} catch (err) {
const listEl = document.getElementById('ma-sb-list');
if (listEl) listEl.innerHTML = `<div class="ma-sb-empty">Erreur de chargement.</div>`;
}
if (refreshBtn) refreshBtn.classList.remove('spinning');
};
const resetSbCountdown = () => {
clearInterval(sbCountInterval);
if (!sbLiveEnabled || !sbOpen) return;
sbCountdown = 5;
const timerEl = document.getElementById('ma-sb-timer');
if (timerEl) timerEl.textContent = '5s';
sbCountInterval = setInterval(() => {
if (!sbOpen || !sbLiveEnabled) { clearInterval(sbCountInterval); return; }
sbCountdown--;
const timerEl = document.getElementById('ma-sb-timer');
if (timerEl) timerEl.textContent = sbCountdown > 0 ? `${sbCountdown}s` : '…';
if (sbCountdown <= 0) {
clearInterval(sbCountInterval);
fetchTopics().then(resetSbCountdown);
}
}, 1000);
};
// Bouton live refresh toggle
document.getElementById('ma-sb-live')?.addEventListener('click', () => {
sbLiveEnabled = !sbLiveEnabled;
localStorage.setItem('MA_SbLiveEnabled', sbLiveEnabled ? 'true' : 'false');
const liveBtn = document.getElementById('ma-sb-live');
const liveIcon = liveBtn?.querySelector('.material-symbols-outlined');
const timerEl = document.getElementById('ma-sb-timer');
if (sbLiveEnabled) {
liveBtn?.classList.remove('paused');
if (liveIcon) liveIcon.textContent = 'pause_circle';
if (liveBtn) liveBtn.title = "Désactiver l'actualisation automatique";
resetSbCountdown();
} else {
clearInterval(sbCountInterval);
liveBtn?.classList.add('paused');
if (liveIcon) liveIcon.textContent = 'play_circle';
if (liveBtn) liveBtn.title = "Activer l'actualisation automatique";
if (timerEl) timerEl.textContent = '–';
}
});
// Bouton actualisation manuelle
document.getElementById('ma-sb-refresh')?.addEventListener('click', () => {
clearInterval(sbCountInterval);
fetchTopics().then(resetSbCountdown);
});
// Ouverture auto au chargement — déclenche fetchTopics() + resetSbCountdown() via toggleSb()
toggleSb();
// Synchroniser l'état visuel du bouton live avec la valeur persistée
if (!sbLiveEnabled) {
const liveBtn = document.getElementById('ma-sb-live');
const liveIcon = liveBtn?.querySelector('.material-symbols-outlined');
liveBtn?.classList.add('paused');
if (liveIcon) liveIcon.textContent = 'play_circle';
if (liveBtn) liveBtn.title = "Activer l'actualisation automatique";
}
}
// --- Délégation globale : Bouton "Répondre" / "Nouveau Sujet" ---
document.addEventListener('click', (e) => {
const btn = e.target.closest('.ma-btn-new');
if (!btn) return;
// Priorité : Utiliser le bouton natif de JVC pour bénéficier de sa logique intégrée
const nativeBtn = document.querySelector('.icon-reply')?.closest('button, a') ||
document.querySelector('.js-post-message') ||
document.querySelector('.js-post-topic') ||
document.querySelector('a[href*="post-topic"]');
if (nativeBtn) {
nativeBtn.click();
} else {
// Repli si aucun bouton natif n'est trouvé
const IS_TOPIC = document.getElementById('listMessages') !== null;
if (IS_TOPIC) {
const target = document.querySelector('.bloc-form-reponse, #bloc-form-reponse, .reponse-area, .container-form-ecrire');
if (target) {
const headerOffset = 100;
const elementPosition = target.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({ top: offsetPosition, behavior: 'smooth' });
setTimeout(() => { const txt = target.querySelector('textarea'); if (txt) txt.focus(); }, 800);
} else {
const lastPageLink = document.querySelector('.bloc-pagi-default a:last-of-type, .pagination__item:last-child a, .pagination__dropdownList a:last-child');
if (lastPageLink && lastPageLink.href) window.location.href = lastPageLink.href + '#bloc-form-reponse';
}
}
}
});
});
})();