PageSourceSearch

https://www.oberwolfach.de/typo3conf/ext/hwlocationmap/Resources/Public/Js/hw_locationmap_map.js?1713876999

js oberwolfach.de collected 2026-10-02 20:36:23 UTC 22,078 bytes, 465 lines download raw bytes

1// Copyright for the additional Leaflet ExtraMarkers by coryasilva
2//
3// The MIT License (MIT)
4//
5// Copyright (c) 2014 coryasilva
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25/* Copyright for the additional Leaflet Pan Control - L.control.HwPan
26Copyright (c) 2012, Kartena AB
27All rights reserved.
28
29    Redistribution and use in source and binary forms, with or without
30modification, are permitted provided that the following conditions are met:
31
32    1. Redistributions of source code must retain the above copyright notice, this
33list of conditions and the following disclaimer.
342. Redistributions in binary form must reproduce the above copyright notice,
35    this list of conditions and the following disclaimer in the documentation
36and/or other materials provided with the distribution.
37
38    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
39ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
40WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
42ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
48
49// The rest of the code is provided by Hirsch & Wölfl GmbH, to be used inside its web-projects.
50// Copyright (c) 2022 Hirsch & Wölfl GmbH [email protected], [email protected]
51
52if (document.readyState === 'complete') {
53    hwLocationmap();
54} else {
55    document.addEventListener('readystatechange', () => {
56        if (document.readyState === 'complete') {
57            hwLocationmap();
58        }
59    });
60}
61
62
63function hwLocationmap() {
64
65    // find all HWLocationmap maps
66    const hwLocationmapElements = document.getElementsByClassName('hw_location_map');
67
68    // for every found map...
69    for (const mapElement of hwLocationmapElements) {
70        runLocationMap(mapElement).then(map => {
71            addResizeObserver(mapElement, map);
72        });
73    }
74
75    // Start of the Leaflet ExtraMarkers
76    var ExtraMarker = L.Icon.extend({
77        options: {
78            iconSize: [ 35, 45 ],
79            iconAnchor: [ 17, 42 ],
80            popupAnchor: [ 1, -32 ],
81            shadowAnchor: [ 10, 12 ],
82            shadowSize: [ 36, 16 ],
83            className: "",
84            prefix: "",
85            extraClasses: "",
86            shape: "circle",
87            icon: "",
88            innerHTML: "",
89            markerColor: "red",
90            svgBorderColor: "#fff",
91            svgOpacity: 1,
92            iconColor: "#fff",
93            iconRotate: 0,
94            number: "",
95            svg: false,
96            name: ""
97        },
98        initialize: function(options) {
99            options = L.Util.setOptions(this, options);
100        },
101        createIcon: function() {
102            var div = document.createElement("div"), options = this.options;
103            if (options.icon) {
104                div.innerHTML = this._createInner();
105            }
106            if (options.innerHTML) {
107                div.innerHTML = options.innerHTML;
108            }
109            if (options.bgPos) {
110                div.style.backgroundPosition = -options.bgPos.x + "px " + -options.bgPos.y + "px";
111            }
112            if (!options.svg) {
113                this._setIconStyles(div, options.shape + "-" + options.markerColor);
114            } else {
115                this._setIconStyles(div, "svg");
116            }
117            return div;
118        },
119        _getColorHex: function (color) {
120            var colorMap = {
121                red: "#a23337",
122                "orange-dark": "#d73e29",
123                orange: "#ef9227",
124                yellow: "#f5bb39",
125                "blue-dark": "#276273",
126                cyan: "#32a9dd",
127                purple: "#440444",
128                violet: "#90278d",
129                pink: "#c057a0",
130                green: "#006838",
131                white: "#e8e8e8",
132                black: "#211c1d"
133            };
134            return colorMap[color] || color;
135        },
136        _createSvg: function (shape, markerColor) {
137            var svgMap = {
138                circle: '<svg class="extra-markers" width="32" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 2.746c-8.284 0-15 6.853-15 15.307 0 .963.098 1.902.265 2.816a15.413 15.413 0 002.262 5.684l.134.193 12.295 17.785 12.439-17.863.056-.08a15.422 15.422 0 002.343-6.112c.123-.791.206-1.597.206-2.423 0-8.454-6.716-15.307-15-15.307" fill="' + markerColor + '" /><path d="M17.488 2.748c-8.284 0-15 6.853-15 15.307 0 .963.098 1.902.265 2.816a15.413 15.413 0 002.262 5.684l.134.193 12.295 17.785 12.44-17.863.055-.08a15.422 15.422 0 002.343-6.112c.124-.791.206-1.597.206-2.423 0-8.454-6.716-15.307-15-15.307m0 1.071c7.68 0 13.929 6.386 13.929 14.236 0 .685-.064 1.423-.193 2.258-.325 2.075-1.059 3.99-2.164 5.667l-.055.078-11.557 16.595L6.032 26.14l-.12-.174a14.256 14.256 0 01-2.105-5.29 14.698 14.698 0 01-.247-2.62c0-7.851 6.249-14.237 13.928-14.237" fill="#231f20" opacity=".15" /></svg>',
139                square: '<svg class="extra-markers" width="33" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M28.205 3.217H6.777c-2.367 0-4.286 1.87-4.286 4.179v19.847c0 2.308 1.919 4.179 4.286 4.179h5.357l5.337 13.58 5.377-13.58h5.357c2.366 0 4.285-1.87 4.285-4.179V7.396c0-2.308-1.919-4.179-4.285-4.179" fill="' + markerColor + '" /><g opacity=".15" transform="matrix(1.0714 0 0 -1.0714 -233.22 146.783)"><path d="M244 134h-20c-2.209 0-4-1.746-4-3.9v-18.525c0-2.154 1.791-3.9 4-3.9h5L233.982 95 239 107.675h5c2.209 0 4 1.746 4 3.9V130.1c0 2.154-1.791 3.9-4 3.9m0-1c1.654 0 3-1.301 3-2.9v-18.525c0-1.599-1.346-2.9-3-2.9h-5.68l-.25-.632-4.084-10.318-4.055 10.316-.249.634H224c-1.654 0-3 1.301-3 2.9V130.1c0 1.599 1.346 2.9 3 2.9h20" fill="#231f20" /></g></svg>',
140                star:   '<svg class="extra-markers" width="34" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M32.92 16.93l-3.525-3.525V8.419a1.983 1.983 0 00-1.983-1.982h-4.985L18.9 2.91a1.984 1.984 0 00-2.803 0l-3.524 3.526H7.588a1.983 1.983 0 00-1.982 
1401.982v4.986L2.081 16.93a1.982 1.982 0 000 2.803l3.525 3.526v4.984c0 1.096.888 1.983 1.982 1.983h4.986L17.457 45l4.97-14.773h4.985a1.983 1.983 0 001.983-1.983V23.26l3.525-3.526a1.982 1.982 0 000-2.803" fill="' + markerColor + '" /><g opacity=".15" transform="matrix(1.0667 0 0 -1.0667 -347.3 97.26)"><path d="M342 89c-.476 0-.951-.181-1.314-.544l-3.305-3.305h-4.673a1.858 1.858 0 01-1.859-1.858v-4.674l-3.305-3.305a1.857 1.857 0 010-2.627l3.305-3.305v-4.674a1.86 1.86 0 011.859-1.859h4.673L341.959 49l4.659 13.849h4.674a1.86 1.86 0 011.859 1.859v4.674l3.305 3.305a1.858 1.858 0 010 2.627l-3.305 3.305v4.674a1.859 1.859 0 01-1.859 1.858h-4.674l-3.304 3.305A1.851 1.851 0 01342 89m0-1a.853.853 0 00.607-.251l3.304-3.305.293-.293h5.088a.86.86 0 00.859-.858v-5.088l3.598-3.598A.852.852 0 00356 74a.85.85 0 00-.251-.606l-3.598-3.598v-5.088a.86.86 0 00-.859-.859h-5.393l-.229-.681-3.702-11.006-3.637 11.001-.227.686h-5.396a.86.86 0 00-.859.859v5.088l-3.598 3.598c-.162.162-.251.377-.251.606s.089.445.251.607l3.598 3.598v5.088a.86.86 0 00.859.858h5.087l3.598 3.598A.853.853 0 00342 88" fill="#231f20" /></g></svg>',
141                penta:  '<svg class="extra-markers" width="33" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M1.872 17.35L9.679 2.993h15.615L33.1 17.35 17.486 44.992z" fill="' + markerColor + '" /><g opacity=".15" transform="matrix(1.0769 0 0 -1.0769 -272.731 48.23)"><path d="M276.75 42h-14.5L255 28.668 269.5 3 284 28.668zm-.595-1l6.701-12.323L269.5 5.033l-13.356 23.644L262.845 41z" fill="#231f20" /></g></svg>'
142            };
143            return svgMap[shape];
144        },
145        _createInner: function() {
146            var iconStyle = "", iconNumber = "", iconClass = "", result = "", options = this.options;
147            if (options.iconColor) {
148                iconStyle = "color: " + options.iconColor + ";";
149            }
150            if (options.iconRotate !== 0) {
151                iconStyle += "-webkit-transform: rotate(" + options.iconRotate + "deg);";
152                iconStyle += "-moz-transform: rotate(" + options.iconRotate + "deg);";
153                iconStyle += "-o-transform: rotate(" + options.iconRotate + "deg);";
154                iconStyle += "-ms-transform: rotate(" + options.iconRotate + "deg);";
155                iconStyle += "transform: rotate(" + options.iconRotate + "deg);";
156            }
157            if (options.number) {
158                iconNumber = 'number="' + options.number + '" ';
159            }
160            if (options.extraClasses.length) {
161                iconClass += options.extraClasses + " ";
162            }
163            if (options.prefix.length) {
164                iconClass += options.prefix + " ";
165            }
166            if (options.icon.length) {
167                iconClass += options.icon + " ";
168            }
169            if (options.svg) {
170                result += this._createSvg(options.shape, this._getColorHex(options.markerColor));
171            }
172
173            result += '<i ' + iconNumber + 'style="' + iconStyle + '" class="' + iconClass + '"></i>';
174
175            if (options.name.length) {
176                result += '<div class="' + (options.nameClasses !== undefined ? options.nameClasses : '') + '">' + options.name + '</div>';
177            }
178
179            return result;
180        },
181        _setIconStyles: function(img, name) {
182            var options = this.options, size = L.point(options[name === "shadow" ? "shadowSize" : "iconSize"]), anchor, leafletName;
183            if (name === "shadow") {
184                anchor = L.point(options.shadowAnchor || options.iconAnchor);
185                leafletName = "shadow";
186            } else {
187                anchor = L.point(options.iconAnchor);
188                leafletName = "icon";
189            }
190            if (!anchor && size) {
191                anchor = size.divideBy(2, true);
192            }
193            img.className = "leaflet-marker-" + leafletName + " extra-marker extra-marker-" + name + " " + options.className;
194            if (anchor) {
195                img.style.marginLeft = -anchor.x + "px";
196                img.style.marginTop = -anchor.y + "px";
197            }
198            if (size) {
199                img.style.width = size.x + "px";
200                img.style.height = size.y + "px";
201            }
202        },
203        createShadow: function() {
204            var div = document.createElement("div");
205            this._setIconStyles(div, "shadow");
206            return div;
207        }
208    });
209    // End of the Leaflet ExtraMarkers
210
211    // create the leaflet map
212    async function runLocationMap(mapElement) {
213
214        // Map settings
215        const {
216            minZoom = 4,
217            maxZoom = 18,
218            defaultZoom = null,
219        } = mapElement.dataset;
220
221        // Get language attributes for zoom, pan, fullscreen from a div container, which is filled with from a language file
222        let languageAttributes = document.querySelector('.hw_map_location__js_attributes');
223
224        // initialize map
225        let leafletMap = L.map(mapElement.getAttribute('id'), {
226            minZoom: minZoom,
227            maxZoom: maxZoom,
228            keyboard: true,
229            zoomControl: false,
230            scrollWheelZoom: false,
231            dragging: true,
232            touchZoom: true,
233            zoomAnimation: true,
234            gestureHandling: true,
235            markerSet: false,
236        }
236).setView([mapElement.getAttribute('data-start-lat'), mapElement.getAttribute('data-start-lng')], 15);
237
238        // map layer
239        let osmLayer = L.tileLayer('/index.php?eID=tx_hwlib_osmTile&s={s}&x={x}&y={y}&z={z}', {
240            minZoom: minZoom,
241            maxZoom: maxZoom,
242            noWrap: true,
243            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap-Mitwirkende</a>'
244        }).addTo(leafletMap)
245
246        // scale
247        L.control.scale(
248            {
249                position: 'bottomleft',
250                imperial: false
251            }
252        ).addTo(leafletMap);
253
254        L.control.HwPan = L.Control.extend({
255            options: {
256                position: 'topleft',
257                panOffset: 500
258            },
259            onAdd: function (map) {
260                let className = 'hw_map_location__control__pan_map',
261                    container = L.DomUtil.create('div', className),
262                    off = this.options.panOffset;
263
264                this._panButton(languageAttributes.dataset.languageUparrow, className + '--up',
265                    container, map, new L.Point(    0 , -off));
266                this._panButton(languageAttributes.dataset.languageLeftarrow, className + '--left',
267                    container, map, new L.Point( -off ,  0));
268                this._panButton(languageAttributes.dataset.languageRightarrow, className + '--right',
269                    container, map, new L.Point(  off ,  0));
270                this._panButton(languageAttributes.dataset.languageDownarrow, className + '--down',
271                    container, map, new L.Point(    0 ,  off));
272                let controlContainer;
273                // Add pan control class to the control container
274                if (this.options.position === 'topleft') {
275                    controlContainer = L.DomUtil.get(map._controlCorners.topleft);
276                } else if (this.options.position === 'topright') {
277                    controlContainer = L.DomUtil.get(map._controlCorners.topright);
278                } else if (this.options.position === 'bottomleft') {
279                    controlContainer = L.DomUtil.get(map._controlCorners.bottomleft);
280                } else {
281                    controlContainer = L.DomUtil.get(map._controlCorners.bottomright);
282                }
283                if(!L.DomUtil.hasClass(controlContainer, 'has-hw-leaflet-pan-control')) {
284                    L.DomUtil.addClass(controlContainer, 'has-hw-leaflet-pan-control');
285                }
286
287                return container;
288            },
289            _panButton: function (title, className, container, map, offset) {
290                var wrapper = L.DomUtil.create('div', className + '__wrap', container);
291                var button = L.DomUtil.create('button', className, wrapper);
292                button.type = 'button';
293                button.title = title;
294                L.DomEvent
295                    .on(button, 'click', L.DomEvent.stopPropagation)
296                    .on(button, 'click', L.DomEvent.preventDefault)
297                    .on(button, 'click', function(){ map.panBy(offset); }, map)
298                    .on(button, 'dblclick', L.DomEvent.stopPropagation);
299
300                return button;
301            }
302        });
303
304        L.control.hwPan = function(options) {
305            return new L.control.HwPan(options);
306        };
307
308        L.control.hwPan().addTo(leafletMap);
309
310        if(!mapElement.hasAttribute("data-config-zoom") || (mapElement.hasAttribute("data-config-zoom") && mapElement.getAttribute("data-config-zoom") === "true")) {
311            L.control.zoom(
312                {
313                    position: 'topright',
314                    zoomInText: '',
315                    zoomInTitle: languageAttributes.dataset.languageZoomIn,
316                    zoomOutText: '',
317                    zoomOutTitle: languageAttributes.dataset.languageZoomOut
318                }
319            ).addTo(leafletMap);
320        }
321
322        if (parseInt(mapElement.dataset.allowFullscreen) === 1) {
323            // Fullscreen
324            L.control.fullscreen(
325                {
326                position: 'topright',
327                title: languageAttributes.dataset.languageEnableFullscreen,
328                titleCancel: languageAttributes.dataset.languageDisableFullscreen,
329                forceSeparateButton: true,
330            }
331            ).addTo(leafletMap);
332
333            // Add or remove fullscreen class to document.body
334            const fullScreenApiAvailable = document.fullscreenEnabled || document.mozFullscreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled;
335
336            // device apple touch
337            const deviceAppleTouch = navigator.platform === 'iPad' || navigator.platform === 'iPhone' || navigator.platform === 'iPod';
338
339            // on fullscreen open
340            leafletMap.on('enterFullscreen', () => {
341                document.body.classList.add('hw_locationmap__fullscreen');
342
343                if(deviceAppleTouch) {
344                    document.body.classList.add('hwgeomap3_fullscreen_apple_touch');
345                }
346
347                if (!fullScreenApiAvailable) {
348                    document.body.classList.add('hw_locationmap__pseudo_fullscreen');
349                }
350
351            });
352
353            // on fullscreen close
354            leafletMap.on('exitFullscreen', () => {
355                document.body.classList.remove('hw_locationmap__fullscreen');
356
357                if(deviceAppleTouch) {
358                    document.body.classList.remove('hwgeomap3_fullscreen_apple_touch');
359                }
360
361                if (!fullScreenApiAvailable) {
362                    document.body.classList.remove('hw_locationmap__pseudo_fullscreen');
363                }
364            });
365        }
366
367        let markerGroup;
368        // create a new group container for markers
369        if (mapElement.dataset.disableClustering === '1') {
370            markerGroup = L.featureGroup();
371        } else {
372            markerGroup = L.markerClusterGroup({showCoverageOnHover: false});
373        }
374
375        markerGroup.addTo(leafletMap);
376
377
378        // get markers per AJAX request
379        getMarkers(mapElement, leafletMap, markerGroup);
380
381
382        // Events
383        leafletMap.on('popupopen', (e) => {
384            // Set Popup maxWidth to viewport size
385            if (leafletMap._container.getBoundingClientRect().width < e.popup.options.maxWidth && leafletMap._container.getBoundingClientRect().width < e.popup._containerWidth) {
386                e.popup.options.maxWidth = leafletMap._container.getBoundingClientRect().width - (40 * 2);
387                e.popup.update();
388            }
389
390            document.body.classList.add('hw_locationmap__popup');
391
392            // Center Popup
393            let px = leafletMap.project(e.target._popup._latlng);
394            px.y -= (e.target._popup._container.clientHeight / 2);
395            leafletMap.panTo(leafletMap.unproject(px), {animate: true});
396        });
397
398        leafletMap.on('popupclose', (e) => {
399           document.body.classList.remove('hw_locationmap__popup');
400        });
401
402        return leafletMap;
403
404
405        // AJAX-Call for markers
406        function getMarkers(mapElement, leafletMap, markerGroup) {
407
408            let ajaxCall = new XMLHttpRequest();
409            ajaxCall.onreadystatechange = function() {
410                if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
411
412                    // add markers form AJAX request to map
413                    addMarkersToMap(leafletMap, markerGroup, JSON.parse(this.responseText));
414                }
415            };
416            ajaxCall.open("GET", mapElement.getAttribute('data-webservice-url'));
417            ajaxCall.send();
418        }
419
420
421        // draw markers on map
422        function addMarkersToMap(leafletMap, markerGroup, ajaxData) {
423
424            // remove old markers
425            markerGroup.clearLayers();
426
427            // add new markers
428            if (ajaxData.length > 0) {
429                for (let i = 0; i < ajaxData.length; i++) {
430
431                    // create new marker
432                    L.marker([ajaxData[i].lat, ajaxData[i].lng], {
433                        icon: new ExtraMarker(ajaxData[i]['iconConfig'])
434                    })
435                        .bindPopup(ajaxData[i].popupContent, ajaxData[i].popupConfig)
436                        .addTo(markerGroup);
437                }
438
439                // invalidate map size
440                leafletMap.invalidateSize(true);
441
442                if (defaultZoom !== null && defaultZoom !== '') {
443                    // Set map view to bounds center and default zoom level
444                    leafletMap.setView(markerGroup.getBounds().getCenter()).setZoom(defaultZoom);
445                } else {
446                    // Set map view to fit bounds with padding
447                    leafletMap.fitBounds(markerGroup.getBounds().pad(0.4));
448                }
449            }
450        }
451    }
452}
453
454
455// Function to observe resize changes
456function addResizeObserver(element, map) {
457    const resizeObserver = new ResizeObserver(entries => {
458        for (let entry of entries) {
459            if (entry.target === element) {
460                map.invalidateSize();
461            }
462        }
463    });
464    resizeObserver.observe(element);
465}

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.