<?php
/*
 * MapQuizzzes
 * @version 2026-05-06 from  2026-05-06
 * @author Heinrich Ahrens (https://ahrens.tk/MapQuizzzes, heinz.ahrens@web.de)
 * @license https://opensource.org/licenses/MIT MIT License
 * Copyright (c) 2026 Heinrich Ahrens (https://ahrens.tk/MapQuizzzes)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
 
 
$configFile = __DIR__ . "/MapQuizzzes.json";
// Optional rename this file to hide the global config
// Optional update "quizDataConfigJsonFile" in your global config to hide quiz-specific config

// All settings are overridden by your global and quiz-specific config files
// Do not change below here - settings only for fallback

$selectedLang = "en";
$quizName = "MapQuizzzes";
$instructionsUrl = "instructions.html";
$legalUrl = "legal.html";
$privacyUrl = "privacy.html";
$quizDataConfigJsonFile = "quizdata.json";
$availableTours = [];

$privacyNotice = "The required map data is retrieved directly from the servers of the respective providers.";
$quizNotice = "Welcome to MapQuizzzes.";


$defaultTranslations = [
        "privacyHeading" => "Privacy Notice",
        "tourNotAvailable" => "Currently no tours available.",
        "linkInstructions" => "Instructions",
        "linklegalNotice" => "Legal Notice",
        "linkPrivacy" => "Privacy"
];

$uiTranslations = $defaultTranslations;

if (file_exists($configFile)) {
    $jsonContent = file_get_contents($configFile);
    $config = json_decode($jsonContent, true);

    if (json_last_error() === JSON_ERROR_NONE && is_array($config)) {
        if (!empty($config["language"])) {
            $selectedLang = $config["language"];

            $uiTranslations =
                $defaultTranslations[$selectedLang] ??
                $defaultTranslations;
        }
        if (isset($config["quizName"])) {
            $quizName = $config["quizName"];
        }
        if (isset($config["instructionsUrl"])) { 
            $instructionsUrl = $config["instructionsUrl"]; 
        }
        if (isset($config["legalUrl"])) { 
            $legalUrl = $config["legalUrl"]; 
        }
        if (isset($config["privacyUrl"])) { 
            $privacyUrl = $config["privacyUrl"]; 
        }
    
        if (isset($config["quizDataConfigJsonFile"])) {
            $quizDataConfigJsonFile = $config["quizDataConfigJsonFile"];
        }
        if (isset($config["availableTours"])) {
            $availableTours = $config["availableTours"];
        }
        if (isset($config["privacyNotice"])) {
            $privacyNotice = $config["privacyNotice"];
        }
        if (isset($config["quizNotice"])) {
            $quizNotice = $config["quizNotice"];
        }


        if (isset($config["tr"]) && is_array($config["tr"])) {
            $jsonTr =
                $config["tr"][$selectedLang] ?? ($config["tr"]["en"] ?? []);
            $uiTranslations = array_merge($uiTranslations, $jsonTr);
        }
    }
}

function loadMappingTable($folder)
{
    global $quizDataConfigJsonFile;

    $fullPath =
        __DIR__ . "/" . basename($folder) . "/" . $quizDataConfigJsonFile;
    $modalHeaderTxtDir = __DIR__ . "/" . $folder . "/modalheader/";
    $modalFooterTxtDir = __DIR__ . "/" . $folder . "/modalfooter/";

    $mappingData = [];

    if (!file_exists($fullPath) || !is_readable($fullPath)) {
        error_log(
            "Error: Configuration file missing or unreadable: $fullPath"
        );
        return [];
    }

    $jsonContent = file_get_contents($fullPath);
    if ($jsonContent === false) {
        error_log("Error: Could not read content of $fullPath.");
        return [];
    }

    $data = json_decode($jsonContent, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        error_log("JSON syntax error in $fullPath: " . json_last_error_msg());
        return [];
    }

    if (!is_array($data)) {
        return [];
    }

    foreach ($data as $item) {
        $id = $item["id"] ?? "";
        if (empty($id)) {
            continue;
        }

        $number = (string) ($item["number"] ?? "");
        $titleValue = (string) ($item["title"] ?? $id);

        $modalHeaderTxt = "";
if ($id !== "") {
    $modalHeaderTxtFile = $modalHeaderTxtDir . $id . ".txt";
    if (file_exists($modalHeaderTxtFile) && is_readable($modalHeaderTxtFile)) {
        $content = file_get_contents($modalHeaderTxtFile);
        if ($content !== false) {
            $modalHeaderTxt = $content;
        }
    }
}

        $modalFooterTxt = "";
if ($id !== "") {
    $modalFooterTxtFile = $modalFooterTxtDir . $id . ".txt";
    if (file_exists($modalFooterTxtFile) && is_readable($modalFooterTxtFile)) {
        $content = file_get_contents($modalFooterTxtFile);
        if ($content !== false) {
            $modalFooterTxt = $content;
        }
    }
}
        

        $mappingData[$id] = [
            "number" => $number,
            "title" => $titleValue,
            "largetitle" => (string) ($item["largetitle"] ?? $titleValue),
            "question" => (string) ($item["question"] ?? ""),
            "hint" => (string) ($item["hint"] ?? ""),
            "answer" => (string) ($item["answer"] ?? ""),
            "latitude" => isset($item["latitude"])
                ? floatval($item["latitude"])
                : null,
            "longitude" => isset($item["longitude"])
                ? floatval($item["longitude"])
                : null,
            "info" => (string) ($item["info"] ?? ""),
            "link" => (string) ($item["link"] ?? ""),
             "modalHeaderTxt" => $modalHeaderTxt,
            "modalFooterTxt" => $modalFooterTxt,  
            "gpxShow" => isset($item["gpxShow"]) ? floatval($item["gpxShow"]) : null,
            "stepbystep" => isset($item["stepbystep"]) ? (bool)$item["stepbystep"] : true
        ];
    }

    return $mappingData;
}

if (isset($_GET["tour"])) {
    $requestedTour = $_GET["tour"];


    if (!in_array($requestedTour, $availableTours)) {
        header("Content-Type: application/json", true, 403);
        echo json_encode(["error" => "Tour not found"]);
        exit();
    }


    $quizDataConfig = loadMappingTable($requestedTour);

    header("Content-Type: application/json");
    echo json_encode([
        "tourData" => $quizDataConfig
    ]);
    exit();
}
?>

<!doctype html>
<html lang="<?php echo $selectedLang; ?>">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title><?php echo $quizName; ?></title>

<style>
   body,
   html {
      height: 100%;
      margin: 0;
      padding: 0;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
         Helvetica, Arial, sans-serif;
      color: #444;
   }
   #map {
      display: none;
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
   }
   #image-container {
      display: flex;
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: url("images/background.jpg") no-repeat center center;
      background-size: cover;
      z-index: 20000;
      align-items: center;
      justify-content: center;
   }
   #consentNotice {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: rgba(255, 255, 255, 0.98);
      padding: 20px;
      border-radius: 15px;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
      z-index: 10001;
      width: 90%;
      max-width: 450px;
      box-sizing: border-box;
      max-height: 85vh;
      overflow-y: auto;
      overflow-x: hidden;
      display: flex;
      flex-direction: column;
      align-items: center;
      margin: 0 !important;
   }
   .quiz-badge {
      display: inline-block;
      margin-bottom: 25px;
      padding: 11px 22px;
      background-color: #ffea00;
      color: darkblue;
      font-size: 1.8em;
      font-weight: bold;
      border: 2px solid darkblue;
      border-radius: 15px;
      box-shadow: 4px 4px 11px rgba(0, 0, 0, 0.2);
      transform: rotate(-2deg);
      text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
   }
   .leaflet-right .leaflet-control {
      margin-right: 15px !important;
      margin-top: 15px !important;
   }
   .leaflet-bar {
      border: none !important;
      box-shadow: none !important;
      margin-bottom: 0 !important;
   }

   #modalOverlay {
      display: none;
      position: fixed;
      z-index: 30000;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.8);
      align-items: center;
      justify-content: center;
   }
   #modalContent {
      background: #fff;
      padding: 20px;
      border-radius: 12px;
      display: flex;
      flex-direction: column;
      align-items: center;
      width: 85%;
      max-width: 500px;
      max-height: 80vh !important;
      overflow-y: auto;
      box-sizing: border-box;
   }
   .modal-title {
      font-size: 1.3em;
      font-weight: bold;
      color: #222;
      margin: 0;
      line-height: 1.2;
      text-align: center;
      width: 100%;
   }
   .modal-question {
      font-size: 1.2em;
      color: #444;
      margin: 5px 0;
      line-height: 1.4;
      text-align: center;
   }
   #showAnswerButton
    {
      width: 100%;
      font-size: 1.2em;
      padding: 8px;
      line-height: 1.3;
      border-radius: 6px;
      box-sizing: border-box;
   }
   #showAnswerButton {
      background-color: #007bff;
      color: white;
      cursor: pointer;
      transition: background-color 0.3s;
   }

   #consentNotice p,
   #consentNotice h3,
   #consentNotice span {
      width: 100%;
      text-align: center;
      margin-left: 0 !important;
      margin-right: 0 !important;
   }
   .modal-image-container {
      position: relative;
      display: block;
      width: fit-content;
      margin: 0 auto;
   }
   #modalImage {
      display: block;
      max-width: 100%;
      max-height: 45vh;
      border-radius: 8px;
      object-fit: contain;
      margin-bottom: 15px;
   }
   .image-number-badge {
      position: absolute;
      top: 12px;
      left: 12px;
      background: rgba(0, 0, 0, 0.65);
      color: white;
      padding: 8px 16px;
      border-radius: 8px;
      font-size: 1.6em;
      font-weight: bold;
      z-index: 10002;
      pointer-events: none;
      display: flex;
      align-items: center;
      justify-content: center;
      min-width: 20px;
   }
   @media (max-width: 600px) {
      #modalContent {
         padding: 15px;
         width: 90%;
         max-height: 80vh !important;
      }
      #modalImage {
         max-height: 35vh;
      }
   }
   .qr-modal-overlay {
      display: none;
      position: fixed;
      z-index: 50000;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.7);
      align-items: center;
      justify-content: center;
   }

   .qr-modal-content {
      background: white;
      padding: 20px;
      border-radius: 15px;
      text-align: center;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
      width: 90%;
      max-width: 450px;
      box-sizing: border-box;
   }
   .qr-code {
      margin-bottom: 20px;
      width: 100%;
      display: flex;
      justify-content: center;
   }

   #qrCode canvas {
      max-width: 100% !important;
      height: auto !important;
      margin: 0 auto;
      display: block;
   }

   .qr-url {
      font-size: 14px;
      color: blue;
      text-decoration: underline;
   }

   .tour-select-button {
      display: flex !important;
      justify-content: center !important;
      align-items: center !important;
      background: none !important;
      border: none !important;
      padding: 0 !important;
      margin: 10px auto !important;
      cursor: pointer !important;
      width: fit-content !important;
   }

   .tour-name-badge {
      display: block;
      position: relative;
      bottom: 0;
      left: 0;
      transform: rotate(-3deg);
      margin: 0 auto;
      background-color: #ffea00;
      color: darkblue;
      padding: 10px 25px;
      border: 2px solid darkblue;
      border-radius: 12px;
      font-size: 18px;
      font-weight: bold;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.3);
      white-space: nowrap;
      z-index: 10;
      width: fit-content;
   }

   .gallery-button,
   .locate-button,
   .home-button,
   .qr-button {
      width: 36px !important;
      height: 36px !important;
      display: flex !important;
      align-items: center !important;
      justify-content: center !important;
      background-color: #ffffff !important;
      border: 1px solid rgba(0, 0, 0, 0.3) !important;
      border-radius: 4px !important;
      box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4) !important;
      cursor: pointer !important;
      margin-bottom: 8px !important;
      padding: 0 !important;
      box-sizing: border-box !important;
      overflow: hidden !important;
   }

   .gallery-button:hover,
   .locate-button:hover,
   .home-button:hover,
   .qr-button:hover {
      background-color: #f4f4f4 !important;
   }

   

   .qr-start-btn {
      position: absolute;
      right: -35px;
      bottom: -30px;
      background: white;
      border: 3px solid darkblue;
      border-radius: 12px;
      padding: 8px;
      cursor: pointer;
      box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 10;
      transform: rotate(10deg);
      pointer-events: auto;
   }
   .custom-attribution:hover {
      text-decoration: none !important;
   }
   .custom-attribution {
      text-decoration: none;
   }
   .highlight-red-box {
      outline: 3px solid #ff0000 !important;
      outline-offset: 4px;
      position: relative;
      z-index: 999;
      box-shadow: none !important;
   }
   #infoRow {
      width: 100%;
      margin-top: 10px;
      visibility: hidden;
      display: flex;
      align-items: center;
      text-align: center;
   }
   #infoRow::-webkit-scrollbar {
      width: 6px;
   }
   #infoRow::-webkit-scrollbar-thumb {
      background-color: #ccc;
      border-radius: 10px;
   }
   .external-txt-content {
      text-align: left;
      font-size: 0.95em;
      background: transparent;
      padding: 0;
      margin-top: 10px;
      white-space: normal;
   }
   .pin-row {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 100%;
      margin: -5px 0 5px 0;
   }
   .pin-row img {
      width: 18px !important;
      height: auto;
      cursor: pointer;
      transition: transform 0.2s;
   }
   #hintSpan {
      font-size: 1.1rem;
      color: #333;
      display: block;
      margin-top: 10px;
      text-align: center;
   }

   .visited-icon img {
      outline: 4px solid #007bff !important;
      outline-offset: -4px;
      border-radius: 4px;
      box-shadow: 0 0 10px rgba(0, 123, 255, 0.8);
   }



   .overlay-container {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-image: url("images/background.jpg");
      background-repeat: no-repeat;
      background-position: center center;
      background-size: cover;
      z-index: 20000;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 10px;
      box-sizing: border-box;
      background-color: transparent !important;
      filter: none !important;
      backdrop-filter: none !important;
   }

   .consent-window {
      background: #ffffff;
      padding: 30px;
      border-radius: 15px;
      text-align: center;
      width: 100%;
      max-width: 450px;
      max-height: 90vh;
      overflow-y: auto;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
         Helvetica, Arial, sans-serif;
   }

   .scroll-container {
      background:
         linear-gradient(white 30%, rgba(255, 255, 255, 0)),
         linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%,
         radial-gradient(
            farthest-side at 50% 0,
            rgba(0, 0, 0, 0.4),
            rgba(0, 0, 0, 0)
         ),
         radial-gradient(
               farthest-side at 50% 100%,
               rgba(0, 0, 0, 0.4),
               rgba(0, 0, 0, 0)
            )
            0 100%;
      background-repeat: no-repeat;
      background-size:
         100% 40px,
         100% 40px,
         100% 20px,
         100% 20px;
      background-attachment: local, local, scroll, scroll;
      transition: background-size 0.2s;
   }

   .scroll-container::-webkit-scrollbar {
      width: 8px;
   }
   .scroll-container::-webkit-scrollbar-track {
      background: #f1f1f1;
      border-radius: 10px;
   }
   .scroll-container::-webkit-scrollbar-thumb {
      background-color: #bbb;
      border-radius: 10px;
      border: 2px solid #f1f1f1;
   }
   .consent-privacy-text {
    font-size: 0.9em;
    color: #444;
    padding: 10px 10px 0 10px; 
    margin-bottom: 5px;
    line-height: 1.4;
    text-align: center;
}
   .consent-quiz-notice-text {
    margin-top: 10px;
    font-size: 0.85em;
    color: #666;
    text-align: center;
    background: #ffffff;
    padding: 10px 0;
}
   
   
</style>


        <link rel="icon" href="data:," />
        <link rel="stylesheet" href="css/leaflet.css" />
    </head>

    <body>
      <div id="image-container" class="overlay-container">
   
<div id="consentNotice" class="consent-window">
    <header style="width: 100%; padding-bottom: 5px; margin-bottom: 20px; text-align: center;">
        <div style="position: relative; display: inline-block;">
            <h2 id="mainQuizTitle" class="quiz-badge" style="margin: 0"></h2>
            <div class="qr-start-btn" onclick="openQRModal()" style="cursor: pointer;">
                <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="black">
                    <path d="M3 3h8v8H3V3zm2 2v4h4V5H5zm8-2h8v8h-8V3zm2 2v4h4V5h-4zM3 13h8v8H3v-8zm2 2v4h4v-4H5zm13-2h3v2h-3v-2zm-3 0h2v2h-2v-2zm3 3h3v2h-3v-2zm-3 0h2v2h-2v-2zm3 3h3v2h-3v-2zm-3 0h2v2h-2v-2zM13 13h2v2h-2v-2zm0 3h2v2h-2v-2z"/>
                </svg>
            </div>
        </div>
    </header>

<div class="consent-privacy-text">
    <?php echo $privacyNotice; ?>
</div>

    <div style="width: 100%; text-align: center;">
        <div style="font-weight: bold; margin-bottom: 2px;">
            <?php echo htmlspecialchars($uiTranslations['acceptAndStart']); ?>
        </div>

        <div class="scroll-container" style="width: 100%; min-height: 140px; max-height: 40vh; overflow-y: auto; padding-right: 5px;">
            <div style="display: flex; flex-direction: column; align-items: center; width: 100%; padding: 15px 0;">
                <?php if (empty($availableTours)): ?>
                    <p style="font-weight: bold; margin: 20px 0;">
                        <?php echo htmlspecialchars($uiTranslations['tourNotAvailable']); ?>
                    </p>
                <?php else: 
                    foreach ($availableTours as $tour): ?>
                        <button class="tour-select-button" data-tour="<?php echo htmlspecialchars($tour); ?>" style="width: 100%; margin: 12px 0; background: none; border: none; padding: 0; cursor: pointer;">
                            <span class="tour-name-badge">
                                <?php echo htmlspecialchars($tour); ?>
                            </span>
                        </button>
                    <?php endforeach; 
                endif; ?>
            </div>
        </div>
    </div>

<?php if (!empty($quizNotice)): ?>
<div class="consent-quiz-notice-text">
    <?php echo $quizNotice; ?>
</div>
<?php endif; ?>

    <nav style="display: flex; justify-content: center; gap: 20px; font-size: 0.8em; margin-top: 20px;">
        <a href="<?php echo htmlspecialchars($instructionsUrl); ?>" style="color: #007bff; text-decoration: none;">
            <?php echo htmlspecialchars($uiTranslations['linkInstructions']); ?>
        </a>
        <a href="<?php echo htmlspecialchars($legalUrl); ?>" style="color: #007bff; text-decoration: none;">
            <?php echo htmlspecialchars($uiTranslations['linklegalNotice']); ?>
        </a>
        <a href="<?php echo htmlspecialchars($privacyUrl); ?>" style="color: #007bff; text-decoration: none;">
            <?php echo htmlspecialchars($uiTranslations['linkPrivacy']); ?>
        </a>
    </nav>
</div>
   
</div>

        <div id="map"></div>
        <div id="modalOverlay">
            <div id="modalContent">
                <div class="modal-image-container">
                    <img id="modalImage" src="" />
                    <div class="image-number-badge"></div>
                </div>

                <div id="contentWrapper">
                    <div
                        id="m-title-row"
                        class="modal-title"
                        style="font-weight: bold; font-size: 1.2em; text-align: center; margin-bottom: 10px"
                    ></div>

                    <div id="m-pin-row" class="pin-row" style="margin-bottom: 10px; display: none">
                        <a id="m-google-link" href="#" target="_blank">
                            <img
                                src="images/pin.svg"
                                alt="Pin"
                                style="width: 18px; height: auto; display: block; margin: 0 auto"
                            />
                        </a>
                    </div>
                    
                    <div id="m-top-row" style="margin-top: 10px; width: 100%; text-align: left;">
                    <div id="m-top-content" class="external-txt-content"></div>
                    </div>

                    <div id="m-quiz-area" style="display: none; text-align: center; width: 100%">
                        <div id="m-question" class="modal-question" style="margin-bottom: 10px"></div>

                        <div id="hintContainer" style="min-height: 1.2em; margin-bottom: 10px">
                            <div id="hintSpan" style="visibility: hidden; color: #666; font-style: italic"></div>
                        </div>

                        <button
                            id="showAnswerButton"
                            style="width: auto; min-width: 200px; padding: 10px 20px; margin-bottom: 10px"
                        ></button>
                    </div>

                    <div id="infoRow" style="visibility: hidden; width: 100%">
                        <div
                            id="m-info-content"
                            style="display: flex; flex-direction: column; gap: 5px; width: 100%; text-align: center"
                        ></div>
                    </div>
                </div>
            </div>
        </div>

        <div id="qrModalOverlay" class="qr-modal-overlay">
            <div id="qrModalContent" class="qr-modal-content">
                <div id="qrCode" class="qr-code"></div>
                <a id="qrUrl" href="#" target="_blank" class="qr-url"></a>
            </div>
        </div>

        <script src="js/leaflet.js"></script>
        <script src="js/piexifjs.js"></script>
        <script src="js/leaflet.polylineDecorator.js"></script>
        <script src="js/qrcode.min.js"></script>

        <script>

            function tr(key, param = null) {
                let text = translations[key] || key;
                if (param !== null) {
                    text = text.replace('%s', param);
                }
                return text;
            }


            function addThumbnailToMap(lat, lon, displayUrl, quizPointId) {
                const thumbnailSize = 100;
                const mapping = mappingData[quizPointId];
                const number = mapping ? mapping.number : '?';
                const title = mapping ? mapping.title : '?';

                const thumbnailIcon = L.divIcon({
                    className: 'thumbnail-marker',
                    html: `
                        <div style="position: relative; width: ${thumbnailSize}px; height: ${thumbnailSize}px; background-color: #eeeeee; border-radius: 5px; overflow: hidden; border: 1px solid #666; box-shadow: 0 0 3px rgba(0,0,0,0.5);">
                            <img src="${displayUrl}"
                                 alt=""
                                 style="display: block; width: 100%; height: 100%; object-fit: cover; cursor: pointer;" />

                            <div style="position: absolute; top: 5px; left: 5px; background: rgba(0, 0, 0, 0.8); color: white; font-size: 13px; font-weight: bold; padding: 2px 5px; border-radius: 5px;">
                                ${number}
                            </div>
                            <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; font-size: 12px; font-weight: bold; padding: 2px 5px; border-radius: 5px; text-align: center; width: 80%; line-height: 1.2;">
                                ${title}
                            </div>
                        </div>
                    `,
                    iconSize: [thumbnailSize, thumbnailSize],
                    iconAnchor: [thumbnailSize / 2, thumbnailSize / 2]
                });

                const marker = L.marker([lat, lon], { icon: thumbnailIcon }).addTo(markerLayerGroup);
                marker.on('click', () => openModal(quizPointId));
            }


 function openModal(quizPointId) {
    const mapping = mappingData[quizPointId];
    


    const imgUrl = tourFolder + '/img/' + quizPointId; 
    const modalImg = document.getElementById('modalImage');
    const modalOverlay = document.getElementById('modalOverlay');

    currentOpenQuizPointId = quizPointId;
    modalImg.style.display = 'none';

    modalImg.onerror = () => {
        modalImg.src = "data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='640' height='480' viewBox='0 0 640 480'%3E%3Crect width='640' height='480' fill='%23eeeeee'/%3E%3Crect x='1' y='1' width='638' height='478' fill='none' stroke='%23cccccc' stroke-width='2'/%3E%3Cg transform='translate(130, 50) scale(16)' opacity='0.2'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2' stroke='%23000' fill='none'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.5' fill='%23000'/%3E%3Cpolyline points='21 15 16 10 5 21' stroke='%23000' fill='none'/%3E%3C/g%3E%3C/svg%3E";
        modalImg.style.objectFit = 'contain';
        showModal();
    };

    modalImg.onload = () => {
        modalImg.style.objectFit = 'contain';
        showModal();
    };
    

    function showModal() {
        updateModalContent(quizPointId);
        updateModalNumber(quizPointId); 
        modalOverlay.style.display = 'flex';
        modalImg.style.display = 'block';
    }

    modalImg.src = imgUrl;
}


function unlockNextMarker(currentId) {
    const currentIndex = window.sortedQuizPoints.indexOf(currentId);
    const nextIndex = currentIndex + 1;
    if (nextIndex < window.sortedQuizPoints.length) {
        const nextId = window.sortedQuizPoints[nextIndex];
        if (!unlockedMarkerIds.includes(nextId)) {
            unlockedMarkerIds.push(nextId);
        }
        createMapMarkerFromImage(nextId, nextIndex, () => { }, () => {});
        updatePolyline(); 
        
        if (mappingData[nextId] && mappingData[nextId].gpxShow !== null) {
            updateGpxRoute(mappingData[nextId].gpxShow);
        }
    }
}

function updatePolyline() {

    lineLayerGroup.clearLayers();

if (hasGpx) {
        return; 
    }

    if (allGpxPoints && allGpxPoints.length > 0) {
        return;
    }


    const latlngs = [];

    unlockedMarkerIds.forEach(id => {
        const mapping = mappingData[id];
        let lat = mapping?.latitude;
        let lon = mapping?.longitude;

        if (lat === null || lon === null) {
            const extra = imageDataMap.get(id);
            if (extra?.gps) {
                lat = extra.gps.lat;
                lon = extra.gps.lon;
            }
        }

        if (lat !== null && lon !== null) {
            latlngs.push([lat, lon]);
        }
    });


    if (latlngs.length > 1) {

        
        const polyline = L.polyline(latlngs, { 
            color: '#0000FF', 
            weight: 4, 
            opacity: 0.6, 
            lineJoin: 'round' 
        }).addTo(lineLayerGroup);


        L.polylineDecorator(polyline, {
            patterns: [
                { 
                    offset: '10%', 
                    repeat: '100px', 
                    symbol: L.Symbol.arrowHead({
                        pixelSize: 12, 
                        polygon: false, 
                        pathOptions: { 
                            stroke: true, 
                            color: '#00008B', 
                            weight: 3, 
                            opacity: 0.9 
                        }
                    }) 
                }
            ]
        }).addTo(lineLayerGroup);
    }
}




            function markAsVisited(quizPointId) {
                map.eachLayer(function(layer) {
                    if (layer instanceof L.Marker && layer.options.icon && layer.options.icon.options.html) {
                        if (layer.options.icon.options.html.includes(quizPointId)) {
                            if (layer._icon) {
                                layer._icon.classList.add('visited-icon');
                            }
                            let currentClassName = layer.options.icon.options.className || "";
                            if (!currentClassName.includes('visited-icon')) {
                                layer.options.icon.options.className = currentClassName + ' visited-icon';
                            }
                        }
                    }
                });
            }


            function updateModalNumber(quizPointId) {
                const mapping = mappingData[quizPointId];
                const badge = document.querySelector('.image-number-badge');

                if (!mapping || !mapping.number) {
                    if (badge) badge.style.display = 'none';
                    return;
                }

                if (badge) {
                    badge.textContent = mapping.number;
                    badge.style.display = 'flex';
                }
            }



           function updateModalContent(quizPointId) {

    if (currentModalTimer) {
        clearInterval(currentModalTimer);
        currentModalTimer = null;
    }

    const mapping = mappingData[quizPointId] || {};
    const imageName = quizPointId;
    
    const topContent = document.getElementById('m-top-content');
    const topRow = document.getElementById('m-top-row');

    topContent.innerHTML = mapping.modalHeaderTxt || "";
    topRow.style.display = 'block';


    let lat = mapping.latitude ?? (imageDataMap.get(imageName)?.gps?.lat || null);
    let lon = mapping.longitude ?? (imageDataMap.get(imageName)?.gps?.lon || null);
    const hasCoords = lat !== null && lon !== null;
    const hasQuestion = (mapping.question || "").trim() !== "";
    const hasHint = (mapping.hint || "").trim() !== "";
    const hasAnswer = (mapping.answer || "").trim() !== "";

    const displayTitle = (mapping.largetitle && mapping.largetitle.trim() !== "")
                         ? mapping.largetitle
                         : (mapping.title || imageName);

    document.getElementById('m-title-row').textContent = displayTitle;


    const pinRow = document.getElementById('m-pin-row');
    if (hasCoords) {
        pinRow.style.display = 'block';
        const isApple = /iPad|iPhone|iPod/.test(navigator.userAgent);
        const latFixed = lat.toFixed(4);
        const lonFixed = lon.toFixed(4);

        let link = isApple 
            ? `maps://maps.apple.com/?q=${latFixed},${lonFixed}`
            : `https://www.google.com/maps/search/?api=1&query=${latFixed},${lonFixed}`;

        document.getElementById('m-google-link').href = link;
        document.getElementById('m-google-link').title = tr('openMaps');
    } else {
        pinRow.style.display = 'none';
    }

    const quizArea = document.getElementById('m-quiz-area');
    const showBtn = document.getElementById('showAnswerButton');
    const hintSpan = document.getElementById('hintSpan');

    if (hasQuestion) {
        quizArea.style.display = 'block';
        document.getElementById('m-question').textContent = mapping.question;
        hintSpan.textContent = hasHint ? `(${tr('hintPrefix')}: ${mapping.hint})` : "";
        hintSpan.style.visibility = 'hidden';

        let step = hasHint ? 0 : (hasAnswer ? 1 : 2);
        
        const render = () => {
            showBtn.disabled = false;
            showBtn.style.cursor = "pointer";
            if (step === 0) {
                showBtn.textContent = tr('showHint');
                showBtn.style.backgroundColor = "";
            } else if (step === 1) {
                showBtn.textContent = tr('showAnswer');
                showBtn.style.backgroundColor = "#fd7e14";
            } else if (step === 2) {
                showBtn.textContent = hasAnswer ? tr('confirmShowAnswer') : tr('noAnswer');
                showBtn.style.backgroundColor = hasAnswer ? "#dc3545" : "#28a745";
                if(!hasAnswer) showBtn.style.cursor = "default";
            } else if (step === 3 && hasAnswer) {
                showBtn.textContent = mapping.answer;
                showBtn.style.backgroundColor = "#28a745";
                showBtn.style.cursor = "default";
            }
        };
        render();

        showBtn.onclick = (e) => {
            e.stopPropagation();
            if (step === 0) {
                hintSpan.style.visibility = 'visible';
                step = hasAnswer ? 1 : 2;
                render();
            } else if (step === 1) {
                step = 2;
                render();
            } else if (step === 2 && hasAnswer) {
                let count = 3;
                showBtn.disabled = true;
                showBtn.style.cursor = "wait";
                showBtn.style.backgroundColor = "#007bff";
                showBtn.textContent = tr('countdownPlural', count);

                currentModalTimer = setInterval(() => {
                    count--;
                    if (count > 0) {
                        const key = (count === 1) ? 'countdownSingular' : 'countdownPlural';
                        showBtn.textContent = tr(key, count);
                    } else {
                        clearInterval(currentModalTimer);
                        currentModalTimer = null;
                        step = 3;
                        render();
                        document.getElementById('infoRow').style.visibility = 'visible';
                        markAsVisited(imageName);
                    }
                }, 1000);
            }
        };
    } else {
        quizArea.style.display = 'none';
    }

    const infoContent = document.getElementById('m-info-content');
    const infoRow = document.getElementById('infoRow');
    infoContent.innerHTML = "";

    if (mapping.link) {
        const linkText = mapping.info || tr('openLink');
        infoContent.innerHTML += `<div style="padding: 0 10px;"><a href="${mapping.link}" target="_blank" style="color: #007bff; font-weight: bold;">${linkText}</a></div>`;
    }
    if (mapping.modalFooterTxt) {
        infoContent.innerHTML += `<div class="external-txt-content" style="padding: 0 10px; text-align: left;">${mapping.modalFooterTxt}</div>`;
    }

    infoRow.style.visibility = (!hasQuestion && (mapping.link || mapping.modalFooterTxt)) ? 'visible' : 'hidden';
}

async function processAllImages(images) {
    const validImages = images.filter(image => image);
    validImages.sort((a, b) => {
        const numA = parseInt(mappingData[a]?.number || 0);
        const numB = parseInt(mappingData[b]?.number || 0);
        return numA - numB;
    });

    window.sortedQuizPoints = validImages;

    const loadPromises = validImages.map((id) => {
        return new Promise((resolve) => {
            const thumbUrl = tourFolder + '/min/' + id;
            const img = new Image();
            img.crossOrigin = "Anonymous";

            img.onload = function() {
                const canvas = document.createElement('canvas');
                canvas.width = this.width;
                canvas.height = this.height;
                const ctx = canvas.getContext('2d');
                ctx.drawImage(this, 0, 0);

                const dataUrl = canvas.toDataURL('image/jpeg', 0.8);
                try {
                    const exifObj = piexif.load(dataUrl);
                    const gps = getGpsFromExif(exifObj);
                    if (gps) {
                        if (!mappingData[id].latitude) mappingData[id].latitude = gps.lat;
                        if (!mappingData[id].longitude) mappingData[id].longitude = gps.lon;
                        imageDataMap.set(id, { gps: gps });
                    }
                } catch (e) {
                    console.warn("EXIF read error:", id);
                }

                canvas.toBlob((blob) => {
                    const objectUrl = URL.createObjectURL(blob);
                    blobCache.set(id, objectUrl);
                    resolve();
                }, 'image/jpeg', 0.8);
            };
            img.onerror = () => resolve();
            img.src = thumbUrl;
        });
    });

    await Promise.all(loadPromises);
    
    
    const firstId = validImages[0];
    const isStepByStep = mappingData[firstId]?.stepbystep !== false;

    if (!isStepByStep) {
        validImages.forEach(id => {
            if (!unlockedMarkerIds.includes(id)) {
                unlockedMarkerIds.push(id);
            }
        });
    } else {
        if (firstId && !unlockedMarkerIds.includes(firstId)) {
            unlockedMarkerIds.push(firstId);
        }
    }

    updatePolyline(); 

    if (!isStepByStep) {
        const markersToLoad = validImages.map((id, index) => {
            return new Promise((resolve) => {
                createMapMarkerFromImage(id, index, () => resolve(), () => resolve());
            });
        });
        
        await Promise.all(markersToLoad);
        fitMapToMarkers(map);
    } else if (validImages.length > 0) {
        createMapMarkerFromImage(firstId, 0, () => {
            const firstPoint = mappingData[firstId];
            if (firstPoint && firstPoint.latitude !== null && firstPoint.longitude !== null) {
                map.setView([firstPoint.latitude, firstPoint.longitude], 17);
            } else {
                fitMapToMarkers(map);
            }
        }, () => {});
    }    
    
    
}



            function getGpsFromExif(exifData) {
                if (!exifData || !exifData["GPS"]) return null;

                const gpsData = exifData["GPS"];
                const lat = convertGpsToDecimal(gpsData[piexif.GPSIFD.GPSLatitude], gpsData[piexif.GPSIFD.GPSLatitudeRef]);
                const lon = convertGpsToDecimal(gpsData[piexif.GPSIFD.GPSLongitude], gpsData[piexif.GPSIFD.GPSLongitudeRef]);

                return (lat && lon) ? { lat, lon } : null;
            }

            function normalizeCoords(lat, lon) {
                const nLat = parseFloat(lat);
                const nLon = parseFloat(lon);

                if (!isNaN(nLat) && !isNaN(nLon) &&
                    nLat >= -90 && nLat <= 90 &&
                    nLon >= -180 && nLon <= 180) {
                    return { lat: nLat, lon: nLon };
                }
                return null;
            }



           async function createMapMarkerFromImage(quizPointId, index, resolve, reject) {
    const imageUrl = tourFolder + '/min/' + quizPointId;
    const mapping = mappingData[quizPointId] || {};


    let lat = mapping.latitude;
    let lon = mapping.longitude;


    if (lat === null || lon === null) {
        try {
            const response = await fetch(imageUrl);
            const blob = await response.blob();
            const reader = new FileReader();
            
            reader.onload = function(e) {
                const exifObj = piexif.load(e.target.result);
                const gps = getGpsFromExif(exifObj);
                
                if (gps) {
                    lat = gps.lat;
                    lon = gps.lon;
                    imageDataMap.set(quizPointId, { gps: gps });
                }
                
                if (lat !== null && lon !== null) {
                    addThumbnailToMap(lat, lon, imageUrl, quizPointId);
                    resolve();
                } else {
                    console.warn("Keine Koordinaten für " + quizPointId + " gefunden (weder JSON noch EXIF)");
                    reject();
                }
            };
            reader.readAsDataURL(blob);
            return; 
        } catch (err) {
            console.error("Fehler beim Laden des Bild-GPS: ", err);
        }
    }


    if (lat !== null && lon !== null) {
        addThumbnailToMap(lat, lon, imageUrl, quizPointId);
        resolve();
    } else {
        reject();
    }
}


            function fitMapToMarkers(map) {
                if (markerLayerGroup.getLayers().length > 0) {

                    const bounds = markerLayerGroup.getBounds();

                    const edgePadding = window.innerWidth < 600 ? [50, 50] : [100, 100];

                    map.fitBounds(bounds, {
                        padding: edgePadding,
                        maxZoom: 18
                    });
                }
            }


            function processImageForMap(exifData, mapping) {

                if (mapping && mapping.latitude !== null && mapping.longitude !== null) {
                    return {
                        lat: mapping.latitude,
                        lon: mapping.longitude
                    };
                }

                if (!exifData || !exifData["GPS"]) {
                    return null;
                }

                const gpsData = exifData["GPS"];
                const lat = convertGpsToDecimal(gpsData[piexif.GPSIFD.GPSLatitude], gpsData[piexif.GPSIFD.GPSLatitudeRef]);
                const lon = convertGpsToDecimal(gpsData[piexif.GPSIFD.GPSLongitude], gpsData[piexif.GPSIFD.GPSLongitudeRef]);

                return lat && lon ? {
                    lat,
                    lon
                } : null;
            }


            function convertGpsToDecimal(gpsData, ref) {
                if (!gpsData || gpsData.length < 3) {

                    return null;
                }

                try {
                    const degrees = gpsData[0][0] / gpsData[0][1];
                    const minutes = gpsData[1][0] / gpsData[1][1];
                    const seconds = gpsData[2][0] / gpsData[2][1];

                    const decimal = degrees + minutes / 60 + seconds / 3600;
                    return (ref === "S" || ref === "W") ? -decimal : decimal;
                } catch (error) {
                    return null;
                }
            }


            function addHomeButton(map) {
                const HomeButton = L.Control.extend({
                    options: { position: 'topright' },
                    onAdd: function() {
                        const button = L.DomUtil.create('div', 'home-button leaflet-bar');
                        button.innerHTML = `
                            <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
                                <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
                            </svg>`;

                        button.onclick = () => {
                            const baseUrl = window.location.origin + window.location.pathname;

                            if (tourFolder) {
                                location.href = baseUrl;
                            } else {
                                location.href = './';
                            }
                        };

                        return button;
                    }
                });
                map.addControl(new HomeButton());
            }




            function addQrButton(map) {
                const qrControl = L.control({
                    position: "topright"
                });

                qrControl.onAdd = function() {
                    const div = L.DomUtil.create("div", "custom-control-container");
                    div.innerHTML = `
                        <div class="qr-button">
                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
                                <path d="M3 3h8v8H3V3zm2 2v4h4V5H5zm8-2h8v8h-8V3zm2 2v4h4V5h-4zM3 13h8v8H3v-8zm2 2v4h4v-4H5zm13-2h3v2h-3v-2zm-3 0h2v2h-2v-2zm3 3h3v2h-3v-2zm-3 0h2v2h-2v-2zm3 3h3v2h-3v-2zm-3 0h2v2h-2v-2zM13 13h2v2h-2v-2zm0 3h2v2h-2v-2z"/>
                            </svg>
                        </div>`;

                    div.onclick = function(e) {
                        L.DomEvent.stopPropagation(e);


                        if (typeof openQRModal === "function") {
                            openQRModal();
                        } 
                    };

                    return div;
                };
                qrControl.addTo(map);
            }



function closeModal() {
    modalOverlay.style.display = 'none';

    if (currentModalTimer) {
        clearInterval(currentModalTimer);
        currentModalTimer = null;
    }

    const modalImg = document.getElementById('modalImage');
    if (modalImg) {
        modalImg.src = '';
        modalImg.onerror = null;
        modalImg.onload = null;
    }

    const infoRow = document.getElementById('infoRow');
    if (infoRow) infoRow.style.visibility = 'hidden';

    const hint = document.getElementById('hintSpan');
    if (hint) hint.style.visibility = 'hidden';

    const infoContent = document.getElementById('m-info-content');
    if (infoContent) infoContent.innerHTML = '';

    updatePolyline(); 
    if (currentOpenQuizPointId && mappingData[currentOpenQuizPointId]) {
        const config = mappingData[currentOpenQuizPointId];
        console.log("Modal geschlossen. Zeichne Route für:", currentOpenQuizPointId, "Wert:", config.gpxShow);
        
        updateGpxRoute(config.gpxShow);

        unlockNextMarker(currentOpenQuizPointId);

        currentOpenQuizPointId = null; 
    }
}



            function addLocateButton(map) {
                const locateControl = L.control({
                    position: "topright"
                });

                locateControl.onAdd = function() {
                    const div = L.DomUtil.create("div", "custom-control-container");

                    div.innerHTML = `
                        <div class="locate-button">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                <path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
                            </svg>
                        </div>`;

                    div.addEventListener("click", function() {
                        if (navigator.geolocation) {
                            const loadingMessage = L.control({
                                position: "topleft"
                            });
                            loadingMessage.onAdd = function() {
                                const loadingDiv = L.DomUtil.create("div", "loading-message");
                                loadingDiv.innerHTML = `
                                    <div style="background: white; padding: 10px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);">
                                        <span><?php echo htmlspecialchars($uiTranslations['loadingLocation'] ?? ''); ?></span>
                                    </div>`;
                                return loadingDiv;
                            };
                            loadingMessage.addTo(map);

                            navigator.geolocation.getCurrentPosition(
                                function(position) {
                                    const {
                                        latitude: userLat,
                                        longitude: userLng
                                    } = position.coords;
                                    map.setView([userLat, userLng], 18);

                                    if (typeof currentLocationMarker !== 'undefined' && currentLocationMarker) {
                                        map.removeLayer(currentLocationMarker);
                                    }

                                    currentLocationMarker = L.circleMarker([userLat, userLng], {
                                        radius: 8,
                                        color: 'blue',
                                        weight: 2,
                                        opacity: 1,
                                        fillColor: '#0078ff',
                                        fillOpacity: 0.8
                                    }).addTo(map);

                                    map.removeControl(loadingMessage);
                                },
                                function(error) {
                                    alert(translations.locationError + " " + error.message);
                                    map.removeControl(loadingMessage);
                                }, {
                                    enableHighAccuracy: true,
                                    timeout: 5000,
                                    maximumAge: 0,
                                }
                            );
                        } else {
                            alert(translations.locationError + " " + error.message);
                        }
                    });

                    return div;
                };

                locateControl.addTo(map);
            }


async function handleTourActivation(selectedTour) {
    tourFolder = selectedTour;
    hasGpx = false;

    document.getElementById("consentNotice").style.display = "none";
    const imageContainer = document.getElementById("image-container");
    if (imageContainer) imageContainer.style.display = "none";
    document.getElementById("map").style.display = "block";

    setTimeout(() => map.invalidateSize(), 100);

    try {
        const response = await fetch(`?tour=${encodeURIComponent(selectedTour)}`);
        const data = await response.json();

        if (data.error) {
            console.error("Server-Fehler:", data.error);
            return;
        }

        mappingData = data.tourData;
        images = Object.keys(mappingData);
        unlockedMarkerIds = []; 

        if (window.gpxLayerGroup) {
            gpxLayerGroup.clearLayers();
        }
        allGpxPoints = []; 

        const manualGpxUrl = `${selectedTour}/quizdata.gpx`;
        try {
            const gpxResponse = await fetch(manualGpxUrl, { cache: 'no-store' });
            
            
            
            if (gpxResponse.ok) {
                hasGpx = true; 
                const gpxText = await gpxResponse.text();
                const parser = new DOMParser();
                const xml = parser.parseFromString(gpxText, "text/xml");
                const trackPoints = xml.getElementsByTagName("trkpt");
                
                let tempPoints = [];
                for (let i = 0; i < trackPoints.length; i++) {
                    const lat = parseFloat(trackPoints[i].getAttribute("lat"));
                    const lon = parseFloat(trackPoints[i].getAttribute("lon"));
                    if (!isNaN(lat) && !isNaN(lon)) {
                        tempPoints.push([lat, lon]);
                    }
                }
                allGpxPoints = tempPoints;

                if (allGpxPoints.length > 0) {
                    updateGpxRoute(0);
                }
            } else {
                hasGpx = false; 
            }
        } catch (gpxErr) {
            hasGpx = false;
        }

        const galleryLink = document.getElementById('dynamicGalleryLink');
        if (galleryLink) {
            galleryLink.href = `gallery.php?path=${tourFolder}/img`;
        }

        map.eachLayer((layer) => {
            if (layer instanceof L.Marker || layer instanceof L.Polyline || (layer.options && layer.options.patterns)) {
                if (window.gpxLayerGroup && !gpxLayerGroup.hasLayer(layer)) {
                    map.removeLayer(layer);
                }
            }
        });

        if (images.length > 0) {
            await processAllImages(images);
            
            const firstId = window.sortedQuizPoints[0];
            const isStepByStepDisabled = mappingData[firstId] && mappingData[firstId].stepbystep === false;

            if (isStepByStepDisabled) {
                unlockedMarkerIds = [...window.sortedQuizPoints];
                unlockedMarkerIds.forEach((id, index) => {
                    createMapMarkerFromImage(id, index, () => {}, () => {});
                });
                
                updatePolyline();
                if (hasGpx) {
                    updateGpxRoute(unlockedMarkerIds.length - 1);
                }
            } else {
                unlockedMarkerIds = [firstId];
                createMapMarkerFromImage(firstId, 0, () => {}, () => {});
                if (hasGpx) {
                    updateGpxRoute(0);
                }
            }
            
            fitMapToMarkers(map);
           
        }

    } catch (e) {
        console.error("Kritischer Fehler:", e);
    }
}


function updateGpxDisplay(percentage) {
    if (!allGpxPoints || allGpxPoints.length === 0) return;
    
 
    gpxLayerGroup.clearLayers();

    const pointsCount = Math.floor(allGpxPoints.length * percentage);
    const visibleCoords = allGpxPoints.slice(0, pointsCount);

    if (visibleCoords.length > 1) {
        L.polyline(visibleCoords, {
            color: '#0000FF',
            weight: 5,
            opacity: 0.7
        }).addTo(gpxLayerGroup);
    }
}

  
            function openQRModal() {
                const qrCodeContainer = document.getElementById('qrCode');
                const qrUrl = document.getElementById('qrUrl');

                let currentUrl = window.location.href;
                if (currentUrl.endsWith('/')) {
                    currentUrl = currentUrl.slice(0, -1);
                }

                qrCodeContainer.innerHTML = '';
                new QRCode(qrCodeContainer, {
                    text: currentUrl,
                    width: 400,
                    height: 400
                });

                qrUrl.href = currentUrl;
                qrUrl.textContent = currentUrl;
                qrUrl.style.color = 'blue';
                qrUrl.style.textDecoration = 'underline';

                qrModalOverlay.style.display = 'flex';

                qrCodeContainer.addEventListener('click', () => {
                    qrModalOverlay.style.display = 'none';
                });
            }

                function closeQRModal() {
                const qrModalOverlay = document.getElementById('qrModalOverlay');
                qrModalOverlay.style.display = 'none';
            }


function updateGpxRoute(percentage) {
    if (!gpxLayerGroup) {
        setTimeout(() => updateGpxRoute(percentage), 100);
        return;
    }

    if (!allGpxPoints || allGpxPoints.length === 0) {
        return;
    }



    let p = parseFloat(percentage);

    if (isNaN(p)) {
        p = 1.0;
    }

    p = Math.min(1.0, Math.max(0.0, p));

    const totalPoints = allGpxPoints.length;
    const pointsCount = Math.floor(totalPoints * p);
    



    gpxLayerGroup.clearLayers();

    const visibleCoords = allGpxPoints.slice(0, pointsCount);

    if (visibleCoords.length >= 2) {

        const progressLine = L.polyline(visibleCoords, {
            color: '#0000FF',
            weight: 5,
            opacity: 0.7,
            dashArray: '10, 15',
            lineJoin: 'round',
            smoothFactor: 1
        }).addTo(gpxLayerGroup);


        if (typeof L.polylineDecorator === "function") {
            L.polylineDecorator(progressLine, {
                patterns: [{
                    offset: 25,      
                    repeat: 60,    
                    symbol: L.Symbol.arrowHead({
                        pixelSize: 12, 
                        polygon: false,   
                        pathOptions: { 
                            color: '#0000FF', 
                            stroke: true, 
                            weight: 3,    
                            opacity: 1.0 
                        }
                    })
                }]
            }).addTo(gpxLayerGroup);
        }
    } 
}



function initMap() {

    map = L.map('map', { preferCanvas: true });


    gpxLayerGroup = L.layerGroup().addTo(map);
    lineLayerGroup = L.layerGroup().addTo(map);
    markerLayerGroup = L.featureGroup().addTo(map);


    const commonAttribution = [
        `<a href='${window.location.pathname}' class='custom-attribution'>${quizTitle}</a>`,
        `<a href='${instructionsUrl}' class='custom-attribution'>${tr('linkInstructions')}</a>`,
        `<a href='${legalUrl}' class='custom-attribution'>${tr('linklegalNotice')}</a>`,
        `<a href='${privacyUrl}' class='custom-attribution'>${tr('linkPrivacy')}</a>`
    ].join(' | ');

    const osmLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
        maxZoom: 20,
        maxNativeZoom: 19,
        attribution: `© OpenStreetMap contributors <br> ${commonAttribution}`
    }).addTo(map);

    const satelliteLayer = L.tileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", {
        maxZoom: 20,
        maxNativeZoom: 20,
        attribution: `Tiles © <a href="https://www.esri.com/">Esri</a> | Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community | ${commonAttribution}`
    });


    L.control.layers({"OpenStreetMap": osmLayer, "Esri": satelliteLayer}).addTo(map);


    const galleryControl = L.control({ position: 'topright' });
    galleryControl.onAdd = function() {
        const div = L.DomUtil.create('div', 'custom-control-container');
        div.innerHTML = `<a id="dynamicGalleryLink" href="#" target="_blank" class="gallery-button"><svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg></a>`;
        return div;
    };

    addHomeButton(map);
    galleryControl.addTo(map);
    addQrButton(map);
    addLocateButton(map);


    if (typeof allGpxPoints !== 'undefined' && allGpxPoints.length > 0) {
        console.log("Map nachträglich mit vorhandenen GPX-Punkten befüllt.");
        if (typeof unlockedMarkerIds !== 'undefined' && unlockedMarkerIds.length > 0) {
            const lastMarkerId = unlockedMarkerIds[unlockedMarkerIds.length - 1];
            updateGpxRoute(mappingData[lastMarkerId]?.gpxShow || 1.0);
        } else {
            updateGpxRoute(0.1);
        }
    }
    
}

            function initUI() {
                document.getElementById('modalOverlay').onclick = closeModal;
                document.getElementById('qrModalOverlay').onclick = closeQRModal;

                document.querySelectorAll(".tour-select-button").forEach(button => {
                    button.onclick = function() {
                        gpxLayerGroup.clearLayers();
                        lineLayerGroup.clearLayers();
                        markerLayerGroup.clearLayers();
                      handleTourActivation(this.getAttribute("data-tour"));
                    };
                });
            }

            let map;
            let gpxLayerGroup = null;
            let lineLayerGroup, markerLayerGroup;
            let tourFolder = "";
            let hasGpx = false;
            let mappingData = {};
            let unlockedMarkerIds = [];
            let images = [];
            let currentImageIndex = 0;
            let currentModalTimer = null;
            let allGpxPoints = [];
            let currentOpenQuizPointId = null;
            const imageDataMap = new Map();
            const blobCache = new Map();


            const translations = <?php echo json_encode($uiTranslations, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_HEX_APOS); ?>;
            const quizTitle = <?php echo json_encode($quizName, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_QUOT); ?>;
            const quizNotice = <?php echo json_encode($quizNotice); ?>;
            const instructionsUrl = <?php echo json_encode($instructionsUrl); ?>;
            const legalUrl = <?php echo json_encode($legalUrl); ?>;
            const privacyUrl = <?php echo json_encode($privacyUrl); ?>;



            document.addEventListener("DOMContentLoaded", function() {
                document.title = quizTitle;
                const titleElement = document.getElementById('mainQuizTitle');
                if (titleElement) titleElement.textContent = quizTitle;
                initMap();
                initUI();
            });


        </script>
    </body>
</html>
