eweng.space/assets/main.js

224 lines
7.6 KiB
JavaScript

/* === 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 `<span style="display:inline-block;animation:colorshift 12s ease-in-out infinite,levite ${duree}s ease-in-out infinite;animation-delay:-${delayColor}s,-${delayLevite}s">${c}</span>`;
}).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
? `<img src="${album.cover}" alt="${album.titre}" onerror="this.outerHTML='<div class=\\'placeholder\\'>${album.titre}</div>'">`
: `<div class="placeholder">${album.titre}</div>`;
const fav = album.favoris ? '<span class="badge-fav">♥</span>' : '';
carte.innerHTML = `
${fav}
${media}
<div class="nom">
${album.titre}<br>
<span class="artiste">${album.artiste}</span>
</div>
`;
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.';
});
}
/* === FEED DEPECHES (depeches.html) === */
async function chargerDepeches() {
const feed = document.getElementById('feed');
function escapeHtml(str) {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
async function parseTexte(texte) {
const urlRegex = /(https?:\/\/[^\s]+)/g;
const urls = texte.match(urlRegex) || [];
let html = texte.replace(urlRegex, '___URL___');
// échappe le HTML et convertit les retours à la ligne
html = escapeHtml(html).replace(/\n/g, '<br>');
const embeds = await Promise.all(urls.map(async url => {
if (url.includes('soundcloud.com')) {
try {
const r = await fetch(`https://soundcloud.com/oembed?format=json&url=${encodeURIComponent(url)}&maxwidth=500`);
const data = await r.json();
return data.html;
} catch { return `<a href="${url}" target="_blank">${url}</a>`; }
}
if (url.includes('youtube.com') || url.includes('youtu.be')) {
const id = url.match(/(?:v=|youtu\.be\/)([^&\s]+)/)?.[1];
if (id) return `<iframe width="500" height="281" src="https://www.youtube.com/embed/${id}" frameborder="0" allowfullscreen style="max-width:100%;display:block;margin-top:0.8rem;"></iframe>`;
}
return `<a href="${url}" target="_blank">${url}</a>`;
}));
embeds.forEach(e => { html = html.replace('___URL___', e); });
return html;
}
try {
const r = await fetch('/api/depeches');
const depeches = await r.json();
feed.innerHTML = '';
if (depeches.length === 0) {
feed.innerHTML = '<p class="chargement">aucune dépêche pour l\'instant.</p>';
return;
}
for (const d of depeches) {
const el = document.createElement('div');
el.className = 'depeche';
const texteHtml = await parseTexte(d.texte);
const img = d.image
? `<img src="${d.image}" class="depeche-image" alt="">`
: '';
el.innerHTML = `
<div class="depeche-date">ewen - ${d.date}</div>
<div class="depeche-texte">${texteHtml}</div>
${img}
`;
feed.appendChild(el);
}
} catch {
feed.innerHTML = '<p class="chargement">impossible de charger les dépêches.</p>';
}
}
/* === LIGHTBOX (images dans les entrées) === */
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) 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 = '&times;';
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();
});