1/** 2 * Facebook Tracking Parameters Capture & Persistence (Funnel Module) 3 * 4 * Captures URL parameters on first touch and stores them in localStorage 5 * for use in Facebook Conversions API calls. 6 * 7 * Captured parameters: 8 * - fbclid (Facebook Click ID) 9 * - fbp (Facebook Browser ID from cookie) 10 * - fbc (Facebook Click - auto-generated from fbclid) 11 * - gclid, msclkid, ttclid, twclid (Other platform click IDs) 12 * - utm_* (All UTM parameters) 13 * - Custom parameters (any starting with 'custom_') 14 * 15 * Browser compatible: No ES6 (no const/let/arrow/for-of) 16 */ 17 18(function() { 19 'use strict'; 20 21 /** 22 * Get cookie value by name 23 */ 24 function getCookie(name) { 25 var nameEQ = name + "="; 26 var ca = document.cookie.split(';'); 27 for (var i = 0; i < ca.length; i++) { 28 var c = ca[i]; 29 while (c.charAt(0) === ' ') c = c.substring(1, c.length); 30 if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); 31 } 32 return null; 33 } 34 35 /** 36 * Generate Facebook Click parameter (fbc) from fbclid 37 * Format: fb.1.{timestamp}.{fbclid} â timestamp must be first-click time 38 */ 39 function generateFbc(fbclid, timestamp) { 40 if (!fbclid) return null; 41 if (!timestamp) { 42 timestamp = Math.floor(Date.now() / 1000); 43 } 44 return 'fb.1.' + timestamp + '.' + fbclid; 45 } 46 47 function isValidFbp(value) { 48 return value && String(value).indexOf('fb.1.') === 0; 49 } 50 51 function isValidFbc(value) { 52 return value && /^fb\.1\.\d+\..+/.test(String(value)); 53 } 54 55 /** 56 * Get current tracking parameters from localStorage 57 */ 58 function getStoredParams() { 59 try { 60 var stored = localStorage.getItem('tracking_params'); 61 return stored ? JSON.parse(stored) : {}; 62 } catch (e) { 63 return {}; 64 } 65 } 66 67 /** 68 * Save tracking parameters to localStorage 69 */ 70 function saveParams(params) { 71 try { 72 localStorage.setItem('tracking_params', JSON.stringify(params)); 73 } catch (e) { 74 // localStorage not available 75 } 76 } 77 78 /** 79 * Parse URL query string into key-value pairs (no URLSearchParams needed) 80 */ 81 function parseQueryString(search) { 82 var params = {}; 83 var queryString = search.substring(1); // remove leading ? 84 if (!queryString) return params; 85 86 var pairs = queryString.split('&'); 87 for (var i = 0; i < pairs.length; i++) { 88 var pair = pairs[i].split('='); 89 if (pair[0]) { 90 var key = decodeURIComponent(pair[0]); 91 var value = pair.length > 1 ? decodeURIComponent(pair[1]) : ''; 92 params[key] = value; 93 } 94 } 95 return params; 96 } 97 98 /** 99 * Capture and persist tracking parameters from URL 100 */ 101 function captureTrackingParams() { 102 try { 103 var urlParams = parseQueryString(window.location.search); 104 var currentParams = getStoredParams(); 105 var updated = false; 106 107 // List of parameters to capture 108 var paramsToCapture = [ 109 'fbclid', // Facebook Click ID 110 'gclid', // Google Click ID 111 'msclkid', // Microsoft Click ID 112 'ttclid', // TikTok Click ID 113 'twclid', // Twitter Click ID 114 'li_fat_id', // LinkedIn First Party Ad Tracking 115 'ScCid', // Snapchat Click ID 116 'utm_source', 117 'utm_medium', 118 'utm_campaign', 119 'utm_term', 120 'utm_content', 121 'utm_id' 122 ]; 123 124 // Capture standard parameters 125 for (var i = 0; i < paramsToCapture.length; i++) { 126 var param = paramsToCapture[i]; 127 var value = urlParams[param]; 128 if (value && value !== currentParams[param]) { 129 currentParams[param] = value; 130 updated = true; 131 } 132 } 133 134 // Capture any custom parameters (custom_*)
135 for (var key in urlParams) { 136 if (urlParams.hasOwnProperty(key) && key.indexOf('custom_') === 0) { 137 if (urlParams[key] !== currentParams[key]) { 138 currentParams[key] = urlParams[key]; 139 updated = true; 140 } 141 } 142 } 143 144 // Capture fbclid on first touch; preserve first-click fbc timestamp 145 if (urlParams.fbclid && urlParams.fbclid !== currentParams.fbclid) { 146 currentParams.fbclid = urlParams.fbclid; 147 currentParams.fbc_ts = Math.floor(Date.now() / 1000); 148 currentParams.fbc = null; 149 updated = true; 150 } else if (urlParams.fbclid && !currentParams.fbclid) { 151 currentParams.fbclid = urlParams.fbclid; 152 if (!currentParams.fbc_ts) { 153 currentParams.fbc_ts = Math.floor(Date.now() / 1000); 154 } 155 updated = true; 156 } 157 158 // Prefer Meta _fbc cookie, else build fbc once from first-touch fbclid 159 var fbcCookie = getCookie('_fbc'); 160 if (fbcCookie && isValidFbc(fbcCookie)) { 161 if (fbcCookie !== currentParams.fbc) { 162 currentParams.fbc = fbcCookie; 163 updated = true; 164 } 165 } else if (currentParams.fbclid && !isValidFbc(currentParams.fbc)) { 166 if (!currentParams.fbc_ts) { 167 currentParams.fbc_ts = Math.floor(Date.now() / 1000); 168 } 169 var builtFbc = generateFbc(currentParams.fbclid, currentParams.fbc_ts); 170 if (builtFbc !== currentParams.fbc) { 171 currentParams.fbc = builtFbc; 172 updated = true; 173 } 174 } 175 176 // Capture fbp from cookie (set by Facebook Pixel JS) 177 var fbpCookie = getCookie('_fbp'); 178 if (fbpCookie && isValidFbp(fbpCookie) && fbpCookie !== currentParams.fbp) { 179 currentParams.fbp = fbpCookie; 180 updated = true; 181 } 182 183 // Capture referrer on first visit 184 if (!currentParams.ref && document.referrer) { 185 currentParams.ref = document.referrer; 186 updated = true; 187 } 188 189 // Add capture timestamp if this is first time 190 if (!currentParams._captured_at) { 191 currentParams._captured_at = new Date().toISOString(); 192 updated = true; 193 } 194 195 // Save if anything changed 196 if (updated) { 197 saveParams(currentParams); 198 } 199 } catch (e) { 200 // Fail silently 201 } 202 } 203 204 /** 205 * Global function to retrieve tracking parameters 206 * Used by other scripts to get params for CAPI calls 207 */ 208 window.getTrackingParams = function() { 209 captureTrackingParams(); 210 return getStoredParams(); 211 }; 212 213 /** 214 * Global function to manually set a tracking parameter 215 */ 216 window.setTrackingParam = function(key, value) { 217 try { 218 var params = getStoredParams(); 219 params[key] = value; 220 saveParams(params); 221 } catch (e) { 222 // Fail silently 223 } 224 }; 225 226 /** 227 * Global function to clear all tracking parameters 228 */ 229 window.clearTrackingParams = function() { 230 try { 231 localStorage.removeItem('tracking_params'); 232 } catch (e) { 233 // Fail silently 234 } 235 }; 236 237 // Run immediately when script loads 238 captureTrackingParams(); 239 240 // Re-capture after Meta Pixel JS sets _fbp / _fbc cookies 241 setTimeout(captureTrackingParams, 1500); 242 setTimeout(captureTrackingParams, 3500); 243 244 // Also run when DOM is ready (in case cookies load later) 245 if (document.readyState === 'loading') { 246 document.addEventListener('DOMContentLoaded', captureTrackingParams); 247 } 248 249})();
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.