(() => { "use strict"; const modeEl = document.getElementById("qr-mode"); const modeTextWrap = document.getElementById("mode-text"); const modeWifiWrap = document.getElementById("mode-wifi"); const modeVcardWrap = document.getElementById("mode-vcard"); const textEl = document.getElementById("qr-text"); const wifiSsidEl = document.getElementById("wifi-ssid"); const wifiEncryptionEl = document.getElementById("wifi-encryption"); const wifiPasswordEl = document.getElementById("wifi-password"); const wifiPasswordField = document.getElementById("wifi-password-field"); const wifiHiddenEl = document.getElementById("wifi-hidden"); const vcardFirstnameEl = document.getElementById("vcard-firstname"); const vcardLastnameEl = document.getElementById("vcard-lastname"); const vcardPhoneEl = document.getElementById("vcard-phone"); const vcardEmailEl = document.getElementById("vcard-email"); const vcardOrgEl = document.getElementById("vcard-org"); const vcardTitleEl = document.getElementById("vcard-title"); const vcardWebsiteEl = document.getElementById("vcard-website"); const cellSizeEl = document.getElementById("qr-cell-size"); const correctionEl = document.getElementById("qr-correction"); const fgColorEl = document.getElementById("qr-fg-color"); const bgColorEl = document.getElementById("qr-bg-color"); const logoInputEl = document.getElementById("qr-logo-input"); const logoClearBtn = document.getElementById("qr-logo-clear"); const generateBtn = document.getElementById("generate-btn"); const downloadBtn = document.getElementById("download-btn"); const canvas = document.getElementById("qr-canvas"); const statusEl = document.getElementById("qr-status"); const ctx = canvas.getContext("2d"); let logoImage = null; function readLogoFile(file) { if (!file) return; const reader = new FileReader(); reader.onload = () => { const img = new Image(); img.onload = () => { logoImage = img; render(); }; img.src = reader.result; }; reader.readAsDataURL(file); } logoInputEl.addEventListener("change", (e) => { readLogoFile(e.target.files[0]); }); logoClearBtn.addEventListener("click", () => { logoImage = null; logoInputEl.value = ""; render(); }); function escapeWifiField(value) { return String(value).replace(/([\\;,":])/g, "\\$1"); } function buildWifiPayload() { const ssid = wifiSsidEl.value; const encryption = wifiEncryptionEl.value; const password = wifiPasswordEl.value; const hidden = wifiHiddenEl.checked; if (!ssid) return ""; let payload = `WIFI:T:${encryption};S:${escapeWifiField(ssid)};`; if (encryption !== "nopass" && password) { payload += `P:${escapeWifiField(password)};`; } payload += `H:${hidden ? "true" : "false"};;`; return payload; } function escapeVcardField(value) { return String(value).replace(/([\\;,])/g, "\\$1").replace(/\n/g, "\\n"); } function buildVcardPayload() { const firstName = vcardFirstnameEl.value.trim(); const lastName = vcardLastnameEl.value.trim(); const phone = vcardPhoneEl.value.trim(); const email = vcardEmailEl.value.trim(); const org = vcardOrgEl.value.trim(); const title = vcardTitleEl.value.trim(); const website = vcardWebsiteEl.value.trim(); if (!firstName && !lastName) return ""; const fullName = [firstName, lastName].filter(Boolean).join(" "); let payload = "BEGIN:VCARD\nVERSION:3.0\n"; payload += `N:${escapeVcardField(lastName)};${escapeVcardField(firstName)};;;\n`; payload += `FN:${escapeVcardField(fullName)}\n`; if (org) payload += `ORG:${escapeVcardField(org)}\n`; if (title) payload += `TITLE:${escapeVcardField(title)}\n`; if (phone) payload += `TEL;TYPE=CELL:${escapeVcardField(phone)}\n`; if (email) payload += `EMAIL:${escapeVcardField(email)}\n`; if (website) payload += `URL:${escapeVcardField(website)}\n`; payload += "END:VCARD"; return payload; } function updateModeVisibility() { const mode = modeEl.value; modeTextWrap.style.display = mode === "text" ? "block" : "none"; modeWifiWrap.style.display = mode === "wifi" ? "block" : "none"; modeVcardWrap.style.display = mode === "vcard" ? "block" : "none"; wifiPasswordField.style.display = wifiEncryptionEl.value === "nopass" ? "none" : "block"; } modeEl.addEventListener("change", () => { updateModeVisibility(); render(); }); wifiEncryptionEl.addEventListener("change", updateModeVisibility); [wifiSsidEl, wifiEncryptionEl, wifiPasswordEl, wifiHiddenEl].forEach((el) => { el.addEventListener("input", render); el.addEventListener("change", render); }); [ vcardFirstnameEl, vcardLastnameEl, vcardPhoneEl, vcardEmailEl, vcardOrgEl, vcardTitleEl, vcardWebsiteEl, ].forEach((el) => { el.addEventListener("input", render); }); function render() { const mode = modeEl.value; let text; if (mode === "wifi") { text = buildWifiPayload(); } else if (mode === "vcard") { text = buildVcardPayload(); } else { text = textEl.value; } statusEl.classList.remove("error"); if (!text) { ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.width = 280; canvas.height = 280; if (mode === "wifi") { statusEl.textContent = "Renseignez au moins le nom du réseau (SSID) pour générer le QR code."; } else if (mode === "vcard") { statusEl.textContent = "Renseignez au moins un prénom ou un nom pour générer le QR code."; } else { statusEl.textContent = "Saisissez un texte ou une URL pour générer le QR code."; } return; } const cellSize = parseInt(cellSizeEl.value, 10) || 8; const requestedLevel = correctionEl.value; const level = logoImage ? "H" : requestedLevel; const fgColor = fgColorEl.value || "#000000"; const bgColor = bgColorEl.value || "#ffffff"; const margin = 4; // modules try { const qr = qrcode(0, level); qr.addData(text); qr.make(); const moduleCount = qr.getModuleCount(); const size = (moduleCount + margin * 2) * cellSize; canvas.width = size; canvas.height = size; ctx.imageSmoothingEnabled = false; ctx.fillStyle = bgColor; ctx.fillRect(0, 0, size, size); ctx.fillStyle = fgColor; for (let row = 0; row < moduleCount; row++) { for (let col = 0; col < moduleCount; col++) { if (qr.isDark(row, col)) { ctx.fillRect((col + margin) * cellSize, (row + margin) * cellSize, cellSize, cellSize); } } } if (logoImage) { const logoSize = size * 0.22; const pad = logoSize * 0.14; const x = (size - logoSize) / 2; const y = (size - logoSize) / 2; ctx.fillStyle = bgColor; ctx.fillRect(x - pad, y - pad, logoSize + pad * 2, logoSize + pad * 2); ctx.imageSmoothingEnabled = true; ctx.drawImage(logoImage, x, y, logoSize, logoSize); } statusEl.textContent = logoImage ? `QR code généré (correction ${level}, forcée par le logo).` : `QR code généré (correction ${level}).`; } catch (err) { statusEl.textContent = "Erreur : " + (err.message || "contenu trop long pour être encodé."); statusEl.classList.add("error"); } } function downloadPng() { canvas.toBlob((blob) => { if (!blob) return; const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "qrcode.png"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }, "image/png"); } [textEl, cellSizeEl, correctionEl, fgColorEl, bgColorEl].forEach((el) => { el.addEventListener("input", render); }); generateBtn.addEventListener("click", render); downloadBtn.addEventListener("click", downloadPng); updateModeVisibility(); render(); })();