Torn War Stuff Enhanced Beta

Show travel status and hospital time and sort by hospital time on war page.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Torn War Stuff Enhanced Beta
// @namespace    namespace-beta
// @version      2.0-beta8
// @author       xentac
// @description  Show travel status and hospital time and sort by hospital time on war page.
// @license      MIT
// @match        https://www.torn.com/factions.php*
// @connect      api.torn.com
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';

  const n=new Set;const importCSS = async e=>{n.has(e)||(n.add(e),(d=>{const t=document.createElement("style");t.textContent=d,(document.head||document.documentElement).appendChild(t);})(e));};

  var LogLevel = ((LogLevel2) => {
    LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
    LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
    LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
    LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
    LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
    return LogLevel2;
  })(LogLevel || {});
  class Logger {
constructor(prefix = "", defaultLevel = 1, state = {}) {
      this.isPDA = false;
      this.colors = {
        debug: "#7f8c8d",
        info: "#3498db",
        warn: "#f39c12",
        error: "#e74c3c"
      };
      this.prefix = prefix;
      this.defaultLevel = defaultLevel;
      this.state = state;
      this.detectPDA();
    }
detectPDA() {
      if (typeof window !== "undefined") {
        if (window.flutter_inappwebview) {
          this.isPDA = true;
        }
        window.addEventListener("flutterInAppWebViewPlatformReady", () => {
          window.flutter_inappwebview.callHandler("isTornPDA").then((response) => {
            if (response?.isTornPDA) {
              this.isPDA = true;
            }
          }).catch(() => {
          });
        });
      }
    }
setLevel(level) {
      this.state.explicitLevel = level;
    }
getLevel() {
      return this.state.explicitLevel !== void 0 ? this.state.explicitLevel : this.defaultLevel;
    }
debug(...args) {
      if (this.getLevel() <= 0) {
        if (this.isPDA) {
          console.log(`${this.formatPrefix("DEBUG")}`, ...this.formatArgs(args));
        } else {
          console.log(
            `%c${this.formatPrefix("DEBUG")}`,
            `color: ${this.colors.debug}; font-weight: bold`,
            ...args
          );
        }
      }
    }
info(...args) {
      if (this.getLevel() <= 1) {
        if (this.isPDA) {
          console.info(`${this.formatPrefix("INFO")}`, ...this.formatArgs(args));
        } else {
          console.info(
            `%c${this.formatPrefix("INFO")}`,
            `color: ${this.colors.info}; font-weight: bold`,
            ...args
          );
        }
      }
    }
warn(...args) {
      if (this.getLevel() <= 2) {
        if (this.isPDA) {
          console.warn(`${this.formatPrefix("WARN")}`, ...this.formatArgs(args));
        } else {
          console.warn(
            `%c${this.formatPrefix("WARN")}`,
            `color: ${this.colors.warn}; font-weight: bold`,
            ...args
          );
        }
      }
    }
error(...args) {
      if (this.getLevel() <= 3) {
        if (this.isPDA) {
          console.error(
            `${this.formatPrefix("ERROR")}`,
            ...this.formatArgs(args)
          );
        } else {
          console.error(
            `%c${this.formatPrefix("ERROR")}`,
            `color: ${this.colors.error}; font-weight: bold`,
            ...args
          );
        }
      }
    }
group(label, collapsed = false) {
      if (this.getLevel() < 4) {
        if (collapsed) {
          console.groupCollapsed(this.formatPrefix(""), label);
        } else {
          console.group(this.formatPrefix(""), label);
        }
      }
    }
groupEnd() {
      if (this.getLevel() < 4) {
        console.groupEnd();
      }
    }
child(subPrefix) {
      const childPrefix = this.prefix ? `${this.prefix}:${subPrefix}` : subPrefix;
      return new Logger(childPrefix, this.defaultLevel, this.state);
    }
formatPrefix(level) {
      const prefix = this.prefix ? `[${this.prefix}]` : "";
      return level ? `${prefix} - [${level}]: ` : `${prefix}: `;
    }
formatArgs(args) {
      return args.map((arg) => {
        if (typeof arg === "object" && arg !== null) {
          try {
            return JSON.stringify(arg, null, 2);
          } catch {
            return String(arg);
          }
        }
        return arg;
      });
    }
  }
  const logger = new Logger(
    "TWSE",
    1
);
  const log$7 = logger.child("storage");
  class Storage {
constructor(prefix) {
      this.prefix = prefix;
    }
set(key, value, expireConfig) {
      try {
        const item = {
          value,
          expiration: expireConfig ? Date.now() + expireConfig.amount * (expireConfig.unit || 6e4) : null
        };
        localStorage.setItem(this.prefix + key, JSON.stringify(item));
      } catch (error) {
        log$7.error(`Error storing item '${key}':`, error);
      }
    }
get(key) {
      try {
        const itemStr = localStorage.getItem(this.prefix + key);
        if (!itemStr) {
          return null;
        }
        let item = null;
        try {
          item = JSON.parse(itemStr);
        } catch {
          item = null;
        }
        if (!item) {
          log$7.warn(`Key '${key}' has invalid JSON in it.`);
          this.remove(key);
          return null;
        }
        if (item.expiration && Date.now() > item.expiration) {
          this.remove(key);
          log$7.debug(`Key '${key}' has expired.`);
          return null;
        }
        return item.value;
      } catch (error) {
        log$7.error(`Error retrieving item '${key}':`, error);
        return null;
      }
    }
remove(key) {
      try {
        localStorage.removeItem(this.prefix + key);
      } catch (error) {
        log$7.error(`Error removing item '${key}':`, error);
      }
    }
has(key) {
      return this.get(key) !== null;
    }
clearAll() {
      try {
        Object.keys(localStorage).filter((key) => key.startsWith(this.prefix)).forEach((key) => {
          localStorage.removeItem(key);
        });
      } catch (error) {
        log$7.error("Error clearing storage:", error);
      }
    }
  }
  class Config {
    constructor(prefix = "twse-config-") {
      this.legacyPrefix = "xentac-torn_war_stuff_enhanced-";
      this.storage = new Storage(prefix);
      logger.setLevel(this.debug_logs ? LogLevel.DEBUG : LogLevel.INFO);
    }
get apiKey() {
      const key = this.storage.get(
        "apikey"
);
      if (key) {
        return key;
      }
      const legacyKey = localStorage.getItem(`${this.legacyPrefix}apikey`);
      if (legacyKey) {
        return legacyKey;
      }
      return "";
    }
set apiKey(val) {
      this.storage.set("apikey", val);
      localStorage.setItem(`${this.legacyPrefix}apikey`, val);
    }
get debug_logs() {
      return this.storage.get(
        "debug_logs"
) ?? false;
    }
    set debug_logs(val) {
      this.storage.set("debug_logs", val);
      logger.setLevel(val ? LogLevel.DEBUG : LogLevel.INFO);
    }
get war_sorting() {
      return this.storage.get(
        "war_sorting"
) ?? true;
    }
    set war_sorting(val) {
      this.storage.set("war_sorting", val);
    }
get bubble_position() {
      return this.storage.get(
        "bubble_position"
) ?? null;
    }
    set bubble_position(val) {
      if (val === null) {
        this.storage.remove(
          "bubble_position"
);
      } else {
        this.storage.set("bubble_position", val);
      }
    }
get bubble_minimized() {
      return this.storage.get(
        "bubble_minimized"
) ?? false;
    }
    set bubble_minimized(val) {
      this.storage.set("bubble_minimized", val);
    }
get bubble_enabled() {
      return this.storage.get(
        "bubble_enabled"
) ?? true;
    }
    set bubble_enabled(val) {
      this.storage.set("bubble_enabled", val);
    }
get copy_button_enabled() {
      return this.storage.get(
        "copy_button_enabled"
) ?? true;
    }
    set copy_button_enabled(val) {
      this.storage.set("copy_button_enabled", val);
    }
reset() {
      this.storage.remove(
        "debug_logs"
);
      this.storage.remove(
        "war_sorting"
);
      this.storage.remove(
        "bubble_position"
);
      this.storage.remove(
        "bubble_minimized"
);
      this.storage.remove(
        "bubble_enabled"
);
      this.storage.remove(
        "copy_button_enabled"
);
    }
  }
  const twseconfig = new Config();
  var StartTime = ((StartTime2) => {
    StartTime2[StartTime2["DocumentStart"] = 0] = "DocumentStart";
    StartTime2[StartTime2["DocumentBody"] = 1] = "DocumentBody";
    StartTime2[StartTime2["DocumentEnd"] = 2] = "DocumentEnd";
    return StartTime2;
  })(StartTime || {});
  const log$6 = logger.child("feature:key-manager");
  const KeyManagerFeature = {
    name: "Key Manager",
    description: "Allows the user to register their Torn API key via a Tampermonkey menu command",
    executionTime: StartTime.DocumentEnd,
    shouldRun() {
      return true;
    },
    run() {
      if (typeof GM_registerMenuCommand !== "undefined") {
        GM_registerMenuCommand("Torn War Stuff: Register Key", () => {
          const defaultPrompt = twseconfig.apiKey;
          const key = prompt("Please enter a Torn API Key:", defaultPrompt);
          if (key !== null) {
            const trimmedKey = key.trim();
            if (trimmedKey.length === 16 || trimmedKey === "") {
              twseconfig.apiKey = trimmedKey;
              log$6.info("Successfully updated API Key registration");
              alert("Torn API key registered successfully!");
            } else {
              alert("Invalid key! A Torn API key must be exactly 16 characters.");
            }
          }
        });
        log$6.debug("Tampermonkey menu command 'Register Key' initialized");
      } else {
        log$6.warn("GM_registerMenuCommand is not available in this context.");
      }
    }
  };
  const __vite_glob_0_0 = Object.freeze( Object.defineProperty({
    __proto__: null,
    default: KeyManagerFeature
  }, Symbol.toStringTag, { value: "Module" }));
  const log$5 = logger.child("dom");
  function waitForElement(selector, timeoutMs = 15e3) {
    return new Promise((resolve) => {
      const existing = document.querySelector(selector);
      if (existing) {
        return resolve(existing);
      }
      const observer = new MutationObserver((_2, obs) => {
        const el = document.querySelector(selector);
        if (el) {
          obs.disconnect();
          resolve(el);
        }
      });
      observer.observe(document.documentElement, {
        childList: true,
        subtree: true
      });
      if (timeoutMs > 0) {
        setTimeout(() => {
          observer.disconnect();
          log$5.debug(`Timeout waiting for element selector: '${selector}'`);
          resolve(null);
        }, timeoutMs);
      }
    });
  }
  function observeElement(target, callback, options = { childList: true, subtree: true }) {
    const observer = new MutationObserver((mutations, obs) => {
      if (!target.isConnected) {
        cleanup();
        return;
      }
      callback(mutations, obs);
    });
    const intervalId = setInterval(() => {
      if (!target.isConnected) {
        cleanup();
      }
    }, 1e4);
    function cleanup() {
      clearInterval(intervalId);
      observer.disconnect();
    }
    const originalDisconnect = observer.disconnect.bind(observer);
    observer.disconnect = () => {
      clearInterval(intervalId);
      originalDisconnect();
    };
    observer.observe(target, options);
    return observer;
  }
  function on_navigation(callback) {
    const nav = window.navigation;
    if (nav) {
      nav.addEventListener("currententrychange", callback);
      return () => {
        nav.removeEventListener("currententrychange", callback);
      };
    }
    const delayedCallback = () => {
      setTimeout(callback, 0);
    };
    window.addEventListener("popstate", delayedCallback);
    window.addEventListener("hashchange", delayedCallback);
    return () => {
      window.removeEventListener("popstate", delayedCallback);
      window.removeEventListener("hashchange", delayedCallback);
    };
  }
  const t$2 = globalThis, e$2 = t$2.ShadowRoot && (void 0 === t$2.ShadyCSS || t$2.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, s$2 = Symbol(), o$4 = new WeakMap();
  let n$3 = class n {
    constructor(t2, e2, o2) {
      if (this._$cssResult$ = true, o2 !== s$2) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
      this.cssText = t2, this.t = e2;
    }
    get styleSheet() {
      let t2 = this.o;
      const s2 = this.t;
      if (e$2 && void 0 === t2) {
        const e2 = void 0 !== s2 && 1 === s2.length;
        e2 && (t2 = o$4.get(s2)), void 0 === t2 && ((this.o = t2 = new CSSStyleSheet()).replaceSync(this.cssText), e2 && o$4.set(s2, t2));
      }
      return t2;
    }
    toString() {
      return this.cssText;
    }
  };
  const r$4 = (t2) => new n$3("string" == typeof t2 ? t2 : t2 + "", void 0, s$2), S$1 = (s2, o2) => {
    if (e$2) s2.adoptedStyleSheets = o2.map((t2) => t2 instanceof CSSStyleSheet ? t2 : t2.styleSheet);
    else for (const e2 of o2) {
      const o3 = document.createElement("style"), n3 = t$2.litNonce;
      void 0 !== n3 && o3.setAttribute("nonce", n3), o3.textContent = e2.cssText, s2.appendChild(o3);
    }
  }, c$2 = e$2 ? (t2) => t2 : (t2) => t2 instanceof CSSStyleSheet ? ((t3) => {
    let e2 = "";
    for (const s2 of t3.cssRules) e2 += s2.cssText;
    return r$4(e2);
  })(t2) : t2;
  const { is: i$2, defineProperty: e$1, getOwnPropertyDescriptor: h$1, getOwnPropertyNames: r$3, getOwnPropertySymbols: o$3, getPrototypeOf: n$2 } = Object, a$1 = globalThis, c$1 = a$1.trustedTypes, l$1 = c$1 ? c$1.emptyScript : "", p$1 = a$1.reactiveElementPolyfillSupport, d$1 = (t2, s2) => t2, u$1 = { toAttribute(t2, s2) {
    switch (s2) {
      case Boolean:
        t2 = t2 ? l$1 : null;
        break;
      case Object:
      case Array:
        t2 = null == t2 ? t2 : JSON.stringify(t2);
    }
    return t2;
  }, fromAttribute(t2, s2) {
    let i2 = t2;
    switch (s2) {
      case Boolean:
        i2 = null !== t2;
        break;
      case Number:
        i2 = null === t2 ? null : Number(t2);
        break;
      case Object:
      case Array:
        try {
          i2 = JSON.parse(t2);
        } catch (t3) {
          i2 = null;
        }
    }
    return i2;
  } }, f$1 = (t2, s2) => !i$2(t2, s2), b$1 = { attribute: true, type: String, converter: u$1, reflect: false, useDefault: false, hasChanged: f$1 };
  Symbol.metadata ??= Symbol("metadata"), a$1.litPropertyMetadata ??= new WeakMap();
  let y$1 = class y extends HTMLElement {
    static addInitializer(t2) {
      this._$Ei(), (this.l ??= []).push(t2);
    }
    static get observedAttributes() {
      return this.finalize(), this._$Eh && [...this._$Eh.keys()];
    }
    static createProperty(t2, s2 = b$1) {
      if (s2.state && (s2.attribute = false), this._$Ei(), this.prototype.hasOwnProperty(t2) && ((s2 = Object.create(s2)).wrapped = true), this.elementProperties.set(t2, s2), !s2.noAccessor) {
        const i2 = Symbol(), h2 = this.getPropertyDescriptor(t2, i2, s2);
        void 0 !== h2 && e$1(this.prototype, t2, h2);
      }
    }
    static getPropertyDescriptor(t2, s2, i2) {
      const { get: e2, set: r2 } = h$1(this.prototype, t2) ?? { get() {
        return this[s2];
      }, set(t3) {
        this[s2] = t3;
      } };
      return { get: e2, set(s3) {
        const h2 = e2?.call(this);
        r2?.call(this, s3), this.requestUpdate(t2, h2, i2);
      }, configurable: true, enumerable: true };
    }
    static getPropertyOptions(t2) {
      return this.elementProperties.get(t2) ?? b$1;
    }
    static _$Ei() {
      if (this.hasOwnProperty(d$1("elementProperties"))) return;
      const t2 = n$2(this);
      t2.finalize(), void 0 !== t2.l && (this.l = [...t2.l]), this.elementProperties = new Map(t2.elementProperties);
    }
    static finalize() {
      if (this.hasOwnProperty(d$1("finalized"))) return;
      if (this.finalized = true, this._$Ei(), this.hasOwnProperty(d$1("properties"))) {
        const t3 = this.properties, s2 = [...r$3(t3), ...o$3(t3)];
        for (const i2 of s2) this.createProperty(i2, t3[i2]);
      }
      const t2 = this[Symbol.metadata];
      if (null !== t2) {
        const s2 = litPropertyMetadata.get(t2);
        if (void 0 !== s2) for (const [t3, i2] of s2) this.elementProperties.set(t3, i2);
      }
      this._$Eh = new Map();
      for (const [t3, s2] of this.elementProperties) {
        const i2 = this._$Eu(t3, s2);
        void 0 !== i2 && this._$Eh.set(i2, t3);
      }
      this.elementStyles = this.finalizeStyles(this.styles);
    }
    static finalizeStyles(s2) {
      const i2 = [];
      if (Array.isArray(s2)) {
        const e2 = new Set(s2.flat(1 / 0).reverse());
        for (const s3 of e2) i2.unshift(c$2(s3));
      } else void 0 !== s2 && i2.push(c$2(s2));
      return i2;
    }
    static _$Eu(t2, s2) {
      const i2 = s2.attribute;
      return false === i2 ? void 0 : "string" == typeof i2 ? i2 : "string" == typeof t2 ? t2.toLowerCase() : void 0;
    }
    constructor() {
      super(), this._$Ep = void 0, this.isUpdatePending = false, this.hasUpdated = false, this._$Em = null, this._$Ev();
    }
    _$Ev() {
      this._$ES = new Promise((t2) => this.enableUpdating = t2), this._$AL = new Map(), this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((t2) => t2(this));
    }
    addController(t2) {
      (this._$EO ??= new Set()).add(t2), void 0 !== this.renderRoot && this.isConnected && t2.hostConnected?.();
    }
    removeController(t2) {
      this._$EO?.delete(t2);
    }
    _$E_() {
      const t2 = new Map(), s2 = this.constructor.elementProperties;
      for (const i2 of s2.keys()) this.hasOwnProperty(i2) && (t2.set(i2, this[i2]), delete this[i2]);
      t2.size > 0 && (this._$Ep = t2);
    }
    createRenderRoot() {
      const t2 = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
      return S$1(t2, this.constructor.elementStyles), t2;
    }
    connectedCallback() {
      this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(true), this._$EO?.forEach((t2) => t2.hostConnected?.());
    }
    enableUpdating(t2) {
    }
    disconnectedCallback() {
      this._$EO?.forEach((t2) => t2.hostDisconnected?.());
    }
    attributeChangedCallback(t2, s2, i2) {
      this._$AK(t2, i2);
    }
    _$ET(t2, s2) {
      const i2 = this.constructor.elementProperties.get(t2), e2 = this.constructor._$Eu(t2, i2);
      if (void 0 !== e2 && true === i2.reflect) {
        const h2 = (void 0 !== i2.converter?.toAttribute ? i2.converter : u$1).toAttribute(s2, i2.type);
        this._$Em = t2, null == h2 ? this.removeAttribute(e2) : this.setAttribute(e2, h2), this._$Em = null;
      }
    }
    _$AK(t2, s2) {
      const i2 = this.constructor, e2 = i2._$Eh.get(t2);
      if (void 0 !== e2 && this._$Em !== e2) {
        const t3 = i2.getPropertyOptions(e2), h2 = "function" == typeof t3.converter ? { fromAttribute: t3.converter } : void 0 !== t3.converter?.fromAttribute ? t3.converter : u$1;
        this._$Em = e2;
        const r2 = h2.fromAttribute(s2, t3.type);
        this[e2] = r2 ?? this._$Ej?.get(e2) ?? r2, this._$Em = null;
      }
    }
    requestUpdate(t2, s2, i2, e2 = false, h2) {
      if (void 0 !== t2) {
        const r2 = this.constructor;
        if (false === e2 && (h2 = this[t2]), i2 ??= r2.getPropertyOptions(t2), !((i2.hasChanged ?? f$1)(h2, s2) || i2.useDefault && i2.reflect && h2 === this._$Ej?.get(t2) && !this.hasAttribute(r2._$Eu(t2, i2)))) return;
        this.C(t2, s2, i2);
      }
      false === this.isUpdatePending && (this._$ES = this._$EP());
    }
    C(t2, s2, { useDefault: i2, reflect: e2, wrapped: h2 }, r2) {
      i2 && !(this._$Ej ??= new Map()).has(t2) && (this._$Ej.set(t2, r2 ?? s2 ?? this[t2]), true !== h2 || void 0 !== r2) || (this._$AL.has(t2) || (this.hasUpdated || i2 || (s2 = void 0), this._$AL.set(t2, s2)), true === e2 && this._$Em !== t2 && (this._$Eq ??= new Set()).add(t2));
    }
    async _$EP() {
      this.isUpdatePending = true;
      try {
        await this._$ES;
      } catch (t3) {
        Promise.reject(t3);
      }
      const t2 = this.scheduleUpdate();
      return null != t2 && await t2, !this.isUpdatePending;
    }
    scheduleUpdate() {
      return this.performUpdate();
    }
    performUpdate() {
      if (!this.isUpdatePending) return;
      if (!this.hasUpdated) {
        if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) {
          for (const [t4, s3] of this._$Ep) this[t4] = s3;
          this._$Ep = void 0;
        }
        const t3 = this.constructor.elementProperties;
        if (t3.size > 0) for (const [s3, i2] of t3) {
          const { wrapped: t4 } = i2, e2 = this[s3];
          true !== t4 || this._$AL.has(s3) || void 0 === e2 || this.C(s3, void 0, i2, e2);
        }
      }
      let t2 = false;
      const s2 = this._$AL;
      try {
        t2 = this.shouldUpdate(s2), t2 ? (this.willUpdate(s2), this._$EO?.forEach((t3) => t3.hostUpdate?.()), this.update(s2)) : this._$EM();
      } catch (s3) {
        throw t2 = false, this._$EM(), s3;
      }
      t2 && this._$AE(s2);
    }
    willUpdate(t2) {
    }
    _$AE(t2) {
      this._$EO?.forEach((t3) => t3.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = true, this.firstUpdated(t2)), this.updated(t2);
    }
    _$EM() {
      this._$AL = new Map(), this.isUpdatePending = false;
    }
    get updateComplete() {
      return this.getUpdateComplete();
    }
    getUpdateComplete() {
      return this._$ES;
    }
    shouldUpdate(t2) {
      return true;
    }
    update(t2) {
      this._$Eq &&= this._$Eq.forEach((t3) => this._$ET(t3, this[t3])), this._$EM();
    }
    updated(t2) {
    }
    firstUpdated(t2) {
    }
  };
  y$1.elementStyles = [], y$1.shadowRootOptions = { mode: "open" }, y$1[d$1("elementProperties")] = new Map(), y$1[d$1("finalized")] = new Map(), p$1?.({ ReactiveElement: y$1 }), (a$1.reactiveElementVersions ??= []).push("2.1.2");
  const t$1 = globalThis, i$1 = (t2) => t2, s$1 = t$1.trustedTypes, e = s$1 ? s$1.createPolicy("lit-html", { createHTML: (t2) => t2 }) : void 0, h = "$lit$", o$2 = `lit$${Math.random().toFixed(9).slice(2)}$`, n$1 = "?" + o$2, r$2 = `<${n$1}>`, l = document, c = () => l.createComment(""), a = (t2) => null === t2 || "object" != typeof t2 && "function" != typeof t2, u = Array.isArray, d = (t2) => u(t2) || "function" == typeof t2?.[Symbol.iterator], f = "[ 	\n\f\r]", v = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, _ = /-->/g, m = />/g, p = RegExp(`>|${f}(?:([^\\s"'>=/]+)(${f}*=${f}*(?:[^ 	
\f\r"'\`<>=]|("|')|))|$)`, "g"), g = /'/g, $ = /"/g, y2 = /^(?:script|style|textarea|title)$/i, x = (t2) => (i2, ...s2) => ({ _$litType$: t2, strings: i2, values: s2 }), b = x(1), E = Symbol.for("lit-noChange"), A = Symbol.for("lit-nothing"), C = new WeakMap(), P = l.createTreeWalker(l, 129);
  function V(t2, i2) {
    if (!u(t2) || !t2.hasOwnProperty("raw")) throw Error("invalid template strings array");
    return void 0 !== e ? e.createHTML(i2) : i2;
  }
  const N = (t2, i2) => {
    const s2 = t2.length - 1, e2 = [];
    let n3, l2 = 2 === i2 ? "<svg>" : 3 === i2 ? "<math>" : "", c2 = v;
    for (let i3 = 0; i3 < s2; i3++) {
      const s3 = t2[i3];
      let a2, u2, d2 = -1, f2 = 0;
      for (; f2 < s3.length && (c2.lastIndex = f2, u2 = c2.exec(s3), null !== u2); ) f2 = c2.lastIndex, c2 === v ? "!--" === u2[1] ? c2 = _ : void 0 !== u2[1] ? c2 = m : void 0 !== u2[2] ? (y2.test(u2[2]) && (n3 = RegExp("</" + u2[2], "g")), c2 = p) : void 0 !== u2[3] && (c2 = p) : c2 === p ? ">" === u2[0] ? (c2 = n3 ?? v, d2 = -1) : void 0 === u2[1] ? d2 = -2 : (d2 = c2.lastIndex - u2[2].length, a2 = u2[1], c2 = void 0 === u2[3] ? p : '"' === u2[3] ? $ : g) : c2 === $ || c2 === g ? c2 = p : c2 === _ || c2 === m ? c2 = v : (c2 = p, n3 = void 0);
      const x2 = c2 === p && t2[i3 + 1].startsWith("/>") ? " " : "";
      l2 += c2 === v ? s3 + r$2 : d2 >= 0 ? (e2.push(a2), s3.slice(0, d2) + h + s3.slice(d2) + o$2 + x2) : s3 + o$2 + (-2 === d2 ? i3 : x2);
    }
    return [V(t2, l2 + (t2[s2] || "<?>") + (2 === i2 ? "</svg>" : 3 === i2 ? "</math>" : "")), e2];
  };
  class S {
    constructor({ strings: t2, _$litType$: i2 }, e2) {
      let r2;
      this.parts = [];
      let l2 = 0, a2 = 0;
      const u2 = t2.length - 1, d2 = this.parts, [f2, v2] = N(t2, i2);
      if (this.el = S.createElement(f2, e2), P.currentNode = this.el.content, 2 === i2 || 3 === i2) {
        const t3 = this.el.content.firstChild;
        t3.replaceWith(...t3.childNodes);
      }
      for (; null !== (r2 = P.nextNode()) && d2.length < u2; ) {
        if (1 === r2.nodeType) {
          if (r2.hasAttributes()) for (const t3 of r2.getAttributeNames()) if (t3.endsWith(h)) {
            const i3 = v2[a2++], s2 = r2.getAttribute(t3).split(o$2), e3 = /([.?@])?(.*)/.exec(i3);
            d2.push({ type: 1, index: l2, name: e3[2], strings: s2, ctor: "." === e3[1] ? I : "?" === e3[1] ? L : "@" === e3[1] ? z : H }), r2.removeAttribute(t3);
          } else t3.startsWith(o$2) && (d2.push({ type: 6, index: l2 }), r2.removeAttribute(t3));
          if (y2.test(r2.tagName)) {
            const t3 = r2.textContent.split(o$2), i3 = t3.length - 1;
            if (i3 > 0) {
              r2.textContent = s$1 ? s$1.emptyScript : "";
              for (let s2 = 0; s2 < i3; s2++) r2.append(t3[s2], c()), P.nextNode(), d2.push({ type: 2, index: ++l2 });
              r2.append(t3[i3], c());
            }
          }
        } else if (8 === r2.nodeType) if (r2.data === n$1) d2.push({ type: 2, index: l2 });
        else {
          let t3 = -1;
          for (; -1 !== (t3 = r2.data.indexOf(o$2, t3 + 1)); ) d2.push({ type: 7, index: l2 }), t3 += o$2.length - 1;
        }
        l2++;
      }
    }
    static createElement(t2, i2) {
      const s2 = l.createElement("template");
      return s2.innerHTML = t2, s2;
    }
  }
  function M(t2, i2, s2 = t2, e2) {
    if (i2 === E) return i2;
    let h2 = void 0 !== e2 ? s2._$Co?.[e2] : s2._$Cl;
    const o2 = a(i2) ? void 0 : i2._$litDirective$;
    return h2?.constructor !== o2 && (h2?._$AO?.(false), void 0 === o2 ? h2 = void 0 : (h2 = new o2(t2), h2._$AT(t2, s2, e2)), void 0 !== e2 ? (s2._$Co ??= [])[e2] = h2 : s2._$Cl = h2), void 0 !== h2 && (i2 = M(t2, h2._$AS(t2, i2.values), h2, e2)), i2;
  }
  class R {
    constructor(t2, i2) {
      this._$AV = [], this._$AN = void 0, this._$AD = t2, this._$AM = i2;
    }
    get parentNode() {
      return this._$AM.parentNode;
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    u(t2) {
      const { el: { content: i2 }, parts: s2 } = this._$AD, e2 = (t2?.creationScope ?? l).importNode(i2, true);
      P.currentNode = e2;
      let h2 = P.nextNode(), o2 = 0, n3 = 0, r2 = s2[0];
      for (; void 0 !== r2; ) {
        if (o2 === r2.index) {
          let i3;
          2 === r2.type ? i3 = new k(h2, h2.nextSibling, this, t2) : 1 === r2.type ? i3 = new r2.ctor(h2, r2.name, r2.strings, this, t2) : 6 === r2.type && (i3 = new Z(h2, this, t2)), this._$AV.push(i3), r2 = s2[++n3];
        }
        o2 !== r2?.index && (h2 = P.nextNode(), o2++);
      }
      return P.currentNode = l, e2;
    }
    p(t2) {
      let i2 = 0;
      for (const s2 of this._$AV) void 0 !== s2 && (void 0 !== s2.strings ? (s2._$AI(t2, s2, i2), i2 += s2.strings.length - 2) : s2._$AI(t2[i2])), i2++;
    }
  }
  class k {
    get _$AU() {
      return this._$AM?._$AU ?? this._$Cv;
    }
    constructor(t2, i2, s2, e2) {
      this.type = 2, this._$AH = A, this._$AN = void 0, this._$AA = t2, this._$AB = i2, this._$AM = s2, this.options = e2, this._$Cv = e2?.isConnected ?? true;
    }
    get parentNode() {
      let t2 = this._$AA.parentNode;
      const i2 = this._$AM;
      return void 0 !== i2 && 11 === t2?.nodeType && (t2 = i2.parentNode), t2;
    }
    get startNode() {
      return this._$AA;
    }
    get endNode() {
      return this._$AB;
    }
    _$AI(t2, i2 = this) {
      t2 = M(this, t2, i2), a(t2) ? t2 === A || null == t2 || "" === t2 ? (this._$AH !== A && this._$AR(), this._$AH = A) : t2 !== this._$AH && t2 !== E && this._(t2) : void 0 !== t2._$litType$ ? this.$(t2) : void 0 !== t2.nodeType ? this.T(t2) : d(t2) ? this.k(t2) : this._(t2);
    }
    O(t2) {
      return this._$AA.parentNode.insertBefore(t2, this._$AB);
    }
    T(t2) {
      this._$AH !== t2 && (this._$AR(), this._$AH = this.O(t2));
    }
    _(t2) {
      this._$AH !== A && a(this._$AH) ? this._$AA.nextSibling.data = t2 : this.T(l.createTextNode(t2)), this._$AH = t2;
    }
    $(t2) {
      const { values: i2, _$litType$: s2 } = t2, e2 = "number" == typeof s2 ? this._$AC(t2) : (void 0 === s2.el && (s2.el = S.createElement(V(s2.h, s2.h[0]), this.options)), s2);
      if (this._$AH?._$AD === e2) this._$AH.p(i2);
      else {
        const t3 = new R(e2, this), s3 = t3.u(this.options);
        t3.p(i2), this.T(s3), this._$AH = t3;
      }
    }
    _$AC(t2) {
      let i2 = C.get(t2.strings);
      return void 0 === i2 && C.set(t2.strings, i2 = new S(t2)), i2;
    }
    k(t2) {
      u(this._$AH) || (this._$AH = [], this._$AR());
      const i2 = this._$AH;
      let s2, e2 = 0;
      for (const h2 of t2) e2 === i2.length ? i2.push(s2 = new k(this.O(c()), this.O(c()), this, this.options)) : s2 = i2[e2], s2._$AI(h2), e2++;
      e2 < i2.length && (this._$AR(s2 && s2._$AB.nextSibling, e2), i2.length = e2);
    }
    _$AR(t2 = this._$AA.nextSibling, s2) {
      for (this._$AP?.(false, true, s2); t2 !== this._$AB; ) {
        const s3 = i$1(t2).nextSibling;
        i$1(t2).remove(), t2 = s3;
      }
    }
    setConnected(t2) {
      void 0 === this._$AM && (this._$Cv = t2, this._$AP?.(t2));
    }
  }
  class H {
    get tagName() {
      return this.element.tagName;
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    constructor(t2, i2, s2, e2, h2) {
      this.type = 1, this._$AH = A, this._$AN = void 0, this.element = t2, this.name = i2, this._$AM = e2, this.options = h2, s2.length > 2 || "" !== s2[0] || "" !== s2[1] ? (this._$AH = Array(s2.length - 1).fill(new String()), this.strings = s2) : this._$AH = A;
    }
    _$AI(t2, i2 = this, s2, e2) {
      const h2 = this.strings;
      let o2 = false;
      if (void 0 === h2) t2 = M(this, t2, i2, 0), o2 = !a(t2) || t2 !== this._$AH && t2 !== E, o2 && (this._$AH = t2);
      else {
        const e3 = t2;
        let n3, r2;
        for (t2 = h2[0], n3 = 0; n3 < h2.length - 1; n3++) r2 = M(this, e3[s2 + n3], i2, n3), r2 === E && (r2 = this._$AH[n3]), o2 ||= !a(r2) || r2 !== this._$AH[n3], r2 === A ? t2 = A : t2 !== A && (t2 += (r2 ?? "") + h2[n3 + 1]), this._$AH[n3] = r2;
      }
      o2 && !e2 && this.j(t2);
    }
    j(t2) {
      t2 === A ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t2 ?? "");
    }
  }
  class I extends H {
    constructor() {
      super(...arguments), this.type = 3;
    }
    j(t2) {
      this.element[this.name] = t2 === A ? void 0 : t2;
    }
  }
  class L extends H {
    constructor() {
      super(...arguments), this.type = 4;
    }
    j(t2) {
      this.element.toggleAttribute(this.name, !!t2 && t2 !== A);
    }
  }
  class z extends H {
    constructor(t2, i2, s2, e2, h2) {
      super(t2, i2, s2, e2, h2), this.type = 5;
    }
    _$AI(t2, i2 = this) {
      if ((t2 = M(this, t2, i2, 0) ?? A) === E) return;
      const s2 = this._$AH, e2 = t2 === A && s2 !== A || t2.capture !== s2.capture || t2.once !== s2.once || t2.passive !== s2.passive, h2 = t2 !== A && (s2 === A || e2);
      e2 && this.element.removeEventListener(this.name, this, s2), h2 && this.element.addEventListener(this.name, this, t2), this._$AH = t2;
    }
    handleEvent(t2) {
      "function" == typeof this._$AH ? this._$AH.call(this.options?.host ?? this.element, t2) : this._$AH.handleEvent(t2);
    }
  }
  class Z {
    constructor(t2, i2, s2) {
      this.element = t2, this.type = 6, this._$AN = void 0, this._$AM = i2, this.options = s2;
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    _$AI(t2) {
      M(this, t2);
    }
  }
  const B = t$1.litHtmlPolyfillSupport;
  B?.(S, k), (t$1.litHtmlVersions ??= []).push("3.3.3");
  const D = (t2, i2, s2) => {
    const e2 = s2?.renderBefore ?? i2;
    let h2 = e2._$litPart$;
    if (void 0 === h2) {
      const t3 = s2?.renderBefore ?? null;
      e2._$litPart$ = h2 = new k(i2.insertBefore(c(), t3), t3, void 0, s2 ?? {});
    }
    return h2._$AI(t2), h2;
  };
  const s = globalThis;
  class i extends y$1 {
    constructor() {
      super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0;
    }
    createRenderRoot() {
      const t2 = super.createRenderRoot();
      return this.renderOptions.renderBefore ??= t2.firstChild, t2;
    }
    update(t2) {
      const r2 = this.render();
      this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t2), this._$Do = D(r2, this.renderRoot, this.renderOptions);
    }
    connectedCallback() {
      super.connectedCallback(), this._$Do?.setConnected(true);
    }
    disconnectedCallback() {
      super.disconnectedCallback(), this._$Do?.setConnected(false);
    }
    render() {
      return E;
    }
  }
  i._$litElement$ = true, i["finalized"] = true, s.litElementHydrateSupport?.({ LitElement: i });
  const o$1 = s.litElementPolyfillSupport;
  o$1?.({ LitElement: i });
  (s.litElementVersions ??= []).push("4.2.2");
  const t = (t2) => (e2, o2) => {
    void 0 !== o2 ? o2.addInitializer(() => {
      customElements.define(t2, e2);
    }) : customElements.define(t2, e2);
  };
  const o = { attribute: true, type: String, converter: u$1, reflect: false, hasChanged: f$1 }, r$1 = (t2 = o, e2, r2) => {
    const { kind: n3, metadata: i2 } = r2;
    let s2 = globalThis.litPropertyMetadata.get(i2);
    if (void 0 === s2 && globalThis.litPropertyMetadata.set(i2, s2 = new Map()), "setter" === n3 && ((t2 = Object.create(t2)).wrapped = true), s2.set(r2.name, t2), "accessor" === n3) {
      const { name: o2 } = r2;
      return { set(r3) {
        const n4 = e2.get.call(this);
        e2.set.call(this, r3), this.requestUpdate(o2, n4, t2, true, r3);
      }, init(e3) {
        return void 0 !== e3 && this.C(o2, void 0, t2, e3), e3;
      } };
    }
    if ("setter" === n3) {
      const { name: o2 } = r2;
      return function(r3) {
        const n4 = this[o2];
        e2.call(this, r3), this.requestUpdate(o2, n4, t2, true, r3);
      };
    }
    throw Error("Unsupported decorator location: " + n3);
  };
  function n2(t2) {
    return (e2, o2) => "object" == typeof o2 ? r$1(t2, e2, o2) : ((t3, e3, o3) => {
      const r2 = e3.hasOwnProperty(o3);
      return e3.constructor.createProperty(o3, t3), r2 ? Object.getOwnPropertyDescriptor(e3, o3) : void 0;
    })(t2, e2, o2);
  }
  function r(r2) {
    return n2({ ...r2, state: true, attribute: false });
  }
  var __defProp = Object.defineProperty;
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  var __decorateClass = (decorators, target, key, kind) => {
    var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
    for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
      if (decorator = decorators[i2])
        result = (kind ? decorator(target, key, result) : decorator(result)) || result;
    if (kind && result) __defProp(target, key, result);
    return result;
  };
  let TWSESettingsPanel = class extends i {
    constructor() {
      super(...arguments);
      this.apiKey = "";
      this.warSorting = true;
      this.bubbleEnabled = true;
      this.copyButtonEnabled = true;
      this.debugLogs = false;
      this.draftApiKey = "";
      this.draftWarSorting = true;
      this.draftBubbleEnabled = true;
      this.draftCopyButtonEnabled = true;
      this.draftDebugLogs = false;
      this.showSavedMessage = false;
    }
    createRenderRoot() {
      return this;
    }
    connectedCallback() {
      super.connectedCallback();
      this.resetDrafts();
    }
    willUpdate(changedProperties) {
      if (changedProperties.has("apiKey") || changedProperties.has("warSorting") || changedProperties.has("bubbleEnabled") || changedProperties.has("copyButtonEnabled") || changedProperties.has("debugLogs")) {
        this.resetDrafts();
      }
    }
    resetDrafts() {
      this.draftApiKey = this.apiKey;
      this.draftWarSorting = this.warSorting;
      this.draftBubbleEnabled = this.bubbleEnabled;
      this.draftCopyButtonEnabled = this.copyButtonEnabled;
      this.draftDebugLogs = this.debugLogs;
    }
    handleSave() {
      this.showSavedMessage = true;
      setTimeout(() => {
        this.showSavedMessage = false;
      }, 3e3);
      this.dispatchEvent(
        new CustomEvent("twse-save", {
          detail: {
            apiKey: this.draftApiKey,
            warSorting: this.draftWarSorting,
            bubbleEnabled: this.draftBubbleEnabled,
            copyButtonEnabled: this.draftCopyButtonEnabled,
            debugLogs: this.draftDebugLogs
          },
          bubbles: true,
          composed: true
        })
      );
    }
    handleReset() {
      if (confirm("Are you sure you want to reset all settings to defaults?")) {
        this.dispatchEvent(
          new CustomEvent("twse-reset", {
            bubbles: true,
            composed: true
          })
        );
      }
    }
    handleClearCache() {
      if (confirm("Are you sure you want to clear all TWSE war monitoring cache?")) {
        this.dispatchEvent(
          new CustomEvent("twse-clear-cache", {
            bubbles: true,
            composed: true
          })
        );
      }
    }
    onKeyInput(e2) {
      this.draftApiKey = e2.target.value.trim();
      this.showSavedMessage = false;
    }
    onWarSortingChange(e2) {
      this.draftWarSorting = e2.target.checked;
      this.showSavedMessage = false;
    }
    onBubbleEnabledChange(e2) {
      this.draftBubbleEnabled = e2.target.checked;
      this.showSavedMessage = false;
    }
    onCopyButtonEnabledChange(e2) {
      this.draftCopyButtonEnabled = e2.target.checked;
      this.showSavedMessage = false;
    }
    onDebugLogsChange(e2) {
      this.draftDebugLogs = e2.target.checked;
      this.showSavedMessage = false;
    }
    render() {
      return b`
      <details class="accordion cont-gray border-round twse-settings-details">
        <summary style="cursor: pointer; font-weight: bold; user-select: none;">
          Torn War Stuff Enhanced Settings
        </summary>

        <div style="margin-top: 15px;">
          <!-- API Key Section -->
          <div class="input-row">
            <label for="twse-api-key">Torn API Key:</label>
            <input
              id="twse-api-key"
              type="text"
              class="${this.apiKey ? "blur-mode" : ""}"
              placeholder="Paste 16-char API key here..."
              maxlength="16"
              .value=${this.draftApiKey}
              @input=${this.onKeyInput}
            />
            <div class="twse-api-explanation">
              <strong>Info:</strong> Provide a valid 16-character public API key to pull faction war information and real-time member statuses.
            </div>
          </div>

          <!-- Feature Toggles -->
          <h3>Feature Toggles:</h3>

          <!-- War sorting toggle -->
          <div class="input-row-inline">
            <input
              id="twse-war-sorting"
              type="checkbox"
              .checked=${this.draftWarSorting}
              @change=${this.onWarSortingChange}
            />
            <label for="twse-war-sorting">Enable War Page Sorting (automatically sorts okay/traveling/hospitalized members)</label>
          </div>

          <!-- Chain bubble toggle -->
          <div class="input-row-inline">
            <input
              id="twse-chain-bubble-toggle"
              type="checkbox"
              .checked=${this.draftBubbleEnabled}
              @change=${this.onBubbleEnabledChange}
            />
            <label for="twse-chain-bubble-toggle">Show Floating Chain Bubble (displays real-time countdown of your faction's chain)</label>
          </div>

          <!-- Copy button toggle -->
          <div class="input-row-inline">
            <input
              id="twse-copy-btn-toggle"
              type="checkbox"
              .checked=${this.draftCopyButtonEnabled}
              @change=${this.onCopyButtonEnabledChange}
            />
            <label for="twse-copy-btn-toggle">Enable "Copy Name [ID]" Button next to members</label>
          </div>

          <!-- Debug logs toggle -->
          <div class="input-row-inline">
            <input
              id="twse-debug-logs"
              type="checkbox"
              .checked=${this.draftDebugLogs}
              @change=${this.onDebugLogsChange}
            />
            <label for="twse-debug-logs">Enable Developer/Debug Logging</label>
          </div>

          <!-- Action Buttons -->
          <div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px; margin-top: 20px;">
            <button class="torn-btn btn-save" @click=${this.handleSave}>
              Save Settings
            </button>
            <button class="torn-btn btn-secondary" @click=${this.handleReset}>
              Reset to Defaults
            </button>
            <button class="torn-btn btn-secondary" @click=${this.handleClearCache}>
              Clear Cache
            </button>
            ${this.showSavedMessage ? b`<span style="color: #4CAF50; font-weight: bold; margin-left: 10px;">✓ Saved!</span>` : ""}
          </div>
        </div>
      </details>
    `;
    }
  };
  __decorateClass([
    n2({ type: String })
  ], TWSESettingsPanel.prototype, "apiKey", 2);
  __decorateClass([
    n2({ type: Boolean })
  ], TWSESettingsPanel.prototype, "warSorting", 2);
  __decorateClass([
    n2({ type: Boolean })
  ], TWSESettingsPanel.prototype, "bubbleEnabled", 2);
  __decorateClass([
    n2({ type: Boolean })
  ], TWSESettingsPanel.prototype, "copyButtonEnabled", 2);
  __decorateClass([
    n2({ type: Boolean })
  ], TWSESettingsPanel.prototype, "debugLogs", 2);
  __decorateClass([
    r()
  ], TWSESettingsPanel.prototype, "draftApiKey", 2);
  __decorateClass([
    r()
  ], TWSESettingsPanel.prototype, "draftWarSorting", 2);
  __decorateClass([
    r()
  ], TWSESettingsPanel.prototype, "draftBubbleEnabled", 2);
  __decorateClass([
    r()
  ], TWSESettingsPanel.prototype, "draftCopyButtonEnabled", 2);
  __decorateClass([
    r()
  ], TWSESettingsPanel.prototype, "draftDebugLogs", 2);
  __decorateClass([
    r()
  ], TWSESettingsPanel.prototype, "showSavedMessage", 2);
  TWSESettingsPanel = __decorateClass([
    t("twse-settings-panel")
  ], TWSESettingsPanel);
  const log$4 = logger.child("feature:settings");
  const SettingsFeature = {
    name: "Settings",
    description: "Renders and handles the settings panel at the bottom of the faction page",
    executionTime: StartTime.DocumentEnd,
    shouldRun() {
      return window.location.href.includes("factions.php");
    },
    async run() {
      const factionsContainer = await waitForElement("#factions");
      if (!factionsContainer) {
        log$4.warn("Failed to find #factions element to append settings panel");
        return;
      }
      const panel = document.createElement("twse-settings-panel");
      panel.apiKey = twseconfig.apiKey;
      panel.warSorting = twseconfig.war_sorting;
      panel.bubbleEnabled = twseconfig.bubble_enabled;
      panel.copyButtonEnabled = twseconfig.copy_button_enabled;
      panel.debugLogs = twseconfig.debug_logs;
      panel.addEventListener("twse-save", (e2) => {
        const detail = e2.detail;
        twseconfig.apiKey = detail.apiKey;
        twseconfig.war_sorting = detail.warSorting;
        twseconfig.bubble_enabled = detail.bubbleEnabled;
        twseconfig.copy_button_enabled = detail.copyButtonEnabled;
        twseconfig.debug_logs = detail.debugLogs;
        log$4.info("Settings saved successfully");
        window.dispatchEvent(new CustomEvent("twse-config-updated"));
      });
      panel.addEventListener("twse-reset", () => {
        twseconfig.reset();
        panel.apiKey = twseconfig.apiKey;
        panel.warSorting = twseconfig.war_sorting;
        panel.bubbleEnabled = twseconfig.bubble_enabled;
        panel.copyButtonEnabled = twseconfig.copy_button_enabled;
        panel.debugLogs = twseconfig.debug_logs;
        log$4.info("Settings reset to defaults");
        window.dispatchEvent(new CustomEvent("twse-config-updated"));
      });
      panel.addEventListener("twse-clear-cache", () => {
        log$4.info("Settings cleared caching successfully");
        window.dispatchEvent(new CustomEvent("twse-clear-cache"));
      });
      factionsContainer.appendChild(panel);
      log$4.debug("Settings panel successfully appended to #factions container");
    }
  };
  const __vite_glob_0_1 = Object.freeze( Object.defineProperty({
    __proto__: null,
    default: SettingsFeature
  }, Symbol.toStringTag, { value: "Module" }));
  const log$3 = logger.child("api");
  class TornApiClient {
    constructor() {
      this.baseUrl = "https://api.torn.com/faction/";
    }
async fetchFactionData(factionId) {
      const tornpdakey = "###PDA-APIKEY###";
      let key = twseconfig.apiKey;
      if (!tornpdakey.startsWith("###PDA")) {
        key = tornpdakey;
      }
      if (!key || key.length !== 16) {
        log$3.warn("Torn API key is invalid or not set. Skipping API request.");
        return null;
      }
      const url = `${this.baseUrl}${factionId}?selections=basic,chain&key=${key}&comment=TornWarStuffEnhanced`;
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTP Error status: ${response.status}`);
        }
        const data = await response.json();
        if (data.error) {
          log$3.error(
            `Torn API returned error code ${data.error.code}: ${data.error.error}`
          );
          return data;
        }
        return data;
      } catch (e2) {
        log$3.error(
          `Network or parse error fetching faction ${factionId} data:`,
          e2
        );
        return null;
      }
    }
isUnrecoverableError(errorCode) {
      const unrecoverable = [0, 1, 2, 3, 4, 6, 7, 10, 12, 13, 14, 16, 18, 21];
      return unrecoverable.includes(errorCode);
    }
isRateLimitError(errorCode) {
      const rateLimits = [5, 8, 9];
      return rateLimits.includes(errorCode);
    }
  }
  const tornApi = new TornApiClient();
  const log$2 = logger.child("cache");
  class FactionCache {
    constructor() {
      this.prefix = "xentac-torn_war_stuff_enhanced-status-";
      this.ttlMs = 1e4;
    }

get(factionId) {
      try {
        const key = `${this.prefix}${factionId}`;
        const cacheStr = localStorage.getItem(key);
        if (!cacheStr) {
          return null;
        }
        const parsed = JSON.parse(cacheStr);
        if (!parsed || typeof parsed.timestamp !== "number" || !parsed.status) {
          this.remove(factionId);
          return null;
        }
        const now = Date.now();
        if (now - parsed.timestamp > this.ttlMs) {
          this.remove(factionId);
          return null;
        }
        return parsed.status;
      } catch (e2) {
        log$2.error(`Error reading cached status for faction ${factionId}:`, e2);
        this.remove(factionId);
        return null;
      }
    }
set(factionId, status) {
      try {
        const key = `${this.prefix}${factionId}`;
        const cacheItem = {
          timestamp: Date.now(),
          status
        };
        localStorage.setItem(key, JSON.stringify(cacheItem));
      } catch (e2) {
        log$2.error(`Error caching status for faction ${factionId}:`, e2);
      }
    }
remove(factionId) {
      try {
        const key = `${this.prefix}${factionId}`;
        localStorage.removeItem(key);
      } catch (e2) {
        log$2.error(`Error removing cached status for faction ${factionId}:`, e2);
      }
    }
cleanExpired() {
      try {
        const now = Date.now();
        let cleanedCount = 0;
        for (let i2 = 0; i2 < localStorage.length; i2++) {
          const key = localStorage.key(i2);
          if (!key || !key.startsWith(this.prefix)) {
            continue;
          }
          const value = localStorage.getItem(key);
          if (!value) {
            continue;
          }
          try {
            const parsed = JSON.parse(value);
            if (!parsed || now - parsed.timestamp > this.ttlMs) {
              localStorage.removeItem(key);
              cleanedCount++;
              i2--;
            }
          } catch {
            localStorage.removeItem(key);
            cleanedCount++;
            i2--;
          }
        }
        if (cleanedCount > 0) {
          log$2.info(`Cleaned ${cleanedCount} expired cached statuses`);
        }
      } catch (e2) {
        log$2.error("Error sweeping expired cached statuses:", e2);
      }
    }
clearAll() {
      try {
        const keysToRemove = [];
        for (let i2 = 0; i2 < localStorage.length; i2++) {
          const key = localStorage.key(i2);
          if (key?.startsWith(this.prefix)) {
            keysToRemove.push(key);
          }
        }
        keysToRemove.forEach((key) => {
          localStorage.removeItem(key);
        });
        log$2.info(`Cleared all cached faction statuses`);
      } catch (e2) {
        log$2.error("Error clearing cached statuses:", e2);
      }
    }
  }
  const factionCache = new FactionCache();
  function getCurrentTimeSec() {
    const w = window;
    if (typeof w.getCurrentTimestamp === "function") {
      try {
        return w.getCurrentTimestamp() / 1e3;
      } catch (_e) {
      }
    }
    return Date.now() / 1e3;
  }
  function pad_with_zeros(n3) {
    if (n3 < 10) {
      return `0${n3}`;
    }
    return String(n3);
  }
  function calc_delta(delta, include_seconds = true, pad_hour = true) {
    const s2 = Math.floor(delta % 60);
    const m2 = Math.floor(delta / 60 % 60);
    const h2 = Math.floor(delta / 60 / 60);
    const hour_minute = `${pad_hour ? pad_with_zeros(h2) : h2}:${pad_with_zeros(m2)}`;
    return hour_minute + (include_seconds ? `:${pad_with_zeros(s2)}` : "");
  }
  function formatChainTimeout(seconds) {
    const isNegative = seconds < 0;
    const absSeconds = Math.abs(seconds);
    const m2 = Math.floor(absSeconds / 60);
    const s2 = Math.floor(absSeconds % 60);
    return `${isNegative ? "-" : ""}${m2}:${pad_with_zeros(s2)}`;
  }
  function formatChainCooldown(seconds) {
    if (seconds <= 0) return "0:00";
    const s2 = Math.floor(seconds % 60);
    const m2 = Math.floor(seconds / 60 % 60);
    const h2 = Math.floor(seconds / 3600 % 24);
    const d2 = Math.floor(seconds / 86400);
    if (d2 > 0) return `${d2}d${h2}h`;
    if (h2 > 0) return `${h2}h${m2}m`;
    if (m2 >= 10) return `${m2}m`;
    return `${m2}:${pad_with_zeros(s2)}`;
  }
  const DEST_TABLE = new Map([
    ["mexico", "MX"],
    ["cayman islands", "CI"],
    ["canada", "CA"],
    ["hawaii", "HI"],
    ["united kingdom", "UK"],
    ["argentina", "AR"],
    ["switzerland", "SW"],
    ["japan", "JP"],
    ["china", "CN"],
    ["uae", "UAE"],
    ["south africa", "SA"],
    ["torn", "TC"]
  ]);
  function shorten_destination(dest) {
    return DEST_TABLE.get(dest.toLowerCase().trim()) ?? dest;
  }
  const TRAVELING_REGEX = /Traveling from ([\S ]+) to ([\S ]+)/;
  function extract_destinations_from_description(description) {
    if (!description.startsWith("Traveling from")) {
      return null;
    }
    const match = TRAVELING_REGEX.exec(description);
    if (!match) {
      return null;
    }
    return {
      from: shorten_destination(match[1]),
      to: shorten_destination(match[2])
    };
  }
  const stylesCss = ".members-list li:has(div.status[data-twse-highlight=true]){background-color:#99eb99!important}.members-list li:has(div.status[data-twse-status-differs=true]){background-color:#c4974c!important}.members-list div.status[data-twse-traveling=true]:after{color:#696026!important}:root .dark-mode .members-list li:has(div.status[data-twse-highlight=true]){background-color:#446944!important}:root .dark-mode .members-list li:has(div.status[data-twse-status-differs=true]){background-color:#795315!important}:root .dark-mode .members-list div.status[data-twse-traveling=true]:after{color:#ffed76!important}.members-list div.status{position:relative!important;color:transparent!important}.members-list div.status:after{content:var(--twse-content);position:absolute;top:0;left:0;width:calc(100% - 10px);height:100%;background:inherit;display:flex;right:10px;justify-content:flex-end;align-items:center;white-space:nowrap!important}.members-list .ok.status:after{color:var(--user-status-green-color)}.members-list .not-ok.status:after{color:var(--user-status-red-color)}.members-list .abroad.status:after,.members-list .traveling.status:after{color:var(--user-status-blue-color)}.twse-sort-toggle-container{position:absolute;left:10px;display:inline-flex;align-items:center}.twse-sort-toggle-label{display:inline-flex;align-items:center;gap:6px;cursor:pointer;color:#999;font-size:13px;-webkit-user-select:none;user-select:none}.twse-sort-toggle-checkbox{cursor:pointer;margin:0;width:13px;height:13px}.members-list li .member{position:relative!important;display:flex!important;align-items:center}.twse-copy-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:4px;color:#888;transition:color .15s,background-color .15s,transform .1s;border-radius:4px;z-index:10}.twse-copy-btn:hover{color:#333;background-color:#0000000d}:root .dark-mode .twse-copy-btn:hover{color:#fff;background-color:#ffffff26}.twse-copy-btn:active{transform:translateY(-50%) scale(.9)}.twse-copy-btn.success{color:#494!important}:root .dark-mode .twse-copy-btn.success{color:#69eb69!important}.twse-chain-bubble{position:fixed;bottom:100px;right:20px;z-index:9999;background:#1e1e1ed9;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:6px 10px;box-shadow:0 8px 32px #0000005e;color:#e0e0e0;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:11px;line-height:1.5;display:flex;flex-direction:column;transition:opacity .3s ease,transform .3s ease;min-width:100px;pointer-events:auto;cursor:grab;user-select:none;-webkit-user-select:none}.twse-chain-bubble.hidden{opacity:0;transform:translateY(10px);pointer-events:none}.twse-chain-body{display:flex;flex-direction:column;gap:4px;width:100%}.twse-chain-tag,.twse-chain-mult{display:none}.twse-chain-row{display:flex;justify-content:space-between;align-items:center;gap:12px}.twse-chain-stats{display:flex;align-items:center;gap:6px;width:100%}.twse-chain-count{font-weight:600;color:#fff}.twse-chain-timer{margin-left:auto;font-family:monospace;font-weight:700;padding:2px 6px;border-radius:4px;background:#0000004d}.twse-chain-timer.okay{color:#69eb69}.twse-chain-timer.cooldown{color:#64b5f6;background:#64b5f626}.twse-chain-count.cooldown{color:#64b5f6}.twse-chain-timer.negative{color:#ff5252}.twse-chain-timer.urgent{color:#ff5252;background:#ff525226;animation:twse-pulse 1s infinite alternate}@keyframes twse-pulse{0%{box-shadow:0 0 2px #ff525266}to{box-shadow:0 0 8px #ff5252cc}}body.twse-copy-disabled .twse-copy-btn,body.twse-bubble-disabled #twse-chain-bubble{display:none!important}body{--twse-bg-color: #f0f0f0;--twse-alt-bg-color: #fff;--twse-border-color: #ccc;--twse-input-color: #333;--twse-text-color: #000;--twse-hover-color: #ddd;--twse-glow-color: #4caf50;--twse-success-color: #4caf50}:root .dark-mode{--twse-bg-color: #333;--twse-alt-bg-color: #383838;--twse-border-color: #444;--twse-input-color: #ccc;--twse-text-color: #ccc;--twse-hover-color: #555;--twse-glow-color: #4caf50;--twse-success-color: #4caf50}twse-settings-panel{display:block;margin-top:20px;clear:both}twse-settings-panel .accordion{margin:10px 0;padding:15px;background-color:var(--twse-bg-color);border:1px solid var(--twse-border-color);border-radius:5px;color:var(--twse-text-color);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}twse-settings-panel .accordion.glow{border-color:var(--twse-glow-color);box-shadow:0 0 8px #4caf5080}twse-settings-panel .input-row{display:flex;flex-direction:column;gap:5px;margin-bottom:15px}twse-settings-panel .input-row-inline{display:flex;align-items:center;gap:10px;margin-bottom:15px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}twse-settings-panel .input-row-inline input[type=checkbox]{cursor:pointer;width:14px;height:14px;margin:0}twse-settings-panel .input-row-inline label{cursor:pointer;line-height:1.4}twse-settings-panel .blur-mode{filter:blur(4px);transition:filter .2s ease}twse-settings-panel .blur-mode:hover,twse-settings-panel .blur-mode:focus{filter:blur(0)}twse-settings-panel input[type=text]{box-sizing:border-box;text-align:left;vertical-align:top;width:250px;height:34px;margin-right:8px;padding:8px 10px;line-height:14px;display:inline-block;border:1px solid var(--twse-border-color);border-radius:5px;background-color:var(--twse-alt-bg-color);color:var(--twse-text-color);outline:none}twse-settings-panel input[type=text]:focus{border-color:var(--twse-glow-color)}twse-settings-panel .twse-api-explanation{background-color:var(--twse-alt-bg-color);border:1px solid var(--twse-border-color);border-radius:8px;color:var(--twse-text-color);margin-top:5px;margin-bottom:5px;padding:10px 14px;font-size:12px;line-height:1.4;max-width:600px}twse-settings-panel h3{margin:20px 0 12px;font-size:14px;font-weight:700;border-bottom:1px solid var(--twse-border-color);padding-bottom:6px}";
  importCSS(stylesCss);
  const log$1 = logger.child("feature:war-monitor");
  const TRAVELING = "data-twse-traveling";
  const HIGHLIGHT = "data-twse-highlight";
  const STATUS_DIFFERS = "data-twse-status-differs";
  function shouldRunMonitor() {
    if (!window.location.href.includes("factions.php")) {
      return false;
    }
    const hash = window.location.hash || "";
    if (!hash.startsWith("#/war/")) {
      return false;
    }
    return true;
  }
  const WarMonitorFeature = {
    name: "War Monitor",
    description: "Monitors active Faction wars, retrieves real-time member statuses, and decorates rows",
    executionTime: StartTime.DocumentEnd,
    shouldRun() {
      return window.location.href.includes("factions.php");
    },
    async run() {
      let active = false;
      let stopMonitor = null;
      const startMonitor = async () => {
        if (active) return;
        active = true;
        factionCache.cleanExpired();
        const syncBodyClasses = () => {
          document.body.classList.toggle(
            "twse-copy-disabled",
            !twseconfig.copy_button_enabled
          );
          document.body.classList.toggle(
            "twse-bubble-disabled",
            !twseconfig.bubble_enabled
          );
        };
        syncBodyClasses();
        let running = true;
        let foundWar = false;
        let pageVisible = !document.hidden;
        let everSorted = false;
        let ffscouterSortingDeferred = false;
        const memberStatus = new Map();
        const memberLis = new Map();
        const deferredWrites = [];
        const deferredStyles = [];
        let lastRequestTime = 0;
        const minTimeBetweenRequestsMs = 1e4;
        const activeChains = new Map();
        const onConfigUpdated = () => {
          syncBodyClasses();
          const checkbox = document.querySelector(
            "#twse-war-sort-checkbox"
          );
          if (checkbox) {
            checkbox.checked = twseconfig.war_sorting;
          }
        };
        window.addEventListener("twse-config-updated", onConfigUpdated);
        const onClearCache = () => {
          log$1.info("Received twse-clear-cache event. Purging all caches.");
          memberStatus.clear();
          factionCache.clearAll();
          activeChains.clear();
          updateStatuses();
        };
        window.addEventListener("twse-clear-cache", onClearCache);
        let bubbleContainer = document.getElementById(
          "twse-chain-bubble"
        );
        if (!bubbleContainer) {
          bubbleContainer = document.createElement("div");
          bubbleContainer.id = "twse-chain-bubble";
          bubbleContainer.className = "twse-chain-bubble hidden";
          document.body.appendChild(bubbleContainer);
        }
        if (bubbleContainer && !bubbleContainer.querySelector(".twse-chain-body")) {
          bubbleContainer.innerHTML = `<div class="twse-chain-body"></div>`;
        }
        const getBubbleRect = () => {
          if (bubbleContainer && typeof bubbleContainer.getBoundingClientRect === "function") {
            const r2 = bubbleContainer.getBoundingClientRect();
            return {
              left: r2.left ?? 0,
              top: r2.top ?? 0,
              width: r2.width || 170,
              height: r2.height || 60
            };
          }
          return { left: 0, top: 0, width: 170, height: 60 };
        };
        const clampToScreen = () => {
          if (!bubbleContainer) return;
          const rect = getBubbleRect();
          const w = rect.width;
          const h2 = rect.height;
          const currentLeft = parseFloat(bubbleContainer.style.left);
          const currentTop = parseFloat(bubbleContainer.style.top);
          if (!Number.isNaN(currentLeft) && !Number.isNaN(currentTop)) {
            const maxLeft = window.innerWidth - w;
            const maxTop = window.innerHeight - h2;
            bubbleContainer.style.left = `${Math.max(0, Math.min(currentLeft, maxLeft))}px`;
            bubbleContainer.style.top = `${Math.max(0, Math.min(currentTop, maxTop))}px`;
          }
        };
        window.addEventListener("resize", clampToScreen, { passive: true });
        if (bubbleContainer) {
          const savedPos = twseconfig.bubble_position;
          if (savedPos) {
            bubbleContainer.style.bottom = "auto";
            bubbleContainer.style.right = "auto";
            bubbleContainer.style.left = `${savedPos.left}px`;
            bubbleContainer.style.top = `${savedPos.top}px`;
            setTimeout(clampToScreen, 0);
          }
          let isDragging = false;
          let startX = 0;
          let startY = 0;
          let initialX = 0;
          let initialY = 0;
          const dragStart = (e2) => {
            isDragging = true;
            const isTouch = e2.type === "touchstart";
            const touchEvent = e2;
            const mouseEvent = e2;
            const clientX = isTouch && touchEvent.touches && touchEvent.touches.length > 0 ? touchEvent.touches[0].clientX : mouseEvent.clientX;
            const clientY = isTouch && touchEvent.touches && touchEvent.touches.length > 0 ? touchEvent.touches[0].clientY : mouseEvent.clientY;
            startX = clientX;
            startY = clientY;
            if (bubbleContainer) {
              const rect = getBubbleRect();
              initialX = rect.left;
              initialY = rect.top;
              bubbleContainer.style.transition = "none";
              bubbleContainer.style.cursor = "grabbing";
            }
            if (!isTouch && e2.cancelable) {
              e2.preventDefault();
            }
            window.getSelection()?.removeAllRanges();
            document.addEventListener("mousemove", dragMove);
            document.addEventListener("touchmove", dragMove, { passive: false });
            document.addEventListener("mouseup", dragEnd);
            document.addEventListener("touchend", dragEnd);
          };
          const dragMove = (e2) => {
            if (!isDragging || !bubbleContainer) return;
            if (e2.cancelable) {
              e2.preventDefault();
            }
            const isTouch = e2.type === "touchmove";
            const touchEvent = e2;
            const mouseEvent = e2;
            const clientX = isTouch && touchEvent.touches && touchEvent.touches.length > 0 ? touchEvent.touches[0].clientX : mouseEvent.clientX;
            const clientY = isTouch && touchEvent.touches && touchEvent.touches.length > 0 ? touchEvent.touches[0].clientY : mouseEvent.clientY;
            const dx = clientX - startX;
            const dy = clientY - startY;
            const rect = getBubbleRect();
            const w = rect.width;
            const h2 = rect.height;
            let newLeft = initialX + dx;
            let newTop = initialY + dy;
            const maxLeft = window.innerWidth - w;
            const maxTop = window.innerHeight - h2;
            newLeft = Math.max(0, Math.min(newLeft, maxLeft));
            newTop = Math.max(0, Math.min(newTop, maxTop));
            bubbleContainer.style.bottom = "auto";
            bubbleContainer.style.right = "auto";
            bubbleContainer.style.left = `${newLeft}px`;
            bubbleContainer.style.top = `${newTop}px`;
          };
          const dragEnd = () => {
            isDragging = false;
            if (bubbleContainer) {
              bubbleContainer.style.cursor = "grab";
              const left = parseFloat(bubbleContainer.style.left) || 0;
              const top = parseFloat(bubbleContainer.style.top) || 0;
              twseconfig.bubble_position = { left, top };
            }
            document.removeEventListener("mousemove", dragMove);
            document.removeEventListener("touchmove", dragMove);
            document.removeEventListener("mouseup", dragEnd);
            document.removeEventListener("touchend", dragEnd);
          };
          bubbleContainer.addEventListener("mousedown", dragStart);
          bubbleContainer.addEventListener("touchstart", dragStart, {
            passive: false
          });
        }
        const onVisibilityChange = () => {
          pageVisible = !document.hidden;
        };
        document.addEventListener("visibilitychange", onVisibilityChange);
        async function copyToClipboard(text) {
          if (typeof window !== "undefined" && window.flutter_inappwebview && typeof window.flutter_inappwebview.callHandler === "function") {
            try {
              await window.flutter_inappwebview.callHandler(
                "copyToClipboard",
                text
              );
              return true;
            } catch (err) {
              log$1.error("Failed to copy using Torn PDA callHandler", err);
            }
          }
          try {
            if (navigator.clipboard?.writeText) {
              await navigator.clipboard.writeText(text);
              return true;
            }
          } catch (err) {
            log$1.error("Failed to copy using clipboard API", err);
          }
          try {
            const textarea = document.createElement("textarea");
            textarea.value = text;
            textarea.style.position = "fixed";
            textarea.style.opacity = "0";
            document.body.appendChild(textarea);
            textarea.select();
            const success = document.execCommand("copy");
            document.body.removeChild(textarea);
            return success;
          } catch (err) {
            log$1.error("Failed to copy using fallback", err);
            return false;
          }
        }
        function injectCopyButton(id, li) {
          if (li.querySelector(".twse-copy-btn")) return;
          const atag = li.querySelector(
            "a[href^='/profiles.php']"
          );
          if (!atag) return;
          const parent = li.querySelector(".member");
          if (!parent) return;
          const copyBtn = document.createElement("button");
          copyBtn.className = "twse-copy-btn";
          copyBtn.type = "button";
          copyBtn.title = "Copy Name [ID]";
          copyBtn.innerHTML = `
          <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="twse-copy-icon"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
        `;
          copyBtn.addEventListener("click", async (e2) => {
            e2.preventDefault();
            e2.stopPropagation();
            const name = atag.textContent?.trim() || "";
            const copyText = `${name} [${id}]`;
            const success = await copyToClipboard(copyText);
            if (success) {
              copyBtn.classList.add("success");
              copyBtn.innerHTML = `
              <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="twse-copy-icon-success"><polyline points="20 6 9 17 4 12"></polyline></svg>
            `;
              setTimeout(() => {
                copyBtn.classList.remove("success");
                copyBtn.innerHTML = `
                <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="twse-copy-icon"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
              `;
              }, 1e3);
            }
          });
          parent.appendChild(copyBtn);
        }
        function extractAllMemberLis() {
          memberLis.clear();
          const memberLists = document.querySelectorAll("ul.members-list");
          memberLists.forEach((ul) => {
            const lis = ul.querySelectorAll("li.enemy, li.your");
            lis.forEach((li) => {
              const atag = li.querySelector(
                "a[href^='/profiles.php']"
              );
              if (!atag) return;
              const parts = atag.href.split("ID=");
              if (parts.length <= 1) return;
              const id = parts[1];
              memberLis.set(id, {
                li,
                statusDiv: li.querySelector("div.status")
              });
              injectCopyButton(id, li);
            });
          });
        }
        function getFactionIds() {
          const memberLists = document.querySelectorAll("ul.members-list");
          const ids = [];
          memberLists.forEach((elem) => {
            const q = elem.querySelector(
              "a[href^='/factions.php']"
            );
            if (!q) return;
            const s2 = q.href.split("ID=");
            if (s2.length <= 1) return;
            const id = s2[1];
            if (id) {
              ids.push(id);
            }
          });
          return ids;
        }
        function getSortedColumn(memberList) {
          const parent = memberList.parentNode;
          if (!parent) return { column: null, order: null };
          const memberDiv = parent.querySelector("div.member div");
          const levelDiv = parent.querySelector("div.level div");
          const pointsDiv = parent.querySelector("div.points div");
          const statusDiv = parent.querySelector("div.status div");
          let column = null;
          let classname = "";
          if (memberDiv?.className.includes("activeIcon__")) {
            column = "member";
            classname = memberDiv.className;
          } else if (levelDiv?.className.includes("activeIcon__")) {
            column = "level";
            classname = levelDiv.className;
          } else if (pointsDiv?.className.includes("activeIcon__")) {
            column = "points";
            classname = pointsDiv.className;
          } else if (statusDiv?.className.includes("activeIcon__")) {
            column = "status";
            classname = statusDiv.className;
          }
          const order = classname.includes("asc__") ? "asc" : "desc";
          if (column && (column !== "points" || order !== "desc")) {
            everSorted = true;
          }
          return { column, order };
        }
        function populateCachedStatus(factionId) {
          const cached = factionCache.get(factionId);
          if (!cached) return;
          for (const [id, status] of Object.entries(cached)) {
            memberStatus.set(id, status);
          }
          log$1.info(
            `Populated war monitor cache with stored statuses for faction: ${factionId}`
          );
        }
        function queueAttrWrite(elem, attr, value) {
          if (elem.getAttribute(attr) !== value) {
            deferredWrites.push([elem, attr, value]);
            return true;
          }
          return false;
        }
        function queueStyleWrite(elem, prop, value) {
          if (elem.style.getPropertyValue(prop) !== value) {
            deferredStyles.push([elem, prop, value]);
          }
        }
        function calculateFlightTimeRemaining(li) {
          const earliestArrivalAttr = li.getAttribute("data-earliest-arrival");
          const latestArrivalAttr = li.getAttribute("data-latest-arrival");
          if (!earliestArrivalAttr && !latestArrivalAttr) return "";
          const earliestArrival = parseInt(earliestArrivalAttr || "", 10);
          const latestArrival = parseInt(latestArrivalAttr || "", 10);
          if (Number.isNaN(earliestArrival) && Number.isNaN(latestArrival))
            return "";
          const now = getCurrentTimeSec();
          if (!Number.isNaN(earliestArrival) && earliestArrival > now) {
            const remaining = Math.round(earliestArrival - now);
            return ` ${calc_delta(remaining, false, false)}`;
          }
          if (!Number.isNaN(latestArrival) && latestArrival > now) {
            const remaining = Math.round(latestArrival - now);
            return ` <${calc_delta(remaining, false, false)}`;
          }
          return " LATE";
        }
        async function updateStatuses() {
          if (!running) return;
          const factionIds = getFactionIds();
          if (factionIds.length === 0) return;
          const now = Date.now();
          if (now - lastRequestTime < minTimeBetweenRequestsMs) return;
          lastRequestTime = now;
          for (const factionId of factionIds) {
            log$1.debug(`Fetching API status update for faction: ${factionId}`);
            const data = await tornApi.fetchFactionData(factionId);
            if (!data) continue;
            if (data.error) {
              if (tornApi.isUnrecoverableError(data.error.code)) {
                log$1.error(
                  "Torn API returned unrecoverable error. Halting war monitor polling."
                );
                running = false;
                break;
              }
              continue;
            }
            if (!data.members) continue;
            const reqTime = Date.now();
            const factionStatus = {};
            for (const [id, memberData] of Object.entries(data.members)) {
              const status = memberData.status;
              status.last_req_time = reqTime;
              const prev = memberStatus.get(id);
              const prev_state = prev?.state ?? "Unknown";
              const prev_since = prev?.since ?? reqTime;
              if (prev_state === status.state) {
                status.since = prev_since;
              } else {
                status.since = reqTime;
              }
              memberStatus.set(id, status);
              factionStatus[id] = status;
            }
            factionCache.set(factionId, factionStatus);
            if (data.chain) {
              activeChains.set(factionId, {
                current: data.chain.current,
                max: data.chain.max,
                timeout: data.chain.timeout,
                modifier: data.chain.modifier,
                tag: data.tag || "",
                apiReceivedAt: getCurrentTimeSec(),
                cooldown: data.chain.cooldown || 0,
                end: data.chain.end
              });
            }
          }
        }
        function watch() {
          deferredWrites.length = 0;
          deferredStyles.length = 0;
          let dirtySort = false;
          memberLis.forEach((elem, id) => {
            const li = elem.li;
            const statusDiv = elem.statusDiv;
            if (!li || !statusDiv) return;
            const status = memberStatus.get(id);
            if (!status || !running) {
              queueStyleWrite(
                statusDiv,
                "--twse-content",
                `"${statusDiv.textContent || ""}"`
              );
              return;
            }
            if (queueAttrWrite(li, "data-until", String(status.until))) {
              dirtySort = true;
            }
            if (queueAttrWrite(li, "data-since", String(status.since))) {
              dirtySort = true;
            }
            let dataLocation = "";
            switch (status.state) {
              case "Abroad":
              case "Traveling": {
                const hasTravelingClass = statusDiv.classList.contains("traveling") || statusDiv.classList.contains("abroad");
                if (!hasTravelingClass) {
                  if (statusDiv.textContent === "Okay") {
                    queueAttrWrite(statusDiv, STATUS_DIFFERS, "true");
                    if (queueAttrWrite(li, "data-sortA", "0")) {
                      dirtySort = true;
                    }
                  }
                  queueStyleWrite(
                    statusDiv,
                    "--twse-content",
                    `"${statusDiv.textContent || ""}"`
                  );
                  break;
                }
                queueAttrWrite(statusDiv, STATUS_DIFFERS, "false");
                if (status.description.includes("In ")) {
                  if (queueAttrWrite(li, "data-sortA", "4")) {
                    dirtySort = true;
                  }
                  const content = shorten_destination(
                    status.description.split("In ")[1]
                  );
                  dataLocation = content;
                  queueStyleWrite(statusDiv, "--twse-content", `"${content}"`);
                  break;
                }
                const route = extract_destinations_from_description(
                  status.description
                );
                if (route?.from === "TC") {
                  if (queueAttrWrite(li, "data-sortA", "5")) {
                    dirtySort = true;
                  }
                  const dest = route.to;
                  dataLocation = `► ${dest}`;
                  const remaining = calculateFlightTimeRemaining(li);
                  queueStyleWrite(
                    statusDiv,
                    "--twse-content",
                    `"${dataLocation}${remaining}"`
                  );
                } else if (route?.to === "TC") {
                  if (queueAttrWrite(li, "data-sortA", "3")) {
                    dirtySort = true;
                  }
                  const dest = route.from;
                  dataLocation = `◄ ${dest}`;
                  const remaining = calculateFlightTimeRemaining(li);
                  queueStyleWrite(
                    statusDiv,
                    "--twse-content",
                    `"${dataLocation}${remaining}"`
                  );
                } else {
                  if (queueAttrWrite(li, "data-sortA", "6")) {
                    dirtySort = true;
                  }
                  dataLocation = "Traveling";
                  queueStyleWrite(
                    statusDiv,
                    "--twse-content",
                    `"${dataLocation}"`
                  );
                }
                break;
              }
              case "Hospital":
              case "Jail": {
                const now = getCurrentTimeSec();
                const timeRemaining = Math.round(status.until - now);
                const hasHospitalClass = statusDiv.classList.contains("hospital") || statusDiv.classList.contains("jail");
                if (!hasHospitalClass) {
                  if (timeRemaining >= 0) {
                    if (queueAttrWrite(li, "data-sortA", "0")) {
                      dirtySort = true;
                    }
                    queueAttrWrite(statusDiv, STATUS_DIFFERS, "true");
                  }
                  queueStyleWrite(
                    statusDiv,
                    "--twse-content",
                    `"${statusDiv.textContent || ""}"`
                  );
                  queueAttrWrite(statusDiv, TRAVELING, "false");
                  queueAttrWrite(statusDiv, HIGHLIGHT, "false");
                  break;
                }
                queueAttrWrite(statusDiv, STATUS_DIFFERS, "false");
                if (queueAttrWrite(li, "data-sortA", "2")) {
                  dirtySort = true;
                }
                if (status.description.includes("In a")) {
                  queueAttrWrite(statusDiv, TRAVELING, "true");
                } else {
                  queueAttrWrite(statusDiv, TRAVELING, "false");
                }
                if (timeRemaining <= 0) {
                  queueAttrWrite(statusDiv, HIGHLIGHT, "false");
                  break;
                }
                const timeStr = calc_delta(timeRemaining);
                queueStyleWrite(statusDiv, "--twse-content", `"${timeStr}"`);
                if (timeRemaining < 300) {
                  queueAttrWrite(statusDiv, HIGHLIGHT, "true");
                } else {
                  queueAttrWrite(statusDiv, HIGHLIGHT, "false");
                }
                break;
              }
              default:
                queueStyleWrite(
                  statusDiv,
                  "--twse-content",
                  `"${statusDiv.textContent || ""}"`
                );
                if (queueAttrWrite(li, "data-sortA", "1")) {
                  dirtySort = true;
                }
                queueAttrWrite(statusDiv, TRAVELING, "false");
                queueAttrWrite(statusDiv, HIGHLIGHT, "false");
                queueAttrWrite(statusDiv, STATUS_DIFFERS, "false");
                break;
            }
            if (li.getAttribute("data-location") !== dataLocation) {
              queueAttrWrite(li, "data-location", dataLocation);
              dirtySort = true;
            }
          });
          if (deferredWrites.length > 0) {
            for (const [elem, attr, val] of deferredWrites) {
              elem.setAttribute(attr, val);
            }
            deferredWrites.length = 0;
          }
          if (deferredStyles.length > 0) {
            for (const [elem, prop, val] of deferredStyles) {
              elem.style.setProperty(prop, val);
            }
            deferredStyles.length = 0;
          }
          if (twseconfig.war_sorting && dirtySort) {
            const memberLists = document.querySelectorAll("ul.members-list");
            for (let i2 = 0; i2 < memberLists.length; i2++) {
              const listElem = memberLists[i2];
              let sortedColumn = getSortedColumn(listElem);
              if (!everSorted) {
                sortedColumn = { column: "status", order: "asc" };
              }
              if (listElem.getAttribute("data-ffscouter-active-filter") === "true") {
                ffscouterSortingDeferred = true;
                continue;
              }
              if (sortedColumn.column !== "status") {
                continue;
              }
              const lis = Array.from(listElem.childNodes);
              const validLis = lis.filter(
                (node) => node.nodeType === Node.ELEMENT_NODE
              );
              const sortedLis = validLis.sort((a2, b2) => {
                let left = a2;
                let right = b2;
                if (sortedColumn.order === "desc") {
                  left = b2;
                  right = a2;
                }
                const sortA_a = parseInt(
                  left.getAttribute("data-sortA") || "1",
                  10
                );
                const sortA_b = parseInt(
                  right.getAttribute("data-sortA") || "1",
                  10
                );
                const sorta = sortA_a - sortA_b;
                if (sorta !== 0) {
                  return sorta;
                }
                const leftLocation = left.getAttribute("data-location") || "";
                const rightLocation = right.getAttribute("data-location") || "";
                if (leftLocation && rightLocation) {
                  if (leftLocation < rightLocation) return -1;
                  if (leftLocation > rightLocation) return 1;
                  return 0;
                }
                if (sortA_a === 0 || sortA_a === 1) {
                  const since_a = parseInt(
                    left.getAttribute("data-since") || "0",
                    10
                  );
                  const since_b = parseInt(
                    right.getAttribute("data-since") || "0",
                    10
                  );
                  return since_b - since_a;
                }
                const until_a = parseInt(
                  left.getAttribute("data-until") || "0",
                  10
                );
                const until_b = parseInt(
                  right.getAttribute("data-until") || "0",
                  10
                );
                return until_a - until_b;
              });
              let sorted = true;
              for (let j = 0; j < sortedLis.length; j++) {
                if (listElem.children[j] !== sortedLis[j]) {
                  sorted = false;
                  break;
                }
              }
              if (!sorted) {
                const fragment = document.createDocumentFragment();
                sortedLis.forEach((li) => {
                  fragment.appendChild(li);
                });
                listElem.appendChild(fragment);
              }
            }
          }
          if (ffscouterSortingDeferred) {
            const memberLists = document.querySelectorAll("ul.members-list");
            let activeFilterFound = false;
            for (let i2 = 0; i2 < memberLists.length; i2++) {
              if (memberLists[i2].getAttribute("data-ffscouter-active-filter") === "true") {
                activeFilterFound = true;
                break;
              }
            }
            if (!activeFilterFound) {
              ffscouterSortingDeferred = false;
              dirtySort = true;
            }
          }
          for (const [id, ref] of memberLis) {
            if (!ref.li.isConnected) {
              memberLis.delete(id);
            }
          }
          updateChainBubble();
        }
        function updateChainBubble() {
          if (!bubbleContainer) return;
          if (!foundWar || activeChains.size === 0) {
            bubbleContainer.classList.add("hidden");
            return;
          }
          const bodyContainer = bubbleContainer.querySelector(".twse-chain-body");
          if (!bodyContainer) return;
          let html = "";
          const now = getCurrentTimeSec();
          activeChains.forEach((chain) => {
            let formattedTime = "";
            let timerClass = "okay";
            let countClass = "";
            if (chain.cooldown > 0) {
              const elapsed = now - chain.apiReceivedAt;
              const remainingCooldown = Math.max(0, chain.cooldown - elapsed);
              formattedTime = formatChainCooldown(remainingCooldown);
              timerClass = "cooldown";
              countClass = "cooldown";
            } else if (chain.timeout === 0) {
              formattedTime = "-:--";
              timerClass = "okay";
            } else {
              const elapsed = now - chain.apiReceivedAt;
              const remaining = chain.end && chain.end > 0 ? chain.end - now : chain.timeout - elapsed;
              formattedTime = formatChainTimeout(remaining);
              if (remaining < 0) {
                timerClass = "negative";
              } else if (remaining < 60) {
                timerClass = "urgent";
              }
            }
            html += `
            <div class="twse-chain-row">
              <span class="twse-chain-tag">[${chain.tag || "Faction"}]</span>
              <div class="twse-chain-stats">
                <span class="twse-chain-count ${countClass}">${chain.current}/${chain.max}</span>
                <span class="twse-chain-mult">${chain.modifier.toFixed(2)}x</span>
                <span class="twse-chain-timer ${timerClass}">${formattedTime}</span>
              </div>
            </div>
          `;
          });
          bodyContainer.innerHTML = html;
          bubbleContainer.classList.remove("hidden");
        }
        let descriptionsObserver = null;
        let innerDescriptionsObserver = null;
        const initWarMonitoring = (descriptions) => {
          foundWar = false;
          log$1.info("Descriptions container detected. Starting observation.");
          let injectedToggle = false;
          const injectSortingToggle = (descEl) => {
            if (injectedToggle) return;
            if (descEl.querySelector("#twse-war-sort-checkbox")) {
              injectedToggle = true;
              return;
            }
            const graphContainer = descEl.querySelector('[class*="graphIcon"]');
            if (!graphContainer || !graphContainer.parentNode) return;
            const parent = graphContainer.parentNode;
            parent.style.position = "relative";
            const computedStyle = window.getComputedStyle(graphContainer);
            const toggleContainer = document.createElement("div");
            toggleContainer.className = "twse-sort-toggle-container";
            toggleContainer.style.top = computedStyle.top && computedStyle.top !== "auto" ? computedStyle.top : "10px";
            toggleContainer.innerHTML = `
            <label class="twse-sort-toggle-label">
              <input type="checkbox" id="twse-war-sort-checkbox" class="twse-sort-toggle-checkbox" ${twseconfig.war_sorting ? "checked" : ""} />
              TWSE Sort
            </label>
          `;
            graphContainer.parentNode.insertBefore(
              toggleContainer,
              graphContainer
            );
            log$1.info(
              "Successfully injected war sorting toggle checkbox before Graph link."
            );
            injectedToggle = true;
            const checkbox = toggleContainer.querySelector(
              "#twse-war-sort-checkbox"
            );
            if (checkbox) {
              checkbox.addEventListener("change", (e2) => {
                const isChecked = e2.target.checked;
                log$1.info(`War sorting configuration changed: ${isChecked}`);
                twseconfig.war_sorting = isChecked;
              });
            }
          };
          injectSortingToggle(descriptions);
          innerDescriptionsObserver = observeElement(descriptions, () => {
            if (!injectedToggle) {
              injectSortingToggle(descriptions);
            }
            if (!foundWar && descriptions.querySelector(".faction-war")) {
              foundWar = true;
              extractAllMemberLis();
              const ids = getFactionIds();
              ids.forEach(populateCachedStatus);
              updateStatuses();
            }
          });
          if (descriptions.querySelector(".faction-war")) {
            foundWar = true;
            extractAllMemberLis();
            const ids = getFactionIds();
            ids.forEach(populateCachedStatus);
            updateStatuses();
          }
        };
        const factWarList = await waitForElement("#faction_war_list_id");
        if (!active) return;
        if (factWarList) {
          descriptionsObserver = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
              for (const node of mutation.addedNodes) {
                if (node instanceof HTMLElement && node.classList.contains("descriptions")) {
                  log$1.info("Observed descriptions container added to DOM");
                  initWarMonitoring(node);
                }
              }
            }
          });
          descriptionsObserver.observe(factWarList, { childList: true });
          const existingDescriptions = factWarList.querySelector(".descriptions");
          if (existingDescriptions) {
            log$1.info("Found existing descriptions container");
            initWarMonitoring(existingDescriptions);
          }
        }
        const pollingInterval = setInterval(() => {
          if (running && foundWar) {
            updateStatuses();
          }
        }, 1e4);
        const watchInterval = setInterval(() => {
          if (foundWar && running && pageVisible) {
            watch();
          }
        }, 500);
        stopMonitor = () => {
          active = false;
          running = false;
          clearInterval(pollingInterval);
          clearInterval(watchInterval);
          if (descriptionsObserver) {
            descriptionsObserver.disconnect();
          }
          if (innerDescriptionsObserver) {
            innerDescriptionsObserver.disconnect();
          }
          window.removeEventListener("twse-config-updated", onConfigUpdated);
          window.removeEventListener("twse-clear-cache", onClearCache);
          window.removeEventListener("resize", clampToScreen);
          document.removeEventListener("visibilitychange", onVisibilityChange);
          if (bubbleContainer) {
            bubbleContainer.remove();
            bubbleContainer = null;
          }
          document.querySelector(".twse-sort-toggle-container")?.remove();
        };
      };
      const handleNavigation = () => {
        const shouldRun = shouldRunMonitor();
        if (shouldRun) {
          if (stopMonitor) {
            stopMonitor();
            stopMonitor = null;
          }
          startMonitor();
        } else if (!shouldRun && active) {
          if (stopMonitor) {
            stopMonitor();
            stopMonitor = null;
          }
        }
      };
      on_navigation(handleNavigation);
      if (shouldRunMonitor()) {
        startMonitor();
      }
      window.dispatchEvent(new Event("FFScouterV2DisableWarMonitor"));
    }
  };
  const __vite_glob_0_2 = Object.freeze( Object.defineProperty({
    __proto__: null,
    default: WarMonitorFeature
  }, Symbol.toStringTag, { value: "Module" }));
  const modules = Object.assign({
    "./key-manager/index.ts": __vite_glob_0_0,
    "./settings/index.ts": __vite_glob_0_1,
    "./war-monitor/index.ts": __vite_glob_0_2
  });
  const Features = Object.values(modules).map((mod) => mod.default).filter((feat) => !!feat && "name" in feat);
  const log = logger.child("boot");
  async function boot() {
    log.info("Initializing Torn War Stuff Enhanced...");
    for (const feature of Features) {
      try {
        const shouldRun = await feature.shouldRun();
        if (!shouldRun) {
          continue;
        }
        log.debug(`Booting feature: '${feature.name}'`);
        if (feature.executionTime === StartTime.DocumentStart) {
          feature.run();
        } else if (feature.executionTime === StartTime.DocumentBody) {
          if (document.body) {
            feature.run();
          } else {
            let booted = false;
            const trigger = () => {
              if (booted) return;
              booted = true;
              bodyObserver.disconnect();
              feature.run();
            };
            const bodyObserver = new MutationObserver(() => {
              if (document.body) {
                trigger();
              }
            });
            bodyObserver.observe(document.documentElement, {
              childList: true
            });
            document.addEventListener("DOMContentLoaded", trigger);
          }
        } else {
          if (document.readyState === "complete" || document.readyState === "interactive") {
            feature.run();
          } else {
            document.addEventListener("DOMContentLoaded", () => {
              feature.run();
            });
          }
        }
      } catch (e2) {
        log.error(`Error running feature '${feature.name}':`, e2);
      }
    }
  }
  boot();

})();