feat: todos os qr codes com os campos funcionais!!! (falta apenas o dinamico)

This commit is contained in:
Ricardo Carneiro 2025-07-31 23:59:45 -03:00
parent 9634176e18
commit ee160135af
2 changed files with 657 additions and 49 deletions

View File

@ -103,7 +103,7 @@
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3" id="content-group">
<label class="form-label fw-semibold"> <label class="form-label fw-semibold">
<i class="fas fa-edit"></i> @Localizer["Content"] <i class="fas fa-edit"></i> @Localizer["Content"]
</label> </label>
@ -253,12 +253,183 @@
VERSION:3.0 VERSION:3.0
CHARSET=UTF-8 CHARSET=UTF-8
Preencha os campos acima para ver o preview... Preencha os campos acima para ver o preview...
END:VCARD</pre> END:VCARD
</pre>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- WiFi Interface (dynamic) -->
<div id="wifi-interface" class="mb-3" style="display: none;">
<div class="alert alert-info">
<i class="fas fa-wifi"></i>
<strong>@Localizer["WiFiQRTitle"]</strong> - @Localizer["WiFiQRDescription"]
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">@Localizer["NetworkName"] *</label>
<input type="text" id="wifi-ssid" name="wifi-ssid" class="form-control" placeholder="NomeDaSuaRede" required>
<div class="invalid-feedback">@Localizer["NetworkNameRequired"]</div>
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">@Localizer["SecurityType"]</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="wifi-security" id="wifi-security-wpa" value="WPA" checked>
<label class="form-check-label" for="wifi-security-wpa">@Localizer["WPARecommended"]</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="wifi-security" id="wifi-security-wep" value="WEP">
<label class="form-check-label" for="wifi-security-wep">@Localizer["WEPLegacy"]</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="wifi-security" id="wifi-security-nopass" value="nopass">
<label class="form-check-label" for="wifi-security-nopass">@Localizer["OpenNetwork"]</label>
</div>
</div>
<div class="form-group mb-3" id="wifi-password-group">
<label class="form-label fw-semibold">@Localizer["NetworkPassword"] *</label>
<div class="input-group">
<input type="password" id="wifi-password" name="wifi-password" class="form-control" placeholder="SenhaDaSuaRede" required>
<button class="btn btn-outline-secondary" type="button" id="toggle-password">
<i class="fas fa-eye"></i>
</button>
</div>
<div class="invalid-feedback">@Localizer["PasswordRequiredForSecure"]</div>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="wifi-hidden" name="wifi-hidden">
<label class="form-check-label" for="wifi-hidden">@Localizer["HiddenNetwork"]</label>
</div>
<div class="wifi-preview">
<h6 class="fw-bold text-success mb-2">
<i class="fas fa-eye"></i> @Localizer["WiFiPreviewTitle"]
</h6>
<div class="card bg-light">
<div class="card-body">
<pre id="wifi-preview-text" class="mb-0 small text-muted">WIFI:T:WPA;S:;P:;H:false;;</pre>
</div>
</div>
</div>
</div>
<!-- WiFi Interface (dynamic) -->
<div id="wifi-interface" class="mb-3" style="display: none;">
<div class="alert alert-info">
<i class="fas fa-wifi"></i>
<strong>@Localizer["WiFiQRTitle"]</strong> - @Localizer["WiFiQRDescription"]
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">@Localizer["NetworkName"] *</label>
<input type="text" id="wifi-ssid2" class="form-control" placeholder="NomeDaSuaRede" required>
<div class="invalid-feedback">@Localizer["NetworkNameRequired"]</div>
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">@Localizer["SecurityType"]</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="wifi-security" id="wifi-security-wpa2" value="WPA" checked>
<label class="form-check-label" for="wifi-security-wpa">@Localizer["WPARecommended"]</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="wifi-security" id="wifi-security-wep2" value="WEP">
<label class="form-check-label" for="wifi-security-wep">@Localizer["WEPLegacy"]</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="wifi-security" id="wifi-security-nopass2" value="nopass">
<label class="form-check-label" for="wifi-security-nopass">@Localizer["OpenNetwork"]</label>
</div>
</div>
<div class="form-group mb-3" id="wifi-password-group">
<label class="form-label fw-semibold">@Localizer["NetworkPassword"] *</label>
<div class="input-group">
<input type="password" id="wifi-password2" class="form-control" placeholder="SenhaDaSuaRede" required>
<button class="btn btn-outline-secondary" type="button" id="toggle-password2">
<i class="fas fa-eye"></i>
</button>
</div>
<div class="invalid-feedback">@Localizer["PasswordRequiredForSecure"]</div>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="wifi-hidden2">
<label class="form-check-label" for="wifi-hidden">@Localizer["HiddenNetwork"]</label>
</div>
<div class="wifi-preview">
<h6 class="fw-bold text-success mb-2">
<i class="fas fa-eye"></i> @Localizer["WiFiPreviewTitle"]
</h6>
<div class="card bg-light">
<div class="card-body">
<pre id="wifi-preview-text" class="mb-0 small text-muted">WIFI:T:WPA;S:;P:;H:false;;</pre>
</div>
</div>
</div>
</div>
<!-- SMS Interface (dynamic) -->
<div id="sms-interface" class="mb-3" style="display: none;">
<div class="alert alert-info">
<strong>QR Code SMS:</strong> Permite enviar SMS automático com número e mensagem pré-definidos.
Compatível com Android, iOS e dispositivos modernos.
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">Número do Celular *</label>
<input type="tel" id="sms-number" name="sms-number" class="form-control" placeholder="11999998888" required>
<small class="text-muted">Apenas números (DDD + número, sem espaços)</small>
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">Mensagem *</label>
<textarea id="sms-message" name="sms-message" class="form-control" rows="3" placeholder="Sua mensagem aqui..." required></textarea>
<small class="text-muted">Mensagem que será pré-preenchida no SMS</small>
</div>
<div class="sms-preview">
<h6> Preview do SMS</h6>
<pre id="sms-preview-text" class="bg-light p-3 rounded">SMSTO::</pre>
</div>
</div>
<!-- Email Interface (dynamic) -->
<div id="email-interface" class="mb-3" style="display: none;">
<div class="alert alert-info">
<strong>QR Code Email:</strong> Permite enviar email automático com destinatário, assunto e mensagem pré-definidos.
Compatível com Android, iOS e dispositivos modernos.
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">Email Destinatário *</label>
<input type="email" id="email-to" name="email-to" class="form-control" placeholder="contato@empresa.com" required>
<small class="text-muted">Email que receberá a mensagem</small>
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">Assunto *</label>
<input type="text" id="email-subject" name="email-subject" class="form-control" placeholder="Assunto do email" required>
<small class="text-muted">Linha de assunto do email</small>
</div>
<div class="form-group mb-3">
<label class="form-label fw-semibold">Mensagem</label>
<textarea id="email-body" name="email-body" class="form-control" rows="4" placeholder="Corpo da mensagem..."></textarea>
<small class="text-muted">Corpo da mensagem (opcional)</small>
</div>
<div class="email-preview">
<h6> Preview do Email</h6>
<pre id="email-preview-text" class="bg-light p-3 rounded">mailto:?subject=&body=</pre>
</div>
</div>
<!-- Advanced customization (collapsible) --> <!-- Advanced customization (collapsible) -->
<div class="accordion mb-3" id="customization-accordion"> <div class="accordion mb-3" id="customization-accordion">
<div class="accordion-item"> <div class="accordion-item">

