1/******/ (() => { // webpackBootstrap 2/******/ "use strict"; 3// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules. 4(() => { 5// extracted by mini-css-extract-plugin 6 7})(); 8 9// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules. 10(() => { 11 12;// ./packages/portal/Resources/Private/Components/Atoms/Map/Scripts/base/util/spiderifier.ts 13const TWO_PI = Math.PI * 2; 14const CLASSNAME_CONTAINER = 'map__leg'; 15const CLASSNAME_LINE = 'map__leg__line'; 16const CLASSNAME_PIN = 'map__leg__pin'; 17class Spiderifier { 18 map; 19 options = { 20 circleSpiralSwitchover: 9, 21 circleFootSeparation: 25, 22 spiralFootSeparation: 28, 23 spiralLengthStart: 15, 24 spiralLengthFactor: 4 25 }; 26 previousSpiderLegs = []; 27 constructor(map, options = {}) { 28 this.map = map; 29 this.options = { 30 ...this.options, 31 ...options 32 }; 33 map.on('zoomstart', () => { 34 this.unspiderfy(); 35 }); 36 map.on('click', event => { 37 if (!event.originalEvent.target.closest(CLASSNAME_CONTAINER)) { 38 this.unspiderfy(); 39 } 40 }); 41 } 42 spiderfy(latLng, data) { 43 this.unspiderfy(); 44 const spiderLegParams = this.generateSpiderLegParams(data.length); 45 const spiderLegs = data.map((item, index) => { 46 const params = spiderLegParams[index]; 47 const elements = this.createMarkerElements(params, item); 48 return { 49 data: item, 50 elements: elements, 51 marker: new maplibregl.Marker({ 52 element: elements.container 53 }).setLngLat(latLng), 54 param: params 55 }; 56 }); 57 spiderLegs.forEach(leg => leg.marker.addTo(this.map)); 58 this.previousSpiderLegs = spiderLegs; 59 if (this.options.spiderfyCallback) { 60 this.options.spiderfyCallback(); 61 } 62 } 63 unspiderfy() { 64 this.previousSpiderLegs.forEach(leg => leg.marker.remove()); 65 this.previousSpiderLegs = []; 66 if (this.options.unspiderfyCallback) { 67 this.options.unspiderfyCallback(); 68 } 69 } 70 generateSpiderLegParams(count) { 71 if (count >= this.options.circleSpiralSwitchover) { 72 return this.generateSpiralParams(count); 73 } else { 74 return this.generateCircleParams(count); 75 } 76 } 77 generateSpiralParams(count) { 78 let legLength = this.options.spiralLengthStart; 79 let angle = 0; 80 const output = []; 81 for (let index = 0; index < count; index++) { 82 angle += this.options.spiralFootSeparation / legLength + index * 0.0005; 83 output.push({ 84 x: legLength * Math.cos(angle), 85 y: legLength * Math.sin(angle), 86 angle: angle, 87 legLength: legLength, 88 index: index 89 }); 90 legLength += TWO_PI * this.options.spiralLengthFactor / angle; 91 } 92 return output; 93 } 94 generateCircleParams(count) { 95 const circumference = this.options.circleFootSeparation * (2 + count); 96 const legLength = circumference / TWO_PI; 97 const angleStep = TWO_PI / count; 98 const output = []; 99 for (let index = 0; index < count; index++) { 100 const angle = index * angleStep; 101 output.push({ 102 x: legLength * Math.cos(angle), 103 y: legLength * Math.sin(angle), 104 angle: angle, 105 legLength: legLength, 106 index: index 107 }); 108 } 109 return output; 110 } 111 createMarkerElements(params, data) { 112 const container = document.createElement('div'); 113 const line = document.createElement('div'); 114 const pin = document.createElement('div'); 115 container.classList.add(CLASSNAME_CONTAINER); 116 container.style.marginLeft = params.x + 'px'; 117 container.style.marginTop = params.y + 'px'; 118 line.classList.add(CLASSNAME_LINE); 119 line.style.height = params.legLength + 'px'; 120 line.style.transform = 'rotate(' + (params.angle - Math.PI / 2) + 'rad)'; 121 pin.classList.add(CLASSNAME_PIN); 122 if (this.options.markerBuilderCallback) { 123 pin.appendChild(this.options.markerBuilderCallback(data).getElement()); 124 } 125 container.appendChild(line); 126 container.appendChild(pin); 127 return { 128 container: container, 129 line: line, 130 pin: pin 131 }; 132 } 133} 134;
134// ./packages/portal/Resources/Private/Components/Atoms/Map/Scripts/base/marker-painter/cluster-marker-painter.ts 135 136const SOURCE = 'imx-clustersource'; 137const LAYER = 'imx-clusterlayer'; 138const MAXZOOM = 16; 139class ClusterMarkerPainter { 140 maptoolkit; 141 util; 142 maplibre; 143 spiderifier; 144 builtMarkers = {}; 145 shownMarkers = {}; 146 markerCallback = (data, marker) => marker; 147 constructor(maptoolkit, util) { 148 this.maptoolkit = maptoolkit; 149 this.util = util; 150 this.maplibre = maptoolkit.gl; 151 this.maplibre.addSource(SOURCE, { 152 type: 'geojson', 153 cluster: true, 154 clusterMaxZoom: 18, 155 // max zoom level to not resolve clusters into individual markers 156 data: { 157 type: 'FeatureCollection', 158 features: [] 159 } 160 }); 161 this.maplibre.addLayer({ 162 id: LAYER, 163 type: 'symbol', 164 source: SOURCE 165 }); 166 this.maplibre.on('render', () => { 167 if (!this.maplibre.isSourceLoaded(SOURCE)) return; 168 const features = this.maplibre.querySourceFeatures(SOURCE); 169 const newMarkers = {}; 170 for (const feature of features) { 171 const props = feature.properties; 172 const id = props.cluster_id ?? props.ident; 173 if (!this.builtMarkers[id]) { 174 if (props.cluster) { 175 this.builtMarkers[id] = this.builtMarkers[id] ?? this.buildClusterMarker(feature); 176 } else { 177 const data = JSON.parse(props.marker); 178 const marker = data.icon ? this.util.createIconMarker(data) : this.util.createDefaultMarker(data); 179 this.builtMarkers[id] = this.markerCallback(data, marker); 180 } 181 } 182 newMarkers[id] = this.builtMarkers[id]; 183 if (!this.shownMarkers[id]) { 184 this.builtMarkers[id].addTo(this.maplibre); 185 } 186 } 187 for (const id in this.shownMarkers) { 188 if (!newMarkers[id]) { 189 this.shownMarkers[id].remove(); 190 } 191 } 192 this.shownMarkers = newMarkers; 193 }); 194 this.spiderifier = new Spiderifier(this.maplibre, { 195 spiralLengthStart: 45, 196 circleFootSeparation: 75, 197 spiderfyCallback: () => Object.values(this.builtMarkers).forEach(marker => this.util.backgroundMarker(marker)), 198 unspiderfyCallback: () => Object.values(this.builtMarkers).forEach(marker => this.util.foregroundMarker(marker)), 199 markerBuilderCallback: data => { 200 return this.markerCallback(data, data.icon ? this.util.createIconMarker(data) : this.util.createDefaultMarker(data)); 201 } 202 }); 203 } 204 paint(data, callback) { 205 this.markerCallback = callback ?? ((data, marker) => marker); 206 Object.keys(this.builtMarkers).forEach(id => this.builtMarkers[id].remove()); 207 Object.keys(this.shownMarkers).forEach(id => this.shownMarkers[id].remove()); 208 this.builtMarkers = {}; 209 this.shownMarkers = {}; 210 this.maplibre.getSource(SOURCE).setData({ 211 type: 'FeatureCollection', 212 features: data.map(marker => { 213 return { 214 type: 'Feature', 215 geometry: { 216 type: 'Point', 217 coordinates: marker.lnglat 218 }, 219 properties: { 220 ident: marker.ident, 221 marker: marker 222 } 223 }; 224 }) 225 }); 226 } 227 buildClusterMarker(feature) { 228 const marker = this.util.createClusterMarker(feature.properties.point_count, feature.geometry.coordinates); 229 marker.getElement().addEventListener('click', () => { 230 if (this.maplibre.getZoom() < MAXZOOM) { 231 this.zoomToCluster(feature); 232 } else { 233 this.spiderify(feature); 234 } 235 }); 236 return marker; 237 } 238 zoomToCluster(feature) { 239 this.maplibre.getSource(SOURCE).getClusterExpansionZoom(feature.properties.cluster_id, (error, zoom) => { 240 if (error) return; 241 if (zoom > MAXZOOM) { 242 this.spiderify(feature); 243 } 244 this.maplibre.easeTo({ 245 center: feature.geometry.coordinates, 246 zoom: Math.max(zoom, this.maplibre.getZoom() + 2) 247 }); 248 }); 249 } 250 spiderify(feature) { 251 this.maplibre.getSource(SOURCE).getClusterLeaves(feature.properties.cluster_id, 100, 0, (error, leaves) => { 252 if (error) { 253 return console.error('error while getting leaves of a cluster', error); 254 } 255 this.spiderifier.spiderfy(feature.geometry.coordinates, leaves.map(leaf => leaf.properties.marker)); 256 }); 257 } 258} 259;// ./packages/portal/Resources/Private/Components/Assistants/Shared/Scripts/Classes/json-request.ts 260window.imx = window.imx || {}; 261window.imx.jsonrequest = window.imx.jsonrequest || {}; 262class JsonRequest { 263 request(url) { 264 if (!window.imx.jsonrequest[url]) { 265 window.imx.jsonrequest[url] = fetch(url).then(response => { 266 return response.json(); 267 }); 268 } 269 return window.imx.jsonrequest[url]; 270 } 271} 272;// ./packages/portal/Resources/Private/Components/Assistants/Shared/Scripts/Util/dispatch-custom-event.ts 273/** 274 * Creates and dispatches a custom event, that can be used to loosely couple things together. 275 */ 276function dispatchCustomEvent(target, name, data) { 277 const event = new CustomEvent(name, { 278 detail: data || {} 279 }); 280 target.dispatchEvent(event); 281} 282;// ./packages/portal/Resources/Private/Components/Assistants/Shared/Scripts/Util/load-style.ts 283window.imx = window.imx || {}; 284window.imx.styleloader = window.imx.styleloader || {}; 285function loadStyle(file) { 286 window.imx.styleloader[file] = window.imx.styleloader[file] || new Promise((resolve, reject) => { 287 if (document.querySelector(`link[href*="${file}"]`)) { 288 resolve(file); 289 return; 290 } 291 const link = document.createElement('link'); 292 link.href = file; 293 link.rel = 'stylesheet'; 294 link.onload = () => resolve(file); 295 link.onerror = () => reject(new Error(`Style load error for ${file}`)); 296 document.head.appendChild(link); 297 }); 298 return window.imx.styleloader[file]; 299} 300;// ./packages/portal/Resources/Private/Components/Assistants/Shared/Scripts/Util/load-script.ts 301window.imx = window.imx || {}; 302window.imx.scriptloader = window.imx.scriptloader || {}; 303function loadScript(file) { 304 window.imx.scriptloader[file] = window.imx.scriptloader[file] || new Promise((resolve, reject) => { 305 if (document.querySelector(`script[src*="${file}"]`)) { 306 resolve(file); 307 return; 308 } 309 const script = document.createElement('script'); 310 script.setAttribute('src', file); 311 script.onload = () => resolve(file); 312 script.onerror = () => reject(new Error(`Script load error for ${file}`)); 313 document.body.appendChild(script); 314 }); 315 return window.imx.scriptloader[file]; 316} 317;// ./packages/portal/Resources/Private/Components/Assistants/Shared/Scripts/Classes/content-loader.ts 318/** 319 * LICENSE 320 * 321 * This software and its source code is protected by copyright law (Sec. 69a ff. UrhG). 322 * It is not allowed to make any kinds of modifications, nor must it be copied, 323 * or published without explicit permission. Misuse will lead to persecution. 324 * 325 * @copyright 2020 infomax websolutions GmbH 326 * @link https://www.infomax-online.de 327 * @author Benjamin Hofmann <[email protected]> 328 * @since 2020-02-17 329 */ 330 331 332 333 334/** 335 * 336 * 337 * @copyright 2020 infomax websolutions GmbH 338 * @link https://www.infomax-online.de 339 */ 340class ContentLoader { 341 constructor() { 342 this.request = new JsonRequest(); 343 } 344 load(url) { 345 const promise = this.request.request(url).then(json => { 346 return Promise.all([new Promise(resolve => resolve(json.content)), this.injectCss(json.assets.css), this.injectJavaScript(json.assets.js)]); 347 }).then(data => { 348 return data[0]; 349 }); 350 Promise.all([promise]).finally(() => { 351 dispatchCustomEvent(document, 'ContentLoader.load'); 352 }); 353 return promise; 354 } 355 injectCss(files) { 356 const promises = []; 357 files.filter(file => { 358 return !document.querySelector(`link[href*="${file}"]`); 359 }
359).forEach(file => { 360 promises.push(loadStyle(file)); 361 }); 362 return Promise.all(promises); 363 } 364 injectJavaScript(files) { 365 const promises = []; 366 files.filter(file => { 367 return !document.querySelector(`script[src*="${file}"]`); 368 }).forEach(file => { 369 promises.push(loadScript(file)); 370 }); 371 return Promise.all(promises); 372 } 373} 374;// ./packages/portal/Resources/Private/Components/Assistants/Shared/Scripts/Classes/base-url.ts 375class BaseUrl { 376 getBaseUrl() { 377 const pathSegments = window.location.pathname.split('/').filter(Boolean); 378 const possibleLangCode = pathSegments[0]; 379 const isLangCode = possibleLangCode && /^[a-z]{2,3}$/.test(possibleLangCode); 380 const basePath = isLangCode ? `/${possibleLangCode}` : '/'; 381 return new URL(`${basePath}`, window.location.origin).toString(); 382 } 383} 384;// ./packages/portal/Resources/Private/Components/Structures/MapList/Scripts/map/_init.ts 385 386 387 388const loader = new ContentLoader(); 389const baseUrl = new BaseUrl(); 390document.querySelectorAll('.mapList').forEach(mapList => { 391 const mapWrapper = mapList.querySelector('.mapList__wrapper__map'); 392 const listWrapper = mapList.querySelector('.mapList__wrapper__list'); 393 const listItems = listWrapper.querySelectorAll('.mapList__listItem .cardTeaser'); 394 const infoboard = mapList.querySelector('.mapList__infoboard'); 395 const infoboardCloser = infoboard.querySelector('.mapList__infoboard__closer'); 396 const infoboardContent = infoboard.querySelector('.mapList__infoboard__content'); 397 const markerMap = new Map(); 398 const mql = window.matchMedia('(max-width: 1000px)'); 399 let activeMarker; 400 let activePolyline; 401 let listVisible = false; 402 const markerData = JSON.parse(mapWrapper.dataset.markers); 403 const allCoordinates = markerData.map(data => data.lnglat); 404 // Calculate dynamic map padding based on screen size and visibility 405 const getPaddingOptions = () => ({ 406 top: !listVisible && mql.matches ? 150 : 70, 407 right: !listVisible && mql.matches ? 40 : listWrapper.offsetWidth + 40, 408 bottom: 40, 409 left: 40 410 }); 411 const showInfoboard = (ident, map, util) => { 412 infoboard.removeAttribute('hidden'); 413 infoboard.setAttribute('data-loading', '1'); 414 // Remove previous highlighted marker 415 if (activeMarker) { 416 util.lowlightMarker(activeMarker); 417 activeMarker = undefined; 418 } 419 // Remove previous polyline 420 if (activePolyline) { 421 activePolyline.remove(); 422 activePolyline = undefined; 423 } 424 const url = new URL(`${baseUrl.getBaseUrl()}/action/teaser`, window.location.origin); 425 url.searchParams.set('ident', ident); 426 url.searchParams.set('parent', mapWrapper.dataset.parent); 427 return loader.load(url.toString()).then(response => { 428 infoboard.setAttribute('data-loading', '0'); 429 infoboardContent.innerHTML = response.html; 430 if (response.polyline) { 431 activePolyline = util.createPolyline(response.polyline).addTo(map); 432 util.fitBounds(response.polyline); 433 } 434 }); 435 }; 436 const hideInfoboard = (util, map) => { 437 infoboard.setAttribute('hidden', 'hidden'); 438 infoboard.setAttribute('data-loading', '0'); 439 infoboardContent.innerHTML = ''; 440 if (activeMarker) { 441 util.lowlightMarker(activeMarker); 442 activeMarker = undefined; 443 } 444 // Remove previous polyline 445 if (activePolyline) { 446 activePolyline.remove(); 447 activePolyline = undefined; 448 } 449 util.fitBounds(allCoordinates); 450 }; 451 document.addEventListener(`map.${mapWrapper.id.replace('--map', '')}.ready`, event => { 452 const { 453 map, 454 util 455 } = event.detail; 456 // Ensure map resizes when wrapper is in view 457 new IntersectionObserver(() => map.gl.resize()).observe(mapWrapper); 458 // Adjust padding when list visibility changes (e.g., on scroll or resize) 459 new IntersectionObserver(([entry]) => { 460 listVisible = entry.isIntersecting; 461 map.gl.setPadding(getPaddingOptions()); 462 util.fitBounds(allCoordinates); 463 }).observe(listWrapper); 464 // Adjust padding when screen width crosses threshold 465 mql.addEventListener('change', () => { 466 map.gl.setPadding(getPaddingOptions()); 467 util.fitBounds(allCoordinates); 468 }); 469 // Close infoboard when close button is clicked 470 infoboardCloser.addEventListener('click', () => hideInfoboard(util, map)); 471 new ClusterMarkerPainter(map, util).paint(markerData, (data, marker) => { 472 markerMap.set(data.ident, marker); 473 // Click on marker opens infoboard and polyline 474 marker.getElement().addEventListener('click', () => { 475 showInfoboard(data.ident, map, util).then(() => { 476 util.highlightMarker(marker); 477 activeMarker = marker; 478 }); 479 }); 480 return marker; 481 }); 482 // Highlight markers on hover in the list
483 listItems.forEach(item => { 484 const ident = item.getAttribute('data-ident'); 485 item.addEventListener('mouseenter', () => { 486 const marker = markerMap.get(ident); 487 if (marker) util.highlightMarker(marker); 488 }); 489 item.addEventListener('mouseleave', () => { 490 const marker = markerMap.get(ident); 491 if (marker && marker !== activeMarker) util.lowlightMarker(marker); 492 }); 493 }); 494 }); 495}); 496})(); 497 498// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules. 499(() => { 500 501 502const mql = window.matchMedia('(max-width: 1000px)'); 503const mapWrappers = document.querySelectorAll('.mapList__wrapper__map'); 504const listWrappers = document.querySelectorAll('.mapList__wrapper__list'); 505function updateListView(mql) { 506 if (mql.matches) { 507 mapWrappers.forEach(wrapper => wrapper.setAttribute('hidden', 'hidden')); 508 listWrappers.forEach(wrapper => wrapper.removeAttribute('hidden')); 509 } else { 510 mapWrappers.forEach(wrapper => wrapper.removeAttribute('hidden')); 511 listWrappers.forEach(wrapper => wrapper.removeAttribute('hidden')); 512 } 513} 514mql.addEventListener('change', event => updateListView(event)); 515updateListView(mql); 516})(); 517 518/******/ })() 519;
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.