/* ================================================================================================ HUBSPOT UNIVERSAL CUSTOM JS EMBED JS Version: v20251028.1-cr Client: Pregis Platforms: WordPress, Optimizely Backend: HubSpot NOTES - Unified for both WordPress (v2 iframe) and Optimizely (v4 inline) environments - Automatically detects available HubSpot API (v2 or v4) and renders accordingly - Forces HubSpot styling in both modes • v2 uses iframe and css: 'https://js.hsforms.net/forms/shell.css' • v4 injects shell.css link into '' if missing - Includes console warning if shell.css fails to load due to CSP - Preserves all prior features • UTM cookie persistence + container tagging • Robust ID enrichment from event.detail or iframe src • Unified dataLayer push for 'hubspot-form-submit' (de-duped) • Full debug logging via '?autofill_debug=1' • Backward compatible with existing markup: - '
' - Multi-form safe • Dedupes render targets and listeners - Auto-height behavior • v2 (iframe) auto-resizes via HubSpot postMessage (no extra JS needed here) • v4 (inline) renders directly in DOM (no iframe height concerns) • Adding the reCAPTCHA disclosure below the iframe does not affect v2 auto-height - If scrollbars appear, check parent/ancestor CSS for fixed heights or overflow IMPORTANT (reCAPTCHA placement) - For reCAPTCHA compliance and UX, this file includes a **separate IIFE at the bottom** that • Always hides the floating Google badge • In 'v2 (iframe)' contexts, inserts the required Google disclosure directly below the injected iframe - We cannot inject inside the iframe DOM due to cross-origin sandboxing • In 'v4 (inline)' contexts, this block is a no-op by default (we can add an inline-placement branch later if needed) - Keeping this logic outside the main IIFE avoids scope issues and keeps form rendering concerns separate TROUBLESHOOTING/TIPS - If HubSpot forms appear unstyled, verify that • CSP allows 'https://js.hsforms.net' in 'style-src' and 'script-src' • Any consent manager (ex: OneTrust) loads this script only after marketing consent - If forms don’t render, ensure 'https://js.hsforms.net/forms/v2.js' is not blocked - Always cache-bust the hosted JS URL after updates (ex: '?v=20251028.1') - Debug mode available via '?autofill_debug=1' for console tracing ================================================================================================ */ (function () { if (window.__HS_PREGIS_UNIFIED__) return; window.__HS_PREGIS_UNIFIED__ = true; const DEFAULT_PORTAL_ID = '5177788'; const DEFAULT_REGION = 'na1'; const COOKIE_DAYS = 90; const HUBSPOT_SHELL_CSS = 'https://js.hsforms.net/forms/shell.css'; // Keep HS iframe flexible in containers (function injectHsIframeSafetyCss() { if (document.getElementById('hs-iframe-safety-style')) return; const style = document.createElement('style'); style.id = 'hs-iframe-safety-style'; style.textContent = ` /* Ensure HubSpot form iframes resize properly and never clip */ [data-hubspot-form] iframe { width: 100% !important; border: 0; } [data-hubspot-form] { overflow: visible !important; } `; document.head.appendChild(style); })(); const ALIASES = { utm_source: 'utm_source', source: 'utm_source', utm_medium: 'utm_medium', medium: 'utm_medium', utm_campaign: 'utm_campaign', campaign: 'utm_campaign', utm_content: 'utm_content', content: 'utm_content', utm_term: 'utm_term', term: 'utm_term', referrer: 'referrer', document_referrer: 'referrer', page_url: 'page_url', url: 'page_url', gclid: 'gclid', msclkid: 'msclkid', fbclid: 'fbclid', zi_company_name: 'zoominfo_company_name', zoominfo_company_name: 'zoominfo_company_name', zi_industry: 'zoominfo_industry', zoominfo_industry: 'zoominfo_industry', zi_subindustry: 'zoominfo_subindustry', zoominfo_subindustry: 'zoominfo_subindustry', zi_naics: 'zoominfo_naics', zoominfo_naics: 'zoominfo_naics', zi_revenue: 'zoominfo_revenue', zoominfo_revenue: 'zoominfo_revenue', zi_employee: 'zoominfo_employee', zoominfo_employee: 'zoominfo_employee' }; const qs = new URLSearchParams(location.search); const DEBUG = qs.get('autofill_debug') === '1'; const log = (...a) => { if (DEBUG) console.log('[HS Embed]', ...a); }; function urlVal(key) { let v = qs.get(key); if (v != null) return v; const L = key.toLowerCase(); for (const [k, val] of qs.entries()) if (k.toLowerCase() === L) return val; return ''; } function setCookie(name, value, days = COOKIE_DAYS) { const d = new Date(); d.setTime(d.getTime() + days * 864e5); document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value || '')};expires=${d.toUTCString()};path=/;SameSite=Lax`; } function getCookie(name) { const t = encodeURIComponent(name) + '='; const parts = document.cookie ? document.cookie.split(';') : []; for (let c of parts) { c = c.trim(); if (c.indexOf(t) === 0) return decodeURIComponent(c.substring(t.length)); } return ''; } // Persist any UTM/attribution params (case-insensitive) Object.keys(ALIASES).forEach(a => { const v = (urlVal(a) || '').trim(); if (v) setCookie(a, v); }); if (!qs.has('referrer') && document.referrer) setCookie('referrer', document.referrer); function collectUTMsSafe() { const pick = k => { const v = (urlVal(k) || getCookie(k) || '').trim(); return v || null; }; const out = {}; ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'msclkid', 'fbclid'].forEach(k => { const v = pick(k); if (v) out[k] = v; }); return out; } function ensureShellCssOnce() { try { const present = Array .from(document.querySelectorAll('link[rel="stylesheet"]')) .some(l => (l.href || '').includes('/forms/shell.css')); if (!present) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = HUBSPOT_SHELL_CSS; document.head.appendChild(link); log('Injected HubSpot shell.css'); } } catch (e) { console.warn('[HS Embed] ensureShellCssOnce failed', e); } } function inlineFormsPresent() { return !!document.querySelector('.hs-form') && !document.querySelector('iframe[src*="hsforms"], iframe[src*="js.hsforms.net"]'); } // ----- RENDER (v2 if available; otherwise style inline v4) ---------------------------------- (function bootstrapV2Render() { const V2_SRC = 'https://js.hsforms.net/forms/v2.js'; const SHELL = 'https://js.hsforms.net/forms/shell.css'; const targets = () => document.querySelectorAll('[data-hubspot-form]'); function loadV2IfNeeded(cb) { if (window.hbspt?.forms?.create) return cb(); const present = document.querySelector(`script[src*="${V2_SRC}"]`); if (!present) { const s = document.createElement('script'); s.src = V2_SRC; s.async = true; s.defer = true; s.crossOrigin = 'anonymous'; // <-- ADD THIS LINE HERE s.onload = () => waitForV2(cb); s.onerror = () => console.warn('[HS Embed] Failed to load v2.js'); document.head.appendChild(s); } else { waitForV2(cb); } } function waitForV2(cb, t0 = Date.now()) { if (window.hbspt?.forms?.create) return cb(); if (Date.now() - t0 > 8000) { console.warn('[HS Embed] v2.js not ready after 8s'); return; } setTimeout(() => waitForV2(cb, t0), 50); } const rendered = new WeakSet(); function ensureElementId(el, formId) { if (!el.id) el.id = `hsf-${(formId || 'x').replace(/[^a-z0-9_-]/gi, '').slice(0, 36)}`; return `#${el.id}`; } function renderTarget(el) { if (!el || rendered.has(el)) return; const formId = el.getAttribute('data-form-id'); if (!formId) { console.warn('[HS Embed] Missing data-form-id on', el); return; } const portalId = el.getAttribute('data-portal-id') || DEFAULT_PORTAL_ID; const region = el.getAttribute('data-region') || DEFAULT_REGION; // Make target unambiguous and DOM-anchored const targetSelector = ensureElementId(el, formId); if (!document.documentElement.contains(el)) { console.warn('[HS Embed] Target not in DOM at render time:', el); return; } // Hard reset in case inline/v4 markup was injected into our container try { el.querySelectorAll('.hs-form').forEach(n => n.remove()); } catch (_) { } el.innerHTML = ''; // Microtask to beat late inline renderers Promise.resolve().then(() => { try { window.hbspt.forms.create({ region, portalId, formId, target: targetSelector, // <-- selector string (not element) onFormReady() { console.log('[HS Embed] v2 ready', { portalId, formId, target: targetSelector }); }, onFormSubmit() { /* unified listeners handle dataLayer */ } }); rendered.add(el); } catch (e) { console.warn('[HS Embed] v2 render failed', e); } }); } function renderAll() { targets().forEach(renderTarget); } function start() { loadV2IfNeeded(renderAll); const mo = new MutationObserver(muts => { muts.forEach(m => (m.addedNodes || []).forEach(n => { if (n?.nodeType === 1) { if (n.matches?.('[data-hubspot-form]')) renderTarget(n); n.querySelectorAll?.('[data-hubspot-form]').forEach(renderTarget); } })); }); mo.observe(document.documentElement, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', start); } else { start(); } })(); // ----- Tag containers with UTMs ------------------------------------------------------------- (function armContainerUtmHints() { const utms = collectUTMsSafe(); if (!Object.keys(utms).length) return; const tag = (el) => { try { for (const k in utms) el.dataset[`utm_${k}`] = utms[k]; el.dataset.pageUrl = location.href; el.dataset.pageTitle = document.title; } catch { /* no-op */ } }; document.querySelectorAll('[data-hubspot-form]').forEach(tag); const mo = new MutationObserver(muts => { muts.forEach(m => { (m.addedNodes || []).forEach(n => { if (n?.nodeType === 1) { if (n.matches?.('[data-hubspot-form]')) tag(n); n.querySelectorAll?.('[data-hubspot-form]').forEach(tag); } }); }); }); mo.observe(document.documentElement, { childList: true, subtree: true }); })(); // ----- ID enrichment helpers (v2 via iframe src; v4 via event.detail) ----------------------- function parseHsIframeSrc(src) { if (!src) return {}; try { const u = new URL(src); return { portalId: u.searchParams.get('_hsPortalId') || u.searchParams.get('portalId') || undefined, formGuid: u.searchParams.get('_hsFormId') || u.searchParams.get('formId') || undefined, formInstanceId: u.searchParams.get('_hsInstanceId') || u.searchParams.get('formInstanceId') || undefined }; } catch { return {}; } } function findIframeFromMessageEvent(ev) { try { const frames = document.querySelectorAll('iframe'); for (const f of frames) if (f.contentWindow === ev.source) return f; } catch { /* no-op */ } return null; } function findIframeFromDomEvent(evt) { const path = evt?.composedPath?.() || []; for (const n of path) { if (n && n.tagName === 'IFRAME') return n; if (n?.firstChild && n.firstChild.tagName === 'IFRAME') return n.firstChild; } if (evt?.srcElement?.firstChild?.tagName === 'IFRAME') return evt.srcElement.firstChild; if (evt?.target?.firstChild?.tagName === 'IFRAME') return evt.target.firstChild; if (evt?.srcElement?.querySelector) { const f = evt.srcElement.querySelector('iframe'); if (f) return f; } if (evt?.target?.querySelector) { const f = evt.target.querySelector('iframe'); if (f) return f; } return null; } function enrichCtxFromV4CustomEvent(evt, ctx) { const d = evt?.detail || {}; ctx.portalId = ctx.portalId || d.portalId || d.id?.portalId || d.form?.portalId; ctx.formGuid = ctx.formGuid || d.formGuid || d.id?.formGuid || d.form?.formGuid || d.formId; ctx.formInstanceId = ctx.formInstanceId || d.formInstanceId || d.context?.formInstanceId || d.id?.formInstanceId; if (!ctx.portalId || !ctx.formGuid || !ctx.formInstanceId) { const iframe = findIframeFromDomEvent(evt); const from = parseHsIframeSrc(iframe?.getAttribute('src')); ctx.portalId = ctx.portalId || from.portalId; ctx.formGuid = ctx.formGuid || from.formGuid; ctx.formInstanceId = ctx.formInstanceId || from.formInstanceId; } return ctx; } function enrichCtxFromMessageEvent(ev, ctx) { if (!ctx.portalId || !ctx.formGuid || !ctx.formInstanceId) { const iframe = findIframeFromMessageEvent(ev); const from = parseHsIframeSrc(iframe?.getAttribute('src')); ctx.portalId = ctx.portalId || from.portalId; ctx.formGuid = ctx.formGuid || from.formGuid; ctx.formInstanceId = ctx.formInstanceId || from.formInstanceId; } return ctx; } // ----- HubSpot dataLayer (unified, de-duped) ------------------------------------------------ (function armHubSpotUnifiedListeners() { const seen = new Map(); const DEDUPE_MS = 3000; const shouldPushOnce = (key) => { const now = Date.now(); const last = seen.get(key) || 0; if (now - last < DEDUPE_MS) return false; seen.set(key, now); return true; }; function toAliasedFields(fieldsArray) { const out = {}; (fieldsArray || []).forEach(f => { const raw = (f?.name || '').toLowerCase(); for (const aliasKey in ALIASES) { if (ALIASES[aliasKey] === raw) out[aliasKey] = f?.value; } }); if (!out.page_url) out.page_url = location.href; const backfill = collectUTMsSafe(); for (const k in backfill) if (!out[k]) out[k] = backfill[k]; return out; } function pushCanonical(ctx, fieldsObj, eventName) { const payload = { event: 'hubspot-form-submit', hubspot_event: eventName || 'onFormSubmitted', hubspot_portal_id: ctx.portalId || null, hubspot_form_guid: ctx.formGuid || null, hubspot_form_instance_id: ctx.formInstanceId || null, page_url: location.href, page_title: document.title, ...fieldsObj }; window.dataLayer = window.dataLayer || []; window.dataLayer.push(payload); if (DEBUG) console.log('[HS → dataLayer]', payload); } // v3/v4 postMessage (iframe) window.addEventListener('message', function (event) { const d = event && event.data; if (!d || d.type !== 'hsFormCallback' || d.eventName !== 'onFormSubmitted') return; let ctx = { portalId: d.id?.portalId, formGuid: d.id?.formGuid, formInstanceId: d.id?.formInstanceId }; ctx = enrichCtxFromMessageEvent(event, ctx); const key = ctx.formInstanceId || ctx.formGuid || 'unknown'; if (!shouldPushOnce(key)) return; const aliased = toAliasedFields(d.data?.fields); pushCanonical(ctx, aliased, d.eventName); }, true); // v4 CustomEvent (inline) document.addEventListener('hs-form-event:on-submission:success', function (evt) { try { let ctx = { portalId: evt?.detail?.portalId || evt?.detail?.id?.portalId || evt?.detail?.form?.portalId, formGuid: evt?.detail?.formGuid || evt?.detail?.id?.formGuid || evt?.detail?.form?.formGuid || evt?.detail?.formId, formInstanceId: evt?.detail?.formInstanceId || evt?.detail?.context?.formInstanceId || evt?.detail?.id?.formInstanceId }; ctx = enrichCtxFromV4CustomEvent(evt, ctx); const fieldsArray = evt?.detail?.submissionValues || evt?.detail?.data?.fields || evt?.detail?.fields || evt?.detail?.values || []; const key = ctx.formInstanceId || ctx.formGuid || 'unknown'; if (!shouldPushOnce(key)) return; const aliased = toAliasedFields(fieldsArray); pushCanonical(ctx, aliased, 'onFormSubmitted'); } catch (e) { if (DEBUG) console.warn('[HS v4 CustomEvent listener error]', e); } }, true); })(); })(); // ----- reCAPTCHA UX (separate IIFE) ------------------------------------------------------------- (function recaptchaDisclosure_v2orInline() { // Hide badge globally (policy-compliant when paired with disclosure) if (!document.getElementById('hs-recaptcha-hide-style')) { const style = document.createElement('style'); style.id = 'hs-recaptcha-hide-style'; style.textContent = ` html body .grecaptcha-badge, .grecaptcha-badge { display:none !important; visibility:hidden !important; opacity:0 !important; pointer-events:none !important; } .hs-recaptcha-disclosure { font-family:'Roboto',sans-serif; color:#33475b; font-size:12px; margin-top:14px; line-height:24px; } .hs-recaptcha-disclosure a { text-decoration: underline; color:#0000EE; } .hs-recaptcha-disclosure a:hover { color:#551A8B; } `; document.head.appendChild(style); } const DISC_HTML = 'This site is protected by reCAPTCHA and the Google ' + 'Privacy Policy and ' + 'Terms of Service apply.'; const makeDisclosure = () => { const p = document.createElement('p'); p.className = 'hs-recaptcha-disclosure'; p.innerHTML = DISC_HTML; return p; }; // Be flexible: HubSpot iframes sometimes have empty src initially, or different titles. const isHsFormIframe = (f) => { if (!f || f.tagName !== 'IFRAME') return false; const id = (f.getAttribute('id') || '').toLowerCase(); const cls = (f.getAttribute('class') || '').toLowerCase(); const src = (f.getAttribute('src') || '').toLowerCase(); const title = (f.getAttribute('title') || '').toLowerCase(); return ( id.startsWith('hs-form-iframe') || cls.includes('hs-form-iframe') || /(?:js\.hsforms\.net|hsforms)/.test(src) || title.includes('hubspot') || title.includes('form') ); }; const findInlineFormUnder = (container) => container.querySelector('.hs-form') || container.querySelector('[data-form-id] .hs-form'); // Prefer descendants; otherwise accept immediate next sibling (HubSpot sometimes inserts next to target) function findHsIframeNear(container) { let iframe = Array.from(container.querySelectorAll('iframe')).find(isHsFormIframe); if (iframe) return iframe; const sib = container.nextElementSibling; if (sib) { if (isHsFormIframe(sib)) return sib; iframe = sib.querySelector && Array.from(sib.querySelectorAll('iframe')).find(isHsFormIframe); if (iframe) return iframe; } return null; } const processed = new WeakSet(); function placeForContainer(container) { // Case 1: v2/v3 iframe const iframe = findHsIframeNear(container); if (iframe && !processed.has(iframe)) { if (!iframe.nextElementSibling || !iframe.nextElementSibling.classList || !iframe.nextElementSibling.classList.contains('hs-recaptcha-disclosure')) { iframe.insertAdjacentElement('afterend', makeDisclosure()); } processed.add(iframe); return; } // Case 2: v4 inline (no iframe) — append after the inline form block const inlineForm = findInlineFormUnder(container); if (inlineForm && !processed.has(inlineForm)) { if (!inlineForm.nextElementSibling || !inlineForm.nextElementSibling.classList || !inlineForm.nextElementSibling.classList.contains('hs-recaptcha-disclosure')) { inlineForm.insertAdjacentElement('afterend', makeDisclosure()); } processed.add(inlineForm); } } function run() { document.querySelectorAll('[data-hubspot-form]').forEach(placeForContainer); } // Initial + observe for injected nodes; plus a few retries for late src/title population run(); const mo = new MutationObserver(run); mo.observe(document.documentElement, { childList: true, subtree: true }); setTimeout(run, 600); setTimeout(run, 1500); setTimeout(run, 3000); })();