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>

770 lines (769 loc) 31.4 kB
import { f as gt, p as yt, t as pt, a as wt } from "./index-4W3UIPTc.js"; const S = { CONTAINER: "urn:oasis:names:tc:opendocument:xmlns:container", XHTML: "http://www.w3.org/1999/xhtml", OPF: "http://www.idpf.org/2007/opf", EPUB: "http://www.idpf.org/2007/ops", DC: "http://purl.org/dc/elements/1.1/", DCTERMS: "http://purl.org/dc/terms/", ENC: "http://www.w3.org/2001/04/xmlenc#", NCX: "http://www.daisy.org/z3986/2005/ncx/", XLINK: "http://www.w3.org/1999/xlink", SMIL: "http://www.w3.org/ns/SMIL" }, A = { XML: "application/xml", NCX: "application/x-dtbncx+xml", XHTML: "application/xhtml+xml", HTML: "text/html", CSS: "text/css", SVG: "image/svg+xml", JS: /\/(x-)?(javascript|ecmascript)/ }, C = { a11y: "http://www.idpf.org/epub/vocab/package/a11y/#", dcterms: "http://purl.org/dc/terms/", marc: "http://id.loc.gov/vocabulary/", media: "http://www.idpf.org/epub/vocab/overlays/#", onix: "http://www.editeur.org/ONIX/book/codelists/current.html#", rendition: "http://www.idpf.org/vocab/rendition/#", schema: "http://schema.org/", xsd: "http://www.w3.org/2001/XMLSchema#", msv: "http://www.idpf.org/epub/vocab/structure/magazine/#", prism: "http://www.prismstandard.org/specifications/3.0/PRISM_CV_Spec_3.0.htm#" }, bt = { art: "artist", aut: "author", clr: "colorist", edt: "editor", ill: "illustrator", nrt: "narrator", trl: "translator", pbl: "publisher" }, vt = { "02": "isbn", "06": "doi", 15: "isbn", 26: "doi", 34: "issn" }, D = (i) => i.toLowerCase().replace(/[-:](.)/g, (t, e) => e.toUpperCase()), St = (i) => i ? i.replace(/[\t\n\f\r ]+/g, " ").replace(/^[\t\n\f\r ]+/, "").replace(/[\t\n\f\r ]+$/, "") : "", Et = (i, t, e) => e ? (s) => { var r, o; return (o = (r = s.getAttribute(i)) == null ? void 0 : r.split(/\s/)) == null ? void 0 : o.includes(t); } : typeof t == "function" ? (s) => t(s.getAttribute(i)) : (s) => s.getAttribute(i) === t, P = (...i) => (t) => t ? Object.fromEntries(i.map((e) => [D(e), t.getAttribute(e)])) : null, R = (i) => St(i == null ? void 0 : i.textContent), k = (i, t) => { const e = i.lookupNamespaceURI(null) === t || i.lookupPrefix(t), s = e ? (r, o) => (a) => a.namespaceURI === t && a.localName === o : (r, o) => (a) => a.localName === o; return { $: (r, o) => [...r.children].find(s(r, o)), $$: (r, o) => [...r.children].filter(s(r, o)), $$$: e ? (r, o) => [...r.getElementsByTagNameNS(t, o)] : (r, o) => [...r.getElementsByTagName(o)] }; }, $ = (i, t) => { try { if (t.includes(":")) return new URL(i, t); const e = "https://invalid.invalid/", s = new URL(i, e + t); return s.search = "", decodeURI(s.href.replace(e, "")); } catch (e) { return console.warn(e), i; } }, ft = (i) => /^(?!blob)\w+:/i.test(i), It = (i, t) => { if (!i) return t; const e = i.replace(/\/$/, "").split("/"), s = t.replace(/\/$/, "").split("/"), r = (e.length > s.length ? e : s).findIndex((o, a) => e[a] !== s[a]); return r < 0 ? "" : Array(e.length - r).fill("..").concat(s.slice(r)).join("/"); }, At = (i) => i.slice(0, i.lastIndexOf("/") + 1), x = async (i, t, e) => { const s = []; i.replace(t, (...o) => (s.push(o), null)); const r = []; for (const o of s) r.push(await e(...o)); return i.replace(t, () => r.shift()); }, Lt = (i) => i.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"), X = (i) => { for (const [e, s] of Object.entries(i)) s == null ? delete i[e] : Array.isArray(s) ? (i[e] = s.filter((r) => r).map((r) => typeof r == "object" && !Array.isArray(r) ? X(r) : r), i[e].length ? i[e].length === 1 && (i[e] = i[e][0]) : delete i[e]) : typeof s == "object" && (i[e] = X(s), Object.keys(s).length || delete i[e]); const t = Object.keys(i); return t.length === 1 && t[0] === "name" ? i[t[0]] : i; }, Tt = (i) => { const t = new Map(Object.entries(C)), e = i.documentElement.getAttributeNS(S.EPUB, "prefix") || i.documentElement.getAttribute("prefix"); if (e) for (const [, s, r] of e.matchAll(/(.+): +(.+)[ \t\r\n]*/g)) t.set(s, r); return t; }, ut = (i, t) => { if (!i) return null; const [e, s] = i.split(":"), r = s ? e : null, o = s || e, a = t.get(r); return a ? a + o : null; }, Rt = (i) => { var j, q, _, z, V, W, G, J, K, Q, Y, Z, tt, et, st, rt, it, nt, ot, at, ct, lt, ht; const { $: t } = k(i, S.OPF), e = t(i.documentElement, "metadata"), s = Object.groupBy(e.children, (n) => n.namespaceURI === S.DC ? "dc" : n.namespaceURI === S.OPF && n.localName === "meta" ? n.hasAttribute("name") ? "legacyMeta" : "meta" : ""), r = e.getAttribute("xml:lang") ?? i.documentElement.getAttribute("xml:lang") ?? "und", o = Tt(i), a = (n) => { const g = n.getAttribute("property"), v = n.getAttribute("scheme"); return { property: ut(g, o) ?? g, scheme: ut(v, o) ?? v, lang: n.getAttribute("xml:lang"), value: R(n), props: h(n), // `opf:` attributes from EPUB 2 & EPUB 3.1 (removed in EPUB 3.2) attrs: Object.fromEntries(Array.from(n.attributes).filter((E) => E.namespaceURI === S.OPF).map((E) => [E.localName, E.value])) }; }, c = Map.groupBy(s.meta ?? [], (n) => n.getAttribute("refines")), h = (n) => { const g = c.get(n ? "#" + n.getAttribute("id") : null); return g ? Object.groupBy(g.map(a), (v) => v.property) : null; }, p = Object.fromEntries(Object.entries(Object.groupBy(s.dc, (n) => n.localName)).map(([n, g]) => [n, g.map(a)])), d = h() ?? {}, m = Object.fromEntries(((j = s.legacyMeta) == null ? void 0 : j.map((n) => [n.getAttribute("name"), n.getAttribute("content")])) ?? []), u = (n) => { var g; return (g = n == null ? void 0 : n[0]) == null ? void 0 : g.value; }, l = (n, g) => { var v; return u((v = n == null ? void 0 : n.props) == null ? void 0 : v[g]); }, b = (n) => { var I; if (!n) return null; const g = ((I = n.props) == null ? void 0 : I["alternate-script"]) ?? [], v = n.attrs["alt-rep"]; if (!g.length && (!n.lang || n.lang === r) && !v) return n.value; const E = { [n.lang ?? r]: n.value }; v && (E[n.attrs["alt-rep-lang"]] = v); for (const O of g) E[O.lang] ??= O.value; return E; }, f = (n) => { var g, v, E, I, O; return n ? { name: b(n), sortAs: b((v = (g = n.props) == null ? void 0 : g["file-as"]) == null ? void 0 : v[0]) ?? n.attrs["file-as"], role: ((O = (I = (E = n.props) == null ? void 0 : E.role) == null ? void 0 : I.filter((H) => H.scheme === C.marc + "relators")) == null ? void 0 : O.map((H) => H.value)) ?? [n.attrs.role], code: l(n, "term") ?? n.attrs.term, scheme: l(n, "authority") ?? n.attrs.authority } : null; }, y = (n) => { var g; return { name: b(n), // NOTE: webpub requires number but EPUB allows values like "2.2.1" position: u((g = n.props) == null ? void 0 : g["group-position"]) }; }, w = (n) => { var E; const { value: g } = n; if (/^urn:/i.test(g)) return g; if (/^doi:/i.test(g)) return `urn:${g}`; const v = (E = n.props) == null ? void 0 : E["identifier-type"]; if (!v) { const I = n.attrs.scheme; return I ? /^(doi|isbn|uuid)$/i.test(I) ? `urn:${I}:${g}` : { scheme: I, value: g } : g; } if (v.scheme === C.onix + "codelist5") { const I = vt[v.value]; if (I) return `urn:${I}:${g}`; } return g; }, N = Object.groupBy( d["belongs-to-collection"] ?? [], (n) => l(n, "collection-type") === "series" ? "series" : "collection" ), L = ((q = p.title) == null ? void 0 : q.find((n) => l(n, "title-type") === "main")) ?? ((_ = p.title) == null ? void 0 : _[0]), T = { identifier: mt(i), title: b(L), sortAs: b((V = (z = L == null ? void 0 : L.props) == null ? void 0 : z["file-as"]) == null ? void 0 : V[0]) ?? ((W = L == null ? void 0 : L.attrs) == null ? void 0 : W["file-as"]) ?? (m == null ? void 0 : m["calibre:title_sort"]), subtitle: (J = (G = p.title) == null ? void 0 : G.find((n) => l(n, "title-type") === "subtitle")) == null ? void 0 : J.value, language: (K = p.language) == null ? void 0 : K.map((n) => n.value), description: u(p.description), publisher: f((Q = p.publisher) == null ? void 0 : Q[0]), published: ((Z = (Y = p.date) == null ? void 0 : Y.find((n) => n.attrs.event === "publication")) == null ? void 0 : Z.value) ?? u(p.date), modified: u(d[C.dcterms + "modified"]) ?? ((et = (tt = p.date) == null ? void 0 : tt.find((n) => n.attrs.event === "modification")) == null ? void 0 : et.value), subject: (st = p.subject) == null ? void 0 : st.map(f), belongsTo: { collection: (rt = N.collection) == null ? void 0 : rt.map(y), series: ((it = N.series) == null ? void 0 : it.map(y)) ?? (m == null ? void 0 : m["calibre:series"]) ? { name: m == null ? void 0 : m["calibre:series"], position: parseFloat(m == null ? void 0 : m["calibre:series_index"]) } : null }, altIdentifier: (nt = p.identifier) == null ? void 0 : nt.map(w), source: (ot = p.source) == null ? void 0 : ot.map(w), // NOTE: not in webpub schema rights: u(p.rights) // NOTE: not in webpub schema }, M = (n) => (g) => { var E; const v = new Set((E = g.role) == null ? void 0 : E.map((I) => bt[I] ?? n)); return [v.size ? v : [n], g]; }; for (const [n, g] of [].concat( ((ct = (at = p.creator) == null ? void 0 : at.map(f)) == null ? void 0 : ct.map(M("author"))) ?? [], ((ht = (lt = p.contributor) == null ? void 0 : lt.map(f)) == null ? void 0 : ht.map(M("contributor"))) ?? [] )) for (const v of n) T[v] ? T[v].push(g) : T[v] = [g]; X(T), T.altIdentifier === T.identifier && delete T.altIdentifier; const U = {}, B = {}; for (const [n, g] of Object.entries(d)) n.startsWith(C.rendition) ? U[D(n.replace(C.rendition, ""))] = u(g) : n.startsWith(C.media) && (B[D(n.replace(C.media, ""))] = u(g)); return B.duration && (B.duration = F(B.duration)), { metadata: T, rendition: U, media: B }; }, Nt = (i, t = (e) => e) => { var b; const { $: e, $$: s, $$$: r } = k(i, S.XHTML), o = (f) => f ? decodeURI(t(f)) : null, a = (f) => (y) => { var U; const w = e(y, "a") ?? e(y, "span"), N = e(y, "ol"), L = o(w == null ? void 0 : w.getAttribute("href")), M = { label: R(w) || (w == null ? void 0 : w.getAttribute("title")), href: L, subitems: c(N) }; return f && (M.type = (U = w == null ? void 0 : w.getAttributeNS(S.EPUB, "type")) == null ? void 0 : U.split(/\s/)), M; }, c = (f, y) => f ? s(f, "li").map(a(y)) : null, h = (f, y) => c(e(f, "ol"), y), p = r(i, "nav"); let d = null, m = null, u = null, l = []; for (const f of p) { const y = ((b = f.getAttributeNS(S.EPUB, "type")) == null ? void 0 : b.split(/\s/)) ?? []; y.includes("toc") ? d ??= h(f) : y.includes("page-list") ? m ??= h(f) : y.includes("landmarks") ? u ??= h(f, !0) : l.push({ label: R(f.firstElementChild), type: y, list: h(f) }); } return { toc: d, pageList: m, landmarks: u, others: l }; }, Ct = (i, t = (e) => e) => { const { $: e, $$: s } = k(i, S.NCX), r = (h) => h ? decodeURI(t(h)) : null, o = (h) => { const p = e(h, "navLabel"), d = e(h, "content"), m = R(p), u = r(d.getAttribute("src")); if (h.localName === "navPoint") { const l = s(h, "navPoint"); return { label: m, href: u, subitems: l.length ? l.map(o) : null }; } return { label: m, href: u }; }, a = (h, p) => s(h, p).map(o), c = (h, p) => { const d = e(i.documentElement, h); return d ? a(d, p) : null; }; return { toc: c("navMap", "navPoint"), pageList: c("pageList", "pageTarget"), others: s(i.documentElement, "navList").map((h) => ({ label: R(e(h, "navLabel")), list: a(h, "navTarget") })) }; }, F = (i) => { if (!i) return; const t = i.split(":").map((a) => parseFloat(a)); if (t.length === 3) { const [a, c, h] = t; return a * 60 * 60 + c * 60 + h; } if (t.length === 2) { const [a, c] = t; return a * 60 + c; } const [e, s] = i.split(/(?=[^\d.])/), r = parseFloat(e), o = s === "h" ? 60 * 60 : s === "min" ? 60 : s === "ms" ? 1e-3 : 1; return r * o; }; class $t extends EventTarget { #e; #s; #t; #n; #i; #r; #d = 1; #f = 1; #l; constructor(t, e) { super(), this.book = t, this.loadXML = e; } async #g(t) { if (this.#s === t) return; const e = await this.loadXML(t.href), s = (a) => a ? $(a, t.href) : null, { $: r, $$$: o } = k(e, S.SMIL); this.#n = -1, this.#i = -1, this.#e = o(e, "par").reduce((a, c) => { var b; const h = s((b = r(c, "text")) == null ? void 0 : b.getAttribute("src")), p = r(c, "audio"); if (!h || !p) return a; const d = s(p.getAttribute("src")), m = F(p.getAttribute("clipBegin")), u = F(p.getAttribute("clipEnd")), l = a.at(-1); return (l == null ? void 0 : l.src) === d ? l.items.push({ text: h, begin: m, end: u }) : a.push({ src: d, items: [{ text: h, begin: m, end: u }] }), a; }, []), this.#s = t; } get #h() { return this.#e[this.#n]; } get #o() { var t, e; return (e = (t = this.#h) == null ? void 0 : t.items) == null ? void 0 : e[this.#i]; } #a(t) { console.error(t), this.dispatchEvent(new CustomEvent("error", { detail: t })); } #p() { this.dispatchEvent(new CustomEvent("highlight", { detail: this.#o })); } #u() { this.dispatchEvent(new CustomEvent("unhighlight", { detail: this.#o })); } async #c(t, e) { var a; this.#m(), this.#n = t, this.#i = e; const s = (a = this.#h) == null ? void 0 : a.src; if (!s || !this.#o) return this.start(this.#t + 1); const r = URL.createObjectURL(await this.book.loadBlob(s)), o = new Audio(r); this.#r = o, o.volume = this.#d, o.playbackRate = this.#f, o.addEventListener("timeupdate", () => { var d, m; if (o.paused) return; const c = o.currentTime, { items: h } = this.#h; if (c > ((d = this.#o) == null ? void 0 : d.end) && (this.#u(), this.#i === h.length - 1)) { this.#c(this.#n + 1, 0).catch((u) => this.#a(u)); return; } const p = this.#i; for (; ((m = h[this.#i + 1]) == null ? void 0 : m.begin) <= c; ) this.#i++; this.#i !== p && this.#p(); }), o.addEventListener("error", () => this.#a(new Error(`Failed to load ${s}`))), o.addEventListener("playing", () => this.#p()), o.addEventListener("ended", () => { this.#u(), URL.revokeObjectURL(r), this.#r = null, this.#c(t + 1, 0).catch((c) => this.#a(c)); }), this.#l === "paused" ? (this.#p(), o.currentTime = this.#o.begin ?? 0) : o.addEventListener("canplaythrough", () => { o.currentTime = this.#o.begin ?? 0, this.#l = "playing", o.play().catch((c) => this.#a(c)); }, { once: !0 }); } async start(t, e = () => !0) { var a; (a = this.#r) == null || a.pause(); const s = this.book.sections[t], r = s == null ? void 0 : s.id; if (!r) return; const { mediaOverlay: o } = s; if (!o) return this.start(t + 1); this.#t = t, await this.#g(o); for (let c = 0; c < this.#e.length; c++) { const { items: h } = this.#e[c]; for (let p = 0; p < h.length; p++) if (h[p].text.split("#")[0] === r && e(h[p], p, h)) return this.#c(c, p).catch((d) => this.#a(d)); } } pause() { var t; this.#l = "paused", (t = this.#r) == null || t.pause(); } resume() { var t; this.#l = "playing", (t = this.#r) == null || t.play().catch((e) => this.#a(e)); } #m() { this.#r && (this.#r.pause(), URL.revokeObjectURL(this.#r.src), this.#r = null, this.#u()); } stop() { this.#l = "stopped", this.#m(); } prev() { this.#i > 0 ? this.#c(this.#n, this.#i - 1) : this.#n > 0 ? this.#c( this.#n - 1, this.#e[this.#n - 1].items.length - 1 ) : this.#t > 0 && this.start(this.#t - 1, (t, e, s) => e === s.length - 1); } next() { this.#c(this.#n, this.#i + 1); } setVolume(t) { this.#d = t, this.#r && (this.#r.volume = t); } setRate(t) { this.#f = t, this.#r && (this.#r.playbackRate = t); } } const Mt = /([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})/, Ut = (i) => { for (const t of i.getElementsByTagNameNS(S.DC, "identifier")) { const [e] = R(t).split(":").slice(-1); if (Mt.test(e)) return e; } return ""; }, mt = (i) => R( i.getElementById(i.documentElement.getAttribute("unique-identifier")) ?? i.getElementsByTagNameNS(S.DC, "identifier")[0] ), dt = async (i, t, e) => { const s = new Uint8Array(await e.slice(0, t).arrayBuffer()); t = Math.min(t, s.length); for (var r = 0; r < t; r++) s[r] = s[r] ^ i[r % i.length]; return new Blob([s, e.slice(t)], { type: e.type }); }, Bt = async (i) => { const t = new TextEncoder().encode(i), e = await globalThis.crypto.subtle.digest("SHA-1", t); return new Uint8Array(e); }, Ot = (i = Bt) => ({ "http://www.idpf.org/2008/embedding": { key: (t) => i(mt(t).replaceAll(/[\u0020\u0009\u000d\u000a]/g, "")), decode: (t, e) => dt(t, 1040, e) }, "http://ns.adobe.com/pdf/enc#RC": { key: (t) => { const e = Ut(t).replaceAll("-", ""); return Uint8Array.from({ length: 16 }, (s, r) => parseInt(e.slice(r * 2, r * 2 + 2), 16)); }, decode: (t, e) => dt(t, 1024, e) } }); class kt { #e = /* @__PURE__ */ new Map(); #s = /* @__PURE__ */ new Map(); #t; constructor(t) { this.#t = t; } async init(t, e) { if (!t) return; const s = Array.from( t.getElementsByTagNameNS(S.ENC, "EncryptedData"), (r) => { var o, a; return { algorithm: (o = r.getElementsByTagNameNS(S.ENC, "EncryptionMethod")[0]) == null ? void 0 : o.getAttribute("Algorithm"), uri: (a = r.getElementsByTagNameNS(S.ENC, "CipherReference")[0]) == null ? void 0 : a.getAttribute("URI") }; } ); for (const { algorithm: r, uri: o } of s) { if (!this.#s.has(r)) { const a = this.#t[r]; if (!a) { console.warn("Unknown encryption algorithm"); continue; } const c = await a.key(e); this.#s.set(r, (h) => a.decode(c, h)); } this.#e.set(o, r); } } getDecoder(t) { return this.#s.get(this.#e.get(t)) ?? ((e) => e); } } class xt { constructor({ opf: t, resolveHref: e }) { var d, m, u, l, b; this.opf = t; const { $: s, $$: r, $$$: o } = k(t, S.OPF), a = s(t.documentElement, "manifest"), c = s(t.documentElement, "spine"), h = r(c, "itemref"); this.manifest = r(a, "item").map(P("href", "id", "media-type", "properties", "media-overlay")).map((f) => { var y; return f.href = e(f.href), f.properties = (y = f.properties) == null ? void 0 : y.split(/\s/), f; }), this.manifestById = new Map(this.manifest.map((f) => [f.id, f])), this.spine = h.map(P("idref", "id", "linear", "properties")).map((f) => { var y; return f.properties = (y = f.properties) == null ? void 0 : y.split(/\s/), f; }), this.pageProgressionDirection = c.getAttribute("page-progression-direction"), this.navPath = (d = this.getItemByProperty("nav")) == null ? void 0 : d.href, this.ncxPath = (m = this.getItemByID(c.getAttribute("toc")) ?? this.manifest.find((f) => f.mediaType === A.NCX)) == null ? void 0 : m.href; const p = s(t.documentElement, "guide"); p && (this.guide = r(p, "reference").map(P("type", "title", "href")).map(({ type: f, title: y, href: w }) => ({ label: y, type: f.split(/\s/), href: e(w) }))), this.cover = this.getItemByProperty("cover-image") ?? this.getItemByID((u = o(t, "meta").find(Et("name", "cover"))) == null ? void 0 : u.getAttribute("content")) ?? this.getItemByHref((b = (l = this.guide) == null ? void 0 : l.find((f) => f.type.includes("cover"))) == null ? void 0 : b.href), this.cfis = gt(h); } getItemByID(t) { return this.manifestById.get(t); } getItemByHref(t) { return this.manifest.find((e) => e.href === t); } getItemByProperty(t) { return this.manifest.find((e) => { var s; return (s = e.properties) == null ? void 0 : s.includes(t); }); } resolveCFI(t) { const e = yt(t), s = (e.parent ?? e).shift(); let r = pt(this.opf, s); r && r.nodeName !== "idref" && (s.at(-1).id = null, r = pt(this.opf, s)); const o = r == null ? void 0 : r.getAttribute("idref"); return { index: this.spine.findIndex((h) => h.idref === o), anchor: (h) => wt(h, e) }; } } class Pt { #e = /* @__PURE__ */ new Map(); #s = /* @__PURE__ */ new Map(); #t = /* @__PURE__ */ new Map(); eventTarget = new EventTarget(); constructor({ loadText: t, loadBlob: e, resources: s }) { this.loadText = t, this.loadBlob = e, this.manifest = s.manifest, this.assets = s.manifest; } async createURL(t, e, s, r) { if (!e) return ""; const o = { data: e, type: s }; Object.defineProperty(o, "name", { value: t }); const a = new CustomEvent("data", { detail: o }); this.eventTarget.dispatchEvent(a); const c = await a.detail.data, h = await a.detail.type, p = URL.createObjectURL(new Blob([c], { type: h })); if (this.#e.set(t, p), this.#t.set(t, 1), r) { const d = this.#s.get(r); d ? d.push(t) : this.#s.set(r, [t]); } return p; } ref(t, e) { const s = this.#s.get(e); return s != null && s.includes(t) || (this.#t.set(t, this.#t.get(t) + 1), s ? s.push(t) : this.#s.set(e, [t])), this.#e.get(t); } unref(t) { if (!this.#t.has(t)) return; const e = this.#t.get(t) - 1; if (e < 1) { URL.revokeObjectURL(this.#e.get(t)), this.#e.delete(t), this.#t.delete(t); const s = this.#s.get(t); if (s) for (; s.length; ) this.unref(s.pop()); this.#s.delete(t); } else this.#t.set(t, e); } // load manifest item, recursively loading all resources as needed async loadItem(t, e = []) { if (!t) return null; const { href: s, mediaType: r } = t, o = A.JS.test(t.mediaType), a = { type: r, isScript: o, allow: !0 }, c = new CustomEvent("load", { detail: a }); if (this.eventTarget.dispatchEvent(c), !await c.detail.allow) return null; const p = e.at(-1); if (this.#e.has(s)) return this.ref(s, p); if ((o || [A.XHTML, A.HTML, A.CSS, A.SVG].includes(r)) && e.every((u) => u !== s)) return this.loadReplaced(t, e); const m = Promise.resolve().then(() => this.loadBlob(s)); return this.createURL(s, m, r, p); } async loadHref(t, e, s = []) { if (ft(t)) return t; const r = $(t, e), o = this.manifest.find((a) => a.href === r); return o ? this.loadItem(o, s.concat(e)) : t; } async loadReplaced(t, e = []) { var h, p; const { href: s, mediaType: r } = t, o = e.at(-1); let a = ""; try { a = await this.loadText(s); } catch (d) { return this.createURL(s, Promise.reject(d), r, o); } if (!a) return null; if ([A.XHTML, A.HTML, A.SVG].includes(r)) { let d = new DOMParser().parseFromString(a, r); if (r === A.XHTML && (d.querySelector("parsererror") || !((h = d.documentElement) != null && h.namespaceURI)) && (console.warn(((p = d.querySelector("parsererror")) == null ? void 0 : p.innerText) ?? "Invalid XHTML"), t.mediaType = A.HTML, d = new DOMParser().parseFromString(a, t.mediaType)), [A.XHTML, A.SVG].includes(t.mediaType)) { let l = d.firstChild; for (; l instanceof ProcessingInstruction; ) { if (l.data) { const b = await x( l.data, /(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i, (f, y, w, N) => this.loadHref(w, s, e).then((L) => `${y}${L}${N}`) ); l.replaceWith(d.createProcessingInstruction( l.target, b )); } l = l.nextSibling; } } const m = async (l, b) => l.setAttribute( b, await this.loadHref(l.getAttribute(b), s, e) ); for (const l of d.querySelectorAll("link[href]")) await m(l, "href"); for (const l of d.querySelectorAll("[src]")) await m(l, "src"); for (const l of d.querySelectorAll("[poster]")) await m(l, "poster"); for (const l of d.querySelectorAll("object[data]")) await m(l, "data"); for (const l of d.querySelectorAll("[*|href]:not([href])")) l.setAttributeNS(S.XLINK, "href", await this.loadHref( l.getAttributeNS(S.XLINK, "href"), s, e )); for (const l of d.querySelectorAll("style")) l.textContent && (l.textContent = await this.replaceCSS(l.textContent, s, e)); for (const l of d.querySelectorAll("[style]")) l.setAttribute( "style", await this.replaceCSS(l.getAttribute("style"), s, e) ); const u = new XMLSerializer().serializeToString(d); return this.createURL(s, u, t.mediaType, o); } const c = r === A.CSS ? await this.replaceCSS(a, s, e) : await this.replaceString(a, s, e); return this.createURL(s, c, r, o); } async replaceCSS(t, e, s = []) { const r = await x( t, /url\(\s*["']?([^'"\n]*?)\s*["']?\s*\)/gi, (o, a) => this.loadHref(a, e, s).then((c) => `url("${c}")`) ); return x( r, /@import\s*["']([^"'\n]*?)["']/gi, (o, a) => this.loadHref(a, e, s).then((c) => `@import "${c}"`) ); } // find & replace all possible relative paths for all assets without parsing replaceString(t, e, s = []) { const r = /* @__PURE__ */ new Map(), o = this.assets.map((c) => { if (c.href === e) return; const h = It(At(e), c.href), p = encodeURI(h), d = "/" + c.href, m = encodeURI(d), u = /* @__PURE__ */ new Set([h, p, d, m]); for (const l of u) r.set(l, c); return Array.from(u); }).flat().filter((c) => c); if (!o.length) return t; const a = new RegExp(o.map(Lt).join("|"), "g"); return x(t, a, async (c) => this.loadItem( r.get(c.replace(/^\//, "")), s.concat(e) )); } unloadItem(t) { this.unref(t == null ? void 0 : t.href); } destroy() { for (const t of this.#e.values()) URL.revokeObjectURL(t); } } const Ht = (i, t) => i.getElementById(t) ?? i.querySelector(`[name="${CSS.escape(t)}"]`), Dt = (i) => { for (const t of i) { if (t === "page-spread-left" || t === "rendition:page-spread-left") return "left"; if (t === "page-spread-right" || t === "rendition:page-spread-right") return "right"; if (t === "rendition:page-spread-center") return "center"; } }, Xt = (i) => i ? { fixedLayout: R(i.querySelector('option[name="fixed-layout"]')), openToSpread: R(i.querySelector('option[name="open-to-spread"]')) } : null; class jt { parser = new DOMParser(); #e; #s; constructor({ loadText: t, loadBlob: e, getSize: s, sha1: r }) { this.loadText = t, this.loadBlob = e, this.getSize = s, this.#s = new kt(Ot(r)); } async #t(t) { const e = await this.loadText(t); if (!e) return null; const s = this.parser.parseFromString(e, A.XML); if (s.querySelector("parsererror")) throw new Error(`XML parsing error: ${t} ${s.querySelector("parsererror").innerText}`); return s; } async init() { const t = await this.#t("META-INF/container.xml"); if (!t) throw new Error("Failed to load container file"); const e = Array.from( t.getElementsByTagNameNS(S.CONTAINER, "rootfile"), P("full-path", "media-type") ).filter((u) => u.mediaType === "application/oebps-package+xml"); if (!e.length) throw new Error("No package document defined in container"); const s = e[0].fullPath, r = await this.#t(s); if (!r) throw new Error("Failed to load package document"); const o = await this.#t("META-INF/encryption.xml"); await this.#s.init(o, r), this.resources = new xt({ opf: r, resolveHref: (u) => $(u, s) }), this.#e = new Pt({ loadText: this.loadText, loadBlob: (u) => Promise.resolve(this.loadBlob(u)).then(this.#s.getDecoder(u)), resources: this.resources }), this.transformTarget = this.#e.eventTarget, this.sections = this.resources.spine.map((u, l) => { const { idref: b, linear: f, properties: y = [] } = u, w = this.resources.getItemByID(b); return w ? { id: w.href, load: () => this.#e.loadItem(w), unload: () => this.#e.unloadItem(w), createDocument: () => this.loadDocument(w), size: this.getSize(w.href), cfi: this.resources.cfis[l], linear: f, pageSpread: Dt(y), resolveHref: (N) => $(N, w.href), mediaOverlay: w.mediaOverlay ? this.resources.getItemByID(w.mediaOverlay) : null } : (console.warn(`Could not find item with ID "${b}" in manifest`), null); }).filter((u) => u); const { navPath: a, ncxPath: c } = this.resources; if (a) try { const u = (b) => $(b, a), l = Nt(await this.#t(a), u); this.toc = l.toc, this.pageList = l.pageList, this.landmarks = l.landmarks; } catch (u) { console.warn(u); } if (!this.toc && c) try { const u = (b) => $(b, c), l = Ct(await this.#t(c), u); this.toc = l.toc, this.pageList = l.pageList; } catch (u) { console.warn(u); } this.landmarks ??= this.resources.guide; const { metadata: h, rendition: p, media: d } = Rt(r); this.metadata = h, this.rendition = p, this.media = d, this.dir = this.resources.pageProgressionDirection; const m = Xt( await this.#t("META-INF/com.apple.ibooks.display-options.xml") ?? await this.#t("META-INF/com.kobobooks.display-options.xml") ); return m && (m.fixedLayout === "true" && (this.rendition.layout ??= "pre-paginated"), m.openToSpread === "false" && (this.sections.find((u) => u.linear !== "no").pageSpread ??= this.dir === "rtl" ? "left" : "right")), this; } async loadDocument(t) { const e = await this.loadText(t.href); return this.parser.parseFromString(e, t.mediaType); } getMediaOverlay() { return new $t(this, this.#t.bind(this)); } resolveCFI(t) { return this.resources.resolveCFI(t); } resolveHref(t) { const [e, s] = t.split("#"), r = this.resources.getItemByHref(decodeURI(e)); return r ? { index: this.resources.spine.findIndex(({ idref: c }) => c === r.id), anchor: s ? (c) => Ht(c, s) : () => 0 } : null; } splitTOCHref(t) { return (t == null ? void 0 : t.split("#")) ?? []; } getTOCFragment(t, e) { return t.getElementById(e) ?? t.querySelector(`[name="${CSS.escape(e)}"]`); } isExternal(t) { return ft(t); } async getCover() { var e; const t = (e = this.resources) == null ? void 0 : e.cover; return t != null && t.href ? new Blob([await this.loadBlob(t.href)], { type: t.mediaType }) : null; } async getCalibreBookmarks() { const t = await this.loadText("META-INF/calibre_bookmarks.txt"), e = "encoding=json+base64:"; if (t != null && t.startsWith(e)) { const s = atob(t.slice(e.length)); return JSON.parse(s); } } destroy() { var t; (t = this.#e) == null || t.destroy(); } } export { jt as EPUB };