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).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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);
    }
  })();

})();