/* === ANIMATION TITRE-NAV (toutes les pages) === */
function animerTitreNav() {
document.querySelectorAll('.fenetre-nom').forEach(el => {
el.innerHTML = el.textContent.split('').map((c, i) => {
if (c === ' ') return ' ';
const delayColor = (i * 0.3).toFixed(1);
const delayLevite = (Math.random() * 4).toFixed(2);
const duree = (3 + Math.random() * 3).toFixed(2);
return `${c}`;
}).join('');
});
}
/* === /NOW (index.html) === */
function afficherNow() {
const phrasesNow = [
"/now : faut que je fasses les courses.",
"/now : les arbres vibrent !",
"/now : les mains, les jambes, avancent..",
"/now : lave toi le visage.",
"/now : il pleut, et c'est très bien comme ça.",
"/now : pas mal la page d'accueil !",
"/now : i love my computer",
"/now : demain je serais en cours, j'espère",
"/now : je resterais auprès de toi pour toujours",
"/now : je serais toi j'irais prendre l'air",
"/now : je dois vraiment aller dormir la"
];
const spanNow = document.querySelector('.now');
if (spanNow) {
const indexAleatoire = Math.floor(Math.random() * phrasesNow.length);
spanNow.textContent = phrasesNow[indexAleatoire];
}
}
/* === GRILLE ALBUMS (reviews/index.html) === */
function toSlug(titre) {
return titre
.toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.replace(/\s+/g, '-')
.replace(/[^a-z0-9-]/g, '');
}
function chargerAlbums() {
fetch('data/albums.json')
.then(r => r.json())
.then(albums => {
const grille = document.getElementById('grille');
albums.forEach(album => {
const carte = document.createElement('div');
carte.className = 'carte';
const media = album.cover
? ``
: `
$1');
return text;
}
function parseHashtags(text) {
return text.replace(/(^|\s)(#[\wÀ-ÿ]+)/g, (m, before, tag) => {
const q = encodeURIComponent(tag);
return `${before}${tag}`;
});
}
function parseMentions(text) {
return text.replace(/(^|\s)(@[\w.]+)/g, (m, before, mention) => {
return `${before}${mention}`;
});
}
function timeAgo(dateStr) {
const m = dateStr.match(/(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2})/);
if (!m) return dateStr;
const [, d, mo, y, H, Mi] = m;
const date = new Date(`${y}-${mo}-${d}T${H}:${Mi}:00`);
const sec = Math.floor((Date.now() - date) / 1000);
if (sec < 60) return "à l'instant";
if (sec < 3600) return `il y a ${Math.floor(sec / 60)} min`;
if (sec < 86400)return `il y a ${Math.floor(sec / 3600)} h`;
if (sec < 604800)return `il y a ${Math.floor(sec / 86400)} j`;
if (sec < 2592000)return `il y a ${Math.floor(sec / 604800)} sem`;
return dateStr;
}
/* ── split texte / urls ──────────────────────────── */
function splitTextAndUrls(text) {
const re = /(https?:\/\/[^\s]+)/g;
const parts = [];
let last = 0, m;
while ((m = re.exec(text)) !== null) {
if (m.index > last) parts.push({ type: 'text', value: text.slice(last, m.index) });
parts.push({ type: 'url', value: m[1] });
last = m.index + m[0].length;
}
if (last < text.length) parts.push({ type: 'text', value: text.slice(last) });
return parts;
}
/* ── embeds ──────────────────────────────────────── */
function styledLink(url) {
try {
const domain = new URL(url).hostname.replace(/^www\./, '');
return `${escapeHtml(domain)}${escapeHtml(url)}`;
} catch {
return `${escapeHtml(url)}`;
}
}
async function resolveEmbed(url) {
/* vidéo 16:9 */
const yt = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
if (yt) return ``;
const vm = url.match(/vimeo\.com\/(\d+)/);
if (vm) return ``;
const dm = url.match(/(?:dailymotion\.com\/video\/|dai\.ly\/)([a-zA-Z0-9]+)/);
if (dm) return ``;
const twClip = url.match(/(?:clips\.twitch\.tv\/|twitch\.tv\/[^/]+\/clip\/)([a-zA-Z0-9_-]+)/);
if (twClip) return ``;
const twVid = url.match(/twitch\.tv\/videos\/(\d+)/);
if (twVid) return ``;
const coub = url.match(/coub\.com\/view\/([a-zA-Z0-9]+)/);
if (coub) return ``;
/* musique */
const sp = url.match(/open\.spotify\.com\/(track|album|playlist|episode|show)\/([a-zA-Z0-9]+)/);
if (sp) return ``;
const dz = url.match(/deezer\.com\/[a-z]{2}\/(track|album|playlist)\/(\d+)/);
if (dz) return ``;
const am = url.match(/music\.apple\.com\/[a-z]{2}\/(album|song|playlist)\/[^/]+\/(\d+)/);
if (am) return ``;
if (url.includes('mixcloud.com')) {
return ``;
}
const aud = url.match(/audiomack\.com\/([^/]+)\/(song|album|playlist)\/([^/]+)/);
if (aud) return ``;
/* oembed */
if (url.includes('soundcloud.com')) {
try {
const r = await fetch(`https://soundcloud.com/oembed?format=json&url=${encodeURIComponent(url)}&maxwidth=500`);
const d = await r.json();
return ``;
} catch { /* fallthrough */ }
}
if (url.includes('bandcamp.com')) {
try {
const r = await fetch(`https://bandcamp.com/api/embed/1/oembed?url=${encodeURIComponent(url)}&format=json`);
if (!r.ok) throw new Error('ko');
const d = await r.json();
return ``;
} catch { /* fallthrough */ }
}
if (url.includes('tiktok.com')) {
try {
const r = await fetch(`https://www.tiktok.com/oembed?url=${encodeURIComponent(url)}`);
const d = await r.json();
return ``;
} catch { /* fallthrough */ }
}
if (url.includes('reddit.com')) {
try {
const r = await fetch(`https://www.reddit.com/oembed?url=${encodeURIComponent(url)}`);
const d = await r.json();
return ``;
} catch { /* fallthrough */ }
}
return null;
}
/* ── parser principal ────────────────────────────── */
async function parseTexte(texte) {
const parts = splitTextAndUrls(texte);
const resolved = await Promise.all(parts.map(async part => {
if (part.type === 'text') {
let html = escapeHtml(part.value);
html = parseMarkdown(html);
html = parseHashtags(html);
html = parseMentions(html);
html = html.replace(/\n/g, 'aucune dépêche pour l\'instant.
'; return; } for (const d of depeches) { const el = document.createElement('div'); el.className = 'depeche'; el.id = d.id; const texteHtml = await parseTexte(d.texte); const img = d.image ? `impossible de charger les dépêches.
'; } } /* ═══════════════════════════════════════════════════ LIGHTBOX ═══════════════════════════════════════════════════ */ function initLightbox() { const contenu = document.querySelector('.fenetre-contenu'); if (!contenu) return; contenu.addEventListener('click', e => { const img = e.target.closest('img:not(.thanks)'); if (!img || img.closest('.depeche-link') || img.closest('.embed-container')) return; const overlay = document.createElement('div'); overlay.className = 'lightbox'; const fond = document.createElement('div'); fond.className = 'lightbox-fond'; overlay.appendChild(fond); const clone = document.createElement('img'); clone.className = 'lightbox-image'; clone.src = img.src; clone.alt = img.alt; overlay.appendChild(clone); const fermer = document.createElement('button'); fermer.className = 'lightbox-fermer'; fermer.innerHTML = '×'; overlay.appendChild(fermer); document.body.appendChild(overlay); overlay.addEventListener('click', e => { if (e.target === fond || e.target === fermer || e.target === overlay) { overlay.remove(); } }); document.addEventListener('keydown', function onKey(e) { if (e.key === 'Escape') { overlay.remove(); document.removeEventListener('keydown', onKey); } }); }); } /* ═══════════════════════════════════════════════════ INIT ═══════════════════════════════════════════════════ */ document.addEventListener('DOMContentLoaded', () => { animerTitreNav(); if (document.querySelector('.now')) { afficherNow(); } if (document.getElementById('grille')) { chargerAlbums(); } if (document.getElementById('feed')) { chargerDepeches(); } initLightbox(); });