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,1222 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const unescapeHTML = (str) => {
4
- if (!str) return "";
5
- const textarea = document.createElement("textarea");
6
- textarea.innerHTML = str;
7
- return textarea.value;
8
- };
9
- const MIME = {
10
- XML: "application/xml",
11
- XHTML: "application/xhtml+xml",
12
- HTML: "text/html",
13
- CSS: "text/css",
14
- SVG: "image/svg+xml"
15
- };
16
- const PDB_HEADER = {
17
- name: [0, 32, "string"],
18
- type: [60, 4, "string"],
19
- creator: [64, 4, "string"],
20
- numRecords: [76, 2, "uint"]
21
- };
22
- const PALMDOC_HEADER = {
23
- compression: [0, 2, "uint"],
24
- numTextRecords: [8, 2, "uint"],
25
- recordSize: [10, 2, "uint"],
26
- encryption: [12, 2, "uint"]
27
- };
28
- const MOBI_HEADER = {
29
- magic: [16, 4, "string"],
30
- length: [20, 4, "uint"],
31
- type: [24, 4, "uint"],
32
- encoding: [28, 4, "uint"],
33
- uid: [32, 4, "uint"],
34
- version: [36, 4, "uint"],
35
- titleOffset: [84, 4, "uint"],
36
- titleLength: [88, 4, "uint"],
37
- localeRegion: [94, 1, "uint"],
38
- localeLanguage: [95, 1, "uint"],
39
- resourceStart: [108, 4, "uint"],
40
- huffcdic: [112, 4, "uint"],
41
- numHuffcdic: [116, 4, "uint"],
42
- exthFlag: [128, 4, "uint"],
43
- trailingFlags: [240, 4, "uint"],
44
- indx: [244, 4, "uint"]
45
- };
46
- const KF8_HEADER = {
47
- resourceStart: [108, 4, "uint"],
48
- fdst: [192, 4, "uint"],
49
- numFdst: [196, 4, "uint"],
50
- frag: [248, 4, "uint"],
51
- skel: [252, 4, "uint"],
52
- guide: [260, 4, "uint"]
53
- };
54
- const EXTH_HEADER = {
55
- magic: [0, 4, "string"],
56
- length: [4, 4, "uint"],
57
- count: [8, 4, "uint"]
58
- };
59
- const INDX_HEADER = {
60
- magic: [0, 4, "string"],
61
- length: [4, 4, "uint"],
62
- type: [8, 4, "uint"],
63
- idxt: [20, 4, "uint"],
64
- numRecords: [24, 4, "uint"],
65
- encoding: [28, 4, "uint"],
66
- language: [32, 4, "uint"],
67
- total: [36, 4, "uint"],
68
- ordt: [40, 4, "uint"],
69
- ligt: [44, 4, "uint"],
70
- numLigt: [48, 4, "uint"],
71
- numCncx: [52, 4, "uint"]
72
- };
73
- const TAGX_HEADER = {
74
- magic: [0, 4, "string"],
75
- length: [4, 4, "uint"],
76
- numControlBytes: [8, 4, "uint"]
77
- };
78
- const HUFF_HEADER = {
79
- magic: [0, 4, "string"],
80
- offset1: [8, 4, "uint"],
81
- offset2: [12, 4, "uint"]
82
- };
83
- const CDIC_HEADER = {
84
- magic: [0, 4, "string"],
85
- length: [4, 4, "uint"],
86
- numEntries: [8, 4, "uint"],
87
- codeLength: [12, 4, "uint"]
88
- };
89
- const FDST_HEADER = {
90
- magic: [0, 4, "string"],
91
- numEntries: [8, 4, "uint"]
92
- };
93
- const FONT_HEADER = {
94
- flags: [8, 4, "uint"],
95
- dataStart: [12, 4, "uint"],
96
- keyLength: [16, 4, "uint"],
97
- keyStart: [20, 4, "uint"]
98
- };
99
- const MOBI_ENCODING = {
100
- 1252: "windows-1252",
101
- 65001: "utf-8"
102
- };
103
- const EXTH_RECORD_TYPE = {
104
- 100: ["creator", "string", true],
105
- 101: ["publisher"],
106
- 103: ["description"],
107
- 104: ["isbn"],
108
- 105: ["subject", "string", true],
109
- 106: ["date"],
110
- 108: ["contributor", "string", true],
111
- 109: ["rights"],
112
- 110: ["subjectCode", "string", true],
113
- 112: ["source", "string", true],
114
- 113: ["asin"],
115
- 121: ["boundary", "uint"],
116
- 122: ["fixedLayout"],
117
- 125: ["numResources", "uint"],
118
- 126: ["originalResolution"],
119
- 127: ["zeroGutter"],
120
- 128: ["zeroMargin"],
121
- 129: ["coverURI"],
122
- 132: ["regionMagnification"],
123
- 201: ["coverOffset", "uint"],
124
- 202: ["thumbnailOffset", "uint"],
125
- 503: ["title"],
126
- 524: ["language", "string", true],
127
- 527: ["pageProgressionDirection"]
128
- };
129
- const MOBI_LANG = {
130
- 1: [
131
- "ar",
132
- "ar-SA",
133
- "ar-IQ",
134
- "ar-EG",
135
- "ar-LY",
136
- "ar-DZ",
137
- "ar-MA",
138
- "ar-TN",
139
- "ar-OM",
140
- "ar-YE",
141
- "ar-SY",
142
- "ar-JO",
143
- "ar-LB",
144
- "ar-KW",
145
- "ar-AE",
146
- "ar-BH",
147
- "ar-QA"
148
- ],
149
- 2: ["bg"],
150
- 3: ["ca"],
151
- 4: ["zh", "zh-TW", "zh-CN", "zh-HK", "zh-SG"],
152
- 5: ["cs"],
153
- 6: ["da"],
154
- 7: ["de", "de-DE", "de-CH", "de-AT", "de-LU", "de-LI"],
155
- 8: ["el"],
156
- 9: [
157
- "en",
158
- "en-US",
159
- "en-GB",
160
- "en-AU",
161
- "en-CA",
162
- "en-NZ",
163
- "en-IE",
164
- "en-ZA",
165
- "en-JM",
166
- null,
167
- "en-BZ",
168
- "en-TT",
169
- "en-ZW",
170
- "en-PH"
171
- ],
172
- 10: [
173
- "es",
174
- "es-ES",
175
- "es-MX",
176
- null,
177
- "es-GT",
178
- "es-CR",
179
- "es-PA",
180
- "es-DO",
181
- "es-VE",
182
- "es-CO",
183
- "es-PE",
184
- "es-AR",
185
- "es-EC",
186
- "es-CL",
187
- "es-UY",
188
- "es-PY",
189
- "es-BO",
190
- "es-SV",
191
- "es-HN",
192
- "es-NI",
193
- "es-PR"
194
- ],
195
- 11: ["fi"],
196
- 12: ["fr", "fr-FR", "fr-BE", "fr-CA", "fr-CH", "fr-LU", "fr-MC"],
197
- 13: ["he"],
198
- 14: ["hu"],
199
- 15: ["is"],
200
- 16: ["it", "it-IT", "it-CH"],
201
- 17: ["ja"],
202
- 18: ["ko"],
203
- 19: ["nl", "nl-NL", "nl-BE"],
204
- 20: ["no", "nb", "nn"],
205
- 21: ["pl"],
206
- 22: ["pt", "pt-BR", "pt-PT"],
207
- 23: ["rm"],
208
- 24: ["ro"],
209
- 25: ["ru"],
210
- 26: ["hr", null, "sr"],
211
- 27: ["sk"],
212
- 28: ["sq"],
213
- 29: ["sv", "sv-SE", "sv-FI"],
214
- 30: ["th"],
215
- 31: ["tr"],
216
- 32: ["ur"],
217
- 33: ["id"],
218
- 34: ["uk"],
219
- 35: ["be"],
220
- 36: ["sl"],
221
- 37: ["et"],
222
- 38: ["lv"],
223
- 39: ["lt"],
224
- 41: ["fa"],
225
- 42: ["vi"],
226
- 43: ["hy"],
227
- 44: ["az"],
228
- 45: ["eu"],
229
- 46: ["hsb"],
230
- 47: ["mk"],
231
- 48: ["st"],
232
- 49: ["ts"],
233
- 50: ["tn"],
234
- 52: ["xh"],
235
- 53: ["zu"],
236
- 54: ["af"],
237
- 55: ["ka"],
238
- 56: ["fo"],
239
- 57: ["hi"],
240
- 58: ["mt"],
241
- 59: ["se"],
242
- 62: ["ms"],
243
- 63: ["kk"],
244
- 65: ["sw"],
245
- 67: ["uz", null, "uz-UZ"],
246
- 68: ["tt"],
247
- 69: ["bn"],
248
- 70: ["pa"],
249
- 71: ["gu"],
250
- 72: ["or"],
251
- 73: ["ta"],
252
- 74: ["te"],
253
- 75: ["kn"],
254
- 76: ["ml"],
255
- 77: ["as"],
256
- 78: ["mr"],
257
- 79: ["sa"],
258
- 82: ["cy", "cy-GB"],
259
- 83: ["gl", "gl-ES"],
260
- 87: ["kok"],
261
- 97: ["ne"],
262
- 98: ["fy"]
263
- };
264
- const concatTypedArray = (a, b) => {
265
- const result = new a.constructor(a.length + b.length);
266
- result.set(a);
267
- result.set(b, a.length);
268
- return result;
269
- };
270
- const concatTypedArray3 = (a, b, c) => {
271
- const result = new a.constructor(a.length + b.length + c.length);
272
- result.set(a);
273
- result.set(b, a.length);
274
- result.set(c, a.length + b.length);
275
- return result;
276
- };
277
- const decoder = new TextDecoder();
278
- const getString = (buffer) => decoder.decode(buffer);
279
- const getUint = (buffer) => {
280
- if (!buffer) return;
281
- const l = buffer.byteLength;
282
- const func = l === 4 ? "getUint32" : l === 2 ? "getUint16" : "getUint8";
283
- return new DataView(buffer)[func](0);
284
- };
285
- const getStruct = (def, buffer) => Object.fromEntries(Array.from(Object.entries(def)).map(([key, [start, len, type]]) => [
286
- key,
287
- (type === "string" ? getString : getUint)(buffer.slice(start, start + len))
288
- ]));
289
- const getDecoder = (x) => new TextDecoder(MOBI_ENCODING[x]);
290
- const getVarLen = (byteArray, i = 0) => {
291
- let value = 0, length = 0;
292
- for (const byte of byteArray.subarray(i, i + 4)) {
293
- value = value << 7 | (byte & 127) >>> 0;
294
- length++;
295
- if (byte & 128) break;
296
- }
297
- return { value, length };
298
- };
299
- const getVarLenFromEnd = (byteArray) => {
300
- let value = 0;
301
- for (const byte of byteArray.subarray(-4)) {
302
- if (byte & 128) value = 0;
303
- value = value << 7 | byte & 127;
304
- }
305
- return value;
306
- };
307
- const countBitsSet = (x) => {
308
- let count = 0;
309
- for (; x > 0; x = x >> 1) if ((x & 1) === 1) count++;
310
- return count;
311
- };
312
- const countUnsetEnd = (x) => {
313
- let count = 0;
314
- while ((x & 1) === 0) x = x >> 1, count++;
315
- return count;
316
- };
317
- const decompressPalmDOC = (array) => {
318
- let output = [];
319
- for (let i = 0; i < array.length; i++) {
320
- const byte = array[i];
321
- if (byte === 0) output.push(0);
322
- else if (byte <= 8)
323
- for (const x of array.subarray(i + 1, (i += byte) + 1))
324
- output.push(x);
325
- else if (byte <= 127) output.push(byte);
326
- else if (byte <= 191) {
327
- const bytes = byte << 8 | array[i++ + 1];
328
- const distance = (bytes & 16383) >>> 3;
329
- const length = (bytes & 7) + 3;
330
- for (let j = 0; j < length; j++)
331
- output.push(output[output.length - distance]);
332
- } else output.push(32, byte ^ 128);
333
- }
334
- return Uint8Array.from(output);
335
- };
336
- const read32Bits = (byteArray, from) => {
337
- const startByte = from >> 3;
338
- const end = from + 32;
339
- const endByte = end >> 3;
340
- let bits = 0n;
341
- for (let i = startByte; i <= endByte; i++)
342
- bits = bits << 8n | BigInt(byteArray[i] ?? 0);
343
- return bits >> 8n - BigInt(end & 7) & 0xffffffffn;
344
- };
345
- const huffcdic = async (mobi, loadRecord) => {
346
- const huffRecord = await loadRecord(mobi.huffcdic);
347
- const { magic, offset1, offset2 } = getStruct(HUFF_HEADER, huffRecord);
348
- if (magic !== "HUFF") throw new Error("Invalid HUFF record");
349
- const table1 = Array.from({ length: 256 }, (_, i) => offset1 + i * 4).map((offset) => getUint(huffRecord.slice(offset, offset + 4))).map((x) => [x & 128, x & 31, x >>> 8]);
350
- const table2 = [null].concat(Array.from({ length: 32 }, (_, i) => offset2 + i * 8).map((offset) => [
351
- getUint(huffRecord.slice(offset, offset + 4)),
352
- getUint(huffRecord.slice(offset + 4, offset + 8))
353
- ]));
354
- const dictionary = [];
355
- for (let i = 1; i < mobi.numHuffcdic; i++) {
356
- const record = await loadRecord(mobi.huffcdic + i);
357
- const cdic = getStruct(CDIC_HEADER, record);
358
- if (cdic.magic !== "CDIC") throw new Error("Invalid CDIC record");
359
- const n = Math.min(1 << cdic.codeLength, cdic.numEntries - dictionary.length);
360
- const buffer = record.slice(cdic.length);
361
- for (let i2 = 0; i2 < n; i2++) {
362
- const offset = getUint(buffer.slice(i2 * 2, i2 * 2 + 2));
363
- const x = getUint(buffer.slice(offset, offset + 2));
364
- const length = x & 32767;
365
- const decompressed = x & 32768;
366
- const value = new Uint8Array(
367
- buffer.slice(offset + 2, offset + 2 + length)
368
- );
369
- dictionary.push([value, decompressed]);
370
- }
371
- }
372
- const decompress = (byteArray) => {
373
- let output = new Uint8Array();
374
- const bitLength = byteArray.byteLength * 8;
375
- for (let i = 0; i < bitLength; ) {
376
- const bits = Number(read32Bits(byteArray, i));
377
- let [found, codeLength, value] = table1[bits >>> 24];
378
- if (!found) {
379
- while (bits >>> 32 - codeLength < table2[codeLength][0])
380
- codeLength += 1;
381
- value = table2[codeLength][1];
382
- }
383
- if ((i += codeLength) > bitLength) break;
384
- const code = value - (bits >>> 32 - codeLength);
385
- let [result, decompressed] = dictionary[code];
386
- if (!decompressed) {
387
- result = decompress(result);
388
- dictionary[code] = [result, true];
389
- }
390
- output = concatTypedArray(output, result);
391
- }
392
- return output;
393
- };
394
- return decompress;
395
- };
396
- const getIndexData = async (indxIndex, loadRecord) => {
397
- const indxRecord = await loadRecord(indxIndex);
398
- const indx = getStruct(INDX_HEADER, indxRecord);
399
- if (indx.magic !== "INDX") throw new Error("Invalid INDX record");
400
- const decoder2 = getDecoder(indx.encoding);
401
- const tagxBuffer = indxRecord.slice(indx.length);
402
- const tagx = getStruct(TAGX_HEADER, tagxBuffer);
403
- if (tagx.magic !== "TAGX") throw new Error("Invalid TAGX section");
404
- const numTags = (tagx.length - 12) / 4;
405
- const tagTable = Array.from({ length: numTags }, (_, i) => new Uint8Array(tagxBuffer.slice(12 + i * 4, 12 + i * 4 + 4)));
406
- const cncx = {};
407
- let cncxRecordOffset = 0;
408
- for (let i = 0; i < indx.numCncx; i++) {
409
- const record = await loadRecord(indxIndex + indx.numRecords + i + 1);
410
- const array = new Uint8Array(record);
411
- for (let pos = 0; pos < array.byteLength; ) {
412
- const index = pos;
413
- const { value, length } = getVarLen(array, pos);
414
- pos += length;
415
- const result = record.slice(pos, pos + value);
416
- pos += value;
417
- cncx[cncxRecordOffset + index] = decoder2.decode(result);
418
- }
419
- cncxRecordOffset += 65536;
420
- }
421
- const table = [];
422
- for (let i = 0; i < indx.numRecords; i++) {
423
- const record = await loadRecord(indxIndex + 1 + i);
424
- const array = new Uint8Array(record);
425
- const indx2 = getStruct(INDX_HEADER, record);
426
- if (indx2.magic !== "INDX") throw new Error("Invalid INDX record");
427
- for (let j = 0; j < indx2.numRecords; j++) {
428
- const offsetOffset = indx2.idxt + 4 + 2 * j;
429
- const offset = getUint(record.slice(offsetOffset, offsetOffset + 2));
430
- const length = getUint(record.slice(offset, offset + 1));
431
- const name = getString(record.slice(offset + 1, offset + 1 + length));
432
- const tags = [];
433
- const startPos = offset + 1 + length;
434
- let controlByteIndex = 0;
435
- let pos = startPos + tagx.numControlBytes;
436
- for (const [tag, numValues, mask, end] of tagTable) {
437
- if (end & 1) {
438
- controlByteIndex++;
439
- continue;
440
- }
441
- const offset2 = startPos + controlByteIndex;
442
- const value = getUint(record.slice(offset2, offset2 + 1)) & mask;
443
- if (value === mask) {
444
- if (countBitsSet(mask) > 1) {
445
- const { value: value2, length: length2 } = getVarLen(array, pos);
446
- tags.push([tag, null, value2, numValues]);
447
- pos += length2;
448
- } else tags.push([tag, 1, null, numValues]);
449
- } else tags.push([tag, value >> countUnsetEnd(mask), null, numValues]);
450
- }
451
- const tagMap = {};
452
- for (const [tag, valueCount, valueBytes, numValues] of tags) {
453
- const values = [];
454
- if (valueCount != null) {
455
- for (let i2 = 0; i2 < valueCount * numValues; i2++) {
456
- const { value, length: length2 } = getVarLen(array, pos);
457
- values.push(value);
458
- pos += length2;
459
- }
460
- } else {
461
- let count = 0;
462
- while (count < valueBytes) {
463
- const { value, length: length2 } = getVarLen(array, pos);
464
- values.push(value);
465
- pos += length2;
466
- count += length2;
467
- }
468
- }
469
- tagMap[tag] = values;
470
- }
471
- table.push({ name, tagMap });
472
- }
473
- }
474
- return { table, cncx };
475
- };
476
- const getNCX = async (indxIndex, loadRecord) => {
477
- const { table, cncx } = await getIndexData(indxIndex, loadRecord);
478
- const items = table.map(({ tagMap }, index) => {
479
- var _a, _b, _c, _d, _e, _f;
480
- return {
481
- index,
482
- offset: (_a = tagMap[1]) == null ? void 0 : _a[0],
483
- size: (_b = tagMap[2]) == null ? void 0 : _b[0],
484
- label: cncx[tagMap[3]] ?? "",
485
- headingLevel: (_c = tagMap[4]) == null ? void 0 : _c[0],
486
- pos: tagMap[6],
487
- parent: (_d = tagMap[21]) == null ? void 0 : _d[0],
488
- firstChild: (_e = tagMap[22]) == null ? void 0 : _e[0],
489
- lastChild: (_f = tagMap[23]) == null ? void 0 : _f[0]
490
- };
491
- });
492
- const getChildren = (item) => {
493
- if (item.firstChild == null) return item;
494
- item.children = items.filter((x) => x.parent === item.index).map(getChildren);
495
- return item;
496
- };
497
- return items.filter((item) => item.headingLevel === 0).map(getChildren);
498
- };
499
- const getEXTH = (buf, encoding) => {
500
- const { magic, count } = getStruct(EXTH_HEADER, buf);
501
- if (magic !== "EXTH") throw new Error("Invalid EXTH header");
502
- const decoder2 = getDecoder(encoding);
503
- const results = {};
504
- let offset = 12;
505
- for (let i = 0; i < count; i++) {
506
- const type = getUint(buf.slice(offset, offset + 4));
507
- const length = getUint(buf.slice(offset + 4, offset + 8));
508
- if (type in EXTH_RECORD_TYPE) {
509
- const [name, typ, many] = EXTH_RECORD_TYPE[type];
510
- const data = buf.slice(offset + 8, offset + length);
511
- const value = typ === "uint" ? getUint(data) : decoder2.decode(data);
512
- if (many) {
513
- results[name] ??= [];
514
- results[name].push(value);
515
- } else results[name] = value;
516
- }
517
- offset += length;
518
- }
519
- return results;
520
- };
521
- const getFont = async (buf, unzlib) => {
522
- const { flags, dataStart, keyLength, keyStart } = getStruct(FONT_HEADER, buf);
523
- const array = new Uint8Array(buf.slice(dataStart));
524
- if (flags & 2) {
525
- const bytes = keyLength === 16 ? 1024 : 1040;
526
- const key = new Uint8Array(buf.slice(keyStart, keyStart + keyLength));
527
- const length = Math.min(bytes, array.length);
528
- for (var i = 0; i < length; i++) array[i] = array[i] ^ key[i % key.length];
529
- }
530
- if (flags & 1) try {
531
- return await unzlib(array);
532
- } catch (e) {
533
- console.warn(e);
534
- console.warn("Failed to decompress font");
535
- }
536
- return array;
537
- };
538
- const isMOBI = async (file) => {
539
- const magic = getString(await file.slice(60, 68).arrayBuffer());
540
- return magic === "BOOKMOBI";
541
- };
542
- class PDB {
543
- #file;
544
- #offsets;
545
- pdb;
546
- async open(file) {
547
- this.#file = file;
548
- const pdb = getStruct(PDB_HEADER, await file.slice(0, 78).arrayBuffer());
549
- this.pdb = pdb;
550
- const buffer = await file.slice(78, 78 + pdb.numRecords * 8).arrayBuffer();
551
- this.#offsets = Array.from(
552
- { length: pdb.numRecords },
553
- (_, i) => getUint(buffer.slice(i * 8, i * 8 + 4))
554
- ).map((x, i, a) => [x, a[i + 1]]);
555
- }
556
- loadRecord(index) {
557
- const offsets = this.#offsets[index];
558
- if (!offsets) throw new RangeError("Record index out of bounds");
559
- return this.#file.slice(...offsets).arrayBuffer();
560
- }
561
- async loadMagic(index) {
562
- const start = this.#offsets[index][0];
563
- return getString(await this.#file.slice(start, start + 4).arrayBuffer());
564
- }
565
- }
566
- class MOBI extends PDB {
567
- #start = 0;
568
- #resourceStart;
569
- #decoder;
570
- #encoder;
571
- #decompress;
572
- #removeTrailingEntries;
573
- constructor({ unzlib }) {
574
- super();
575
- this.unzlib = unzlib;
576
- }
577
- async open(file) {
578
- var _a;
579
- await super.open(file);
580
- this.headers = this.#getHeaders(await super.loadRecord(0));
581
- this.#resourceStart = this.headers.mobi.resourceStart;
582
- let isKF8 = this.headers.mobi.version >= 8;
583
- if (!isKF8) {
584
- const boundary = (_a = this.headers.exth) == null ? void 0 : _a.boundary;
585
- if (boundary < 4294967295) try {
586
- this.headers = this.#getHeaders(await super.loadRecord(boundary));
587
- this.#start = boundary;
588
- isKF8 = true;
589
- } catch (e) {
590
- console.warn(e);
591
- console.warn("Failed to open KF8; falling back to MOBI");
592
- }
593
- }
594
- await this.#setup();
595
- return isKF8 ? new KF8(this).init() : new MOBI6(this).init();
596
- }
597
- #getHeaders(buf) {
598
- const palmdoc = getStruct(PALMDOC_HEADER, buf);
599
- const mobi = getStruct(MOBI_HEADER, buf);
600
- if (mobi.magic !== "MOBI") throw new Error("Missing MOBI header");
601
- const { titleOffset, titleLength, localeLanguage, localeRegion } = mobi;
602
- mobi.title = buf.slice(titleOffset, titleOffset + titleLength);
603
- const lang = MOBI_LANG[localeLanguage];
604
- mobi.language = (lang == null ? void 0 : lang[localeRegion >> 2]) ?? (lang == null ? void 0 : lang[0]);
605
- const exth = mobi.exthFlag & 64 ? getEXTH(buf.slice(mobi.length + 16), mobi.encoding) : null;
606
- const kf8 = mobi.version >= 8 ? getStruct(KF8_HEADER, buf) : null;
607
- return { palmdoc, mobi, exth, kf8 };
608
- }
609
- async #setup() {
610
- const { palmdoc, mobi } = this.headers;
611
- this.#decoder = getDecoder(mobi.encoding);
612
- this.#encoder = new TextEncoder();
613
- const { compression } = palmdoc;
614
- this.#decompress = compression === 1 ? (f) => f : compression === 2 ? decompressPalmDOC : compression === 17480 ? await huffcdic(mobi, this.loadRecord.bind(this)) : null;
615
- if (!this.#decompress) throw new Error("Unknown compression type");
616
- const { trailingFlags } = mobi;
617
- const multibyte = trailingFlags & 1;
618
- const numTrailingEntries = countBitsSet(trailingFlags >>> 1);
619
- this.#removeTrailingEntries = (array) => {
620
- for (let i = 0; i < numTrailingEntries; i++) {
621
- const length = getVarLenFromEnd(array);
622
- array = array.subarray(0, -length);
623
- }
624
- if (multibyte) {
625
- const length = (array[array.length - 1] & 3) + 1;
626
- array = array.subarray(0, -length);
627
- }
628
- return array;
629
- };
630
- }
631
- decode(...args) {
632
- return this.#decoder.decode(...args);
633
- }
634
- encode(...args) {
635
- return this.#encoder.encode(...args);
636
- }
637
- loadRecord(index) {
638
- return super.loadRecord(this.#start + index);
639
- }
640
- loadMagic(index) {
641
- return super.loadMagic(this.#start + index);
642
- }
643
- loadText(index) {
644
- return this.loadRecord(index + 1).then((buf) => new Uint8Array(buf)).then(this.#removeTrailingEntries).then(this.#decompress);
645
- }
646
- async loadResource(index) {
647
- const buf = await super.loadRecord(this.#resourceStart + index);
648
- const magic = getString(buf.slice(0, 4));
649
- if (magic === "FONT") return getFont(buf, this.unzlib);
650
- if (magic === "VIDE" || magic === "AUDI") return buf.slice(12);
651
- return buf;
652
- }
653
- getNCX() {
654
- const index = this.headers.mobi.indx;
655
- if (index < 4294967295) return getNCX(index, this.loadRecord.bind(this));
656
- }
657
- getMetadata() {
658
- var _a, _b;
659
- const { mobi, exth } = this.headers;
660
- return {
661
- identifier: mobi.uid.toString(),
662
- title: unescapeHTML((exth == null ? void 0 : exth.title) || this.decode(mobi.title)),
663
- author: (_a = exth == null ? void 0 : exth.creator) == null ? void 0 : _a.map(unescapeHTML),
664
- publisher: unescapeHTML(exth == null ? void 0 : exth.publisher),
665
- language: (exth == null ? void 0 : exth.language) ?? mobi.language,
666
- published: exth == null ? void 0 : exth.date,
667
- description: unescapeHTML(exth == null ? void 0 : exth.description),
668
- subject: (_b = exth == null ? void 0 : exth.subject) == null ? void 0 : _b.map(unescapeHTML),
669
- rights: unescapeHTML(exth == null ? void 0 : exth.rights),
670
- contributor: exth == null ? void 0 : exth.contributor
671
- };
672
- }
673
- async getCover() {
674
- const { exth } = this.headers;
675
- const offset = (exth == null ? void 0 : exth.coverOffset) < 4294967295 ? exth == null ? void 0 : exth.coverOffset : (exth == null ? void 0 : exth.thumbnailOffset) < 4294967295 ? exth == null ? void 0 : exth.thumbnailOffset : null;
676
- if (offset != null) {
677
- const buf = await this.loadResource(offset);
678
- return new Blob([buf]);
679
- }
680
- }
681
- }
682
- const mbpPagebreakRegex = /<\s*(?:mbp:)?pagebreak[^>]*>/gi;
683
- const fileposRegex = /<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>/gi;
684
- const getIndent = (el) => {
685
- let x = 0;
686
- while (el) {
687
- const parent = el.parentElement;
688
- if (parent) {
689
- const tag = parent.tagName.toLowerCase();
690
- if (tag === "p") x += 1.5;
691
- else if (tag === "blockquote") x += 2;
692
- }
693
- el = parent;
694
- }
695
- return x;
696
- };
697
- function rawBytesToString(uint8Array) {
698
- const chunkSize = 32768;
699
- let result = "";
700
- for (let i = 0; i < uint8Array.length; i += chunkSize) {
701
- result += String.fromCharCode.apply(null, uint8Array.subarray(i, i + chunkSize));
702
- }
703
- return result;
704
- }
705
- class MOBI6 {
706
- parser = new DOMParser();
707
- serializer = new XMLSerializer();
708
- #resourceCache = /* @__PURE__ */ new Map();
709
- #textCache = /* @__PURE__ */ new Map();
710
- #cache = /* @__PURE__ */ new Map();
711
- #sections;
712
- #fileposList = [];
713
- #type = MIME.HTML;
714
- constructor(mobi) {
715
- this.mobi = mobi;
716
- }
717
- async init() {
718
- var _a;
719
- const recordBuffers = [];
720
- for (let i = 0; i < this.mobi.headers.palmdoc.numTextRecords; i++) {
721
- const buf = await this.mobi.loadText(i);
722
- recordBuffers.push(buf);
723
- }
724
- const totalLength = recordBuffers.reduce((sum, buf) => sum + buf.byteLength, 0);
725
- const array = new Uint8Array(totalLength);
726
- recordBuffers.reduce((offset, buf) => {
727
- array.set(new Uint8Array(buf), offset);
728
- return offset + buf.byteLength;
729
- }, 0);
730
- const str = rawBytesToString(array);
731
- this.#sections = [0].concat(Array.from(str.matchAll(mbpPagebreakRegex), (m) => m.index)).map((start, i, a) => {
732
- const end = a[i + 1] ?? array.length;
733
- return { book: this, raw: array.subarray(start, end) };
734
- }).map((section, i, arr) => {
735
- var _a2;
736
- section.start = ((_a2 = arr[i - 1]) == null ? void 0 : _a2.end) ?? 0;
737
- section.end = section.start + section.raw.byteLength;
738
- return section;
739
- });
740
- this.sections = this.#sections.map((section, index) => ({
741
- id: index,
742
- load: () => this.loadSection(section),
743
- createDocument: () => this.createDocument(section),
744
- size: section.end - section.start
745
- }));
746
- try {
747
- this.landmarks = await this.getGuide();
748
- const tocHref = (_a = this.landmarks.find(({ type }) => type == null ? void 0 : type.includes("toc"))) == null ? void 0 : _a.href;
749
- if (tocHref) {
750
- const { index } = this.resolveHref(tocHref);
751
- const doc = await this.sections[index].createDocument();
752
- let lastItem;
753
- let lastLevel = 0;
754
- let lastIndent = 0;
755
- const lastLevelOfIndent = /* @__PURE__ */ new Map();
756
- const lastParentOfLevel = /* @__PURE__ */ new Map();
757
- this.toc = Array.from(doc.querySelectorAll("a[filepos]")).reduce((arr, a) => {
758
- var _a2;
759
- const indent = getIndent(a);
760
- const item = {
761
- label: ((_a2 = a.innerText) == null ? void 0 : _a2.trim()) ?? "",
762
- href: `filepos:${a.getAttribute("filepos")}`
763
- };
764
- const level = indent > lastIndent ? lastLevel + 1 : indent === lastIndent ? lastLevel : lastLevelOfIndent.get(indent) ?? Math.max(0, lastLevel - 1);
765
- if (level > lastLevel) {
766
- if (lastItem) {
767
- lastItem.subitems ??= [];
768
- lastItem.subitems.push(item);
769
- lastParentOfLevel.set(level, lastItem);
770
- } else arr.push(item);
771
- } else {
772
- const parent = lastParentOfLevel.get(level);
773
- if (parent) parent.subitems.push(item);
774
- else arr.push(item);
775
- }
776
- lastItem = item;
777
- lastLevel = level;
778
- lastIndent = indent;
779
- lastLevelOfIndent.set(indent, level);
780
- return arr;
781
- }, []);
782
- }
783
- } catch (e) {
784
- console.warn(e);
785
- }
786
- this.#fileposList = [...new Set(
787
- Array.from(str.matchAll(fileposRegex), (m) => m[1])
788
- )].map((filepos) => ({ filepos, number: Number(filepos) })).sort((a, b) => a.number - b.number);
789
- this.metadata = this.mobi.getMetadata();
790
- this.getCover = this.mobi.getCover.bind(this.mobi);
791
- return this;
792
- }
793
- async getGuide() {
794
- const doc = await this.createDocument(this.#sections[0]);
795
- return Array.from(doc.getElementsByTagName("reference"), (ref) => {
796
- var _a;
797
- return {
798
- label: ref.getAttribute("title"),
799
- type: (_a = ref.getAttribute("type")) == null ? void 0 : _a.split(/\s/),
800
- href: `filepos:${ref.getAttribute("filepos")}`
801
- };
802
- });
803
- }
804
- async loadResource(index) {
805
- if (this.#resourceCache.has(index)) return this.#resourceCache.get(index);
806
- const raw = await this.mobi.loadResource(index);
807
- const url = URL.createObjectURL(new Blob([raw]));
808
- this.#resourceCache.set(index, url);
809
- return url;
810
- }
811
- async loadRecindex(recindex) {
812
- return this.loadResource(Number(recindex) - 1);
813
- }
814
- async replaceResources(doc) {
815
- for (const img of doc.querySelectorAll("img[recindex]")) {
816
- const recindex = img.getAttribute("recindex");
817
- try {
818
- img.src = await this.loadRecindex(recindex);
819
- } catch {
820
- console.warn(`Failed to load image ${recindex}`);
821
- }
822
- }
823
- for (const media of doc.querySelectorAll("[mediarecindex]")) {
824
- const mediarecindex = media.getAttribute("mediarecindex");
825
- const recindex = media.getAttribute("recindex");
826
- try {
827
- media.src = await this.loadRecindex(mediarecindex);
828
- if (recindex) media.poster = await this.loadRecindex(recindex);
829
- } catch {
830
- console.warn(`Failed to load media ${mediarecindex}`);
831
- }
832
- }
833
- for (const a of doc.querySelectorAll("[filepos]")) {
834
- const filepos = a.getAttribute("filepos");
835
- a.href = `filepos:${filepos}`;
836
- }
837
- }
838
- async loadText(section) {
839
- if (this.#textCache.has(section)) return this.#textCache.get(section);
840
- const { raw } = section;
841
- const fileposList = this.#fileposList.filter(({ number }) => number >= section.start && number < section.end).map((obj) => ({ ...obj, offset: obj.number - section.start }));
842
- let arr = raw;
843
- if (fileposList.length) {
844
- arr = raw.subarray(0, fileposList[0].offset);
845
- fileposList.forEach(({ filepos, offset }, i) => {
846
- const next = fileposList[i + 1];
847
- const a = this.mobi.encode(`<a id="filepos${filepos}"></a>`);
848
- arr = concatTypedArray3(arr, a, raw.subarray(offset, next == null ? void 0 : next.offset));
849
- });
850
- }
851
- const str = this.mobi.decode(arr).replaceAll(mbpPagebreakRegex, "");
852
- this.#textCache.set(section, str);
853
- return str;
854
- }
855
- async createDocument(section) {
856
- const str = await this.loadText(section);
857
- return this.parser.parseFromString(str, this.#type);
858
- }
859
- async loadSection(section) {
860
- if (this.#cache.has(section)) return this.#cache.get(section);
861
- const doc = await this.createDocument(section);
862
- const style = doc.createElement("style");
863
- doc.head.append(style);
864
- style.append(doc.createTextNode(`blockquote {
865
- margin-block-start: 0;
866
- margin-block-end: 0;
867
- margin-inline-start: 1em;
868
- margin-inline-end: 0;
869
- }`));
870
- await this.replaceResources(doc);
871
- const result = this.serializer.serializeToString(doc);
872
- const url = URL.createObjectURL(new Blob([result], { type: this.#type }));
873
- this.#cache.set(section, url);
874
- return url;
875
- }
876
- resolveHref(href) {
877
- const filepos = href.match(/filepos:(.*)/)[1];
878
- const number = Number(filepos);
879
- const index = this.#sections.findIndex((section) => section.end > number);
880
- const anchor = (doc) => doc.getElementById(`filepos${filepos}`);
881
- return { index, anchor };
882
- }
883
- splitTOCHref(href) {
884
- const filepos = href.match(/filepos:(.*)/)[1];
885
- const number = Number(filepos);
886
- const index = this.#sections.findIndex((section) => section.end > number);
887
- return [index, `filepos${filepos}`];
888
- }
889
- getTOCFragment(doc, id) {
890
- return doc.getElementById(id);
891
- }
892
- isExternal(uri) {
893
- return /^(?!blob|filepos)\w+:/i.test(uri);
894
- }
895
- destroy() {
896
- for (const url of this.#resourceCache.values()) URL.revokeObjectURL(url);
897
- for (const url of this.#cache.values()) URL.revokeObjectURL(url);
898
- }
899
- }
900
- const kindleResourceRegex = /kindle:(flow|embed):(\w+)(?:\?mime=(\w+\/[-+.\w]+))?/;
901
- const kindlePosRegex = /kindle:pos:fid:(\w+):off:(\w+)/;
902
- const parseResourceURI = (str) => {
903
- const [resourceType, id, type] = str.match(kindleResourceRegex).slice(1);
904
- return { resourceType, id: parseInt(id, 32), type };
905
- };
906
- const parsePosURI = (str) => {
907
- const [fid, off] = str.match(kindlePosRegex).slice(1);
908
- return { fid: parseInt(fid, 32), off: parseInt(off, 32) };
909
- };
910
- const makePosURI = (fid = 0, off = 0) => `kindle:pos:fid:${fid.toString(32).toUpperCase().padStart(4, "0")}:off:${off.toString(32).toUpperCase().padStart(10, "0")}`;
911
- const getFragmentSelector = (str) => {
912
- const match = str.match(/\s(id|name|aid)\s*=\s*['"]([^'"]*)['"]/i);
913
- if (!match) return;
914
- const [, attr, value] = match;
915
- return `[${attr}="${CSS.escape(value)}"]`;
916
- };
917
- const replaceSeries = async (str, regex, f) => {
918
- const matches = [];
919
- str.replace(regex, (...args) => (matches.push(args), null));
920
- const results = [];
921
- for (const args of matches) results.push(await f(...args));
922
- return str.replace(regex, () => results.shift());
923
- };
924
- const getPageSpread = (properties) => {
925
- for (const p of properties) {
926
- if (p === "page-spread-left" || p === "rendition:page-spread-left")
927
- return "left";
928
- if (p === "page-spread-right" || p === "rendition:page-spread-right")
929
- return "right";
930
- if (p === "rendition:page-spread-center") return "center";
931
- }
932
- };
933
- class KF8 {
934
- parser = new DOMParser();
935
- serializer = new XMLSerializer();
936
- transformTarget = new EventTarget();
937
- #cache = /* @__PURE__ */ new Map();
938
- #fragmentOffsets = /* @__PURE__ */ new Map();
939
- #fragmentSelectors = /* @__PURE__ */ new Map();
940
- #tables = {};
941
- #sections;
942
- #fullRawLength;
943
- #rawHead = new Uint8Array();
944
- #rawTail = new Uint8Array();
945
- #lastLoadedHead = -1;
946
- #lastLoadedTail = -1;
947
- #type = MIME.XHTML;
948
- #inlineMap = /* @__PURE__ */ new Map();
949
- constructor(mobi) {
950
- this.mobi = mobi;
951
- }
952
- async init() {
953
- var _a, _b, _c, _d;
954
- const loadRecord = this.mobi.loadRecord.bind(this.mobi);
955
- const { kf8 } = this.mobi.headers;
956
- try {
957
- const fdstBuffer = await loadRecord(kf8.fdst);
958
- const fdst = getStruct(FDST_HEADER, fdstBuffer);
959
- if (fdst.magic !== "FDST") throw new Error("Missing FDST record");
960
- const fdstTable = Array.from(
961
- { length: fdst.numEntries },
962
- (_, i) => 12 + i * 8
963
- ).map((offset) => [
964
- getUint(fdstBuffer.slice(offset, offset + 4)),
965
- getUint(fdstBuffer.slice(offset + 4, offset + 8))
966
- ]);
967
- this.#tables.fdstTable = fdstTable;
968
- this.#fullRawLength = fdstTable[fdstTable.length - 1][1];
969
- } catch {
970
- }
971
- const skelTable = (await getIndexData(kf8.skel, loadRecord)).table.map(({ name, tagMap }, index) => ({
972
- index,
973
- name,
974
- numFrag: tagMap[1][0],
975
- offset: tagMap[6][0],
976
- length: tagMap[6][1]
977
- }));
978
- const fragData = await getIndexData(kf8.frag, loadRecord);
979
- const fragTable = fragData.table.map(({ name, tagMap }) => ({
980
- insertOffset: parseInt(name),
981
- selector: fragData.cncx[tagMap[2][0]],
982
- index: tagMap[4][0],
983
- offset: tagMap[6][0],
984
- length: tagMap[6][1]
985
- }));
986
- this.#tables.skelTable = skelTable;
987
- this.#tables.fragTable = fragTable;
988
- this.#sections = skelTable.reduce((arr, skel) => {
989
- const last = arr[arr.length - 1];
990
- const fragStart = (last == null ? void 0 : last.fragEnd) ?? 0, fragEnd = fragStart + skel.numFrag;
991
- const frags = fragTable.slice(fragStart, fragEnd);
992
- const length = skel.length + frags.map((f) => f.length).reduce((a, b) => a + b, 0);
993
- const totalLength = ((last == null ? void 0 : last.totalLength) ?? 0) + length;
994
- return arr.concat({ skel, frags, fragEnd, length, totalLength });
995
- }, []);
996
- const resources = await this.getResourcesByMagic(["RESC", "PAGE"]);
997
- const pageSpreads = /* @__PURE__ */ new Map();
998
- if (resources.RESC) {
999
- const buf = await this.mobi.loadRecord(resources.RESC);
1000
- const str = this.mobi.decode(buf.slice(16)).replace(/\0/g, "");
1001
- const index = str.search(/\?>/);
1002
- const xmlStr = `<package>${str.slice(index)}</package>`;
1003
- const opf = this.parser.parseFromString(xmlStr, MIME.XML);
1004
- for (const $itemref of opf.querySelectorAll("spine > itemref")) {
1005
- const i = parseInt($itemref.getAttribute("skelid"));
1006
- pageSpreads.set(i, getPageSpread(
1007
- ((_a = $itemref.getAttribute("properties")) == null ? void 0 : _a.split(" ")) ?? []
1008
- ));
1009
- }
1010
- }
1011
- this.sections = this.#sections.map((section, index) => section.frags.length ? {
1012
- id: index,
1013
- load: () => this.loadSection(section),
1014
- createDocument: () => this.createDocument(section),
1015
- size: section.length,
1016
- pageSpread: pageSpreads.get(index)
1017
- } : { linear: "no" });
1018
- try {
1019
- const ncx = await this.mobi.getNCX();
1020
- const map = ({ label, pos, children }) => {
1021
- const [fid, off] = pos;
1022
- const href = makePosURI(fid, off);
1023
- const arr = this.#fragmentOffsets.get(fid);
1024
- if (arr) arr.push(off);
1025
- else this.#fragmentOffsets.set(fid, [off]);
1026
- return { label: unescapeHTML(label), href, subitems: children == null ? void 0 : children.map(map) };
1027
- };
1028
- this.toc = ncx == null ? void 0 : ncx.map(map);
1029
- this.landmarks = await this.getGuide();
1030
- } catch (e) {
1031
- console.warn(e);
1032
- }
1033
- const { exth } = this.mobi.headers;
1034
- this.dir = exth.pageProgressionDirection;
1035
- this.rendition = {
1036
- layout: exth.fixedLayout === "true" ? "pre-paginated" : "reflowable",
1037
- viewport: Object.fromEntries(((_d = (_c = (_b = exth.originalResolution) == null ? void 0 : _b.split("x")) == null ? void 0 : _c.slice(0, 2)) == null ? void 0 : _d.map((x, i) => [i ? "height" : "width", x])) ?? [])
1038
- };
1039
- this.metadata = this.mobi.getMetadata();
1040
- this.getCover = this.mobi.getCover.bind(this.mobi);
1041
- return this;
1042
- }
1043
- // is this really the only way of getting to RESC, PAGE, etc.?
1044
- async getResourcesByMagic(keys) {
1045
- const results = {};
1046
- const start = this.mobi.headers.kf8.resourceStart;
1047
- const end = this.mobi.pdb.numRecords;
1048
- for (let i = start; i < end; i++) {
1049
- try {
1050
- const magic = await this.mobi.loadMagic(i);
1051
- const match = keys.find((key) => key === magic);
1052
- if (match) results[match] = i;
1053
- } catch {
1054
- }
1055
- }
1056
- return results;
1057
- }
1058
- async getGuide() {
1059
- const index = this.mobi.headers.kf8.guide;
1060
- if (index < 4294967295) {
1061
- const loadRecord = this.mobi.loadRecord.bind(this.mobi);
1062
- const { table, cncx } = await getIndexData(index, loadRecord);
1063
- return table.map(({ name, tagMap }) => {
1064
- var _a, _b;
1065
- return {
1066
- label: cncx[tagMap[1][0]] ?? "",
1067
- type: name == null ? void 0 : name.split(/\s/),
1068
- href: makePosURI(((_a = tagMap[6]) == null ? void 0 : _a[0]) ?? ((_b = tagMap[3]) == null ? void 0 : _b[0]))
1069
- };
1070
- });
1071
- }
1072
- }
1073
- async loadResourceBlob(str) {
1074
- var _a;
1075
- const { resourceType, id, type } = parseResourceURI(str);
1076
- const raw = resourceType === "flow" ? await this.loadFlow(id) : await this.mobi.loadResource(id - 1);
1077
- const result = [MIME.XHTML, MIME.HTML, MIME.CSS, MIME.SVG].includes(type) ? await this.replaceResources(this.mobi.decode(raw)) : raw;
1078
- const detail = { data: result, type };
1079
- const event = new CustomEvent("data", { detail });
1080
- this.transformTarget.dispatchEvent(event);
1081
- const newData = await event.detail.data;
1082
- const newType = await event.detail.type;
1083
- const doc = newType === MIME.SVG ? this.parser.parseFromString(newData, newType) : null;
1084
- return [
1085
- new Blob([newData], { newType }),
1086
- // SVG wrappers need to be inlined
1087
- // as browsers don't allow external resources when loading SVG as an image
1088
- ((_a = doc == null ? void 0 : doc.getElementsByTagNameNS("http://www.w3.org/2000/svg", "image")) == null ? void 0 : _a.length) ? doc.documentElement : null
1089
- ];
1090
- }
1091
- async loadResource(str) {
1092
- if (this.#cache.has(str)) return this.#cache.get(str);
1093
- const [blob, inline] = await this.loadResourceBlob(str);
1094
- const url = inline ? str : URL.createObjectURL(blob);
1095
- if (inline) this.#inlineMap.set(url, inline);
1096
- this.#cache.set(str, url);
1097
- return url;
1098
- }
1099
- replaceResources(str) {
1100
- const regex = new RegExp(kindleResourceRegex, "g");
1101
- return replaceSeries(str, regex, this.loadResource.bind(this));
1102
- }
1103
- // NOTE: there doesn't seem to be a way to access text randomly?
1104
- // how to know the decompressed size of the records without decompressing?
1105
- // 4096 is just the maximum size
1106
- async loadRaw(start, end) {
1107
- const distanceHead = end - this.#rawHead.length;
1108
- const distanceEnd = this.#fullRawLength == null ? Infinity : this.#fullRawLength - this.#rawTail.length - start;
1109
- if (distanceHead < 0 || distanceHead < distanceEnd) {
1110
- while (this.#rawHead.length < end) {
1111
- const index = ++this.#lastLoadedHead;
1112
- const data = await this.mobi.loadText(index);
1113
- this.#rawHead = concatTypedArray(this.#rawHead, data);
1114
- }
1115
- return this.#rawHead.slice(start, end);
1116
- }
1117
- while (this.#fullRawLength - this.#rawTail.length > start) {
1118
- const index = this.mobi.headers.palmdoc.numTextRecords - 1 - ++this.#lastLoadedTail;
1119
- const data = await this.mobi.loadText(index);
1120
- this.#rawTail = concatTypedArray(data, this.#rawTail);
1121
- }
1122
- const rawTailStart = this.#fullRawLength - this.#rawTail.length;
1123
- return this.#rawTail.slice(start - rawTailStart, end - rawTailStart);
1124
- }
1125
- loadFlow(index) {
1126
- if (index < 4294967295)
1127
- return this.loadRaw(...this.#tables.fdstTable[index]);
1128
- }
1129
- async loadText(section) {
1130
- const { skel, frags, length } = section;
1131
- const raw = await this.loadRaw(skel.offset, skel.offset + length);
1132
- let skeleton = raw.slice(0, skel.length);
1133
- for (const frag of frags) {
1134
- const insertOffset = frag.insertOffset - skel.offset;
1135
- const offset = skel.length + frag.offset;
1136
- const fragRaw = raw.slice(offset, offset + frag.length);
1137
- skeleton = concatTypedArray3(
1138
- skeleton.slice(0, insertOffset),
1139
- fragRaw,
1140
- skeleton.slice(insertOffset)
1141
- );
1142
- const offsets = this.#fragmentOffsets.get(frag.index);
1143
- if (offsets) for (const offset2 of offsets) {
1144
- const str = this.mobi.decode(fragRaw.slice(offset2));
1145
- const selector = getFragmentSelector(str);
1146
- this.#setFragmentSelector(frag.index, offset2, selector);
1147
- }
1148
- }
1149
- return this.mobi.decode(skeleton);
1150
- }
1151
- async createDocument(section) {
1152
- const str = await this.loadText(section);
1153
- return this.parser.parseFromString(str, this.#type);
1154
- }
1155
- async loadSection(section) {
1156
- var _a;
1157
- if (this.#cache.has(section)) return this.#cache.get(section);
1158
- const str = await this.loadText(section);
1159
- const replaced = await this.replaceResources(str);
1160
- let doc = this.parser.parseFromString(replaced, this.#type);
1161
- if (doc.querySelector("parsererror") || !((_a = doc.documentElement) == null ? void 0 : _a.namespaceURI)) {
1162
- this.#type = MIME.HTML;
1163
- doc = this.parser.parseFromString(replaced, this.#type);
1164
- }
1165
- for (const [url2, node] of this.#inlineMap) {
1166
- for (const el of doc.querySelectorAll(`img[src="${url2}"]`))
1167
- el.replaceWith(node);
1168
- }
1169
- const url = URL.createObjectURL(
1170
- new Blob([this.serializer.serializeToString(doc)], { type: this.#type })
1171
- );
1172
- this.#cache.set(section, url);
1173
- return url;
1174
- }
1175
- getIndexByFID(fid) {
1176
- return this.#sections.findIndex((section) => section.frags.some((frag) => frag.index === fid));
1177
- }
1178
- #setFragmentSelector(id, offset, selector) {
1179
- const map = this.#fragmentSelectors.get(id);
1180
- if (map) map.set(offset, selector);
1181
- else {
1182
- const map2 = /* @__PURE__ */ new Map();
1183
- this.#fragmentSelectors.set(id, map2);
1184
- map2.set(offset, selector);
1185
- }
1186
- }
1187
- async resolveHref(href) {
1188
- var _a;
1189
- const { fid, off } = parsePosURI(href);
1190
- const index = this.getIndexByFID(fid);
1191
- if (index < 0) return;
1192
- const saved = (_a = this.#fragmentSelectors.get(fid)) == null ? void 0 : _a.get(off);
1193
- if (saved) return { index, anchor: (doc) => doc.querySelector(saved) };
1194
- const { skel, frags } = this.#sections[index];
1195
- const frag = frags.find((frag2) => frag2.index === fid);
1196
- const offset = skel.offset + skel.length + frag.offset;
1197
- const fragRaw = await this.loadRaw(offset, offset + frag.length);
1198
- const str = this.mobi.decode(fragRaw.slice(off));
1199
- const selector = getFragmentSelector(str);
1200
- this.#setFragmentSelector(fid, off, selector);
1201
- const anchor = (doc) => doc.querySelector(selector);
1202
- return { index, anchor };
1203
- }
1204
- splitTOCHref(href) {
1205
- const pos = parsePosURI(href);
1206
- const index = this.getIndexByFID(pos.fid);
1207
- return [index, pos];
1208
- }
1209
- getTOCFragment(doc, { fid, off }) {
1210
- var _a;
1211
- const selector = (_a = this.#fragmentSelectors.get(fid)) == null ? void 0 : _a.get(off);
1212
- return doc.querySelector(selector);
1213
- }
1214
- isExternal(uri) {
1215
- return /^(?!blob|kindle)\w+:/i.test(uri);
1216
- }
1217
- destroy() {
1218
- for (const url of this.#cache.values()) URL.revokeObjectURL(url);
1219
- }
1220
- }
1221
- exports.MOBI = MOBI;
1222
- exports.isMOBI = isMOBI;