1// Helper function to retrieve a specific cookie by name 2function getCookie(name) { 3 const value = `; ${document.cookie}`; 4 const parts = value.split(`; ${name}=`); 5 if (parts.length === 2) return parts.pop().split(";").shift(); 6} 7 8function parseJsonCookie(cookieVal) { 9 if (!cookieVal) return null; 10 try { 11 return JSON.parse(cookieVal); 12 } catch (e1) { 13 try { 14 return JSON.parse(decodeURIComponent(cookieVal)); 15 } catch (e2) { 16 return null; 17 } 18 } 19} 20 21 22 23// Helper function to log a JavaScript error 24function logJSError(message) { 25 const errorData = { 26 message: message, 27 url: window.location.href, 28 timestamp: new Date().toISOString(), 29 }; 30 31 fetch("/wp-json/form-webhook/v1/log-js-error", { 32 method: "POST", 33 headers: { 34 "Content-Type": "application/json", 35 }, 36 body: JSON.stringify(errorData), 37 }).catch((err) => { 38 console.error("Failed to send error to server:", err); 39 console.error("Original error:", message); 40 }); 41} 42 43function updateKadenceVisitorId(roomvoForm) { 44 if (!roomvoForm) return; 45 46 47 const visitorIdInput = roomvoForm.querySelector('input[data-label="visitor_id"]'); 48 if (!visitorIdInput) return; 49 const rawCookie = getCookie("ffvisitorids"); 50 const parsed = parseJsonCookie(rawCookie); 51 if (!parsed || typeof parsed !== "object") return; 52 53 let visitorId = null; 54 55 // 1) Prefer ffvendorurlpath if it matches a key in ffvisitorids 56 const vendorKeyFromCookie = getCookie("ffvendorurlpath"); 57 if (vendorKeyFromCookie && Object.prototype.hasOwnProperty.call(parsed, vendorKeyFromCookie)) { 58 visitorId = parsed[vendorKeyFromCookie]; 59 } else { 60 // 2) Existing behavior: use current field value as key 61 const visitorVendorID = visitorIdInput.value; 62 if (visitorVendorID && Object.prototype.hasOwnProperty.call(parsed, visitorVendorID)) { 63 visitorId = parsed[visitorVendorID]; 64 } else { 65 // 3) Final fallback 66 const vals = Object.values(parsed); 67 visitorId = vals.length > 0 ? vals[0] : null; 68 } 69 } 70 71 if (!visitorId) return; 72 73 visitorIdInput.value = visitorId; 74 visitorIdInput.dispatchEvent(new Event("input", { bubbles: true })); 75 visitorIdInput.dispatchEvent(new Event("change", { bubbles: true })); 76} 77 78// Populates the Kadence form visitor_id field 79function processKadenceFormField(visitorIdInput, rawCookie) { 80 if (!visitorIdInput || !rawCookie) return; 81 82 const visitorVendorID = visitorIdInput.value; 83 84 try { 85 const parsed = parseJsonCookie(rawCookie); 86 let visitorId = null; 87 88 if ( 89 visitorVendorID && 90 parsed && 91 typeof parsed === "object" && 92 Object.prototype.hasOwnProperty.call(parsed, visitorVendorID) 93 ) { 94 visitorId = parsed[visitorVendorID]; 95 } else if ( 96 parsed && 97 typeof parsed === "object" && 98 Object.keys(parsed).length > 0 99 ) { 100 visitorId = Object.values(parsed)[0]; 101 } 102 103 if (visitorId) { 104 visitorIdInput.value = visitorId; 105 visitorIdInput.dispatchEvent(new Event("input", { bubbles: true })); 106 visitorIdInput.dispatchEvent(new Event("change", { bubbles: true })); 107 } 108 } catch (e) { 109 const parseErrorMsg = "Error parsing ffvisitorids cookie: " + e.message; 110 console.error(parseErrorMsg); 111 logJSError(parseErrorMsg); 112 } 113} 114 115// Initialize Form Handlers - Called after the cookie pulling is complete 116function initializeFormHandlers(rawCookie, roomvoForm, ameliaForm) { 117 const locationURL = window.location.href; 118 // --- Logic for Kadence / Roomvo Forms --- 119 if (roomvoForm) { 120 const maxFieldRetries = 20; // 10 seconds 121 const fieldRetryInterval = 500; 122 let fieldRetryCount = 0; 123 124 function findKadenceField() { 125 const visitorIdInput = roomvoForm.querySelector( 126 'input[data-label="visitor_id"]' 127 ); 128 if (visitorIdInput) { 129 updateKadenceVisitorId(roomvoForm); 130 } else if (fieldRetryCount < maxFieldRetries) { 131 fieldRetryCount++; 132 setTimeout(findKadenceField, fieldRetryInterval); 133 } else { 134 console.error( 135 "Please add a visitor_id field in this Kadence Roomvo form. Field not found after 10 seconds.", 136 roomvoForm 137 ); 138 } 139 } 140 141 findKadenceField(); 142 } 143 144 // --- Logic for Amelia Forms --- 145 if (ameliaForm) { 146 // Define Amelia custom field IDs 147 const visitorInputID = 3; 148 const locationUrlInputID = 4; 149 const utmSourceID = 5; 150 const utmMediumID = 6; 151 const utmCampaignID = 7; 152 const utmTermID = 8; 153 154 window.ameliaActions = { 155 InitInfoStep: function (success = null, error = null, data) { 156 const visitorIDInputWrapper = document.querySelector( 157 `#am-cf-${visitorInputID}` 158 ); 159 const locationUrlWrapper = document.querySelector( 160 `#am-cf-${locationUrlInputID}` 161 ); 162 const utmSourceIDInputWrapper = document.querySelector( 163 `#am-cf-${utmSourceID}` 164 ); 165 const utmMediumIDInputWrapper = document.querySelector( 166 `#am-cf-${utmMediumID}` 167 ); 168 const utmCampaignIDInputWrapper = document.querySelector( 169 `#am-cf-${utmCampaignID}` 170 ); 171 const utmTermIDInputWrapper = document.querySelector( 172 `#am-cf-${utmTermID}` 173 ); 174 175 const fieldsToHide = [ 176 visitorIDInputWrapper, 177 locationUrlWrapper, 178 utmSourceIDInputWrapper, 179 utmMediumIDInputWrapper, 180 utmCampaignIDInputWrapper, 181 utmTermIDInputWrapper, 182 ]; 183 184 fieldsToHide.forEach((field) => { 185 if (field) { 186 field.style.display = "none"; 187 } 188 }); 189 190 const locationUrlInput = document.querySelector( 191 `#am-cf-${locationUrlInputID} input` 192 ); 193 194 if (locationUrlInput) { 195 locationUrlInput.value = locationURL; 196 locationUrlInput.dispatchEvent( 197 new Event("input", { bubbles: true }) 198 ); 199 locationUrlInput.dispatchEvent( 200 new Event("change", { bubbles: true }) 201 ); 202 } 203 204 (async () => { 205 try { 206 const response = await fetch("/wp-json/vendor-id/v1/get"); 207 const data = await response.json(); 208 const vendorId = data.vendor_id; 209 210 if (!vendorId) { 211 console.warn("Vendor ID not found in API response for Amelia."); 212 return; 213 } 214 215 const visitorIdInputAmelia = document.querySelector( 216 `#am-cf-${visitorInputID} input` 217 ); 218 219 if (!visitorIdInputAmelia) { 220 console.warn( 221 `Amelia Visitor ID input field #am-cf-${visitorInputID} not found.` 222 ); 223 return; 224 } 225 226 if (!rawCookie) { 227 console.warn("ffvisitorids cookie not found for Amelia."); 228 return; 229 } 230 231 let visitorId = null; 232 233 try { 234 const parsed = parseJsonCookie(rawCookie); 235 236 if (parsed && typeof parsed === "object") { 237 visitorId = parsed[vendorId] || Object.values(parsed)[0]; 238 } 239 } catch (e) { 240 console.error("Error parsing ffvisitorids cookie for Amelia", e); 241 return; 242 } 243 244 if (visitorId) { 245 visitorIdInputAmelia.value = visitorId; 246 visitorIdInputAmelia.dispatchEvent( 247 new Event("input", { bubbles: true }) 248 ); 249 visitorIdInputAmelia.dispatchEvent( 250 new Event("change", { bubbles: true }) 251 ); 252 } 253 } catch (err) { 254 console.error( 255 "Unexpected error during Amelia visitor ID handling:", 256 err 257 ); 258 } 259 })(); 260 261 // Fill hidden UTM fields from URL parameters 262 const params = new URL(window.location.href).searchParams; 263 264 const utmSource = params.get("utm_source"); 265 const utmMedium = params.get("utm_medium"); 266 const utmCampaign = params.get("utm_campaign"); 267 const utmTerm = params.get("utm_term"); 268
269 const utmSourceInput = document.querySelector( 270 `#am-cf-${utmSourceID} input` 271 ); 272 const utmMediumInput = document.querySelector( 273 `#am-cf-${utmMediumID} input` 274 ); 275 const utmCampaignInput = document.querySelector( 276 `#am-cf-${utmCampaignID} input` 277 ); 278 const utmTermInput = document.querySelector( 279 `#am-cf-${utmTermID} input` 280 ); 281 282 function updateAmeliaField(inputElement, value, fieldName) { 283 if (inputElement && value) { 284 inputElement.value = value; 285 inputElement.dispatchEvent(new Event("input", { bubbles: true })); 286 inputElement.dispatchEvent(new Event("change", { bubbles: true })); 287 } else if (value && !inputElement) { 288 console.warn( 289 `Amelia ${fieldName} input field not found, but value exists.` 290 ); 291 } 292 } 293 294 updateAmeliaField(utmSourceInput, utmSource, "utm_source"); 295 updateAmeliaField(utmMediumInput, utmMedium, "utm_medium"); 296 updateAmeliaField(utmCampaignInput, utmCampaign, "utm_campaign"); 297 updateAmeliaField(utmTermInput, utmTerm, "utm_term"); 298 }, 299 }; 300 } 301} 302 303// Get ffvisitorids cookie once, then initialize all matching forms individually 304function findCookieAndInitializeAll(roomvoForms, ameliaForms) { 305 const maxRetries = 20; 306 const retryInterval = 500; 307 let retryCount = 0; 308 309 const cookieIntervalID = setInterval(() => { 310 const cookie = getCookie("ffvisitorids"); 311 312 if (cookie) { 313 clearInterval(cookieIntervalID); 314 315 roomvoForms.forEach(function (form) { 316 initializeFormHandlers(cookie, form, null); 317 }); 318 319 ameliaForms.forEach(function (form) { 320 initializeFormHandlers(cookie, null, form); 321 }); 322 323 return; 324 } 325 326 if (retryCount >= maxRetries) { 327 clearInterval(cookieIntervalID); 328 329 const errorMsg = "ffvisitorids cookie not found after 10 seconds."; 330 console.error(errorMsg + " URL: " + window.location.href); 331 logJSError(errorMsg); 332 333 roomvoForms.forEach(function (form) { 334 initializeFormHandlers(undefined, form, null); 335 }); 336 337 ameliaForms.forEach(function (form) { 338 initializeFormHandlers(undefined, null, form); 339 }); 340 341 return; 342 } 343 344 retryCount++; 345 }, retryInterval); 346} 347 348// Run the script after the DOM is fully loaded 349document.addEventListener("DOMContentLoaded", function () { 350 const roomvoForms = document.querySelectorAll( 351 "div.roomvo-contact-form>form.kb-advanced-form" 352 ); 353 354 const ameliaForms = document.querySelectorAll(".amelia-form"); 355 356 // If there are no supported forms on this page, stop. 357 // Normal forms without the Roomvo or Amelia class are ignored. 358 if (!roomvoForms.length && !ameliaForms.length) { 359 return; 360 } 361 // Listen once, then update every Roomvo form on the page. 362 if (roomvoForms.length) { 363 window.addEventListener("dlh-store-changed", function () { 364 setTimeout(function () { 365 roomvoForms.forEach(function (form) { 366 updateKadenceVisitorId(form); 367 }); 368 }, 150); 369 }); 370 } 371 372 findCookieAndInitializeAll(roomvoForms, ameliaForms); 373});
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.