// ============================================================
// Amavero — MOSAÏQUE (version test V2 - Optimisation boutons)
// Nouvelle structure : amavero_futur_v8 + proches.json
// ============================================================

const AMV_MOSA_URL_IMAGES = "https://lfadev.com/amavero/cors.php?f=amavero.json";
const AMV_MOSA_URL_PROCHES = "https://lfadev.com/amavero/cors.php?f=proches.json";

let amvMosaImages = [];
let amvMosaProches = {};       // clé normalisée -> [équivalents...]
let amvMosaMotsFrequents = [];

// -------------------------
// Normalisation
// -------------------------
function amvMosaNormaliser(texte) {
  return (texte || "")
    .toString()
    .toLowerCase()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "") // Supprime les accents
    .replace(/œ/g, "oe")
    .replace(/[^a-z0-9 ]/g, "") // <--- C'EST CETTE LIGNE QUI SUPPRIME TOUS LES CARACTÈRES SPÉCIAUX (y compris les apostrophes)
    .trim();
}

// -------------------------
// Chargement JSON (CORRECTION TEMPORAIRE DE DÉBOGAGE)
// -------------------------
// DANS amvMosaChargerDonnees() - Remplacer tout le contenu de la fonction.

async function amvMosaChargerDonnees() {
    const log = document.getElementById("mosaique-log");
    amvMosaSetBoutons(true); 

    try {
        // --- 1. Chargement des images (amavero_futur_v9.json ou v10) ---
        const rImg = await fetch(AMV_MOSA_URL_IMAGES);
        amvMosaImages = await rImg.json();
        if (log) log.textContent = `✅ ${amvMosaImages.length} images prêtes.`;
        console.log("✅ Images Mosaïque chargées :", amvMosaImages.length);

        // --- 2. Chargement de proches.json (avec gestion des erreurs strictes) ---
        try {
            const rProches = await fetch(AMV_MOSA_URL_PROCHES);
            if (!rProches.ok) {
                throw new Error(`Erreur réseau HTTP ${rProches.status}`);
            }

            // Lecture en texte brut, puis parsing strict.
            const textProches = await rProches.text(); 
            const dataProches = JSON.parse(textProches);

            amvMosaProches = {};
            for (const cle in dataProches) {
                if (!Object.prototype.hasOwnProperty.call(dataProches, cle)) continue;
                const n = amvMosaNormaliser(cle);
                if (!n) continue;
                amvMosaProches[n] = Array.isArray(dataProches[cle]) ? dataProches[cle] : [];
            }
            console.log("✅ proches.json Mosaïque chargé :", Object.keys(amvMosaProches).length, "entrées");
            
            // Log de vérification des clés:
            console.log("CLÉS PRÉSENTES:", Object.keys(amvMosaProches)); 

        } catch (e2) {
            console.error("❌ ERREUR Mosaïque: proches.json n'a pu être lu (structure/encodage):", e2.message);
            amvMosaProches = {};
        }

        amvMosaCalculerMotsFrequents();
        amvMosaActiverAutocompletion();
        amvMosaSetBoutons(false); 

    } catch (e) {
        console.error("❌ Erreur chargement Mosaïque général:", e);
        if (log) log.textContent = "❌ Erreur de chargement des données.";
        amvMosaSetBoutons(true); // Laisser désactivé si le chargement échoue.
    }
}// -------------------------
// Mots fréquents (≥9) pour l’auto-complétion
// (Fonction inchangée)
// -------------------------
function amvMosaCalculerMotsFrequents() {
  if (!amvMosaImages.length) return;
  const compteur = {};
  for (const img of amvMosaImages) {
    const motsCles = img.MotsClés || "";
    if (!motsCles) continue;
    const mots = motsCles
      .split(",")
      .map(m => m.trim())
      .filter(m => m.length > 0);
    for (const mot of mots) {
      const n = amvMosaNormaliser(mot);
      if (!n) continue;
      if (!compteur[n]) compteur[n] = { count: 0, forme: mot };
      compteur[n].count++;
    }
  }

  amvMosaMotsFrequents = Object.values(compteur)
    .filter(o => o.count >= 9)
    .map(o => o.forme)
    .sort((a, b) => a.localeCompare(b, "fr"));

  console.log("✅ Mots fréquents Mosaïque :", amvMosaMotsFrequents.length);
}

