1/** 2 * GHL Extension for Gravity Forms Pro - Frontend UTM Tracker 3 * 4 * - Captures UTM parameters from the current page URL. 5 * - Persists them in localStorage so they survive page navigations. 6 * - Injects them as hidden fields into every Gravity Form on the page 7 * before submission so they are available in $_POST on the server. 8 */ 9(function () { 10 'use strict'; 11 12 var UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; 13 var STORAGE_KEY = 'gfghlpro_utm_params'; 14 var FIELD_PREFIX = 'gfghlpro_'; 15 var EXPIRY_TIME_MS = 5 * 60 * 1000; // 5 minutes in milliseconds 16 17 /** 18 * Read stored UTM data from localStorage. 19 * Returns a plain object (empty if nothing stored / parse error or if expired). 20 */ 21 function getStoredUTM() { 22 try { 23 var stored = JSON.parse(localStorage.getItem(STORAGE_KEY)); 24 if (!stored) return {}; 25 26 // Check for expiration 27 if (stored._timestamp) { 28 var now = new Date().getTime(); 29 if (now - stored._timestamp > EXPIRY_TIME_MS) { 30 localStorage.removeItem(STORAGE_KEY); 31 return {}; 32 } 33 } 34 35 return stored; 36 } catch (e) { 37 return {}; 38 } 39 } 40 41 /** 42 * Check the current URL for any UTM parameters. 43 * If found, merge them with any previously stored values and save. 44 * Existing values are ONLY overwritten when the URL contains a fresh value. 45 * The expiration timestamp is updated on every page load if data exists, keeping the session alive. 46 */ 47 function captureUTMFromURL() { 48 if (!window.URLSearchParams) { return; } // old browser guard 49 50 var urlParams = new URLSearchParams(window.location.search); 51 var stored = getStoredUTM(); 52 var hasNew = false; 53 var hasData = false; 54 55 UTM_PARAMS.forEach(function (param) { 56 var val = urlParams.get(param); 57 if (val && val.length > 0) { 58 stored[param] = val.trim(); 59 hasNew = true; 60 } 61 if (stored[param]) { 62 hasData = true; 63 } 64 }); 65 66 // Save if we have new params, or update timestamp if we have existing params 67 if (hasNew || hasData) { 68 stored._timestamp = new Date().getTime(); 69 try { 70 localStorage.setItem(STORAGE_KEY, JSON.stringify(stored)); 71 } catch (e) { /* localStorage blocked (private mode, etc.) */ } 72 } 73 } 74 75 /** 76 * Inject a hidden <input> for each UTM param into a given <form> element. 77 * If the field already exists (e.g., from a previous call), just update its value. 78 */ 79 function injectIntoForm(form) { 80 var utmData = getStoredUTM(); 81 82 UTM_PARAMS.forEach(function (param) { 83 var fieldName = FIELD_PREFIX + param; 84 var existing = form.querySelector('input[name="' + fieldName + '"]'); 85 86 if (!existing) { 87 var input = document.createElement('input'); 88 input.type = 'hidden'; 89 input.name = fieldName; 90 input.id = fieldName + '_' + (form.id || ''); 91 input.value = utmData[param] || ''; 92 form.appendChild(input); 93 } else { 94 existing.value = utmData[param] || ''; 95 } 96 }); 97 } 98 99 /** 100 * Find every Gravity Form on the page and inject UTM hidden fields into each. 101 */ 102 function injectUTMIntoAllForms() { 103 var forms = document.querySelectorAll('form[id^="gform_"]'); 104 forms.forEach(function (form) { 105 injectIntoForm(form); 106 }); 107 } 108 109 /* ââ Run on page load âââââââââââââââââââââââââââââââââââââââââââââââ */ 110 111 // Step 1: Persist any UTM params present in the current URL immediately. 112 captureUTMFromURL(); 113 114 // Step 2: Wait for the DOM before injecting into forms. 115 if (document.readyState === 'loading') { 116 document.addEventListener('DOMContentLoaded', injectUTMIntoAllForms); 117 } else { 118 injectUTMIntoAllForms(); // DOM already ready 119 } 120 121 // Step 3: Re-inject when Gravity Forms renders a form dynamically 122 // (e.g., AJAX-embedded forms, multi-page forms). 123 document.addEventListener('gform_post_render', injectUTMIntoAllForms); 124 125 // Step 4: Re-inject just before any GF form is submitted 126 // so the values are definitely fresh at send time. 127 document.addEventListener('gform_pre_submit', function (e) { 128 if (e.target && e.target.id && e.target.id.indexOf('gform_') === 0) { 129 injectIntoForm(e.target); 130 } 131 }); 132 133})();
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.