UNPKG

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

264 lines (263 loc) 8.79 kB
const N = (a) => a ? a.replace(/[\t\n\f\r ]+/g, " ").replace(/^[\t\n\f\r ]+/, "").replace(/[\t\n\f\r ]+$/, "") : "", d = (a) => N(a == null ? void 0 : a.textContent), w = { XLINK: "http://www.w3.org/1999/xlink", EPUB: "http://www.idpf.org/2007/ops" }, S = { XML: "application/xml", XHTML: "application/xhtml+xml" }, m = { strong: ["strong", "self"], emphasis: ["em", "self"], style: ["span", "self"], a: "anchor", strikethrough: ["s", "self"], sub: ["sub", "self"], sup: ["sup", "self"], code: ["code", "self"], image: "image" }, D = { tr: ["tr", ["align"]], th: ["th", ["colspan", "rowspan", "align", "valign"]], td: ["td", ["colspan", "rowspan", "align", "valign"]] }, E = { epigraph: ["blockquote"], subtitle: ["h2", m], "text-author": ["p", m], date: ["p", m], stanza: "stanza" }, T = { title: ["header", { p: ["h1", m], "empty-line": ["br"] }], epigraph: ["blockquote", "self"], image: "image", annotation: ["aside"], section: ["section", "self"], p: ["p", m], poem: ["blockquote", E], subtitle: ["h2", m], cite: ["blockquote", "self"], "empty-line": ["br"], table: ["table", D], "text-author": ["p", m] }; E.epigraph.push(T); const $ = { image: "image", title: ["section", { p: ["h1", m], "empty-line": ["br"] }], epigraph: ["section", T], section: ["section", T] }, k = (a) => { const t = a.getAttributeNS(w.XLINK, "href"), [, o] = t.split("#"), s = a.getRootNode().getElementById(o); return s ? `data:${s.getAttribute("content-type")};base64,${s.textContent}` : t; }; class X { constructor(t) { this.fb2 = t, this.doc = document.implementation.createDocument(w.XHTML, "html"); } image(t) { const o = this.doc.createElement("img"); return o.alt = t.getAttribute("alt"), o.title = t.getAttribute("title"), o.setAttribute("src", k(t)), o; } anchor(t) { const o = this.convert(t, { a: ["a", m] }); return o.setAttribute("href", t.getAttributeNS(w.XLINK, "href")), t.getAttribute("type") === "note" && o.setAttributeNS(w.EPUB, "epub:type", "noteref"), o; } stanza(t) { const o = this.convert(t, { stanza: ["p", { title: ["header", { p: ["strong", m], "empty-line": ["br"] }], subtitle: ["p", m] }] }); for (const s of t.children) s.nodeName === "v" && (o.append(this.doc.createTextNode(s.textContent)), o.append(this.doc.createElement("br"))); return o; } convert(t, o) { if (t.nodeType === 3) return this.doc.createTextNode(t.textContent); if (t.nodeType === 4) return this.doc.createCDATASection(t.textContent); if (t.nodeType === 8) return this.doc.createComment(t.textContent); const s = o == null ? void 0 : o[t.nodeName]; if (!s) return null; if (typeof s == "string") return this[s](t); const [p, c] = s, u = this.doc.createElement(p); if (t.id && (u.id = t.id), u.classList.add(t.nodeName), Array.isArray(c)) for (const g of c) u.setAttribute(g, t.getAttribute(g)); const y = c === "self" ? o : Array.isArray(c) ? null : c; let b = t.firstChild; for (; b; ) { const g = this.convert(b, y); g && u.append(g), b = b.nextSibling; } return u; } } const z = async (a) => { var u; const t = await a.arrayBuffer(), o = new TextDecoder("utf-8").decode(t), s = new DOMParser(), p = s.parseFromString(o, S.XML), c = p.xmlEncoding || ((u = o.match(/^<\?xml\s+version\s*=\s*["']1.\d+"\s+encoding\s*=\s*["']([A-Za-z0-9._-]*)["']/)) == null ? void 0 : u[1]); if (c && c.toLowerCase() !== "utf-8") { const y = new TextDecoder(c).decode(t); return s.parseFromString(y, S.XML); } return p; }, O = URL.createObjectURL(new Blob([` @namespace epub "http://www.idpf.org/2007/ops"; body > img, section > img { display: block; margin: auto; } .title h1 { text-align: center; } body > section > .title, body.notesBodyType > .title { margin: 3em 0; } body.notesBodyType > section .title h1 { text-align: start; } body.notesBodyType > section .title { margin: 1em 0; } p { text-indent: 1em; margin: 0; } :not(p) + p, p:first-child { text-indent: 0; } .poem p { text-indent: 0; margin: 1em 0; } .text-author, .date { text-align: end; } .text-author:before { content: "—"; } table { border-collapse: collapse; } td, th { padding: .25em; } a[epub|type~="noteref"] { font-size: .75em; vertical-align: super; } body:not(.notesBodyType) > .title, body:not(.notesBodyType) > .epigraph { margin: 3em 0; } `], { type: "text/css" })), I = (a) => `<?xml version="1.0" encoding="utf-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head><link href="${O}" rel="stylesheet" type="text/css"/></head> <body>${a}</body> </html>`, v = "data-foliate-id", H = async (a) => { const t = {}, o = await z(a), s = new X(o), p = (e) => o.querySelector(e), c = (e) => [...o.querySelectorAll(e)], u = (e) => { const n = d(e.querySelector("nickname")); if (n) return n; const r = d(e.querySelector("first-name")), i = d(e.querySelector("middle-name")), l = d(e.querySelector("last-name")), f = [r, i, l].filter((h) => h).join(" "), x = l ? [l, [r, i].filter((h) => h).join(" ")].join(", ") : null; return { name: f, sortAs: x }; }, y = (e) => (e == null ? void 0 : e.getAttribute("value")) ?? d(e), b = p("title-info annotation"); if (t.metadata = { title: d(p("title-info book-title")), identifier: d(p("document-info id")), language: d(p("title-info lang")), author: c("title-info author").map(u), translator: c("title-info translator").map(u), producer: c("document-info author").map(u).concat(c("document-info program-used").map(d)), publisher: d(p("publish-info publisher")), published: y(p("title-info date")), modified: y(p("document-info date")), description: b ? s.convert( b, { annotation: ["div", T] } ).innerHTML : null, subject: c("title-info genre").map(d) }, p("coverpage image")) { const e = k(p("coverpage image")); t.getCover = () => fetch(e).then((n) => n.blob()); } else t.getCover = () => null; const g = Array.from(o.querySelectorAll("body"), (e) => { const n = s.convert(e, { body: ["body", $] }); return [Array.from(n.children, (r) => { const i = [r, ...r.querySelectorAll("[id]")].map((l) => l.id); return { el: r, ids: i }; }), n]; }), C = [], q = g[0][0].map(({ el: e, ids: n }) => { const r = Array.from( e.querySelectorAll(":scope > section > .title"), (i, l) => (i.setAttribute(v, l), { title: d(i), index: l }) ); return { ids: n, titles: r, el: e }; }).concat(g.slice(1).map(([e, n]) => { const r = e.map((i) => i.ids).flat(); return n.classList.add("notesBodyType"), { ids: r, el: n, linear: "no" }; })).map(({ ids: e, titles: n, el: r, linear: i }) => { var M; const l = I(r.outerHTML), f = new Blob([l], { type: S.XHTML }), x = URL.createObjectURL(f); C.push(x); const h = N( ((M = r.querySelector(".title, .subtitle, p")) == null ? void 0 : M.textContent) ?? (r.classList.contains("title") ? r.textContent : "") ); return { ids: e, title: h, titles: n, load: () => x, createDocument: () => new DOMParser().parseFromString(l, S.XHTML), // doo't count image data as it'd skew the size too much size: f.size - Array.from( r.querySelectorAll("[src]"), (L) => { var A; return ((A = L.getAttribute("src")) == null ? void 0 : A.length) ?? 0; } ).reduce((L, A) => L + A, 0), linear: i }; }), B = /* @__PURE__ */ new Map(); return t.sections = q.map((e, n) => { const { ids: r, load: i, createDocument: l, size: f, linear: x } = e; for (const h of r) h && B.set(h, n); return { id: n, load: i, createDocument: l, size: f, linear: x }; }), t.toc = q.map(({ title: e, titles: n }, r) => { const i = r.toString(); return { label: e, href: i, subitems: n != null && n.length ? n.map(({ title: l, index: f }) => ({ label: l, href: `${i}#${f}` })) : null }; }).filter((e) => e), t.resolveHref = (e) => { const [n, r] = e.split("#"); return n ? { index: Number(n), anchor: (i) => i.querySelector(`[${v}="${r}"]`) } : { index: B.get(r), anchor: (i) => i.getElementById(r) }; }, t.splitTOCHref = (e) => { var n; return ((n = e == null ? void 0 : e.split("#")) == null ? void 0 : n.map((r) => Number(r))) ?? []; }, t.getTOCFragment = (e, n) => e.querySelector(`[${v}="${n}"]`), t.destroy = () => { for (const e of C) URL.revokeObjectURL(e); }, t; }; export { H as makeFB2 };