// -------------------------
// Filtrage par un mot — matching exact (cohérent avec le tableau)
// -------------------------
function amvMosaFiltrerParMot(mot) {
  if (!mot) return [];
  const cle = amvMosaNormaliser(mot);
  if (!cle) return [];
  return amvMosaImages.filter(img => {
    const mots = (img.MotsClés || "")
      .split(',')
      .map(m => amvMosaNormaliser(m))
      .filter(m => m.length > 0);
    return mots.includes(cle);
  });
}

// -------------------------
// Filtrage ET (deux mots) — matching exact (cohérent avec le tableau)
// -------------------------
function amvMosaFiltrerParDeuxMots(mot1, mot2) {
  const cle1 = amvMosaNormaliser(mot1);
  const cle2 = amvMosaNormaliser(mot2);
  if (!cle1 || !cle2) return [];
  return amvMosaImages.filter(img => {
    const mots = (img.MotsClés || "")
      .split(',')
      .map(m => amvMosaNormaliser(m))
      .filter(m => m.length > 0);
    return mots.includes(cle1) && mots.includes(cle2);
  });
}

// -------------------------
// Construire le pool (1 ou 2 mots)
// -------------------------
function amvMosaConstruirePool(motSaisi, motSaisi2) {
  const mot1 = (motSaisi  || "").trim();
  const mot2 = (motSaisi2 || "").trim();
  if (!mot1) return [];

  const deja = new Set();
  let pool = [];

  function ajouterImages(liste) {
    for (const img of liste) {
      const key = img.URLImage || img.NomFichier || JSON.stringify(img);
      if (!deja.has(key)) { deja.add(key); pool.push(img); }
    }
  }

  // --- MODE DEUX MOTS (ET) ---
  if (mot2) {
    // 1) Intersection directe
    ajouterImages(amvMosaFiltrerParDeuxMots(mot1, mot2));

    // 2) Si < 9 : étendre mot1 avec ses proches, conserver contrainte mot2
    if (pool.length < 9) {
      const cle1 = amvMosaNormaliser(mot1);
      const equiv1 = amvMosaProches[cle1] || [];
      for (const syn of equiv1) {
        if (pool.length >= 9) break;
        ajouterImages(amvMosaFiltrerParDeuxMots(syn, mot2));
      }
    }

    // 3) Si encore < 9 : étendre mot2 avec ses proches
    if (pool.length < 9) {
      const cle2 = amvMosaNormaliser(mot2);
      const equiv2 = amvMosaProches[cle2] || [];
      for (const syn of equiv2) {
        if (pool.length >= 9) break;
        ajouterImages(amvMosaFiltrerParDeuxMots(mot1, syn));
      }
    }

    return pool;
  }

  // --- MODE UN MOT (comportement original) ---
  const cleNorm = mot1.toLowerCase();
  ajouterImages(amvMosaFiltrerParMot(mot1));

  if (pool.length < 9) {
    const equiv = amvMosaProches[cleNorm] || [];
    let index = 0, securite = 0;
    while (pool.length < 9 && equiv.length && securite < 500) {
      const syn = equiv[index % equiv.length];
      const candidats = amvMosaFiltrerParMot(syn).filter(img => {
        const key = img.URLImage || img.NomFichier || JSON.stringify(img);
        return !deja.has(key);
      });
      if (candidats.length) {
        const alea = candidats[Math.floor(Math.random() * candidats.length)];
        const k = alea.URLImage || alea.NomFichier || JSON.stringify(alea);
        deja.add(k); pool.push(alea);
      }
      index++; securite++;
    }
  }

  return pool;
}

