1(function initWorldMap() { 2 // Check if DOM is already loaded, otherwise wait for it 3 if (document.readyState === "loading") { 4 document.addEventListener("DOMContentLoaded", init); 5 } else { 6 init(); 7 } 8 9 function init() { 10 // Definieren der benötigten Variablen 11 const filterTriggers = document.querySelectorAll(".legend_item"); 12 const pins = document.querySelectorAll(".map-pin"); 13 const sites = document.querySelectorAll(".site"); 14 const defaultFilter = document.querySelector(".default"); 15 const svgBox = d3.select("#svg"); 16 const image = svgBox.selectChild("#image"); 17 const worldmap = document.querySelector(".worldmap"); 18 const zoomIcon = document.getElementById("zoomIcon"); 19 const zoomTooltip = zoomIcon.querySelector(".zoom-tooltip"); 20 let active = {}; 21 let tooltipTimeout; 22 let initialZoomApplied = false; 23 const ww = window.innerWidth; 24 25 const productionSitesLegend = document.querySelector( 26 '.legend_item[data-target="production-pins"]', 27 ); 28 const productionPins = document.querySelector(".production-pins"); 29 30 active.fill = defaultFilter; 31 active.pins = false; 32 active.pin = false; 33 active.infobox = false; 34 active.highlight = false; 35 36 // Initialisieren der Pin-Listener 37 initializePinListeners(); 38 39 // Initialisieren der Filter-Listener 40 initializeFilterListeners(); 41 42 applyActiveClassForResolution(); 43 44 // Select the legend items 45 const legendItemProduction = document.querySelector( 46 ".legend_item--production", 47 ); 48 const legendItemRnd = document.querySelector(".legend_item--rnd"); 49 50 // Add click event listeners with toggle functionality 51 legendItemProduction.addEventListener("click", () => { 52 removeActiveClass(".rnd-sites"); 53 addActiveClass(".production-sites"); 54 }); 55 56 legendItemRnd.addEventListener("click", () => { 57 removeActiveClass(".production-sites"); 58 addActiveClass(".rnd-sites"); 59 }); 60 61 function addActiveClass(selector) { 62 const element = document.querySelector(selector); 63 if (element) { 64 element.classList.add("active"); 65 } 66 } 67 68 function removeActiveClass(selector) { 69 const element = document.querySelector(selector); 70 if (element) { 71 element.classList.remove("active"); 72 } 73 } 74 75 function applyActiveClassForResolution() { 76 const windowWidth = window.innerWidth; 77 const productionSitesElement = 78 document.querySelector(".production-sites"); 79 80 if (productionSitesElement) { 81 if (windowWidth <= 768) { 82 productionSitesElement.classList.add("active"); 83 } else { 84 productionSitesElement.classList.remove("active"); 85 } 86 } else { 87 console.error(".production-sites element not found"); 88 } 89 } 90 91 // Event-Listener für Kartenpins 92 function initializePinListeners() { 93 if (pins.length) { 94 pins.forEach((pin) => { 95 pin.addEventListener("click", handlePinClick); 96 }); 97 } 98 } 99 100 // Verarbeiten von Pin-Klicks 101 function handlePinClick(e) { 102 const pin = e.currentTarget; 103 const infoBox = document.querySelector( 104 'div[data-pin="' + pin.dataset.target + '"]', 105 ); 106 togglePinState(pin, infoBox); 107 } 108 109 // Ãndern des Status eines Pins 110 function togglePinState(pin, infoBox) { 111 if (active.pin && active.pin !== pin) { 112 active.pin.classList.remove("map-pin--active"); 113 } 114 115 if (active.infobox && active.infobox !== infoBox) { 116 active.infobox.classList.remove("site--active"); 117 } 118 119 pin.classList.toggle("map-pin--active"); 120 infoBox.classList.toggle("site--active"); 121 122 active.infobox = active.infobox === infoBox ? false : infoBox; 123 active.pin = active.infobox ? pin : false; 124 } 125 126 // Event-Listener für Filter 127 function initializeFilterListeners() { 128 if (filterTriggers.length) { 129 filterTriggers.forEach((filterTrigger) => { 130 filterTrigger.addEventListener("click", handleFilterClick); 131 }); 132 } 133 } 134 135 // Verarbeiten der Filteraktionen 136 function handleFilterClick() { 137 const action = this.dataset.action; 138 139 if (this.classList.contains("legend_item--production")) { 140 addActiveClass(".production-sites"); 141 } else if (this.classList.contains("legend_item--rnd")) { 142 addActiveClass(".rnd-sites"); 143 } 144 145 switch (action) { 146 case "fill": 147 handleFillAction(this); 148 break; 149 case "pin": 150 handlePinAction(this); 151 break; 152 case "highlight": 153 handleHighlightAction(this); 154 break; 155 case "clear": 156 handleClearAction(); 157 break; 158 } 159 } 160 161 // Füllaktion verarbeiten 162 function handleFillAction(filterTrigger) { 163 if (active.highlight) { 164 resetHighlight(); 165 } 166 167 if (active.pins) { 168 resetPins(); 169 } 170 171 if (filterTrigger !== active.fill) { 172 updateActiveFill(filterTrigger); 173 } else if (filterTrigger !== defaultFilter) {
174 resetToDefaultFill(); 175 } 176 } 177 178 // Aktives Fill-Element aktualisieren 179 function updateActiveFill(newFill) { 180 active.fill.classList.remove("legend_item--active"); 181 const elementToUnfill = document.getElementById( 182 active.fill.dataset.target, 183 ); 184 newFill.classList.add("legend_item--active"); 185 const elementToFill = document.getElementById(newFill.dataset.target); 186 active.fill = newFill; 187 188 elementToFill.classList.add("fill--active"); 189 elementToUnfill.classList.remove("fill--active"); 190 } 191 192 // Zurücksetzen auf das Standard-Fill 193 function resetToDefaultFill() { 194 defaultFilter.classList.add("legend_item--active"); 195 const elementToFill = document.getElementById( 196 defaultFilter.dataset.target, 197 ); 198 const elementToUnfill = document.getElementById( 199 active.fill.dataset.target, 200 ); 201 active.fill = defaultFilter; 202 203 elementToFill.classList.add("fill--active"); 204 elementToUnfill.classList.remove("fill--active"); 205 } 206 207 // Pin-Aktion verarbeiten 208 function handlePinAction(filterTrigger) { 209 let pinsToDisplay, pinsToRemove; 210 211 if (active.fill !== defaultFilter) { 212 resetFill(); 213 } 214 215 if (active.highlight) { 216 resetHighlight(); 217 } 218 219 if (active.pin) { 220 active.pin.classList.remove("map-pin--active"); 221 if (active.infobox) { 222 active.infobox.classList.remove("site--active"); 223 } 224 } 225 226 toggleLegendItem(filterTrigger); 227 228 if (filterTrigger === active.pins) { 229 pinsToRemove = document.querySelector( 230 "." + filterTrigger.dataset.target, 231 ); 232 active.pins = false; 233 } else { 234 if (active.pins) { 235 active.pins.classList.remove("legend_item--active"); 236 pinsToRemove = document.querySelector( 237 "." + active.pins.dataset.target, 238 ); 239 } 240 pinsToDisplay = document.querySelector( 241 "." + filterTrigger.dataset.target, 242 ); 243 active.pins = filterTrigger; 244 } 245 246 if (pinsToDisplay) { 247 displayPins(pinsToDisplay); 248 } 249 250 if (pinsToRemove) { 251 removePins(pinsToRemove); 252 } 253 } 254 255 // Umschalten der Legenden-Elemente 256 function toggleLegendItem(filterTrigger) { 257 filterTrigger.classList.toggle("legend_item--active"); 258 } 259 260 // Highlight-Aktion verarbeiten 261 function handleHighlightAction(filterTrigger) { 262 if (active.fill !== defaultFilter) { 263 resetFill(); 264 } 265 266 if (active.pins) { 267 resetPins(); 268 } 269 270 const highlight = document.getElementById( 271 filterTrigger.dataset.continent, 272 ); 273 highlight.classList.toggle("continent--active"); 274 filterTrigger.classList.toggle("legend_item--active"); 275 276 if (active.highlight && filterTrigger !== active.highlight) { 277 active.highlight.classList.remove("legend_item--active"); 278 document 279 .getElementById(active.highlight.dataset.continent) 280 .classList.remove("continent--active"); 281 } 282 283 active.highlight = 284 filterTrigger !== active.highlight ? filterTrigger : false; 285 } 286 287 // Verarbeiten der Clear-Aktion 288 function handleClearAction() { 289 if (active.fill !== defaultFilter) { 290 resetFill(); 291 } 292 293 if (active.highlight) { 294 resetHighlight(); 295 } 296 297 if (active.pins) { 298 resetPins(); 299 } 300 } 301 302 // Zurücksetzen des Fills 303 function resetFill() { 304 active.fill.classList.remove("legend_item--active"); 305 defaultFilter.classList.add("legend_item--active"); 306 active.fill = defaultFilter; 307 } 308 309 // Zurücksetzen der Pins 310 function resetPins() { 311 if (active.pins) { 312 active.pins.classList.remove("legend_item--active"); 313 const pinsToRemove = document.querySelector( 314 "." + active.pins.dataset.target, 315 ); 316 if (pinsToRemove) { 317 removePins(pinsToRemove); 318 } 319 } 320 321 if (active.pin) { 322 active.pin.classList.remove("map-pin--active"); 323 324 if (active.infobox) { 325 active.infobox.classList.remove("site--active"); 326 } 327 } 328 329 active.pins = false; 330 active.pin = false; 331 active.infobox = false; 332 } 333 334 // Zurücksetzen des Highlights 335 function resetHighlight() { 336 if (active.highlight) { 337 active.highlight.classList.remove("legend_item--active"); 338 document 339 .getElementById(active.highlight.dataset.continent) 340 .classList.remove("continent--active"); 341 active.highlight = false; 342 } 343 } 344 345 // Pins anzeigen 346 function displayPins(element) { 347 element.classList.remove("pins--inactive"); 348 } 349 350 // Pins ausblenden 351 function removePins(element) { 352 element.classList.add("pins--inactive"); 353 } 354 355 // Aktualisieren der Pin-Positionen 356 function updatePinPosition() { 357 const worldmapRect = worldmap.getBoundingClientRect(); 358 pins.forEach((pin) => { 359 const pinData = pin.getAttribute("data-target"); 360 const correspondingBox = Array.from(sites).find( 361 (box) => box.getAttribute("data-pin") === pinData, 362 ); 363 364 if (correspondingBox) { 365 positionInfoBox(correspondingBox, pin, worldmapRect); 366 } 367 }); 368 } 369 370 // Positionieren der Infobox relativ zum Pin 371 function positionInfoBox(correspondingBox, pin, worldmapRect) { 372 const correspondingPin = pin.getBoundingClientRect(); 373 const pinLeft = correspondingPin.left - worldmapRect.left; 374 const pinTop = correspondingPin.top - worldmapRect.top; 375 376 const offsetX = 0; 377 const offsetY = correspondingPin.height + 10; 378 379 correspondingBox.style.left = pinLeft + offsetX + "px"; 380 correspondingBox.style.top = pinTop - offsetY + "px"; 381 } 382 383 updatePinPosition(); 384 385 // Zoom-Logik und Pinch-to-Zoom 386 if (ww > 768) { 387 function zoomed(event) { 388 const { transform } = event; 389 applyZoomTransform(transform); 390 updatePinPosition(); 391 resizePins(transform.k); 392 } 393 394 function applyZoomTransform(transform) { 395 image.attr("transform", transform.toString()); 396 } 397 398 function resizePins(scaleFactor) { 399 const windowWidth = window.innerWidth; 400 const pinSize = windowWidth <= 768 ? 25 : 16; 401 402 d3.selectAll(".map-pin").attr("r", pinSize / scaleFactor); 403 d3.selectAll(".map-pin").style("stroke-width", 2 / scaleFactor + "px"); 404 } 405 406 // Anpassung der Pin-GröÃe basierend auf Zoom-Faktor 407 408 const { width, height } = image.node().getBBox(); 409 const { width: svgBoxWidth, height: svgBoxHeight }
409 = svgBox 410 .node() 411 .getBoundingClientRect(); 412 const minScale = Math.max(svgBoxWidth / width, svgBoxHeight / height); 413 414 const zoom = d3 415 .zoom() 416 .scaleExtent([minScale, 8]) 417 .extent([ 418 [0, 0], 419 [svgBoxWidth, svgBoxHeight], 420 ]) 421 .translateExtent([ 422 [0, 0], 423 [width, height], 424 ]) 425 .on("zoom", zoomed); 426 427 svgBox.call(zoom); 428 429 const windowWidth = window.innerWidth; 430 const initialScale = windowWidth <= 768 ? 2 : 1.2; 431 svgBox 432 .transition() 433 .duration(750) 434 .call(zoom.scaleTo, initialScale) 435 .on("end", function () { 436 // Select Germany pin only after zoom transition completes 437 selectGermanyPinInitially(); 438 }); 439 440 // Initialisieren der Pin-GröÃe 441 initializePinSize(); 442 443 function initializePinSize() { 444 const initialZoom = minScale; 445 resizePins(initialZoom); 446 } 447 } else { 448 // For mobile (ww <= 768), select Germany pin immediately 449 selectGermanyPinInitially(); 450 } 451 452 showProductionSitesInitially(); 453 454 // Produktionsstandorte anzeigen 455 function showProductionSitesInitially() { 456 if (productionSitesLegend && productionPins) { 457 productionSitesLegend.classList.add("legend_item--active"); 458 productionPins.classList.remove("pins--inactive"); 459 active.pins = productionSitesLegend; 460 } 461 } 462 463 // Deutschland-Pin initial auswählen 464 function selectGermanyPinInitially() { 465 const germanyPin = document.querySelector( 466 '.map-pin[data-target="info-box-production-germany"]', 467 ); 468 469 if (germanyPin) { 470 germanyPin.dispatchEvent(new Event("click")); 471 } else { 472 console.error("Germany pin not found"); 473 } 474 } 475 476 // Tooltip-Verhalten für das Zoom-Icon 477 worldmap.addEventListener("mouseenter", handleMapHover); 478 worldmap.addEventListener("mouseleave", handleMapMouseLeave); 479 480 zoomIcon.addEventListener("mouseenter", function () { 481 showTooltip(); 482 }); 483 484 zoomIcon.addEventListener("mouseleave", function () { 485 hideTooltip(); 486 }); 487 488 function handleMapHover() { 489 zoomIcon.classList.add("hover-zoom"); 490 showTooltip(); 491 492 clearTimeout(tooltipTimeout); 493 tooltipTimeout = setTimeout(function () { 494 hideTooltip(); 495 }, 5000); 496 } 497 498 function handleMapMouseLeave() { 499 zoomIcon.classList.remove("hover-zoom"); 500 } 501 502 function showTooltip() { 503 zoomTooltip.style.display = "block"; 504 zoomTooltip.classList.remove("fade-out"); 505 } 506 507 function hideTooltip() { 508 zoomTooltip.classList.add("fade-out"); 509 } 510 } // end of init function 511})();
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.