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.
- package/README.md +43 -15
- package/lib/index.css +32 -32
- package/lib/overlayer.js +175 -0
- package/lib/vue-book-reader.es.js +8868 -1065
- package/lib/vue-book-reader.umd.js +1704 -1120
- package/package.json +14 -8
- package/lib/comic-book-Bjl5tZ3h.cjs +0 -45
- package/lib/comic-book-PSmOcoJj.js +0 -45
- package/lib/epub-2C1S8RbY.js +0 -969
- package/lib/epub-CeaB7HuF.cjs +0 -969
- package/lib/fb2-BZ-PbCUN.js +0 -323
- package/lib/fb2-COptS59M.cjs +0 -323
- package/lib/fflate-CXkSS9we.cjs +0 -145
- package/lib/fflate-_abAaq3-.js +0 -145
- package/lib/fixed-layout-BhGoTqYn.cjs +0 -289
- package/lib/fixed-layout-CiUiCen-.js +0 -289
- package/lib/mobi-BKd5Fggk.cjs +0 -1222
- package/lib/mobi-M3pI3jcR.js +0 -1222
- package/lib/paginator-BdBNKhdV.cjs +0 -1028
- package/lib/paginator-D5cTls8p.js +0 -1028
- package/lib/pdf-DYzRHfF8.js +0 -137
- package/lib/pdf-vLpNJVyc.cjs +0 -137
- package/lib/search-CQGJojwL.js +0 -115
- package/lib/search-DauBnLi1.cjs +0 -115
- package/lib/tts-CTh0nMvu.js +0 -290
- package/lib/tts-Du0MFg8M.cjs +0 -290
- package/lib/view--95lChht.cjs +0 -1139
- package/lib/view-T2ghn1aG.js +0 -1140
- package/lib/vue-book-reader.cjs.js +0 -1357
- package/lib/zip-BtClDCuW.cjs +0 -1513
- package/lib/zip-DbJSXXRR.js +0 -1513
package/lib/view-T2ghn1aG.js
DELETED
|
@@ -1,1140 +0,0 @@
|
|
|
1
|
-
const findIndices = (arr, f) => arr.map((x, i, a) => f(x, i, a) ? i : null).filter((x) => x != null);
|
|
2
|
-
const splitAt = (arr, is) => [-1, ...is, arr.length].reduce(({ xs, a }, b) => ({ xs: (xs == null ? void 0 : xs.concat([arr.slice(a + 1, b)])) ?? [], a: b }), {}).xs;
|
|
3
|
-
const concatArrays = (a, b) => a.slice(0, -1).concat([a[a.length - 1].concat(b[0])]).concat(b.slice(1));
|
|
4
|
-
const isNumber = /\d/;
|
|
5
|
-
const isCFI = /^epubcfi\((.*)\)$/;
|
|
6
|
-
const escapeCFI = (str) => str.replace(/[\^[\](),;=]/g, "^$&");
|
|
7
|
-
const wrap = (x) => isCFI.test(x) ? x : `epubcfi(${x})`;
|
|
8
|
-
const unwrap = (x) => {
|
|
9
|
-
var _a;
|
|
10
|
-
return ((_a = x.match(isCFI)) == null ? void 0 : _a[1]) ?? x;
|
|
11
|
-
};
|
|
12
|
-
const lift = (f) => (...xs) => `epubcfi(${f(...xs.map((x) => {
|
|
13
|
-
var _a;
|
|
14
|
-
return ((_a = x.match(isCFI)) == null ? void 0 : _a[1]) ?? x;
|
|
15
|
-
}))})`;
|
|
16
|
-
const joinIndir = lift((...xs) => xs.join("!"));
|
|
17
|
-
const tokenizer = (str) => {
|
|
18
|
-
const tokens = [];
|
|
19
|
-
let state, escape, value = "";
|
|
20
|
-
const push = (x) => (tokens.push(x), state = null, value = "");
|
|
21
|
-
const cat = (x) => (value += x, escape = false);
|
|
22
|
-
for (const char of Array.from(str.trim()).concat("")) {
|
|
23
|
-
if (char === "^" && !escape) {
|
|
24
|
-
escape = true;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (state === "!") push(["!"]);
|
|
28
|
-
else if (state === ",") push([","]);
|
|
29
|
-
else if (state === "/" || state === ":") {
|
|
30
|
-
if (isNumber.test(char)) {
|
|
31
|
-
cat(char);
|
|
32
|
-
continue;
|
|
33
|
-
} else push([state, parseInt(value)]);
|
|
34
|
-
} else if (state === "~") {
|
|
35
|
-
if (isNumber.test(char) || char === ".") {
|
|
36
|
-
cat(char);
|
|
37
|
-
continue;
|
|
38
|
-
} else push(["~", parseFloat(value)]);
|
|
39
|
-
} else if (state === "@") {
|
|
40
|
-
if (char === ":") {
|
|
41
|
-
push(["@", parseFloat(value)]);
|
|
42
|
-
state = "@";
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (isNumber.test(char) || char === ".") {
|
|
46
|
-
cat(char);
|
|
47
|
-
continue;
|
|
48
|
-
} else push(["@", parseFloat(value)]);
|
|
49
|
-
} else if (state === "[") {
|
|
50
|
-
if (char === ";" && !escape) {
|
|
51
|
-
push(["[", value]);
|
|
52
|
-
state = ";";
|
|
53
|
-
} else if (char === "," && !escape) {
|
|
54
|
-
push(["[", value]);
|
|
55
|
-
state = "[";
|
|
56
|
-
} else if (char === "]" && !escape) push(["[", value]);
|
|
57
|
-
else cat(char);
|
|
58
|
-
continue;
|
|
59
|
-
} else if (state == null ? void 0 : state.startsWith(";")) {
|
|
60
|
-
if (char === "=" && !escape) {
|
|
61
|
-
state = `;${value}`;
|
|
62
|
-
value = "";
|
|
63
|
-
} else if (char === ";" && !escape) {
|
|
64
|
-
push([state, value]);
|
|
65
|
-
state = ";";
|
|
66
|
-
} else if (char === "]" && !escape) push([state, value]);
|
|
67
|
-
else cat(char);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (char === "/" || char === ":" || char === "~" || char === "@" || char === "[" || char === "!" || char === ",") state = char;
|
|
71
|
-
}
|
|
72
|
-
return tokens;
|
|
73
|
-
};
|
|
74
|
-
const findTokens = (tokens, x) => findIndices(tokens, ([t]) => t === x);
|
|
75
|
-
const parser = (tokens) => {
|
|
76
|
-
const parts = [];
|
|
77
|
-
let state;
|
|
78
|
-
for (const [type, val] of tokens) {
|
|
79
|
-
if (type === "/") parts.push({ index: val });
|
|
80
|
-
else {
|
|
81
|
-
const last = parts[parts.length - 1];
|
|
82
|
-
if (type === ":") last.offset = val;
|
|
83
|
-
else if (type === "~") last.temporal = val;
|
|
84
|
-
else if (type === "@") last.spatial = (last.spatial ?? []).concat(val);
|
|
85
|
-
else if (type === ";s") last.side = val;
|
|
86
|
-
else if (type === "[") {
|
|
87
|
-
if (state === "/" && val) last.id = val;
|
|
88
|
-
else {
|
|
89
|
-
last.text = (last.text ?? []).concat(val);
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
state = type;
|
|
95
|
-
}
|
|
96
|
-
return parts;
|
|
97
|
-
};
|
|
98
|
-
const parserIndir = (tokens) => splitAt(tokens, findTokens(tokens, "!")).map(parser);
|
|
99
|
-
const parse = (cfi) => {
|
|
100
|
-
const tokens = tokenizer(unwrap(cfi));
|
|
101
|
-
const commas = findTokens(tokens, ",");
|
|
102
|
-
if (!commas.length) return parserIndir(tokens);
|
|
103
|
-
const [parent, start, end] = splitAt(tokens, commas).map(parserIndir);
|
|
104
|
-
return { parent, start, end };
|
|
105
|
-
};
|
|
106
|
-
const partToString = ({ index, id, offset, temporal, spatial, text, side }) => {
|
|
107
|
-
var _a;
|
|
108
|
-
const param = side ? `;s=${side}` : "";
|
|
109
|
-
return `/${index}` + (id ? `[${escapeCFI(id)}${param}]` : "") + (offset != null && index % 2 ? `:${offset}` : "") + (temporal ? `~${temporal}` : "") + (spatial ? `@${spatial.join(":")}` : "") + (text || !id && side ? "[" + (((_a = text == null ? void 0 : text.map(escapeCFI)) == null ? void 0 : _a.join(",")) ?? "") + param + "]" : "");
|
|
110
|
-
};
|
|
111
|
-
const toInnerString = (parsed) => parsed.parent ? [parsed.parent, parsed.start, parsed.end].map(toInnerString).join(",") : parsed.map((parts) => parts.map(partToString).join("")).join("!");
|
|
112
|
-
const toString = (parsed) => wrap(toInnerString(parsed));
|
|
113
|
-
const collapse = (x, toEnd) => typeof x === "string" ? toString(collapse(parse(x), toEnd)) : x.parent ? concatArrays(x.parent, x[toEnd ? "end" : "start"]) : x;
|
|
114
|
-
const buildRange = (from, to) => {
|
|
115
|
-
if (typeof from === "string") from = parse(from);
|
|
116
|
-
if (typeof to === "string") to = parse(to);
|
|
117
|
-
from = collapse(from);
|
|
118
|
-
to = collapse(to, true);
|
|
119
|
-
const localFrom = from[from.length - 1], localTo = to[to.length - 1];
|
|
120
|
-
const localParent = [], localStart = [], localEnd = [];
|
|
121
|
-
let pushToParent = true;
|
|
122
|
-
const len = Math.max(localFrom.length, localTo.length);
|
|
123
|
-
for (let i = 0; i < len; i++) {
|
|
124
|
-
const a = localFrom[i], b = localTo[i];
|
|
125
|
-
pushToParent &&= (a == null ? void 0 : a.index) === (b == null ? void 0 : b.index) && !(a == null ? void 0 : a.offset) && !(b == null ? void 0 : b.offset);
|
|
126
|
-
if (pushToParent) localParent.push(a);
|
|
127
|
-
else {
|
|
128
|
-
if (a) localStart.push(a);
|
|
129
|
-
if (b) localEnd.push(b);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
const parent = from.slice(0, -1).concat([localParent]);
|
|
133
|
-
return toString({ parent, start: [localStart], end: [localEnd] });
|
|
134
|
-
};
|
|
135
|
-
const isTextNode = ({ nodeType }) => nodeType === 3 || nodeType === 4;
|
|
136
|
-
const isElementNode = ({ nodeType }) => nodeType === 1;
|
|
137
|
-
const getChildNodes = (node, filter2) => {
|
|
138
|
-
const nodes = Array.from(node.childNodes).filter((node2) => isTextNode(node2) || isElementNode(node2));
|
|
139
|
-
return filter2 ? nodes.map((node2) => {
|
|
140
|
-
const accept = filter2(node2);
|
|
141
|
-
if (accept === NodeFilter.FILTER_REJECT) return null;
|
|
142
|
-
else if (accept === NodeFilter.FILTER_SKIP) return getChildNodes(node2, filter2);
|
|
143
|
-
else return node2;
|
|
144
|
-
}).flat().filter((x) => x) : nodes;
|
|
145
|
-
};
|
|
146
|
-
const indexChildNodes = (node, filter2) => {
|
|
147
|
-
const nodes = getChildNodes(node, filter2).reduce((arr, node2) => {
|
|
148
|
-
let last = arr[arr.length - 1];
|
|
149
|
-
if (!last) arr.push(node2);
|
|
150
|
-
else if (isTextNode(node2)) {
|
|
151
|
-
if (Array.isArray(last)) last.push(node2);
|
|
152
|
-
else if (isTextNode(last)) arr[arr.length - 1] = [last, node2];
|
|
153
|
-
else arr.push(node2);
|
|
154
|
-
} else {
|
|
155
|
-
if (isElementNode(last)) arr.push(null, node2);
|
|
156
|
-
else arr.push(node2);
|
|
157
|
-
}
|
|
158
|
-
return arr;
|
|
159
|
-
}, []);
|
|
160
|
-
if (isElementNode(nodes[0])) nodes.unshift("first");
|
|
161
|
-
if (isElementNode(nodes[nodes.length - 1])) nodes.push("last");
|
|
162
|
-
nodes.unshift("before");
|
|
163
|
-
nodes.push("after");
|
|
164
|
-
return nodes;
|
|
165
|
-
};
|
|
166
|
-
const partsToNode = (node, parts, filter2) => {
|
|
167
|
-
const { id } = parts[parts.length - 1];
|
|
168
|
-
if (id) {
|
|
169
|
-
const el = node.ownerDocument.getElementById(id);
|
|
170
|
-
if (el) return { node: el, offset: 0 };
|
|
171
|
-
}
|
|
172
|
-
for (const { index } of parts) {
|
|
173
|
-
const newNode = node ? indexChildNodes(node, filter2)[index] : null;
|
|
174
|
-
if (newNode === "first") return { node: node.firstChild ?? node };
|
|
175
|
-
if (newNode === "last") return { node: node.lastChild ?? node };
|
|
176
|
-
if (newNode === "before") return { node, before: true };
|
|
177
|
-
if (newNode === "after") return { node, after: true };
|
|
178
|
-
node = newNode;
|
|
179
|
-
}
|
|
180
|
-
const { offset } = parts[parts.length - 1];
|
|
181
|
-
if (!Array.isArray(node)) return { node, offset };
|
|
182
|
-
let sum = 0;
|
|
183
|
-
for (const n of node) {
|
|
184
|
-
const { length } = n.nodeValue;
|
|
185
|
-
if (sum + length >= offset) return { node: n, offset: offset - sum };
|
|
186
|
-
sum += length;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
const nodeToParts = (node, offset, filter2) => {
|
|
190
|
-
const { parentNode, id } = node;
|
|
191
|
-
const indexed = indexChildNodes(parentNode, filter2);
|
|
192
|
-
const index = indexed.findIndex((x) => Array.isArray(x) ? x.some((x2) => x2 === node) : x === node);
|
|
193
|
-
const chunk = indexed[index];
|
|
194
|
-
if (Array.isArray(chunk)) {
|
|
195
|
-
let sum = 0;
|
|
196
|
-
for (const x of chunk) {
|
|
197
|
-
if (x === node) {
|
|
198
|
-
sum += offset;
|
|
199
|
-
break;
|
|
200
|
-
} else sum += x.nodeValue.length;
|
|
201
|
-
}
|
|
202
|
-
offset = sum;
|
|
203
|
-
}
|
|
204
|
-
const part = { id, index, offset };
|
|
205
|
-
return (parentNode !== node.ownerDocument.documentElement ? nodeToParts(parentNode, null, filter2).concat(part) : [part]).filter((x) => x.index !== -1);
|
|
206
|
-
};
|
|
207
|
-
const fromRange = (range, filter2) => {
|
|
208
|
-
const { startContainer, startOffset, endContainer, endOffset } = range;
|
|
209
|
-
const start = nodeToParts(startContainer, startOffset, filter2);
|
|
210
|
-
if (range.collapsed) return toString([start]);
|
|
211
|
-
const end = nodeToParts(endContainer, endOffset, filter2);
|
|
212
|
-
return buildRange([start], [end]);
|
|
213
|
-
};
|
|
214
|
-
const toRange = (doc, parts, filter2) => {
|
|
215
|
-
const startParts = collapse(parts);
|
|
216
|
-
const endParts = collapse(parts, true);
|
|
217
|
-
const root = doc.documentElement;
|
|
218
|
-
const start = partsToNode(root, startParts[0], filter2);
|
|
219
|
-
const end = partsToNode(root, endParts[0], filter2);
|
|
220
|
-
const range = doc.createRange();
|
|
221
|
-
if (start.before) range.setStartBefore(start.node);
|
|
222
|
-
else if (start.after) range.setStartAfter(start.node);
|
|
223
|
-
else range.setStart(start.node, start.offset);
|
|
224
|
-
if (end.before) range.setEndBefore(end.node);
|
|
225
|
-
else if (end.after) range.setEndAfter(end.node);
|
|
226
|
-
else range.setEnd(end.node, end.offset);
|
|
227
|
-
return range;
|
|
228
|
-
};
|
|
229
|
-
const fromElements = (elements) => {
|
|
230
|
-
const results = [];
|
|
231
|
-
const { parentNode } = elements[0];
|
|
232
|
-
const parts = nodeToParts(parentNode);
|
|
233
|
-
for (const [index, node] of indexChildNodes(parentNode).entries()) {
|
|
234
|
-
const el = elements[results.length];
|
|
235
|
-
if (node === el)
|
|
236
|
-
results.push(toString([parts.concat({ id: el.id, index })]));
|
|
237
|
-
}
|
|
238
|
-
return results;
|
|
239
|
-
};
|
|
240
|
-
const toElement = (doc, parts) => partsToNode(doc.documentElement, collapse(parts)).node;
|
|
241
|
-
const fake = {
|
|
242
|
-
fromIndex: (index) => wrap(`/6/${(index + 1) * 2}`),
|
|
243
|
-
toIndex: (parts) => (parts == null ? void 0 : parts.at(-1).index) / 2 - 1
|
|
244
|
-
};
|
|
245
|
-
const assignIDs = (toc) => {
|
|
246
|
-
let id = 0;
|
|
247
|
-
const assignID = (item) => {
|
|
248
|
-
item.id = id++;
|
|
249
|
-
if (item.subitems) for (const subitem of item.subitems) assignID(subitem);
|
|
250
|
-
};
|
|
251
|
-
for (const item of toc) assignID(item);
|
|
252
|
-
return toc;
|
|
253
|
-
};
|
|
254
|
-
const flatten = (items) => items.map((item) => {
|
|
255
|
-
var _a;
|
|
256
|
-
return ((_a = item.subitems) == null ? void 0 : _a.length) ? [item, flatten(item.subitems)].flat() : item;
|
|
257
|
-
}).flat();
|
|
258
|
-
class TOCProgress {
|
|
259
|
-
async init({ toc, ids, splitHref, getFragment }) {
|
|
260
|
-
assignIDs(toc);
|
|
261
|
-
const items = flatten(toc);
|
|
262
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
263
|
-
for (const [i, item] of items.entries()) {
|
|
264
|
-
const [id, fragment] = await splitHref(item == null ? void 0 : item.href) ?? [];
|
|
265
|
-
const value = { fragment, item };
|
|
266
|
-
if (grouped.has(id)) grouped.get(id).items.push(value);
|
|
267
|
-
else grouped.set(id, { prev: items[i - 1], items: [value] });
|
|
268
|
-
}
|
|
269
|
-
const map = /* @__PURE__ */ new Map();
|
|
270
|
-
for (const [i, id] of ids.entries()) {
|
|
271
|
-
if (grouped.has(id)) map.set(id, grouped.get(id));
|
|
272
|
-
else map.set(id, map.get(ids[i - 1]));
|
|
273
|
-
}
|
|
274
|
-
this.ids = ids;
|
|
275
|
-
this.map = map;
|
|
276
|
-
this.getFragment = getFragment;
|
|
277
|
-
}
|
|
278
|
-
getProgress(index, range) {
|
|
279
|
-
var _a;
|
|
280
|
-
if (!this.ids) return;
|
|
281
|
-
const id = this.ids[index];
|
|
282
|
-
const obj = this.map.get(id);
|
|
283
|
-
if (!obj) return null;
|
|
284
|
-
const { prev, items } = obj;
|
|
285
|
-
if (!items) return prev;
|
|
286
|
-
if (!range || items.length === 1 && !items[0].fragment) return items[0].item;
|
|
287
|
-
const doc = range.startContainer.getRootNode();
|
|
288
|
-
for (const [i, { fragment }] of items.entries()) {
|
|
289
|
-
const el = this.getFragment(doc, fragment);
|
|
290
|
-
if (!el) continue;
|
|
291
|
-
if (range.comparePoint(el, 0) > 0)
|
|
292
|
-
return ((_a = items[i - 1]) == null ? void 0 : _a.item) ?? prev;
|
|
293
|
-
}
|
|
294
|
-
return items[items.length - 1].item;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
class SectionProgress {
|
|
298
|
-
constructor(sections, sizePerLoc, sizePerTimeUnit) {
|
|
299
|
-
this.sizes = sections.map((s) => s.linear != "no" && s.size > 0 ? s.size : 0);
|
|
300
|
-
this.sizePerLoc = sizePerLoc;
|
|
301
|
-
this.sizePerTimeUnit = sizePerTimeUnit;
|
|
302
|
-
this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0);
|
|
303
|
-
this.sectionFractions = this.#getSectionFractions();
|
|
304
|
-
}
|
|
305
|
-
#getSectionFractions() {
|
|
306
|
-
const { sizeTotal } = this;
|
|
307
|
-
const results = [0];
|
|
308
|
-
let sum = 0;
|
|
309
|
-
for (const size of this.sizes) results.push((sum += size) / sizeTotal);
|
|
310
|
-
return results;
|
|
311
|
-
}
|
|
312
|
-
// get progress given index of and fractions within a section
|
|
313
|
-
getProgress(index, fractionInSection, pageFraction = 0) {
|
|
314
|
-
const { sizes, sizePerLoc, sizePerTimeUnit, sizeTotal } = this;
|
|
315
|
-
const sizeInSection = sizes[index] ?? 0;
|
|
316
|
-
const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0);
|
|
317
|
-
const size = sizeBefore + fractionInSection * sizeInSection;
|
|
318
|
-
const nextSize = size + pageFraction * sizeInSection;
|
|
319
|
-
const remainingTotal = sizeTotal - size;
|
|
320
|
-
const remainingSection = (1 - fractionInSection) * sizeInSection;
|
|
321
|
-
return {
|
|
322
|
-
fraction: nextSize / sizeTotal,
|
|
323
|
-
section: {
|
|
324
|
-
current: index,
|
|
325
|
-
total: sizes.length
|
|
326
|
-
},
|
|
327
|
-
location: {
|
|
328
|
-
current: Math.floor(size / sizePerLoc),
|
|
329
|
-
next: Math.floor(nextSize / sizePerLoc),
|
|
330
|
-
total: Math.ceil(sizeTotal / sizePerLoc)
|
|
331
|
-
},
|
|
332
|
-
time: {
|
|
333
|
-
section: remainingSection / sizePerTimeUnit,
|
|
334
|
-
total: remainingTotal / sizePerTimeUnit
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
// the inverse of `getProgress`
|
|
339
|
-
// get index of and fraction in section based on total fraction
|
|
340
|
-
getSection(fraction) {
|
|
341
|
-
if (fraction <= 0) return [0, 0];
|
|
342
|
-
if (fraction >= 1) return [this.sizes.length - 1, 1];
|
|
343
|
-
fraction = fraction + Number.EPSILON;
|
|
344
|
-
const { sizeTotal } = this;
|
|
345
|
-
let index = this.sectionFractions.findIndex((x) => x > fraction) - 1;
|
|
346
|
-
if (index < 0) return [0, 0];
|
|
347
|
-
while (!this.sizes[index]) index++;
|
|
348
|
-
const fractionInSection = (fraction - this.sectionFractions[index]) / (this.sizes[index] / sizeTotal);
|
|
349
|
-
return [index, fractionInSection];
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const createSVGElement = (tag) => document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
353
|
-
class Overlayer {
|
|
354
|
-
#svg = createSVGElement("svg");
|
|
355
|
-
#map = /* @__PURE__ */ new Map();
|
|
356
|
-
constructor() {
|
|
357
|
-
Object.assign(this.#svg.style, {
|
|
358
|
-
position: "absolute",
|
|
359
|
-
top: "0",
|
|
360
|
-
left: "0",
|
|
361
|
-
width: "100%",
|
|
362
|
-
height: "100%",
|
|
363
|
-
pointerEvents: "none"
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
get element() {
|
|
367
|
-
return this.#svg;
|
|
368
|
-
}
|
|
369
|
-
add(key, range, draw, options) {
|
|
370
|
-
if (this.#map.has(key)) this.remove(key);
|
|
371
|
-
if (typeof range === "function") range = range(this.#svg.getRootNode());
|
|
372
|
-
const rects = range.getClientRects();
|
|
373
|
-
const element = draw(rects, options);
|
|
374
|
-
this.#svg.append(element);
|
|
375
|
-
this.#map.set(key, { range, draw, options, element, rects });
|
|
376
|
-
}
|
|
377
|
-
remove(key) {
|
|
378
|
-
if (!this.#map.has(key)) return;
|
|
379
|
-
this.#svg.removeChild(this.#map.get(key).element);
|
|
380
|
-
this.#map.delete(key);
|
|
381
|
-
}
|
|
382
|
-
redraw() {
|
|
383
|
-
for (const obj of this.#map.values()) {
|
|
384
|
-
const { range, draw, options, element } = obj;
|
|
385
|
-
this.#svg.removeChild(element);
|
|
386
|
-
const rects = range.getClientRects();
|
|
387
|
-
const el = draw(rects, options);
|
|
388
|
-
this.#svg.append(el);
|
|
389
|
-
obj.element = el;
|
|
390
|
-
obj.rects = rects;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
hitTest({ x, y }) {
|
|
394
|
-
const arr = Array.from(this.#map.entries());
|
|
395
|
-
for (let i = arr.length - 1; i >= 0; i--) {
|
|
396
|
-
const [key, obj] = arr[i];
|
|
397
|
-
for (const { left, top, right, bottom } of obj.rects)
|
|
398
|
-
if (top <= y && left <= x && bottom > y && right > x)
|
|
399
|
-
return [key, obj.range];
|
|
400
|
-
}
|
|
401
|
-
return [];
|
|
402
|
-
}
|
|
403
|
-
static underline(rects, options = {}) {
|
|
404
|
-
const { color = "red", width: strokeWidth = 2, writingMode } = options;
|
|
405
|
-
const g = createSVGElement("g");
|
|
406
|
-
g.setAttribute("fill", color);
|
|
407
|
-
if (writingMode === "vertical-rl" || writingMode === "vertical-lr")
|
|
408
|
-
for (const { right, top, height } of rects) {
|
|
409
|
-
const el = createSVGElement("rect");
|
|
410
|
-
el.setAttribute("x", right - strokeWidth);
|
|
411
|
-
el.setAttribute("y", top);
|
|
412
|
-
el.setAttribute("height", height);
|
|
413
|
-
el.setAttribute("width", strokeWidth);
|
|
414
|
-
g.append(el);
|
|
415
|
-
}
|
|
416
|
-
else for (const { left, bottom, width } of rects) {
|
|
417
|
-
const el = createSVGElement("rect");
|
|
418
|
-
el.setAttribute("x", left);
|
|
419
|
-
el.setAttribute("y", bottom - strokeWidth);
|
|
420
|
-
el.setAttribute("height", strokeWidth);
|
|
421
|
-
el.setAttribute("width", width);
|
|
422
|
-
g.append(el);
|
|
423
|
-
}
|
|
424
|
-
return g;
|
|
425
|
-
}
|
|
426
|
-
static strikethrough(rects, options = {}) {
|
|
427
|
-
const { color = "red", width: strokeWidth = 2, writingMode } = options;
|
|
428
|
-
const g = createSVGElement("g");
|
|
429
|
-
g.setAttribute("fill", color);
|
|
430
|
-
if (writingMode === "vertical-rl" || writingMode === "vertical-lr")
|
|
431
|
-
for (const { right, left, top, height } of rects) {
|
|
432
|
-
const el = createSVGElement("rect");
|
|
433
|
-
el.setAttribute("x", (right + left) / 2);
|
|
434
|
-
el.setAttribute("y", top);
|
|
435
|
-
el.setAttribute("height", height);
|
|
436
|
-
el.setAttribute("width", strokeWidth);
|
|
437
|
-
g.append(el);
|
|
438
|
-
}
|
|
439
|
-
else for (const { left, top, bottom, width } of rects) {
|
|
440
|
-
const el = createSVGElement("rect");
|
|
441
|
-
el.setAttribute("x", left);
|
|
442
|
-
el.setAttribute("y", (top + bottom) / 2);
|
|
443
|
-
el.setAttribute("height", strokeWidth);
|
|
444
|
-
el.setAttribute("width", width);
|
|
445
|
-
g.append(el);
|
|
446
|
-
}
|
|
447
|
-
return g;
|
|
448
|
-
}
|
|
449
|
-
static squiggly(rects, options = {}) {
|
|
450
|
-
const { color = "red", width: strokeWidth = 2, writingMode } = options;
|
|
451
|
-
const g = createSVGElement("g");
|
|
452
|
-
g.setAttribute("fill", "none");
|
|
453
|
-
g.setAttribute("stroke", color);
|
|
454
|
-
g.setAttribute("stroke-width", strokeWidth);
|
|
455
|
-
const block = strokeWidth * 1.5;
|
|
456
|
-
if (writingMode === "vertical-rl" || writingMode === "vertical-lr")
|
|
457
|
-
for (const { right, top, height } of rects) {
|
|
458
|
-
const el = createSVGElement("path");
|
|
459
|
-
const n = Math.round(height / block / 1.5);
|
|
460
|
-
const inline = height / n;
|
|
461
|
-
const ls = Array.from(
|
|
462
|
-
{ length: n },
|
|
463
|
-
(_, i) => `l${i % 2 ? -block : block} ${inline}`
|
|
464
|
-
).join("");
|
|
465
|
-
el.setAttribute("d", `M${right} ${top}${ls}`);
|
|
466
|
-
g.append(el);
|
|
467
|
-
}
|
|
468
|
-
else for (const { left, bottom, width } of rects) {
|
|
469
|
-
const el = createSVGElement("path");
|
|
470
|
-
const n = Math.round(width / block / 1.5);
|
|
471
|
-
const inline = width / n;
|
|
472
|
-
const ls = Array.from(
|
|
473
|
-
{ length: n },
|
|
474
|
-
(_, i) => `l${inline} ${i % 2 ? block : -block}`
|
|
475
|
-
).join("");
|
|
476
|
-
el.setAttribute("d", `M${left} ${bottom}${ls}`);
|
|
477
|
-
g.append(el);
|
|
478
|
-
}
|
|
479
|
-
return g;
|
|
480
|
-
}
|
|
481
|
-
static highlight(rects, options = {}) {
|
|
482
|
-
const { color = "red" } = options;
|
|
483
|
-
const g = createSVGElement("g");
|
|
484
|
-
g.setAttribute("fill", color);
|
|
485
|
-
g.style.opacity = "var(--overlayer-highlight-opacity, .3)";
|
|
486
|
-
g.style.mixBlendMode = "var(--overlayer-highlight-blend-mode, normal)";
|
|
487
|
-
for (const { left, top, height, width } of rects) {
|
|
488
|
-
const el = createSVGElement("rect");
|
|
489
|
-
el.setAttribute("x", left);
|
|
490
|
-
el.setAttribute("y", top);
|
|
491
|
-
el.setAttribute("height", height);
|
|
492
|
-
el.setAttribute("width", width);
|
|
493
|
-
g.append(el);
|
|
494
|
-
}
|
|
495
|
-
return g;
|
|
496
|
-
}
|
|
497
|
-
static outline(rects, options = {}) {
|
|
498
|
-
const { color = "red", width: strokeWidth = 3, radius = 3 } = options;
|
|
499
|
-
const g = createSVGElement("g");
|
|
500
|
-
g.setAttribute("fill", "none");
|
|
501
|
-
g.setAttribute("stroke", color);
|
|
502
|
-
g.setAttribute("stroke-width", strokeWidth);
|
|
503
|
-
for (const { left, top, height, width } of rects) {
|
|
504
|
-
const el = createSVGElement("rect");
|
|
505
|
-
el.setAttribute("x", left);
|
|
506
|
-
el.setAttribute("y", top);
|
|
507
|
-
el.setAttribute("height", height);
|
|
508
|
-
el.setAttribute("width", width);
|
|
509
|
-
el.setAttribute("rx", radius);
|
|
510
|
-
g.append(el);
|
|
511
|
-
}
|
|
512
|
-
return g;
|
|
513
|
-
}
|
|
514
|
-
// make an exact copy of an image in the overlay
|
|
515
|
-
// one can then apply filters to the entire element, without affecting them;
|
|
516
|
-
// it's a bit silly and probably better to just invert images twice
|
|
517
|
-
// (though the color will be off in that case if you do heu-rotate)
|
|
518
|
-
static copyImage([rect], options = {}) {
|
|
519
|
-
const { src } = options;
|
|
520
|
-
const image = createSVGElement("image");
|
|
521
|
-
const { left, top, height, width } = rect;
|
|
522
|
-
image.setAttribute("href", src);
|
|
523
|
-
image.setAttribute("x", left);
|
|
524
|
-
image.setAttribute("y", top);
|
|
525
|
-
image.setAttribute("height", height);
|
|
526
|
-
image.setAttribute("width", width);
|
|
527
|
-
return image;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
const walkRange = (range, walker) => {
|
|
531
|
-
const nodes = [];
|
|
532
|
-
for (let node = walker.currentNode; node; node = walker.nextNode()) {
|
|
533
|
-
const compare = range.comparePoint(node, 0);
|
|
534
|
-
if (compare === 0) nodes.push(node);
|
|
535
|
-
else if (compare > 0) break;
|
|
536
|
-
}
|
|
537
|
-
return nodes;
|
|
538
|
-
};
|
|
539
|
-
const walkDocument = (_, walker) => {
|
|
540
|
-
const nodes = [];
|
|
541
|
-
for (let node = walker.nextNode(); node; node = walker.nextNode())
|
|
542
|
-
nodes.push(node);
|
|
543
|
-
return nodes;
|
|
544
|
-
};
|
|
545
|
-
const filter = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_CDATA_SECTION;
|
|
546
|
-
const acceptNode = (node) => {
|
|
547
|
-
if (node.nodeType === 1) {
|
|
548
|
-
const name = node.tagName.toLowerCase();
|
|
549
|
-
if (name === "script" || name === "style") return NodeFilter.FILTER_REJECT;
|
|
550
|
-
return NodeFilter.FILTER_SKIP;
|
|
551
|
-
}
|
|
552
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
553
|
-
};
|
|
554
|
-
const textWalker = function* (x, func, filterFunc) {
|
|
555
|
-
const root = x.commonAncestorContainer ?? x.body ?? x;
|
|
556
|
-
const walker = document.createTreeWalker(root, filter, { acceptNode: filterFunc || acceptNode });
|
|
557
|
-
const walk = x.commonAncestorContainer ? walkRange : walkDocument;
|
|
558
|
-
const nodes = walk(x, walker);
|
|
559
|
-
const strs = nodes.map((node) => node.nodeValue);
|
|
560
|
-
const makeRange = (startIndex, startOffset, endIndex, endOffset) => {
|
|
561
|
-
const range = document.createRange();
|
|
562
|
-
range.setStart(nodes[startIndex], startOffset);
|
|
563
|
-
range.setEnd(nodes[endIndex], endOffset);
|
|
564
|
-
return range;
|
|
565
|
-
};
|
|
566
|
-
for (const match of func(strs, makeRange)) yield match;
|
|
567
|
-
};
|
|
568
|
-
const SEARCH_PREFIX = "foliate-search:";
|
|
569
|
-
const isZip = async (file) => {
|
|
570
|
-
const arr = new Uint8Array(await file.slice(0, 4).arrayBuffer());
|
|
571
|
-
return arr[0] === 80 && arr[1] === 75 && arr[2] === 3 && arr[3] === 4;
|
|
572
|
-
};
|
|
573
|
-
const isPDF = async (file) => {
|
|
574
|
-
const arr = new Uint8Array(await file.slice(0, 5).arrayBuffer());
|
|
575
|
-
return arr[0] === 37 && arr[1] === 80 && arr[2] === 68 && arr[3] === 70 && arr[4] === 45;
|
|
576
|
-
};
|
|
577
|
-
const isCBZ = ({ name, type }) => type === "application/vnd.comicbook+zip" || name.endsWith(".cbz");
|
|
578
|
-
const isFB2 = ({ name, type }) => type === "application/x-fictionbook+xml" || name.endsWith(".fb2");
|
|
579
|
-
const isFBZ = ({ name, type }) => type === "application/x-zip-compressed-fb2" || name.endsWith(".fb2.zip") || name.endsWith(".fbz");
|
|
580
|
-
const makeZipLoader = async (file) => {
|
|
581
|
-
const { configure, ZipReader, BlobReader, TextWriter, BlobWriter } = await import("./zip-DbJSXXRR.js");
|
|
582
|
-
configure({ useWebWorkers: false });
|
|
583
|
-
const reader = new ZipReader(new BlobReader(file));
|
|
584
|
-
const entries = await reader.getEntries();
|
|
585
|
-
const map = new Map(entries.map((entry) => [entry.filename, entry]));
|
|
586
|
-
const load = (f) => (name, ...args) => map.has(name) ? f(map.get(name), ...args) : null;
|
|
587
|
-
const loadText = load((entry) => entry.getData(new TextWriter()));
|
|
588
|
-
const loadBlob = load((entry, type) => entry.getData(new BlobWriter(type)));
|
|
589
|
-
const getSize = (name) => {
|
|
590
|
-
var _a;
|
|
591
|
-
return ((_a = map.get(name)) == null ? void 0 : _a.uncompressedSize) ?? 0;
|
|
592
|
-
};
|
|
593
|
-
return { entries, loadText, loadBlob, getSize };
|
|
594
|
-
};
|
|
595
|
-
const getFileEntries = async (entry) => entry.isFile ? entry : (await Promise.all(Array.from(
|
|
596
|
-
await new Promise((resolve, reject) => entry.createReader().readEntries((entries) => resolve(entries), (error) => reject(error))),
|
|
597
|
-
getFileEntries
|
|
598
|
-
))).flat();
|
|
599
|
-
const makeDirectoryLoader = async (entry) => {
|
|
600
|
-
const entries = await getFileEntries(entry);
|
|
601
|
-
const files = await Promise.all(
|
|
602
|
-
entries.map((entry2) => new Promise((resolve, reject) => entry2.file(
|
|
603
|
-
(file) => resolve([file, entry2.fullPath]),
|
|
604
|
-
(error) => reject(error)
|
|
605
|
-
)))
|
|
606
|
-
);
|
|
607
|
-
const map = new Map(files.map(([file, path]) => [path.replace(entry.fullPath + "/", ""), file]));
|
|
608
|
-
const decoder = new TextDecoder();
|
|
609
|
-
const decode = (x) => x ? decoder.decode(x) : null;
|
|
610
|
-
const getBuffer = (name) => {
|
|
611
|
-
var _a;
|
|
612
|
-
return ((_a = map.get(name)) == null ? void 0 : _a.arrayBuffer()) ?? null;
|
|
613
|
-
};
|
|
614
|
-
const loadText = async (name) => decode(await getBuffer(name));
|
|
615
|
-
const loadBlob = (name) => map.get(name);
|
|
616
|
-
const getSize = (name) => {
|
|
617
|
-
var _a;
|
|
618
|
-
return ((_a = map.get(name)) == null ? void 0 : _a.size) ?? 0;
|
|
619
|
-
};
|
|
620
|
-
return { loadText, loadBlob, getSize };
|
|
621
|
-
};
|
|
622
|
-
class ResponseError extends Error {
|
|
623
|
-
}
|
|
624
|
-
class NotFoundError extends Error {
|
|
625
|
-
}
|
|
626
|
-
class UnsupportedTypeError extends Error {
|
|
627
|
-
}
|
|
628
|
-
const fetchFile = async (url) => {
|
|
629
|
-
const res = await fetch(url);
|
|
630
|
-
if (!res.ok) throw new ResponseError(
|
|
631
|
-
`${res.status} ${res.statusText}`,
|
|
632
|
-
{ cause: res }
|
|
633
|
-
);
|
|
634
|
-
return new File([await res.blob()], new URL(res.url).pathname);
|
|
635
|
-
};
|
|
636
|
-
const makeBook = async (file) => {
|
|
637
|
-
if (typeof file === "string") file = await fetchFile(file);
|
|
638
|
-
let book;
|
|
639
|
-
if (file.isDirectory) {
|
|
640
|
-
const loader = await makeDirectoryLoader(file);
|
|
641
|
-
const { EPUB } = await import("./epub-2C1S8RbY.js");
|
|
642
|
-
book = await new EPUB(loader).init();
|
|
643
|
-
} else if (!file.size) throw new NotFoundError("File not found");
|
|
644
|
-
else if (await isZip(file)) {
|
|
645
|
-
const loader = await makeZipLoader(file);
|
|
646
|
-
if (isCBZ(file)) {
|
|
647
|
-
const { makeComicBook } = await import("./comic-book-PSmOcoJj.js");
|
|
648
|
-
book = makeComicBook(loader, file);
|
|
649
|
-
} else if (isFBZ(file)) {
|
|
650
|
-
const { makeFB2 } = await import("./fb2-BZ-PbCUN.js");
|
|
651
|
-
const { entries } = loader;
|
|
652
|
-
const entry = entries.find((entry2) => entry2.filename.endsWith(".fb2"));
|
|
653
|
-
const blob = await loader.loadBlob((entry ?? entries[0]).filename);
|
|
654
|
-
book = await makeFB2(blob);
|
|
655
|
-
} else {
|
|
656
|
-
const { EPUB } = await import("./epub-2C1S8RbY.js");
|
|
657
|
-
book = await new EPUB(loader).init();
|
|
658
|
-
}
|
|
659
|
-
} else if (await isPDF(file)) {
|
|
660
|
-
const { makePDF } = await import("./pdf-DYzRHfF8.js");
|
|
661
|
-
book = await makePDF(file);
|
|
662
|
-
} else {
|
|
663
|
-
const { isMOBI, MOBI } = await import("./mobi-M3pI3jcR.js");
|
|
664
|
-
if (await isMOBI(file)) {
|
|
665
|
-
const fflate = await import("./fflate-_abAaq3-.js");
|
|
666
|
-
book = await new MOBI({ unzlib: fflate.unzlibSync }).open(file);
|
|
667
|
-
} else if (isFB2(file)) {
|
|
668
|
-
const { makeFB2 } = await import("./fb2-BZ-PbCUN.js");
|
|
669
|
-
book = await makeFB2(file);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
if (!book) throw new UnsupportedTypeError("File type not supported");
|
|
673
|
-
return book;
|
|
674
|
-
};
|
|
675
|
-
class CursorAutohider {
|
|
676
|
-
#timeout;
|
|
677
|
-
#el;
|
|
678
|
-
#check;
|
|
679
|
-
#state;
|
|
680
|
-
constructor(el, check, state = {}) {
|
|
681
|
-
this.#el = el;
|
|
682
|
-
this.#check = check;
|
|
683
|
-
this.#state = state;
|
|
684
|
-
if (this.#state.hidden) this.hide();
|
|
685
|
-
this.#el.addEventListener("mousemove", ({ screenX, screenY }) => {
|
|
686
|
-
if (screenX === this.#state.x && screenY === this.#state.y) return;
|
|
687
|
-
this.#state.x = screenX, this.#state.y = screenY;
|
|
688
|
-
this.show();
|
|
689
|
-
if (this.#timeout) clearTimeout(this.#timeout);
|
|
690
|
-
if (check()) this.#timeout = setTimeout(this.hide.bind(this), 1e3);
|
|
691
|
-
}, false);
|
|
692
|
-
}
|
|
693
|
-
cloneFor(el) {
|
|
694
|
-
return new CursorAutohider(el, this.#check, this.#state);
|
|
695
|
-
}
|
|
696
|
-
hide() {
|
|
697
|
-
this.#el.style.cursor = "none";
|
|
698
|
-
this.#state.hidden = true;
|
|
699
|
-
}
|
|
700
|
-
show() {
|
|
701
|
-
this.#el.style.removeProperty("cursor");
|
|
702
|
-
this.#state.hidden = false;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
class History extends EventTarget {
|
|
706
|
-
#arr = [];
|
|
707
|
-
#index = -1;
|
|
708
|
-
pushState(x) {
|
|
709
|
-
const last = this.#arr[this.#index];
|
|
710
|
-
if (last === x || (last == null ? void 0 : last.fraction) && last.fraction === x.fraction) return;
|
|
711
|
-
this.#arr[++this.#index] = x;
|
|
712
|
-
this.#arr.length = this.#index + 1;
|
|
713
|
-
this.dispatchEvent(new Event("index-change"));
|
|
714
|
-
}
|
|
715
|
-
replaceState(x) {
|
|
716
|
-
const index = this.#index;
|
|
717
|
-
this.#arr[index] = x;
|
|
718
|
-
}
|
|
719
|
-
back() {
|
|
720
|
-
const index = this.#index;
|
|
721
|
-
if (index <= 0) return;
|
|
722
|
-
const detail = { state: this.#arr[index - 1] };
|
|
723
|
-
this.#index = index - 1;
|
|
724
|
-
this.dispatchEvent(new CustomEvent("popstate", { detail }));
|
|
725
|
-
this.dispatchEvent(new Event("index-change"));
|
|
726
|
-
}
|
|
727
|
-
forward() {
|
|
728
|
-
const index = this.#index;
|
|
729
|
-
if (index >= this.#arr.length - 1) return;
|
|
730
|
-
const detail = { state: this.#arr[index + 1] };
|
|
731
|
-
this.#index = index + 1;
|
|
732
|
-
this.dispatchEvent(new CustomEvent("popstate", { detail }));
|
|
733
|
-
this.dispatchEvent(new Event("index-change"));
|
|
734
|
-
}
|
|
735
|
-
get canGoBack() {
|
|
736
|
-
return this.#index > 0;
|
|
737
|
-
}
|
|
738
|
-
get canGoForward() {
|
|
739
|
-
return this.#index < this.#arr.length - 1;
|
|
740
|
-
}
|
|
741
|
-
clear() {
|
|
742
|
-
this.#arr = [];
|
|
743
|
-
this.#index = -1;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
const languageInfo = (lang) => {
|
|
747
|
-
var _a, _b;
|
|
748
|
-
if (!lang) return {};
|
|
749
|
-
try {
|
|
750
|
-
const canonical = Intl.getCanonicalLocales(lang)[0];
|
|
751
|
-
const locale = new Intl.Locale(canonical);
|
|
752
|
-
const isCJK = ["zh", "ja", "kr"].includes(locale.language);
|
|
753
|
-
const direction = (_b = ((_a = locale.getTextInfo) == null ? void 0 : _a.call(locale)) ?? locale.textInfo) == null ? void 0 : _b.direction;
|
|
754
|
-
return { canonical, locale, isCJK, direction };
|
|
755
|
-
} catch (e) {
|
|
756
|
-
console.warn(e);
|
|
757
|
-
return {};
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
class View extends HTMLElement {
|
|
761
|
-
#root = this.attachShadow({ mode: "closed" });
|
|
762
|
-
#sectionProgress;
|
|
763
|
-
#tocProgress;
|
|
764
|
-
#pageProgress;
|
|
765
|
-
#searchResults = /* @__PURE__ */ new Map();
|
|
766
|
-
#cursorAutohider = new CursorAutohider(this, () => this.hasAttribute("autohide-cursor"));
|
|
767
|
-
isFixedLayout = false;
|
|
768
|
-
lastLocation;
|
|
769
|
-
history = new History();
|
|
770
|
-
constructor() {
|
|
771
|
-
super();
|
|
772
|
-
this.history.addEventListener("popstate", ({ detail }) => {
|
|
773
|
-
const resolved = this.resolveNavigation(detail.state);
|
|
774
|
-
this.renderer.goTo(resolved);
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
async open(book) {
|
|
778
|
-
var _a, _b;
|
|
779
|
-
if (typeof book === "string" || typeof book.arrayBuffer === "function" || book.isDirectory) book = await makeBook(book);
|
|
780
|
-
this.book = book;
|
|
781
|
-
this.language = languageInfo((_a = book.metadata) == null ? void 0 : _a.language);
|
|
782
|
-
if (book.splitTOCHref && book.getTOCFragment) {
|
|
783
|
-
const ids = book.sections.map((s) => s.id);
|
|
784
|
-
this.#sectionProgress = new SectionProgress(book.sections, 1500, 1600);
|
|
785
|
-
const splitHref = book.splitTOCHref.bind(book);
|
|
786
|
-
const getFragment = book.getTOCFragment.bind(book);
|
|
787
|
-
this.#tocProgress = new TOCProgress();
|
|
788
|
-
await this.#tocProgress.init({
|
|
789
|
-
toc: book.toc ?? [],
|
|
790
|
-
ids,
|
|
791
|
-
splitHref,
|
|
792
|
-
getFragment
|
|
793
|
-
});
|
|
794
|
-
this.#pageProgress = new TOCProgress();
|
|
795
|
-
await this.#pageProgress.init({
|
|
796
|
-
toc: book.pageList ?? [],
|
|
797
|
-
ids,
|
|
798
|
-
splitHref,
|
|
799
|
-
getFragment
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
this.isFixedLayout = ((_b = this.book.rendition) == null ? void 0 : _b.layout) === "pre-paginated";
|
|
803
|
-
if (this.isFixedLayout) {
|
|
804
|
-
await import("./fixed-layout-CiUiCen-.js");
|
|
805
|
-
this.renderer = document.createElement("foliate-fxl");
|
|
806
|
-
} else {
|
|
807
|
-
await import("./paginator-D5cTls8p.js");
|
|
808
|
-
this.renderer = document.createElement("foliate-paginator");
|
|
809
|
-
}
|
|
810
|
-
this.renderer.setAttribute("exportparts", "head,foot,filter");
|
|
811
|
-
this.renderer.addEventListener("load", (e) => this.#onLoad(e.detail));
|
|
812
|
-
this.renderer.addEventListener("relocate", (e) => this.#onRelocate(e.detail));
|
|
813
|
-
this.renderer.addEventListener("create-overlayer", (e) => e.detail.attach(this.#createOverlayer(e.detail)));
|
|
814
|
-
this.renderer.open(book);
|
|
815
|
-
this.#root.append(this.renderer);
|
|
816
|
-
if (book.sections.some((section) => section.mediaOverlay)) {
|
|
817
|
-
const activeClass = book.media.activeClass;
|
|
818
|
-
const playbackActiveClass = book.media.playbackActiveClass;
|
|
819
|
-
this.mediaOverlay = book.getMediaOverlay();
|
|
820
|
-
let lastActive;
|
|
821
|
-
this.mediaOverlay.addEventListener("highlight", (e) => {
|
|
822
|
-
const resolved = this.resolveNavigation(e.detail.text);
|
|
823
|
-
this.renderer.goTo(resolved).then(() => {
|
|
824
|
-
const { doc } = this.renderer.getContents().find((x) => x.index = resolved.index);
|
|
825
|
-
const el = resolved.anchor(doc);
|
|
826
|
-
el.classList.add(activeClass);
|
|
827
|
-
if (playbackActiveClass) el.ownerDocument.documentElement.classList.add(playbackActiveClass);
|
|
828
|
-
lastActive = new WeakRef(el);
|
|
829
|
-
});
|
|
830
|
-
});
|
|
831
|
-
this.mediaOverlay.addEventListener("unhighlight", () => {
|
|
832
|
-
const el = lastActive == null ? void 0 : lastActive.deref();
|
|
833
|
-
if (el) {
|
|
834
|
-
el.classList.remove(activeClass);
|
|
835
|
-
if (playbackActiveClass) el.ownerDocument.documentElement.classList.remove(playbackActiveClass);
|
|
836
|
-
}
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
close() {
|
|
841
|
-
var _a, _b;
|
|
842
|
-
(_a = this.renderer) == null ? void 0 : _a.destroy();
|
|
843
|
-
(_b = this.renderer) == null ? void 0 : _b.remove();
|
|
844
|
-
this.#sectionProgress = null;
|
|
845
|
-
this.#tocProgress = null;
|
|
846
|
-
this.#pageProgress = null;
|
|
847
|
-
this.#searchResults = /* @__PURE__ */ new Map();
|
|
848
|
-
this.lastLocation = null;
|
|
849
|
-
this.history.clear();
|
|
850
|
-
this.tts = null;
|
|
851
|
-
this.mediaOverlay = null;
|
|
852
|
-
}
|
|
853
|
-
goToTextStart() {
|
|
854
|
-
var _a, _b;
|
|
855
|
-
return this.goTo(((_b = (_a = this.book.landmarks) == null ? void 0 : _a.find((m) => m.type.includes("bodymatter") || m.type.includes("text"))) == null ? void 0 : _b.href) ?? this.book.sections.findIndex((s) => s.linear !== "no"));
|
|
856
|
-
}
|
|
857
|
-
async init({ lastLocation, showTextStart }) {
|
|
858
|
-
const resolved = lastLocation ? this.resolveNavigation(lastLocation) : null;
|
|
859
|
-
if (resolved) {
|
|
860
|
-
await this.renderer.goTo(resolved);
|
|
861
|
-
this.history.pushState(lastLocation);
|
|
862
|
-
} else if (showTextStart) await this.goToTextStart();
|
|
863
|
-
else {
|
|
864
|
-
this.history.pushState(0);
|
|
865
|
-
await this.next();
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
#emit(name, detail, cancelable) {
|
|
869
|
-
return this.dispatchEvent(new CustomEvent(name, { detail, cancelable }));
|
|
870
|
-
}
|
|
871
|
-
#onRelocate({ reason, range, index, fraction, size }) {
|
|
872
|
-
var _a, _b, _c;
|
|
873
|
-
const progress = ((_a = this.#sectionProgress) == null ? void 0 : _a.getProgress(index, fraction, size)) ?? {};
|
|
874
|
-
const tocItem = (_b = this.#tocProgress) == null ? void 0 : _b.getProgress(index, range);
|
|
875
|
-
const pageItem = (_c = this.#pageProgress) == null ? void 0 : _c.getProgress(index, range);
|
|
876
|
-
const cfi = this.getCFI(index, range);
|
|
877
|
-
this.lastLocation = { ...progress, tocItem, pageItem, cfi, range };
|
|
878
|
-
if (reason === "snap" || reason === "page" || reason === "scroll")
|
|
879
|
-
this.history.replaceState(cfi);
|
|
880
|
-
this.#emit("relocate", this.lastLocation);
|
|
881
|
-
}
|
|
882
|
-
#onLoad({ doc, index }) {
|
|
883
|
-
doc.documentElement.lang ||= this.language.canonical ?? "";
|
|
884
|
-
if (!this.language.isCJK)
|
|
885
|
-
doc.documentElement.dir ||= this.language.direction ?? "";
|
|
886
|
-
this.#handleLinks(doc, index);
|
|
887
|
-
this.#cursorAutohider.cloneFor(doc.documentElement);
|
|
888
|
-
this.#emit("load", { doc, index });
|
|
889
|
-
}
|
|
890
|
-
#handleLinks(doc, index) {
|
|
891
|
-
const { book } = this;
|
|
892
|
-
const section = book.sections[index];
|
|
893
|
-
doc.addEventListener("click", (e) => {
|
|
894
|
-
var _a, _b;
|
|
895
|
-
const a = e.target.closest("a[href]");
|
|
896
|
-
if (!a) return;
|
|
897
|
-
e.preventDefault();
|
|
898
|
-
const href_ = a.getAttribute("href");
|
|
899
|
-
const href = ((_a = section == null ? void 0 : section.resolveHref) == null ? void 0 : _a.call(section, href_)) ?? href_;
|
|
900
|
-
if ((_b = book == null ? void 0 : book.isExternal) == null ? void 0 : _b.call(book, href))
|
|
901
|
-
Promise.resolve(this.#emit("external-link", { a, href }, true)).then((x) => x ? globalThis.open(href, "_blank") : null).catch((e2) => console.error(e2));
|
|
902
|
-
else Promise.resolve(this.#emit("link", { a, href }, true)).then((x) => x ? this.goTo(href) : null).catch((e2) => console.error(e2));
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
async addAnnotation(annotation, remove) {
|
|
906
|
-
var _a;
|
|
907
|
-
const { value } = annotation;
|
|
908
|
-
if (value.startsWith(SEARCH_PREFIX)) {
|
|
909
|
-
const cfi = value.replace(SEARCH_PREFIX, "");
|
|
910
|
-
const { index: index2, anchor: anchor2 } = await this.resolveNavigation(cfi);
|
|
911
|
-
const obj2 = this.#getOverlayer(index2);
|
|
912
|
-
if (obj2) {
|
|
913
|
-
const { overlayer, doc } = obj2;
|
|
914
|
-
if (remove) {
|
|
915
|
-
overlayer.remove(value);
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
const range = doc ? anchor2(doc) : anchor2;
|
|
919
|
-
overlayer.add(value, range, Overlayer.outline);
|
|
920
|
-
}
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
const { index, anchor } = await this.resolveNavigation(value);
|
|
924
|
-
const obj = this.#getOverlayer(index);
|
|
925
|
-
if (obj) {
|
|
926
|
-
const { overlayer, doc } = obj;
|
|
927
|
-
overlayer.remove(value);
|
|
928
|
-
if (!remove) {
|
|
929
|
-
const range = doc ? anchor(doc) : anchor;
|
|
930
|
-
const draw = (func, opts) => overlayer.add(value, range, func, opts);
|
|
931
|
-
this.#emit("draw-annotation", { draw, annotation, doc, range });
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
const label = ((_a = this.#tocProgress.getProgress(index)) == null ? void 0 : _a.label) ?? "";
|
|
935
|
-
return { index, label };
|
|
936
|
-
}
|
|
937
|
-
deleteAnnotation(annotation) {
|
|
938
|
-
return this.addAnnotation(annotation, true);
|
|
939
|
-
}
|
|
940
|
-
#getOverlayer(index) {
|
|
941
|
-
return this.renderer.getContents().find((x) => x.index === index && x.overlayer);
|
|
942
|
-
}
|
|
943
|
-
#createOverlayer({ doc, index }) {
|
|
944
|
-
const overlayer = new Overlayer();
|
|
945
|
-
doc.addEventListener("click", (e) => {
|
|
946
|
-
const [value, range] = overlayer.hitTest(e);
|
|
947
|
-
if (value && !value.startsWith(SEARCH_PREFIX)) {
|
|
948
|
-
this.#emit("show-annotation", { value, index, range });
|
|
949
|
-
}
|
|
950
|
-
}, false);
|
|
951
|
-
const list = this.#searchResults.get(index);
|
|
952
|
-
if (list) for (const item of list) this.addAnnotation(item);
|
|
953
|
-
this.#emit("create-overlay", { index });
|
|
954
|
-
return overlayer;
|
|
955
|
-
}
|
|
956
|
-
async showAnnotation(annotation) {
|
|
957
|
-
const { value } = annotation;
|
|
958
|
-
const resolved = await this.goTo(value);
|
|
959
|
-
if (resolved) {
|
|
960
|
-
const { index, anchor } = resolved;
|
|
961
|
-
const { doc } = this.#getOverlayer(index);
|
|
962
|
-
const range = anchor(doc);
|
|
963
|
-
this.#emit("show-annotation", { value, index, range });
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
getCFI(index, range) {
|
|
967
|
-
const baseCFI = this.book.sections[index].cfi ?? fake.fromIndex(index);
|
|
968
|
-
if (!range) return baseCFI;
|
|
969
|
-
return joinIndir(baseCFI, fromRange(range));
|
|
970
|
-
}
|
|
971
|
-
resolveCFI(cfi) {
|
|
972
|
-
if (this.book.resolveCFI)
|
|
973
|
-
return this.book.resolveCFI(cfi);
|
|
974
|
-
else {
|
|
975
|
-
const parts = parse(cfi);
|
|
976
|
-
const index = fake.toIndex((parts.parent ?? parts).shift());
|
|
977
|
-
const anchor = (doc) => toRange(doc, parts);
|
|
978
|
-
return { index, anchor };
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
resolveNavigation(target) {
|
|
982
|
-
try {
|
|
983
|
-
if (typeof target === "number") return { index: target };
|
|
984
|
-
if (typeof target.fraction === "number") {
|
|
985
|
-
const [index, anchor] = this.#sectionProgress.getSection(target.fraction);
|
|
986
|
-
return { index, anchor };
|
|
987
|
-
}
|
|
988
|
-
if (isCFI.test(target)) return this.resolveCFI(target);
|
|
989
|
-
return this.book.resolveHref(target);
|
|
990
|
-
} catch (e) {
|
|
991
|
-
console.error(e);
|
|
992
|
-
console.error(`Could not resolve target ${target}`);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
async goTo(target) {
|
|
996
|
-
const resolved = this.resolveNavigation(target);
|
|
997
|
-
try {
|
|
998
|
-
await this.renderer.goTo(resolved);
|
|
999
|
-
this.history.pushState(target);
|
|
1000
|
-
return resolved;
|
|
1001
|
-
} catch (e) {
|
|
1002
|
-
console.error(e);
|
|
1003
|
-
console.error(`Could not go to ${target}`);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
async goToFraction(frac) {
|
|
1007
|
-
const [index, anchor] = this.#sectionProgress.getSection(frac);
|
|
1008
|
-
await this.renderer.goTo({ index, anchor });
|
|
1009
|
-
this.history.pushState({ fraction: frac });
|
|
1010
|
-
}
|
|
1011
|
-
async select(target) {
|
|
1012
|
-
try {
|
|
1013
|
-
const obj = await this.resolveNavigation(target);
|
|
1014
|
-
await this.renderer.goTo({ ...obj, select: true });
|
|
1015
|
-
this.history.pushState(target);
|
|
1016
|
-
} catch (e) {
|
|
1017
|
-
console.error(e);
|
|
1018
|
-
console.error(`Could not go to ${target}`);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
deselect() {
|
|
1022
|
-
for (const { doc } of this.renderer.getContents())
|
|
1023
|
-
doc.defaultView.getSelection().removeAllRanges();
|
|
1024
|
-
}
|
|
1025
|
-
getSectionFractions() {
|
|
1026
|
-
var _a;
|
|
1027
|
-
return (((_a = this.#sectionProgress) == null ? void 0 : _a.sectionFractions) ?? []).map((x) => x + Number.EPSILON);
|
|
1028
|
-
}
|
|
1029
|
-
getProgressOf(index, range) {
|
|
1030
|
-
var _a, _b;
|
|
1031
|
-
const tocItem = (_a = this.#tocProgress) == null ? void 0 : _a.getProgress(index, range);
|
|
1032
|
-
const pageItem = (_b = this.#pageProgress) == null ? void 0 : _b.getProgress(index, range);
|
|
1033
|
-
return { tocItem, pageItem };
|
|
1034
|
-
}
|
|
1035
|
-
async getTOCItemOf(target) {
|
|
1036
|
-
try {
|
|
1037
|
-
const { index, anchor } = await this.resolveNavigation(target);
|
|
1038
|
-
const doc = await this.book.sections[index].createDocument();
|
|
1039
|
-
const frag = anchor(doc);
|
|
1040
|
-
const isRange = frag instanceof Range;
|
|
1041
|
-
const range = isRange ? frag : doc.createRange();
|
|
1042
|
-
if (!isRange) range.selectNodeContents(frag);
|
|
1043
|
-
return this.#tocProgress.getProgress(index, range);
|
|
1044
|
-
} catch (e) {
|
|
1045
|
-
console.error(e);
|
|
1046
|
-
console.error(`Could not get ${target}`);
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
async prev(distance) {
|
|
1050
|
-
await this.renderer.prev(distance);
|
|
1051
|
-
}
|
|
1052
|
-
async next(distance) {
|
|
1053
|
-
await this.renderer.next(distance);
|
|
1054
|
-
}
|
|
1055
|
-
goLeft() {
|
|
1056
|
-
return this.book.dir === "rtl" ? this.next() : this.prev();
|
|
1057
|
-
}
|
|
1058
|
-
goRight() {
|
|
1059
|
-
return this.book.dir === "rtl" ? this.prev() : this.next();
|
|
1060
|
-
}
|
|
1061
|
-
async *#searchSection(matcher, query, index) {
|
|
1062
|
-
const doc = await this.book.sections[index].createDocument();
|
|
1063
|
-
for (const { range, excerpt } of matcher(doc, query))
|
|
1064
|
-
yield { cfi: this.getCFI(index, range), excerpt };
|
|
1065
|
-
}
|
|
1066
|
-
async *#searchBook(matcher, query) {
|
|
1067
|
-
const { sections } = this.book;
|
|
1068
|
-
for (const [index, { createDocument }] of sections.entries()) {
|
|
1069
|
-
if (!createDocument) continue;
|
|
1070
|
-
const doc = await createDocument();
|
|
1071
|
-
const subitems = Array.from(matcher(doc, query), ({ range, excerpt }) => ({ cfi: this.getCFI(index, range), excerpt }));
|
|
1072
|
-
const progress = (index + 1) / sections.length;
|
|
1073
|
-
yield { progress };
|
|
1074
|
-
if (subitems.length) yield { index, subitems };
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
async *search(opts) {
|
|
1078
|
-
var _a;
|
|
1079
|
-
this.clearSearch();
|
|
1080
|
-
const { searchMatcher } = await import("./search-CQGJojwL.js");
|
|
1081
|
-
const { query, index } = opts;
|
|
1082
|
-
const matcher = searchMatcher(
|
|
1083
|
-
textWalker,
|
|
1084
|
-
{ defaultLocale: this.language, ...opts }
|
|
1085
|
-
);
|
|
1086
|
-
const iter = index != null ? this.#searchSection(matcher, query, index) : this.#searchBook(matcher, query);
|
|
1087
|
-
const list = [];
|
|
1088
|
-
this.#searchResults.set(index, list);
|
|
1089
|
-
for await (const result of iter) {
|
|
1090
|
-
if (result.subitems) {
|
|
1091
|
-
const list2 = result.subitems.map(({ cfi }) => ({ value: SEARCH_PREFIX + cfi }));
|
|
1092
|
-
this.#searchResults.set(result.index, list2);
|
|
1093
|
-
for (const item of list2) this.addAnnotation(item);
|
|
1094
|
-
yield {
|
|
1095
|
-
label: ((_a = this.#tocProgress.getProgress(result.index)) == null ? void 0 : _a.label) ?? "",
|
|
1096
|
-
subitems: result.subitems
|
|
1097
|
-
};
|
|
1098
|
-
} else {
|
|
1099
|
-
if (result.cfi) {
|
|
1100
|
-
const item = { value: SEARCH_PREFIX + result.cfi };
|
|
1101
|
-
list.push(item);
|
|
1102
|
-
this.addAnnotation(item);
|
|
1103
|
-
}
|
|
1104
|
-
yield result;
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
yield "done";
|
|
1108
|
-
}
|
|
1109
|
-
clearSearch() {
|
|
1110
|
-
for (const list of this.#searchResults.values())
|
|
1111
|
-
for (const item of list) this.deleteAnnotation(item);
|
|
1112
|
-
this.#searchResults.clear();
|
|
1113
|
-
}
|
|
1114
|
-
async initTTS(granularity = "word", highlight) {
|
|
1115
|
-
const doc = this.renderer.getContents()[0].doc;
|
|
1116
|
-
if (this.tts && this.tts.doc === doc) return;
|
|
1117
|
-
const { TTS } = await import("./tts-CTh0nMvu.js");
|
|
1118
|
-
this.tts = new TTS(doc, textWalker, highlight || ((range) => this.renderer.scrollToAnchor(range, true)), granularity);
|
|
1119
|
-
}
|
|
1120
|
-
startMediaOverlay() {
|
|
1121
|
-
const { index } = this.renderer.getContents()[0];
|
|
1122
|
-
return this.mediaOverlay.start(index);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
customElements.define("foliate-view", View);
|
|
1126
|
-
const view = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1127
|
-
__proto__: null,
|
|
1128
|
-
NotFoundError,
|
|
1129
|
-
ResponseError,
|
|
1130
|
-
UnsupportedTypeError,
|
|
1131
|
-
View,
|
|
1132
|
-
makeBook
|
|
1133
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1134
|
-
export {
|
|
1135
|
-
toRange as a,
|
|
1136
|
-
fromElements as f,
|
|
1137
|
-
parse as p,
|
|
1138
|
-
toElement as t,
|
|
1139
|
-
view as v
|
|
1140
|
-
};
|