@papyrus-sdk/ui-react-native 0.2.24 → 0.2.26
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 +3 -1
- package/dist/{chunk-B36LWZCX.mjs → chunk-K2ZLYLHR.mjs} +84 -1
- package/dist/chunk-K2ZLYLHR.mjs.map +1 -0
- package/dist/{chunk-B25KK4VL.mjs → chunk-KXNP73MZ.mjs} +1 -7
- package/dist/chunk-KXNP73MZ.mjs.map +1 -0
- package/dist/gesture/pinchZoom.d.mts +35 -1
- package/dist/gesture/pinchZoom.d.ts +35 -1
- package/dist/gesture/pinchZoom.js +89 -2
- package/dist/gesture/pinchZoom.js.map +1 -1
- package/dist/gesture/pinchZoom.mjs +11 -3
- package/dist/gesture/selectionInteraction.d.mts +1 -6
- package/dist/gesture/selectionInteraction.d.ts +1 -6
- package/dist/gesture/selectionInteraction.js +0 -7
- package/dist/gesture/selectionInteraction.js.map +1 -1
- package/dist/gesture/selectionInteraction.mjs +1 -3
- package/dist/index.d.mts +38 -27
- package/dist/index.d.ts +38 -27
- package/dist/index.js +3939 -2799
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3723 -2586
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/runtime/comicRuntime.test.ts +151 -0
- package/runtime/documentLoadRuntime.test.ts +172 -0
- package/runtime/epubScrollDiagnostics.test.ts +107 -0
- package/runtime/epubScrollDiagnostics.ts +127 -0
- package/runtime/index.html +1529 -956
- package/runtime/runtime.js +590 -24
- package/runtime/syncComicRuntime.mjs +22 -6
- package/dist/chunk-B25KK4VL.mjs.map +0 -1
- package/dist/chunk-B36LWZCX.mjs.map +0 -1
package/runtime/index.html
CHANGED
|
@@ -20482,358 +20482,644 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20482
20482
|
</script>
|
|
20483
20483
|
<!-- @papyrus-comic-runtime:end -->
|
|
20484
20484
|
<script>
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
|
|
20488
|
-
|
|
20485
|
+
(function () {
|
|
20486
|
+
const viewer = document.getElementById('viewer');
|
|
20487
|
+
const DEFAULT_FONT_SIZE = 16;
|
|
20488
|
+
const TEXT_PAGE_CHUNK = 1600;
|
|
20489
20489
|
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
|
|
20495
|
-
|
|
20496
|
-
|
|
20497
|
-
|
|
20498
|
-
|
|
20499
|
-
|
|
20500
|
-
|
|
20501
|
-
|
|
20502
|
-
|
|
20503
|
-
|
|
20504
|
-
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
|
|
20490
|
+
let currentType = null;
|
|
20491
|
+
let book = null;
|
|
20492
|
+
let rendition = null;
|
|
20493
|
+
let spineItems = [];
|
|
20494
|
+
let textPages = [];
|
|
20495
|
+
let textContainer = null;
|
|
20496
|
+
let currentPage = 1;
|
|
20497
|
+
let pageCount = 0;
|
|
20498
|
+
let zoom = 1.0;
|
|
20499
|
+
let rotation = 0;
|
|
20500
|
+
let comicArchive = null;
|
|
20501
|
+
let comicEntries = [];
|
|
20502
|
+
let comicContainer = null;
|
|
20503
|
+
let comicPages = [];
|
|
20504
|
+
let comicObserver = null;
|
|
20505
|
+
let comicScrollFrame = null;
|
|
20506
|
+
const comicVisiblePages = new Set();
|
|
20507
|
+
const comicObjectUrls = new Map();
|
|
20508
|
+
const comicLoading = new Map();
|
|
20509
|
+
const comicDimensions = new Map();
|
|
20510
|
+
const comicPreviewCache = new Map();
|
|
20511
|
+
const comicPreviewLoading = new Map();
|
|
20512
|
+
const COMIC_CACHE_LIMIT = 16;
|
|
20513
|
+
const COMIC_PREVIEW_CACHE_LIMIT = 8;
|
|
20514
|
+
const comicHelpers = window.PapyrusComicRuntime || {};
|
|
20515
|
+
const runtimeConfig = window.__PAPYRUS_RUNTIME_CONFIG__ || {};
|
|
20516
|
+
const epubDiagnosticsEnabled =
|
|
20517
|
+
runtimeConfig.epubDiagnostics === true ||
|
|
20518
|
+
runtimeConfig.epubDiagnostics === 'true';
|
|
20519
|
+
const EPUB_STAGE_TIMEOUT_MS = 10000;
|
|
20520
|
+
let comicDispose = null;
|
|
20521
|
+
let comicGeneration = 0;
|
|
20522
|
+
let epubInteractionCleanup = null;
|
|
20523
|
+
const epubSelectionCleanups = new Set();
|
|
20524
|
+
let epubScrollDiagnostics = null;
|
|
20525
|
+
// Shared generation for all document formats. The historical variable name
|
|
20526
|
+
// is kept because the shipped runtime tests and diagnostics refer to it.
|
|
20527
|
+
let epubLoadGeneration = 0;
|
|
20517
20528
|
|
|
20518
|
-
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20523
|
-
|
|
20524
|
-
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
const sendResponse = (id, ok, data, error) => {
|
|
20529
|
-
sendMessage({ type: 'response', id, ok, data, error });
|
|
20530
|
-
};
|
|
20529
|
+
const sendMessage = (payload) => {
|
|
20530
|
+
if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
|
|
20531
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(payload));
|
|
20532
|
+
return;
|
|
20533
|
+
}
|
|
20534
|
+
if (window.parent && window.parent !== window) {
|
|
20535
|
+
window.parent.postMessage(payload, '*');
|
|
20536
|
+
}
|
|
20537
|
+
};
|
|
20531
20538
|
|
|
20532
|
-
|
|
20533
|
-
|
|
20539
|
+
const sendResponse = (id, ok, data, error) => {
|
|
20540
|
+
sendMessage({ type: 'response', id, ok, data, error });
|
|
20541
|
+
};
|
|
20534
20542
|
|
|
20535
|
-
|
|
20536
|
-
|
|
20537
|
-
const id = `runtime-asset-${Date.now()}-${Math.random()}`;
|
|
20538
|
-
const timeout = setTimeout(() => {
|
|
20539
|
-
runtimeAssetRequests.delete(id);
|
|
20540
|
-
reject(new Error(`Tempo esgotado ao carregar asset CBR: ${url}`));
|
|
20541
|
-
}, 15000);
|
|
20542
|
-
runtimeAssetRequests.set(id, {resolve, reject, timeout});
|
|
20543
|
-
sendMessage({type: 'asset-request', id, url, encoding});
|
|
20544
|
-
});
|
|
20543
|
+
const runtimeAssetRequests = new Map();
|
|
20544
|
+
const fileChunkRequests = new Map();
|
|
20545
20545
|
|
|
20546
|
-
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
};
|
|
20546
|
+
const requestRuntimeAsset = (url, encoding) =>
|
|
20547
|
+
new Promise((resolve, reject) => {
|
|
20548
|
+
const id = `runtime-asset-${Date.now()}-${Math.random()}`;
|
|
20549
|
+
const timeout = setTimeout(() => {
|
|
20550
|
+
runtimeAssetRequests.delete(id);
|
|
20551
|
+
reject(new Error(`Tempo esgotado ao carregar asset CBR: ${url}`));
|
|
20552
|
+
}, 15000);
|
|
20553
|
+
runtimeAssetRequests.set(id, {resolve, reject, timeout});
|
|
20554
|
+
sendMessage({type: 'asset-request', id, url, encoding});
|
|
20555
|
+
});
|
|
20557
20556
|
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20557
|
+
const encodeArrayBufferAsDataUrl = async (buffer) => {
|
|
20558
|
+
const bytes = new Uint8Array(buffer);
|
|
20559
|
+
let binary = '';
|
|
20560
|
+
const chunkSize = 0x8000;
|
|
20561
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
20562
|
+
binary += String.fromCharCode(
|
|
20563
|
+
...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length)),
|
|
20564
|
+
);
|
|
20565
|
+
}
|
|
20566
|
+
return `data:application/wasm;base64,${btoa(binary)}`;
|
|
20567
|
+
};
|
|
20568
20568
|
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20573
|
-
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20569
|
+
const fetchRuntimeAsset = async (url, encoding) => {
|
|
20570
|
+
try {
|
|
20571
|
+
const response = await fetch(url);
|
|
20572
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
20573
|
+
if (encoding === 'text') return await response.text();
|
|
20574
|
+
return await encodeArrayBufferAsDataUrl(await response.arrayBuffer());
|
|
20575
|
+
} catch {
|
|
20576
|
+
return await requestRuntimeAsset(url, encoding);
|
|
20577
|
+
}
|
|
20578
|
+
};
|
|
20579
20579
|
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
20590
|
-
}
|
|
20591
|
-
chunks.push(bytes);
|
|
20592
|
-
totalLength += bytes.length;
|
|
20593
|
-
offset += bytes.length;
|
|
20594
|
-
if (result.done) break;
|
|
20595
|
-
}
|
|
20580
|
+
const requestLocalFileChunk = (uri, offset, length) =>
|
|
20581
|
+
new Promise((resolve, reject) => {
|
|
20582
|
+
const id = `file-chunk-${Date.now()}-${Math.random()}`;
|
|
20583
|
+
const timeout = setTimeout(() => {
|
|
20584
|
+
fileChunkRequests.delete(id);
|
|
20585
|
+
reject(new Error(`Tempo esgotado ao ler arquivo local: ${uri}`));
|
|
20586
|
+
}, 30000);
|
|
20587
|
+
fileChunkRequests.set(id, {resolve, reject, timeout});
|
|
20588
|
+
sendMessage({type: 'file-chunk-request', id, uri, offset, length});
|
|
20589
|
+
});
|
|
20596
20590
|
|
|
20597
|
-
|
|
20598
|
-
|
|
20599
|
-
|
|
20600
|
-
|
|
20601
|
-
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20591
|
+
const readLocalFile = async (uri) => {
|
|
20592
|
+
const chunkSize = 1024 * 1024;
|
|
20593
|
+
const chunks = [];
|
|
20594
|
+
let offset = 0;
|
|
20595
|
+
let totalLength = 0;
|
|
20596
|
+
while (true) {
|
|
20597
|
+
const result = await requestLocalFileChunk(uri, offset, chunkSize);
|
|
20598
|
+
const bytes = decodeBase64(result.data || '');
|
|
20599
|
+
if (bytes.length === 0 && !result.done) {
|
|
20600
|
+
throw new Error('Leitura nativa retornou um chunk vazio.');
|
|
20601
|
+
}
|
|
20602
|
+
chunks.push(bytes);
|
|
20603
|
+
totalLength += bytes.length;
|
|
20604
|
+
offset += bytes.length;
|
|
20605
|
+
if (result.done) break;
|
|
20606
|
+
}
|
|
20605
20607
|
|
|
20606
|
-
|
|
20607
|
-
|
|
20608
|
-
|
|
20609
|
-
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
|
|
20613
|
-
|
|
20614
|
-
},
|
|
20615
|
-
});
|
|
20616
|
-
};
|
|
20608
|
+
const output = new Uint8Array(totalLength);
|
|
20609
|
+
let outputOffset = 0;
|
|
20610
|
+
chunks.forEach((chunk) => {
|
|
20611
|
+
output.set(chunk, outputOffset);
|
|
20612
|
+
outputOffset += chunk.length;
|
|
20613
|
+
});
|
|
20614
|
+
return output.buffer;
|
|
20615
|
+
};
|
|
20617
20616
|
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20617
|
+
const sendState = (extra) => {
|
|
20618
|
+
sendMessage({
|
|
20619
|
+
type: 'state',
|
|
20620
|
+
payload: {
|
|
20621
|
+
pageCount,
|
|
20622
|
+
currentPage,
|
|
20623
|
+
zoom,
|
|
20624
|
+
...(extra || {}),
|
|
20625
|
+
},
|
|
20626
|
+
});
|
|
20627
|
+
};
|
|
20621
20628
|
|
|
20622
|
-
|
|
20623
|
-
|
|
20624
|
-
|
|
20625
|
-
sendEvent('RUNTIME_ERROR', { message, context, stack });
|
|
20626
|
-
};
|
|
20629
|
+
const sendEvent = (name, payload) => {
|
|
20630
|
+
sendMessage({ type: 'event', name, payload });
|
|
20631
|
+
};
|
|
20627
20632
|
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20633
|
+
const sendEpubDiagnostic = (name, payload) => {
|
|
20634
|
+
if (!epubDiagnosticsEnabled) return;
|
|
20635
|
+
sendEvent(name, {
|
|
20636
|
+
loadId: payload.loadId,
|
|
20637
|
+
timestamp: Date.now(),
|
|
20638
|
+
sourceKind: payload.sourceKind,
|
|
20639
|
+
byteLength: payload.byteLength,
|
|
20640
|
+
currentType: currentType,
|
|
20641
|
+
durationMs: payload.durationMs,
|
|
20642
|
+
uri: payload.uri,
|
|
20643
|
+
status: payload.status,
|
|
20644
|
+
contentLength: payload.contentLength,
|
|
20645
|
+
contentType: payload.contentType,
|
|
20646
|
+
transferEncoding: payload.transferEncoding,
|
|
20647
|
+
error: payload.error,
|
|
20648
|
+
});
|
|
20649
|
+
};
|
|
20632
20650
|
|
|
20633
|
-
|
|
20634
|
-
|
|
20635
|
-
|
|
20636
|
-
|
|
20651
|
+
const withEpubStageTimeout = (promise, stage, payload) =>
|
|
20652
|
+
new Promise((resolve, reject) => {
|
|
20653
|
+
let settled = false;
|
|
20654
|
+
const startedAt = performance.now();
|
|
20655
|
+
const timeout = setTimeout(() => {
|
|
20656
|
+
if (settled) return;
|
|
20657
|
+
settled = true;
|
|
20658
|
+
const error = new Error(`EPUB ${stage} excedeu ${EPUB_STAGE_TIMEOUT_MS}ms`);
|
|
20659
|
+
sendEpubDiagnostic('epub.load.timeout', {
|
|
20660
|
+
...payload,
|
|
20661
|
+
error: { message: error.message, stage },
|
|
20662
|
+
durationMs: performance.now() - startedAt,
|
|
20637
20663
|
});
|
|
20664
|
+
reject(error);
|
|
20665
|
+
}, EPUB_STAGE_TIMEOUT_MS);
|
|
20638
20666
|
|
|
20639
|
-
|
|
20640
|
-
|
|
20641
|
-
|
|
20642
|
-
|
|
20667
|
+
Promise.resolve(promise).then(
|
|
20668
|
+
(value) => {
|
|
20669
|
+
if (settled) return;
|
|
20670
|
+
settled = true;
|
|
20671
|
+
clearTimeout(timeout);
|
|
20672
|
+
resolve(value);
|
|
20673
|
+
},
|
|
20674
|
+
(error) => {
|
|
20675
|
+
if (settled) return;
|
|
20676
|
+
settled = true;
|
|
20677
|
+
clearTimeout(timeout);
|
|
20678
|
+
reject(error);
|
|
20679
|
+
},
|
|
20680
|
+
);
|
|
20681
|
+
});
|
|
20643
20682
|
|
|
20644
|
-
|
|
20645
|
-
|
|
20646
|
-
|
|
20647
|
-
|
|
20648
|
-
|
|
20649
|
-
}
|
|
20650
|
-
if (comicScrollFrame !== null) {
|
|
20651
|
-
cancelAnimationFrame(comicScrollFrame);
|
|
20652
|
-
comicScrollFrame = null;
|
|
20653
|
-
}
|
|
20654
|
-
comicObjectUrls.forEach((url) => URL.revokeObjectURL(url));
|
|
20655
|
-
comicObjectUrls.clear();
|
|
20656
|
-
comicLoading.clear();
|
|
20657
|
-
comicDimensions.clear();
|
|
20658
|
-
comicPreviewCache.clear();
|
|
20659
|
-
comicPreviewLoading.clear();
|
|
20660
|
-
if (comicDispose) {
|
|
20661
|
-
void comicDispose();
|
|
20662
|
-
comicDispose = null;
|
|
20663
|
-
}
|
|
20664
|
-
comicArchive = null;
|
|
20665
|
-
comicEntries = [];
|
|
20666
|
-
comicContainer = null;
|
|
20667
|
-
comicPages = [];
|
|
20668
|
-
};
|
|
20683
|
+
const isEpubDiagnosticsEnabled = () => {
|
|
20684
|
+
const configured = runtimeConfig.epubScrollDiagnostics;
|
|
20685
|
+
return configured === true || configured === '1' || configured === 'true' ||
|
|
20686
|
+
Boolean(window.__PAPYRUS_EPUB_SCROLL_DIAGNOSTICS__);
|
|
20687
|
+
};
|
|
20669
20688
|
|
|
20670
|
-
|
|
20671
|
-
|
|
20672
|
-
while (viewer.firstChild) {
|
|
20673
|
-
viewer.removeChild(viewer.firstChild);
|
|
20674
|
-
}
|
|
20675
|
-
};
|
|
20689
|
+
const createEpubScrollDiagnostics = (manager) => {
|
|
20690
|
+
if (!isEpubDiagnosticsEnabled()) return null;
|
|
20676
20691
|
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
|
|
20680
|
-
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
|
|
20692
|
+
const state = {
|
|
20693
|
+
lastScrollTop: null,
|
|
20694
|
+
lastTouchY: null,
|
|
20695
|
+
lastTouchX: null,
|
|
20696
|
+
lastInputDeltaY: 0,
|
|
20697
|
+
touchActive: false,
|
|
20698
|
+
selectionActive: false,
|
|
20699
|
+
gestureLock: false,
|
|
20700
|
+
consecutiveStallSamples: 0,
|
|
20701
|
+
firstStallTimestamp: null,
|
|
20702
|
+
stallReported: false,
|
|
20703
|
+
};
|
|
20704
|
+
const container = manager && manager.container;
|
|
20687
20705
|
|
|
20688
|
-
|
|
20689
|
-
|
|
20690
|
-
|
|
20691
|
-
|
|
20706
|
+
const getSnapshot = () => {
|
|
20707
|
+
const scrollTop = Number(container && container.scrollTop) || 0;
|
|
20708
|
+
const scrollHeight = Number(container && container.scrollHeight) || 0;
|
|
20709
|
+
const clientHeight = Number(container && container.clientHeight) || 0;
|
|
20710
|
+
let scrollEnabled = true;
|
|
20711
|
+
try {
|
|
20712
|
+
const overflowY = window.getComputedStyle(container).overflowY;
|
|
20713
|
+
scrollEnabled = overflowY !== 'hidden' && overflowY !== 'clip';
|
|
20714
|
+
} catch (_) {}
|
|
20715
|
+
return { scrollTop, scrollHeight, clientHeight, scrollEnabled };
|
|
20716
|
+
};
|
|
20692
20717
|
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
}
|
|
20704
|
-
if (source.kind === 'base64') {
|
|
20705
|
-
return decodeBase64(source.data).buffer;
|
|
20706
|
-
}
|
|
20707
|
-
throw new Error('Fonte de quadrinho inválida.');
|
|
20708
|
-
};
|
|
20718
|
+
const emit = (name, payload) => {
|
|
20719
|
+
sendEvent(name, {
|
|
20720
|
+
timestamp: Date.now(),
|
|
20721
|
+
documentType: currentType,
|
|
20722
|
+
currentPage,
|
|
20723
|
+
spineIndex: Math.max(0, currentPage - 1),
|
|
20724
|
+
progress: pageCount > 0 ? currentPage / pageCount : null,
|
|
20725
|
+
...(payload || {}),
|
|
20726
|
+
});
|
|
20727
|
+
};
|
|
20709
20728
|
|
|
20710
|
-
|
|
20711
|
-
|
|
20712
|
-
|
|
20713
|
-
|
|
20714
|
-
|
|
20715
|
-
if (extension === 'webp') return 'image/webp';
|
|
20716
|
-
return 'image/jpeg';
|
|
20717
|
-
};
|
|
20729
|
+
const resetStall = () => {
|
|
20730
|
+
state.consecutiveStallSamples = 0;
|
|
20731
|
+
state.firstStallTimestamp = null;
|
|
20732
|
+
state.stallReported = false;
|
|
20733
|
+
};
|
|
20718
20734
|
|
|
20719
|
-
|
|
20720
|
-
|
|
20721
|
-
|
|
20722
|
-
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
|
|
20727
|
-
|
|
20728
|
-
|
|
20729
|
-
|
|
20730
|
-
|
|
20731
|
-
|
|
20732
|
-
|
|
20733
|
-
currentPage = nextPage;
|
|
20734
|
-
sendState();
|
|
20735
|
-
}
|
|
20736
|
-
};
|
|
20735
|
+
const recordScroll = (event) => {
|
|
20736
|
+
const snapshot = getSnapshot();
|
|
20737
|
+
const previousScrollTop = state.lastScrollTop === null
|
|
20738
|
+
? snapshot.scrollTop
|
|
20739
|
+
: state.lastScrollTop;
|
|
20740
|
+
const deltaY = state.lastInputDeltaY;
|
|
20741
|
+
const inputDirection = deltaY > 4 ? 'up' : deltaY < -4 ? 'down' : 'none';
|
|
20742
|
+
const offsetUnchanged = Math.abs(snapshot.scrollTop - previousScrollTop) <= 1;
|
|
20743
|
+
const validStallSample = inputDirection === 'up' &&
|
|
20744
|
+
snapshot.scrollTop > 2 &&
|
|
20745
|
+
snapshot.scrollEnabled &&
|
|
20746
|
+
!state.selectionActive &&
|
|
20747
|
+
!state.gestureLock &&
|
|
20748
|
+
offsetUnchanged;
|
|
20737
20749
|
|
|
20738
|
-
|
|
20739
|
-
|
|
20740
|
-
|
|
20741
|
-
|
|
20742
|
-
|
|
20750
|
+
if (validStallSample) {
|
|
20751
|
+
state.consecutiveStallSamples += 1;
|
|
20752
|
+
if (state.firstStallTimestamp === null) {
|
|
20753
|
+
state.firstStallTimestamp = Date.now();
|
|
20754
|
+
}
|
|
20755
|
+
const durationMs = Date.now() - state.firstStallTimestamp;
|
|
20756
|
+
if (!state.stallReported && state.consecutiveStallSamples >= 3 && durationMs >= 150) {
|
|
20757
|
+
state.stallReported = true;
|
|
20758
|
+
emit('epub.scroll.stall', {
|
|
20759
|
+
...snapshot,
|
|
20760
|
+
previousScrollTop,
|
|
20761
|
+
deltaY,
|
|
20762
|
+
direction: 'up',
|
|
20763
|
+
consecutiveSamples: state.consecutiveStallSamples,
|
|
20764
|
+
durationMs,
|
|
20765
|
+
selectionActive: state.selectionActive,
|
|
20766
|
+
gestureLock: state.gestureLock,
|
|
20743
20767
|
});
|
|
20744
|
-
}
|
|
20768
|
+
}
|
|
20769
|
+
} else if (!offsetUnchanged || inputDirection !== 'up') {
|
|
20770
|
+
resetStall();
|
|
20771
|
+
}
|
|
20745
20772
|
|
|
20746
|
-
|
|
20747
|
-
|
|
20748
|
-
|
|
20749
|
-
|
|
20750
|
-
|
|
20751
|
-
|
|
20752
|
-
|
|
20753
|
-
|
|
20754
|
-
|
|
20755
|
-
|
|
20756
|
-
|
|
20757
|
-
|
|
20758
|
-
const image = comicPages[index]?.querySelector('img');
|
|
20759
|
-
const page = comicPages[index];
|
|
20760
|
-
const dimensions = comicDimensions.get(index);
|
|
20761
|
-
const width = dimensions?.width || image?.naturalWidth || 0;
|
|
20762
|
-
const height = dimensions?.height || image?.naturalHeight || 0;
|
|
20763
|
-
if (page && width > 0 && height > 0) {
|
|
20764
|
-
page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
|
|
20765
|
-
width,
|
|
20766
|
-
height
|
|
20767
|
-
);
|
|
20768
|
-
}
|
|
20769
|
-
if (image) image.removeAttribute('src');
|
|
20770
|
-
}
|
|
20771
|
-
};
|
|
20773
|
+
state.lastScrollTop = snapshot.scrollTop;
|
|
20774
|
+
emit('epub.scroll', {
|
|
20775
|
+
...snapshot,
|
|
20776
|
+
previousScrollTop,
|
|
20777
|
+
deltaY,
|
|
20778
|
+
direction: snapshot.scrollTop < previousScrollTop ? 'up' :
|
|
20779
|
+
snapshot.scrollTop > previousScrollTop ? 'down' : 'none',
|
|
20780
|
+
touchActive: state.touchActive,
|
|
20781
|
+
selectionActive: state.selectionActive,
|
|
20782
|
+
gestureLock: state.gestureLock,
|
|
20783
|
+
});
|
|
20784
|
+
};
|
|
20772
20785
|
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
|
|
20777
|
-
|
|
20778
|
-
|
|
20779
|
-
|
|
20786
|
+
return {
|
|
20787
|
+
setSelectionActive(active) {
|
|
20788
|
+
state.selectionActive = Boolean(active);
|
|
20789
|
+
emit('epub.selection', { selectionActive: state.selectionActive });
|
|
20790
|
+
},
|
|
20791
|
+
setGestureLock(active) {
|
|
20792
|
+
state.gestureLock = Boolean(active);
|
|
20793
|
+
emit('epub.gesture-lock', { gestureLock: state.gestureLock });
|
|
20794
|
+
},
|
|
20795
|
+
touchStart(event) {
|
|
20796
|
+
const touch = event && event.touches && event.touches[0];
|
|
20797
|
+
state.touchActive = true;
|
|
20798
|
+
state.lastTouchY = touch ? Number(touch.clientY) : null;
|
|
20799
|
+
state.lastTouchX = touch ? Number(touch.clientX) : null;
|
|
20800
|
+
state.lastInputDeltaY = 0;
|
|
20801
|
+
resetStall();
|
|
20802
|
+
emit('epub.touch.start', { x: state.lastTouchX, y: state.lastTouchY });
|
|
20803
|
+
},
|
|
20804
|
+
touchMove(event) {
|
|
20805
|
+
const touch = event && event.touches && event.touches[0];
|
|
20806
|
+
if (!touch) return;
|
|
20807
|
+
const previousY = state.lastTouchY;
|
|
20808
|
+
const previousX = state.lastTouchX;
|
|
20809
|
+
state.lastTouchY = Number(touch.clientY);
|
|
20810
|
+
state.lastTouchX = Number(touch.clientX);
|
|
20811
|
+
state.lastInputDeltaY = previousY === null ? 0 : previousY - state.lastTouchY;
|
|
20812
|
+
emit('epub.touch.move', {
|
|
20813
|
+
x: state.lastTouchX,
|
|
20814
|
+
y: state.lastTouchY,
|
|
20815
|
+
deltaY: previousY === null ? 0 : previousY - state.lastTouchY,
|
|
20816
|
+
deltaX: previousX === null ? 0 : previousX - state.lastTouchX,
|
|
20817
|
+
});
|
|
20818
|
+
recordScroll();
|
|
20819
|
+
},
|
|
20820
|
+
touchEnd(event) {
|
|
20821
|
+
const touch = event && event.changedTouches && event.changedTouches[0];
|
|
20822
|
+
state.touchActive = false;
|
|
20823
|
+
emit('epub.touch.end', {
|
|
20824
|
+
x: touch ? Number(touch.clientX) : state.lastTouchX,
|
|
20825
|
+
y: touch ? Number(touch.clientY) : state.lastTouchY,
|
|
20826
|
+
});
|
|
20827
|
+
state.lastTouchY = null;
|
|
20828
|
+
state.lastTouchX = null;
|
|
20829
|
+
state.lastInputDeltaY = 0;
|
|
20830
|
+
resetStall();
|
|
20831
|
+
},
|
|
20832
|
+
scroll: recordScroll,
|
|
20833
|
+
relocated(location) {
|
|
20834
|
+
emit('epub.relocated', {
|
|
20835
|
+
href: location && location.start ? location.start.href || null : null,
|
|
20836
|
+
cfi: location && location.start ? location.start.cfi || null : null,
|
|
20837
|
+
});
|
|
20838
|
+
},
|
|
20839
|
+
settled(event) {
|
|
20840
|
+
emit('epub.momentum.settled', {
|
|
20841
|
+
scrollTop: Number(event && event.top) || 0,
|
|
20842
|
+
});
|
|
20843
|
+
},
|
|
20844
|
+
command(name, payload) {
|
|
20845
|
+
emit('epub.programmatic-scroll', { command: name, ...(payload || {}) });
|
|
20846
|
+
},
|
|
20847
|
+
};
|
|
20848
|
+
};
|
|
20780
20849
|
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
20786
|
-
!comicHelpers.isCurrentComicPageLoad(
|
|
20787
|
-
loadGeneration,
|
|
20788
|
-
comicGeneration,
|
|
20789
|
-
entry,
|
|
20790
|
-
comicEntries[pageIndex]
|
|
20791
|
-
)
|
|
20792
|
-
) {
|
|
20793
|
-
return null;
|
|
20794
|
-
}
|
|
20795
|
-
const url = URL.createObjectURL(
|
|
20796
|
-
new Blob([blob], { type: comicMimeType(entry.name) })
|
|
20797
|
-
);
|
|
20798
|
-
comicObjectUrls.set(pageIndex, url);
|
|
20799
|
-
const image = comicPages[pageIndex]?.querySelector('img');
|
|
20800
|
-
if (image) image.src = url;
|
|
20801
|
-
evictComicUrls();
|
|
20802
|
-
return url;
|
|
20803
|
-
});
|
|
20804
|
-
comicLoading.set(pageIndex, pending);
|
|
20805
|
-
try {
|
|
20806
|
-
return await pending;
|
|
20807
|
-
} finally {
|
|
20808
|
-
if (comicLoading.get(pageIndex) === pending) {
|
|
20809
|
-
comicLoading.delete(pageIndex);
|
|
20810
|
-
}
|
|
20811
|
-
}
|
|
20812
|
-
};
|
|
20850
|
+
const reportError = (error, context) => {
|
|
20851
|
+
const message = error && error.message ? error.message : String(error);
|
|
20852
|
+
const stack = error && error.stack ? error.stack : null;
|
|
20853
|
+
sendEvent('RUNTIME_ERROR', { message, context, stack });
|
|
20854
|
+
};
|
|
20813
20855
|
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
image.style.transform = `rotate(${rotation}deg)`;
|
|
20819
|
-
};
|
|
20856
|
+
const shouldIgnoreError = (error) => {
|
|
20857
|
+
const message = error && error.message ? error.message : String(error || '');
|
|
20858
|
+
return message.includes('ResizeObserver loop');
|
|
20859
|
+
};
|
|
20820
20860
|
|
|
20821
|
-
|
|
20822
|
-
|
|
20823
|
-
|
|
20861
|
+
window.addEventListener('error', (event) => {
|
|
20862
|
+
const error = event.error || event.message;
|
|
20863
|
+
if (shouldIgnoreError(error)) return;
|
|
20864
|
+
reportError(error, 'window.error');
|
|
20865
|
+
});
|
|
20824
20866
|
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
|
|
20867
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
20868
|
+
if (shouldIgnoreError(event.reason)) return;
|
|
20869
|
+
reportError(event.reason, 'unhandledrejection');
|
|
20870
|
+
});
|
|
20871
|
+
|
|
20872
|
+
const clearComicState = () => {
|
|
20873
|
+
comicGeneration += 1;
|
|
20874
|
+
if (comicObserver) {
|
|
20875
|
+
comicObserver.disconnect();
|
|
20876
|
+
comicObserver = null;
|
|
20877
|
+
}
|
|
20878
|
+
if (comicScrollFrame !== null) {
|
|
20879
|
+
cancelAnimationFrame(comicScrollFrame);
|
|
20880
|
+
comicScrollFrame = null;
|
|
20881
|
+
}
|
|
20882
|
+
comicObjectUrls.forEach((url) => URL.revokeObjectURL(url));
|
|
20883
|
+
comicObjectUrls.clear();
|
|
20884
|
+
comicLoading.clear();
|
|
20885
|
+
comicVisiblePages.clear();
|
|
20886
|
+
comicDimensions.clear();
|
|
20887
|
+
comicPreviewCache.clear();
|
|
20888
|
+
comicPreviewLoading.clear();
|
|
20889
|
+
if (comicDispose) {
|
|
20890
|
+
void comicDispose();
|
|
20891
|
+
comicDispose = null;
|
|
20892
|
+
}
|
|
20893
|
+
comicArchive = null;
|
|
20894
|
+
comicEntries = [];
|
|
20895
|
+
comicContainer = null;
|
|
20896
|
+
comicPages = [];
|
|
20897
|
+
};
|
|
20898
|
+
|
|
20899
|
+
const clearViewer = () => {
|
|
20900
|
+
if (epubInteractionCleanup) {
|
|
20901
|
+
epubInteractionCleanup();
|
|
20902
|
+
epubInteractionCleanup = null;
|
|
20903
|
+
}
|
|
20904
|
+
epubSelectionCleanups.forEach((cleanup) => cleanup());
|
|
20905
|
+
epubSelectionCleanups.clear();
|
|
20906
|
+
clearComicState();
|
|
20907
|
+
while (viewer.firstChild) {
|
|
20908
|
+
viewer.removeChild(viewer.firstChild);
|
|
20909
|
+
}
|
|
20910
|
+
};
|
|
20911
|
+
|
|
20912
|
+
const decodeBase64 = (value) => {
|
|
20913
|
+
const clean = value.replace(/\s/g, '');
|
|
20914
|
+
const binary = atob(clean);
|
|
20915
|
+
const len = binary.length;
|
|
20916
|
+
const bytes = new Uint8Array(len);
|
|
20917
|
+
for (let i = 0; i < len; i += 1) {
|
|
20918
|
+
bytes[i] = binary.charCodeAt(i);
|
|
20919
|
+
}
|
|
20920
|
+
return bytes;
|
|
20921
|
+
};
|
|
20922
|
+
|
|
20923
|
+
const toExactArrayBuffer = (bytes) => {
|
|
20924
|
+
if (
|
|
20925
|
+
bytes.byteOffset === 0 &&
|
|
20926
|
+
bytes.byteLength === bytes.buffer.byteLength
|
|
20927
|
+
) {
|
|
20928
|
+
return bytes.buffer;
|
|
20929
|
+
}
|
|
20930
|
+
return bytes.buffer.slice(
|
|
20931
|
+
bytes.byteOffset,
|
|
20932
|
+
bytes.byteOffset + bytes.byteLength
|
|
20933
|
+
);
|
|
20934
|
+
};
|
|
20935
|
+
|
|
20936
|
+
const decodeBase64ToText = (value) => {
|
|
20937
|
+
const bytes = decodeBase64(value);
|
|
20938
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
20939
|
+
};
|
|
20940
|
+
|
|
20941
|
+
const isMetroAssetUri = (value) => {
|
|
20942
|
+
try {
|
|
20943
|
+
const url = new URL(value);
|
|
20944
|
+
return (
|
|
20945
|
+
(url.protocol === 'http:' || url.protocol === 'https:') &&
|
|
20946
|
+
url.pathname.startsWith('/assets/') &&
|
|
20947
|
+
url.searchParams.has('platform') &&
|
|
20948
|
+
url.searchParams.has('hash')
|
|
20949
|
+
);
|
|
20950
|
+
} catch {
|
|
20951
|
+
return false;
|
|
20952
|
+
}
|
|
20953
|
+
};
|
|
20954
|
+
|
|
20955
|
+
const sourceToArrayBuffer = async (source) => {
|
|
20956
|
+
if (source.kind === 'uri') {
|
|
20957
|
+
if (/^(?:file|content|android\.resource):\/\//i.test(source.uri)) {
|
|
20958
|
+
return readLocalFile(source.uri);
|
|
20959
|
+
}
|
|
20960
|
+
const isEpubHttpSource = isMetroAssetUri(source.uri);
|
|
20961
|
+
if (isEpubHttpSource) {
|
|
20962
|
+
sendEpubDiagnostic('epub.source.fetch.start', {uri: source.uri});
|
|
20963
|
+
}
|
|
20964
|
+
const response = await fetch(source.uri);
|
|
20965
|
+
if (isEpubHttpSource) {
|
|
20966
|
+
sendEpubDiagnostic('epub.source.fetch.response', {
|
|
20967
|
+
uri: source.uri,
|
|
20968
|
+
status: response.status,
|
|
20969
|
+
contentLength: response.headers.get('content-length'),
|
|
20970
|
+
contentType: response.headers.get('content-type'),
|
|
20971
|
+
transferEncoding: response.headers.get('transfer-encoding'),
|
|
20972
|
+
});
|
|
20973
|
+
}
|
|
20974
|
+
if (!response.ok && response.status !== 0) {
|
|
20975
|
+
throw new Error(`Falha ao carregar quadrinho (${response.status})`);
|
|
20976
|
+
}
|
|
20977
|
+
if (isEpubHttpSource) {
|
|
20978
|
+
sendEpubDiagnostic('epub.source.body.start', {uri: source.uri});
|
|
20979
|
+
}
|
|
20980
|
+
const body = await response.arrayBuffer();
|
|
20981
|
+
if (isEpubHttpSource) {
|
|
20982
|
+
sendEpubDiagnostic('epub.source.body.end', {
|
|
20983
|
+
uri: source.uri,
|
|
20984
|
+
byteLength: body.byteLength,
|
|
20985
|
+
});
|
|
20986
|
+
}
|
|
20987
|
+
return body;
|
|
20988
|
+
}
|
|
20989
|
+
if (source.kind === 'base64') {
|
|
20990
|
+
return toExactArrayBuffer(decodeBase64(source.data));
|
|
20991
|
+
}
|
|
20992
|
+
throw new Error('Fonte de quadrinho inválida.');
|
|
20993
|
+
};
|
|
20994
|
+
|
|
20995
|
+
const comicMimeType = (name) => {
|
|
20996
|
+
const extension = name.split('.').pop()?.toLowerCase();
|
|
20997
|
+
if (extension === 'png') return 'image/png';
|
|
20998
|
+
if (extension === 'gif') return 'image/gif';
|
|
20999
|
+
if (extension === 'svg') return 'image/svg+xml';
|
|
21000
|
+
if (extension === 'webp') return 'image/webp';
|
|
21001
|
+
return 'image/jpeg';
|
|
21002
|
+
};
|
|
21003
|
+
|
|
21004
|
+
const updateComicCurrentPage = () => {
|
|
21005
|
+
if (!comicContainer || comicPages.length === 0) return;
|
|
21006
|
+
const viewportTop = comicContainer.scrollTop;
|
|
21007
|
+
let closestIndex = currentPage - 1;
|
|
21008
|
+
let closestDistance = Number.POSITIVE_INFINITY;
|
|
21009
|
+
comicPages.forEach((page, index) => {
|
|
21010
|
+
const distance = Math.abs(page.offsetTop - viewportTop);
|
|
21011
|
+
if (distance < closestDistance) {
|
|
21012
|
+
closestDistance = distance;
|
|
21013
|
+
closestIndex = index;
|
|
21014
|
+
}
|
|
21015
|
+
});
|
|
21016
|
+
const nextPage = Math.max(1, Math.min(pageCount, closestIndex + 1));
|
|
21017
|
+
if (nextPage !== currentPage) {
|
|
21018
|
+
currentPage = nextPage;
|
|
21019
|
+
sendState();
|
|
21020
|
+
}
|
|
21021
|
+
};
|
|
21022
|
+
|
|
21023
|
+
const scheduleComicCurrentPage = () => {
|
|
21024
|
+
if (comicScrollFrame !== null) return;
|
|
21025
|
+
comicScrollFrame = requestAnimationFrame(() => {
|
|
21026
|
+
comicScrollFrame = null;
|
|
21027
|
+
updateComicCurrentPage();
|
|
21028
|
+
});
|
|
21029
|
+
};
|
|
21030
|
+
|
|
21031
|
+
const evictComicUrls = () => {
|
|
21032
|
+
if (comicObjectUrls.size <= COMIC_CACHE_LIMIT) return;
|
|
21033
|
+
const protectedIndexes = comicHelpers.getProtectedComicPageIndexes(
|
|
21034
|
+
currentPage,
|
|
21035
|
+
Array.from(comicVisiblePages),
|
|
21036
|
+
Array.from(comicLoading.keys())
|
|
21037
|
+
);
|
|
21038
|
+
for (const [index, url] of comicObjectUrls) {
|
|
21039
|
+
if (comicObjectUrls.size <= COMIC_CACHE_LIMIT) break;
|
|
21040
|
+
if (protectedIndexes.has(index)) continue;
|
|
21041
|
+
URL.revokeObjectURL(url);
|
|
21042
|
+
comicObjectUrls.delete(index);
|
|
21043
|
+
const image = comicPages[index]?.querySelector('img');
|
|
21044
|
+
const page = comicPages[index];
|
|
21045
|
+
const dimensions = comicDimensions.get(index);
|
|
21046
|
+
const width = dimensions?.width || image?.naturalWidth || 0;
|
|
21047
|
+
const height = dimensions?.height || image?.naturalHeight || 0;
|
|
21048
|
+
if (page && width > 0 && height > 0) {
|
|
21049
|
+
page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
|
|
21050
|
+
width,
|
|
21051
|
+
height
|
|
21052
|
+
);
|
|
21053
|
+
}
|
|
21054
|
+
if (image) image.removeAttribute('src');
|
|
21055
|
+
}
|
|
21056
|
+
};
|
|
21057
|
+
|
|
21058
|
+
const ensureComicPage = async (pageIndex) => {
|
|
21059
|
+
if (comicObjectUrls.has(pageIndex)) {
|
|
21060
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
21061
|
+
if (image && !image.src) image.src = comicObjectUrls.get(pageIndex);
|
|
21062
|
+
return comicObjectUrls.get(pageIndex);
|
|
21063
|
+
}
|
|
21064
|
+
if (comicLoading.has(pageIndex)) return comicLoading.get(pageIndex);
|
|
21065
|
+
|
|
21066
|
+
const entry = comicEntries[pageIndex];
|
|
21067
|
+
if (!entry) throw new Error(`Página de quadrinho inválida: ${pageIndex}`);
|
|
21068
|
+
const loadGeneration = comicGeneration;
|
|
21069
|
+
const pending = entry.async('blob').then((blob) => {
|
|
21070
|
+
if (
|
|
21071
|
+
!comicHelpers.isCurrentComicPageLoad(
|
|
21072
|
+
loadGeneration,
|
|
21073
|
+
comicGeneration,
|
|
21074
|
+
entry,
|
|
21075
|
+
comicEntries[pageIndex]
|
|
21076
|
+
)
|
|
21077
|
+
) {
|
|
21078
|
+
return null;
|
|
21079
|
+
}
|
|
21080
|
+
const url = URL.createObjectURL(
|
|
21081
|
+
new Blob([blob], { type: comicMimeType(entry.name) })
|
|
21082
|
+
);
|
|
21083
|
+
comicObjectUrls.set(pageIndex, url);
|
|
21084
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
21085
|
+
if (image) image.src = url;
|
|
21086
|
+
evictComicUrls();
|
|
21087
|
+
return url;
|
|
21088
|
+
});
|
|
21089
|
+
comicLoading.set(pageIndex, pending);
|
|
21090
|
+
try {
|
|
21091
|
+
return await pending;
|
|
21092
|
+
} finally {
|
|
21093
|
+
if (comicLoading.get(pageIndex) === pending) {
|
|
21094
|
+
comicLoading.delete(pageIndex);
|
|
21095
|
+
evictComicUrls();
|
|
21096
|
+
}
|
|
21097
|
+
}
|
|
21098
|
+
};
|
|
21099
|
+
|
|
21100
|
+
const renderComicPage = (pageIndex) => {
|
|
21101
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
21102
|
+
if (!image) return;
|
|
21103
|
+
image.style.width = `${Math.max(40, Math.round(100 * zoom))}%`;
|
|
21104
|
+
image.style.transform = `rotate(${rotation}deg)`;
|
|
21105
|
+
};
|
|
21106
|
+
|
|
21107
|
+
const applyComicLayout = () => {
|
|
21108
|
+
comicPages.forEach((_, pageIndex) => renderComicPage(pageIndex));
|
|
21109
|
+
};
|
|
21110
|
+
|
|
21111
|
+
const loadComic = async (source, format) => {
|
|
21112
|
+
clearViewer();
|
|
21113
|
+
let entries;
|
|
21114
|
+
if (format === 'cbr') {
|
|
21115
|
+
const clientUrl = runtimeConfig.cbrClientUrl;
|
|
21116
|
+
const workerUrl = runtimeConfig.cbrWorkerUrl;
|
|
21117
|
+
const wasmUrl = runtimeConfig.cbrWasmUrl;
|
|
21118
|
+
if (!clientUrl || !workerUrl || !wasmUrl) {
|
|
21119
|
+
throw new Error(
|
|
21120
|
+
'CBR no mobile exige o pacote opcional de runtime libarchive.'
|
|
21121
|
+
);
|
|
21122
|
+
}
|
|
20837
21123
|
|
|
20838
21124
|
const [clientSource, workerSource, wasmSource] = await Promise.all([
|
|
20839
21125
|
fetchRuntimeAsset(clientUrl, 'text'),
|
|
@@ -20845,716 +21131,1003 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20845
21131
|
workerSource,
|
|
20846
21132
|
wasmSource
|
|
20847
21133
|
);
|
|
20848
|
-
|
|
20849
|
-
|
|
20850
|
-
|
|
20851
|
-
|
|
20852
|
-
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
|
|
20884
|
-
}
|
|
20885
|
-
)
|
|
20886
|
-
.map((item) => {
|
|
20887
|
-
const name = `${item.path || ''}${item.file.name}`;
|
|
20888
|
-
return {
|
|
20889
|
-
name,
|
|
20890
|
-
async: async () => item.file.extract(),
|
|
20891
|
-
};
|
|
20892
|
-
});
|
|
20893
|
-
comicDispose = async () => {
|
|
20894
|
-
await archive.close();
|
|
20895
|
-
URL.revokeObjectURL(workerObjectUrl);
|
|
20896
|
-
URL.revokeObjectURL(clientObjectUrl);
|
|
20897
|
-
};
|
|
20898
|
-
} catch (error) {
|
|
20899
|
-
URL.revokeObjectURL(workerObjectUrl);
|
|
20900
|
-
URL.revokeObjectURL(clientObjectUrl);
|
|
20901
|
-
throw error;
|
|
20902
|
-
}
|
|
20903
|
-
} else {
|
|
20904
|
-
if (!window.JSZip) {
|
|
20905
|
-
throw new Error('O runtime ZIP do leitor mobile não foi carregado.');
|
|
21134
|
+
const workerObjectUrl = URL.createObjectURL(
|
|
21135
|
+
new Blob([patchedWorkerSource], { type: 'text/javascript' })
|
|
21136
|
+
);
|
|
21137
|
+
const clientObjectUrl = URL.createObjectURL(
|
|
21138
|
+
new Blob([clientSource], { type: 'text/javascript' })
|
|
21139
|
+
);
|
|
21140
|
+
try {
|
|
21141
|
+
const archiveModule = await import(clientObjectUrl);
|
|
21142
|
+
const createCbrWorker = () => {
|
|
21143
|
+
const worker = new Worker(workerObjectUrl, {type: 'module'});
|
|
21144
|
+
worker.addEventListener('error', (event) => {
|
|
21145
|
+
sendEvent('RUNTIME_ERROR', {
|
|
21146
|
+
message: `CBR worker: ${event?.message || 'falha ao executar o worker'}`,
|
|
21147
|
+
context: 'comic.worker',
|
|
21148
|
+
});
|
|
21149
|
+
});
|
|
21150
|
+
worker.addEventListener('messageerror', () => {
|
|
21151
|
+
sendEvent('RUNTIME_ERROR', {
|
|
21152
|
+
message: 'CBR worker: falha ao transferir mensagem',
|
|
21153
|
+
context: 'comic.worker',
|
|
21154
|
+
});
|
|
21155
|
+
});
|
|
21156
|
+
return worker;
|
|
21157
|
+
};
|
|
21158
|
+
archiveModule.Archive.init({ getWorker: createCbrWorker });
|
|
21159
|
+
const bytes = await sourceToArrayBuffer(source);
|
|
21160
|
+
const file = new Blob([bytes], {
|
|
21161
|
+
type: 'application/vnd.comicbook-rar',
|
|
21162
|
+
});
|
|
21163
|
+
const archive = await archiveModule.Archive.open(file);
|
|
21164
|
+
const files = await archive.getFilesArray();
|
|
21165
|
+
entries = files
|
|
21166
|
+
.filter(
|
|
21167
|
+
(item) => {
|
|
21168
|
+
const name = `${item?.path || ''}${item?.file?.name || ''}`;
|
|
21169
|
+
return item?.file && comicHelpers.isComicImageName(name);
|
|
20906
21170
|
}
|
|
21171
|
+
)
|
|
21172
|
+
.map((item) => {
|
|
21173
|
+
const name = `${item.path || ''}${item.file.name}`;
|
|
21174
|
+
return {
|
|
21175
|
+
name,
|
|
21176
|
+
async: async () => item.file.extract(),
|
|
21177
|
+
};
|
|
21178
|
+
});
|
|
21179
|
+
comicDispose = async () => {
|
|
21180
|
+
await archive.close();
|
|
21181
|
+
URL.revokeObjectURL(workerObjectUrl);
|
|
21182
|
+
URL.revokeObjectURL(clientObjectUrl);
|
|
21183
|
+
};
|
|
21184
|
+
} catch (error) {
|
|
21185
|
+
URL.revokeObjectURL(workerObjectUrl);
|
|
21186
|
+
URL.revokeObjectURL(clientObjectUrl);
|
|
21187
|
+
throw error;
|
|
21188
|
+
}
|
|
21189
|
+
} else {
|
|
21190
|
+
if (!window.JSZip) {
|
|
21191
|
+
throw new Error('O runtime ZIP do leitor mobile não foi carregado.');
|
|
21192
|
+
}
|
|
20907
21193
|
|
|
20908
|
-
|
|
20909
|
-
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
|
|
20914
|
-
|
|
20915
|
-
|
|
20916
|
-
|
|
21194
|
+
const bytes = await sourceToArrayBuffer(source);
|
|
21195
|
+
comicArchive = await window.JSZip.loadAsync(bytes);
|
|
21196
|
+
entries = Object.keys(comicArchive.files)
|
|
21197
|
+
.filter((name) => {
|
|
21198
|
+
const entry = comicArchive.files[name];
|
|
21199
|
+
return !entry.dir && comicHelpers.isComicImageName(name);
|
|
21200
|
+
})
|
|
21201
|
+
.map((name) => comicArchive.files[name]);
|
|
21202
|
+
}
|
|
20917
21203
|
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
21204
|
+
const names = comicHelpers.sortComicPageNames(
|
|
21205
|
+
entries.map((entry) => entry.name)
|
|
21206
|
+
);
|
|
21207
|
+
const entriesByName = new Map(entries.map((entry) => [entry.name, entry]));
|
|
21208
|
+
comicEntries = names.map((name) => entriesByName.get(name));
|
|
21209
|
+
if (comicEntries.length === 0) {
|
|
21210
|
+
if (comicDispose) {
|
|
21211
|
+
await comicDispose();
|
|
21212
|
+
comicDispose = null;
|
|
21213
|
+
}
|
|
21214
|
+
throw new Error('O arquivo não contém páginas de imagem.');
|
|
21215
|
+
}
|
|
20930
21216
|
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
|
|
21217
|
+
comicContainer = document.createElement('div');
|
|
21218
|
+
comicContainer.style.width = '100%';
|
|
21219
|
+
comicContainer.style.height = '100%';
|
|
21220
|
+
comicContainer.style.overflow = 'auto';
|
|
21221
|
+
comicContainer.style.padding = '16px 0 120px';
|
|
21222
|
+
comicContainer.style.boxSizing = 'border-box';
|
|
21223
|
+
comicContainer.style.background = '#ffffff';
|
|
21224
|
+
comicContainer.addEventListener('scroll', scheduleComicCurrentPage, {
|
|
21225
|
+
passive: true,
|
|
21226
|
+
});
|
|
21227
|
+
viewer.style.overflow = 'hidden';
|
|
21228
|
+
viewer.appendChild(comicContainer);
|
|
20943
21229
|
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
21230
|
+
comicPages = comicEntries.map((entry, pageIndex) => {
|
|
21231
|
+
const page = document.createElement('div');
|
|
21232
|
+
page.dataset.pageIndex = String(pageIndex);
|
|
21233
|
+
page.style.width = '100%';
|
|
21234
|
+
page.style.minHeight = '80px';
|
|
21235
|
+
page.style.display = 'flex';
|
|
21236
|
+
page.style.justifyContent = 'center';
|
|
21237
|
+
page.style.alignItems = 'flex-start';
|
|
21238
|
+
page.style.padding = '0 12px 20px';
|
|
21239
|
+
page.style.boxSizing = 'border-box';
|
|
20954
21240
|
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
});
|
|
20970
|
-
page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
|
|
20971
|
-
image.naturalWidth,
|
|
20972
|
-
image.naturalHeight
|
|
20973
|
-
);
|
|
20974
|
-
}
|
|
20975
|
-
});
|
|
20976
|
-
page.appendChild(image);
|
|
20977
|
-
comicContainer.appendChild(page);
|
|
20978
|
-
return page;
|
|
21241
|
+
const image = document.createElement('img');
|
|
21242
|
+
image.alt = `Página ${pageIndex + 1}`;
|
|
21243
|
+
image.draggable = false;
|
|
21244
|
+
image.decoding = 'async';
|
|
21245
|
+
image.loading = 'lazy';
|
|
21246
|
+
image.style.display = 'block';
|
|
21247
|
+
image.style.maxWidth = 'none';
|
|
21248
|
+
image.style.height = 'auto';
|
|
21249
|
+
image.style.userSelect = 'none';
|
|
21250
|
+
image.addEventListener('load', () => {
|
|
21251
|
+
if (image.naturalWidth > 0 && image.naturalHeight > 0) {
|
|
21252
|
+
comicDimensions.set(pageIndex, {
|
|
21253
|
+
width: image.naturalWidth,
|
|
21254
|
+
height: image.naturalHeight,
|
|
20979
21255
|
});
|
|
21256
|
+
page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
|
|
21257
|
+
image.naturalWidth,
|
|
21258
|
+
image.naturalHeight
|
|
21259
|
+
);
|
|
21260
|
+
}
|
|
21261
|
+
});
|
|
21262
|
+
page.appendChild(image);
|
|
21263
|
+
comicContainer.appendChild(page);
|
|
21264
|
+
return page;
|
|
21265
|
+
});
|
|
20980
21266
|
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
message: error?.message || String(error),
|
|
20990
|
-
context: 'comic.page',
|
|
20991
|
-
})
|
|
20992
|
-
);
|
|
20993
|
-
});
|
|
20994
|
-
scheduleComicCurrentPage();
|
|
20995
|
-
},
|
|
20996
|
-
{ root: comicContainer, rootMargin: '600px 0px' }
|
|
20997
|
-
);
|
|
20998
|
-
comicPages.forEach((page) => comicObserver.observe(page));
|
|
20999
|
-
} else {
|
|
21000
|
-
for (let pageIndex = 0; pageIndex < Math.min(3, comicPages.length); pageIndex += 1) {
|
|
21001
|
-
void ensureComicPage(pageIndex);
|
|
21267
|
+
if (typeof IntersectionObserver === 'function') {
|
|
21268
|
+
comicObserver = new IntersectionObserver(
|
|
21269
|
+
(observations) => {
|
|
21270
|
+
observations.forEach((observation) => {
|
|
21271
|
+
const pageIndex = Number(observation.target.dataset.pageIndex);
|
|
21272
|
+
if (!observation.isIntersecting) {
|
|
21273
|
+
comicVisiblePages.delete(pageIndex);
|
|
21274
|
+
return;
|
|
21002
21275
|
}
|
|
21003
|
-
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
const readComicBlobAsDataUrl = (blob, mimeType) =>
|
|
21012
|
-
new Promise((resolve, reject) => {
|
|
21013
|
-
const reader = new FileReader();
|
|
21014
|
-
reader.onerror = () => reject(new Error('Falha ao gerar preview.'));
|
|
21015
|
-
reader.onload = () => resolve(reader.result);
|
|
21016
|
-
reader.readAsDataURL(new Blob([blob], { type: mimeType }));
|
|
21276
|
+
comicVisiblePages.add(pageIndex);
|
|
21277
|
+
void ensureComicPage(pageIndex).catch((error) =>
|
|
21278
|
+
sendEvent('RUNTIME_ERROR', {
|
|
21279
|
+
message: error?.message || String(error),
|
|
21280
|
+
context: 'comic.page',
|
|
21281
|
+
})
|
|
21282
|
+
);
|
|
21017
21283
|
});
|
|
21284
|
+
scheduleComicCurrentPage();
|
|
21285
|
+
},
|
|
21286
|
+
{ root: comicContainer, rootMargin: '600px 0px' }
|
|
21287
|
+
);
|
|
21288
|
+
comicPages.forEach((page) => comicObserver.observe(page));
|
|
21289
|
+
} else {
|
|
21290
|
+
for (let pageIndex = 0; pageIndex < Math.min(3, comicPages.length); pageIndex += 1) {
|
|
21291
|
+
void ensureComicPage(pageIndex);
|
|
21292
|
+
}
|
|
21293
|
+
}
|
|
21294
|
+
pageCount = comicPages.length;
|
|
21295
|
+
currentPage = 1;
|
|
21296
|
+
await ensureComicPage(0);
|
|
21297
|
+
applyComicLayout();
|
|
21298
|
+
return { pageCount };
|
|
21299
|
+
};
|
|
21018
21300
|
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
const image = new Image();
|
|
21027
|
-
image.decoding = 'async';
|
|
21028
|
-
await new Promise((resolve, reject) => {
|
|
21029
|
-
image.onload = resolve;
|
|
21030
|
-
image.onerror = () => reject(new Error('Falha ao decodificar preview.'));
|
|
21031
|
-
image.src = sourceUrl;
|
|
21032
|
-
});
|
|
21033
|
-
const size = comicHelpers.getComicPreviewSize(
|
|
21034
|
-
image.naturalWidth || image.width,
|
|
21035
|
-
image.naturalHeight || image.height
|
|
21036
|
-
);
|
|
21037
|
-
const canvas = document.createElement('canvas');
|
|
21038
|
-
canvas.width = size.width;
|
|
21039
|
-
canvas.height = size.height;
|
|
21040
|
-
const context = canvas.getContext('2d');
|
|
21041
|
-
if (!context) throw new Error('Canvas indisponível para preview.');
|
|
21042
|
-
context.fillStyle = '#ffffff';
|
|
21043
|
-
context.fillRect(0, 0, size.width, size.height);
|
|
21044
|
-
context.drawImage(image, 0, 0, size.width, size.height);
|
|
21045
|
-
return canvas.toDataURL('image/jpeg', 0.78);
|
|
21046
|
-
} catch {
|
|
21047
|
-
try {
|
|
21048
|
-
const response = await fetch(sourceUrl);
|
|
21049
|
-
const blob = await response.blob();
|
|
21050
|
-
return await readComicBlobAsDataUrl(blob, mimeType);
|
|
21051
|
-
} catch {
|
|
21052
|
-
return null;
|
|
21053
|
-
}
|
|
21054
|
-
}
|
|
21055
|
-
};
|
|
21301
|
+
const readComicBlobAsDataUrl = (blob, mimeType) =>
|
|
21302
|
+
new Promise((resolve, reject) => {
|
|
21303
|
+
const reader = new FileReader();
|
|
21304
|
+
reader.onerror = () => reject(new Error('Falha ao gerar preview.'));
|
|
21305
|
+
reader.onload = () => resolve(reader.result);
|
|
21306
|
+
reader.readAsDataURL(new Blob([blob], { type: mimeType }));
|
|
21307
|
+
});
|
|
21056
21308
|
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
21061
|
-
|
|
21062
|
-
|
|
21063
|
-
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21073
|
-
|
|
21074
|
-
|
|
21075
|
-
|
|
21076
|
-
|
|
21077
|
-
|
|
21078
|
-
|
|
21079
|
-
|
|
21080
|
-
|
|
21081
|
-
|
|
21082
|
-
|
|
21083
|
-
|
|
21084
|
-
|
|
21085
|
-
|
|
21086
|
-
|
|
21087
|
-
|
|
21088
|
-
|
|
21089
|
-
|
|
21090
|
-
|
|
21091
|
-
|
|
21092
|
-
|
|
21309
|
+
const createComicPagePreview = async (pageIndex) => {
|
|
21310
|
+
const entry = comicEntries[pageIndex];
|
|
21311
|
+
if (!entry) return null;
|
|
21312
|
+
const mimeType = comicMimeType(entry.name);
|
|
21313
|
+
const sourceUrl = await ensureComicPage(pageIndex);
|
|
21314
|
+
if (!sourceUrl) return null;
|
|
21315
|
+
try {
|
|
21316
|
+
const image = new Image();
|
|
21317
|
+
image.decoding = 'async';
|
|
21318
|
+
await new Promise((resolve, reject) => {
|
|
21319
|
+
image.onload = resolve;
|
|
21320
|
+
image.onerror = () => reject(new Error('Falha ao decodificar preview.'));
|
|
21321
|
+
image.src = sourceUrl;
|
|
21322
|
+
});
|
|
21323
|
+
const size = comicHelpers.getComicPreviewSize(
|
|
21324
|
+
image.naturalWidth || image.width,
|
|
21325
|
+
image.naturalHeight || image.height
|
|
21326
|
+
);
|
|
21327
|
+
const canvas = document.createElement('canvas');
|
|
21328
|
+
canvas.width = size.width;
|
|
21329
|
+
canvas.height = size.height;
|
|
21330
|
+
const context = canvas.getContext('2d');
|
|
21331
|
+
if (!context) throw new Error('Canvas indisponível para preview.');
|
|
21332
|
+
context.fillStyle = '#ffffff';
|
|
21333
|
+
context.fillRect(0, 0, size.width, size.height);
|
|
21334
|
+
context.drawImage(image, 0, 0, size.width, size.height);
|
|
21335
|
+
return canvas.toDataURL('image/jpeg', 0.78);
|
|
21336
|
+
} catch {
|
|
21337
|
+
try {
|
|
21338
|
+
const response = await fetch(sourceUrl);
|
|
21339
|
+
const blob = await response.blob();
|
|
21340
|
+
return await readComicBlobAsDataUrl(blob, mimeType);
|
|
21341
|
+
} catch {
|
|
21342
|
+
return null;
|
|
21343
|
+
}
|
|
21344
|
+
}
|
|
21345
|
+
};
|
|
21093
21346
|
|
|
21094
|
-
|
|
21095
|
-
|
|
21096
|
-
|
|
21097
|
-
|
|
21098
|
-
|
|
21099
|
-
|
|
21100
|
-
|
|
21101
|
-
|
|
21102
|
-
|
|
21103
|
-
|
|
21104
|
-
|
|
21105
|
-
|
|
21106
|
-
|
|
21107
|
-
|
|
21108
|
-
|
|
21109
|
-
|
|
21110
|
-
|
|
21111
|
-
|
|
21347
|
+
const getComicPagePreview = async (pageIndex) => {
|
|
21348
|
+
const entry = comicEntries[pageIndex];
|
|
21349
|
+
if (!entry) return null;
|
|
21350
|
+
if (comicPreviewCache.has(pageIndex)) {
|
|
21351
|
+
return comicPreviewCache.get(pageIndex);
|
|
21352
|
+
}
|
|
21353
|
+
if (comicPreviewLoading.has(pageIndex)) {
|
|
21354
|
+
return comicPreviewLoading.get(pageIndex);
|
|
21355
|
+
}
|
|
21356
|
+
const loadGeneration = comicGeneration;
|
|
21357
|
+
const pending = createComicPagePreview(pageIndex).then((preview) => {
|
|
21358
|
+
if (
|
|
21359
|
+
!comicHelpers.isCurrentComicPageLoad(
|
|
21360
|
+
loadGeneration,
|
|
21361
|
+
comicGeneration,
|
|
21362
|
+
entry,
|
|
21363
|
+
comicEntries[pageIndex]
|
|
21364
|
+
)
|
|
21365
|
+
) {
|
|
21366
|
+
return null;
|
|
21367
|
+
}
|
|
21368
|
+
comicPreviewCache.set(pageIndex, preview);
|
|
21369
|
+
while (comicPreviewCache.size > COMIC_PREVIEW_CACHE_LIMIT) {
|
|
21370
|
+
comicPreviewCache.delete(comicPreviewCache.keys().next().value);
|
|
21371
|
+
}
|
|
21372
|
+
return preview;
|
|
21373
|
+
});
|
|
21374
|
+
comicPreviewLoading.set(pageIndex, pending);
|
|
21375
|
+
try {
|
|
21376
|
+
return await pending;
|
|
21377
|
+
} finally {
|
|
21378
|
+
if (comicPreviewLoading.get(pageIndex) === pending) {
|
|
21379
|
+
comicPreviewLoading.delete(pageIndex);
|
|
21380
|
+
}
|
|
21381
|
+
}
|
|
21382
|
+
};
|
|
21112
21383
|
|
|
21113
|
-
|
|
21114
|
-
|
|
21115
|
-
|
|
21116
|
-
|
|
21384
|
+
const getComicPageDimensions = async (pageIndex) => {
|
|
21385
|
+
const known = comicDimensions.get(pageIndex);
|
|
21386
|
+
if (known) return known;
|
|
21387
|
+
await ensureComicPage(pageIndex);
|
|
21388
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
21389
|
+
if (image && !image.complete) {
|
|
21390
|
+
await new Promise((resolve) => {
|
|
21391
|
+
image.addEventListener('load', resolve, { once: true });
|
|
21392
|
+
image.addEventListener('error', resolve, { once: true });
|
|
21393
|
+
});
|
|
21394
|
+
}
|
|
21395
|
+
return (
|
|
21396
|
+
comicDimensions.get(pageIndex) || {
|
|
21397
|
+
width: image?.naturalWidth || 0,
|
|
21398
|
+
height: image?.naturalHeight || 0,
|
|
21399
|
+
}
|
|
21400
|
+
);
|
|
21401
|
+
};
|
|
21117
21402
|
|
|
21118
|
-
|
|
21119
|
-
|
|
21120
|
-
|
|
21121
|
-
|
|
21122
|
-
};
|
|
21403
|
+
const normalizeHref = (href) => {
|
|
21404
|
+
if (!href) return '';
|
|
21405
|
+
return href.split('#')[0];
|
|
21406
|
+
};
|
|
21123
21407
|
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21408
|
+
const getSpineIndexByHref = (href) => {
|
|
21409
|
+
const normalized = normalizeHref(href);
|
|
21410
|
+
if (!normalized) return -1;
|
|
21411
|
+
return spineItems.findIndex((item) => normalizeHref(item.href) === normalized);
|
|
21412
|
+
};
|
|
21128
21413
|
|
|
21129
|
-
|
|
21130
|
-
|
|
21131
|
-
|
|
21132
|
-
|
|
21133
|
-
const outlineItem = { title, pageIndex };
|
|
21134
|
-
if (children.length > 0) outlineItem.children = children;
|
|
21135
|
-
return outlineItem;
|
|
21136
|
-
};
|
|
21414
|
+
const buildOutline = async () => {
|
|
21415
|
+
if (!book || !book.loaded || !book.loaded.navigation) return [];
|
|
21416
|
+
const nav = await book.loaded.navigation;
|
|
21417
|
+
const toc = nav && nav.toc ? nav.toc : [];
|
|
21137
21418
|
|
|
21138
|
-
|
|
21139
|
-
|
|
21419
|
+
const mapItem = (item) => {
|
|
21420
|
+
const title = item.label || item.title || '';
|
|
21421
|
+
const pageIndex = getSpineIndexByHref(item.href || '');
|
|
21422
|
+
const children = Array.isArray(item.subitems) ? item.subitems.map(mapItem) : [];
|
|
21423
|
+
const outlineItem = { title, pageIndex };
|
|
21424
|
+
if (children.length > 0) outlineItem.children = children;
|
|
21425
|
+
return outlineItem;
|
|
21426
|
+
};
|
|
21140
21427
|
|
|
21141
|
-
|
|
21142
|
-
|
|
21143
|
-
const fontSize = `${Math.round(zoom * 100)}%`;
|
|
21144
|
-
if (typeof rendition.themes.fontSize === 'function') {
|
|
21145
|
-
rendition.themes.fontSize(fontSize);
|
|
21146
|
-
} else if (typeof rendition.themes.override === 'function') {
|
|
21147
|
-
rendition.themes.override('font-size', fontSize);
|
|
21148
|
-
}
|
|
21149
|
-
};
|
|
21428
|
+
return toc.map(mapItem);
|
|
21429
|
+
};
|
|
21150
21430
|
|
|
21151
|
-
|
|
21152
|
-
|
|
21153
|
-
|
|
21154
|
-
|
|
21155
|
-
|
|
21431
|
+
const applyEpubZoom = () => {
|
|
21432
|
+
if (!rendition || !rendition.themes) return;
|
|
21433
|
+
const fontSize = `${Math.round(zoom * 100)}%`;
|
|
21434
|
+
if (typeof rendition.themes.fontSize === 'function') {
|
|
21435
|
+
rendition.themes.fontSize(fontSize);
|
|
21436
|
+
} else if (typeof rendition.themes.override === 'function') {
|
|
21437
|
+
rendition.themes.override('font-size', fontSize);
|
|
21438
|
+
}
|
|
21439
|
+
};
|
|
21156
21440
|
|
|
21157
|
-
|
|
21158
|
-
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
textContainer.style.lineHeight = '1.6';
|
|
21163
|
-
textContainer.style.whiteSpace = 'pre-wrap';
|
|
21164
|
-
textContainer.style.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
|
|
21165
|
-
viewer.appendChild(textContainer);
|
|
21166
|
-
}
|
|
21167
|
-
textContainer.textContent = text;
|
|
21168
|
-
applyTextZoom();
|
|
21169
|
-
};
|
|
21441
|
+
const applyTextZoom = () => {
|
|
21442
|
+
if (!textContainer) return;
|
|
21443
|
+
const fontSize = Math.max(12, Math.round(DEFAULT_FONT_SIZE * zoom));
|
|
21444
|
+
textContainer.style.fontSize = `${fontSize}px`;
|
|
21445
|
+
};
|
|
21170
21446
|
|
|
21171
|
-
|
|
21172
|
-
|
|
21173
|
-
|
|
21174
|
-
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21447
|
+
const renderTextPage = (pageIndex) => {
|
|
21448
|
+
const text = textPages[pageIndex] || '';
|
|
21449
|
+
if (!textContainer) {
|
|
21450
|
+
textContainer = document.createElement('div');
|
|
21451
|
+
textContainer.style.padding = '24px';
|
|
21452
|
+
textContainer.style.lineHeight = '1.6';
|
|
21453
|
+
textContainer.style.whiteSpace = 'pre-wrap';
|
|
21454
|
+
textContainer.style.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
|
|
21455
|
+
viewer.appendChild(textContainer);
|
|
21456
|
+
}
|
|
21457
|
+
textContainer.textContent = text;
|
|
21458
|
+
applyTextZoom();
|
|
21459
|
+
};
|
|
21178
21460
|
|
|
21179
|
-
|
|
21180
|
-
|
|
21181
|
-
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
} else if (source.kind === 'text') {
|
|
21187
|
-
text = source.text || '';
|
|
21188
|
-
}
|
|
21461
|
+
const paginateText = (text) => {
|
|
21462
|
+
const pages = [];
|
|
21463
|
+
for (let i = 0; i < text.length; i += TEXT_PAGE_CHUNK) {
|
|
21464
|
+
pages.push(text.slice(i, i + TEXT_PAGE_CHUNK));
|
|
21465
|
+
}
|
|
21466
|
+
return pages.length > 0 ? pages : [''];
|
|
21467
|
+
};
|
|
21189
21468
|
|
|
21190
|
-
|
|
21191
|
-
|
|
21192
|
-
|
|
21193
|
-
|
|
21194
|
-
|
|
21195
|
-
|
|
21196
|
-
|
|
21197
|
-
|
|
21469
|
+
const loadText = async (source, isCurrentLoad = () => true) => {
|
|
21470
|
+
let text = '';
|
|
21471
|
+
if (source.kind === 'uri') {
|
|
21472
|
+
const res = await fetch(source.uri);
|
|
21473
|
+
text = await res.text();
|
|
21474
|
+
} else if (source.kind === 'base64') {
|
|
21475
|
+
text = decodeBase64ToText(source.data);
|
|
21476
|
+
} else if (source.kind === 'text') {
|
|
21477
|
+
text = source.text || '';
|
|
21478
|
+
}
|
|
21198
21479
|
|
|
21199
|
-
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
|
|
21203
|
-
|
|
21204
|
-
|
|
21205
|
-
|
|
21480
|
+
if (!isCurrentLoad()) {
|
|
21481
|
+
throw new Error('Document load superseded by a newer load');
|
|
21482
|
+
}
|
|
21483
|
+
clearViewer();
|
|
21484
|
+
textPages = paginateText(text);
|
|
21485
|
+
pageCount = textPages.length;
|
|
21486
|
+
currentPage = 1;
|
|
21487
|
+
textContainer = null;
|
|
21488
|
+
renderTextPage(0);
|
|
21489
|
+
return { pageCount };
|
|
21490
|
+
};
|
|
21206
21491
|
|
|
21207
|
-
|
|
21208
|
-
|
|
21209
|
-
|
|
21210
|
-
|
|
21211
|
-
|
|
21212
|
-
|
|
21213
|
-
|
|
21214
|
-
|
|
21492
|
+
const loadEpub = async (source, loadId, generation) => {
|
|
21493
|
+
const diagnosticPayload = {
|
|
21494
|
+
loadId,
|
|
21495
|
+
sourceKind: source.kind,
|
|
21496
|
+
};
|
|
21497
|
+
const isCurrentLoad = () => generation === epubLoadGeneration;
|
|
21498
|
+
sendEpubDiagnostic('epub.load.start', diagnosticPayload);
|
|
21499
|
+
|
|
21500
|
+
if (epubInteractionCleanup) {
|
|
21501
|
+
epubInteractionCleanup();
|
|
21502
|
+
epubInteractionCleanup = null;
|
|
21503
|
+
}
|
|
21504
|
+
if (rendition && typeof rendition.destroy === 'function') {
|
|
21505
|
+
rendition.destroy();
|
|
21506
|
+
}
|
|
21507
|
+
if (book && typeof book.destroy === 'function') {
|
|
21508
|
+
book.destroy();
|
|
21509
|
+
}
|
|
21510
|
+
|
|
21511
|
+
let data = null;
|
|
21512
|
+
if (source.kind === 'uri') {
|
|
21513
|
+
data = await withEpubStageTimeout(
|
|
21514
|
+
sourceToArrayBuffer(source),
|
|
21515
|
+
'source',
|
|
21516
|
+
diagnosticPayload,
|
|
21517
|
+
);
|
|
21518
|
+
} else if (source.kind === 'base64') {
|
|
21519
|
+
// epub.js expects an ArrayBuffer for binary books. Passing the decoded
|
|
21520
|
+
// Uint8Array directly can leave `book.ready` pending in Android WebView.
|
|
21521
|
+
data = toExactArrayBuffer(decodeBase64(source.data));
|
|
21522
|
+
} else if (source.kind === 'text') {
|
|
21523
|
+
const encoder = new TextEncoder();
|
|
21524
|
+
data = encoder.encode(source.text || '').buffer;
|
|
21525
|
+
}
|
|
21526
|
+
|
|
21527
|
+
if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
|
|
21528
|
+
sendEpubDiagnostic('epub.source.received', {
|
|
21529
|
+
...diagnosticPayload,
|
|
21530
|
+
byteLength: data?.byteLength,
|
|
21531
|
+
});
|
|
21532
|
+
sendEpubDiagnostic('epub.source.decoded', {
|
|
21533
|
+
...diagnosticPayload,
|
|
21534
|
+
byteLength: data?.byteLength,
|
|
21535
|
+
});
|
|
21536
|
+
book = ePub(data);
|
|
21537
|
+
sendEpubDiagnostic('epub.book.created', {
|
|
21538
|
+
...diagnosticPayload,
|
|
21539
|
+
byteLength: data?.byteLength,
|
|
21540
|
+
});
|
|
21541
|
+
sendEpubDiagnostic('epub.book.ready.start', diagnosticPayload);
|
|
21542
|
+
await withEpubStageTimeout(book.ready, 'book.ready', diagnosticPayload);
|
|
21543
|
+
if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
|
|
21544
|
+
sendEpubDiagnostic('epub.book.ready.end', diagnosticPayload);
|
|
21545
|
+
|
|
21546
|
+
spineItems = book.spine && book.spine.items ? book.spine.items : [];
|
|
21547
|
+
pageCount = spineItems.length;
|
|
21548
|
+
currentPage = 1;
|
|
21549
|
+
sendEpubDiagnostic('epub.spine.ready', {
|
|
21550
|
+
...diagnosticPayload,
|
|
21551
|
+
pageCount,
|
|
21552
|
+
});
|
|
21553
|
+
|
|
21554
|
+
clearViewer();
|
|
21555
|
+
rendition = book.renderTo(viewer, {
|
|
21556
|
+
manager: 'continuous',
|
|
21557
|
+
width: '100%',
|
|
21558
|
+
height: '100%',
|
|
21559
|
+
flow: 'scrolled-continuous',
|
|
21560
|
+
spread: 'none',
|
|
21561
|
+
});
|
|
21562
|
+
sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
|
|
21563
|
+
|
|
21564
|
+
if (rendition && rendition.hooks && rendition.hooks.content) {
|
|
21565
|
+
rendition.hooks.content.register((contents) => {
|
|
21566
|
+
const frame = contents && contents.document ? contents.document.defaultView.frameElement : null;
|
|
21567
|
+
if (frame) {
|
|
21568
|
+
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
|
|
21569
|
+
}
|
|
21570
|
+
const selectionDocument = contents && contents.document;
|
|
21571
|
+
const selectionWindow = contents && contents.window;
|
|
21572
|
+
if (
|
|
21573
|
+
selectionDocument &&
|
|
21574
|
+
typeof selectionDocument.addEventListener === 'function' &&
|
|
21575
|
+
selectionWindow &&
|
|
21576
|
+
typeof selectionWindow.getSelection === 'function'
|
|
21577
|
+
) {
|
|
21578
|
+
const handleSelectionChange = () => {
|
|
21579
|
+
const selection = selectionWindow.getSelection();
|
|
21580
|
+
const text = selection ? selection.toString().trim() : '';
|
|
21581
|
+
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(Boolean(text));
|
|
21582
|
+
};
|
|
21583
|
+
selectionDocument.addEventListener('selectionchange', handleSelectionChange);
|
|
21584
|
+
const selectionCleanup = () => {
|
|
21585
|
+
selectionDocument.removeEventListener('selectionchange', handleSelectionChange);
|
|
21586
|
+
epubSelectionCleanups.delete(selectionCleanup);
|
|
21587
|
+
};
|
|
21588
|
+
epubSelectionCleanups.add(selectionCleanup);
|
|
21589
|
+
if (contents && typeof contents === 'object') {
|
|
21590
|
+
contents.__papyrusSelectionCleanup = selectionCleanup;
|
|
21215
21591
|
}
|
|
21592
|
+
}
|
|
21593
|
+
});
|
|
21594
|
+
}
|
|
21216
21595
|
|
|
21217
|
-
|
|
21218
|
-
|
|
21596
|
+
if (
|
|
21597
|
+
rendition &&
|
|
21598
|
+
rendition.hooks &&
|
|
21599
|
+
rendition.hooks.unloaded &&
|
|
21600
|
+
typeof rendition.hooks.unloaded.register === 'function'
|
|
21601
|
+
) {
|
|
21602
|
+
rendition.hooks.unloaded.register((view) => {
|
|
21603
|
+
const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
|
|
21604
|
+
if (typeof cleanup === 'function') cleanup();
|
|
21605
|
+
});
|
|
21606
|
+
}
|
|
21219
21607
|
|
|
21220
|
-
|
|
21221
|
-
|
|
21222
|
-
|
|
21608
|
+
if (rendition && typeof rendition.on === 'function') {
|
|
21609
|
+
rendition.on('selected', (cfiRange, contents) => {
|
|
21610
|
+
const selection = contents && contents.window ? contents.window.getSelection() : null;
|
|
21611
|
+
const text = selection ? selection.toString().trim() : '';
|
|
21612
|
+
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(Boolean(text));
|
|
21613
|
+
if (text) {
|
|
21614
|
+
sendEvent('TEXT_SELECTED', { text, pageIndex: Math.max(0, currentPage - 1) });
|
|
21615
|
+
}
|
|
21616
|
+
if (rendition && rendition.annotations && typeof rendition.annotations.remove === 'function') {
|
|
21617
|
+
rendition.annotations.remove(cfiRange, 'highlight');
|
|
21618
|
+
}
|
|
21619
|
+
});
|
|
21620
|
+
rendition.on('relocated', (location) => {
|
|
21621
|
+
const start = location && location.start;
|
|
21622
|
+
let index = start && Number.isInteger(start.index) ? start.index : -1;
|
|
21623
|
+
if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
|
|
21624
|
+
if (index < 0 || index >= spineItems.length) return;
|
|
21625
|
+
epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
|
|
21626
|
+
const nextPage = index + 1;
|
|
21627
|
+
if (nextPage === currentPage) return;
|
|
21628
|
+
currentPage = nextPage;
|
|
21629
|
+
sendState();
|
|
21630
|
+
});
|
|
21631
|
+
}
|
|
21223
21632
|
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
21227
|
-
|
|
21228
|
-
|
|
21229
|
-
|
|
21230
|
-
|
|
21633
|
+
sendEpubDiagnostic('epub.display.start', diagnosticPayload);
|
|
21634
|
+
await withEpubStageTimeout(
|
|
21635
|
+
displayEpubPage(0),
|
|
21636
|
+
'rendition.display',
|
|
21637
|
+
diagnosticPayload,
|
|
21638
|
+
);
|
|
21639
|
+
if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
|
|
21640
|
+
sendEpubDiagnostic('epub.display.end', diagnosticPayload);
|
|
21641
|
+
if (
|
|
21642
|
+
rendition &&
|
|
21643
|
+
rendition.manager &&
|
|
21644
|
+
typeof rendition.manager.on === 'function' &&
|
|
21645
|
+
typeof rendition.on === 'function'
|
|
21646
|
+
) {
|
|
21647
|
+
let touchStart = null;
|
|
21648
|
+
let lastScrollOffset = -1;
|
|
21649
|
+
const manager = rendition.manager;
|
|
21650
|
+
epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
|
|
21231
21651
|
|
|
21232
|
-
|
|
21233
|
-
|
|
21234
|
-
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
|
|
21652
|
+
// ContinuousManager schedules checks through its serial queue. Observe
|
|
21653
|
+
// the queue itself so diagnostics can distinguish queued work from the
|
|
21654
|
+
// check currently being executed without changing epub.js semantics.
|
|
21655
|
+
const originalCheck = typeof manager.check === 'function' ? manager.check : null;
|
|
21656
|
+
const queue = manager.q;
|
|
21657
|
+
const originalEnqueue = queue && typeof queue.enqueue === 'function'
|
|
21658
|
+
? queue.enqueue
|
|
21659
|
+
: null;
|
|
21660
|
+
let checkRequestId = 0;
|
|
21661
|
+
if (originalCheck) {
|
|
21662
|
+
manager.check = function (...args) {
|
|
21663
|
+
const requestId = ++checkRequestId;
|
|
21664
|
+
const checkContext = this;
|
|
21665
|
+
if (epubScrollDiagnostics) {
|
|
21666
|
+
epubScrollDiagnostics.command('manager.check.request', {
|
|
21667
|
+
requestId,
|
|
21668
|
+
queueLength: manager.q && typeof manager.q.length === 'function'
|
|
21669
|
+
? manager.q.length()
|
|
21670
|
+
: null,
|
|
21238
21671
|
});
|
|
21239
21672
|
}
|
|
21240
|
-
|
|
21241
|
-
|
|
21242
|
-
|
|
21243
|
-
|
|
21244
|
-
|
|
21245
|
-
|
|
21246
|
-
|
|
21247
|
-
|
|
21248
|
-
|
|
21249
|
-
|
|
21250
|
-
|
|
21673
|
+
epubScrollDiagnostics && epubScrollDiagnostics.command('manager.check.start', {
|
|
21674
|
+
requestId,
|
|
21675
|
+
queueLength: queue && typeof queue.length === 'function'
|
|
21676
|
+
? queue.length()
|
|
21677
|
+
: null,
|
|
21678
|
+
});
|
|
21679
|
+
let result;
|
|
21680
|
+
try {
|
|
21681
|
+
result = originalCheck.apply(checkContext, args);
|
|
21682
|
+
} catch (error) {
|
|
21683
|
+
epubScrollDiagnostics && epubScrollDiagnostics.command('manager.check.end', {
|
|
21684
|
+
requestId,
|
|
21685
|
+
status: 'error',
|
|
21686
|
+
message: error && error.message ? error.message : String(error),
|
|
21251
21687
|
});
|
|
21688
|
+
throw error;
|
|
21252
21689
|
}
|
|
21690
|
+
return Promise.resolve(result)
|
|
21691
|
+
.then(
|
|
21692
|
+
(result) => {
|
|
21693
|
+
epubScrollDiagnostics && epubScrollDiagnostics.command('manager.check.end', {
|
|
21694
|
+
requestId,
|
|
21695
|
+
status: 'ready',
|
|
21696
|
+
});
|
|
21697
|
+
return result;
|
|
21698
|
+
},
|
|
21699
|
+
(error) => {
|
|
21700
|
+
epubScrollDiagnostics && epubScrollDiagnostics.command('manager.check.end', {
|
|
21701
|
+
requestId,
|
|
21702
|
+
status: 'error',
|
|
21703
|
+
message: error && error.message ? error.message : String(error),
|
|
21704
|
+
});
|
|
21705
|
+
throw error;
|
|
21706
|
+
},
|
|
21707
|
+
);
|
|
21708
|
+
};
|
|
21709
|
+
}
|
|
21710
|
+
if (originalEnqueue) {
|
|
21711
|
+
queue.enqueue = function (...args) {
|
|
21712
|
+
const queueLengthBefore = typeof queue.length === 'function' ? queue.length() : null;
|
|
21713
|
+
const result = originalEnqueue.apply(this, args);
|
|
21714
|
+
epubScrollDiagnostics && epubScrollDiagnostics.command('manager.queue.enqueue', {
|
|
21715
|
+
queueLengthBefore,
|
|
21716
|
+
queueLengthAfter: typeof queue.length === 'function' ? queue.length() : null,
|
|
21717
|
+
running: Boolean(queue.running),
|
|
21718
|
+
});
|
|
21719
|
+
return result;
|
|
21720
|
+
};
|
|
21721
|
+
}
|
|
21722
|
+
const getTouchPoint = (event) => {
|
|
21723
|
+
const touch =
|
|
21724
|
+
(event && event.changedTouches && event.changedTouches[0]) ||
|
|
21725
|
+
(event && event.touches && event.touches[0]);
|
|
21726
|
+
if (!touch) return null;
|
|
21727
|
+
const x = Number(touch.screenX ?? touch.clientX);
|
|
21728
|
+
const y = Number(touch.screenY ?? touch.clientY);
|
|
21729
|
+
return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
|
|
21730
|
+
};
|
|
21731
|
+
const handleScroll = (event) => {
|
|
21732
|
+
const offsetY = Math.max(0, Number(event && event.top) || 0);
|
|
21733
|
+
epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
|
|
21734
|
+
if (Math.abs(offsetY - lastScrollOffset) < 1) return;
|
|
21735
|
+
lastScrollOffset = offsetY;
|
|
21736
|
+
sendEvent('VIEWER_SCROLL', { offsetY });
|
|
21737
|
+
};
|
|
21738
|
+
const handleTouchStart = (event) => {
|
|
21739
|
+
touchStart = getTouchPoint(event);
|
|
21740
|
+
epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
|
|
21741
|
+
};
|
|
21742
|
+
const handleTouchMove = (event) => {
|
|
21743
|
+
epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
|
|
21744
|
+
};
|
|
21745
|
+
const handleTouchEnd = (event) => {
|
|
21746
|
+
const end = getTouchPoint(event);
|
|
21747
|
+
const start = touchStart;
|
|
21748
|
+
touchStart = null;
|
|
21749
|
+
if (start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
|
|
21750
|
+
sendEvent('VIEWER_TAP', {});
|
|
21751
|
+
}
|
|
21752
|
+
epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
|
|
21753
|
+
};
|
|
21253
21754
|
|
|
21254
|
-
|
|
21255
|
-
|
|
21755
|
+
manager.on('scroll', handleScroll);
|
|
21756
|
+
const handleScrolled = (event) => {
|
|
21757
|
+
epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
|
|
21758
|
+
};
|
|
21759
|
+
manager.on('scrolled', handleScrolled);
|
|
21760
|
+
rendition.on('touchstart', handleTouchStart);
|
|
21761
|
+
rendition.on('touchmove', handleTouchMove);
|
|
21762
|
+
rendition.on('touchend', handleTouchEnd);
|
|
21763
|
+
epubInteractionCleanup = () => {
|
|
21764
|
+
epubSelectionCleanups.forEach((cleanup) => cleanup());
|
|
21765
|
+
epubSelectionCleanups.clear();
|
|
21766
|
+
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(false);
|
|
21767
|
+
if (originalCheck) manager.check = originalCheck;
|
|
21768
|
+
if (originalEnqueue) queue.enqueue = originalEnqueue;
|
|
21769
|
+
if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
|
|
21770
|
+
if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
|
|
21771
|
+
if (typeof rendition.off === 'function') {
|
|
21772
|
+
rendition.off('touchstart', handleTouchStart);
|
|
21773
|
+
rendition.off('touchmove', handleTouchMove);
|
|
21774
|
+
rendition.off('touchend', handleTouchEnd);
|
|
21775
|
+
}
|
|
21776
|
+
epubScrollDiagnostics = null;
|
|
21777
|
+
};
|
|
21778
|
+
}
|
|
21779
|
+
applyEpubZoom();
|
|
21256
21780
|
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
|
|
21781
|
+
const outline = await withEpubStageTimeout(
|
|
21782
|
+
buildOutline(),
|
|
21783
|
+
'outline',
|
|
21784
|
+
diagnosticPayload,
|
|
21785
|
+
);
|
|
21786
|
+
if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
|
|
21787
|
+
sendEpubDiagnostic('epub.load.ready', {
|
|
21788
|
+
...diagnosticPayload,
|
|
21789
|
+
pageCount,
|
|
21790
|
+
});
|
|
21791
|
+
return { pageCount, outline };
|
|
21792
|
+
};
|
|
21260
21793
|
|
|
21261
|
-
|
|
21262
|
-
|
|
21263
|
-
|
|
21264
|
-
|
|
21265
|
-
|
|
21266
|
-
|
|
21267
|
-
|
|
21268
|
-
|
|
21269
|
-
|
|
21794
|
+
const displayEpubPage = async (pageIndex) => {
|
|
21795
|
+
if (!rendition) return;
|
|
21796
|
+
const item = spineItems[pageIndex];
|
|
21797
|
+
if (!item) return;
|
|
21798
|
+
const target = item.href || item.idref || item.cfiBase || pageIndex;
|
|
21799
|
+
epubScrollDiagnostics && epubScrollDiagnostics.command('display', {
|
|
21800
|
+
target,
|
|
21801
|
+
pageIndex,
|
|
21802
|
+
});
|
|
21803
|
+
await rendition.display(target);
|
|
21804
|
+
currentPage = pageIndex + 1;
|
|
21805
|
+
sendState();
|
|
21806
|
+
};
|
|
21270
21807
|
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
21279
|
-
|
|
21280
|
-
|
|
21281
|
-
|
|
21282
|
-
|
|
21808
|
+
const getTextContent = async (pageIndex) => {
|
|
21809
|
+
if (currentType === 'text') {
|
|
21810
|
+
const text = textPages[pageIndex] || '';
|
|
21811
|
+
return [{
|
|
21812
|
+
str: text,
|
|
21813
|
+
dir: 'ltr',
|
|
21814
|
+
width: 0,
|
|
21815
|
+
height: 0,
|
|
21816
|
+
transform: [1, 0, 0, 1, 0, 0],
|
|
21817
|
+
fontName: 'default',
|
|
21818
|
+
}];
|
|
21819
|
+
}
|
|
21283
21820
|
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21297
|
-
|
|
21298
|
-
|
|
21299
|
-
|
|
21300
|
-
|
|
21301
|
-
|
|
21302
|
-
|
|
21303
|
-
|
|
21821
|
+
if (currentType === 'epub') {
|
|
21822
|
+
if (!book) return [];
|
|
21823
|
+
const item = spineItems[pageIndex];
|
|
21824
|
+
if (!item) return [];
|
|
21825
|
+
try {
|
|
21826
|
+
const section = book.spine.get(item.idref || item.href);
|
|
21827
|
+
const text = section && typeof section.text === 'function' ? await section.text() : '';
|
|
21828
|
+
if (!text) return [];
|
|
21829
|
+
return [{
|
|
21830
|
+
str: text,
|
|
21831
|
+
dir: 'ltr',
|
|
21832
|
+
width: 0,
|
|
21833
|
+
height: 0,
|
|
21834
|
+
transform: [1, 0, 0, 1, 0, 0],
|
|
21835
|
+
fontName: 'default',
|
|
21836
|
+
}];
|
|
21837
|
+
} catch (err) {
|
|
21838
|
+
return [];
|
|
21839
|
+
}
|
|
21840
|
+
}
|
|
21304
21841
|
|
|
21305
|
-
|
|
21306
|
-
|
|
21842
|
+
return [];
|
|
21843
|
+
};
|
|
21307
21844
|
|
|
21308
|
-
|
|
21309
|
-
|
|
21310
|
-
|
|
21311
|
-
|
|
21845
|
+
const getPageText = async (pageIndex) => {
|
|
21846
|
+
const items = await getTextContent(pageIndex);
|
|
21847
|
+
return items.map((item) => item.str).join(' ');
|
|
21848
|
+
};
|
|
21312
21849
|
|
|
21313
|
-
|
|
21314
|
-
|
|
21315
|
-
|
|
21850
|
+
const searchText = async (query) => {
|
|
21851
|
+
const normalized = query.toLowerCase();
|
|
21852
|
+
const results = [];
|
|
21316
21853
|
|
|
21317
|
-
|
|
21318
|
-
|
|
21319
|
-
|
|
21320
|
-
|
|
21321
|
-
|
|
21322
|
-
|
|
21323
|
-
|
|
21324
|
-
|
|
21325
|
-
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
|
|
21329
|
-
|
|
21330
|
-
|
|
21854
|
+
for (let i = 0; i < pageCount; i += 1) {
|
|
21855
|
+
const text = await getPageText(i);
|
|
21856
|
+
if (!text) continue;
|
|
21857
|
+
const lower = text.toLowerCase();
|
|
21858
|
+
let pos = lower.indexOf(normalized, 0);
|
|
21859
|
+
let matchIndex = 0;
|
|
21860
|
+
while (pos !== -1) {
|
|
21861
|
+
const start = Math.max(0, pos - 20);
|
|
21862
|
+
const end = Math.min(text.length, pos + query.length + 20);
|
|
21863
|
+
results.push({ pageIndex: i, text: text.substring(start, end), matchIndex });
|
|
21864
|
+
matchIndex += 1;
|
|
21865
|
+
pos = lower.indexOf(normalized, pos + 1);
|
|
21866
|
+
}
|
|
21867
|
+
}
|
|
21331
21868
|
|
|
21332
|
-
|
|
21333
|
-
|
|
21869
|
+
return results;
|
|
21870
|
+
};
|
|
21334
21871
|
|
|
21335
|
-
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
|
|
21872
|
+
const getPageDimensions = () => ({
|
|
21873
|
+
width: viewer.clientWidth || 0,
|
|
21874
|
+
height: viewer.clientHeight || 0,
|
|
21875
|
+
});
|
|
21339
21876
|
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21877
|
+
const getPageIndex = (dest) => {
|
|
21878
|
+
if (!dest) return null;
|
|
21879
|
+
if (typeof dest === 'string') return getSpineIndexByHref(dest);
|
|
21880
|
+
if (typeof dest !== 'object') return null;
|
|
21881
|
+
if (dest.kind === 'href') return getSpineIndexByHref(dest.value);
|
|
21882
|
+
if (dest.kind === 'pageIndex') return dest.value;
|
|
21883
|
+
if (dest.kind === 'pageNumber') return Math.max(0, dest.value - 1);
|
|
21884
|
+
return null;
|
|
21885
|
+
};
|
|
21349
21886
|
|
|
21350
|
-
|
|
21351
|
-
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21887
|
+
const selectText = async (pageIndex) => {
|
|
21888
|
+
const text = await getPageText(pageIndex);
|
|
21889
|
+
if (!text) return null;
|
|
21890
|
+
return {
|
|
21891
|
+
text,
|
|
21892
|
+
rects: [{ x: 0, y: 0, width: 1, height: 1 }],
|
|
21893
|
+
};
|
|
21894
|
+
};
|
|
21358
21895
|
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
|
|
21366
|
-
|
|
21367
|
-
|
|
21896
|
+
const applyZoom = () => {
|
|
21897
|
+
if (currentType === 'text') {
|
|
21898
|
+
applyTextZoom();
|
|
21899
|
+
} else if (currentType === 'epub') {
|
|
21900
|
+
applyEpubZoom();
|
|
21901
|
+
} else if (currentType === 'comic') {
|
|
21902
|
+
applyComicLayout();
|
|
21903
|
+
}
|
|
21904
|
+
};
|
|
21368
21905
|
|
|
21369
|
-
|
|
21370
|
-
|
|
21906
|
+
const handleCommand = async (message) => {
|
|
21907
|
+
const { id, kind, payload } = message;
|
|
21371
21908
|
|
|
21372
|
-
|
|
21373
|
-
|
|
21374
|
-
|
|
21375
|
-
|
|
21909
|
+
try {
|
|
21910
|
+
if (kind === 'load') {
|
|
21911
|
+
const generation = (epubLoadGeneration += 1);
|
|
21912
|
+
currentType = payload.type;
|
|
21913
|
+
zoom = 1.0;
|
|
21376
21914
|
|
|
21377
|
-
|
|
21378
|
-
|
|
21379
|
-
|
|
21380
|
-
|
|
21381
|
-
|
|
21382
|
-
|
|
21915
|
+
if (currentType === 'text') {
|
|
21916
|
+
const result = await loadText(
|
|
21917
|
+
payload.source,
|
|
21918
|
+
() => generation === epubLoadGeneration,
|
|
21919
|
+
);
|
|
21920
|
+
if (generation !== epubLoadGeneration) {
|
|
21921
|
+
throw new Error('Document load superseded by a newer load');
|
|
21922
|
+
}
|
|
21923
|
+
sendEvent('document.ready', {
|
|
21924
|
+
loadId: id,
|
|
21925
|
+
pageCount: result.pageCount,
|
|
21926
|
+
});
|
|
21927
|
+
sendState();
|
|
21928
|
+
sendResponse(id, true, result);
|
|
21929
|
+
return;
|
|
21930
|
+
}
|
|
21383
21931
|
|
|
21384
|
-
|
|
21385
|
-
|
|
21386
|
-
|
|
21387
|
-
|
|
21388
|
-
|
|
21389
|
-
|
|
21932
|
+
if (currentType === 'epub') {
|
|
21933
|
+
const result = await loadEpub(payload.source, id, generation);
|
|
21934
|
+
sendEvent('document.ready', {
|
|
21935
|
+
loadId: id,
|
|
21936
|
+
pageCount: result.pageCount,
|
|
21937
|
+
});
|
|
21938
|
+
sendState({ outline: result.outline || [] });
|
|
21939
|
+
sendResponse(id, true, result);
|
|
21940
|
+
return;
|
|
21941
|
+
}
|
|
21390
21942
|
|
|
21391
|
-
|
|
21392
|
-
|
|
21393
|
-
|
|
21394
|
-
|
|
21395
|
-
|
|
21396
|
-
|
|
21943
|
+
if (currentType === 'comic') {
|
|
21944
|
+
const result = await loadComic(payload.source, payload.format || 'cbz');
|
|
21945
|
+
sendState();
|
|
21946
|
+
sendResponse(id, true, result);
|
|
21947
|
+
return;
|
|
21948
|
+
}
|
|
21397
21949
|
|
|
21398
|
-
|
|
21399
|
-
|
|
21950
|
+
throw new Error('Unsupported document type');
|
|
21951
|
+
}
|
|
21400
21952
|
|
|
21401
|
-
|
|
21402
|
-
|
|
21403
|
-
|
|
21404
|
-
|
|
21405
|
-
|
|
21406
|
-
|
|
21407
|
-
|
|
21408
|
-
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
|
|
21416
|
-
|
|
21417
|
-
|
|
21418
|
-
|
|
21419
|
-
|
|
21420
|
-
|
|
21421
|
-
|
|
21953
|
+
if (kind === 'go-to-page') {
|
|
21954
|
+
const page = Math.max(1, payload.page || 1);
|
|
21955
|
+
if (currentType === 'text') {
|
|
21956
|
+
currentPage = page;
|
|
21957
|
+
renderTextPage(page - 1);
|
|
21958
|
+
sendState();
|
|
21959
|
+
} else if (currentType === 'epub') {
|
|
21960
|
+
await displayEpubPage(page - 1);
|
|
21961
|
+
} else if (currentType === 'comic') {
|
|
21962
|
+
currentPage = page;
|
|
21963
|
+
const target = comicPages[page - 1];
|
|
21964
|
+
if (target) {
|
|
21965
|
+
await ensureComicPage(page - 1);
|
|
21966
|
+
target.scrollIntoView({ block: 'start' });
|
|
21967
|
+
renderComicPage(page - 1);
|
|
21968
|
+
sendState();
|
|
21969
|
+
}
|
|
21970
|
+
}
|
|
21971
|
+
sendResponse(id, true, { currentPage });
|
|
21972
|
+
return;
|
|
21973
|
+
}
|
|
21422
21974
|
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
|
|
21975
|
+
if (kind === 'set-zoom') {
|
|
21976
|
+
zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
|
|
21977
|
+
applyZoom();
|
|
21978
|
+
sendState();
|
|
21979
|
+
sendResponse(id, true, { zoom });
|
|
21980
|
+
return;
|
|
21981
|
+
}
|
|
21430
21982
|
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
|
|
21983
|
+
if (kind === 'set-rotation') {
|
|
21984
|
+
rotation = ((payload.rotation || 0) % 360 + 360) % 360;
|
|
21985
|
+
if (currentType === 'comic') applyComicLayout();
|
|
21986
|
+
sendResponse(id, true, {});
|
|
21987
|
+
return;
|
|
21988
|
+
}
|
|
21437
21989
|
|
|
21438
|
-
|
|
21439
|
-
|
|
21440
|
-
|
|
21441
|
-
|
|
21442
|
-
|
|
21990
|
+
if (kind === 'get-text-content') {
|
|
21991
|
+
const items = await getTextContent(payload.pageIndex || 0);
|
|
21992
|
+
sendResponse(id, true, items);
|
|
21993
|
+
return;
|
|
21994
|
+
}
|
|
21443
21995
|
|
|
21444
|
-
|
|
21445
|
-
|
|
21446
|
-
|
|
21447
|
-
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
|
|
21455
|
-
|
|
21996
|
+
if (kind === 'get-page-dimensions') {
|
|
21997
|
+
if (currentType === 'comic') {
|
|
21998
|
+
sendResponse(
|
|
21999
|
+
id,
|
|
22000
|
+
true,
|
|
22001
|
+
await getComicPageDimensions(payload.pageIndex || 0)
|
|
22002
|
+
);
|
|
22003
|
+
} else {
|
|
22004
|
+
sendResponse(id, true, getPageDimensions());
|
|
22005
|
+
}
|
|
22006
|
+
return;
|
|
22007
|
+
}
|
|
21456
22008
|
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
|
|
21460
|
-
|
|
21461
|
-
|
|
21462
|
-
|
|
21463
|
-
|
|
21464
|
-
|
|
21465
|
-
|
|
21466
|
-
|
|
21467
|
-
|
|
21468
|
-
|
|
22009
|
+
if (kind === 'get-page-preview') {
|
|
22010
|
+
if (currentType !== 'comic') {
|
|
22011
|
+
sendResponse(id, true, null);
|
|
22012
|
+
return;
|
|
22013
|
+
}
|
|
22014
|
+
sendResponse(
|
|
22015
|
+
id,
|
|
22016
|
+
true,
|
|
22017
|
+
await getComicPagePreview(payload.pageIndex || 0)
|
|
22018
|
+
);
|
|
22019
|
+
return;
|
|
22020
|
+
}
|
|
21469
22021
|
|
|
21470
|
-
|
|
21471
|
-
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
|
|
22022
|
+
if (kind === 'search-text') {
|
|
22023
|
+
const results = await searchText(payload.query || '');
|
|
22024
|
+
sendResponse(id, true, results);
|
|
22025
|
+
return;
|
|
22026
|
+
}
|
|
21475
22027
|
|
|
21476
|
-
|
|
21477
|
-
|
|
21478
|
-
|
|
21479
|
-
|
|
21480
|
-
|
|
22028
|
+
if (kind === 'select-text') {
|
|
22029
|
+
const selection = await selectText(payload.pageIndex || 0);
|
|
22030
|
+
sendResponse(id, true, selection);
|
|
22031
|
+
return;
|
|
22032
|
+
}
|
|
21481
22033
|
|
|
21482
|
-
|
|
21483
|
-
|
|
21484
|
-
|
|
21485
|
-
|
|
21486
|
-
|
|
22034
|
+
if (kind === 'get-outline') {
|
|
22035
|
+
const outline = await buildOutline();
|
|
22036
|
+
sendResponse(id, true, outline);
|
|
22037
|
+
return;
|
|
22038
|
+
}
|
|
21487
22039
|
|
|
21488
|
-
|
|
21489
|
-
|
|
21490
|
-
|
|
21491
|
-
|
|
21492
|
-
|
|
22040
|
+
if (kind === 'get-page-index') {
|
|
22041
|
+
const index = getPageIndex(payload.dest);
|
|
22042
|
+
sendResponse(id, true, index);
|
|
22043
|
+
return;
|
|
22044
|
+
}
|
|
21493
22045
|
|
|
21494
|
-
|
|
21495
|
-
|
|
21496
|
-
|
|
21497
|
-
|
|
21498
|
-
|
|
22046
|
+
if (kind === 'destroy') {
|
|
22047
|
+
clearViewer();
|
|
22048
|
+
sendResponse(id, true, {});
|
|
22049
|
+
return;
|
|
22050
|
+
}
|
|
21499
22051
|
|
|
21500
|
-
|
|
21501
|
-
|
|
21502
|
-
|
|
21503
|
-
|
|
21504
|
-
|
|
22052
|
+
sendResponse(id, false, null, 'Unknown command');
|
|
22053
|
+
} catch (err) {
|
|
22054
|
+
const errorMessage = err && err.message ? err.message : String(err);
|
|
22055
|
+
const isSuperseded = /superseded by a newer load/i.test(errorMessage);
|
|
22056
|
+
if (kind === 'load' && isSuperseded) {
|
|
22057
|
+
sendEvent('document.stale', { loadId: id, error: errorMessage });
|
|
22058
|
+
} else if (kind === 'load') {
|
|
22059
|
+
sendEvent('document.error', {
|
|
22060
|
+
loadId: id,
|
|
22061
|
+
error: { message: errorMessage, stack: err?.stack || null },
|
|
22062
|
+
});
|
|
22063
|
+
}
|
|
22064
|
+
if (kind === 'load' && payload?.type === 'epub') {
|
|
22065
|
+
sendEpubDiagnostic('epub.load.error', {
|
|
22066
|
+
loadId: id,
|
|
22067
|
+
sourceKind: payload.source?.kind,
|
|
22068
|
+
error: { message: errorMessage, stack: err?.stack || null },
|
|
22069
|
+
});
|
|
22070
|
+
sendEvent('document.error', {
|
|
22071
|
+
loadId: id,
|
|
22072
|
+
error: { message: errorMessage, stack: err?.stack || null },
|
|
22073
|
+
});
|
|
22074
|
+
}
|
|
22075
|
+
sendResponse(id, false, null, errorMessage);
|
|
22076
|
+
}
|
|
22077
|
+
};
|
|
21505
22078
|
|
|
21506
|
-
|
|
21507
|
-
|
|
21508
|
-
|
|
22079
|
+
const onMessage = (event) => {
|
|
22080
|
+
let message = null;
|
|
22081
|
+
const raw = event && typeof event === 'object' ? event.data : null;
|
|
21509
22082
|
|
|
21510
|
-
|
|
21511
|
-
|
|
21512
|
-
|
|
21513
|
-
|
|
21514
|
-
|
|
21515
|
-
|
|
21516
|
-
|
|
21517
|
-
|
|
21518
|
-
|
|
21519
|
-
|
|
21520
|
-
|
|
21521
|
-
|
|
21522
|
-
|
|
21523
|
-
|
|
21524
|
-
|
|
21525
|
-
|
|
22083
|
+
if (raw && typeof raw === 'object' && (raw.kind || raw.type) && raw.id) {
|
|
22084
|
+
message = raw;
|
|
22085
|
+
} else if (typeof raw === 'string') {
|
|
22086
|
+
if (raw.startsWith('setImmediate$')) return;
|
|
22087
|
+
const trimmed = raw.trim();
|
|
22088
|
+
if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('['))) {
|
|
22089
|
+
return;
|
|
22090
|
+
}
|
|
22091
|
+
try {
|
|
22092
|
+
message = JSON.parse(trimmed);
|
|
22093
|
+
} catch (err) {
|
|
22094
|
+
return;
|
|
22095
|
+
}
|
|
22096
|
+
} else {
|
|
22097
|
+
return;
|
|
22098
|
+
}
|
|
21526
22099
|
|
|
21527
|
-
|
|
22100
|
+
if (!message || message.id == null) return;
|
|
21528
22101
|
|
|
21529
|
-
|
|
21530
|
-
|
|
21531
|
-
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
|
|
21535
|
-
|
|
21536
|
-
|
|
21537
|
-
|
|
22102
|
+
if (message.type === 'asset-response') {
|
|
22103
|
+
const pending = runtimeAssetRequests.get(message.id);
|
|
22104
|
+
if (!pending) return;
|
|
22105
|
+
runtimeAssetRequests.delete(message.id);
|
|
22106
|
+
clearTimeout(pending.timeout);
|
|
22107
|
+
if (message.ok) pending.resolve(message.data);
|
|
22108
|
+
else pending.reject(new Error(message.error || 'Falha ao carregar asset CBR.'));
|
|
22109
|
+
return;
|
|
22110
|
+
}
|
|
21538
22111
|
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
22112
|
+
if (message.type === 'file-chunk-response') {
|
|
22113
|
+
const pending = fileChunkRequests.get(message.id);
|
|
22114
|
+
if (!pending) return;
|
|
22115
|
+
fileChunkRequests.delete(message.id);
|
|
22116
|
+
clearTimeout(pending.timeout);
|
|
22117
|
+
if (message.ok) pending.resolve(message);
|
|
22118
|
+
else pending.reject(new Error(message.error || 'Falha ao ler arquivo local.'));
|
|
22119
|
+
return;
|
|
22120
|
+
}
|
|
21548
22121
|
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
22122
|
+
if (!message.kind) return;
|
|
22123
|
+
handleCommand(message);
|
|
22124
|
+
};
|
|
21552
22125
|
|
|
21553
|
-
|
|
21554
|
-
|
|
22126
|
+
window.addEventListener('message', onMessage);
|
|
22127
|
+
document.addEventListener('message', onMessage);
|
|
21555
22128
|
|
|
21556
|
-
|
|
21557
|
-
|
|
22129
|
+
sendMessage({ type: 'ready' });
|
|
22130
|
+
})();
|
|
21558
22131
|
</script>
|
|
21559
22132
|
</body>
|
|
21560
22133
|
</html>
|