Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>QRGenX - Free QR Code Generator</title> | |
| <script src="https://cdn.tailwindcss.com"> | |
| // Get current location | |
| function getCurrentLocation() { | |
| if (navigator.geolocation) { | |
| navigator.geolocation.getCurrentPosition( | |
| function(position) { | |
| document.getElementById('locationLatitude').value = position.coords.latitude.toFixed(6); | |
| document.getElementById('locationLongitude').value = position.coords.longitude.toFixed(6); | |
| }, | |
| function(error) { | |
| showError("Unable to get current location: " + error.message); | |
| } | |
| ); | |
| } else { | |
| showError("Geolocation is not supported by this browser"); | |
| } | |
| } | |
| </script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .qr-placeholder { | |
| background-image: | |
| linear-gradient(45deg, #f0f0f0 25%, transparent 25%), | |
| linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), | |
| linear-gradient(45deg, transparent 75%, #f0f0f0 75%), | |
| linear-gradient(-45deg, transparent 75%, #f0f0f0 75%); | |
| background-size: 20px 20px; | |
| background-position: 0 0, 0 10px, 10px -10px, -10px 0px; | |
| } | |
| .color-picker { | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| appearance: none; | |
| width: 100%; | |
| height: 40px; | |
| background-color: transparent; | |
| border: none; | |
| cursor: pointer; | |
| } | |
| .color-picker::-webkit-color-swatch { | |
| border-radius: 8px; | |
| border: 2px solid #e5e7eb; | |
| } | |
| .color-picker::-moz-color-swatch { | |
| border-radius: 8px; | |
| border: 2px solid #e5e7eb; | |
| } | |
| .custom-file-input::-webkit-file-upload-button { | |
| visibility: hidden; | |
| } | |
| .custom-file-input::before { | |
| content: 'Choose icon'; | |
| display: inline-block; | |
| background: linear-gradient(to bottom, #f9f9f9, #e3e3e3); | |
| border: 1px solid #999; | |
| border-radius: 6px; | |
| padding: 8px 16px; | |
| outline: none; | |
| white-space: nowrap; | |
| cursor: pointer; | |
| font-weight: 500; | |
| font-size: 14px; | |
| color: #333; | |
| } | |
| .custom-file-input:hover::before { | |
| border-color: #666; | |
| } | |
| .custom-file-input:active::before { | |
| background: linear-gradient(to bottom, #e3e3e3, #f9f9f9); | |
| } | |
| #previewCanvas { | |
| max-width: 100%; | |
| height: auto; | |
| transition: all 0.3s ease; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Header --> | |
| <header class="text-center mb-12"> | |
| <div class="flex justify-center items-center gap-2 mb-4"> | |
| <i class="fas fa-qrcode text-4xl text-blue-600"></i> | |
| <h1 class="text-4xl font-bold text-gray-800">QRGenX</h1> | |
| </div> | |
| <p class="text-lg text-gray-600 max-w-2xl mx-auto"> | |
| Create beautiful, customizable QR codes for free. No registration required. | |
| </p> | |
| </header> | |
| <div class="flex flex-col lg:flex-row gap-8"> | |
| <!-- QR Code Generator Form --> | |
| <div class="w-full lg:w-1/2 bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800 mb-6">Customize Your QR Code</h2> | |
| <form id="qrForm" class="space-y-6"> | |
| <!-- Content Type --> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Content Type</label> | |
| <div class="grid grid-cols-2 gap-3"> | |
| <button type="button" data-type="url" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-link"></i> | |
| URL | |
| </button> | |
| <button type="button" data-type="text" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-font"></i> | |
| Text | |
| </button> | |
| <button type="button" data-type="wifi" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-wifi"></i> | |
| WiFi | |
| </button> | |
| <button type="button" data-type="contact" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-user"></i> | |
| Contact | |
| </button> | |
| <button type="button" data-type="email" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-envelope"></i> | |
| </button> | |
| <button type="button" data-type="sms" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-comment"></i> | |
| SMS | |
| </button> | |
| <button type="button" data-type="phone" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-phone"></i> | |
| Phone | |
| </button> | |
| <button type="button" data-type="location" class="qr-type-btn py-2 px-4 border rounded-lg font-medium flex items-center justify-center gap-2" onclick="setActiveType(this)"> | |
| <i class="fas fa-map-marker-alt"></i> | |
| Location | |
| </button> | |
| </div> | |
| </div> | |
| <!-- URL Input (Default) --> | |
| <div id="urlInput" class="content-input"> | |
| <label for="qrContent" class="block text-sm font-medium text-gray-700 mb-2">Website URL</label> | |
| <div class="flex"> | |
| <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm"> | |
| <i class="fas fa-link"></i> | |
| </span> | |
| <input type="url" id="qrContent" name="qrContent" placeholder="https://example.com" class="flex-1 min-w-0 block w-full px-3 py-2 rounded-none rounded-r-md border border-gray-300 focus:ring-blue-500 focus:border-blue-500" required> | |
| </div> | |
| </div> | |
| <!-- Text Input --> | |
| <div id="textInput" class="content-input hidden"> | |
| <label for="qrTextContent" class="block text-sm font-medium text-gray-700 mb-2">Text Content</label> | |
| <textarea id="qrTextContent" name="qrTextContent" rows="3" placeholder="Enter any text you want to encode" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"></textarea> | |
| </div> | |
| <!-- WiFi Input --> | |
| <div id="wifiInput" class="content-input hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="wifiSsid" class="block text-sm font-medium text-gray-700 mb-2">Network Name (SSID)</label> | |
| <input type="text" id="wifiSsid" name="wifiSsid" placeholder="MyWiFiNetwork" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="wifiPassword" class="block text-sm font-medium text-gray-700 mb-2">Password</label> | |
| <input type="text" id="wifiPassword" name="wifiPassword" placeholder="Password123" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Security Type</label> | |
| <select id="wifiSecurity" name="wifiSecurity" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="WPA">WPA/WPA2</option> | |
| <option value="WEP">WEP</option> | |
| <option value="">None</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Contact Input --> | |
| <div id="contactInput" class="content-input hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="contactName" class="block text-sm font-medium text-gray-700 mb-2">Name</label> | |
| <input type="text" id="contactName" name="contactName" placeholder="John Doe" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="contactPhone" class="block text-sm font-medium text-gray-700 mb-2">Phone</label> | |
| <input type="tel" id="contactPhone" name="contactPhone" placeholder="+1234567890" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="contactEmail" class="block text-sm font-medium text-gray-700 mb-2">Email</label> | |
| <input type="email" id="contactEmail" name="contactEmail" placeholder="john@example.com" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Email Input --> | |
| <div id="emailInput" class="content-input hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="emailTo" class="block text-sm font-medium text-gray-700 mb-2">Recipient Email</label> | |
| <input type="email" id="emailTo" name="emailTo" placeholder="recipient@example.com" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="emailSubject" class="block text-sm font-medium text-gray-700 mb-2">Subject</label> | |
| <input type="text" id="emailSubject" name="emailSubject" placeholder="Email Subject" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="emailBody" class="block text-sm font-medium text-gray-700 mb-2">Message</label> | |
| <textarea id="emailBody" name="emailBody" rows="3" placeholder="Your message here..." class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"></textarea> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- SMS Input --> | |
| <div id="smsInput" class="content-input hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="smsNumber" class="block text-sm font-medium text-gray-700 mb-2">Phone Number</label> | |
| <input type="tel" id="smsNumber" name="smsNumber" placeholder="+1234567890" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="smsMessage" class="block text-sm font-medium text-gray-700 mb-2">Message</label> | |
| <textarea id="smsMessage" name="smsMessage" rows="3" placeholder="Your SMS message..." class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"></textarea> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Phone Input --> | |
| <div id="phoneInput" class="content-input hidden"> | |
| <div> | |
| <label for="phoneNumber" class="block text-sm font-medium text-gray-700 mb-2">Phone Number</label> | |
| <input type="tel" id="phoneNumber" name="phoneNumber" placeholder="+1234567890" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| </div> | |
| <!-- Location Input --> | |
| <div id="locationInput" class="content-input hidden"> | |
| <div class="space-y-4"> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div> | |
| <label for="locationLatitude" class="block text-sm font-medium text-gray-700 mb-2">Latitude</label> | |
| <input type="number" id="locationLatitude" name="locationLatitude" step="0.000001" placeholder="40.7128" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="locationLongitude" class="block text-sm font-medium text-gray-700 mb-2">Longitude</label> | |
| <input type="number" id="locationLongitude" name="locationLongitude" step="0.000001" placeholder="-74.0060" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| </div> | |
| <div> | |
| <button type="button" onclick="getCurrentLocation()" class="w-full py-2 px-4 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-gray-50 hover:bg-gray-100 flex items-center justify-center gap-2"> | |
| <i class="fas fa-location-arrow"></i> Use Current Location | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Design Options --> | |
| <div class="pt-4 border-t border-gray-200"> | |
| <h3 class="text-lg font-medium text-gray-800 mb-4">Design Options</h3> | |
| <!-- Colors --> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">QR Color</label> | |
| <div class="flex items-center gap-3"> | |
| <input type="color" id="qrColor" value="#000000" class="color-picker"> | |
| <span id="qrColorHex" class="text-sm font-mono bg-gray-100 px-2 py-1 rounded">#000000</span> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Background Color</label> | |
| <div class="flex items-center gap-3"> | |
| <input type="color" id="bgColor" value="#ffffff" class="color-picker"> | |
| <span id="bgColorHex" class="text-sm font-mono bg-gray-100 px-2 py-1 rounded">#ffffff</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Custom Icon --> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">Custom Icon</label> | |
| <div class="flex items-center gap-3"> | |
| <input type="file" id="logoFile" accept="image/*" class="custom-file-input"> | |
| <button type="button" id="removeLogo" class="text-sm text-red-600 hover:text-red-800 hidden"> | |
| <i class="fas fa-trash-alt"></i> Remove | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Icon Size and Position --> | |
| <div class="grid grid-cols-2 gap-4 mb-4" id="logoOptions" style="display: none;"> | |
| <div> | |
| <label for="logoSize" class="block text-sm font-medium text-gray-700 mb-2">Icon Size (%)</label> | |
| <input type="range" id="logoSize" min="5" max="30" value="15" class="w-full"> | |
| <div class="flex justify-between"> | |
| <span class="text-xs text-gray-500">5%</span> | |
| <span class="text-xs text-gray-500" id="logoSizeValue">15%</span> | |
| <span class="text-xs text-gray-500">30%</span> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="logoBorderRadius" class="block text-sm font-medium text-gray-700 mb-2">Icon Radius</label> | |
| <select id="logoBorderRadius" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="0">Sharp</option> | |
| <option value="5">Slightly rounded</option> | |
| <option value="15" selected>Rounded</option> | |
| <option value="50">Circle</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- QR Style --> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">QR Style</label> | |
| <div class="flex gap-3"> | |
| <button type="button" data-style="square" onclick="setQRStyle(this)" class="qr-style-btn active:scale-95 px-4 py-2 border rounded-lg font-medium flex items-center justify-center gap-2 bg-blue-100 border-blue-300"> | |
| <i class="fas fa-square-full"></i> | |
| Squares | |
| </button> | |
| <button type="button" data-style="dots" onclick="setQRStyle(this)" class="qr-style-btn active:scale-95 px-4 py-2 border rounded-lg font-medium flex items-center justify-center gap-2"> | |
| <i class="fas fa-circle"></i> | |
| Dots | |
| </button> | |
| <button type="button" data-style="rounded" onclick="setQRStyle(this)" class="qr-style-btn active:scale-95 px-4 py-2 border rounded-lg font-medium flex items-center justify-center gap-2"> | |
| <i class="fas fa-circle-notch"></i> | |
| Rounded | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Error Correction --> | |
| <div> | |
| <label for="errorCorrection" class="block text-sm font-medium text-gray-700 mb-2">Error Correction Level</label> | |
| <select id="errorCorrection" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="L">Low (7%)</option> | |
| <option value="M" selected>Medium (15%)</option> | |
| <option value="Q">Quartile (25%)</option> | |
| <option value="H">High (30%)</option> | |
| </select> | |
| <p class="mt-1 text-xs text-gray-500">Higher levels make the QR code more resistant to damage but increase complexity.</p> | |
| </div> | |
| </div> | |
| <!-- Generate Button --> | |
| <div> | |
| <button type="button" onclick="generateQRCode()" class="w-full flex justify-center items-center py-3 px-4 border border-transparent rounded-md shadow-sm text-lg font-medium text-white bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all"> | |
| <i class="fas fa-qrcode mr-2"></i> Generate QR Code | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| <!-- QR Code Preview --> | |
| <div class="w-full lg:w-1/2"> | |
| <div class="bg-white rounded-xl shadow-md p-6 sticky top-4"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-2xl font-semibold text-gray-800">QR Code Preview</h2> | |
| <div id="downloadBtns" class="hidden"> | |
| <button id="downloadPNG" onclick="downloadQR('png')" class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm font-medium hover:bg-gray-50 flex items-center gap-1"> | |
| <i class="fas fa-download text-blue-600"></i> PNG | |
| </button> | |
| </div> | |
| </div> | |
| <div id="qrPreview" class="flex flex-col items-center justify-center p-8 border-2 border-dashed border-gray-300 rounded-lg qr-placeholder min-h-[300px]"> | |
| <i class="fas fa-qrcode text-5xl text-gray-400 mb-4"></i> | |
| <p class="text-gray-500 text-center">Your custom QR code will appear here.<br>Configure options on the left and click "Generate".</p> | |
| </div> | |
| <div id="qrResult" class="hidden mt-6"> | |
| <canvas id="previewCanvas" class="mx-auto"></canvas> | |
| <div class="mt-6 space-y-4"> | |
| <div class="flex gap-2 justify-center"> | |
| <button id="downloadSVG" onclick="downloadQR('svg')" class="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 flex items-center gap-2"> | |
| <i class="fas fa-download"></i> SVG | |
| </button> | |
| <button id="downloadJPEG" onclick="downloadQR('jpeg')" class="px-4 py-2 bg-green-600 text-white rounded-lg font-medium hover:bg-green-700 flex items-center gap-2"> | |
| <i class="fas fa-download"></i> JPEG | |
| </button> | |
| <button id="downloadPDF" onclick="downloadQR('pdf')" class="px-4 py-2 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 flex items-center gap-2"> | |
| <i class="fas fa-download"></i> PDF | |
| </button> | |
| </div> | |
| <div class="bg-blue-50 p-3 rounded-lg text-blue-800 text-sm flex items-start gap-2"> | |
| <i class="fas fa-info-circle mt-0.5"></i> | |
| <div> | |
| <strong>Tip:</strong> Test your QR code with your phone camera before sharing. If you added a logo, make sure the QR remains scannable. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- How To Use Section --> | |
| <section class="mt-16 bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800 mb-4">How To Use</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <div class="flex items-center gap-3 mb-3"> | |
| <div class="bg-blue-100 w-8 h-8 rounded-full flex items-center justify-center text-blue-700"> | |
| <span class="font-bold">1</span> | |
| </div> | |
| <h3 class="font-medium text-gray-800">Enter Content</h3> | |
| </div> | |
| <p class="text-gray-600 text-sm">Choose what type of content you want to encode (URL, text, WiFi, or contact info).</p> | |
| </div> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <div class="flex items-center gap-3 mb-3"> | |
| <div class="bg-blue-100 w-8 h-8 rounded-full flex items-center justify-center text-blue-700"> | |
| <span class="font-bold">2</span> | |
| </div> | |
| <h3 class="font-medium text-gray-800">Customize Design</h3> | |
| </div> | |
| <p class="text-gray-600 text-sm">Adjust colors, add a logo, change the style, or set error correction level.</p> | |
| </div> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <div class="flex items-center gap-3 mb-3"> | |
| <div class="bg-blue-100 w-8 h-8 rounded-full flex items-center justify-center text-blue-700"> | |
| <span class="font-bold">3</span> | |
| </div> | |
| <h3 class="font-medium text-gray-800">Download & Share</h3> | |
| </div> | |
| <p class="text-gray-600 text-sm">Generate and download your QR code in multiple formats (PNG, SVG, JPEG, PDF).</p> | |
| </div> | |
| </div> | |
| </section> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js"></script> | |
| <script> | |
| // Variables | |
| let activeType = "url"; | |
| let qrStyle = "square"; | |
| let currentLogo = null; | |
| let qrCodeDataURL = null; | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Set up color pickers | |
| document.getElementById('qrColor').addEventListener('input', function() { | |
| document.getElementById('qrColorHex').textContent = this.value; | |
| }); | |
| document.getElementById('bgColor').addEventListener('input', function() { | |
| document.getElementById('bgColorHex').textContent = this.value; | |
| }); | |
| // Set up logo upload | |
| document.getElementById('logoFile').addEventListener('change', function(e) { | |
| if (e.target.files && e.target.files[0]) { | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| currentLogo = event.target.result; | |
| document.getElementById('logoOptions').style.display = 'grid'; | |
| document.getElementById('removeLogo').classList.remove('hidden'); | |
| }; | |
| reader.readAsDataURL(e.target.files[0]); | |
| } | |
| }); | |
| document.getElementById('removeLogo').addEventListener('click', function() { | |
| currentLogo = null; | |
| document.getElementById('logoOptions').style.display = 'none'; | |
| document.getElementById('logoFile').value = ''; | |
| this.classList.add('hidden'); | |
| if (document.getElementById('qrResult').classList.contains('hidden') === false) { | |
| generateQRCode(); | |
| } | |
| }); | |
| // Live preview updates for certain controls | |
| const liveUpdateElements = ['logoSize', 'logoBorderRadius', 'qrColor', 'bgColor', 'errorCorrection']; | |
| liveUpdateElements.forEach(id => { | |
| document.getElementById(id).addEventListener('input', function() { | |
| if (this.id === 'logoSize') { | |
| document.getElementById('logoSizeValue').textContent = this.value + '%'; | |
| } | |
| if (document.getElementById('qrResult').classList.contains('hidden') === false) { | |
| generateQRCode(); | |
| } | |
| }); | |
| }); | |
| }); | |
| // Set active content type | |
| function setActiveType(button) { | |
| activeType = button.getAttribute('data-type'); | |
| // Update active button state | |
| document.querySelectorAll('.qr-type-btn').forEach(btn => { | |
| btn.classList.remove('bg-blue-100', 'border-blue-300'); | |
| }); | |
| button.classList.add('bg-blue-100', 'border-blue-300'); | |
| // Show the correct input section | |
| document.querySelectorAll('.content-input').forEach(section => { | |
| section.classList.add('hidden'); | |
| }); | |
| document.getElementById(activeType + 'Input').classList.remove('hidden'); | |
| } | |
| // Set QR style | |
| function setQRStyle(button) { | |
| qrStyle = button.getAttribute('data-style'); | |
| // Update active button state | |
| document.querySelectorAll('.qr-style-btn').forEach(btn => { | |
| btn.classList.remove('bg-blue-100', 'border-blue-300'); | |
| }); | |
| button.classList.add('bg-blue-100', 'border-blue-300'); | |
| if (document.getElementById('qrResult').classList.contains('hidden') === false) { | |
| generateQRCode(); | |
| } | |
| } | |
| // Generate QR code | |
| function generateQRCode() { | |
| let content = ''; | |
| // Get content based on active type | |
| switch(activeType) { | |
| case 'url': | |
| content = document.getElementById('qrContent').value; | |
| if (!content) { | |
| showError("Please enter a URL"); | |
| return; | |
| } | |
| if (!content.startsWith('http://') && !content.startsWith('https://')) { | |
| content = 'https://' + content; | |
| } | |
| break; | |
| case 'text': | |
| content = document.getElementById('qrTextContent').value; | |
| if (!content) { | |
| showError("Please enter some text"); | |
| return; | |
| } | |
| break; | |
| case 'wifi': | |
| const ssid = document.getElementById('wifiSsid').value; | |
| if (!ssid) { | |
| showError("Please enter a WiFi network name"); | |
| return; | |
| } | |
| const password = document.getElementById('wifiPassword').value || ''; | |
| const security = document.getElementById('wifiSecurity').value; | |
| if (security) { | |
| content = `WIFI:T:${security};S:${ssid};P:${password};;`; | |
| } else { | |
| content = `WIFI:T:nopass;S:${ssid};;`; | |
| } | |
| break; | |
| case 'contact': | |
| const name = document.getElementById('contactName').value || ''; | |
| const phone = document.getElementById('contactPhone').value || ''; | |
| const email = document.getElementById('contactEmail').value || ''; | |
| if (!name && !phone && !email) { | |
| showError("Please enter at least one contact field"); | |
| return; | |
| } | |
| content = 'BEGIN:VCARD\nVERSION:3.0\n'; | |
| if (name) content += `FN:${name}\nN:${name.split(' ').reverse().join(';')};;;\n`; | |
| if (phone) content += `TEL:${phone}\n`; | |
| if (email) content += `EMAIL:${email}\n`; | |
| content += 'END:VCARD'; | |
| break; | |
| case 'email': | |
| const emailTo = document.getElementById('emailTo').value; | |
| const emailSubject = document.getElementById('emailSubject').value || ''; | |
| const emailBody = document.getElementById('emailBody').value || ''; | |
| if (!emailTo) { | |
| showError("Please enter a recipient email address"); | |
| return; | |
| } | |
| content = `mailto:${emailTo}`; | |
| if (emailSubject || emailBody) { | |
| content += '?'; | |
| if (emailSubject) content += `subject=${encodeURIComponent(emailSubject)}`; | |
| if (emailSubject && emailBody) content += '&'; | |
| if (emailBody) content += `body=${encodeURIComponent(emailBody)}`; | |
| } | |
| break; | |
| case 'sms': | |
| const smsNumber = document.getElementById('smsNumber').value; | |
| const smsMessage = document.getElementById('smsMessage').value || ''; | |
| if (!smsNumber) { | |
| showError("Please enter a phone number"); | |
| return; | |
| } | |
| content = `smsto:${smsNumber}`; | |
| if (smsMessage) { | |
| content += `:${encodeURIComponent(smsMessage)}`; | |
| } | |
| break; | |
| case 'phone': | |
| const phoneNumber = document.getElementById('phoneNumber').value; | |
| if (!phoneNumber) { | |
| showError("Please enter a phone number"); | |
| return; | |
| } | |
| content = `tel:${phoneNumber}`; | |
| break; | |
| case 'location': | |
| const latitude = document.getElementById('locationLatitude').value; | |
| const longitude = document.getElementById('locationLongitude').value; | |
| if (!latitude || !longitude) { | |
| showError("Please enter both latitude and longitude coordinates"); | |
| return; | |
| } | |
| content = `geo:${latitude},${longitude}`; | |
| break; | |
| } | |
| // Get design options | |
| const qrColor = document.getElementById('qrColor').value; | |
| const bgColor = document.getElementById('bgColor').value; | |
| const errorCorrection = document.getElementById('errorCorrection').value; | |
| const logoBorderRadius = document.getElementById('logoBorderRadius').value; | |
| const logoSize = parseInt(document.getElementById('logoSize').value) / 100; | |
| // Clear any previous errors | |
| document.querySelectorAll('.error-message').forEach(el => el.remove()); | |
| // Hide placeholder and show result container | |
| document.getElementById('qrPreview').classList.add('hidden'); | |
| document.getElementById('qrResult').classList.remove('hidden'); | |
| document.getElementById('downloadBtns').classList.remove('hidden'); | |
| // Generate QR code with options | |
| const canvas = document.getElementById('previewCanvas'); | |
| // Apply style options | |
| let dotOptions = { type: 'square' }; | |
| if (qrStyle === 'dots') { | |
| dotOptions = { type: 'dots' }; | |
| } else if (qrStyle === 'rounded') { | |
| dotOptions = { type: 'rounded' }; | |
| } | |
| QRCode.toCanvas(canvas, content, { | |
| width: 300, | |
| color: { | |
| dark: qrColor, | |
| light: bgColor | |
| }, | |
| errorCorrectionLevel: errorCorrection, | |
| margin: 1, | |
| ...dotOptions | |
| }, function(error) { | |
| if (error) { | |
| showError("Failed to generate QR code: " + error.message); | |
| return; | |
| } | |
| qrCodeDataURL = canvas.toDataURL('image/png'); | |
| // Add logo if provided | |
| if (currentLogo) { | |
| addLogoToQR(canvas, currentLogo, logoSize, logoBorderRadius); | |
| } | |
| }); | |
| } | |
| // Add logo to QR code | |
| function addLogoToQR(canvas, logoData, sizePercent, borderRadius) { | |
| const ctx = canvas.getContext('2d'); | |
| // Create a temporary canvas to draw the rounded logo | |
| const logoCanvas = document.createElement('canvas'); | |
| const logoCtx = logoCanvas.getContext('2d'); | |
| const logoImg = new Image(); | |
| logoImg.onload = function() { | |
| // Logo dimensions (15-30% of QR code size) | |
| const logoSize = canvas.width * sizePercent; | |
| const center = canvas.width / 2; | |
| // Draw rounded logo on temp canvas | |
| logoCanvas.width = logoSize; | |
| logoCanvas.height = logoSize; | |
| // Create rounded path | |
| logoCtx.beginPath(); | |
| logoCtx.moveTo(borderRadius, 0); | |
| logoCtx.lineTo(logoSize - borderRadius, 0); | |
| logoCtx.quadraticCurveTo(logoSize, 0, logoSize, borderRadius); | |
| logoCtx.lineTo(logoSize, logoSize - borderRadius); | |
| logoCtx.quadraticCurveTo(logoSize, logoSize, logoSize - borderRadius, logoSize); | |
| logoCtx.lineTo(borderRadius, logoSize); | |
| logoCtx.quadraticCurveTo(0, logoSize, 0, logoSize - borderRadius); | |
| logoCtx.lineTo(0, borderRadius); | |
| logoCtx.quadraticCurveTo(0, 0, borderRadius, 0); | |
| logoCtx.closePath(); | |
| logoCtx.clip(); | |
| // Draw logo image | |
| logoCtx.drawImage(logoImg, 0, 0, logoSize, logoSize); | |
| // Draw temp canvas on QR code | |
| ctx.drawImage(logoCanvas, center - logoSize/2, center - logoSize/2); | |
| }; | |
| logoImg.src = logoData; | |
| } | |
| // Download QR code | |
| function downloadQR(format) { | |
| if (!qrCodeDataURL) { | |
| showError("Please generate a QR code first"); | |
| return; | |
| } | |
| const canvas = document.getElementById('previewCanvas'); | |
| const link = document.createElement('a'); | |
| let filename = 'qr-code'; | |
| switch(activeType) { | |
| case 'url': | |
| const url = document.getElementById('qrContent').value; | |
| filename = url.replace(/^https?:\/\//, '').split('/')[0] || 'qr-code'; | |
| break; | |
| case 'text': | |
| filename = 'qr-code-text'; | |
| break; | |
| case 'wifi': | |
| filename = 'wifi-' + document.getElementById('wifiSsid').value; | |
| break; | |
| case 'contact': | |
| const name = document.getElementById('contactName').value || 'contact'; | |
| filename = name.toLowerCase().replace(/\s+/g, '-'); | |
| break; | |
| } | |
| switch(format) { | |
| case 'png': | |
| link.href = canvas.toDataURL('image/png'); | |
| link.download = filename + '.png'; | |
| break; | |
| case 'jpeg': | |
| link.href = canvas.toDataURL('image/jpeg', 0.92); | |
| link.download = filename + '.jpg'; | |
| break; | |
| case 'svg': | |
| // For SVG, we'll recreate the QR code as SVG | |
| let content = ''; | |
| switch(activeType) { | |
| case 'url': content = document.getElementById('qrContent').value; break; | |
| case 'text': content = document.getElementById('qrTextContent').value; break; | |
| case 'wifi': | |
| const ssid = document.getElementById('wifiSsid').value; | |
| const password = document.getElementById('wifiPassword').value || ''; | |
| const security = document.getElementById('wifiSecurity').value; | |
| content = security ? `WIFI:T:${security};S:${ssid};P:${password};;` : `WIFI:T:nopass;S:${ssid};;`; | |
| break; | |
| case 'contact': | |
| const nameC = document.getElementById('contactName').value || ''; | |
| const phone = document.getElementById('contactPhone').value || ''; | |
| const email = document.getElementById('contactEmail').value || ''; | |
| content = 'BEGIN:VCARD\nVERSION:3.0\n'; | |
| if (nameC) content += `FN:${nameC}\nN:${nameC.split(' ').reverse().join(';')};;;\n`; | |
| if (phone) content += `TEL:${phone}\n`; | |
| if (email) content += `EMAIL:${email}\n`; | |
| content += 'END:VCARD'; | |
| break; | |
| case 'email': | |
| content = document.getElementById('emailTo').value; | |
| break; | |
| case 'sms': | |
| content = document.getElementById('smsNumber').value; | |
| break; | |
| case 'phone': | |
| content = document.getElementById('phoneNumber').value; | |
| break; | |
| case 'location': | |
| content = `${document.getElementById('locationLatitude').value},${document.getElementById('locationLongitude').value}`; | |
| break; | |
| } | |
| QRCode.toString(content, { | |
| type: 'svg', | |
| color: { | |
| dark: document.getElementById('qrColor').value, | |
| light: document.getElementById('bgColor').value | |
| }, | |
| errorCorrectionLevel: document.getElementById('errorCorrection').value, | |
| margin: 1 | |
| }, function(err, svg) { | |
| if (err) { | |
| showError("Failed to generate SVG: " + err.message); | |
| return; | |
| } | |
| const blob = new Blob([svg], {type: 'image/svg+xml'}); | |
| link.href = URL.createObjectURL(blob); | |
| link.download = filename + '.svg'; | |
| link.click(); | |
| URL.revokeObjectURL(link.href); | |
| }); | |
| return; | |
| case 'pdf': | |
| const { jsPDF } = window.jspdf; | |
| const pdf = new jsPDF({ | |
| orientation: 'portrait', | |
| unit: 'mm' | |
| }); | |
| // Add QR code image | |
| const qrWidth = 50; // mm | |
| const qrHeight = (canvas.height / canvas.width) * qrWidth; | |
| pdf.addImage(canvas, 'PNG', | |
| (pdf.internal.pageSize.getWidth() - qrWidth) / 2, | |
| (pdf.internal.pageSize.getHeight() - qrHeight) / 2 - 10, | |
| qrWidth, qrHeight); | |
| // Add content text | |
| let pdfContent = ''; | |
| switch(activeType) { | |
| case 'url': pdfContent = document.getElementById('qrContent').value; break; | |
| case 'text': pdfContent = document.getElementById('qrTextContent').value; break; | |
| case 'wifi': | |
| pdfContent = `WiFi Network: ${document.getElementById('wifiSsid').value}\n`; | |
| if (document.getElementById('wifiPassword').value) { | |
| pdfContent += `Password: ${document.getElementById('wifiPassword').value}\n`; | |
| } | |
| break; | |
| case 'contact': | |
| if (document.getElementById('contactName').value) { | |
| pdfContent += `Name: ${document.getElementById('contactName').value}\n`; | |
| } | |
| if (document.getElementById('contactPhone').value) { | |
| pdfContent += `Phone: ${document.getElementById('contactPhone').value}\n`; | |
| } | |
| if (document.getElementById('contactEmail').value) { | |
| pdfContent += `Email: ${document.getElementById('contactEmail').value}\n`; | |
| } | |
| break; | |
| case 'email': | |
| pdfContent += `To: ${document.getElementById('emailTo').value}\n`; | |
| if (document.getElementById('emailSubject').value) { | |
| pdfContent += `Subject: ${document.getElementById('emailSubject').value}\n`; | |
| } | |
| if (document.getElementById('emailBody').value) { | |
| pdfContent += `Message: ${document.getElementById('emailBody').value}\n`; | |
| } | |
| break; | |
| case 'sms': | |
| pdfContent += `To: ${document.getElementById('smsNumber').value}\n`; | |
| if (document.getElementById('smsMessage').value) { | |
| pdfContent += `Message: ${document.getElementById('smsMessage').value}\n`; | |
| } | |
| break; | |
| case 'phone': | |
| pdfContent += `Phone: ${document.getElementById('phoneNumber').value}\n`; | |
| break; | |
| case 'location': | |
| pdfContent += `Latitude: ${document.getElementById('locationLatitude').value}\n`; | |
| pdfContent += `Longitude: ${document.getElementById('locationLongitude').value}\n`; | |
| break; | |
| } | |
| if (pdfContent) { | |
| pdf.setFontSize(10); | |
| pdf.setTextColor(100); | |
| pdf.text(pdfContent, | |
| pdf.internal.pageSize.getWidth() / 2, | |
| (pdf.internal.pageSize.getHeight() - qrHeight) / 2 + qrHeight + 10, | |
| { align: 'center' } | |
| ); | |
| } | |
| pdf.save(filename + '.pdf'); | |
| return; | |
| } | |
| link.click(); | |
| } | |
| // Show error message | |
| function showError(message) { | |
| // Remove any existing error messages | |
| document.querySelectorAll('.error-message').forEach(el => el.remove()); | |
| const errorEl = document.createElement('div'); | |
| errorEl.className = 'error-message bg-red-50 text-red-700 p-3 text-sm rounded-lg mb-4 flex items-start gap-2'; | |
| errorEl.innerHTML = `<i class="fas fa-exclamation-circle mt-0.5"></i><span>${message}</span>`; | |
| const form = document.getElementById('qrForm'); | |
| form.insertBefore(errorEl, form.firstChild); | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=martiantrader/qrcodegenerator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |