@papyrus-sdk/ui-react-native 0.2.35-beta.11 → 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 +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +371 -231
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +315 -175
- 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 +134 -91
- package/runtime/runtime.js +134 -91
- package/runtime/syncComicRuntime.mjs +6 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papyrus-sdk/ui-react-native",
|
|
3
|
-
"version": "0.2.35-beta.11",
|
|
3
|
+
"version": "0.2.35-beta.11.epub.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"papyrus",
|
|
@@ -38,17 +38,13 @@
|
|
|
38
38
|
"bugs": {
|
|
39
39
|
"url": "https://github.com/solrachix/Papyrus/issues"
|
|
40
40
|
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "node runtime/syncComicRuntime.mjs && tsup",
|
|
43
|
-
"check:ink-worklets": "pnpm run build && node ../../scripts/ci/check-native-ink-worklets.cjs"
|
|
44
|
-
},
|
|
45
41
|
"dependencies": {
|
|
46
42
|
"react-native-svg": "^15.5.0"
|
|
47
43
|
},
|
|
48
44
|
"peerDependencies": {
|
|
49
|
-
"@papyrus-sdk/core": "0.2.23-beta.3",
|
|
50
|
-
"@papyrus-sdk/engine-native": "0.2.22-beta.9",
|
|
51
|
-
"@papyrus-sdk/types": "0.2.16-beta.3",
|
|
45
|
+
"@papyrus-sdk/core": "0.2.23-beta.3.epub.1",
|
|
46
|
+
"@papyrus-sdk/engine-native": "0.2.22-beta.9.epub.1",
|
|
47
|
+
"@papyrus-sdk/types": "0.2.16-beta.3.epub.1",
|
|
52
48
|
"@react-native-clipboard/clipboard": "^1.16.3",
|
|
53
49
|
"react": ">=18.2.0 <20",
|
|
54
50
|
"react-native": ">=0.81.0",
|
|
@@ -63,15 +59,19 @@
|
|
|
63
59
|
}
|
|
64
60
|
},
|
|
65
61
|
"devDependencies": {
|
|
66
|
-
"@papyrus-sdk/core": "workspace:*",
|
|
67
|
-
"@papyrus-sdk/engine-native": "workspace:*",
|
|
68
|
-
"@papyrus-sdk/types": "workspace:*",
|
|
69
62
|
"@react-native-clipboard/clipboard": "^1.16.3",
|
|
70
63
|
"@types/react": "19.2.14",
|
|
71
64
|
"react": "^19.1.0",
|
|
72
65
|
"react-native": "^0.81.0",
|
|
73
66
|
"react-native-gesture-handler": "~2.28.0",
|
|
74
67
|
"react-native-safe-area-context": "^5.6.2",
|
|
75
|
-
"react-native-reanimated": "3.19.5"
|
|
68
|
+
"react-native-reanimated": "3.19.5",
|
|
69
|
+
"@papyrus-sdk/core": "0.2.23-beta.3.epub.1",
|
|
70
|
+
"@papyrus-sdk/types": "0.2.16-beta.3.epub.1",
|
|
71
|
+
"@papyrus-sdk/engine-native": "0.2.22-beta.9.epub.1"
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "node runtime/syncComicRuntime.mjs && tsup",
|
|
75
|
+
"check:ink-worklets": "pnpm run build && node ../../scripts/ci/check-native-ink-worklets.cjs"
|
|
76
76
|
}
|
|
77
|
-
}
|
|
77
|
+
}
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
|
|
76
76
|
var style = annotation.markupStyle || annotation.type;
|
|
77
77
|
var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
|
|
78
|
+
if(style === 'highlight')opacity=Math.min(0.35,opacity);
|
|
78
79
|
rects.forEach(function(rect) {
|
|
79
80
|
var mark = doc.createElement('div');
|
|
80
81
|
var bodyRect=doc.body.getBoundingClientRect();
|
|
@@ -87,8 +88,10 @@
|
|
|
87
88
|
if (hasNote) {
|
|
88
89
|
var last = rects[rects.length-1], button = doc.createElement('button');
|
|
89
90
|
button.textContent = '●'; button.setAttribute('aria-label',options.labels().comment || '');
|
|
90
|
-
|
|
91
|
-
var
|
|
91
|
+
var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
|
|
92
|
+
var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
|
|
93
|
+
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';
|
|
94
|
+
var key = column+":"+Math.floor(last.top/44);
|
|
92
95
|
var neighbors=noteGroups.get(key);
|
|
93
96
|
if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
|
|
94
97
|
neighbors.ids.push(annotation.id);
|
|
@@ -109,10 +112,10 @@
|
|
|
109
112
|
doc.addEventListener('click',tapMarkup);
|
|
110
113
|
var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
|
|
111
114
|
if (observer) observer.observe(doc.body);
|
|
112
|
-
win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
|
|
115
|
+
win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
|
|
113
116
|
if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
|
|
114
117
|
schedule();
|
|
115
|
-
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();}};
|
|
118
|
+
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();}};
|
|
116
119
|
}
|
|
117
120
|
return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
|
|
118
121
|
});
|
|
@@ -15,14 +15,18 @@ it('renders true strikeout, grouped notes and reconciles 120 stable IDs after re
|
|
|
15
15
|
let top=20;Object.defineProperty(range,'getClientRects',{value:()=>[{left:10,right:50,top,bottom:top+20,width:40,height:20}]});
|
|
16
16
|
let queue:FrameRequestCallback[]=[];
|
|
17
17
|
const originalRAF=window.requestAnimationFrame;window.requestAnimationFrame=cb=>{queue.push(cb);return queue.length;};
|
|
18
|
-
let snapshot=Array.from({length:120},(_,i)=>({id:`n${i}`,anchor,color:'#ff0000',markupStyle:'strikeout',noteContent:`note ${i}`}));
|
|
18
|
+
let snapshot=Array.from({length:120},(_,i)=>({id:`n${i}`,anchor,color:'#ff0000',opacity:1,markupStyle:'strikeout',noteContent:`note ${i}`}));
|
|
19
19
|
const taps:string[]=[];
|
|
20
|
-
const renderer=annotations.install({document,window,range:()=>range},{href:'chapter.xhtml',documentId:undefined,annotations:()=>snapshot,labels:()=>({comment:'Nota'}),isCurrent:()=>true,onTap:(id:string)=>taps.push(id)});
|
|
20
|
+
const renderer=annotations.install({document,window,range:()=>range},{href:'chapter.xhtml',documentId:undefined,pageWidth:()=>1024,annotations:()=>snapshot,labels:()=>({comment:'Nota'}),isCurrent:()=>true,onTap:(id:string)=>taps.push(id)});
|
|
21
21
|
const flush=()=>{const tasks=queue;queue=[];tasks.forEach(cb=>cb(0));};flush();
|
|
22
22
|
expect(document.querySelectorAll('[data-annotation-id]')).toHaveLength(120);
|
|
23
23
|
expect(document.querySelectorAll('[data-papyrus-overlay] button')).toHaveLength(1);
|
|
24
24
|
const group=document.querySelector('[data-annotation-id="n0"]')!;
|
|
25
25
|
expect((group.firstChild as HTMLElement).style.top).toBe('30px');
|
|
26
|
+
expect((group.lastChild as HTMLElement).style.left).toBe('980px');
|
|
27
|
+
snapshot=snapshot.map(item=>({...item,markupStyle:'highlight'}));renderer.schedule();flush();
|
|
28
|
+
expect(Number((group.firstChild as HTMLElement).style.opacity)).toBeLessThanOrEqual(0.35);
|
|
29
|
+
snapshot=snapshot.map(item=>({...item,markupStyle:'strikeout'}));renderer.schedule();flush();
|
|
26
30
|
expect((group.lastChild as HTMLElement).textContent).toBe('120');
|
|
27
31
|
(group.lastChild as HTMLElement).click();expect(taps).toEqual(['n0']);
|
|
28
32
|
top=80;renderer.schedule();flush();expect(document.querySelector('[data-annotation-id="n0"]')).toBe(group);expect((group.firstChild as HTMLElement).style.top).toBe('90px');
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
(function(root){
|
|
2
|
+
function mode(value){return value==='chapter-scroll'?'chapter-scroll':'paged';}
|
|
3
|
+
function validCfi(value){return typeof value==='string' && value.length<=4096 && /^epubcfi\([^\n<>]+\)$/.test(value);}
|
|
4
|
+
function options(value){return {manager:'default',flow:mode(value)==='paged'?'paginated':'scrolled-doc',spread:'none',width:'100%',height:'100%',allowScriptedContent:false};}
|
|
5
|
+
function gesture(start,end,state){
|
|
6
|
+
if(!start || !end || state.selected || state.interactive || state.multitouch)return null;
|
|
7
|
+
var dx=end.x-start.x,dy=end.y-start.y,elapsed=end.time-start.time;
|
|
8
|
+
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';
|
|
9
|
+
if(elapsed>=0 && elapsed<450 && Math.hypot(dx,dy)<=12)return 'tap';
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function location(raw,count,readingMode){
|
|
13
|
+
var start=raw && raw.start;if(!start || !validCfi(start.cfi))return null;
|
|
14
|
+
var displayed=start.displayed||{},chapter=Math.max(1,(Number(start.index)||0)+1);
|
|
15
|
+
var visualPage=mode(readingMode)==='paged' && Number.isFinite(displayed.page)?displayed.page:null;
|
|
16
|
+
var total=mode(readingMode)==='paged' && Number.isFinite(displayed.total)?displayed.total:null;
|
|
17
|
+
var fraction=total?Math.max(0,(visualPage-1)/total):0;
|
|
18
|
+
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)};
|
|
19
|
+
}
|
|
20
|
+
function install(contents,config){
|
|
21
|
+
var doc=contents.document,win=contents.window,start=null,blocked=false;
|
|
22
|
+
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;}
|
|
23
|
+
function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
|
|
24
|
+
function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
|
|
25
|
+
function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
|
|
26
|
+
function move(e){if(e.touches.length!==1)blocked=true;}
|
|
27
|
+
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);}
|
|
28
|
+
function cancel(){start=null;blocked=true;}
|
|
29
|
+
doc.addEventListener('touchstart',begin,{passive:true});doc.addEventListener('touchmove',move,{passive:true});doc.addEventListener('touchend',end,{passive:true});doc.addEventListener('touchcancel',cancel,{passive:true});
|
|
30
|
+
return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);};
|
|
31
|
+
}
|
|
32
|
+
root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,install:install};
|
|
33
|
+
})(typeof window==='undefined'?globalThis:window);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {readFileSync} from 'node:fs';
|
|
2
|
+
import {JSDOM} from 'jsdom';
|
|
3
|
+
import {describe,it,expect} from 'vitest';
|
|
4
|
+
const load=()=>{const d=new JSDOM('',{runScripts:'outside-only'});d.window.eval(readFileSync('packages/ui-react-native/runtime/epubReader.js','utf8'));return (d.window as any).PapyrusEpubReader;};
|
|
5
|
+
describe('EPUB reading policy',()=>{
|
|
6
|
+
it('defaults to real paginated default manager and one visual page',()=>{const p=load();expect(p.options('paged')).toMatchObject({manager:'default',flow:'paginated',spread:'none'});expect(p.mode()).toBe('paged');expect(p.options('chapter-scroll').flow).toBe('scrolled-doc');});
|
|
7
|
+
it('recognizes one swipe with RTL reversal, excluding selection/interactivity/multitouch',()=>{const p=load(),start={x:200,y:100,time:0},end={x:40,y:108,time:200};expect(p.gesture(start,end,{mode:'paged'})).toBe('next');expect(p.gesture(start,end,{mode:'paged',rtl:true})).toBe('prev');for(const k of ['selected','interactive','multitouch'])expect(p.gesture(start,end,{mode:'paged',[k]:true})).toBe(null);expect(p.gesture(start,{x:202,y:103,time:600},{mode:'paged'})).toBe(null);expect(p.gesture(start,{x:202,y:103,time:100},{mode:'paged'})).toBe('tap');});
|
|
8
|
+
it('separates chapter and visual page and never treats spine count as pages',()=>{const p=load();expect(p.location({start:{index:2,cfi:'epubcfi(/6/6!/4/2)',displayed:{page:4,total:12}}},10,'paged')).toMatchObject({chapter:3,chapterCount:10,visualPage:4,visualPageCount:12,cfi:'epubcfi(/6/6!/4/2)'});expect(p.location({start:{index:2,cfi:'epubcfi(/6/6!/4/2)'}},10,'chapter-scroll').visualPage).toBe(null);});
|
|
9
|
+
it('rejects invalid location CFIs',()=>{expect(load().validCfi('javascript:bad')).toBe(false);expect(load().validCfi('epubcfi(/6/4!/4/2)')).toBe(true);});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('EPUB iframe gestures', () => {
|
|
13
|
+
it('cleans listeners and excludes annotations and links from tap', () => {
|
|
14
|
+
const d=new JSDOM('<p>text</p><a href="#">link</a>',{runScripts:'outside-only'});
|
|
15
|
+
d.window.eval(readFileSync('packages/ui-react-native/runtime/epubReader.js','utf8'));
|
|
16
|
+
const p=(d.window as any).PapyrusEpubReader;
|
|
17
|
+
let taps=0,hit=true;
|
|
18
|
+
const cleanup=p.install({document:d.window.document,window:d.window},{mode:()=> 'paged',rtl:()=>false,blocked:()=>false,current:()=>true,annotationHit:()=>hit,tap:()=>taps++,turn:()=>{}});
|
|
19
|
+
function touch(target:Element){for(const type of ['touchstart','touchend']){const e=new d.window.Event(type,{bubbles:true});Object.defineProperties(e,{touches:{value:type==='touchend'?[]:[{clientX:30,clientY:40}]},changedTouches:{value:[{clientX:30,clientY:40}]}});target.dispatchEvent(e);}}
|
|
20
|
+
touch(d.window.document.querySelector('p')!);expect(taps).toBe(0);hit=false;touch(d.window.document.querySelector('a')!);expect(taps).toBe(0);touch(d.window.document.querySelector('p')!);expect(taps).toBe(1);cleanup();touch(d.window.document.querySelector('p')!);expect(taps).toBe(1);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {readFileSync} from 'node:fs';
|
|
2
|
+
import {JSDOM} from 'jsdom';
|
|
3
|
+
import {describe,it,expect,vi} from 'vitest';
|
|
4
|
+
|
|
5
|
+
function runtime() {
|
|
6
|
+
const dom=new JSDOM('<div id="viewer"></div>',{runScripts:'outside-only'});
|
|
7
|
+
const messages:any[]=[], renditions:any[]=[];
|
|
8
|
+
(dom.window as any).ReactNativeWebView={postMessage:(raw:string)=>messages.push(JSON.parse(raw))};
|
|
9
|
+
const book:any={ready:Promise.resolve(),spine:{items:[{href:'a.xhtml'},{href:'b.xhtml'}]},loaded:{navigation:Promise.resolve({toc:[]})},destroy:vi.fn(),renderTo:vi.fn((_viewer,options)=>{
|
|
10
|
+
const events:any={};
|
|
11
|
+
const r:any={options,hooks:{content:{register:vi.fn()},unloaded:{register:vi.fn()}},on:(name:string,fn:any)=>events[name]=fn,themes:{fontSize:vi.fn()},destroy:vi.fn(),display:vi.fn(async()=>{}),next:vi.fn(async()=>{}),prev:vi.fn(async()=>{}),emit:(name:string,data:any)=>events[name]?.(data)};
|
|
12
|
+
renditions.push(r);return r;
|
|
13
|
+
})};
|
|
14
|
+
(dom.window as any).ePub=()=>book;
|
|
15
|
+
dom.window.eval(readFileSync('packages/ui-react-native/runtime/runtime.js','utf8'));
|
|
16
|
+
const send=async(id:string,kind:string,payload:any)=>{
|
|
17
|
+
dom.window.dispatchEvent(new dom.window.MessageEvent('message',{data:JSON.stringify({id,kind,payload})}));
|
|
18
|
+
for(let n=0;n<100;n++){const result=messages.find(m=>m.type==='response'&&m.id===id);if(result)return result;await new Promise(r=>setTimeout(r,1));}
|
|
19
|
+
throw new Error('No response: '+id);
|
|
20
|
+
};
|
|
21
|
+
return {send,messages,renditions,book,dom};
|
|
22
|
+
}
|
|
23
|
+
describe('EPUB runtime bridge',()=>{
|
|
24
|
+
it('loads paginated by default and emits visual location within the same chapter',async()=>{
|
|
25
|
+
const r=runtime();expect((await r.send('load','load',{type:'epub',documentSessionId:'s',source:{kind:'base64',data:'AA=='}})).ok).toBe(true);
|
|
26
|
+
expect(r.renditions[0].options.flow).toBe('paginated');
|
|
27
|
+
r.renditions[0].emit('relocated',{start:{index:0,href:'a.xhtml',cfi:'epubcfi(/6/2!/4/2:10)',displayed:{page:2,total:8}}});
|
|
28
|
+
expect(r.messages).toContainEqual(expect.objectContaining({name:'EPUB_LOCATION',payload:expect.objectContaining({location:expect.objectContaining({visualPage:2,chapter:1})})}));
|
|
29
|
+
});
|
|
30
|
+
it('rebuilds chapter scroll at the current CFI and rejects a stale session',async()=>{
|
|
31
|
+
const r=runtime();await r.send('load','load',{type:'epub',documentSessionId:'s',source:{kind:'base64',data:'AA=='}});
|
|
32
|
+
const cfi='epubcfi(/6/2!/4/2:10)';r.renditions[0].emit('relocated',{start:{index:0,cfi,displayed:{page:2,total:8}}});
|
|
33
|
+
expect((await r.send('mode','set-epub-view-mode',{epubMode:'chapter-scroll',documentSessionId:'s'})).ok).toBe(true);
|
|
34
|
+
expect(r.renditions[1].options.flow).toBe('scrolled-doc');expect(r.renditions[1].display).toHaveBeenCalledWith(cfi);
|
|
35
|
+
expect((await r.send('old','epub-go-to-location',{cfi,documentSessionId:'old'})).ok).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
it('serializes rapid mode changes and restores on the rebuilt rendition',async()=>{
|
|
38
|
+
const r=runtime();await r.send('load','load',{type:'epub',documentSessionId:'s',source:{kind:'base64',data:'AA=='}});
|
|
39
|
+
const cfi='epubcfi(/6/2!/4/2:10)';
|
|
40
|
+
await Promise.all([r.send('scroll','set-epub-view-mode',{epubMode:'chapter-scroll',documentSessionId:'s'}),r.send('paged','set-epub-view-mode',{epubMode:'paged',documentSessionId:'s'}),r.send('restore','epub-go-to-location',{cfi,documentSessionId:'s'})]);
|
|
41
|
+
expect(r.renditions.at(-1).options.flow).toBe('paginated');
|
|
42
|
+
expect(r.renditions.at(-1).display).toHaveBeenCalledWith(cfi);
|
|
43
|
+
});
|
|
44
|
+
it('keeps TXT zoom independent of EPUB state',async()=>{const r=runtime();await r.send('load','load',{type:'text',source:{kind:'text',text:'sample'}});expect((await r.send('zoom','set-zoom',{zoom:1.2})).ok).toBe(true);});
|
|
45
|
+
});
|
package/runtime/index.html
CHANGED
|
@@ -20483,6 +20483,42 @@ 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){
|
|
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 fraction=total?Math.max(0,(visualPage-1)/total):0;
|
|
20504
|
+
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)};
|
|
20505
|
+
}
|
|
20506
|
+
function install(contents,config){
|
|
20507
|
+
var doc=contents.document,win=contents.window,start=null,blocked=false;
|
|
20508
|
+
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;}
|
|
20509
|
+
function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
|
|
20510
|
+
function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
|
|
20511
|
+
function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
|
|
20512
|
+
function move(e){if(e.touches.length!==1)blocked=true;}
|
|
20513
|
+
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);}
|
|
20514
|
+
function cancel(){start=null;blocked=true;}
|
|
20515
|
+
doc.addEventListener('touchstart',begin,{passive:true});doc.addEventListener('touchmove',move,{passive:true});doc.addEventListener('touchend',end,{passive:true});doc.addEventListener('touchcancel',cancel,{passive:true});
|
|
20516
|
+
return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);};
|
|
20517
|
+
}
|
|
20518
|
+
root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,install:install};
|
|
20519
|
+
})(typeof window==='undefined'?globalThis:window);
|
|
20520
|
+
|
|
20521
|
+
/* @papyrus-epub-reader:end */
|
|
20486
20522
|
/* @papyrus-epub-annotations:start */
|
|
20487
20523
|
(function (root, factory) {
|
|
20488
20524
|
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
@@ -20561,6 +20597,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20561
20597
|
var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
|
|
20562
20598
|
var style = annotation.markupStyle || annotation.type;
|
|
20563
20599
|
var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
|
|
20600
|
+
if(style === 'highlight')opacity=Math.min(0.35,opacity);
|
|
20564
20601
|
rects.forEach(function(rect) {
|
|
20565
20602
|
var mark = doc.createElement('div');
|
|
20566
20603
|
var bodyRect=doc.body.getBoundingClientRect();
|
|
@@ -20573,8 +20610,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20573
20610
|
if (hasNote) {
|
|
20574
20611
|
var last = rects[rects.length-1], button = doc.createElement('button');
|
|
20575
20612
|
button.textContent = '●'; button.setAttribute('aria-label',options.labels().comment || '');
|
|
20576
|
-
|
|
20577
|
-
var
|
|
20613
|
+
var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
|
|
20614
|
+
var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
|
|
20615
|
+
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';
|
|
20616
|
+
var key = column+":"+Math.floor(last.top/44);
|
|
20578
20617
|
var neighbors=noteGroups.get(key);
|
|
20579
20618
|
if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
|
|
20580
20619
|
neighbors.ids.push(annotation.id);
|
|
@@ -20595,10 +20634,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20595
20634
|
doc.addEventListener('click',tapMarkup);
|
|
20596
20635
|
var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
|
|
20597
20636
|
if (observer) observer.observe(doc.body);
|
|
20598
|
-
win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
|
|
20637
|
+
win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
|
|
20599
20638
|
if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
|
|
20600
20639
|
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();}};
|
|
20640
|
+
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
20641
|
}
|
|
20603
20642
|
return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
|
|
20604
20643
|
});
|
|
@@ -21679,26 +21718,55 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21679
21718
|
pageCount,
|
|
21680
21719
|
});
|
|
21681
21720
|
|
|
21721
|
+
if (book.locations?.generate) {
|
|
21722
|
+
const activeBook=book;
|
|
21723
|
+
activeBook.locations.generate(150).then(() => {
|
|
21724
|
+
if (isCurrentLoad() && activeBook===book && epubLocation) {
|
|
21725
|
+
epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
|
|
21726
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
21727
|
+
}
|
|
21728
|
+
}).catch(() => {});
|
|
21729
|
+
}
|
|
21730
|
+
return renderEpubRendition(loadId,generation,null);
|
|
21731
|
+
};
|
|
21732
|
+
|
|
21733
|
+
const renderEpubRendition = async (loadId,generation,target) => {
|
|
21734
|
+
const diagnosticPayload={loadId};
|
|
21735
|
+
const isCurrentLoad=()=>generation===epubLoadGeneration;
|
|
21736
|
+
if(epubInteractionCleanup)epubInteractionCleanup();
|
|
21737
|
+
if(rendition)rendition.destroy();
|
|
21682
21738
|
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
|
-
});
|
|
21739
|
+
rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
|
|
21691
21740
|
sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
|
|
21692
21741
|
|
|
21693
21742
|
if (rendition && rendition.hooks && rendition.hooks.content) {
|
|
21694
21743
|
const session = documentSessionId;
|
|
21695
21744
|
const activeRendition = rendition;
|
|
21696
21745
|
rendition.hooks.content.register((contents) => {
|
|
21746
|
+
const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
|
|
21747
|
+
mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
|
|
21748
|
+
rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
|
|
21749
|
+
blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
|
|
21750
|
+
annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
|
|
21751
|
+
current: () => session === documentSessionId && activeRendition === rendition,
|
|
21752
|
+
tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
|
|
21753
|
+
turn: direction => {
|
|
21754
|
+
epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
|
|
21755
|
+
if(session !== documentSessionId || activeRendition !== rendition)return;
|
|
21756
|
+
return direction<0?activeRendition.prev():activeRendition.next();
|
|
21757
|
+
});
|
|
21758
|
+
epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
|
|
21759
|
+
},
|
|
21760
|
+
});
|
|
21761
|
+
const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
|
|
21762
|
+
epubSelectionCleanups.add(gestureCleanup);
|
|
21763
|
+
contents.__papyrusGestureCleanup=gestureCleanup;
|
|
21697
21764
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
21698
21765
|
if (href && window.PapyrusEpubAnnotations) {
|
|
21699
21766
|
const renderer = window.PapyrusEpubAnnotations.install(contents, {
|
|
21700
21767
|
href, get documentId() { return annotationDocumentId; },
|
|
21701
21768
|
isCurrent: () => session === documentSessionId && activeRendition === rendition,
|
|
21769
|
+
pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
|
|
21702
21770
|
annotations: () => epubAnnotations, labels: () => annotationLabels,
|
|
21703
21771
|
onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
|
|
21704
21772
|
});
|
|
@@ -21717,8 +21785,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21717
21785
|
typeof selectionWindow.getSelection === 'function'
|
|
21718
21786
|
) {
|
|
21719
21787
|
let selectionTimer = null;
|
|
21720
|
-
|
|
21721
|
-
let lastSelectionAt = 0;
|
|
21788
|
+
|
|
21722
21789
|
const reportSelection = () => {
|
|
21723
21790
|
selectionTimer = null;
|
|
21724
21791
|
if (session !== documentSessionId || activeRendition !== rendition) return;
|
|
@@ -21733,11 +21800,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21733
21800
|
try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
|
|
21734
21801
|
const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
|
|
21735
21802
|
if (!anchor) return;
|
|
21736
|
-
|
|
21737
|
-
const now = Date.now();
|
|
21738
|
-
if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
|
|
21739
|
-
lastSelectionKey = key;
|
|
21740
|
-
lastSelectionAt = now;
|
|
21803
|
+
if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
21741
21804
|
const payload = {documentSessionId: session, text: anchor.quote,
|
|
21742
21805
|
pageIndex: contents.sectionIndex + 1, anchor};
|
|
21743
21806
|
nativeMenuSelection = {contents, payload};
|
|
@@ -21752,6 +21815,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21752
21815
|
// The content document still emits selectionchange, so resolve its
|
|
21753
21816
|
// live Range to a CFI before the native menu action can fire.
|
|
21754
21817
|
if (text) reportSelection();
|
|
21818
|
+
else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
|
|
21755
21819
|
};
|
|
21756
21820
|
selectionDocument.addEventListener('selectionchange', handleSelectionChange);
|
|
21757
21821
|
const selectionCleanup = () => {
|
|
@@ -21774,6 +21838,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21774
21838
|
typeof rendition.hooks.unloaded.register === 'function'
|
|
21775
21839
|
) {
|
|
21776
21840
|
rendition.hooks.unloaded.register((view) => {
|
|
21841
|
+
view?.contents?.__papyrusGestureCleanup?.();
|
|
21777
21842
|
const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
|
|
21778
21843
|
if (typeof cleanup === 'function') cleanup();
|
|
21779
21844
|
const renderer = epubAnnotationRenderers.get(view?.contents);
|
|
@@ -21789,13 +21854,13 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21789
21854
|
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
21790
21855
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
21791
21856
|
const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
|
|
21792
|
-
if (!anchor) return;
|
|
21857
|
+
if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
21793
21858
|
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
|
|
21794
21859
|
const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
|
|
21795
21860
|
nativeMenuSelection = {contents, payload};
|
|
21796
21861
|
sendEvent('EPUB_TEXT_SELECTED', payload);
|
|
21797
21862
|
});
|
|
21798
|
-
['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
|
|
21863
|
+
['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
|
|
21799
21864
|
if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
21800
21865
|
}));
|
|
21801
21866
|
rendition.on('relocated', (location) => {
|
|
@@ -21805,6 +21870,11 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21805
21870
|
if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
|
|
21806
21871
|
if (index < 0 || index >= spineItems.length) return;
|
|
21807
21872
|
epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
|
|
21873
|
+
epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll');
|
|
21874
|
+
if(epubLocation){
|
|
21875
|
+
if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
|
|
21876
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
21877
|
+
}
|
|
21808
21878
|
const nextPage = index + 1;
|
|
21809
21879
|
if (nextPage === currentPage) return;
|
|
21810
21880
|
currentPage = nextPage;
|
|
@@ -21812,9 +21882,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21812
21882
|
});
|
|
21813
21883
|
}
|
|
21814
21884
|
|
|
21885
|
+
applyEpubZoom();
|
|
21815
21886
|
sendEpubDiagnostic('epub.display.start', diagnosticPayload);
|
|
21816
21887
|
await withEpubStageTimeout(
|
|
21817
|
-
|
|
21888
|
+
rendition.display(target || spineItems[0]?.href),
|
|
21818
21889
|
'rendition.display',
|
|
21819
21890
|
diagnosticPayload,
|
|
21820
21891
|
);
|
|
@@ -21826,7 +21897,6 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21826
21897
|
typeof rendition.manager.on === 'function' &&
|
|
21827
21898
|
typeof rendition.on === 'function'
|
|
21828
21899
|
) {
|
|
21829
|
-
let touchStart = null;
|
|
21830
21900
|
let lastScrollOffset = -1;
|
|
21831
21901
|
const manager = rendition.manager;
|
|
21832
21902
|
epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
|
|
@@ -21901,57 +21971,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21901
21971
|
return result;
|
|
21902
21972
|
};
|
|
21903
21973
|
}
|
|
21904
|
-
const
|
|
21905
|
-
const touch =
|
|
21906
|
-
(event && event.changedTouches && event.changedTouches[0]) ||
|
|
21907
|
-
(event && event.touches && event.touches[0]);
|
|
21908
|
-
if (!touch) return null;
|
|
21909
|
-
const x = Number(touch.screenX ?? touch.clientX);
|
|
21910
|
-
const y = Number(touch.screenY ?? touch.clientY);
|
|
21911
|
-
return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
|
|
21912
|
-
};
|
|
21913
|
-
const handleScroll = (event) => {
|
|
21974
|
+
const handleScroll = event => {
|
|
21914
21975
|
const offsetY = Math.max(0, Number(event && event.top) || 0);
|
|
21915
21976
|
epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
|
|
21916
|
-
if (Math.abs(offsetY
|
|
21917
|
-
lastScrollOffset
|
|
21918
|
-
sendEvent('VIEWER_SCROLL',
|
|
21919
|
-
};
|
|
21920
|
-
const handleTouchStart = (event) => {
|
|
21921
|
-
touchStart = event?.touches?.length > 1 ? null : getTouchPoint(event);
|
|
21922
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
|
|
21977
|
+
if (Math.abs(offsetY-lastScrollOffset)<1) return;
|
|
21978
|
+
lastScrollOffset=offsetY;
|
|
21979
|
+
sendEvent('VIEWER_SCROLL',{offsetY});
|
|
21923
21980
|
};
|
|
21924
|
-
const handleTouchMove = (event) => {
|
|
21925
|
-
if (event?.touches?.length > 1) touchStart = null;
|
|
21926
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
|
|
21927
|
-
};
|
|
21928
|
-
const handleTouchEnd = (event) => {
|
|
21929
|
-
const end = getTouchPoint(event);
|
|
21930
|
-
const start = touchStart;
|
|
21931
|
-
touchStart = null;
|
|
21932
|
-
const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
|
|
21933
|
-
if (!selectedText && start && end && epubViewMode === 'single' && Math.abs(end.x-start.x) >= 48 && Math.abs(end.x-start.x) > Math.abs(end.y-start.y)*1.5) {
|
|
21934
|
-
const active = rendition;
|
|
21935
|
-
const interactionSession = documentSessionId;
|
|
21936
|
-
epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
|
|
21937
|
-
if (active !== rendition || interactionSession !== documentSessionId) return;
|
|
21938
|
-
return end.x < start.x ? active.next() : active.prev();
|
|
21939
|
-
});
|
|
21940
|
-
epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR', {message:String(error)}));
|
|
21941
|
-
} else if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
|
|
21942
|
-
sendEvent('VIEWER_TAP', {});
|
|
21943
|
-
}
|
|
21944
|
-
epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
|
|
21945
|
-
};
|
|
21946
|
-
|
|
21947
21981
|
manager.on('scroll', handleScroll);
|
|
21948
21982
|
const handleScrolled = (event) => {
|
|
21949
21983
|
epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
|
|
21950
21984
|
};
|
|
21951
21985
|
manager.on('scrolled', handleScrolled);
|
|
21952
|
-
|
|
21953
|
-
rendition.on('touchmove', handleTouchMove);
|
|
21954
|
-
rendition.on('touchend', handleTouchEnd);
|
|
21986
|
+
|
|
21955
21987
|
epubInteractionCleanup = () => {
|
|
21956
21988
|
epubSelectionCleanups.forEach((cleanup) => cleanup());
|
|
21957
21989
|
epubSelectionCleanups.clear();
|
|
@@ -21960,16 +21992,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
21960
21992
|
if (originalEnqueue) queue.enqueue = originalEnqueue;
|
|
21961
21993
|
if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
|
|
21962
21994
|
if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
|
|
21963
|
-
if (typeof rendition.off === 'function') {
|
|
21964
|
-
rendition.off('touchstart', handleTouchStart);
|
|
21965
|
-
rendition.off('touchmove', handleTouchMove);
|
|
21966
|
-
rendition.off('touchend', handleTouchEnd);
|
|
21967
|
-
}
|
|
21968
21995
|
epubScrollDiagnostics = null;
|
|
21969
21996
|
};
|
|
21970
21997
|
}
|
|
21971
|
-
applyEpubZoom();
|
|
21972
|
-
|
|
21973
21998
|
const outline = await withEpubStageTimeout(
|
|
21974
21999
|
buildOutline(),
|
|
21975
22000
|
'outline',
|
|
@@ -22107,7 +22132,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22107
22132
|
};
|
|
22108
22133
|
|
|
22109
22134
|
let nativeMenuSelection = null;
|
|
22110
|
-
let epubViewMode = "
|
|
22135
|
+
let epubViewMode = "single";
|
|
22136
|
+
let epubLocation = null;
|
|
22111
22137
|
|
|
22112
22138
|
const handleCommand = async (message) => {
|
|
22113
22139
|
const { id, kind, payload } = message;
|
|
@@ -22120,7 +22146,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22120
22146
|
epubAnnotations = [];
|
|
22121
22147
|
annotationDocumentId = undefined;
|
|
22122
22148
|
zoom = 1.0;
|
|
22123
|
-
epubViewMode = "continuous";
|
|
22149
|
+
epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
|
|
22150
|
+
epubLocation = null;
|
|
22151
|
+
nativeMenuSelection = null;
|
|
22124
22152
|
epubNavigationRequest += 1;
|
|
22125
22153
|
epubNavigationQueue = Promise.resolve();
|
|
22126
22154
|
|
|
@@ -22234,25 +22262,40 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
22234
22262
|
|
|
22235
22263
|
if (kind === 'set-epub-view-mode') {
|
|
22236
22264
|
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
sendResponse(id,true,null);
|
|
22265
|
+
const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
|
|
22266
|
+
{
|
|
22267
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
22268
|
+
if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
|
|
22269
|
+
const target=epubLocation?.cfi;
|
|
22270
|
+
epubViewMode=next;
|
|
22271
|
+
await renderEpubRendition('layout',epubLoadGeneration,target);
|
|
22272
|
+
});
|
|
22273
|
+
await epubNavigationQueue;
|
|
22274
|
+
}
|
|
22275
|
+
sendResponse(id,true,null);return;
|
|
22276
|
+
}
|
|
22277
|
+
if(kind === 'epub-go-to-location'){
|
|
22278
|
+
if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
|
|
22279
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>payload.documentSessionId===documentSessionId?rendition.display(payload.cfi):undefined);
|
|
22280
|
+
await epubNavigationQueue;sendResponse(id,true,null);return;
|
|
22248
22281
|
}
|
|
22249
22282
|
|
|
22250
22283
|
if (kind === 'set-zoom') {
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
22284
|
+
const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
|
|
22285
|
+
if(currentType === 'epub') {
|
|
22286
|
+
const session=documentSessionId;
|
|
22287
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
22288
|
+
if(session!==documentSessionId)return;
|
|
22289
|
+
const active=rendition;
|
|
22290
|
+
const cfi=epubLocation?.cfi;
|
|
22291
|
+
zoom=requestedZoom;applyEpubZoom();
|
|
22292
|
+
// Let the content CSS update before redisplaying the logical anchor.
|
|
22293
|
+
await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
|
|
22294
|
+
if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
|
|
22295
|
+
});
|
|
22296
|
+
await epubNavigationQueue;
|
|
22297
|
+
} else {zoom=requestedZoom;applyZoom();}
|
|
22298
|
+
sendState();sendResponse(id,true,{zoom});return;
|
|
22256
22299
|
}
|
|
22257
22300
|
|
|
22258
22301
|
if (kind === 'set-rotation') {
|