// QR Rapido Speed Generator class QRRapidoGenerator { constructor() { this.startTime = 0; this.currentQR = null; this.timerInterval = null; this.languageStrings = { 'pt-BR': { tagline: 'Gere QR codes em segundos!', generating: 'Gerando...', generated: 'Gerado em', seconds: 's', ultraFast: 'Geração ultra rápida!', fast: 'Geração rápida!', normal: 'Geração normal', error: 'Erro na geração. Tente novamente.', success: 'QR Code salvo no histórico!' }, 'es': { tagline: '¡Genera códigos QR en segundos!', generating: 'Generando...', generated: 'Generado en', seconds: 's', ultraFast: '¡Generación ultra rápida!', fast: '¡Generación rápida!', normal: 'Generación normal', error: 'Error en la generación. Inténtalo de nuevo.', success: '¡Código QR guardado en el historial!' }, 'en': { tagline: 'Generate QR codes in seconds!', generating: 'Generating...', generated: 'Generated in', seconds: 's', ultraFast: 'Ultra fast generation!', fast: 'Fast generation!', normal: 'Normal generation', error: 'Generation error. Please try again.', success: 'QR Code saved to history!' } }; this.currentLang = localStorage.getItem('qrrapido-lang') || 'pt-BR'; this.initializeEvents(); this.checkAdFreeStatus(); this.updateLanguage(); this.updateStatsCounters(); } initializeEvents() { // Form submission with timer const form = document.getElementById('qr-speed-form'); if (form) { form.addEventListener('submit', this.generateQRWithTimer.bind(this)); } // Quick style selection document.querySelectorAll('input[name="quick-style"]').forEach(radio => { radio.addEventListener('change', this.applyQuickStyle.bind(this)); }); // QR type change with hints const qrType = document.getElementById('qr-type'); if (qrType) { qrType.addEventListener('change', this.updateContentHints.bind(this)); } // Language selector document.querySelectorAll('[data-lang]').forEach(link => { link.addEventListener('click', this.changeLanguage.bind(this)); }); // Real-time preview for premium users if (this.isPremiumUser()) { this.setupRealTimePreview(); } // Download buttons this.setupDownloadButtons(); // Share functionality this.setupShareButtons(); // Save to history const saveBtn = document.getElementById('save-to-history'); if (saveBtn) { saveBtn.addEventListener('click', this.saveToHistory.bind(this)); } } setupDownloadButtons() { const pngBtn = document.getElementById('download-png'); const svgBtn = document.getElementById('download-svg'); const pdfBtn = document.getElementById('download-pdf'); if (pngBtn) pngBtn.addEventListener('click', () => this.downloadQR('png')); if (svgBtn) svgBtn.addEventListener('click', () => this.downloadQR('svg')); if (pdfBtn) pdfBtn.addEventListener('click', () => this.downloadQR('pdf')); } setupShareButtons() { // Check if Web Share API is supported and show/hide native share option if (navigator.share && this.isMobileDevice()) { const nativeShareOption = document.getElementById('native-share-option'); if (nativeShareOption) { nativeShareOption.classList.remove('d-none'); } } // Show save to gallery option on mobile if (this.isMobileDevice()) { const saveGalleryOption = document.getElementById('save-gallery-option'); if (saveGalleryOption) { saveGalleryOption.classList.remove('d-none'); } } // Add event listeners to share buttons const shareButtons = { 'native-share': () => this.shareNative(), 'share-whatsapp': () => this.shareWhatsApp(), 'share-telegram': () => this.shareTelegram(), 'share-email': () => this.shareEmail(), 'copy-qr-link': () => this.copyToClipboard(), 'save-to-gallery': () => this.saveToGallery() }; Object.entries(shareButtons).forEach(([id, handler]) => { const button = document.getElementById(id); if (button) { button.addEventListener('click', (e) => { e.preventDefault(); handler(); }); } }); } isMobileDevice() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || (navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform)); } async shareNative() { if (!this.currentQR || !navigator.share) return; try { // Create a blob from the base64 image const base64Response = await fetch(`data:image/png;base64,${this.currentQR.base64}`); const blob = await base64Response.blob(); const file = new File([blob], 'qrcode.png', { type: 'image/png' }); const shareData = { title: 'QR Code - QR Rapido', text: 'QR Code gerado com QR Rapido - o gerador mais rápido do Brasil!', url: window.location.origin, files: [file] }; // Check if files can be shared if (navigator.canShare && navigator.canShare(shareData)) { await navigator.share(shareData); } else { // Fallback without files await navigator.share({ title: shareData.title, text: shareData.text, url: shareData.url }); } this.trackShareEvent('native'); } catch (error) { console.error('Error sharing:', error); if (error.name !== 'AbortError') { this.showError('Erro ao compartilhar. Tente outro método.'); } } } shareWhatsApp() { if (!this.currentQR) return; const text = encodeURIComponent('QR Code gerado com QR Rapido - o gerador mais rápido do Brasil! ' + window.location.origin); const url = `https://wa.me/?text=${text}`; if (this.isMobileDevice()) { window.open(url, '_blank'); } else { window.open(`https://web.whatsapp.com/send?text=${text}`, '_blank'); } this.trackShareEvent('whatsapp'); } shareTelegram() { if (!this.currentQR) return; const text = encodeURIComponent('QR Code gerado com QR Rapido - o gerador mais rápido do Brasil!'); const url = encodeURIComponent(window.location.origin); const telegramUrl = `https://t.me/share/url?url=${url}&text=${text}`; window.open(telegramUrl, '_blank'); this.trackShareEvent('telegram'); } shareEmail() { if (!this.currentQR) return; const subject = encodeURIComponent('QR Code - QR Rapido'); const body = encodeURIComponent(`Olá!\n\nCompartilho com você este QR Code gerado no QR Rapido, o gerador mais rápido do Brasil!\n\nAcesse: ${window.location.origin}\n\nAbraços!`); const mailtoUrl = `mailto:?subject=${subject}&body=${body}`; window.location.href = mailtoUrl; this.trackShareEvent('email'); } async copyToClipboard() { if (!this.currentQR) return; try { const shareText = `QR Code gerado com QR Rapido - ${window.location.origin}`; if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(shareText); } else { // Fallback for older browsers const textArea = document.createElement('textarea'); textArea.value = shareText; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); document.execCommand('copy'); textArea.remove(); } this.showSuccess('Link copiado para a área de transferência!'); this.trackShareEvent('copy'); } catch (error) { console.error('Error copying to clipboard:', error); this.showError('Erro ao copiar link. Tente novamente.'); } } async saveToGallery() { if (!this.currentQR) return; try { // Create a blob from the base64 image const base64Response = await fetch(`data:image/png;base64,${this.currentQR.base64}`); const blob = await base64Response.blob(); // Create download link const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `qrrapido-${new Date().toISOString().slice(0,10)}-${Date.now()}.png`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); this.showSuccess('QR Code baixado! Verifique sua galeria/downloads.'); this.trackShareEvent('gallery'); } catch (error) { console.error('Error saving to gallery:', error); this.showError('Erro ao salvar na galeria. Tente novamente.'); } } trackShareEvent(method) { // Google Analytics if (typeof gtag !== 'undefined') { gtag('event', 'qr_shared', { 'share_method': method, 'user_type': this.isPremiumUser() ? 'premium' : 'free', 'language': this.currentLang }); } // Internal tracking console.log(`QR Code shared via ${method}`); } async generateQRWithTimer(e) { e.preventDefault(); // Validation if (!this.validateForm()) return; // Start timer this.startTime = performance.now(); this.showGenerationStarted(); const formData = this.collectFormData(); try { const response = await fetch('/api/QR/GenerateRapid', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData) }); if (!response.ok) { if (response.status === 429) { this.showUpgradeModal('Limite de QR codes atingido! Upgrade para QR Rapido Premium e gere códigos ilimitados.'); return; } throw new Error('Erro na geração'); } const result = await response.json(); if (!result.success) { throw new Error(result.error || 'Erro desconhecido'); } const generationTime = ((performance.now() - this.startTime) / 1000).toFixed(1); this.displayQRResult(result, generationTime); this.updateSpeedStats(generationTime); this.trackGenerationEvent(formData.type, generationTime); } catch (error) { console.error('Erro ao gerar QR:', error); this.showError(this.languageStrings[this.currentLang].error); } finally { this.hideGenerationLoading(); } } validateForm() { const qrType = document.getElementById('qr-type').value; const qrContent = document.getElementById('qr-content').value.trim(); if (!qrType) { this.showError('Selecione o tipo de QR code'); return false; } if (!qrContent) { this.showError('Digite o conteúdo do QR code'); return false; } if (qrContent.length > 4000) { this.showError('Conteúdo muito longo. Máximo 4000 caracteres.'); return false; } return true; } collectFormData() { const quickStyle = document.querySelector('input[name="quick-style"]:checked')?.value || 'classic'; const styleSettings = this.getStyleSettings(quickStyle); return { type: document.getElementById('qr-type').value, content: document.getElementById('qr-content').value, quickStyle: quickStyle, primaryColor: document.getElementById('primary-color').value, backgroundColor: document.getElementById('bg-color').value, size: parseInt(document.getElementById('qr-size').value), margin: parseInt(document.getElementById('qr-margin').value), cornerStyle: document.getElementById('corner-style')?.value || 'square', optimizeForSpeed: true, language: this.currentLang, ...styleSettings }; } getStyleSettings(style) { const styles = { classic: { primaryColor: '#000000', backgroundColor: '#FFFFFF' }, modern: { primaryColor: '#007BFF', backgroundColor: '#F8F9FA' }, colorful: { primaryColor: '#FF6B35', backgroundColor: '#FFF3E0' } }; return styles[style] || styles.classic; } displayQRResult(result, generationTime) { const previewDiv = document.getElementById('qr-preview'); if (!previewDiv) return; previewDiv.innerHTML = ` QR Code gerado em ${generationTime}s `; // Show generation statistics this.showGenerationStats(generationTime); // Show download buttons const downloadSection = document.getElementById('download-section'); if (downloadSection) { downloadSection.style.display = 'block'; } // Save current data this.currentQR = { base64: result.qrCodeBase64, id: result.qrId, generationTime: generationTime }; // Update counter for free users if (result.remainingQRs !== undefined) { this.updateRemainingCounter(result.remainingQRs); } } showGenerationStarted() { const button = document.getElementById('generate-btn'); const spinner = button?.querySelector('.spinner-border'); const timer = document.querySelector('.generation-timer'); if (button) button.disabled = true; if (spinner) spinner.classList.remove('d-none'); if (timer) timer.classList.remove('d-none'); // Update timer in real time this.timerInterval = setInterval(() => { const elapsed = ((performance.now() - this.startTime) / 1000).toFixed(1); const timerSpan = timer?.querySelector('span'); if (timerSpan) { timerSpan.textContent = `${elapsed}s`; } }, 100); // Preview loading const preview = document.getElementById('qr-preview'); if (preview) { preview.innerHTML = `

