// wizard.js — autosave, insertions, couleur catégorie, génération slug document.addEventListener('DOMContentLoaded', function () { var page = document.getElementById('vl-page'); var uuid = page ? page.dataset.uuid : ''; var autosaveUrl = page ? page.dataset.autosaveUrl : ''; // ─── Auto-resize textarea + scroll curseur ────────────────────────────── var ta = document.getElementById('wz-content'); if (ta) { function resizeTa() { ta.style.height = 'auto'; ta.style.height = ta.scrollHeight + 'px'; } ta.addEventListener('input', resizeTa); resizeTa(); function scrollToCursor() { var lineH = parseFloat(getComputedStyle(ta).lineHeight) || 20; var padT = parseFloat(getComputedStyle(ta).paddingTop) || 8; var lines = ta.value.substr(0, ta.selectionStart).split('\n').length; var cursorY = ta.getBoundingClientRect().top + padT + lines * lineH; var margin = lineH * 3; if (cursorY > window.innerHeight - margin) { window.scrollBy({ top: cursorY - window.innerHeight + margin, behavior: 'instant' }); } else if (cursorY < margin) { window.scrollBy({ top: cursorY - margin, behavior: 'instant' }); } } ta.addEventListener('keyup', scrollToCursor); ta.addEventListener('click', scrollToCursor); } // ─── Ctrl+Enter soumet le formulaire ──────────────────────────────────── var form = document.querySelector('form[method="POST"]'); if (form) { form.addEventListener('keydown', function (e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { form.submit(); } }); } // ─── Génération slug automatique (étape 1 / création) ─────────────────── var titleInput = document.getElementById('wz-title'); var slugField = document.getElementById('slug'); var slugPreview = document.getElementById('slug-preview'); function slugify(s) { var map = {'à':'a','â':'a','ä':'a','é':'e','è':'e','ê':'e','ë':'e','î':'i','ï':'i','ô':'o','ö':'o','ù':'u','û':'u','ü':'u','ç':'c','æ':'ae','œ':'oe'}; return s.toLowerCase() .replace(/[àâäéèêëîïôöùûüçæœ]/g, function(c) { return map[c] || c; }) .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); } if (titleInput && slugField) { if (slugField.value !== '') slugField._auto = false; titleInput.addEventListener('input', function () { if (slugField._auto !== false) { var gen = slugify(this.value); slugField.value = gen; if (slugPreview) slugPreview.textContent = gen; } }); slugField.addEventListener('input', function () { this._auto = (this.value === ''); if (slugPreview) slugPreview.textContent = this.value; }); } // ─── Autosave ──────────────────────────────────────────────────────────── var indicator = document.getElementById('autosave-indicator'); if (indicator && uuid && autosaveUrl) { var timer = null; var titleEl = document.getElementById('wz-title'); var contentEl = document.getElementById('wz-content'); function scheduleAutosave() { clearTimeout(timer); timer = setTimeout(doAutosave, 3000); } async function doAutosave() { if (!titleEl || !contentEl) return; indicator.textContent = 'Sauvegarde…'; try { var res = await fetch(autosaveUrl, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ title: titleEl.value, content: contentEl.value, slug: slugField ? slugField.value : '', }), }); var data = await res.json(); indicator.textContent = data.ok ? 'Brouillon sauvegardé à ' + data.time : 'Erreur de sauvegarde'; } catch (err) { indicator.textContent = 'Erreur de sauvegarde'; } } if (titleEl) titleEl.addEventListener('input', scheduleAutosave); if (ta) ta.addEventListener('input', scheduleAutosave); } // ─── Insertion Markdown depuis miniatures ──────────────────────────────── var insertUrl = page ? page.dataset.insertUrl : ''; document.querySelectorAll('[data-insert-ref]').forEach(function (el) { el.addEventListener('click', function () { if (!ta) return; var ref = this.dataset.insertRef; var isImage = /\.(jpe?g|png|gif|webp|svg|avif)(\?.*)?$/i.test(ref); var md = isImage ? '![](' + ref + ')' : '[' + ref + '](' + ref + ')'; var sep = ta.value.length > 0 && !ta.value.endsWith('\n') ? '\n' : ''; ta.value += sep + md; ta.focus(); ta.selectionStart = ta.selectionEnd = ta.value.length; ta.dispatchEvent(new Event('input')); }); }); if (insertUrl) { var isImg = /\.(jpe?g|png|gif|webp|svg|avif)(\?.*)?$/i.test(insertUrl); var name = decodeURIComponent(insertUrl.split('/').pop().split('?')[0]) || 'fichier'; var ref = isImg ? '![](' + insertUrl + ')' : '[' + name + '](' + insertUrl + ')'; if (ta) { var sep = ta.value.length > 0 && !ta.value.endsWith('\n') ? '\n' : ''; ta.value += sep + ref; ta.dispatchEvent(new Event('input')); } } // ─── Copier référence Markdown (bouton MD dans la liste des fichiers) ──── document.querySelectorAll('[data-copy-md-name]').forEach(function (btn) { btn.addEventListener('click', function () { if (!ta) return; var name = this.dataset.copyMdName; var isImage = this.dataset.copyMdIsImage === '1'; var md = isImage ? '![](' + name + ')' : '[' + name + '](' + name + ')'; var sep = ta.value.length > 0 && !ta.value.endsWith('\n') ? '\n' : ''; ta.value += sep + md; ta.focus(); ta.dispatchEvent(new Event('input')); }); }); // ─── Aperçu couleur catégorie (étape 3) ───────────────────────────────── var KNOWN_CATS = { 'actualité': 10, 'travaux': 35, 'scolaire': 55, 'linux': 120, 'domotique': 160, 'télécom': 190, 'blog': 220, 'informatique': 255, 'réflexion': 285, 'loisirs': 320, 'perso': 345, }; var FREE_HUES = [87, 140, 205, 237, 302]; var catInput = document.getElementById('category'); var catSwatch = document.getElementById('cat-swatch'); var catHint = document.getElementById('cat-hint'); var catSwatches = document.getElementById('cat-free-swatches'); function catHue(name) { var key = name.toLowerCase().trim(); if (KNOWN_CATS[key] !== undefined) return KNOWN_CATS[key]; var h = 0; for (var i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) & 0xffff; return h % 360; } function updateCatSwatch() { if (!catInput || !catSwatch) return; var v = catInput.value.trim(); if (v === '') { catSwatch.style.background = '#e5e7eb'; catSwatch.title = ''; if (catHint) catHint.textContent = ''; } else { var hue = catHue(v); catSwatch.style.background = 'hsl(' + hue + ',55%,52%)'; catSwatch.title = 'hsl(' + hue + ', 55%, 52%)'; if (catHint) { var known = KNOWN_CATS[v.toLowerCase()] !== undefined; catHint.textContent = known ? 'Couleur fixe' : 'Nouvelle catégorie (couleur générée)'; } } } if (catInput) { catInput.addEventListener('input', updateCatSwatch); updateCatSwatch(); if (catSwatches) { FREE_HUES.forEach(function (h) { var sw = document.createElement('span'); sw.style.cssText = 'display:inline-block;width:20px;height:20px;border-radius:4px;cursor:pointer;background:hsl(' + h + ',55%,52%)'; sw.title = 'hsl(' + h + ', 55%, 52%)'; sw.addEventListener('click', function () { // trouver ou créer le nom correspondant catInput.dispatchEvent(new Event('input')); }); catSwatches.appendChild(sw); }); } } // ─── Plan (TOC dynamique) ──────────────────────────────────────────────── var tocList = document.getElementById('wz-toc-list'); if (tocList && ta) { function buildToc() { var lines = ta.value.split('\n'); var items = []; lines.forEach(function (line) { var m = line.match(/^(#{1,6})\s+(.+)/); if (m) { items.push({ level: m[1].length, text: m[2].trim() }); } }); if (items.length === 0) { tocList.innerHTML = '
  • Aucun titre
  • '; return; } var minLevel = Math.min.apply(null, items.map(function (i) { return i.level; })); tocList.innerHTML = items.map(function (item) { var indent = (item.level - minLevel) * 12; var escaped = item.text.replace(/&/g,'&').replace(/' + 'H' + item.level + '' + escaped + ''; }).join(''); } ta.addEventListener('input', buildToc); buildToc(); } // ─── Sélection catégorie — pills .wz-cat-pick (étape 3) ───────────────── document.querySelectorAll('.wz-cat-pick').forEach(function (btn) { btn.addEventListener('click', function () { var catInp = document.getElementById('category'); if (catInp) { catInp.value = this.dataset.cat; catInp.dispatchEvent(new Event('input')); } document.querySelectorAll('.wz-cat-pick').forEach(function (b) { b.classList.remove('active'); }); this.classList.add('active'); }); }); // ─── Toggle tags — pills .wz-tag-pill (étape 4) ────────────────────────── document.querySelectorAll('.wz-tag-pills').forEach(function (container) { var targetId = container.dataset.target; var inp = document.getElementById(targetId); if (!inp) return; container.querySelectorAll('.wz-tag-pill').forEach(function (pill) { pill.addEventListener('click', function () { var val = this.dataset.value; var parts = inp.value.split(',').map(function (s) { return s.trim(); }).filter(Boolean); var idx = parts.indexOf(val); if (idx >= 0) { parts.splice(idx, 1); this.classList.remove('btn-secondary', 'btn-info'); this.classList.add(this.classList.contains('btn-outline-info') ? 'btn-outline-info' : 'btn-outline-secondary'); } else { parts.push(val); var isDetected = this.classList.contains('btn-outline-info') || this.classList.contains('btn-info'); this.classList.remove('btn-outline-secondary', 'btn-outline-info'); this.classList.add(isDetected ? 'btn-info' : 'btn-secondary'); } inp.value = parts.join(', '); }); }); }); // ─── Image de couverture .wz-cover-thumb (étape 5) ─────────────────────── document.querySelectorAll('.wz-cover-thumb').forEach(function (img) { img.addEventListener('click', function () { document.querySelectorAll('.wz-cover-thumb').forEach(function (i) { i.classList.remove('wz-cover-selected'); }); this.classList.add('wz-cover-selected'); }); }); // ─── Compteurs SEO (étape 5) ────────────────────────────────────────────── (function () { function counter(inputId, counterId, warn) { var el = document.getElementById(inputId); var ct = document.getElementById(counterId); if (!el || !ct) return; function upd() { var l = el.value.length; ct.textContent = l + ' / ' + warn; ct.className = 'small ' + (l > warn ? 'text-danger' : (l < warn * 0.5 ? 'text-muted' : 'text-success')); } el.addEventListener('input', upd); upd(); } counter('seo_title', 'seo_title_counter', 60); counter('seo_description', 'seo_desc_counter', 155); }()); // ─── Confirmation data-confirm ──────────────────────────────────────────── document.querySelectorAll('[data-confirm]').forEach(function (el) { el.addEventListener('click', function (e) { if (!confirm(this.dataset.confirm)) e.preventDefault(); }); }); });