routerconsole/jsp/js/geomap.js

/* I2P+ geomap.js by dr|z3d */
/* Heavily modified from https://cartosvg.com/mercator */

/**
 * @module geomap
 * @description Interactive SVG world map for the NetDB page showing router distribution
 * by country, floodfill, or bandwidth tier. Supports hover infoboxes, color-coded
 * shape classes based on router counts, and periodic data refresh from /netdb.
 * @author dr|z3d
 * @license AGPL3 or later
 */

(function () {
  'use strict';

  // ============ CONFIGURATION ============
  const CONFIG = {
    STORE_INTERVAL: 15_000,    // Refresh router counts every 15s
    DEBOUNCE_DELAY: 30,        // UI event debounce (ms)
    FETCH_TIMEOUT: 15_000,     // Retry delay on error (ms)
    MAX_RETRY_DELAY: 120_000,  // Max exponential backoff cap (ms)
    RETRY_MULTIPLIER: 1.5,     // Exponential backoff factor
    MAP_WIDTH: 720,
    MAP_HEIGHT: 475,
  };

  // ============ DOM REFERENCES ============
  const geomap = document.querySelector("#geomap");
  const infobox = document.querySelector("#netdbmap #info");

  // ============ STATE ============
  let debugging = false;
  let verbose = false;
  let currentRouterClass = getQueryParameter("class") || "countries";
  let routerCounts = JSON.parse(localStorage.getItem("routerCounts")) || {};
  let retryAttempt = 0;
  let fetchIntervalId = null;

  // ============ CACHED DATA ============
  // Pre-compute count threshold classes for efficient removal
  const COUNT_CLASSES = new Set([
    'count_1', 'count_10', 'count_50', 'count_100',
    'count_200', 'count_300', 'count_400', 'count_500'
  ]);

  // Thresholds in descending order for efficient lookup (no reverse() needed)
  const COUNT_THRESHOLDS = [
    { threshold: 500, className: "count_500" },
    { threshold: 400, className: "count_400" },
    { threshold: 300, className: "count_300" },
    { threshold: 200, className: "count_200" },
    { threshold: 100, className: "count_100" },
    { threshold: 50, className: "count_50" },
    { threshold: 10, className: "count_10" },
    { threshold: 1, className: "count_1" },
  ];

  // WeakMap for private element state (avoids DOM property pollution)
  const elementState = new WeakMap();

  // ============ MAP DATA ============
  const map = {
    data: {
      countries: {
        Afghanistan: { region: "Asia", code: "af" },
        Albania: { region: "Europe", code: "al" },
        Algeria: { region: "Africa", code: "dz" },
        Andorra: { region: "Europe", code: "ad" },
        Angola: { region: "Africa", code: "ao" },
        "Antigua and Barbuda": { region: "Americas", code: "ag" },
        Argentina: { region: "Americas", code: "ar" },
        Armenia: { region: "Asia", code: "am" },
        Australia: { region: "Oceania", code: "au" },
        Austria: { region: "Europe", code: "at" },
        Azerbaijan: { region: "Asia", code: "az" },
        Bahamas: { region: "Americas", code: "bs" },
        Bahrain: { region: "Asia", code: "bh" },
        Bangladesh: { region: "Asia", code: "bd" },
        Barbados: { region: "Americas", code: "bb" },
        Belarus: { region: "Europe", code: "by" },
        Belgium: { region: "Europe", code: "be" },
        Belize: { region: "Americas", code: "bz" },
        Benin: { region: "Africa", code: "bj" },
        Bhutan: { region: "Asia", code: "bt" },
        Bolivia: { region: "Americas", code: "bo" },
        "Bosnia and Herzegovina": { region: "Europe", code: "ba" },
        Botswana: { region: "Africa", code: "bw" },
        Brazil: { region: "Americas", code: "br" },
        "Brunei Darussalam": { region: "Asia", code: "bn" },
        Bulgaria: { region: "Europe", code: "bg" },
        "Burkina Faso": { region: "Africa", code: "bf" },
        Burundi: { region: "Africa", code: "bi" },
        "Cabo Verde": { region: "Africa", code: "cv" },
        Cambodia: { region: "Asia", code: "kh" },
        Cameroon: { region: "Africa", code: "cm" },
        Canada: { region: "Americas", code: "ca" },
        "Central African Republic": { region: "Africa", code: "cf" },
        Chad: { region: "Africa", code: "td" },
        Chile: { region: "Americas", code: "cl" },
        China: { region: "Asia", code: "cn" },
        Colombia: { region: "Americas", code: "co" },
        Comoros: { region: "Africa", code: "km" },
        "Congo (Democratic Republic)": { region: "Africa", code: "cd" },
        Congo: { region: "Africa", code: "cg" },
        "Costa Rica": { region: "Americas", code: "cr" },
        "Côte d'Ivoire": { region: "Africa", code: "ci" },
        Croatia: { region: "Europe", code: "hr" },
        Cuba: { region: "Americas", code: "cu" },
        Cyprus: { region: "Asia", code: "cy" },
        Czechia: { region: "Europe", code: "cz" },
        Denmark: { region: "Europe", code: "dk" },
        Djibouti: { region: "Africa", code: "dj" },
        "Dominican Republic": { region: "Americas", code: "do" },
        Dominica: { region: "Americas", code: "dm" },
        Ecuador: { region: "Americas", code: "ec" },
        Egypt: { region: "Africa", code: "eg" },
        "El Salvador": { region: "Americas", code: "sv" },
        "Equatorial Guinea": { region: "Africa", code: "gq" },
        Eritrea: { region: "Africa", code: "er" },
        Estonia: { region: "Europe", code: "ee" },
        Eswatini: { region: "Africa", code: "sz" },
        Ethiopia: { region: "Africa", code: "et" },
        "Falkland Islands": { region: "Americas", code: "fk" },
        Fiji: { region: "Oceania", code: "fj" },
        Finland: { region: "Europe", code: "fi" },
        France: { region: "Europe", code: "fr" },
        Gabon: { region: "Africa", code: "ga" },
        Gambia: { region: "Africa", code: "gm" },
        Georgia: { region: "Asia", code: "ge" },
        Germany: { region: "Europe", code: "de" },
        Ghana: { region: "Africa", code: "gh" },
        Greece: { region: "Europe", code: "gr" },
        Greenland: { region: "Americas", code: "gl" },
        Grenada: { region: "Americas", code: "gd" },
        Guatemala: { region: "Americas", code: "gt" },
        "Guinea-Bissau": { region: "Africa", code: "gw" },
        Guinea: { region: "Africa", code: "gn" },
        Guyana: { region: "Americas", code: "gy" },
        Haiti: { region: "Americas", code: "ht" },
        Honduras: { region: "Americas", code: "hn" },
        Hungary: { region: "Europe", code: "hu" },
        Iceland: { region: "Europe", code: "is" },
        India: { region: "Asia", code: "in" },
        Indonesia: { region: "Asia", code: "id" },
        Iran: { region: "Asia", code: "ir" },
        Iraq: { region: "Asia", code: "iq" },
        Ireland: { region: "Europe", code: "ie" },
        Israel: { region: "Asia", code: "il" },
        Italy: { region: "Europe", code: "it" },
        Jamaica: { region: "Americas", code: "jm" },
        Japan: { region: "Asia", code: "jp" },
        Jordan: { region: "Asia", code: "jo" },
        Kazakhstan: { region: "Asia", code: "kz" },
        Kenya: { region: "Africa", code: "ke" },
        Kiribati: { region: "Oceania", code: "ki" },
        "North Korea": { region: "Asia", code: "kp" },
        "South Korea": { region: "Asia", code: "kr" },
        Kuwait: { region: "Asia", code: "kw" },
        Kyrgyzstan: { region: "Asia", code: "kg" },
        "Lao People's Democratic Republic": { region: "Asia", code: "la" },
        Latvia: { region: "Europe", code: "lv" },
        Lebanon: { region: "Asia", code: "lb" },
        Lesotho: { region: "Africa", code: "ls" },
        Liberia: { region: "Africa", code: "lr" },
        Libya: { region: "Africa", code: "ly" },
        Liechtenstein: { region: "Europe", code: "li" },
        Lithuania: { region: "Europe", code: "lt" },
        Luxembourg: { region: "Europe", code: "lu" },
        Madagascar: { region: "Africa", code: "mg" },
        Malawi: { region: "Africa", code: "mw" },
        Malaysia: { region: "Asia", code: "my" },
        Mali: { region: "Africa", code: "ml" },
        Malta: { region: "Europe", code: "mt" },
        Mauritania: { region: "Africa", code: "mr" },
        Mauritius: { region: "Africa", code: "mu" },
        Mexico: { region: "Americas", code: "mx" },
        "Micronesia (Federated States of)": { region: "Oceania", code: "fm" },
        Moldova: { region: "Europe", code: "md" },
        Mongolia: { region: "Asia", code: "mn" },
        Montenegro: { region: "Europe", code: "me" },
        Morocco: { region: "Africa", code: "ma" },
        Mozambique: { region: "Africa", code: "mz" },
        Myanmar: { region: "Asia", code: "mm" },
        Namibia: { region: "Africa", code: "na" },
        Nauru: { region: "Oceania", code: "nr" },
        Nepal: { region: "Asia", code: "np" },
        Netherlands: { region: "Europe", code: "nl" },
        "New Zealand": { region: "Oceania", code: "nz" },
        Nicaragua: { region: "Americas", code: "ni" },
        Nigeria: { region: "Africa", code: "ng" },
        Niger: { region: "Africa", code: "ne" },
        "North Macedonia": { region: "Europe", code: "mk" },
        Norway: { region: "Europe", code: "no" },
        Oman: { region: "Asia", code: "om" },
        Pakistan: { region: "Asia", code: "pk" },
        Palau: { region: "Oceania", code: "pw" },
        Panama: { region: "Americas", code: "pa" },
        "Papua New Guinea": { region: "Oceania", code: "pg" },
        Paraguay: { region: "Americas", code: "py" },
        Peru: { region: "Americas", code: "pe" },
        Philippines: { region: "Asia", code: "ph" },
        Poland: { region: "Europe", code: "pl" },
        Portugal: { region: "Europe", code: "pt" },
        Qatar: { region: "Asia", code: "qa" },
        Romania: { region: "Europe", code: "ro" },
        Russia: { region: "Europe", code: "ru" },
        Rwanda: { region: "Africa", code: "rw" },
        "Saint Kitts and Nevis": { region: "Americas", code: "kn" },
        "Saint Lucia": { region: "Americas", code: "lc" },
        "Saint Vincent and the Grenadines": { region: "Americas", code: "vc" },
        Samoa: { region: "Oceania", code: "ws" },
        "San Marino": { region: "Europe", code: "sm" },
        "Sao Tome and Principe": { region: "Africa", code: "st" },
        "Saudi Arabia": { region: "Asia", code: "sa" },
        Senegal: { region: "Africa", code: "sn" },
        Serbia: { region: "Europe", code: "rs" },
        Seychelles: { region: "Africa", code: "sc" },
        "Sierra Leone": { region: "Africa", code: "sl" },
        Singapore: { region: "Asia", code: "sg" },
        Slovakia: { region: "Europe", code: "sk" },
        Slovenia: { region: "Europe", code: "si" },
        "Solomon Islands": { region: "Oceania", code: "sb" },
        Somalia: { region: "Africa", code: "so" },
        "South Africa": { region: "Africa", code: "za" },
        "South Sudan": { region: "Africa", code: "ss" },
        Spain: { region: "Europe", code: "es" },
        "Sri Lanka": { region: "Asia", code: "lk" },
        Sudan: { region: "Africa", code: "sd" },
        Suriname: { region: "Americas", code: "sr" },
        Sweden: { region: "Europe", code: "se" },
        Switzerland: { region: "Europe", code: "ch" },
        Syria: { region: "Asia", code: "sy" },
        Taiwan: { region: "Asia", code: "tw" },
        Tajikistan: { region: "Asia", code: "tj" },
        Tanzania: { region: "Africa", code: "tz" },
        Thailand: { region: "Asia", code: "th" },
        Tibet: { region: "Asia", code: "cn" },
        "Timor-Leste": { region: "Asia", code: "tl" },
        Togo: { region: "Africa", code: "tg" },
        Tonga: { region: "Oceania", code: "to" },
        "Trinidad and Tobago": { region: "Americas", code: "tt" },
        Tunisia: { region: "Africa", code: "tn" },
        Turkey: { region: "Asia", code: "tr" },
        Turkmenistan: { region: "Asia", code: "tm" },
        Uganda: { region: "Africa", code: "ug" },
        Ukraine: { region: "Europe", code: "ua" },
        "United Arab Emirates": { region: "Asia", code: "ae" },
        "United Kingdom": { region: "Europe", code: "gb" },
        "United States of America": { region: "Americas", code: "us" },
        Uruguay: { region: "Americas", code: "uy" },
        Uzbekistan: { region: "Asia", code: "uz" },
        Vanuatu: { region: "Oceania", code: "vu" },
        Venezuela: { region: "Americas", code: "ve" },
        "Viet Nam": { region: "Asia", code: "vn" },
        "Western Sahara": { region: "Africa", code: "eh" },
        Yemen: { region: "Asia", code: "ye" },
        Zambia: { region: "Africa", code: "zm" },
        Zimbabwe: { region: "Africa", code: "zw" },
      },
    },
    infoboxHTML: {
      countries: '<span><b>Country: </b> <img class=mapflag width=9 height=6 src="/flags.jsp?c=${data.code}"> ${shapeId} (${data.code})</span><br>' +
        "<span><b>Region: </b>${data.region}</span><br>\n" + "<span><b>Routers: 0</b></span>\n",
    },
  };

  // ============ UTILITIES ============

  /**
   * Retrieves a URL query parameter by name.
   * @function getQueryParameter
   * @param {string} name - The query parameter name
   * @returns {string|null} The parameter value, or null if not present
   */
  function getQueryParameter(name) {
    return new URLSearchParams(window.location.search).get(name);
  }

  // Template rendering helper - cleaner than chained replace()
  /**
   * Renders a template string with variable substitution.
   * @function renderTemplate
   * @param {string} template - The template string with ${variable} placeholders
   * @param {Object} context - The context object containing variable values
   * @returns {string} The rendered template
   */
  function renderTemplate(template, context) {
    return template.replace(/\$\{([^}]+)\}/g, (_, path) => {
      return path.split('.').reduce((obj, key) => obj?.[key], context);
    });
  }

  // Debounce with proper return value handling
  /**
   * Creates a debounced version of the given function.
   * @function debounce
   * @param {Function} func - The function to debounce
   * @param {number} wait - The debounce delay in milliseconds
   * @param {boolean} [immediate=false] - Whether to call on the leading edge
   * @returns {Function} The debounced function
   */
  function debounce(func, wait, immediate = false) {
    let timeout;
    return function (...args) {
      const context = this;
      const callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(() => {
        timeout = null;
        if (!immediate) func.apply(context, args);
      }, wait);
      if (callNow) func.apply(context, args);
    };
  }

  // Exponential backoff scheduler for retry logic
  /**
   * Schedules a retry with exponential backoff.
   * @function scheduleRetry
   * @param {Function} callback - The function to retry
   * @param {number} [attempt=0] - The current attempt number
   * @returns {void}
   */
  function scheduleRetry(callback, attempt = 0) {
    const delay = Math.min(
      CONFIG.FETCH_TIMEOUT * Math.pow(CONFIG.RETRY_MULTIPLIER, attempt),
      CONFIG.MAX_RETRY_DELAY
    );
    setTimeout(callback, delay);
  }

  // ============ FLAG LOADING ============

  const preloadedFlags = new Set();

  /**
   * Preloads country flag images into a hidden container for faster rendering.
   * @function preloadFlags
   * @param {string[]} codes - Array of ISO country codes to preload flags for
   * @returns {void}
   */
  function preloadFlags(codes) {
    const flagContainer = document.createElement("div");
    flagContainer.id = "preloadFlags";
    flagContainer.hidden = true;
    document.body.appendChild(flagContainer);

    const newImages = codes
      .filter(code => !preloadedFlags.has(code))
      .map(code => {
        const img = new Image();
        img.src = `/flags.jsp?c=${code}`;
        flagContainer.appendChild(img);
        return new Promise((resolve) => {
          img.onload = () => {
            preloadedFlags.add(code);
            resolve();
          };
          img.onerror = () => {
            if (debugging) console.warn(`Failed to load flag: ${code}`);
            resolve(); // Resolve anyway to not block Promise.all
          };
        });
      });

    Promise.all(newImages)
      .then(() => {
        if (flagContainer.parentNode) {
          document.body.removeChild(flagContainer);
        }
      })
      .catch(err => {
        if (debugging) console.warn("Flag preload error:", err);
        if (flagContainer.parentNode) {
          document.body.removeChild(flagContainer);
        }
      });
  }

  // Preload all country flags on init
  preloadFlags(Object.values(map.data.countries).map(country => country.code));

  // ============ ROUTER COUNTS ============

  /**
   * Fetches router data from /netdb, parses country/floodfill/tier counts,
   * and stores them in localStorage for map rendering.
   * @async
   * @function storeRouterCounts
   * @returns {Promise<void>}
   */
  async function storeRouterCounts() {
    try {
      const response = await fetch("/netdb");
      if (!response.ok) throw new Error("HTTP " + response.status);

      const parser = new DOMParser();
      const doc = parser.parseFromString(await response.text(), "text/html");
      const rows = doc.querySelectorAll("#cclist tr");

      routerCounts = {
        countries: {},
        floodfill: {},
        tierX: {}
      };

      rows.forEach(row => {
        const countryLink = row.querySelector('a[href^="/netdb?c="]');
        if (countryLink && row.children[3]) {
          const cc = countryLink.href.split("=")[1];
          const count = parseInt(row.children[3].textContent.trim(), 10);
          if (!isNaN(count)) routerCounts.countries[cc] = count;
        }

        ["floodfill", "tierX"].forEach(type => {
          const sel = type === "floodfill" ? "td.countFF a" : "td.countX a";
          const link = row.querySelector(sel);
          if (link) {
            const cc = link.href.match(/cc=([a-zA-Z]{2})/)?.[1];
            if (cc) {
              const count = parseInt(link.textContent.trim(), 10);
              if (!isNaN(count)) routerCounts[type][cc] = count;
            }
          }
        });
      });

      routerCounts.totals = {
        countries: Object.values(routerCounts.countries).reduce((a, b) => a + b, 0),
        floodfill: Object.values(routerCounts.floodfill).reduce((a, b) => a + b, 0),
        tierX: Object.values(routerCounts.tierX).reduce((a, b) => a + b, 0)
      };

      localStorage.setItem("routerCounts", JSON.stringify(routerCounts));
      localStorage.setItem("currentRouterClass", currentRouterClass);
      retryAttempt = 0;

      if (debugging) console.log("Router counts updated:", routerCounts);
      updateShapeClasses(currentRouterClass);

      if (!fetchIntervalId) {
        fetchIntervalId = setInterval(() => {
          storeRouterCounts();
        }, CONFIG.STORE_INTERVAL);
      }
    } catch (error) {
      if (debugging) console.error("Error fetching router counts:", error);
      localStorage.removeItem("routerCounts");
      localStorage.removeItem("currentRouterClass");
      scheduleRetry(storeRouterCounts, ++retryAttempt);
    }
  }

  // Use module-level routerCounts (no localStorage parse needed)
  /**
   * Gets the router count for a specific country/shape and classification.
   * @function getRouterCount
   * @param {string} shapeId - The country/shape identifier
   * @param {string} [routerClass="countries"] - The classification type (countries, floodfill, tierX)
   * @returns {number} The router count for the given shape and class
   */
  function getRouterCount(shapeId, routerClass = "countries") {
    const code = map.data.countries[shapeId]?.code;
    return code ? (routerCounts[routerClass]?.[code] || 0) : 0;
  }

  function getRouterTotalByClass(routerClass) {
    return routerCounts.totals?.[routerClass] || 0;
  }

  /**
   * Gets the total router count for a specific classification.
   * @function getRouterTotalByClass
   * @param {string} routerClass - The classification type (countries, floodfill, tierX)
   * @returns {number} The total count, or 0 if unavailable
   */

  // ============ UI UPDATES ============

  /**
   * Updates the CSS class of an SVG shape based on the router count threshold.
   * @function updateShapeClass
   * @param {string} shapeId - The SVG element ID to update
   * @param {number} count - The router count for this shape
   * @returns {void}
   */
  function updateShapeClass(shapeId, count) {
    const svgElement = document.getElementById(shapeId);
    if (!svgElement) return;

    if (debugging && verbose) {
      console.log(`Updating ${shapeId}: count=${count}`);
    }

    // Remove all count classes efficiently using precomputed Set
    svgElement.classList.remove(...COUNT_CLASSES);

    // Find matching threshold (array already in descending order)
    const match = COUNT_THRESHOLDS.find(({ threshold }) => count >= threshold);
    if (match) svgElement.classList.add(match.className);
  }

  /**
   * Updates all SVG shape classes for the given router classification.
   * @function updateShapeClasses
   * @param {string} routerClass - The classification type to update
   * @returns {void}
   */
  function updateShapeClasses(routerClass) {
    if (!routerCounts[routerClass]) return;

    // Use requestIdleCallback for non-critical updates when available
    const updateFn = () => {
      Object.keys(map.data.countries).forEach(shapeId => {
        updateShapeClass(shapeId, getRouterCount(shapeId, routerClass));
      });
    };

    if ('requestIdleCallback' in window) {
      requestIdleCallback(updateFn, { timeout: 100 });
    } else {
      requestAnimationFrame(updateFn);
    }
  }

  /**
   * Populates the infobox element with country data, flag, and router count.
   * @function createInfobox
   * @param {Object} data - Country data with region and code
   * @param {string} infoboxTemplate - HTML template for the infobox
   * @param {string} shapeId - The country/shape name
   * @param {string} [routerClass="countries"] - The current classification type
   * @returns {void}
   */
  function createInfobox(data, infoboxTemplate, shapeId, routerClass = "countries") {
    if (!data) return;

    const routerCount = getRouterCount(shapeId, routerClass);
    const tierName = routerClass !== "countries"
      ? routerClass.replace("tier", "Bandwidth tier ").replace("floodfill", "Floodfills")
      : "";

    // Use template helper for cleaner substitution
    let html = renderTemplate(infoboxTemplate, { shapeId, data });

    // Special case replacements
    html = html
      .replace(/<b>Routers: 0<\/b>/g, `<b>${tierName || "Routers"}:</b> ${routerCount}`)
      .replace("United States of America", "USA");

    infobox.innerHTML = html;
    infobox.classList.remove("hidden");
  }

  /**
   * Renders the total router count in the infobox for the current classification.
   * @function renderTotals
   * @returns {void}
   */
  function renderTotals() {
    const label = currentRouterClass === "floodfill" ? "Floodfills" :
                  currentRouterClass === "tierX" ? "X tier Routers" : "Routers";
    const total = getRouterTotalByClass(currentRouterClass);

    requestAnimationFrame(() => {
      infobox.innerHTML = `<b>Known ${label}:</b> ${total}`;
    });
  }

  // ============ EVENT HANDLERS ============

  // Define debounced functions once at module scope (fixes recreation bug)
  const debouncedHandleEvent = debounce(handleEventCore, CONFIG.DEBOUNCE_DELAY);
  const debouncedRenderTotals = debounce(renderTotals, CONFIG.DEBOUNCE_DELAY);

  /**
   * Core handler for mouse events on map paths.
   * @function handleEventCore
   * @param {Event} event - The mouse event
   * @returns {void}
   */
  function handleEventCore(event) {
    const target = event.target;
    const sectionId = target?.parentNode?.id;

    if (target?.matches("path[id]")) {
      const shapeId = target.id;
      target.classList.add("hover");

      const data = map.data[sectionId]?.[shapeId];
      if (data) {
        createInfobox(data, map.infoboxHTML[sectionId], shapeId, currentRouterClass);
        const count = getRouterCount(shapeId, currentRouterClass);
        target.classList.toggle("link", count > 0);

        // Prefetch netdb page for countries with routers
        const caps = getCapsParam();
        if (count > 0 && !document.querySelector('link[href="/netdb?c=' + data.code + caps + '"]')) {
          const link = document.createElement("link");
          link.rel = "prefetch";
          link.href = "/netdb?c=" + data.code + caps;
          document.head.appendChild(link);
        }
      }
    } else {
      renderTotals();
    }
  }

  // Throttled DOM repositioning for mousemove (reduces layout thrashing)
  let lastRepositionTime = 0;
  const REPOSITION_THROTTLE = 100; // ms

  /**
   * Gets the caps query parameter value based on the current router class.
   * @function getCapsParam
   * @returns {string} The caps parameter string (e.g., "&caps=f" for floodfill, "&caps=X" for tierX)
   */
  function getCapsParam() {
    if (currentRouterClass === "floodfill") return "&caps=f";
    if (currentRouterClass === "tierX") return "&caps=X";
    return "";
  }

  /**
   * Handles throttled DOM repositioning of hovered path elements.
   * @function handleMouseReposition
   * @param {Element} target - The target element to reposition
   * @returns {void}
   */
  function handleMouseReposition(target) {
    const now = performance.now();
    if (now - lastRepositionTime < REPOSITION_THROTTLE) return;

    if (target.tagName === "path" && target.parentNode?.tagName === "g") {
      const container = target.parentNode;
      const state = elementState.get(target) || {};

      if (state.previousPos === undefined) {
        state.previousPos = Array.from(container.children).indexOf(target);
        elementState.set(target, state);
      }

      if (target !== container.lastElementChild) {
        container.appendChild(target);
        lastRepositionTime = now;
      }
    }
  }

  // ============ EVENT BINDINGS ============

  geomap.addEventListener("mouseenter", debouncedHandleEvent);

  geomap.addEventListener("mouseout", ({ target }) => {
    // Restore element position using WeakMap state
    const state = elementState.get(target);
    if (state?.previousPos !== undefined) {
      const container = target.parentNode;
      const children = Array.from(container.children);
      const referenceNode = children[state.previousPos];
      if (referenceNode && referenceNode !== target) {
        container.insertBefore(target, referenceNode);
      }
    }
    debouncedRenderTotals();
  });

  geomap.addEventListener("mouseleave", () => {
    renderTotals();
    // Remove hover state from all paths
    geomap.querySelectorAll("path.hover").forEach(el => el.classList.remove("hover"));
  });

  geomap.addEventListener("mousemove", event => {
    const { target } = event;
    debouncedHandleEvent(event);
    handleMouseReposition(target);
  });

  geomap.addEventListener("click", (e) => {
    const path = e.target.closest("path[id]");
    if (!path) return;

    const country = map.data.countries[path.id];
    const count = getRouterCount(path.id, currentRouterClass);

    if (country && count > 0) {
      path.classList.add("link");
      const caps = getCapsParam();
      window.location.href = "/netdb?c=" + country.code + caps;
    }
  });

  // ============ INITIALIZATION ============

  /**
   * Highlights the active navigation button based on the current URL query parameter.
   * @function setNavButtonActive
   * @returns {void}
   */
  function setNavButtonActive() {
    const active = getQueryParameter("class");
    const navButtons = document.querySelectorAll("#nav span");
    navButtons.forEach(btn => btn.classList.remove("active"));

    const activeIdMap = { floodfill: "byff", tierX: "byX" };
    const activeId = activeIdMap[active] || "bycc";
    document.getElementById(activeId)?.classList.add("active");
  }

  document.addEventListener("DOMContentLoaded", () => {
    const routerClassFromURL = getQueryParameter("class") || "countries";
    currentRouterClass = routerClassFromURL;

    localStorage.setItem("currentRouterClass", routerClassFromURL);
    storeRouterCounts();

    if (debugging) console.log("Initialized with routerClass:", routerClassFromURL);

    updateShapeClasses(routerClassFromURL);
    setNavButtonActive();

    // Delayed style removal for smooth load
    setTimeout(() => {
      const countriesGroup = geomap.querySelector("#countries");
      if (countriesGroup) countriesGroup.removeAttribute("style");
    }, 300);
  });

  // ============ CLEANUP ============

  window.addEventListener("beforeunload", () => {
    if (fetchIntervalId) {
      clearInterval(fetchIntervalId);
      fetchIntervalId = null;
    }
    netdbWorker.terminate();
  });

})();