2026-07-08 11:05:21 +02:00
|
|
|
function renderList(container, items, templateFn, emptyMessage) {
|
|
|
|
|
if (!items.length) {
|
|
|
|
|
container.innerHTML = `<p class="empty">${escapeHtml(emptyMessage || 'Rien à afficher pour le moment.')}</p>`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
container.innerHTML = items.map(templateFn).join('');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function escapeHtml(str) {
|
|
|
|
|
return String(str ?? '').replace(/[&<>"']/g, (c) => ({
|
|
|
|
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
|
|
|
}[c]));
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 11:50:12 +02:00
|
|
|
function youtubeVideoId(url) {
|
2026-07-08 11:05:21 +02:00
|
|
|
try {
|
|
|
|
|
const parsed = new URL(url);
|
|
|
|
|
if (parsed.hostname === 'youtu.be') {
|
2026-07-08 11:50:12 +02:00
|
|
|
return parsed.pathname.slice(1) || null;
|
|
|
|
|
}
|
|
|
|
|
if (parsed.pathname === '/watch') {
|
|
|
|
|
return parsed.searchParams.get('v');
|
|
|
|
|
}
|
|
|
|
|
if (parsed.pathname.startsWith('/embed/')) {
|
|
|
|
|
return parsed.pathname.split('/embed/')[1] || null;
|
2026-07-08 11:05:21 +02:00
|
|
|
}
|
2026-07-08 11:50:12 +02:00
|
|
|
return null;
|
2026-07-08 11:05:21 +02:00
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-08 11:50:12 +02:00
|
|
|
|
|
|
|
|
function youtubeEmbedUrl(url) {
|
|
|
|
|
const videoId = youtubeVideoId(url);
|
|
|
|
|
return videoId ? `https://www.youtube.com/embed/${videoId}` : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function youtubeThumbnailUrl(url) {
|
|
|
|
|
const videoId = youtubeVideoId(url);
|
|
|
|
|
return videoId ? `https://img.youtube.com/vi/${videoId}/mqdefault.jpg` : null;
|
|
|
|
|
}
|