(function () {
'use strict';
/* =========================
BOOTSTRAP SEGURO (GTM / SPA)
========================= */
if (window.__NPW_LOADED__) return;
window.__NPW_LOADED__ = true;
/* =========================
CONFIGURAÇÃO GLOBAL
========================= */
var NPW = {
config: {
defaultWhatsLink: "https://api.whatsapp.com/send/?phone=5511999954200&text=Estou no site da Dra. Estrela Machado e gostaria de mais informações",
apiURL: "https://wapi.bernardo.app.br/v1/estrelatricologia",
clientId: "estrelatricologia",
interceptSelector: 'a.bnd-whats',
minNameLength: 3,
phoneDigits: 11
},
state: {
pendingUrl: null,
submitting: false
}
};
/* =========================
READY
========================= */
function onReady(fn) {
if (document.readyState !== 'loading') fn();
else document.addEventListener('DOMContentLoaded', fn);
}
onReady(initNPW);
/* =========================
INIT
========================= */
function initNPW() {
if (!document.body || document.getElementById('npw-root')) return;
createShadowRoot();
bindEvents();
observeWhatsLinks();
}
/* =========================
SHADOW DOM - ISOLAMENTO COMPLETO
========================= */
function createShadowRoot() {
// Container raiz com Shadow DOM
var root = document.createElement('div');
root.id = 'npw-root';
document.body.appendChild(root);
// Criar Shadow DOM
var shadow = root.attachShadow({ mode: 'closed' });
// CSS ISOLADO (não afeta a página)
var style = document.createElement('style');
style.textContent = `
:host {
all: initial;
display: block;
font-family: 'Segoe UI', Tahoma, sans-serif;
}
/* Reset dentro do Shadow DOM */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
#npw-btn {
display: flex;
align-items: center;
gap: 8px;
position: fixed;
bottom: 20px;
left: 20px;
z-index: 10000;
background: #854d0e;
color: #fff;
border: 0;
padding: 12px 20px;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,.15);
font-size: 16px;
font-family: inherit;
}
#npw-btn img {
width: 16px;
height: 16px;
display: block;
}
#npw-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,.5);
display: none;
justify-content: center;
align-items: center;
z-index: 99999;
animation: fadeIn 0.2s ease;
}
#npw-overlay.open {
display: flex;
}
#npw-modal {
background: #fff;
padding: 24px;
border-radius: 12px;
max-width: 400px;
width: 90%;
display: flex;
flex-direction: column;
gap: 14px;
position: relative;
box-shadow: 0 10px 40px rgba(0,0,0,.2);
animation: slideUp 0.3s ease;
}
#npw-modal h2 {
margin: 0;
font-size: 17px;
color: #333;
font-weight: 600;
font-family: inherit;
}
#npw-modal input {
padding: 10px;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 15px;
font-family: inherit;
transition: border-color 0.2s;
width: 100%;
outline: none;
}
#npw-modal input:focus {
border-color: #854d0e;
}
#npw-modal input.error {
border-color: #e74c3c;
}
#npw-modal button {
background: #854d0e;
color: #fff;
border: 0;
padding: 10px;
border-radius: 8px;
font-weight: 700;
cursor: pointer;
font-size: 15px;
font-family: inherit;
transition: opacity 0.2s, transform 0.1s;
}
#npw-modal button:hover {
opacity: 0.9;
}
#npw-modal button:active {
transform: scale(0.98);
}
#npw-modal button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
#npw-error {
color: #e74c3c;
font-size: 14px;
min-height: 18px;
font-family: inherit;
}
/* Animações */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Mobile Responsivo */
@media (max-width: 480px) {
#npw-btn {
bottom: 10px;
left: 10px;
padding: 10px 16px;
font-size: 14px;
}
#npw-modal {
padding: 20px;
width: 95%;
}
}
`;
// HTML ISOLADO
var template = document.createElement('template');
template.innerHTML = `
`;
shadow.appendChild(style);
shadow.appendChild(template.content.cloneNode(true));
// Salvar referências
NPW.shadow = shadow;
NPW.root = root;
NPW.elements = {
btn: shadow.getElementById('npw-btn'),
overlay: shadow.getElementById('npw-overlay'),
modal: shadow.getElementById('npw-modal'),
name: shadow.getElementById('npw-name'),
phone: shadow.getElementById('npw-phone'),
submit: shadow.getElementById('npw-submit'),
error: shadow.getElementById('npw-error')
};
}
/* =========================
EVENT BINDING MELHORADO
========================= */
function bindEvents() {
var el = NPW.elements;
// Salvar HTML original do botão
NPW.state.buttonHTML = el.btn.innerHTML;
// Máscara do telefone
el.phone.addEventListener('input', maskPhone);
// Toggle modal
el.btn.addEventListener('click', toggleModal);
// Fechar ao clicar no overlay
el.overlay.addEventListener('click', function(e) {
if (e.target === el.overlay) closeModal();
});
// Submit
el.submit.addEventListener('click', function() {
if (!NPW.state.submitting) handleSubmit();
});
// Enter key
el.name.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
el.phone.focus();
}
});
el.phone.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
if (!NPW.state.submitting) handleSubmit();
}
});
// ESC global (melhorado)
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeModal();
});
}
/* =========================
MODAL - MELHORADO
========================= */
function toggleModal() {
var overlay = NPW.elements.overlay;
var btn = NPW.elements.btn;
var isOpen = overlay.classList.contains('open');
if (isOpen) {
closeModal();
} else {
openModalUI();
}
}
function openModalUI() {
var overlay = NPW.elements.overlay;
var btn = NPW.elements.btn;
overlay.classList.add('open');
btn.innerHTML = 'Fechar';
setTimeout(function() {
NPW.elements.name.focus();
}, 100);
clearError();
}
function closeModal() {
var overlay = NPW.elements.overlay;
var btn = NPW.elements.btn;
overlay.classList.remove('open');
btn.innerHTML = NPW.state.buttonHTML;
NPW.state.pendingUrl = null;
NPW.state.submitting = false;
clearError();
NPW.elements.name.value = '';
NPW.elements.phone.value = '';
}
function showError(msg, input) {
var errorDiv = NPW.elements.error;
if (errorDiv) errorDiv.textContent = msg;
if (input) {
input.classList.add('error');
input.focus();
}
}
function clearError() {
var errorDiv = NPW.elements.error;
if (errorDiv) errorDiv.textContent = '';
NPW.elements.name.classList.remove('error');
NPW.elements.phone.classList.remove('error');
}
/* =========================
WHATS LINKS - EVENT DELEGATION (MELHOR PARA REACT)
========================= */
function observeWhatsLinks() {
// Usar event delegation em vez de listeners individuais
document.addEventListener('click', handleLinkClick);
// Observer para links dinâmicos (apenas para manter compatibilidade)
new MutationObserver(function() {
// Não precisa fazer nada, o event delegation já cuida
}).observe(document.body, { childList: true, subtree: true });
}
function handleLinkClick(e) {
var link = e.target.closest(NPW.config.interceptSelector);
if (!link) return;
// Ignorar se for React Router ou navegação interna
if (link.getAttribute('data-npw-ignore') !== null) return;
if (link.target === '_blank') return;
// Verificar se é um link do WhatsApp
var href = link.href || '';
if (!href.includes('whatsapp.com') && !href.includes('wa.me')) return;
e.preventDefault();
e.stopPropagation();
NPW.state.pendingUrl = href;
openModalUI();
}
/* =========================
FORM - MELHORADO
========================= */
function getBodyData(name, phoneDigits) {
try {
var params = new URLSearchParams(window.location.search);
// Lista completa de parâmetros de tracking
var trackingParams = {
number: phoneDigits,
name: name,
id: NPW.config.clientId,
page: location.href,
gclid: '',
braid: '',
gbraid: '',
wbraid: '',
utm_source: '',
utm_medium: '',
utm_campaign: '',
utm_content: '',
utm_term: '',
fbclid: '',
msclkid: '',
li_fat_id: '',
integration: 'whatsapp'
};
// Preenche com valores da URL ou sessionStorage
for (var key in trackingParams) {
if (key === 'number' || key === 'name' || key === 'id' || key === 'page' || key === 'integration') continue;
var value = params.get(key);
if (value) {
sessionStorage.setItem(key, value);
trackingParams[key] = value;
} else {
trackingParams[key] = sessionStorage.getItem(key) || '';
}
}
// Constrói a string
var bodyParts = [];
for (var key in trackingParams) {
bodyParts.push(key + '=' + encodeURIComponent(trackingParams[key]));
}
return bodyParts.join('&');
} catch (error) {
// Fallback em caso de erro
return 'number=' + encodeURIComponent(phoneDigits) +
'&name=' + encodeURIComponent(name) +
'&id=' + encodeURIComponent(NPW.config.clientId) +
'&page=' + encodeURIComponent(location.href);
}
}
function maskPhone(e) {
var v = e.target.value.replace(/\D/g, '').slice(0, 11);
v = v.replace(/^(\d{2})(\d)/, '($1) $2');
v = v.replace(/(\d{5})(\d)/, '$1-$2');
e.target.value = v;
}
function handleSubmit() {
var name = NPW.elements.name.value.trim();
var phoneDigits = NPW.elements.phone.value.replace(/\D/g, '');
var submit = NPW.elements.submit;
clearError();
if (name.length < NPW.config.minNameLength) {
return showError('Informe seu nome completo.', NPW.elements.name);
}
if (phoneDigits.length !== NPW.config.phoneDigits) {
return showError('Número de WhatsApp inválido. Use DDD + 9 dígitos.', NPW.elements.phone);
}
// Popup
var popup = window.open('about:blank', '_blank');
NPW.state.submitting = true;
submit.disabled = true;
submit.textContent = 'Enviando...';
// Enviar para API
fetch(NPW.config.apiURL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: getBodyData(name, phoneDigits)
})
.catch(function(err) {
showError('Erro ao enviar. Tente novamente.', null);
})
.finally(function() {
// DataLayer (GTM)
if (window.dataLayer) {
window.dataLayer.push({
event: 'whatsapp_lead_submit',
leadsUserData: {
phone_number: '+55' + phoneDigits,
address: {
first_name: name,
country: 'BR'
}
}
});
}
// Redirecionar para WhatsApp
var finalUrl = NPW.state.pendingUrl || NPW.config.defaultWhatsLink;
if (popup) {
popup.location.href = finalUrl;
} else {
window.open(finalUrl, '_blank');
}
closeModal();
NPW.state.submitting = false;
submit.disabled = false;
submit.textContent = 'Enviar mensagem';
});
}
})();