PageSourceSearch

https://www.nordrest.se/wp-content/plugins/dynamic-content-for-ele…/js/dynamic-google-maps.js?ver=3.4.16

js nordrest.se collected 2026-10-04 21:57:58 UTC 17,053 bytes, 630 lines download raw bytes

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.