qr-code-generator / script.js
nguyenthanhasia's picture
Upload script.js with huggingface_hub
54dda09 verified
// 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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
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!');
});