// -------------------------
// Affichage de la mosaïque
// (Fonction inchangée)
// -------------------------
function amvMosaAfficher(imagesSel, motAffiche) {
  const zone = document.getElementById("mosaique");
  const leg = document.getElementById("legendes");
  const log = document.getElementById("mosaique-log");
  const actions = document.getElementById("mosaique-actions");

  if (!zone || !leg || !log || !actions) return;

  zone.innerHTML = "";
  leg.innerHTML = "";
  actions.style.display = "none";

  if (!imagesSel.length) {
    log.textContent = `Aucune image trouvée pour « ${motAffiche} ».`;
    return;
  }

  log.textContent = `${imagesSel.length} image(s) trouvée(s) pour « ${motAffiche} ».`;

  const max = Math.min(9, imagesSel.length);
  const selection = imagesSel.slice(0, max);

  for (const img of selection) {
    const fig = document.createElement("figure");
    Object.assign(fig.style, {
      margin: "0",
      padding: "0",
      border: "2px solid white",
      background: "white",
      aspectRatio: "1 / 1",
      overflow: "hidden",
      position: "relative",
      lineHeight: "0",
      fontSize: "0",
      backgroundImage: `url('${img.URLImage}')`,
      backgroundSize: "cover",
      backgroundPosition: "center",
      backgroundRepeat: "no-repeat"
    });

    const lien = document.createElement("a");
    lien.href = img.URLPage;
    lien.title = img.Légende || "";
    lien.target = "_blank";
    Object.assign(lien.style, {
      position: "absolute",
      top: "0",
      left: "0",
      right: "0",
      bottom: "0",
      width: "100%",
      height: "100%",
      display: "block",
      zIndex: "10",
      cursor: "pointer",
      transition: "background-color 0.2s"
    });

    lien.addEventListener("mouseenter", () => {
      lien.style.backgroundColor = "rgba(0,0,0,0.2)";
    });
    lien.addEventListener("mouseleave", () => {
      lien.style.backgroundColor = "transparent";
    });

    fig.appendChild(lien);
    zone.appendChild(fig);
  }

  const textes = selection.map(i => i.Légende || "");
  leg.innerHTML =
    "<strong>Légendes (de gauche à droite) :</strong> " +
    textes.join("; ") +
    " — © Amavero art et poésie — Tous droits réservés aux artistes";

  actions.style.display = "flex";
  actions.dataset.mot = motAffiche;
}

// -------------------------
// Ajout: Gestion état des boutons
// -------------------------
function amvMosaSetBoutons(desactiver) {
  ["btnRecherche","btnRandom","btnAutre","motCle1","motCle2"].forEach(id => {
    const el = document.getElementById(id);
    if (el) el.disabled = desactiver;
  });
  const log = document.getElementById("mosaique-log");
  if (log) {
    log.style.color = desactiver ? "orange" : "green";
    if (desactiver) log.textContent = "⏳ Chargement des données en cours...";
  }
}

// -------------------------
// Autocomplétion champ 1 — tous les mots fréquents (≥9 occurrences)
// -------------------------
function amvMosaActiverAutocompletionSur(inputId) {
  if (!amvMosaMotsFrequents.length) return;

  const input = document.getElementById(inputId);
  if (!input) return;

  const parent = input.parentElement;
  if (!parent) return;

  if (parent.querySelector('.amv-suggestions')) return;

  const suggestions = document.createElement("div");
  suggestions.className = "amv-suggestions";
  Object.assign(suggestions.style, {
    position: "absolute",
    top: "100%",
    left: "0",
    right: "0",
    background: "#fff",
    border: "1px solid #ccc",
    borderRadius: "6px",
    maxHeight: "180px",
    overflowY: "auto",
    zIndex: "1000",
    display: "none"
  });
  parent.appendChild(suggestions);

  function norm(t) { return amvMosaNormaliser(t); }

  function renderSuggestions() {
    const val = (input.value || "").trim();
    if (!val) { suggestions.style.display = "none"; suggestions.innerHTML = ""; return; }
    const v = norm(val);
    const liste = amvMosaMotsFrequents.filter(m => norm(m).startsWith(v)).slice(0, 10);
    if (!liste.length) { suggestions.style.display = "none"; suggestions.innerHTML = ""; return; }
    suggestions.innerHTML = "";
    for (const mot of liste) {
      const opt = document.createElement("div");
      opt.textContent = mot;
      opt.style.padding = "6px 8px";
      opt.style.cursor = "pointer";
      opt.onmouseover = () => (opt.style.background = "#eee");
      opt.onmouseout  = () => (opt.style.background = "#fff");
      opt.onclick = () => {
        input.value = mot;
        suggestions.style.display = "none";
        // Vider mot2 quand mot1 change
        const input2 = document.getElementById("motCle2");
        if (input2) input2.value = "";
      };
      suggestions.appendChild(opt);
    }
    suggestions.style.display = "block";
  }

  input.addEventListener("input", renderSuggestions);
  input.addEventListener("focus", renderSuggestions);
  // Vider mot2 quand mot1 change au clavier
  input.addEventListener("input", () => {
    const input2 = document.getElementById("motCle2");
    if (input2) input2.value = "";
  });
  document.addEventListener("click", (e) => {
    if (!suggestions.contains(e.target) && e.target !== input) {
      suggestions.style.display = "none";
    }
  });
}

