vue-book-reader
Version:
vue-book-reader is a vue wrapper for [foliate-js](https://github.com/johnfactotum/foliate-js) - library for rendering e-books in the browser. Supports EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental; requires PDF.js), or add support for other formats
738 lines (737 loc) • 31.5 kB
JavaScript
var Ht = Object.defineProperty;
var Dt = (r, t, e) => t in r ? Ht(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
var Nt = (r, t, e) => (Dt(r, typeof t != "symbol" ? t + "" : t, e), e), $t = (r, t, e) => {
if (!t.has(r))
throw TypeError("Cannot " + e);
};
var o = (r, t, e) => ($t(r, t, "read from private field"), e ? e.call(r) : t.get(r)), b = (r, t, e) => {
if (t.has(r))
throw TypeError("Cannot add the same private member more than once");
t instanceof WeakSet ? t.add(r) : t.set(r, e);
}, I = (r, t, e, s) => ($t(r, t, "write to private field"), s ? s.call(r, e) : t.set(r, e), e);
var Rt = (r, t, e, s) => ({
set _(n) {
I(r, t, n, e);
},
get _() {
return o(r, t, s);
}
}), w = (r, t, e) => ($t(r, t, "access private method"), e);
import { f as Xt, p as qt, t as Ut, a as _t } from "./index-UYMDWtcl.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"
}, N = {
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)/
}, At = (r) => r.toLowerCase().replace(/[-:](.)/g, (t, e) => e.toUpperCase()), zt = (r) => r ? r.replace(/[\t\n\f\r ]+/g, " ").replace(/^[\t\n\f\r ]+/, "").replace(/[\t\n\f\r ]+$/, "") : "", ct = (r, t, e) => e ? (s) => {
var n, c;
return (c = (n = s.getAttribute(r)) == null ? void 0 : n.split(/\s/)) == null ? void 0 : c.includes(t);
} : typeof t == "function" ? (s) => t(s.getAttribute(r)) : (s) => s.getAttribute(r) === t, Et = (...r) => (t) => t ? Object.fromEntries(r.map((e) => [At(e), t.getAttribute(e)])) : null, B = (r) => zt(r == null ? void 0 : r.textContent), mt = (r, t) => {
const e = r.lookupNamespaceURI(null) === t || r.lookupPrefix(t), s = e ? (n, c) => (a) => a.namespaceURI === t && a.localName === c : (n, c) => (a) => a.localName === c;
return {
$: (n, c) => [...n.children].find(s(n, c)),
$$: (n, c) => [...n.children].filter(s(n, c)),
$$$: e ? (n, c) => [...n.getElementsByTagNameNS(t, c)] : (n, c) => [...n.getElementsByTagName(c)]
};
}, et = (r, t) => {
try {
if (t.includes(":"))
return new URL(r, t);
const e = "https://invalid.invalid/", s = new URL(r, e + t);
return s.search = "", decodeURI(s.href.replace(e, ""));
} catch (e) {
return console.warn(e), r;
}
}, Ot = (r) => /^(?!blob)\w+:/i.test(r), Gt = (r, t) => {
if (!r)
return t;
const e = r.replace(/\/$/, "").split("/"), s = t.replace(/\/$/, "").split("/"), n = (e.length > s.length ? e : s).findIndex((c, a) => e[a] !== s[a]);
return n < 0 ? "" : Array(e.length - n).fill("..").concat(s.slice(n)).join("/");
}, Vt = (r) => r.slice(0, r.lastIndexOf("/") + 1), St = async (r, t, e) => {
const s = [];
r.replace(t, (...c) => (s.push(c), null));
const n = [];
for (const c of s)
n.push(await e(...c));
return r.replace(t, () => n.shift());
}, Wt = (r) => r.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"), z = { attrs: ["dir", "xml:lang"] }, J = { name: "alternate-script", many: !0, ...z, props: ["file-as"] }, Bt = {
many: !0,
...z,
props: [{ name: "role", many: !0, attrs: ["scheme"] }, "file-as", J],
setLegacyAttrs: (r, t) => {
var e;
if (!((e = r.role) != null && e.length)) {
const s = t.getAttributeNS(S.OPF, "role");
s && (r.role = [{ value: s }]);
}
r.fileAs ?? (r.fileAs = t.getAttributeNS(S.OPF, "file-as"));
}
}, Jt = [
{
name: "title",
many: !0,
...z,
props: ["title-type", "display-seq", "file-as", J]
},
{
name: "identifier",
many: !0,
props: [{ name: "identifier-type", attrs: ["scheme"] }],
setLegacyAttrs: (r, t) => {
if (!r.identifierType) {
const e = t.getAttributeNS(S.OPF, "scheme");
e && (r.identifierType = { value: e });
}
}
},
{ name: "language", many: !0 },
{ name: "creator", ...Bt },
{ name: "contributor", ...Bt },
{ name: "publisher", ...z, props: ["file-as", J] },
{ name: "description", ...z, props: [J] },
{ name: "rights", ...z, props: [J] },
{ name: "date" },
{ name: "dcterms:modified", type: "meta" },
{
name: "subject",
many: !0,
...z,
props: ["term", "authority", J],
setLegacyAttrs: (r, t) => {
r.term ?? (r.term = t.getAttributeNS(S.OPF, "term")), r.authority ?? (r.authority = t.getAttributeNS(S.OPF, "authority"));
}
},
{ name: "source", many: !0 },
{
name: "belongs-to-collection",
type: "meta",
many: !0,
...z,
props: [
"collection-type",
"group-position",
"dcterms:identifier",
"file-as",
J,
{ name: "belongs-to-collection", recursive: !0 }
]
}
], Kt = (r) => {
var g, A;
const { $: t, $$: e } = mt(r, S.OPF), s = t(r.documentElement, "metadata"), n = Array.from(s.children), c = (m, l) => {
var _;
if (!l)
return null;
const { props: f = [], attrs: v = [] } = m, k = B(l);
if (!f.length && !v.length)
return k;
const x = l.getAttribute("id"), j = x ? n.filter(ct("refines", "#" + x)) : [], q = Object.fromEntries([["value", k]].concat(f.map((M) => {
const { many: gt, recursive: yt } = M, W = typeof M == "string" ? M : M.name, ot = ct("property", W), at = yt ? m : M;
return [At(W), gt ? j.filter(ot).map((wt) => c(at, wt)) : c(at, j.find(ot))];
})).concat(v.map((M) => [At(M), l.getAttribute(M)])));
return (_ = m.setLegacyAttrs) == null || _.call(m, q, l), q;
}, a = n.filter(ct("refines", null)), u = Object.fromEntries(Jt.map((m) => {
const { type: l, name: f, many: v } = m, k = l === "meta" ? (x) => x.namespaceURI === S.OPF && x.getAttribute("property") === f : (x) => x.namespaceURI === S.DC && x.localName === f;
return [At(f), v ? a.filter(k).map((x) => c(m, x)) : c(m, a.find(k))];
})), i = e(s, "meta"), p = (m) => i.filter(ct("property", (l) => l == null ? void 0 : l.startsWith(m))).map((l) => [l.getAttribute("property").replace(m, ""), l]), d = Object.fromEntries(p("rendition:").map(([m, l]) => [m, B(l)])), h = { narrator: [], duration: {} };
for (const [m, l] of p("media:")) {
const f = B(l);
m === "duration" ? h.duration[((A = (g = l.getAttribute("refines")) == null ? void 0 : g.split("#")) == null ? void 0 : A[1]) ?? ""] = Ct(f) : m === "active-class" ? h.activeClass = f : m === "narrator" ? h.narrator.push(f) : m === "playback-active-class" && (h.playbackActiveClass = f);
}
return { metadata: u, rendition: d, media: h };
}, Qt = (r, t = (e) => e) => {
var m;
const { $: e, $$: s, $$$: n } = mt(r, S.XHTML), c = (l) => l ? decodeURI(t(l)) : null, a = (l) => (f) => {
var _;
const v = e(f, "a") ?? e(f, "span"), k = e(f, "ol"), x = c(v == null ? void 0 : v.getAttribute("href")), q = { label: B(v) || (v == null ? void 0 : v.getAttribute("title")), href: x, subitems: u(k) };
return l && (q.type = (_ = v == null ? void 0 : v.getAttributeNS(S.EPUB, "type")) == null ? void 0 : _.split(/\s/)), q;
}, u = (l, f) => l ? s(l, "li").map(a(f)) : null, i = (l, f) => u(e(l, "ol"), f), p = n(r, "nav");
let d = null, h = null, g = null, A = [];
for (const l of p) {
const f = ((m = l.getAttributeNS(S.EPUB, "type")) == null ? void 0 : m.split(/\s/)) ?? [];
f.includes("toc") ? d ?? (d = i(l)) : f.includes("page-list") ? h ?? (h = i(l)) : f.includes("landmarks") ? g ?? (g = i(l, !0)) : A.push({
label: B(l.firstElementChild),
type: f,
list: i(l)
});
}
return { toc: d, pageList: h, landmarks: g, others: A };
}, Yt = (r, t = (e) => e) => {
const { $: e, $$: s } = mt(r, S.NCX), n = (i) => i ? decodeURI(t(i)) : null, c = (i) => {
const p = e(i, "navLabel"), d = e(i, "content"), h = B(p), g = n(d.getAttribute("src"));
if (i.localName === "navPoint") {
const A = s(i, "navPoint");
return { label: h, href: g, subitems: A.length ? A.map(c) : null };
}
return { label: h, href: g };
}, a = (i, p) => s(i, p).map(c), u = (i, p) => {
const d = e(r.documentElement, i);
return d ? a(d, p) : null;
};
return {
toc: u("navMap", "navPoint"),
pageList: u("pageList", "pageTarget"),
others: s(r.documentElement, "navList").map((i) => ({
label: B(e(i, "navLabel")),
list: a(i, "navTarget")
}))
};
}, Ct = (r) => {
if (!r)
return;
const t = r.split(":").map((a) => parseFloat(a));
if (t.length === 3) {
const [a, u, i] = t;
return a * 60 * 60 + u * 60 + i;
}
if (t.length === 2) {
const [a, u] = t;
return a * 60 + u;
}
const [e, s] = r.split(/(?=[^\d.])/), n = parseFloat(e), c = s === "h" ? 60 * 60 : s === "min" ? 60 : s === "ms" ? 1e-3 : 1;
return n * c;
};
var G, lt, Y, C, $, T, ht, ut, Tt, Pt, st, Lt, V, tt, F, K, pt, Mt, rt, It, H, Q;
class Zt extends EventTarget {
constructor(e, s) {
super();
b(this, Tt);
b(this, st);
b(this, V);
b(this, F);
b(this, pt);
b(this, rt);
b(this, H);
b(this, G, void 0);
b(this, lt, void 0);
b(this, Y, void 0);
b(this, C, void 0);
b(this, $, void 0);
b(this, T, void 0);
b(this, ht, 1);
b(this, ut, 1);
this.book = e, this.loadXML = s;
}
async start(e, s = () => !0) {
var u;
(u = o(this, T)) == null || u.pause();
const n = this.book.sections[e], c = n == null ? void 0 : n.id;
if (!c)
return;
const { mediaOverlay: a } = n;
if (!a)
return this.start(e + 1);
I(this, Y, e), await w(this, Tt, Pt).call(this, a);
for (let i = 0; i < o(this, G).length; i++) {
const { items: p } = o(this, G)[i];
for (let d = 0; d < p.length; d++)
if (p[d].text.split("#")[0] === c && s(p[d], d, p))
return w(this, H, Q).call(this, i, d).catch((h) => w(this, F, K).call(this, h));
}
}
pause() {
var e;
(e = o(this, T)) == null || e.pause();
}
resume() {
var e;
(e = o(this, T)) == null || e.play().catch((s) => w(this, F, K).call(this, s));
}
prev() {
o(this, $) > 0 ? w(this, H, Q).call(this, o(this, C), o(this, $) - 1) : o(this, C) > 0 ? w(this, H, Q).call(this, o(this, C) - 1, o(this, G)[o(this, C) - 1].items.length - 1) : o(this, Y) > 0 && this.start(o(this, Y) - 1, (e, s, n) => s === n.length - 1);
}
next() {
w(this, H, Q).call(this, o(this, C), o(this, $) + 1);
}
setVolume(e) {
I(this, ht, e), o(this, T) && (o(this, T).volume = e);
}
setRate(e) {
I(this, ut, e), o(this, T) && (o(this, T).playbackRate = e);
}
}
G = new WeakMap(), lt = new WeakMap(), Y = new WeakMap(), C = new WeakMap(), $ = new WeakMap(), T = new WeakMap(), ht = new WeakMap(), ut = new WeakMap(), Tt = new WeakSet(), Pt = async function(e) {
if (o(this, lt) === e)
return;
const s = await this.loadXML(e.href), n = (u) => u ? et(u, e.href) : null, { $: c, $$$: a } = mt(s, S.SMIL);
I(this, C, -1), I(this, $, -1), I(this, G, a(s, "par").reduce((u, i) => {
var l;
const p = n((l = c(i, "text")) == null ? void 0 : l.getAttribute("src")), d = c(i, "audio");
if (!p || !d)
return u;
const h = n(d.getAttribute("src")), g = Ct(d.getAttribute("clipBegin")), A = Ct(d.getAttribute("clipEnd")), m = u.at(-1);
return (m == null ? void 0 : m.src) === h ? m.items.push({ text: p, begin: g, end: A }) : u.push({ src: h, items: [{ text: p, begin: g, end: A }] }), u;
}, [])), I(this, lt, e);
}, st = new WeakSet(), Lt = function() {
return o(this, G)[o(this, C)];
}, V = new WeakSet(), tt = function() {
var e, s;
return (s = (e = o(this, st, Lt)) == null ? void 0 : e.items) == null ? void 0 : s[o(this, $)];
}, F = new WeakSet(), K = function(e) {
console.error(e), this.dispatchEvent(new CustomEvent("error", { detail: e }));
}, pt = new WeakSet(), Mt = function() {
this.dispatchEvent(new CustomEvent("highlight", { detail: o(this, V, tt) }));
}, rt = new WeakSet(), It = function() {
this.dispatchEvent(new CustomEvent("unhighlight", { detail: o(this, V, tt) }));
}, H = new WeakSet(), Q = async function(e, s) {
var u;
o(this, T) && (o(this, T).pause(), URL.revokeObjectURL(o(this, T).src), I(this, T, null)), I(this, C, e), I(this, $, s);
const n = (u = o(this, st, Lt)) == null ? void 0 : u.src;
if (!n || !o(this, V, tt))
return this.start(o(this, Y) + 1);
const c = URL.createObjectURL(await this.book.loadBlob(n)), a = new Audio(c);
I(this, T, a), a.addEventListener("timeupdate", () => {
var h, g;
if (a.paused)
return;
const i = a.currentTime, { items: p } = o(this, st, Lt);
if (i > ((h = o(this, V, tt)) == null ? void 0 : h.end) && (w(this, rt, It).call(this), o(this, $) === p.length - 1)) {
w(this, H, Q).call(this, o(this, C) + 1, 0).catch((A) => w(this, F, K).call(this, A));
return;
}
const d = o(this, $);
for (; ((g = p[o(this, $) + 1]) == null ? void 0 : g.begin) <= i; )
Rt(this, $)._++;
o(this, $) !== d && w(this, pt, Mt).call(this);
}), a.addEventListener("error", () => w(this, F, K).call(this, new Error(`Failed to load ${n}`))), a.addEventListener("playing", () => w(this, pt, Mt).call(this)), a.addEventListener("pause", () => w(this, rt, It).call(this)), a.addEventListener("ended", () => {
w(this, rt, It).call(this), URL.revokeObjectURL(c), I(this, T, null), w(this, H, Q).call(this, e + 1, 0).catch((i) => w(this, F, K).call(this, i));
}), a.addEventListener("canplaythrough", () => {
a.currentTime = o(this, V, tt).begin ?? 0, a.volume = o(this, ht), a.playbackRate = o(this, ut), a.play().catch((i) => w(this, F, K).call(this, i));
});
};
const jt = /([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})/, te = (r) => {
for (const t of r.getElementsByTagNameNS(S.DC, "identifier")) {
const [e] = B(t).split(":").slice(-1);
if (jt.test(e))
return e;
}
return "";
}, Ft = (r) => B(
r.getElementById(r.documentElement.getAttribute("unique-identifier")) ?? r.getElementsByTagNameNS(S.DC, "identifier")[0]
), kt = async (r, t, e) => {
const s = new Uint8Array(await e.slice(0, t).arrayBuffer());
t = Math.min(t, s.length);
for (var n = 0; n < t; n++)
s[n] = s[n] ^ r[n % r.length];
return new Blob([s, e.slice(t)], { type: e.type });
}, ee = async (r) => {
const t = new TextEncoder().encode(r), e = await globalThis.crypto.subtle.digest("SHA-1", t);
return new Uint8Array(e);
}, se = (r = ee) => ({
"http://www.idpf.org/2008/embedding": {
key: (t) => r(Ft(t).replaceAll(/[\u0020\u0009\u000d\u000a]/g, "")),
decode: (t, e) => kt(t, 1040, e)
},
"http://ns.adobe.com/pdf/enc#RC": {
key: (t) => {
const e = te(t).replaceAll("-", "");
return Uint8Array.from({ length: 16 }, (s, n) => parseInt(e.slice(n * 2, n * 2 + 2), 16));
},
decode: (t, e) => kt(t, 1024, e)
}
});
var dt, it, ft;
class re {
constructor(t) {
b(this, dt, /* @__PURE__ */ new Map());
b(this, it, /* @__PURE__ */ new Map());
b(this, ft, void 0);
I(this, ft, t);
}
async init(t, e) {
if (!t)
return;
const s = Array.from(
t.getElementsByTagNameNS(S.ENC, "EncryptedData"),
(n) => {
var c, a;
return {
algorithm: (c = n.getElementsByTagNameNS(S.ENC, "EncryptionMethod")[0]) == null ? void 0 : c.getAttribute("Algorithm"),
uri: (a = n.getElementsByTagNameNS(S.ENC, "CipherReference")[0]) == null ? void 0 : a.getAttribute("URI")
};
}
);
for (const { algorithm: n, uri: c } of s) {
if (!o(this, it).has(n)) {
const a = o(this, ft)[n];
if (!a) {
console.warn("Unknown encryption algorithm");
continue;
}
const u = await a.key(e);
o(this, it).set(n, (i) => a.decode(u, i));
}
o(this, dt).set(c, n);
}
}
getDecoder(t) {
return o(this, it).get(o(this, dt).get(t)) ?? ((e) => e);
}
}
dt = new WeakMap(), it = new WeakMap(), ft = new WeakMap();
class ie {
constructor({ opf: t, resolveHref: e }) {
var d, h, g, A, m;
this.opf = t;
const { $: s, $$: n, $$$: c } = mt(t, S.OPF), a = s(t.documentElement, "manifest"), u = s(t.documentElement, "spine"), i = n(u, "itemref");
this.manifest = n(a, "item").map(Et("href", "id", "media-type", "properties", "media-overlay")).map((l) => {
var f;
return l.href = e(l.href), l.properties = (f = l.properties) == null ? void 0 : f.split(/\s/), l;
}), this.spine = i.map(Et("idref", "id", "linear", "properties")).map((l) => {
var f;
return l.properties = (f = l.properties) == null ? void 0 : f.split(/\s/), l;
}), this.pageProgressionDirection = u.getAttribute("page-progression-direction"), this.navPath = (d = this.getItemByProperty("nav")) == null ? void 0 : d.href, this.ncxPath = (h = this.getItemByID(u.getAttribute("toc")) ?? this.manifest.find((l) => l.mediaType === N.NCX)) == null ? void 0 : h.href;
const p = s(t.documentElement, "guide");
p && (this.guide = n(p, "reference").map(Et("type", "title", "href")).map(({ type: l, title: f, href: v }) => ({
label: f,
type: l.split(/\s/),
href: e(v)
}))), this.cover = this.getItemByProperty("cover-image") ?? this.getItemByID((g = c(t, "meta").find(ct("name", "cover"))) == null ? void 0 : g.getAttribute("content")) ?? this.getItemByHref((m = (A = this.guide) == null ? void 0 : A.find((l) => l.type.includes("cover"))) == null ? void 0 : m.href), this.cfis = Xt(i);
}
getItemByID(t) {
return this.manifest.find((e) => e.id === 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 = qt(t), s = (e.parent ?? e).shift();
let n = Ut(this.opf, s);
n && n.nodeName !== "idref" && (s.at(-1).id = null, n = Ut(this.opf, s));
const c = n == null ? void 0 : n.getAttribute("idref");
return { index: this.spine.findIndex((i) => i.idref === c), anchor: (i) => _t(i, e) };
}
}
var D, X, O;
class ne {
constructor({ loadText: t, loadBlob: e, resources: s }) {
b(this, D, /* @__PURE__ */ new Map());
b(this, X, /* @__PURE__ */ new Map());
b(this, O, /* @__PURE__ */ new Map());
Nt(this, "allowScript", !1);
this.loadText = t, this.loadBlob = e, this.manifest = s.manifest, this.assets = s.manifest;
}
createURL(t, e, s, n) {
if (!e)
return "";
const c = URL.createObjectURL(new Blob([e], { type: s }));
if (o(this, D).set(t, c), o(this, O).set(t, 1), n) {
const a = o(this, X).get(n);
a ? a.push(t) : o(this, X).set(n, [t]);
}
return c;
}
ref(t, e) {
const s = o(this, X).get(e);
return s != null && s.includes(t) || (o(this, O).set(t, o(this, O).get(t) + 1), s ? s.push(t) : o(this, X).set(e, [t])), o(this, D).get(t);
}
unref(t) {
if (!o(this, O).has(t))
return;
const e = o(this, O).get(t) - 1;
if (e < 1) {
URL.revokeObjectURL(o(this, D).get(t)), o(this, D).delete(t), o(this, O).delete(t);
const s = o(this, X).get(t);
if (s)
for (; s.length; )
this.unref(s.pop());
o(this, X).delete(t);
} else
o(this, O).set(t, e);
}
// load manifest item, recursively loading all resources as needed
async loadItem(t, e = []) {
if (!t)
return null;
const { href: s, mediaType: n } = t, c = N.JS.test(t.mediaType);
if (c && !this.allowScript)
return null;
const a = e.at(-1);
return o(this, D).has(s) ? this.ref(s, a) : (c || [N.XHTML, N.HTML, N.CSS, N.SVG].includes(n)) && e.every((i) => i !== s) ? this.loadReplaced(t, e) : this.createURL(s, await this.loadBlob(s), n, a);
}
async loadHref(t, e, s = []) {
if (Ot(t))
return t;
const n = et(t, e), c = this.manifest.find((a) => a.href === n);
return c ? this.loadItem(c, s.concat(e)) : t;
}
async loadReplaced(t, e = []) {
const { href: s, mediaType: n } = t, c = e.at(-1), a = await this.loadText(s);
if (!a)
return null;
if ([N.XHTML, N.HTML, N.SVG].includes(n)) {
let i = new DOMParser().parseFromString(a, n);
if (n === N.XHTML && i.querySelector("parsererror") && (console.warn(i.querySelector("parsererror").innerText), t.mediaType = N.HTML, i = new DOMParser().parseFromString(a, t.mediaType)), [N.XHTML, N.SVG].includes(t.mediaType)) {
let h = i.firstChild;
for (; h instanceof ProcessingInstruction; ) {
if (h.data) {
const g = await St(
h.data,
/(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i,
(A, m, l, f) => this.loadHref(l, s, e).then((v) => `${m}${v}${f}`)
);
h.replaceWith(i.createProcessingInstruction(
h.target,
g
));
}
h = h.nextSibling;
}
}
const p = async (h, g) => h.setAttribute(
g,
await this.loadHref(h.getAttribute(g), s, e)
);
for (const h of i.querySelectorAll("link[href]"))
await p(h, "href");
for (const h of i.querySelectorAll("[src]"))
await p(h, "src");
for (const h of i.querySelectorAll("[poster]"))
await p(h, "poster");
for (const h of i.querySelectorAll("object[data]"))
await p(h, "data");
for (const h of i.querySelectorAll("[*|href]:not([href])"))
h.setAttributeNS(S.XLINK, "href", await this.loadHref(
h.getAttributeNS(S.XLINK, "href"),
s,
e
));
for (const h of i.querySelectorAll("style"))
h.textContent && (h.textContent = await this.replaceCSS(h.textContent, s, e));
for (const h of i.querySelectorAll("[style]"))
h.setAttribute(
"style",
await this.replaceCSS(h.getAttribute("style"), s, e)
);
const d = new XMLSerializer().serializeToString(i);
return this.createURL(s, d, t.mediaType, c);
}
const u = n === N.CSS ? await this.replaceCSS(a, s, e) : await this.replaceString(a, s, e);
return this.createURL(s, u, n, c);
}
async replaceCSS(t, e, s = []) {
const n = await St(
t,
/url\(\s*["']?([^'"\n]*?)\s*["']?\s*\)/gi,
(i, p) => this.loadHref(p, e, s).then((d) => `url("${d}")`)
), c = await St(
n,
/@import\s*["']([^"'\n]*?)["']/gi,
(i, p) => this.loadHref(p, e, s).then((d) => `@import "${d}"`)
), a = (window == null ? void 0 : window.innerWidth) ?? 800, u = (window == null ? void 0 : window.innerHeight) ?? 600;
return c.replace(new RegExp("(?<=[{\\s;])-epub-", "gi"), "").replace(/(\d*\.?\d+)vw/gi, (i, p) => parseFloat(p) * a / 100 + "px").replace(/(\d*\.?\d+)vh/gi, (i, p) => parseFloat(p) * u / 100 + "px").replace(/page-break-(after|before|inside)\s*:/gi, (i, p) => `-webkit-column-break-${p}:`).replace(/break-(after|before|inside)\s*:\s*(avoid-)?page/gi, (i, p, d) => `break-${p}: ${d ?? ""}column`);
}
// find & replace all possible relative paths for all assets without parsing
replaceString(t, e, s = []) {
const n = /* @__PURE__ */ new Map(), c = this.assets.map((u) => {
if (u.href === e)
return;
const i = Gt(Vt(e), u.href), p = encodeURI(i), d = "/" + u.href, h = encodeURI(d), g = /* @__PURE__ */ new Set([i, p, d, h]);
for (const A of g)
n.set(A, u);
return Array.from(g);
}).flat().filter((u) => u);
if (!c.length)
return t;
const a = new RegExp(c.map(Wt).join("|"), "g");
return St(t, a, async (u) => this.loadItem(
n.get(u.replace(/^\//, "")),
s.concat(e)
));
}
unloadItem(t) {
this.unref(t == null ? void 0 : t.href);
}
destroy() {
for (const t of o(this, D).values())
URL.revokeObjectURL(t);
}
}
D = new WeakMap(), X = new WeakMap(), O = new WeakMap();
const oe = (r, t) => r.getElementById(t) ?? r.querySelector(`[name="${CSS.escape(t)}"]`), ae = (r) => {
for (const t of r) {
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";
}
}, ce = (r) => r ? {
fixedLayout: B(r.querySelector('option[name="fixed-layout"]')),
openToSpread: B(r.querySelector('option[name="open-to-spread"]'))
} : null;
var Z, nt, U, P;
class ue {
constructor({ loadText: t, loadBlob: e, getSize: s, sha1: n }) {
b(this, U);
Nt(this, "parser", new DOMParser());
b(this, Z, void 0);
b(this, nt, void 0);
this.loadText = t, this.loadBlob = e, this.getSize = s, I(this, nt, new re(se(n)));
}
async init() {
var l, f, v, k, x, j, q, _, M, gt, yt, W, ot, at, wt;
const t = await w(this, U, P).call(this, "META-INF/container.xml");
if (!t)
throw new Error("Failed to load container file");
const e = Array.from(
t.getElementsByTagNameNS(S.CONTAINER, "rootfile"),
Et("full-path", "media-type")
).filter((y) => y.mediaType === "application/oebps-package+xml");
if (!e.length)
throw new Error("No package document defined in container");
const s = e[0].fullPath, n = await w(this, U, P).call(this, s);
if (!n)
throw new Error("Failed to load package document");
const c = await w(this, U, P).call(this, "META-INF/encryption.xml");
await o(this, nt).init(c, n), this.resources = new ie({
opf: n,
resolveHref: (y) => et(y, s)
}), I(this, Z, new ne({
loadText: this.loadText,
loadBlob: (y) => Promise.resolve(this.loadBlob(y)).then(o(this, nt).getDecoder(y)),
resources: this.resources
})), this.sections = this.resources.spine.map((y, E) => {
const { idref: L, linear: xt, properties: bt = [] } = y, R = this.resources.getItemByID(L);
return R ? {
id: R.href,
load: () => o(this, Z).loadItem(R),
unload: () => o(this, Z).unloadItem(R),
createDocument: () => this.loadDocument(R),
size: this.getSize(R.href),
cfi: this.resources.cfis[E],
linear: xt,
pageSpread: ae(bt),
resolveHref: (vt) => et(vt, R.href),
mediaOverlay: R.mediaOverlay ? this.resources.getItemByID(R.mediaOverlay) : null
} : (console.warn(`Could not find item with ID "${L}" in manifest`), null);
}).filter((y) => y);
const { navPath: a, ncxPath: u } = this.resources;
if (a)
try {
const y = (L) => et(L, a), E = Qt(await w(this, U, P).call(this, a), y);
this.toc = E.toc, this.pageList = E.pageList, this.landmarks = E.landmarks;
} catch (y) {
console.warn(y);
}
if (!this.toc && u)
try {
const y = (L) => et(L, u), E = Yt(await w(this, U, P).call(this, u), y);
this.toc = E.toc, this.pageList = E.pageList;
} catch (y) {
console.warn(y);
}
this.landmarks ?? (this.landmarks = this.resources.guide);
const { metadata: i, rendition: p, media: d } = Kt(n);
this.rendition = p, this.media = d, this.dir = this.resources.pageProgressionDirection;
const h = ce(
await w(this, U, P).call(this, "META-INF/com.apple.ibooks.display-options.xml") ?? await w(this, U, P).call(this, "META-INF/com.kobobooks.display-options.xml")
);
h && (h.fixedLayout === "true" && ((l = this.rendition).layout ?? (l.layout = "pre-paginated")), h.openToSpread === "false" && ((f = this.sections.find((y) => y.linear !== "no")).pageSpread ?? (f.pageSpread = this.dir === "rtl" ? "left" : "right"))), this.parsedMetadata = i;
const g = (v = i == null ? void 0 : i.title) == null ? void 0 : v[0];
this.metadata = {
title: g == null ? void 0 : g.value,
subtitle: (x = (k = i == null ? void 0 : i.title) == null ? void 0 : k.find((y) => y.titleType === "subtitle")) == null ? void 0 : x.value,
sortAs: g == null ? void 0 : g.fileAs,
language: i == null ? void 0 : i.language,
identifier: Ft(n),
description: (j = i == null ? void 0 : i.description) == null ? void 0 : j.value,
publisher: (q = i == null ? void 0 : i.publisher) == null ? void 0 : q.value,
published: i == null ? void 0 : i.date,
modified: i == null ? void 0 : i.dctermsModified,
subject: (M = (_ = i == null ? void 0 : i.subject) == null ? void 0 : _.filter(({ value: y, term: E }) => y || E)) == null ? void 0 : M.map(({ value: y, term: E, authority: L }) => ({ name: y, code: E, scheme: L })),
rights: (gt = i == null ? void 0 : i.rights) == null ? void 0 : gt.value
};
const A = {
art: "artist",
aut: "author",
bkp: "producer",
clr: "colorist",
edt: "editor",
ill: "illustrator",
nrt: "narrator",
trl: "translator",
pbl: "publisher"
}, m = (y) => (E) => {
var bt;
const L = [...new Set((bt = E.role) == null ? void 0 : bt.map(({ value: R, scheme: vt }) => (!vt || vt === "marc:relators" ? A[R] : null) ?? y))], xt = { name: E.value, sortAs: E.fileAs };
return [L != null && L.length ? L : [y], xt];
};
return (wt = (at = (yt = i == null ? void 0 : i.creator) == null ? void 0 : yt.map(m("author"))) == null ? void 0 : at.concat((ot = (W = i == null ? void 0 : i.contributor) == null ? void 0 : W.map) == null ? void 0 : ot.call(W, m("contributor")))) == null || wt.forEach(([y, E]) => y.forEach((L) => {
this.metadata[L] ? this.metadata[L].push(E) : this.metadata[L] = [E];
})), this;
}
async loadDocument(t) {
const e = await this.loadText(t.href);
return this.parser.parseFromString(e, t.mediaType);
}
getMediaOverlay() {
return new Zt(this, w(this, U, P).bind(this));
}
resolveCFI(t) {
return this.resources.resolveCFI(t);
}
resolveHref(t) {
const [e, s] = t.split("#"), n = this.resources.getItemByHref(decodeURI(e));
return n ? { index: this.resources.spine.findIndex(({ idref: u }) => u === n.id), anchor: s ? (u) => oe(u, 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 Ot(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 = o(this, Z)) == null || t.destroy();
}
}
Z = new WeakMap(), nt = new WeakMap(), U = new WeakSet(), P = async function(t) {
const e = await this.loadText(t);
if (!e)
return null;
const s = this.parser.parseFromString(e, N.XML);
if (s.querySelector("parsererror"))
throw new Error(`XML parsing error: ${t}
${s.querySelector("parsererror").innerText}`);
return s;
};
export {
ue as EPUB
};