1(function (window, document) { 2 'use strict'; 3 4 if (window.EAD_CRM_FORM_BRIDGE_ENABLED === false) { 5 return; 6 } 7 8 var MESSAGE_SOURCE = 'ead-crm-form'; 9 var CONFIRMED_LEAD_KEY = 'ead_confirmed_lead'; 10 var TRACKABLE_EVENTS = { 11 form_view: true, 12 form_step_start: true, 13 form_step_complete: true, 14 form_step_error: true, 15 form_submit_success: true, 16 form_submit_error: true 17 }; 18 var instances = []; 19 20 function normalizeOrigin(value) { 21 try { 22 var parsed = new URL(String(value || '').trim()); 23 return (parsed.protocol === 'http:' || parsed.protocol === 'https:') 24 ? parsed.origin 25 : ''; 26 } catch (error) { 27 return ''; 28 } 29 } 30 31 function safeToken(value, fallback) { 32 var normalized = String(value == null ? '' : value) 33 .trim() 34 .toLowerCase() 35 .replace(/[^a-z0-9_]+/g, '_') 36 .replace(/^_+|_+$/g, ''); 37 return normalized || fallback || ''; 38 } 39 40 function validHash(value) { 41 var normalized = String(value || '').trim().toLowerCase(); 42 return /^[a-f0-9]{64}$/.test(normalized) ? normalized : ''; 43 } 44 45 function validLeadId(value) { 46 var normalized = String(value == null ? '' : value).trim(); 47 return /^\d{1,20}$/.test(normalized) && normalized !== '0' 48 ? normalized 49 : null; 50 } 51 52 function pickAttribution(key, stored, params) { 53 return params.get(key) || stored[key] || ''; 54 } 55 56 function journeyFor(instance) { 57 if (!instance.journey && window.EadTrack && typeof window.EadTrack.getJourney === 'function') { 58 instance.journey = window.EadTrack.getJourney(); 59 } 60 return instance.journey; 61 } 62 63 function updateProgress(instance, data) { 64 var status = instance.iframe.dataset.progressId ? document.getElementById(instance.iframe.dataset.progressId) : null; 65 if (!status) return; 66 // Neste formulário o contato é salvo primeiro; perfil e negócio são complementos opcionais. 67 var step = Number(data.step); 68 if (data.event === 'form_submit_success' && validLeadId(data.lead_id)) { 69 status.textContent = 'Solicitação recebida. Você pode complementar seu perfil.'; 70 } else if (data.event === 'form_view' || data.event === 'form_step_start') { 71 if (step === 1) status.textContent = 'Seus dados de contato'; 72 if (step === 2) status.textContent = 'Perfil · complemento opcional'; 73 if (step === 3) status.textContent = 'Seu projeto · complemento opcional'; 74 } 75 } 76 77 function buildAttribution(instance) { 78 var params = new URLSearchParams(window.location.search); 79 var stored = (window.EadUtm && typeof window.EadUtm.get === 'function') 80 ? window.EadUtm.get() 81 : {}; 82 var sanitizeUrl = (window.EadUtm && typeof window.EadUtm.sanitizeAttributionUrl === 'function') 83 ? window.EadUtm.sanitizeAttributionUrl 84 : function (value) { return String(value || ''); }; 85 var pageUrl = sanitizeUrl(window.location.href); 86 var referrer = (window.EadUtm && typeof window.EadUtm.pickReferrer === 'function') 87 ? window.EadUtm.pickReferrer() 88 : ''; 89 90 return { 91 type: 'eadmin-embed-attribution', 92 source: 'ead-site', 93 fonte: pickAttribution('fonte', stored, params) 94 || instance.iframe.dataset.fonte 95 || 'site', 96 utm_source: pickAttribution('utm_source', stored, params), 97 utm_medium: pickAttribution('utm_medium', stored, params), 98 // Landing pages de campanha marcam o lead mesmo sem UTM na URL (orgânico/direto); 99 // uma UTM real sempre prevalece. 100 utm_campaign: pickAttribution('utm_campaign', stored, params) 101 || safeToken(instance.iframe.dataset.defaultUtmCampaign, ''), 102 utm_term: pickAttribution('utm_term', stored, params), 103 utm_content: pickAttribution('utm_content', stored, params), 104 gclid: pickAttribution('gclid', stored, params), 105 gbraid: pickAttribution('gbraid', stored, params), 106 wbraid: pickAttribution('wbraid', stored, params), 107 gclid_captured_at: pickAttribution('gclid_captured_at', stored, params), 108 gbraid_captured_at: pickAttribution('gbraid_captured_at', stored, params), 109 wbraid_captured_at: pickAttribution('wbraid_captured_at', stored, params), 110 fbclid: pickAttribution('fbclid', stored, params), 111 fbp: pickAttribution('fbp', stored, params), 112 fbc: pickAttribution('fbc', stored, params), 113 li_fat_id: pickAttribution('li_fat_id', stored, params), 114 msclkid: pickAttribution('msclkid', stored, params), 115 landing_page_url: sanitizeUrl(stored.landing_page_url || pageUrl), 116 first_touch_at: pickAttribution('first_touch_at', stored, params), 117 last_touch_at: stored.last_touch_at || params.get('last_touch_at') || '', 118 parceiro_id: (window.EadPartner && typeof window.EadPartner.getId === 'function') 119 ? window.EadPartner.getId() 120 : '', 121 ab_demo_variant: (window.EadConversion && typeof window.EadConversion.readAbVariant === 'function') 122 ? window.EadConversion.readAbVariant() 123 : (document.documentElement.getAttribute('data-ab') || ''), 124 lead_segment: journeyFor(instance) ? journeyFor(instance).lead_segment : '', 125 entry_cta: journeyFor(instance) ? journeyFor(instance).entry_cta : '', 126 page_url: pageUrl, 127 pagina_origem: pageUrl, 128 referrer: referrer 129 }; 130 } 131 132 function sendAttribution(instance) { 133 try { 134 if (instance.iframe.contentWindow) { 135 instance.iframe.contentWindow.postMessage( 136 buildAttribution(instance), 137 instance.crmOrigin 138 ); 139 } 140 } catch (error) { 141 /* O iframe continua funcional mesmo sem a ponte de atribuição. */ 142 } 143 } 144 145 function resize(instance, height) { 146 var parsed = parseInt(String(height || ''), 10); 147 if (!Number.isFinite(parsed) || parsed <= 0) { 148 return; 149 } 150 151 var maxHeight = Math.max(instance.minHeight, Math.min(parsed, 5000));
152 instance.iframe.style.height = maxHeight + 'px'; 153 instance.iframe.setAttribute('scrolling', 'no'); 154 if (instance.loadingElement) { 155 instance.loadingElement.hidden = true; 156 } 157 } 158 159 function eventDedupeKey(data) { 160 if (data.event === 'form_view') { 161 return 'form_view'; 162 } 163 if (data.event === 'form_step_start' || data.event === 'form_step_complete') { 164 return data.event + ':' + String(data.step || ''); 165 } 166 if (data.event === 'form_submit_success') { 167 return data.event + ':' + String(data.lead_id || ''); 168 } 169 return ''; 170 } 171 172 function rememberConfirmedLead(formId, leadId, segment) { 173 if (!leadId) { 174 return; 175 } 176 177 try { 178 window.sessionStorage.setItem(CONFIRMED_LEAD_KEY, JSON.stringify({ 179 form_id: formId, 180 lead_id: leadId, 181 // Perfil informado no formulário do CRM (empresas|infoprodutores), quando o CRM enviar. 182 segment: segment || '', 183 recorded_at: Date.now() 184 })); 185 } catch (error) { 186 /* sessionStorage indisponÃvel */ 187 } 188 } 189 190 // Segmento vindo do próprio CRM no postMessage de form_submit_success (data.segment). 191 function crmSegment(data) { 192 var value = String((data && (data.segment || data.lead_segment)) || '').toLowerCase(); 193 return (value === 'empresas' || value === 'infoprodutores') ? value : ''; 194 } 195 196 function pushTrackingEvent(instance, data) { 197 if (!TRACKABLE_EVENTS[data.event]) { 198 return; 199 } 200 201 if (data.event === 'form_submit_success' && !validLeadId(data.lead_id)) return; 202 var dedupeKey = eventDedupeKey(data); 203 if (dedupeKey && instance.sentEvents[dedupeKey]) { 204 return; 205 } 206 if (dedupeKey) { 207 instance.sentEvents[dedupeKey] = true; 208 } 209 210 var step = parseInt(String(data.step || ''), 10); 211 var leadId = validLeadId(data.lead_id); 212 var payload = { 213 event: 'ead_form_' + data.event, 214 form_id: instance.formId, 215 form_step: step >= 1 && step <= 3 ? step : null, 216 form_step_name: safeToken(data.step_name, null) || null, 217 form_field: safeToken(data.field, null) || null, 218 form_error_type: safeToken(data.error_type, null) || null, 219 form_submission_phase: safeToken(data.submission_phase, null) || null,
220 lead_id: leadId 221 }; 222 223 if (data.event === 'form_submit_success' && data.user_data && typeof data.user_data === 'object') { 224 var emailHash = validHash(data.user_data.sha256_email_address); 225 var phoneHash = validHash(data.user_data.sha256_phone_number); 226 var userData = {}; 227 if (emailHash) { 228 userData.sha256_email_address = emailHash; 229 } 230 if (phoneHash) { 231 userData.sha256_phone_number = phoneHash; 232 } 233 if (Object.keys(userData).length > 0) { 234 payload.user_data = userData; 235 } 236 } 237 238 var journey = journeyFor(instance); 239 var fromCrm = crmSegment(data); 240 if (journey) { 241 // Perfil declarado no formulário tem prioridade sobre o interesse de navegação. 242 payload.lead_segment = fromCrm || journey.lead_segment; 243 payload.segment_source = fromCrm ? 'crm' : journey.segment_source; 244 payload.entry_cta = journey.entry_cta; 245 payload.site_version = journey.site_version; 246 // Campos existentes na configuração publicada do GTM: sem reenviar o lead. 247 payload.cta = journey.entry_cta || 'demonstracao'; 248 payload.local = (journey.entry_location || 'demonstracao') + '_' + journey.lead_segment; 249 payload.plano = journey.plan_id || null; 250 } 251 updateProgress(instance, data); 252 window.dataLayer = window.dataLayer || []; 253 window.dataLayer.push(payload); 254 255 if (data.event === 'form_submit_success') { 256 rememberConfirmedLead(instance.formId, leadId, crmSegment(data)); 257 } 258 } 259 260 function instanceForSource(source, origin) { 261 return instances.find(function (instance) { 262 return instance.iframe.contentWindow === source && instance.crmOrigin === origin; 263 }) || null; 264 } 265 266 function onMessage(event) { 267 var instance = instanceForSource(event.source, event.origin); 268 if (!instance || !event.data || typeof event.data !== 'object') { 269 return; 270 } 271 272 var data = event.data; 273 if (data.source === MESSAGE_SOURCE) { 274 if (data.event === 'resize') { 275 resize(instance, data.height); 276 return; 277 } 278 pushTrackingEvent(instance, data); 279 if (data.event === 'form_view') { 280 if (instance.loadingElement) { 281 instance.loadingElement.hidden = true; 282 } 283 sendAttribution(instance); 284 } 285 return; 286 } 287 288 // Compatibilidade durante o deploy escalonado do CRM. 289 if (data.type === 'eadmin-embed-request-attribution') { 290 sendAttribution(instance); 291 return; 292 } 293 if (data.type === 'eadmin-embed-resize') { 294 resize(instance, data.height); 295 } 296 } 297 298 function initIframe(iframe) { 299 if (!iframe || iframe.dataset.eadminCrmBridgeReady === '1') { 300 return null; 301 } 302 303 var crmOrigin = normalizeOrigin( 304 iframe.dataset.crmOrigin || 'https://crm.eadplataforma.com' 305 ); 306 if (!crmOrigin) { 307 return null; 308 } 309 310 var instance = { 311 iframe: iframe, 312 crmOrigin: crmOrigin, 313 formId: safeToken(iframe.dataset.formId, 'demonstracao'), 314 minHeight: Math.max(160, parseInt(iframe.dataset.minHeight || '200', 10) || 200), 315 loadingElement: iframe.dataset.loadingId 316 ? document.getElementById(iframe.dataset.loadingId) 317 : null, 318 sentEvents: {} 319 }; 320 iframe.dataset.eadminCrmBridgeReady = '1'; 321 iframe.addEventListener('load', function () { 322 if (instance.loadingElement) { 323 instance.loadingElement.hidden = true; 324 } 325 sendAttribution(instance); 326 }); 327 instances.push(instance); 328 329 var base = iframe.getAttribute('data-embed-base'); 330 if (base) { 331 var url = new URL(base, window.location.origin); 332 var fonte = iframe.dataset.fonte || url.searchParams.get('fonte') || 'site'; 333 url.searchParams.set('fonte', fonte); 334 iframe.src = url.toString(); 335 } 336 337 window.setTimeout(function () { 338 sendAttribution(instance); 339 }, 500); 340 341 return instance; 342 } 343 344 function init() { 345 document.querySelectorAll('[data-eadmin-crm-form]').forEach(initIframe); 346 } 347 348 window.addEventListener('message', onMessage); 349 window.EadCrmFormBridge = { 350 init: init, 351 has: function (iframe) { 352 return !!(iframe && iframe.dataset.eadminCrmBridgeReady === '1'); 353 } 354 }; 355 356 if (document.readyState === 'loading') { 357 document.addEventListener('DOMContentLoaded', init); 358 } else { 359 init(); 360 } 361})(window, document);
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.