// -------------------------
// Autocomplétion champ 2 — uniquement les mots co-occurrents avec mot1
// Contrainte : ≥9 images ayant à la fois mot1 ET le mot candidat
// -------------------------
function amvMosaActiverAutocompletionMot2() {
  const input2 = document.getElementById("motCle2");
  if (!input2) return;

  const parent = input2.parentElement;
  if (!parent) return;

  if (parent.querySelector('.amv-suggestions-mot2')) return;

  const suggestions = document.createElement("div");
  suggestions.className = "amv-suggestions-mot2";
  Object.assign(suggestions.style, {
    position: "absolute",
    top: "100%",
    left: "0",
    right: "0",
    background: "#fff",
    border: "1px solid #ccc",
    borderRadius: "6px",
    maxHeight: "180px",
    overflowY: "auto",
    zIndex: "1000",
    display: "none"
  });
  parent.appendChild(suggestions);

  function norm(t) { return amvMosaNormaliser(t); }

  function renderSuggestionsMot2() {
    const val  = (input2.value || "").trim();
    if (!val) { suggestions.style.display = "none"; suggestions.innerHTML = ""; return; }

    const v     = norm(val);
    const mot1r = (document.getElementById("motCle1") || {}).value || "";
    const mot1n = norm(mot1r);

    let candidats = [];

    if (mot1n) {
      // Étape 1 : collecter les formes candidates via split par virgule
      const imagesMot1 = amvMosaFiltrerParMot(mot1r);
      const trouvés = {};
      for (const img of imagesMot1) {
        const mots = (img.MotsClés || "").split(",").map(m => m.trim()).filter(m => m);
        for (const m of mots) {
          const n = norm(m);
          if (n === mot1n || !n) continue;
          if (!trouvés[n]) trouvés[n] = m;
        }
      }

      // Étape 2 : compter avec amvMosaFiltrerParDeuxMots (même algo que la recherche)
      // → le chiffre affiché = ce que la recherche trouvera exactement
      const prefixCandidats = Object.entries(trouvés)
        .filter(([n]) => n.startsWith(v));

      for (const [, forme] of prefixCandidats) {
        const images = amvMosaFiltrerParDeuxMots(mot1r, forme);
        // Compter les images distinctes (après déduplication par URLImage)
        // = ce que la mosaïque affichera réellement
        const urlsUniques = new Set(images.map(img => img.URLImage || img.NomFichier || JSON.stringify(img)));
        const count = urlsUniques.size;
        if (count >= 9) {
          candidats.push({ mot: forme, count });
        }
      }

      candidats.sort((a, b) => b.count - a.count);
      candidats = candidats.slice(0, 10);

    } else {
      candidats = amvMosaMotsFrequents
        .filter(m => norm(m).startsWith(v))
        .slice(0, 10)
        .map(m => ({ mot: m, count: null }));
    }

    if (!candidats.length) { suggestions.style.display = "none"; suggestions.innerHTML = ""; return; }

    suggestions.innerHTML = "";
    for (const c of candidats) {
      const opt = document.createElement("div");
      opt.style.padding = "6px 8px";
      opt.style.cursor = "pointer";
      opt.style.display = "flex";
      opt.style.justifyContent = "space-between";
      opt.style.alignItems = "center";

      const texte = document.createElement("span");
      texte.textContent = c.mot;

      const badge = document.createElement("span");
      if (c.count !== null) {
        badge.textContent = c.count + " img.";
        Object.assign(badge.style, {
          fontSize: "11px",
          color: "#999",
          marginLeft: "8px"
        });
      }

      opt.appendChild(texte);
      opt.appendChild(badge);

      opt.onmouseover = () => (opt.style.background = "#eee");
      opt.onmouseout  = () => (opt.style.background = "#fff");
      opt.onclick = () => {
        input2.value = c.mot;
        suggestions.style.display = "none";
      };
      suggestions.appendChild(opt);
    }
    suggestions.style.display = "block";
  }

  input2.addEventListener("input", renderSuggestionsMot2);
  input2.addEventListener("focus", renderSuggestionsMot2);
  document.addEventListener("click", (e) => {
    if (!suggestions.contains(e.target) && e.target !== input2) {
      suggestions.style.display = "none";
    }
  });
}

