From 205876a28ff5805a9feee2cdc41844e71eb0eca3 Mon Sep 17 00:00:00 2001 From: Alexander Wainwright Date: Thu, 18 Dec 2025 18:30:58 +1000 Subject: [PATCH] Add reset button --- index.html | 7 ++-- script.js | 102 +++++++++++++++++++++++++++++------------------------ 2 files changed, 60 insertions(+), 49 deletions(-) diff --git a/index.html b/index.html index bd599b7..a63062e 100644 --- a/index.html +++ b/index.html @@ -133,13 +133,16 @@ +
- +
- +
diff --git a/script.js b/script.js index 106a2df..ed7e292 100644 --- a/script.js +++ b/script.js @@ -3,6 +3,7 @@ document.addEventListener('DOMContentLoaded', () => { const qrContainer = document.getElementById('qr-container'); const downloadBtn = document.getElementById('btn-download'); const stickyWrapper = document.querySelector('.sticky-wrapper'); + const clearBtn = document.getElementById('btn-clear'); // Gauge Elements const capMeter = document.getElementById('capacity-meter'); @@ -23,7 +24,7 @@ document.addEventListener('DOMContentLoaded', () => { let debounceTimer; - // --- 2. STATE GETTERS (Pure Functions) --- + // --- 2. STATE GETTERS --- function getFormat() { for (const r of fmtRadios) { @@ -40,7 +41,6 @@ document.addEventListener('DOMContentLoaded', () => { const ssid = document.getElementById('inp-wifi-ssid').value; const pass = document.getElementById('inp-wifi-pass').value; const type = document.getElementById('inp-wifi-type').value; - // Note: Even if empty, we return the structure so the logic can decide if (!ssid) return ''; const cleanSSID = ssid.replace(/([\\;,:])/g, '\\$1'); const cleanPass = pass.replace(/([\\;,:])/g, '\\$1'); @@ -60,7 +60,6 @@ document.addEventListener('DOMContentLoaded', () => { function generateSVGString(modules, size, fg, bg) { const count = modules.length; const modSize = size / count; - let pathData = ''; for (let r = 0; r < count; r++) { for (let c = 0; c < count; c++) { @@ -71,7 +70,6 @@ document.addEventListener('DOMContentLoaded', () => { } } } - return ` @@ -94,15 +92,11 @@ document.addEventListener('DOMContentLoaded', () => { stickyWrapper.classList.add('has-error'); } - // --- 4. CORE LOGIC --- - - // A. Capacity Gauge (Fast / Instant) function updateCapacityUI(textData) { if (!textData) { capMeter.style.display = 'none'; return; } - const blob = new Blob([textData]); const bytes = blob.size; @@ -139,9 +133,9 @@ document.addEventListener('DOMContentLoaded', () => { } } -// B. The Main Renderer (Stateless: Flush and Rebuild) + // --- 4. CORE RENDERER --- + function renderQR() { - // 1. Gather State const textData = getDataString(); const format = getFormat(); const ecc = QRCode.CorrectLevel[optEcc.value]; @@ -151,11 +145,9 @@ document.addEventListener('DOMContentLoaded', () => { hexFg.textContent = colorDark; hexBg.textContent = colorLight; - // 2. Reset DOM qrContainer.innerHTML = ''; stickyWrapper.classList.remove('has-error'); - // 3. Handle Empty State if (!textData || textData.trim() === '') { downloadBtn.disabled = true; downloadBtn.textContent = `DOWNLOAD ${format.toUpperCase()}`; @@ -164,12 +156,10 @@ document.addEventListener('DOMContentLoaded', () => { downloadBtn.textContent = `DOWNLOAD ${format.toUpperCase()}`; - // 4. Validate Size let size = parseInt(optSize.value) || 256; if (size < 64) size = 64; if (size > 4000) size = 4000; - // 5. Generate try { const instance = new QRCode(qrContainer, { text: textData, @@ -180,7 +170,6 @@ document.addEventListener('DOMContentLoaded', () => { correctLevel : ecc }); - // 6. Post-Process if (format === 'svg') { const modules = instance._oQRCode.modules; const nodes = qrContainer.childNodes; @@ -194,47 +183,26 @@ document.addEventListener('DOMContentLoaded', () => { const img = qrContainer.querySelector('img'); if(img) img.style.display = 'block'; } - downloadBtn.disabled = false; } catch (e) { - // 7. Smart Error Handling - - // Calculate usage to guess the error type const blob = new Blob([textData]); const bytes = blob.size; const maxCapacityMap = { 'L': 2953, 'M': 2331, 'Q': 1663, 'H': 1273 }; const maxBytes = maxCapacityMap[optEcc.value] || 1273; - // If we are over (or extremely close to) the limit, it's definitely a capacity issue. - // (We use >= because sometimes overhead pushes it over even if bytes == maxBytes) if (bytes >= maxBytes) { - renderError( - "CAPACITY EXCEEDED", - "REDUCE TEXT OR LOWER ECC LEVEL" - ); - } - else { - // If usage is low but it crashed, it's a real bug (e.g. invalid char code). - // Show the specific error for debugging. - console.error(e); // Keep looking at console for devs - renderError( - "UNKNOWN ERROR", - `CODE: ${e.name || 'Except'} // ${e.message || 'Check Console'}` - ); + renderError("CAPACITY EXCEEDED", "REDUCE TEXT OR LOWER ECC LEVEL"); + } else { + console.error(e); + renderError("UNKNOWN ERROR", `CODE: ${e.name || 'Except'}`); } } } - // --- 5. EVENT ORCHESTRATION --- - function handleUpdate(immediate = false) { const text = getDataString(); - - // Always update gauge immediately updateCapacityUI(text); - - // Debounce the heavy rendering clearTimeout(debounceTimer); if (immediate) { renderQR(); @@ -243,33 +211,58 @@ document.addEventListener('DOMContentLoaded', () => { } } + // --- 5. RESET LOGIC (Button Only) --- + function resetApp() { + // Clear Inputs + const inputs = document.querySelectorAll('.input-form input, .input-form textarea'); + inputs.forEach(el => el.value = ''); + + // Reset Mode to Text + modeSelector.value = 'text'; + + // Update UI Classes + document.querySelectorAll('.input-form').forEach(f => f.classList.remove('active')); + document.getElementById('form-text').classList.add('active'); + + // Update Output + handleUpdate(true); + + // Focus + const firstField = document.querySelector('#form-text input'); + if (firstField) firstField.focus(); + } + // --- 6. LISTENERS --- - // Configuration Inputs (Colors, ECC, Size) -> Trigger Rebuild [optEcc, optSize, optFg, optBg].forEach(el => { el.addEventListener('input', () => handleUpdate(false)); }); - // Format Change -> Trigger Rebuild (Instant) fmtRadios.forEach(r => r.addEventListener('change', () => handleUpdate(true))); - // Mode Switch -> Change Form & Rebuild modeSelector.addEventListener('change', (e) => { const newMode = e.target.value; document.querySelectorAll('.input-form').forEach(f => f.classList.remove('active')); document.getElementById(`form-${newMode}`).classList.add('active'); + + const newForm = document.getElementById(`form-${newMode}`); + const firstField = newForm.querySelector('input, textarea'); + if (firstField) firstField.focus(); + handleUpdate(true); }); - // Data Entry -> Debounced Rebuild document.querySelectorAll('.input-form').forEach(form => { form.addEventListener('input', () => handleUpdate(false)); }); - // Download Handler + // Clear Button + if (clearBtn) { + clearBtn.addEventListener('click', resetApp); + } + downloadBtn.addEventListener('click', () => { const format = getFormat(); - if (format === 'png') { const img = qrContainer.querySelector('img'); if (img && img.src) { @@ -297,6 +290,21 @@ document.addEventListener('DOMContentLoaded', () => { } }); - // Initial Render + // --- 7. STARTUP SEQUENCE (Browser Restore Friendly) --- + + // Check what mode the browser remembered + const initialMode = modeSelector.value; + + // Force the correct form to show based on that mode + document.querySelectorAll('.input-form').forEach(f => f.classList.remove('active')); + const initialForm = document.getElementById(`form-${initialMode}`); + if (initialForm) { + initialForm.classList.add('active'); + // Auto-focus the restored form + const startField = initialForm.querySelector('input, textarea'); + if (startField) startField.focus(); + } + + // Render whatever data the browser remembered handleUpdate(true); });