1/** 2 * ESPM Avise-me â Modal único (extensão â API EC; graduação/tecnólogo/pós â HubSpot, decidido no servidor) 3 * 4 * Substitui os popups Elementor/HubSpot por formulário inline que 5 * envia dados diretamente para a API EC via REST endpoint do WordPress. 6 * 7 * Expõe: window.abrirModalAviseMe() â chamado por todos os módulos 8 * Requer: ESPM_AVISEME (injetado via wp_localize_script) 9 */ 10(function() { 11 'use strict'; 12 13 // Configuração injetada pelo PHP 14 var CONFIG = (typeof ESPM_AVISEME !== 'undefined') ? ESPM_AVISEME : {}; 15 var modalAberto = false; 16 17 /** 18 * Aplica máscara de telefone: (DD) 99999-9999 19 */ 20 function mascaraTelefone(valor) { 21 var nums = valor.replace(/\D/g, '').substring(0, 11); 22 if (nums.length === 0) return ''; 23 if (nums.length <= 2) return '(' + nums; 24 if (nums.length <= 7) return '(' + nums.substring(0, 2) + ') ' + nums.substring(2); 25 return '(' + nums.substring(0, 2) + ') ' + nums.substring(2, 7) + '-' + nums.substring(7); 26 } 27 28 /** 29 * Remove máscara â retorna só dÃgitos 30 */ 31 function limparTelefone(valor) { 32 return valor.replace(/\D/g, ''); 33 } 34 35 /** 36 * Valida formato de e-mail 37 */ 38 function validarEmail(email) { 39 return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 40 } 41 42 /** 43 * Cria e exibe o modal do Avise-me 44 * @param {string|number} [oferecidoId] - SKU da variação (oferecido_id na API EC). Se omitido, usa CONFIG.oferecidoId. 45 */ 46 function abrirModalAviseMe(oferecidoId) { 47 if (modalAberto) return; 48 modalAberto = true; 49 50 // oferecido_id: parâmetro dinâmico (SKU) > config PHP 51 var idFinal = oferecidoId || CONFIG.oferecidoId || ''; 52 53 // Remove modal anterior se existir 54 var existente = document.getElementById('espm-aviseme-modal'); 55 if (existente) existente.remove(); 56 57 var modal = document.createElement('div'); 58 modal.id = 'espm-aviseme-modal'; 59 modal.className = 'espm-aviseme-overlay'; 60 modal.innerHTML = 61 '<div class="espm-aviseme-container">' + 62 '<button type="button" class="espm-aviseme-fechar" aria-label="Fechar">×</button>' + 63 '<h3 class="espm-aviseme-titulo">Avise-me quando abrir</h3>' + 64 '<p class="espm-aviseme-subtitulo">' + (CONFIG.nomeProduto || 'Este curso') + '</p>' + 65 '<form id="espm-aviseme-form" class="espm-aviseme-form">' + 66 '<input type="hidden" name="oferecido_id" value="' + idFinal + '">' + 67 '<input type="hidden" name="produto_id" value="' + (CONFIG.produtoId || '') + '">' + 68 '<input type="hidden" name="url" value="' + window.location.href + '">' + 69 '<div class="espm-aviseme-campo">' + 70 '<input type="text" name="nome" placeholder="Nome *" required autocomplete="given-name">' + 71 '</div>' + 72 '<div class="espm-aviseme-campo">' + 73 '<input type="text" name="sobrenome" placeholder="Sobrenome *" required autocomplete="family-name">' + 74 '</div>' + 75 '<div class="espm-aviseme-campo">' + 76 '<input type="email" name="email" placeholder="E-mail *" required autocomplete="email">' + 77 '</div>' + 78 '<div class="espm-aviseme-campo">' + 79 '<input type="tel" name="telefone" placeholder="Telefone (opcional)" autocomplete="tel">' + 80 '</div>' + 81 '<button type="submit" class="espm-aviseme-enviar">Quero ser avisado</button>' +
82 '<div id="espm-aviseme-msg" class="espm-aviseme-msg" style="display:none;"></div>' + 83 '</form>' + 84 '</div>'; 85 86 document.body.appendChild(modal); 87 88 // Fechar ao clicar no X 89 modal.querySelector('.espm-aviseme-fechar').addEventListener('click', fecharModalAviseMe); 90 91 // Fechar ao clicar no overlay 92 modal.addEventListener('click', function(e) { 93 if (e.target === modal) fecharModalAviseMe(); 94 }); 95 96 // Fechar com ESC 97 function escHandler(e) { 98 if (e.key === 'Escape') { 99 fecharModalAviseMe(); 100 document.removeEventListener('keydown', escHandler); 101 } 102 } 103 document.addEventListener('keydown', escHandler); 104 105 // Máscara de telefone 106 var telInput = modal.querySelector('input[name="telefone"]'); 107 telInput.addEventListener('input', function() { 108 var pos = this.selectionStart; 109 var antes = this.value.length; 110 this.value = mascaraTelefone(this.value); 111 var depois = this.value.length; 112 this.setSelectionRange(pos + (depois - antes), pos + (depois - antes)); 113 }); 114 115 // Submit do formulário 116 modal.querySelector('#espm-aviseme-form').addEventListener('submit', function(e) { 117 e.preventDefault(); 118 enviarFormularioAviseMe(this); 119 }); 120 121 // Animação de entrada 122 requestAnimationFrame(function() { 123 modal.classList.add('espm-aviseme-visivel'); 124 }); 125 126 // console.log('[ESPM AVISE-ME] Modal aberto | oferecido_id:', idFinal, '| produto:', CONFIG.nomeProduto); 127 } 128 129 /** 130 * Fecha o modal com animação 131 */ 132 function fecharModalAviseMe() { 133 var modal = document.getElementById('espm-aviseme-modal'); 134 if (modal) { 135 modal.classList.remove('espm-aviseme-visivel'); 136 setTimeout(function() { 137 modal.remove(); 138 modalAberto = false; 139 }, 300); 140 } else { 141 modalAberto = false; 142 } 143 } 144 145 /** 146 * Envia o formulário para o REST endpoint 147 */ 148 function enviarFormularioAviseMe(form) { 149 var btn = form.querySelector('.espm-aviseme-enviar'); 150 var msgEl = form.querySelector('#espm-aviseme-msg'); 151 var textoOriginal = btn.textContent; 152 153 // Validação de e-mail 154 var emailInput = form.querySelector('input[name="email"]'); 155 if (!validarEmail(emailInput.value)) { 156 msgEl.className = 'espm-aviseme-msg espm-aviseme-erro'; 157 msgEl.textContent = 'Por favor, informe um e-mail válido.'; 158 msgEl.style.display = 'block'; 159 emailInput.focus(); 160 return; 161 } 162 163 btn.textContent = 'Enviando...'; 164 btn.disabled = true; 165 msgEl.style.display = 'none'; 166 167 // Coleta dados do formulário 168 var dados = {}; 169 var inputs = form.querySelectorAll('input'); 170 for (var i = 0; i < inputs.length; i++) { 171 if (inputs[i].name) { 172 dados[inputs[i].name] = inputs[i].value; 173 } 174 } 175 176 // Variação escolhida no form do Woo (campus/perÃodo/formato são lidos dela no servidor). 177 // O template repete o form (desktop/mobile/flutuante): vale o primeiro com variação escolhida. 178 var variacoes = document.querySelectorAll('form.variations_form input[name="variation_id"]'); 179 for (var v = 0; v < variacoes.length; v++) { 180 if (variacoes[v].value && variacoes[v].value !== '0') { 181 dados.variacao_id = variacoes[v].value; 182 break; 183 } 184 } 185 186 // Remove máscara do telefone antes de enviar 187 if (dados.telefone) { 188 dados.telefone = limparTelefone(dados.telefone); 189 } 190 191 // console.group('[ESPM AVISE-ME] Enviando formulário'); 192 // console.log('Endpoint:', CONFIG.restUrl); 193 // console.log('Dados:', dados); 194 195 fetch(CONFIG.restUrl, { 196 method: 'POST', 197 credentials: 'same-origin', 198 headers: { 'Content-Type': 'application/json' }, 199 body: JSON.stringify(dados) 200 }) 201 .then(function(res) { return res.json(); }) 202 .then(function(data) { 203 // console.log('Resposta:', data); 204 // console.groupEnd(); 205 206 if (data.sucesso) {
207 msgEl.className = 'espm-aviseme-msg espm-aviseme-sucesso'; 208 msgEl.textContent = data.mensagem || 'Cadastro realizado com sucesso!'; 209 msgEl.style.display = 'block'; 210 211 // Fecha modal após 2.5s 212 setTimeout(fecharModalAviseMe, 2500); 213 } else { 214 msgEl.className = 'espm-aviseme-msg espm-aviseme-erro'; 215 msgEl.textContent = data.mensagem || 'Erro ao enviar. Tente novamente.'; 216 msgEl.style.display = 'block'; 217 btn.textContent = textoOriginal; 218 btn.disabled = false; 219 } 220 }) 221 .catch(function(err) { 222 // console.error('[ESPM AVISE-ME] Erro:', err); 223 // console.groupEnd(); 224 225 msgEl.className = 'espm-aviseme-msg espm-aviseme-erro'; 226 msgEl.textContent = 'Erro de conexão. Tente novamente.'; 227 msgEl.style.display = 'block'; 228 btn.textContent = textoOriginal; 229 btn.disabled = false; 230 }); 231 } 232 233 // Expõe globalmente para todos os módulos 234 window.abrirModalAviseMe = abrirModalAviseMe; 235 window.fecharModalAviseMe = fecharModalAviseMe; 236 237})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.