vue-book-reader 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,969 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const view = require("./view--95lChht.cjs");
4
- const NS$2 = {
5
- CONTAINER: "urn:oasis:names:tc:opendocument:xmlns:container",
6
- XHTML: "http://www.w3.org/1999/xhtml",
7
- OPF: "http://www.idpf.org/2007/opf",
8
- EPUB: "http://www.idpf.org/2007/ops",
9
- DC: "http://purl.org/dc/elements/1.1/",
10
- ENC: "http://www.w3.org/2001/04/xmlenc#",
11
- NCX: "http://www.daisy.org/z3986/2005/ncx/",
12
- XLINK: "http://www.w3.org/1999/xlink",
13
- SMIL: "http://www.w3.org/ns/SMIL"
14
- };
15
- const MIME$2 = {
16
- XML: "application/xml",
17
- NCX: "application/x-dtbncx+xml",
18
- XHTML: "application/xhtml+xml",
19
- HTML: "text/html",
20
- CSS: "text/css",
21
- SVG: "image/svg+xml",
22
- JS: /\/(x-)?(javascript|ecmascript)/
23
- };
24
- const PREFIX = {
25
- a11y: "http://www.idpf.org/epub/vocab/package/a11y/#",
26
- dcterms: "http://purl.org/dc/terms/",
27
- marc: "http://id.loc.gov/vocabulary/",
28
- media: "http://www.idpf.org/epub/vocab/overlays/#",
29
- onix: "http://www.editeur.org/ONIX/book/codelists/current.html#",
30
- rendition: "http://www.idpf.org/vocab/rendition/#",
31
- schema: "http://schema.org/",
32
- xsd: "http://www.w3.org/2001/XMLSchema#",
33
- msv: "http://www.idpf.org/epub/vocab/structure/magazine/#",
34
- prism: "http://www.prismstandard.org/specifications/3.0/PRISM_CV_Spec_3.0.htm#"
35
- };
36
- const RELATORS = {
37
- art: "artist",
38
- aut: "author",
39
- clr: "colorist",
40
- edt: "editor",
41
- ill: "illustrator",
42
- nrt: "narrator",
43
- trl: "translator",
44
- pbl: "publisher"
45
- };
46
- const ONIX5 = {
47
- "02": "isbn",
48
- "06": "doi",
49
- "15": "isbn",
50
- "26": "doi",
51
- "34": "issn"
52
- };
53
- const camel = (x) => x.toLowerCase().replace(/[-:](.)/g, (_, g) => g.toUpperCase());
54
- const normalizeWhitespace$2 = (str) => str ? str.replace(/[\t\n\f\r ]+/g, " ").replace(/^[\t\n\f\r ]+/, "").replace(/[\t\n\f\r ]+$/, "") : "";
55
- const filterAttribute = (attr, value, isList) => isList ? (el) => {
56
- var _a, _b;
57
- return (_b = (_a = el.getAttribute(attr)) == null ? void 0 : _a.split(/\s/)) == null ? void 0 : _b.includes(value);
58
- } : typeof value === "function" ? (el) => value(el.getAttribute(attr)) : (el) => el.getAttribute(attr) === value;
59
- const getAttributes = (...xs) => (el) => el ? Object.fromEntries(xs.map((x) => [camel(x), el.getAttribute(x)])) : null;
60
- const getElementText$1 = (el) => normalizeWhitespace$2(el == null ? void 0 : el.textContent);
61
- const childGetter = (doc, ns) => {
62
- const useNS = doc.lookupNamespaceURI(null) === ns || doc.lookupPrefix(ns);
63
- const f = useNS ? (el, name) => (el2) => el2.namespaceURI === ns && el2.localName === name : (el, name) => (el2) => el2.localName === name;
64
- return {
65
- $: (el, name) => [...el.children].find(f(el, name)),
66
- $$: (el, name) => [...el.children].filter(f(el, name)),
67
- $$$: useNS ? (el, name) => [...el.getElementsByTagNameNS(ns, name)] : (el, name) => [...el.getElementsByTagName(name)]
68
- };
69
- };
70
- const resolveURL = (url, relativeTo) => {
71
- try {
72
- if (relativeTo.includes(":")) return new URL(url, relativeTo);
73
- const root = "https://invalid.invalid/";
74
- const obj = new URL(url, root + relativeTo);
75
- obj.search = "";
76
- return decodeURI(obj.href.replace(root, ""));
77
- } catch (e) {
78
- console.warn(e);
79
- return url;
80
- }
81
- };
82
- const isExternal = (uri) => /^(?!blob)\w+:/i.test(uri);
83
- const pathRelative = (from, to) => {
84
- if (!from) return to;
85
- const as = from.replace(/\/$/, "").split("/");
86
- const bs = to.replace(/\/$/, "").split("/");
87
- const i = (as.length > bs.length ? as : bs).findIndex((_, i2) => as[i2] !== bs[i2]);
88
- return i < 0 ? "" : Array(as.length - i).fill("..").concat(bs.slice(i)).join("/");
89
- };
90
- const pathDirname = (str) => str.slice(0, str.lastIndexOf("/") + 1);
91
- const replaceSeries$1 = async (str, regex, f) => {
92
- const matches = [];
93
- str.replace(regex, (...args) => (matches.push(args), null));
94
- const results = [];
95
- for (const args of matches) results.push(await f(...args));
96
- return str.replace(regex, () => results.shift());
97
- };
98
- const regexEscape = (str) => str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
99
- const tidy = (obj) => {
100
- for (const [key, val] of Object.entries(obj))
101
- if (val == null) delete obj[key];
102
- else if (Array.isArray(val)) {
103
- obj[key] = val.filter((x) => x).map((x) => typeof x === "object" && !Array.isArray(x) ? tidy(x) : x);
104
- if (!obj[key].length) delete obj[key];
105
- else if (obj[key].length === 1) obj[key] = obj[key][0];
106
- } else if (typeof val === "object") {
107
- obj[key] = tidy(val);
108
- if (!Object.keys(val).length) delete obj[key];
109
- }
110
- const keys = Object.keys(obj);
111
- if (keys.length === 1 && keys[0] === "name") return obj[keys[0]];
112
- return obj;
113
- };
114
- const getPrefixes = (doc) => {
115
- const map = new Map(Object.entries(PREFIX));
116
- const value = doc.documentElement.getAttributeNS(NS$2.EPUB, "prefix") || doc.documentElement.getAttribute("prefix");
117
- if (value) for (const [, prefix, url] of value.matchAll(/(.+): +(.+)[ \t\r\n]*/g)) map.set(prefix, url);
118
- return map;
119
- };
120
- const getPropertyURL = (value, prefixes) => {
121
- if (!value) return null;
122
- const [a, b] = value.split(":");
123
- const prefix = b ? a : null;
124
- const reference = b ? b : a;
125
- const baseURL = prefixes.get(prefix);
126
- return baseURL ? baseURL + reference : null;
127
- };
128
- const getMetadata = (opf) => {
129
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
130
- const { $ } = childGetter(opf, NS$2.OPF);
131
- const $metadata = $(opf.documentElement, "metadata");
132
- const els = Object.groupBy($metadata.children, (el) => el.namespaceURI === NS$2.DC ? "dc" : el.namespaceURI === NS$2.OPF && el.localName === "meta" ? el.hasAttribute("name") ? "legacyMeta" : "meta" : "");
133
- const baseLang = $metadata.getAttribute("xml:lang") ?? opf.documentElement.getAttribute("xml:lang") ?? "und";
134
- const prefixes = getPrefixes(opf);
135
- const parse = (el) => {
136
- const property = el.getAttribute("property");
137
- const scheme = el.getAttribute("scheme");
138
- return {
139
- property: getPropertyURL(property, prefixes) ?? property,
140
- scheme: getPropertyURL(scheme, prefixes) ?? scheme,
141
- lang: el.getAttribute("xml:lang"),
142
- value: getElementText$1(el),
143
- props: getProperties(el),
144
- // `opf:` attributes from EPUB 2 & EPUB 3.1 (removed in EPUB 3.2)
145
- attrs: Object.fromEntries(Array.from(el.attributes).filter((attr) => attr.namespaceURI === NS$2.OPF).map((attr) => [attr.localName, attr.value]))
146
- };
147
- };
148
- const refines = Map.groupBy(els.meta ?? [], (el) => el.getAttribute("refines"));
149
- const getProperties = (el) => {
150
- const els2 = refines.get(el ? "#" + el.getAttribute("id") : null);
151
- if (!els2) return null;
152
- return Object.groupBy(els2.map(parse), (x) => x.property);
153
- };
154
- const dc = Object.fromEntries(Object.entries(Object.groupBy(els.dc, (el) => el.localName)).map(([name, els2]) => [name, els2.map(parse)]));
155
- const properties = getProperties() ?? {};
156
- const legacyMeta = Object.fromEntries(((_a = els.legacyMeta) == null ? void 0 : _a.map((el) => [el.getAttribute("name"), el.getAttribute("content")])) ?? []);
157
- const one = (x) => {
158
- var _a2;
159
- return (_a2 = x == null ? void 0 : x[0]) == null ? void 0 : _a2.value;
160
- };
161
- const prop = (x, p) => {
162
- var _a2;
163
- return one((_a2 = x == null ? void 0 : x.props) == null ? void 0 : _a2[p]);
164
- };
165
- const makeLanguageMap = (x) => {
166
- var _a2;
167
- if (!x) return null;
168
- const alts = ((_a2 = x.props) == null ? void 0 : _a2["alternate-script"]) ?? [];
169
- const altRep = x.attrs["alt-rep"];
170
- if (!alts.length && (!x.lang || x.lang === baseLang) && !altRep) return x.value;
171
- const map = { [x.lang ?? baseLang]: x.value };
172
- if (altRep) map[x.attrs["alt-rep-lang"]] = altRep;
173
- for (const y of alts) map[y.lang] ??= y.value;
174
- return map;
175
- };
176
- const makeContributor = (x) => {
177
- var _a2, _b2, _c2, _d2, _e2;
178
- return x ? {
179
- name: makeLanguageMap(x),
180
- sortAs: makeLanguageMap((_b2 = (_a2 = x.props) == null ? void 0 : _a2["file-as"]) == null ? void 0 : _b2[0]) ?? x.attrs["file-as"],
181
- role: ((_e2 = (_d2 = (_c2 = x.props) == null ? void 0 : _c2.role) == null ? void 0 : _d2.filter((x2) => x2.scheme === PREFIX.marc + "relators")) == null ? void 0 : _e2.map((x2) => x2.value)) ?? [x.attrs.role],
182
- code: prop(x, "term") ?? x.attrs.term,
183
- scheme: prop(x, "authority") ?? x.attrs.authority
184
- } : null;
185
- };
186
- const makeCollection = (x) => {
187
- var _a2;
188
- return {
189
- name: makeLanguageMap(x),
190
- // NOTE: webpub requires number but EPUB allows values like "2.2.1"
191
- position: one((_a2 = x.props) == null ? void 0 : _a2["group-position"])
192
- };
193
- };
194
- const makeAltIdentifier = (x) => {
195
- var _a2;
196
- const { value } = x;
197
- if (/^urn:/i.test(value)) return value;
198
- if (/^doi:/i.test(value)) return `urn:${value}`;
199
- const type = (_a2 = x.props) == null ? void 0 : _a2["identifier-type"];
200
- if (!type) {
201
- const scheme = x.attrs.scheme;
202
- if (!scheme) return value;
203
- if (/^(doi|isbn|uuid)$/i.test(scheme)) return `urn:${scheme}:${value}`;
204
- return { scheme, value };
205
- }
206
- if (type.scheme === PREFIX.onix + "codelist5") {
207
- const nid = ONIX5[type.value];
208
- if (nid) return `urn:${nid}:${value}`;
209
- }
210
- return value;
211
- };
212
- const belongsTo = Object.groupBy(
213
- properties["belongs-to-collection"] ?? [],
214
- (x) => prop(x, "collection-type") === "series" ? "series" : "collection"
215
- );
216
- const mainTitle = ((_b = dc.title) == null ? void 0 : _b.find((x) => prop(x, "title-type") === "main")) ?? ((_c = dc.title) == null ? void 0 : _c[0]);
217
- const metadata = {
218
- identifier: getIdentifier(opf),
219
- title: makeLanguageMap(mainTitle),
220
- sortAs: makeLanguageMap((_e = (_d = mainTitle == null ? void 0 : mainTitle.props) == null ? void 0 : _d["file-as"]) == null ? void 0 : _e[0]) ?? ((_f = mainTitle == null ? void 0 : mainTitle.attrs) == null ? void 0 : _f["file-as"]) ?? (legacyMeta == null ? void 0 : legacyMeta["calibre:title_sort"]),
221
- subtitle: (_h = (_g = dc.title) == null ? void 0 : _g.find((x) => prop(x, "title-type") === "subtitle")) == null ? void 0 : _h.value,
222
- language: (_i = dc.language) == null ? void 0 : _i.map((x) => x.value),
223
- description: one(dc.description),
224
- publisher: makeContributor((_j = dc.publisher) == null ? void 0 : _j[0]),
225
- published: ((_l = (_k = dc.date) == null ? void 0 : _k.find((x) => x.attrs.event === "publication")) == null ? void 0 : _l.value) ?? one(dc.date),
226
- modified: one(properties[PREFIX.dcterms + "modified"]) ?? ((_n = (_m = dc.date) == null ? void 0 : _m.find((x) => x.attrs.event === "modification")) == null ? void 0 : _n.value),
227
- subject: (_o = dc.subject) == null ? void 0 : _o.map(makeContributor),
228
- belongsTo: {
229
- collection: (_p = belongsTo.collection) == null ? void 0 : _p.map(makeCollection),
230
- series: ((_q = belongsTo.series) == null ? void 0 : _q.map(makeCollection)) ?? (legacyMeta == null ? void 0 : legacyMeta["calibre:series"]) ? {
231
- name: legacyMeta == null ? void 0 : legacyMeta["calibre:series"],
232
- position: parseFloat(legacyMeta == null ? void 0 : legacyMeta["calibre:series_index"])
233
- } : null
234
- },
235
- altIdentifier: (_r = dc.identifier) == null ? void 0 : _r.map(makeAltIdentifier),
236
- source: (_s = dc.source) == null ? void 0 : _s.map(makeAltIdentifier),
237
- // NOTE: not in webpub schema
238
- rights: one(dc.rights)
239
- // NOTE: not in webpub schema
240
- };
241
- const remapContributor = (defaultKey) => (x) => {
242
- var _a2;
243
- const keys = new Set((_a2 = x.role) == null ? void 0 : _a2.map((role) => RELATORS[role] ?? defaultKey));
244
- return [keys.size ? keys : [defaultKey], x];
245
- };
246
- for (const [keys, val] of [].concat(
247
- ((_u = (_t = dc.creator) == null ? void 0 : _t.map(makeContributor)) == null ? void 0 : _u.map(remapContributor("author"))) ?? [],
248
- ((_w = (_v = dc.contributor) == null ? void 0 : _v.map(makeContributor)) == null ? void 0 : _w.map(remapContributor("contributor"))) ?? []
249
- ))
250
- for (const key of keys)
251
- if (metadata[key]) metadata[key].push(val);
252
- else metadata[key] = [val];
253
- tidy(metadata);
254
- if (metadata.altIdentifier === metadata.identifier)
255
- delete metadata.altIdentifier;
256
- const rendition = {};
257
- const media = {};
258
- for (const [key, val] of Object.entries(properties)) {
259
- if (key.startsWith(PREFIX.rendition))
260
- rendition[camel(key.replace(PREFIX.rendition, ""))] = one(val);
261
- else if (key.startsWith(PREFIX.media))
262
- media[camel(key.replace(PREFIX.media, ""))] = one(val);
263
- }
264
- if (media.duration) media.duration = parseClock(media.duration);
265
- return { metadata, rendition, media };
266
- };
267
- const parseNav = (doc, resolve = (f) => f) => {
268
- var _a;
269
- const { $, $$, $$$ } = childGetter(doc, NS$2.XHTML);
270
- const resolveHref = (href) => href ? decodeURI(resolve(href)) : null;
271
- const parseLI = (getType) => ($li) => {
272
- var _a2;
273
- const $a = $($li, "a") ?? $($li, "span");
274
- const $ol = $($li, "ol");
275
- const href = resolveHref($a == null ? void 0 : $a.getAttribute("href"));
276
- const label = getElementText$1($a) || ($a == null ? void 0 : $a.getAttribute("title"));
277
- const result = { label, href, subitems: parseOL($ol) };
278
- if (getType) result.type = (_a2 = $a == null ? void 0 : $a.getAttributeNS(NS$2.EPUB, "type")) == null ? void 0 : _a2.split(/\s/);
279
- return result;
280
- };
281
- const parseOL = ($ol, getType) => $ol ? $$($ol, "li").map(parseLI(getType)) : null;
282
- const parseNav2 = ($nav, getType) => parseOL($($nav, "ol"), getType);
283
- const $$nav = $$$(doc, "nav");
284
- let toc = null, pageList = null, landmarks = null, others = [];
285
- for (const $nav of $$nav) {
286
- const type = ((_a = $nav.getAttributeNS(NS$2.EPUB, "type")) == null ? void 0 : _a.split(/\s/)) ?? [];
287
- if (type.includes("toc")) toc ??= parseNav2($nav);
288
- else if (type.includes("page-list")) pageList ??= parseNav2($nav);
289
- else if (type.includes("landmarks")) landmarks ??= parseNav2($nav, true);
290
- else others.push({
291
- label: getElementText$1($nav.firstElementChild),
292
- type,
293
- list: parseNav2($nav)
294
- });
295
- }
296
- return { toc, pageList, landmarks, others };
297
- };
298
- const parseNCX = (doc, resolve = (f) => f) => {
299
- const { $, $$ } = childGetter(doc, NS$2.NCX);
300
- const resolveHref = (href) => href ? decodeURI(resolve(href)) : null;
301
- const parseItem = (el) => {
302
- const $label = $(el, "navLabel");
303
- const $content = $(el, "content");
304
- const label = getElementText$1($label);
305
- const href = resolveHref($content.getAttribute("src"));
306
- if (el.localName === "navPoint") {
307
- const els = $$(el, "navPoint");
308
- return { label, href, subitems: els.length ? els.map(parseItem) : null };
309
- }
310
- return { label, href };
311
- };
312
- const parseList = (el, itemName) => $$(el, itemName).map(parseItem);
313
- const getSingle = (container, itemName) => {
314
- const $container = $(doc.documentElement, container);
315
- return $container ? parseList($container, itemName) : null;
316
- };
317
- return {
318
- toc: getSingle("navMap", "navPoint"),
319
- pageList: getSingle("pageList", "pageTarget"),
320
- others: $$(doc.documentElement, "navList").map((el) => ({
321
- label: getElementText$1($(el, "navLabel")),
322
- list: parseList(el, "navTarget")
323
- }))
324
- };
325
- };
326
- const parseClock = (str) => {
327
- if (!str) return;
328
- const parts = str.split(":").map((x2) => parseFloat(x2));
329
- if (parts.length === 3) {
330
- const [h, m, s] = parts;
331
- return h * 60 * 60 + m * 60 + s;
332
- }
333
- if (parts.length === 2) {
334
- const [m, s] = parts;
335
- return m * 60 + s;
336
- }
337
- const [x, unit] = str.split(/(?=[^\d.])/);
338
- const n = parseFloat(x);
339
- const f = unit === "h" ? 60 * 60 : unit === "min" ? 60 : unit === "ms" ? 1e-3 : 1;
340
- return n * f;
341
- };
342
- class MediaOverlay extends EventTarget {
343
- #entries;
344
- #lastMediaOverlayItem;
345
- #sectionIndex;
346
- #audioIndex;
347
- #itemIndex;
348
- #audio;
349
- #volume = 1;
350
- #rate = 1;
351
- #state;
352
- constructor(book, loadXML) {
353
- super();
354
- this.book = book;
355
- this.loadXML = loadXML;
356
- }
357
- async #loadSMIL(item) {
358
- if (this.#lastMediaOverlayItem === item) return;
359
- const doc = await this.loadXML(item.href);
360
- const resolve = (href) => href ? resolveURL(href, item.href) : null;
361
- const { $, $$$ } = childGetter(doc, NS$2.SMIL);
362
- this.#audioIndex = -1;
363
- this.#itemIndex = -1;
364
- this.#entries = $$$(doc, "par").reduce((arr, $par) => {
365
- var _a;
366
- const text = resolve((_a = $($par, "text")) == null ? void 0 : _a.getAttribute("src"));
367
- const $audio = $($par, "audio");
368
- if (!text || !$audio) return arr;
369
- const src = resolve($audio.getAttribute("src"));
370
- const begin = parseClock($audio.getAttribute("clipBegin"));
371
- const end = parseClock($audio.getAttribute("clipEnd"));
372
- const last = arr.at(-1);
373
- if ((last == null ? void 0 : last.src) === src) last.items.push({ text, begin, end });
374
- else arr.push({ src, items: [{ text, begin, end }] });
375
- return arr;
376
- }, []);
377
- this.#lastMediaOverlayItem = item;
378
- }
379
- get #activeAudio() {
380
- return this.#entries[this.#audioIndex];
381
- }
382
- get #activeItem() {
383
- var _a, _b;
384
- return (_b = (_a = this.#activeAudio) == null ? void 0 : _a.items) == null ? void 0 : _b[this.#itemIndex];
385
- }
386
- #error(e) {
387
- console.error(e);
388
- this.dispatchEvent(new CustomEvent("error", { detail: e }));
389
- }
390
- #highlight() {
391
- this.dispatchEvent(new CustomEvent("highlight", { detail: this.#activeItem }));
392
- }
393
- #unhighlight() {
394
- this.dispatchEvent(new CustomEvent("unhighlight", { detail: this.#activeItem }));
395
- }
396
- async #play(audioIndex, itemIndex) {
397
- var _a;
398
- this.#stop();
399
- this.#audioIndex = audioIndex;
400
- this.#itemIndex = itemIndex;
401
- const src = (_a = this.#activeAudio) == null ? void 0 : _a.src;
402
- if (!src || !this.#activeItem) return this.start(this.#sectionIndex + 1);
403
- const url = URL.createObjectURL(await this.book.loadBlob(src));
404
- const audio = new Audio(url);
405
- this.#audio = audio;
406
- audio.volume = this.#volume;
407
- audio.playbackRate = this.#rate;
408
- audio.addEventListener("timeupdate", () => {
409
- var _a2, _b;
410
- if (audio.paused) return;
411
- const t = audio.currentTime;
412
- const { items } = this.#activeAudio;
413
- if (t > ((_a2 = this.#activeItem) == null ? void 0 : _a2.end)) {
414
- this.#unhighlight();
415
- if (this.#itemIndex === items.length - 1) {
416
- this.#play(this.#audioIndex + 1, 0).catch((e) => this.#error(e));
417
- return;
418
- }
419
- }
420
- const oldIndex = this.#itemIndex;
421
- while (((_b = items[this.#itemIndex + 1]) == null ? void 0 : _b.begin) <= t) this.#itemIndex++;
422
- if (this.#itemIndex !== oldIndex) this.#highlight();
423
- });
424
- audio.addEventListener("error", () => this.#error(new Error(`Failed to load ${src}`)));
425
- audio.addEventListener("playing", () => this.#highlight());
426
- audio.addEventListener("ended", () => {
427
- this.#unhighlight();
428
- URL.revokeObjectURL(url);
429
- this.#audio = null;
430
- this.#play(audioIndex + 1, 0).catch((e) => this.#error(e));
431
- });
432
- if (this.#state === "paused") {
433
- this.#highlight();
434
- audio.currentTime = this.#activeItem.begin ?? 0;
435
- } else audio.addEventListener("canplaythrough", () => {
436
- audio.currentTime = this.#activeItem.begin ?? 0;
437
- this.#state = "playing";
438
- audio.play().catch((e) => this.#error(e));
439
- }, { once: true });
440
- }
441
- async start(sectionIndex, filter = () => true) {
442
- var _a;
443
- (_a = this.#audio) == null ? void 0 : _a.pause();
444
- const section = this.book.sections[sectionIndex];
445
- const href = section == null ? void 0 : section.id;
446
- if (!href) return;
447
- const { mediaOverlay } = section;
448
- if (!mediaOverlay) return this.start(sectionIndex + 1);
449
- this.#sectionIndex = sectionIndex;
450
- await this.#loadSMIL(mediaOverlay);
451
- for (let i = 0; i < this.#entries.length; i++) {
452
- const { items } = this.#entries[i];
453
- for (let j = 0; j < items.length; j++) {
454
- if (items[j].text.split("#")[0] === href && filter(items[j], j, items))
455
- return this.#play(i, j).catch((e) => this.#error(e));
456
- }
457
- }
458
- }
459
- pause() {
460
- var _a;
461
- this.#state = "paused";
462
- (_a = this.#audio) == null ? void 0 : _a.pause();
463
- }
464
- resume() {
465
- var _a;
466
- this.#state = "playing";
467
- (_a = this.#audio) == null ? void 0 : _a.play().catch((e) => this.#error(e));
468
- }
469
- #stop() {
470
- if (this.#audio) {
471
- this.#audio.pause();
472
- URL.revokeObjectURL(this.#audio.src);
473
- this.#audio = null;
474
- this.#unhighlight();
475
- }
476
- }
477
- stop() {
478
- this.#state = "stopped";
479
- this.#stop();
480
- }
481
- prev() {
482
- if (this.#itemIndex > 0) this.#play(this.#audioIndex, this.#itemIndex - 1);
483
- else if (this.#audioIndex > 0) this.#play(
484
- this.#audioIndex - 1,
485
- this.#entries[this.#audioIndex - 1].items.length - 1
486
- );
487
- else if (this.#sectionIndex > 0)
488
- this.start(this.#sectionIndex - 1, (_, i, items) => i === items.length - 1);
489
- }
490
- next() {
491
- this.#play(this.#audioIndex, this.#itemIndex + 1);
492
- }
493
- setVolume(volume) {
494
- this.#volume = volume;
495
- if (this.#audio) this.#audio.volume = volume;
496
- }
497
- setRate(rate) {
498
- this.#rate = rate;
499
- if (this.#audio) this.#audio.playbackRate = rate;
500
- }
501
- }
502
- const isUUID = /([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})/;
503
- const getUUID = (opf) => {
504
- for (const el of opf.getElementsByTagNameNS(NS$2.DC, "identifier")) {
505
- const [id] = getElementText$1(el).split(":").slice(-1);
506
- if (isUUID.test(id)) return id;
507
- }
508
- return "";
509
- };
510
- const getIdentifier = (opf) => getElementText$1(
511
- opf.getElementById(opf.documentElement.getAttribute("unique-identifier")) ?? opf.getElementsByTagNameNS(NS$2.DC, "identifier")[0]
512
- );
513
- const deobfuscate = async (key, length, blob) => {
514
- const array = new Uint8Array(await blob.slice(0, length).arrayBuffer());
515
- length = Math.min(length, array.length);
516
- for (var i = 0; i < length; i++) array[i] = array[i] ^ key[i % key.length];
517
- return new Blob([array, blob.slice(length)], { type: blob.type });
518
- };
519
- const WebCryptoSHA1 = async (str) => {
520
- const data = new TextEncoder().encode(str);
521
- const buffer = await globalThis.crypto.subtle.digest("SHA-1", data);
522
- return new Uint8Array(buffer);
523
- };
524
- const deobfuscators = (sha1 = WebCryptoSHA1) => ({
525
- "http://www.idpf.org/2008/embedding": {
526
- key: (opf) => sha1(getIdentifier(opf).replaceAll(/[\u0020\u0009\u000d\u000a]/g, "")),
527
- decode: (key, blob) => deobfuscate(key, 1040, blob)
528
- },
529
- "http://ns.adobe.com/pdf/enc#RC": {
530
- key: (opf) => {
531
- const uuid = getUUID(opf).replaceAll("-", "");
532
- return Uint8Array.from({ length: 16 }, (_, i) => parseInt(uuid.slice(i * 2, i * 2 + 2), 16));
533
- },
534
- decode: (key, blob) => deobfuscate(key, 1024, blob)
535
- }
536
- });
537
- class Encryption {
538
- #uris = /* @__PURE__ */ new Map();
539
- #decoders = /* @__PURE__ */ new Map();
540
- #algorithms;
541
- constructor(algorithms) {
542
- this.#algorithms = algorithms;
543
- }
544
- async init(encryption, opf) {
545
- if (!encryption) return;
546
- const data = Array.from(
547
- encryption.getElementsByTagNameNS(NS$2.ENC, "EncryptedData"),
548
- (el) => {
549
- var _a, _b;
550
- return {
551
- algorithm: (_a = el.getElementsByTagNameNS(NS$2.ENC, "EncryptionMethod")[0]) == null ? void 0 : _a.getAttribute("Algorithm"),
552
- uri: (_b = el.getElementsByTagNameNS(NS$2.ENC, "CipherReference")[0]) == null ? void 0 : _b.getAttribute("URI")
553
- };
554
- }
555
- );
556
- for (const { algorithm, uri } of data) {
557
- if (!this.#decoders.has(algorithm)) {
558
- const algo = this.#algorithms[algorithm];
559
- if (!algo) {
560
- console.warn("Unknown encryption algorithm");
561
- continue;
562
- }
563
- const key = await algo.key(opf);
564
- this.#decoders.set(algorithm, (blob) => algo.decode(key, blob));
565
- }
566
- this.#uris.set(uri, algorithm);
567
- }
568
- }
569
- getDecoder(uri) {
570
- return this.#decoders.get(this.#uris.get(uri)) ?? ((x) => x);
571
- }
572
- }
573
- class Resources {
574
- constructor({ opf, resolveHref }) {
575
- var _a, _b, _c, _d, _e;
576
- this.opf = opf;
577
- const { $, $$, $$$ } = childGetter(opf, NS$2.OPF);
578
- const $manifest = $(opf.documentElement, "manifest");
579
- const $spine = $(opf.documentElement, "spine");
580
- const $$itemref = $$($spine, "itemref");
581
- this.manifest = $$($manifest, "item").map(getAttributes("href", "id", "media-type", "properties", "media-overlay")).map((item) => {
582
- var _a2;
583
- item.href = resolveHref(item.href);
584
- item.properties = (_a2 = item.properties) == null ? void 0 : _a2.split(/\s/);
585
- return item;
586
- });
587
- this.manifestById = new Map(this.manifest.map((item) => [item.id, item]));
588
- this.spine = $$itemref.map(getAttributes("idref", "id", "linear", "properties")).map((item) => {
589
- var _a2;
590
- return item.properties = (_a2 = item.properties) == null ? void 0 : _a2.split(/\s/), item;
591
- });
592
- this.pageProgressionDirection = $spine.getAttribute("page-progression-direction");
593
- this.navPath = (_a = this.getItemByProperty("nav")) == null ? void 0 : _a.href;
594
- this.ncxPath = (_b = this.getItemByID($spine.getAttribute("toc")) ?? this.manifest.find((item) => item.mediaType === MIME$2.NCX)) == null ? void 0 : _b.href;
595
- const $guide = $(opf.documentElement, "guide");
596
- if ($guide) this.guide = $$($guide, "reference").map(getAttributes("type", "title", "href")).map(({ type, title, href }) => ({
597
- label: title,
598
- type: type.split(/\s/),
599
- href: resolveHref(href)
600
- }));
601
- this.cover = this.getItemByProperty("cover-image") ?? this.getItemByID((_c = $$$(opf, "meta").find(filterAttribute("name", "cover"))) == null ? void 0 : _c.getAttribute("content")) ?? this.getItemByHref((_e = (_d = this.guide) == null ? void 0 : _d.find((ref) => ref.type.includes("cover"))) == null ? void 0 : _e.href);
602
- this.cfis = view.fromElements($$itemref);
603
- }
604
- getItemByID(id) {
605
- return this.manifestById.get(id);
606
- }
607
- getItemByHref(href) {
608
- return this.manifest.find((item) => item.href === href);
609
- }
610
- getItemByProperty(prop) {
611
- return this.manifest.find((item) => {
612
- var _a;
613
- return (_a = item.properties) == null ? void 0 : _a.includes(prop);
614
- });
615
- }
616
- resolveCFI(cfi) {
617
- const parts = view.parse(cfi);
618
- const top = (parts.parent ?? parts).shift();
619
- let $itemref = view.toElement(this.opf, top);
620
- if ($itemref && $itemref.nodeName !== "idref") {
621
- top.at(-1).id = null;
622
- $itemref = view.toElement(this.opf, top);
623
- }
624
- const idref = $itemref == null ? void 0 : $itemref.getAttribute("idref");
625
- const index = this.spine.findIndex((item) => item.idref === idref);
626
- const anchor = (doc) => view.toRange(doc, parts);
627
- return { index, anchor };
628
- }
629
- }
630
- class Loader {
631
- #cache = /* @__PURE__ */ new Map();
632
- #children = /* @__PURE__ */ new Map();
633
- #refCount = /* @__PURE__ */ new Map();
634
- eventTarget = new EventTarget();
635
- constructor({ loadText, loadBlob, resources }) {
636
- this.loadText = loadText;
637
- this.loadBlob = loadBlob;
638
- this.manifest = resources.manifest;
639
- this.assets = resources.manifest;
640
- }
641
- async createURL(href, data, type, parent) {
642
- if (!data) return "";
643
- const detail = { data, type };
644
- Object.defineProperty(detail, "name", { value: href });
645
- const event = new CustomEvent("data", { detail });
646
- this.eventTarget.dispatchEvent(event);
647
- const newData = await event.detail.data;
648
- const newType = await event.detail.type;
649
- const url = URL.createObjectURL(new Blob([newData], { type: newType }));
650
- this.#cache.set(href, url);
651
- this.#refCount.set(href, 1);
652
- if (parent) {
653
- const childList = this.#children.get(parent);
654
- if (childList) childList.push(href);
655
- else this.#children.set(parent, [href]);
656
- }
657
- return url;
658
- }
659
- ref(href, parent) {
660
- const childList = this.#children.get(parent);
661
- if (!(childList == null ? void 0 : childList.includes(href))) {
662
- this.#refCount.set(href, this.#refCount.get(href) + 1);
663
- if (childList) childList.push(href);
664
- else this.#children.set(parent, [href]);
665
- }
666
- return this.#cache.get(href);
667
- }
668
- unref(href) {
669
- if (!this.#refCount.has(href)) return;
670
- const count = this.#refCount.get(href) - 1;
671
- if (count < 1) {
672
- URL.revokeObjectURL(this.#cache.get(href));
673
- this.#cache.delete(href);
674
- this.#refCount.delete(href);
675
- const childList = this.#children.get(href);
676
- if (childList) while (childList.length) this.unref(childList.pop());
677
- this.#children.delete(href);
678
- } else this.#refCount.set(href, count);
679
- }
680
- // load manifest item, recursively loading all resources as needed
681
- async loadItem(item, parents = []) {
682
- if (!item) return null;
683
- const { href, mediaType } = item;
684
- const isScript = MIME$2.JS.test(item.mediaType);
685
- const detail = { type: mediaType, isScript, allow: true };
686
- const event = new CustomEvent("load", { detail });
687
- this.eventTarget.dispatchEvent(event);
688
- const allow = await event.detail.allow;
689
- if (!allow) return null;
690
- const parent = parents.at(-1);
691
- if (this.#cache.has(href)) return this.ref(href, parent);
692
- const shouldReplace = (isScript || [MIME$2.XHTML, MIME$2.HTML, MIME$2.CSS, MIME$2.SVG].includes(mediaType)) && parents.every((p) => p !== href);
693
- if (shouldReplace) return this.loadReplaced(item, parents);
694
- const tryLoadBlob = Promise.resolve().then(() => this.loadBlob(href));
695
- return this.createURL(href, tryLoadBlob, mediaType, parent);
696
- }
697
- async loadHref(href, base, parents = []) {
698
- if (isExternal(href)) return href;
699
- const path = resolveURL(href, base);
700
- const item = this.manifest.find((item2) => item2.href === path);
701
- if (!item) return href;
702
- return this.loadItem(item, parents.concat(base));
703
- }
704
- async loadReplaced(item, parents = []) {
705
- var _a, _b;
706
- const { href, mediaType } = item;
707
- const parent = parents.at(-1);
708
- let str = "";
709
- try {
710
- str = await this.loadText(href);
711
- } catch (e) {
712
- return this.createURL(href, Promise.reject(e), mediaType, parent);
713
- }
714
- if (!str) return null;
715
- if ([MIME$2.XHTML, MIME$2.HTML, MIME$2.SVG].includes(mediaType)) {
716
- let doc = new DOMParser().parseFromString(str, mediaType);
717
- if (mediaType === MIME$2.XHTML && (doc.querySelector("parsererror") || !((_a = doc.documentElement) == null ? void 0 : _a.namespaceURI))) {
718
- console.warn(((_b = doc.querySelector("parsererror")) == null ? void 0 : _b.innerText) ?? "Invalid XHTML");
719
- item.mediaType = MIME$2.HTML;
720
- doc = new DOMParser().parseFromString(str, item.mediaType);
721
- }
722
- if ([MIME$2.XHTML, MIME$2.SVG].includes(item.mediaType)) {
723
- let child = doc.firstChild;
724
- while (child instanceof ProcessingInstruction) {
725
- if (child.data) {
726
- const replacedData = await replaceSeries$1(
727
- child.data,
728
- /(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i,
729
- (_, p1, p2, p3) => this.loadHref(p2, href, parents).then((p22) => `${p1}${p22}${p3}`)
730
- );
731
- child.replaceWith(doc.createProcessingInstruction(
732
- child.target,
733
- replacedData
734
- ));
735
- }
736
- child = child.nextSibling;
737
- }
738
- }
739
- const replace = async (el, attr) => el.setAttribute(
740
- attr,
741
- await this.loadHref(el.getAttribute(attr), href, parents)
742
- );
743
- for (const el of doc.querySelectorAll("link[href]")) await replace(el, "href");
744
- for (const el of doc.querySelectorAll("[src]")) await replace(el, "src");
745
- for (const el of doc.querySelectorAll("[poster]")) await replace(el, "poster");
746
- for (const el of doc.querySelectorAll("object[data]")) await replace(el, "data");
747
- for (const el of doc.querySelectorAll("[*|href]:not([href])"))
748
- el.setAttributeNS(NS$2.XLINK, "href", await this.loadHref(
749
- el.getAttributeNS(NS$2.XLINK, "href"),
750
- href,
751
- parents
752
- ));
753
- for (const el of doc.querySelectorAll("[srcset]"))
754
- el.setAttribute("srcset", await replaceSeries$1(
755
- el.getAttribute("srcset"),
756
- /(\s*)(.+?)\s*((?:\s[\d.]+[wx])+\s*(?:,|$)|,\s+|$)/g,
757
- (_, p1, p2, p3) => this.loadHref(p2, href, parents).then((p22) => `${p1}${p22}${p3}`)
758
- ));
759
- for (const el of doc.querySelectorAll("style"))
760
- if (el.textContent) el.textContent = await this.replaceCSS(el.textContent, href, parents);
761
- for (const el of doc.querySelectorAll("[style]"))
762
- el.setAttribute(
763
- "style",
764
- await this.replaceCSS(el.getAttribute("style"), href, parents)
765
- );
766
- const result2 = new XMLSerializer().serializeToString(doc);
767
- return this.createURL(href, result2, item.mediaType, parent);
768
- }
769
- const result = mediaType === MIME$2.CSS ? await this.replaceCSS(str, href, parents) : await this.replaceString(str, href, parents);
770
- return this.createURL(href, result, mediaType, parent);
771
- }
772
- async replaceCSS(str, href, parents = []) {
773
- const replacedUrls = await replaceSeries$1(
774
- str,
775
- /url\(\s*["']?([^'"\n]*?)\s*["']?\s*\)/gi,
776
- (_, url) => this.loadHref(url, href, parents).then((url2) => `url("${url2}")`)
777
- );
778
- return replaceSeries$1(
779
- replacedUrls,
780
- /@import\s*["']([^"'\n]*?)["']/gi,
781
- (_, url) => this.loadHref(url, href, parents).then((url2) => `@import "${url2}"`)
782
- );
783
- }
784
- // find & replace all possible relative paths for all assets without parsing
785
- replaceString(str, href, parents = []) {
786
- const assetMap = /* @__PURE__ */ new Map();
787
- const urls = this.assets.map((asset) => {
788
- if (asset.href === href) return;
789
- const relative = pathRelative(pathDirname(href), asset.href);
790
- const relativeEnc = encodeURI(relative);
791
- const rootRelative = "/" + asset.href;
792
- const rootRelativeEnc = encodeURI(rootRelative);
793
- const set = /* @__PURE__ */ new Set([relative, relativeEnc, rootRelative, rootRelativeEnc]);
794
- for (const url of set) assetMap.set(url, asset);
795
- return Array.from(set);
796
- }).flat().filter((x) => x);
797
- if (!urls.length) return str;
798
- const regex = new RegExp(urls.map(regexEscape).join("|"), "g");
799
- return replaceSeries$1(str, regex, async (match) => this.loadItem(
800
- assetMap.get(match.replace(/^\//, "")),
801
- parents.concat(href)
802
- ));
803
- }
804
- unloadItem(item) {
805
- this.unref(item == null ? void 0 : item.href);
806
- }
807
- destroy() {
808
- for (const url of this.#cache.values()) URL.revokeObjectURL(url);
809
- }
810
- }
811
- const getHTMLFragment = (doc, id) => doc.getElementById(id) ?? doc.querySelector(`[name="${CSS.escape(id)}"]`);
812
- const getPageSpread$1 = (properties) => {
813
- for (const p of properties) {
814
- if (p === "page-spread-left" || p === "rendition:page-spread-left")
815
- return "left";
816
- if (p === "page-spread-right" || p === "rendition:page-spread-right")
817
- return "right";
818
- if (p === "rendition:page-spread-center") return "center";
819
- }
820
- };
821
- const getDisplayOptions = (doc) => {
822
- if (!doc) return null;
823
- return {
824
- fixedLayout: getElementText$1(doc.querySelector('option[name="fixed-layout"]')),
825
- openToSpread: getElementText$1(doc.querySelector('option[name="open-to-spread"]'))
826
- };
827
- };
828
- class EPUB {
829
- parser = new DOMParser();
830
- #loader;
831
- #encryption;
832
- constructor({ loadText, loadBlob, getSize, sha1 }) {
833
- this.loadText = loadText;
834
- this.loadBlob = loadBlob;
835
- this.getSize = getSize;
836
- this.#encryption = new Encryption(deobfuscators(sha1));
837
- }
838
- async #loadXML(uri) {
839
- const str = await this.loadText(uri);
840
- if (!str) return null;
841
- const doc = this.parser.parseFromString(str, MIME$2.XML);
842
- if (doc.querySelector("parsererror"))
843
- throw new Error(`XML parsing error: ${uri}
844
- ${doc.querySelector("parsererror").innerText}`);
845
- return doc;
846
- }
847
- async init() {
848
- const $container = await this.#loadXML("META-INF/container.xml");
849
- if (!$container) throw new Error("Failed to load container file");
850
- const opfs = Array.from(
851
- $container.getElementsByTagNameNS(NS$2.CONTAINER, "rootfile"),
852
- getAttributes("full-path", "media-type")
853
- ).filter((file) => file.mediaType === "application/oebps-package+xml");
854
- if (!opfs.length) throw new Error("No package document defined in container");
855
- const opfPath = opfs[0].fullPath;
856
- const opf = await this.#loadXML(opfPath);
857
- if (!opf) throw new Error("Failed to load package document");
858
- const $encryption = await this.#loadXML("META-INF/encryption.xml");
859
- await this.#encryption.init($encryption, opf);
860
- this.resources = new Resources({
861
- opf,
862
- resolveHref: (url) => resolveURL(url, opfPath)
863
- });
864
- this.#loader = new Loader({
865
- loadText: this.loadText,
866
- loadBlob: (uri) => Promise.resolve(this.loadBlob(uri)).then(this.#encryption.getDecoder(uri)),
867
- resources: this.resources
868
- });
869
- this.transformTarget = this.#loader.eventTarget;
870
- this.sections = this.resources.spine.map((spineItem, index) => {
871
- const { idref, linear, properties = [] } = spineItem;
872
- const item = this.resources.getItemByID(idref);
873
- if (!item) {
874
- console.warn(`Could not find item with ID "${idref}" in manifest`);
875
- return null;
876
- }
877
- return {
878
- id: item.href,
879
- load: () => this.#loader.loadItem(item),
880
- unload: () => this.#loader.unloadItem(item),
881
- createDocument: () => this.loadDocument(item),
882
- size: this.getSize(item.href),
883
- cfi: this.resources.cfis[index],
884
- linear,
885
- pageSpread: getPageSpread$1(properties),
886
- resolveHref: (href) => resolveURL(href, item.href),
887
- mediaOverlay: item.mediaOverlay ? this.resources.getItemByID(item.mediaOverlay) : null
888
- };
889
- }).filter((s) => s);
890
- const { navPath, ncxPath } = this.resources;
891
- if (navPath) try {
892
- const resolve = (url) => resolveURL(url, navPath);
893
- const nav = parseNav(await this.#loadXML(navPath), resolve);
894
- this.toc = nav.toc;
895
- this.pageList = nav.pageList;
896
- this.landmarks = nav.landmarks;
897
- } catch (e) {
898
- console.warn(e);
899
- }
900
- if (!this.toc && ncxPath) try {
901
- const resolve = (url) => resolveURL(url, ncxPath);
902
- const ncx = parseNCX(await this.#loadXML(ncxPath), resolve);
903
- this.toc = ncx.toc;
904
- this.pageList = ncx.pageList;
905
- } catch (e) {
906
- console.warn(e);
907
- }
908
- this.landmarks ??= this.resources.guide;
909
- const { metadata, rendition, media } = getMetadata(opf);
910
- this.metadata = metadata;
911
- this.rendition = rendition;
912
- this.media = media;
913
- this.dir = this.resources.pageProgressionDirection;
914
- const displayOptions = getDisplayOptions(
915
- await this.#loadXML("META-INF/com.apple.ibooks.display-options.xml") ?? await this.#loadXML("META-INF/com.kobobooks.display-options.xml")
916
- );
917
- if (displayOptions) {
918
- if (displayOptions.fixedLayout === "true")
919
- this.rendition.layout ??= "pre-paginated";
920
- if (displayOptions.openToSpread === "false") this.sections.find((section) => section.linear !== "no").pageSpread ??= this.dir === "rtl" ? "left" : "right";
921
- }
922
- return this;
923
- }
924
- async loadDocument(item) {
925
- const str = await this.loadText(item.href);
926
- return this.parser.parseFromString(str, item.mediaType);
927
- }
928
- getMediaOverlay() {
929
- return new MediaOverlay(this, this.#loadXML.bind(this));
930
- }
931
- resolveCFI(cfi) {
932
- return this.resources.resolveCFI(cfi);
933
- }
934
- resolveHref(href) {
935
- const [path, hash] = href.split("#");
936
- const item = this.resources.getItemByHref(decodeURI(path));
937
- if (!item) return null;
938
- const index = this.resources.spine.findIndex(({ idref }) => idref === item.id);
939
- const anchor = hash ? (doc) => getHTMLFragment(doc, hash) : () => 0;
940
- return { index, anchor };
941
- }
942
- splitTOCHref(href) {
943
- return (href == null ? void 0 : href.split("#")) ?? [];
944
- }
945
- getTOCFragment(doc, id) {
946
- return doc.getElementById(id) ?? doc.querySelector(`[name="${CSS.escape(id)}"]`);
947
- }
948
- isExternal(uri) {
949
- return isExternal(uri);
950
- }
951
- async getCover() {
952
- var _a;
953
- const cover = (_a = this.resources) == null ? void 0 : _a.cover;
954
- return (cover == null ? void 0 : cover.href) ? new Blob([await this.loadBlob(cover.href)], { type: cover.mediaType }) : null;
955
- }
956
- async getCalibreBookmarks() {
957
- const txt = await this.loadText("META-INF/calibre_bookmarks.txt");
958
- const magic = "encoding=json+base64:";
959
- if (txt == null ? void 0 : txt.startsWith(magic)) {
960
- const json = atob(txt.slice(magic.length));
961
- return JSON.parse(json);
962
- }
963
- }
964
- destroy() {
965
- var _a;
966
- (_a = this.#loader) == null ? void 0 : _a.destroy();
967
- }
968
- }
969
- exports.EPUB = EPUB;