/* === 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 ? `${album.titre}` : `
${album.titre}
`; const fav = album.favoris ? '' : ''; carte.innerHTML = ` ${fav} ${media}
${album.titre}
${album.artiste}
`; carte.addEventListener('mouseenter', () => { grille.classList.add('hovering'); carte.classList.add('actif'); }); carte.addEventListener('mouseleave', () => { grille.classList.remove('hovering'); carte.classList.remove('actif'); }); carte.addEventListener('click', () => { window.location.href = `albums/${toSlug(album.titre)}.html`; }); grille.appendChild(carte); }); }) .catch(() => { document.getElementById('grille').textContent = 'impossible de charger les albums.'; }); } /* ═══════════════════════════════════════════════════ DEPECHES — VERSION ENRICHIE ═══════════════════════════════════════════════════ */ /* ── helpers texte ───────────────────────────────── */ function escapeHtml(str) { return str .replace(/&/g, '&') .replace(//g, '>'); } function parseMarkdown(text) { // gras text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); // italique text = text.replace(/\*([^*]+)\*/g, '$1'); // barré text = text.replace(/~~([^~]+)~~/g, '$1'); // code inline text = text.replace(/`([^`]+)`/g, '$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 `
${d.html}
`; } 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 `
${d.html}
`; } 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 `
${d.html}
`; } 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 `
${d.html}
`; } 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, '
'); return html; } const embed = await resolveEmbed(part.value); return embed || styledLink(part.value); })); return resolved.join(''); } /* ── rendu du feed ───────────────────────────────── */ async function chargerDepeches() { const feed = document.getElementById('feed'); try { const r = await fetch('/depeches'); const depeches = await r.json(); feed.innerHTML = ''; if (depeches.length === 0) { feed.innerHTML = '

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 ? `` : ''; const permalink = `${location.origin}${location.pathname}#${d.id}`; el.innerHTML = `
ewen — ${timeAgo(d.date)}
${texteHtml}
${img} `; /* actions */ el.querySelector('[data-action="copy"]').addEventListener('click', function () { navigator.clipboard.writeText(permalink).then(() => { this.textContent = '✓'; setTimeout(() => this.textContent = '#', 1200); }); }); el.querySelector('[data-action="share"]').addEventListener('click', function () { if (navigator.share) { navigator.share({ title: 'dépêche', url: permalink }); } else { navigator.clipboard.writeText(permalink).then(() => { this.textContent = '✓'; setTimeout(() => this.textContent = '↗', 1200); }); } }); feed.appendChild(el); } /* scroll vers l'ancre si présente */ if (location.hash) { const target = document.querySelector(location.hash); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } catch { feed.innerHTML = '

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