UNPKG

@triabin/vue-book-reader

Version:

Forked from jinhuan138/vue-book-reader, add some features

784 lines (783 loc) 29.8 kB
const A = (a) => new Promise((t) => setTimeout(t, a)), z = (a, t, e) => { let i; return (...s) => { const n = () => { i = null, a(...s); }; i && clearTimeout(i), i = setTimeout(n, t); }; }, L = (a, t, e) => e * (t - a) + a, B = (a) => 1 - (1 - a) * (1 - a), I = (a, t, e, i, s) => new Promise((n) => { let o; const r = (h) => { o ??= h; const l = Math.min(1, (h - o) / e); s(L(a, t, i(l))), l < 1 ? requestAnimationFrame(r) : n(); }; requestAnimationFrame(r); }), V = (a) => { if (!(a != null && a.collapsed)) return a; const { endOffset: t, endContainer: e } = a; if (e.nodeType === 1) { const i = e.childNodes[t]; return (i == null ? void 0 : i.nodeType) === 1 ? i : e; } if (t + 1 < e.length) a.setEnd(e, t + 1); else if (t > 1) a.setStart(e, t - 1); else return e.parentNode; return a; }, b = (a, t, e, i = e) => { const s = a.createRange(); return s.setStart(t, e), s.setEnd(t, i), s; }, C = (a, t, e, i = 0, s = t.nodeValue.length) => { if (s - i === 1) return e(b(a, t, i), b(a, t, s)) < 0 ? i : s; const n = Math.floor(i + (s - i) / 2), o = e(b(a, t, i, n), b(a, t, n, s)); return o < 0 ? C(a, t, e, i, n) : o > 0 ? C(a, t, e, n, s) : n; }, { SHOW_ELEMENT: N, SHOW_TEXT: O, SHOW_CDATA_SECTION: F, FILTER_ACCEPT: T, FILTER_REJECT: k, FILTER_SKIP: M } = NodeFilter, D = N | O | F, E = (a) => { let t = 1 / 0, e = -1 / 0, i = 1 / 0, s = -1 / 0; for (const n of a.getClientRects()) i = Math.min(i, n.left), t = Math.min(t, n.top), e = Math.max(e, n.right), s = Math.max(s, n.bottom); return new DOMRect(i, t, e - i, s - t); }, P = (a, t, e, i) => { const s = (d) => { var m, g; const u = (m = d.localName) == null ? void 0 : m.toLowerCase(); if (u === "script" || u === "style") return k; if (d.nodeType === 1) { const { left: f, right: y } = i(d.getBoundingClientRect()); if (y < t || f > e) return k; if (f >= t && y <= e) return T; } else { if (!((g = d.nodeValue) != null && g.trim())) return M; const f = a.createRange(); f.selectNodeContents(d); const { left: y, right: w } = i(f.getBoundingClientRect()); if (w >= t && y <= e) return T; } return M; }, n = a.createTreeWalker(a.body, D, { acceptNode: s }), o = []; for (let d = n.nextNode(); d; d = n.nextNode()) o.push(d); const r = o[0] ?? a.body, h = o[o.length - 1] ?? r, l = r.nodeType === 1 ? 0 : C(a, r, (d, u) => { const m = i(E(d)), g = i(E(u)); return m.right < t && g.left > t ? 0 : g.left > t ? -1 : 1; }), c = h.nodeType === 1 ? 0 : C(a, h, (d, u) => { const m = i(E(d)), g = i(E(u)); return m.right < e && g.left > e ? 0 : g.left > e ? -1 : 1; }), p = a.createRange(); return p.setStart(r, l), p.setEnd(h, c), p; }, R = (a) => { const t = document.createRange(); return t.setStart(a.anchorNode, a.anchorOffset), t.setEnd(a.focusNode, a.focusOffset), t.collapsed; }, S = (a, t) => { let e; if (a.startContainer ? e = a.cloneRange() : a.nodeType && (e = document.createRange(), e.selectNode(a)), e) { const i = e.startContainer.ownerDocument.defaultView.getSelection(); i.removeAllRanges(), t === -1 ? e.collapse(!0) : t === 1 && e.collapse(), i.addRange(e); } }, j = (a) => { const { defaultView: t } = a, { writingMode: e, direction: i } = t.getComputedStyle(a.body), s = e === "vertical-rl" || e === "vertical-lr", n = a.body.dir === "rtl" || i === "rtl" || a.documentElement.dir === "rtl"; return { vertical: s, rtl: n }; }, W = (a) => { const t = a.defaultView.getComputedStyle(a.body); return t.backgroundColor === "rgba(0, 0, 0, 0)" && t.backgroundImage === "none" ? a.defaultView.getComputedStyle(a.documentElement).background : t.background; }, $ = (a, t) => Array.from({ length: a }, () => { const e = document.createElement("div"), i = document.createElement("div"); return e.append(i), i.setAttribute("part", t), e; }), v = (a, t) => { const { style: e } = a; for (const [i, s] of Object.entries(t)) e.setProperty(i, s, "important"); }; class q { #a = new ResizeObserver(() => this.expand()); #o = document.createElement("div"); #i = document.createElement("iframe"); #h = document.createRange(); #e; #s = !1; #c = !1; #t = !0; #n; #r = {}; constructor({ container: t, onExpand: e }) { this.container = t, this.onExpand = e, this.#i.setAttribute("part", "filter"), this.#o.append(this.#i), Object.assign(this.#o.style, { boxSizing: "content-box", position: "relative", overflow: "hidden", flex: "0 0 auto", width: "100%", height: "100%", display: "flex", justifyContent: "center", alignItems: "center" }), Object.assign(this.#i.style, { overflow: "hidden", border: "0", display: "none", width: "100%", height: "100%" }), this.#i.setAttribute("sandbox", "allow-same-origin allow-scripts"), this.#i.setAttribute("scrolling", "no"); } get element() { return this.#o; } get document() { return this.#i.contentDocument; } async load(t, e, i) { if (typeof t != "string") throw new Error(`${t} is not string`); return new Promise((s) => { this.#i.addEventListener("load", () => { const n = this.document; e == null || e(n), this.#i.style.display = "block"; const { vertical: o, rtl: r } = j(n); this.docBackground = W(n), n.body.style.background = "none"; const h = this.docBackground; this.#i.style.display = "none", this.#s = o, this.#c = r, this.#h.selectNodeContents(n.body); const l = i == null ? void 0 : i({ vertical: o, rtl: r, background: h }); this.#i.style.display = "block", this.render(l), this.#a.observe(n.body), n.fonts.ready.then(() => this.expand()), s(); }, { once: !0 }), this.#i.src = t; }); } render(t) { !t || !this.document || (this.#t = t.flow !== "scrolled", this.#r = t, this.#t ? this.columnize(t) : this.scrolled(t)); } scrolled({ margin: t, gap: e, columnWidth: i }) { const s = this.#s, n = this.document; v(n.documentElement, { "box-sizing": "border-box", padding: s ? `${t * 1.5}px ${e}px` : `0 ${e}px`, "column-width": "auto", height: "auto", width: "auto" }), v(n.body, { [s ? "max-height" : "max-width"]: `${i}px`, margin: "auto" }), this.setImageSize(), this.expand(); } columnize({ width: t, height: e, margin: i, gap: s, columnWidth: n }) { const o = this.#s; this.#n = o ? e : t; const r = this.document; v(r.documentElement, { "box-sizing": "border-box", "column-width": `${Math.trunc(n)}px`, "column-gap": o ? `${i}px` : `${s}px`, "column-fill": "auto", ...o ? { width: `${t}px` } : { height: `${e}px` }, padding: o ? `${i / 2}px ${s}px` : `0 ${s / 2}px`, overflow: "hidden", // force wrap long words "overflow-wrap": "break-word", // reset some potentially problematic props position: "static", border: "0", margin: "0", "max-height": "none", "max-width": "none", "min-height": "none", "min-width": "none", // fix glyph clipping in WebKit "-webkit-line-box-contain": "block glyphs replaced" }), v(r.body, { "max-height": "none", "max-width": "none", margin: "0" }), this.setImageSize(), this.expand(); } setImageSize() { const { width: t, height: e, margin: i } = this.#r, s = this.#s, n = this.document; for (const o of n.body.querySelectorAll("img, svg, video")) { const { maxHeight: r, maxWidth: h } = n.defaultView.getComputedStyle(o); v(o, { "max-height": s ? r !== "none" && r !== "0px" ? r : "100%" : `${e - i * 2}px`, "max-width": s ? `${t - i * 2}px` : h !== "none" && h !== "0px" ? h : "100%", "object-fit": "contain", "page-break-inside": "avoid", "break-inside": "avoid", "box-sizing": "border-box" }); } } expand() { const { documentElement: t } = this.document; if (this.#t) { const e = this.#s ? "height" : "width", i = this.#s ? "width" : "height", s = this.#h.getBoundingClientRect(), n = t.getBoundingClientRect(), r = (this.#s ? 0 : this.#c ? n.right - s.right : s.left - n.left) + s[e], l = Math.ceil(r / this.#n) * this.#n; this.#o.style.padding = "0", this.#i.style[e] = `${l}px`, this.#o.style[e] = `${l + this.#n * 2}px`, this.#i.style[i] = "100%", this.#o.style[i] = "100%", t.style[e] = `${this.#n}px`, this.#e && (this.#e.element.style.margin = "0", this.#e.element.style.left = this.#s ? "0" : `${this.#n}px`, this.#e.element.style.top = this.#s ? `${this.#n}px` : "0", this.#e.element.style[e] = `${l}px`, this.#e.redraw()); } else { const e = this.#s ? "width" : "height", i = this.#s ? "height" : "width", n = t.getBoundingClientRect()[e], { margin: o, gap: r } = this.#r, h = this.#s ? `0 ${r}px` : `${o}px 0`; this.#o.style.padding = h, this.#i.style[e] = `${n}px`, this.#o.style[e] = `${n}px`, this.#i.style[i] = "100%", this.#o.style[i] = "100%", this.#e && (this.#e.element.style.margin = h, this.#e.element.style.left = "0", this.#e.element.style.top = "0", this.#e.element.style[e] = `${n}px`, this.#e.redraw()); } this.onExpand(); } set overlayer(t) { this.#e = t, this.#o.append(t.element); } get overlayer() { return this.#e; } destroy() { this.document && this.#a.unobserve(this.document.body); } } class H extends HTMLElement { static observedAttributes = [ "flow", "gap", "margin", "max-inline-size", "max-block-size", "max-column-count" ]; #a = this.attachShadow({ mode: "closed" }); #o = new ResizeObserver(() => this.render()); #i; #h; #e; #s; #c; #t; #n = !1; #r = !1; #m = 0; #d = -1; #l = 0; // anchor view to a fraction (0-1), Range, or Element #E = !1; #x = !1; // while true, prevent any further navigation #T; #k = /* @__PURE__ */ new WeakMap(); #M = matchMedia("(prefers-color-scheme: dark)"); #S; #g; #v; #C; #P; constructor() { super(), this.#a.innerHTML = `<style> :host { display: block; container-type: size; } :host, #top { box-sizing: border-box; position: relative; overflow: hidden; width: 100%; height: 100%; } #top { --_gap: 7%; --_margin: 48px; --_max-inline-size: 100%; --_max-block-size: 100%; --_max-column-count: 2; --_max-column-count-portrait: 1; --_max-column-count-spread: var(--_max-column-count); --_half-gap: calc(var(--_gap) / 2); --_max-width: calc(var(--_max-inline-size) * var(--_max-column-count-spread)); --_max-height: var(--_max-block-size); display: grid; grid-template-columns: minmax(var(--_half-gap), 1fr) var(--_half-gap) minmax(0, calc(var(--_max-width) - var(--_gap))) var(--_half-gap) minmax(var(--_half-gap), 1fr); grid-template-rows: minmax(var(--_margin), 1fr) minmax(0, var(--_max-height)) minmax(var(--_margin), 1fr); &.vertical { --_max-column-count-spread: var(--_max-column-count-portrait); --_max-width: var(--_max-block-size); --_max-height: calc(var(--_max-inline-size) * var(--_max-column-count-spread)); } @container (orientation: portrait) { & { --_max-column-count-spread: var(--_max-column-count-portrait); } &.vertical { --_max-column-count-spread: var(--_max-column-count); } } } #background { grid-column: 1 / -1; grid-row: 1 / -1; } #container { grid-column: 2 / 5; grid-row: 2; overflow: hidden; } :host([flow="scrolled"]) #container { grid-column: 1 / -1; grid-row: 1 / -1; overflow: auto; } #header { grid-column: 3 / 4; grid-row: 1; } #footer { grid-column: 3 / 4; grid-row: 3; align-self: end; } #header, #footer { display: grid; height: var(--_margin); } :is(#header, #footer) > * { display: flex; align-items: center; min-width: 0; } :is(#header, #footer) > * > * { width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: center; font-size: .75em; opacity: .6; } </style> <div id="top"> <div id="background" part="filter"></div> <div id="header"></div> <div id="container" part="container"></div> <div id="footer"></div> </div> `, this.#i = this.#a.getElementById("top"), this.#h = this.#a.getElementById("background"), this.#e = this.#a.getElementById("container"), this.#s = this.#a.getElementById("header"), this.#c = this.#a.getElementById("footer"), this.#o.observe(this.#e), this.#e.addEventListener("scroll", () => this.dispatchEvent(new Event("scroll"))), this.#e.addEventListener("scroll", z(() => { this.scrolled && (this.#E ? this.#E = !1 : this.#b("scroll")); }, 250)); const t = { passive: !1 }; this.addEventListener("touchstart", this.#$.bind(this), t), this.addEventListener("touchmove", this.#A.bind(this), t), this.addEventListener("touchend", this.#L.bind(this)), this.addEventListener("load", ({ detail: { doc: i } }) => { i.addEventListener("touchstart", this.#$.bind(this), t), i.addEventListener("touchmove", this.#A.bind(this), t), i.addEventListener("touchend", this.#L.bind(this)); }), this.addEventListener("relocate", ({ detail: i }) => { i.reason === "selection" ? S(this.#l, 0) : i.reason === "navigation" && (this.#l === 1 ? S(i.range, 1) : typeof this.#l == "number" ? S(i.range, -1) : S(this.#l, -1)); }); const e = z((i, s) => { if (!s.rangeCount) return; const n = s.getRangeAt(0), o = R(s); o && n.compareBoundaryPoints(Range.START_TO_START, i) < 0 ? this.prev() : !o && n.compareBoundaryPoints(Range.END_TO_END, i) > 0 && this.next(); }, 700); this.addEventListener("load", ({ detail: { doc: i } }) => { let s = !1; i.addEventListener("pointerdown", () => s = !0), i.addEventListener("pointerup", () => s = !1); let n = !1; i.addEventListener("keydown", () => n = !0), i.addEventListener("keyup", () => n = !1), i.addEventListener("selectionchange", () => { if (this.scrolled) return; const o = this.#P; if (!o) return; const r = i.getSelection(); if (r.rangeCount) { if (s && r.type === "Range") e(o, r); else if (n) { const h = r.getRangeAt(0).cloneRange(); R(r) || h.collapse(), this.#y(h); } } }), i.addEventListener("focusin", (o) => this.scrolled ? null : ( // NOTE: `requestAnimationFrame` is needed in WebKit requestAnimationFrame(() => this.#y(o.target)) )); }), this.#S = () => { this.#t && this.#_(this.#t.docBackground, this.columnCount); }, this.#M.addEventListener("change", this.#S); } attributeChangedCallback(t, e, i) { switch (t) { case "flow": this.render(); break; case "gap": case "margin": case "max-block-size": case "max-column-count": this.#i.style.setProperty("--_" + t, i), this.render(); break; case "max-inline-size": this.#i.style.setProperty("--_" + t, i), this.render(); break; } } open(t) { var e; this.bookDir = t.dir, this.sections = t.sections, (e = t.transformTarget) == null || e.addEventListener("data", ({ detail: i }) => { if (i.type !== "text/css") return; const s = innerWidth, n = innerHeight; i.data = Promise.resolve(i.data).then((o) => o.replace(new RegExp("(?<=[{\\s;])-epub-", "gi"), "").replace(/(\d*\.?\d+)vw/gi, (r, h) => parseFloat(h) * s / 100 + "px").replace(/(\d*\.?\d+)vh/gi, (r, h) => parseFloat(h) * n / 100 + "px").replace(/page-break-(after|before|inside)\s*:/gi, (r, h) => `-webkit-column-break-${h}:`).replace(/break-(after|before|inside)\s*:\s*(avoid-)?page/gi, (r, h, l) => `break-${h}: ${l ?? ""}column`)); }); } #N() { return this.#t && (this.#t.destroy(), this.#e.removeChild(this.#t.element)), this.#t = new q({ container: this, onExpand: () => this.#y(this.#l) }), this.#e.append(this.#t.element), this.#t; } #_(t, e) { var o; const i = (o = this.#t) == null ? void 0 : o.document; if (!i) return; const n = i.defaultView.getComputedStyle(i.documentElement).getPropertyValue("--theme-bg-color"); if (t && n) { const r = t.split(/\s(?=(?:url|rgb|hsl|#[0-9a-fA-F]{3,6}))/); r[0] = n, t = r.join(" "); } /cover.*fixed|fixed.*cover/.test(t) && (t = t.replace("cover", "auto 100%").replace("fixed", "")), this.#h.innerHTML = "", this.#h.style.display = "grid", this.#h.style.gridTemplateColumns = `repeat(${e}, 1fr)`; for (let r = 0; r < e; r++) { const h = document.createElement("div"); h.style.background = t, h.style.width = "100%", h.style.height = "100%", this.#h.appendChild(h); } } #R({ vertical: t, rtl: e, background: i }) { this.#n = t, this.#r = e, this.#i.classList.toggle("vertical", t); const { width: s, height: n } = this.#e.getBoundingClientRect(), o = t ? n : s, r = getComputedStyle(this.#i), h = parseFloat(r.getPropertyValue("--_max-inline-size")), l = parseInt(r.getPropertyValue("--_max-column-count-spread")), c = parseFloat(r.getPropertyValue("--_margin")); this.#m = c; const p = parseFloat(r.getPropertyValue("--_gap")) / 100, d = -p / (p - 1) * o, u = this.getAttribute("flow"); if (u === "scrolled") { this.setAttribute("dir", t ? "rtl" : "ltr"), this.#i.style.padding = "0"; const x = h; return this.heads = null, this.feet = null, this.#s.replaceChildren(), this.#c.replaceChildren(), { flow: u, margin: c, gap: d, columnWidth: x }; } const m = Math.min(l, Math.ceil(o / h)), g = t ? o / m - c : o / m - d; this.setAttribute("dir", e ? "rtl" : "ltr"), this.columnCount = m, this.#_(i, this.columnCount); const f = t ? Math.min(2, Math.ceil(s / h)) : m, y = { gridTemplateColumns: `repeat(${f}, 1fr)`, gap: `${d}px`, direction: this.bookDir === "rtl" ? "rtl" : "ltr" }; Object.assign(this.#s.style, y), Object.assign(this.#c.style, y); const w = $(f, "head"), _ = $(f, "foot"); return this.heads = w.map((x) => x.children[0]), this.feet = _.map((x) => x.children[0]), this.#s.replaceChildren(...w), this.#c.replaceChildren(..._), { height: n, width: s, margin: c, gap: d, columnWidth: g }; } render() { this.#t && (this.#t.render(this.#R({ vertical: this.#n, rtl: this.#r })), this.#y(this.#l)); } get scrolled() { return this.getAttribute("flow") === "scrolled"; } get scrollProp() { const { scrolled: t } = this; return this.#n ? t ? "scrollLeft" : "scrollTop" : t ? "scrollTop" : "scrollLeft"; } get sideProp() { const { scrolled: t } = this; return this.#n ? t ? "width" : "height" : t ? "height" : "width"; } get size() { return this.#e.getBoundingClientRect()[this.sideProp]; } get viewSize() { return this.#t.element.getBoundingClientRect()[this.sideProp]; } get start() { return Math.abs(this.#e[this.scrollProp]); } get end() { return this.start + this.size; } get page() { return Math.floor((this.start + this.end) / 2 / this.size); } get pages() { return Math.round(this.viewSize / this.size); } // this is the current position of the container get containerPosition() { return this.#e[this.scrollProp]; } // this is the new position of the containr set containerPosition(t) { this.#e[this.scrollProp] = t; } scrollBy(t, e) { const i = this.#n ? e : t, [s, n, o] = this.#g, r = this.#r, h = r ? s - o : s - n, l = r ? s + n : s + o; this.containerPosition = Math.max(h, Math.min( l, this.containerPosition + i )); } snap(t, e) { const i = this.#n ? e : t, [s, n, o] = this.#g, { start: r, end: h, pages: l, size: c } = this, p = Math.abs(s) - n, d = Math.abs(s) + o, u = i * (this.#r ? -c : c), m = Math.floor( Math.max(p, Math.min(d, (r + h) / 2 + (isNaN(u) ? 0 : u))) / c ); this.#f(m, "snap").then(() => { const g = m <= 0 ? -1 : m >= l - 1 ? 1 : null; if (g) return this.#z({ index: this.#u(g), anchor: g < 0 ? () => 1 : () => 0 }); }); } #$(t) { const e = t.changedTouches[0]; this.#v = { x: e == null ? void 0 : e.screenX, y: e == null ? void 0 : e.screenY, t: t.timeStamp, vx: 0, xy: 0 }; } #A(t) { var p; const e = this.#v; if (e.pinched || (e.pinched = globalThis.visualViewport.scale > 1, this.scrolled || e.pinched)) return; if (t.touches.length > 1) { this.#C && t.preventDefault(); return; } const i = (p = this.#t) == null ? void 0 : p.document, s = i == null ? void 0 : i.getSelection(); if (s && s.rangeCount > 0 && !s.isCollapsed) return; t.preventDefault(); const n = t.changedTouches[0], o = n.screenX, r = n.screenY, h = e.x - o, l = e.y - r, c = t.timeStamp - e.t; e.x = o, e.y = r, e.t = t.timeStamp, e.vx = h / c, e.vy = l / c, this.#C = !0, Math.abs(h) >= Math.abs(l) ? this.scrollBy(h, 0) : Math.abs(l) > Math.abs(h) && this.scrollBy(0, l); } #L() { this.#C = !1, !this.scrolled && requestAnimationFrame(() => { globalThis.visualViewport.scale === 1 && this.snap(this.#v.vx, this.#v.vy); }); } // allows one to process rects as if they were LTR and horizontal #w() { if (this.scrolled) { const e = this.viewSize, i = this.#m; return this.#n ? ({ left: s, right: n }) => ({ left: e - n - i, right: e - s - i }) : ({ top: s, bottom: n }) => ({ left: s + i, right: n + i }); } const t = this.pages * this.size; return this.#r ? ({ left: e, right: i }) => ({ left: t - i, right: t - e }) : this.#n ? ({ top: e, bottom: i }) => ({ left: e, right: i }) : (e) => e; } async #O(t, e) { if (this.scrolled) { const s = this.#w()(t).left - this.#m; return this.#p(s, e); } const i = this.#w()(t).left; return this.#f(Math.floor(i / this.size) + (this.#r ? -1 : 1), e); } async #p(t, e, i) { const { size: s } = this; if (this.containerPosition === t) { this.#g = [t, this.atStart ? 0 : s, this.atEnd ? 0 : s], this.#b(e); return; } if (this.scrolled && this.#n && (t = -t), (e === "snap" || i) && this.hasAttribute("animated")) return I( this.containerPosition, t, 300, B, (n) => this.containerPosition = n ).then(() => { this.#g = [t, this.atStart ? 0 : s, this.atEnd ? 0 : s], this.#b(e); }); this.containerPosition = t, this.#g = [t, this.atStart ? 0 : s, this.atEnd ? 0 : s], this.#b(e); } async #f(t, e, i) { const s = this.size * (this.#r ? -t : t); return this.#p(s, e, i); } async scrollToAnchor(t, e) { return this.#y(t, e ? "selection" : "navigation"); } async #y(t, e = "anchor") { var r, h; this.#l = t; const i = (h = (r = V(t)) == null ? void 0 : r.getClientRects) == null ? void 0 : h.call(r); if (i) { const l = Array.from(i).find((c) => c.width > 0 && c.height > 0) || i[0]; if (!l) return; await this.#O(l, e); return; } if (this.scrolled) { await this.#p(t * this.viewSize, e); return; } const { pages: s } = this; if (!s) return; const n = s - 2, o = Math.round(t * (n - 1)); await this.#f(o + 1, e); } #F() { if (this.scrolled) return P( this.#t.document, this.start + this.#m, this.end - this.#m, this.#w() ); const t = this.#r ? -this.size : this.size; return P( this.#t.document, this.start - t, this.end - t, this.#w() ); } #b(t) { const e = this.#F(); this.#P = e, t !== "selection" && t !== "navigation" && t !== "anchor" ? this.#l = e : this.#E = !0; const i = this.#d, s = { reason: t, range: e, index: i }; if (this.scrolled) s.fraction = this.start / this.viewSize; else if (this.pages > 0) { const { page: n, pages: o } = this; this.#s.style.visibility = n > 1 ? "visible" : "hidden", s.fraction = (n - 1) / (o - 2), s.size = 1 / (o - 2); } this.dispatchEvent(new CustomEvent("relocate", { detail: s })); } async #B(t) { var h, l; const { index: e, src: i, anchor: s, onLoad: n, select: o } = await t; this.#d = e; const r = (l = (h = this.#t) == null ? void 0 : h.document) == null ? void 0 : l.hasFocus(); if (i) { const c = this.#N(), p = (u) => { if (u.head) { const m = u.createElement("style"); u.head.prepend(m); const g = u.createElement("style"); u.head.append(g), this.#k.set(u, [m, g]); } n == null || n({ doc: u, index: e }); }, d = this.#R.bind(this); await c.load(i, p, d), this.dispatchEvent(new CustomEvent("create-overlayer", { detail: { doc: c.document, index: e, attach: (u) => c.overlayer = u } })), this.#t = c; } await this.scrollToAnchor((typeof s == "function" ? s(this.#t.document) : s) ?? 0, o), r && this.focusView(); } #I(t) { return t >= 0 && t <= this.sections.length - 1; } async #z({ index: t, anchor: e, select: i }) { if (t === this.#d) await this.#B({ index: t, anchor: e, select: i }); else { const s = this.#d, n = (o) => { var r, h; (h = (r = this.sections[s]) == null ? void 0 : r.unload) == null || h.call(r), this.setStyles(this.#T), this.dispatchEvent(new CustomEvent("load", { detail: o })); }; await this.#B(Promise.resolve(this.sections[t].load()).then((o) => ({ index: t, src: o, anchor: e, onLoad: n, select: i })).catch((o) => (console.warn(o), console.warn(new Error(`Failed to load section ${t}`)), {}))); } } async goTo(t) { if (this.#x) return; const e = await t; if (this.#I(e.index)) return this.#z(e); } #D(t) { if (!this.#t) return !0; if (this.scrolled) return this.start > 0 ? this.#p( Math.max(0, this.start - (t ?? this.size)), null, !0 ) : !this.atStart; if (this.atStart) return; const e = this.page - 1; return this.#f(e, "page", !0).then(() => e <= 0); } #j(t) { if (!this.#t) return !0; if (this.scrolled) return this.viewSize - this.end > 2 ? this.#p( Math.min(this.viewSize, t ? this.start + t : this.end), null, !0 ) : !this.atEnd; if (this.atEnd) return; const e = this.page + 1, i = this.pages; return this.#f(e, "page", !0).then(() => e >= i - 1); } get atStart() { return this.#u(-1) == null && this.page <= 1; } get atEnd() { return this.#u(1) == null && this.page >= this.pages - 2; } #u(t) { var e; for (let i = this.#d + t; this.#I(i); i += t) if (((e = this.sections[i]) == null ? void 0 : e.linear) !== "no") return i; } async #V(t, e) { if (this.#x) return; this.#x = !0; const i = t === -1, s = await (i ? this.#D(e) : this.#j(e)); s && await this.#z({ index: this.#u(t), anchor: i ? () => 1 : () => 0 }), (s || !this.hasAttribute("animated")) && await A(100), this.#x = !1; } async prev(t) { return await this.#V(-1, t); } async next(t) { return await this.#V(1, t); } prevSection() { return this.goTo({ index: this.#u(-1) }); } nextSection() { return this.goTo({ index: this.#u(1) }); } firstSection() { const t = this.sections.findIndex((e) => e.linear !== "no"); return this.goTo({ index: t }); } lastSection() { const t = this.sections.findLastIndex((e) => e.linear !== "no"); return this.goTo({ index: t }); } getContents() { return this.#t ? [{ index: this.#d, overlayer: this.#t.overlayer, doc: this.#t.document }] : []; } setStyles(t) { var n, o, r, h, l; this.#T = t; const e = this.#k.get((n = this.#t) == null ? void 0 : n.document); if (!e) return; const [i, s] = e; if (Array.isArray(t)) { const [c, p] = t; i.textContent = c, s.textContent = p; } else s.textContent = t; requestAnimationFrame(() => { this.#_(this.#t.docBackground, this.columnCount); }), (l = (h = (r = (o = this.#t) == null ? void 0 : o.document) == null ? void 0 : r.fonts) == null ? void 0 : h.ready) == null || l.then(() => this.#t.expand()); } focusView() { this.#t.document.defaultView.focus(); } destroy() { var t, e; this.#o.unobserve(this), this.#t.destroy(), this.#t = null, (e = (t = this.sections[this.#d]) == null ? void 0 : t.unload) == null || e.call(t), this.#M.removeEventListener("change", this.#S); } } customElements.define("foliate-paginator", H); export { H as Paginator };