1(() => { 2 'use strict'; 3 window.dataLayer = window.dataLayer || []; 4 5 const attributionKeys = [ 6 'utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 7 'gclid', 'gbraid', 'wbraid', 'fbclid', 'li_fat_id', 'msclkid' 8 ]; 9 const attributionStorageKey = 'pdi_attribution_v1'; 10 const successKeys = new Map(); 11 const startedForms = new WeakSet(); 12 const downloadPattern = /\.(pdf|docx?|xlsx?|pptx?|zip|csv|txt|xml)(?:[?#]|$)/i; 13 14 const umamiEventNames = { 15 pdi_download: 'asset_download', 16 pdi_outbound_click: 'outbound_click', 17 pdi_cta_click: 'cta_click', 18 pdi_button_click: 'button_click', 19 pdi_form_start: 'form_start', 20 dirty_data_index_form_start: 'dirty_data_index_form_start', 21 pdi_form_submit: 'form_submit', 22 pdi_form_success: 'form_success', 23 generate_lead: 'lead_generated', 24 pdi_form_validation_failed: 'form_validation_failed', 25 pdi_thank_you_view: 'thank_you_view' 26 }; 27 const allowedAnalyticsKeys = new Set([ 28 'page_path', 'form_provider', 'form_id', 'form_name', 'lead_type', 29 'file_extension', 'status', 'result', 'element_text', 'destination_host', 30 'ready_count', 'total_count' 31 ]); 32 const safeAnalyticsData = (data = {}) => Object.fromEntries( 33 Object.entries(data).filter(([key, value]) => { 34 if (!allowedAnalyticsKeys.has(key) || !['string', 'number', 'boolean'].includes(typeof value)) return false; 35 return value !== ''; 36 }).map(([key, value]) => [ 37 key, 38 typeof value === 'string' ? value.slice(0, 120) : value 39 ]) 40 ); 41 const track = (event, data = {}) => { 42 if (!window.umami || typeof window.umami.track !== 'function') return; 43 try { window.umami.track(event, safeAnalyticsData(data)); } catch (_) {} 44 }; 45 window.pdiTrackEvent = (event, data = {}) => track(event, { 46 page_path: window.location.pathname, 47 ...data 48 }); 49 const push = (event, data = {}) => { 50 window.dataLayer.push({event, ...data}); 51 if (umamiEventNames[event]) track(umamiEventNames[event], data); 52 }; 53 const text = (element) => ((element && (element.getAttribute('aria-label') || element.textContent)) || '') 54 .trim().replace(/\s+/g, ' ').slice(0, 120); 55 const href = (element) => (element && element.getAttribute && element.getAttribute('href')) || ''; 56 const formName = (form) => form.getAttribute('data-form-name') || form.getAttribute('name') || form.id || 'website_form'; 57 58 function readAttribution() { 59 let stored = {}; 60 try { stored = JSON.parse(sessionStorage.getItem(attributionStorageKey) || '{}') || {}; } catch (_) {} 61 const query = new URLSearchParams(window.location.search); 62 attributionKeys.forEach((key) => { 63 if (query.has(key)) stored[key] = String(query.get(key) || '').slice(0, 200); 64 }); 65 try { sessionStorage.setItem(attributionStorageKey, JSON.stringify(stored)); } catch (_) {} 66 return stored; 67 } 68 69 const attribution = readAttribution(); 70 71 function populateAttribution(form) { 72 attributionKeys.forEach((key) => { 73 const fields = form.querySelectorAll('[name="' + key + '"]'); 74 fields.forEach((field) => { 75 if (!field.value && attribution[key]) field.value = attribution[key]; 76 }); 77 }); 78 } 79 80 function commonFormData(form, provider) { 81 return { 82 form_provider: provider || form.getAttribute('data-form-provider') || 'html', 83 form_id: form.id || form.getAttribute('data-form-id') || '', 84 form_name: formName(form), 85 form_action: form.getAttribute('action') || '', 86 page_path: window.location.pathname, 87 lead_type: form.getAttribute('data-lead-type') || '' 88 }; 89 } 90 91 function success(detail = {}) { 92 const key = [detail.form_provider || '', detail.form_id || '', detail.form_name || '', window.location.pathname].join('|'); 93 const now = Date.now(); 94 if (successKeys.has(key) && now - successKeys.get(key) < 5000) return; 95 successKeys.set(key, now); 96 const payload = {page_path: window.location.pathname, ...detail}; 97 push('pdi_form_success', payload); 98 push('generate_lead', payload); 99 } 100 101 document.addEventListener('click', (event) => { 102 const target = event.target.closest('a,button,input[type=button],input[type=submit]'); 103 if (!target || target.closest('#pdi-common-header .pdi-nav__toggle, #pdi-common-header .pdi-nav__branch-toggle, #pdi-common-header .pdi-nav__mobile-toggle')) return; 104 const url = href(target); 105 const label = text(target); 106 const classes = target.className ? String(target.className) : ''; 107 const common = { 108 element_text: label, 109 element_url: url, 110 element_classes: classes.slice(0, 200), 111 page_path: location.pathname 112 }; 113 if (url && downloadPattern.test(url)) { 114 const match = url.match(downloadPattern); 115 push('pdi_download', {...common, file_extension: match ? match[1].toLowerCase() : ''}); 116 } 117 if (url && /^https?:/i.test(url)) {
118 try { 119 const destinationHost = new URL(url, location.href).hostname; 120 if (destinationHost !== location.hostname) { 121 push('pdi_outbound_click', {...common, destination_host: destinationHost}); 122 } 123 } catch (_) {} 124 } 125 if (/cta|button|btn/i.test(classes) || /demo|assessment|contact|schedule|book|download|get started/i.test(label + ' ' + url)) { 126 push('pdi_cta_click', common); 127 } else if (target.tagName === 'BUTTON' || target.matches('input[type=button],input[type=submit]')) { 128 push('pdi_button_click', common); 129 } 130 }, true); 131 132 document.addEventListener('focusin', (event) => { 133 const form = event.target && event.target.closest ? event.target.closest('form') : null; 134 if (!form || startedForms.has(form)) return; 135 startedForms.add(form); 136 populateAttribution(form); 137 push('pdi_form_start', commonFormData(form)); 138 if (form.closest('#pdi-ddi-lead-form')) { 139 push('dirty_data_index_form_start', commonFormData(form)); 140 } 141 }, true); 142 143 document.addEventListener('submit', (event) => { 144 const form = event.target; 145 if (!form || form.nodeName !== 'FORM') return; 146 populateAttribution(form); 147 push('pdi_form_submit', commonFormData(form)); 148 }, true); 149 150 document.querySelectorAll('form').forEach(populateAttribution); 151 window.addEventListener('pdi:form-success', (event) => success(event.detail || {})); 152 153 document.addEventListener('wpcf7mailsent', (event) => { 154 const form = event.target && event.target.closest ? event.target.closest('form') : null; 155 success({ 156 ...commonFormData(form || document.createElement('form'), 'contact_form_7'), 157 form_id: String((event.detail && event.detail.contactFormId) || '') 158 }); 159 }); 160 161 if (window.jQuery) { 162 window.jQuery(document).on('wpformsAjaxSubmitSuccess', (event, formData) => success({ 163 form_provider: 'wpforms', 164 form_id: String((formData && formData.id) || ''), 165 page_path: window.location.pathname 166 })); 167 } 168 169 window.addEventListener('message', (event) => { 170 let host = ''; 171 try { host = new URL(event.origin).hostname.toLowerCase(); } catch (_) { return; } 172 const trusted = host === window.location.hostname || host === 'forms.hsforms.com' || host.endsWith('.hubspot.com') || host.endsWith('.hsforms.com'); 173 if (!trusted) return; 174 const data = event.data || {}; 175 if (data.type === 'hsFormCallback' && ['onFormSubmitted', 'onFormSubmit'].includes(data.eventName)) { 176 success({ 177 form_provider: 'hubspot', 178 form_id: String(data.id || data.formGuid || ''), 179 page_path: window.location.pathname 180 }); 181 } 182 }); 183 184 if (/thank(?:-you|s)?|success/i.test(location.pathname)) push('pdi_thank_you_view', {page_path: location.pathname}); 185})();
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.