Hentai Reader

A high-performance universal reader for image galleries, now with mobile and touch device support, plus infinite scroll, immersive reader mode, and smart image loading, with ongoing multi-site support (E-Hentai, 18comic, 4KHD).

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name               Hentai Reader
// @name:zh-CN         绅士阅读器
// @namespace          http://tampermonkey.net/
// @version            3.3.0
// @author             Leovikii
// @description        A high-performance universal reader for image galleries, now with mobile and touch device support, plus infinite scroll, immersive reader mode, and smart image loading, with ongoing multi-site support (E-Hentai, 18comic, 4KHD).
// @description:zh-CN  面向图库的高性能通用阅读器,现已适配移动端与触控设备。支持无限卷轴、沉浸式阅读器与智能图片加载,多网站持续适配(E-Hentai、禁漫天堂、4KHD)
// @license            GPL-3.0-only
// @icon               data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20256%20256%22%3E%3Crect%20x%3D%2224%22%20y%3D%2217%22%20width%3D%22201%22%20height%3D%22203%22%20rx%3D%2223%22%20fill%3D%22%23d93675%22%2F%3E%3Crect%20x%3D%2236%22%20y%3D%2228%22%20width%3D%22178%22%20height%3D%22182%22%20rx%3D%2212%22%20fill%3D%22%23ffd8e5%22%2F%3E%3Cpath%20d%3D%22M24%2052h160q22%200%2022%2022v143q0%2022-22%2022H46q-22%200-22-22Z%22%20fill%3D%22%23ff78ad%22%2F%3E%3Cpath%20d%3D%22M48%2017v11q-12%200-12%2012t18%2012v51h152v90H54v46h-8q-22%200-22-22V40q0-23%2023-23Z%22%20fill%3D%22%23d93675%22%2F%3E%3Cpath%20fill%3D%22%23fff8fb%22%20d%3D%22M62.36%20106.04h14.26c9.51%200%2015.94.37%2019.31%201.11%203.37.73%206.11%202.6%208.23%205.62%202.12%203.01%203.18%207.82%203.18%2014.43%200%206.02-.75%2010.08-2.25%2012.15-1.5%202.07-4.45%203.32-8.85%203.73%203.99.99%206.67%202.31%208.04%203.97%201.37%201.66%202.22%203.19%202.56%204.57.33%201.39.5%205.21.5%2011.46v20.44H88.63v-25.75c0-4.14-.32-6.71-.98-7.7-.65-.99-2.37-1.48-5.14-1.48v34.93H62.36Zm20.15%2013.26v17.23c2.26%200%203.85-.31%204.76-.94.91-.62%201.36-2.64%201.36-6.05v-4.26c0-2.46-.44-4.07-1.31-4.83-.88-.77-2.48-1.15-4.81-1.15Zm61.87-13.26v77.48h-19.33v-41.54c0-6-.15-9.6-.43-10.81-.29-1.22-1.08-2.13-2.37-2.75-1.29-.63-4.17-.94-8.64-.94h-1.91v-9.03c9.34-2.01%2016.44-6.15%2021.29-12.41Zm44.41%2033.52c2.83%201.15%205.02%202.87%206.55%205.15%201.53%202.28%202.3%207.72%202.3%2016.3%200%206.26-.61%2010.87-1.82%2013.84-1.21%202.97-3.57%205.4-7.08%207.3-3.51%201.9-8.22%202.85-14.12%202.85-5.74%200-10.29-.91-13.64-2.73-3.35-1.82-5.75-4.23-7.2-7.23-1.45-3-2.17-7.99-2.17-14.98%200-4.7.35-8.77%201.07-12.21.72-3.45%202.98-6.21%206.77-8.29-2.39-1.14-4.18-2.97-5.38-5.47-1.2-2.5-1.8-5.68-1.8-9.54%200-6.66%201.86-11.66%205.56-14.99%203.7-3.33%209.23-5%2016.6-5%208.52%200%2014.4%201.77%2017.66%205.31%203.25%203.54%204.88%208.59%204.88%2015.16%200%204.17-.54%207.19-1.6%209.04-1.07%201.84-3.27%203.68-6.58%205.49Zm-10.87-16.62c0-2.62-.23-4.35-.69-5.2-.46-.84-1.35-1.26-2.66-1.26-1.27%200-2.13.45-2.58%201.36-.45.91-.67%202.61-.67%205.1v5.88c0%202.33.26%203.94.77%204.84.51.89%201.37%201.34%202.58%201.34%201.28%200%202.14-.44%202.58-1.32.45-.88.67-2.69.67-5.43Zm.39%2032.2c0-2.97-.31-4.94-.91-5.91-.61-.97-1.57-1.46-2.87-1.46-1.28%200-2.2.47-2.76%201.41-.55.94-.83%202.93-.83%205.96v10.05c0%203.35.31%205.5.93%206.46.62.96%201.6%201.44%202.94%201.44%201.37%200%202.3-.49%202.78-1.46.48-.98.72-3.17.72-6.58Z%22%2F%3E%3C%2Fsvg%3E
// @homepageURL        https://github.com/Leovikii/Hentai-Reader
// @match              https://e-hentai.org/g/*
// @match              https://exhentai.org/g/*
// @match              https://e-hentai.org/s/*
// @match              https://exhentai.org/s/*
// @match              *://*.4khd.com/*
// @match              *://*.xxtt.ink/*
// @match              *://*.uuss.uk/*
// @match              *://*.ssuu.uk/*
// @match              *://*.18comic.vip/*
// @match              *://*.18comic.ink/*
// @grant              GM_addStyle
// @grant              GM_getValue
// @grant              GM_setValue
// @grant              unsafeWindow
// ==/UserScript==

(function () {
  'use strict';

  const d=new Set;const importCSS = async e=>{d.has(e)||(d.add(e),(t=>{typeof GM_addStyle=="function"?GM_addStyle(t):(document.head||document.documentElement).appendChild(document.createElement("style")).append(t);})(e));};

  const photoswipeCss = "/*! PhotoSwipe main CSS by Dmytro Semenov | photoswipe.com */.pswp{--pswp-bg: #000;--pswp-placeholder-bg: #222;--pswp-root-z-index: 100000;--pswp-preloader-color: rgba(79, 79, 79, .4);--pswp-preloader-color-secondary: rgba(255, 255, 255, .9);--pswp-icon-color: #fff;--pswp-icon-color-secondary: #4f4f4f;--pswp-icon-stroke-color: #4f4f4f;--pswp-icon-stroke-width: 2px;--pswp-error-text-color: var(--pswp-icon-color)}.pswp{position:fixed;top:0;left:0;width:100%;height:100%;z-index:var(--pswp-root-z-index);display:none;touch-action:none;outline:0;opacity:.003;contain:layout style size;-webkit-tap-highlight-color:rgba(0,0,0,0)}.pswp:focus{outline:0}.pswp *{box-sizing:border-box}.pswp img{max-width:none}.pswp--open{display:block}.pswp,.pswp__bg{transform:translateZ(0);will-change:opacity}.pswp__bg{opacity:.005;background:var(--pswp-bg)}.pswp,.pswp__scroll-wrap{overflow:hidden}.pswp__scroll-wrap,.pswp__bg,.pswp__container,.pswp__item,.pswp__content,.pswp__img,.pswp__zoom-wrap{position:absolute;top:0;left:0;width:100%;height:100%}.pswp__img,.pswp__zoom-wrap{width:auto;height:auto}.pswp--click-to-zoom.pswp--zoom-allowed .pswp__img{cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img:active{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img,.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img:active,.pswp__img{cursor:-webkit-zoom-out;cursor:-moz-zoom-out;cursor:zoom-out}.pswp__container,.pswp__img,.pswp__button,.pswp__counter{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pswp__item{z-index:1;overflow:hidden}.pswp__hidden{display:none!important}.pswp__content{pointer-events:none}.pswp__content>*{pointer-events:auto}.pswp__error-msg-container{display:grid}.pswp__error-msg{margin:auto;font-size:1em;line-height:1;color:var(--pswp-error-text-color)}.pswp .pswp__hide-on-close{opacity:.005;will-change:opacity;transition:opacity var(--pswp-transition-duration) cubic-bezier(.4,0,.22,1);z-index:10;pointer-events:none}.pswp--ui-visible .pswp__hide-on-close{opacity:1;pointer-events:auto}.pswp__button{position:relative;display:block;width:50px;height:60px;padding:0;margin:0;overflow:hidden;cursor:pointer;background:none;border:0;box-shadow:none;opacity:.85;-webkit-appearance:none;-webkit-touch-callout:none}.pswp__button:hover,.pswp__button:active,.pswp__button:focus{transition:none;padding:0;background:none;border:0;box-shadow:none;opacity:1}.pswp__button:disabled{opacity:.3;cursor:auto}.pswp__icn{fill:var(--pswp-icon-color);color:var(--pswp-icon-color-secondary)}.pswp__icn{position:absolute;top:14px;left:9px;width:32px;height:32px;overflow:hidden;pointer-events:none}.pswp__icn-shadow{stroke:var(--pswp-icon-stroke-color);stroke-width:var(--pswp-icon-stroke-width);fill:none}.pswp__icn:focus{outline:0}div.pswp__img--placeholder,.pswp__img--with-bg{background:var(--pswp-placeholder-bg)}.pswp__top-bar{position:absolute;left:0;top:0;width:100%;height:60px;display:flex;flex-direction:row;justify-content:flex-end;z-index:10;pointer-events:none!important}.pswp__top-bar>*{pointer-events:auto;will-change:opacity}.pswp__button--close{margin-right:6px}.pswp__button--arrow{position:absolute;width:75px;height:100px;top:50%;margin-top:-50px}.pswp__button--arrow:disabled{display:none;cursor:default}.pswp__button--arrow .pswp__icn{top:50%;margin-top:-30px;width:60px;height:60px;background:none;border-radius:0}.pswp--one-slide .pswp__button--arrow{display:none}.pswp--touch .pswp__button--arrow{visibility:hidden}.pswp--has_mouse .pswp__button--arrow{visibility:visible}.pswp__button--arrow--prev{right:auto;left:0}.pswp__button--arrow--next{right:0}.pswp__button--arrow--next .pswp__icn{left:auto;right:14px;transform:scaleX(-1)}.pswp__button--zoom{display:none}.pswp--zoom-allowed .pswp__button--zoom{display:block}.pswp--zoomed-in .pswp__zoom-icn-bar-v{display:none}.pswp__preloader{position:relative;overflow:hidden;width:50px;height:60px;margin-right:auto}.pswp__preloader .pswp__icn{opacity:0;transition:opacity .2s linear;animation:pswp-clockwise .6s linear infinite}.pswp__preloader--active .pswp__icn{opacity:.85}@keyframes pswp-clockwise{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.pswp__counter{height:30px;margin-top:15px;margin-inline-start:20px;font-size:14px;line-height:30px;color:var(--pswp-icon-color);text-shadow:1px 1px 3px var(--pswp-icon-color-secondary);opacity:.85}.pswp--one-slide .pswp__counter{display:none}";
  importCSS(photoswipeCss);
  const globalCss = ":root{--hr-accent: #F596AA;--hr-accent-hover: #F7ABBE}html.hr-instant-scroll,html.hr-instant-scroll body{scroll-behavior:auto!important}@keyframes hr-spin{to{transform:rotate(360deg)}}.hr-spinner{animation:hr-spin 1s linear infinite}html.scroll-mode body{margin:0}html.scroll-mode #gdt{display:flex;flex-direction:column;align-items:center;width:100%;max-width:1200px;margin:auto;padding-bottom:100px}.hr-page-batch{width:100%;display:flex;flex-direction:column;align-items:center;margin-bottom:60px}html.hr-reader-open .hr-page-batch{visibility:hidden}.r-img{display:block;width:auto;max-width:100%;margin-bottom:20px;background:transparent;box-shadow:0 0 20px #00000080;position:relative;content-visibility:auto;contain-intrinsic-size:auto 100vh}.r-ph{color:#555;margin-bottom:50px;text-align:center;min-height:400px;display:flex;align-items:center;justify-content:center;font-family:sans-serif;font-size:18px;border:1px dashed #333;width:100%;flex-direction:column;gap:10px;content-visibility:auto;contain-intrinsic-size:auto 400px}.r-ph.loading{color:#888;border-color:#555}.r-ph.error{color:#d44;border-color:#d44}.hr-pagination-error{box-sizing:border-box;margin:24px auto;padding:14px 18px;width:min(92vw,520px);display:flex;align-items:center;justify-content:space-between;gap:16px;color:#fff;background:#781824eb;border:1px solid rgba(255,255,255,.25);border-radius:12px}.hr-pagination-error[hidden]{display:none}.hr-pagination-error button{min-width:72px;min-height:40px;color:#fff;background:var(--hr-accent);border:0;border-radius:8px;cursor:pointer}.hr-pagination-error button:focus-visible{outline:2px solid #fff;outline-offset:2px}";
  importCSS(globalCss);
  var _GM_getValue = (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  var _GM_setValue = (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  const CFG = {
    scrollPageRootMargin: "3000px 0px",


imageMaterializeConcurrent: 1
  };
  function loadSettings(adapter) {
    var _a, _b;
    const prefix = adapter ? `${adapter.name}_` : "";
    const globalScrollMode = _GM_getValue("scrollMode", true);
    const scrollDefault = ((_a = adapter == null ? void 0 : adapter.scrollPolicy) == null ? void 0 : _a.defaultEnabled) ?? globalScrollMode;
    const scrollMode = ((_b = adapter == null ? void 0 : adapter.scrollPolicy) == null ? void 0 : _b.configurable) === false ? scrollDefault : _GM_getValue(`${prefix}scrollMode`, scrollDefault);
    return {
      scrollMode,
      autoEnterSinglePage: _GM_getValue("autoEnterSinglePage", false),
      clickToEnterReader: _GM_getValue("clickToEnterReader", true),
      doublePageMode: _GM_getValue("doublePageMode", true),
      autoPlayInterval: Math.max(1e3, Number(_GM_getValue("autoPlayInterval", 5e3)) || 5e3),
      thumbnailPosition: _GM_getValue("thumbnailPosition", "bottom")
    };
  }
  class Store {
constructor() {
      this.listeners = new Map();
      this.activeAdapter = null;
      this.currPage = 1;
      this.perPage = 20;
      this.imageOffset = 0;
      this.nextUrl = null;
      this.prevUrl = null;
      this.isFetching = false;
      this.galleryItems = [];
      this.autoPlay = false;
      this._settings = loadSettings();
    }
    get settings() {
      return this._settings;
    }
    reloadSettings() {
      this._settings = loadSettings(this.activeAdapter ?? void 0);
    }
    updateSetting(key, value) {
      var _a, _b;
      if (key === "scrollMode" && ((_b = (_a = this.activeAdapter) == null ? void 0 : _a.scrollPolicy) == null ? void 0 : _b.configurable) === false) return;
      this._settings[key] = value;
      if (key === "scrollMode" && this.activeAdapter) {
        _GM_setValue(`${this.activeAdapter.name}_scrollMode`, value);
      } else {
        _GM_setValue(key, value);
      }
      this.emit("settingsChanged");
    }
    on(event, listener) {
      if (!this.listeners.has(event)) {
        this.listeners.set(event, new Set());
      }
      this.listeners.get(event).add(listener);
      return () => {
        const listeners = this.listeners.get(event);
        listeners == null ? void 0 : listeners.delete(listener);
        if ((listeners == null ? void 0 : listeners.size) === 0) this.listeners.delete(event);
      };
    }
    emit(event) {
      var _a;
      (_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((fn) => fn());
    }
  }
  const store = new Store();
  const systemClock = {
    now: () => Date.now(),
    setTimeout: (callback, delay) => setTimeout(callback, delay),
    clearTimeout: (id) => clearTimeout(id)
  };
  class NetLimiter {
    constructor(maxConcurrent, clock = systemClock) {
      this.queue = [];
      this.active = 0;
      this.pausedUntil = 0;
      this.seq = 0;
      this.pauseTimer = null;
      this.maxConcurrent = maxConcurrent;
      this.clock = clock;
    }
run(task, options = 0) {
      const normalized = typeof options === "number" ? { priority: options } : options;
      return new Promise((resolve, reject) => {
        var _a;
        if ((_a = normalized.signal) == null ? void 0 : _a.aborted) {
          reject(new DOMException("Request cancelled", "AbortError"));
          return;
        }
        const job = {
          run: task,
          resolve,
          reject,
          priority: normalized.priority ?? 0,
          seq: this.seq++,
          key: normalized.key,
          signal: normalized.signal
        };
        this.queue.push(job);
        if (normalized.signal) {
          const onAbort = () => {
            var _a2;
            const index = this.queue.indexOf(job);
            if (index === -1) return;
            this.queue.splice(index, 1);
            (_a2 = job.removeAbortListener) == null ? void 0 : _a2.call(job);
            reject(new DOMException("Request cancelled", "AbortError"));
          };
          normalized.signal.addEventListener("abort", onAbort, { once: true });
          job.removeAbortListener = () => {
            var _a2;
            return (_a2 = normalized.signal) == null ? void 0 : _a2.removeEventListener("abort", onAbort);
          };
          if (normalized.signal.aborted) onAbort();
        }
        this.pump();
      });
    }
promote(key, priority) {
      for (const job of this.queue) {
        if (job.key === key && priority > job.priority) job.priority = priority;
      }
      this.pump();
    }
pauseFor(ms) {
      const until = this.clock.now() + ms;
      if (until <= this.pausedUntil) return;
      this.pausedUntil = until;
      this.schedulePauseWake(true);
    }
cancel(predicate) {
      var _a;
      if (this.queue.length === 0) return;
      const kept = [];
      for (const job of this.queue) {
        if (predicate(job.priority, job.seq, job.key)) {
          (_a = job.removeAbortListener) == null ? void 0 : _a.call(job);
          job.reject({ cancelled: true });
        } else {
          kept.push(job);
        }
      }
      this.queue = kept;
    }
    pump() {
      var _a;
      const now = this.clock.now();
      if (now < this.pausedUntil) {
        this.schedulePauseWake();
        return;
      }
      if (this.pauseTimer !== null) {
        this.clock.clearTimeout(this.pauseTimer);
        this.pauseTimer = null;
      }
      while (this.active < this.maxConcurrent && this.queue.length > 0) {
        let bestIdx = 0;
        for (let i = 1; i < this.queue.length; i++) {
          const cand = this.queue[i];
          const best = this.queue[bestIdx];
          if (cand.priority > best.priority || cand.priority === best.priority && cand.seq < best.seq) {
            bestIdx = i;
          }
        }
        const job = this.queue.splice(bestIdx, 1)[0];
        (_a = job.removeAbortListener) == null ? void 0 : _a.call(job);
        this.active++;
        Promise.resolve().then(() => job.run()).then(
          (value) => {
            job.resolve(value);
            this.active--;
            this.pump();
          },
          (reason) => {
            job.reject(reason);
            this.active--;
            this.pump();
          }
        );
      }
    }
    schedulePauseWake(reschedule = false) {
      if (reschedule && this.pauseTimer !== null) {
        this.clock.clearTimeout(this.pauseTimer);
        this.pauseTimer = null;
      }
      if (this.pauseTimer !== null) return;
      const delay = Math.max(0, this.pausedUntil - this.clock.now() + 10);
      this.pauseTimer = this.clock.setTimeout(() => {
        this.pauseTimer = null;
        this.pump();
      }, delay);
    }
  }
  const LOAD_PRIORITY = {
    scroll: 0,
    warmup: 5,
    thumbnail: 10,
    pageHtml: 50,
    foreground: 100
  };
  const READER_PREFETCH = {
    ahead: 5,
    behind: 2
  };
  function getReaderPrefetchIndices(center, total, direction, policy = READER_PREFETCH) {
    if (total <= 0) return [];
    const lo = Math.max(0, center - (direction === 1 ? policy.behind : policy.ahead));
    const hi = Math.min(total - 1, center + (direction === 1 ? policy.ahead : policy.behind));
    const indices = [];
    for (let distance = 0; distance <= Math.max(hi - center, center - lo); distance++) {
      const forward = center + direction * distance;
      if (forward >= lo && forward <= hi) indices.push(forward);
      const back = center - direction * distance;
      if (distance > 0 && back >= lo && back <= hi) indices.push(back);
    }
    return indices;
  }
  const HATH_SOURCE_PATTERN = /^\/h\/[0-9a-f]{40}-\d+-(\d+)-(\d+)-[a-z0-9]+(?:\/|$)/i;
  const MAX_SOURCE_DIMENSION = 1e5;
  function parseEHentaiSourceDimensions(src) {
    let url;
    try {
      url = new URL(src);
    } catch {
      return void 0;
    }
    if (url.hostname !== "hath.network" && !url.hostname.endsWith(".hath.network")) return void 0;
    const match = url.pathname.match(HATH_SOURCE_PATTERN);
    if (!match) return void 0;
    const width = Number(match[1]);
    const height = Number(match[2]);
    if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0 || width > MAX_SOURCE_DIMENSION || height > MAX_SOURCE_DIMENSION) return void 0;
    return { width, height };
  }
  function paginationUrl(doc, marker) {
    const pagination = doc.querySelector(".ptt");
    if (!pagination) return null;
    const link = Array.from(pagination.querySelectorAll("td a")).find((anchor) => (anchor.textContent ?? "").includes(marker));
    return (link == null ? void 0 : link.href) ?? null;
  }
  function getEHentaiNextUrl(doc) {
    return paginationUrl(doc, ">");
  }
  function getEHentaiPrevUrl(doc) {
    return paginationUrl(doc, "<");
  }
  function extractEHentaiItems(doc) {
    return Array.from(doc.querySelectorAll("#gdt a")).map((element) => {
      const anchor = element;
      const url = anchor.href;
      let thumb;
      let thumbX;
      let thumbY;
      let thumbW;
      let thumbH;
      const sprite = anchor.querySelector('div[style*="background"]');
      if (sprite) {
        const style = sprite.getAttribute("style") || "";
        const source = style.match(/url\(['"]?([^)'"]+)['"]?\)/);
        if (source) thumb = source[1];
        const afterUrl = style.slice(style.indexOf(source ? source[0] : "") + (source ? source[0].length : 0));
        const position = afterUrl.match(/(-?\d+)(?:px)?\s+(-?\d+)(?:px)?/);
        if (position) {
          thumbX = Math.abs(parseInt(position[1], 10));
          thumbY = Math.abs(parseInt(position[2], 10));
        }
        const width = style.match(/width:\s*(\d+)px/);
        const height = style.match(/height:\s*(\d+)px/);
        if (width) thumbW = parseInt(width[1], 10);
        if (height) thumbH = parseInt(height[1], 10);
      } else {
        const img = anchor.querySelector("img");
        if ((img == null ? void 0 : img.src) && !img.src.endsWith("x.gif")) thumb = img.src;
      }
      let preview = { kind: "none" };
      if (thumb && thumbW !== void 0 && thumbH !== void 0 && (thumbX !== void 0 || thumbY !== void 0)) {
        preview = {
          kind: "sprite",
          src: thumb,
          crop: {
            x: thumbX ?? 0,
            y: thumbY ?? 0,
            width: thumbW,
            height: thumbH
          }
        };
      } else if (thumb) {
        preview = {
          kind: "url",
          src: thumb,
          ...thumbW !== void 0 && thumbH !== void 0 ? { size: { width: thumbW, height: thumbH } } : {}
        };
      }
      return { key: url, viewerUrl: url, preview };
    });
  }
  function parseEHentaiPageMetadata(doc, itemCount) {
    var _a;
    let totalPages = 1;
    for (const cell of Array.from(doc.querySelectorAll(".ptt td"))) {
      const page = parseInt(cell.textContent ?? "", 10);
      if (!Number.isNaN(page) && page > totalPages) totalPages = page;
    }
    const range = ((_a = doc.querySelector(".gpc")) == null ? void 0 : _a.textContent) ?? "";
    const match = range.match(/([\d,]+)\s*-\s*([\d,]+)[^\d]+([\d,]+)/);
    if (!match) return { totalPages };
    const start = parseInt(match[1].replace(/,/g, ""), 10);
    const end = parseInt(match[2].replace(/,/g, ""), 10);
    const total = parseInt(match[3].replace(/,/g, ""), 10);
    const imageOffset = start - 1;
    let perPage;
    if (start === 1) perPage = end;
    else if (end < total) perPage = end - start + 1;
    else if (totalPages > 1) perPage = Math.round(imageOffset / (totalPages - 1));
    else perPage = itemCount;
    return { totalPages, imageOffset, perPage };
  }
  function buildEHentaiViewerUrl(url, nlToken) {
    if (!nlToken) return url;
    const viewerUrl = new URL(url);
    viewerUrl.searchParams.set("nl", nlToken);
    return viewerUrl.toString();
  }
  function parseEHentaiViewer(doc) {
    var _a;
    const image = doc.querySelector("#img");
    if (!(image == null ? void 0 : image.src)) return null;
    const onerror = image.getAttribute("onerror") || "";
    const token = (_a = onerror.match(/nl\(['"]([^'"]+)['"]\)/)) == null ? void 0 : _a[1];
    const dimensions = parseEHentaiSourceDimensions(image.src);
    return {
      src: image.src,
      ...token ? { nl: token } : {},
      ...dimensions ? { dimensions } : {}
    };
  }
  let parser$1;
  function parseDocument(html) {
    parser$1 ?? (parser$1 = new DOMParser());
    return parser$1.parseFromString(html, "text/html");
  }
  const PAGE_FETCH_PRIORITY = LOAD_PRIORITY.pageHtml;
  const REQUEST_POLICY = {
    concurrent: 3,
    rateLimitCooldownMs: 5e3,
    foregroundLoadTimeoutMs: 12e3,
    backgroundLoadTimeoutMs: 2e4
  };
  const requestLimiter = new NetLimiter(REQUEST_POLICY.concurrent);
  function limitedFetch(url, options = {}) {
    return requestLimiter.run(
      async () => {
        const response = await fetch(url, {
          signal: options.signal,
          ...options.fresh ? { cache: "no-store" } : {}
        });
        enforceRateLimit(response);
        return response;
      },
      { priority: options.priority, key: options.key, signal: options.signal }
    );
  }
  function enforceRateLimit(response) {
    if (response.status !== 429 && response.status !== 503) return;
    requestLimiter.pauseFor(REQUEST_POLICY.rateLimitCooldownMs);
    throw new Error(`Rate limited: HTTP ${response.status}`);
  }
  const EHentaiAdapter = {
    name: "E-Hentai/ExHentai",
    match(url) {
      return /https?:\/\/(e-|ex)hentai\.org\/(g|s)\//.test(url);
    },
    async loadInitialPage(doc, url) {
      const items = extractEHentaiItems(doc);
      const metadata = parseEHentaiPageMetadata(doc, items.length);
      return {
        pageUrl: url,
        items,
        nextUrl: getEHentaiNextUrl(doc),
        prevUrl: getEHentaiPrevUrl(doc),
        totalPages: metadata.totalPages,
        position: {
          startIndex: metadata.imageOffset ?? 0,
          pageSize: metadata.perPage ?? items.length
        }
      };
    },
    async resolveImage(url, context) {
      const response = await limitedFetch(buildEHentaiViewerUrl(url, context.retryToken), {
        priority: context.priority,
        key: url,
        signal: context.signal,
        fresh: context.force || !!context.retryToken
      });
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const html = await response.text();
      const doc = parseDocument(html);
      const viewer = parseEHentaiViewer(doc);
      if (!viewer) throw new Error("Image not found");
      return {
        src: viewer.src,
        ...viewer.nl ? { retryToken: viewer.nl } : {},
        ...viewer.dimensions ? { sourceDimensions: viewer.dimensions } : {},
        loadTimeoutMs: context.priority >= LOAD_PRIORITY.foreground - 10 ? REQUEST_POLICY.foregroundLoadTimeoutMs : REQUEST_POLICY.backgroundLoadTimeoutMs
      };
    },
    async loadPage(url, signal) {
      const response = await limitedFetch(url, { priority: PAGE_FETCH_PRIORITY, key: url, signal });
      if (!response.ok) throw new Error("Failed to fetch page");
      const html = await response.text();
      const doc = parseDocument(html);
      return {
        pageUrl: url,
        items: extractEHentaiItems(doc),
        nextUrl: getEHentaiNextUrl(doc),
        prevUrl: getEHentaiPrevUrl(doc)
      };
    },
    getContainer() {
      return document.querySelector("#gdt") || document.querySelector(".gm");
    },
    hideOriginalElements() {
      const hiddenSelectors = [
        ".c1",
        ".c2",
        ".c3",
        ".c4",
        ".c5",
        ".c6",
        ".c7",
        ".c8",
        ".ptt",
        ".ptb",
        ".gdtl",
        ".gdtm",
        "#gdo",
        "#cdiv",
        "table.itg"
      ];
      const elements = Array.from(document.querySelectorAll(hiddenSelectors.join(",")));
      const displays = elements.map((element) => element.style.display);
      elements.forEach((element) => {
        element.style.display = "none";
      });
      return () => elements.forEach((element, index) => {
        element.style.display = displays[index];
      });
    },
    imageRequestQueue: {
      promote(url, priority) {
        requestLimiter.promote(url, priority);
      },
      cancelPrefetch(keepUrls) {
        requestLimiter.cancel((priority, _seq, key) => priority <= LOAD_PRIORITY.thumbnail && !!key && !keepUrls.has(key));
      }
    },
    onReaderClose(globalIndex, context) {
      if (context.scrollMode) return;
      const targetPage = Math.floor(globalIndex / context.pageSize);
      const url = new URL(window.location.href);
      const currentPage = parseInt(url.searchParams.get("p") || "0", 10);
      if (targetPage !== currentPage) {
        url.searchParams.set("p", String(targetPage));
        window.location.href = url.toString();
      }
    }
  };
  function extract4KHDImages(doc) {
    const images = Array.from(doc.querySelectorAll(
      "figure.wp-block-image img, #basicExample img, .entry-content p img"
    ));
    return images.map((image) => {
      const img = image;
      let src = img.getAttribute("data-src") || img.getAttribute("data-lazy-src") || img.src;
      let thumb = src;
      thumb = thumb.replace(/i\d\.wp\.com\//, "");
      thumb = thumb.replace("pic.4khd.com", "img.4khd.com");
      thumb = thumb.replace(/\/w\d+-rw\//, "/w300-h300-rw/");
      src = src.replace(/i\d\.wp\.com\//, "");
      src = src.replace("pic.4khd.com", "img.4khd.com");
      src = src.replace(/\?.+$/, "");
      src = src.replace(/\/w\d+-rw\//, "/w2500-h2500-rw/");
      return {
        key: src,
        viewerUrl: src,
        preview: { kind: "url", src: thumb }
      };
    }).filter((item) => item.viewerUrl && !item.viewerUrl.includes("avatar"));
  }
  function findPageLink(doc, direction, currentUrl) {
    const pageBox = doc.querySelector(".page-link-box, .pagination, .nav-links, .nav-previous");
    if (!pageBox) return null;
    const current = pageBox.querySelector(".current, .active") || Array.from(pageBox.querySelectorAll("span")).find((span) => !span.querySelector("a"));
    if (current) {
      const currentPageNum = parseInt(current.textContent || "1", 10);
      const targetPage = direction === "next" ? currentPageNum + 1 : currentPageNum - 1;
      if (!Number.isNaN(currentPageNum) && targetPage >= 1) {
        const link = Array.from(pageBox.querySelectorAll("a")).find(
          (anchor) => parseInt(anchor.textContent || "0", 10) === targetPage
        );
        if ((link == null ? void 0 : link.href) && link.href !== currentUrl) return link.href;
      }
    }
    const selector = direction === "next" ? "a.next, a.next.page-numbers" : "a.prev, a.prev.page-numbers";
    const fallback = pageBox.querySelector(selector) || doc.querySelector(selector);
    if ((fallback == null ? void 0 : fallback.href) && fallback.href !== currentUrl) return fallback.href;
    return null;
  }
  function get4KHDNextUrl(doc, currentUrl) {
    return findPageLink(doc, "next", currentUrl);
  }
  function get4KHDPrevUrl(doc, currentUrl) {
    return findPageLink(doc, "prev", currentUrl);
  }
  const parser = new DOMParser();
  const FourKHDAdapter = {
    name: "4KHD",
    scrollPolicy: { defaultEnabled: true, configurable: false },
    match(url) {
      return url.includes("4khd.com") || url.includes("xxtt.ink") || url.includes("uuss.uk") || url.includes("ssuu.uk");
    },
    async loadInitialPage(doc, url) {
      const items = extract4KHDImages(doc);
      const totalPage = 1;
      const pageBox = doc.querySelector(".page-link-box, .pagination, .nav-links, .nav-previous");
      let currentPageNum = 1;
      if (pageBox) {
        const current = pageBox.querySelector(".current, .active") || Array.from(pageBox.querySelectorAll("span")).find((s) => !s.querySelector("a"));
        if (current) {
          currentPageNum = parseInt(current.textContent || "1", 10);
          if (isNaN(currentPageNum)) currentPageNum = 1;
        }
      }
      const perPage = items.length > 0 ? items.length : 20;
      return {
        pageUrl: url,
        items,
        nextUrl: get4KHDNextUrl(doc, url),
        prevUrl: get4KHDPrevUrl(doc, url),
        totalPages: totalPage,
        position: {
          startIndex: (currentPageNum - 1) * perPage,
          pageSize: perPage
        }
      };
    },
    async resolveImage(url) {
      return { src: url };
    },
    async loadPage(url, signal) {
      const response = await fetch(url, { signal });
      if (!response.ok) throw new Error("Failed to fetch page");
      const html = await response.text();
      const doc = parser.parseFromString(html, "text/html");
      const items = extract4KHDImages(doc);
      return {
        pageUrl: url,
        items,
        nextUrl: get4KHDNextUrl(doc, url),
        prevUrl: get4KHDPrevUrl(doc, url)
      };
    },
    getContainer() {
      const entryContent = document.querySelector(".entry-content, .wp-block-post-content");
      if (entryContent) return entryContent;
      const basicExample = document.querySelector("#basicExample");
      if (basicExample && basicExample.parentElement) return basicExample.parentElement;
      return document.querySelector(".post-content");
    },
    hideOriginalElements() {
      const HIDDEN_SELECTORS = [
        ".centbtd",
        ".popup",
        ".wp-container-13",
        ".popup-iframe",
        "#basicExample",
        ".wp-block-image",
        ".page-link-box"
      ];
      const elements = Array.from(document.querySelectorAll(HIDDEN_SELECTORS.join(",")));
      const displays = elements.map((element) => element.style.display);
      elements.forEach((el) => {
        el.style.display = "none";
      });
      return () => elements.forEach((element, index) => {
        element.style.display = displays[index];
      });
    }
  };
  const IMAGE_LOAD_TIMEOUT = {
    foreground: 2e4,
    background: 3e4
  };
  function get18ComicImageLoadTimeout(priority, foregroundPriority = 100) {
    return priority >= foregroundPriority - 10 ? IMAGE_LOAD_TIMEOUT.foreground : IMAGE_LOAD_TIMEOUT.background;
  }
  function isMaterializeData(value) {
    if (!value || typeof value !== "object") return false;
    const data = value;
    return data.kind === "18comic-scramble" && typeof data.aid === "string" && typeof data.imageId === "string";
  }
  function resolve18ComicSource(url) {
    const parsed = new URL(url);
    const aid = parsed.searchParams.get("18aid");
    const scrambleId = parsed.searchParams.get("18scid");
    parsed.searchParams.delete("18aid");
    parsed.searchParams.delete("18scid");
    const src = parsed.toString();
    if (src.includes(".gif") || !aid || !scrambleId || Number(aid) < Number(scrambleId)) {
      return { src };
    }
    const fileName = parsed.pathname.split("/").pop() || "";
    const imageId = fileName.split(".")[0];
    if (!imageId) return { src };
    return {
      src,
      materializeData: { kind: "18comic-scramble", aid, imageId }
    };
  }
  async function materialize18ComicImage(resolved, signal, deps) {
    var _a;
    if (!isMaterializeData(resolved.materializeData)) return resolved;
    const { aid, imageId } = resolved.materializeData;
    const segments = deps.getSegmentCount(aid, imageId);
    if (!segments) throw new Error("18comic segment count is unavailable");
    if (segments <= 1) return { src: resolved.src };
    const sourceBlob = await deps.fetchBlob(resolved.src, signal);
    if (signal.aborted) throw new DOMException("Materialization cancelled", "AbortError");
    const bitmap = await deps.createBitmap(sourceBlob);
    let canvas;
    try {
      if (signal.aborted) throw new DOMException("Materialization cancelled", "AbortError");
      canvas = deps.createCanvas(bitmap.width, bitmap.height);
      const context = canvas.getContext("2d");
      if (!context) throw new Error("18comic 2D canvas context is unavailable");
      context.fillStyle = "#ffffff";
      context.fillRect(0, 0, bitmap.width, bitmap.height);
      const remainder = bitmap.height % segments;
      const segmentHeight = Math.floor(bitmap.height / segments);
      let sourceY = bitmap.height - remainder - segmentHeight;
      let destinationY = remainder + segmentHeight;
      context.drawImage(
        bitmap,
        0,
        sourceY,
        bitmap.width,
        remainder + segmentHeight,
        0,
        0,
        bitmap.width,
        remainder + segmentHeight
      );
      for (let index = 1; index < segments; index++) {
        sourceY -= segmentHeight;
        context.drawImage(
          bitmap,
          0,
          sourceY,
          bitmap.width,
          segmentHeight,
          0,
          destinationY,
          bitmap.width,
          segmentHeight
        );
        destinationY += segmentHeight;
      }
      if (signal.aborted) throw new DOMException("Materialization cancelled", "AbortError");
      const output = await canvas.convertToBlob({ type: "image/jpeg", quality: 0.85 });
      if (signal.aborted) throw new DOMException("Materialization cancelled", "AbortError");
      return {
        src: deps.createObjectUrl(output),
        ownsObjectUrl: true,
        byteSize: output.size,
        decodedDimensions: { width: bitmap.width, height: bitmap.height }
      };
    } finally {
      if (canvas) (_a = deps.releaseCanvas) == null ? void 0 : _a.call(deps, canvas);
      bitmap.close();
    }
  }
  function create18ComicBrowserMaterializer(getSegmentCount) {
    const deps = {
      async fetchBlob(src, signal) {
        const response = await fetch(src, { signal });
        if (!response.ok) throw new Error(`Failed to fetch image: HTTP ${response.status}`);
        return response.blob();
      },
      createBitmap: (blob) => createImageBitmap(blob),
      createCanvas: (width, height) => new OffscreenCanvas(width, height),
      releaseCanvas: (canvas) => {
        const offscreen = canvas;
        offscreen.width = 1;
        offscreen.height = 1;
      },
      createObjectUrl: (blob) => URL.createObjectURL(blob),
      getSegmentCount
    };
    return (resolved, signal) => materialize18ComicImage(resolved, signal, deps);
  }
  function extractChapterItems(doc, pageUrl, useUnsafeFallback, suppressNativeDecode) {
    const html = doc.documentElement.innerHTML;
    const aidMatch = html.match(/aid\s*=\s*['"]?(\d+)['"]?/);
    const scrambleMatch = html.match(/scramble_id\s*=\s*['"]?(\d+)['"]?/);
    const aid = (aidMatch == null ? void 0 : aidMatch[1]) || (useUnsafeFallback && unsafeWindow.aid ? String(unsafeWindow.aid) : "");
    const scrambleId = (scrambleMatch == null ? void 0 : scrambleMatch[1]) || (useUnsafeFallback && unsafeWindow.scramble_id ? String(unsafeWindow.scramble_id) : "");
    const items = [];
    const seenUrls = new Set();
    const images = doc.querySelectorAll(".scramble-page img[id], .owl-item .center img[id]");
    images.forEach((element) => {
      const image = element;
      const source = image.getAttribute("data-original") || image.getAttribute("data-src") || image.src;
      if (!source) return;
      const url = new URL(source, pageUrl);
      if (aid) url.searchParams.set("18aid", aid);
      if (scrambleId) url.searchParams.set("18scid", scrambleId);
      const viewerUrl = url.toString();
      if (!seenUrls.has(viewerUrl)) {
        seenUrls.add(viewerUrl);
        items.push({ key: viewerUrl, viewerUrl, preview: { kind: "none" } });
      }
      if (suppressNativeDecode) {
        try {
          image.getBoundingClientRect = () => ({
            top: 999999,
            left: 0,
            right: 0,
            bottom: 999999,
            width: 0,
            height: 0,
            x: 0,
            y: 0,
            toJSON: () => {
            }
          });
        } catch {
        }
      }
    });
    return items;
  }
  function getNextUrl(doc) {
    var _a;
    const active = doc.querySelector(".pagination li.active");
    const pageLink = (_a = active == null ? void 0 : active.nextElementSibling) == null ? void 0 : _a.querySelector("a:not(.prevnext)");
    if (pageLink) return pageLink.href;
    const nextChapter = Array.from(doc.querySelectorAll('.menu-bolock-ul a[href^="/photo/"]')).find((anchor) => {
      var _a2;
      return (_a2 = anchor.textContent) == null ? void 0 : _a2.includes("涓嬩竴");
    });
    return nextChapter ? nextChapter.href : null;
  }
  function getPrevUrl(doc) {
    var _a;
    const active = doc.querySelector(".pagination li.active");
    const pageLink = (_a = active == null ? void 0 : active.previousElementSibling) == null ? void 0 : _a.querySelector("a:not(.prevnext)");
    if (pageLink) return pageLink.href;
    const prevChapter = Array.from(doc.querySelectorAll('.menu-bolock-ul a[href^="/photo/"]')).find((anchor) => {
      var _a2;
      return (_a2 = anchor.textContent) == null ? void 0 : _a2.includes("涓婁竴");
    });
    return prevChapter ? prevChapter.href : null;
  }
  const materialize = create18ComicBrowserMaterializer((aid, imageId) => {
    if (!unsafeWindow.get_num) return void 0;
    return unsafeWindow.get_num(btoa(aid), btoa(imageId));
  });
  const Comic18Adapter = {
    name: "18comic",
    scrollPolicy: { defaultEnabled: true, configurable: false },
    match(url) {
      return url.includes("18comic.vip") || url.includes("18comic.ink");
    },
    async loadInitialPage(doc, pageUrl) {
      return {
        pageUrl,
        items: extractChapterItems(doc, pageUrl, true, true),
        nextUrl: getNextUrl(doc),
        prevUrl: getPrevUrl(doc)
      };
    },
    async loadPage(url, signal) {
      const response = await fetch(url, { signal });
      if (!response.ok) throw new Error(`Failed to fetch page: HTTP ${response.status}`);
      const html = await response.text();
      const doc = new DOMParser().parseFromString(html, "text/html");
      return {
        pageUrl: url,
        items: extractChapterItems(doc, url, false, false),
        nextUrl: getNextUrl(doc),
        prevUrl: getPrevUrl(doc)
      };
    },
    async resolveImage(url, context) {
      const loadTimeoutMs = get18ComicImageLoadTimeout(
        context.priority,
        LOAD_PRIORITY.foreground
      );
      try {
        return { ...resolve18ComicSource(url), loadTimeoutMs };
      } catch {
        return { src: url, loadTimeoutMs };
      }
    },
    async materializeImage(resolved, signal) {
      return materialize(resolved, signal);
    },
    getContainer() {
      return document.querySelector(".scramble-page") || document.body;
    },
    hideOriginalElements() {
      const pages = Array.from(document.querySelectorAll(".scramble-page"));
      const removed = [
        ...pages.slice(1),
        ...Array.from(document.querySelectorAll(".owl-carousel"))
      ].map((element) => ({ element, parent: element.parentNode, next: element.nextSibling }));
      removed.forEach(({ element }) => element.remove());
      return () => {
        for (const { element, parent, next } of removed) {
          if (!parent || element.parentNode) continue;
          parent.insertBefore(element, (next == null ? void 0 : next.parentNode) === parent ? next : null);
        }
      };
    }
  };
  const adapters = [EHentaiAdapter, FourKHDAdapter, Comic18Adapter];
  const SiteManager = {
    register(adapter) {
      adapters.push(adapter);
    },
    getAdapter(url, doc = document) {
      return adapters.find((a) => a.match(url, doc)) || null;
    }
  };
  class EmptyGalleryPageError extends Error {
    constructor(url) {
      super(`Gallery page has no items: ${url}`);
      this.name = "EmptyGalleryPageError";
    }
  }
  class GalleryPageLoader {
    constructor(adapter) {
      this.inFlight = new Map();
      this.loadedUrls = new Set();
      this.retryDelaysMs = [1e3, 2e3, 4e3];
      this.adapter = adapter;
    }
    async loadInitialPage(doc, url) {
      const page = this.normalize(url, await this.adapter.loadInitialPage(doc, url));
      this.markLoaded(url, page.pageUrl);
      return page;
    }
    loadPage(url, signal) {
      if (this.loadedUrls.has(url)) return Promise.resolve(null);
      const existing = this.inFlight.get(url);
      if (existing) return existing;
      const request = this.fetchWithRetry(url, signal).then((page) => {
        if (page.items.length === 0) throw new EmptyGalleryPageError(url);
        const normalized = this.normalize(url, page);
        this.markLoaded(url, normalized.pageUrl);
        return normalized;
      }).finally(() => {
        this.inFlight.delete(url);
      });
      this.inFlight.set(url, request);
      return request;
    }
    async fetchWithRetry(url, signal) {
      for (let attempt = 0; ; attempt++) {
        try {
          return await this.adapter.loadPage(url, signal);
        } catch (error) {
          if ((signal == null ? void 0 : signal.aborted) || (error == null ? void 0 : error.name) === "AbortError") {
            throw error;
          }
          if (attempt >= this.retryDelaysMs.length) throw error;
          await this.waitForRetry(this.retryDelaysMs[attempt], signal);
        }
      }
    }
    waitForRetry(delayMs, signal) {
      if (signal == null ? void 0 : signal.aborted) return Promise.reject(new DOMException("Request cancelled", "AbortError"));
      return new Promise((resolve, reject) => {
        const timer = setTimeout(finish, delayMs);
        const onAbort = () => {
          clearTimeout(timer);
          signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
          reject(new DOMException("Request cancelled", "AbortError"));
        };
        function finish() {
          signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
          resolve();
        }
        signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
      });
    }
    hasLoaded(url) {
      return this.loadedUrls.has(url);
    }
    markLoaded(...urls) {
      for (const url of urls) {
        if (url) this.loadedUrls.add(url);
      }
    }
    normalize(requestedUrl, page) {
      const isLoop = (url) => !!url && (url === requestedUrl || url === page.pageUrl || this.loadedUrls.has(url));
      return {
        ...page,
        nextUrl: isLoop(page.nextUrl) ? null : page.nextUrl,
        prevUrl: isLoop(page.prevUrl) ? null : page.prevUrl
      };
    }
  }
  const defaultPolicy = {
    resolveAttempts: 4,
    alternateSourceRetries: 3,
    freshResolveRetries: 2,
    retryDelay: 1e3,
    cacheEntries: 80,
    ownedObjectUrlEntries: 24,
    ownedBlobBytes: 96 * 1024 * 1024
  };
  class ImageLoadService {
    constructor(deps, policy = {}) {
      this.active = new Map();
      this.cache = new Map();
      this.phases = new Map();
      this.listeners = new Map();
      this.deps = deps;
      this.policy = { ...defaultPolicy, ...policy };
    }
    acquire(url, options) {
      var _a, _b;
      const token = Symbol(options.intent);
      const requestedLane = options.lane ?? this.defaultLane(options.intent);
      const cached = this.cache.get(url);
      if (cached) {
        this.touchCache(url, cached);
        cached.leases.add(token);
        this.setPhase(url, "ready");
        return this.createLease(url, token, Promise.resolve(cached.asset));
      }
      let load = this.active.get(url);
      if (load) {
        load.leases.add(token);
        if (this.canRetrySource(options.intent)) load.sourceRetryLeases.add(token);
        const shouldPromoteLane = requestedLane === "foreground" && load.lane === "background";
        if (options.priority > load.priority || shouldPromoteLane) {
          load.priority = options.priority;
          if (shouldPromoteLane) load.lane = "foreground";
          (_b = (_a = this.deps).promote) == null ? void 0 : _b.call(_a, url, load.priority, load.lane);
        }
        return this.createLease(url, token, load.result);
      }
      const controller = new AbortController();
      load = {
        controller,
        leases: new Set([token]),
        sourceRetryLeases: new Set(this.canRetrySource(options.intent) ? [token] : []),
        priority: options.priority,
        lane: requestedLane,
        result: Promise.resolve(null)
      };
      const task = () => this.run(url, load);
      const scheduled = this.deps.schedule ? this.deps.schedule(url, task, {
        priority: load.priority,
        lane: load.lane,
        signal: controller.signal
      }) : task();
      load.result = scheduled.catch((error) => {
        if (controller.signal.aborted || (error == null ? void 0 : error.name) === "AbortError") {
          return this.finishWithoutAsset(url, controller.signal);
        }
        throw error;
      }).finally(() => {
        if (this.active.get(url) === load) this.active.delete(url);
      });
      this.active.set(url, load);
      return this.createLease(url, token, load.result);
    }
    getPhase(url) {
      return this.phases.get(url) ?? "idle";
    }
    getCached(url) {
      var _a;
      return (_a = this.cache.get(url)) == null ? void 0 : _a.asset;
    }
getDimensionsHint(url) {
      var _a;
      return (_a = this.active.get(url)) == null ? void 0 : _a.dimensionsHint;
    }
    getLatestCached() {
      let latest;
      for (const entry of this.cache.values()) latest = entry.asset;
      return latest;
    }
getStats() {
      let activeLeases = 0;
      let cachedLeases = 0;
      let leasedCacheEntries = 0;
      let ownedObjectUrls = 0;
      let ownedBlobBytes = 0;
      let listeners = 0;
      for (const load of this.active.values()) activeLeases += load.leases.size;
      for (const entry of this.cache.values()) {
        cachedLeases += entry.leases.size;
        if (entry.leases.size > 0) leasedCacheEntries++;
        if (entry.asset.ownsObjectUrl) {
          ownedObjectUrls++;
          ownedBlobBytes += entry.asset.byteSize ?? 0;
        }
      }
      for (const subscribers of this.listeners.values()) listeners += subscribers.size;
      return {
        activeLoads: this.active.size,
        cachedEntries: this.cache.size,
        activeLeases,
        cachedLeases,
        leasedCacheEntries,
        ownedObjectUrls,
        ownedBlobBytes,
        phases: this.phases.size,
        listeners
      };
    }
    async run(url, load) {
      var _a, _b, _c, _d;
      const signal = load.controller.signal;
      let resolved = null;
      for (let attempt = 0; attempt < this.policy.resolveAttempts && !signal.aborted; attempt++) {
        this.setPhase(url, "resolving");
        const candidate = await this.resolve(url, void 0, attempt > 0, load);
        resolved = await this.materialize(url, candidate, load);
        this.rememberDimensionsHint(load, resolved == null ? void 0 : resolved.sourceDimensions);
        if (resolved == null ? void 0 : resolved.src) break;
        if (attempt + 1 < this.policy.resolveAttempts) await this.wait(signal);
      }
      if (!(resolved == null ? void 0 : resolved.src) || signal.aborted) {
        this.discard(url, resolved);
        return this.finishWithoutAsset(url, signal);
      }
      let loaded = await this.tryLoad(url, resolved, load);
      let retryToken = resolved.retryToken;
      const attemptedSources = new Set([resolved.src]);
      let attemptedAlternateResolve = false;
      for (let attempt = 0; !loaded && retryToken && load.sourceRetryLeases.size > 0 && attempt < this.policy.alternateSourceRetries && !signal.aborted; attempt++) {
        this.setPhase(url, "switching-source");
        attemptedAlternateResolve = true;
        const candidate = await this.resolve(url, retryToken, true, load);
        const next = await this.materialize(url, candidate, load);
        if (!(next == null ? void 0 : next.src)) continue;
        retryToken = next.retryToken;
        if (attemptedSources.has(next.src)) {
          this.discard(url, next);
          if (!retryToken) break;
          continue;
        }
        attemptedSources.add(next.src);
        this.discard(url, resolved);
        resolved = next;
        this.rememberDimensionsHint(load, next.sourceDimensions);
        loaded = await this.tryLoad(url, next, load);
      }
      for (let attempt = 0; !loaded && load.sourceRetryLeases.size > 0 && attempt < this.policy.freshResolveRetries && !signal.aborted; attempt++) {
        await this.wait(signal);
        if (signal.aborted) break;
        const candidate = await this.resolve(url, void 0, true, load);
        const next = await this.materialize(url, candidate, load);
        if (!(next == null ? void 0 : next.src)) continue;
        if (attemptedAlternateResolve && attemptedSources.has(next.src)) {
          this.discard(url, next);
          break;
        }
        attemptedSources.add(next.src);
        this.discard(url, resolved);
        resolved = next;
        this.rememberDimensionsHint(load, next.sourceDimensions);
        loaded = await this.tryLoad(url, next, load);
      }
      if (!loaded || signal.aborted) {
        this.discard(url, resolved);
        return this.finishWithoutAsset(url, signal);
      }
      const asset = {
        ...resolved,
        width: loaded.width,
        height: loaded.height
      };
      this.cache.set(url, { asset, leases: load.leases });
      (_b = (_a = this.deps).setResolvedSource) == null ? void 0 : _b.call(_a, url, asset.src);
      try {
        (_d = (_c = this.deps).onReady) == null ? void 0 : _d.call(_c, url, asset);
      } catch {
      }
      this.touchCache(url, this.cache.get(url));
      this.setPhase(url, "ready");
      this.trimCache();
      return asset;
    }
    async tryLoad(url, resolved, load) {
      if (load.controller.signal.aborted) return null;
      const decoded = resolved.decodedDimensions;
      if (decoded && Number.isFinite(decoded.width) && Number.isFinite(decoded.height) && decoded.width > 0 && decoded.height > 0) {
        return decoded;
      }
      this.setPhase(url, "downloading");
      try {
        return await this.runSourceAttempt(
          load.controller.signal,
          resolved.loadTimeoutMs,
          (signal) => this.deps.loadBytes(
            resolved.src,
            signal,
            load.priority,
            load.lane
          )
        );
      } catch {
        return null;
      }
    }
    async resolve(url, retryToken, force, load) {
      try {
        return await this.deps.resolve(url, {
          retryToken,
          force,
          priority: load.priority,
          signal: load.controller.signal
        });
      } catch {
        return null;
      }
    }
    async materialize(url, resolved, load) {
      if (!(resolved == null ? void 0 : resolved.src) || load.controller.signal.aborted) return resolved;
      const materialize2 = this.deps.materialize;
      if (!materialize2) return resolved;
      try {
        const materialized = await this.runSourceAttempt(
          load.controller.signal,
          resolved.loadTimeoutMs,
          (signal) => materialize2(url, resolved, signal, load.priority)
        );
        if (!materialized) this.discard(url, resolved);
        return materialized;
      } catch {
        this.discard(url, resolved);
        return null;
      }
    }
async runSourceAttempt(parentSignal, timeoutMs, task) {
      const attemptController = new AbortController();
      const abortAttempt = () => attemptController.abort();
      if (parentSignal.aborted) abortAttempt();
      else parentSignal.addEventListener("abort", abortAttempt, { once: true });
      const timeout = timeoutMs && timeoutMs > 0 ? setTimeout(abortAttempt, timeoutMs) : null;
      try {
        return await task(attemptController.signal);
      } finally {
        if (timeout !== null) clearTimeout(timeout);
        parentSignal.removeEventListener("abort", abortAttempt);
      }
    }
    discard(url, resolved) {
      var _a, _b, _c, _d;
      if (!(resolved == null ? void 0 : resolved.ownsObjectUrl)) return;
      (_b = (_a = this.deps).invalidateResolved) == null ? void 0 : _b.call(_a, url, resolved.src);
      (_d = (_c = this.deps).revokeObjectUrl) == null ? void 0 : _d.call(_c, resolved.src);
    }
    finishWithoutAsset(url, signal) {
      this.setPhase(url, signal.aborted ? "cancelled" : "error");
      return null;
    }
    async wait(signal) {
      if (signal.aborted) return;
      const delay = this.deps.delay ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
      await new Promise((resolve) => {
        const finish = () => {
          signal.removeEventListener("abort", finish);
          resolve();
        };
        signal.addEventListener("abort", finish, { once: true });
        delay(this.policy.retryDelay).then(finish, finish);
      });
    }
    createLease(url, token, result) {
      let released = false;
      const ownedListeners = new Set();
      const service = this;
      return {
        url,
        result,
        get phase() {
          return service.getPhase(url);
        },
        subscribe(listener) {
          let listeners = service.listeners.get(url);
          if (!listeners) {
            listeners = new Set();
            service.listeners.set(url, listeners);
          }
          listeners.add(listener);
          ownedListeners.add(listener);
          listener(service.getPhase(url));
          return () => {
            listeners.delete(listener);
            ownedListeners.delete(listener);
            if (listeners.size === 0) service.listeners.delete(url);
          };
        },
        release() {
          var _a, _b;
          if (released) return;
          released = true;
          for (const listener of ownedListeners) (_a = service.listeners.get(url)) == null ? void 0 : _a.delete(listener);
          ownedListeners.clear();
          if (((_b = service.listeners.get(url)) == null ? void 0 : _b.size) === 0) service.listeners.delete(url);
          service.release(url, token);
        }
      };
    }
    release(url, token) {
      const active = this.active.get(url);
      if (active == null ? void 0 : active.leases.delete(token)) {
        active.sourceRetryLeases.delete(token);
        if (active.leases.size === 0) {
          active.controller.abort();
          this.active.delete(url);
          this.setPhase(url, "cancelled");
          return;
        }
      }
      const cached = this.cache.get(url);
      if (cached == null ? void 0 : cached.leases.delete(token)) this.trimCache();
    }
    setPhase(url, phase) {
      var _a;
      if (this.phases.get(url) === phase) return;
      this.phases.set(url, phase);
      (_a = this.listeners.get(url)) == null ? void 0 : _a.forEach((listener) => listener(phase));
    }
canRetrySource(intent) {
      return intent === "foreground" || intent === "neighbor" || intent === "scroll";
    }
    rememberDimensionsHint(load, dimensions) {
      if (!dimensions || !Number.isFinite(dimensions.width) || !Number.isFinite(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) {
        load.dimensionsHint = void 0;
        return;
      }
      load.dimensionsHint = {
        width: dimensions.width,
        height: dimensions.height
      };
    }
    defaultLane(intent) {
      return intent === "foreground" || intent === "neighbor" ? "foreground" : "background";
    }
    touchCache(url, entry) {
      this.cache.delete(url);
      this.cache.set(url, entry);
    }
    trimCache() {
      var _a, _b, _c, _d, _e, _f;
      let ownedEntries = 0;
      let ownedBytes = 0;
      for (const entry of this.cache.values()) {
        if (!entry.asset.ownsObjectUrl) continue;
        ownedEntries++;
        ownedBytes += entry.asset.byteSize ?? 0;
      }
      const isOverBudget = () => this.cache.size > this.policy.cacheEntries || ownedEntries > this.policy.ownedObjectUrlEntries || ownedBytes > this.policy.ownedBlobBytes;
      if (!isOverBudget()) return;
      for (const [url, entry] of this.cache) {
        if (!isOverBudget()) break;
        if (entry.leases.size > 0) continue;
        const generalOverflow = this.cache.size > this.policy.cacheEntries;
        if (!generalOverflow && !entry.asset.ownsObjectUrl) continue;
        this.cache.delete(url);
        if (entry.asset.ownsObjectUrl) {
          ownedEntries--;
          ownedBytes -= entry.asset.byteSize ?? 0;
        }
        (_b = (_a = this.deps).invalidateResolved) == null ? void 0 : _b.call(_a, url, entry.asset.src);
        (_d = (_c = this.deps).onEvict) == null ? void 0 : _d.call(_c, url, entry.asset);
        if (entry.asset.ownsObjectUrl) {
          (_f = (_e = this.deps).revokeObjectUrl) == null ? void 0 : _f.call(_e, entry.asset.src);
        }
        if (!this.active.has(url)) this.phases.delete(url);
      }
    }
  }
  class MaterializeScheduler {
    constructor(maxConcurrent) {
      this.queue = [];
      this.active = 0;
      this.sequence = 0;
      this.maxConcurrent = maxConcurrent;
    }
    run(key, task, priority, signal) {
      if (signal.aborted) return Promise.reject(new DOMException("Materialization cancelled", "AbortError"));
      return new Promise((resolve, reject) => {
        const job = {
          key,
          priority,
          sequence: this.sequence++,
          signal,
          task,
          resolve,
          reject,
          onAbort: () => {
          }
        };
        job.onAbort = () => {
          const index = this.queue.indexOf(job);
          if (index === -1) return;
          this.queue.splice(index, 1);
          reject(new DOMException("Materialization cancelled", "AbortError"));
        };
        signal.addEventListener("abort", job.onAbort, { once: true });
        this.queue.push(job);
        this.pump();
      });
    }
    promote(key, priority) {
      for (const job of this.queue) {
        if (job.key === key && priority > job.priority) job.priority = priority;
      }
    }
    pump() {
      while (this.active < this.maxConcurrent && this.queue.length > 0) {
        let bestIndex = 0;
        for (let index = 1; index < this.queue.length; index++) {
          const candidate = this.queue[index];
          const best = this.queue[bestIndex];
          if (candidate.priority > best.priority || candidate.priority === best.priority && candidate.sequence < best.sequence) {
            bestIndex = index;
          }
        }
        const job = this.queue.splice(bestIndex, 1)[0];
        job.signal.removeEventListener("abort", job.onAbort);
        if (job.signal.aborted) {
          job.reject(new DOMException("Materialization cancelled", "AbortError"));
          continue;
        }
        this.active++;
        Promise.resolve().then(job.task).then(job.resolve, job.reject).finally(() => {
          this.active--;
          this.pump();
        });
      }
    }
  }
  const DEFAULT_LIMITS = { total: 4, background: 2 };
  class ImageTaskScheduler {
    constructor(getLimits = () => DEFAULT_LIMITS) {
      this.queue = [];
      this.activeJobs = new Set();
      this.sequence = 0;
      this.activeBackground = 0;
      this.getLimits = getLimits;
    }
    run(key, task, options) {
      if (options.signal.aborted) {
        return Promise.reject(new DOMException("Image task cancelled", "AbortError"));
      }
      return new Promise((resolve, reject) => {
        const job = {
          key,
          priority: options.priority,
          lane: options.lane,
          sequence: this.sequence++,
          signal: options.signal,
          task,
          resolve,
          reject,
          onAbort: () => {
          }
        };
        job.onAbort = () => {
          const index = this.queue.indexOf(job);
          if (index === -1) return;
          this.queue.splice(index, 1);
          reject(new DOMException("Image task cancelled", "AbortError"));
        };
        options.signal.addEventListener("abort", job.onAbort, { once: true });
        this.queue.push(job);
        this.pump();
      });
    }
promote(key, priority, lane = "foreground") {
      for (const job of this.queue) {
        if (job.key !== key) continue;
        if (priority > job.priority) job.priority = priority;
        if (lane === "foreground") job.lane = "foreground";
      }
      for (const job of this.activeJobs) {
        if (job.key !== key) continue;
        if (priority > job.priority) job.priority = priority;
        if (lane === "foreground" && job.lane === "background") {
          job.lane = "foreground";
          this.activeBackground--;
        }
      }
      this.pump();
    }
notifyLimitsChanged() {
      this.pump();
    }
    getStats() {
      return {
        active: this.activeJobs.size,
        activeBackground: this.activeBackground,
        queued: this.queue.length,
        queuedBackground: this.queue.filter((job) => job.lane === "background").length
      };
    }
    normalizedLimits() {
      const limits = this.getLimits();
      const total = Math.max(1, Math.floor(limits.total));
      return {
        total,
        background: Math.max(0, Math.min(total, Math.floor(limits.background)))
      };
    }
    pump() {
      const limits = this.normalizedLimits();
      while (this.activeJobs.size < limits.total) {
        let bestIndex = -1;
        for (let index = 0; index < this.queue.length; index++) {
          const candidate = this.queue[index];
          if (candidate.lane === "background" && this.activeBackground >= limits.background) continue;
          if (bestIndex === -1) {
            bestIndex = index;
            continue;
          }
          const best = this.queue[bestIndex];
          if (candidate.priority > best.priority || candidate.priority === best.priority && candidate.sequence < best.sequence) {
            bestIndex = index;
          }
        }
        if (bestIndex === -1) return;
        const job = this.queue.splice(bestIndex, 1)[0];
        job.signal.removeEventListener("abort", job.onAbort);
        if (job.signal.aborted) {
          job.reject(new DOMException("Image task cancelled", "AbortError"));
          continue;
        }
        this.activeJobs.add(job);
        if (job.lane === "background") this.activeBackground++;
        Promise.resolve().then(job.task).then(job.resolve, job.reject).finally(() => {
          this.activeJobs.delete(job);
          if (job.lane === "background") this.activeBackground--;
          this.pump();
        });
      }
    }
  }
  const materializeScheduler = new MaterializeScheduler(CFG.imageMaterializeConcurrent);
  function currentImageTaskLimits() {
    const connection = navigator.connection;
    if (document.visibilityState === "hidden" || (connection == null ? void 0 : connection.saveData)) {
      return { total: 4, background: 0 };
    }
    if ((connection == null ? void 0 : connection.effectiveType) === "slow-2g" || (connection == null ? void 0 : connection.effectiveType) === "2g") {
      return { total: 2, background: 1 };
    }
    return { total: 4, background: 2 };
  }
  const imageTaskScheduler = new ImageTaskScheduler(currentImageTaskLimits);
  document.addEventListener("visibilitychange", () => imageTaskScheduler.notifyLimitsChanged());
  const networkConnection = navigator.connection;
  networkConnection == null ? void 0 : networkConnection.addEventListener("change", () => imageTaskScheduler.notifyLimitsChanged());
  function loadImageBytes(src, signal, _priority, lane) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.decoding = "async";
      img.fetchPriority = lane === "foreground" ? "high" : "low";
      const cleanup = () => {
        signal.removeEventListener("abort", onAbort);
        img.onload = null;
        img.onerror = null;
      };
      const onAbort = () => {
        cleanup();
        img.src = "data:,";
        reject(new DOMException("Image load cancelled", "AbortError"));
      };
      img.onload = () => {
        const dimensions = { width: img.naturalWidth, height: img.naturalHeight };
        cleanup();
        resolve(dimensions);
      };
      img.onerror = () => {
        cleanup();
        reject(new Error(`Image byte load failed: ${src}`));
      };
      signal.addEventListener("abort", onAbort, { once: true });
      if (signal.aborted) onAbort();
      else img.src = src;
    });
  }
  const imageLoadService = new ImageLoadService({
    resolve: async (url, context) => {
      var _a;
      return ((_a = store.activeAdapter) == null ? void 0 : _a.resolveImage(url, context)) ?? null;
    },
    materialize: (url, resolved, signal, priority) => {
      var _a;
      const materialize2 = (_a = store.activeAdapter) == null ? void 0 : _a.materializeImage;
      if (!materialize2) return Promise.resolve(resolved);
      return materializeScheduler.run(
        url,
        () => materialize2(resolved, signal),
        priority,
        signal
      );
    },
    loadBytes: loadImageBytes,
    schedule: (url, task, options) => imageTaskScheduler.run(url, task, options),
    promote: (url, priority, lane) => {
      var _a, _b, _c;
      imageTaskScheduler.promote(url, priority, lane);
      materializeScheduler.promote(url, priority);
      (_c = (_b = (_a = store.activeAdapter) == null ? void 0 : _a.imageRequestQueue) == null ? void 0 : _b.promote) == null ? void 0 : _c.call(_b, url, priority);
    },
    revokeObjectUrl: (src) => URL.revokeObjectURL(src)
  });
  function acquireImage(url, options) {
    return imageLoadService.acquire(url, options);
  }
  function getCachedImage(url) {
    return imageLoadService.getCached(url);
  }
  function getImageDimensionsHint(url) {
    return imageLoadService.getDimensionsHint(url);
  }
  function getLatestCachedImage() {
    return imageLoadService.getLatestCached();
  }
  function cancelImagePrefetch(keepUrls) {
    var _a, _b, _c;
    (_c = (_b = (_a = store.activeAdapter) == null ? void 0 : _a.imageRequestQueue) == null ? void 0 : _b.cancelPrefetch) == null ? void 0 : _c.call(_b, keepUrls);
  }
  const imageLoadedListeners = new Set();
  function notifyScrollImageLoaded(event) {
    imageLoadedListeners.forEach((listener) => listener(event));
  }
  function subscribeScrollImageLoaded(listener) {
    imageLoadedListeners.add(listener);
    return () => imageLoadedListeners.delete(listener);
  }
  function validGeometry(geometry) {
    return Number.isFinite(geometry.width) && Number.isFinite(geometry.height) && geometry.width > 0 && geometry.height > 0;
  }
  function findScrollItem(itemKey, fallbackIndex, preferred) {
    if ((preferred == null ? void 0 : preferred.isConnected) && (preferred.dataset.itemKey === itemKey || preferred.dataset.viewerUrl === itemKey || preferred.dataset.url === itemKey)) {
      return preferred;
    }
    const items = Array.from(document.querySelectorAll(".r-img, .r-ph"));
    return items.find(
      (element) => element.dataset.itemKey === itemKey || element.dataset.viewerUrl === itemKey || element.dataset.url === itemKey
    ) ?? items[fallbackIndex] ?? null;
  }
  function applyKnownImageGeometry(element, geometry) {
    if (!validGeometry(geometry)) return false;
    const width = Math.round(geometry.width);
    const height = Math.round(geometry.height);
    element.style.aspectRatio = `${width} / ${height}`;
    element.style.width = "100%";
    element.style.maxWidth = `${width}px`;
    element.style.height = "auto";
    if (element.classList.contains("r-ph")) {
      element.style.minHeight = "0";
      element.style.marginBottom = "20px";
    }
    return true;
  }
  function withInstantDocumentScroll(action) {
    const rootBehavior = document.documentElement.style.scrollBehavior;
    const bodyBehavior = document.body.style.scrollBehavior;
    const alreadyForced = document.documentElement.classList.contains("hr-instant-scroll");
    document.documentElement.classList.add("hr-instant-scroll");
    document.documentElement.style.scrollBehavior = "auto";
    document.body.style.scrollBehavior = "auto";
    try {
      action();
    } finally {
      document.documentElement.style.scrollBehavior = rootBehavior;
      document.body.style.scrollBehavior = bodyBehavior;
      if (!alreadyForced) document.documentElement.classList.remove("hr-instant-scroll");
    }
  }
  function restoreDocumentScroll(top) {
    if (!Number.isFinite(top) || Math.abs(window.scrollY - top) < 2) return;
    withInstantDocumentScroll(() => window.scrollTo({ top, behavior: "auto" }));
  }
  function isScrollItemCentered(target, tolerance = 2) {
    const rect = target.getBoundingClientRect();
    const centeredTop = (window.innerHeight - rect.height) / 2;
    return Math.abs(rect.top - centeredTop) < tolerance;
  }
  function jumpToScrollItem(target) {
    if (!target.isConnected) return;
    const rect = target.getBoundingClientRect();
    const top = window.scrollY + rect.top - (window.innerHeight - rect.height) / 2;
    withInstantDocumentScroll(() => window.scrollTo({
      top: Math.max(0, top),
      behavior: "auto"
    }));
  }
  function restoreReaderScroll(request) {
    for (const geometry of request.nearbyGeometry ?? []) {
      const element = findScrollItem(geometry.key, geometry.index, geometry.preferred);
      if (element) applyKnownImageGeometry(element, geometry);
    }
    if (!request.target) {
      restoreDocumentScroll(request.entryScrollY);
      return;
    }
    const target = findScrollItem(
      request.target.key,
      request.target.index,
      request.target.preferred
    );
    if (target && !isScrollItemCentered(target)) jumpToScrollItem(target);
  }
  const isZH = navigator.language.toLowerCase().includes("zh");
  const i18n = {
readerMode: isZH ? "阅读器模式" : "Reader Mode",
    settings: isZH ? "设置" : "Settings",
    backToTop: isZH ? "回到顶部" : "Back to Top",
    play: isZH ? "播放" : "Play",
    pause: isZH ? "暂停" : "Pause",
scrollMode: isZH ? "卷轴模式" : "Scroll Mode",
    autoEnter: isZH ? "自动进入阅读器" : "Auto Enter Reader",
    clickToEnter: isZH ? "点击图片进入阅读器" : "Click Image to Enter Reader",
    doublePageMode: isZH ? "动态双页阅读" : "Dynamic Two-page View",
    playSpeed: isZH ? "自动翻页间隔 (秒)" : "Auto Play Interval (s)",
    thumbnailPosition: isZH ? "缩略图位置" : "Thumbnail Position",
    posBottom: isZH ? "底部" : "Bottom",
    posTop: isZH ? "顶部" : "Top",
    posLeft: isZH ? "左侧" : "Left",
    posRight: isZH ? "右侧" : "Right",
    close: isZH ? "关闭" : "Close",
    downloading: isZH ? "下载中..." : "Downloading...",
    switchingImageSource: isZH ? "切换节点中..." : "Switching source...",
    loadFailed: isZH ? "加载失败" : "Load Failed",
    waitImagesToLoad: isZH ? "请等待图片加载" : "Please wait for images to load",
    resolvingImage: isZH ? "解析图片真实地址中..." : "Resolving Image URL...",
    paginationFailed: isZH ? "加载下一页失败" : "Failed to load the next page",
    retry: isZH ? "重试" : "Retry"
  };
  function setErrorState(placeholder, pIndex, index) {
    placeholder.className = "r-ph sp-placeholder error";
    placeholder.innerHTML = `
    <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; transform: translateY(-20px);">
      <div style="display: flex; align-items: center; gap: 10px; background: rgba(200, 40, 40, 0.8); border: 1px solid rgba(255, 255, 255, 0.2); padding: 10px 20px; border-radius: 30px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); backdrop-filter: blur(8px); margin-bottom: 16px;">
        <svg style="color: #fff; width: 20px; height: 20px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
          <circle cx="12" cy="12" r="10"></circle>
          <line x1="12" y1="8" x2="12" y2="12"></line>
          <line x1="12" y1="16" x2="12.01" y2="16"></line>
        </svg>
        <div style="font-size: 15px; color: #fff; font-weight: 500; letter-spacing: 0.5px;">Load Failed</div>
      </div>
      <div style="font-size: 14px; color: rgba(255, 255, 255, 0.5); font-family: monospace; letter-spacing: 1px;">P${pIndex}-${index + 1}</div>
    </div>
  `;
  }
  let lazyLoadObserver = null;
  const placeholderLeases = new WeakMap();
  const pendingPlaceholders = new Set();
  let pendingLoadObserver = null;
  const imageElementLeases = new WeakMap();
  const ownedPlaceholders = new WeakMap();
  const ownedImages = new Set();
  let ownedImageObserver = null;
  let ownedObserverResizeRaf = 0;
  let pendingSweepRaf = 0;
  function stopObservingPendingLoad(placeholder, lease) {
    if (placeholderLeases.get(placeholder) !== lease) return;
    pendingLoadObserver == null ? void 0 : pendingLoadObserver.unobserve(placeholder);
    pendingPlaceholders.delete(placeholder);
  }
  function cancelPendingLoad(placeholder, reobserve) {
    const lease = placeholderLeases.get(placeholder);
    pendingLoadObserver == null ? void 0 : pendingLoadObserver.unobserve(placeholder);
    pendingPlaceholders.delete(placeholder);
    if (!lease) return;
    placeholderLeases.delete(placeholder);
    delete placeholder.dataset.isFetching;
    delete placeholder.dataset.lazyLoaded;
    lease.release();
    if (reobserve && placeholder.isConnected) lazyLoadObserver == null ? void 0 : lazyLoadObserver.observe(placeholder);
  }
  function rebuildPendingLoadObserver() {
    pendingLoadObserver == null ? void 0 : pendingLoadObserver.disconnect();
    const cancelDistance = Math.max(2e3, Math.max(1, window.innerHeight) * 6);
    pendingLoadObserver = new IntersectionObserver((entries) => {
      for (const entry of entries) {
        if (!entry.isIntersecting) cancelPendingLoad(entry.target, true);
      }
    }, { rootMargin: `${cancelDistance}px 0px ${cancelDistance}px 0px` });
    pendingPlaceholders.forEach((placeholder) => pendingLoadObserver == null ? void 0 : pendingLoadObserver.observe(placeholder));
  }
  function observePendingLoad(placeholder) {
    pendingPlaceholders.add(placeholder);
    if (!pendingLoadObserver) rebuildPendingLoadObserver();
    pendingLoadObserver == null ? void 0 : pendingLoadObserver.observe(placeholder);
  }
  function cancelFarPendingLoads() {
    if (document.documentElement.classList.contains("hr-reader-open")) return;
    const cancelDistance = Math.max(2e3, Math.max(1, window.innerHeight) * 6);
    for (const placeholder of pendingPlaceholders) {
      const rect = placeholder.getBoundingClientRect();
      if (rect.bottom < -cancelDistance || rect.top > window.innerHeight + cancelDistance) {
        cancelPendingLoad(placeholder, true);
      }
    }
  }
  window.addEventListener("scroll", () => {
    cancelAnimationFrame(pendingSweepRaf);
    pendingSweepRaf = requestAnimationFrame(cancelFarPendingLoads);
  }, { passive: true });
  function restoreOwnedImage(image) {
    const placeholder = ownedPlaceholders.get(image);
    const lease = imageElementLeases.get(image);
    if (!placeholder || !lease || !image.parentNode) {
      ownedImageObserver == null ? void 0 : ownedImageObserver.unobserve(image);
      ownedImages.delete(image);
      return;
    }
    delete placeholder.dataset.lazyLoaded;
    delete placeholder.dataset.isFetching;
    applyKnownImageGeometry(placeholder, {
      width: Number(image.dataset.naturalWidth) || image.getBoundingClientRect().width,
      height: Number(image.dataset.naturalHeight) || image.getBoundingClientRect().height
    });
    ownedImageObserver == null ? void 0 : ownedImageObserver.unobserve(image);
    ownedImages.delete(image);
    image.parentNode.replaceChild(placeholder, image);
    imageElementLeases.delete(image);
    ownedPlaceholders.delete(image);
    lease.release();
    lazyLoadObserver == null ? void 0 : lazyLoadObserver.observe(placeholder);
  }
  function rebuildOwnedImageObserver() {
    ownedImageObserver == null ? void 0 : ownedImageObserver.disconnect();
    const releaseDistance = Math.max(1, window.innerHeight) * 6;
    ownedImageObserver = new IntersectionObserver((entries) => {
      if (document.documentElement.classList.contains("hr-reader-open")) return;
      for (const entry of entries) {
        if (!entry.isIntersecting) restoreOwnedImage(entry.target);
      }
    }, { rootMargin: `${releaseDistance}px 0px ${releaseDistance}px 0px` });
    ownedImages.forEach((image) => ownedImageObserver == null ? void 0 : ownedImageObserver.observe(image));
  }
  function observeOwnedImage(image) {
    ownedImages.add(image);
    if (!ownedImageObserver) rebuildOwnedImageObserver();
    ownedImageObserver == null ? void 0 : ownedImageObserver.observe(image);
  }
  window.addEventListener("resize", () => {
    cancelAnimationFrame(ownedObserverResizeRaf);
    ownedObserverResizeRaf = requestAnimationFrame(() => {
      rebuildOwnedImageObserver();
      rebuildPendingLoadObserver();
    });
  }, { passive: true });
  function scheduleAutomaticRetry(placeholder, pIndex, index) {
    delete placeholder.dataset.isFetching;
    setErrorState(placeholder, pIndex, index);
    const attempts = parseInt(placeholder.dataset.autoRetryAttempts || "0", 10);
    if (attempts >= 1) return;
    placeholder.dataset.autoRetryAttempts = String(attempts + 1);
    setTimeout(() => {
      delete placeholder.dataset.lazyLoaded;
      if (document.documentElement.classList.contains("hr-reader-open")) return;
      if (lazyLoadObserver) lazyLoadObserver.observe(placeholder);
      else loadPlaceholderImage(placeholder);
    }, 750);
  }
  function loadPlaceholderImage(placeholder) {
    const url = placeholder.dataset.url;
    const adapter = store.activeAdapter;
    if (!url || !adapter) return;
    if (placeholder.dataset.isFetching === "true") {
      return;
    }
    placeholder.dataset.isFetching = "true";
    placeholder.dataset.lazyLoaded = "true";
    const pIndex = parseInt(placeholder.dataset.pIndex || "0", 10);
    const index = parseInt(placeholder.dataset.index || "0", 10);
    const rect = placeholder.getBoundingClientRect();
    const inViewport = rect.bottom >= 0 && rect.top <= window.innerHeight;
    const nearViewport = rect.bottom >= -window.innerHeight * 2 && rect.top <= window.innerHeight * 3;
    const lease = acquireImage(url, {
      intent: inViewport ? "foreground" : "scroll",
      priority: inViewport ? LOAD_PRIORITY.foreground : nearViewport ? LOAD_PRIORITY.scroll : LOAD_PRIORITY.scroll - 10
    });
    placeholderLeases.set(placeholder, lease);
    observePendingLoad(placeholder);
    lease.result.then((asset) => {
      var _a;
      if (placeholderLeases.get(placeholder) !== lease) return;
      stopObservingPendingLoad(placeholder, lease);
      if (!asset) {
        scheduleAutomaticRetry(placeholder, pIndex, index);
        return;
      }
      const img = document.createElement("img");
      img.className = "r-img";
      img.alt = `Page ${pIndex}-${index + 1}`;
      img.decoding = "async";
      img.fetchPriority = inViewport ? "high" : nearViewport ? "auto" : "low";
      img.dataset.viewerUrl = url;
      if (placeholder.dataset.itemKey) img.dataset.itemKey = placeholder.dataset.itemKey;
      img.dataset.realSrc = asset.src;
      if (placeholder.dataset.thumb) img.dataset.thumbSrc = placeholder.dataset.thumb;
      if (placeholder.dataset.thumbW) img.dataset.thumbW = placeholder.dataset.thumbW;
      if (placeholder.dataset.thumbH) img.dataset.thumbH = placeholder.dataset.thumbH;
      if (placeholder.dataset.thumbX !== void 0) img.dataset.thumbX = placeholder.dataset.thumbX;
      if (placeholder.dataset.thumbY !== void 0) img.dataset.thumbY = placeholder.dataset.thumbY;
      applyKnownImageGeometry(img, asset);
      img.onload = () => {
        if (img.dataset.locked || img.naturalWidth <= 0) return;
        applyKnownImageGeometry(img, {
          width: img.naturalWidth,
          height: img.naturalHeight
        });
        img.dataset.locked = "true";
      };
      img.onerror = () => {
        var _a2;
        ownedImageObserver == null ? void 0 : ownedImageObserver.unobserve(img);
        ownedImages.delete(img);
        (_a2 = imageElementLeases.get(img)) == null ? void 0 : _a2.release();
        imageElementLeases.delete(img);
        if (img.parentNode) img.parentNode.replaceChild(placeholder, img);
        scheduleAutomaticRetry(placeholder, pIndex, index);
      };
      img.src = asset.src;
      (_a = placeholder.parentNode) == null ? void 0 : _a.replaceChild(img, placeholder);
      if (asset.ownsObjectUrl) {
        img.dataset.ownsObjectUrl = "true";
        img.dataset.naturalWidth = String(asset.width);
        img.dataset.naturalHeight = String(asset.height);
        placeholderLeases.delete(placeholder);
        imageElementLeases.set(img, lease);
        ownedPlaceholders.set(img, placeholder);
        observeOwnedImage(img);
      }
      const itemKey = placeholder.dataset.itemKey;
      const itemIndex = itemKey ? store.galleryItems.findIndex((item) => item.key === itemKey) : store.galleryItems.findIndex((item) => item.viewerUrl === url);
      if (itemIndex !== -1) {
        notifyScrollImageLoaded({ index: itemIndex, element: img, viewerUrl: url });
      }
    }).catch(() => {
      if (placeholderLeases.get(placeholder) !== lease) return;
      stopObservingPendingLoad(placeholder, lease);
      scheduleAutomaticRetry(placeholder, pIndex, index);
    }).finally(() => {
      if (placeholderLeases.get(placeholder) === lease) {
        stopObservingPendingLoad(placeholder, lease);
        placeholderLeases.delete(placeholder);
        lease.release();
      }
    });
  }
  function initLazyLoad() {
    if (lazyLoadObserver) return;
    lazyLoadObserver = new IntersectionObserver((entries) => {
      if (document.documentElement.classList.contains("hr-reader-open")) return;
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          const placeholder = entry.target;
          if (placeholder.dataset.lazyLoaded) return;
          placeholder.dataset.lazyLoaded = "true";
          lazyLoadObserver == null ? void 0 : lazyLoadObserver.unobserve(placeholder);
          loadPlaceholderImage(placeholder);
        }
      });
    }, { rootMargin: "2000px 0px 2000px 0px" });
  }
  function pauseLazyLoad() {
    lazyLoadObserver == null ? void 0 : lazyLoadObserver.disconnect();
    [...pendingPlaceholders].forEach((placeholder) => cancelPendingLoad(placeholder, false));
  }
  function resumeLazyLoad() {
    if (!lazyLoadObserver) return;
    if (ownedImages.size > 0) rebuildOwnedImageObserver();
    document.querySelectorAll(".r-ph").forEach((ph) => {
      if (!ph.dataset.lazyLoaded) lazyLoadObserver.observe(ph);
    });
  }
  function processBatch(items, pIndex, container, prepend = false, pageUrl) {
    var _a;
    const batchDiv = document.createElement("div");
    batchDiv.className = "hr-page-batch";
    if (pageUrl) {
      batchDiv.dataset.pageUrl = pageUrl;
    }
    const fragment = document.createDocumentFragment();
    initLazyLoad();
    let targetContainer = container;
    if (!targetContainer) {
      targetContainer = document.querySelector("#gdt-hidden") || ((_a = store.activeAdapter) == null ? void 0 : _a.getContainer()) || document.querySelector(".scroll-mode .entry-content, .scroll-mode .wp-block-post-content, .scroll-mode .post-content") || document.body;
    }
    items.forEach((item, index) => {
      const url = item.viewerUrl;
      const placeholder = document.createElement("div");
      placeholder.className = "r-ph sp-placeholder loading";
      placeholder.dataset.url = url;
      placeholder.dataset.pIndex = String(pIndex);
      placeholder.dataset.index = String(index);
      placeholder.dataset.itemKey = item.key;
      if (item.dimensions) {
        applyKnownImageGeometry(placeholder, item.dimensions);
      }
      if (item.preview.kind === "url") {
        placeholder.dataset.thumb = item.preview.src;
        if (item.preview.size) {
          placeholder.dataset.thumbW = String(item.preview.size.width);
          placeholder.dataset.thumbH = String(item.preview.size.height);
        }
      } else if (item.preview.kind === "sprite") {
        placeholder.dataset.thumb = item.preview.src;
        placeholder.dataset.thumbW = String(item.preview.crop.width);
        placeholder.dataset.thumbH = String(item.preview.crop.height);
        placeholder.dataset.thumbX = String(item.preview.crop.x);
        placeholder.dataset.thumbY = String(item.preview.crop.y);
      }
      placeholder.innerHTML = `
      <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; transform: translateY(-20px);">
        <div style="display: flex; align-items: center; gap: 10px; background: rgba(20, 20, 20, 0.8); border: 1px solid rgba(255, 255, 255, 0.1); padding: 10px 20px; border-radius: 30px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); backdrop-filter: blur(8px); margin-bottom: 16px;">
          <svg class="hr-spinner" style="color: #F596AA; width: 20px; height: 20px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
            <path d="M21 12a9 9 0 1 1-6.219-8.56"></path>
          </svg>
          <div style="font-size: 15px; color: #f3f4f6; font-weight: 500; letter-spacing: 0.5px;">Loading...</div>
        </div>
        <div style="font-size: 14px; color: rgba(255, 255, 255, 0.5); font-family: monospace; letter-spacing: 1px;">P${pIndex}-${index + 1}</div>
      </div>
    `;
      fragment.appendChild(placeholder);
      if (store.settings.scrollMode && !document.documentElement.classList.contains("hr-reader-open")) {
        lazyLoadObserver == null ? void 0 : lazyLoadObserver.observe(placeholder);
      }
    });
    batchDiv.appendChild(fragment);
    if (prepend && targetContainer.firstChild) {
      store.galleryItems = [...items, ...store.galleryItems];
      targetContainer.insertBefore(batchDiv, targetContainer.firstChild);
    } else {
      store.galleryItems.push(...items);
      targetContainer.appendChild(batchDiv);
    }
  }
  function setupAutoScroll(pageLoader) {
    const scrollSent = document.createElement("div");
    document.body.appendChild(scrollSent);
    const errorSentinel = document.createElement("div");
    errorSentinel.className = "hr-pagination-error";
    errorSentinel.hidden = true;
    const errorText = document.createElement("span");
    errorText.textContent = i18n.paginationFailed;
    const retryButton = document.createElement("button");
    retryButton.type = "button";
    retryButton.textContent = i18n.retry;
    errorSentinel.append(errorText, retryButton);
    scrollSent.before(errorSentinel);
    const showError = () => {
      errorSentinel.hidden = false;
      pageObs.unobserve(scrollSent);
    };
    const hideError = () => {
      errorSentinel.hidden = true;
    };
    async function loadNextPage() {
      var _a;
      if (!store.nextUrl || store.isFetching) return;
      const requestedUrl = store.nextUrl;
      store.isFetching = true;
      hideError();
      try {
        const page = await pageLoader.loadPage(requestedUrl);
        if (!page) {
          store.nextUrl = null;
          pageObs.disconnect();
          return;
        }
        store.currPage++;
        processBatch(page.items, store.currPage, ((_a = store.activeAdapter) == null ? void 0 : _a.getContainer()) || document.querySelector(".scroll-mode .entry-content, .scroll-mode .wp-block-post-content, .scroll-mode .post-content") || document.body, false, page.pageUrl);
        store.nextUrl = page.nextUrl;
        if (!store.nextUrl) pageObs.disconnect();
      } catch (err) {
        if (err instanceof EmptyGalleryPageError) {
          store.nextUrl = null;
          pageObs.disconnect();
        } else {
          console.error("[Hentai-Reader] Infinite pagination failed", err);
          showError();
        }
      } finally {
        store.isFetching = false;
      }
    }
    const pageObs = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) void loadNextPage();
    }, { rootMargin: CFG.scrollPageRootMargin });
    retryButton.addEventListener("click", () => {
      hideError();
      pageObs.observe(scrollSent);
      void loadNextPage();
    });
    pageObs.observe(scrollSent);
    return () => {
      pageObs.disconnect();
      retryButton.replaceWith();
      errorSentinel.remove();
      scrollSent.remove();
    };
  }
  const readerCss = '.sp-close-btn{position:absolute;top:20px;right:20px;width:40px;height:40px;background:#333c;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:24px;color:#fff;transition:all .3s;z-index:10;pointer-events:auto}.sp-close-btn{transform:scale(var(--hr-scale, 1));transform-origin:top right}.sp-close-btn:hover{background:#555555e6;transform:scale(calc(1.1 * var(--hr-scale, 1)))}.sp-hidden-by-pswp{opacity:0!important;pointer-events:none!important}.sp-sidebar-track{position:absolute;background:#2828284d;border-radius:6px;z-index:10;pointer-events:none;opacity:0;transition:opacity .3s,background .3s}.sp-sidebar-track.active{opacity:1;pointer-events:auto}.sp-sidebar-track:hover{background:#32323280}.sp-sidebar-track.position-bottom{bottom:12px;left:10%;width:80%;height:12px}.sp-sidebar-track.position-top{top:12px;left:10%;width:80%;height:12px}.sp-sidebar-track.position-left{left:12px;top:10%;height:80%;width:12px}.sp-sidebar-track.position-right{right:12px;top:10%;height:80%;width:12px}.sp-sidebar-thumb{position:absolute;background:#fff6;border-radius:6px;transition:background .3s;cursor:grab;-webkit-user-select:none;user-select:none}.sp-sidebar-thumb:hover{background:#fff9}.sp-sidebar-thumb:active{cursor:grabbing;background:#ffffffb3}.sp-sidebar-track.position-bottom .sp-sidebar-thumb,.sp-sidebar-track.position-top .sp-sidebar-thumb{top:0;height:100%;min-width:60px}.sp-sidebar-track.position-left .sp-sidebar-thumb,.sp-sidebar-track.position-right .sp-sidebar-thumb{left:0;width:100%;min-height:60px}.sp-sidebar-label{position:absolute;background:#1a1a1af2;padding:8px 14px;border-radius:8px;color:#fff;font-family:monospace;font-size:14px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .3s;box-shadow:0 2px 8px #0000004d}.sp-sidebar-track.position-bottom .sp-sidebar-label{bottom:calc(100% + 16px);left:50%;transform:translate(-50%)}.sp-sidebar-track.position-top .sp-sidebar-label{top:calc(100% + 16px);left:50%;transform:translate(-50%)}.sp-sidebar-track.position-left .sp-sidebar-label{left:calc(100% + 16px);top:50%;transform:translateY(-50%)}.sp-sidebar-track.position-right .sp-sidebar-label{right:calc(100% + 16px);top:50%;transform:translateY(-50%)}.sp-sidebar-track:hover .sp-sidebar-label{opacity:1}.sp-thumb-panel{position:absolute;background:#141414f2;border-radius:8px;border:1px solid rgba(255,255,255,.1);box-shadow:0 -4px 16px #00000080;pointer-events:none;display:flex;z-index:11;opacity:0;transition:opacity .3s,transform .3s cubic-bezier(.2,.8,.2,1);-webkit-user-select:none;user-select:none}.sp-thumb-panel.position-top{top:32px;left:50%;transform:translate(-50%) translateY(-20px);flex-direction:row;max-width:calc(80% / var(--hr-scale, 1));width:fit-content;height:116px}.sp-thumb-panel.position-top{transform-origin:top center;transform:translate(-50%) translateY(-20px) scale(var(--hr-scale, 1))}.sp-thumb-panel.position-top.active{opacity:1;transform:translate(-50%) translateY(0) scale(var(--hr-scale, 1));pointer-events:auto}.sp-thumb-panel.position-bottom{bottom:32px;left:50%;transform-origin:bottom center;transform:translate(-50%) translateY(20px) scale(var(--hr-scale, 1));flex-direction:row;max-width:calc(80% / var(--hr-scale, 1));width:fit-content;height:116px}.sp-thumb-panel.position-bottom.active{opacity:1;transform:translate(-50%) translateY(0) scale(var(--hr-scale, 1));pointer-events:auto}.sp-thumb-panel.position-left{left:32px;top:50%;transform-origin:left center;transform:translateY(-50%) translate(-20px) scale(var(--hr-scale, 1));flex-direction:column;max-height:calc(80% / var(--hr-scale, 1));height:fit-content;width:116px}.sp-thumb-panel.position-left.active{opacity:1;transform:translateY(-50%) translate(0) scale(var(--hr-scale, 1));pointer-events:auto}.sp-thumb-panel.position-right{right:32px;top:50%;transform-origin:right center;transform:translateY(-50%) translate(20px) scale(var(--hr-scale, 1));flex-direction:column;max-height:calc(80% / var(--hr-scale, 1));height:fit-content;width:116px}.sp-thumb-panel.position-right.active{opacity:1;transform:translateY(-50%) translate(0) scale(var(--hr-scale, 1));pointer-events:auto}.sp-thumb-viewport{flex:1;min-width:0;min-height:0;position:relative;border-radius:inherit;overflow:auto;overscroll-behavior:none;-webkit-overflow-scrolling:touch;scrollbar-width:none}.sp-thumb-viewport::-webkit-scrollbar{display:none}.sp-thumb-content{position:relative;width:100%;height:100%}.sp-thumb-item{position:absolute;border-radius:4px;overflow:hidden;cursor:pointer;border:2px solid transparent;transition:border-color .15s;box-sizing:border-box;padding:0;background:#222;font:inherit}.sp-thumb-item:focus-visible{outline:2px solid #fff;outline-offset:2px}.sp-thumb-panel.position-bottom .sp-thumb-item,.sp-thumb-panel.position-top .sp-thumb-item{top:8px;left:0;width:72px;height:calc(100% - 16px)}.sp-thumb-panel.position-left .sp-thumb-item,.sp-thumb-panel.position-right .sp-thumb-item{left:8px;top:0;width:calc(100% - 16px);height:72px}.sp-thumb-item:hover{border-color:#fff6}.sp-thumb-item.sp-thumb-active{border-color:var(--hr-accent)}.sp-thumb-img{width:100%;height:100%;object-fit:cover;pointer-events:none}.sp-thumb-ph{width:100%;height:100%;background:#222;display:flex;align-items:center;justify-content:center;color:#555;font-family:monospace;font-size:13px;-webkit-user-select:none;user-select:none}.sp-thumb-ph.loading{color:#aaa;gap:6px}.sp-thumb-ph.loading:after{content:"";width:10px;height:10px;border:2px solid rgba(255,255,255,.2);border-top-color:var(--hr-accent);border-radius:50%;animation:hr-spin 1s linear infinite}.sp-thumb-ph.error{color:#ef8080}.sp-thumb-label{position:absolute;bottom:0;right:0;padding:1px 5px;background:#00000080;color:#fff9;font-family:monospace;font-size:11px;border-radius:4px 0 0;pointer-events:none}.sp-thumb-counter{position:absolute;display:flex;align-items:center;justify-content:center;background:#1e1e1ebf;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:#fff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:12px;font-weight:500;letter-spacing:.5px;padding:4px 10px;border-radius:12px;border:1px solid rgba(255,255,255,.15);box-shadow:0 4px 12px #0000004d;pointer-events:none;z-index:20;white-space:nowrap}.sp-thumb-panel.position-bottom .sp-thumb-counter{top:-36px;left:50%;transform:translate(-50%)}.sp-thumb-panel.position-top .sp-thumb-counter{bottom:-36px;left:50%;transform:translate(-50%)}.sp-thumb-panel.position-left .sp-thumb-counter,.sp-thumb-panel.position-right .sp-thumb-counter{bottom:-44px;left:50%;transform:translate(-50%);font-size:14px;padding:6px 16px;border-radius:16px}:root.hr-mobile .sp-thumb-panel.position-bottom{bottom:16px;max-width:calc(90% / var(--hr-scale, 1));height:130px}:root.hr-mobile .sp-thumb-panel.position-top{top:16px;max-width:calc(90% / var(--hr-scale, 1));height:130px}:root.hr-mobile .sp-thumb-panel.position-left{left:16px;max-height:calc(90% / var(--hr-scale, 1));width:130px}:root.hr-mobile .sp-thumb-panel.position-right{right:16px;max-height:calc(90% / var(--hr-scale, 1));width:130px}:root.hr-mobile .sp-thumb-panel.position-bottom .sp-thumb-item,:root.hr-mobile .sp-thumb-panel.position-top .sp-thumb-item{width:86px;height:calc(100% - 10px);top:5px}:root.hr-mobile .sp-thumb-panel.position-left .sp-thumb-item,:root.hr-mobile .sp-thumb-panel.position-right .sp-thumb-item{width:calc(100% - 10px);height:86px;left:5px}.sp-hud-container{position:absolute;top:40px;left:50%;transform:translate(-50%);z-index:100;opacity:0;pointer-events:none;transition:opacity .3s ease,transform .3s ease}.sp-hud-container{transform:translate(-50%) scale(var(--hr-scale, 1))}.sp-hud-container.show{opacity:1;transform:translate(-50%) scale(var(--hr-scale, 1))}.sp-hud-box{display:flex;align-items:center;gap:10px;background:#141414b3;border:1px solid rgba(255,255,255,.1);padding:10px 20px;border-radius:30px;box-shadow:0 4px 16px #00000080;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:all .2s}.sp-hud-box.hud-error{background:#b41e1eb3;border-color:#ff64644d}.sp-hud-box.hud-error:hover{background:#dc2828cc}.sp-hud-text{font-size:14px;color:#f3f4f6;font-family:sans-serif;font-weight:500;letter-spacing:.5px;white-space:nowrap;-webkit-user-select:none;user-select:none}.sp-hud-page{font-size:13px;color:#ffffff80;font-family:monospace;letter-spacing:1px;margin-left:6px;padding-left:10px;border-left:1px solid rgba(255,255,255,.2);-webkit-user-select:none;user-select:none}.pswp__error-msg-container,.pswp__error-msg{display:none!important}.pswp__counter{color:#fff!important;background:#000000c7;border-radius:6px}.hr-reader-spread{box-sizing:border-box;display:flex;align-items:center;justify-content:center;width:100%;height:100%;gap:20px;overflow:hidden}.hr-reader-spread__page{box-sizing:border-box;display:block;width:calc(50% - 10px);max-width:calc(50% - 10px);height:100%;object-fit:contain;flex:0 0 calc(50% - 10px)}.hr-reader-spread__page:only-child{width:100%;max-width:100%;flex-basis:100%}.hr-reader-spread__page:not(:only-child):first-child{object-position:right center}.hr-reader-spread__page:not(:only-child):last-child{object-position:left center}.hr-reader-spread__page--pending{position:relative;min-width:0;border:1px solid rgba(245,150,170,.16);background:radial-gradient(circle at center,rgba(245,150,170,.12),transparent 28%),#ffffff06}.hr-reader-spread__page--pending:after{position:absolute;top:50%;left:50%;width:28px;height:28px;margin:-16px 0 0 -16px;content:"";border:2px solid rgba(245,150,170,.22);border-top-color:#f596aa;border-radius:50%;animation:hr-reader-pending-spin .8s linear infinite}@keyframes hr-reader-pending-spin{to{transform:rotate(360deg)}}';
  importCSS(readerCss);
  function createPrefetchController(session, policy = READER_PREFETCH) {
    const downloads = new Map();
    let lastCenter = -1;
    let generation = 0;
    let idleHandle = null;
    let idleKind = null;
    function urlAt(index) {
      var _a;
      return (_a = session.itemAt(index)) == null ? void 0 : _a.viewerUrl;
    }
    function ensureDownload(url, options = { intent: "warmup", priority: LOAD_PRIORITY.warmup, lane: "background" }) {
      const existing = downloads.get(url);
      if (existing && existing.priority >= options.priority && (existing.lane === "foreground" || options.lane === "background")) {
        return existing.lease;
      }
      const lease = acquireImage(url, options);
      existing == null ? void 0 : existing.lease.release();
      downloads.set(url, { lease, priority: options.priority, lane: options.lane });
      lease.result.then(() => {
        var _a;
        if (((_a = downloads.get(url)) == null ? void 0 : _a.lease) !== lease) return;
        downloads.delete(url);
        lease.release();
      }).catch(() => {
        var _a;
        if (((_a = downloads.get(url)) == null ? void 0 : _a.lease) !== lease) return;
        downloads.delete(url);
        lease.release();
      });
      return lease;
    }
    function abort(url) {
      var _a;
      (_a = downloads.get(url)) == null ? void 0 : _a.lease.release();
      downloads.delete(url);
    }
    function cancelIdleStage() {
      if (idleHandle === null) return;
      if (idleKind === "idle" && "cancelIdleCallback" in window) {
        window.cancelIdleCallback(idleHandle);
      } else {
        clearTimeout(idleHandle);
      }
      idleHandle = null;
      idleKind = null;
    }
    function scheduleIdleStage(callback) {
      cancelIdleStage();
      if ("requestIdleCallback" in window) {
        idleKind = "idle";
        idleHandle = window.requestIdleCallback(() => {
          idleHandle = null;
          idleKind = null;
          callback();
        }, { timeout: 800 });
      } else {
        idleKind = "timeout";
        idleHandle = setTimeout(() => {
          idleHandle = null;
          idleKind = null;
          callback();
        }, 250);
      }
    }
    function networkAllowsExtraWarmup() {
      const connection = navigator.connection;
      return !(connection == null ? void 0 : connection.saveData) && (connection == null ? void 0 : connection.effectiveType) !== "slow-2g" && (connection == null ? void 0 : connection.effectiveType) !== "2g";
    }
    function setWindow(center, direction) {
      const total = session.imageCount;
      if (total === 0) return;
      const currentGeneration = ++generation;
      cancelIdleStage();
      const indices = getReaderPrefetchIndices(center, total, direction, policy);
      const wanted = new Set();
      for (const i of indices) {
        const url = urlAt(i);
        if (url) wanted.add(url);
      }
      for (const url of Array.from(downloads.keys())) {
        if (!wanted.has(url)) abort(url);
      }
      const jumped = lastCenter < 0 || Math.abs(center - lastCenter) > policy.ahead + policy.behind;
      if (jumped) cancelImagePrefetch(wanted);
      lastCenter = center;
      const currentUrl = urlAt(center);
      const nextUrl = urlAt(center + direction);
      const currentLease = currentUrl ? ensureDownload(currentUrl, {
        intent: "foreground",
        priority: LOAD_PRIORITY.foreground,
        lane: "foreground"
      }) : null;
      if (nextUrl) {
        ensureDownload(nextUrl, {
          intent: "neighbor",
          priority: LOAD_PRIORITY.foreground - 1,
          lane: "foreground"
        });
      }
      Promise.resolve(currentLease == null ? void 0 : currentLease.result).finally(() => {
        if (generation !== currentGeneration) return;
        for (const distance of [2, 3]) {
          const stagedUrl = urlAt(center + direction * distance);
          if (stagedUrl && wanted.has(stagedUrl)) ensureDownload(stagedUrl);
        }
        scheduleIdleStage(() => {
          if (generation !== currentGeneration) return;
          for (const index of indices) {
            const url = urlAt(index);
            if (url) ensureDownload(url);
          }
        });
      });
    }
    function warmup(indices) {
      const [first, second, ...rest] = indices;
      const firstUrl = first === void 0 ? void 0 : urlAt(first);
      if (firstUrl) ensureDownload(firstUrl);
      if (networkAllowsExtraWarmup()) {
        const secondUrl = second === void 0 ? void 0 : urlAt(second);
        if (secondUrl) ensureDownload(secondUrl);
      }
      if (rest.length > 0) {
        scheduleIdleStage(() => {
          for (const index of rest) {
            const url = urlAt(index);
            if (url) ensureDownload(url);
          }
        });
      }
    }
    function clear() {
      generation++;
      cancelIdleStage();
      for (const url of Array.from(downloads.keys())) abort(url);
      lastCenter = -1;
    }
    return { setWindow, warmup, clear };
  }
  function getWheelPageLoadBehavior(members, isLoading) {
    const loadingCount = members.filter(isLoading).length;
    if (members.length === 1 && loadingCount === 1) return "frontier";
    return "ready";
  }
  const WHEEL_DECAY = 0.82;
  const MIN_VELOCITY = 20;
  const MAX_VELOCITY = 150;
  const TURN_INTERVAL_SLOW = 150;
  const TURN_INTERVAL_FAST = 50;
  const GESTURE_GAP = 150;
  function createWheelPager(config) {
    let wheelVelocity = 0;
    let scrollRafId = null;
    let lastTurnTime = 0;
    let gestureConsumed = false;
    let lastWheelTime = 0;
    function stop() {
      if (scrollRafId !== null) {
        cancelAnimationFrame(scrollRafId);
        scrollRafId = null;
      }
      wheelVelocity = 0;
    }
    function scrollTick(now) {
      scrollRafId = null;
      wheelVelocity *= WHEEL_DECAY;
      if (Math.abs(wheelVelocity) < MIN_VELOCITY) {
        wheelVelocity = 0;
        return;
      }
      const dir = wheelVelocity > 0 ? 1 : -1;
      if (gestureConsumed) {
        scrollRafId = requestAnimationFrame(scrollTick);
        return;
      }
      const v = Math.min(MAX_VELOCITY, Math.abs(wheelVelocity));
      const t = Math.sqrt((v - MIN_VELOCITY) / (MAX_VELOCITY - MIN_VELOCITY));
      const interval = TURN_INTERVAL_SLOW + t * (TURN_INTERVAL_FAST - TURN_INTERVAL_SLOW);
      if (now - lastTurnTime >= interval) {
        const target = config.getCurrentIndex() + dir;
        if (target < 0) {
          stop();
          config.onEdgeBackward();
          return;
        }
        if (target >= config.getImageCount()) {
          stop();
          config.onEdgeForward();
          return;
        }
        const loadBehavior = config.getPageLoadBehavior(target);
        config.stopMotion();
        config.goTo(target);
        lastTurnTime = now;
        if (loadBehavior === "frontier") {
          gestureConsumed = true;
        }
      }
      scrollRafId = requestAnimationFrame(scrollTick);
    }
    function onWheel(event) {
      if (config.isCurrentZoomed()) return;
      event.preventDefault();
      let delta = event.deltaY;
      if (event.deltaMode === 1) delta *= 33;
      else if (event.deltaMode === 2) delta *= window.innerHeight;
      const now = event.timeStamp;
      if (now - lastWheelTime > GESTURE_GAP) {
        gestureConsumed = false;
      }
      lastWheelTime = now;
      if (Math.sign(delta) !== Math.sign(wheelVelocity) && wheelVelocity !== 0) {
        wheelVelocity = 0;
      }
      wheelVelocity = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, wheelVelocity + delta));
      if (scrollRafId === null) {
        scrollRafId = requestAnimationFrame(scrollTick);
      }
    }
    return { onWheel, stop };
  }
  function createAutoPlay(nextImageFn, context) {
    let timer = null;
    function start() {
      if (timer) clearInterval(timer);
      if (context.isAutoPlayEnabled()) {
        timer = setInterval(nextImageFn, Math.max(1e3, context.getAutoPlayInterval()));
      }
    }
    function stop() {
      if (timer) {
        clearInterval(timer);
        timer = null;
      }
    }
    function reset() {
      if (context.isAutoPlayEnabled()) {
        stop();
        start();
      }
    }
    function stopAtEnd() {
      context.setAutoPlayEnabled(false);
      stop();
    }
    return { start, stop, reset, stopAtEnd };
  }
  function createReaderPaginationController(session, deps, context) {
    function checkNearEnd() {
      if (!context.getNextUrl() || context.isPageFetching()) return;
      const remainingImages = session.imageCount - session.currentIndex;
      if (remainingImages <= READER_PREFETCH.ahead) loadNext();
    }
    function loadNext() {
      const requestedUrl = context.getNextUrl();
      if (!requestedUrl || context.isPageFetching()) return;
      let shouldContinue = false;
      context.setPageFetching(true);
      deps.onLoading("next");
      deps.pageLoader.loadPage(requestedUrl).then((page) => {
        if (!page) {
          context.setNextUrl(null);
          deps.onIdle();
          return;
        }
        deps.appendPage(page);
        deps.syncImages();
        deps.onPageAdded("next");
        deps.onIdle();
        shouldContinue = true;
      }).catch((err) => {
        if (err instanceof EmptyGalleryPageError) {
          context.setNextUrl(null);
          deps.onIdle();
          return;
        }
        console.error("[Single Page] Load failed", err);
        deps.onError("next");
      }).finally(() => {
        context.setPageFetching(false);
        if (shouldContinue) checkNearEnd();
      });
    }
    function loadPrev() {
      const requestedUrl = context.getPrevUrl();
      if (!requestedUrl || context.isPageFetching()) return;
      context.setPageFetching(true);
      deps.onLoading("prev");
      deps.pageLoader.loadPage(requestedUrl).then((page) => {
        if (!page) {
          context.setPrevUrl(null);
          deps.onIdle();
          return;
        }
        deps.prependPage(page);
        deps.syncImages();
        deps.afterPrepend(page.items.length);
        deps.onPageAdded("prev");
        deps.onIdle();
      }).catch((err) => {
        if (err instanceof EmptyGalleryPageError) {
          context.setPrevUrl(null);
          deps.onIdle();
          return;
        }
        console.error("[Single Page] Load prev failed", err);
        deps.onError("prev");
      }).finally(() => {
        context.setPageFetching(false);
      });
    }
    return { loadNext, loadPrev, checkNearEnd };
  }
  function createReaderImageController(session, deps) {
    const byteState = new Map();
    const leases = new Map();
    let disposed = false;
    function releaseLease(url) {
      const held = leases.get(url);
      if (!held) return;
      held.unsubscribe();
      held.lease.release();
      leases.delete(url);
    }
    function markLoadFailed(viewerUrl) {
      releaseLease(viewerUrl);
      byteState.set(viewerUrl, "error");
      notify(viewerUrl);
    }
    function urlAt(index) {
      var _a;
      const itemUrl = (_a = session.itemAt(index)) == null ? void 0 : _a.viewerUrl;
      if (itemUrl) return itemUrl;
      const el = session.elementAt(index);
      return (el == null ? void 0 : el.dataset.url) || (el == null ? void 0 : el.dataset.viewerUrl) || void 0;
    }
    function indexOfUrl(url) {
      return session.indexOfViewerUrl(url);
    }
    function getPhase(index) {
      var _a;
      const url = urlAt(index);
      if (!url) return "error";
      if (byteState.get(url) === "error") return "error";
      const servicePhase = (_a = leases.get(url)) == null ? void 0 : _a.lease.phase;
      if (servicePhase === "error" || servicePhase === "cancelled") return "error";
      if (servicePhase === "resolving") return "resolving";
      if (servicePhase === "switching-source") return "switching-source";
      if (servicePhase === "downloading") return "downloading";
      if (byteState.get(url) === "loaded" || deps.getSlideContentState(index) === "loaded") {
        return "loaded";
      }
      return "downloading";
    }
    function notify(url) {
      const index = indexOfUrl(url);
      if (index !== -1) deps.onPhaseChange(index, getPhase(index));
    }
    function ensureLease(index, viewerUrl) {
      var _a;
      if (!viewerUrl || leases.has(viewerUrl) || disposed) return;
      const isActive = ((_a = deps.getActiveIndices) == null ? void 0 : _a.call(deps).includes(index)) ?? index === deps.getCurrentIndex();
      const distance = Math.abs(index - deps.getCurrentIndex());
      const lease = acquireImage(viewerUrl, {
        intent: isActive ? "foreground" : "neighbor",
        priority: isActive ? LOAD_PRIORITY.foreground : LOAD_PRIORITY.foreground - distance
      });
      const held = { lease, unsubscribe: () => {
      } };
      leases.set(viewerUrl, held);
      held.unsubscribe = lease.subscribe(() => notify(viewerUrl));
      lease.result.then((asset) => {
        var _a2;
        if (disposed || ((_a2 = leases.get(viewerUrl)) == null ? void 0 : _a2.lease) !== lease) return;
        const liveIndex = indexOfUrl(viewerUrl);
        if (!asset || liveIndex === -1) {
          markLoadFailed(viewerUrl);
          return;
        }
        byteState.set(viewerUrl, "loaded");
        notify(viewerUrl);
        deps.onAssetReady(liveIndex);
      }).catch(() => {
        var _a2;
        if (disposed || ((_a2 = leases.get(viewerUrl)) == null ? void 0 : _a2.lease) !== lease) return;
        markLoadFailed(viewerUrl);
      });
    }
    function getItemData(index) {
      var _a;
      const el = session.elementAt(index);
      const item = session.itemAt(index);
      if (!el) return { src: "", w: 0, h: 0 };
      const viewerUrl = (item == null ? void 0 : item.viewerUrl) || el.dataset.url || el.dataset.viewerUrl || "";
      const fallbackSrc = el.dataset.realSrc || el.src || "";
      const cachedAsset = getCachedImage(viewerUrl);
      const resolvedSrc = (cachedAsset == null ? void 0 : cachedAsset.src) || fallbackSrc;
      let dim = cachedAsset ? { w: cachedAsset.width, h: cachedAsset.height } : void 0;
      if (!dim && (item == null ? void 0 : item.dimensions)) {
        dim = { w: item.dimensions.width, h: item.dimensions.height };
      }
      if (!dim && el.tagName === "IMG") {
        const htmlImg = el;
        if (htmlImg.complete && htmlImg.naturalWidth > 0) {
          dim = { w: htmlImg.naturalWidth, h: htmlImg.naturalHeight };
        }
      }
      if (!dim) {
        const fallbackW = window.innerWidth;
        const previewSize = (item == null ? void 0 : item.preview.kind) === "url" ? item.preview.size : (item == null ? void 0 : item.preview.kind) === "sprite" ? item.preview.crop : void 0;
        let fallbackRatio = 1.414;
        if (previewSize && previewSize.width > 0 && previewSize.height > 0) {
          fallbackRatio = previewSize.height / previewSize.width;
        } else {
          const latestCached = getLatestCachedImage();
          if (latestCached && latestCached.width > 0 && latestCached.height > 0) {
            fallbackRatio = latestCached.height / latestCached.width;
          }
        }
        dim = { w: fallbackW, h: fallbackW * fallbackRatio };
      }
      const trueDimKnown = !!(item == null ? void 0 : item.dimensions) || !!cachedAsset || el.tagName === "IMG" && el.complete && el.naturalWidth > 0;
      const msrc = (item == null ? void 0 : item.preview.kind) === "url" ? item.preview.src : void 0;
      if (viewerUrl) ensureLease(index, viewerUrl);
      const serviceReady = ((_a = leases.get(viewerUrl)) == null ? void 0 : _a.lease.phase) === "ready";
      if (serviceReady && resolvedSrc && !resolvedSrc.includes("x.gif") && trueDimKnown) {
        return { src: resolvedSrc, msrc, w: dim.w, h: dim.h };
      }
      return { src: "", msrc, w: dim.w, h: dim.h };
    }
    function handleContentLoad(index) {
      const url = urlAt(index);
      if (!url || byteState.get(url) === "loaded") return;
      byteState.set(url, "loading");
      notify(url);
    }
    function getSpreadItemData(indices, width, height) {
      return {
        src: "",
        w: Math.max(1, width),
        h: Math.max(1, height),
        hrSpread: indices.map((index) => {
          const data = getItemData(index);
          return {
            index,
            src: data.src,
            alt: `Page ${index + 1}`
          };
        })
      };
    }
    function handleLoadComplete(index, isError) {
      const url = urlAt(index);
      if (!url) return;
      byteState.set(url, isError ? "error" : "loaded");
      notify(url);
    }
    function releaseOutside(center) {
      for (const url of leases.keys()) {
        const index = indexOfUrl(url);
        if (index !== -1 && Math.abs(index - center) <= 2) continue;
        releaseLease(url);
      }
    }
    function dispose() {
      disposed = true;
      for (const url of Array.from(leases.keys())) releaseLease(url);
    }
    return {
      getSpreadItemData,
      getPhase,
      isLoading: (index) => {
        const phase = getPhase(index);
        return phase === "resolving" || phase === "switching-source" || phase === "downloading";
      },
      handleContentLoad,
      handleLoadComplete,
      releaseOutside,
      dispose
    };
  }
  function createStatusHUD() {
    const container = document.createElement("div");
    container.className = "sp-hud-container";
    let renderedKey = "";
    let visible = false;
    function show(config) {
      const isError = config.status === "error";
      const nextKey = `${config.status}\0${config.text}\0${config.pageText ?? ""}`;
      if (renderedKey !== nextKey) {
        const color = isError ? "#ef4444" : "#F596AA";
        const iconSvg = isError ? `<svg style="color: ${color}; width: 18px; height: 18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>` : `<svg class="hr-spinner" style="color: ${color}; width: 18px; height: 18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg>`;
        container.innerHTML = `
        <div class="sp-hud-box ${isError ? "hud-error" : ""}">
          ${iconSvg}
          <div class="sp-hud-text">${config.text}</div>
          ${config.pageText ? `<div class="sp-hud-page">${config.pageText}</div>` : ""}
        </div>
      `;
        renderedKey = nextKey;
      }
      if (!visible) {
        container.classList.add("show");
        visible = true;
      }
    }
    function hide() {
      if (visible) {
        container.classList.remove("show");
        visible = false;
      }
    }
    return { show, hide, getElement: () => container };
  }
  function createThumbnailPlan(item, loadedSource) {
    switch (item.preview.kind) {
      case "url":
        return { src: item.preview.src, requestFullImage: false };
      case "sprite":
        return { src: item.preview.src, crop: item.preview.crop, requestFullImage: false };
      case "derived":
      case "none":
        return loadedSource ? { src: loadedSource, requestFullImage: false } : { requestFullImage: true };
    }
  }
  const VISIBLE_COUNT = 15;
  const BUFFER = 3;
  const PRELOAD_SETTLE_MS = 300;
  function createThumbnailPanel(onIndexChange, onScrollToBottom, onScrollToTop, options) {
    var _a;
    const thumbPanel = document.createElement("div");
    thumbPanel.className = "sp-thumb-panel";
    const viewport = document.createElement("div");
    viewport.className = "sp-thumb-viewport";
    const content = document.createElement("div");
    content.className = "sp-thumb-content";
    const counter = document.createElement("div");
    counter.className = "sp-thumb-counter";
    viewport.appendChild(content);
    thumbPanel.appendChild(viewport);
    thumbPanel.appendChild(counter);
    let lastCenteredIndex = -1;
    let clickedFromPanel = false;
    let lastContentSize = "";
    let lastContentVertical = null;
    const itemPool = [];
    const activeItems = new Map();
    let hideTimeout;
    let isPanelActive = false;
    let preloadTimer = null;
    let userScrollArmed = false;
    const touchOnlyUi = options.inputCapabilities.touchOnlyUi;
    function openPanel(keepOpen = false) {
      var _a2, _b;
      if (options.getImageCount() === 0) return;
      clearTimeout(hideTimeout);
      isPanelActive = true;
      thumbPanel.classList.add("active");
      if (keepOpen) {
        if (touchOnlyUi) {
          (_a2 = options.onMobileInteractionStart) == null ? void 0 : _a2.call(options);
        }
      } else {
        if (touchOnlyUi) {
          (_b = options.onMobileInteractionEnd) == null ? void 0 : _b.call(options);
        } else {
          hideTimeout = setTimeout(() => {
            closePanel();
          }, 2e3);
        }
      }
    }
    function clearThumbnailPreloadTimer() {
      if (preloadTimer) {
        clearTimeout(preloadTimer);
        preloadTimer = null;
      }
    }
    function cancelThumbnailPreloadWork() {
      var _a2;
      clearThumbnailPreloadTimer();
      (_a2 = options.cancelThumbnailPreloads) == null ? void 0 : _a2.call(options);
    }
    function closePanel() {
      isPanelActive = false;
      userScrollArmed = false;
      thumbPanel.classList.remove("active");
      clearThumbnailPreloadTimer();
    }
    thumbPanel.addEventListener("pointerenter", (e) => {
      if (e.pointerType === "mouse") openPanel(true);
    });
    thumbPanel.addEventListener("pointerleave", (e) => {
      if (e.pointerType === "mouse") openPanel(false);
    });
    thumbPanel.addEventListener("pointermove", (e) => {
      if (e.pointerType === "mouse") openPanel(true);
    });
    (_a = options.subscribeThumbnailChange) == null ? void 0 : _a.call(options, (idx) => {
      if (activeItems.has(idx)) {
        renderItemContent(activeItems.get(idx), idx);
      }
    });
    function isVertical() {
      const pos = options.getThumbnailPosition();
      return pos === "left" || pos === "right";
    }
    function getItemSize() {
      return document.documentElement.classList.contains("hr-mobile") ? 96 : 80;
    }
    function vpSize() {
      if (isVertical()) {
        return viewport.offsetHeight || Math.min(VISIBLE_COUNT * getItemSize(), options.getImageCount() * getItemSize());
      } else {
        return viewport.offsetWidth || Math.min(VISIBLE_COUNT * getItemSize(), options.getImageCount() * getItemSize());
      }
    }
    function maxOffset() {
      return Math.max(0, options.getImageCount() * getItemSize() - vpSize());
    }
    function acquireItem() {
      return itemPool.pop() || (() => {
        const el = document.createElement("button");
        el.type = "button";
        el.className = "sp-thumb-item";
        return el;
      })();
    }
    function releaseItem(el) {
      el.remove();
      itemPool.push(el);
    }
    const THUMB_CACHE_MAX = 60;
    const thumbCache = new Map();
    function getCachedThumb(src) {
      const c = thumbCache.get(src);
      if (c) {
        thumbCache.delete(src);
        thumbCache.set(src, c);
      }
      return c;
    }
    function putCachedThumb(src, canvas) {
      thumbCache.set(src, canvas);
      if (thumbCache.size > THUMB_CACHE_MAX) {
        const oldest = thumbCache.keys().next().value;
        if (oldest !== void 0) thumbCache.delete(oldest);
      }
    }
    function makeThumbCanvas(source, natW, natH, crop) {
      let w = crop ? crop.w : natW;
      let h = crop ? crop.h : natH;
      if (w > 300) {
        h = h * 300 / w;
        w = 300;
      }
      const c = document.createElement("canvas");
      c.width = w;
      c.height = h;
      const ctx = c.getContext("2d");
      if (ctx) {
        if (crop) {
          ctx.drawImage(source, crop.x, crop.y, crop.w, crop.h, 0, 0, w, h);
        } else {
          ctx.drawImage(source, 0, 0, w, h);
        }
      }
      return c;
    }
    function blitToCanvas(dest, cache) {
      dest.width = cache.width;
      dest.height = cache.height;
      const ctx = dest.getContext("2d");
      if (ctx) ctx.drawImage(cache, 0, 0);
    }
    function renderItemContent(el, index) {
      var _a2, _b, _c, _d;
      el.dataset.index = String(index);
      el.classList.toggle("sp-thumb-active", index === options.getCurrentIndex());
      el.setAttribute("aria-label", `Page ${options.getDisplayNumber(index)}`);
      if (index === options.getCurrentIndex()) el.setAttribute("aria-current", "page");
      else el.removeAttribute("aria-current");
      const img = options.getImageAt(index);
      const item = options.getItemAt(index);
      const isLoadedImg = img && img.tagName === "IMG" && img.complete && img.naturalWidth > 0;
      const preloadedAsset = (_a2 = options.getPreloadedAsset) == null ? void 0 : _a2.call(options, index);
      const loadedSource = (preloadedAsset == null ? void 0 : preloadedAsset.src) || (isLoadedImg ? img.dataset.realSrc || img.src : void 0);
      const plan = item ? createThumbnailPlan(item, loadedSource) : {};
      const thumbSrc = plan.src || "";
      const isDerived = (item == null ? void 0 : item.preview.kind) === "none" || (item == null ? void 0 : item.preview.kind) === "derived";
      const crop = plan.crop ? {
        x: plan.crop.x,
        y: plan.crop.y,
        w: plan.crop.width,
        h: plan.crop.height
      } : void 0;
      const cacheKey = isDerived && item ? `derived:${item.key}` : crop ? `${thumbSrc}#${crop.x},${crop.y},${crop.w},${crop.h}` : thumbSrc;
      const cached = cacheKey ? getCachedThumb(cacheKey) : void 0;
      if (cached || thumbSrc) {
        let thumbCanvas = el.querySelector("canvas.sp-thumb-img");
        if (!thumbCanvas) {
          el.innerHTML = "";
          thumbCanvas = document.createElement("canvas");
          thumbCanvas.className = "sp-thumb-img";
          el.appendChild(thumbCanvas);
          const label2 = document.createElement("span");
          label2.className = "sp-thumb-label";
          el.appendChild(label2);
        }
        if (thumbCanvas.dataset.src !== cacheKey) {
          thumbCanvas.dataset.src = cacheKey;
          if (cached) {
            blitToCanvas(thumbCanvas, cached);
            if (isDerived && preloadedAsset) (_b = options.finishThumbnailPreload) == null ? void 0 : _b.call(options, index);
          } else if (isDerived && isLoadedImg && loadedSource === thumbSrc) {
            const c = makeThumbCanvas(img, img.naturalWidth, img.naturalHeight);
            putCachedThumb(cacheKey, c);
            blitToCanvas(thumbCanvas, c);
            if (preloadedAsset) (_c = options.finishThumbnailPreload) == null ? void 0 : _c.call(options, index);
          } else {
            const tempImg = new Image();
            tempImg.decoding = "async";
            tempImg.onload = () => {
              var _a3;
              const c = makeThumbCanvas(tempImg, tempImg.naturalWidth, tempImg.naturalHeight, crop);
              putCachedThumb(cacheKey, c);
              if (thumbCanvas.dataset.src === cacheKey) {
                blitToCanvas(thumbCanvas, c);
              }
              if (isDerived) (_a3 = options.finishThumbnailPreload) == null ? void 0 : _a3.call(options, index);
            };
            tempImg.onerror = () => {
              var _a3;
              if (isDerived) (_a3 = options.finishThumbnailPreload) == null ? void 0 : _a3.call(options, index, true);
            };
            tempImg.src = thumbSrc;
          }
        }
        const label = el.querySelector(".sp-thumb-label");
        if (label) label.textContent = String(options.getDisplayNumber(index));
      } else {
        let ph = el.querySelector(".sp-thumb-ph");
        if (!ph) {
          el.innerHTML = "";
          ph = document.createElement("div");
          ph.className = "sp-thumb-ph";
          el.appendChild(ph);
        }
        const phase = ((_d = options.getPreloadPhase) == null ? void 0 : _d.call(options, index)) ?? "idle";
        ph.classList.toggle("loading", phase === "loading");
        ph.classList.toggle("error", phase === "error");
        ph.textContent = phase === "error" ? `${options.getDisplayNumber(index)} !` : String(options.getDisplayNumber(index));
      }
    }
    function getScrollOffset() {
      return isVertical() ? viewport.scrollTop : viewport.scrollLeft;
    }
    function visiblePreloadIndices() {
      const total = options.getImageCount();
      const itemSize = getItemSize();
      const offset = getScrollOffset();
      const viewportSize = vpSize();
      const first = Math.max(0, Math.floor(offset / itemSize));
      const last = Math.min(total - 1, Math.ceil((offset + viewportSize) / itemSize) - 1);
      const center = offset + viewportSize / 2;
      const indices = [];
      for (let index = first; index <= last; index++) {
        const item = options.getItemAt(index);
        if (!item || item.preview.kind !== "none" && item.preview.kind !== "derived") continue;
        if (getCachedThumb(`derived:${item.key}`)) continue;
        const element = options.getImageAt(index);
        if ((element == null ? void 0 : element.tagName) === "IMG" && element.complete && element.naturalWidth > 0) continue;
        indices.push(index);
      }
      return indices.sort((a, b) => {
        const distanceA = Math.abs((a + 0.5) * itemSize - center);
        const distanceB = Math.abs((b + 0.5) * itemSize - center);
        return distanceA - distanceB || a - b;
      });
    }
    function scheduleThumbnailPreload() {
      cancelThumbnailPreloadWork();
      preloadTimer = setTimeout(() => {
        var _a2;
        preloadTimer = null;
        userScrollArmed = false;
        if (!isPanelActive) return;
        (_a2 = options.preloadThumbnails) == null ? void 0 : _a2.call(options, visiblePreloadIndices());
      }, PRELOAD_SETTLE_MS);
    }
    let isProgrammaticScroll = false;
    let programmaticScrollTimer;
    function setScrollOffset(val) {
      const current = getScrollOffset();
      if (Math.abs(current - val) < 1) return;
      isProgrammaticScroll = true;
      if (programmaticScrollTimer) clearTimeout(programmaticScrollTimer);
      programmaticScrollTimer = setTimeout(() => {
        isProgrammaticScroll = false;
      }, 50);
      if (isVertical()) {
        viewport.scrollTop = val;
      } else {
        viewport.scrollLeft = val;
      }
    }
    function renderVisibleItems() {
      const total = options.getImageCount();
      if (total === 0) return;
      const vp = vpSize();
      const itemSize = getItemSize();
      const wantSize = `${total * itemSize + 16}px`;
      const vertical = isVertical();
      if (lastContentSize !== wantSize || lastContentVertical !== vertical) {
        if (vertical) {
          content.style.width = "100%";
          content.style.height = wantSize;
        } else {
          content.style.width = wantSize;
          content.style.height = "100%";
        }
        lastContentSize = wantSize;
        lastContentVertical = vertical;
      }
      const scrollOffset = getScrollOffset();
      const startIdx = Math.max(0, Math.floor(scrollOffset / itemSize) - BUFFER);
      const endIdx = Math.min(total - 1, Math.ceil((scrollOffset + vp) / itemSize) + BUFFER);
      for (const [idx, el] of activeItems) {
        if (idx < startIdx || idx > endIdx) {
          releaseItem(el);
          activeItems.delete(idx);
        }
      }
      for (let i = startIdx; i <= endIdx; i++) {
        let el = activeItems.get(i);
        if (!el) {
          el = acquireItem();
          activeItems.set(i, el);
          content.appendChild(el);
        }
        if (isVertical()) {
          el.style.transform = `translateY(${i * itemSize + 8}px)`;
        } else {
          el.style.transform = `translateX(${i * itemSize + 8}px)`;
        }
        renderItemContent(el, i);
      }
    }
    function centerOnCurrent() {
      const vp = vpSize();
      const itemSize = getItemSize();
      const target = options.getCurrentIndex() * itemSize - vp / 2 + itemSize / 2;
      setScrollOffset(target);
    }
    function ensureVisible() {
      const vp = vpSize();
      const itemSize = getItemSize();
      const itemStart = options.getCurrentIndex() * itemSize;
      const itemEnd = itemStart + itemSize;
      const currentScroll = getScrollOffset();
      if (itemStart < currentScroll) {
        setScrollOffset(itemStart);
      } else if (itemEnd > currentScroll + vp) {
        setScrollOffset(itemEnd - vp);
      }
    }
    function update() {
      const total = options.getImageCount();
      if (total === 0) return;
      const currentIndex = options.getCurrentIndex();
      if (currentIndex !== lastCenteredIndex) {
        userScrollArmed = false;
        cancelThumbnailPreloadWork();
        if (clickedFromPanel) {
          ensureVisible();
          clickedFromPanel = false;
        } else {
          centerOnCurrent();
        }
        lastCenteredIndex = currentIndex;
      }
      renderVisibleItems();
      const displayLabel = `${options.getDisplayNumber(currentIndex)} / ${options.getDisplayNumber(total - 1)}`;
      counter.textContent = displayLabel;
    }
    let lastScrollPos = 0;
    let scrollRafPending = false;
    viewport.addEventListener("scroll", () => {
      if (scrollRafPending) return;
      scrollRafPending = true;
      requestAnimationFrame(() => {
        scrollRafPending = false;
        renderVisibleItems();
        if (isProgrammaticScroll) {
          isProgrammaticScroll = false;
          if (programmaticScrollTimer) clearTimeout(programmaticScrollTimer);
          lastScrollPos = getScrollOffset();
          return;
        }
        if (!userScrollArmed) return;
        scheduleThumbnailPreload();
        const currentScroll = getScrollOffset();
        const isScrollingUp = currentScroll < lastScrollPos;
        const isScrollingDown = currentScroll > lastScrollPos;
        if (onScrollToTop && currentScroll <= 5 && isScrollingUp) {
          onScrollToTop();
        } else if (onScrollToBottom && currentScroll >= maxOffset() - 5 && isScrollingDown) {
          onScrollToBottom();
        }
        lastScrollPos = currentScroll;
        openPanel(true);
        clearTimeout(hideTimeout);
        hideTimeout = setTimeout(() => openPanel(false), 500);
      });
    }, { passive: true });
    thumbPanel.addEventListener("wheel", (e) => {
      e.stopPropagation();
    }, { passive: true });
    viewport.addEventListener("wheel", (e) => {
      userScrollArmed = true;
      if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
        const prevScroll = getScrollOffset();
        if (!isVertical()) {
          e.preventDefault();
          viewport.scrollBy({ left: e.deltaY, behavior: "auto" });
        }
        const mOffset = maxOffset();
        if (prevScroll <= 0 && e.deltaY < 0 && onScrollToTop) {
          onScrollToTop();
        } else if (prevScroll >= mOffset - 1 && e.deltaY > 0 && onScrollToBottom) {
          onScrollToBottom();
        }
      }
    }, { passive: false });
    let isDragging = false;
    let hasDragged = false;
    let startX = 0;
    let startY = 0;
    let startScroll = 0;
    let lastTime = 0;
    let lastClientX = 0;
    let lastClientY = 0;
    let velocityX = 0;
    let velocityY = 0;
    let inertiaRaf = 0;
    let dragPointerId = null;
    viewport.addEventListener("pointerdown", (e) => {
      if (e.pointerType !== "mouse" || e.button !== 0) return;
      cancelAnimationFrame(inertiaRaf);
      isDragging = true;
      dragPointerId = e.pointerId;
      hasDragged = false;
      startX = e.clientX;
      startY = e.clientY;
      startScroll = isVertical() ? viewport.scrollTop : viewport.scrollLeft;
      lastClientX = e.clientX;
      lastClientY = e.clientY;
      lastTime = performance.now();
      velocityX = 0;
      velocityY = 0;
      viewport.style.cursor = "grabbing";
    });
    window.addEventListener("pointermove", (e) => {
      if (!isDragging || e.pointerId !== dragPointerId) return;
      if (Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5) {
        hasDragged = true;
        userScrollArmed = true;
      }
      e.preventDefault();
      const now = performance.now();
      const dt = now - lastTime;
      if (dt > 5) {
        const dx = e.clientX - lastClientX;
        const dy = e.clientY - lastClientY;
        velocityX = 0.6 * velocityX + 0.4 * (-dx / dt);
        velocityY = 0.6 * velocityY + 0.4 * (-dy / dt);
        lastClientX = e.clientX;
        lastClientY = e.clientY;
        lastTime = now;
      }
      const mOffset = maxOffset();
      if (isVertical()) {
        const newScroll = startScroll - (e.clientY - startY);
        viewport.scrollTop = newScroll;
        if (startScroll <= 0 && newScroll < -30 && onScrollToTop) {
          onScrollToTop();
          startScroll = 0;
          startY = e.clientY;
        } else if (startScroll >= mOffset - 1 && newScroll > mOffset + 30 && onScrollToBottom) {
          onScrollToBottom();
          startScroll = mOffset;
          startY = e.clientY;
        }
      } else {
        const newScroll = startScroll - (e.clientX - startX);
        viewport.scrollLeft = newScroll;
        if (startScroll <= 0 && newScroll < -30 && onScrollToTop) {
          onScrollToTop();
          startScroll = 0;
          startX = e.clientX;
        } else if (startScroll >= mOffset - 1 && newScroll > mOffset + 30 && onScrollToBottom) {
          onScrollToBottom();
          startScroll = mOffset;
          startX = e.clientX;
        }
      }
    });
    function endMouseDrag(withInertia) {
      if (!isDragging) return;
      isDragging = false;
      dragPointerId = null;
      viewport.style.cursor = "";
      if (!withInertia) {
        velocityX = 0;
        velocityY = 0;
        return;
      }
      const timeSinceLastMove = performance.now() - lastTime;
      if (timeSinceLastMove > 50) {
        velocityX = 0;
        velocityY = 0;
      }
      let virtualScroll = isVertical() ? viewport.scrollTop : viewport.scrollLeft;
      const startInertia = () => {
        const v = isVertical() ? velocityY : velocityX;
        if (Math.abs(v) < 0.05) return;
        const mOffset = maxOffset();
        virtualScroll += v * 16;
        if (isVertical()) {
          viewport.scrollTop = virtualScroll;
          if (virtualScroll < -30 && onScrollToTop) {
            onScrollToTop();
            return;
          }
          if (virtualScroll > mOffset + 30 && onScrollToBottom) {
            onScrollToBottom();
            return;
          }
          velocityY *= 0.95;
          if (virtualScroll < 0 || virtualScroll > mOffset) velocityY *= 0.8;
        } else {
          viewport.scrollLeft = virtualScroll;
          if (virtualScroll < -30 && onScrollToTop) {
            onScrollToTop();
            return;
          }
          if (virtualScroll > mOffset + 30 && onScrollToBottom) {
            onScrollToBottom();
            return;
          }
          velocityX *= 0.95;
          if (virtualScroll < 0 || virtualScroll > mOffset) velocityX *= 0.8;
        }
        inertiaRaf = requestAnimationFrame(startInertia);
      };
      inertiaRaf = requestAnimationFrame(startInertia);
    }
    window.addEventListener("pointerup", (e) => {
      if (e.pointerId === dragPointerId) endMouseDrag(true);
    });
    window.addEventListener("pointercancel", (e) => {
      if (e.pointerId === dragPointerId) endMouseDrag(false);
    });
    let touchStartX = 0;
    let touchStartY = 0;
    viewport.addEventListener("touchstart", (e) => {
      touchStartX = e.touches[0].clientX;
      touchStartY = e.touches[0].clientY;
    }, { passive: true });
    viewport.addEventListener("touchmove", (e) => {
      userScrollArmed = true;
      const currentScroll = getScrollOffset();
      const mOffset = maxOffset();
      if (isVertical()) {
        const dy = e.touches[0].clientY - touchStartY;
        if (currentScroll <= 0 && dy > 40 && onScrollToTop) {
          onScrollToTop();
          touchStartY = e.touches[0].clientY;
        } else if (currentScroll >= mOffset - 1 && dy < -40 && onScrollToBottom) {
          onScrollToBottom();
          touchStartY = e.touches[0].clientY;
        }
      } else {
        const dx = e.touches[0].clientX - touchStartX;
        if (currentScroll <= 0 && dx > 40 && onScrollToTop) {
          onScrollToTop();
          touchStartX = e.touches[0].clientX;
        } else if (currentScroll >= mOffset - 1 && dx < -40 && onScrollToBottom) {
          onScrollToBottom();
          touchStartX = e.touches[0].clientX;
        }
      }
    }, { passive: true });
    content.addEventListener("click", (e) => {
      if (hasDragged) {
        e.preventDefault();
        e.stopPropagation();
        return;
      }
      const item = e.target.closest(".sp-thumb-item");
      if (item == null ? void 0 : item.dataset.index) {
        const index = parseInt(item.dataset.index);
        if (!isNaN(index) && index >= 0 && index < options.getImageCount()) {
          clickedFromPanel = true;
          onIndexChange(index);
        }
      }
    });
    window.addEventListener("resize", () => {
      userScrollArmed = false;
      cancelThumbnailPreloadWork();
      if (options.getImageCount() > 0) {
        renderVisibleItems();
      }
    }, { passive: true });
    options.subscribeSettingsChanged(() => {
      userScrollArmed = false;
      cancelThumbnailPreloadWork();
      centerOnCurrent();
      renderVisibleItems();
    });
    return {
      getElement: () => thumbPanel,
      update,
      openPanel,
      closePanel,
      isActive: () => isPanelActive,
      resetCentering: () => {
        lastCenteredIndex = -1;
      }
    };
  }
  function createProgressTrack(onIndexChange, panelIsActive, options) {
    const progressTrack = document.createElement("div");
    progressTrack.className = "sp-sidebar-track";
    const progressThumb = document.createElement("div");
    progressThumb.className = "sp-sidebar-thumb";
    const progressLabel = document.createElement("div");
    progressLabel.className = "sp-sidebar-label";
    progressTrack.appendChild(progressThumb);
    progressTrack.appendChild(progressLabel);
    let cachedTrackSize = 0;
    let isDragging = false;
    let dragStartPos = 0;
    let thumbStartPos = 0;
    let previousBodyUserSelect = null;
    function isVertical() {
      const pos = options.getThumbnailPosition();
      return pos === "left" || pos === "right";
    }
    function refreshTrackSize() {
      cachedTrackSize = isVertical() ? progressTrack.offsetHeight : progressTrack.offsetWidth;
    }
    window.addEventListener("resize", refreshTrackSize, { passive: true });
    options.subscribeSettingsChanged(() => {
      progressThumb.style.width = "";
      progressThumb.style.height = "";
      progressThumb.style.left = "";
      progressThumb.style.top = "";
      refreshTrackSize();
    });
    function update() {
      const imageCount = options.getImageCount();
      if (imageCount === 0) return;
      if (!cachedTrackSize) refreshTrackSize();
      const trackSize = cachedTrackSize;
      let thumbSize;
      if (imageCount <= 10) {
        thumbSize = 60;
      } else if (imageCount <= 50) {
        thumbSize = Math.max(60, trackSize * (10 / imageCount));
      } else {
        thumbSize = Math.max(60, trackSize * (5 / imageCount));
      }
      const currentIndex = options.getCurrentIndex();
      const scrollProgress = currentIndex / Math.max(1, imageCount - 1);
      const maxThumbPos = trackSize - thumbSize;
      const thumbPos = scrollProgress * maxThumbPos;
      if (isVertical()) {
        progressThumb.style.height = `${thumbSize}px`;
        progressThumb.style.top = `${thumbPos}px`;
      } else {
        progressThumb.style.width = `${thumbSize}px`;
        progressThumb.style.left = `${thumbPos}px`;
      }
      const displayLabel = `${options.getDisplayNumber(currentIndex)} / ${options.getDisplayNumber(imageCount - 1)}`;
      progressLabel.textContent = displayLabel;
    }
    progressTrack.onclick = (e) => {
      if (e.target === progressThumb) return;
      const rect = progressTrack.getBoundingClientRect();
      let scrollProgress = 0;
      if (isVertical()) {
        const clickY = e.clientY - rect.top;
        scrollProgress = Math.min(1, Math.max(0, clickY / rect.height));
      } else {
        const clickX = e.clientX - rect.left;
        scrollProgress = Math.min(1, Math.max(0, clickX / rect.width));
      }
      const imageCount = options.getImageCount();
      const targetIndex = Math.round(scrollProgress * (imageCount - 1));
      if (targetIndex >= 0 && targetIndex < imageCount) {
        onIndexChange(targetIndex);
      }
    };
    progressThumb.onmousedown = (e) => {
      e.preventDefault();
      e.stopPropagation();
      isDragging = true;
      if (isVertical()) {
        dragStartPos = e.clientY;
        thumbStartPos = progressThumb.offsetTop;
      } else {
        dragStartPos = e.clientX;
        thumbStartPos = progressThumb.offsetLeft;
      }
      previousBodyUserSelect = document.body.style.userSelect;
      document.body.style.userSelect = "none";
    };
    document.addEventListener("mousemove", (e) => {
      if (!isDragging) return;
      const delta = isVertical() ? e.clientY - dragStartPos : e.clientX - dragStartPos;
      const newPos = thumbStartPos + delta;
      const trackSize = cachedTrackSize;
      const thumbSize = isVertical() ? progressThumb.offsetHeight : progressThumb.offsetWidth;
      const maxPos = trackSize - thumbSize;
      const clampedPos = Math.max(0, Math.min(maxPos, newPos));
      const scrollProgress = maxPos > 0 ? clampedPos / maxPos : 0;
      const imageCount = options.getImageCount();
      const targetIndex = Math.round(scrollProgress * (imageCount - 1));
      if (targetIndex >= 0 && targetIndex < imageCount && targetIndex !== options.getCurrentIndex()) {
        onIndexChange(targetIndex);
      }
    });
    function endProgressDrag(wake = true) {
      if (!isDragging) return;
      isDragging = false;
      document.body.style.userSelect = previousBodyUserSelect ?? "";
      previousBodyUserSelect = null;
      if (wake) wakeUp();
    }
    document.addEventListener("mouseup", () => endProgressDrag());
    window.addEventListener("blur", () => endProgressDrag(false));
    progressThumb.onclick = (e) => e.stopPropagation();
    let progressWakeTimer = null;
    function wakeUp() {
      if (panelIsActive()) return;
      progressTrack.classList.add("active");
      if (progressWakeTimer) clearTimeout(progressWakeTimer);
      progressWakeTimer = setTimeout(() => {
        progressTrack.classList.remove("active");
      }, 1500);
    }
    function sleep() {
      progressTrack.classList.remove("active");
      if (progressWakeTimer) clearTimeout(progressWakeTimer);
    }
    return {
      getElement: () => progressTrack,
      update,
      wakeUp,
      sleep
    };
  }
  function createMouseTracker(panel, progress, getThumbnailPosition, touchOnlyUi) {
    if (touchOnlyUi) return;
    const SENSITIVITY = 140;
    document.addEventListener("mousemove", (e) => {
      if (!document.querySelector(".pswp")) return;
      if (e.clientX < 0 || e.clientY < 0 || e.clientX >= window.innerWidth - 1 || e.clientY >= window.innerHeight - 1) {
        panel.closePanel();
        return;
      }
      const pos = getThumbnailPosition();
      let distanceToEdge = Infinity;
      switch (pos) {
        case "bottom":
          distanceToEdge = window.innerHeight - e.clientY;
          break;
        case "top":
          distanceToEdge = e.clientY;
          break;
        case "left":
          distanceToEdge = e.clientX;
          break;
        case "right":
          distanceToEdge = window.innerWidth - e.clientX;
          break;
      }
      if (distanceToEdge <= SENSITIVITY) {
        if (!panel.isActive()) {
          panel.openPanel();
          progress.sleep();
        }
      } else {
        if (panel.isActive()) {
          panel.closePanel();
        }
      }
    });
    document.addEventListener("mouseout", (e) => {
      if (!e.relatedTarget) panel.closePanel();
    });
    document.documentElement.addEventListener("mouseleave", () => {
      panel.closePanel();
    });
  }
  function createSidebar(onIndexChange, onScrollToBottom, onScrollToTop, options) {
    const panel = createThumbnailPanel(onIndexChange, onScrollToBottom, onScrollToTop, options);
    const progress = createProgressTrack(onIndexChange, panel.isActive, options);
    createMouseTracker(
      panel,
      progress,
      options.getThumbnailPosition,
      options.inputCapabilities.touchOnlyUi
    );
    function update() {
      panel.update();
      progress.update();
    }
    function applyPositionClasses() {
      const newPos = options.getThumbnailPosition();
      panel.getElement().classList.remove(`position-top`, `position-bottom`, `position-left`, `position-right`);
      progress.getElement().classList.remove(`position-top`, `position-bottom`, `position-left`, `position-right`);
      panel.getElement().classList.add(`position-${newPos}`);
      progress.getElement().classList.add(`position-${newPos}`);
    }
    applyPositionClasses();
    options.subscribeSettingsChanged(applyPositionClasses);
    return {
      update,
      getElements: () => [progress.getElement(), panel.getElement()],
      wakeUpProgressBar: progress.wakeUp,
      openPanel: panel.openPanel,
      closePanel: panel.closePanel,
      resetCentering: panel.resetCentering
    };
  }
  function createReaderShell(options) {
    const hud = createStatusHUD();
    const sidebar = createSidebar(
      options.onIndexChange,
      options.onScrollToBottom,
      options.onScrollToTop,
      {
        onMobileInteractionStart: options.onMobileInteractionStart,
        onMobileInteractionEnd: options.onMobileInteractionEnd,
        subscribeThumbnailChange: options.thumbnailController.subscribeChange,
        preloadThumbnails: options.thumbnailController.preload,
        cancelThumbnailPreloads: options.thumbnailController.cancelPreloads,
        finishThumbnailPreload: options.thumbnailController.finishPreload,
        getPreloadedAsset: options.thumbnailController.getPreloadedAsset,
        getPreloadPhase: options.thumbnailController.getPreloadPhase,
        getImageCount: options.getImageCount,
        getCurrentIndex: options.getCurrentIndex,
        getImageAt: options.getImageAt,
        getItemAt: options.getItemAt,
        getDisplayNumber: options.getDisplayNumber,
        getThumbnailPosition: options.getThumbnailPosition,
        subscribeSettingsChanged: options.subscribeSettingsChanged,
        inputCapabilities: options.inputCapabilities
      }
    );
    function mount(driver, counter) {
      driver.registerCounter(counter);
      const sidebarElements = sidebar.getElements();
      sidebarElements.forEach((element) => {
        element.style.pointerEvents = "auto";
        element.dataset.readerWheelBlock = "";
      });
      driver.appendUi([...sidebarElements, hud.getElement()]);
      const unsubscribeVisibility = driver.observeUiVisibility((isVisible) => {
        sidebarElements.forEach((element) => {
          element.classList.toggle("sp-hidden-by-pswp", !isVisible);
        });
        if (options.inputCapabilities.touchOnlyUi) {
          if (isVisible) sidebar.openPanel(false);
          else sidebar.closePanel();
        }
      });
      return () => {
        unsubscribeVisibility();
        sidebar.closePanel();
        options.thumbnailController.cancelPreloads();
      };
    }
    return {
      update: sidebar.update,
      resetCentering: sidebar.resetCentering,
      showStatus: hud.show,
      hideStatus: hud.hide,
      mount
    };
  }
  class ReaderSession {
    constructor(getItems) {
      this.images = [];
      this.index = 0;
      this.navigated = false;
      this.previousIndex = -1;
      this.getItems = getItems;
    }
    syncImages(images) {
      const changed = images.length !== this.images.length || images.some((image, index) => image !== this.images[index]);
      if (!changed) return false;
      this.images = images;
      return true;
    }
    replaceImage(index, image) {
      if (index < 0 || index >= this.images.length) return;
      this.images[index] = image;
    }
    setCurrentIndex(index, trackNavigation = true) {
      if (trackNavigation && this.previousIndex !== -1 && this.previousIndex !== index) {
        this.navigated = true;
      }
      this.previousIndex = index;
      this.index = index;
    }
    reset(startIndex) {
      this.navigated = false;
      this.previousIndex = startIndex;
      this.index = startIndex;
    }
    prepend(count) {
      this.index += count;
      this.previousIndex = this.index;
    }
    get currentIndex() {
      return this.index;
    }
    get lastIndex() {
      return this.previousIndex;
    }
    get hasNavigated() {
      return this.navigated;
    }
    get imageCount() {
      return this.images.length;
    }
    elementAt(index) {
      return this.images[index];
    }
    itemAt(index) {
      return this.getItems()[index];
    }
    indexOfViewerUrl(url) {
      return this.getItems().findIndex((item) => item.viewerUrl === url);
    }
    snapshotImages() {
      return this.images;
    }
  }
  function createThumbnailController(session, scroll, deps) {
    const acquire = deps.acquire;
    const maxConcurrent = Math.max(1, deps.maxConcurrent ?? 2);
    const entries = new Map();
    const listeners = new Set();
    let pending = [];
    let active = 0;
    function notify(index) {
      listeners.forEach((listener) => listener(index));
    }
    function releaseEntry(entry) {
      var _a, _b;
      (_a = entry.releaseSlot) == null ? void 0 : _a.call(entry);
      entry.releaseSlot = void 0;
      (_b = entry.lease) == null ? void 0 : _b.release();
      entry.lease = void 0;
      entry.asset = void 0;
    }
    function pump() {
      while (active < maxConcurrent && pending.length > 0) {
        const index = pending.shift();
        const entry = entries.get(index);
        const item = session.itemAt(index);
        if (!entry || entry.phase !== "loading" || !item) continue;
        active++;
        let slotHeld = true;
        entry.releaseSlot = () => {
          if (!slotHeld) return;
          slotHeld = false;
          active--;
        };
        const lease = acquire(item.viewerUrl, {
          intent: "thumbnail",
          priority: deps.priority
        });
        entry.lease = lease;
        lease.result.then((asset) => {
          if (entries.get(index) !== entry) return;
          if (!asset) {
            releaseEntry(entry);
            entry.phase = "error";
          } else {
            entry.asset = asset;
            entry.phase = "ready";
          }
          notify(index);
        }).catch(() => {
          if (entries.get(index) !== entry) return;
          releaseEntry(entry);
          entry.phase = "error";
          notify(index);
        }).finally(() => {
          var _a;
          (_a = entry.releaseSlot) == null ? void 0 : _a.call(entry);
          entry.releaseSlot = void 0;
          pump();
        });
      }
    }
    function cancelPreloads() {
      pending = [];
      const changed = Array.from(entries.keys());
      entries.forEach(releaseEntry);
      entries.clear();
      changed.forEach(notify);
      pump();
    }
    function preload(indices) {
      cancelPreloads();
      const unique = new Set();
      for (const index of indices) {
        if (unique.has(index)) continue;
        const item = session.itemAt(index);
        if (!item || item.preview.kind !== "none" && item.preview.kind !== "derived") continue;
        const element = session.elementAt(index);
        if ((element == null ? void 0 : element.tagName) === "IMG" && element.complete && element.naturalWidth > 0) continue;
        unique.add(index);
        entries.set(index, { phase: "loading" });
        pending.push(index);
        notify(index);
      }
      pump();
    }
    function finishPreload(index, failed = false) {
      const entry = entries.get(index);
      if (!entry) return;
      releaseEntry(entry);
      if (failed) {
        entry.phase = "error";
      } else {
        entries.delete(index);
      }
      notify(index);
    }
    return {
      preload,
      cancelPreloads,
      finishPreload,
      getPreloadedAsset(index) {
        var _a;
        return (_a = entries.get(index)) == null ? void 0 : _a.asset;
      },
      getPreloadPhase(index) {
        var _a;
        return ((_a = entries.get(index)) == null ? void 0 : _a.phase) ?? "idle";
      },
      subscribeChange(listener) {
        listeners.add(listener);
        const unsubscribeScroll = scroll.subscribeImageLoaded((event) => listener(event.index));
        return () => {
          listeners.delete(listener);
          unsubscribeScroll();
        };
      }
    };
  }
  const DEFAULT_GUTTER = 20;
  const UNKNOWN_PORTRAIT_RATIO = 2 / 3;
  function hasReliablePortraitSize(page) {
    return !!page && Number.isFinite(page.width) && Number.isFinite(page.height) && (page.width ?? 0) > 0 && (page.height ?? 0) > 0 && page.width < page.height;
  }
  function portraitRatio(page) {
    if (!page) return null;
    const hasSize = Number.isFinite(page.width) && Number.isFinite(page.height) && (page.width ?? 0) > 0 && (page.height ?? 0) > 0;
    if (!hasSize) return void 0;
    if (!hasReliablePortraitSize(page)) return null;
    return page.width / page.height;
  }
  function pairState(first, second, enabled, viewport) {
    if (!enabled || !first || !second || !(viewport.width > 0) || !(viewport.height > 0)) return null;
    const firstRatio = portraitRatio(first);
    const secondRatio = portraitRatio(second);
    if (firstRatio === null || secondRatio === null) return null;
    const gutter = Math.max(0, viewport.gutter ?? DEFAULT_GUTTER);
    const fittedWidth = viewport.height * ((firstRatio ?? UNKNOWN_PORTRAIT_RATIO) + (secondRatio ?? UNKNOWN_PORTRAIT_RATIO));
    if (fittedWidth + gutter > viewport.width) return null;
    if (first.failed || second.failed) return "pending-pair";
    return firstRatio === void 0 || secondRatio === void 0 ? "pending-pair" : "pair";
  }
  function createSingle(page, index) {
    const width = page.width && page.width > 0 ? page.width : 1;
    const height = page.height && page.height > 0 ? page.height : 1;
    return {
      key: `single:${page.key}`,
      state: "single",
      logicalIndices: [index],
      primaryIndex: index,
      width,
      height
    };
  }
  function createSpreadLayout(pages, viewport, enabled, preferredPrimaryKey) {
    const spreads = [];
    const logicalToSpread = new Array(pages.length);
    for (let index = 0; index < pages.length; index += 2) {
      const first = pages[index];
      const second = pages[index + 1];
      const state = pairState(first, second, enabled, viewport);
      if (second && state) {
        const preferredIndex = preferredPrimaryKey === second.key ? index + 1 : index;
        const spreadIndex = spreads.length;
        spreads.push({
          key: `pair:${first.key}:${second.key}`,
          state,
          logicalIndices: [index, index + 1],
          primaryIndex: preferredIndex,

width: viewport.width,
          height: viewport.height
        });
        logicalToSpread[index] = spreadIndex;
        logicalToSpread[index + 1] = spreadIndex;
      } else {
        logicalToSpread[index] = spreads.length;
        spreads.push(createSingle(first, index));
        if (second) {
          logicalToSpread[index + 1] = spreads.length;
          spreads.push(createSingle(second, index + 1));
        }
      }
    }
    function build(nextSpreads) {
      return {
        spreads: nextSpreads,
        spreadIndexForLogical: (index) => logicalToSpread[index] ?? -1,
        logicalIndexForSpread: (index) => {
          var _a;
          return ((_a = nextSpreads[index]) == null ? void 0 : _a.primaryIndex) ?? -1;
        },
        withPrimaryLogical: (index) => {
          const spreadIndex = logicalToSpread[index];
          if (spreadIndex === void 0) return build(nextSpreads);
          const spread = nextSpreads[spreadIndex];
          if (!spread.logicalIndices.includes(index) || spread.primaryIndex === index) return build(nextSpreads);
          const updated = nextSpreads.slice();
          updated[spreadIndex] = { ...spread, primaryIndex: index };
          return build(updated);
        }
      };
    }
    return build(spreads);
  }
  function formatSpreadCounter(spread, offset, total) {
    if (!spread) return `0 / ${offset + total}`;
    const first = offset + spread.logicalIndices[0] + 1;
    const last = offset + spread.logicalIndices[spread.logicalIndices.length - 1] + 1;
    return `${first === last ? first : `${first}–${last}`} / ${offset + total}`;
  }
  function createInteractionSettleScheduler(options) {
    const requiredIdleFrames = options.requiredIdleFrames ?? 2;
    const burstFrames = options.burstFrames ?? 8;
    const retryDelayMs = options.retryDelayMs ?? 250;
    const maxBlockedChecks = options.maxBlockedChecks ?? 40;
    const requestFrame = options.requestFrame ?? requestAnimationFrame;
    const cancelFrame = options.cancelFrame ?? cancelAnimationFrame;
    const setDelay = options.setDelay ?? ((callback, delayMs) => window.setTimeout(callback, delayMs));
    const clearDelay = options.clearDelay ?? ((id) => window.clearTimeout(id));
    let requested = false;
    let frameId = 0;
    let timerId;
    let idleFrames = 0;
    let blockedChecks = 0;
    let blockedNotified = false;
    function clearScheduled() {
      if (frameId) {
        cancelFrame(frameId);
        frameId = 0;
      }
      if (timerId !== void 0) {
        clearDelay(timerId);
        timerId = void 0;
      }
    }
    function scheduleFrame() {
      if (!requested || frameId || timerId !== void 0) return;
      frameId = requestFrame(run);
    }
    function scheduleDelayedFrame() {
      if (!requested || frameId || timerId !== void 0) return;
      timerId = setDelay(() => {
        timerId = void 0;
        scheduleFrame();
      }, retryDelayMs);
    }
    function run() {
      var _a;
      frameId = 0;
      if (!requested) return;
      if (options.isBlocked()) {
        idleFrames = 0;
        blockedChecks++;
        if (!blockedNotified) {
          blockedNotified = true;
          (_a = options.onBlocked) == null ? void 0 : _a.call(options);
        }
        if (blockedChecks >= maxBlockedChecks) {
          requested = false;
          return;
        }
        if (blockedChecks < burstFrames) scheduleFrame();
        else scheduleDelayedFrame();
        return;
      }
      blockedChecks = 0;
      idleFrames++;
      if (idleFrames < requiredIdleFrames) {
        scheduleFrame();
        return;
      }
      requested = false;
      idleFrames = 0;
      blockedNotified = false;
      options.onSettled();
    }
    return {
      request() {
        if (!requested) {
          requested = true;
          idleFrames = 0;
          blockedChecks = 0;
          blockedNotified = false;
        } else {
          idleFrames = 0;
          if (timerId !== void 0 && !options.isBlocked()) {
            clearDelay(timerId);
            timerId = void 0;
          }
        }
        scheduleFrame();
      },
      cancel() {
        requested = false;
        idleFrames = 0;
        blockedChecks = 0;
        blockedNotified = false;
        clearScheduled();
      }
    };
  }
  function getReaderInputCapabilities(matches = (query) => window.matchMedia(query).matches, maxTouchPoints = navigator.maxTouchPoints) {
    const hasHoveringFinePointer = matches("(hover: hover) and (pointer: fine)") || matches("(any-hover: hover) and (any-pointer: fine)");
    const hasCoarsePointer = matches("(pointer: coarse)") || matches("(any-pointer: coarse)") || maxTouchPoints > 0;
    return { touchOnlyUi: !hasHoveringFinePointer && hasCoarsePointer };
  }
  const RETURN_GEOMETRY_RADIUS = 5;
  function createReaderController(deps) {
    const inputCapabilities = getReaderInputCapabilities();
    let pswp = null;
    let isActive = false;
    let isReinitializing = false;
    let reinitializationDepth = 0;
    let overflowSnapshot = null;
    const session = new ReaderSession(deps.context.getGalleryItems);
    let spreadLayout = createSpreadLayout([], { width: 1, height: 1 }, false);
    let onMobileInteractionStart = () => {
    };
    let onMobileInteractionEnd = () => {
    };
    let entryScrollY = 0;
    let entryItemKey = "";
    let refreshActiveLayout = () => {
    };
    let refreshActiveHud = () => {
    };
    const failedLogicalIndices = new Set();
    const renderedFailedLogicalIndices = new Set();
    function startReinitializing() {
      reinitializationDepth++;
      isReinitializing = true;
    }
    function finishReinitializing() {
      reinitializationDepth = Math.max(0, reinitializationDepth - 1);
      isReinitializing = reinitializationDepth > 0;
    }
    function itemKeyAt(index) {
      var _a;
      const element = session.elementAt(index);
      return ((_a = session.itemAt(index)) == null ? void 0 : _a.key) || (element == null ? void 0 : element.dataset.itemKey) || (element == null ? void 0 : element.dataset.viewerUrl) || (element == null ? void 0 : element.dataset.url) || "";
    }
    function spreadPages() {
      return Array.from({ length: session.imageCount }, (_, index) => {
        var _a, _b;
        const item = session.itemAt(index);
        const element = session.elementAt(index);
        const cached = item && getCachedImage(item.viewerUrl);
        const sourceHint = item && getImageDimensionsHint(item.viewerUrl);
        let width = (cached == null ? void 0 : cached.width) ?? ((_a = item == null ? void 0 : item.dimensions) == null ? void 0 : _a.width) ?? (sourceHint == null ? void 0 : sourceHint.width);
        let height = (cached == null ? void 0 : cached.height) ?? ((_b = item == null ? void 0 : item.dimensions) == null ? void 0 : _b.height) ?? (sourceHint == null ? void 0 : sourceHint.height);
        const previewSize = (item == null ? void 0 : item.preview.kind) === "url" ? item.preview.size : (item == null ? void 0 : item.preview.kind) === "sprite" ? item.preview.crop : void 0;
        if ((!width || !height) && (previewSize == null ? void 0 : previewSize.width) && previewSize.height) {
          width = previewSize.width;
          height = previewSize.height;
        }
        if ((!width || !height) && (element == null ? void 0 : element.tagName) === "IMG") {
          const image = element;
          if (image.complete && image.naturalWidth > 0 && image.naturalHeight > 0) {
            width = image.naturalWidth;
            height = image.naturalHeight;
          }
        }
        return {
          key: itemKeyAt(index) || `logical:${index}`,
          width,
          height,
          failed: failedLogicalIndices.has(index)
        };
      });
    }
    function calculateSpreadLayout(preferredLogicalIndex = session.currentIndex) {
      return createSpreadLayout(
        spreadPages(),
        { width: window.innerWidth, height: window.innerHeight, gutter: 20 },
        deps.context.isDoublePageModeEnabled(),
        itemKeyAt(preferredLogicalIndex)
      );
    }
    function currentSpread() {
      return pswp ? spreadLayout.spreads[pswp.currentIndex] : void 0;
    }
    function activeLogicalIndices() {
      var _a;
      return ((_a = currentSpread()) == null ? void 0 : _a.logicalIndices) ?? [session.currentIndex];
    }
    function syncImages() {
      const freshImages = Array.from(document.querySelectorAll(".r-img, .r-ph"));
      if (session.syncImages(freshImages)) {
        if (!pswp) {
          spreadLayout = calculateSpreadLayout();
          shell.update();
        }
      }
    }
    const prefetch = createPrefetchController(session, deps.prefetchPolicy);
    const thumbnailController = createThumbnailController(session, deps.scroll, {
      acquire: acquireImage,
      priority: LOAD_PRIORITY.thumbnail
    });
    const autoPlay = createAutoPlay(() => pswp == null ? void 0 : pswp.next(), deps.context);
    let pagination;
    const shell = createReaderShell({
      inputCapabilities,
      onIndexChange: (index) => {
        session.setCurrentIndex(index);
        spreadLayout = spreadLayout.withPrimaryLogical(index);
        pswp == null ? void 0 : pswp.goTo(spreadLayout.spreadIndexForLogical(index));
        autoPlay.reset();
      },
      onScrollToBottom: () => pagination.loadNext(),
      onScrollToTop: () => pagination.loadPrev(),
      onMobileInteractionStart: () => onMobileInteractionStart(),
      onMobileInteractionEnd: () => onMobileInteractionEnd(),
      thumbnailController,
      getImageCount: () => session.imageCount,
      getCurrentIndex: () => session.currentIndex,
      getImageAt: (index) => session.elementAt(index),
      getItemAt: (index) => session.itemAt(index),
      getDisplayNumber: (index) => deps.context.getImageOffset() + index + 1,
      getThumbnailPosition: deps.context.getThumbnailPosition,
      subscribeSettingsChanged: deps.context.subscribeSettingsChanged
    });
    pagination = createReaderPaginationController(session, {
      pageLoader: deps.pageLoader,
      appendPage: deps.onLoadNextPage,
      prependPage: deps.onLoadPrevPage,
      syncImages,
      afterPrepend: (itemCount) => {
        session.prepend(itemCount);
        deps.context.setImageOffset(Math.max(0, deps.context.getImageOffset() - itemCount));
      },
      onPageAdded: (direction) => {
        refreshActiveLayout();
        prefetch.setWindow(session.currentIndex, direction === "next" ? 1 : -1);
      },
      onLoading: () => shell.showStatus({ status: "loading", text: i18n.downloading }),
      onIdle: () => refreshActiveHud(),
      onError: () => shell.showStatus({ status: "error", text: i18n.loadFailed })
    }, deps.context);
    function open(startIdx) {
      if (isActive) return;
      session.syncImages(Array.from(document.querySelectorAll(".r-img, .r-ph")));
      if (session.imageCount === 0) {
        alert(i18n.waitImagesToLoad);
        return;
      }
      let startIndex = startIdx ?? 0;
      if (startIdx === void 0) {
        if (deps.context.isScrollMode()) {
          let minDistance = Infinity;
          session.snapshotImages().forEach((img, index) => {
            const rect = img.getBoundingClientRect();
            const viewportCenter = window.innerHeight / 2;
            if (rect.top <= viewportCenter && rect.bottom >= viewportCenter) {
              startIndex = index;
              minDistance = -1;
            } else if (minDistance !== -1) {
              const distanceToCenter = rect.bottom < viewportCenter ? viewportCenter - rect.bottom : rect.top - viewportCenter;
              if (distanceToCenter < minDistance) {
                minDistance = distanceToCenter;
                startIndex = index;
              }
            }
          });
        } else {
          startIndex = session.currentIndex;
        }
      }
      entryScrollY = window.scrollY;
      entryItemKey = itemKeyAt(startIndex);
      session.reset(startIndex);
      spreadLayout = calculateSpreadLayout(startIndex);
      isActive = true;
      overflowSnapshot = {
        documentElement: document.documentElement.style.overflow,
        body: document.body.style.overflow
      };
      document.documentElement.style.overflow = "hidden";
      document.body.style.overflow = "hidden";
      deps.context.emitReaderModeChanged();
      if (deps.context.isScrollMode()) {
        deps.scroll.pause();
        document.documentElement.classList.add("hr-reader-open");
      }
      shell.resetCentering();
      try {
        initPhotoSwipe(spreadLayout.spreadIndexForLogical(startIndex));
      } catch (error) {
        console.error("[Hentai-Reader] Reader initialization failed", error);
        prefetch.clear();
        isActive = false;
        if (pswp) {
          const failedDriver = pswp;
          pswp = null;
          try {
            failedDriver.destroy();
          } catch {
          }
        }
        if (overflowSnapshot) {
          document.documentElement.style.overflow = overflowSnapshot.documentElement;
          document.body.style.overflow = overflowSnapshot.body;
          overflowSnapshot = null;
        }
        document.documentElement.classList.remove("hr-reader-open");
        if (deps.context.isScrollMode()) deps.scroll.resume();
        deps.context.emitReaderModeChanged();
        return;
      }
      if (deps.context.isAutoPlayEnabled()) {
        autoPlay.start();
      }
    }
    function initPhotoSwipe(startSpreadIndex) {
      let mobileUiTimeout;
      const touchOnlyUi = inputCapabilities.touchOnlyUi;
      function triggerMobileUITimeout() {
        clearTimeout(mobileUiTimeout);
        if (!touchOnlyUi) return;
        mobileUiTimeout = setTimeout(() => {
          pswp == null ? void 0 : pswp.hideUi();
        }, 2e3);
      }
      function cancelMobileUITimeout() {
        clearTimeout(mobileUiTimeout);
      }
      function syncUiAvailabilityForCurrent() {
        if (!pswp) return;
        if (touchOnlyUi) return;
        if (!pswp.isCurrentContentLoaded()) {
          cancelMobileUITimeout();
          pswp.showUi();
        }
      }
      onMobileInteractionStart = cancelMobileUITimeout;
      onMobileInteractionEnd = triggerMobileUITimeout;
      function handleScreenClick(point, defaultCenterAction) {
        const width = window.innerWidth;
        if (point.x < width * 0.3) {
          if ((pswp == null ? void 0 : pswp.currentIndex) === 0 && deps.context.getPrevUrl() && !deps.context.isPageFetching()) {
            pagination.loadPrev();
          } else {
            pswp == null ? void 0 : pswp.prev();
          }
        } else if (point.x > width * 0.7) {
          if ((pswp == null ? void 0 : pswp.currentIndex) === spreadLayout.spreads.length - 1 && deps.context.getNextUrl() && !deps.context.isPageFetching()) {
            pagination.loadNext();
          } else {
            pswp == null ? void 0 : pswp.next();
          }
        } else {
          if (defaultCenterAction === "zoom" && (pswp == null ? void 0 : pswp.canToggleCurrentZoom())) {
            pswp.toggleCurrentZoom(point);
          } else {
            const isNowVisible = pswp == null ? void 0 : pswp.toggleUi();
            if (isNowVisible) triggerMobileUITimeout();
          }
        }
      }
      pswp = deps.createDriver({
        startIndex: startSpreadIndex,
        onBackgroundClick: (point) => handleScreenClick(point, "toggle"),
        onImageClick: (point) => handleScreenClick(point, "zoom"),
        onTap: (point) => handleScreenClick(point, "toggle"),
        onRenderedImageStateChange: ({ logicalIndex, state }) => {
          if (state === "error") renderedFailedLogicalIndices.add(logicalIndex);
          else renderedFailedLogicalIndices.delete(logicalIndex);
          if (!activeLogicalIndices().includes(logicalIndex)) return;
          refreshHudForCurrent();
          syncUiAvailabilityForCurrent();
          if (state === "loaded" && deps.context.isAutoPlayEnabled() && (pswp == null ? void 0 : pswp.isCurrentContentLoaded())) autoPlay.start();
        }
      });
      pswp.on("zoomLevelsUpdate", (e) => {
        const zl = e.zoomLevels;
        if (zl.initial > zl.fit) zl.fit = zl.initial;
      });
      const unsubscribeImageLoaded = deps.scroll.subscribeImageLoaded(({ index, element }) => {
        session.replaceImage(index, element);
        if (pswp && index >= 0 && index < session.imageCount) {
          refreshSpreadLayout(session.currentIndex, index);
        }
      });
      pswp.on("destroy", () => {
        clearTimeout(mobileUiTimeout);
        onMobileInteractionStart = () => {
        };
        onMobileInteractionEnd = () => {
        };
        unsubscribeImageLoaded();
      });
      function showImagePhase(phase) {
        switch (phase) {
          case "error":
            shell.showStatus({ status: "error", text: i18n.loadFailed });
            break;
          case "resolving":
            shell.showStatus({ status: "loading", text: i18n.resolvingImage });
            break;
          case "switching-source":
            shell.showStatus({ status: "loading", text: i18n.switchingImageSource });
            break;
          case "downloading":
            shell.showStatus({ status: "loading", text: i18n.downloading });
            break;
          case "loaded":
            shell.hideStatus();
            break;
        }
      }
      const imageController = createReaderImageController(session, {
        getCurrentIndex: () => session.currentIndex,
        getActiveIndices: activeLogicalIndices,
        getSlideContentState: (index) => pswp == null ? void 0 : pswp.getSlideContentState(spreadLayout.spreadIndexForLogical(index)),
        onPhaseChange: (index, phase) => {
          const isActiveIndex = activeLogicalIndices().includes(index);
          const wasFailed = failedLogicalIndices.has(index);
          if (phase === "error") failedLogicalIndices.add(index);
          else if (phase === "loaded") failedLogicalIndices.delete(index);
          if (wasFailed !== failedLogicalIndices.has(index)) refreshActiveLayout();
          if (phase === "downloading") refreshSpreadLayout(session.currentIndex, index);
          if (isActiveIndex) refreshHudForCurrent();
        },
        onAssetReady: (index) => {
          const isActiveIndex = activeLogicalIndices().includes(index);
          refreshSpreadLayout(session.currentIndex, index);
          refreshHudForCurrent();
          if (isActiveIndex) syncUiAvailabilityForCurrent();
          if (isActiveIndex && deps.context.isAutoPlayEnabled() && (pswp == null ? void 0 : pswp.isCurrentContentLoaded())) autoPlay.start();
        }
      });
      let resizeRaf = 0;
      let pendingLayoutRequest = {
        preferredLogicalIndex: session.currentIndex,
        refreshLogicalIndex: session.currentIndex
      };
      const layoutSettleScheduler = createInteractionSettleScheduler({
        isBlocked: () => (pswp == null ? void 0 : pswp.isInteracting()) ?? false,
        onBlocked: () => {
          showImagePhase("downloading");
        },
        onSettled: applyPendingSpreadLayout
      });
      function refreshSpreadLayout(preferredLogicalIndex = session.currentIndex, refreshLogicalIndex = preferredLogicalIndex) {
        if (!pswp) return;
        pendingLayoutRequest = {
          preferredLogicalIndex,
          refreshLogicalIndex
        };
        layoutSettleScheduler.request();
      }
      function applyPendingSpreadLayout() {
        if (!pswp) return;
        const {
          preferredLogicalIndex,
          refreshLogicalIndex
        } = pendingLayoutRequest;
        const previousKeys = spreadLayout.spreads.map((spread) => spread.key).join("|");
        const next = calculateSpreadLayout(preferredLogicalIndex);
        const nextKeys = next.spreads.map((spread) => spread.key).join("|");
        spreadLayout = next;
        const targetSpread = spreadLayout.spreadIndexForLogical(preferredLogicalIndex);
        if (targetSpread < 0) return;
        if (previousKeys !== nextKeys) {
          startReinitializing();
          try {
            session.setCurrentIndex(preferredLogicalIndex, false);
            pswp.syncLayout(targetSpread);
            shell.update();
          } finally {
            finishReinitializing();
            refreshHudForCurrent();
          }
          return;
        }
        if (pswp.currentIndex !== targetSpread) {
          startReinitializing();
          try {
            pswp.stopMotion();
            pswp.goTo(targetSpread);
            shell.update();
          } finally {
            queueMicrotask(() => {
              finishReinitializing();
              refreshHudForCurrent();
            });
          }
        } else {
          const refreshSpread = spreadLayout.spreadIndexForLogical(refreshLogicalIndex);
          if (refreshSpread >= 0) {
            pswp.refreshSlide(refreshSpread);
            refreshHudForCurrent();
            syncUiAvailabilityForCurrent();
          }
        }
      }
      refreshActiveLayout = () => refreshSpreadLayout(session.currentIndex);
      const onResize = () => {
        cancelAnimationFrame(resizeRaf);
        resizeRaf = requestAnimationFrame(() => refreshSpreadLayout(session.currentIndex));
      };
      window.addEventListener("resize", onResize, { passive: true });
      function refreshHudForCurrent() {
        if (!pswp) return;
        const activeIndices = activeLogicalIndices();
        if (activeIndices.some((index) => renderedFailedLogicalIndices.has(index))) {
          showImagePhase("error");
          return;
        }
        const phases = activeIndices.map((index) => imageController.getPhase(index));
        const phase = phases.includes("error") ? "error" : phases.includes("resolving") ? "resolving" : phases.includes("switching-source") ? "switching-source" : phases.includes("downloading") ? "downloading" : "loaded";
        showImagePhase(phase === "loaded" && !pswp.isCurrentContentLoaded() ? "downloading" : phase);
      }
      refreshActiveHud = refreshHudForCurrent;
      pswp.on("destroy", () => {
        cancelAnimationFrame(resizeRaf);
        layoutSettleScheduler.cancel();
        window.removeEventListener("resize", onResize);
        refreshActiveLayout = () => {
        };
        refreshActiveHud = () => {
        };
        imageController.dispose();
      });
      pswp.on("contentLoadImage", (e) => {
        var _a;
        if (typeof ((_a = e.content) == null ? void 0 : _a.index) === "number") {
          const logicalIndex = spreadLayout.logicalIndexForSpread(e.content.index);
          if (logicalIndex >= 0) imageController.handleContentLoad(logicalIndex);
        }
      });
      pswp.on("loadComplete", (e) => {
        var _a;
        if (typeof ((_a = e.content) == null ? void 0 : _a.index) === "number") {
          const logicalIndex = spreadLayout.logicalIndexForSpread(e.content.index);
          if (logicalIndex >= 0) imageController.handleLoadComplete(logicalIndex, e.isError);
        }
      });
      pswp.on("numItems", (e) => {
        e.numItems = spreadLayout.spreads.length;
      });
      pswp.on("itemData", (e) => {
        const spread = spreadLayout.spreads[e.index];
        if (!spread) {
          e.itemData = { src: "", w: 1, h: 1 };
        } else {
          e.itemData = imageController.getSpreadItemData(
            spread.logicalIndices,
            spread.width,
            spread.height
          );
        }
      });
      const wheelPager = createWheelPager({
        getCurrentIndex: () => (pswp == null ? void 0 : pswp.currentIndex) ?? 0,
        isCurrentZoomed: () => (pswp == null ? void 0 : pswp.isCurrentZoomed()) ?? false,
        goTo: (index) => pswp == null ? void 0 : pswp.goTo(index),
        stopMotion: () => pswp == null ? void 0 : pswp.stopMotion(),
        getImageCount: () => spreadLayout.spreads.length,
        getPageLoadBehavior: (index) => {
          var _a;
          return getWheelPageLoadBehavior(
            ((_a = spreadLayout.spreads[index]) == null ? void 0 : _a.logicalIndices) ?? [],
            (logical) => imageController.isLoading(logical)
          );
        },
        onEdgeForward: () => {
          if (deps.context.getNextUrl() && !deps.context.isPageFetching()) pagination.loadNext();
        },
        onEdgeBackward: () => {
          if (deps.context.getPrevUrl() && !deps.context.isPageFetching()) pagination.loadPrev();
        }
      });
      pswp.on("destroy", () => wheelPager.stop());
      pswp.on("change", () => {
        if (isReinitializing) {
          return;
        }
        if (pswp) {
          const previousIndex = session.currentIndex;
          const nextLogicalIndex = spreadLayout.logicalIndexForSpread(pswp.currentIndex);
          const isNavigatingBackwards = nextLogicalIndex < previousIndex;
          session.setCurrentIndex(nextLogicalIndex);
          shell.update();
          pagination.checkNearEnd();
          refreshHudForCurrent();
          syncUiAvailabilityForCurrent();
          if (session.currentIndex <= 3 && deps.context.getPrevUrl() && session.hasNavigated && isNavigatingBackwards) {
            pagination.loadPrev();
          }
          if (deps.context.isAutoPlayEnabled()) {
            autoPlay.stop();
            if (pswp.currentIndex >= spreadLayout.spreads.length - 1 && !deps.context.getNextUrl()) {
              autoPlay.stopAtEnd();
            } else if (pswp.isCurrentContentLoaded()) {
              autoPlay.start();
            }
          }
          refreshSpreadLayout(session.currentIndex, session.currentIndex);
          prefetch.setWindow(session.currentIndex, isNavigatingBackwards ? -1 : 1);
          imageController.releaseOutside(session.currentIndex);
        }
      });
      pswp.on("uiRegister", () => {
        if (!pswp) return;
        const unmountShell = shell.mount(
          pswp,
          (index) => formatSpreadCounter(spreadLayout.spreads[index], deps.context.getImageOffset(), session.imageCount)
        );
        const removeWheel = pswp.installWheel((event) => wheelPager.onWheel(event));
        const removeEdgeSwipe = pswp.installEdgeSwipe({
          onBackward: () => {
            if ((pswp == null ? void 0 : pswp.currentIndex) === 0 && deps.context.getPrevUrl() && !deps.context.isPageFetching()) {
              pagination.loadPrev();
            }
          },
          onForward: () => {
            if ((pswp == null ? void 0 : pswp.currentIndex) === spreadLayout.spreads.length - 1 && deps.context.getNextUrl() && !deps.context.isPageFetching()) {
              pagination.loadNext();
            }
          }
        });
        pswp.on("destroy", () => {
          removeWheel();
          removeEdgeSwipe();
          unmountShell();
        });
        triggerMobileUITimeout();
      });
      pswp.on("close", () => {
        close();
      });
      pswp.on("pointerUp", () => {
        refreshSpreadLayout(session.currentIndex, session.currentIndex);
      });
      pswp.init();
      refreshHudForCurrent();
      syncUiAvailabilityForCurrent();
      prefetch.setWindow(session.currentIndex, 1);
    }
    function close() {
      if (!isActive) return;
      isActive = false;
      autoPlay.stop();
      deps.context.setAutoPlayEnabled(false);
      prefetch.clear();
      if (overflowSnapshot) {
        document.documentElement.style.overflow = overflowSnapshot.documentElement;
        document.body.style.overflow = overflowSnapshot.body;
        overflowSnapshot = null;
      }
      if (deps.context.isScrollMode()) {
        const currentKey = itemKeyAt(session.currentIndex);
        const returnedToEntry = !!entryItemKey && currentKey === entryItemKey;
        if (session.hasNavigated && !returnedToEntry) {
          const item = session.itemAt(session.currentIndex);
          const key = (item == null ? void 0 : item.key) || currentKey;
          const firstGeometryIndex = Math.max(0, session.currentIndex - RETURN_GEOMETRY_RADIUS);
          const lastGeometryIndex = Math.min(
            session.imageCount - 1,
            session.currentIndex + RETURN_GEOMETRY_RADIUS
          );
          const nearbyGeometry = [];
          for (let index = firstGeometryIndex; index <= lastGeometryIndex; index++) {
            const nearbyItem = session.itemAt(index);
            const cachedAsset = nearbyItem && getCachedImage(nearbyItem.viewerUrl);
            if (!nearbyItem || !cachedAsset) continue;
            nearbyGeometry.push({
              key: nearbyItem.key,
              index,
              preferred: session.elementAt(index),
              width: cachedAsset.width,
              height: cachedAsset.height
            });
          }
          deps.scroll.restore({
            entryScrollY,
            nearbyGeometry,
            target: {
              key,
              index: session.currentIndex,
              preferred: session.elementAt(session.currentIndex)
            }
          });
        } else {
          deps.scroll.restore({ entryScrollY });
        }
        document.documentElement.classList.remove("hr-reader-open");
        deps.scroll.resume();
      }
      if (pswp) {
        const p = pswp;
        pswp = null;
        try {
          p.destroy();
        } catch (err) {
          console.error("[Hentai-Reader] Error destroying PhotoSwipe:", err);
        }
      }
      deps.context.emitReaderModeChanged();
      deps.context.onReaderClose(deps.context.getImageOffset() + session.currentIndex);
    }
    deps.context.subscribeSettingsChanged(() => {
      if (!isActive) return;
      refreshActiveLayout();
      if (deps.context.isAutoPlayEnabled()) {
        autoPlay.start();
      } else {
        autoPlay.stop();
      }
    });
    deps.context.subscribeReaderModeChanged(() => {
      if (!isActive || !pswp) return;
      const el = document.querySelector(".pswp__counter");
      if (el) {
        el.innerHTML = formatSpreadCounter(currentSpread(), deps.context.getImageOffset(), session.imageCount);
      }
    });
    function warmupInitial(count) {
      if (isActive) return;
      session.syncImages(Array.from(document.querySelectorAll(".r-img, .r-ph")));
      if (session.imageCount === 0) return;
      const indices = [];
      for (let i = 0; i < Math.min(count, session.imageCount); i++) {
        indices.push(i);
      }
      prefetch.warmup(indices);
    }
    return {
      open,
      close,
      isActive: () => isActive,
      warmupInitial
    };
  }
function createElement(className, tagName, appendToEl) {
    const el = document.createElement(tagName);
    if (className) {
      el.className = className;
    }
    if (appendToEl) {
      appendToEl.appendChild(el);
    }
    return el;
  }
  function equalizePoints(p1, p2) {
    p1.x = p2.x;
    p1.y = p2.y;
    if (p2.id !== void 0) {
      p1.id = p2.id;
    }
    return p1;
  }
  function roundPoint(p) {
    p.x = Math.round(p.x);
    p.y = Math.round(p.y);
  }
  function getDistanceBetween(p1, p2) {
    const x = Math.abs(p1.x - p2.x);
    const y = Math.abs(p1.y - p2.y);
    return Math.sqrt(x * x + y * y);
  }
  function pointsEqual(p1, p2) {
    return p1.x === p2.x && p1.y === p2.y;
  }
  function clamp(val, min, max) {
    return Math.min(Math.max(val, min), max);
  }
  function toTransformString(x, y, scale) {
    let propValue = `translate3d(${x}px,${y || 0}px,0)`;
    if (scale !== void 0) {
      propValue += ` scale3d(${scale},${scale},1)`;
    }
    return propValue;
  }
  function setTransform(el, x, y, scale) {
    el.style.transform = toTransformString(x, y, scale);
  }
  const defaultCSSEasing = "cubic-bezier(.4,0,.22,1)";
  function setTransitionStyle(el, prop, duration, ease) {
    el.style.transition = prop ? `${prop} ${duration}ms ${ease || defaultCSSEasing}` : "none";
  }
  function setWidthHeight(el, w, h) {
    el.style.width = typeof w === "number" ? `${w}px` : w;
    el.style.height = typeof h === "number" ? `${h}px` : h;
  }
  function removeTransitionStyle(el) {
    setTransitionStyle(el);
  }
  function decodeImage(img) {
    if ("decode" in img) {
      return img.decode().catch(() => {
      });
    }
    if (img.complete) {
      return Promise.resolve(img);
    }
    return new Promise((resolve, reject) => {
      img.onload = () => resolve(img);
      img.onerror = reject;
    });
  }
  const LOAD_STATE = {
    IDLE: "idle",
    LOADING: "loading",
    LOADED: "loaded",
    ERROR: "error"
  };
  function specialKeyUsed(e) {
    return "button" in e && e.button === 1 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey;
  }
  function getElementsFromOption(option, legacySelector, parent = document) {
    let elements = [];
    if (option instanceof Element) {
      elements = [option];
    } else if (option instanceof NodeList || Array.isArray(option)) {
      elements = Array.from(option);
    } else {
      const selector = typeof option === "string" ? option : legacySelector;
      if (selector) {
        elements = Array.from(parent.querySelectorAll(selector));
      }
    }
    return elements;
  }
  function isSafari() {
    return !!(navigator.vendor && navigator.vendor.match(/apple/i));
  }
  let supportsPassive = false;
  try {
    window.addEventListener("test", null, Object.defineProperty({}, "passive", {
      get: () => {
        supportsPassive = true;
      }
    }));
  } catch (e) {
  }
  class DOMEvents {
    constructor() {
      this._pool = [];
    }
add(target, type, listener, passive) {
      this._toggleListener(target, type, listener, passive);
    }
remove(target, type, listener, passive) {
      this._toggleListener(target, type, listener, passive, true);
    }
removeAll() {
      this._pool.forEach((poolItem) => {
        this._toggleListener(poolItem.target, poolItem.type, poolItem.listener, poolItem.passive, true, true);
      });
      this._pool = [];
    }
_toggleListener(target, type, listener, passive, unbind, skipPool) {
      if (!target) {
        return;
      }
      const methodName = unbind ? "removeEventListener" : "addEventListener";
      const types = type.split(" ");
      types.forEach((eType) => {
        if (eType) {
          if (!skipPool) {
            if (unbind) {
              this._pool = this._pool.filter((poolItem) => {
                return poolItem.type !== eType || poolItem.listener !== listener || poolItem.target !== target;
              });
            } else {
              this._pool.push({
                target,
                type: eType,
                listener,
                passive
              });
            }
          }
          const eventOptions = supportsPassive ? {
            passive: passive || false
          } : false;
          target[methodName](eType, listener, eventOptions);
        }
      });
    }
  }
  function getViewportSize(options, pswp) {
    if (options.getViewportSizeFn) {
      const newViewportSize = options.getViewportSizeFn(options, pswp);
      if (newViewportSize) {
        return newViewportSize;
      }
    }
    return {
      x: document.documentElement.clientWidth,



y: window.innerHeight
    };
  }
  function parsePaddingOption(prop, options, viewportSize, itemData, index) {
    let paddingValue = 0;
    if (options.paddingFn) {
      paddingValue = options.paddingFn(viewportSize, itemData, index)[prop];
    } else if (options.padding) {
      paddingValue = options.padding[prop];
    } else {
      const legacyPropName = "padding" + prop[0].toUpperCase() + prop.slice(1);
      if (options[legacyPropName]) {
        paddingValue = options[legacyPropName];
      }
    }
    return Number(paddingValue) || 0;
  }
  function getPanAreaSize(options, viewportSize, itemData, index) {
    return {
      x: viewportSize.x - parsePaddingOption("left", options, viewportSize, itemData, index) - parsePaddingOption("right", options, viewportSize, itemData, index),
      y: viewportSize.y - parsePaddingOption("top", options, viewportSize, itemData, index) - parsePaddingOption("bottom", options, viewportSize, itemData, index)
    };
  }
  class PanBounds {
constructor(slide) {
      this.slide = slide;
      this.currZoomLevel = 1;
      this.center =
{
        x: 0,
        y: 0
      };
      this.max =
{
        x: 0,
        y: 0
      };
      this.min =
{
        x: 0,
        y: 0
      };
    }
update(currZoomLevel) {
      this.currZoomLevel = currZoomLevel;
      if (!this.slide.width) {
        this.reset();
      } else {
        this._updateAxis("x");
        this._updateAxis("y");
        this.slide.pswp.dispatch("calcBounds", {
          slide: this.slide
        });
      }
    }
_updateAxis(axis) {
      const {
        pswp
      } = this.slide;
      const elSize = this.slide[axis === "x" ? "width" : "height"] * this.currZoomLevel;
      const paddingProp = axis === "x" ? "left" : "top";
      const padding = parsePaddingOption(paddingProp, pswp.options, pswp.viewportSize, this.slide.data, this.slide.index);
      const panAreaSize = this.slide.panAreaSize[axis];
      this.center[axis] = Math.round((panAreaSize - elSize) / 2) + padding;
      this.max[axis] = elSize > panAreaSize ? Math.round(panAreaSize - elSize) + padding : this.center[axis];
      this.min[axis] = elSize > panAreaSize ? padding : this.center[axis];
    }
reset() {
      this.center.x = 0;
      this.center.y = 0;
      this.max.x = 0;
      this.max.y = 0;
      this.min.x = 0;
      this.min.y = 0;
    }
correctPan(axis, panOffset) {
      return clamp(panOffset, this.max[axis], this.min[axis]);
    }
  }
  const MAX_IMAGE_WIDTH = 4e3;
  class ZoomLevel {
constructor(options, itemData, index, pswp) {
      this.pswp = pswp;
      this.options = options;
      this.itemData = itemData;
      this.index = index;
      this.panAreaSize = null;
      this.elementSize = null;
      this.fit = 1;
      this.fill = 1;
      this.vFill = 1;
      this.initial = 1;
      this.secondary = 1;
      this.max = 1;
      this.min = 1;
    }
update(maxWidth, maxHeight, panAreaSize) {
      const elementSize = {
        x: maxWidth,
        y: maxHeight
      };
      this.elementSize = elementSize;
      this.panAreaSize = panAreaSize;
      const hRatio = panAreaSize.x / elementSize.x;
      const vRatio = panAreaSize.y / elementSize.y;
      this.fit = Math.min(1, hRatio < vRatio ? hRatio : vRatio);
      this.fill = Math.min(1, hRatio > vRatio ? hRatio : vRatio);
      this.vFill = Math.min(1, vRatio);
      this.initial = this._getInitial();
      this.secondary = this._getSecondary();
      this.max = Math.max(this.initial, this.secondary, this._getMax());
      this.min = Math.min(this.fit, this.initial, this.secondary);
      if (this.pswp) {
        this.pswp.dispatch("zoomLevelsUpdate", {
          zoomLevels: this,
          slideData: this.itemData
        });
      }
    }
_parseZoomLevelOption(optionPrefix) {
      const optionName = (
optionPrefix + "ZoomLevel"
      );
      const optionValue = this.options[optionName];
      if (!optionValue) {
        return;
      }
      if (typeof optionValue === "function") {
        return optionValue(this);
      }
      if (optionValue === "fill") {
        return this.fill;
      }
      if (optionValue === "fit") {
        return this.fit;
      }
      return Number(optionValue);
    }
_getSecondary() {
      let currZoomLevel = this._parseZoomLevelOption("secondary");
      if (currZoomLevel) {
        return currZoomLevel;
      }
      currZoomLevel = Math.min(1, this.fit * 3);
      if (this.elementSize && currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
        currZoomLevel = MAX_IMAGE_WIDTH / this.elementSize.x;
      }
      return currZoomLevel;
    }
_getInitial() {
      return this._parseZoomLevelOption("initial") || this.fit;
    }
_getMax() {
      return this._parseZoomLevelOption("max") || Math.max(1, this.fit * 4);
    }
  }
  class Slide {
constructor(data, index, pswp) {
      this.data = data;
      this.index = index;
      this.pswp = pswp;
      this.isActive = index === pswp.currIndex;
      this.currentResolution = 0;
      this.panAreaSize = {
        x: 0,
        y: 0
      };
      this.pan = {
        x: 0,
        y: 0
      };
      this.isFirstSlide = this.isActive && !pswp.opener.isOpen;
      this.zoomLevels = new ZoomLevel(pswp.options, data, index, pswp);
      this.pswp.dispatch("gettingData", {
        slide: this,
        data: this.data,
        index
      });
      this.content = this.pswp.contentLoader.getContentBySlide(this);
      this.container = createElement("pswp__zoom-wrap", "div");
      this.holderElement = null;
      this.currZoomLevel = 1;
      this.width = this.content.width;
      this.height = this.content.height;
      this.heavyAppended = false;
      this.bounds = new PanBounds(this);
      this.prevDisplayedWidth = -1;
      this.prevDisplayedHeight = -1;
      this.pswp.dispatch("slideInit", {
        slide: this
      });
    }
setIsActive(isActive) {
      if (isActive && !this.isActive) {
        this.activate();
      } else if (!isActive && this.isActive) {
        this.deactivate();
      }
    }
append(holderElement) {
      this.holderElement = holderElement;
      this.container.style.transformOrigin = "0 0";
      if (!this.data) {
        return;
      }
      this.calculateSize();
      this.load();
      this.updateContentSize();
      this.appendHeavy();
      this.holderElement.appendChild(this.container);
      this.zoomAndPanToInitial();
      this.pswp.dispatch("firstZoomPan", {
        slide: this
      });
      this.applyCurrentZoomPan();
      this.pswp.dispatch("afterSetContent", {
        slide: this
      });
      if (this.isActive) {
        this.activate();
      }
    }
    load() {
      this.content.load(false);
      this.pswp.dispatch("slideLoad", {
        slide: this
      });
    }
appendHeavy() {
      const {
        pswp
      } = this;
      if (this.heavyAppended || !pswp.opener.isOpen || pswp.mainScroll.isShifted() || !this.isActive && false) {
        return;
      }
      if (this.pswp.dispatch("appendHeavy", {
        slide: this
      }).defaultPrevented) {
        return;
      }
      this.heavyAppended = true;
      this.content.append();
      this.pswp.dispatch("appendHeavyContent", {
        slide: this
      });
    }
activate() {
      this.isActive = true;
      this.appendHeavy();
      this.content.activate();
      this.pswp.dispatch("slideActivate", {
        slide: this
      });
    }
deactivate() {
      this.isActive = false;
      this.content.deactivate();
      if (this.currZoomLevel !== this.zoomLevels.initial) {
        this.calculateSize();
      }
      this.currentResolution = 0;
      this.zoomAndPanToInitial();
      this.applyCurrentZoomPan();
      this.updateContentSize();
      this.pswp.dispatch("slideDeactivate", {
        slide: this
      });
    }
destroy() {
      this.content.hasSlide = false;
      this.content.remove();
      this.container.remove();
      this.pswp.dispatch("slideDestroy", {
        slide: this
      });
    }
    resize() {
      if (this.currZoomLevel === this.zoomLevels.initial || !this.isActive) {
        this.calculateSize();
        this.currentResolution = 0;
        this.zoomAndPanToInitial();
        this.applyCurrentZoomPan();
        this.updateContentSize();
      } else {
        this.calculateSize();
        this.bounds.update(this.currZoomLevel);
        this.panTo(this.pan.x, this.pan.y);
      }
    }
updateContentSize(force) {
      const scaleMultiplier = this.currentResolution || this.zoomLevels.initial;
      if (!scaleMultiplier) {
        return;
      }
      const width = Math.round(this.width * scaleMultiplier) || this.pswp.viewportSize.x;
      const height = Math.round(this.height * scaleMultiplier) || this.pswp.viewportSize.y;
      if (!this.sizeChanged(width, height) && !force) {
        return;
      }
      this.content.setDisplayedSize(width, height);
    }
sizeChanged(width, height) {
      if (width !== this.prevDisplayedWidth || height !== this.prevDisplayedHeight) {
        this.prevDisplayedWidth = width;
        this.prevDisplayedHeight = height;
        return true;
      }
      return false;
    }
getPlaceholderElement() {
      var _this$content$placeho;
      return (_this$content$placeho = this.content.placeholder) === null || _this$content$placeho === void 0 ? void 0 : _this$content$placeho.element;
    }
zoomTo(destZoomLevel, centerPoint, transitionDuration, ignoreBounds) {
      const {
        pswp
      } = this;
      if (!this.isZoomable() || pswp.mainScroll.isShifted()) {
        return;
      }
      pswp.dispatch("beforeZoomTo", {
        destZoomLevel,
        centerPoint,
        transitionDuration
      });
      pswp.animations.stopAllPan();
      const prevZoomLevel = this.currZoomLevel;
      if (!ignoreBounds) {
        destZoomLevel = clamp(destZoomLevel, this.zoomLevels.min, this.zoomLevels.max);
      }
      this.setZoomLevel(destZoomLevel);
      this.pan.x = this.calculateZoomToPanOffset("x", centerPoint, prevZoomLevel);
      this.pan.y = this.calculateZoomToPanOffset("y", centerPoint, prevZoomLevel);
      roundPoint(this.pan);
      const finishTransition = () => {
        this._setResolution(destZoomLevel);
        this.applyCurrentZoomPan();
      };
      if (!transitionDuration) {
        finishTransition();
      } else {
        pswp.animations.startTransition({
          isPan: true,
          name: "zoomTo",
          target: this.container,
          transform: this.getCurrentTransform(),
          onComplete: finishTransition,
          duration: transitionDuration,
          easing: pswp.options.easing
        });
      }
    }
toggleZoom(centerPoint) {
      this.zoomTo(this.currZoomLevel === this.zoomLevels.initial ? this.zoomLevels.secondary : this.zoomLevels.initial, centerPoint, this.pswp.options.zoomAnimationDuration);
    }
setZoomLevel(currZoomLevel) {
      this.currZoomLevel = currZoomLevel;
      this.bounds.update(this.currZoomLevel);
    }
calculateZoomToPanOffset(axis, point, prevZoomLevel) {
      const totalPanDistance = this.bounds.max[axis] - this.bounds.min[axis];
      if (totalPanDistance === 0) {
        return this.bounds.center[axis];
      }
      if (!point) {
        point = this.pswp.getViewportCenterPoint();
      }
      if (!prevZoomLevel) {
        prevZoomLevel = this.zoomLevels.initial;
      }
      const zoomFactor = this.currZoomLevel / prevZoomLevel;
      return this.bounds.correctPan(axis, (this.pan[axis] - point[axis]) * zoomFactor + point[axis]);
    }
panTo(panX, panY) {
      this.pan.x = this.bounds.correctPan("x", panX);
      this.pan.y = this.bounds.correctPan("y", panY);
      this.applyCurrentZoomPan();
    }
isPannable() {
      return Boolean(this.width) && this.currZoomLevel > this.zoomLevels.fit;
    }
isZoomable() {
      return Boolean(this.width) && this.content.isZoomable();
    }
applyCurrentZoomPan() {
      this._applyZoomTransform(this.pan.x, this.pan.y, this.currZoomLevel);
      if (this === this.pswp.currSlide) {
        this.pswp.dispatch("zoomPanUpdate", {
          slide: this
        });
      }
    }
    zoomAndPanToInitial() {
      this.currZoomLevel = this.zoomLevels.initial;
      this.bounds.update(this.currZoomLevel);
      equalizePoints(this.pan, this.bounds.center);
      this.pswp.dispatch("initialZoomPan", {
        slide: this
      });
    }
_applyZoomTransform(x, y, zoom) {
      zoom /= this.currentResolution || this.zoomLevels.initial;
      setTransform(this.container, x, y, zoom);
    }
    calculateSize() {
      const {
        pswp
      } = this;
      equalizePoints(this.panAreaSize, getPanAreaSize(pswp.options, pswp.viewportSize, this.data, this.index));
      this.zoomLevels.update(this.width, this.height, this.panAreaSize);
      pswp.dispatch("calcSlideSize", {
        slide: this
      });
    }
getCurrentTransform() {
      const scale = this.currZoomLevel / (this.currentResolution || this.zoomLevels.initial);
      return toTransformString(this.pan.x, this.pan.y, scale);
    }
_setResolution(newResolution) {
      if (newResolution === this.currentResolution) {
        return;
      }
      this.currentResolution = newResolution;
      this.updateContentSize();
      this.pswp.dispatch("resolutionChanged");
    }
  }
  const PAN_END_FRICTION = 0.35;
  const VERTICAL_DRAG_FRICTION = 0.6;
  const MIN_RATIO_TO_CLOSE = 0.4;
  const MIN_NEXT_SLIDE_SPEED = 0.5;
  function project(initialVelocity, decelerationRate) {
    return initialVelocity * decelerationRate / (1 - decelerationRate);
  }
  class DragHandler {
constructor(gestures) {
      this.gestures = gestures;
      this.pswp = gestures.pswp;
      this.startPan = {
        x: 0,
        y: 0
      };
    }
    start() {
      if (this.pswp.currSlide) {
        equalizePoints(this.startPan, this.pswp.currSlide.pan);
      }
      this.pswp.animations.stopAll();
    }
    change() {
      const {
        p1,
        prevP1,
        dragAxis
      } = this.gestures;
      const {
        currSlide
      } = this.pswp;
      if (dragAxis === "y" && this.pswp.options.closeOnVerticalDrag && currSlide && currSlide.currZoomLevel <= currSlide.zoomLevels.fit && !this.gestures.isMultitouch) {
        const panY = currSlide.pan.y + (p1.y - prevP1.y);
        if (!this.pswp.dispatch("verticalDrag", {
          panY
        }).defaultPrevented) {
          this._setPanWithFriction("y", panY, VERTICAL_DRAG_FRICTION);
          const bgOpacity = 1 - Math.abs(this._getVerticalDragRatio(currSlide.pan.y));
          this.pswp.applyBgOpacity(bgOpacity);
          currSlide.applyCurrentZoomPan();
        }
      } else {
        const mainScrollChanged = this._panOrMoveMainScroll("x");
        if (!mainScrollChanged) {
          this._panOrMoveMainScroll("y");
          if (currSlide) {
            roundPoint(currSlide.pan);
            currSlide.applyCurrentZoomPan();
          }
        }
      }
    }
    end() {
      const {
        velocity
      } = this.gestures;
      const {
        mainScroll,
        currSlide
      } = this.pswp;
      let indexDiff = 0;
      this.pswp.animations.stopAll();
      if (mainScroll.isShifted()) {
        const mainScrollShiftDiff = mainScroll.x - mainScroll.getCurrSlideX();
        const currentSlideVisibilityRatio = mainScrollShiftDiff / this.pswp.viewportSize.x;
        if (velocity.x < -MIN_NEXT_SLIDE_SPEED && currentSlideVisibilityRatio < 0 || velocity.x < 0.1 && currentSlideVisibilityRatio < -0.5) {
          indexDiff = 1;
          velocity.x = Math.min(velocity.x, 0);
        } else if (velocity.x > MIN_NEXT_SLIDE_SPEED && currentSlideVisibilityRatio > 0 || velocity.x > -0.1 && currentSlideVisibilityRatio > 0.5) {
          indexDiff = -1;
          velocity.x = Math.max(velocity.x, 0);
        }
        mainScroll.moveIndexBy(indexDiff, true, velocity.x);
      }
      if (currSlide && currSlide.currZoomLevel > currSlide.zoomLevels.max || this.gestures.isMultitouch) {
        this.gestures.zoomLevels.correctZoomPan(true);
      } else {
        this._finishPanGestureForAxis("x");
        this._finishPanGestureForAxis("y");
      }
    }
_finishPanGestureForAxis(axis) {
      const {
        velocity
      } = this.gestures;
      const {
        currSlide
      } = this.pswp;
      if (!currSlide) {
        return;
      }
      const {
        pan,
        bounds
      } = currSlide;
      const panPos = pan[axis];
      const restoreBgOpacity = this.pswp.bgOpacity < 1 && axis === "y";
      const decelerationRate = 0.995;
      const projectedPosition = panPos + project(velocity[axis], decelerationRate);
      if (restoreBgOpacity) {
        const vDragRatio = this._getVerticalDragRatio(panPos);
        const projectedVDragRatio = this._getVerticalDragRatio(projectedPosition);
        if (vDragRatio < 0 && projectedVDragRatio < -MIN_RATIO_TO_CLOSE || vDragRatio > 0 && projectedVDragRatio > MIN_RATIO_TO_CLOSE) {
          this.pswp.close();
          return;
        }
      }
      const correctedPanPosition = bounds.correctPan(axis, projectedPosition);
      if (panPos === correctedPanPosition) {
        return;
      }
      const dampingRatio = correctedPanPosition === projectedPosition ? 1 : 0.82;
      const initialBgOpacity = this.pswp.bgOpacity;
      const totalPanDist = correctedPanPosition - panPos;
      this.pswp.animations.startSpring({
        name: "panGesture" + axis,
        isPan: true,
        start: panPos,
        end: correctedPanPosition,
        velocity: velocity[axis],
        dampingRatio,
        onUpdate: (pos) => {
          if (restoreBgOpacity && this.pswp.bgOpacity < 1) {
            const animationProgressRatio = 1 - (correctedPanPosition - pos) / totalPanDist;
            this.pswp.applyBgOpacity(clamp(initialBgOpacity + (1 - initialBgOpacity) * animationProgressRatio, 0, 1));
          }
          pan[axis] = Math.floor(pos);
          currSlide.applyCurrentZoomPan();
        }
      });
    }
_panOrMoveMainScroll(axis) {
      const {
        p1,
        dragAxis,
        prevP1,
        isMultitouch
      } = this.gestures;
      const {
        currSlide,
        mainScroll
      } = this.pswp;
      const delta = p1[axis] - prevP1[axis];
      const newMainScrollX = mainScroll.x + delta;
      if (!delta || !currSlide) {
        return false;
      }
      if (axis === "x" && !currSlide.isPannable() && !isMultitouch) {
        mainScroll.moveTo(newMainScrollX, true);
        return true;
      }
      const {
        bounds
      } = currSlide;
      const newPan = currSlide.pan[axis] + delta;
      if (this.pswp.options.allowPanToNext && dragAxis === "x" && axis === "x" && !isMultitouch) {
        const currSlideMainScrollX = mainScroll.getCurrSlideX();
        const mainScrollShiftDiff = mainScroll.x - currSlideMainScrollX;
        const isLeftToRight = delta > 0;
        const isRightToLeft = !isLeftToRight;
        if (newPan > bounds.min[axis] && isLeftToRight) {
          const wasAtMinPanPosition = bounds.min[axis] <= this.startPan[axis];
          if (wasAtMinPanPosition) {
            mainScroll.moveTo(newMainScrollX, true);
            return true;
          } else {
            this._setPanWithFriction(axis, newPan);
          }
        } else if (newPan < bounds.max[axis] && isRightToLeft) {
          const wasAtMaxPanPosition = this.startPan[axis] <= bounds.max[axis];
          if (wasAtMaxPanPosition) {
            mainScroll.moveTo(newMainScrollX, true);
            return true;
          } else {
            this._setPanWithFriction(axis, newPan);
          }
        } else {
          if (mainScrollShiftDiff !== 0) {
            if (mainScrollShiftDiff > 0) {
              mainScroll.moveTo(Math.max(newMainScrollX, currSlideMainScrollX), true);
              return true;
            } else if (mainScrollShiftDiff < 0) {
              mainScroll.moveTo(Math.min(newMainScrollX, currSlideMainScrollX), true);
              return true;
            }
          } else {
            this._setPanWithFriction(axis, newPan);
          }
        }
      } else {
        if (axis === "y") {
          if (!mainScroll.isShifted() && bounds.min.y !== bounds.max.y) {
            this._setPanWithFriction(axis, newPan);
          }
        } else {
          this._setPanWithFriction(axis, newPan);
        }
      }
      return false;
    }


_getVerticalDragRatio(panY) {
      var _this$pswp$currSlide$, _this$pswp$currSlide;
      return (panY - ((_this$pswp$currSlide$ = (_this$pswp$currSlide = this.pswp.currSlide) === null || _this$pswp$currSlide === void 0 ? void 0 : _this$pswp$currSlide.bounds.center.y) !== null && _this$pswp$currSlide$ !== void 0 ? _this$pswp$currSlide$ : 0)) / (this.pswp.viewportSize.y / 3);
    }
_setPanWithFriction(axis, potentialPan, customFriction) {
      const {
        currSlide
      } = this.pswp;
      if (!currSlide) {
        return;
      }
      const {
        pan,
        bounds
      } = currSlide;
      const correctedPan = bounds.correctPan(axis, potentialPan);
      if (correctedPan !== potentialPan || customFriction) {
        const delta = Math.round(potentialPan - pan[axis]);
        pan[axis] += delta * (customFriction || PAN_END_FRICTION);
      } else {
        pan[axis] = potentialPan;
      }
    }
  }
  const UPPER_ZOOM_FRICTION = 0.05;
  const LOWER_ZOOM_FRICTION = 0.15;
  function getZoomPointsCenter(p, p1, p2) {
    p.x = (p1.x + p2.x) / 2;
    p.y = (p1.y + p2.y) / 2;
    return p;
  }
  class ZoomHandler {
constructor(gestures) {
      this.gestures = gestures;
      this._startPan = {
        x: 0,
        y: 0
      };
      this._startZoomPoint = {
        x: 0,
        y: 0
      };
      this._zoomPoint = {
        x: 0,
        y: 0
      };
      this._wasOverFitZoomLevel = false;
      this._startZoomLevel = 1;
    }
    start() {
      const {
        currSlide
      } = this.gestures.pswp;
      if (currSlide) {
        this._startZoomLevel = currSlide.currZoomLevel;
        equalizePoints(this._startPan, currSlide.pan);
      }
      this.gestures.pswp.animations.stopAllPan();
      this._wasOverFitZoomLevel = false;
    }
    change() {
      const {
        p1,
        startP1,
        p2,
        startP2,
        pswp
      } = this.gestures;
      const {
        currSlide
      } = pswp;
      if (!currSlide) {
        return;
      }
      const minZoomLevel = currSlide.zoomLevels.min;
      const maxZoomLevel = currSlide.zoomLevels.max;
      if (!currSlide.isZoomable() || pswp.mainScroll.isShifted()) {
        return;
      }
      getZoomPointsCenter(this._startZoomPoint, startP1, startP2);
      getZoomPointsCenter(this._zoomPoint, p1, p2);
      let currZoomLevel = 1 / getDistanceBetween(startP1, startP2) * getDistanceBetween(p1, p2) * this._startZoomLevel;
      if (currZoomLevel > currSlide.zoomLevels.initial + currSlide.zoomLevels.initial / 15) {
        this._wasOverFitZoomLevel = true;
      }
      if (currZoomLevel < minZoomLevel) {
        if (pswp.options.pinchToClose && !this._wasOverFitZoomLevel && this._startZoomLevel <= currSlide.zoomLevels.initial) {
          const bgOpacity = 1 - (minZoomLevel - currZoomLevel) / (minZoomLevel / 1.2);
          if (!pswp.dispatch("pinchClose", {
            bgOpacity
          }).defaultPrevented) {
            pswp.applyBgOpacity(bgOpacity);
          }
        } else {
          currZoomLevel = minZoomLevel - (minZoomLevel - currZoomLevel) * LOWER_ZOOM_FRICTION;
        }
      } else if (currZoomLevel > maxZoomLevel) {
        currZoomLevel = maxZoomLevel + (currZoomLevel - maxZoomLevel) * UPPER_ZOOM_FRICTION;
      }
      currSlide.pan.x = this._calculatePanForZoomLevel("x", currZoomLevel);
      currSlide.pan.y = this._calculatePanForZoomLevel("y", currZoomLevel);
      currSlide.setZoomLevel(currZoomLevel);
      currSlide.applyCurrentZoomPan();
    }
    end() {
      const {
        pswp
      } = this.gestures;
      const {
        currSlide
      } = pswp;
      if ((!currSlide || currSlide.currZoomLevel < currSlide.zoomLevels.initial) && !this._wasOverFitZoomLevel && pswp.options.pinchToClose) {
        pswp.close();
      } else {
        this.correctZoomPan();
      }
    }
_calculatePanForZoomLevel(axis, currZoomLevel) {
      const zoomFactor = currZoomLevel / this._startZoomLevel;
      return this._zoomPoint[axis] - (this._startZoomPoint[axis] - this._startPan[axis]) * zoomFactor;
    }
correctZoomPan(ignoreGesture) {
      const {
        pswp
      } = this.gestures;
      const {
        currSlide
      } = pswp;
      if (!(currSlide !== null && currSlide !== void 0 && currSlide.isZoomable())) {
        return;
      }
      if (this._zoomPoint.x === 0) {
        ignoreGesture = true;
      }
      const prevZoomLevel = currSlide.currZoomLevel;
      let destinationZoomLevel;
      let currZoomLevelNeedsChange = true;
      if (prevZoomLevel < currSlide.zoomLevels.initial) {
        destinationZoomLevel = currSlide.zoomLevels.initial;
      } else if (prevZoomLevel > currSlide.zoomLevels.max) {
        destinationZoomLevel = currSlide.zoomLevels.max;
      } else {
        currZoomLevelNeedsChange = false;
        destinationZoomLevel = prevZoomLevel;
      }
      const initialBgOpacity = pswp.bgOpacity;
      const restoreBgOpacity = pswp.bgOpacity < 1;
      const initialPan = equalizePoints({
        x: 0,
        y: 0
      }, currSlide.pan);
      let destinationPan = equalizePoints({
        x: 0,
        y: 0
      }, initialPan);
      if (ignoreGesture) {
        this._zoomPoint.x = 0;
        this._zoomPoint.y = 0;
        this._startZoomPoint.x = 0;
        this._startZoomPoint.y = 0;
        this._startZoomLevel = prevZoomLevel;
        equalizePoints(this._startPan, initialPan);
      }
      if (currZoomLevelNeedsChange) {
        destinationPan = {
          x: this._calculatePanForZoomLevel("x", destinationZoomLevel),
          y: this._calculatePanForZoomLevel("y", destinationZoomLevel)
        };
      }
      currSlide.setZoomLevel(destinationZoomLevel);
      destinationPan = {
        x: currSlide.bounds.correctPan("x", destinationPan.x),
        y: currSlide.bounds.correctPan("y", destinationPan.y)
      };
      currSlide.setZoomLevel(prevZoomLevel);
      const panNeedsChange = !pointsEqual(destinationPan, initialPan);
      if (!panNeedsChange && !currZoomLevelNeedsChange && !restoreBgOpacity) {
        currSlide._setResolution(destinationZoomLevel);
        currSlide.applyCurrentZoomPan();
        return;
      }
      pswp.animations.stopAllPan();
      pswp.animations.startSpring({
        isPan: true,
        start: 0,
        end: 1e3,
        velocity: 0,
        dampingRatio: 1,
        naturalFrequency: 40,
        onUpdate: (now) => {
          now /= 1e3;
          if (panNeedsChange || currZoomLevelNeedsChange) {
            if (panNeedsChange) {
              currSlide.pan.x = initialPan.x + (destinationPan.x - initialPan.x) * now;
              currSlide.pan.y = initialPan.y + (destinationPan.y - initialPan.y) * now;
            }
            if (currZoomLevelNeedsChange) {
              const newZoomLevel = prevZoomLevel + (destinationZoomLevel - prevZoomLevel) * now;
              currSlide.setZoomLevel(newZoomLevel);
            }
            currSlide.applyCurrentZoomPan();
          }
          if (restoreBgOpacity && pswp.bgOpacity < 1) {
            pswp.applyBgOpacity(clamp(initialBgOpacity + (1 - initialBgOpacity) * now, 0, 1));
          }
        },
        onComplete: () => {
          currSlide._setResolution(destinationZoomLevel);
          currSlide.applyCurrentZoomPan();
        }
      });
    }
  }
  function didTapOnMainContent(event) {
    return !!
event.target.closest(".pswp__container");
  }
  class TapHandler {
constructor(gestures) {
      this.gestures = gestures;
    }
click(point, originalEvent) {
      const targetClassList = (
originalEvent.target.classList
      );
      const isImageClick = targetClassList.contains("pswp__img");
      const isBackgroundClick = targetClassList.contains("pswp__item") || targetClassList.contains("pswp__zoom-wrap");
      if (isImageClick) {
        this._doClickOrTapAction("imageClick", point, originalEvent);
      } else if (isBackgroundClick) {
        this._doClickOrTapAction("bgClick", point, originalEvent);
      }
    }
tap(point, originalEvent) {
      if (didTapOnMainContent(originalEvent)) {
        this._doClickOrTapAction("tap", point, originalEvent);
      }
    }
doubleTap(point, originalEvent) {
      if (didTapOnMainContent(originalEvent)) {
        this._doClickOrTapAction("doubleTap", point, originalEvent);
      }
    }
_doClickOrTapAction(actionName, point, originalEvent) {
      var _this$gestures$pswp$e;
      const {
        pswp
      } = this.gestures;
      const {
        currSlide
      } = pswp;
      const actionFullName = (
actionName + "Action"
      );
      const optionValue = pswp.options[actionFullName];
      if (pswp.dispatch(actionFullName, {
        point,
        originalEvent
      }).defaultPrevented) {
        return;
      }
      if (typeof optionValue === "function") {
        optionValue.call(pswp, point, originalEvent);
        return;
      }
      switch (optionValue) {
        case "close":
        case "next":
          pswp[optionValue]();
          break;
        case "zoom":
          currSlide === null || currSlide === void 0 || currSlide.toggleZoom(point);
          break;
        case "zoom-or-close":
          if (currSlide !== null && currSlide !== void 0 && currSlide.isZoomable() && currSlide.zoomLevels.secondary !== currSlide.zoomLevels.initial) {
            currSlide.toggleZoom(point);
          } else if (pswp.options.clickToCloseNonZoomable) {
            pswp.close();
          }
          break;
        case "toggle-controls":
          (_this$gestures$pswp$e = this.gestures.pswp.element) === null || _this$gestures$pswp$e === void 0 || _this$gestures$pswp$e.classList.toggle("pswp--ui-visible");
          break;
      }
    }
  }
  const AXIS_SWIPE_HYSTERISIS = 10;
  const DOUBLE_TAP_DELAY = 300;
  const MIN_TAP_DISTANCE = 25;
  class Gestures {
constructor(pswp) {
      this.pswp = pswp;
      this.dragAxis = null;
      this.p1 = {
        x: 0,
        y: 0
      };
      this.p2 = {
        x: 0,
        y: 0
      };
      this.prevP1 = {
        x: 0,
        y: 0
      };
      this.prevP2 = {
        x: 0,
        y: 0
      };
      this.startP1 = {
        x: 0,
        y: 0
      };
      this.startP2 = {
        x: 0,
        y: 0
      };
      this.velocity = {
        x: 0,
        y: 0
      };
      this._lastStartP1 = {
        x: 0,
        y: 0
      };
      this._intervalP1 = {
        x: 0,
        y: 0
      };
      this._numActivePoints = 0;
      this._ongoingPointers = [];
      this._touchEventEnabled = "ontouchstart" in window;
      this._pointerEventEnabled = !!window.PointerEvent;
      this.supportsTouch = this._touchEventEnabled || this._pointerEventEnabled && navigator.maxTouchPoints > 1;
      this._numActivePoints = 0;
      this._intervalTime = 0;
      this._velocityCalculated = false;
      this.isMultitouch = false;
      this.isDragging = false;
      this.isZooming = false;
      this.raf = null;
      this._tapTimer = null;
      if (!this.supportsTouch) {
        pswp.options.allowPanToNext = false;
      }
      this.drag = new DragHandler(this);
      this.zoomLevels = new ZoomHandler(this);
      this.tapHandler = new TapHandler(this);
      pswp.on("bindEvents", () => {
        pswp.events.add(
          pswp.scrollWrap,
          "click",
this._onClick.bind(this)
        );
        if (this._pointerEventEnabled) {
          this._bindEvents("pointer", "down", "up", "cancel");
        } else if (this._touchEventEnabled) {
          this._bindEvents("touch", "start", "end", "cancel");
          if (pswp.scrollWrap) {
            pswp.scrollWrap.ontouchmove = () => {
            };
            pswp.scrollWrap.ontouchend = () => {
            };
          }
        } else {
          this._bindEvents("mouse", "down", "up");
        }
      });
    }
_bindEvents(pref, down, up, cancel) {
      const {
        pswp
      } = this;
      const {
        events
      } = pswp;
      const cancelEvent = cancel ? pref + cancel : "";
      events.add(
        pswp.scrollWrap,
        pref + down,
this.onPointerDown.bind(this)
      );
      events.add(
        window,
        pref + "move",
this.onPointerMove.bind(this)
      );
      events.add(
        window,
        pref + up,
this.onPointerUp.bind(this)
      );
      if (cancelEvent) {
        events.add(
          pswp.scrollWrap,
          cancelEvent,
this.onPointerUp.bind(this)
        );
      }
    }
onPointerDown(e) {
      const isMousePointer = e.type === "mousedown" || e.pointerType === "mouse";
      if (isMousePointer && e.button > 0) {
        return;
      }
      const {
        pswp
      } = this;
      if (!pswp.opener.isOpen) {
        e.preventDefault();
        return;
      }
      if (pswp.dispatch("pointerDown", {
        originalEvent: e
      }).defaultPrevented) {
        return;
      }
      if (isMousePointer) {
        pswp.mouseDetected();
        this._preventPointerEventBehaviour(e, "down");
      }
      pswp.animations.stopAll();
      this._updatePoints(e, "down");
      if (this._numActivePoints === 1) {
        this.dragAxis = null;
        equalizePoints(this.startP1, this.p1);
      }
      if (this._numActivePoints > 1) {
        this._clearTapTimer();
        this.isMultitouch = true;
      } else {
        this.isMultitouch = false;
      }
    }
onPointerMove(e) {
      this._preventPointerEventBehaviour(e, "move");
      if (!this._numActivePoints) {
        return;
      }
      this._updatePoints(e, "move");
      if (this.pswp.dispatch("pointerMove", {
        originalEvent: e
      }).defaultPrevented) {
        return;
      }
      if (this._numActivePoints === 1 && !this.isDragging) {
        if (!this.dragAxis) {
          this._calculateDragDirection();
        }
        if (this.dragAxis && !this.isDragging) {
          if (this.isZooming) {
            this.isZooming = false;
            this.zoomLevels.end();
          }
          this.isDragging = true;
          this._clearTapTimer();
          this._updateStartPoints();
          this._intervalTime = Date.now();
          this._velocityCalculated = false;
          equalizePoints(this._intervalP1, this.p1);
          this.velocity.x = 0;
          this.velocity.y = 0;
          this.drag.start();
          this._rafStopLoop();
          this._rafRenderLoop();
        }
      } else if (this._numActivePoints > 1 && !this.isZooming) {
        this._finishDrag();
        this.isZooming = true;
        this._updateStartPoints();
        this.zoomLevels.start();
        this._rafStopLoop();
        this._rafRenderLoop();
      }
    }
_finishDrag() {
      if (this.isDragging) {
        this.isDragging = false;
        if (!this._velocityCalculated) {
          this._updateVelocity(true);
        }
        this.drag.end();
        this.dragAxis = null;
      }
    }
onPointerUp(e) {
      if (!this._numActivePoints) {
        return;
      }
      this._updatePoints(e, "up");
      if (this.pswp.dispatch("pointerUp", {
        originalEvent: e
      }).defaultPrevented) {
        return;
      }
      if (this._numActivePoints === 0) {
        this._rafStopLoop();
        if (this.isDragging) {
          this._finishDrag();
        } else if (!this.isZooming && !this.isMultitouch) {
          this._finishTap(e);
        }
      }
      if (this._numActivePoints < 2 && this.isZooming) {
        this.isZooming = false;
        this.zoomLevels.end();
        if (this._numActivePoints === 1) {
          this.dragAxis = null;
          this._updateStartPoints();
        }
      }
    }
_rafRenderLoop() {
      if (this.isDragging || this.isZooming) {
        this._updateVelocity();
        if (this.isDragging) {
          if (!pointsEqual(this.p1, this.prevP1)) {
            this.drag.change();
          }
        } else {
          if (!pointsEqual(this.p1, this.prevP1) || !pointsEqual(this.p2, this.prevP2)) {
            this.zoomLevels.change();
          }
        }
        this._updatePrevPoints();
        this.raf = requestAnimationFrame(this._rafRenderLoop.bind(this));
      }
    }
_updateVelocity(force) {
      const time = Date.now();
      const duration = time - this._intervalTime;
      if (duration < 50 && !force) {
        return;
      }
      this.velocity.x = this._getVelocity("x", duration);
      this.velocity.y = this._getVelocity("y", duration);
      this._intervalTime = time;
      equalizePoints(this._intervalP1, this.p1);
      this._velocityCalculated = true;
    }
_finishTap(e) {
      const {
        mainScroll
      } = this.pswp;
      if (mainScroll.isShifted()) {
        mainScroll.moveIndexBy(0, true);
        return;
      }
      if (e.type.indexOf("cancel") > 0) {
        return;
      }
      if (e.type === "mouseup" || e.pointerType === "mouse") {
        this.tapHandler.click(this.startP1, e);
        return;
      }
      const tapDelay = this.pswp.options.doubleTapAction ? DOUBLE_TAP_DELAY : 0;
      if (this._tapTimer) {
        this._clearTapTimer();
        if (getDistanceBetween(this._lastStartP1, this.startP1) < MIN_TAP_DISTANCE) {
          this.tapHandler.doubleTap(this.startP1, e);
        }
      } else {
        equalizePoints(this._lastStartP1, this.startP1);
        this._tapTimer = setTimeout(() => {
          this.tapHandler.tap(this.startP1, e);
          this._clearTapTimer();
        }, tapDelay);
      }
    }
_clearTapTimer() {
      if (this._tapTimer) {
        clearTimeout(this._tapTimer);
        this._tapTimer = null;
      }
    }
_getVelocity(axis, duration) {
      const displacement = this.p1[axis] - this._intervalP1[axis];
      if (Math.abs(displacement) > 1 && duration > 5) {
        return displacement / duration;
      }
      return 0;
    }
_rafStopLoop() {
      if (this.raf) {
        cancelAnimationFrame(this.raf);
        this.raf = null;
      }
    }
_preventPointerEventBehaviour(e, pointerType) {
      const preventPointerEvent = this.pswp.applyFilters("preventPointerEvent", true, e, pointerType);
      if (preventPointerEvent) {
        e.preventDefault();
      }
    }
_updatePoints(e, pointerType) {
      if (this._pointerEventEnabled) {
        const pointerEvent = (
e
        );
        const pointerIndex = this._ongoingPointers.findIndex((ongoingPointer) => {
          return ongoingPointer.id === pointerEvent.pointerId;
        });
        if (pointerType === "up" && pointerIndex > -1) {
          this._ongoingPointers.splice(pointerIndex, 1);
        } else if (pointerType === "down" && pointerIndex === -1) {
          this._ongoingPointers.push(this._convertEventPosToPoint(pointerEvent, {
            x: 0,
            y: 0
          }));
        } else if (pointerIndex > -1) {
          this._convertEventPosToPoint(pointerEvent, this._ongoingPointers[pointerIndex]);
        }
        this._numActivePoints = this._ongoingPointers.length;
        if (this._numActivePoints > 0) {
          equalizePoints(this.p1, this._ongoingPointers[0]);
        }
        if (this._numActivePoints > 1) {
          equalizePoints(this.p2, this._ongoingPointers[1]);
        }
      } else {
        const touchEvent = (
e
        );
        this._numActivePoints = 0;
        if (touchEvent.type.indexOf("touch") > -1) {
          if (touchEvent.touches && touchEvent.touches.length > 0) {
            this._convertEventPosToPoint(touchEvent.touches[0], this.p1);
            this._numActivePoints++;
            if (touchEvent.touches.length > 1) {
              this._convertEventPosToPoint(touchEvent.touches[1], this.p2);
              this._numActivePoints++;
            }
          }
        } else {
          this._convertEventPosToPoint(
e,
            this.p1
          );
          if (pointerType === "up") {
            this._numActivePoints = 0;
          } else {
            this._numActivePoints++;
          }
        }
      }
    }
_updatePrevPoints() {
      equalizePoints(this.prevP1, this.p1);
      equalizePoints(this.prevP2, this.p2);
    }
_updateStartPoints() {
      equalizePoints(this.startP1, this.p1);
      equalizePoints(this.startP2, this.p2);
      this._updatePrevPoints();
    }
_calculateDragDirection() {
      if (this.pswp.mainScroll.isShifted()) {
        this.dragAxis = "x";
      } else {
        const diff = Math.abs(this.p1.x - this.startP1.x) - Math.abs(this.p1.y - this.startP1.y);
        if (diff !== 0) {
          const axisToCheck = diff > 0 ? "x" : "y";
          if (Math.abs(this.p1[axisToCheck] - this.startP1[axisToCheck]) >= AXIS_SWIPE_HYSTERISIS) {
            this.dragAxis = axisToCheck;
          }
        }
      }
    }
_convertEventPosToPoint(e, p) {
      p.x = e.pageX - this.pswp.offset.x;
      p.y = e.pageY - this.pswp.offset.y;
      if ("pointerId" in e) {
        p.id = e.pointerId;
      } else if (e.identifier !== void 0) {
        p.id = e.identifier;
      }
      return p;
    }
_onClick(e) {
      if (this.pswp.mainScroll.isShifted()) {
        e.preventDefault();
        e.stopPropagation();
      }
    }
  }
  const MAIN_SCROLL_END_FRICTION = 0.35;
  class MainScroll {
constructor(pswp) {
      this.pswp = pswp;
      this.x = 0;
      this.slideWidth = 0;
      this._currPositionIndex = 0;
      this._prevPositionIndex = 0;
      this._containerShiftIndex = -1;
      this.itemHolders = [];
    }
resize(resizeSlides) {
      const {
        pswp
      } = this;
      const newSlideWidth = Math.round(pswp.viewportSize.x + pswp.viewportSize.x * pswp.options.spacing);
      const slideWidthChanged = newSlideWidth !== this.slideWidth;
      if (slideWidthChanged) {
        this.slideWidth = newSlideWidth;
        this.moveTo(this.getCurrSlideX());
      }
      this.itemHolders.forEach((itemHolder, index) => {
        if (slideWidthChanged) {
          setTransform(itemHolder.el, (index + this._containerShiftIndex) * this.slideWidth);
        }
        if (resizeSlides && itemHolder.slide) {
          itemHolder.slide.resize();
        }
      });
    }
resetPosition() {
      this._currPositionIndex = 0;
      this._prevPositionIndex = 0;
      this.slideWidth = 0;
      this._containerShiftIndex = -1;
    }
appendHolders() {
      this.itemHolders = [];
      for (let i = 0; i < 3; i++) {
        const el = createElement("pswp__item", "div", this.pswp.container);
        el.setAttribute("role", "group");
        el.setAttribute("aria-roledescription", "slide");
        el.setAttribute("aria-hidden", "true");
        el.style.display = i === 1 ? "block" : "none";
        this.itemHolders.push({
          el
});
      }
    }
canBeSwiped() {
      return this.pswp.getNumItems() > 1;
    }
moveIndexBy(diff, animate, velocityX) {
      const {
        pswp
      } = this;
      let newIndex = pswp.potentialIndex + diff;
      const numSlides = pswp.getNumItems();
      if (pswp.canLoop()) {
        newIndex = pswp.getLoopedIndex(newIndex);
        const distance = (diff + numSlides) % numSlides;
        if (distance <= numSlides / 2) {
          diff = distance;
        } else {
          diff = distance - numSlides;
        }
      } else {
        if (newIndex < 0) {
          newIndex = 0;
        } else if (newIndex >= numSlides) {
          newIndex = numSlides - 1;
        }
        diff = newIndex - pswp.potentialIndex;
      }
      pswp.potentialIndex = newIndex;
      this._currPositionIndex -= diff;
      pswp.animations.stopMainScroll();
      const destinationX = this.getCurrSlideX();
      if (!animate) {
        this.moveTo(destinationX);
        this.updateCurrItem();
      } else {
        pswp.animations.startSpring({
          isMainScroll: true,
          start: this.x,
          end: destinationX,
          velocity: velocityX || 0,
          naturalFrequency: 30,
          dampingRatio: 1,
onUpdate: (x) => {
            this.moveTo(x);
          },
          onComplete: () => {
            this.updateCurrItem();
            pswp.appendHeavy();
          }
        });
        let currDiff = pswp.potentialIndex - pswp.currIndex;
        if (pswp.canLoop()) {
          const currDistance = (currDiff + numSlides) % numSlides;
          if (currDistance <= numSlides / 2) {
            currDiff = currDistance;
          } else {
            currDiff = currDistance - numSlides;
          }
        }
        if (Math.abs(currDiff) > 1) {
          this.updateCurrItem();
        }
      }
      return Boolean(diff);
    }
getCurrSlideX() {
      return this.slideWidth * this._currPositionIndex;
    }
isShifted() {
      return this.x !== this.getCurrSlideX();
    }
updateCurrItem() {
      var _this$itemHolders$;
      const {
        pswp
      } = this;
      const positionDifference = this._prevPositionIndex - this._currPositionIndex;
      if (!positionDifference) {
        return;
      }
      this._prevPositionIndex = this._currPositionIndex;
      pswp.currIndex = pswp.potentialIndex;
      let diffAbs = Math.abs(positionDifference);
      let tempHolder;
      if (diffAbs >= 3) {
        this._containerShiftIndex += positionDifference + (positionDifference > 0 ? -3 : 3);
        diffAbs = 3;
        this.itemHolders.forEach((itemHolder) => {
          var _itemHolder$slide;
          (_itemHolder$slide = itemHolder.slide) === null || _itemHolder$slide === void 0 || _itemHolder$slide.destroy();
          itemHolder.slide = void 0;
        });
      }
      for (let i = 0; i < diffAbs; i++) {
        if (positionDifference > 0) {
          tempHolder = this.itemHolders.shift();
          if (tempHolder) {
            this.itemHolders[2] = tempHolder;
            this._containerShiftIndex++;
            setTransform(tempHolder.el, (this._containerShiftIndex + 2) * this.slideWidth);
            pswp.setContent(tempHolder, pswp.currIndex - diffAbs + i + 2);
          }
        } else {
          tempHolder = this.itemHolders.pop();
          if (tempHolder) {
            this.itemHolders.unshift(tempHolder);
            this._containerShiftIndex--;
            setTransform(tempHolder.el, this._containerShiftIndex * this.slideWidth);
            pswp.setContent(tempHolder, pswp.currIndex + diffAbs - i - 2);
          }
        }
      }
      if (Math.abs(this._containerShiftIndex) > 50 && !this.isShifted()) {
        this.resetPosition();
        this.resize();
      }
      pswp.animations.stopAllPan();
      this.itemHolders.forEach((itemHolder, i) => {
        if (itemHolder.slide) {
          itemHolder.slide.setIsActive(i === 1);
        }
      });
      pswp.currSlide = (_this$itemHolders$ = this.itemHolders[1]) === null || _this$itemHolders$ === void 0 ? void 0 : _this$itemHolders$.slide;
      pswp.contentLoader.updateLazy(positionDifference);
      if (pswp.currSlide) {
        pswp.currSlide.applyCurrentZoomPan();
      }
      pswp.dispatch("change");
    }
moveTo(x, dragging) {
      if (!this.pswp.canLoop() && dragging) {
        let newSlideIndexOffset = (this.slideWidth * this._currPositionIndex - x) / this.slideWidth;
        newSlideIndexOffset += this.pswp.currIndex;
        const delta = Math.round(x - this.x);
        if (newSlideIndexOffset < 0 && delta > 0 || newSlideIndexOffset >= this.pswp.getNumItems() - 1 && delta < 0) {
          x = this.x + delta * MAIN_SCROLL_END_FRICTION;
        }
      }
      this.x = x;
      if (this.pswp.container) {
        setTransform(this.pswp.container, x);
      }
      this.pswp.dispatch("moveMainScroll", {
        x,
        dragging: dragging !== null && dragging !== void 0 ? dragging : false
      });
    }
  }
  const KeyboardKeyCodesMap = {
    Escape: 27,
    z: 90,
    ArrowLeft: 37,
    ArrowUp: 38,
    ArrowRight: 39,
    ArrowDown: 40,
    Tab: 9
  };
  const getKeyboardEventKey = (key, isKeySupported) => {
    return isKeySupported ? key : KeyboardKeyCodesMap[key];
  };
  class Keyboard {
constructor(pswp) {
      this.pswp = pswp;
      this._wasFocused = false;
      pswp.on("bindEvents", () => {
        if (pswp.options.trapFocus) {
          if (!pswp.options.initialPointerPos) {
            this._focusRoot();
          }
          pswp.events.add(
            document,
            "focusin",
this._onFocusIn.bind(this)
          );
        }
        pswp.events.add(
          document,
          "keydown",
this._onKeyDown.bind(this)
        );
      });
      const lastActiveElement = (
document.activeElement
      );
      pswp.on("destroy", () => {
        if (pswp.options.returnFocus && lastActiveElement && this._wasFocused) {
          lastActiveElement.focus();
        }
      });
    }
_focusRoot() {
      if (!this._wasFocused && this.pswp.element) {
        this.pswp.element.focus();
        this._wasFocused = true;
      }
    }
_onKeyDown(e) {
      const {
        pswp
      } = this;
      if (pswp.dispatch("keydown", {
        originalEvent: e
      }).defaultPrevented) {
        return;
      }
      if (specialKeyUsed(e)) {
        return;
      }
      let keydownAction;
      let axis;
      let isForward = false;
      const isKeySupported = "key" in e;
      switch (isKeySupported ? e.key : e.keyCode) {
        case getKeyboardEventKey("Escape", isKeySupported):
          if (pswp.options.escKey) {
            keydownAction = "close";
          }
          break;
        case getKeyboardEventKey("z", isKeySupported):
          keydownAction = "toggleZoom";
          break;
        case getKeyboardEventKey("ArrowLeft", isKeySupported):
          axis = "x";
          break;
        case getKeyboardEventKey("ArrowUp", isKeySupported):
          axis = "y";
          break;
        case getKeyboardEventKey("ArrowRight", isKeySupported):
          axis = "x";
          isForward = true;
          break;
        case getKeyboardEventKey("ArrowDown", isKeySupported):
          isForward = true;
          axis = "y";
          break;
        case getKeyboardEventKey("Tab", isKeySupported):
          this._focusRoot();
          break;
      }
      if (axis) {
        e.preventDefault();
        const {
          currSlide
        } = pswp;
        if (pswp.options.arrowKeys && axis === "x" && pswp.getNumItems() > 1) {
          keydownAction = isForward ? "next" : "prev";
        } else if (currSlide && currSlide.currZoomLevel > currSlide.zoomLevels.fit) {
          currSlide.pan[axis] += isForward ? -80 : 80;
          currSlide.panTo(currSlide.pan.x, currSlide.pan.y);
        }
      }
      if (keydownAction) {
        e.preventDefault();
        pswp[keydownAction]();
      }
    }
_onFocusIn(e) {
      const {
        template
      } = this.pswp;
      if (template && document !== e.target && template !== e.target && !template.contains(
e.target
      )) {
        template.focus();
      }
    }
  }
  const DEFAULT_EASING = "cubic-bezier(.4,0,.22,1)";
  class CSSAnimation {
constructor(props) {
      var _props$prop;
      this.props = props;
      const {
        target,
        onComplete,
        transform,
        onFinish = () => {
        },
        duration = 333,
        easing = DEFAULT_EASING
      } = props;
      this.onFinish = onFinish;
      const prop = transform ? "transform" : "opacity";
      const propValue = (_props$prop = props[prop]) !== null && _props$prop !== void 0 ? _props$prop : "";
      this._target = target;
      this._onComplete = onComplete;
      this._finished = false;
      this._onTransitionEnd = this._onTransitionEnd.bind(this);
      this._helperTimeout = setTimeout(() => {
        setTransitionStyle(target, prop, duration, easing);
        this._helperTimeout = setTimeout(() => {
          target.addEventListener("transitionend", this._onTransitionEnd, false);
          target.addEventListener("transitioncancel", this._onTransitionEnd, false);
          this._helperTimeout = setTimeout(() => {
            this._finalizeAnimation();
          }, duration + 500);
          target.style[prop] = propValue;
        }, 30);
      }, 0);
    }
_onTransitionEnd(e) {
      if (e.target === this._target) {
        this._finalizeAnimation();
      }
    }
_finalizeAnimation() {
      if (!this._finished) {
        this._finished = true;
        this.onFinish();
        if (this._onComplete) {
          this._onComplete();
        }
      }
    }
destroy() {
      if (this._helperTimeout) {
        clearTimeout(this._helperTimeout);
      }
      removeTransitionStyle(this._target);
      this._target.removeEventListener("transitionend", this._onTransitionEnd, false);
      this._target.removeEventListener("transitioncancel", this._onTransitionEnd, false);
      if (!this._finished) {
        this._finalizeAnimation();
      }
    }
  }
  const DEFAULT_NATURAL_FREQUENCY = 12;
  const DEFAULT_DAMPING_RATIO = 0.75;
  class SpringEaser {
constructor(initialVelocity, dampingRatio, naturalFrequency) {
      this.velocity = initialVelocity * 1e3;
      this._dampingRatio = dampingRatio || DEFAULT_DAMPING_RATIO;
      this._naturalFrequency = naturalFrequency || DEFAULT_NATURAL_FREQUENCY;
      this._dampedFrequency = this._naturalFrequency;
      if (this._dampingRatio < 1) {
        this._dampedFrequency *= Math.sqrt(1 - this._dampingRatio * this._dampingRatio);
      }
    }
easeFrame(deltaPosition, deltaTime) {
      let displacement = 0;
      let coeff;
      deltaTime /= 1e3;
      const naturalDumpingPow = Math.E ** (-this._dampingRatio * this._naturalFrequency * deltaTime);
      if (this._dampingRatio === 1) {
        coeff = this.velocity + this._naturalFrequency * deltaPosition;
        displacement = (deltaPosition + coeff * deltaTime) * naturalDumpingPow;
        this.velocity = displacement * -this._naturalFrequency + coeff * naturalDumpingPow;
      } else if (this._dampingRatio < 1) {
        coeff = 1 / this._dampedFrequency * (this._dampingRatio * this._naturalFrequency * deltaPosition + this.velocity);
        const dumpedFCos = Math.cos(this._dampedFrequency * deltaTime);
        const dumpedFSin = Math.sin(this._dampedFrequency * deltaTime);
        displacement = naturalDumpingPow * (deltaPosition * dumpedFCos + coeff * dumpedFSin);
        this.velocity = displacement * -this._naturalFrequency * this._dampingRatio + naturalDumpingPow * (-this._dampedFrequency * deltaPosition * dumpedFSin + this._dampedFrequency * coeff * dumpedFCos);
      }
      return displacement;
    }
  }
  class SpringAnimation {
constructor(props) {
      this.props = props;
      this._raf = 0;
      const {
        start,
        end,
        velocity,
        onUpdate,
        onComplete,
        onFinish = () => {
        },
        dampingRatio,
        naturalFrequency
      } = props;
      this.onFinish = onFinish;
      const easer = new SpringEaser(velocity, dampingRatio, naturalFrequency);
      let prevTime = Date.now();
      let deltaPosition = start - end;
      const animationLoop = () => {
        if (this._raf) {
          deltaPosition = easer.easeFrame(deltaPosition, Date.now() - prevTime);
          if (Math.abs(deltaPosition) < 1 && Math.abs(easer.velocity) < 50) {
            onUpdate(end);
            if (onComplete) {
              onComplete();
            }
            this.onFinish();
          } else {
            prevTime = Date.now();
            onUpdate(deltaPosition + end);
            this._raf = requestAnimationFrame(animationLoop);
          }
        }
      };
      this._raf = requestAnimationFrame(animationLoop);
    }
destroy() {
      if (this._raf >= 0) {
        cancelAnimationFrame(this._raf);
      }
      this._raf = 0;
    }
  }
  class Animations {
    constructor() {
      this.activeAnimations = [];
    }
startSpring(props) {
      this._start(props, true);
    }
startTransition(props) {
      this._start(props);
    }
_start(props, isSpring) {
      const animation = isSpring ? new SpringAnimation(
props
      ) : new CSSAnimation(
props
      );
      this.activeAnimations.push(animation);
      animation.onFinish = () => this.stop(animation);
      return animation;
    }
stop(animation) {
      animation.destroy();
      const index = this.activeAnimations.indexOf(animation);
      if (index > -1) {
        this.activeAnimations.splice(index, 1);
      }
    }
    stopAll() {
      this.activeAnimations.forEach((animation) => {
        animation.destroy();
      });
      this.activeAnimations = [];
    }
stopAllPan() {
      this.activeAnimations = this.activeAnimations.filter((animation) => {
        if (animation.props.isPan) {
          animation.destroy();
          return false;
        }
        return true;
      });
    }
    stopMainScroll() {
      this.activeAnimations = this.activeAnimations.filter((animation) => {
        if (animation.props.isMainScroll) {
          animation.destroy();
          return false;
        }
        return true;
      });
    }






isPanRunning() {
      return this.activeAnimations.some((animation) => {
        return animation.props.isPan;
      });
    }
  }
  class ScrollWheel {
constructor(pswp) {
      this.pswp = pswp;
      pswp.events.add(
        pswp.element,
        "wheel",
this._onWheel.bind(this)
      );
    }
_onWheel(e) {
      e.preventDefault();
      const {
        currSlide
      } = this.pswp;
      let {
        deltaX,
        deltaY
      } = e;
      if (!currSlide) {
        return;
      }
      if (this.pswp.dispatch("wheel", {
        originalEvent: e
      }).defaultPrevented) {
        return;
      }
      if (e.ctrlKey || this.pswp.options.wheelToZoom) {
        if (currSlide.isZoomable()) {
          let zoomFactor = -deltaY;
          if (e.deltaMode === 1) {
            zoomFactor *= 0.05;
          } else {
            zoomFactor *= e.deltaMode ? 1 : 2e-3;
          }
          zoomFactor = 2 ** zoomFactor;
          const destZoomLevel = currSlide.currZoomLevel * zoomFactor;
          currSlide.zoomTo(destZoomLevel, {
            x: e.clientX,
            y: e.clientY
          });
        }
      } else {
        if (currSlide.isPannable()) {
          if (e.deltaMode === 1) {
            deltaX *= 18;
            deltaY *= 18;
          }
          currSlide.panTo(currSlide.pan.x - deltaX, currSlide.pan.y - deltaY);
        }
      }
    }
  }
  function addElementHTML(htmlData) {
    if (typeof htmlData === "string") {
      return htmlData;
    }
    if (!htmlData || !htmlData.isCustomSVG) {
      return "";
    }
    const svgData = htmlData;
    let out = '<svg aria-hidden="true" class="pswp__icn" viewBox="0 0 %d %d" width="%d" height="%d">';
    out = out.split("%d").join(
svgData.size || 32
    );
    if (svgData.outlineID) {
      out += '<use class="pswp__icn-shadow" xlink:href="#' + svgData.outlineID + '"/>';
    }
    out += svgData.inner;
    out += "</svg>";
    return out;
  }
  class UIElement {
constructor(pswp, data) {
      var _container;
      const name = data.name || data.className;
      let elementHTML = data.html;
      if (pswp.options[name] === false) {
        return;
      }
      if (typeof pswp.options[name + "SVG"] === "string") {
        elementHTML = pswp.options[name + "SVG"];
      }
      pswp.dispatch("uiElementCreate", {
        data
      });
      let className = "";
      if (data.isButton) {
        className += "pswp__button ";
        className += data.className || `pswp__button--${data.name}`;
      } else {
        className += data.className || `pswp__${data.name}`;
      }
      let tagName = data.isButton ? data.tagName || "button" : data.tagName || "div";
      tagName =
tagName.toLowerCase();
      const element = createElement(className, tagName);
      if (data.isButton) {
        if (tagName === "button") {
          element.type = "button";
        }
        let {
          title
        } = data;
        const {
          ariaLabel
        } = data;
        if (typeof pswp.options[name + "Title"] === "string") {
          title = pswp.options[name + "Title"];
        }
        if (title) {
          element.title = title;
        }
        const ariaText = ariaLabel || title;
        if (ariaText) {
          element.setAttribute("aria-label", ariaText);
        }
      }
      element.innerHTML = addElementHTML(elementHTML);
      if (data.onInit) {
        data.onInit(element, pswp);
      }
      if (data.onClick) {
        element.onclick = (e) => {
          if (typeof data.onClick === "string") {
            pswp[data.onClick]();
          } else if (typeof data.onClick === "function") {
            data.onClick(e, element, pswp);
          }
        };
      }
      const appendTo = data.appendTo || "bar";
      let container = pswp.element;
      if (appendTo === "bar") {
        if (!pswp.topBar) {
          pswp.topBar = createElement("pswp__top-bar pswp__hide-on-close", "div", pswp.scrollWrap);
        }
        container = pswp.topBar;
      } else {
        element.classList.add("pswp__hide-on-close");
        if (appendTo === "wrapper") {
          container = pswp.scrollWrap;
        }
      }
      (_container = container) === null || _container === void 0 || _container.appendChild(pswp.applyFilters("uiElement", element, data));
    }
  }
  function initArrowButton(element, pswp, isNextButton) {
    element.classList.add("pswp__button--arrow");
    element.setAttribute("aria-controls", "pswp__items");
    pswp.on("change", () => {
      if (!pswp.options.loop) {
        if (isNextButton) {
          element.disabled = !(pswp.currIndex < pswp.getNumItems() - 1);
        } else {
          element.disabled = !(pswp.currIndex > 0);
        }
      }
    });
  }
  const arrowPrev = {
    name: "arrowPrev",
    className: "pswp__button--arrow--prev",
    title: "Previous",
    order: 10,
    isButton: true,
    appendTo: "wrapper",
    html: {
      isCustomSVG: true,
      size: 60,
      inner: '<path d="M29 43l-3 3-16-16 16-16 3 3-13 13 13 13z" id="pswp__icn-arrow"/>',
      outlineID: "pswp__icn-arrow"
    },
    onClick: "prev",
    onInit: initArrowButton
  };
  const arrowNext = {
    name: "arrowNext",
    className: "pswp__button--arrow--next",
    title: "Next",
    order: 11,
    isButton: true,
    appendTo: "wrapper",
    html: {
      isCustomSVG: true,
      size: 60,
      inner: '<use xlink:href="#pswp__icn-arrow"/>',
      outlineID: "pswp__icn-arrow"
    },
    onClick: "next",
    onInit: (el, pswp) => {
      initArrowButton(el, pswp, true);
    }
  };
  const closeButton = {
    name: "close",
    title: "Close",
    order: 20,
    isButton: true,
    html: {
      isCustomSVG: true,
      inner: '<path d="M24 10l-2-2-6 6-6-6-2 2 6 6-6 6 2 2 6-6 6 6 2-2-6-6z" id="pswp__icn-close"/>',
      outlineID: "pswp__icn-close"
    },
    onClick: "close"
  };
  const zoomButton = {
    name: "zoom",
    title: "Zoom",
    order: 10,
    isButton: true,
    html: {
      isCustomSVG: true,
inner: '<path d="M17.426 19.926a6 6 0 1 1 1.5-1.5L23 22.5 21.5 24l-4.074-4.074z" id="pswp__icn-zoom"/><path fill="currentColor" class="pswp__zoom-icn-bar-h" d="M11 16v-2h6v2z"/><path fill="currentColor" class="pswp__zoom-icn-bar-v" d="M13 12h2v6h-2z"/>',
      outlineID: "pswp__icn-zoom"
    },
    onClick: "toggleZoom"
  };
  const loadingIndicator = {
    name: "preloader",
    appendTo: "bar",
    order: 7,
    html: {
      isCustomSVG: true,
inner: '<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2 16a5.2 5.2 0 1 1-5.2-5.2V8a8 8 0 1 0 8 8h-2.8Z" id="pswp__icn-loading"/>',
      outlineID: "pswp__icn-loading"
    },
    onInit: (indicatorElement, pswp) => {
      let isVisible;
      let delayTimeout = null;
      const toggleIndicatorClass = (className, add) => {
        indicatorElement.classList.toggle("pswp__preloader--" + className, add);
      };
      const setIndicatorVisibility = (visible) => {
        if (isVisible !== visible) {
          isVisible = visible;
          toggleIndicatorClass("active", visible);
        }
      };
      const updatePreloaderVisibility = () => {
        var _pswp$currSlide;
        if (!((_pswp$currSlide = pswp.currSlide) !== null && _pswp$currSlide !== void 0 && _pswp$currSlide.content.isLoading())) {
          setIndicatorVisibility(false);
          if (delayTimeout) {
            clearTimeout(delayTimeout);
            delayTimeout = null;
          }
          return;
        }
        if (!delayTimeout) {
          delayTimeout = setTimeout(() => {
            var _pswp$currSlide2;
            setIndicatorVisibility(Boolean((_pswp$currSlide2 = pswp.currSlide) === null || _pswp$currSlide2 === void 0 ? void 0 : _pswp$currSlide2.content.isLoading()));
            delayTimeout = null;
          }, pswp.options.preloaderDelay);
        }
      };
      pswp.on("change", updatePreloaderVisibility);
      pswp.on("loadComplete", (e) => {
        if (pswp.currSlide === e.slide) {
          updatePreloaderVisibility();
        }
      });
      if (pswp.ui) {
        pswp.ui.updatePreloaderVisibility = updatePreloaderVisibility;
      }
    }
  };
  const counterIndicator = {
    name: "counter",
    order: 5,
    onInit: (counterElement, pswp) => {
      pswp.on("change", () => {
        counterElement.innerText = pswp.currIndex + 1 + pswp.options.indexIndicatorSep + pswp.getNumItems();
      });
    }
  };
  function setZoomedIn(el, isZoomedIn) {
    el.classList.toggle("pswp--zoomed-in", isZoomedIn);
  }
  class UI {
constructor(pswp) {
      this.pswp = pswp;
      this.isRegistered = false;
      this.uiElementsData = [];
      this.items = [];
      this.updatePreloaderVisibility = () => {
      };
      this._lastUpdatedZoomLevel = void 0;
    }
    init() {
      const {
        pswp
      } = this;
      this.isRegistered = false;
      this.uiElementsData = [closeButton, arrowPrev, arrowNext, zoomButton, loadingIndicator, counterIndicator];
      pswp.dispatch("uiRegister");
      this.uiElementsData.sort((a, b) => {
        return (a.order || 0) - (b.order || 0);
      });
      this.items = [];
      this.isRegistered = true;
      this.uiElementsData.forEach((uiElementData) => {
        this.registerElement(uiElementData);
      });
      pswp.on("change", () => {
        var _pswp$element;
        (_pswp$element = pswp.element) === null || _pswp$element === void 0 || _pswp$element.classList.toggle("pswp--one-slide", pswp.getNumItems() === 1);
      });
      pswp.on("zoomPanUpdate", () => this._onZoomPanUpdate());
    }
registerElement(elementData) {
      if (this.isRegistered) {
        this.items.push(new UIElement(this.pswp, elementData));
      } else {
        this.uiElementsData.push(elementData);
      }
    }
_onZoomPanUpdate() {
      const {
        template,
        currSlide,
        options
      } = this.pswp;
      if (this.pswp.opener.isClosing || !template || !currSlide) {
        return;
      }
      let {
        currZoomLevel
      } = currSlide;
      if (!this.pswp.opener.isOpen) {
        currZoomLevel = currSlide.zoomLevels.initial;
      }
      if (currZoomLevel === this._lastUpdatedZoomLevel) {
        return;
      }
      this._lastUpdatedZoomLevel = currZoomLevel;
      const currZoomLevelDiff = currSlide.zoomLevels.initial - currSlide.zoomLevels.secondary;
      if (Math.abs(currZoomLevelDiff) < 0.01 || !currSlide.isZoomable()) {
        setZoomedIn(template, false);
        template.classList.remove("pswp--zoom-allowed");
        return;
      }
      template.classList.add("pswp--zoom-allowed");
      const potentialZoomLevel = currZoomLevel === currSlide.zoomLevels.initial ? currSlide.zoomLevels.secondary : currSlide.zoomLevels.initial;
      setZoomedIn(template, potentialZoomLevel <= currZoomLevel);
      if (options.imageClickAction === "zoom" || options.imageClickAction === "zoom-or-close") {
        template.classList.add("pswp--click-to-zoom");
      }
    }
  }
  function getBoundsByElement(el) {
    const thumbAreaRect = el.getBoundingClientRect();
    return {
      x: thumbAreaRect.left,
      y: thumbAreaRect.top,
      w: thumbAreaRect.width
    };
  }
  function getCroppedBoundsByElement(el, imageWidth, imageHeight) {
    const thumbAreaRect = el.getBoundingClientRect();
    const hRatio = thumbAreaRect.width / imageWidth;
    const vRatio = thumbAreaRect.height / imageHeight;
    const fillZoomLevel = hRatio > vRatio ? hRatio : vRatio;
    const offsetX = (thumbAreaRect.width - imageWidth * fillZoomLevel) / 2;
    const offsetY = (thumbAreaRect.height - imageHeight * fillZoomLevel) / 2;
    const bounds = {
      x: thumbAreaRect.left + offsetX,
      y: thumbAreaRect.top + offsetY,
      w: imageWidth * fillZoomLevel
    };
    bounds.innerRect = {
      w: thumbAreaRect.width,
      h: thumbAreaRect.height,
      x: offsetX,
      y: offsetY
    };
    return bounds;
  }
  function getThumbBounds(index, itemData, instance) {
    const event = instance.dispatch("thumbBounds", {
      index,
      itemData,
      instance
    });
    if (event.thumbBounds) {
      return event.thumbBounds;
    }
    const {
      element
    } = itemData;
    let thumbBounds;
    let thumbnail;
    if (element && instance.options.thumbSelector !== false) {
      const thumbSelector = instance.options.thumbSelector || "img";
      thumbnail = element.matches(thumbSelector) ? element : (
element.querySelector(thumbSelector)
      );
    }
    thumbnail = instance.applyFilters("thumbEl", thumbnail, itemData, index);
    if (thumbnail) {
      if (!itemData.thumbCropped) {
        thumbBounds = getBoundsByElement(thumbnail);
      } else {
        thumbBounds = getCroppedBoundsByElement(thumbnail, itemData.width || itemData.w || 0, itemData.height || itemData.h || 0);
      }
    }
    return instance.applyFilters("thumbBounds", thumbBounds, itemData, index);
  }
  class PhotoSwipeEvent {
constructor(type, details) {
      this.type = type;
      this.defaultPrevented = false;
      if (details) {
        Object.assign(this, details);
      }
    }
    preventDefault() {
      this.defaultPrevented = true;
    }
  }
  class Eventable {
    constructor() {
      this._listeners = {};
      this._filters = {};
      this.pswp = void 0;
      this.options = void 0;
    }
addFilter(name, fn, priority = 100) {
      var _this$_filters$name, _this$_filters$name2, _this$pswp;
      if (!this._filters[name]) {
        this._filters[name] = [];
      }
      (_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.push({
        fn,
        priority
      });
      (_this$_filters$name2 = this._filters[name]) === null || _this$_filters$name2 === void 0 || _this$_filters$name2.sort((f1, f2) => f1.priority - f2.priority);
      (_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.addFilter(name, fn, priority);
    }
removeFilter(name, fn) {
      if (this._filters[name]) {
        this._filters[name] = this._filters[name].filter((filter) => filter.fn !== fn);
      }
      if (this.pswp) {
        this.pswp.removeFilter(name, fn);
      }
    }
applyFilters(name, ...args) {
      var _this$_filters$name3;
      (_this$_filters$name3 = this._filters[name]) === null || _this$_filters$name3 === void 0 || _this$_filters$name3.forEach((filter) => {
        args[0] = filter.fn.apply(this, args);
      });
      return args[0];
    }
on(name, fn) {
      var _this$_listeners$name, _this$pswp2;
      if (!this._listeners[name]) {
        this._listeners[name] = [];
      }
      (_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.push(fn);
      (_this$pswp2 = this.pswp) === null || _this$pswp2 === void 0 || _this$pswp2.on(name, fn);
    }
off(name, fn) {
      var _this$pswp3;
      if (this._listeners[name]) {
        this._listeners[name] = this._listeners[name].filter((listener) => fn !== listener);
      }
      (_this$pswp3 = this.pswp) === null || _this$pswp3 === void 0 || _this$pswp3.off(name, fn);
    }
dispatch(name, details) {
      var _this$_listeners$name2;
      if (this.pswp) {
        return this.pswp.dispatch(name, details);
      }
      const event = (
new PhotoSwipeEvent(name, details)
      );
      (_this$_listeners$name2 = this._listeners[name]) === null || _this$_listeners$name2 === void 0 || _this$_listeners$name2.forEach((listener) => {
        listener.call(this, event);
      });
      return event;
    }
  }
  class Placeholder {
constructor(imageSrc, container) {
      this.element = createElement("pswp__img pswp__img--placeholder", imageSrc ? "img" : "div", container);
      if (imageSrc) {
        const imgEl = (
this.element
        );
        imgEl.decoding = "async";
        imgEl.alt = "";
        imgEl.src = imageSrc;
        imgEl.setAttribute("role", "presentation");
      }
      this.element.setAttribute("aria-hidden", "true");
    }
setDisplayedSize(width, height) {
      if (!this.element) {
        return;
      }
      if (this.element.tagName === "IMG") {
        setWidthHeight(this.element, 250, "auto");
        this.element.style.transformOrigin = "0 0";
        this.element.style.transform = toTransformString(0, 0, width / 250);
      } else {
        setWidthHeight(this.element, width, height);
      }
    }
    destroy() {
      var _this$element;
      if ((_this$element = this.element) !== null && _this$element !== void 0 && _this$element.parentNode) {
        this.element.remove();
      }
      this.element = null;
    }
  }
  class Content {
constructor(itemData, instance, index) {
      this.instance = instance;
      this.data = itemData;
      this.index = index;
      this.element = void 0;
      this.placeholder = void 0;
      this.slide = void 0;
      this.displayedImageWidth = 0;
      this.displayedImageHeight = 0;
      this.width = Number(this.data.w) || Number(this.data.width) || 0;
      this.height = Number(this.data.h) || Number(this.data.height) || 0;
      this.isAttached = false;
      this.hasSlide = false;
      this.isDecoding = false;
      this.state = LOAD_STATE.IDLE;
      if (this.data.type) {
        this.type = this.data.type;
      } else if (this.data.src) {
        this.type = "image";
      } else {
        this.type = "html";
      }
      this.instance.dispatch("contentInit", {
        content: this
      });
    }
    removePlaceholder() {
      if (this.placeholder && !this.keepPlaceholder()) {
        setTimeout(() => {
          if (this.placeholder) {
            this.placeholder.destroy();
            this.placeholder = void 0;
          }
        }, 1e3);
      }
    }
load(isLazy, reload) {
      if (this.slide && this.usePlaceholder()) {
        if (!this.placeholder) {
          const placeholderSrc = this.instance.applyFilters(
            "placeholderSrc",

this.data.msrc && this.slide.isFirstSlide ? this.data.msrc : false,
            this
          );
          this.placeholder = new Placeholder(placeholderSrc, this.slide.container);
        } else {
          const placeholderEl = this.placeholder.element;
          if (placeholderEl && !placeholderEl.parentElement) {
            this.slide.container.prepend(placeholderEl);
          }
        }
      }
      if (this.element && !reload) {
        return;
      }
      if (this.instance.dispatch("contentLoad", {
        content: this,
        isLazy
      }).defaultPrevented) {
        return;
      }
      if (this.isImageContent()) {
        this.element = createElement("pswp__img", "img");
        if (this.displayedImageWidth) {
          this.loadImage(isLazy);
        }
      } else {
        this.element = createElement("pswp__content", "div");
        this.element.innerHTML = this.data.html || "";
      }
      if (reload && this.slide) {
        this.slide.updateContentSize(true);
      }
    }
loadImage(isLazy) {
      var _this$data$src, _this$data$alt;
      if (!this.isImageContent() || !this.element || this.instance.dispatch("contentLoadImage", {
        content: this,
        isLazy
      }).defaultPrevented) {
        return;
      }
      const imageElement = (
this.element
      );
      this.updateSrcsetSizes();
      if (this.data.srcset) {
        imageElement.srcset = this.data.srcset;
      }
      imageElement.src = (_this$data$src = this.data.src) !== null && _this$data$src !== void 0 ? _this$data$src : "";
      imageElement.alt = (_this$data$alt = this.data.alt) !== null && _this$data$alt !== void 0 ? _this$data$alt : "";
      this.state = LOAD_STATE.LOADING;
      if (imageElement.complete) {
        this.onLoaded();
      } else {
        imageElement.onload = () => {
          this.onLoaded();
        };
        imageElement.onerror = () => {
          this.onError();
        };
      }
    }
setSlide(slide) {
      this.slide = slide;
      this.hasSlide = true;
      this.instance = slide.pswp;
    }
onLoaded() {
      this.state = LOAD_STATE.LOADED;
      if (this.slide && this.element) {
        this.instance.dispatch("loadComplete", {
          slide: this.slide,
          content: this
        });
        if (this.slide.isActive && this.slide.heavyAppended && !this.element.parentNode) {
          this.append();
          this.slide.updateContentSize(true);
        }
        if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
          this.removePlaceholder();
        }
      }
    }
onError() {
      this.state = LOAD_STATE.ERROR;
      if (this.slide) {
        this.displayError();
        this.instance.dispatch("loadComplete", {
          slide: this.slide,
          isError: true,
          content: this
        });
        this.instance.dispatch("loadError", {
          slide: this.slide,
          content: this
        });
      }
    }
isLoading() {
      return this.instance.applyFilters("isContentLoading", this.state === LOAD_STATE.LOADING, this);
    }
isError() {
      return this.state === LOAD_STATE.ERROR;
    }
isImageContent() {
      return this.type === "image";
    }
setDisplayedSize(width, height) {
      if (!this.element) {
        return;
      }
      if (this.placeholder) {
        this.placeholder.setDisplayedSize(width, height);
      }
      if (this.instance.dispatch("contentResize", {
        content: this,
        width,
        height
      }).defaultPrevented) {
        return;
      }
      setWidthHeight(this.element, width, height);
      if (this.isImageContent() && !this.isError()) {
        const isInitialSizeUpdate = !this.displayedImageWidth && width;
        this.displayedImageWidth = width;
        this.displayedImageHeight = height;
        if (isInitialSizeUpdate) {
          this.loadImage(false);
        } else {
          this.updateSrcsetSizes();
        }
        if (this.slide) {
          this.instance.dispatch("imageSizeChange", {
            slide: this.slide,
            width,
            height,
            content: this
          });
        }
      }
    }
isZoomable() {
      return this.instance.applyFilters("isContentZoomable", this.isImageContent() && this.state !== LOAD_STATE.ERROR, this);
    }
updateSrcsetSizes() {
      if (!this.isImageContent() || !this.element || !this.data.srcset) {
        return;
      }
      const image = (
this.element
      );
      const sizesWidth = this.instance.applyFilters("srcsetSizesWidth", this.displayedImageWidth, this);
      if (!image.dataset.largestUsedSize || sizesWidth > parseInt(image.dataset.largestUsedSize, 10)) {
        image.sizes = sizesWidth + "px";
        image.dataset.largestUsedSize = String(sizesWidth);
      }
    }
usePlaceholder() {
      return this.instance.applyFilters("useContentPlaceholder", this.isImageContent(), this);
    }
lazyLoad() {
      if (this.instance.dispatch("contentLazyLoad", {
        content: this
      }).defaultPrevented) {
        return;
      }
      this.load(true);
    }
keepPlaceholder() {
      return this.instance.applyFilters("isKeepingPlaceholder", this.isLoading(), this);
    }
destroy() {
      this.hasSlide = false;
      this.slide = void 0;
      if (this.instance.dispatch("contentDestroy", {
        content: this
      }).defaultPrevented) {
        return;
      }
      this.remove();
      if (this.placeholder) {
        this.placeholder.destroy();
        this.placeholder = void 0;
      }
      if (this.isImageContent() && this.element) {
        this.element.onload = null;
        this.element.onerror = null;
        this.element = void 0;
      }
    }
displayError() {
      if (this.slide) {
        var _this$instance$option, _this$instance$option2;
        let errorMsgEl = createElement("pswp__error-msg", "div");
        errorMsgEl.innerText = (_this$instance$option = (_this$instance$option2 = this.instance.options) === null || _this$instance$option2 === void 0 ? void 0 : _this$instance$option2.errorMsg) !== null && _this$instance$option !== void 0 ? _this$instance$option : "";
        errorMsgEl =
this.instance.applyFilters("contentErrorElement", errorMsgEl, this);
        this.element = createElement("pswp__content pswp__error-msg-container", "div");
        this.element.appendChild(errorMsgEl);
        this.slide.container.innerText = "";
        this.slide.container.appendChild(this.element);
        this.slide.updateContentSize(true);
        this.removePlaceholder();
      }
    }
append() {
      if (this.isAttached || !this.element) {
        return;
      }
      this.isAttached = true;
      if (this.state === LOAD_STATE.ERROR) {
        this.displayError();
        return;
      }
      if (this.instance.dispatch("contentAppend", {
        content: this
      }).defaultPrevented) {
        return;
      }
      const supportsDecode = "decode" in this.element;
      if (this.isImageContent()) {
        if (supportsDecode && this.slide && (!this.slide.isActive || isSafari())) {
          this.isDecoding = true;
          this.element.decode().catch(() => {
          }).finally(() => {
            this.isDecoding = false;
            this.appendImage();
          });
        } else {
          this.appendImage();
        }
      } else if (this.slide && !this.element.parentNode) {
        this.slide.container.appendChild(this.element);
      }
    }
activate() {
      if (this.instance.dispatch("contentActivate", {
        content: this
      }).defaultPrevented || !this.slide) {
        return;
      }
      if (this.isImageContent() && this.isDecoding && !isSafari()) {
        this.appendImage();
      } else if (this.isError()) {
        this.load(false, true);
      }
      if (this.slide.holderElement) {
        this.slide.holderElement.setAttribute("aria-hidden", "false");
      }
    }
deactivate() {
      this.instance.dispatch("contentDeactivate", {
        content: this
      });
      if (this.slide && this.slide.holderElement) {
        this.slide.holderElement.setAttribute("aria-hidden", "true");
      }
    }
remove() {
      this.isAttached = false;
      if (this.instance.dispatch("contentRemove", {
        content: this
      }).defaultPrevented) {
        return;
      }
      if (this.element && this.element.parentNode) {
        this.element.remove();
      }
      if (this.placeholder && this.placeholder.element) {
        this.placeholder.element.remove();
      }
    }
appendImage() {
      if (!this.isAttached) {
        return;
      }
      if (this.instance.dispatch("contentAppendImage", {
        content: this
      }).defaultPrevented) {
        return;
      }
      if (this.slide && this.element && !this.element.parentNode) {
        this.slide.container.appendChild(this.element);
      }
      if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
        this.removePlaceholder();
      }
    }
  }
  const MIN_SLIDES_TO_CACHE = 5;
  function lazyLoadData(itemData, instance, index) {
    const content = instance.createContentFromData(itemData, index);
    let zoomLevel;
    const {
      options
    } = instance;
    if (options) {
      zoomLevel = new ZoomLevel(options, itemData, -1);
      let viewportSize;
      if (instance.pswp) {
        viewportSize = instance.pswp.viewportSize;
      } else {
        viewportSize = getViewportSize(options, instance);
      }
      const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index);
      zoomLevel.update(content.width, content.height, panAreaSize);
    }
    content.lazyLoad();
    if (zoomLevel) {
      content.setDisplayedSize(Math.ceil(content.width * zoomLevel.initial), Math.ceil(content.height * zoomLevel.initial));
    }
    return content;
  }
  function lazyLoadSlide(index, instance) {
    const itemData = instance.getItemData(index);
    if (instance.dispatch("lazyLoadSlide", {
      index,
      itemData
    }).defaultPrevented) {
      return;
    }
    return lazyLoadData(itemData, instance, index);
  }
  class ContentLoader {
constructor(pswp) {
      this.pswp = pswp;
      this.limit = Math.max(pswp.options.preload[0] + pswp.options.preload[1] + 1, MIN_SLIDES_TO_CACHE);
      this._cachedItems = [];
    }
updateLazy(diff) {
      const {
        pswp
      } = this;
      if (pswp.dispatch("lazyLoad").defaultPrevented) {
        return;
      }
      const {
        preload
      } = pswp.options;
      const isForward = diff === void 0 ? true : diff >= 0;
      let i;
      for (i = 0; i <= preload[1]; i++) {
        this.loadSlideByIndex(pswp.currIndex + (isForward ? i : -i));
      }
      for (i = 1; i <= preload[0]; i++) {
        this.loadSlideByIndex(pswp.currIndex + (isForward ? -i : i));
      }
    }
loadSlideByIndex(initialIndex) {
      const index = this.pswp.getLoopedIndex(initialIndex);
      let content = this.getContentByIndex(index);
      if (!content) {
        content = lazyLoadSlide(index, this.pswp);
        if (content) {
          this.addToCache(content);
        }
      }
    }
getContentBySlide(slide) {
      let content = this.getContentByIndex(slide.index);
      if (!content) {
        content = this.pswp.createContentFromData(slide.data, slide.index);
        this.addToCache(content);
      }
      content.setSlide(slide);
      return content;
    }
addToCache(content) {
      this.removeByIndex(content.index);
      this._cachedItems.push(content);
      if (this._cachedItems.length > this.limit) {
        const indexToRemove = this._cachedItems.findIndex((item) => {
          return !item.isAttached && !item.hasSlide;
        });
        if (indexToRemove !== -1) {
          const removedItem = this._cachedItems.splice(indexToRemove, 1)[0];
          removedItem.destroy();
        }
      }
    }
removeByIndex(index) {
      const indexToRemove = this._cachedItems.findIndex((item) => item.index === index);
      if (indexToRemove !== -1) {
        this._cachedItems.splice(indexToRemove, 1);
      }
    }
getContentByIndex(index) {
      return this._cachedItems.find((content) => content.index === index);
    }
    destroy() {
      this._cachedItems.forEach((content) => content.destroy());
      this._cachedItems = [];
    }
  }
  class PhotoSwipeBase extends Eventable {
getNumItems() {
      var _this$options;
      let numItems = 0;
      const dataSource = (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.dataSource;
      if (dataSource && "length" in dataSource) {
        numItems = dataSource.length;
      } else if (dataSource && "gallery" in dataSource) {
        if (!dataSource.items) {
          dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
        }
        if (dataSource.items) {
          numItems = dataSource.items.length;
        }
      }
      const event = this.dispatch("numItems", {
        dataSource,
        numItems
      });
      return this.applyFilters("numItems", event.numItems, dataSource);
    }
createContentFromData(slideData, index) {
      return new Content(slideData, this, index);
    }
getItemData(index) {
      var _this$options2;
      const dataSource = (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.dataSource;
      let dataSourceItem = {};
      if (Array.isArray(dataSource)) {
        dataSourceItem = dataSource[index];
      } else if (dataSource && "gallery" in dataSource) {
        if (!dataSource.items) {
          dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
        }
        dataSourceItem = dataSource.items[index];
      }
      let itemData = dataSourceItem;
      if (itemData instanceof Element) {
        itemData = this._domElementToItemData(itemData);
      }
      const event = this.dispatch("itemData", {
        itemData: itemData || {},
        index
      });
      return this.applyFilters("itemData", event.itemData, index);
    }
_getGalleryDOMElements(galleryElement) {
      var _this$options3, _this$options4;
      if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.children || (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.childSelector) {
        return getElementsFromOption(this.options.children, this.options.childSelector, galleryElement) || [];
      }
      return [galleryElement];
    }
_domElementToItemData(element) {
      const itemData = {
        element
      };
      const linkEl = (
element.tagName === "A" ? element : element.querySelector("a")
      );
      if (linkEl) {
        itemData.src = linkEl.dataset.pswpSrc || linkEl.href;
        if (linkEl.dataset.pswpSrcset) {
          itemData.srcset = linkEl.dataset.pswpSrcset;
        }
        itemData.width = linkEl.dataset.pswpWidth ? parseInt(linkEl.dataset.pswpWidth, 10) : 0;
        itemData.height = linkEl.dataset.pswpHeight ? parseInt(linkEl.dataset.pswpHeight, 10) : 0;
        itemData.w = itemData.width;
        itemData.h = itemData.height;
        if (linkEl.dataset.pswpType) {
          itemData.type = linkEl.dataset.pswpType;
        }
        const thumbnailEl = element.querySelector("img");
        if (thumbnailEl) {
          var _thumbnailEl$getAttri;
          itemData.msrc = thumbnailEl.currentSrc || thumbnailEl.src;
          itemData.alt = (_thumbnailEl$getAttri = thumbnailEl.getAttribute("alt")) !== null && _thumbnailEl$getAttri !== void 0 ? _thumbnailEl$getAttri : "";
        }
        if (linkEl.dataset.pswpCropped || linkEl.dataset.cropped) {
          itemData.thumbCropped = true;
        }
      }
      return this.applyFilters("domItemData", itemData, element, linkEl);
    }
lazyLoadData(itemData, index) {
      return lazyLoadData(itemData, this, index);
    }
  }
  const MIN_OPACITY = 3e-3;
  class Opener {
constructor(pswp) {
      this.pswp = pswp;
      this.isClosed = true;
      this.isOpen = false;
      this.isClosing = false;
      this.isOpening = false;
      this._duration = void 0;
      this._useAnimation = false;
      this._croppedZoom = false;
      this._animateRootOpacity = false;
      this._animateBgOpacity = false;
      this._placeholder = void 0;
      this._opacityElement = void 0;
      this._cropContainer1 = void 0;
      this._cropContainer2 = void 0;
      this._thumbBounds = void 0;
      this._prepareOpen = this._prepareOpen.bind(this);
      pswp.on("firstZoomPan", this._prepareOpen);
    }
    open() {
      this._prepareOpen();
      this._start();
    }
    close() {
      if (this.isClosed || this.isClosing || this.isOpening) {
        return;
      }
      const slide = this.pswp.currSlide;
      this.isOpen = false;
      this.isOpening = false;
      this.isClosing = true;
      this._duration = this.pswp.options.hideAnimationDuration;
      if (slide && slide.currZoomLevel * slide.width >= this.pswp.options.maxWidthToAnimate) {
        this._duration = 0;
      }
      this._applyStartProps();
      setTimeout(() => {
        this._start();
      }, this._croppedZoom ? 30 : 0);
    }
_prepareOpen() {
      this.pswp.off("firstZoomPan", this._prepareOpen);
      if (!this.isOpening) {
        const slide = this.pswp.currSlide;
        this.isOpening = true;
        this.isClosing = false;
        this._duration = this.pswp.options.showAnimationDuration;
        if (slide && slide.zoomLevels.initial * slide.width >= this.pswp.options.maxWidthToAnimate) {
          this._duration = 0;
        }
        this._applyStartProps();
      }
    }
_applyStartProps() {
      const {
        pswp
      } = this;
      const slide = this.pswp.currSlide;
      const {
        options
      } = pswp;
      if (options.showHideAnimationType === "fade") {
        options.showHideOpacity = true;
        this._thumbBounds = void 0;
      } else if (options.showHideAnimationType === "none") {
        options.showHideOpacity = false;
        this._duration = 0;
        this._thumbBounds = void 0;
      } else if (this.isOpening && pswp._initialThumbBounds) {
        this._thumbBounds = pswp._initialThumbBounds;
      } else {
        this._thumbBounds = this.pswp.getThumbBounds();
      }
      this._placeholder = slide === null || slide === void 0 ? void 0 : slide.getPlaceholderElement();
      pswp.animations.stopAll();
      this._useAnimation = Boolean(this._duration && this._duration > 50);
      this._animateZoom = Boolean(this._thumbBounds) && (slide === null || slide === void 0 ? void 0 : slide.content.usePlaceholder()) && (!this.isClosing || !pswp.mainScroll.isShifted());
      if (!this._animateZoom) {
        this._animateRootOpacity = true;
        if (this.isOpening && slide) {
          slide.zoomAndPanToInitial();
          slide.applyCurrentZoomPan();
        }
      } else {
        var _options$showHideOpac;
        this._animateRootOpacity = (_options$showHideOpac = options.showHideOpacity) !== null && _options$showHideOpac !== void 0 ? _options$showHideOpac : false;
      }
      this._animateBgOpacity = !this._animateRootOpacity && this.pswp.options.bgOpacity > MIN_OPACITY;
      this._opacityElement = this._animateRootOpacity ? pswp.element : pswp.bg;
      if (!this._useAnimation) {
        this._duration = 0;
        this._animateZoom = false;
        this._animateBgOpacity = false;
        this._animateRootOpacity = true;
        if (this.isOpening) {
          if (pswp.element) {
            pswp.element.style.opacity = String(MIN_OPACITY);
          }
          pswp.applyBgOpacity(1);
        }
        return;
      }
      if (this._animateZoom && this._thumbBounds && this._thumbBounds.innerRect) {
        var _this$pswp$currSlide;
        this._croppedZoom = true;
        this._cropContainer1 = this.pswp.container;
        this._cropContainer2 = (_this$pswp$currSlide = this.pswp.currSlide) === null || _this$pswp$currSlide === void 0 ? void 0 : _this$pswp$currSlide.holderElement;
        if (pswp.container) {
          pswp.container.style.overflow = "hidden";
          pswp.container.style.width = pswp.viewportSize.x + "px";
        }
      } else {
        this._croppedZoom = false;
      }
      if (this.isOpening) {
        if (this._animateRootOpacity) {
          if (pswp.element) {
            pswp.element.style.opacity = String(MIN_OPACITY);
          }
          pswp.applyBgOpacity(1);
        } else {
          if (this._animateBgOpacity && pswp.bg) {
            pswp.bg.style.opacity = String(MIN_OPACITY);
          }
          if (pswp.element) {
            pswp.element.style.opacity = "1";
          }
        }
        if (this._animateZoom) {
          this._setClosedStateZoomPan();
          if (this._placeholder) {
            this._placeholder.style.willChange = "transform";
            this._placeholder.style.opacity = String(MIN_OPACITY);
          }
        }
      } else if (this.isClosing) {
        if (pswp.mainScroll.itemHolders[0]) {
          pswp.mainScroll.itemHolders[0].el.style.display = "none";
        }
        if (pswp.mainScroll.itemHolders[2]) {
          pswp.mainScroll.itemHolders[2].el.style.display = "none";
        }
        if (this._croppedZoom) {
          if (pswp.mainScroll.x !== 0) {
            pswp.mainScroll.resetPosition();
            pswp.mainScroll.resize();
          }
        }
      }
    }
_start() {
      if (this.isOpening && this._useAnimation && this._placeholder && this._placeholder.tagName === "IMG") {
        new Promise((resolve) => {
          let decoded = false;
          let isDelaying = true;
          decodeImage(
this._placeholder
          ).finally(() => {
            decoded = true;
            if (!isDelaying) {
              resolve(true);
            }
          });
          setTimeout(() => {
            isDelaying = false;
            if (decoded) {
              resolve(true);
            }
          }, 50);
          setTimeout(resolve, 250);
        }).finally(() => this._initiate());
      } else {
        this._initiate();
      }
    }
_initiate() {
      var _this$pswp$element, _this$pswp$element2;
      (_this$pswp$element = this.pswp.element) === null || _this$pswp$element === void 0 || _this$pswp$element.style.setProperty("--pswp-transition-duration", this._duration + "ms");
      this.pswp.dispatch(this.isOpening ? "openingAnimationStart" : "closingAnimationStart");
      this.pswp.dispatch(
"initialZoom" + (this.isOpening ? "In" : "Out")
      );
      (_this$pswp$element2 = this.pswp.element) === null || _this$pswp$element2 === void 0 || _this$pswp$element2.classList.toggle("pswp--ui-visible", this.isOpening);
      if (this.isOpening) {
        if (this._placeholder) {
          this._placeholder.style.opacity = "1";
        }
        this._animateToOpenState();
      } else if (this.isClosing) {
        this._animateToClosedState();
      }
      if (!this._useAnimation) {
        this._onAnimationComplete();
      }
    }
_onAnimationComplete() {
      const {
        pswp
      } = this;
      this.isOpen = this.isOpening;
      this.isClosed = this.isClosing;
      this.isOpening = false;
      this.isClosing = false;
      pswp.dispatch(this.isOpen ? "openingAnimationEnd" : "closingAnimationEnd");
      pswp.dispatch(
"initialZoom" + (this.isOpen ? "InEnd" : "OutEnd")
      );
      if (this.isClosed) {
        pswp.destroy();
      } else if (this.isOpen) {
        var _pswp$currSlide;
        if (this._animateZoom && pswp.container) {
          pswp.container.style.overflow = "visible";
          pswp.container.style.width = "100%";
        }
        (_pswp$currSlide = pswp.currSlide) === null || _pswp$currSlide === void 0 || _pswp$currSlide.applyCurrentZoomPan();
      }
    }
_animateToOpenState() {
      const {
        pswp
      } = this;
      if (this._animateZoom) {
        if (this._croppedZoom && this._cropContainer1 && this._cropContainer2) {
          this._animateTo(this._cropContainer1, "transform", "translate3d(0,0,0)");
          this._animateTo(this._cropContainer2, "transform", "none");
        }
        if (pswp.currSlide) {
          pswp.currSlide.zoomAndPanToInitial();
          this._animateTo(pswp.currSlide.container, "transform", pswp.currSlide.getCurrentTransform());
        }
      }
      if (this._animateBgOpacity && pswp.bg) {
        this._animateTo(pswp.bg, "opacity", String(pswp.options.bgOpacity));
      }
      if (this._animateRootOpacity && pswp.element) {
        this._animateTo(pswp.element, "opacity", "1");
      }
    }
_animateToClosedState() {
      const {
        pswp
      } = this;
      if (this._animateZoom) {
        this._setClosedStateZoomPan(true);
      }
      if (this._animateBgOpacity && pswp.bgOpacity > 0.01 && pswp.bg) {
        this._animateTo(pswp.bg, "opacity", "0");
      }
      if (this._animateRootOpacity && pswp.element) {
        this._animateTo(pswp.element, "opacity", "0");
      }
    }
_setClosedStateZoomPan(animate) {
      if (!this._thumbBounds) return;
      const {
        pswp
      } = this;
      const {
        innerRect
      } = this._thumbBounds;
      const {
        currSlide,
        viewportSize
      } = pswp;
      if (this._croppedZoom && innerRect && this._cropContainer1 && this._cropContainer2) {
        const containerOnePanX = -viewportSize.x + (this._thumbBounds.x - innerRect.x) + innerRect.w;
        const containerOnePanY = -viewportSize.y + (this._thumbBounds.y - innerRect.y) + innerRect.h;
        const containerTwoPanX = viewportSize.x - innerRect.w;
        const containerTwoPanY = viewportSize.y - innerRect.h;
        if (animate) {
          this._animateTo(this._cropContainer1, "transform", toTransformString(containerOnePanX, containerOnePanY));
          this._animateTo(this._cropContainer2, "transform", toTransformString(containerTwoPanX, containerTwoPanY));
        } else {
          setTransform(this._cropContainer1, containerOnePanX, containerOnePanY);
          setTransform(this._cropContainer2, containerTwoPanX, containerTwoPanY);
        }
      }
      if (currSlide) {
        equalizePoints(currSlide.pan, innerRect || this._thumbBounds);
        currSlide.currZoomLevel = this._thumbBounds.w / currSlide.width;
        if (animate) {
          this._animateTo(currSlide.container, "transform", currSlide.getCurrentTransform());
        } else {
          currSlide.applyCurrentZoomPan();
        }
      }
    }
_animateTo(target, prop, propValue) {
      if (!this._duration) {
        target.style[prop] = propValue;
        return;
      }
      const {
        animations
      } = this.pswp;
      const animProps = {
        duration: this._duration,
        easing: this.pswp.options.easing,
        onComplete: () => {
          if (!animations.activeAnimations.length) {
            this._onAnimationComplete();
          }
        },
        target
      };
      animProps[prop] = propValue;
      animations.startTransition(animProps);
    }
  }
  const defaultOptions = {
    allowPanToNext: true,
    spacing: 0.1,
    loop: true,
    pinchToClose: true,
    closeOnVerticalDrag: true,
    hideAnimationDuration: 333,
    showAnimationDuration: 333,
    zoomAnimationDuration: 333,
    escKey: true,
    arrowKeys: true,
    trapFocus: true,
    returnFocus: true,
    maxWidthToAnimate: 4e3,
    clickToCloseNonZoomable: true,
    imageClickAction: "zoom-or-close",
    bgClickAction: "close",
    tapAction: "toggle-controls",
    doubleTapAction: "zoom",
    indexIndicatorSep: " / ",
    preloaderDelay: 2e3,
    bgOpacity: 0.8,
    index: 0,
    errorMsg: "The image cannot be loaded",
    preload: [1, 2],
    easing: "cubic-bezier(.4,0,.22,1)"
  };
  class PhotoSwipe extends PhotoSwipeBase {
constructor(options) {
      super();
      this.options = this._prepareOptions(options || {});
      this.offset = {
        x: 0,
        y: 0
      };
      this._prevViewportSize = {
        x: 0,
        y: 0
      };
      this.viewportSize = {
        x: 0,
        y: 0
      };
      this.bgOpacity = 1;
      this.currIndex = 0;
      this.potentialIndex = 0;
      this.isOpen = false;
      this.isDestroying = false;
      this.hasMouse = false;
      this._initialItemData = {};
      this._initialThumbBounds = void 0;
      this.topBar = void 0;
      this.element = void 0;
      this.template = void 0;
      this.container = void 0;
      this.scrollWrap = void 0;
      this.currSlide = void 0;
      this.events = new DOMEvents();
      this.animations = new Animations();
      this.mainScroll = new MainScroll(this);
      this.gestures = new Gestures(this);
      this.opener = new Opener(this);
      this.keyboard = new Keyboard(this);
      this.contentLoader = new ContentLoader(this);
    }
init() {
      if (this.isOpen || this.isDestroying) {
        return false;
      }
      this.isOpen = true;
      this.dispatch("init");
      this.dispatch("beforeOpen");
      this._createMainStructure();
      let rootClasses = "pswp--open";
      if (this.gestures.supportsTouch) {
        rootClasses += " pswp--touch";
      }
      if (this.options.mainClass) {
        rootClasses += " " + this.options.mainClass;
      }
      if (this.element) {
        this.element.className += " " + rootClasses;
      }
      this.currIndex = this.options.index || 0;
      this.potentialIndex = this.currIndex;
      this.dispatch("firstUpdate");
      this.scrollWheel = new ScrollWheel(this);
      if (Number.isNaN(this.currIndex) || this.currIndex < 0 || this.currIndex >= this.getNumItems()) {
        this.currIndex = 0;
      }
      if (!this.gestures.supportsTouch) {
        this.mouseDetected();
      }
      this.updateSize();
      this.offset.y = window.pageYOffset;
      this._initialItemData = this.getItemData(this.currIndex);
      this.dispatch("gettingData", {
        index: this.currIndex,
        data: this._initialItemData,
        slide: void 0
      });
      this._initialThumbBounds = this.getThumbBounds();
      this.dispatch("initialLayout");
      this.on("openingAnimationEnd", () => {
        const {
          itemHolders
        } = this.mainScroll;
        if (itemHolders[0]) {
          itemHolders[0].el.style.display = "block";
          this.setContent(itemHolders[0], this.currIndex - 1);
        }
        if (itemHolders[2]) {
          itemHolders[2].el.style.display = "block";
          this.setContent(itemHolders[2], this.currIndex + 1);
        }
        this.appendHeavy();
        this.contentLoader.updateLazy();
        this.events.add(window, "resize", this._handlePageResize.bind(this));
        this.events.add(window, "scroll", this._updatePageScrollOffset.bind(this));
        this.dispatch("bindEvents");
      });
      if (this.mainScroll.itemHolders[1]) {
        this.setContent(this.mainScroll.itemHolders[1], this.currIndex);
      }
      this.dispatch("change");
      this.opener.open();
      this.dispatch("afterInit");
      return true;
    }
getLoopedIndex(index) {
      const numSlides = this.getNumItems();
      if (this.options.loop) {
        if (index > numSlides - 1) {
          index -= numSlides;
        }
        if (index < 0) {
          index += numSlides;
        }
      }
      return clamp(index, 0, numSlides - 1);
    }
    appendHeavy() {
      this.mainScroll.itemHolders.forEach((itemHolder) => {
        var _itemHolder$slide;
        (_itemHolder$slide = itemHolder.slide) === null || _itemHolder$slide === void 0 || _itemHolder$slide.appendHeavy();
      });
    }
goTo(index) {
      this.mainScroll.moveIndexBy(this.getLoopedIndex(index) - this.potentialIndex);
    }
next() {
      this.goTo(this.potentialIndex + 1);
    }
prev() {
      this.goTo(this.potentialIndex - 1);
    }
zoomTo(...args) {
      var _this$currSlide;
      (_this$currSlide = this.currSlide) === null || _this$currSlide === void 0 || _this$currSlide.zoomTo(...args);
    }
toggleZoom() {
      var _this$currSlide2;
      (_this$currSlide2 = this.currSlide) === null || _this$currSlide2 === void 0 || _this$currSlide2.toggleZoom();
    }
close() {
      if (!this.opener.isOpen || this.isDestroying) {
        return;
      }
      this.isDestroying = true;
      this.dispatch("close");
      this.events.removeAll();
      this.opener.close();
    }
destroy() {
      var _this$element;
      if (!this.isDestroying) {
        this.options.showHideAnimationType = "none";
        this.close();
        return;
      }
      this.dispatch("destroy");
      this._listeners = {};
      if (this.scrollWrap) {
        this.scrollWrap.ontouchmove = null;
        this.scrollWrap.ontouchend = null;
      }
      (_this$element = this.element) === null || _this$element === void 0 || _this$element.remove();
      this.mainScroll.itemHolders.forEach((itemHolder) => {
        var _itemHolder$slide2;
        (_itemHolder$slide2 = itemHolder.slide) === null || _itemHolder$slide2 === void 0 || _itemHolder$slide2.destroy();
      });
      this.contentLoader.destroy();
      this.events.removeAll();
    }
refreshSlideContent(slideIndex) {
      this.contentLoader.removeByIndex(slideIndex);
      this.mainScroll.itemHolders.forEach((itemHolder, i) => {
        var _this$currSlide$index, _this$currSlide3;
        let potentialHolderIndex = ((_this$currSlide$index = (_this$currSlide3 = this.currSlide) === null || _this$currSlide3 === void 0 ? void 0 : _this$currSlide3.index) !== null && _this$currSlide$index !== void 0 ? _this$currSlide$index : 0) - 1 + i;
        if (this.canLoop()) {
          potentialHolderIndex = this.getLoopedIndex(potentialHolderIndex);
        }
        if (potentialHolderIndex === slideIndex) {
          this.setContent(itemHolder, slideIndex, true);
          if (i === 1) {
            var _itemHolder$slide3;
            this.currSlide = itemHolder.slide;
            (_itemHolder$slide3 = itemHolder.slide) === null || _itemHolder$slide3 === void 0 || _itemHolder$slide3.setIsActive(true);
          }
        }
      });
      this.dispatch("change");
    }
setContent(holder, index, force) {
      if (this.canLoop()) {
        index = this.getLoopedIndex(index);
      }
      if (holder.slide) {
        if (holder.slide.index === index && !force) {
          return;
        }
        holder.slide.destroy();
        holder.slide = void 0;
      }
      if (!this.canLoop() && (index < 0 || index >= this.getNumItems())) {
        return;
      }
      const itemData = this.getItemData(index);
      holder.slide = new Slide(itemData, index, this);
      if (index === this.currIndex) {
        this.currSlide = holder.slide;
      }
      holder.slide.append(holder.el);
    }
getViewportCenterPoint() {
      return {
        x: this.viewportSize.x / 2,
        y: this.viewportSize.y / 2
      };
    }
updateSize(force) {
      if (this.isDestroying) {
        return;
      }
      const newViewportSize = getViewportSize(this.options, this);
      if (!force && pointsEqual(newViewportSize, this._prevViewportSize)) {
        return;
      }
      equalizePoints(this._prevViewportSize, newViewportSize);
      this.dispatch("beforeResize");
      equalizePoints(this.viewportSize, this._prevViewportSize);
      this._updatePageScrollOffset();
      this.dispatch("viewportSize");
      this.mainScroll.resize(this.opener.isOpen);
      if (!this.hasMouse && window.matchMedia("(any-hover: hover)").matches) {
        this.mouseDetected();
      }
      this.dispatch("resize");
    }
applyBgOpacity(opacity) {
      this.bgOpacity = Math.max(opacity, 0);
      if (this.bg) {
        this.bg.style.opacity = String(this.bgOpacity * this.options.bgOpacity);
      }
    }
mouseDetected() {
      if (!this.hasMouse) {
        var _this$element2;
        this.hasMouse = true;
        (_this$element2 = this.element) === null || _this$element2 === void 0 || _this$element2.classList.add("pswp--has_mouse");
      }
    }
_handlePageResize() {
      this.updateSize();
      if (/iPhone|iPad|iPod/i.test(window.navigator.userAgent)) {
        setTimeout(() => {
          this.updateSize();
        }, 500);
      }
    }
_updatePageScrollOffset() {
      this.setScrollOffset(0, window.pageYOffset);
    }
setScrollOffset(x, y) {
      this.offset.x = x;
      this.offset.y = y;
      this.dispatch("updateScrollOffset");
    }
_createMainStructure() {
      this.element = createElement("pswp", "div");
      this.element.setAttribute("tabindex", "-1");
      this.element.setAttribute("role", "dialog");
      this.template = this.element;
      this.bg = createElement("pswp__bg", "div", this.element);
      this.scrollWrap = createElement("pswp__scroll-wrap", "section", this.element);
      this.container = createElement("pswp__container", "div", this.scrollWrap);
      this.scrollWrap.setAttribute("aria-roledescription", "carousel");
      this.container.setAttribute("aria-live", "off");
      this.container.setAttribute("id", "pswp__items");
      this.mainScroll.appendHolders();
      this.ui = new UI(this);
      this.ui.init();
      (this.options.appendToEl || document.body).appendChild(this.element);
    }
getThumbBounds() {
      return getThumbBounds(this.currIndex, this.currSlide ? this.currSlide.data : this._initialItemData, this);
    }
canLoop() {
      return this.options.loop && this.getNumItems() > 2;
    }
_prepareOptions(options) {
      if (window.matchMedia("(prefers-reduced-motion), (update: slow)").matches) {
        options.showHideAnimationType = "none";
        options.zoomAnimationDuration = 0;
      }
      return {
        ...defaultOptions,
        ...options
      };
    }
  }
  function getSpreadImageRenderState(image) {
    if (!image.complete) return "loading";
    return image.naturalWidth > 0 && image.naturalHeight > 0 ? "loaded" : "error";
  }
  function shouldRetrySpreadImage(attempts, isCurrent) {
    return isCurrent && attempts < 1;
  }
  function getPhotoSwipeHolderPosition(currentIndex, targetIndex, holderCount) {
    const position = targetIndex - currentIndex + 1;
    return position >= 0 && position < holderCount ? position : null;
  }
  function shouldRetainMountedSpreadImage(tagName, src) {
    return tagName === "IMG" && !!src;
  }
  function shouldHandleSpreadMouseClick(pointerType, defaultPrevented, button) {
    return pointerType === "mouse" && !defaultPrevented && button === 0;
  }
  function getSpreadMouseClickAction(target, uiVisible) {
    if (!(target == null ? void 0 : target.closest("[data-reader-spread]"))) return null;
    if (!uiVisible) return "background";
    return target.closest("img.hr-reader-spread__page") ? "image" : "background";
  }
  function reconcilePhotoSwipeHolder(holderElement, activeSlide, previousSlide) {
    const activeContainer = activeSlide == null ? void 0 : activeSlide.container;
    if (!holderElement || !activeContainer) return;
    const previousContainer = previousSlide == null ? void 0 : previousSlide.container;
    if (previousSlide && previousSlide !== activeSlide && (previousContainer == null ? void 0 : previousContainer.parentElement) === holderElement) {
      previousSlide.destroy();
    }
    for (const child of Array.from(holderElement.children)) {
      if (child !== activeContainer && child.classList.contains("pswp__zoom-wrap")) {
        child.remove();
      }
    }
  }
  class PhotoSwipeDriver {
    constructor(options) {
      this.options = options;
      this.slidesByHolder = new WeakMap();
      this.renderedImageBindings = new WeakMap();
      this.renderedImageRetryAttempts = new Map();
      this.renderedImageRetryPending = new Set();
      this.renderedImageRetryTimers = new Set();
      this.instance = new PhotoSwipe({
        index: options.startIndex,
        counter: false,
        bgOpacity: 1,
        spacing: 0.1,
        loop: false,
        wheelToZoom: false,
        preload: [1, 1],
        closeOnVerticalDrag: true,
        arrowPrev: false,
        arrowNext: false,
        doubleTapAction: false,
        initialZoomLevel: (zoomLevelObject) => {
          if (!zoomLevelObject.panAreaSize || !zoomLevelObject.elementSize) return zoomLevelObject.fit;
          const hRatio = zoomLevelObject.panAreaSize.x / zoomLevelObject.elementSize.x;
          const vRatio = zoomLevelObject.panAreaSize.y / zoomLevelObject.elementSize.y;
          return hRatio < vRatio ? hRatio : vRatio;
        },
        secondaryZoomLevel: (zoomLevelObject) => {
          if (!zoomLevelObject.panAreaSize || !zoomLevelObject.elementSize) return zoomLevelObject.fit * 1.5;
          const hRatio = zoomLevelObject.panAreaSize.x / zoomLevelObject.elementSize.x;
          return hRatio * 1.5;
        },
        maxZoomLevel: (zoomLevelObject) => {
          if (!zoomLevelObject.panAreaSize || !zoomLevelObject.elementSize) return 4;
          const hRatio = zoomLevelObject.panAreaSize.x / zoomLevelObject.elementSize.x;
          return Math.max(hRatio * 3, 4);
        },
        bgClickAction: (point) => options.onBackgroundClick(point),
        imageClickAction: (point) => options.onImageClick(point),
        tapAction: (point) => options.onTap(point)
      });
      this.instance.addFilter("itemData", (itemData, index) => {
        if (!Array.isArray(itemData.hrSpread)) return itemData;
        const escape = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
        const fetchPriority = index === this.instance.currIndex ? "high" : "low";
        const images = itemData.hrSpread.map(
          (page) => page.src ? `<img class="hr-reader-spread__page" data-logical-index="${Number(page.index)}" src="${escape(page.src)}" alt="${escape(page.alt || "")}" decoding="async" fetchpriority="${fetchPriority}">` : `<span class="hr-reader-spread__page hr-reader-spread__page--pending" data-logical-index="${Number(page.index)}" aria-hidden="true"></span>`
        ).join("");
        return {
          ...itemData,
          type: "html",
          html: `<div class="hr-reader-spread" data-reader-spread>${images}</div>`
        };
      });
      this.instance.addFilter("isContentZoomable", (zoomable, content) => {
        var _a;
        return Array.isArray((_a = content == null ? void 0 : content.data) == null ? void 0 : _a.hrSpread) ? true : zoomable;
      });
      this.instance.on("afterSetContent", ({ slide }) => {
        const holderElement = slide == null ? void 0 : slide.holderElement;
        if (!holderElement) return;
        reconcilePhotoSwipeHolder(holderElement, slide, this.slidesByHolder.get(holderElement));
        this.slidesByHolder.set(holderElement, slide);
        this.bindSpreadImages(slide);
      });
      this.instance.on("bindEvents", () => {
        const root = this.instance.element;
        if (!root) return;
        let lastPointerType = "";
        const onPointerDown = (event) => {
          lastPointerType = event.pointerType;
        };
        const onClick = (event) => {
          const pointerType = event.pointerType || lastPointerType;
          lastPointerType = "";
          if (!shouldHandleSpreadMouseClick(pointerType, event.defaultPrevented, event.button)) return;
          const action = getSpreadMouseClickAction(
            event.target,
            root.classList.contains("pswp--ui-visible")
          );
          if (!action) return;
          event.preventDefault();
          event.stopPropagation();
          const point = { x: event.clientX, y: event.clientY };
          if (action === "image") options.onImageClick(point);
          else options.onBackgroundClick(point);
        };
        root.addEventListener("pointerdown", onPointerDown);
        root.addEventListener("click", onClick);
        this.instance.on("destroy", () => {
          root.removeEventListener("pointerdown", onPointerDown);
          root.removeEventListener("click", onClick);
        });
      });
    }
    get currentIndex() {
      return this.instance.currIndex;
    }
    on(event, listener) {
      this.instance.on(event, listener);
    }
    init() {
      var _a;
      this.instance.init();
      (_a = this.instance.element) == null ? void 0 : _a.setAttribute("aria-label", "Image reader");
    }
    destroy() {
      this.renderedImageRetryTimers.forEach((timer) => clearTimeout(timer));
      this.renderedImageRetryTimers.clear();
      this.renderedImageRetryPending.clear();
      this.instance.destroy();
    }
    next() {
      this.instance.next();
    }
    prev() {
      this.instance.prev();
    }
    goTo(index) {
      this.instance.goTo(index);
    }
    refreshSlide(index) {
      var _a, _b, _c, _d, _e, _f, _g, _h;
      const instance = this.instance;
      const holders = ((_a = instance.mainScroll) == null ? void 0 : _a.itemHolders) ?? [];
      const holderPosition = getPhotoSwipeHolderPosition(
        this.instance.currIndex,
        index,
        holders.length
      );
      if (holderPosition === null) {
        (_c = (_b = instance.contentLoader) == null ? void 0 : _b.removeByIndex) == null ? void 0 : _c.call(_b, index);
        return;
      }
      const holder = holders[holderPosition];
      const itemData = instance.getItemData(index);
      if (((_d = holder == null ? void 0 : holder.slide) == null ? void 0 : _d.index) === index && this.patchSpreadSlide(holder.slide, itemData)) return;
      (_f = (_e = instance.contentLoader) == null ? void 0 : _e.removeByIndex) == null ? void 0 : _f.call(_e, index);
      instance.setContent(holder, index, true);
      if (index === this.instance.currIndex) {
        instance.currSlide = holder.slide;
        (_h = (_g = holder.slide) == null ? void 0 : _g.setIsActive) == null ? void 0 : _h.call(_g, true);
      }
    }
    syncLayout(index) {
      var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
      const instance = this.instance;
      this.instance.goTo(index);
      const itemCount = this.instance.getNumItems();
      const holders = ((_a = instance.mainScroll) == null ? void 0 : _a.itemHolders) ?? [];
      for (let holderIndex = 0; holderIndex < holders.length; holderIndex++) {
        const holder = holders[holderIndex];
        const expectedIndex = this.instance.currIndex - 1 + holderIndex;
        if (expectedIndex < 0 || expectedIndex >= itemCount) {
          if (holder.slide) instance.setContent(holder, expectedIndex, true);
          continue;
        }
        if (((_b = holder.slide) == null ? void 0 : _b.index) !== expectedIndex) {
          instance.setContent(holder, expectedIndex, true);
        }
        const slide = holder.slide;
        if (slide && !this.patchSpreadSlide(slide, instance.getItemData(expectedIndex))) {
          instance.setContent(holder, expectedIndex, true);
        }
      }
      instance.currSlide = (_c = holders[1]) == null ? void 0 : _c.slide;
      const visibleContents = new Set(holders.map((holder) => {
        var _a2;
        return (_a2 = holder.slide) == null ? void 0 : _a2.content;
      }).filter(Boolean));
      const cachedItems = [...((_d = instance.contentLoader) == null ? void 0 : _d._cachedItems) ?? []];
      for (const content of cachedItems) {
        if (visibleContents.has(content)) continue;
        const staleSlide = content.slide;
        if ((_e = staleSlide == null ? void 0 : staleSlide.container) == null ? void 0 : _e.parentElement) staleSlide.destroy();
        content.destroy();
        (_g = (_f = instance.contentLoader) == null ? void 0 : _f.removeByIndex) == null ? void 0 : _g.call(_f, content.index);
      }
      (_h = this.instance.element) == null ? void 0 : _h.classList.toggle("pswp--one-slide", itemCount === 1);
      this.instance.updateSize(true);
      (_j = (_i = instance.contentLoader) == null ? void 0 : _i.updateLazy) == null ? void 0 : _j.call(_i);
      this.instance.dispatch("change");
    }
    isInteracting() {
      var _a, _b, _c, _d, _e, _f, _g;
      const instance = this.instance;
      const activeAnimations = ((_a = instance.animations) == null ? void 0 : _a.activeAnimations) ?? [];
      return !!((_b = instance.gestures) == null ? void 0 : _b.isDragging) || !!((_c = instance.gestures) == null ? void 0 : _c.isZooming) || !!((_e = (_d = instance.mainScroll) == null ? void 0 : _d.isShifted) == null ? void 0 : _e.call(_d)) || !!((_f = instance.opener) == null ? void 0 : _f.isOpening) || !!((_g = instance.opener) == null ? void 0 : _g.isClosing) || activeAnimations.some((animation) => {
        var _a2, _b2;
        return !!((_a2 = animation == null ? void 0 : animation.props) == null ? void 0 : _a2.isMainScroll) || !!((_b2 = animation == null ? void 0 : animation.props) == null ? void 0 : _b2.isPan);
      });
    }
    stopMotion() {
      var _a, _b;
      (_b = (_a = this.instance.mainScroll) == null ? void 0 : _a.stop) == null ? void 0 : _b.call(_a);
    }
    getSlideContentState(index) {
      var _a, _b, _c;
      const instance = this.instance;
      const slide = ((_a = instance.slides) == null ? void 0 : _a[index]) ?? ((_b = instance.getSlideByIndex) == null ? void 0 : _b.call(instance, index));
      return (_c = slide == null ? void 0 : slide.content) == null ? void 0 : _c.state;
    }
    isCurrentContentLoaded() {
      var _a, _b, _c, _d, _e;
      const instance = this.instance;
      const holders = ((_a = instance.mainScroll) == null ? void 0 : _a.itemHolders) ?? [];
      const holderPosition = getPhotoSwipeHolderPosition(
        this.instance.currIndex,
        this.instance.currIndex,
        holders.length
      );
      const holder = holderPosition === null ? void 0 : holders[holderPosition];
      const slide = holder == null ? void 0 : holder.slide;
      if (!slide || slide.index !== this.instance.currIndex) return false;
      if (((_b = slide.container) == null ? void 0 : _b.parentElement) !== holder.el) return false;
      const content = slide.content;
      if (Array.isArray((_c = content == null ? void 0 : content.data) == null ? void 0 : _c.hrSpread)) {
        if (!content.data.hrSpread.every((page) => !!page.src)) return false;
        const root = (_e = (_d = slide.container) == null ? void 0 : _d.querySelector) == null ? void 0 : _e.call(_d, "[data-reader-spread]");
        if (!root) return false;
        const images = new Map();
        root.querySelectorAll("img.hr-reader-spread__page").forEach((image) => {
          images.set(Number(image.dataset.logicalIndex), image);
        });
        return content.data.hrSpread.every((page) => {
          const image = images.get(Number(page.index));
          return !!(image == null ? void 0 : image.getAttribute("src")) && getSpreadImageRenderState(image) === "loaded";
        });
      }
      return (content == null ? void 0 : content.state) === "loaded";
    }
    isCurrentZoomed() {
      const slide = this.instance.currSlide;
      return !!slide && slide.currZoomLevel > slide.zoomLevels.initial;
    }
    isCurrentAtInitialZoom() {
      const slide = this.instance.currSlide;
      return !!slide && slide.currZoomLevel <= slide.zoomLevels.initial;
    }
    canToggleCurrentZoom() {
      const slide = this.instance.currSlide;
      return !!slide && slide.isZoomable() && slide.zoomLevels.secondary !== slide.zoomLevels.initial;
    }
    toggleCurrentZoom(point) {
      var _a;
      (_a = this.instance.currSlide) == null ? void 0 : _a.toggleZoom(point);
    }
    showUi() {
      var _a;
      (_a = this.instance.element) == null ? void 0 : _a.classList.add("pswp--ui-visible");
    }
    hideUi() {
      var _a;
      (_a = this.instance.element) == null ? void 0 : _a.classList.remove("pswp--ui-visible");
    }
    toggleUi() {
      var _a;
      return ((_a = this.instance.element) == null ? void 0 : _a.classList.toggle("pswp--ui-visible")) ?? false;
    }
    appendUi(elements) {
      const root = this.instance.element;
      if (!root) return;
      for (const element of elements) root.appendChild(element);
    }
    registerCounter(render) {
      var _a;
      (_a = this.instance.ui) == null ? void 0 : _a.registerElement({
        name: "custom-counter",
        order: 5,
        onInit: (element, instance) => {
          element.className = "pswp__counter";
          const update = () => {
            element.innerHTML = render(instance.currIndex);
          };
          update();
          instance.on("change", update);
        }
      });
    }
    observeUiVisibility(listener) {
      const root = this.instance.element;
      if (!root) return () => {
      };
      const notify = () => listener(root.classList.contains("pswp--ui-visible"));
      const observer = new MutationObserver(notify);
      observer.observe(root, { attributes: true, attributeFilter: ["class"] });
      notify();
      return () => observer.disconnect();
    }
    installWheel(listener) {
      const root = this.instance.element;
      if (!root) return () => {
      };
      const onWheel = (event) => {
        const target = event.target;
        if (target == null ? void 0 : target.closest("[data-reader-wheel-block]")) return;
        listener(event);
      };
      document.addEventListener("wheel", onWheel, { passive: false, capture: true });
      return () => document.removeEventListener("wheel", onWheel, { capture: true });
    }
    installEdgeSwipe(options) {
      const root = this.instance.element;
      if (!root) return () => {
      };
      let touchStartX = 0;
      let touchStartY = 0;
      const onStart = (event) => {
        if (event.touches.length !== 1) return;
        touchStartX = event.touches[0].clientX;
        touchStartY = event.touches[0].clientY;
      };
      const onEnd = (event) => {
        if (event.changedTouches.length !== 1 || !this.isCurrentAtInitialZoom()) return;
        const deltaX = event.changedTouches[0].clientX - touchStartX;
        const deltaY = event.changedTouches[0].clientY - touchStartY;
        if (Math.abs(deltaX) <= 50 || Math.abs(deltaY) >= Math.abs(deltaX)) return;
        if (deltaX > 0) options.onBackward();
        else options.onForward();
      };
      root.addEventListener("touchstart", onStart, { passive: true });
      root.addEventListener("touchend", onEnd, { passive: true });
      return () => {
        root.removeEventListener("touchstart", onStart);
        root.removeEventListener("touchend", onEnd);
      };
    }
patchSpreadSlide(slide, itemData) {
      var _a, _b, _c, _d;
      if (!Array.isArray((_b = (_a = slide == null ? void 0 : slide.content) == null ? void 0 : _a.data) == null ? void 0 : _b.hrSpread) || !Array.isArray(itemData == null ? void 0 : itemData.hrSpread)) return false;
      const root = (_d = (_c = slide.content.element) == null ? void 0 : _c.querySelector) == null ? void 0 : _d.call(_c, "[data-reader-spread]");
      if (!root) return false;
      const existing = new Map();
      root.querySelectorAll("[data-logical-index]").forEach((element) => {
        existing.set(Number(element.dataset.logicalIndex), element);
      });
      for (const page of itemData.hrSpread) {
        const logicalIndex = Number(page.index);
        const current = existing.get(logicalIndex);
        let next;
        if (page.src) {
          const image = (current == null ? void 0 : current.tagName) === "IMG" ? current : document.createElement("img");
          this.configureSpreadImage(image, page, slide);
          next = image;
        } else if (shouldRetainMountedSpreadImage(
          current == null ? void 0 : current.tagName,
          current == null ? void 0 : current.getAttribute("src")
        )) {
          next = current;
        } else {
          const pending = (current == null ? void 0 : current.tagName) === "SPAN" ? current : document.createElement("span");
          pending.className = "hr-reader-spread__page hr-reader-spread__page--pending";
          pending.dataset.logicalIndex = String(logicalIndex);
          pending.setAttribute("aria-hidden", "true");
          next = pending;
        }
        if (current && current !== next) current.replaceWith(next);
        root.appendChild(next);
        existing.delete(logicalIndex);
      }
      existing.forEach((element) => element.remove());
      slide.data = itemData;
      slide.content.data = itemData;
      slide.content.width = Number(itemData.w) || 1;
      slide.content.height = Number(itemData.h) || 1;
      slide.width = slide.content.width;
      slide.height = slide.content.height;
      slide.zoomLevels.itemData = itemData;
      slide.resize();
      return true;
    }
    bindSpreadImages(slide) {
      var _a, _b, _c, _d, _e;
      const pages = (_b = (_a = slide == null ? void 0 : slide.content) == null ? void 0 : _a.data) == null ? void 0 : _b.hrSpread;
      const root = (_e = (_d = (_c = slide == null ? void 0 : slide.content) == null ? void 0 : _c.element) == null ? void 0 : _d.querySelector) == null ? void 0 : _e.call(_d, "[data-reader-spread]");
      if (!Array.isArray(pages) || !root) return;
      const byIndex = new Map();
      root.querySelectorAll("img.hr-reader-spread__page").forEach((image) => {
        byIndex.set(Number(image.dataset.logicalIndex), image);
      });
      for (const page of pages) {
        const image = byIndex.get(Number(page.index));
        if (image && page.src) this.configureSpreadImage(image, page, slide);
      }
    }
    configureSpreadImage(image, page, slide) {
      const logicalIndex = Number(page.index);
      const src = String(page.src || "");
      const bindingKey = `${logicalIndex}\0${src}`;
      image.className = "hr-reader-spread__page";
      image.dataset.logicalIndex = String(logicalIndex);
      image.alt = page.alt || "";
      image.decoding = "async";
      image.fetchPriority = slide.index === this.instance.currIndex ? "high" : "low";
      if (this.renderedImageBindings.get(image) !== bindingKey) {
        this.renderedImageBindings.set(image, bindingKey);
        image.onload = () => {
          if (this.renderedImageBindings.get(image) !== bindingKey) return;
          this.publishRenderedImageState(logicalIndex, "loaded");
        };
        image.onerror = () => {
          if (this.renderedImageBindings.get(image) !== bindingKey) return;
          this.handleSpreadImageError(image, page, slide, bindingKey);
        };
      }
      if (image.getAttribute("src") !== src) {
        this.publishRenderedImageState(logicalIndex, "loading");
        image.src = src;
        return;
      }
      const state = getSpreadImageRenderState(image);
      if (state === "error") this.handleSpreadImageError(image, page, slide, bindingKey);
      else this.publishRenderedImageState(logicalIndex, state);
    }
    handleSpreadImageError(image, page, slide, bindingKey) {
      const logicalIndex = Number(page.index);
      const attempts = this.renderedImageRetryAttempts.get(bindingKey) ?? 0;
      if (this.renderedImageRetryPending.has(bindingKey)) {
        this.publishRenderedImageState(logicalIndex, "loading");
        return;
      }
      if (!shouldRetrySpreadImage(attempts, slide.index === this.instance.currIndex)) {
        this.publishRenderedImageState(logicalIndex, "error");
        return;
      }
      this.renderedImageRetryAttempts.set(bindingKey, attempts + 1);
      this.renderedImageRetryPending.add(bindingKey);
      this.publishRenderedImageState(logicalIndex, "loading");
      const timer = setTimeout(() => {
        this.renderedImageRetryTimers.delete(timer);
        this.renderedImageRetryPending.delete(bindingKey);
        if (!image.isConnected || this.renderedImageBindings.get(image) !== bindingKey) return;
        const replacement = document.createElement("img");
        image.replaceWith(replacement);
        this.configureSpreadImage(replacement, page, slide);
      }, 300);
      this.renderedImageRetryTimers.add(timer);
    }
    publishRenderedImageState(logicalIndex, state) {
      this.options.onRenderedImageStateChange({ logicalIndex, state });
    }
  }
  function createPhotoSwipeDriver(options) {
    return new PhotoSwipeDriver(options);
  }
  function createReader(pageLoader) {
    var _a;
    const reader = createReaderController({
      pageLoader,
      createDriver: createPhotoSwipeDriver,
      scroll: {
        pause: pauseLazyLoad,
        resume: resumeLazyLoad,
        requestImage: loadPlaceholderImage,
        subscribeImageLoaded: subscribeScrollImageLoaded,
        restore: restoreReaderScroll
      },
      context: {
        getGalleryItems: () => store.galleryItems,
        isScrollMode: () => store.settings.scrollMode,
        isDoublePageModeEnabled: () => store.settings.doublePageMode,
        isAutoPlayEnabled: () => store.autoPlay,
        setAutoPlayEnabled: (enabled) => {
          if (store.autoPlay === enabled) return;
          store.autoPlay = enabled;
          store.emit("settingsChanged");
        },
        getAutoPlayInterval: () => store.settings.autoPlayInterval,
        getThumbnailPosition: () => store.settings.thumbnailPosition,
        getImageOffset: () => store.imageOffset,
        setImageOffset: (offset) => {
          store.imageOffset = offset;
        },
        getNextUrl: () => store.nextUrl,
        setNextUrl: (url) => {
          store.nextUrl = url;
        },
        getPrevUrl: () => store.prevUrl,
        setPrevUrl: (url) => {
          store.prevUrl = url;
        },
        isPageFetching: () => store.isFetching,
        setPageFetching: (fetching) => {
          store.isFetching = fetching;
        },
        subscribeSettingsChanged: (listener) => store.on("settingsChanged", listener),
        subscribeReaderModeChanged: (listener) => store.on("readerModeChanged", listener),
        emitReaderModeChanged: () => store.emit("readerModeChanged"),
        onReaderClose: (globalIndex) => {
          var _a2, _b;
          return (_b = (_a2 = store.activeAdapter) == null ? void 0 : _a2.onReaderClose) == null ? void 0 : _b.call(_a2, globalIndex, {
            scrollMode: store.settings.scrollMode,
            pageSize: store.perPage
          });
        }
      },
      prefetchPolicy: (_a = store.activeAdapter) == null ? void 0 : _a.readerPrefetch,
      onLoadNextPage: (page) => {
        store.currPage++;
        processBatch(page.items, store.currPage, void 0, false, page.pageUrl);
        store.nextUrl = page.nextUrl;
      },
      onLoadPrevPage: (page) => {
        store.currPage--;
        processBatch(page.items, store.currPage, void 0, true, page.pageUrl);
        store.prevUrl = page.prevUrl;
      }
    });
    return reader;
  }
  const floatControlCss = ".bookmark-control{position:fixed;z-index:2147483647;display:flex;flex-direction:column;align-items:center;background:#1e1e1e99;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.1);border-radius:24px;padding:16px 8px;gap:16px;transition:opacity .3s,left .3s cubic-bezier(.2,.8,.2,1),right .3s cubic-bezier(.2,.8,.2,1);box-shadow:0 4px 16px #0000004d;-webkit-user-select:none;user-select:none;touch-action:none;-webkit-tap-highlight-color:transparent;transform:translateY(-50%) scale(var(--hr-scale, 1))}.bookmark-control:not(.dragging){opacity:.4}.bookmark-control:not(.dragging):active{opacity:1}.bookmark-control.left-side{left:0;border-radius:0 28px 28px 0;border-left:none;padding:16px 8px 16px 4px;transform-origin:left center}.bookmark-control.right-side{right:0;border-radius:28px 0 0 28px;border-right:none;padding:16px 4px 16px 8px;transform-origin:right center}.bm-btn{width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;background:transparent;color:#fff;border:0;padding:0;font:inherit;-webkit-tap-highlight-color:transparent}.bm-btn:focus-visible{outline:2px solid #fff;outline-offset:2px}.bookmark-control .bm-btn svg{width:24px;height:24px;opacity:.8}.bookmark-control .bm-btn svg:not([fill=none]),.bookmark-control .bm-btn svg:not([fill=none]) *{fill:currentColor!important;stroke:none!important}.bookmark-control .bm-btn svg[fill=none],.bookmark-control .bm-btn svg[fill=none] *{fill:none!important;stroke:currentColor!important}.bm-btn:hover svg{opacity:1}.bm-mode-btn{background:var(--hr-accent);width:48px;height:48px;box-shadow:0 2px 8px #f596aa66}.bm-mode-btn svg{width:26px;height:26px;opacity:1}@media(hover:hover){.bookmark-control:not(.dragging):hover{opacity:1}.bm-btn:hover{background:#ffffff1a;transform:scale(1.1)}.bm-mode-btn:hover{background:var(--hr-accent-hover);box-shadow:0 4px 12px #f596aa99;transform:scale(1.05)}}:root.hr-mobile .bookmark-control{padding:12px 8px;gap:12px;border-radius:24px}:root.hr-mobile .bookmark-control.left-side{padding:12px 8px 12px 6px;border-radius:0 24px 24px 0}:root.hr-mobile .bookmark-control.right-side{padding:12px 6px 12px 8px;border-radius:24px 0 0 24px}:root.hr-mobile .bm-btn{width:46px;height:46px}:root.hr-mobile .bm-btn svg{width:26px;height:26px}:root.hr-mobile .bm-mode-btn{width:52px;height:52px}:root.hr-mobile .bm-mode-btn svg{width:28px;height:28px}";
  importCSS(floatControlCss);
  const svgSettings = `<svg viewBox="0 0 24 24"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>`;
  const svgReader = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg>`;
  const svgPlay = `<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>`;
  const svgPause = `<svg viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`;
  const svgTop = `<svg viewBox="0 0 24 24"><path d="M4 4h16v2H4V4zm4 8l1.41 1.41L11 11.83V22h2V11.83l1.59 1.58L16 12l-4-4-4 4z"/></svg>`;
  const svgScroll = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="2" width="18" height="20" rx="2" ry="2"></rect><line x1="3" y1="15" x2="21" y2="15"></line><line x1="12" y1="2" x2="10" y2="15"></line><path d="M9 19h.01"></path><path d="M15 19h.01"></path><path d="M11 19.5a1.5 1.5 0 0 0 2 0"></path></svg>`;
  const svgClose = `<svg class="pswp__icn" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
  const settingsPanelCss = ".settings-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;z-index:2147483647;opacity:0;pointer-events:none;transition:opacity .3s;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.settings-backdrop{width:calc(100% / var(--hr-scale, 1));height:calc(100% / var(--hr-scale, 1));transform:scale(var(--hr-scale, 1));transform-origin:top left}.settings-backdrop.show{opacity:1;pointer-events:auto}.settings-bottom-sheet{position:absolute;bottom:-100%;left:0;width:100%;background:#1a1a1a;border-radius:24px 24px 0 0;padding:0 20px 30px;box-sizing:border-box;transition:bottom .3s cubic-bezier(.2,.8,.2,1);box-shadow:0 -4px 20px #00000080;border-top:1px solid rgba(255,255,255,.05)}.settings-backdrop.show .settings-bottom-sheet{bottom:0}:root:not(.hr-mobile) .settings-bottom-sheet{max-width:480px;left:50%;transform:translate(-50%);border-radius:16px 16px 0 0;border:1px solid rgba(255,255,255,.05);border-bottom:none}@media(hover:hover)and (pointer:fine){:root:not(.hr-mobile) .settings-backdrop{background:none;backdrop-filter:none;-webkit-backdrop-filter:none}:root:not(.hr-mobile) .settings-bottom-sheet{position:absolute;width:340px;max-width:none;border-radius:16px;border:1px solid rgba(255,255,255,.1);box-shadow:0 10px 40px #00000080;padding:0 20px 20px;transition:opacity .2s,transform .2s;opacity:0;pointer-events:none;bottom:auto!important;left:auto;transform:scale(.95)}:root:not(.hr-mobile) .settings-backdrop.show .settings-bottom-sheet{opacity:1;pointer-events:auto;transform:scale(1)}}.settings-sheet-header{position:relative;width:100%;height:48px;display:flex;justify-content:center;align-items:center;margin-bottom:20px;border-bottom:1px solid rgba(255,255,255,.05)}.settings-title{font-size:18px;font-weight:600;color:#fff;margin:0}.settings-close-btn,.toggle-switch,.stepper-btn,.segment-item{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;font:inherit}.settings-close-btn:focus-visible,.toggle-switch:focus-visible,.stepper-btn:focus-visible,.segment-item:focus-visible{outline:2px solid #fff;outline-offset:2px}.settings-close-btn{position:absolute;right:0;top:0;bottom:0;margin:auto;width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s;color:#fff9}.settings-close-btn:hover{background:#ffffff1a;color:#fff}.settings-close-btn svg{width:22px;height:22px;stroke:currentColor}.settings-item{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;font-size:15px;color:#eee}.settings-item:last-child{margin-bottom:0}.settings-label{margin-right:10px;white-space:nowrap;font-weight:500}.toggle-switch{width:44px;height:24px;background:#333;border-radius:12px;position:relative;cursor:pointer;transition:background .3s}.toggle-switch.on{background:var(--hr-accent)}.toggle-slider{width:20px;height:20px;background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;transition:left .3s;box-shadow:0 1px 3px #0000004d}.toggle-switch.on .toggle-slider{left:22px}.stepper-control{display:flex;align-items:center;background:#ffffff14;border-radius:8px;overflow:hidden}.stepper-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;color:#fff;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:22px;transition:background .2s}.stepper-btn:hover{background:#ffffff26}.stepper-btn:active{background:#ffffff40}.interval-input{width:50px;background:transparent;border:none;border-left:1px solid rgba(255,255,255,.1);border-right:1px solid rgba(255,255,255,.1);border-radius:0;color:#fff;padding:6px 0;font-size:15px;text-align:center;height:36px;box-sizing:border-box;outline:none}.interval-input::-webkit-outer-spin-button,.interval-input::-webkit-inner-spin-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0}.interval-input[type=number]{-moz-appearance:textfield;-webkit-appearance:textfield;appearance:textfield}.interval-input:focus{background:#ffffff0d}.segment-control{display:flex;background:#ffffff14;border-radius:8px;padding:4px;gap:4px}.segment-item{padding:6px 8px;border-radius:6px;font-size:13px;color:#fff9;cursor:pointer;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-weight:500;white-space:nowrap}.segment-item:hover{color:#ffffffe6}.segment-item.active{background:#ffffff26;color:#fff;box-shadow:0 2px 6px #0003}:root.hr-mobile .settings-sheet-header{height:40px;margin-bottom:10px}:root.hr-mobile .settings-title{font-size:17px}:root.hr-mobile .settings-close-btn{width:36px;height:36px}:root.hr-mobile .settings-close-btn svg{width:20px;height:20px}:root.hr-mobile .settings-item{margin-bottom:14px;font-size:15px;font-weight:600;gap:12px}:root.hr-mobile .settings-item:last-child{margin-bottom:0}:root.hr-mobile .toggle-switch{width:48px;height:28px;border-radius:14px;flex-shrink:0}:root.hr-mobile .toggle-slider{width:24px;height:24px;top:2px;left:2px}:root.hr-mobile .toggle-switch.on .toggle-slider{left:22px}:root.hr-mobile .stepper-control{border-radius:10px}:root.hr-mobile .stepper-btn{width:44px;height:34px;font-size:20px;font-weight:400}:root.hr-mobile .interval-input{width:52px;height:34px;font-size:17px;font-weight:600}:root.hr-mobile .segment-control{padding:3px;gap:3px;border-radius:10px;display:flex;flex:1;min-width:180px}:root.hr-mobile .segment-item{flex:1;padding:7px 6px;font-size:14px;border-radius:8px;font-weight:600;text-align:center;display:flex;justify-content:center;align-items:center;white-space:nowrap}:root.hr-mobile .settings-bottom-sheet{padding:0 20px 20px}.settings-backdrop .settings-bottom-sheet{isolation:isolate;color-scheme:dark;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.settings-backdrop .settings-close-btn,.settings-backdrop .toggle-switch,.settings-backdrop .stepper-btn,.settings-backdrop .segment-item{-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;border:0!important;background-image:none!important;font:inherit!important;text-decoration:none!important;text-shadow:none!important;text-transform:none!important;box-sizing:border-box!important}.settings-backdrop .settings-close-btn{background:transparent!important;color:#fff9!important;box-shadow:none!important;padding:0!important}.settings-backdrop .settings-close-btn:hover{background:#ffffff1a!important;color:#fff!important}.settings-backdrop .toggle-switch{background:#333!important;color:transparent!important;box-shadow:none!important;padding:0!important}.settings-backdrop .toggle-switch.on{background:var(--hr-accent)!important}.settings-backdrop .stepper-btn{background:transparent!important;color:#fff!important;box-shadow:none!important;padding:0!important;margin:0!important}.settings-backdrop .stepper-btn:hover{background:#ffffff26!important}.settings-backdrop .stepper-btn:active{background:#ffffff40!important}.settings-backdrop .segment-item{background:transparent!important;color:#ffffffad!important;box-shadow:none!important;margin:0!important}.settings-backdrop .segment-item:hover{background:#ffffff14!important;color:#ffffffeb!important}.settings-backdrop .segment-item.active,.settings-backdrop .segment-item[aria-pressed=true]{background:#ffffff2e!important;color:#fff!important;box-shadow:0 2px 6px #0003!important}.settings-backdrop .interval-input{-webkit-appearance:textfield!important;-moz-appearance:textfield!important;appearance:textfield!important;background:transparent!important;color:#fff!important;border:0!important;border-left:1px solid rgba(255,255,255,.1)!important;border-right:1px solid rgba(255,255,255,.1)!important;border-radius:0!important;box-shadow:none!important;margin:0!important}";
  importCSS(settingsPanelCss);
  const SETTINGS = [
    { label: i18n.scrollMode, key: "scrollMode" },
    { label: i18n.autoEnter, key: "autoEnterSinglePage" },
    { label: i18n.clickToEnter, key: "clickToEnterReader" },
    { label: i18n.doublePageMode, key: "doublePageMode" }
  ];
  function createSettingsPanel(anchorElement) {
    const backdrop = document.createElement("div");
    backdrop.className = "settings-backdrop";
    backdrop.setAttribute("aria-hidden", "true");
    backdrop.inert = true;
    const bottomSheet = document.createElement("div");
    bottomSheet.className = "settings-bottom-sheet";
    bottomSheet.setAttribute("role", "dialog");
    bottomSheet.setAttribute("aria-modal", "true");
    bottomSheet.setAttribute("aria-labelledby", "hr-settings-title");
    const sheetHeader = document.createElement("div");
    sheetHeader.className = "settings-sheet-header";
    const title = document.createElement("h2");
    title.className = "settings-title";
    title.id = "hr-settings-title";
    title.textContent = i18n.settings;
    const closeBtn = document.createElement("button");
    closeBtn.type = "button";
    closeBtn.className = "settings-close-btn";
    closeBtn.setAttribute("aria-label", i18n.close);
    closeBtn.innerHTML = svgClose.replace('class="pswp__icn"', "");
    closeBtn.onclick = (e) => {
      e.stopPropagation();
      hide();
    };
    sheetHeader.appendChild(title);
    sheetHeader.appendChild(closeBtn);
    bottomSheet.appendChild(sheetHeader);
    SETTINGS.forEach(({ label, key }) => {
      var _a, _b;
      if (key === "scrollMode" && ((_b = (_a = store.activeAdapter) == null ? void 0 : _a.scrollPolicy) == null ? void 0 : _b.configurable) === false) {
        return;
      }
      const item = document.createElement("div");
      item.className = "settings-item";
      const labelEl = document.createElement("span");
      labelEl.className = "settings-label";
      labelEl.textContent = label;
      const toggle = document.createElement("button");
      toggle.type = "button";
      toggle.className = `toggle-switch${store.settings[key] ? " on" : ""}`;
      toggle.setAttribute("role", "switch");
      toggle.setAttribute("aria-label", label);
      toggle.setAttribute("aria-checked", String(store.settings[key]));
      const slider = document.createElement("div");
      slider.className = "toggle-slider";
      toggle.appendChild(slider);
      toggle.onclick = (e) => {
        e.stopPropagation();
        const newValue = !store.settings[key];
        store.updateSetting(key, newValue);
        toggle.classList.toggle("on", newValue);
        toggle.setAttribute("aria-checked", String(newValue));
        if (key === "scrollMode") {
          window.location.reload();
        }
      };
      item.appendChild(labelEl);
      item.appendChild(toggle);
      bottomSheet.appendChild(item);
    });
    const intervalItem = document.createElement("div");
    intervalItem.className = "settings-item";
    const intervalLabel = document.createElement("label");
    intervalLabel.className = "settings-label";
    intervalLabel.textContent = i18n.playSpeed;
    const intervalRight = document.createElement("div");
    intervalRight.className = "stepper-control";
    const minusBtn = document.createElement("button");
    minusBtn.type = "button";
    minusBtn.className = "stepper-btn";
    minusBtn.textContent = "−";
    minusBtn.setAttribute("aria-label", `${i18n.playSpeed}: -1`);
    const intervalInput = document.createElement("input");
    intervalInput.type = "number";
    intervalInput.id = "hr-autoplay-interval";
    intervalInput.name = "hr-autoplay-interval";
    intervalLabel.htmlFor = intervalInput.id;
    intervalInput.className = "interval-input";
    intervalInput.min = "1";
    intervalInput.max = "60";
    intervalInput.step = "1";
    intervalInput.value = String(store.settings.autoPlayInterval / 1e3);
    intervalInput.onclick = (e) => e.stopPropagation();
    const plusBtn = document.createElement("button");
    plusBtn.type = "button";
    plusBtn.className = "stepper-btn";
    plusBtn.textContent = "+";
    plusBtn.setAttribute("aria-label", `${i18n.playSpeed}: +1`);
    const updateInterval = (val) => {
      if (!isNaN(val) && val >= 1 && val <= 60) {
        intervalInput.value = String(val);
        store.updateSetting("autoPlayInterval", val * 1e3);
      }
    };
    intervalInput.onchange = (e) => {
      updateInterval(parseFloat(e.target.value));
    };
    minusBtn.onclick = (e) => {
      e.stopPropagation();
      let current = parseFloat(intervalInput.value);
      if (isNaN(current)) current = 5;
      updateInterval(Math.max(1, current - 1));
    };
    plusBtn.onclick = (e) => {
      e.stopPropagation();
      let current = parseFloat(intervalInput.value);
      if (isNaN(current)) current = 5;
      updateInterval(Math.min(60, current + 1));
    };
    intervalRight.appendChild(minusBtn);
    intervalRight.appendChild(intervalInput);
    intervalRight.appendChild(plusBtn);
    intervalItem.appendChild(intervalLabel);
    intervalItem.appendChild(intervalRight);
    bottomSheet.appendChild(intervalItem);
    const posItem = document.createElement("div");
    posItem.className = "settings-item";
    const posLabel = document.createElement("span");
    posLabel.className = "settings-label";
    posLabel.textContent = i18n.thumbnailPosition;
    const segmentControl = document.createElement("div");
    segmentControl.className = "segment-control";
    const posOptions = [
      { value: "top", label: i18n.posTop },
      { value: "bottom", label: i18n.posBottom },
      { value: "left", label: i18n.posLeft },
      { value: "right", label: i18n.posRight }
    ];
    const segmentItems = [];
    posOptions.forEach((opt) => {
      const item = document.createElement("button");
      item.type = "button";
      item.className = `segment-item${store.settings.thumbnailPosition === opt.value ? " active" : ""}`;
      item.textContent = opt.label;
      item.setAttribute("aria-pressed", String(store.settings.thumbnailPosition === opt.value));
      item.onclick = (e) => {
        e.stopPropagation();
        if (store.settings.thumbnailPosition === opt.value) return;
        store.updateSetting("thumbnailPosition", opt.value);
        segmentItems.forEach((el) => {
          el.classList.remove("active");
          el.setAttribute("aria-pressed", "false");
        });
        item.classList.add("active");
        item.setAttribute("aria-pressed", "true");
      };
      segmentItems.push(item);
      segmentControl.appendChild(item);
    });
    posItem.appendChild(posLabel);
    posItem.appendChild(segmentControl);
    bottomSheet.appendChild(posItem);
    backdrop.appendChild(bottomSheet);
    const show = () => {
      if (window.matchMedia("(hover: hover) and (pointer: fine)").matches && !document.documentElement.classList.contains("hr-mobile")) {
        const panelWidth = 340;
        const rect = anchorElement.getBoundingClientRect();
        const panelRect = bottomSheet.getBoundingClientRect();
        const panelHeight = panelRect.height || 300;
        let top = rect.top + rect.height / 2 - panelHeight / 2;
        top = Math.max(16, Math.min(window.innerHeight - panelHeight - 16, top));
        bottomSheet.style.top = `${top}px`;
        bottomSheet.style.bottom = "auto";
        if (rect.left < window.innerWidth / 2) {
          bottomSheet.style.left = `${rect.right + 20}px`;
          bottomSheet.style.transformOrigin = "left center";
        } else {
          bottomSheet.style.left = `${rect.left - panelWidth - 20}px`;
          bottomSheet.style.transformOrigin = "right center";
        }
        void bottomSheet.offsetHeight;
      } else {
        bottomSheet.style.cssText = "";
      }
      backdrop.classList.add("show");
      backdrop.inert = false;
      backdrop.setAttribute("aria-hidden", "false");
      closeBtn.focus({ preventScroll: true });
    };
    const hide = () => {
      backdrop.classList.remove("show");
      backdrop.setAttribute("aria-hidden", "true");
      backdrop.inert = true;
      if (document.contains(anchorElement)) anchorElement.focus({ preventScroll: true });
    };
    backdrop.onclick = (e) => {
      if (e.target === backdrop) {
        hide();
      }
    };
    backdrop.addEventListener("keydown", (event) => {
      if (event.key === "Escape") {
        event.preventDefault();
        hide();
      }
    });
    const isOpen = () => backdrop.classList.contains("show");
    return {
      getContainerElement: () => backdrop,
      show,
      hide,
      isOpen
    };
  }
  function createFloatControl(readerHandle) {
    const floatControl = document.createElement("div");
    floatControl.className = "bookmark-control";
    let savedPos = { side: "right", topPercent: 50 };
    try {
      const raw = localStorage.getItem("hentai-reader-bookmark-pos");
      if (raw) savedPos = JSON.parse(raw);
    } catch (e) {
    }
    if (savedPos.side === "left") {
      floatControl.classList.add("left-side");
    } else {
      floatControl.classList.add("right-side");
    }
    floatControl.style.top = `${savedPos.topPercent}%`;
    const topBtn = document.createElement("button");
    topBtn.type = "button";
    topBtn.className = "bm-btn bm-top-btn";
    const updateTopBtn = () => {
      if (readerHandle.isActive()) {
        topBtn.innerHTML = store.autoPlay ? svgPause : svgPlay;
        topBtn.title = store.autoPlay ? i18n.pause : i18n.play;
      } else {
        topBtn.innerHTML = svgTop;
        topBtn.title = i18n.backToTop;
      }
      topBtn.setAttribute("aria-label", topBtn.title);
    };
    updateTopBtn();
    topBtn.onclick = (e) => {
      e.stopPropagation();
      if (readerHandle.isActive()) {
        store.autoPlay = !store.autoPlay;
        store.emit("settingsChanged");
      } else {
        const firstImg = document.querySelector(".r-img, .r-ph");
        if (firstImg) {
          firstImg.scrollIntoView({ behavior: "smooth", block: "start" });
        } else {
          window.scrollTo({ top: 0, behavior: "smooth" });
        }
      }
    };
    const modeBtn = document.createElement("button");
    modeBtn.type = "button";
    modeBtn.className = "bm-btn bm-mode-btn";
    modeBtn.innerHTML = svgReader;
    modeBtn.title = i18n.readerMode;
    modeBtn.setAttribute("aria-label", i18n.readerMode);
    modeBtn.onclick = (e) => {
      e.stopPropagation();
      if (readerHandle.isActive()) {
        readerHandle.close();
      } else {
        readerHandle.open();
      }
    };
    const unsubscribeReaderMode = store.on("readerModeChanged", () => {
      modeBtn.innerHTML = readerHandle.isActive() ? svgScroll : svgReader;
      updateTopBtn();
    });
    const unsubscribeSettings = store.on("settingsChanged", () => {
      updateTopBtn();
    });
    const settingsBtn = document.createElement("button");
    settingsBtn.type = "button";
    settingsBtn.className = "bm-btn bm-settings-btn";
    settingsBtn.innerHTML = svgSettings;
    settingsBtn.title = i18n.settings;
    settingsBtn.setAttribute("aria-label", i18n.settings);
    const settings = createSettingsPanel(settingsBtn);
    document.body.appendChild(settings.getContainerElement());
    settingsBtn.onclick = (e) => {
      e.stopPropagation();
      if (settings.isOpen()) {
        settings.hide();
      } else {
        settings.show();
      }
    };
    floatControl.appendChild(topBtn);
    floatControl.appendChild(modeBtn);
    floatControl.appendChild(settingsBtn);
    document.body.appendChild(floatControl);
    let isDragging = false;
    let activePointerId = null;
    let startY = 0;
    let startTop = 0;
    let dragMoved = false;
    function endDrag(commit, e) {
      if (!isDragging) return;
      isDragging = false;
      floatControl.classList.remove("dragging");
      if (activePointerId !== null) {
        try {
          floatControl.releasePointerCapture(activePointerId);
        } catch (err) {
        }
        activePointerId = null;
      }
      if (commit && dragMoved) {
        const rect = floatControl.getBoundingClientRect();
        const centerPercent = (rect.top + rect.height / 2) / window.innerHeight * 100;
        const centerX = e ? e.clientX : rect.left + rect.width / 2;
        const side = centerX < window.innerWidth / 2 ? "left" : "right";
        floatControl.style.top = `${centerPercent}%`;
        localStorage.setItem("hentai-reader-bookmark-pos", JSON.stringify({ side, topPercent: centerPercent }));
      }
    }
    floatControl.addEventListener("pointerdown", (e) => {
      if (e.button !== 0 && e.pointerType === "mouse") return;
      isDragging = true;
      dragMoved = false;
      activePointerId = e.pointerId;
      startY = e.clientY;
      const rect = floatControl.getBoundingClientRect();
      startTop = rect.top + rect.height / 2;
      floatControl.classList.add("dragging");
    });
    floatControl.addEventListener("pointermove", (e) => {
      if (!isDragging || e.pointerId !== activePointerId) return;
      const deltaY = e.clientY - startY;
      if (Math.abs(deltaY) > 5) {
        if (!dragMoved) {
          try {
            floatControl.setPointerCapture(e.pointerId);
          } catch (err) {
          }
          if (settings.isOpen()) settings.hide();
        }
        dragMoved = true;
      }
      const newCenterY = startTop + deltaY;
      const maxCenterY = window.innerHeight - floatControl.offsetHeight / 2;
      const minCenterY = floatControl.offsetHeight / 2;
      const clampedY = Math.max(minCenterY, Math.min(maxCenterY, newCenterY));
      floatControl.style.top = `${clampedY}px`;
      if (e.clientX < window.innerWidth / 2) {
        floatControl.classList.remove("right-side");
        floatControl.classList.add("left-side");
      } else {
        floatControl.classList.remove("left-side");
        floatControl.classList.add("right-side");
      }
    });
    floatControl.addEventListener("pointerup", (e) => {
      if (e.pointerId !== activePointerId) return;
      endDrag(true, e);
    });
    floatControl.addEventListener("pointercancel", (e) => {
      if (e.pointerId !== activePointerId) return;
      endDrag(false, e);
    });
    floatControl.addEventListener("click", (e) => {
      if (dragMoved) {
        e.stopPropagation();
        e.preventDefault();
      }
    }, { capture: true });
    return () => {
      unsubscribeReaderMode();
      unsubscribeSettings();
      settings.getContainerElement().remove();
      floatControl.remove();
    };
  }
  const isMobileBrowser = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  function initViewportScale() {
    let orientationTimer = null;
    const updateScale = () => {
      if (isMobileBrowser) {
        const screenWidth = Math.min(window.screen.width, window.screen.height);
        const layoutWidth = window.innerWidth;
        if (screenWidth < 768) {
          document.documentElement.classList.add("hr-mobile");
        } else {
          document.documentElement.classList.remove("hr-mobile");
        }
        let scale = 1;
        if (layoutWidth > screenWidth && screenWidth > 0) {
          scale = layoutWidth / screenWidth;
        }
        document.documentElement.style.setProperty("--hr-scale", scale.toString());
      } else {
        document.documentElement.classList.remove("hr-mobile");
        document.documentElement.style.setProperty("--hr-scale", "1");
      }
    };
    updateScale();
    window.addEventListener("resize", updateScale);
    const onOrientationChange = () => {
      if (orientationTimer) clearTimeout(orientationTimer);
      orientationTimer = setTimeout(updateScale, 100);
    };
    window.addEventListener("orientationchange", onOrientationChange);
    return () => {
      if (orientationTimer) clearTimeout(orientationTimer);
      window.removeEventListener("resize", updateScale);
      window.removeEventListener("orientationchange", onOrientationChange);
      document.documentElement.classList.remove("hr-mobile");
      document.documentElement.style.removeProperty("--hr-scale");
    };
  }
  (async function main() {
    var _a, _b, _c;
    const adapter = SiteManager.getAdapter(window.location.href);
    if (!adapter) {
      return;
    }
    const rollbacks = [];
    try {
      store.activeAdapter = adapter;
      const pageLoader = new GalleryPageLoader(adapter);
      store.reloadSettings();
      const initialPage = await pageLoader.loadInitialPage(document, window.location.href);
      if (initialPage.items.length === 0) return;
      rollbacks.push(initViewportScale());
      store.imageOffset = ((_a = initialPage.position) == null ? void 0 : _a.startIndex) ?? 0;
      store.perPage = ((_b = initialPage.position) == null ? void 0 : _b.pageSize) ?? initialPage.items.length ?? 20;
      store.nextUrl = initialPage.nextUrl;
      store.prevUrl = initialPage.prevUrl;
      if (store.imageOffset > 0 && store.perPage > 0) {
        store.currPage = Math.floor(store.imageOffset / store.perPage) + 1;
      }
      let container = adapter.getContainer();
      const originalContainerHtml = container == null ? void 0 : container.innerHTML;
      if (store.settings.scrollMode) {
        document.documentElement.classList.add("scroll-mode");
        rollbacks.push(() => document.documentElement.classList.remove("scroll-mode"));
        const restoreOriginal = (_c = adapter.hideOriginalElements) == null ? void 0 : _c.call(adapter);
        if (restoreOriginal) rollbacks.push(restoreOriginal);
        if (!container) {
          container = document.createElement("div");
          container.id = "gdt";
          document.body.appendChild(container);
          rollbacks.push(() => container == null ? void 0 : container.remove());
        } else {
          rollbacks.push(() => {
            if (container && originalContainerHtml !== void 0) container.innerHTML = originalContainerHtml;
          });
        }
        container.innerHTML = "";
        processBatch(initialPage.items, store.currPage, container, false, initialPage.pageUrl);
        rollbacks.push(setupAutoScroll(pageLoader));
      } else {
        const hiddenBox = document.createElement("div");
        hiddenBox.id = "gdt-hidden";
        hiddenBox.style.display = "none";
        document.body.appendChild(hiddenBox);
        rollbacks.push(() => hiddenBox.remove());
        processBatch(initialPage.items, store.currPage, hiddenBox, false, initialPage.pageUrl);
      }
      const readerHandle = createReader(pageLoader);
      rollbacks.push(createFloatControl(readerHandle));
      if (!store.settings.scrollMode) {
        setTimeout(() => {
          if (!readerHandle.isActive()) readerHandle.warmupInitial(3);
        }, 1500);
      }
      if (store.settings.autoEnterSinglePage) {
        setTimeout(() => readerHandle.open(0), 1e3);
      }
      document.body.addEventListener("click", (e) => {
        if (!store.settings.clickToEnterReader || !store.settings.scrollMode || readerHandle.isActive()) return;
        const target = e.target;
        const imgTarget = target.closest(".r-img, .r-ph");
        if (imgTarget) {
          e.preventDefault();
          e.stopPropagation();
          const allImages = Array.from(document.querySelectorAll(".r-img, .r-ph"));
          const index = allImages.indexOf(imgTarget);
          if (index !== -1) {
            readerHandle.open(index);
          }
        }
      }, true);
    } catch (error) {
      for (const rollback of rollbacks.reverse()) {
        try {
          rollback();
        } catch {
        }
      }
      store.galleryItems = [];
      store.activeAdapter = null;
      console.error("[Hentai-Reader] Initialization failed and was rolled back", error);
    }
  })();

})();