1(function ($) { 2 var DyncontEl_GoogleMapsHandler = function ($scope, $) { 3 async function waitForGoogleMapsMap(tries = 50, delay = 100) { 4 return new Promise((resolve, reject) => { 5 const tick = () => { 6 if ( 7 window.google && 8 google.maps && 9 typeof google.maps.Map === "function" 10 ) { 11 resolve(); 12 return; 13 } 14 if (tries-- <= 0) { 15 reject(new Error("Google Maps API not ready")); 16 return; 17 } 18 setTimeout(tick, delay); 19 }; 20 tick(); 21 }); 22 } 23 24 async function initMap() { 25 var $map = $scope.find(".map"); 26 var map = $map[0]; 27 var id_scope = $scope.attr("data-id"); 28 var bounds; 29 var infoWindows = []; 30 31 // Positions 32 if (!$map[0]) { 33 console.error("Missing params"); 34 return; 35 } 36 let positions = $map[0].dataset.positions; 37 try { 38 positions = JSON.parse(positions); 39 } catch (err) { 40 console.error(err); 41 positions = []; 42 } 43 44 // Exit if the map doesn't have positions 45 if (!positions.length) { 46 return; 47 } 48 49 let marker; 50 var latitude = parseFloat(positions[0].lat) || 0; 51 var longitude = parseFloat(positions[0].lng) || 0; 52 var elementSettings = dceGetElementSettings($scope); 53 var mapId = id_scope; 54 var newMarkerObject = Boolean(elementSettings.new_api); 55 var markerWidth = elementSettings.marker_width || 20; 56 var markerHeight = elementSettings.marker_height || 20; 57 58 try { 59 await waitForGoogleMapsMap(); 60 } catch (error) { 61 return; 62 } 63 64 let AdvancedMarkerElement; 65 66 // Import marker library if using new API 67 if (newMarkerObject && google.maps.importLibrary) { 68 try { 69 const markerLib = await google.maps.importLibrary("marker"); 70 AdvancedMarkerElement = markerLib.AdvancedMarkerElement; 71 } catch (error) { 72 console.warn('Failed to load Marker library with importLibrary, falling back to legacy method:', error); 73 } 74 } 75 var zoom = $map.data("zoom") || 10; 76 var imageMarker = positions[0].custom_marker_image || ""; 77 var infoWindow_panel_maxwidth = 78 elementSettings.infoWindow_panel_maxwidth; 79 80 if (markerWidth && markerHeight && imageMarker) { 81 imageMarker = { 82 url: imageMarker, 83 scaledSize: new google.maps.Size(markerWidth, markerHeight), 84 }; 85 } 86 87 // Map Parameters 88 var mapParams = { 89 zoom: zoom, 90 scrollwheel: Boolean(elementSettings.prevent_scroll), 91 mapTypeControl: Boolean(elementSettings.maptypecontrol), 92 panControl: Boolean(elementSettings.pancontrol), 93 rotateControl: Boolean(elementSettings.rotaterontrol), 94 scaleControl: Boolean(elementSettings.scalecontrol), 95 streetViewControl: Boolean(elementSettings.streetviewcontrol), 96 zoomControl: Boolean(elementSettings.zoomcontrol), 97 fullscreenControl: Boolean(elementSettings.fullscreenControl), 98 center: { 99 lat: latitude, 100 lng: longitude, 101 }, 102 }; 103 if (newMarkerObject) { 104 mapParams["mapId"] = mapId; 105 } 106 107 // Map Type (Roadmap, satellite, etc.) 108 if ( 109 elementSettings.map_type && 110 elementSettings.map_type !== "acfmap" 111 ) { 112 mapParams["mapTypeId"] = elementSettings.map_type; 113 } 114 115 // Zoom Minimum and Maximum 116 if (elementSettings.zoom_custom) { 117 minZoom = elementSettings.zoom_minimum.size || 0; 118 maxZoom = elementSettings.zoom_maximum.size || 20; 119 if (minZoom > maxZoom) { 120 minZoom = maxZoom; 121 } 122 mapParams["minZoom"] = minZoom; 123 mapParams["maxZoom"] = maxZoom; 124 } 125 126 if (elementSettings.style_select === "prestyle") { 127 let fileStyle = elementSettings.snazzy_select; 128 let jsonUrl; 129 130 // Check if fileStyle starts with 'http' (compatibility with old settings that contains DCE_URL/json_file without .json extension) 131 if (fileStyle.startsWith("http")) { 132 jsonUrl = fileStyle + ".json"; // Append '.json' extension to the URL 133 } else { 134 // New settings contains only the file name 135 let dceUrl = elementSettings.dce_url; // Get DCE_URL from elementSettings 136 jsonUrl = 137 dceUrl + "assets/maps_style/" + fileStyle + ".json"; // Construct the full URL 138 } 139 140 $.getJSON(jsonUrl, function (json) { 141 mapParams["styles"] = json; 142 _initMap(map, mapParams); 143 }); 144 } else { 145 if (elementSettings.style_select === "custom") { 146 mapParams["styles"] = JSON.parse(elementSettings.style_map); 147 } 148 _initMap(map, mapParams); 149 } 150 151 function _initMap(mapElement, mapParameters) { 152 map = new google.maps.Map(mapElement, mapParameters); 153 var markers = []; 154 var mapDataType = elementSettings.map_data_type; 155 156 // Geolocation 157 if (elementSettings.geolocation == "yes") { 158 const locationButton = document.createElement("button"); 159 locationButton.textContent = 160 elementSettings.geolocation_button_text; 161 locationButton.classList.add("custom-map-control-button"); 162 map.controls[google.maps.ControlPosition.TOP_CENTER].push( 163 locationButton, 164 ); 165 locationButton.addEventListener("click", () => { 166 // HTML5 geolocation 167 if (navigator.geolocation) { 168 navigator.geolocation.getCurrentPosition( 169 (position) => { 170 const pos = { 171 lat: position.coords.latitude, 172 lng: position.coords.longitude, 173 }; 174 map.setCenter(pos); 175 if ( 176 elementSettings.geolocation_change_zoom 177 ) { 178 map.setZoom( 179 elementSettings.geolocation_zoom 180 .size || 10, 181 ); 182 } 183 }, 184 () => { 185 handleLocationError( 186 true, 187 new google.maps.InfoWindow(), 188 map.getCenter(), 189 ); 190 }, 191 ); 192 } else { 193 // Browser doesn't support Geolocation 194 handleLocationError( 195 false, 196 new google.maps.InfoWindow(), 197 map.getCenter(), 198 ); 199 } 200 }); 201 } 202 203 function createAndAddMarker( 204 position, 205 map, 206 icon, 207 animation, 208 index = null, 209 ) { 210 const markerElement = document.createElement("div"); 211 if (icon) { 212 markerElement.innerHTML = `<img src="${icon.url}" alt="marker icon" style="width: ${icon.scaledSize.width}px; height: ${icon.scaledSize.height}px;">`; 213 } 214 if ( 215 !newMarkerObject || 216 !AdvancedMarkerElement 217 ) { 218 marker = new google.maps.Marker({ 219 position, 220 map, 221 icon, 222 animation, 223 }); 224 } else { 225 marker = new AdvancedMarkerElement({ 226 position, 227 map, 228 content: icon ? markerElement : undefined, 229 }); 230 if (animation) { 231 markerElement.style.animation = animation; 232 } 233 } 234 235 if (elementSettings.enable_infoWindow) { 236 if (index !== null) { 237 createInfoWindow(marker, index); 238 } else if (index === null) { 239 createInfoWindow(marker); 240 } 241 } 242 243 return marker; 244 } 245 246 function getImageMarkerList( 247 imageMarkerList, 248 markerWidth, 249 markerHeight, 250 ) { 251 if (markerWidth && markerHeight && imageMarkerList) { 252 return { 253 url: imageMarkerList, 254 scaledSize: new google.maps.Size( 255 markerWidth, 256 markerHeight, 257 ), 258 }; 259 } 260 return imageMarkerList; 261 } 262 263 if (mapDataType === "address") { 264 let address = positions[0].address || ""; 265 let geocoder = new google.maps.Geocoder(); 266 geocoder.geocode( 267 { 268 address: address, 269 }, 270 function (results, status) { 271 if (status === "OK") { 272 map.setCenter(results[0].geometry.location); 273 createAndAddMarker( 274 results[0].geometry.location, 275 map, 276 imageMarker, 277 google.maps.Animation.DROP, 278 ); 279 } 280 }, 281 ); 282 } else if ( 283 mapDataType === "latlng" || 284 (!elementSettings.use_query && 285 (mapDataType === "acfmap" || 286 mapDataType === "metabox_google_maps")) 287 ) { 288 const latLng = new google.maps.LatLng(latitude, longitude); 289 map.panTo(latLng); 290 createAndAddMarker( 291 latLng, 292 map, 293 imageMarker, 294 google.maps.Animation.DROP, 295 ); 296 } else { 297 // Query 298 const bounds = new google.maps.LatLngBounds(); 299 positions.forEach((position, index) => { 300 const latLng = new google.maps.LatLng( 301 position.lat, 302 position.lng, 303 ); 304 map.panTo(latLng); 305 const imageMarkerList = getImageMarkerList( 306 position.custom_marker_image, 307 elementSettings.marker_width, 308 elementSettings.marker_height, 309 ); 310 311 let markerPosition = latLng; 312 313 if (elementSettings.prevent_marker_overlapping == "yes") { 314 // Adjust marker position to prevent overlapping with other markers 315 const adjustedPosition = adjustCoordinates(position.lat, position.lng, index, positions.length); 316 markerPosition = new google.maps.LatLng(adjustedPosition.lat, adjustedPosition.lng); 317 } 318 319 const marker = createAndAddMarker( 320 markerPosition, 321 map, 322 imageMarkerList, 323 google.maps.Animation.DROP, 324 index, 325 ); 326 markers.push(marker); 327 bounds.extend(marker.position); 328 }); 329 330 map.fitBounds(bounds); 331 if (!elementSettings.auto_zoom) { 332 google.maps.event.addListenerOnce( 333 map, 334 "idle", 335 function () { 336 map.setZoom(zoom); 337 }, 338 ); 339 } 340 341 if (elementSettings.markerclustererControl) { 342 new markerClusterer.MarkerClusterer({ 343 map, 344 markers, 345 }); 346 } 347 } 348 } 349 350 // Adjust coordinates to prevent overlapping markers 351 function adjustCoordinates(lat, lng, index, totalPoints) { 352 if (index === 0) { 353 return { lat, lng }; 354 } 355 const radiusInMeters = 1.5; 356 const metersToDegrees = 0.000009;
357 const radius = radiusInMeters * metersToDegrees; 358 // Angle calculation to distribute points around the center 359 const angle = (2 * Math.PI / (totalPoints - 1)) * (index - 1); 360 const dLat = radius * Math.cos(angle); 361 const dLng = radius * Math.sin(angle); 362 return { 363 lat: lat + dLat, 364 lng: lng + dLng, 365 }; 366 } 367 368 function createInfoWindow(marker, index = 0) { 369 let infoWindowData = positions[index].infowindow; 370 let maxWidth; 371 372 if (typeof infoWindow_panel_maxwidth !== "undefined") { 373 maxWidth = infoWindow_panel_maxwidth.size; 374 } 375 376 marker.addListener("click", function () { 377 if (elementSettings.infoWindow_one_at_the_time) { 378 infoWindows.forEach(function (infoWindow) { 379 infoWindow.close(); 380 }); 381 } 382 383 if (positions[index].link) { 384 return (window.location = positions[index].link); 385 } 386 if ( 387 "text" === infoWindowData["type"] && 388 !infoWindowData["content"] 389 ) { 390 return; 391 } 392 if ("text" === infoWindowData["type"]) { 393 if (!marker.infoWindow) { 394 marker.infoWindow = new google.maps.InfoWindow({ 395 content: infoWindowData["content"], 396 maxWidth: maxWidth, 397 }); 398 } else { 399 marker.infoWindow.setContent( 400 infoWindowData["content"], 401 ); 402 } 403 marker.infoWindow.open(map, marker); 404 infoWindows.push(marker.infoWindow); 405 return; 406 } 407 let loadingContent = 408 elementSettings.infoWindow_loading_text || "Loading..."; 409 var loadingInfoWindow = new google.maps.InfoWindow({ 410 content: loadingContent, 411 disableAutoPan: true, 412 }); 413 414 loadingInfoWindow.open(map, marker); 415 416 let templateId = infoWindowData["template-id"] || 0; 417 let postId = infoWindowData["post-id"] || 0; 418 419 // Load template content if not already done 420 if (!marker.templateContent) { 421 var ajaxData = { 422 template_id: templateId, 423 post_id: postId, 424 nonce: dce_ajax_object.nonce, 425 }; 426 427 wp.ajax 428 .post("load_elementor_template_content", ajaxData) 429 .done(function (response) { 430 loadingInfoWindow.close(); 431 marker.templateContent = response; 432 if (!marker.infoWindow) { 433 marker.infoWindow = 434 new google.maps.InfoWindow({ 435 maxWidth: maxWidth, 436 content: response, 437 }); 438 } 439 infoWindows.push(marker.infoWindow); 440 marker.infoWindow.open(map, marker); 441 }) 442 .fail(function (error) { 443 console.error("Ajax Error:", error); 444 loadingInfoWindow.close(); 445 }); 446 } else { 447 loadingInfoWindow.close(); 448 marker.infoWindow.setContent(marker.templateContent); 449 marker.infoWindow.open(map, marker); 450 } 451 }); 452 453 // Close InfoWindow when clicking outside of maps 454 if (elementSettings.infoWindow_click_outside) { 455 document.body.addEventListener("click", function (e) { 456 var isClickOutsideMap = !e.target.closest(".map"); 457 if (marker.infoWindow && isClickOutsideMap) { 458 marker.infoWindow.close(); 459 } 460 }); 461 } 462 } 463 464 function handleLocationError( 465 browserHasGeolocation, 466 infoWindow, 467 pos, 468 ) { 469 infoWindow.setPosition(pos); 470 infoWindow.setContent( 471 browserHasGeolocation 472 ? "Error: The Geolocation service failed." 473 : "Error: Your browser doesn't support geolocation.", 474 ); 475 infoWindow.open(map); 476 } 477 }; 478 // google api might loaded before or after this script based on third 479 // party plugins. So we take both cases into account: 480 if (typeof google !== "undefined") { 481 initMap(); 482 } else { 483 window.addEventListener("dce-google-maps-api-loaded", initMap); 484 } 485 }; 486 487 const reInitElementorWidgets = (domElement) => { 488 if ( 489 !domElement || 490 !window.elementorFrontend || 491 !window.elementorFrontend.elementsHandler || 492 !window.elementorFrontend.elementsHandler.runReadyTrigger 493 ) { 494 return; 495 } 496 const runReadyTrigger = 497 window.elementorFrontend.elementsHandler.runReadyTrigger; 498 499 runReadyTrigger($(domElement)); 500 501 $(domElement) 502 .find(".elementor-widget") 503 .each(function () { 504 runReadyTrigger($(this)); 505 }); 506 507 $(domElement) 508 .find( 509 ".elementor-widget-dyncontel-acf-google-maps, .elementor-widget-dce-metabox-google-maps", 510 ) 511 .each(function () { 512 DyncontEl_GoogleMapsHandler($(this), $); 513 }); 514 }; 515 516 const fadeElements = (queryObject, enabled = false) => { 517 let attributes = queryObject.getAttributes(); 518 519 const defaults = { 520 enabled: enabled, 521 duration: 200, 522 }; 523 if (!defaults.enabled) { 524 $(attributes.queryContainer).css("opacity", 0).animate( 525 { 526 opacity: 1, 527 }, 528 defaults.duration, 529 ); 530 $(attributes.dynamicSections).css("opacity", 0).animate( 531 { 532 opacity: 1, 533 }, 534 defaults.duration, 535 ); 536 return; 537 } 538 [attributes.queryContainer, attributes.dynamicSections].forEac
538h( 539 (selector) => { 540 const $element = $(selector); 541 if ($element.length) { 542 $element.css("opacity", 1).animate( 543 { 544 opacity: 0, 545 }, 546 defaults.duration, 547 ); 548 } 549 }, 550 ); 551 }; 552 553 const initSearchAndFilterProQueryListeners = (query) => { 554 const supportedIntegrations = [ 555 "dynamicooo/dynamic-content-for-elementor", 556 ]; 557 558 if (query.getAttribute("resultsDynamicUpdate") !== "yes") { 559 return; 560 } 561 562 if ( 563 !supportedIntegrations.includes( 564 query.getAttribute("queryIntegration"), 565 ) 566 ) { 567 return; 568 } 569 570 query.on("get-results/finish", function (queryObject) { 571 const queryContainer = queryObject.getQueryContainer(); 572 reInitElementorWidgets(queryContainer); 573 const dynamicSectionsSelector = 574 query.getAttribute("dynamicSections"); 575 const dynamicSectionsArea = document.querySelector( 576 dynamicSectionsSelector, 577 ); 578 reInitElementorWidgets(dynamicSectionsArea); 579 fadeElements(queryObject, false); 580 }); 581 query.on("get-results/start", function (queryObject) { 582 fadeElements(queryObject, true); 583 }); 584 }; 585 586 // The dynamicooo/google-maps/init event is for GDPR plugins like Borlabs: 587 $(window).on( 588 "elementor/frontend/init dynamicooo/google-maps-init", 589 function () { 590 elementorFrontend.hooks.addAction( 591 "frontend/element_ready/dyncontel-acf-google-maps.default", 592 DyncontEl_GoogleMapsHandler, 593 ); 594 elementorFrontend.hooks.addAction( 595 "frontend/element_ready/dce-metabox-google-maps.default", 596 DyncontEl_GoogleMapsHandler, 597 ); 598 }, 599 ); 600 $(window).on("elementor/frontend/init", () => { 601 if (window.searchAndFilter?.frontend?.queries) { 602 const queries = window.searchAndFilter.frontend.queries.getAll(); 603 604 for (let i = 0; i < queries.length; i++) { 605 const query = queries[i]; 606 initSearchAndFilterProQueryListeners(query); 607 } 608 } 609 }); 610})(jQuery); 611 612// Re init layout after ajax request on Search&Filter Pro 613(function ($) { 614 "use strict"; 615 $(function () { 616 // Support for Search & Filter Pro v2 617 $(document).on("sf:ajaxfinish", ".searchandfilter", function (e, data) { 618 if (elementorFrontend) { 619 if ( 620 elementorFrontend.elementsHandler.runReadyTrigger && 621 SF_LDATA.extensions.indexOf("search-filter-elementor") < 0 622 ) { 623 elementorFrontend.elementsHandler.runReadyTrigger( 624 $(data.targetSelector), 625 ); 626 } 627 } 628 }); 629 }); 630})(jQuery);
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.