PageSourceSearch

https://eadplataforma.com/assets/js/crm-form-bridge.js?v=1789756600

js eadplataforma.com collected 2026-09-24 22:12:44 UTC 12,480 bytes, 361 lines download raw bytes

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.