${this.languageStrings[this.currentLang].generating}

`; } } showGenerationStats(generationTime) { const statsDiv = document.querySelector('.generation-stats'); const speedBadge = document.querySelector('.speed-badge'); if (statsDiv) { statsDiv.classList.remove('d-none'); const timeSpan = statsDiv.querySelector('.generation-time'); if (timeSpan) { timeSpan.textContent = `${generationTime}s`; } } // Show speed badge if (speedBadge) { const strings = this.languageStrings[this.currentLang]; let badgeText = strings.normal; let badgeClass = 'bg-secondary'; if (generationTime < 1.0) { badgeText = strings.ultraFast; badgeClass = 'bg-success'; } else if (generationTime < 2.0) { badgeText = strings.fast; badgeClass = 'bg-primary'; } speedBadge.innerHTML = ` ${badgeText} `; speedBadge.classList.remove('d-none'); } } hideGenerationLoading() { const button = document.getElementById('generate-btn'); const spinner = button?.querySelector('.spinner-border'); if (button) button.disabled = false; if (spinner) spinner.classList.add('d-none'); if (this.timerInterval) { clearInterval(this.timerInterval); this.timerInterval = null; } } updateContentHints() { const type = document.getElementById('qr-type')?.value; const hintsElement = document.getElementById('content-hints'); if (!hintsElement || !type) return; const hints = { 'pt-BR': { 'url': 'Ex: https://www.exemplo.com.br', 'text': 'Digite qualquer texto que desejar', 'wifi': 'Nome da rede;Senha;Tipo de segurança (WPA/WEP)', 'vcard': 'Nome;Telefone;Email;Empresa', 'sms': 'Número;Mensagem', 'email': 'email@exemplo.com;Assunto;Mensagem' }, 'es': { 'url': 'Ej: https://www.ejemplo.com', 'text': 'Escribe cualquier texto que desees', 'wifi': 'Nombre de red;Contraseña;Tipo de seguridad (WPA/WEP)', 'vcard': 'Nombre;Teléfono;Email;Empresa', 'sms': 'Número;Mensaje', 'email': 'email@ejemplo.com;Asunto;Mensaje' } }; const langHints = hints[this.currentLang] || hints['pt-BR']; hintsElement.textContent = langHints[type] || 'Digite o conteúdo apropriado para o tipo selecionado'; } changeLanguage(e) { e.preventDefault(); this.currentLang = e.target.dataset.lang; this.updateLanguage(); this.updateContentHints(); // Save preference localStorage.setItem('qrrapido-lang', this.currentLang); // Track language change window.trackLanguageChange && window.trackLanguageChange('pt-BR', this.currentLang); } updateLanguage() { const strings = this.languageStrings[this.currentLang]; // Update tagline const tagline = document.getElementById('tagline'); if (tagline) { tagline.textContent = strings.tagline; } // Update language selector const langMap = { 'pt-BR': 'PT', 'es': 'ES', 'en': 'EN' }; const currentLang = document.getElementById('current-lang'); if (currentLang) { currentLang.textContent = langMap[this.currentLang]; } // Update hints if type already selected const qrType = document.getElementById('qr-type'); if (qrType?.value) { this.updateContentHints(); } } applyQuickStyle(e) { const style = e.target.value; const settings = this.getStyleSettings(style); const primaryColor = document.getElementById('primary-color'); const bgColor = document.getElementById('bg-color'); if (primaryColor) primaryColor.value = settings.primaryColor; if (bgColor) bgColor.value = settings.backgroundColor; } updateStatsCounters() { // Simulate real-time counters setInterval(() => { const totalElement = document.getElementById('total-qrs'); if (totalElement) { const current = parseFloat(totalElement.textContent.replace('K', '')) || 10.5; const newValue = (current + Math.random() * 0.1).toFixed(1); totalElement.textContent = `${newValue}K`; } // Update average time based on real performance const avgElement = document.getElementById('avg-generation-time'); if (avgElement && window.qrRapidoStats) { const avg = window.qrRapidoStats.getAverageTime(); avgElement.textContent = `${avg}s`; } }, 30000); // Update every 30 seconds } trackGenerationEvent(type, time) { // Google Analytics if (typeof gtag !== 'undefined') { gtag('event', 'qr_generated', { 'qr_type': type, 'generation_time': parseFloat(time), 'user_type': this.isPremiumUser() ? 'premium' : 'free', 'language': this.currentLang }); } // Internal statistics if (!window.qrRapidoStats) { window.qrRapidoStats = { times: [], getAverageTime: function() { if (this.times.length === 0) return '1.2'; const avg = this.times.reduce((a, b) => a + b) / this.times.length; return avg.toFixed(1); } }; } window.qrRapidoStats.times.push(parseFloat(time)); } isPremiumUser() { return document.querySelector('.text-success')?.textContent.includes('Premium Ativo') || false; } async downloadQR(format) { if (!this.currentQR) return; try { const response = await fetch(`/api/QR/Download/${this.currentQR.id}?format=${format}`); if (!response.ok) throw new Error('Download failed'); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `qrrapido-${new Date().toISOString().slice(0,10)}.${format}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } catch (error) { console.error('Download error:', error); this.showError('Erro ao fazer download. Tente novamente.'); } } async saveToHistory() { if (!this.currentQR) return; try { const response = await fetch('/api/QR/SaveToHistory', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ qrId: this.currentQR.id }) }); if (response.ok) { this.showSuccess(this.languageStrings[this.currentLang].success); } else { throw new Error('Failed to save'); } } catch (error) { console.error('Save error:', error); this.showError('Erro ao salvar no histórico.'); } } async checkAdFreeStatus() { try { const response = await fetch('/Account/AdFreeStatus'); const status = await response.json(); if (status.isAdFree) { this.hideAllAds(); this.showAdFreeMessage(status.timeRemaining); } } catch (error) { console.error('Error checking ad-free status:', error); } } hideAllAds() { document.querySelectorAll('.ad-container').forEach(ad => { ad.style.display = 'none'; }); } showAdFreeMessage(timeRemaining) { if (timeRemaining <= 0) return; const existing = document.querySelector('.ad-free-notice'); if (existing) return; // Already shown const message = document.createElement('div'); message.className = 'alert alert-success text-center mb-3 ad-free-notice'; message.innerHTML = ` Sessão sem anúncios ativa! Tempo restante: ${this.formatTime(timeRemaining)} Tornar Permanente `; const container = document.querySelector('.container'); const row = container?.querySelector('.row'); if (container && row) { container.insertBefore(message, row); } } formatTime(minutes) { if (minutes === 0) return '0m'; const days = Math.floor(minutes / 1440); const hours = Math.floor((minutes % 1440) / 60); const mins = minutes % 60; if (days > 0) return `${days}d ${hours}h ${mins}m`; if (hours > 0) return `${hours}h ${mins}m`; return `${mins}m`; } showUpgradeModal(message) { const modal = document.createElement('div'); modal.className = 'modal fade'; modal.innerHTML = ` `; document.body.appendChild(modal); const bsModal = new bootstrap.Modal(modal); bsModal.show(); // Remove modal when closed modal.addEventListener('hidden.bs.modal', () => { document.body.removeChild(modal); }); } updateRemainingCounter(remaining) { const counterElement = document.querySelector('.qr-counter'); if (counterElement) { counterElement.textContent = `${remaining} QR codes restantes hoje`; if (remaining <= 3) { counterElement.className = 'badge bg-warning qr-counter'; } if (remaining === 0) { counterElement.className = 'badge bg-danger qr-counter'; } } } showError(message) { this.showAlert(message, 'danger'); } showSuccess(message) { this.showAlert(message, 'success'); } showAlert(message, type) { const alert = document.createElement('div'); alert.className = `alert alert-${type} alert-dismissible fade show`; alert.innerHTML = ` ${message} `; const container = document.querySelector('.container'); const row = container?.querySelector('.row'); if (container && row) { container.insertBefore(alert, row); } // Auto-remove after delay setTimeout(() => { if (alert.parentNode) { alert.parentNode.removeChild(alert); } }, type === 'success' ? 3000 : 5000); } setupRealTimePreview() { const contentField = document.getElementById('qr-content'); const typeField = document.getElementById('qr-type'); if (contentField && typeField) { let previewTimeout; const updatePreview = () => { clearTimeout(previewTimeout); previewTimeout = setTimeout(() => { if (contentField.value.trim() && typeField.value) { // Could implement real-time preview for premium users console.log('Real-time preview update'); } }, 500); }; contentField.addEventListener('input', updatePreview); typeField.addEventListener('change', updatePreview); } } } // Initialize when DOM loads document.addEventListener('DOMContentLoaded', () => { window.qrGenerator = new QRRapidoGenerator(); // Initialize AdSense if necessary if (window.adsbygoogle && document.querySelector('.adsbygoogle')) { (adsbygoogle = window.adsbygoogle || []).push({}); } }); // Global functions for ad control window.QRApp = { refreshAds: function() { if (window.adsbygoogle) { document.querySelectorAll('.adsbygoogle').forEach(ad => { (adsbygoogle = window.adsbygoogle || []).push({}); }); } }, hideAds: function() { document.querySelectorAll('.ad-container').forEach(ad => { ad.style.display = 'none'; }); } };