1jQuery(document).ready(function ($) { 2 var vehicleCacheByPostId = {}; 3 var pendingByPostId = {}; 4 5 function fetchCarData(postID) { 6 var dfd = $.Deferred(); 7 8 if (!postID) { 9 dfd.reject('Missing post ID'); 10 return dfd.promise(); 11 } 12 13 if (!window.wpAjax || !wpAjax.url) { 14 dfd.reject('wpAjax.url is not available'); 15 return dfd.promise(); 16 } 17 18 $.ajax({ 19 url: wpAjax.url, 20 type: 'POST', 21 dataType: 'json', 22 data: { 23 action: 'get_car_popup_data', 24 post_id: postID 25 }, 26 success: function (response) { 27 if (!response || !response.success) { 28 console.error('Car data error:', response ? response.data : 'Invalid response'); 29 dfd.reject(response ? response.data : 'Invalid response'); 30 return; 31 } 32 33 dfd.resolve(response.data); 34 }, 35 error: function (xhr, status, error) { 36 console.error('AJAX Error:', status, error); 37 dfd.reject(error || status || 'AJAX error'); 38 } 39 }); 40 41 return dfd.promise(); 42 } 43 44 function loadCarData(postID, type, callback, options) { 45 fetchAndStoreVehicleDetails(postID, type, options).done(function (d) { 46 console.log('Popup Data Loaded:', d); 47 callback(d); 48 }); 49 } 50 51 function getVehicleStatus(d, type) { 52 if (type === 'used') { 53 return (d.isCPO && d.isCPO === true) ? 'CPO' : 'Used'; 54 } 55 56 return 'New'; 57 } 58 59 function buildVehicleDetails(d, type, yearOverride) { 60 return { 61 displayedPrice: d.price || '0', 62 drivetrain: d.drivetrain || 'NA', 63 engine: d.engine || 'NA', 64 exteriorColor: d.exteriorColor || 'NA', 65 fuelType: d.fuelType || 'NA', 66 interiorColor: d.interiorColor || 'NA', 67 make: d.make || '', 68 miles: d.miles || '', 69 model: d.model || '', 70 msrp: d.msrp || '0', 71 postId: d.postId || '', 72 status: getVehicleStatus(d, type), 73 stockNumber: d.stockNumber || '', 74 stockStatus: d.stockStatus || 'on the lot', 75 transmission: d.transmission || 'NA', 76 trim: d.trim || 'NA', 77 vin: d.vin || '', 78 year: yearOverride || d.year || '' 79 }; 80 } 81 82 /* -------------------------------------------------------- 83 FUNCTION TO UPDATE FORM FIELDS (USED + NEW) 84 -------------------------------------------------------- */ 85 function updateFormFields(popup, d, type) { 86 console.log('Updating Form Fields for popup:', popup); 87 88 var status = getVehicleStatus(d, type); 89 90 // Common fields for both used and new 91 popup.find('#form-field-make').val(d.make || ''); 92 popup.find('#form-field-model').val(d.model || ''); 93 popup.find('#form-field-trim').val(d.trim || ''); 94 popup.find('#form-field-status').val(status); 95 96 // Only update these fields for USED cars 97 if (type === 'used') { 98 popup.find('#form-field-year').val(d.year || ''); 99 popup.find('#form-field-vin').val(d.vin || ''); 100 popup.find('#form-field-miles').val(d.miles || ''); 101 popup.find('#form-field-price').val(d.price || ''); 102 } 103 104 // Trigger Elementor update 105 popup.find('input').trigger('input').trigger('change'); 106 107 console.log('Form fields updated with status:', status); 108 } 109 110 /* -------------------------------------------------------- 111 UPDATE POPUP CONTENT (COMMON FUNCTION) 112 -------------------------------------------------------- */ 113 function updatePopupContent(popup, d) { 114 popup.find('#popup_car_image img').attr('srcset', d.image).attr('alt', d.model); 115 popup.find('#popup_title .elementor-heading-title').text(d.model); 116 popup.find('#popup_year .elementor-heading-title').text(d.year); 117 popup.find('#popup_miles .elementor-heading-title').text(d.miles + ' Ml'); 118 popup.find('#popup_price .elementor-heading-title').text('$ ' +
118Number(d.price).toLocaleString()); 119 popup.find('#popup_dealer_name .elementor-icon-list-text').text(d.dealer); 120 121 console.log('Popup visual content updated'); 122 } 123 124 /* -------------------------------------------------------- 125 STORE VEHICLE DETAILS IN LOCALSTORAGE + GLOBAL 126 -------------------------------------------------------- */ 127 function storeVehicleDetails(d, type, yearOverride) { 128 var vehicleDetails = buildVehicleDetails(d, type, yearOverride); 129 130 localStorage.setItem('vehicleDetails', JSON.stringify(vehicleDetails)); 131 window.FerrariVehicleDetails = vehicleDetails; 132 133 if (typeof window.CustomEvent === 'function') { 134 window.dispatchEvent(new CustomEvent('ferrari:vehicle_details_updated', { 135 detail: vehicleDetails 136 })); 137 } 138 139 console.log('Vehicle details stored in localStorage:', vehicleDetails); 140 141 return vehicleDetails; 142 } 143 144 function fetchAndStoreVehicleDetails(postID, type, options) { 145 var dfd = $.Deferred(); 146 var opts = options || {}; 147 var cacheKey = String(postID); 148 149 if (!opts.force && vehicleCacheByPostId[cacheKey]) { 150 storeVehicleDetails(vehicleCacheByPostId[cacheKey], type, opts.yearOverride); 151 dfd.resolve(vehicleCacheByPostId[cacheKey]); 152 return dfd.promise(); 153 } 154 155 if (!opts.force && pendingByPostId[cacheKey]) { 156 return pendingByPostId[cacheKey]; 157 } 158 159 pendingByPostId[cacheKey] = dfd.promise(); 160 161 fetchCarData(postID) 162 .done(function (d) { 163 d.postId = d.postId || cacheKey; 164 vehicleCacheByPostId[cacheKey] = d; 165 storeVehicleDetails(d, type, opts.yearOverride); 166 dfd.resolve(d); 167 }) 168 .fail(function (error) { 169 dfd.reject(error); 170 }) 171 .always(function () { 172 delete pendingByPostId[cacheKey]; 173 }); 174 175 return dfd.promise(); 176 } 177 178 function primeVehicleData(postID, type, options) { 179 if (!postID) { 180 return; 181 } 182 183 fetchAndStoreVehicleDetails(postID, type, options).fail(function () { 184 // Silent prefetch failure: click handler still has the normal fetch path. 185 }); 186 } 187 188 function isFinanceQuoteTrigger(el) { 189 if (!el || !el.closest) { 190 return false; 191 } 192 193 var trigger = el.closest('.finance-quote, .finance_quote, .cta-finance, [data-finance-quote], a, button, .elementor-button'); 194 if (!trigger) { 195 return false; 196 } 197 198 var className = String(trigger.className || '').toLowerCase(); 199 var text = String(trigger.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase(); 200 var href = String(trigger.getAttribute && trigger.getAttribute('href') || '').toLowerCase(); 201 202 return className.indexOf('finance-quote') !== -1 || 203 className.indexOf('finance_quote') !== -1 || 204 className.indexOf('cta-finance') !== -1 || 205 trigger.hasAttribute('data-finance-quote') || 206 text.indexOf('finance quote') !== -1 || 207 href.indexOf('finance') !== -1; 208 } 209 210 function setFinanceAutofillIntent() { 211 try { 212 sessionStorage.setItem('vehicleInquiry', 'used'); 213 sessionStorage.setItem('fst_vehicle_autofill', '1'); 214 sessionStorage.setItem('fst_finance_autofill', '1'); 215 console.log('[EELMS] Finance autofill intent set'); 216 } catch (e) { 217 console.warn('[EELMS] Failed to set finance inquiry flag:', e); 218 } 219 } 220 221 function interceptPopupOpenUntilDataReady() { 222 document.addEventListener('click', function (event) { 223 var trigger = event.target && event.target.closest ? event.target.closest('.used-car-inquiry, #new-model-inquiry, .new-model-inquiry') : null; 224 var isFinance = false; 225 226 if (!trigger && isFinanceQuoteTrigger(event.target)) { 227 trigger = event.target.closest('.finance-quote, .finance_quote, .cta-finance, [data-finance-quote], a, button, .elementor-button'); 228 isFinance = true; 229 } 230 231 if (!trigger) { 232 return; 233 } 234 235 if (trigger.getAttribute('data-fst-popup-ready') === '1') { 236 trigger.removeAttribute('data-fst-popup-ready'); 237 if (isFinance) { 238 setFinanceAutofillIntent(); 239 } 240 return; 241 } 242 243 var type = isFinance || trigger.matches('.used-car-inquiry') ? 'used' : 'new'; 244 var postID = $(trigger).data('post-id'); 245 var options = {}; 246 247 if (type === 'new') { 248 options.yearOverride = String(new Date().getFullYear()); 249 } 250 251 if (isFinance && !postID) { 252 postID = resolveInventoryPostId(); 253 } 254 255 if (!postID && type === 'new') { 256 postID = $(trigger).closest('[data-post-id]').data('post-id') || resolveNewM
256odelPostId(); 257 } 258 259 if (!postID) { 260 return; 261 } 262 263 event.preventDefault(); 264 event.stopImmediatePropagation(); 265 266 fetchAndStoreVehicleDetails(postID, type, options).always(function () { 267 if (isFinance) { 268 setFinanceAutofillIntent(); 269 } 270 trigger.setAttribute('data-fst-popup-ready', '1'); 271 trigger.click(); 272 }); 273 }, true); 274 } 275 276 function resolveNewModelPostId() { 277 var postID = null; 278 279 // Method 1: From body class (WordPress adds postid-XXX class) 280 var bodyClass = $('body').attr('class') || ''; 281 var bodyMatch = bodyClass.match(/postid-(\d+)/); 282 if (bodyMatch && bodyMatch[1]) { 283 postID = bodyMatch[1]; 284 } 285 286 // Method 2: From article ID 287 if (!postID) { 288 var articleId = $('article[id^="post-"]').first().attr('id'); 289 if (articleId) { 290 postID = articleId.replace('post-', ''); 291 } 292 } 293 294 // Method 3: From data attribute 295 if (!postID) { 296 postID = $('[data-post-id]').first().data('post-id'); 297 } 298 299 // Method 4: From #new-model-inquiry button 300 if (!postID) { 301 postID = $('#new-model-inquiry').data('post-id'); 302 } 303 304 return postID; 305 } 306 307 function resolveInventoryPostId() { 308 var postID = null; 309 310 var bodyClass = $('body').attr('class') || ''; 311 var bodyMatch = bodyClass.match(/postid-(\d+)/); 312 if (bodyMatch && bodyMatch[1]) { 313 postID = bodyMatch[1]; 314 } 315 316 if (!postID) { 317 var articleId = $('article[id^="post-"]').first().attr('id'); 318 if (articleId) { 319 postID = articleId.replace('post-', ''); 320 } 321 } 322 323 if (!postID) { 324 postID = $('[data-post-id]').first().data('post-id'); 325 } 326 327 if (!postID) { 328 postID = $('#used-car-inquiry').data('post-id'); 329 } 330 331 return postID; 332 } 333 334 function initNewModelContactTracking() { 335 var currentPath = window.location.pathname || ''; 336 if (currentPath.indexOf('/new-model/') === -1) { 337 return; 338 } 339 340 var postID = resolveNewModelPostId(); 341 if (!postID) { 342 console.log('[EELMS] No post ID found on new-model page'); 343 return; 344 } 345 346 var currentYear = String(new Date().getFullYear()); 347 348 console.log('[EELMS] New model post ID:', postID); 349 350 // Initial load for redirects and popup tracking context. 351 fetchAndStoreVehicleDetails(postID, 'new', { 352 yearOverride: currentYear 353 }); 354 355 $(document).on('click', 'a[href*="contact"], .contact-us-btn, .contact-button', function (e) { 356 var link = $(this).attr('href'); 357 358 // Keep browser behavior for modified clicks/new tabs. 359 if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || this.target === '_blank') { 360 fetchAndStoreVehicleDetails(postID, 'new', { 361 force: true, 362 yearOverride: currentYear 363 }); 364 return; 365 } 366 367 if (!link || link.charAt(0) === '#' || /^javascript:/i.test(link)) { 368 fetchAndStoreVehicleDetails(postID, 'new', { 369 force: true, 370 yearOverride: currentYear 371 }); 372 return; 373 } 374 375 e.preventDefault(); 376 377 fetchAndStoreVehicleDetails(postID, 'new', { 378 force: true, 379 yearOverride: currentYear 380 }).always(function () { 381 window.location.href = link; 382 }); 383 }); 384 } 385 386 function initInventoryContactTracking() { 387 var currentPath = String(window.location.pathname || '').toLowerCase(); 388 if (currentPath.indexOf('/inventory/') === -1) { 389 return; 390 } 391 392 var postID = resolveInventoryPostId(); 393 if (!postID) { 394 console.log('[EELMS] No post ID found on inventory page'); 395 return; 396 } 397 398 fetchAndStoreVehicleDetails(postID, 'used'); 399 } 400 401 function initVehicleInquiryFlagTracking() { 402 function isAutofillContactLink(link) { 403 var href = String(link || '').toLowerCase(); 404 var normalizedHref = href.replace(/\/+#/g, '#'); 405 var isContactPage = normalizedHref.indexOf('/about/contact') !== -1 || normalizedHref.indexOf('/contact') !== -1; 406 var hasContactAnchor = normalizedHref.indexOf('#contacts') !== -1 || normalizedHref.indexOf('#contact-us') !== -1; 407 408 return isContactPage && hasContactAnchor; 409 } 410 411 function setAutofillIntentFromVehicleDetails(sourceOverride) { 412 try { 413 var stored = localStorage.getItem('vehicleDetails') || ''; 414 if (!stored) return; 415 416 var currentPath = String((window.location && window.location.pathname) || '').toLowerCase(); 417 var source = sourceOverride || (currentPath.indexOf('/new-model/') !== -1 ? 'new' : 'used'); 418 419 sessionStorage.setItem('vehicleInquiry', source); 420 sessionStorage.setItem('fst_vehicle_autofill', '1'); 421 console.log('[EELMS] Vehicle autofill intent set:', source); 422 } catch (e) { 423 console.warn('[EELMS] Failed to set used inquiry flag:', e); 424 } 425 } 426 427 $(document).on('click', '.btn-secondary[href*="contact"], .btn-primary-outline[href*="contact"]', function () { 428 var link = $(this).attr('href'); 429 if (!isAutofillContactLink(link)) { 430 return; 431 } 432 var sourceOverride = $(this).hasClass('btn-primary-outline') ? 'new' : ''; 433 setAutofillIntentFromVehicleDetails(sourceOverride); 434 }); 435 } 436 437 /* -------------------------------------------------------- 438 USED CAR POPUP 439 -------------------------------------------------------- */ 440 $(document).on('mouseenter focusin touchstart', '.used-car-inquiry', function () { 441 var postID = $(this).data('post-id'); 442 if (!postID) { 443 return; 444 } 445 446 primeVehicleData(postID, 'used'); 447 }); 448 449 $(document).on('click', '.used-car-inquiry', function () { 450 var postID = $(this).data('post-id'); 451 if (!postID) { 452 return; 453 } 454 455 loadCarData(postID, 'used', function (d) { 456 var popup = $('.elementor-popup-modal.pre-owned-popup-form'); 457 458 if (!popup.length) { 459 console.warn('Used popup not found'); 460 return; 461 } 462 463 // Elementor popup loads async â wait 150ms 464 setTimeout(function () { 465 updatePopupContent(popup, d); 466 updateFormFields(popup, d, 'used'); 467 storeVehicleDetails(d, 'used'); 468 }, 150); 469 }); 470 }); 471 472 /* -------------------------------------------------------- 473 FINANCE POPUP 474 -------------------------------------------------------- */ 475 $(document).on('click', '.finance-quote, .finance_quote, .cta-finance, [data-finance-quote]', function () { 476 var postID = $(this).data('post-id') || resolveInventoryPostId(); 477 if (!postID) { 478 return; 479 } 480 481 setFinanceAutofillIntent(); 482 loadCarData(postID, 'used', function (d) { 483 var popup = $('.elementor-popup-modal.finance-popup-form'); 484 485 if (!popup.length) { 486 return; 487 } 488 489 setTimeout(function () { 490 updatePopupContent(popup, d); 491 updateFormFields(popup, d, 'used'); 492 storeVehicleDetails(d, 'used'); 493 }, 150); 494 }); 495 }); 496 497 /* -------------------------------------------------------- 498 NEW CAR POPUP 499 -------------------------------------------------------- */ 500 $(document).on('mouseenter focusin touchstart', '#new-model-inquiry, .new-model-inquiry', function () { 501 var postID = $(this).data('post-id'); 502 503 if (!postID) { 504 postID = $(this).closest('[data-post-id]').data('post-id'); 505 } 506 507 if (!postID) {
508 postID = resolveNewModelPostId(); 509 } 510 511 if (!postID) { 512 return; 513 } 514 515 primeVehicleData(postID, 'new', { 516 yearOverride: String(new Date().getFullYear()) 517 }); 518 }); 519 520 $(document).on('click', '#new-model-inquiry', function () { 521 var postID = $(this).data('post-id'); 522 523 // If not found, try parent element (Elementor widget wrapper) 524 if (!postID) { 525 postID = $(this).closest('[data-post-id]').data('post-id'); 526 } 527 528 if (!postID) { 529 console.error('No post ID found for new car inquiry'); 530 return; 531 } 532 533 loadCarData(postID, 'new', function (d) { 534 var popup = $('.elementor-popup-modal.new-car-popup-form'); 535 536 if (!popup.length) { 537 console.warn('New car popup not found'); 538 return; 539 } 540 541 // Elementor popup loads async â wait 150ms 542 setTimeout(function () { 543 updatePopupContent(popup, d); 544 updateFormFields(popup, d, 'new'); 545 storeVehicleDetails(d, 'new'); 546 547 // Update the new car title with model name 548 popup.find('#new-car-title .elementor-heading-title').text('Request more info on the ' + d.model); 549 }, 150); 550 }, { 551 yearOverride: String(new Date().getFullYear()) 552 }); 553 }); 554 555 interceptPopupOpenUntilDataReady(); 556 initNewModelContactTracking(); 557 initInventoryContactTracking(); 558 initVehicleInquiryFlagTracking(); 559});
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.