(() => { const on = (selector, handler) => document.querySelectorAll(selector).forEach(handler); on('[data-copy-text]', (button) => { button.addEventListener('click', async () => { const text = button.getAttribute('data-copy-text') || ''; if (!text) { return; } try { await navigator.clipboard.writeText(text); const previous = button.textContent; button.textContent = 'Copié'; window.setTimeout(() => { button.textContent = previous; }, 1200); } catch { window.prompt('Copie ce texte :', text); } }); }); on('[data-confirm]', (form) => { form.addEventListener('submit', (event) => { const message = form.getAttribute('data-confirm') || 'Confirmer cette action ?'; if (!window.confirm(message)) { event.preventDefault(); } }); }); on('[data-char-count]', (field) => { const counter = field.parentElement?.querySelector('[data-char-count-value]'); if (!counter) { return; } const update = () => { counter.textContent = String(field.value.length); }; field.addEventListener('input', update); update(); }); const editor = document.querySelector('[data-markdown-editor]'); const picker = document.querySelector('[data-media-picker]'); const editorLayout = document.querySelector('[data-editor-layout]'); const pickerTitle = document.querySelector('[data-media-picker-title]'); const pickerHelp = document.querySelector('[data-media-picker-help]'); const pickerClose = document.querySelector('[data-media-picker-close]'); const coverInput = document.querySelector('[data-cover-input]'); const coverPreview = document.querySelector('[data-cover-preview]'); const coverPlaceholder = document.querySelector('[data-cover-placeholder]'); const coverClear = document.querySelector('[data-cover-clear]'); let pickerMode = 'markdown'; const focusEditor = () => editor?.focus(); const updateCoverPreview = (item = null) => { if (!coverInput || !coverPreview || !coverPlaceholder) { return; } if (item && item.id && item.url) { coverInput.value = item.id; coverPreview.src = item.url; coverPreview.alt = 'Image de couverture'; coverPreview.classList.remove('is-hidden'); coverPlaceholder.classList.add('is-hidden'); if (coverClear) { coverClear.disabled = false; } return; } coverInput.value = ''; coverPreview.removeAttribute('src'); coverPreview.classList.add('is-hidden'); coverPlaceholder.classList.remove('is-hidden'); if (coverClear) { coverClear.disabled = true; } }; coverClear?.addEventListener('click', () => updateCoverPreview()); if (editor) { const replaceSelection = (before, after = '', placeholder = '') => { const start = editor.selectionStart; const end = editor.selectionEnd; const selected = editor.value.slice(start, end); const content = selected || placeholder; const insertion = before + content + after; editor.setRangeText(insertion, start, end, 'end'); if (!selected && placeholder) { const cursorStart = start + before.length; editor.setSelectionRange(cursorStart, cursorStart + placeholder.length); } focusEditor(); }; const prefixLines = (prefix, placeholder) => { const start = editor.selectionStart; const end = editor.selectionEnd; const selected = editor.value.slice(start, end) || placeholder; const prefixed = selected .split('\n') .map((line) => (line ? prefix + line : prefix.trimEnd())) .join('\n'); editor.setRangeText(prefixed, start, end, 'end'); focusEditor(); }; on('[data-md-action]', (button) => { button.addEventListener('click', () => { switch (button.getAttribute('data-md-action')) { case 'bold': replaceSelection('**', '**', 'texte'); break; case 'italic': replaceSelection('*', '*', 'texte'); break; case 'heading': prefixLines('## ', 'Titre'); break; case 'list': prefixLines('- ', 'Élément'); break; case 'quote': prefixLines('> ', 'Citation'); break; case 'link': replaceSelection('[', '](https://)', 'texte'); break; case 'code': { const selected = editor.value.slice(editor.selectionStart, editor.selectionEnd); replaceSelection( selected.includes('\n') ? '```\n' : '`', selected.includes('\n') ? '\n```' : '`', 'code' ); break; } } }); }); } const togglePicker = (open, mode = pickerMode) => { if (!picker) { return; } pickerMode = mode; picker.classList.toggle('is-hidden', !open); editorLayout?.classList.toggle('is-picker-open', open); if (!open) { focusEditor(); return; } const isCover = pickerMode === 'cover'; if (pickerTitle) { pickerTitle.textContent = isCover ? 'Choisir une couverture' : 'Insérer une image'; } if (pickerHelp) { pickerHelp.textContent = isCover ? 'Clique sur une image pour l’utiliser comme couverture.' : 'Clique sur une image pour l’insérer dans l’article.'; } picker.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }; on('[data-media-picker-open]', (button) => { button.addEventListener('click', () => { togglePicker(true, button.getAttribute('data-media-picker-open') || 'markdown'); }); }); pickerClose?.addEventListener('click', () => togglePicker(false)); on('[data-media-picker-select]', (button) => { button.addEventListener('click', () => { const item = { id: button.getAttribute('data-media-id') || '', url: button.getAttribute('data-media-url') || '', markdown: button.getAttribute('data-media-markdown') || '', }; if (pickerMode === 'cover') { updateCoverPreview(item); togglePicker(false); return; } if (!editor || !item.markdown) { return; } const start = editor.selectionStart; const end = editor.selectionEnd; const prefix = start > 0 && !editor.value.slice(0, start).endsWith('\n\n') ? '\n\n' : ''; const suffix = end < editor.value.length && !editor.value.slice(end).startsWith('\n\n') ? '\n\n' : ''; editor.setRangeText(prefix + item.markdown + suffix, start, end, 'end'); togglePicker(false); }); }); // Synchronise data-copy-text du bouton Markdown quand l'alt est modifié. on('[data-alt-input]', (input) => { const card = input.closest('.card'); const button = card?.querySelector('[data-markdown-template]'); if (!button) { return; } const update = () => { const template = button.getAttribute('data-markdown-template') || ''; const alt = input.value; button.setAttribute('data-copy-text', template.replace('![](', '![' + alt + '](')); }; input.addEventListener('input', update); update(); }); })();