@papyrus-sdk/ui-react-native 0.2.35-beta.9 → 0.2.35
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/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +520 -229
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +533 -241
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/runtime/comicRuntime.test.ts +9 -9
- package/runtime/epubAnnotations.js +7 -4
- package/runtime/epubAnnotations.test.ts +6 -2
- package/runtime/epubReader.js +58 -0
- package/runtime/epubReader.test.ts +74 -0
- package/runtime/epubRuntime.test.ts +48 -0
- package/runtime/index.html +224 -79
- package/runtime/runtime.js +224 -79
- package/runtime/syncComicRuntime.mjs +6 -3
package/runtime/index.html
CHANGED
|
@@ -20483,6 +20483,67 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20483
20483
|
<!-- @papyrus-comic-runtime:end -->
|
|
20484
20484
|
<script>
|
|
20485
20485
|
(function () {
|
|
20486
|
+
/* @papyrus-epub-reader:start */
|
|
20487
|
+
(function(root){
|
|
20488
|
+
function mode(value){return value==='chapter-scroll'?'chapter-scroll':'paged';}
|
|
20489
|
+
function validCfi(value){return typeof value==='string' && value.length<=4096 && /^epubcfi\([^\n<>]+\)$/.test(value);}
|
|
20490
|
+
function options(value){return {manager:'default',flow:mode(value)==='paged'?'paginated':'scrolled-doc',spread:'none',width:'100%',height:'100%',allowScriptedContent:false};}
|
|
20491
|
+
function gesture(start,end,state){
|
|
20492
|
+
if(!start || !end || state.selected || state.interactive || state.multitouch)return null;
|
|
20493
|
+
var dx=end.x-start.x,dy=end.y-start.y,elapsed=end.time-start.time;
|
|
20494
|
+
if(state.mode==='paged' && Math.abs(dx)>=48 && Math.abs(dx)>Math.abs(dy)*1.5 && elapsed>=0 && elapsed<1500)return (dx<0)!==Boolean(state.rtl)?'next':'prev';
|
|
20495
|
+
if(elapsed>=0 && elapsed<450 && Math.hypot(dx,dy)<=12)return 'tap';
|
|
20496
|
+
return null;
|
|
20497
|
+
}
|
|
20498
|
+
function location(raw,count,readingMode,geometry){
|
|
20499
|
+
var start=raw && raw.start;if(!start || !validCfi(start.cfi))return null;
|
|
20500
|
+
var displayed=start.displayed||{},chapter=Math.max(1,(Number(start.index)||0)+1);
|
|
20501
|
+
var visualPage=mode(readingMode)==='paged' && Number.isFinite(displayed.page)?displayed.page:null;
|
|
20502
|
+
var total=mode(readingMode)==='paged' && Number.isFinite(displayed.total)?displayed.total:null;
|
|
20503
|
+
var atStart=Boolean(raw.atStart),atEnd=Boolean(raw.atEnd);
|
|
20504
|
+
// Chromium may round a column offset down by a fraction of a CSS pixel.
|
|
20505
|
+
// Keep epub.js authoritative outside a 1px boundary, and for RTL layouts.
|
|
20506
|
+
if(visualPage!==null && total>0 && geometry && geometry.direction==='ltr' && geometry.pageWidth>0 && Number.isFinite(geometry.offset)){
|
|
20507
|
+
var boundary=Math.round(geometry.offset/geometry.pageWidth);
|
|
20508
|
+
if(Math.abs(geometry.offset-boundary*geometry.pageWidth)<=1){
|
|
20509
|
+
visualPage=Math.min(total,Math.max(1,boundary+1));
|
|
20510
|
+
atStart=chapter===(geometry.firstIndex ?? 0)+1 && visualPage===1;
|
|
20511
|
+
atEnd=chapter===(geometry.lastIndex ?? count-1)+1 && visualPage===total;
|
|
20512
|
+
}
|
|
20513
|
+
}
|
|
20514
|
+
var fraction=total?Math.max(0,(visualPage-1)/total):0;
|
|
20515
|
+
return {version:1,cfi:start.cfi,href:start.href||'',chapter:chapter,chapterCount:count,visualPage:visualPage,visualPageCount:total,progress:Math.min(1,Math.max(0,(chapter-1+fraction)/Math.max(1,count))),mode:mode(readingMode),atStart:atStart,atEnd:atEnd};
|
|
20516
|
+
}
|
|
20517
|
+
function installResizeAnchor(manager,readCfi){
|
|
20518
|
+
var original=manager.resize,anchor=null;
|
|
20519
|
+
function resize(width,height,target){
|
|
20520
|
+
if(validCfi(target))anchor=target;
|
|
20521
|
+
if(!anchor){var current=readCfi();if(validCfi(current))anchor=current;}
|
|
20522
|
+
// epub.js accepts a logical target as its third argument. Keep it across
|
|
20523
|
+
// keyboard/dialog reflows instead of adopting each new viewport start.
|
|
20524
|
+
return original.call(this,width,height,anchor || target);
|
|
20525
|
+
}
|
|
20526
|
+
manager.resize=resize;
|
|
20527
|
+
return {cfi:function(){return anchor;},invalidate:function(){anchor=null;},destroy:function(){if(manager.resize===resize)manager.resize=original;anchor=null;}};
|
|
20528
|
+
}
|
|
20529
|
+
function install(contents,config){
|
|
20530
|
+
var doc=contents.document,win=contents.window,start=null,blocked=false;
|
|
20531
|
+
function point(e){var t=e.changedTouches&&e.changedTouches[0]||e.touches&&e.touches[0];return t?{x:t.clientX,y:t.clientY,time:Date.now()}:null;}
|
|
20532
|
+
function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
|
|
20533
|
+
function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
|
|
20534
|
+
function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
|
|
20535
|
+
function invalidateScroll(finish){if(config.current() && start && finish && config.mode()==='chapter-scroll' && !blocked && !selected() && Math.abs(finish.y-start.y)>12 && Math.abs(finish.y-start.y)>Math.abs(finish.x-start.x) && config.interact)config.interact();}
|
|
20536
|
+
function move(e){if(e.touches.length!==1)blocked=true;invalidateScroll(point(e));}
|
|
20537
|
+
function end(e){var finish=point(e);invalidateScroll(finish);var action=gesture(start,finish,{mode:config.mode(),rtl:config.rtl(),selected:selected(),interactive:blocked||interactive(e.target)||Boolean(config.annotationHit && config.annotationHit(point(e))),multitouch:blocked});start=null;if(!config.current())return;if(action==='tap')config.tap();else if(action)config.turn(action==='next'?1:-1);}
|
|
20538
|
+
function cancel(){start=null;blocked=true;}
|
|
20539
|
+
function link(e){if(config.current() && e.target?.closest?.('a') && config.interact)config.interact();}
|
|
20540
|
+
doc.addEventListener('touchstart',begin,{passive:true});doc.addEventListener('touchmove',move,{passive:true});doc.addEventListener('touchend',end,{passive:true});doc.addEventListener('touchcancel',cancel,{passive:true});doc.addEventListener('click',link);
|
|
20541
|
+
return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);doc.removeEventListener('click',link);};
|
|
20542
|
+
}
|
|
20543
|
+
root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,installResizeAnchor:installResizeAnchor,install:install};
|
|
20544
|
+
})(typeof window==='undefined'?globalThis:window);
|
|
20545
|
+
|
|
20546
|
+
/* @papyrus-epub-reader:end */
|
|
20486
20547
|
/* @papyrus-epub-annotations:start */
|
|
20487
20548
|
(function (root, factory) {
|
|
20488
20549
|
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
@@ -20561,6 +20622,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20561
20622
|
var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
|
|
20562
20623
|
var style = annotation.markupStyle || annotation.type;
|
|
20563
20624
|
var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
|
|
20625
|
+
if(style === 'highlight')opacity=Math.min(0.35,opacity);
|
|
20564
20626
|
rects.forEach(function(rect) {
|
|
20565
20627
|
var mark = doc.createElement('div');
|
|
20566
20628
|
var bodyRect=doc.body.getBoundingClientRect();
|
|
@@ -20573,8 +20635,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20573
20635
|
if (hasNote) {
|
|
20574
20636
|
var last = rects[rects.length-1], button = doc.createElement('button');
|
|
20575
20637
|
button.textContent = '●'; button.setAttribute('aria-label',options.labels().comment || '');
|
|
20576
|
-
|
|
20577
|
-
var
|
|
20638
|
+
var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
|
|
20639
|
+
var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
|
|
20640
|
+
button.style.cssText = 'position:absolute;pointer-events:auto;border:0;background:transparent;color:'+color+';width:44px;height:44px;left:'+((column+1)*pageWidth-44)+'px;top:'+(Math.floor(last.top/44)*44-doc.body.getBoundingClientRect().top)+'px';
|
|
20641
|
+
var key = column+":"+Math.floor(last.top/44);
|
|
20578
20642
|
var neighbors=noteGroups.get(key);
|
|
20579
20643
|
if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
|
|
20580
20644
|
neighbors.ids.push(annotation.id);
|
|
@@ -20595,10 +20659,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20595
20659
|
doc.addEventListener('click',tapMarkup);
|
|
20596
20660
|
var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
|
|
20597
20661
|
if (observer) observer.observe(doc.body);
|
|
20598
|
-
win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
|
|
20662
|
+
win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
|
|
20599
20663
|
if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
|
|
20600
20664
|
schedule();
|
|
20601
|
-
return {schedule:schedule, range:function(anchor){return resolveRange(contents,anchor);}, destroy:function(){alive=false;if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',schedule);doc.removeEventListener('load',schedule,true);doc.removeEventListener('click',tapMarkup);overlay.remove();doc.body.style.position=originalPosition;registry.clear();}};
|
|
20665
|
+
return {schedule:schedule, hit:function(x,y){return hitRegions.some(function(item){return item.rects.some(function(r){return x>=r.left && x<=r.right && y>=r.top && y<=r.bottom;});});}, range:function(anchor){return resolveRange(contents,anchor);}, destroy:function(){alive=false;if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',schedule);win.removeEventListener('scroll',schedule);doc.removeEventListener('load',schedule,true);doc.removeEventListener('click',tapMarkup);overlay.remove();doc.body.style.position=originalPosition;registry.clear();}};
|
|
20602
20666
|
}
|
|
20603
20667
|
return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
|
|
20604
20668
|
});
|
|
@@ -20611,6 +20675,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20611
20675
|
let currentType = null;
|
|
20612
20676
|
let book = null;
|
|
20613
20677
|
let rendition = null;
|
|
20678
|
+
let epubResizeAnchor = null;
|
|
20614
20679
|
let documentSessionId = null;
|
|
20615
20680
|
let annotationDocumentId;
|
|
20616
20681
|
let annotationLabels = {};
|
|
@@ -21023,6 +21088,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21023
21088
|
};
|
|
21024
21089
|
|
|
21025
21090
|
const clearViewer = () => {
|
|
21091
|
+
if(epubResizeAnchor){epubResizeAnchor.destroy();epubResizeAnchor=null;}
|
|
21026
21092
|
epubAnnotationRenderers.forEach(renderer => renderer.destroy());
|
|
21027
21093
|
epubAnnotationRenderers.clear();
|
|
21028
21094
|
if (epubInteractionCleanup) {
|
|
@@ -21679,26 +21745,57 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21679
21745
|
pageCount,
|
|
21680
21746
|
});
|
|
21681
21747
|
|
|
21748
|
+
if (book.locations?.generate) {
|
|
21749
|
+
const activeBook=book;
|
|
21750
|
+
activeBook.locations.generate(150).then(() => {
|
|
21751
|
+
if (isCurrentLoad() && activeBook===book && epubLocation) {
|
|
21752
|
+
epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
|
|
21753
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
21754
|
+
}
|
|
21755
|
+
}).catch(() => {});
|
|
21756
|
+
}
|
|
21757
|
+
return renderEpubRendition(loadId,generation,null);
|
|
21758
|
+
};
|
|
21759
|
+
|
|
21760
|
+
const renderEpubRendition = async (loadId,generation,target) => {
|
|
21761
|
+
const diagnosticPayload={loadId};
|
|
21762
|
+
const isCurrentLoad=()=>generation===epubLoadGeneration;
|
|
21763
|
+
if(epubInteractionCleanup)epubInteractionCleanup();
|
|
21764
|
+
if(rendition)rendition.destroy();
|
|
21682
21765
|
clearViewer();
|
|
21683
|
-
rendition = book.renderTo(viewer,
|
|
21684
|
-
manager: 'continuous',
|
|
21685
|
-
allowScriptedContent: false,
|
|
21686
|
-
width: '100%',
|
|
21687
|
-
height: '100%',
|
|
21688
|
-
flow: 'scrolled-continuous',
|
|
21689
|
-
spread: 'none',
|
|
21690
|
-
});
|
|
21766
|
+
rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
|
|
21691
21767
|
sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
|
|
21692
21768
|
|
|
21693
21769
|
if (rendition && rendition.hooks && rendition.hooks.content) {
|
|
21694
21770
|
const session = documentSessionId;
|
|
21695
21771
|
const activeRendition = rendition;
|
|
21696
21772
|
rendition.hooks.content.register((contents) => {
|
|
21773
|
+
const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
|
|
21774
|
+
mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
|
|
21775
|
+
rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
|
|
21776
|
+
blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
|
|
21777
|
+
annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
|
|
21778
|
+
current: () => session === documentSessionId && activeRendition === rendition,
|
|
21779
|
+
interact: () => epubResizeAnchor?.invalidate(),
|
|
21780
|
+
tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
|
|
21781
|
+
turn: direction => {
|
|
21782
|
+
epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
|
|
21783
|
+
if(session !== documentSessionId || activeRendition !== rendition)return;
|
|
21784
|
+
epubResizeAnchor?.invalidate();
|
|
21785
|
+
return direction<0?activeRendition.prev():activeRendition.next();
|
|
21786
|
+
});
|
|
21787
|
+
epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
|
|
21788
|
+
},
|
|
21789
|
+
});
|
|
21790
|
+
const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
|
|
21791
|
+
epubSelectionCleanups.add(gestureCleanup);
|
|
21792
|
+
contents.__papyrusGestureCleanup=gestureCleanup;
|
|
21697
21793
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
21698
21794
|
if (href && window.PapyrusEpubAnnotations) {
|
|
21699
21795
|
const renderer = window.PapyrusEpubAnnotations.install(contents, {
|
|
21700
21796
|
href, get documentId() { return annotationDocumentId; },
|
|
21701
21797
|
isCurrent: () => session === documentSessionId && activeRendition === rendition,
|
|
21798
|
+
pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
|
|
21702
21799
|
annotations: () => epubAnnotations, labels: () => annotationLabels,
|
|
21703
21800
|
onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
|
|
21704
21801
|
});
|
|
@@ -21717,8 +21814,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21717
21814
|
typeof selectionWindow.getSelection === 'function'
|
|
21718
21815
|
) {
|
|
21719
21816
|
let selectionTimer = null;
|
|
21720
|
-
|
|
21721
|
-
let lastSelectionAt = 0;
|
|
21817
|
+
|
|
21722
21818
|
const reportSelection = () => {
|
|
21723
21819
|
selectionTimer = null;
|
|
21724
21820
|
if (session !== documentSessionId || activeRendition !== rendition) return;
|
|
@@ -21733,11 +21829,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21733
21829
|
try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
|
|
21734
21830
|
const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
|
|
21735
21831
|
if (!anchor) return;
|
|
21736
|
-
|
|
21737
|
-
const now = Date.now();
|
|
21738
|
-
if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
|
|
21739
|
-
lastSelectionKey = key;
|
|
21740
|
-
lastSelectionAt = now;
|
|
21832
|
+
if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
21741
21833
|
const payload = {documentSessionId: session, text: anchor.quote,
|
|
21742
21834
|
pageIndex: contents.sectionIndex + 1, anchor};
|
|
21743
21835
|
nativeMenuSelection = {contents, payload};
|
|
@@ -21752,6 +21844,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21752
21844
|
// The content document still emits selectionchange, so resolve its
|
|
21753
21845
|
// live Range to a CFI before the native menu action can fire.
|
|
21754
21846
|
if (text) reportSelection();
|
|
21847
|
+
else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
|
|
21755
21848
|
};
|
|
21756
21849
|
selectionDocument.addEventListener('selectionchange', handleSelectionChange);
|
|
21757
21850
|
const selectionCleanup = () => {
|
|
@@ -21774,6 +21867,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21774
21867
|
typeof rendition.hooks.unloaded.register === 'function'
|
|
21775
21868
|
) {
|
|
21776
21869
|
rendition.hooks.unloaded.register((view) => {
|
|
21870
|
+
view?.contents?.__papyrusGestureCleanup?.();
|
|
21777
21871
|
const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
|
|
21778
21872
|
if (typeof cleanup === 'function') cleanup();
|
|
21779
21873
|
const renderer = epubAnnotationRenderers.get(view?.contents);
|
|
@@ -21789,21 +21883,37 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21789
21883
|
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
21790
21884
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
21791
21885
|
const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
|
|
21792
|
-
if (!anchor) return;
|
|
21886
|
+
if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
21793
21887
|
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
|
|
21794
21888
|
const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
|
|
21795
21889
|
nativeMenuSelection = {contents, payload};
|
|
21796
21890
|
sendEvent('EPUB_TEXT_SELECTED', payload);
|
|
21797
21891
|
});
|
|
21798
|
-
['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
|
|
21892
|
+
['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
|
|
21799
21893
|
if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
21800
21894
|
}));
|
|
21801
21895
|
rendition.on('relocated', (location) => {
|
|
21896
|
+
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
21802
21897
|
const start = location && location.start;
|
|
21803
21898
|
let index = start && Number.isInteger(start.index) ? start.index : -1;
|
|
21804
21899
|
if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
|
|
21805
21900
|
if (index < 0 || index >= spineItems.length) return;
|
|
21806
21901
|
epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
|
|
21902
|
+
let pageGeometry;
|
|
21903
|
+
const manager=rendition.manager,layout=manager?.layout;
|
|
21904
|
+
if(epubViewMode==='single' && layout?.divisor===1 && !manager?.settings?.fullsize && manager?.settings?.direction==='ltr'){
|
|
21905
|
+
const visible=manager.visible?.();
|
|
21906
|
+
const view=visible?.find(view=>view.index===index);
|
|
21907
|
+
if(view && manager.container){
|
|
21908
|
+
pageGeometry={direction:'ltr',firstIndex:book.spine.first()?.index,lastIndex:book.spine.last()?.index,pageWidth:layout.pageWidth,offset:manager.container.getBoundingClientRect().left-view.position().left};
|
|
21909
|
+
}
|
|
21910
|
+
}
|
|
21911
|
+
epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll',pageGeometry);
|
|
21912
|
+
if(epubLocation){
|
|
21913
|
+
epubLocation.cfi=epubResizeAnchor?.cfi() || epubLocation.cfi;
|
|
21914
|
+
if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
|
|
21915
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
21916
|
+
}
|
|
21807
21917
|
const nextPage = index + 1;
|
|
21808
21918
|
if (nextPage === currentPage) return;
|
|
21809
21919
|
currentPage = nextPage;
|
|
@@ -21811,21 +21921,23 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21811
21921
|
});
|
|
21812
21922
|
}
|
|
21813
21923
|
|
|
21924
|
+
applyEpubZoom();
|
|
21814
21925
|
sendEpubDiagnostic('epub.display.start', diagnosticPayload);
|
|
21815
21926
|
await withEpubStageTimeout(
|
|
21816
|
-
|
|
21927
|
+
rendition.display(target || spineItems[0]?.href),
|
|
21817
21928
|
'rendition.display',
|
|
21818
21929
|
diagnosticPayload,
|
|
21819
21930
|
);
|
|
21820
21931
|
if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
|
|
21821
21932
|
sendEpubDiagnostic('epub.display.end', diagnosticPayload);
|
|
21933
|
+
const resizeRendition=rendition;
|
|
21934
|
+
epubResizeAnchor=window.PapyrusEpubReader.installResizeAnchor(resizeRendition.manager,()=>resizeRendition.location?.start?.cfi);
|
|
21822
21935
|
if (
|
|
21823
21936
|
rendition &&
|
|
21824
21937
|
rendition.manager &&
|
|
21825
21938
|
typeof rendition.manager.on === 'function' &&
|
|
21826
21939
|
typeof rendition.on === 'function'
|
|
21827
21940
|
) {
|
|
21828
|
-
let touchStart = null;
|
|
21829
21941
|
let lastScrollOffset = -1;
|
|
21830
21942
|
const manager = rendition.manager;
|
|
21831
21943
|
epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
|
|
@@ -21900,48 +22012,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21900
22012
|
return result;
|
|
21901
22013
|
};
|
|
21902
22014
|
}
|
|
21903
|
-
const
|
|
21904
|
-
const touch =
|
|
21905
|
-
(event && event.changedTouches && event.changedTouches[0]) ||
|
|
21906
|
-
(event && event.touches && event.touches[0]);
|
|
21907
|
-
if (!touch) return null;
|
|
21908
|
-
const x = Number(touch.screenX ?? touch.clientX);
|
|
21909
|
-
const y = Number(touch.screenY ?? touch.clientY);
|
|
21910
|
-
return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
|
|
21911
|
-
};
|
|
21912
|
-
const handleScroll = (event) => {
|
|
22015
|
+
const handleScroll = event => {
|
|
21913
22016
|
const offsetY = Math.max(0, Number(event && event.top) || 0);
|
|
21914
22017
|
epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
|
|
21915
|
-
if (Math.abs(offsetY
|
|
21916
|
-
lastScrollOffset
|
|
21917
|
-
sendEvent('VIEWER_SCROLL',
|
|
22018
|
+
if (Math.abs(offsetY-lastScrollOffset)<1) return;
|
|
22019
|
+
lastScrollOffset=offsetY;
|
|
22020
|
+
sendEvent('VIEWER_SCROLL',{offsetY});
|
|
21918
22021
|
};
|
|
21919
|
-
const handleTouchStart = (event) => {
|
|
21920
|
-
touchStart = getTouchPoint(event);
|
|
21921
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
|
|
21922
|
-
};
|
|
21923
|
-
const handleTouchMove = (event) => {
|
|
21924
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
|
|
21925
|
-
};
|
|
21926
|
-
const handleTouchEnd = (event) => {
|
|
21927
|
-
const end = getTouchPoint(event);
|
|
21928
|
-
const start = touchStart;
|
|
21929
|
-
touchStart = null;
|
|
21930
|
-
const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
|
|
21931
|
-
if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
|
|
21932
|
-
sendEvent('VIEWER_TAP', {});
|
|
21933
|
-
}
|
|
21934
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
|
|
21935
|
-
};
|
|
21936
|
-
|
|
21937
22022
|
manager.on('scroll', handleScroll);
|
|
21938
22023
|
const handleScrolled = (event) => {
|
|
21939
22024
|
epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
|
|
21940
22025
|
};
|
|
21941
22026
|
manager.on('scrolled', handleScrolled);
|
|
21942
|
-
|
|
21943
|
-
rendition.on('touchmove', handleTouchMove);
|
|
21944
|
-
rendition.on('touchend', handleTouchEnd);
|
|
22027
|
+
|
|
21945
22028
|
epubInteractionCleanup = () => {
|
|
21946
22029
|
epubSelectionCleanups.forEach((cleanup) => cleanup());
|
|
21947
22030
|
epubSelectionCleanups.clear();
|
|
@@ -21950,16 +22033,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21950
22033
|
if (originalEnqueue) queue.enqueue = originalEnqueue;
|
|
21951
22034
|
if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
|
|
21952
22035
|
if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
|
|
21953
|
-
if (typeof rendition.off === 'function') {
|
|
21954
|
-
rendition.off('touchstart', handleTouchStart);
|
|
21955
|
-
rendition.off('touchmove', handleTouchMove);
|
|
21956
|
-
rendition.off('touchend', handleTouchEnd);
|
|
21957
|
-
}
|
|
21958
22036
|
epubScrollDiagnostics = null;
|
|
21959
22037
|
};
|
|
21960
22038
|
}
|
|
21961
|
-
applyEpubZoom();
|
|
21962
|
-
|
|
21963
22039
|
const outline = await withEpubStageTimeout(
|
|
21964
22040
|
buildOutline(),
|
|
21965
22041
|
'outline',
|
|
@@ -21973,6 +22049,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21973
22049
|
return { pageCount, outline };
|
|
21974
22050
|
};
|
|
21975
22051
|
|
|
22052
|
+
let epubNavigationQueue = Promise.resolve();
|
|
22053
|
+
let epubNavigationRequest = 0;
|
|
21976
22054
|
const displayEpubPage = async (pageIndex) => {
|
|
21977
22055
|
if (!rendition) return;
|
|
21978
22056
|
const item = spineItems[pageIndex];
|
|
@@ -21982,9 +22060,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21982
22060
|
target,
|
|
21983
22061
|
pageIndex,
|
|
21984
22062
|
});
|
|
21985
|
-
|
|
21986
|
-
|
|
21987
|
-
|
|
22063
|
+
const activeRendition = rendition;
|
|
22064
|
+
const session = documentSessionId;
|
|
22065
|
+
const request = ++epubNavigationRequest;
|
|
22066
|
+
const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
|
|
22067
|
+
if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
|
|
22068
|
+
epubResizeAnchor?.invalidate();
|
|
22069
|
+
await activeRendition.display(target);
|
|
22070
|
+
if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
|
|
22071
|
+
currentPage = pageIndex + 1;
|
|
22072
|
+
sendState();
|
|
22073
|
+
});
|
|
22074
|
+
epubNavigationQueue = navigation;
|
|
22075
|
+
await navigation;
|
|
21988
22076
|
};
|
|
21989
22077
|
|
|
21990
22078
|
const getTextContent = async (pageIndex) => {
|
|
@@ -22086,6 +22174,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22086
22174
|
};
|
|
22087
22175
|
|
|
22088
22176
|
let nativeMenuSelection = null;
|
|
22177
|
+
let epubViewMode = "single";
|
|
22178
|
+
let epubLocation = null;
|
|
22089
22179
|
|
|
22090
22180
|
const handleCommand = async (message) => {
|
|
22091
22181
|
const { id, kind, payload } = message;
|
|
@@ -22098,6 +22188,11 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22098
22188
|
epubAnnotations = [];
|
|
22099
22189
|
annotationDocumentId = undefined;
|
|
22100
22190
|
zoom = 1.0;
|
|
22191
|
+
epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
|
|
22192
|
+
epubLocation = null;
|
|
22193
|
+
nativeMenuSelection = null;
|
|
22194
|
+
epubNavigationRequest += 1;
|
|
22195
|
+
epubNavigationQueue = Promise.resolve();
|
|
22101
22196
|
|
|
22102
22197
|
if (currentType === 'text') {
|
|
22103
22198
|
const result = await loadText(
|
|
@@ -22152,17 +22247,24 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22152
22247
|
epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
22153
22248
|
} else {
|
|
22154
22249
|
const annotation = epubAnnotations.find(a => a.id === payload.annotationId);
|
|
22155
|
-
if (!annotation) throw new Error('Annotation not available');
|
|
22250
|
+
if (!annotation || (annotation.anchor.documentId && annotation.anchor.documentId !== annotationDocumentId)) throw new Error('Annotation not available for this document');
|
|
22156
22251
|
// Navigation uses the CFI, never a synthetic percentage/page rectangle.
|
|
22157
22252
|
const navigationSession = documentSessionId;
|
|
22158
22253
|
const activeRendition = rendition;
|
|
22159
22254
|
// Open the confirmed chapter first, resolve its quote, then navigate its actual range.
|
|
22160
|
-
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22255
|
+
epubNavigationRequest += 1;
|
|
22256
|
+
const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
|
|
22257
|
+
if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
|
|
22258
|
+
epubResizeAnchor?.invalidate();
|
|
22259
|
+
await activeRendition.display(annotation.anchor.href);
|
|
22260
|
+
if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
|
|
22261
|
+
const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
|
|
22262
|
+
const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
|
|
22263
|
+
if(!range)throw new Error('EPUB anchor unresolved');
|
|
22264
|
+
await activeRendition.display(contents.cfiFromRange(range));
|
|
22265
|
+
});
|
|
22266
|
+
epubNavigationQueue = navigation;
|
|
22267
|
+
await navigation;
|
|
22166
22268
|
}
|
|
22167
22269
|
sendResponse(id,true,null); return;
|
|
22168
22270
|
}
|
|
@@ -22189,12 +22291,55 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22189
22291
|
return;
|
|
22190
22292
|
}
|
|
22191
22293
|
|
|
22294
|
+
if (kind === 'epub-turn-page') {
|
|
22295
|
+
if (epubViewMode !== 'single') {sendResponse(id,true,null); return;}
|
|
22296
|
+
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB navigation');
|
|
22297
|
+
const active = rendition;
|
|
22298
|
+
epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
|
|
22299
|
+
if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
|
|
22300
|
+
epubResizeAnchor?.invalidate();
|
|
22301
|
+
return payload.direction < 0 ? active.prev() : active.next();
|
|
22302
|
+
});
|
|
22303
|
+
await epubNavigationQueue;
|
|
22304
|
+
sendResponse(id,true,null); return;
|
|
22305
|
+
}
|
|
22306
|
+
|
|
22307
|
+
if (kind === 'set-epub-view-mode') {
|
|
22308
|
+
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
|
|
22309
|
+
const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
|
|
22310
|
+
{
|
|
22311
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
22312
|
+
if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
|
|
22313
|
+
const target=epubResizeAnchor?.cfi() || epubLocation?.cfi;
|
|
22314
|
+
epubViewMode=next;
|
|
22315
|
+
await renderEpubRendition('layout',epubLoadGeneration,target);
|
|
22316
|
+
});
|
|
22317
|
+
await epubNavigationQueue;
|
|
22318
|
+
}
|
|
22319
|
+
sendResponse(id,true,null);return;
|
|
22320
|
+
}
|
|
22321
|
+
if(kind === 'epub-go-to-location'){
|
|
22322
|
+
if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
|
|
22323
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});
|
|
22324
|
+
await epubNavigationQueue;sendResponse(id,true,null);return;
|
|
22325
|
+
}
|
|
22326
|
+
|
|
22192
22327
|
if (kind === 'set-zoom') {
|
|
22193
|
-
|
|
22194
|
-
|
|
22195
|
-
|
|
22196
|
-
|
|
22197
|
-
|
|
22328
|
+
const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
|
|
22329
|
+
if(currentType === 'epub') {
|
|
22330
|
+
const session=documentSessionId;
|
|
22331
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
22332
|
+
if(session!==documentSessionId)return;
|
|
22333
|
+
const active=rendition;
|
|
22334
|
+
const cfi=epubResizeAnchor?.cfi() || epubLocation?.cfi;
|
|
22335
|
+
zoom=requestedZoom;applyEpubZoom();
|
|
22336
|
+
// Let the content CSS update before redisplaying the logical anchor.
|
|
22337
|
+
await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
|
|
22338
|
+
if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
|
|
22339
|
+
});
|
|
22340
|
+
await epubNavigationQueue;
|
|
22341
|
+
} else {zoom=requestedZoom;applyZoom();}
|
|
22342
|
+
sendState();sendResponse(id,true,{zoom});return;
|
|
22198
22343
|
}
|
|
22199
22344
|
|
|
22200
22345
|
if (kind === 'set-rotation') {
|