1/** 2 * HubSpot form submission -> GTM dataLayer bridge, plus V3 (legacy) embed init. 3 * 4 * Pushes the same `form_submission` event shape that the EXT:form confirmation 5 * finisher and the redirect reader (GoogleTagmanager.typoscript) already emit, 6 * so the existing GTM trigger covers HubSpot forms without extra configuration. 7 * 8 * Writes only to the local dataLayer - no data leaves the page here. Consent 9 * gating for GA4 stays in GTM/Usercentrics, as for all other events. 10 */ 11(function () { 12 'use strict'; 13 14 var pushedConversions = {}; 15 16 function pushSubmission(formId) { 17 // Guard against duplicate events (both listeners could fire for one submission). 18 var key = formId || 'unknown'; 19 if (pushedConversions[key]) { 20 return; 21 } 22 pushedConversions[key] = true; 23 24 window.dataLayer = window.dataLayer || []; 25 window.dataLayer.push({ 26 event: 'form_submission', 27 form_type: 'hubspot', 28 form_id: formId || '', 29 form_lang: (document.documentElement.lang || '').slice(0, 2) 30 }); 31 } 32 33 // Modern (v4) forms: DOM CustomEvent dispatched on window. 34 window.addEventListener('hs-form-event:on-submission:success', function (event) { 35 var formId = ''; 36 try { 37 var api = window.HubSpotFormsV4 || window.HubspotFormsV4; 38 if (api && typeof api.getFormFromEvent === 'function') { 39 var form = api.getFormFromEvent(event); 40 if (form && typeof form.getFormId === 'function') { 41 formId = form.getFormId(); 42 } 43 } 44 } catch (e) { 45 // Ignore - event without form id is still worth tracking. 46 } 47 pushSubmission(formId); 48 }); 49 50 // Legacy (v2/v3) forms: postMessage from the form iframe. 51 window.addEventListener('message', function (event) { 52 if (event.data && event.data.type === 'hsFormCallback' && event.data.eventName === 'onFormSubmitted') { 53 pushSubmission(event.data.id || ''); 54 } 55 }); 56 57 function createLegacyEmbeds() { 58 var nodes = document.querySelectorAll('.hubspot-form-target'); 59 if (!nodes.length || !window.hbspt || !window.hbspt.forms || typeof window.hbspt.forms.create !== 'function') { 60 return false; 61 } 62 63 for (var i = 0; i < nodes.length; i++) { 64 var el = nodes[i]; 65 if (el.getAttribute('data-hs-initialized') === '1') { 66 continue; 67 } 68 var formId = el.getAttribute('data-form-id'); 69 var portalId = el.getAttribute('data-portal-id'); 70 var region = el.getAttribute('data-region') || 'eu1'; 71 if (!formId || !portalId || !el.id) { 72 continue; 73 } 74 el.setAttribute('data-hs-initialized', '1'); 75 var options = { 76 region: region, 77 portalId: portalId, 78 formId: formId, 79 target: '#' + el.id 80 }; 81 if (el.getAttribute('data-developer-embed') === '1') { 82 options.css = ''; 83 options.cssRequired = ''; 84 } 85 window.hbspt.forms.create(options); 86 } 87 return true; 88 } 89 90 function initLegacyEmbeds() { 91 if (createLegacyEmbeds()) { 92 return; 93 } 94 window.hsFormsOnReady = window.hsFormsOnReady || []; 95 window.hsFormsOnReady.push(createLegacyEmbeds); 96 window.addEventListener('load', createLegacyEmbeds); 97 } 98 99 if (document.readyState === 'loading') { 100 document.addEventListener('DOMContentLoaded', initLegacyEmbeds); 101 } else { 102 initLegacyEmbeds(); 103 } 104})();
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.