1let map, 2 service, 3 infowindow, 4 usrLocationBtn = document.getElementById('getUserLocation'), 5 locationField = document.getElementById('mapUserLocation'), 6 accordionContainer = document.getElementById('dealershipDetails'), 7 mapUserDistance = document.getElementById('mapUserDistance'), 8 button = document.getElementById('mapUserSubmitButton'), 9 markers = [], 10 usrMarkers = []; 11 12function initMap() { 13 // Don't initialize if no map div exists 14 if (!document.getElementById('map')) { 15 if (typeof initDealerFinderMap === 'function') { 16 initDealerFinderMap(); 17 } 18 return; 19 } 20 21 let AT = {lat: 53.8711559431357, lng: -2.5754641874521125}; 22 23 infowindow = new google.maps.InfoWindow(); 24 25 map = new google.maps.Map(document.getElementById('map'), { 26 zoom: 12, 27 center: AT, 28 disableDefaultUI: true, 29 mapId: 'map_id' 30 }); 31} 32 33function updateMap(response) { 34 clearMarkers(markers); 35 36 // Get the user's location from the input fields 37 let locationInput = document.getElementById('mapUserLocation'); 38 39 if (locationInput && locationInput.value.trim()) { 40 let location = locationInput.value.trim(); 41 let userDistanceInput = document.getElementById('mapUserDistance'); 42 let userDistance = userDistanceInput ? userDistanceInput.value : ''; 43 44 // Use Geocoding API to get coordinates for the user's entered location 45 var geocoder = new google.maps.Geocoder(); 46 47 geocoder.geocode({address: location}, function (results, status) { 48 if (status === 'OK' && results.length > 0) { 49 var location = results[0].geometry.location, 50 usrLat = location.lat(), 51 usrLng = location.lng(); 52 53 // Add user location marker 54 addUserLocationMarker(usrLat, usrLng); 55 56 // Handle the dealerships with distance filtering 57 if (userDistance && userDistance !== '') { 58 handleDealerships(response, usrLat, usrLng, parseInt(userDistance)); 59 } else { 60 // No distance filter, show all 61 addAllMarkers(response); 62 } 63 64 } else { 65 // If geocoding fails, add all markers without distance check 66 addAllMarkers(response); 67 } 68 }); 69 } else { 70 // No location entered, show all markers 71 addAllMarkers(response); 72 } 73} 74 75function handleDealerships(dealerships, usrLat, usrLng, userDistance) { 76 // Clear existing markers and accordions 77 clearMarkers(markers); 78 clearAccordions(); 79 80 let usrRange = document.getElementById('usrRange') ? document.getElementById('usrRange').value : null; 81 let filteredDealerships = []; 82 83 for (var i = 0; i < dealerships.length; i++) { 84 let dealership = dealerships[i], 85 lat = parseFloat(dealership.latitude), 86 lng = parseFloat(dealership.longitude); 87 88 // Check if latitude and longitude are valid numbers 89 if (!isNaN(lat) && !isNaN(lng)) { 90 var distanceInMiles = calculateDistance(usrLat, usrLng, lat, lng); 91 92 // Check distance filter 93 let withinDistance = userDistance === 0 || distanceInMiles <= userDistance; 94 95 if (withinDistance) { 96 // Check range filter if applicable 97 if (usrRange && usrRange !== '') { 98 let campervans = dealership.campervans || []; 99 let motorhomes = dealership.motorhomes || []; 100 101 let hasRange = campervans.some(campervan => campervan.slug === usrRange) || 102 motorhomes.some(motorhome => motorhome.slug === usrRange); 103 104 if (hasRange) { 105 filteredDealerships.push(dealership); 106 addMarkerToMap(lat, lng, dealership); 107 updateAccordions(dealership); 108 } 109 } else { 110 // No range filter, add all within distance 111 filteredDealerships.push(dealership); 112 addMarkerToMap(lat, lng, dealership); 113 updateAccordions(dealership); 114 } 115 } 116 } 117 } 118 119 // Set appropriate zoom level based on results 120 if (filteredDealerships.length > 0) { 121 if (userDistance <= 50) { 122 map.setZoom(8); 123 } else { 124 map.setZoom(6); 125 } 126 127 // Show results heading 128 let resultsHeading = document.getElementById('searchResultsHeading'); 129 if (resultsHeading) { 130 resultsHeading.style.display = 'block'; 131 } 132 } else { 133 displayErrorMessage("No results were found.", "mapAccordions"); 134 } 135} 136 137function displayErrorMessage(message, wrapperId) { 138 let errorMessage = `<p class="text-center w-full text-2.5xl md:text-3.5xl font-semibold">${message}</p>`; 139 let accordionWrapper = document.getElementById(wrapperId); 140 141 if (accordionWrapper) { 142 accordionWrapper.innerHTML = errorMessage; // Replace content instead of append 143 accordionWrapper.style.display = 'block'; 144 145 // Show results heading even for error 146 let resultsHeading = document.getElementById('searchResultsHeading'); 147 if (resultsHeading) { 148 resultsHeading.style.display = 'block'; 149 } 150 } else { 151 console.error("Wrapper element not found with the provided ID:", wrapperId); 152 } 153} 154 155function addAllMarkers(dealerships) { 156 clearMarkers(markers); 157 clearAccordions(); 158 159 for (var i = 0; i < dealerships.length; i++) { 160 var dealership = dealerships[i]; 161 var lat = parseFloat(dealership.latitude); 162 var lng = parseFloat(dealership.longitude); 163 164 if (dealership && !isNaN(lat) && !isNaN(lng)) { 165 addMarkerToMap(lat, lng, dealership); 166 updateAccordions(dealership); 167 } 168 } 169 170 map.setZoom(5); 171 172 // Show results heading 173 let resultsHeading = document.getElementById('searchResultsHeading'); 174 if (resultsHeading) { 175 resultsHeading.style.display = 'block'; 176 } 177} 178 179function addMarkerToMap(latitude, longitude, dealership) { 180 var marker = new google.maps.marker.AdvancedMarkerElement({ 181 position: {lat: latitude, lng: longitude}, 182 map: map, 183 title: dealership.title, 184 }); 185 186 var content = '<div><h2>' + dealership.title + '</h2><p>' + dealership.address + '</p></div>'; 187 188 var infoWindow = new google.maps.InfoWindow({ 189 content: content 190 }); 191 192 marker.addListener('click', function () { 193 infoWindow.setContent(content); 194 infoWindow.open(map, marker); 195 }); 196 197 markers.push(marker); 198} 199 200function geocodeLocation() { 201 let locationInput = document.getElementById('mapUserLocation'); 202 203 if (locationInput && locationInput.value.trim()) { 204 let location = locationInput.value.trim(); 205 206 // Use Geocoding API to get coordinates for the user's entered location 207 var geocoder = new google.maps.Geocoder(); 208 209 geocoder.geocode({address: location}, function (results, status) { 210 if (status === 'OK' && results.length > 0) { 211 var location = results[0].geometry.location; 212 var lat = location.lat(); 213 var lng = location.lng(); 214 215 // Add a marker for the user's entered location 216 addUserLocationMarker(lat, lng); 217 } else { 218 console.log('Geocoding failed. Status:', status); 219 } 220 }); 221 } 222} 223 224function reverseGeocode(usrLat, usrLng) { 225 const geocoder = new google.maps.Geocoder(), 226 latlng = new google.maps.LatLng(usrLat, usrLng); 227 228 geocoder.geocode({'latLng': latlng}, function (results, status) { 229 if (status === google.maps.GeocoderStatus.OK) { 230 if (results[0]) { 231 const addressComponents = results[0].address_components; 232 233 for (let i = 0; i < addressComponents.length; i++) { 234 const types = addressComponents[i].types; 235 236 if (types.includes('postal_code')) { 237 locationField.value = addressComponents[i].long_name; 238 locationField.setAttribute('value', addressComponents[i].long_name); 239 break; 240 } 241 } 242 } else { 243 console.error('No results found'); 244 } 245 } else { 246 console.error(`Geocoder failed: ${status}`); 247 } 248 }); 249} 250 251function glSuccess(position) { 252 const usrLat = position.coords.latitude, 253 usrLng = position.coords.longitude; 254 255 reverseGeocode(usrLat, usrLng); 256 addUserLocationMarker(usrLat, usrLng); 257} 258 259function glError(error) { 260 console.error(`Error getting geolocation: ${error.message}`); 261} 262 263function moveToLocation(lat, lng) { 264 const center = new google.maps.LatLng(lat, lng); 265 map.panTo(center); 266} 267 268function addUserLocationMarker(lat, lng) { 269 clearMarkers(usrMarkers); 270 271 let iconElement = document.createElement('div'); 272 iconElement.innerHTML = '<img src="/wp-content/themes/autotrail/images/usr-location.svg" alt="User Location" style="width: 32px; height: 32px;">'; 273 274 var userMarker = new google.maps.marker.AdvancedMarkerElement({ 275 position: {lat: lat, lng: lng}, 276 title: 'Your location', 277 content: iconElement, 278 }); 279 280 userMarker.map = map; 281 moveToLocation(lat, lng); 282 usrMarkers.push(userMarker); 283} 284 285function clearMarkers(markersArray) { 286 for (var i = 0; i < markersArray.length; i++) { 287 markersArray[i].setMap(null); 288 } 289 290 if (markers === markersArray) { 291 markers = []; 292 } else if (usrMarkers === markersArray) { 293 usrMarkers = []; 294 } 295} 296 297function clearAccordions() { 298 let accordionsContainer = document.getElementById('mapAccordions'); 299 let resultsHeading = document.getElementById('searchResultsHeading'); 300 301 if (accordionsContainer) { 302 accordionsContainer.innerHTML = ''; 303 accordionsContainer.style.display = 'none'; 304 } 305 306 if (resultsHeading) { 307 resultsHeading.style.display = 'none'; 308 } 309} 310 311function updateAccordions(dealership) { 312 let accordionsContainer = document.getElementById("mapAccordions"), 313 resultsHeading = document.getElementById("searchResultsHeading"); 314 315 if (!accordionsContainer || !resultsHeading) return; 316 let facilitiesList = "", 317 servicesList = "", 318 modelsList = "", 319 rangeList = ""; 320 if (dealership.facilities) { 321 facilitiesList = '<p class="font-semibold text-lg">Facilities</p><ul>'; 322 for (let i = 0; i < dealership.facilities.length; i++) { 323 facilitiesList += "<li>" + dealership.facilities[i].facility + "</li>" 324 } 325 facilitiesList += "</ul>" 326 } 327 if (dealership.services) { 328 servicesList = '<p class="font-semibold text-lg">Services</p><ul>'; 329 for (let i = 0; i < dealership.services.length; i++) { 330 servicesList += "<li>" + dealership.services[i].service + "</li>" 331 } 332 servicesList += "</ul>" 333 } 334 if (dealership.models && dealership.models.length > 0) { 335 modelsList = '<p class="font-semibold text-lg">Models in stock</p><ul>'; 336 for (let i = 0; i < dealership.models.length; i++) { 337 modelsList += "<li>" + dealership.models[i].label + "</li>"; 338 } 339 modelsList += "</ul>"; 340 } 341 342 if (dealership.campervans || dealership.motorhomes) { 343 rangeList = '<p class="font-semibold text-lg king">Models in stock</p><ul>'; 344 for (let i = 0; i < dealership.campervans.length; i++) { 345 rangeList += "<li>" + dealership.campervans[i].name + "</li>" 346 } 347 for (let i = 0; i < dealership.motorhomes.length; i++) { 348 rangeList += "<li>" + dealership.motorhomes[i].name + "</li>" 349 } 350 rangeList += "</ul>" 351 } 352 let accordionHTML = `<div class="accordion-wrapper"> 353 <a class="accordion-header" href="#" role="button"> 354 <h3 class="text-2xl">` + dealership.title + `</h3> 355 </a> 356 357 <div class="accordion-content accordion-panel hidden"> 358 <div class="content-wrap">`; 359 if (dealership.facilities || dealership.services) { 360 if (dealership.address || dealership.phone) { 361 accordionHTML += '<div class="contact-wrap">'; 362 if (dealership.address) { 363 accordionHTML += '<div class="column">'; 364 accordionHTML += '<address class="with-location-marker-icon-before">' + dealership.address + "</address>"; 365 accordionHTML += "</div>" 366 } 367 if (dealership.phone) { 368 accordionHTML += '<div class="column">'; 369 accordionHTML += '<a class="phone relative with-telephone-icon-before" href="tel:' + dealership.phone + '">' + dealership.phone + "</a>"; 370 accordionHTML += "</div>" 371 } 372 accordionHTML += "</div>" 373 } 374 if (dealership.facilities) { 375 accordionHTML += '<div class="column content-with-list-style-ticks-small">'; 376 accordionHTML += facilitiesList; 377 accordionHTML += "</div>" 378 } 379 if (dealership.services) { 380 accordionHTML += '<div class="column content-with-list-style-ticks-small">'; 381 accordionHTML += servicesList; 382 accordionHTML += "</div>" 383 } 384 } 385 if (dealership.models && dealership.models.length > 0) { 386 accordionHTML += '<div class="column content-with-list-style-ticks-small">'; 387 accordionHTML += modelsList; 388 accordionHTML += "</div>" 389 } 390 if (dealership.campervans || dealership.motorhomes) { 391 accordionHTML += '<div class="column content-with-list-style-ticks-small">'; 392 accordionHTML += rangeList; 393 accordionHTML += "</div>" 394 } 395 if (!dealership.services && !dealership.facilities) { 396 if (dealership.address || dealership.phone) { 397 accordionHTML += '<div class="column">'; 398 if (dealership.address) { 399 accordionHTML += '<address class="relative with-location-marker-icon-before">' + dealership.address + "</address>" 400 } 401 if (dealership.phone) { 402 accordionHTML += '<a class="phone relative with-telephone-icon-before" href="tel:' + dealership.phone + '">' + dealership.phone + "</a>" 403 } 404 accordionHTML += "</div>" 405 } 406 } 407 if (dealership.url || dealership.email) { 408 accordionHTML += `<div class="button-wrap">`; 409 if (dealership.url) { 410 let websiteURL = dealership.url.startsWith("http://") || dealership.url.startsWith("https://") ? dealership.url : "//" + dealership.url; 411 accordionHTML += `<a class="button" href="${websiteURL}" target="_blank">
412 <span class="button-text">Visit website</span> 413 <span class="with-chevron bg-brand-2"></span> 414 </a>` 415 } 416 if (dealership.email) { 417 accordionHTML += `<button class="enquire-now-button" data-sender-email="${dealership.email}"> 418 <span class="button-text">Enquire now</span> 419 <span class="with-chevron bg-brand-2"></span> 420 </button>` 421 } 422 accordionHTML += `</div>` 423 } 424 accordionHTML += `</div> 425 </div> 426 </div>`; 427 if (dealership === false) { 428 accordionHTML = "no dealerships" 429 } 430 if (!accordionsContainer) return; 431 432accordionsContainer.innerHTML += accordionHTML; 433 accordionsContainer.setAttribute("style", ""); 434 resultsHeading.setAttribute("style", "") 435} 436 437// Initialize map 438let mapDiv = document.getElementById('map'); 439 440if (mapDiv) { 441 window.initMap = initMap; 442} else { 443 window.initMap = function () { 444 return undefined; 445 }; 446} 447 448window.addEventListener('load', function () { 449 if (typeof google !== 'undefined' && google.maps && typeof initMap === 'function') { 450 initMap(); 451 } 452}); 453 454if (usrLocationBtn) { 455 usrLocationBtn.addEventListener('click', function (e) { 456 e.preventDefault(); 457 clearMarkers(usrMarkers); 458 if (navigator.geolocation) { 459 navigator.geolocation.getCurrentPosition(glSuccess, glError); 460 } else { 461 console.error('Geolocation is not supported in this browser.'); 462 } 463 }); 464} 465 466// jQuery section 467(function ($) { 468 $(document).ready(function () { 469 let $html = $('html'); 470 let $body = $('body'); 471 let $enquiryFormModal = $('#enquiry-form-modal'); 472 473 function loadMapData() { 474 let $form = $('.map-form form'); 475 var formData = $form.serialize(); 476 477 let postType, 478 formIDToQuery = $form.attr('id'); 479 480 if (formIDToQuery === 'find-a-dealer') { 481 postType = 'dealerships'; 482 } else if (formIDToQuery === 'find-a-workshop') { 483 postType = 'workshops'; 484 } else if (formIDToQuery === 'find-a-hire-partner') { 485 postType = 'hire'; 486 } 487 488 $.ajax({ 489 url: ajax_maps_object.ajax_url, 490 type: 'post', 491 data: { 492 action: 'get_dealerships', 493 form_data: formData, 494 post_type: postType 495 }, 496 beforeSend: function () { 497 $('#loading-spinner-modal').show().addClass('fade-in-faster'); 498 }, 499 success: function (response) { 500 $('#loading-spinner-modal').hide(); 501 updateMap(response); 502 }, 503 error: function(xhr, status, error) { 504 console.error('AJAX Error:', error); 505 $('#loading-spinner-modal').hide(); 506 } 507 }); 508 } 509 510 function submitForm(formId, postType) { 511 $(formId).submit(function (e) { 512 e.preventDefault(); 513 loadMapData(); 514 }); 515 } 516 517 // Load initial data on page load 518 $(window).load(function() { 519 // Small delay to ensure everything is initialized 520 setTimeout(function() { 521 loadMapData(); 522 }, 500); 523 }); 524 525 submitForm('#find-a-dealer', 'dealerships'); 526 submitForm('#find-a-workshop', 'workshops'); 527 submitForm('#find-a-hire-partner', 'hire'); 528 529 // Modal handlers 530 $body.on("click", ".enquire-now-button", function (e) { 531 let enquiryAddress = $(this).closest('.accordion-content').find('address').text(); 532 let enquirySenderEmail = $(this).data('sender-email'); 533 e.preventDefault(); 534 $('#enquiry-form-dealer-name').text(enquiryAddress); 535 $('.enquiry-form #sender-email').val(enquirySenderEmail); 536 $html.addClass('modal-open'); 537 $enquiryFormModal.show(); 538 $enquiryFormModal.find('.contact-form-block').removeClass('hidden').addClass('fade-in-fast'); 539 }); 540 541 $body.on("click", "#enquiry-form-close-button", function (e) { 542 e.preventDefault(); 543 $html.removeClass('modal-open'); 544 $enquiryFormModal.find('.contact-form-block').addClass('hidden').removeClass('fade-in-fast'); 545 $enquiryFormModal.hide(); 546 }); 547 548 // Enter key handlers 549 $('#mapUserLocation, #mapUserDistance').on('keydown', function(event) { 550 if (event.key === 'Enter' && !$('#mapUserSubmitButton').parent().hasClass('disabled')) { 551 submitNearestForm($(this)); 552 } 553 }); 554 555 function submitNearestForm($element) { 556 let $form = $element.closest('form'); 557 if ($form.length > 0) { 558 $form.submit(); 559 } else {
560 console.error("Form element not found for input:", $element.attr('id')); 561 } 562 } 563 }); 564})(jQuery); 565 566// Utility functions 567function calculateDistance(lat1, lon1, lat2, lon2) { 568 var R = 3959; // Radius of the Earth in miles 569 var dLat = toRadians(lat2 - lat1); 570 var dLon = toRadians(lon2 - lon1); 571 var a = 572 Math.sin(dLat / 2) * Math.sin(dLat / 2) + 573 Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * 574 Math.sin(dLon / 2) * Math.sin(dLon / 2); 575 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 576 var distance = R * c; 577 return distance; 578} 579 580function toRadians(degrees) { 581 return degrees * Math.PI / 180; 582} 583document.addEventListener('click', function(e) { 584 585 const btn = e.target.closest('.select-dealer-btn'); 586 if (!btn) return; 587 588 const card = btn.closest('.dealer-card'); 589 if (!card) return; 590 591 const dropdown = card.querySelector('.dealer-dropdown'); 592 if (!dropdown) return; 593 594 // ð´ If dropdown is OPEN â CLOSE it 595 if (dropdown.style.display !== 'none') { 596 dropdown.style.display = 'none'; 597 return; 598 } 599 600 // ð¢ Otherwise OPEN it 601 602 // close all first 603 document.querySelectorAll('.dealer-dropdown').forEach(d => { 604 d.style.display = 'none'; 605 }); 606 607 // open current 608 dropdown.style.display = 'flex'; 609 610});
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.