UNPKG

vue-book-reader

Version:

<div align="center"> <img width=250 src="https://raw.githubusercontent.com/jinhuan138/vue--book-reader/master/public/logo.png" /> <h1>VueReader</h1> </div>

314 lines (313 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const parseViewport = (str) => { var _a, _b; return (_b = (_a = str == null ? void 0 : str.split(/[,;\s]/)) == null ? void 0 : _a.filter((x) => x)) == null ? void 0 : _b.map((x) => x.split("=").map((x2) => x2.trim())); }; const getViewport = (doc, viewport) => { var _a, _b; if (doc.documentElement.localName === "svg") { const [, , width, height] = ((_a = doc.documentElement.getAttribute("viewBox")) == null ? void 0 : _a.split(/\s/)) ?? []; return { width, height }; } const meta = parseViewport((_b = doc.querySelector('meta[name="viewport"]')) == null ? void 0 : _b.getAttribute("content")); if (meta) return Object.fromEntries(meta); if (typeof viewport === "string") return parseViewport(viewport); if ((viewport == null ? void 0 : viewport.width) && viewport.height) return viewport; const img = doc.querySelector("img"); if (img) return { width: img.naturalWidth, height: img.naturalHeight }; console.warn(new Error("Missing viewport properties")); return { width: 1e3, height: 2e3 }; }; class FixedLayout extends HTMLElement { static observedAttributes = ["zoom"]; #root = this.attachShadow({ mode: "closed" }); #observer = new ResizeObserver(() => this.#render()); #spreads; #index = -1; defaultViewport; spread; #portrait = false; #left; #right; #center; #side; #zoom; constructor() { super(); const sheet = new CSSStyleSheet(); this.#root.adoptedStyleSheets = [sheet]; sheet.replaceSync(`:host { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: auto; }`); this.#observer.observe(this); } attributeChangedCallback(name, _, value) { switch (name) { case "zoom": this.#zoom = value !== "fit-width" && value !== "fit-page" ? parseFloat(value) : value; this.#render(); break; } } async #createFrame({ index, src: srcOption }) { const srcOptionIsString = typeof srcOption === "string"; const src = srcOptionIsString ? srcOption : srcOption == null ? void 0 : srcOption.src; const onZoom = srcOptionIsString ? null : srcOption == null ? void 0 : srcOption.onZoom; const element = document.createElement("div"); element.setAttribute("dir", "ltr"); const iframe = document.createElement("iframe"); element.append(iframe); Object.assign(iframe.style, { border: "0", display: "none", overflow: "hidden" }); iframe.setAttribute("sandbox", "allow-same-origin allow-scripts"); iframe.setAttribute("scrolling", "no"); iframe.setAttribute("part", "filter"); this.#root.append(element); if (!src) return { blank: true, element, iframe }; return new Promise((resolve) => { iframe.addEventListener("load", () => { const doc = iframe.contentDocument; this.dispatchEvent(new CustomEvent("load", { detail: { doc, index } })); const { width, height } = getViewport(doc, this.defaultViewport); resolve({ element, iframe, width: parseFloat(width), height: parseFloat(height), onZoom }); }, { once: true }); iframe.src = src; }); } #render(side = this.#side) { if (!side) return; const left = this.#left ?? {}; const right = this.#center ?? this.#right ?? {}; const target = side === "left" ? left : right; const { width, height } = this.getBoundingClientRect(); const portrait = this.spread !== "both" && this.spread !== "portrait" && height > width; this.#portrait = portrait; const blankWidth = left.width ?? right.width ?? 0; const blankHeight = left.height ?? right.height ?? 0; const scale = typeof this.#zoom === "number" && !isNaN(this.#zoom) ? this.#zoom : (this.#zoom === "fit-width" ? portrait || this.#center ? width / (target.width ?? blankWidth) : width / ((left.width ?? blankWidth) + (right.width ?? blankWidth)) : portrait || this.#center ? Math.min( width / (target.width ?? blankWidth), height / (target.height ?? blankHeight) ) : Math.min( width / ((left.width ?? blankWidth) + (right.width ?? blankWidth)), height / Math.max( left.height ?? blankHeight, right.height ?? blankHeight ) )) || 1; const transform = (frame) => { let { element, iframe, width: width2, height: height2, blank, onZoom } = frame; if (!iframe) return; if (onZoom) onZoom({ doc: frame.iframe.contentDocument, scale }); const iframeScale = onZoom ? scale : 1; Object.assign(iframe.style, { width: `${width2 * iframeScale}px`, height: `${height2 * iframeScale}px`, transform: onZoom ? "none" : `scale(${scale})`, transformOrigin: "top left", display: blank ? "none" : "block" }); Object.assign(element.style, { width: `${(width2 ?? blankWidth) * scale}px`, height: `${(height2 ?? blankHeight) * scale}px`, overflow: "hidden", display: "block", flexShrink: "0", marginBlock: "auto" }); if (portrait && frame !== target) { element.style.display = "none"; } }; if (this.#center) { transform(this.#center); } else { transform(left); transform(right); } } async #showSpread({ left, right, center, side }) { this.#root.replaceChildren(); this.#left = null; this.#right = null; this.#center = null; if (center) { this.#center = await this.#createFrame(center); this.#side = "center"; this.#render(); } else { this.#left = await this.#createFrame(left); this.#right = await this.#createFrame(right); this.#side = this.#left.blank ? "right" : this.#right.blank ? "left" : side; this.#render(); } } #goLeft() { var _a, _b, _c, _d; if (this.#center || ((_a = this.#left) == null ? void 0 : _a.blank)) return; if (this.#portrait && ((_d = (_c = (_b = this.#left) == null ? void 0 : _b.element) == null ? void 0 : _c.style) == null ? void 0 : _d.display) === "none") { this.#side = "left"; this.#render(); this.#reportLocation("page"); return true; } } #goRight() { var _a, _b, _c, _d; if (this.#center || ((_a = this.#right) == null ? void 0 : _a.blank)) return; if (this.#portrait && ((_d = (_c = (_b = this.#right) == null ? void 0 : _b.element) == null ? void 0 : _c.style) == null ? void 0 : _d.display) === "none") { this.#side = "right"; this.#render(); this.#reportLocation("page"); return true; } } open(book) { this.book = book; const { rendition } = book; this.spread = rendition == null ? void 0 : rendition.spread; this.defaultViewport = rendition == null ? void 0 : rendition.viewport; const rtl = book.dir === "rtl"; const ltr = !rtl; this.rtl = rtl; if ((rendition == null ? void 0 : rendition.spread) === "none") this.#spreads = book.sections.map((section) => ({ center: section })); else this.#spreads = book.sections.reduce((arr, section, i) => { const last = arr[arr.length - 1]; const { pageSpread } = section; const newSpread = () => { const spread = {}; arr.push(spread); return spread; }; if (pageSpread === "center") { const spread = last.left || last.right ? newSpread() : last; spread.center = section; } else if (pageSpread === "left") { const spread = last.center || last.left || ltr && i ? newSpread() : last; spread.left = section; } else if (pageSpread === "right") { const spread = last.center || last.right || rtl && i ? newSpread() : last; spread.right = section; } else if (ltr) { if (last.center || last.right) newSpread().left = section; else if (last.left || !i) last.right = section; else last.left = section; } else { if (last.center || last.left) newSpread().right = section; else if (last.right || !i) last.left = section; else last.right = section; } return arr; }, [{}]); } get index() { const spread = this.#spreads[this.#index]; const section = (spread == null ? void 0 : spread.center) ?? (this.#side === "left" ? spread.left ?? spread.right : spread.right ?? spread.left); return this.book.sections.indexOf(section); } #reportLocation(reason) { this.dispatchEvent(new CustomEvent("relocate", { detail: { reason, range: null, index: this.index, fraction: 0, size: 1 } })); } getSpreadOf(section) { const spreads = this.#spreads; for (let index = 0; index < spreads.length; index++) { const { left, right, center } = spreads[index]; if (left === section) return { index, side: "left" }; if (right === section) return { index, side: "right" }; if (center === section) return { index, side: "center" }; } } async goToSpread(index, side, reason) { var _a, _b, _c, _d, _e, _f; if (index < 0 || index > this.#spreads.length - 1) return; if (index === this.#index) { this.#render(side); return; } this.#index = index; const spread = this.#spreads[index]; if (spread.center) { const index2 = this.book.sections.indexOf(spread.center); const src = await ((_b = (_a = spread.center) == null ? void 0 : _a.load) == null ? void 0 : _b.call(_a)); await this.#showSpread({ center: { index: index2, src } }); } else { const indexL = this.book.sections.indexOf(spread.left); const indexR = this.book.sections.indexOf(spread.right); const srcL = await ((_d = (_c = spread.left) == null ? void 0 : _c.load) == null ? void 0 : _d.call(_c)); const srcR = await ((_f = (_e = spread.right) == null ? void 0 : _e.load) == null ? void 0 : _f.call(_e)); const left = { index: indexL, src: srcL }; const right = { index: indexR, src: srcR }; await this.#showSpread({ left, right, side }); } this.#reportLocation(reason); } async select(target) { await this.goTo(target); } async goTo(target) { const { book } = this; const resolved = await target; const section = book.sections[resolved.index]; if (!section) return; const { index, side } = this.getSpreadOf(section); await this.goToSpread(index, side); } async next() { const s = this.rtl ? this.#goLeft() : this.#goRight(); if (!s) return this.goToSpread(this.#index + 1, this.rtl ? "right" : "left", "page"); } async prev() { const s = this.rtl ? this.#goRight() : this.#goLeft(); if (!s) return this.goToSpread(this.#index - 1, this.rtl ? "left" : "right", "page"); } getContents() { return Array.from(this.#root.querySelectorAll("iframe"), (frame) => ({ doc: frame.contentDocument // TODO: index, overlayer })); } destroy() { this.#observer.unobserve(this); } } customElements.define("foliate-fxl", FixedLayout); exports.FixedLayout = FixedLayout;