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