Download script.js from nguyenthanhasia/qr-code-generator: direct link, hf CLI and curl.
- Browser
- Download file 16.1 kB
-
https://huggingface.co/spaces/nguyenthanhasia/qr-code-generator/resolve/main/script.js
- Command line
-
hf download hf://spaces/nguyenthanhasia/qr-code-generator/script.js
-
curl -L -o script.js https://huggingface.co/spaces/nguyenthanhasia/qr-code-generator/resolve/main/script.js
16.1 kB
| // QR Code Generator Application | |
| class QRGenerator { | |
| constructor() { | |
| this.currentType = 'url'; | |
| this.currentQRUrl = null; | |
| this.init(); | |
| } | |
| init() { | |
| this.bindEvents(); | |
| this.updateSizeDisplay(); | |
| this.setActiveTab('url'); | |
| } | |
| bindEvents() { | |
| // Tab switching | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| const type = e.currentTarget.dataset.type; | |
| this.setActiveTab(type); | |
| }); | |
| }); | |
| // Size slider | |
| const sizeSlider = document.getElementById('qr-size'); | |
| sizeSlider.addEventListener('input', () => { | |
| this.updateSizeDisplay(); | |
| }); | |
| // Generate button | |
| document.getElementById('generate-btn').addEventListener('click', () => { | |
| this.generateQRCode(); | |
| }); | |
| // Download button | |
| document.getElementById('download-btn').addEventListener('click', () => { | |
| this.downloadQRCode(); | |
| }); | |
| // Real-time input changes | |
| this.bindInputEvents(); | |
| // Color changes - removed auto-generation | |
| // Users need to click "Generate QR Code" to apply color changes | |
| } | |
| bindInputEvents() { | |
| // Clear QR code when input changes, but don't auto-generate | |
| // URL input | |
| document.getElementById('url-input').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| // Text input | |
| document.getElementById('text-input').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| // Email inputs | |
| document.getElementById('email-input').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| document.getElementById('email-subject').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| document.getElementById('email-body').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| // Phone input | |
| document.getElementById('phone-input').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| // WiFi inputs | |
| document.getElementById('wifi-ssid').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| document.getElementById('wifi-password').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| document.getElementById('wifi-security').addEventListener('change', () => { | |
| this.clearPreview(); | |
| }); | |
| document.getElementById('wifi-hidden').addEventListener('change', () => { | |
| this.clearPreview(); | |
| }); | |
| // SMS inputs | |
| document.getElementById('sms-number').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| document.getElementById('sms-message').addEventListener('input', () => { | |
| this.clearPreview(); | |
| }); | |
| } | |
| setActiveTab(type) { | |
| this.currentType = type; | |
| // Update tab buttons | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| document.querySelector(`[data-type="${type}"]`).classList.add('active'); | |
| // Update content forms | |
| document.querySelectorAll('.content-form').forEach(form => { | |
| form.classList.remove('active'); | |
| }); | |
| document.getElementById(`${type}-form`).classList.add('active'); | |
| // Clear current QR code | |
| this.clearPreview(); | |
| } | |
| updateSizeDisplay() { | |
| const size = document.getElementById('qr-size').value; | |
| document.querySelector('.size-value').textContent = `${size}px`; | |
| } | |
| autoGenerate() { | |
| const content = this.getContentData(); | |
| if (content && content.trim()) { | |
| this.generateQRCode(); | |
| } else { | |
| this.clearPreview(); | |
| } | |
| } | |
| getContentData() { | |
| switch (this.currentType) { | |
| case 'url': | |
| return document.getElementById('url-input').value; | |
| case 'text': | |
| return document.getElementById('text-input').value; | |
| case 'email': | |
| const email = document.getElementById('email-input').value; | |
| const subject = document.getElementById('email-subject').value; | |
| const body = document.getElementById('email-body').value; | |
| if (!email) return ''; | |
| let mailto = `mailto:${email}`; | |
| const params = []; | |
| if (subject) params.push(`subject=${encodeURIComponent(subject)}`); | |
| if (body) params.push(`body=${encodeURIComponent(body)}`); | |
| if (params.length > 0) mailto += `?${params.join('&')}`; | |
| return mailto; | |
| case 'phone': | |
| const phone = document.getElementById('phone-input').value; | |
| return phone ? `tel:${phone}` : ''; | |
| case 'wifi': | |
| const ssid = document.getElementById('wifi-ssid').value; | |
| const password = document.getElementById('wifi-password').value; | |
| const security = document.getElementById('wifi-security').value; | |
| const hidden = document.getElementById('wifi-hidden').checked; | |
| if (!ssid) return ''; | |
| return `WIFI:T:${security};S:${ssid};P:${password};H:${hidden ? 'true' : 'false'};;`; | |
| case 'sms': | |
| const smsNumber = document.getElementById('sms-number').value; | |
| const smsMessage = document.getElementById('sms-message').value; | |
| if (!smsNumber) return ''; | |
| let sms = `sms:${smsNumber}`; | |
| if (smsMessage) sms += `?body=${encodeURIComponent(smsMessage)}`; | |
| return sms; | |
| default: | |
| return ''; | |
| } | |
| } | |
| async generateQRCode() { | |
| const content = this.getContentData(); | |
| if (!content || !content.trim()) { | |
| this.showToast('Please enter content to generate QR code', 'error'); | |
| return; | |
| } | |
| this.showLoading(true); | |
| try { | |
| const qrUrl = this.buildQRUrl(content); | |
| await this.displayQRCode(qrUrl); | |
| this.currentQRUrl = qrUrl; | |
| // Enable download button | |
| document.getElementById('download-btn').disabled = false; | |
| this.showToast('QR code generated successfully!', 'success'); | |
| } catch (error) { | |
| console.error('Error generating QR code:', error); | |
| this.showToast('Failed to generate QR code. Please try again.', 'error'); | |
| } finally { | |
| this.showLoading(false); | |
| } | |
| } | |
| buildQRUrl(content) { | |
| const size = document.getElementById('qr-size').value; | |
| const fgColor = document.getElementById('fg-color').value.replace('#', ''); | |
| const bgColor = document.getElementById('bg-color').value.replace('#', ''); | |
| const errorCorrection = document.getElementById('error-correction').value; | |
| const format = document.getElementById('output-format').value; | |
| const params = new URLSearchParams({ | |
| cht: 'qr', | |
| chs: `${size}x${size}`, | |
| chl: content, | |
| choe: 'UTF-8', | |
| chld: `${errorCorrection}|2`, | |
| icqrf: fgColor, | |
| icqrb: bgColor | |
| }); | |
| if (format === 'svg') { | |
| params.append('chof', '.svg'); | |
| } | |
| return `https://image-charts.com/chart?${params.toString()}`; | |
| } | |
| async displayQRCode(url) { | |
| return new Promise((resolve, reject) => { | |
| const img = new Image(); | |
| img.onload = () => { | |
| const preview = document.getElementById('qr-preview'); | |
| preview.innerHTML = ''; | |
| preview.appendChild(img); | |
| preview.classList.add('has-qr'); | |
| resolve(); | |
| }; | |
| img.onerror = () => { | |
| reject(new Error('Failed to load QR code image')); | |
| }; | |
| img.src = url; | |
| img.alt = 'Generated QR Code'; | |
| img.style.maxWidth = '100%'; | |
| img.style.height = 'auto'; | |
| }); | |
| } | |
| clearPreview() { | |
| const preview = document.getElementById('qr-preview'); | |
| preview.innerHTML = ` | |
| <div class="placeholder"> | |
| <i class="fas fa-qrcode"></i> | |
| <p>Enter content to generate QR code</p> | |
| </div> | |
| `; | |
| preview.classList.remove('has-qr'); | |
| // Disable download button | |
| document.getElementById('download-btn').disabled = true; | |
| this.currentQRUrl = null; | |
| } | |
| async downloadQRCode() { | |
| if (!this.currentQRUrl) { | |
| this.showToast('No QR code to download', 'error'); | |
| return; | |
| } | |
| try { | |
| const format = document.getElementById('output-format').value; | |
| const response = await fetch(this.currentQRUrl); | |
| const blob = await response.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `qrcode.${format}`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| window.URL.revokeObjectURL(url); | |
| this.showToast('QR code downloaded successfully!', 'success'); | |
| } catch (error) { | |
| console.error('Error downloading QR code:', error); | |
| this.showToast('Failed to download QR code', 'error'); | |
| } | |
| } | |
| showLoading(show) { | |
| const overlay = document.getElementById('loading-overlay'); | |
| if (show) { | |
| overlay.classList.add('show'); | |
| } else { | |
| overlay.classList.remove('show'); | |
| } | |
| } | |
| showToast(message, type = 'info') { | |
| const container = document.getElementById('toast-container'); | |
| const toast = document.createElement('div'); | |
| toast.className = `toast ${type}`; | |
| const icon = type === 'success' ? 'fas fa-check-circle' : | |
| type === 'error' ? 'fas fa-exclamation-circle' : | |
| 'fas fa-info-circle'; | |
| toast.innerHTML = ` | |
| <i class="${icon}"></i> | |
| <span>${message}</span> | |
| `; | |
| container.appendChild(toast); | |
| // Auto remove after 3 seconds | |
| setTimeout(() => { | |
| if (toast.parentNode) { | |
| toast.parentNode.removeChild(toast); | |
| } | |
| }, 3000); | |
| } | |
| debounce(func, wait) { | |
| let timeout; | |
| return function executedFunction(...args) { | |
| const later = () => { | |
| clearTimeout(timeout); | |
| func(...args); | |
| }; | |
| clearTimeout(timeout); | |
| timeout = setTimeout(later, wait); | |
| }; | |
| } | |
| // Validation methods | |
| isValidUrl(string) { | |
| try { | |
| new URL(string); | |
| return true; | |
| } catch (_) { | |
| return false; | |
| } | |
| } | |
| isValidEmail(email) { | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| return emailRegex.test(email); | |
| } | |
| isValidPhone(phone) { | |
| const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/; | |
| return phoneRegex.test(phone.replace(/[\s\-\(\)]/g, '')); | |
| } | |
| // Enhanced validation for different content types | |
| validateContent() { | |
| const content = this.getContentData(); | |
| switch (this.currentType) { | |
| case 'url': | |
| if (content && !this.isValidUrl(content)) { | |
| this.showToast('Please enter a valid URL', 'error'); | |
| return false; | |
| } | |
| break; | |
| case 'email': | |
| const email = document.getElementById('email-input').value; | |
| if (email && !this.isValidEmail(email)) { | |
| this.showToast('Please enter a valid email address', 'error'); | |
| return false; | |
| } | |
| break; | |
| case 'phone': | |
| const phone = document.getElementById('phone-input').value; | |
| if (phone && !this.isValidPhone(phone)) { | |
| this.showToast('Please enter a valid phone number', 'error'); | |
| return false; | |
| } | |
| break; | |
| case 'sms': | |
| const smsPhone = document.getElementById('sms-number').value; | |
| if (smsPhone && !this.isValidPhone(smsPhone)) { | |
| this.showToast('Please enter a valid phone number', 'error'); | |
| return false; | |
| } | |
| break; | |
| } | |
| return true; | |
| } | |
| } | |
| // Utility functions | |
| function formatPhoneNumber(phone) { | |
| // Remove all non-digit characters except + | |
| const cleaned = phone.replace(/[^\d\+]/g, ''); | |
| return cleaned; | |
| } | |
| function escapeHtml(text) { | |
| const map = { | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }; | |
| return text.replace(/[&<>"']/g, m => map[m]); | |
| } | |
| // Initialize the application when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const qrGenerator = new QRGenerator(); | |
| // Add some sample data for demonstration | |
| const urlInput = document.getElementById('url-input'); | |
| if (urlInput) { | |
| urlInput.placeholder = 'https://example.com'; | |
| } | |
| // Add keyboard shortcuts | |
| document.addEventListener('keydown', (e) => { | |
| // Ctrl/Cmd + Enter to generate | |
| if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { | |
| e.preventDefault(); | |
| qrGenerator.generateQRCode(); | |
| } | |
| // Ctrl/Cmd + D to download | |
| if ((e.ctrlKey || e.metaKey) && e.key === 'd') { | |
| e.preventDefault(); | |
| if (!document.getElementById('download-btn').disabled) { | |
| qrGenerator.downloadQRCode(); | |
| } | |
| } | |
| }); | |
| // Add focus management for better accessibility | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| btn.click(); | |
| } | |
| }); | |
| }); | |
| // Add form validation on submit | |
| document.querySelectorAll('.form-input, .form-textarea').forEach(input => { | |
| input.addEventListener('blur', () => { | |
| qrGenerator.validateContent(); | |
| }); | |
| }); | |
| // Add drag and drop functionality for future logo upload feature | |
| const previewArea = document.getElementById('qr-preview'); | |
| previewArea.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| previewArea.style.borderColor = 'var(--primary-color)'; | |
| }); | |
| previewArea.addEventListener('dragleave', (e) => { | |
| e.preventDefault(); | |
| previewArea.style.borderColor = ''; | |
| }); | |
| // Add print functionality | |
| window.addEventListener('beforeprint', () => { | |
| document.body.classList.add('printing'); | |
| }); | |
| window.addEventListener('afterprint', () => { | |
| document.body.classList.remove('printing'); | |
| }); | |
| // Add service worker for offline functionality (future enhancement) | |
| if ('serviceWorker' in navigator) { | |
| navigator.serviceWorker.register('/sw.js').catch(err => { | |
| console.log('Service worker registration failed:', err); | |
| }); | |
| } | |
| console.log('QR Generator initialized successfully!'); | |
| }); | |