View File

@ -106,16 +106,21 @@ class QRRapidoGenerator {
}); });
}); });
// VCard fields validation // Add listeners to all relevant fields to update button state
const vcardFields = ['vcard-name', 'vcard-mobile', 'vcard-email']; const fieldsToWatch = [
vcardFields.forEach(fieldId => { 'qr-content', 'vcard-name', 'vcard-mobile', 'vcard-email',
const field = document.getElementById(fieldId); 'wifi-ssid', 'wifi-password', 'sms-number', 'sms-message',
if (field) { 'email-to', 'email-subject', 'email-body'
field.addEventListener('input', () => { ];
this.updateGenerateButton(); fieldsToWatch.forEach(id => {
}); const el = document.getElementById(id);
if (el) {
el.addEventListener('input', () => this.updateGenerateButton());
} }
}); });
document.querySelectorAll('input[name="wifi-security"]').forEach(radio => {
radio.addEventListener('change', () => this.updateGenerateButton());
});
// Language selector // Language selector
document.querySelectorAll('[data-lang]').forEach(link => { document.querySelectorAll('[data-lang]').forEach(link => {
@ -431,6 +436,27 @@ class QRRapidoGenerator {
this.showError('Erro na validação do VCard: ' + error.message); this.showError('Erro na validação do VCard: ' + error.message);
return false; return false;
} }
} else if (qrType === 'wifi') {
const errors = window.wifiGenerator.validateWiFiData();
if (errors.length > 0) {
this.showError(errors.join('<br>'));
return false;
}
return true;
} else if (qrType === 'sms') {
const errors = window.smsGenerator.validateSMSData();
if (errors.length > 0) {
this.showError(errors.join('<br>'));
return false;
}
return true;
} else if (qrType === 'email') {
const errors = window.emailGenerator.validateEmailData();
if (errors.length > 0) {
this.showError(errors.join('<br>'));
return false;
}
return true;
} }
// Normal validation for other types // Normal validation for other types
@ -454,6 +480,62 @@ class QRRapidoGenerator {
const quickStyle = document.querySelector('input[name="quick-style"]:checked')?.value || 'classic'; const quickStyle = document.querySelector('input[name="quick-style"]:checked')?.value || 'classic';
const styleSettings = this.getStyleSettings(quickStyle); const styleSettings = this.getStyleSettings(quickStyle);
if (type === 'wifi') {
const wifiContent = window.wifiGenerator.generateWiFiString();
return {
data: {
type: 'text', // WiFi is treated as text in the backend
content: wifiContent,
quickStyle: quickStyle,
primaryColor: document.getElementById('primary-color').value || (styleSettings.primaryColor || '#000000'),
backgroundColor: document.getElementById('bg-color').value || (styleSettings.backgroundColor || '#FFFFFF'),
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
},
isMultipart: false,
endpoint: '/api/QR/GenerateRapid'
};
} else if (type === 'sms') {
const smsContent = window.smsGenerator.generateSMSString();
return {
data: {
type: 'text',
content: smsContent,
quickStyle: quickStyle,
primaryColor: document.getElementById('primary-color').value || (styleSettings.primaryColor || '#000000'),
backgroundColor: document.getElementById('bg-color').value || (styleSettings.backgroundColor || '#FFFFFF'),
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
},
isMultipart: false,
endpoint: '/api/QR/GenerateRapid'
};
} else if (type === 'email') {
const emailContent = window.emailGenerator.generateEmailString();
return {
data: {
type: 'text',
content: emailContent,
quickStyle: quickStyle,
primaryColor: document.getElementById('primary-color').value || (styleSettings.primaryColor || '#000000'),
backgroundColor: document.getElementById('bg-color').value || (styleSettings.backgroundColor || '#FFFFFF'),
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
},
isMultipart: false,
endpoint: '/api/QR/GenerateRapid'
};
}
// Handle VCard type // Handle VCard type
if (type === 'vcard') { if (type === 'vcard') {
if (window.vcardGenerator) { if (window.vcardGenerator) {
@ -729,7 +811,7 @@ class QRRapidoGenerator {
'wifi': 'Nombre de red;Contraseña;Tipo de seguridad (WPA/WEP)', 'wifi': 'Nombre de red;Contraseña;Tipo de seguridad (WPA/WEP)',
'vcard': 'Nombre;Teléfono;Email;Empresa', 'vcard': 'Nombre;Teléfono;Email;Empresa',
'sms': 'Número;Mensaje', 'sms': 'Número;Mensaje',
'email': 'email@ejemplo.com;Asunto;Mensaje' 'email': 'email@ejemplo.com;Asunto;Mensagem'
} }
}; };
@ -1207,29 +1289,51 @@ class QRRapidoGenerator {
} }
enableContentFields(type) { enableContentFields(type) {
const qrContent = document.getElementById('qr-content'); const contentGroup = document.getElementById('content-group');
const vcardInterface = document.getElementById('vcard-interface'); const vcardInterface = document.getElementById('vcard-interface');
const wifiInterface = document.getElementById('wifi-interface');
const smsInterface = document.getElementById('sms-interface');
const emailInterface = document.getElementById('email-interface');
// Hide all interfaces by default
if (vcardInterface) vcardInterface.style.display = 'none';
if (wifiInterface) wifiInterface.style.display = 'none';
if (smsInterface) smsInterface.style.display = 'none';
if (emailInterface) emailInterface.style.display = 'none';
if (contentGroup) contentGroup.style.display = 'block';
if (type === 'vcard') { if (type === 'vcard') {
// Para vCard, ocultar textarea e mostrar interface específica // Para vCard, ocultar textarea e mostrar interface específica
if (qrContent) { if (contentGroup) contentGroup.style.display = 'none';
qrContent.style.display = 'none';
qrContent.removeAttribute('required');
}
if (vcardInterface) { if (vcardInterface) {
vcardInterface.style.display = 'block'; vcardInterface.style.display = 'block';
this.enableVCardFields(); this.enableVCardFields();
} }
} else if (type === 'wifi') {
// Para WiFi, ocultar textarea e mostrar interface específica
if (contentGroup) contentGroup.style.display = 'none';
if (wifiInterface) {
wifiInterface.style.display = 'block';
}
} else if (type === 'sms') {
// Para SMS, ocultar textarea e mostrar interface específica
if (contentGroup) contentGroup.style.display = 'none';
if (smsInterface) {
smsInterface.style.display = 'block';
}
} else if (type === 'email') {
// Para Email, ocultar textarea e mostrar interface específica
if (contentGroup) contentGroup.style.display = 'none';
if (emailInterface) {
emailInterface.style.display = 'block';
}
} else { } else {
// Para outros tipos, mostrar textarea // Para outros tipos, mostrar textarea
if (contentGroup) contentGroup.style.display = 'block';
const qrContent = document.getElementById('qr-content');
if(qrContent) { if(qrContent) {
qrContent.style.display = 'block';
qrContent.setAttribute('required', 'required');
qrContent.disabled = false; qrContent.disabled = false;
} }
if (vcardInterface) {
vcardInterface.style.display = 'none';
}
} }
} }
@ -1324,29 +1428,36 @@ class QRRapidoGenerator {
const generateBtn = document.getElementById('generate-btn'); const generateBtn = document.getElementById('generate-btn');
if (!generateBtn) return; if (!generateBtn) return;
let shouldEnable = false; let isValid = false;
const type = this.selectedType;
// Verificar se tem tipo selecionado if (type === 'url' || type === 'text') {
if (this.selectedType) {
if (this.selectedType === 'vcard') {
// Para vCard, validar campos obrigatórios
shouldEnable = this.validateVCardFields();
} else {
// Para outros tipos, validar conteúdo
const content = document.getElementById('qr-content')?.value || ''; const content = document.getElementById('qr-content')?.value || '';
shouldEnable = this.validateContent(content); isValid = content.trim().length >= 3;
} else if (type === 'vcard') {
isValid = this.validateVCardFields();
} else if (type === 'wifi') {
const data = window.wifiGenerator.collectWiFiData();
isValid = data.ssid.trim() !== '';
if (data.security !== 'nopass' && !data.password.trim()) {
isValid = false;
} }
} else if (type === 'sms') {
const data = window.smsGenerator.collectSMSData();
isValid = data.number.trim() !== '' && data.message.trim() !== '';
} else if (type === 'email') {
const data = window.emailGenerator.collectEmailData();
isValid = data.to.trim() !== '' && data.subject.trim() !== '';
} }
generateBtn.disabled = !shouldEnable; generateBtn.disabled = !isValid;
// Feedback visual if (isValid) {
if (shouldEnable) { generateBtn.classList.remove('btn-secondary', 'disabled');
generateBtn.classList.remove('btn-secondary');
generateBtn.classList.add('btn-primary'); generateBtn.classList.add('btn-primary');
} else { } else {
generateBtn.classList.remove('btn-primary'); generateBtn.classList.remove('btn-primary');
generateBtn.classList.add('btn-secondary'); generateBtn.classList.add('btn-secondary', 'disabled');
} }
} }
@ -1369,7 +1480,7 @@ class QRRapidoGenerator {
// Garantir que o conteúdo seja tratado como UTF-8 // Garantir que o conteúdo seja tratado como UTF-8
try { try {
// Verificar se há caracteres especiais // Verificar se há caracteres especiais
const hasSpecialChars = /[^\x00-\x7F]/.test(content); const hasSpecialChars = /[^-]/.test(content);
if (hasSpecialChars) { if (hasSpecialChars) {
console.log('Caracteres especiais detectados, aplicando codificação UTF-8'); console.log('Caracteres especiais detectados, aplicando codificação UTF-8');
@ -1441,6 +1552,9 @@ class QRRapidoGenerator {
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
window.qrGenerator = new QRRapidoGenerator(); window.qrGenerator = new QRRapidoGenerator();
window.vcardGenerator = new VCardGenerator(); window.vcardGenerator = new VCardGenerator();
window.wifiGenerator = new WiFiQRGenerator();
window.smsGenerator = new SMSQRGenerator();
window.emailGenerator = new EmailQRGenerator();
// Initialize AdSense if necessary // Initialize AdSense if necessary
if (window.adsbygoogle && document.querySelector('.adsbygoogle')) { if (window.adsbygoogle && document.querySelector('.adsbygoogle')) {
@ -1448,7 +1562,312 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
class SMSQRGenerator {
constructor() {
this.initializeSMSInterface();
}
initializeSMSInterface() {
// Atualizar preview em tempo real
const fieldsToWatch = ['sms-number', 'sms-message'];
fieldsToWatch.forEach(fieldId => {
const element = document.getElementById(fieldId);
if (element) {
element.addEventListener('input', () => this.updatePreview());
element.addEventListener('change', () => this.updatePreview());
}
});
// Inicializar preview
this.updatePreview();
}
updatePreview() {
const smsString = this.generateSMSString();
const previewElement = document.getElementById('sms-preview-text');
if (previewElement) {
previewElement.textContent = smsString;
}
}
generateSMSString() {
const data = this.collectSMSData();
if (!data.number || !data.message) {
return 'SMSTO::';
}
return `SMSTO:${data.number}:${data.message}`;
}
collectSMSData() {
return {
number: document.getElementById('sms-number')?.value || '',
message: document.getElementById('sms-message')?.value || ''
};
}
validateSMSData() {
const data = this.collectSMSData();
const errors = [];
if (!data.number.trim()) {
errors.push('Número do celular é obrigatório');
}
if (!data.message.trim()) {
errors.push('Mensagem é obrigatória');
}
// Validação de telefone brasileiro básica
const phoneRegex = /^\d{10,11}$/;
if (data.number && !phoneRegex.test(data.number.replace(/\D/g, ''))) {
errors.push('Número deve ter 10 ou 11 dígitos (DDD + número)');
}
return errors;
}
}
class EmailQRGenerator {
constructor() {
this.initializeEmailInterface();
}
initializeEmailInterface() {
// Atualizar preview em tempo real
const fieldsToWatch = ['email-to', 'email-subject', 'email-body'];
fieldsToWatch.forEach(fieldId => {
const element = document.getElementById(fieldId);
if (element) {
element.addEventListener('input', () => this.updatePreview());
element.addEventListener('change', () => this.updatePreview());
}
});
// Inicializar preview
this.updatePreview();
}
updatePreview() {
const emailString = this.generateEmailString();
const previewElement = document.getElementById('email-preview-text');
if (previewElement) {
previewElement.textContent = emailString;
}
}
generateEmailString() {
const data = this.collectEmailData();
if (!data.to) {
return 'mailto:?subject=&body=';
}
let emailString = `mailto:${data.to}`;
const params = [];
if (data.subject) {
params.push(`subject=${encodeURIComponent(data.subject)}`);
}
if (data.body) {
params.push(`body=${encodeURIComponent(data.body)}`);
}
if (params.length > 0) {
emailString += '?' + params.join('&');
}
return emailString;
}
collectEmailData() {
return {
to: document.getElementById('email-to')?.value || '',
subject: document.getElementById('email-subject')?.value || '',
body: document.getElementById('email-body')?.value || ''
};
}
validateEmailData() {
const data = this.collectEmailData();
const errors = [];
if (!data.to.trim()) {
errors.push('Email destinatário é obrigatório');
}
if (!data.subject.trim()) {
errors.push('Assunto é obrigatório');
}
// Validação básica de email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (data.to && !emailRegex.test(data.to)) {
errors.push('Email destinatário inválido');
}
return errors;
}
}
class WiFiQRGenerator {
constructor() {
this.initializeWiFiInterface();
}
initializeWiFiInterface() {
// Radio buttons: usar name para group
document.querySelectorAll('input[name="wifi-security"]').forEach(radio => {
radio.addEventListener('change', () => {
this.togglePasswordField();
this.updatePreview();
});
});
// Campos individuais: usar IDs únicos
const fieldsToWatch = ['wifi-ssid', 'wifi-password', 'wifi-hidden'];
fieldsToWatch.forEach(fieldId => {
const element = document.getElementById(fieldId);
if (element) {
element.addEventListener('input', () => this.updatePreview());
element.addEventListener('change', () => this.updatePreview());
}
});
const togglePassword = document.getElementById('toggle-password');
if (togglePassword) {
togglePassword.addEventListener('click', () => {
this.togglePasswordVisibility();
});
}
// Inicializar estado
this.togglePasswordField();
this.updatePreview();
}
togglePasswordField() {
const selectedRadio = document.querySelector('input[name="wifi-security"]:checked');
const securityType = selectedRadio ? selectedRadio.value : 'WPA';
const passwordGroup = document.getElementById('wifi-password-group');
const passwordInput = document.getElementById('wifi-password');
if (securityType === 'nopass') {
passwordGroup.style.display = 'none';
passwordInput.removeAttribute('required');
passwordInput.value = '';
} else {
passwordGroup.style.display = 'block';
passwordInput.setAttribute('required', 'required');
}
}
togglePasswordVisibility() {
const passwordInput = document.getElementById('wifi-password');
const toggleIcon = document.getElementById('toggle-password');
if (passwordInput.type === 'password') {
passwordInput.type = 'text';
toggleIcon.className = 'fas fa-eye-slash';
} else {
passwordInput.type = 'password';
toggleIcon.className = 'fas fa-eye';
}
}
updatePreview() {
const wifiString = this.generateWiFiString();
const previewText = document.getElementById('wifi-preview-text');
if(previewText) {
previewText.textContent = wifiString;
}
}
generateWiFiString() {
const data = this.collectWiFiData();
// Validar dados mínimos
if (!data.ssid) {
return 'WIFI:T:WPA;S:;P:;H:false;;';
}
// Escapar caracteres especiais
const ssid = this.escapeWiFiString(data.ssid);
const password = this.escapeWiFiString(data.password);
// Construir string WiFi
return `WIFI:T:${data.security};S:${ssid};P:${password};H:${data.hidden};;`;
}
collectWiFiData() {
return {
ssid: document.getElementById('wifi-ssid').value,
security: document.querySelector('input[name="wifi-security"]:checked')?.value || 'WPA',
password: document.getElementById('wifi-password').value,
hidden: document.getElementById('wifi-hidden').checked.toString()
};
}
escapeWiFiString(str) {
if (!str) return '';
// Escapar caracteres especiais do formato WiFi
return str.replace(/[\\;,:"]/g, '\\escapeWiFiString(str) { if (!str) return; }');
// Escapar caracteres especiais do formato WiFi
//return str.replace(/[\\;,:""]/g, '\\document.addEventListener('DOMContentLoaded', () => {
// window.qrGenerator = new QRRapidoGenerator();
// window.vcardGenerator = new VCardGenerator();
// // Initialize AdSense if necessary
// if (window.adsbygoogle && document.querySelector('.adsbygoogle')) {
// (adsbygoogle = window.adsbygoogle || []).push({});
// }
//});
return str.replace(/[\\;,:"']/g, '\\$&');
// VCard Generator Class');
}
validateWiFiData() {
const data = this.collectWiFiData();
const errors = [];
// Validações obrigatórias
if (!data.ssid.trim()) {
errors.push('Nome da rede (SSID) é obrigatório');
}
// Validação de senha conforme tipo de segurança
if ((data.security === 'WPA' || data.security === 'WEP') && !data.password.trim()) {
errors.push('Senha é obrigatória para redes protegidas');
}
// Validação de comprimento
if (data.ssid.length > 32) {
errors.push('Nome da rede deve ter no máximo 32 caracteres');
}
if (data.password.length > 63) {
errors.push('Senha deve ter no máximo 63 caracteres');
}
// Validação WEP específica
if (data.security === 'WEP' && data.password) {
const validWEPLengths = [5, 10, 13, 26]; // WEP 64/128 bit
if (!validWEPLengths.includes(data.password.length)) {
errors.push('Senha WEP deve ter 5, 10, 13 ou 26 caracteres');
}
}
return errors;
}
}
// VCard Generator Class // VCard Generator Class
class VCardGenerator { class VCardGenerator {
constructor() { constructor() {
this.initializeVCardInterface(); this.initializeVCardInterface();
@ -1458,7 +1877,7 @@ class VCardGenerator {
encodeQuotedPrintable(text) { encodeQuotedPrintable(text) {
if (!text) return ''; if (!text) return '';
return text.replace(/[^\x20-\x7E]/g, (char) => { return text.replace(/[^\u0020-\u007E]/g, (char) => {
// Para caracteres especiais comuns do português, usar codificação UTF-8 // Para caracteres especiais comuns do português, usar codificação UTF-8
const utf8Bytes = new TextEncoder().encode(char); const utf8Bytes = new TextEncoder().encode(char);
return Array.from(utf8Bytes) return Array.from(utf8Bytes)
@ -1475,7 +1894,7 @@ class VCardGenerator {
if (!needsEncoding) return trimmedText; if (!needsEncoding) return trimmedText;
// Verifica se há caracteres especiais que precisam de codificação // Verifica se há caracteres especiais que precisam de codificação
const hasSpecialChars = /[^\x20-\x7E]/.test(trimmedText); const hasSpecialChars = /[^\u0020-\u007E]/.test(trimmedText);
if (hasSpecialChars) { if (hasSpecialChars) {
return `CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:${this.encodeQuotedPrintable(trimmedText)}`; return `CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:${this.encodeQuotedPrintable(trimmedText)}`;
@ -1674,7 +2093,7 @@ class VCardGenerator {
} }
isValidEmail(email) { isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); return /^[^ @]+@[^ @]+\.[^ @]+$/.test(email);
} }
isValidURL(url) { isValidURL(url) {
@ -1718,3 +2137,21 @@ window.QRApp = {
}); });
} }
}; };
// Google Analytics 4 Event Tracking
window.trackLanguageChange = function(from, to) {
if (typeof gtag !== 'undefined') {
gtag('event', 'language_change', {
'from_language': from,
'to_language': to
});
}
};
window.trackUpgradeClick = function(location) {
if (typeof gtag !== 'undefined') {
gtag('event', 'upgrade_click', {
'click_location': location
});
}
};