@papyrus-sdk/ui-react-native 0.2.35-beta.10 → 0.2.35-beta.11.epub.1
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/LICENSE +21 -0
- package/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +476 -229
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +489 -241
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -13
- package/runtime/epubAnnotations.js +7 -4
- package/runtime/epubAnnotations.test.ts +6 -2
- package/runtime/epubReader.js +33 -0
- package/runtime/epubReader.test.ts +22 -0
- package/runtime/epubRuntime.test.ts +45 -0
- package/runtime/index.html +180 -79
- package/runtime/runtime.js +180 -79
- package/runtime/syncComicRuntime.mjs +6 -3
package/runtime/runtime.js
CHANGED
|
@@ -109,6 +109,42 @@
|
|
|
109
109
|
/* @papyrus-comic-runtime:end */
|
|
110
110
|
|
|
111
111
|
(function () {
|
|
112
|
+
/* @papyrus-epub-reader:start */
|
|
113
|
+
(function(root){
|
|
114
|
+
function mode(value){return value==='chapter-scroll'?'chapter-scroll':'paged';}
|
|
115
|
+
function validCfi(value){return typeof value==='string' && value.length<=4096 && /^epubcfi\([^\n<>]+\)$/.test(value);}
|
|
116
|
+
function options(value){return {manager:'default',flow:mode(value)==='paged'?'paginated':'scrolled-doc',spread:'none',width:'100%',height:'100%',allowScriptedContent:false};}
|
|
117
|
+
function gesture(start,end,state){
|
|
118
|
+
if(!start || !end || state.selected || state.interactive || state.multitouch)return null;
|
|
119
|
+
var dx=end.x-start.x,dy=end.y-start.y,elapsed=end.time-start.time;
|
|
120
|
+
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';
|
|
121
|
+
if(elapsed>=0 && elapsed<450 && Math.hypot(dx,dy)<=12)return 'tap';
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function location(raw,count,readingMode){
|
|
125
|
+
var start=raw && raw.start;if(!start || !validCfi(start.cfi))return null;
|
|
126
|
+
var displayed=start.displayed||{},chapter=Math.max(1,(Number(start.index)||0)+1);
|
|
127
|
+
var visualPage=mode(readingMode)==='paged' && Number.isFinite(displayed.page)?displayed.page:null;
|
|
128
|
+
var total=mode(readingMode)==='paged' && Number.isFinite(displayed.total)?displayed.total:null;
|
|
129
|
+
var fraction=total?Math.max(0,(visualPage-1)/total):0;
|
|
130
|
+
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:Boolean(raw.atStart),atEnd:Boolean(raw.atEnd)};
|
|
131
|
+
}
|
|
132
|
+
function install(contents,config){
|
|
133
|
+
var doc=contents.document,win=contents.window,start=null,blocked=false;
|
|
134
|
+
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;}
|
|
135
|
+
function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
|
|
136
|
+
function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
|
|
137
|
+
function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
|
|
138
|
+
function move(e){if(e.touches.length!==1)blocked=true;}
|
|
139
|
+
function end(e){var action=gesture(start,point(e),{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);}
|
|
140
|
+
function cancel(){start=null;blocked=true;}
|
|
141
|
+
doc.addEventListener('touchstart',begin,{passive:true});doc.addEventListener('touchmove',move,{passive:true});doc.addEventListener('touchend',end,{passive:true});doc.addEventListener('touchcancel',cancel,{passive:true});
|
|
142
|
+
return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);};
|
|
143
|
+
}
|
|
144
|
+
root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,install:install};
|
|
145
|
+
})(typeof window==='undefined'?globalThis:window);
|
|
146
|
+
|
|
147
|
+
/* @papyrus-epub-reader:end */
|
|
112
148
|
/* @papyrus-epub-annotations:start */
|
|
113
149
|
(function (root, factory) {
|
|
114
150
|
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
@@ -187,6 +223,7 @@
|
|
|
187
223
|
var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
|
|
188
224
|
var style = annotation.markupStyle || annotation.type;
|
|
189
225
|
var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
|
|
226
|
+
if(style === 'highlight')opacity=Math.min(0.35,opacity);
|
|
190
227
|
rects.forEach(function(rect) {
|
|
191
228
|
var mark = doc.createElement('div');
|
|
192
229
|
var bodyRect=doc.body.getBoundingClientRect();
|
|
@@ -199,8 +236,10 @@
|
|
|
199
236
|
if (hasNote) {
|
|
200
237
|
var last = rects[rects.length-1], button = doc.createElement('button');
|
|
201
238
|
button.textContent = '●'; button.setAttribute('aria-label',options.labels().comment || '');
|
|
202
|
-
|
|
203
|
-
var
|
|
239
|
+
var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
|
|
240
|
+
var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
|
|
241
|
+
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';
|
|
242
|
+
var key = column+":"+Math.floor(last.top/44);
|
|
204
243
|
var neighbors=noteGroups.get(key);
|
|
205
244
|
if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
|
|
206
245
|
neighbors.ids.push(annotation.id);
|
|
@@ -221,10 +260,10 @@
|
|
|
221
260
|
doc.addEventListener('click',tapMarkup);
|
|
222
261
|
var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
|
|
223
262
|
if (observer) observer.observe(doc.body);
|
|
224
|
-
win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
|
|
263
|
+
win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
|
|
225
264
|
if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
|
|
226
265
|
schedule();
|
|
227
|
-
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();}};
|
|
266
|
+
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();}};
|
|
228
267
|
}
|
|
229
268
|
return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
|
|
230
269
|
});
|
|
@@ -1305,26 +1344,55 @@
|
|
|
1305
1344
|
pageCount,
|
|
1306
1345
|
});
|
|
1307
1346
|
|
|
1347
|
+
if (book.locations?.generate) {
|
|
1348
|
+
const activeBook=book;
|
|
1349
|
+
activeBook.locations.generate(150).then(() => {
|
|
1350
|
+
if (isCurrentLoad() && activeBook===book && epubLocation) {
|
|
1351
|
+
epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
|
|
1352
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
1353
|
+
}
|
|
1354
|
+
}).catch(() => {});
|
|
1355
|
+
}
|
|
1356
|
+
return renderEpubRendition(loadId,generation,null);
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
const renderEpubRendition = async (loadId,generation,target) => {
|
|
1360
|
+
const diagnosticPayload={loadId};
|
|
1361
|
+
const isCurrentLoad=()=>generation===epubLoadGeneration;
|
|
1362
|
+
if(epubInteractionCleanup)epubInteractionCleanup();
|
|
1363
|
+
if(rendition)rendition.destroy();
|
|
1308
1364
|
clearViewer();
|
|
1309
|
-
rendition = book.renderTo(viewer,
|
|
1310
|
-
manager: 'continuous',
|
|
1311
|
-
allowScriptedContent: false,
|
|
1312
|
-
width: '100%',
|
|
1313
|
-
height: '100%',
|
|
1314
|
-
flow: 'scrolled-continuous',
|
|
1315
|
-
spread: 'none',
|
|
1316
|
-
});
|
|
1365
|
+
rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
|
|
1317
1366
|
sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
|
|
1318
1367
|
|
|
1319
1368
|
if (rendition && rendition.hooks && rendition.hooks.content) {
|
|
1320
1369
|
const session = documentSessionId;
|
|
1321
1370
|
const activeRendition = rendition;
|
|
1322
1371
|
rendition.hooks.content.register((contents) => {
|
|
1372
|
+
const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
|
|
1373
|
+
mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
|
|
1374
|
+
rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
|
|
1375
|
+
blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
|
|
1376
|
+
annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
|
|
1377
|
+
current: () => session === documentSessionId && activeRendition === rendition,
|
|
1378
|
+
tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
|
|
1379
|
+
turn: direction => {
|
|
1380
|
+
epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
|
|
1381
|
+
if(session !== documentSessionId || activeRendition !== rendition)return;
|
|
1382
|
+
return direction<0?activeRendition.prev():activeRendition.next();
|
|
1383
|
+
});
|
|
1384
|
+
epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
|
|
1385
|
+
},
|
|
1386
|
+
});
|
|
1387
|
+
const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
|
|
1388
|
+
epubSelectionCleanups.add(gestureCleanup);
|
|
1389
|
+
contents.__papyrusGestureCleanup=gestureCleanup;
|
|
1323
1390
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
1324
1391
|
if (href && window.PapyrusEpubAnnotations) {
|
|
1325
1392
|
const renderer = window.PapyrusEpubAnnotations.install(contents, {
|
|
1326
1393
|
href, get documentId() { return annotationDocumentId; },
|
|
1327
1394
|
isCurrent: () => session === documentSessionId && activeRendition === rendition,
|
|
1395
|
+
pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
|
|
1328
1396
|
annotations: () => epubAnnotations, labels: () => annotationLabels,
|
|
1329
1397
|
onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
|
|
1330
1398
|
});
|
|
@@ -1343,8 +1411,7 @@
|
|
|
1343
1411
|
typeof selectionWindow.getSelection === 'function'
|
|
1344
1412
|
) {
|
|
1345
1413
|
let selectionTimer = null;
|
|
1346
|
-
|
|
1347
|
-
let lastSelectionAt = 0;
|
|
1414
|
+
|
|
1348
1415
|
const reportSelection = () => {
|
|
1349
1416
|
selectionTimer = null;
|
|
1350
1417
|
if (session !== documentSessionId || activeRendition !== rendition) return;
|
|
@@ -1359,11 +1426,7 @@
|
|
|
1359
1426
|
try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
|
|
1360
1427
|
const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
|
|
1361
1428
|
if (!anchor) return;
|
|
1362
|
-
|
|
1363
|
-
const now = Date.now();
|
|
1364
|
-
if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
|
|
1365
|
-
lastSelectionKey = key;
|
|
1366
|
-
lastSelectionAt = now;
|
|
1429
|
+
if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
1367
1430
|
const payload = {documentSessionId: session, text: anchor.quote,
|
|
1368
1431
|
pageIndex: contents.sectionIndex + 1, anchor};
|
|
1369
1432
|
nativeMenuSelection = {contents, payload};
|
|
@@ -1378,6 +1441,7 @@
|
|
|
1378
1441
|
// The content document still emits selectionchange, so resolve its
|
|
1379
1442
|
// live Range to a CFI before the native menu action can fire.
|
|
1380
1443
|
if (text) reportSelection();
|
|
1444
|
+
else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
|
|
1381
1445
|
};
|
|
1382
1446
|
selectionDocument.addEventListener('selectionchange', handleSelectionChange);
|
|
1383
1447
|
const selectionCleanup = () => {
|
|
@@ -1400,6 +1464,7 @@
|
|
|
1400
1464
|
typeof rendition.hooks.unloaded.register === 'function'
|
|
1401
1465
|
) {
|
|
1402
1466
|
rendition.hooks.unloaded.register((view) => {
|
|
1467
|
+
view?.contents?.__papyrusGestureCleanup?.();
|
|
1403
1468
|
const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
|
|
1404
1469
|
if (typeof cleanup === 'function') cleanup();
|
|
1405
1470
|
const renderer = epubAnnotationRenderers.get(view?.contents);
|
|
@@ -1415,21 +1480,27 @@
|
|
|
1415
1480
|
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
1416
1481
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
1417
1482
|
const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
|
|
1418
|
-
if (!anchor) return;
|
|
1483
|
+
if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
1419
1484
|
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
|
|
1420
1485
|
const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
|
|
1421
1486
|
nativeMenuSelection = {contents, payload};
|
|
1422
1487
|
sendEvent('EPUB_TEXT_SELECTED', payload);
|
|
1423
1488
|
});
|
|
1424
|
-
['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
|
|
1489
|
+
['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
|
|
1425
1490
|
if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
1426
1491
|
}));
|
|
1427
1492
|
rendition.on('relocated', (location) => {
|
|
1493
|
+
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
1428
1494
|
const start = location && location.start;
|
|
1429
1495
|
let index = start && Number.isInteger(start.index) ? start.index : -1;
|
|
1430
1496
|
if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
|
|
1431
1497
|
if (index < 0 || index >= spineItems.length) return;
|
|
1432
1498
|
epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
|
|
1499
|
+
epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll');
|
|
1500
|
+
if(epubLocation){
|
|
1501
|
+
if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
|
|
1502
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
1503
|
+
}
|
|
1433
1504
|
const nextPage = index + 1;
|
|
1434
1505
|
if (nextPage === currentPage) return;
|
|
1435
1506
|
currentPage = nextPage;
|
|
@@ -1437,9 +1508,10 @@
|
|
|
1437
1508
|
});
|
|
1438
1509
|
}
|
|
1439
1510
|
|
|
1511
|
+
applyEpubZoom();
|
|
1440
1512
|
sendEpubDiagnostic('epub.display.start', diagnosticPayload);
|
|
1441
1513
|
await withEpubStageTimeout(
|
|
1442
|
-
|
|
1514
|
+
rendition.display(target || spineItems[0]?.href),
|
|
1443
1515
|
'rendition.display',
|
|
1444
1516
|
diagnosticPayload,
|
|
1445
1517
|
);
|
|
@@ -1451,7 +1523,6 @@
|
|
|
1451
1523
|
typeof rendition.manager.on === 'function' &&
|
|
1452
1524
|
typeof rendition.on === 'function'
|
|
1453
1525
|
) {
|
|
1454
|
-
let touchStart = null;
|
|
1455
1526
|
let lastScrollOffset = -1;
|
|
1456
1527
|
const manager = rendition.manager;
|
|
1457
1528
|
epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
|
|
@@ -1526,48 +1597,19 @@
|
|
|
1526
1597
|
return result;
|
|
1527
1598
|
};
|
|
1528
1599
|
}
|
|
1529
|
-
const
|
|
1530
|
-
const touch =
|
|
1531
|
-
(event && event.changedTouches && event.changedTouches[0]) ||
|
|
1532
|
-
(event && event.touches && event.touches[0]);
|
|
1533
|
-
if (!touch) return null;
|
|
1534
|
-
const x = Number(touch.screenX ?? touch.clientX);
|
|
1535
|
-
const y = Number(touch.screenY ?? touch.clientY);
|
|
1536
|
-
return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
|
|
1537
|
-
};
|
|
1538
|
-
const handleScroll = (event) => {
|
|
1600
|
+
const handleScroll = event => {
|
|
1539
1601
|
const offsetY = Math.max(0, Number(event && event.top) || 0);
|
|
1540
1602
|
epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
|
|
1541
|
-
if (Math.abs(offsetY
|
|
1542
|
-
lastScrollOffset
|
|
1543
|
-
sendEvent('VIEWER_SCROLL',
|
|
1544
|
-
};
|
|
1545
|
-
const handleTouchStart = (event) => {
|
|
1546
|
-
touchStart = getTouchPoint(event);
|
|
1547
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
|
|
1548
|
-
};
|
|
1549
|
-
const handleTouchMove = (event) => {
|
|
1550
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
|
|
1551
|
-
};
|
|
1552
|
-
const handleTouchEnd = (event) => {
|
|
1553
|
-
const end = getTouchPoint(event);
|
|
1554
|
-
const start = touchStart;
|
|
1555
|
-
touchStart = null;
|
|
1556
|
-
const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
|
|
1557
|
-
if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
|
|
1558
|
-
sendEvent('VIEWER_TAP', {});
|
|
1559
|
-
}
|
|
1560
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
|
|
1603
|
+
if (Math.abs(offsetY-lastScrollOffset)<1) return;
|
|
1604
|
+
lastScrollOffset=offsetY;
|
|
1605
|
+
sendEvent('VIEWER_SCROLL',{offsetY});
|
|
1561
1606
|
};
|
|
1562
|
-
|
|
1563
1607
|
manager.on('scroll', handleScroll);
|
|
1564
1608
|
const handleScrolled = (event) => {
|
|
1565
1609
|
epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
|
|
1566
1610
|
};
|
|
1567
1611
|
manager.on('scrolled', handleScrolled);
|
|
1568
|
-
|
|
1569
|
-
rendition.on('touchmove', handleTouchMove);
|
|
1570
|
-
rendition.on('touchend', handleTouchEnd);
|
|
1612
|
+
|
|
1571
1613
|
epubInteractionCleanup = () => {
|
|
1572
1614
|
epubSelectionCleanups.forEach((cleanup) => cleanup());
|
|
1573
1615
|
epubSelectionCleanups.clear();
|
|
@@ -1576,16 +1618,9 @@
|
|
|
1576
1618
|
if (originalEnqueue) queue.enqueue = originalEnqueue;
|
|
1577
1619
|
if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
|
|
1578
1620
|
if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
|
|
1579
|
-
if (typeof rendition.off === 'function') {
|
|
1580
|
-
rendition.off('touchstart', handleTouchStart);
|
|
1581
|
-
rendition.off('touchmove', handleTouchMove);
|
|
1582
|
-
rendition.off('touchend', handleTouchEnd);
|
|
1583
|
-
}
|
|
1584
1621
|
epubScrollDiagnostics = null;
|
|
1585
1622
|
};
|
|
1586
1623
|
}
|
|
1587
|
-
applyEpubZoom();
|
|
1588
|
-
|
|
1589
1624
|
const outline = await withEpubStageTimeout(
|
|
1590
1625
|
buildOutline(),
|
|
1591
1626
|
'outline',
|
|
@@ -1599,6 +1634,8 @@
|
|
|
1599
1634
|
return { pageCount, outline };
|
|
1600
1635
|
};
|
|
1601
1636
|
|
|
1637
|
+
let epubNavigationQueue = Promise.resolve();
|
|
1638
|
+
let epubNavigationRequest = 0;
|
|
1602
1639
|
const displayEpubPage = async (pageIndex) => {
|
|
1603
1640
|
if (!rendition) return;
|
|
1604
1641
|
const item = spineItems[pageIndex];
|
|
@@ -1608,9 +1645,18 @@
|
|
|
1608
1645
|
target,
|
|
1609
1646
|
pageIndex,
|
|
1610
1647
|
});
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1648
|
+
const activeRendition = rendition;
|
|
1649
|
+
const session = documentSessionId;
|
|
1650
|
+
const request = ++epubNavigationRequest;
|
|
1651
|
+
const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
|
|
1652
|
+
if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
|
|
1653
|
+
await activeRendition.display(target);
|
|
1654
|
+
if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
|
|
1655
|
+
currentPage = pageIndex + 1;
|
|
1656
|
+
sendState();
|
|
1657
|
+
});
|
|
1658
|
+
epubNavigationQueue = navigation;
|
|
1659
|
+
await navigation;
|
|
1614
1660
|
};
|
|
1615
1661
|
|
|
1616
1662
|
const getTextContent = async (pageIndex) => {
|
|
@@ -1712,6 +1758,8 @@
|
|
|
1712
1758
|
};
|
|
1713
1759
|
|
|
1714
1760
|
let nativeMenuSelection = null;
|
|
1761
|
+
let epubViewMode = "single";
|
|
1762
|
+
let epubLocation = null;
|
|
1715
1763
|
|
|
1716
1764
|
const handleCommand = async (message) => {
|
|
1717
1765
|
const { id, kind, payload } = message;
|
|
@@ -1724,6 +1772,11 @@
|
|
|
1724
1772
|
epubAnnotations = [];
|
|
1725
1773
|
annotationDocumentId = undefined;
|
|
1726
1774
|
zoom = 1.0;
|
|
1775
|
+
epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
|
|
1776
|
+
epubLocation = null;
|
|
1777
|
+
nativeMenuSelection = null;
|
|
1778
|
+
epubNavigationRequest += 1;
|
|
1779
|
+
epubNavigationQueue = Promise.resolve();
|
|
1727
1780
|
|
|
1728
1781
|
if (currentType === 'text') {
|
|
1729
1782
|
const result = await loadText(
|
|
@@ -1778,17 +1831,23 @@
|
|
|
1778
1831
|
epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
1779
1832
|
} else {
|
|
1780
1833
|
const annotation = epubAnnotations.find(a => a.id === payload.annotationId);
|
|
1781
|
-
if (!annotation) throw new Error('Annotation not available');
|
|
1834
|
+
if (!annotation || (annotation.anchor.documentId && annotation.anchor.documentId !== annotationDocumentId)) throw new Error('Annotation not available for this document');
|
|
1782
1835
|
// Navigation uses the CFI, never a synthetic percentage/page rectangle.
|
|
1783
1836
|
const navigationSession = documentSessionId;
|
|
1784
1837
|
const activeRendition = rendition;
|
|
1785
1838
|
// Open the confirmed chapter first, resolve its quote, then navigate its actual range.
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1839
|
+
epubNavigationRequest += 1;
|
|
1840
|
+
const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
|
|
1841
|
+
if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
|
|
1842
|
+
await activeRendition.display(annotation.anchor.href);
|
|
1843
|
+
if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
|
|
1844
|
+
const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
|
|
1845
|
+
const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
|
|
1846
|
+
if(!range)throw new Error('EPUB anchor unresolved');
|
|
1847
|
+
await activeRendition.display(contents.cfiFromRange(range));
|
|
1848
|
+
});
|
|
1849
|
+
epubNavigationQueue = navigation;
|
|
1850
|
+
await navigation;
|
|
1792
1851
|
}
|
|
1793
1852
|
sendResponse(id,true,null); return;
|
|
1794
1853
|
}
|
|
@@ -1815,12 +1874,54 @@
|
|
|
1815
1874
|
return;
|
|
1816
1875
|
}
|
|
1817
1876
|
|
|
1877
|
+
if (kind === 'epub-turn-page') {
|
|
1878
|
+
if (epubViewMode !== 'single') {sendResponse(id,true,null); return;}
|
|
1879
|
+
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB navigation');
|
|
1880
|
+
const active = rendition;
|
|
1881
|
+
epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
|
|
1882
|
+
if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
|
|
1883
|
+
return payload.direction < 0 ? active.prev() : active.next();
|
|
1884
|
+
});
|
|
1885
|
+
await epubNavigationQueue;
|
|
1886
|
+
sendResponse(id,true,null); return;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
if (kind === 'set-epub-view-mode') {
|
|
1890
|
+
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
|
|
1891
|
+
const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
|
|
1892
|
+
{
|
|
1893
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
1894
|
+
if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
|
|
1895
|
+
const target=epubLocation?.cfi;
|
|
1896
|
+
epubViewMode=next;
|
|
1897
|
+
await renderEpubRendition('layout',epubLoadGeneration,target);
|
|
1898
|
+
});
|
|
1899
|
+
await epubNavigationQueue;
|
|
1900
|
+
}
|
|
1901
|
+
sendResponse(id,true,null);return;
|
|
1902
|
+
}
|
|
1903
|
+
if(kind === 'epub-go-to-location'){
|
|
1904
|
+
if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
|
|
1905
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>payload.documentSessionId===documentSessionId?rendition.display(payload.cfi):undefined);
|
|
1906
|
+
await epubNavigationQueue;sendResponse(id,true,null);return;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1818
1909
|
if (kind === 'set-zoom') {
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1910
|
+
const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
|
|
1911
|
+
if(currentType === 'epub') {
|
|
1912
|
+
const session=documentSessionId;
|
|
1913
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
1914
|
+
if(session!==documentSessionId)return;
|
|
1915
|
+
const active=rendition;
|
|
1916
|
+
const cfi=epubLocation?.cfi;
|
|
1917
|
+
zoom=requestedZoom;applyEpubZoom();
|
|
1918
|
+
// Let the content CSS update before redisplaying the logical anchor.
|
|
1919
|
+
await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
|
|
1920
|
+
if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
|
|
1921
|
+
});
|
|
1922
|
+
await epubNavigationQueue;
|
|
1923
|
+
} else {zoom=requestedZoom;applyZoom();}
|
|
1924
|
+
sendState();sendResponse(id,true,{zoom});return;
|
|
1824
1925
|
}
|
|
1825
1926
|
|
|
1826
1927
|
if (kind === 'set-rotation') {
|
|
@@ -24,11 +24,13 @@ const runtimeBodyStart = runtime.indexOf("\n(function () {", runtime.indexOf(com
|
|
|
24
24
|
if (runtimeBodyStart < 0) {
|
|
25
25
|
throw new Error("Main runtime body not found");
|
|
26
26
|
}
|
|
27
|
-
const
|
|
27
|
+
const readerSource=await readFile(resolve(runtimeDirectory,"epubReader.js"),"utf8");
|
|
28
|
+
const readerRuntime=replaceMarkedSource(runtime,"/* @papyrus-epub-reader:start */","/* @papyrus-epub-reader:end */",`\n${readerSource}\n`);
|
|
29
|
+
const runtimeBody = readerRuntime.slice(readerRuntime.indexOf("\n(function () {",readerRuntime.indexOf(comicRuntimeEnd))+1).trim();
|
|
28
30
|
await writeFile(
|
|
29
31
|
runtimePath,
|
|
30
32
|
replaceMarkedSource(
|
|
31
|
-
|
|
33
|
+
readerRuntime,
|
|
32
34
|
"/* @papyrus-comic-runtime:start */",
|
|
33
35
|
"/* @papyrus-comic-runtime:end */",
|
|
34
36
|
`\n${source.trim()}\n`
|
|
@@ -37,12 +39,13 @@ await writeFile(
|
|
|
37
39
|
|
|
38
40
|
const htmlPath = resolve(runtimeDirectory, "index.html");
|
|
39
41
|
const html = await readFile(htmlPath, "utf8");
|
|
40
|
-
|
|
42
|
+
let htmlWithComicRuntime = replaceMarkedSource(
|
|
41
43
|
html,
|
|
42
44
|
"<!-- @papyrus-comic-runtime:start -->",
|
|
43
45
|
"<!-- @papyrus-comic-runtime:end -->",
|
|
44
46
|
`\n <script>\n${source}\n </script>\n`
|
|
45
47
|
);
|
|
48
|
+
htmlWithComicRuntime=htmlWithComicRuntime.replace(/<!-- @papyrus-epub-reader:start -->[\s\S]*?<!-- @papyrus-epub-reader:end -->\n?/g,"");
|
|
46
49
|
const htmlComicRuntimeEnd = "<!-- @papyrus-comic-runtime:end -->";
|
|
47
50
|
const htmlBodyStart = htmlWithComicRuntime.indexOf(
|
|
48
51
|
"<script>",
|