function amvMosaActiverAutocompletion() {
  amvMosaActiverAutocompletionSur("motCle1");
  amvMosaActiverAutocompletionMot2();
  console.log("✅ Autocomplétion Mosaïque activée (mot1 tous mots / mot2 co-occurrences ≥9).");
}
// -------------------------
// Boutons et export JPG
// (Fonctions inchangées)
// -------------------------
function amvMosaInitListeners() {
  const input = document.getElementById("motCle1");
  const btnRecherche = document.getElementById("btnRecherche");
  const btnRandom = document.getElementById("btnRandom");
  const btnAutre = document.getElementById("btnAutre");
  const btnCopier = document.getElementById("btnCopier");
  const btnTelecharger = document.getElementById("btnTelecharger");
  const log = document.getElementById("mosaique-log");

  if (btnRecherche && input) {
    btnRecherche.onclick = () => {
      const saisie  = (input.value || "").trim();
      const saisie2 = ((document.getElementById("motCle2") || {}).value || "").trim(); // ← MODIFIÉ
      if (!saisie) return;
      const pool = amvMosaConstruirePool(saisie, saisie2); // ← MODIFIÉ
      const label = saisie2 ? `« ${saisie} » ET « ${saisie2} »` : `« ${saisie} »`; // ← MODIFIÉ
      if (!pool.length && log) {
        log.textContent = `Aucune image trouvée pour ${label}.`; // ← MODIFIÉ
        return;
      }
      pool.sort(() => Math.random() - 0.5);
      amvMosaAfficher(pool, saisie2 ? `${saisie} + ${saisie2}` : saisie); // ← MODIFIÉ
    };
  }

  if (btnRandom) {
    btnRandom.onclick = () => {
      if (!amvMosaImages.length || !log) return;
      const copie = amvMosaImages.slice();
      copie.sort(() => Math.random() - 0.5);
      const selection = [];
      const deja = new Set();
      for (const img of copie) {
        const key = img.URLImage || img.NomFichier || JSON.stringify(img);
        if (!deja.has(key)) {
          deja.add(key);
          selection.push(img);
        }
        if (selection.length >= 9) break;
      }
      if (!selection.length) {
        log.textContent = "Aucune image disponible.";
        return;
      }
      amvMosaAfficher(selection, "image au hasard");
    };
  }

  if (btnCopier) {
    btnCopier.onclick = () => {
      const leg = document.getElementById("legendes");
      if (!leg) return;
      const txt = leg.innerText || "";
      if (!txt) return;
      navigator.clipboard.writeText(txt).then(() => {
        alert("Légendes copiées !");
      });
    };
  }

  if (btnAutre && input) {
    btnAutre.onclick = () => {
      const saisie  = (input.value || "").trim();
      const saisie2 = ((document.getElementById("motCle2") || {}).value || "").trim(); // ← MODIFIÉ
      if (!saisie) return;
      const pool = amvMosaConstruirePool(saisie, saisie2); // ← MODIFIÉ
      if (!pool.length && log) {
        log.textContent = `Aucune image trouvée.`;
        return;
      }
      pool.sort(() => Math.random() - 0.5);
      amvMosaAfficher(pool, saisie2 ? `${saisie} + ${saisie2}` : saisie); // ← MODIFIÉ
    };
  }

  if (btnTelecharger) {
    btnTelecharger.onclick = async () => {
      const mot1 = (document.getElementById("motCle1") || {}).value || "";
const mot2 = (document.getElementById("motCle2") || {}).value || "";
const motTitre = mot2 ? `${mot1}_${mot2}` : mot1;

      const grille = document.getElementById("mosaique");
      const leg = document.getElementById("legendes");
      if (!grille || !leg) return;

      const legText = leg.innerText || "";

      const gridCanvas = await html2canvas(grille, {
        backgroundColor: "#fff",
        useCORS: true,
        scale: 2,
        allowTaint: false
      });

      const gridW = gridCanvas.width;
      const gridH = gridCanvas.height;

      const padX = 20;
      const padTop = 20;
      const padMid = 20;
      const padBot = 20;
      const fontSizeTitre = 20;
      const fontSizeLegende = 16;
      const lineHeight = 24;

      const tmpCanvas = document.createElement("canvas");
      const tmpCtx = tmpCanvas.getContext("2d");
      tmpCtx.font = `${fontSizeLegende}px Inter, sans-serif`;

      function wrapText(ctx, text, maxWidth) {
        const words = text.split(" ");
        const lines = [];
        let currentLine = "";

        for (let word of words) {
          const testLine = currentLine + (currentLine ? " " : "") + word;
          const metrics = ctx.measureText(testLine);
          if (metrics.width > maxWidth && currentLine) {
            lines.push(currentLine);
            currentLine = word;
          } else {
            currentLine = testLine;
          }
        }
        if (currentLine) lines.push(currentLine);
        return lines;
      }

      const titre = motTitre ? `${motTitre} — ` : "";
      tmpCtx.font = `bold ${fontSizeTitre}px Inter, sans-serif`;
      const titreWidth = tmpCtx.measureText(titre).width;

      tmpCtx.font = `${fontSizeLegende}px Inter, sans-serif`;
      const maxTextWidth = gridW - padX * 2;

      const segments = [];
      if (titre) {
        if (titreWidth < maxTextWidth) {
          const resteLeg = legText;
          const premiere = titre + resteLeg;
          tmpCtx.font = `${fontSizeLegende}px Inter, sans-serif`;
          const premWidth = tmpCtx.measureText(premiere).width;

          if (premWidth <= maxTextWidth) {
            segments.push({ text: titre, bold: true, fontSize: fontSizeTitre });
            segments.push({ text: resteLeg, bold: false, fontSize: fontSizeLegende });
          } else {
            segments.push({ text: titre, bold: true, fontSize: fontSizeTitre });
            const wrapped = wrapText(tmpCtx, resteLeg, maxTextWidth - titreWidth);
            if (wrapped.length) {
              segments.push({ text: wrapped[0], bold: false, fontSize: fontSizeLegende });
              for (let i = 1; i < wrapped.length; i++) {
                segments.push({ text: wrapped[i], bold: false, fontSize: fontSizeLegende, newLine: true });
              }
            }
          }
        } else {
          segments.push({ text: titre, bold: true, fontSize: fontSizeTitre });
          const wrapped = wrapText(tmpCtx, legText, maxTextWidth);
          wrapped.forEach(line =>
            segments.push({ text: line, bold: false, fontSize: fontSizeLegende, newLine: true })
          );
        }
      } else {
        const wrapped = wrapText(tmpCtx, legText, maxTextWidth);
        wrapped.forEach(line =>
          segments.push({ text: line, bold: false, fontSize: fontSizeLegende, newLine: true })
        );
      }

      let nbLignes = 1;
      for (const seg of segments) {
        if (seg.newLine) nbLignes++;
      }

      const textH = nbLignes * lineHeight;
      const finalH = padTop + gridH + padMid + textH + padBot;

      const finalCanvas = document.createElement("canvas");
      finalCanvas.width = gridW;
      finalCanvas.height = finalH;
      const ctx = finalCanvas.getContext("2d");

      ctx.fillStyle = "#fff";
      ctx.fillRect(0, 0, finalCanvas.width, finalCanvas.height);

      ctx.drawImage(gridCanvas, 0, padTop);

      let y = padTop + gridH + padMid;
      let x = padX;
      ctx.fillStyle = "#333";
      ctx.textBaseline = "top";

      for (const seg of segments) {
        if (seg.newLine) {
          y += lineHeight;
          x = padX;
        }
        const fSize = seg.fontSize || fontSizeLegende;
        ctx.font = (seg.bold ? "bold " : "") + `${fSize}px Inter, sans-serif`;
        ctx.fillText(seg.text, x, y);
        x += ctx.measureText(seg.text).width;
      }

      const link = document.createElement("a");
      const nom = (motTitre || "amavero").toLowerCase().replace(/\s+/g, "_");
      link.download = "mosaique-" + nom + ".jpg";
      link.href = finalCanvas.toDataURL("image/jpeg", 0.95);
      link.click();
    };
  }
}
// -------------------------
// Initialisation globale
// -------------------------
(async function amvMosaInit() {
  await amvMosaChargerDonnees();
  amvMosaInitListeners();
})();