1/** 2 * Populate Gravity Forms hidden fields with SBJS attribution data 3 * This allows the data to be passed through to Zoho and other integrations 4 */ 5 6(function($) { 7 'use strict'; 8 9 /** 10 * Helper function to get a cookie value by name 11 */ 12 function getCookie(name) { 13 var value = "; " + document.cookie; 14 var parts = value.split("; " + name + "="); 15 if (parts.length === 2) { 16 return decodeURIComponent(parts.pop().split(";").shift()); 17 } 18 return null; 19 } 20 21 /** 22 * Parse SBJS cookie value (format: key1=value1|key2=value2|...) 23 */ 24 function parseSBJSCookie(cookieValue) { 25 if (!cookieValue) return {}; 26 27 var data = {}; 28 var pairs = cookieValue.split('|'); 29 30 for (var i = 0; i < pairs.length; i++) { 31 var pair = pairs[i].trim(); 32 if (pair) { 33 var parts = pair.split('='); 34 if (parts.length === 2) { 35 var key = parts[0].trim(); 36 var value = parts[1].trim(); 37 data[key] = value; 38 } 39 } 40 } 41 42 return data; 43 } 44 45 /** 46 * Get SBJS attribution values from cookies 47 */ 48 function getSBJSData() { 49 var sbjsCurrent = getCookie('sbjs_current'); 50 var data = {}; 51 52 if (sbjsCurrent) { 53 var parsed = parseSBJSCookie(sbjsCurrent); 54 // Map SBJS keys to friendly names 55 data.source = parsed.src || '(none)'; 56 data.medium = parsed.mdm || '(none)'; 57 data.campaign = parsed.cmp || '(none)'; 58 } else { 59 data.source = '(none)'; 60 data.medium = '(none)'; 61 data.campaign = '(none)'; 62 } 63 64 return data; 65 } 66 67 /** 68 * Find hidden field by label (case-insensitive, partial match) 69 */ 70 function findFieldByLabel($form, labelText) { 71 var searchText = labelText.toLowerCase(); 72 73 // Try to find by label text 74 var $labels = $form.find('label'); 75 var $field = null; 76 77 $labels.each(function() { 78 var labelTextLower = $(this).text().toLowerCase().trim(); 79 if (labelTextLower === searchText || labelTextLower.indexOf(searchText) !== -1) { 80 var $label = $(this); 81 var fieldId = $label.attr('for'); 82 83 if (fieldId) { 84 $field = $form.find('#' + fieldId); 85 } else { 86 // If no 'for' attribute, try to find input within the label 87 $field = $label.find('input[type="hidden"]'); 88 } 89 90 // If still not found, try to find the next sibling input 91 if (!$field || $field.length === 0) { 92 $field = $label.next('input[type="hidden"]'); 93 } 94 95 if ($field && $field.length > 0) { 96 return false; // Break the loop 97 } 98 } 99 }); 100 101 // Fallback: try to find by field admin label or CSS class 102 if (!$field || $field.length === 0) { 103 $field = $form.find('input[type="hidden"][data-label*="' + labelText + '"]'); 104 } 105 106 // Another fallback: try to find by admin label in Gravity Forms structure 107 if (!$field || $field.length === 0) { 108 $form.find('input[type="hidden"]').each(function() { 109 var $input = $(this); 110 var $wrapper = $input.closest('.gfield'); 111 if ($wrapper.length) { 112 var $adminLabel = $wrapper.find('.gfield_admin_label'); 113 if ($adminLabel.length && $adminLabel.text().toLowerCase().indexOf(searchText) !== -1) { 114 $field = $input; 115 return false; 116 } 117 } 118 }); 119 } 120 121 return $field; 122 } 123 124 /** 125 * Populate hidden fields with SBJS data 126 * @param {jQuery} $form - The form jQuery object 127 * @param {boolean} silent - Whether to suppress console logging 128 * @param {boolean} force - Whether to force update even if value hasn't changed (for submission) 129 */ 130 function populateHiddenFields($form, silent, force) { 131 silent = silent || false; 132 force = force || false; 133 var sbjsData = getSBJSData(); 134 var populated = false; 135 136 // Find and populate Source field 137 var $sourceField = findFieldByLabel($form, 'Source'); 138 if ($sourceField && $sourceField.length > 0) { 139 // Disable autocomplete to prevent browser interference 140 $sourceField.attr('autocomplete', 'off'); 141 var currentValue = $sourceField.val(); 142 if (force || currentValue !== sbjsData.source) { 143 $sourceField.val(sbjsData.source); 144 // Use native setAttribute as fallback to ensure value is set 145 if ($sourceField[0]) { 146 $sourceField[0].setAttribute('value', sbjsData.source); 147 } 148 if (!silent) { 149 console.log('>>> SBJS: Populated Source field with:', sbjsData.source); 150 } 151 populated = true; 152 } 153 } else if (!silent) { 154 console.warn('>>> SBJS: Could not find Source hidden field'); 155 } 156 157 // Find and populate Medium field 158 var $mediumField = findFieldByLabel($form, 'Medium'); 159 if ($mediumField && $mediumField.length > 0) { 160 // Disable autocomplete to prevent browser interference 161 $mediumField.attr('autocomplete', 'off'); 162 var currentValue = $mediumField.val(); 163 if (force || currentValue !== sbjsData.medium) { 164 $mediumField.val(sbjsData.medium); 165 // Use native setAttribute as fallback to ensure value is set 166 if ($mediumField[0]) { 167 $mediumField[0].setAttribute('value', sbjsData.medium); 168 } 169 if (!silent) { 170 console.log('>>> SBJS: Populated Medium field with:', sbjsData.medium); 171 } 172 populated = true; 173 } 174 } else if (!silent) { 175 console.warn('>>> SBJS: Could not find Medium hidden field'); 176 } 177 178 // Find and populate Campaign field 179 var $campaignField = findFieldByLabel($form, 'Campaign'); 180 if ($campaignField && $campaignField.length > 0) { 181 // Disable autocomplete to prevent browser interference 182 $campaignField.attr('autocomplete', 'off'); 183 var currentValue = $campaignField.val(); 184 if (force || currentValue !== sbjsData.campaign) { 185 $campaignField.val(sbjsData.campaign); 186 // Use native setAttribute as fallback to ensure value is set 187 if ($campaignField[0]) { 188 $campaignField[0].setAttribute('value', sbjsData.campaign); 189 } 190 if (!silent) { 191 console.log('>>> SBJS: Populated Campaign field with:', sbjsData.campaign); 192 } 193 populated = true; 194 } 195 } else if (!silent) { 196 console.warn('>>> SBJS: Could not find Campaign hidden field'); 197 } 198 199 return populated; 200 } 201 202 /**
203 * Track which forms have been populated to prevent duplicate logging 204 */ 205 var populatedForms = {}; 206 207 /** 208 * Initialize on document ready and form render 209 */ 210 $(document).ready(function() { 211 // Wait a bit for SBJS to initialize 212 setTimeout(function() { 213 // Populate fields on all existing forms 214 $('form[id^="gform_"]').each(function() { 215 var $form = $(this); 216 var formId = $form.attr('id'); 217 218 // Set autocomplete="off" on all hidden fields to prevent browser interference 219 $form.find('input[type="hidden"]').attr('autocomplete', 'off'); 220 221 populateHiddenFields($form); 222 populatedForms[formId] = true; 223 }); 224 }, 500); 225 }); 226 227 // Also populate when Gravity Forms renders/updates forms 228 $(document).on('gform_post_render', function(event, form_id, current_page) { 229 var $form = $('#gform_' + form_id); 230 if ($form.length > 0) { 231 var formId = $form.attr('id'); 232 233 // Set autocomplete="off" on all hidden fields to prevent browser interference 234 $form.find('input[type="hidden"]').attr('autocomplete', 'off'); 235 236 setTimeout(function() { 237 // Only log if this form hasn't been populated yet 238 var silent = populatedForms[formId] || false; 239 populateHiddenFields($form, silent); 240 populatedForms[formId] = true; 241 }, 100); 242 } 243 }); 244 245 // Update fields right before form submission to ensure latest values 246 $(document).on('gform_pre_submission', function(event, form_id) { 247 var $form = $('#gform_' + form_id); 248 if ($form.length > 0) { 249 // Force update on pre-submission to override any autofill/autocomplete values 250 populateHiddenFields($form, true, true); 251 } 252 }); 253 254 // Also handle AJAX form submissions - use synchronous approach to ensure values are set 255 $(document).on('submit', 'form[id^="gform_"]', function(e) { 256 var $form = $(this); 257 // Force update immediately before submission to override any autofill/autocomplete 258 // Use force=true to ensure SBJS data always wins 259 populateHiddenFields($form, true, true); 260 261 // Also set values directly using native methods as a final safeguard 262 var sbjsData = getSBJSData(); 263 var $sourceField = findFieldByLabel($form, 'Source'); 264 var $mediumField = findFieldByLabel($form, 'Medium'); 265 var $campaignField = findFieldByLabel($form, 'Campaign'); 266 267 if ($sourceField && $sourceField.length > 0 && $sourceField[0]) { 268 $sourceField[0].value = sbjsData.source; 269 } 270 if ($mediumField && $mediumField.length > 0 && $mediumField[0]) { 271 $mediumField[0].value = sbjsData.medium; 272 } 273 if ($campaignField && $campaignField.length > 0 && $campaignField[0]) { 274 $campaignField[0].value = sbjsData.campaign; 275 } 276 }); 277 278})(jQuery); 279
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.