Phantom Isles Time for Wolfery

Shows PIT/PIAT center-top on *.wolfery.com; hover popup; time converter (DE/EN, fast drift-based PIT<->real, persistent settings)

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Phantom Isles Time for Wolfery
// @name:de      Phantom Isles Zeit-Popup für Wolfery
// @namespace    https://forum.wolfery.com/u/felinex/
// @version      3.4
// @description  Shows PIT/PIAT center-top on *.wolfery.com; hover popup; time converter (DE/EN, fast drift-based PIT<->real, persistent settings)
// @description:de Zeigt PIT/PIAT zentral-oben auf *.wolfery.com; Popup mit Tooltip; Zeitumrechner (DE/EN, schneller Drift-Ausgleich PIT↔Echtzeit, Einstellungen werden gespeichert)
// @icon         https://static.f-list.net/images/eicon/gloomfort.png
// @license      All Rights Reserved
// @author       Felinex Gloomfort
// @match        *://*.wolfery.com/*
// ==/UserScript==

(function () {
  const isGerman = navigator.language && navigator.language.startsWith('de');
  const T = isGerman ? {
    modePIAT: "PIAT",
    modePIT: "PIT",
    btnPIAT: "Zu PIT wechseln",
    btnPIT: "Zu PIAT wechseln",
    tooltipPIAT: "Phantom Isles Ausgerichtete Zeit",
    tooltipPIT: "Phantom Isles Zeit",
    convertLabel: "Realzeit → PIT/PIAT",
    convertBtn: "Umwandeln",
    convertPlaceholder: "",
    convertPick: "Bitte ein Datum und eine Uhrzeit wählen.",
    convertResultPIAT: "PIAT: ",
    convertResultPIT: "PIT: ",
    reverseLabel: "PIT/PIAT → Realzeit",
    reverseBtn: "Zu Realzeit umwandeln",
    reversePlaceholder: "z.B. 31 Nov 2025 10:39",
    reverseParseFail: "Fehler beim Parsen (Format: TT Monat JJJJ HH:MM)",
    reverseResult: "Real: ",
    reverseResultPITFail: "PIT: Umwandlung fehlgeschlagen",
    settings: "⚙️ Einstellungen",
    close: "Schließen",
    position: "Popup-Position",
    expanded: "Rechner anzeigen",
    collapsed: "Rechner ausblenden",
    autoConvert: "Automatische Zeit-Umwandlung",
    autoConvertOn: "aktiviert",
    autoConvertOff: "deaktiviert"
  } : {
    modePIAT: "PIAT",
    modePIT: "PIT",
    btnPIAT: "Switch to PIT",
    btnPIT: "Switch to PIAT",
    tooltipPIAT: "Phantom Isles Aligned Time",
    tooltipPIT: "Phantom Isles Time",
    convertLabel: "Local time → PIT/PIAT",
    convertBtn: "Convert",
    convertPlaceholder: "",
    convertPick: "Pick a date/time.",
    convertResultPIAT: "PIAT: ",
    convertResultPIT: "PIT: ",
    reverseLabel: "PIT/PIAT → Real Time",
    reverseBtn: "Convert to Real",
    reversePlaceholder: "e.g. 31 Nov 2025 10:39",
    reverseParseFail: "Parse fail (try DD Month YYYY HH:MM)",
    reverseResult: "Real: ",
    reverseResultPITFail: "PIT: Conversion failed",
    settings: "⚙️ Settings",
    close: "Close",
    position: "Popup Position",
    expanded: "Show Calculator",
    collapsed: "Hide Calculator",
    autoConvert: "Auto Time Conversion",
    autoConvertOn: "enabled",
    autoConvertOff: "disabled"
  };

  const monthNames = [
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
  ];
  const monthNames3 = [
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  ];
  const baseFantasyDaysInMonth = [37, 35, 37, 36, 37, 36, 37, 37, 36, 37, 36, 37];

  function isLeapYear(year) {
    return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
  }
  function leapCountSince(startYear, currentYear) {
    let count = 0;
    for (let y = startYear; y < currentYear; y++) {
      if (isLeapYear(y)) count++;
    }
    return count;
  }
  function isDoubleLeap(year, startYear = 1970) {
    let leaps = leapCountSince(startYear, year + 1);
    return isLeapYear(year) && (leaps % 5 === 0);
  }
  function getLeapDriftMinutesSince1970(year) {
    let drift = 0;
    let leapCount = 0;
    for (let y = 1970; y < year; y++) {
      if (isLeapYear(y)) {
        leapCount++;
        if (leapCount % 5 === 0) {
          drift = 0;
        } else {
          drift += 240;
        }
      }
    }
    return drift;
  }
  function unixToPhantomIslesTimePIT(unixTimestampMs, showShort = false) {
    const epochYear = 1970;
    const msPerMinute = 60000;
    let totalRealMinutes = Math.floor(unixTimestampMs / msPerMinute);
    let leapDriftMinutes = 0, leapYearsPassed = 0;
    let currentYear = epochYear, usedMinutes = 0, totalFantasyDays = 0;
    let yearMinutes, fantasyDaysThisYear;
    while (true) {
      let isLeap = isLeapYear(currentYear);
      let isDLeap = isDoubleLeap(currentYear);
      let months = [
        31, isLeap ? 29 : 28, 31, 30, 31, 30,
        31, 31, 30, 31, 30, 31
      ];
      yearMinutes = months.reduce((a, b) => a + b, 0) * 1440;
      fantasyDaysThisYear = 438 + (isLeap ? 1 : 0) + (isDLeap ? 1 : 0);
      if (usedMinutes + yearMinutes > totalRealMinutes) break;
      usedMinutes += yearMinutes;
      totalFantasyDays += fantasyDaysThisYear;
      if (isLeap) {
        leapYearsPassed++;
        if (isDLeap) {
          leapDriftMinutes = 0;
          leapYearsPassed = 0;
        } else {
          leapDriftMinutes += 1440 - 1200;
        }
      }
      currentYear++;
    }
    let minutesIntoThisYear = totalRealMinutes - usedMinutes;
    let isLeap = isLeapYear(currentYear);
    let isDLeap = isDoubleLeap(currentYear);
    let months = [
      31, isLeap ? 29 : 28, 31, 30, 31, 30,
      31, 31, 30, 31, 30, 31
    ];
    yearMinutes = months.reduce((a, b) => a + b, 0) * 1440;
    fantasyDaysThisYear = 438 + (isLeap ? 1 : 0) + (isDLeap ? 1 : 0);
    let yearFraction = minutesIntoThisYear / yearMinutes;
    let fantasyYearDayFloat = yearFraction * fantasyDaysThisYear;
    let fantasyYearDay = Math.floor(fantasyYearDayFloat);
    let fantasyDaysInMonth = baseFantasyDaysInMonth.slice();
    let extraDays = fantasyDaysThisYear - 438;
    if (extraDays > 0) fantasyDaysInMonth[11] += extraDays;
    let fantasyMonth = 0, dayOfYearSum = 0, fantasyDayOfMonth = 1;
    for (let i = 0; i < 12; i++) {
      if (fantasyYearDay < dayOfYearSum + fantasyDaysInMonth[i]) {
        fantasyMonth = i + 1;
        fantasyDayOfMonth = fantasyYearDay - dayOfYearSum + 1;
        break;
      }
      dayOfYearSum += fantasyDaysInMonth[i];
    }
    let dayFraction = fantasyYearDayFloat - fantasyYearDay;
    let fantasyMinutesInDay = dayFraction * 1200 + leapDriftMinutes;
    if (fantasyMinutesInDay >= 1200) {
      fantasyDayOfMonth += Math.floor(fantasyMinutesInDay / 1200);
      fantasyMinutesInDay = fantasyMinutesInDay % 1200;
    }
    let fantasyHour = Math.floor(fantasyMinutesInDay / 50);
    let fantasyMinute = Math.floor(fantasyMinutesInDay % 50);
    const yearStr = currentYear.toString();
    const monthStr = fantasyMonth < 10 ? "0" + fantasyMonth : "" + fantasyMonth;
    const dayStr = fantasyDayOfMonth < 10 ? "0" + fantasyDayOfMonth : "" + fantasyDayOfMonth;
    const hourStr = fantasyHour < 10 ? "0" + fantasyHour : "" + fantasyHour;
    const minuteStr = fantasyMinute < 10 ? "0" + fantasyMinute : "" + fantasyMinute;
    const nameArr = showShort ? monthNames3 : monthNames;
    return `${dayStr} ${nameArr[fantasyMonth - 1]} ${yearStr} ${hourStr}:${minuteStr}`;
  }
  function unixToPhantomIslesTimePIAT(unixTimestampMs, showShort = false) {
    const fantasyDaysPerYear = baseFantasyDaysInMonth.reduce((a, b) => a + b, 0);
    const date = new Date(unixTimestampMs);
    const year = date.getUTCFullYear();
    const isLeap = isLeapYear(year);
    const realDaysInMonth = [
      31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    ];
    const totalMinutesYear = realDaysInMonth.reduce((a, b) => a + b, 0) * 1440;
    const startOfYear = Date.UTC(year, 0, 1, 0, 0, 0, 0);
    const minutesSinceYear = Math.floor((unixTimestampMs - startOfYear) / 60000);
    const yearFraction = minutesSinceYear / totalMinutesYear;
    const fantasyYearDayFloat = yearFraction * fantasyDaysPerYear;
    const fantasyYearDay = Math.floor(fantasyYearDayFloat);
    let fantasyMonth = 0, dayOfYearSum = 0, fantasyDayOfMonth = 1;
    for (let i = 0; i < 12; i++) {
      if (fantasyYearDay < dayOfYearSum + baseFantasyDaysInMonth[i]) {
        fantasyMonth = i + 1;
        fantasyDayOfMonth = fantasyYearDay - dayOfYearSum + 1;
        break;
      }
      dayOfYearSum += baseFantasyDaysInMonth[i];
    }
    let thisFantasyDayMinutes = 1200;
    if (isLeap && fantasyYearDay === fantasyDaysPerYear - 1) {
      thisFantasyDayMinutes = 2640;
    }
    let dayFraction = fantasyYearDayFloat - fantasyYearDay;
    let fantasyMinutesInDay = dayFraction * thisFantasyDayMinutes;
    let fantasyHour = Math.floor(fantasyMinutesInDay / 50);
    let fantasyMinute = Math.floor(fantasyMinutesInDay % 50);
    const yearStr = year.toString();
    const monthStr = fantasyMonth < 10 ? "0" + fantasyMonth : "" + fantasyMonth;
    const dayStr = fantasyDayOfMonth < 10 ? "0" + fantasyDayOfMonth : "" + fantasyDayOfMonth;
    const hourStr = fantasyHour < 10 ? "0" + fantasyHour : "" + fantasyHour;
    const minuteStr = fantasyMinute < 10 ? "0" + fantasyMinute : "" + fantasyMinute;
    const nameArr = showShort ? monthNames3 : monthNames;
    return `${dayStr} ${nameArr[fantasyMonth - 1]} ${yearStr} ${hourStr}:${minuteStr}`;
  }
  function parsePITPIATinput(str) {
    const r = /^(\d{1,2})\.?\s+([A-Za-zäöüÄÖÜ]+)\s+(\d{4})\s+(\d{1,2}):(\d{2})$/;
    const m = str.trim().match(r);
    if (!m) return null;
    let monthIdx = monthNames.findIndex(n => n.toLowerCase().startsWith(m[2].toLowerCase()));
    if (monthIdx === -1) monthIdx = monthNames3.findIndex(n => n.toLowerCase().startsWith(m[2].toLowerCase()));
    if (monthIdx === -1) return null;
    return {
      day: parseInt(m[1], 10),
      monthIdx,
      year: parseInt(m[3], 10),
      hour: parseInt(m[4], 10),
      minute: parseInt(m[5], 10)
    };
  }
  function piatToUnix({day, monthIdx, year, hour, minute}) {
    const fantasyDayOfYear = baseFantasyDaysInMonth.slice(0, monthIdx).reduce((a, b) => a + b, 0) + (day - 1);
    let totalFantasyMinutes = fantasyDayOfYear * 1200 + hour * 50 + minute;
    let isLeap = isLeapYear(year);
    const realDaysInMonth = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let totalRealMinutesYear = realDaysInMonth.reduce((a, b) => a + b, 0) * 1440;
    let baseFantasyDays = baseFantasyDaysInMonth.reduce((a, b) => a + b, 0);
    let minuteFraction = totalFantasyMinutes / (baseFantasyDays * 1200);
    let realMinutesSinceYear = Math.floor(minuteFraction * totalRealMinutesYear);
    let realDate = new Date(Date.UTC(year, 0, 1, 0, 0, 0, 0));
    return realDate.getTime() + realMinutesSinceYear * 60000;
  }
  function pitToUnixViaPIAT(parsed) {
    const drift = getLeapDriftMinutesSince1970(parsed.year);
    let minsSincePIATYear =
      baseFantasyDaysInMonth.slice(0, parsed.monthIdx).reduce((a, b) => a + b, 0) * 1200 +
      (parsed.day - 1) * 1200 +
      parsed.hour * 50 +
      parsed.minute;
    minsSincePIATYear -= drift;
    let totalDays = baseFantasyDaysInMonth.reduce((a, b) => a + b, 0);
    while (minsSincePIATYear < 0) {
      minsSincePIATYear += totalDays * 1200;
      parsed.year--;
    }
    let piatDay = Math.floor(minsSincePIATYear / 1200) + 1;
    let piatMinuteOfDay = minsSincePIATYear % 1200;
    let piatHour = Math.floor(piatMinuteOfDay / 50);
    let piatMinute = piatMinuteOfDay % 50;
    let acc = 0, piatMonthIdx = 0;
    while (piatMonthIdx < 12 && acc + baseFantasyDaysInMonth[piatMonthIdx] < piatDay) {
      acc += baseFantasyDaysInMonth[piatMonthIdx];
      piatMonthIdx++;
    }
    let piatDayOfMonth = piatDay - acc;
    return piatToUnix({
      day: piatDayOfMonth,
      monthIdx: piatMonthIdx,
      year: parsed.year,
      hour: piatHour,
      minute: piatMinute
    });
  }

  // --- Settings/expansion logic ---
  const POSITIONS = [
    { key: "center-top", txt: isGerman ? "Mitte, oben" : "Center, top" },
    { key: "top-left", txt: isGerman ? "Links oben" : "Top left" },
    { key: "top-right", txt: isGerman ? "Rechts oben" : "Top right" },
    { key: "bottom-left", txt: isGerman ? "Links unten" : "Bottom left" },
    { key: "bottom-right", txt: isGerman ? "Rechts unten" : "Bottom right" }
  ];
  const LS_KEY_POSITION = "phantom_isles_popup_position";
  const LS_KEY_CALC_EXPANDED = "phantom_isles_popup_calc_expanded";
  const LS_KEY_AUTO_CONVERT = "phantom_isles_autoconvert_enabled";
  function getSavedPosKey() { return localStorage.getItem(LS_KEY_POSITION) || "center-top"; }
  function setSavedPosKey(key) { localStorage.setItem(LS_KEY_POSITION, key); }
  function getSavedCalcExpanded() {
    const val = localStorage.getItem(LS_KEY_CALC_EXPANDED);
    return val === null ? false : val === "true";
  }
  function setSavedCalcExpanded(x) {
    localStorage.setItem(LS_KEY_CALC_EXPANDED, x ? "true" : "false");
  }
  function getSavedAutoConvert() {
    const val = localStorage.getItem(LS_KEY_AUTO_CONVERT);
    return val === null ? true : val === "true";
  }
  function setSavedAutoConvert(x) {
    localStorage.setItem(LS_KEY_AUTO_CONVERT, x ? "true" : "false");
  }
  function applyPopupPosition(container, posKey) {
    container.style.top = container.style.left = container.style.bottom = container.style.right = "";
    container.style.transform = "";
    switch (posKey) {
      case "center-top":
        container.style.top = "18px";
        container.style.left = "50%";
        container.style.transform = "translateX(-50%)";
        break;
      case "top-left":
        container.style.top = "18px";
        container.style.left = "18px";
        break;
      case "top-right":
        container.style.top = "18px";
        container.style.right = "18px";
        break;
      case "bottom-left":
        container.style.bottom = "18px";
        container.style.left = "18px";
        break;
      case "bottom-right":
        container.style.bottom = "18px";
        container.style.right = "18px";
        break;
      default:
        container.style.top = "18px";
        container.style.left = "50%";
        container.style.transform = "translateX(-50%)";
    }
  }

  function createPopupAndSwitch() {
    const container = document.createElement('div');
    container.id = 'phantom-isles-container';
    container.style.position = 'fixed';
    container.style.zIndex = '99999';
    container.style.fontFamily = 'monospace, sans-serif';
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';

    applyPopupPosition(container, getSavedPosKey());

    const gear = document.createElement('button');
    gear.textContent = "⚙️";
    gear.title = T.settings;
    gear.style.position = "absolute";
    gear.style.right = "2px";
    gear.style.top = "2px";
    gear.style.background = "transparent";
    gear.style.color = "#FFD790";
    gear.style.fontSize = "18px";
    gear.style.border = "none";
    gear.style.cursor = "pointer";
    gear.style.padding = "0 2px";
    gear.style.zIndex = "99999";

    const menu = document.createElement('div');
    menu.style.display = "none";
    menu.style.position = "absolute";
    menu.style.top = "24px";
    menu.style.right = "-4px";
    menu.style.background = "#1C262C";
    menu.style.color = "#FFD790";
    menu.style.border = "1px solid #444";
    menu.style.borderRadius = "8px";
    menu.style.boxShadow = "0 2px 6px rgba(0,0,0,.22)";
    menu.style.padding = "10px";
    menu.style.fontSize = "15px";
    menu.style.zIndex = "100000";
    menu.style.minWidth = "180px";

    const labelPos = document.createElement('div');
    labelPos.textContent = T.position;
    menu.appendChild(labelPos);

    POSITIONS.forEach(pos => {
      const btn = document.createElement('button');
      btn.textContent = pos.txt;
      btn.style.display = "block";
      btn.style.margin = "5px auto";
      btn.style.width = "90%";
      btn.style.fontFamily = 'inherit';
      btn.style.fontSize = "15px";
      btn.style.border = "none";
      btn.style.borderRadius = "6px";
      btn.style.background = "#223c5c";
      btn.style.color = "#FFD790";
      btn.style.padding = "4px";
      btn.style.cursor = "pointer";
      btn.onclick = () => {
        setSavedPosKey(pos.key);
        applyPopupPosition(container, pos.key);
        menu.style.display = "none";
      };
      menu.appendChild(btn);
    });

    // --- Auto convert setting toggle ---
    const autoDiv = document.createElement('div');
    autoDiv.style.marginTop = "8px";
    const autoToggle = document.createElement('input');
    autoToggle.type = "checkbox";
    autoToggle.checked = getSavedAutoConvert();
    autoToggle.id = "pitpiat_auto_toggle";
    autoToggle.style.marginRight = "5px";
    const autoLabel = document.createElement('label');
    autoLabel.textContent = `${T.autoConvert}: ${autoToggle.checked ? T.autoConvertOn : T.autoConvertOff}`;
    autoToggle.onchange = function () {
      setSavedAutoConvert(autoToggle.checked);
      autoLabel.textContent = `${T.autoConvert}: ${autoToggle.checked ? T.autoConvertOn : T.autoConvertOff}`;
      scanAndConvertPitPiatTimes(); // re-run to hide/show as needed
    };
    autoDiv.appendChild(autoToggle);
    autoDiv.appendChild(autoLabel);
    menu.appendChild(autoDiv);

    const closeBtn = document.createElement('button');
    closeBtn.textContent = T.close;
    closeBtn.style.display = "block";
    closeBtn.style.margin = "10px auto 0";
    closeBtn.style.width = "90%";
    closeBtn.style.fontFamily = 'inherit';
    closeBtn.style.fontSize = "15px";
    closeBtn.style.border = "none";
    closeBtn.style.borderRadius = "6px";
    closeBtn.style.background = "#222";
    closeBtn.style.color = "#FFD790";
    closeBtn.style.padding = "4px";
    closeBtn.style.cursor = "pointer";
    closeBtn.onclick = () => { menu.style.display = "none"; };
    menu.appendChild(closeBtn);

    gear.onclick = e => {
      menu.style.display = menu.style.display === "none" ? "block" : "none";
      e.stopPropagation();
    };
    document.body.addEventListener("click", () => { menu.style.display = "none"; });

    container.appendChild(gear);
    container.appendChild(menu);

    const popup = document.createElement('div');
    popup.id = 'phantom-isles-popup';
    popup.style.padding = '10px 18px';
    popup.style.background = 'rgba(0,22,40,0.97)';
    popup.style.color = '#FFD790';
    popup.style.fontSize = '16px';
    popup.style.borderRadius = '10px 10px 3px 3px';
    popup.style.boxShadow = '0 2px 12px 0 rgba(0,0,0,0.19)';
    popup.style.userSelect = 'text';
    popup.style.pointerEvents = 'auto';
    popup.style.textAlign = "center";
    popup.style.whiteSpace = "pre-line";
    popup.title = T.tooltipPIT;

    const btn = document.createElement('button');
    btn.textContent = T.btnPIT;
    btn.style.display = "block";
    btn.style.margin = "0 auto";
    btn.style.border = "none";
    btn.style.borderRadius = "0 0 10px 10px";
    btn.style.background = "#223c5c";
    btn.style.color = "#FFD790";
    btn.style.fontWeight = "bold";
    btn.style.fontFamily = 'monospace, sans-serif';
    btn.style.padding = "5px 15px";
    btn.style.cursor = "pointer";
    btn.style.boxShadow = "0 2px 8px 0 rgba(0,0,0,0.15)";
    btn.style.fontSize = "15px";
    btn.style.userSelect = "none";
    btn.style.pointerEvents = "auto";

    container.appendChild(popup);
    container.appendChild(btn);
    document.body.appendChild(container);

    return { container, popup, btn };
  }

  let mode = "pit";
  const { container, popup, btn } = createPopupAndSwitch();

  function updatePopup() {
    const now = Date.now();
    let shortname = "", pit = "";
    if (mode === "piat") {
      pit = unixToPhantomIslesTimePIAT(now, false);
      shortname = T.modePIAT;
      popup.title = T.tooltipPIAT;
      btn.textContent = T.btnPIAT;
    } else {
      pit = unixToPhantomIslesTimePIT(now, false);
      shortname = T.modePIT;
      popup.title = T.tooltipPIT;
      btn.textContent = T.btnPIT;
    }
    popup.textContent = `⏳ ${shortname}\n${pit}`;
  }
  btn.addEventListener('click', function () {
    mode = mode === "piat" ? "pit" : "piat";
    updatePopup();
  });
  setInterval(updatePopup, 1000);
  updatePopup();

  function addCalculatorSection(parent) {
    const toggleBtn = document.createElement('button');
    toggleBtn.textContent = T.expanded;
    toggleBtn.style.margin = "8px 0 0";
    toggleBtn.style.background = "#222";
    toggleBtn.style.color = "#FFD790";
    toggleBtn.style.fontWeight = "bold";
    toggleBtn.style.border = "none";
    toggleBtn.style.borderRadius = "7px";
    toggleBtn.style.padding = "4px 15px";
    toggleBtn.style.cursor = "pointer";
    toggleBtn.style.fontSize = "15px";
    toggleBtn.style.boxShadow = "0 1px 4px 0 rgba(0,0,0,0.15)";
    toggleBtn.style.display = "block";

    const containerDiv = document.createElement('div');
    containerDiv.style.display = getSavedCalcExpanded() ? "block" : "none";
    function setExpanded(expand) {
      containerDiv.style.display = expand ? "block" : "none";
      toggleBtn.textContent = expand ? T.collapsed : T.expanded;
      setSavedCalcExpanded(expand);
    }
    setExpanded(getSavedCalcExpanded());
    toggleBtn.onclick = () => setExpanded(containerDiv.style.display === "none");
    parent.appendChild(toggleBtn);
    parent.appendChild(containerDiv);

    addCalculator(containerDiv, popup);
    addReverseCalculator(containerDiv);
  }

  function addCalculator(container, popup) {
    const labelDiv = document.createElement('div');
    labelDiv.textContent = T.convertLabel;
    labelDiv.style.fontSize = "13px";
    labelDiv.style.color = "#FFD790";
    labelDiv.style.marginBottom = "4px";
    const calcDiv = document.createElement('div');
    calcDiv.style.marginTop = "12px";
    calcDiv.style.textAlign = "center";
    const input = document.createElement('input');
    input.type = 'datetime-local';
    input.style.fontFamily = 'monospace, sans-serif';
    input.style.fontSize = '15px';
    input.style.marginRight = '6px';
    input.placeholder = T.convertPlaceholder;
    const btnConvert = document.createElement('button');
    btnConvert.textContent = T.convertBtn;
    btnConvert.style.margin = '2px 0';
    btnConvert.style.background = "#146b81";
    btnConvert.style.color = "#FFD790";
    btnConvert.style.fontWeight = "bold";
    btnConvert.style.border = "none";
    btnConvert.style.borderRadius = "4px";
    btnConvert.style.padding = "4px 12px";
    btnConvert.style.cursor = "pointer";
    btnConvert.style.fontSize = "15px";
    btnConvert.style.boxShadow = "0 1px 3px 0 rgba(0,0,0,0.12)";
    const out = document.createElement('div');
    out.style.marginTop = "5px";
    out.style.fontFamily = 'monospace, sans-serif';
    out.style.fontSize = "15px";
    out.style.color = "#FFD790";
    btnConvert.onclick = function () {
      if (!input.value) {
        out.textContent = T.convertPick;
        return;
      }
      const t = new Date(input.value).getTime();
      let result = "";
      if (mode === "piat") {
        result = unixToPhantomIslesTimePIAT(t, false);
        out.textContent = T.convertResultPIAT + result;
      } else {
        result = unixToPhantomIslesTimePIT(t, false);
        out.textContent = T.convertResultPIT + result;
      }
    };
    calcDiv.appendChild(labelDiv);
    calcDiv.appendChild(input);
    calcDiv.appendChild(btnConvert);
    calcDiv.appendChild(out);
    container.appendChild(calcDiv);
  }

  function addReverseCalculator(container) {
    const reverseDiv = document.createElement('div');
    reverseDiv.style.marginTop = "10px";
    reverseDiv.style.textAlign = "center";
    const label = document.createElement('div');
    label.textContent = T.reverseLabel;
    label.style.fontSize = "13px";
    label.style.color = "#FFD790";
    label.style.marginBottom = "4px";
    reverseDiv.appendChild(label);
    const reverseInput = document.createElement('input');
    reverseInput.type = 'text';
    reverseInput.placeholder = T.reversePlaceholder;
    reverseInput.style.fontFamily = 'monospace, sans-serif';
    reverseInput.style.fontSize = '15px';
    reverseInput.style.marginRight = '6px';
    const reverseBtn = document.createElement('button');
    reverseBtn.textContent = T.reverseBtn;
    reverseBtn.style.background = "#446b30";
    reverseBtn.style.color = "#FFD790";
    reverseBtn.style.fontWeight = "bold";
    reverseBtn.style.border = "none";
    reverseBtn.style.borderRadius = "4px";
    reverseBtn.style.padding = "4px 12px";
    reverseBtn.style.cursor = "pointer";
    reverseBtn.style.fontSize = "15px";
    reverseBtn.style.boxShadow = "0 1px 3px 0 rgba(0,0,0,0.12)";
    const reverseOut = document.createElement('div');
    reverseOut.style.marginTop = "5px";
    reverseOut.style.fontFamily = 'monospace, sans-serif';
    reverseOut.style.fontSize = "15px";
    reverseOut.style.color = "#FFD790";
    reverseBtn.onclick = function () {
      let val = reverseInput.value.trim();
      let parsed = parsePITPIATinput(val);
      if (!parsed) {
        reverseOut.textContent = T.reverseParseFail;
        return;
      }
      if (mode === "piat") {
        let t = piatToUnix(parsed);
        let dt = new Date(t);
        reverseOut.textContent = T.reverseResult + dt.toLocaleString();
      } else {
        let t = pitToUnixViaPIAT(parsed);
        if (t == null) {
          reverseOut.textContent = T.reverseResultPITFail;
        } else {
          let dt = new Date(t);
          reverseOut.textContent = T.reverseResult + dt.toLocaleString();
        }
      }
    };
    reverseDiv.appendChild(reverseInput);
    reverseDiv.appendChild(reverseBtn);
    reverseDiv.appendChild(reverseOut);
    container.appendChild(reverseDiv);
  }

  addCalculatorSection(container);

  // --- Automatic time transformation toggle ---
  const pitOrPiatPattern = /(\d{1,2})\.?\s+([A-Za-zäöüÄÖÜ]+)\s+(\d{4})\s+(\d{1,2}):(\d{2})\s+\[(PIT|PIAT)\]/g;
  function convertPitPiatTextToLocal(matchAll) {
    matchAll.forEach(match => {
      let [full] = match;
      let dd = match[1], mon = match[2], yyyy = match[3], hh = match[4], mm = match[5], kind = match[6];
      let parsed = parsePITPIATinput(`${dd} ${mon} ${yyyy} ${hh.padStart(2,"0")}:${mm}`);
      if (!parsed) return;
      let t = kind === "PIAT" ? piatToUnix(parsed) : pitToUnixViaPIAT(parsed);
      let dt = new Date(t);
      let formatted = dt.toLocaleString() + (isGerman ? " (Echtzeit)" : " (local time)");
      document.querySelectorAll('body, body *').forEach(node => {
        if (node.childNodes && node.childNodes.length) {
          node.childNodes.forEach(child => {
            if (child.nodeType === 3 && child.textContent.includes(full)) {
              let newText = child.textContent.replace(full, formatted);
              let span = document.createElement("span");
              span.textContent = newText;
              span.title = full;
              child.parentNode.replaceChild(span, child);
            }
          });
        }
      });
    });
  }
  function scanAndConvertPitPiatTimes() {
    if (!getSavedAutoConvert()) return; // skip if disabled
    let allMatches = [];
    document.querySelectorAll('body, body *').forEach(node => {
      if (node.childNodes && node.childNodes.length) {
        node.childNodes.forEach(child => {
          if (child.nodeType === 3) {
            let txt = child.textContent;
            let re = RegExp(pitOrPiatPattern);
            let res = txt.matchAll(pitOrPiatPattern);
            Array.from(res).forEach(m => { m.input = txt; allMatches.push(m); });
          }
        });
      }
    });
    if (allMatches.length) {
      convertPitPiatTextToLocal(allMatches);
    }
  }
  scanAndConvertPitPiatTimes();
  const mo = new MutationObserver(() => { scanAndConvertPitPiatTimes(); });
  mo.observe(document.body, { childList: true, subtree: true, characterData: true });

})();