@papyrus-sdk/ui-react-native 0.2.35-beta.9 → 0.2.36
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 +829 -236
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +843 -249
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -8
- package/runtime/epubAnnotations.js +276 -8
- package/runtime/epubReader.js +58 -0
- package/runtime/index.html +505 -84
- package/runtime/runtime.js +505 -84
- package/runtime/syncComicRuntime.mjs +6 -3
- package/runtime/comicRuntime.test.ts +0 -241
- package/runtime/documentLoadRuntime.test.ts +0 -172
- package/runtime/epubAnnotations.test.ts +0 -31
- package/runtime/epubScrollDiagnostics.test.ts +0 -107
- package/runtime/epubSelectionMenu.test.ts +0 -39
package/runtime/runtime.js
CHANGED
|
@@ -109,6 +109,67 @@
|
|
|
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,geometry){
|
|
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 atStart=Boolean(raw.atStart),atEnd=Boolean(raw.atEnd);
|
|
130
|
+
// Chromium may round a column offset down by a fraction of a CSS pixel.
|
|
131
|
+
// Keep epub.js authoritative outside a 1px boundary, and for RTL layouts.
|
|
132
|
+
if(visualPage!==null && total>0 && geometry && geometry.direction==='ltr' && geometry.pageWidth>0 && Number.isFinite(geometry.offset)){
|
|
133
|
+
var boundary=Math.round(geometry.offset/geometry.pageWidth);
|
|
134
|
+
if(Math.abs(geometry.offset-boundary*geometry.pageWidth)<=1){
|
|
135
|
+
visualPage=Math.min(total,Math.max(1,boundary+1));
|
|
136
|
+
atStart=chapter===(geometry.firstIndex ?? 0)+1 && visualPage===1;
|
|
137
|
+
atEnd=chapter===(geometry.lastIndex ?? count-1)+1 && visualPage===total;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
var fraction=total?Math.max(0,(visualPage-1)/total):0;
|
|
141
|
+
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};
|
|
142
|
+
}
|
|
143
|
+
function installResizeAnchor(manager,readCfi){
|
|
144
|
+
var original=manager.resize,anchor=null;
|
|
145
|
+
function resize(width,height,target){
|
|
146
|
+
if(validCfi(target))anchor=target;
|
|
147
|
+
if(!anchor){var current=readCfi();if(validCfi(current))anchor=current;}
|
|
148
|
+
// epub.js accepts a logical target as its third argument. Keep it across
|
|
149
|
+
// keyboard/dialog reflows instead of adopting each new viewport start.
|
|
150
|
+
return original.call(this,width,height,anchor || target);
|
|
151
|
+
}
|
|
152
|
+
manager.resize=resize;
|
|
153
|
+
return {cfi:function(){return anchor;},invalidate:function(){anchor=null;},destroy:function(){if(manager.resize===resize)manager.resize=original;anchor=null;}};
|
|
154
|
+
}
|
|
155
|
+
function install(contents,config){
|
|
156
|
+
var doc=contents.document,win=contents.window,start=null,blocked=false;
|
|
157
|
+
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;}
|
|
158
|
+
function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
|
|
159
|
+
function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay],[data-papyrus-ink-capture]'));}
|
|
160
|
+
function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
|
|
161
|
+
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();}
|
|
162
|
+
function move(e){if(e.touches.length!==1)blocked=true;invalidateScroll(point(e));}
|
|
163
|
+
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);}
|
|
164
|
+
function cancel(){start=null;blocked=true;}
|
|
165
|
+
function link(e){if(config.current() && e.target?.closest?.('a') && config.interact)config.interact();}
|
|
166
|
+
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);
|
|
167
|
+
return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);doc.removeEventListener('click',link);};
|
|
168
|
+
}
|
|
169
|
+
root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,installResizeAnchor:installResizeAnchor,install:install};
|
|
170
|
+
})(typeof window==='undefined'?globalThis:window);
|
|
171
|
+
|
|
172
|
+
/* @papyrus-epub-reader:end */
|
|
112
173
|
/* @papyrus-epub-annotations:start */
|
|
113
174
|
(function (root, factory) {
|
|
114
175
|
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
@@ -120,6 +181,84 @@
|
|
|
120
181
|
typeof anchor.href === 'string' && !/^(javascript|data):/i.test(anchor.href) &&
|
|
121
182
|
typeof anchor.quote === 'string' && anchor.quote.length > 0 && anchor.quote.length <= 100000;
|
|
122
183
|
}
|
|
184
|
+
function simplifyPath(points, tolerance, maxPoints) {
|
|
185
|
+
if (!Array.isArray(points) || points.length <= 2) return Array.isArray(points) ? points.slice() : [];
|
|
186
|
+
tolerance = Number.isFinite(tolerance) ? Math.max(0, tolerance) : 1.5;
|
|
187
|
+
maxPoints = Number.isSafeInteger(maxPoints) ? Math.max(2, maxPoints) : 512;
|
|
188
|
+
var keep = new Uint8Array(points.length); keep[0] = 1; keep[points.length - 1] = 1;
|
|
189
|
+
var stack = [[0, points.length - 1]], toleranceSquared = tolerance * tolerance;
|
|
190
|
+
while (stack.length) {
|
|
191
|
+
var interval = stack.pop(), first = interval[0], last = interval[1], a = points[first], b = points[last];
|
|
192
|
+
var dx = b.x - a.x, dy = b.y - a.y, lengthSquared = dx * dx + dy * dy, maxDistance = toleranceSquared, split = -1;
|
|
193
|
+
for (var i = first + 1; i < last; i++) {
|
|
194
|
+
var p = points[i], ratio = lengthSquared ? Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / lengthSquared)) : 0;
|
|
195
|
+
var px = a.x + ratio * dx, py = a.y + ratio * dy, ox = p.x - px, oy = p.y - py, distance = ox * ox + oy * oy;
|
|
196
|
+
if (distance > maxDistance) { maxDistance = distance; split = i; }
|
|
197
|
+
}
|
|
198
|
+
if (split >= 0) { keep[split] = 1; stack.push([first, split], [split, last]); }
|
|
199
|
+
}
|
|
200
|
+
var result = []; for (var j = 0; j < points.length; j++) if (keep[j]) result.push(points[j]);
|
|
201
|
+
if (result.length <= maxPoints) return result;
|
|
202
|
+
var capped = []; for (var k = 0; k < maxPoints; k++) capped.push(result[Math.round(k * (result.length - 1) / (maxPoints - 1))]);
|
|
203
|
+
return capped;
|
|
204
|
+
}
|
|
205
|
+
function compactGesturePoints(points, maxPoints) {
|
|
206
|
+
if (!Array.isArray(points)) return [];
|
|
207
|
+
maxPoints = Number.isSafeInteger(maxPoints) ? Math.max(2, maxPoints) : 1024;
|
|
208
|
+
if (points.length <= maxPoints) return points.slice();
|
|
209
|
+
var compacted = [];
|
|
210
|
+
for (var i = 0; i < maxPoints; i++) compacted.push(points[Math.round(i * (points.length - 1) / (maxPoints - 1))]);
|
|
211
|
+
return compacted;
|
|
212
|
+
}
|
|
213
|
+
function densifyPath(points, maxStep, maxPoints) {
|
|
214
|
+
if (!Array.isArray(points) || !points.length) return [];
|
|
215
|
+
if (points.length === 1) return points.slice();
|
|
216
|
+
maxStep = Number.isFinite(maxStep) ? Math.max(1, maxStep) : 12;
|
|
217
|
+
maxPoints = Number.isSafeInteger(maxPoints) ? Math.max(2, maxPoints) : 512;
|
|
218
|
+
var cumulative = [0], total = 0;
|
|
219
|
+
for (var i = 1; i < points.length; i++) {
|
|
220
|
+
if (![points[i-1].x,points[i-1].y,points[i].x,points[i].y].every(Number.isFinite)) return [];
|
|
221
|
+
total += Math.hypot(points[i].x-points[i-1].x, points[i].y-points[i-1].y);
|
|
222
|
+
cumulative.push(total);
|
|
223
|
+
}
|
|
224
|
+
if (!total) return [points[0], points[points.length-1]];
|
|
225
|
+
var intervals = Math.min(maxPoints-1, Math.max(1, Math.ceil(total/maxStep))), result = [], segment = 1;
|
|
226
|
+
for (var sample = 0; sample <= intervals; sample++) {
|
|
227
|
+
var distance = total*sample/intervals;
|
|
228
|
+
while (segment < cumulative.length-1 && cumulative[segment] < distance) segment++;
|
|
229
|
+
var start = points[segment-1], end = points[segment], span = cumulative[segment]-cumulative[segment-1];
|
|
230
|
+
var ratio = span ? (distance-cumulative[segment-1])/span : 0;
|
|
231
|
+
result.push({x:start.x+(end.x-start.x)*ratio,y:start.y+(end.y-start.y)*ratio});
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
function createSpatialIndex(entries, cellSize) {
|
|
236
|
+
cellSize = Number.isFinite(cellSize) ? Math.max(8, cellSize) : 64;
|
|
237
|
+
var cells = new Map();
|
|
238
|
+
entries.forEach(function(entry) {
|
|
239
|
+
var left = Math.floor(entry.left / cellSize), right = Math.floor(entry.right / cellSize);
|
|
240
|
+
var top = Math.floor(entry.top / cellSize), bottom = Math.floor(entry.bottom / cellSize);
|
|
241
|
+
for (var x = left; x <= right; x++) for (var y = top; y <= bottom; y++) {
|
|
242
|
+
var key = x + ':' + y, bucket = cells.get(key); if (!bucket) { bucket = []; cells.set(key, bucket); } bucket.push(entry);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return {cells:cells,cellSize:cellSize};
|
|
246
|
+
}
|
|
247
|
+
function queryNearestTextRect(index, x, y, maxDistance) {
|
|
248
|
+
if (!index || !Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(maxDistance) || maxDistance < 0) return null;
|
|
249
|
+
var size = index.cellSize, minX = Math.floor((x-maxDistance)/size), maxX = Math.floor((x+maxDistance)/size);
|
|
250
|
+
var minY = Math.floor((y-maxDistance)/size), maxY = Math.floor((y+maxDistance)/size), seen = new Set(), best = null;
|
|
251
|
+
for (var cx=minX; cx<=maxX; cx++) for (var cy=minY; cy<=maxY; cy++) {
|
|
252
|
+
var bucket = index.cells.get(cx+':'+cy); if (!bucket) continue;
|
|
253
|
+
bucket.forEach(function(entry) {
|
|
254
|
+
if (seen.has(entry)) return; seen.add(entry);
|
|
255
|
+
var nearestX = Math.max(entry.left,Math.min(entry.right,x)), nearestY = Math.max(entry.top,Math.min(entry.bottom,y));
|
|
256
|
+
var dx=x-nearestX,dy=y-nearestY,distance=Math.sqrt(dx*dx+dy*dy);
|
|
257
|
+
if (distance<=maxDistance && (!best || distance<best.distance)) best={entry:entry,distance:distance,x:nearestX,y:nearestY};
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return best;
|
|
261
|
+
}
|
|
123
262
|
function textNodes(document) {
|
|
124
263
|
var walker = document.createTreeWalker(document.body, 4);
|
|
125
264
|
var nodes = [], text = '', node;
|
|
@@ -169,13 +308,196 @@
|
|
|
169
308
|
if(win.getComputedStyle(doc.body).position==='static')doc.body.style.position='relative';
|
|
170
309
|
doc.body.appendChild(overlay);
|
|
171
310
|
var registry = new Map(), hitRegions = [];
|
|
311
|
+
var inkRegistry = new Map(), rangeCache = new Map(), spatialIndex = null, activeGesture = null, inkConfig = {enabled:false,preset:'ink'};
|
|
312
|
+
var inkSvg = doc.createElementNS('http://www.w3.org/2000/svg','svg');
|
|
313
|
+
inkSvg.setAttribute('data-papyrus-ink-overlay','');
|
|
314
|
+
inkSvg.style.cssText = 'position:absolute;left:0;top:0;overflow:visible;pointer-events:none;z-index:1';
|
|
315
|
+
overlay.appendChild(inkSvg);
|
|
316
|
+
var inkCanvas = doc.createElement('canvas');
|
|
317
|
+
inkCanvas.dataset.papyrusInkCapture = '';
|
|
318
|
+
inkCanvas.style.cssText = 'position:fixed;left:0;top:0;width:1px;height:1px;z-index:2147483000;background:transparent;pointer-events:none;touch-action:none';
|
|
319
|
+
doc.body.appendChild(inkCanvas);
|
|
320
|
+
var inkContext = null; try { if(typeof win.CanvasRenderingContext2D==='function')inkContext = inkCanvas.getContext('2d'); } catch (_) {}
|
|
321
|
+
function resizeCanvas() {
|
|
322
|
+
var bodyRect=doc.body.getBoundingClientRect(), ratio = Math.max(1,Math.min(3,win.devicePixelRatio || 1));
|
|
323
|
+
var width=Math.max(1,Math.min(4096,doc.body.clientWidth||bodyRect.width||Math.min(4096,win.innerWidth||1)));
|
|
324
|
+
var height=Math.max(1,Math.min(4096,doc.body.clientHeight||bodyRect.height||Math.min(4096,win.innerHeight||1)));
|
|
325
|
+
inkCanvas.style.width=width+'px';inkCanvas.style.height=height+'px';
|
|
326
|
+
inkCanvas.width = Math.round(width*ratio); inkCanvas.height = Math.round(height*ratio);
|
|
327
|
+
if (inkContext) { inkContext.setTransform(ratio,0,0,ratio,0,0); inkContext.clearRect(0,0,width,height); }
|
|
328
|
+
}
|
|
329
|
+
function collectTextRects() {
|
|
330
|
+
var walker = doc.createTreeWalker(doc.body,4), node, entries=[];
|
|
331
|
+
while ((node=walker.nextNode())) {
|
|
332
|
+
var parent=node.parentElement;
|
|
333
|
+
if (!parent || parent.closest('script,style,noscript,textarea,[data-papyrus-overlay],[data-papyrus-ink-capture]')) continue;
|
|
334
|
+
var text=node.nodeValue || '', style=win.getComputedStyle(parent), computed=parseFloat(style.lineHeight);
|
|
335
|
+
for (var start=0; start<text.length;) {
|
|
336
|
+
var end=Math.min(text.length,start+24), last=text.charCodeAt(end-1), next=text.charCodeAt(end);
|
|
337
|
+
if(end<text.length&&last>=0xD800&&last<=0xDBFF&&next>=0xDC00&&next<=0xDFFF)end++;
|
|
338
|
+
var range=doc.createRange(); range.setStart(node,start); range.setEnd(node,end);
|
|
339
|
+
var rects=[]; try { rects=Array.from(range.getClientRects()); } catch (_) {}
|
|
340
|
+
rects.forEach(function(rect) {
|
|
341
|
+
if (!(rect.width>0 && rect.height>0)) return;
|
|
342
|
+
entries.push({left:rect.left+win.scrollX,top:rect.top+win.scrollY,right:rect.right+win.scrollX,bottom:rect.bottom+win.scrollY,width:rect.width,height:rect.height,
|
|
343
|
+
node:node,start:start,end:end,lineHeight:Number.isFinite(computed)&&computed>0?computed:rect.height});
|
|
344
|
+
});
|
|
345
|
+
start=end;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return entries;
|
|
349
|
+
}
|
|
350
|
+
function rebuildSpatialIndex() { spatialIndex=createSpatialIndex(collectTextRects(),64); rangeCache.clear(); }
|
|
351
|
+
function inkViewportExtent() {
|
|
352
|
+
var root=doc.documentElement,body=doc.body,bodyRect=body.getBoundingClientRect();
|
|
353
|
+
var width=root.clientWidth||body.clientWidth||bodyRect.width||win.innerWidth||1;
|
|
354
|
+
var height=root.clientHeight||body.clientHeight||bodyRect.height||win.innerHeight||1;
|
|
355
|
+
return Math.max(1,Math.min(4096,width),Math.min(4096,height));
|
|
356
|
+
}
|
|
357
|
+
function inkStrokeWidthPx(width,preset) {
|
|
358
|
+
var scale=preset==='highlight'?0.8:preset==='underline'?0.1:0.15;
|
|
359
|
+
return Math.max(1,Math.min(24,(Number.isFinite(width)?width:0.006)*inkViewportExtent()*scale));
|
|
360
|
+
}
|
|
361
|
+
function closestGlyph(entry,x,y,maxDistance) {
|
|
362
|
+
var source=(entry.node.nodeValue || '').slice(entry.start,entry.end), best=null, offset=0;
|
|
363
|
+
for (var glyph of source) {
|
|
364
|
+
var range=doc.createRange(); range.setStart(entry.node,entry.start+offset); range.setEnd(entry.node,entry.start+offset+glyph.length); offset+=glyph.length;
|
|
365
|
+
var rects=[]; try { rects=Array.from(range.getClientRects()); } catch (_) {}
|
|
366
|
+
rects.forEach(function(rect) {
|
|
367
|
+
if (!(rect.width>0 && rect.height>0)) return;
|
|
368
|
+
var nx=Math.max(rect.left+win.scrollX,Math.min(rect.right+win.scrollX,x)), ny=Math.max(rect.top+win.scrollY,Math.min(rect.bottom+win.scrollY,y));
|
|
369
|
+
var dx=x-nx,dy=y-ny,distance=Math.sqrt(dx*dx+dy*dy);
|
|
370
|
+
if (distance<=maxDistance && (!best || distance<best.distance)) best={range:range,rect:rect,distance:distance};
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
if (!best || typeof contents.cfiFromRange!=='function') return null;
|
|
374
|
+
var quote=best.range.toString(), cfiRange; try { cfiRange=contents.cfiFromRange(best.range); } catch (_) { return null; }
|
|
375
|
+
if (!quote || typeof cfiRange!=='string' || !/^epubcfi\([^\n<>]+\)$/.test(cfiRange)) return null;
|
|
376
|
+
var lineHeight=Math.max(1,entry.lineHeight || best.rect.height), px=x-(best.rect.left+win.scrollX), py=y-(best.rect.top+win.scrollY);
|
|
377
|
+
return {cfiRange:cfiRange,quote:quote,x:Math.max(-3,Math.min(3,px/lineHeight)),y:Math.max(-3,Math.min(3,py/lineHeight))};
|
|
378
|
+
}
|
|
379
|
+
function renderInkAnnotation(annotation,keep,bodyRect) {
|
|
380
|
+
var baseRect=bodyRect || doc.body.getBoundingClientRect() || {left:0,top:0};
|
|
381
|
+
var data=annotation.epubInk;
|
|
382
|
+
if (!data || data.version!==1 || data.kind!=='epub-ink' || !Array.isArray(data.segments)) return;
|
|
383
|
+
keep.add(annotation.id);
|
|
384
|
+
var group=inkRegistry.get(annotation.id);
|
|
385
|
+
if (!group) { group=doc.createElementNS('http://www.w3.org/2000/svg','g'); group.setAttribute('data-epub-ink-id',annotation.id); inkSvg.appendChild(group); inkRegistry.set(annotation.id,group); }
|
|
386
|
+
group.replaceChildren();
|
|
387
|
+
var color=/^#[0-9a-f]{6}$/i.test(annotation.color)?annotation.color:'#2563eb';
|
|
388
|
+
var opacity=Number.isFinite(annotation.opacity)?Math.max(0,Math.min(1,annotation.opacity)):1;
|
|
389
|
+
var preset=['ink','highlight','underline'].indexOf(data.preset)>=0?data.preset:'ink';
|
|
390
|
+
var stroke=inkStrokeWidthPx(annotation.strokeWidth,preset);
|
|
391
|
+
data.segments.forEach(function(segment) {
|
|
392
|
+
if (segment.href!==options.href || !Array.isArray(segment.points)) return;
|
|
393
|
+
var paths=[], current=[];
|
|
394
|
+
segment.points.forEach(function(point) {
|
|
395
|
+
var range=rangeCache.get(point.cfiRange);
|
|
396
|
+
if (!range) { try { range=contents.range(point.cfiRange); } catch (_) { range=null; } if (range) rangeCache.set(point.cfiRange,range); }
|
|
397
|
+
if (!range || range.toString()!==point.quote) { if(current.length>1)paths.push(current);current=[];return; }
|
|
398
|
+
var rect; try { rect=range.getBoundingClientRect(); } catch (_) { rect=null; }
|
|
399
|
+
if (!rect || !(rect.width>0 && rect.height>0)) { if(current.length>1)paths.push(current);current=[];return; }
|
|
400
|
+
var lineHeight=Math.max(1,rect.height), x=rect.left-baseRect.left+point.x*lineHeight, y=rect.top-baseRect.top+point.y*lineHeight;
|
|
401
|
+
var previous=current[current.length-1], jumpLimit=Math.max(24,lineHeight*2.5);
|
|
402
|
+
if (previous && Math.hypot(x-previous.x,y-previous.y)>jumpLimit) { if(current.length>1)paths.push(current);current=[]; }
|
|
403
|
+
current.push({x:x,y:y,lineHeight:lineHeight});
|
|
404
|
+
});
|
|
405
|
+
if(current.length>1)paths.push(current);
|
|
406
|
+
paths.forEach(function(points) {
|
|
407
|
+
var d='M '+points[0].x.toFixed(1)+' '+points[0].y.toFixed(1);
|
|
408
|
+
for(var i=1;i<points.length;i++){var prev=points[i-1],point=points[i],mx=(prev.x+point.x)/2,my=(prev.y+point.y)/2;d+=' Q '+prev.x.toFixed(1)+' '+prev.y.toFixed(1)+' '+mx.toFixed(1)+' '+my.toFixed(1);}
|
|
409
|
+
var last=points[points.length-1];d+=' L '+last.x.toFixed(1)+' '+last.y.toFixed(1);
|
|
410
|
+
var path=doc.createElementNS('http://www.w3.org/2000/svg','path');path.setAttribute('d',d);path.setAttribute('fill','none');path.setAttribute('stroke',color);path.setAttribute('stroke-width',String(stroke));path.setAttribute('stroke-linecap','round');path.setAttribute('stroke-linejoin','round');path.setAttribute('opacity',String(opacity));group.appendChild(path);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
function clearPreview() {
|
|
415
|
+
if(inkContext)inkContext.clearRect(0,0,inkCanvas.width,inkCanvas.height);
|
|
416
|
+
inkSvg.querySelectorAll('[data-papyrus-ink-preview]').forEach(function(path){path.remove();});
|
|
417
|
+
}
|
|
418
|
+
function createPreviewPath(x,y) {
|
|
419
|
+
var path=doc.createElementNS('http://www.w3.org/2000/svg','path');
|
|
420
|
+
path.setAttribute('data-papyrus-ink-preview','');path.setAttribute('d','M '+x.toFixed(1)+' '+y.toFixed(1));path.setAttribute('fill','none');
|
|
421
|
+
path.setAttribute('stroke',inkConfig.color);path.setAttribute('stroke-width',String(inkStrokeWidthPx(inkConfig.strokeWidth,inkConfig.preset)));
|
|
422
|
+
path.setAttribute('stroke-linecap','round');path.setAttribute('stroke-linejoin','round');path.setAttribute('opacity',String(inkConfig.opacity));
|
|
423
|
+
path.style.pointerEvents='none';inkSvg.appendChild(path);return path;
|
|
424
|
+
}
|
|
425
|
+
function updatePreviewPath(clientX,clientY) {
|
|
426
|
+
var bodyRect=doc.body.getBoundingClientRect(),x=clientX-bodyRect.left,y=clientY-bodyRect.top;
|
|
427
|
+
var gesture=activeGesture;
|
|
428
|
+
if(!gesture.previewPath||gesture.previewPathPoints>=32){
|
|
429
|
+
gesture.previewPath=createPreviewPath(gesture.previewLast?gesture.previewLast.x:x,gesture.previewLast?gesture.previewLast.y:y);
|
|
430
|
+
gesture.previewPathPoints=1;
|
|
431
|
+
}
|
|
432
|
+
var pathData=gesture.previewPath.getAttribute('d')||'';
|
|
433
|
+
gesture.previewPath.setAttribute('d',pathData+' L '+x.toFixed(1)+' '+y.toFixed(1));
|
|
434
|
+
gesture.previewPathPoints++;
|
|
435
|
+
gesture.previewLast={x:x,y:y};
|
|
436
|
+
}
|
|
437
|
+
function cancelGesture() {
|
|
438
|
+
var gesture=activeGesture; activeGesture=null; clearPreview();
|
|
439
|
+
if(gesture)try{inkCanvas.releasePointerCapture(gesture.pointerId);}catch(_){}
|
|
440
|
+
}
|
|
441
|
+
function commitGesture() {
|
|
442
|
+
var gesture=activeGesture; activeGesture=null; clearPreview();
|
|
443
|
+
if(!gesture || !alive || !options.isCurrent() || !inkConfig.enabled || !spatialIndex) return;
|
|
444
|
+
var simplified=simplifyPath(gesture.points,1.5,512), anchorSamples=densifyPath(simplified,12,512), segments=[], current=[];
|
|
445
|
+
anchorSamples.forEach(function(point) {
|
|
446
|
+
var coarse=queryNearestTextRect(spatialIndex,point.x,point.y,28);
|
|
447
|
+
var cutoff=coarse?Math.max(12,Math.min(28,0.8*coarse.entry.lineHeight)):12;
|
|
448
|
+
var anchored=coarse && closestGlyph(coarse.entry,point.x,point.y,cutoff);
|
|
449
|
+
if(!anchored){if(current.length>1)segments.push({href:options.href,spineIndex:options.spineIndex,points:current});current=[];return;}
|
|
450
|
+
current.push(anchored);
|
|
451
|
+
});
|
|
452
|
+
if(current.length>1)segments.push({href:options.href,spineIndex:options.spineIndex,points:current});
|
|
453
|
+
segments=segments.slice(0,64);
|
|
454
|
+
var pointCount=segments.reduce(function(total,segment){return total+segment.points.length;},0);
|
|
455
|
+
if(!pointCount)return;
|
|
456
|
+
var width=Math.max(1,doc.documentElement.scrollWidth,win.innerWidth),height=Math.max(1,doc.documentElement.scrollHeight,win.innerHeight);
|
|
457
|
+
var normalized=simplified.map(function(p){return {x:Math.max(0,Math.min(1,p.x/width)),y:Math.max(0,Math.min(1,p.y/height))};});
|
|
458
|
+
var minX=Math.min.apply(null,normalized.map(function(p){return p.x;})),maxX=Math.max.apply(null,normalized.map(function(p){return p.x;}));
|
|
459
|
+
var minY=Math.min.apply(null,normalized.map(function(p){return p.y;})),maxY=Math.max.apply(null,normalized.map(function(p){return p.y;}));
|
|
460
|
+
var first=segments[0].points[0], href=segments[0].href, documentId=typeof options.documentId==='function'?options.documentId():options.documentId;
|
|
461
|
+
var annotation={id:'epub-ink-'+Date.now()+'-'+Math.random().toString(36).slice(2,9),type:'ink',pageIndex:(options.spineIndex||0)+1,
|
|
462
|
+
rect:{x:minX,y:minY,width:Math.min(1-minX,Math.max(0.001,maxX-minX)),height:Math.min(1-minY,Math.max(0.001,maxY-minY))},path:normalized,color:inkConfig.color,opacity:inkConfig.opacity,strokeWidth:inkConfig.strokeWidth,createdAt:Date.now(),content:first.quote,
|
|
463
|
+
anchor:{version:1,kind:'epub-cfi',cfiRange:first.cfiRange,href:href,spineIndex:options.spineIndex,documentId:documentId,quote:first.quote},
|
|
464
|
+
epubInk:{version:1,kind:'epub-ink',documentId:documentId,preset:inkConfig.preset,segments:segments}};
|
|
465
|
+
if(typeof options.onInkCommit==='function')options.onInkCommit(annotation);
|
|
466
|
+
}
|
|
467
|
+
function pointerDown(event) {
|
|
468
|
+
if(!inkConfig.enabled || !alive || !options.isCurrent() || event.button!==undefined && event.button!==0)return;
|
|
469
|
+
if(activeGesture){cancelGesture();return;}
|
|
470
|
+
event.preventDefault();event.stopPropagation();
|
|
471
|
+
activeGesture={pointerId:event.pointerId,points:[{x:event.clientX+win.scrollX,y:event.clientY+win.scrollY}],last:{x:event.clientX,y:event.clientY},previewPath:null,previewPathPoints:0,previewLast:null};
|
|
472
|
+
updatePreviewPath(event.clientX,event.clientY);
|
|
473
|
+
try{inkCanvas.setPointerCapture(event.pointerId);}catch(_){}
|
|
474
|
+
}
|
|
475
|
+
function pointerMove(event) {
|
|
476
|
+
if(!activeGesture || activeGesture.pointerId!==event.pointerId)return;
|
|
477
|
+
if(!options.isCurrent()||!inkConfig.enabled){cancelGesture();return;}
|
|
478
|
+
event.preventDefault();event.stopPropagation();
|
|
479
|
+
var point={x:event.clientX+win.scrollX,y:event.clientY+win.scrollY};activeGesture.points.push(point);
|
|
480
|
+
if(activeGesture.points.length>2048)activeGesture.points=compactGesturePoints(activeGesture.points,1024);
|
|
481
|
+
if(inkContext){inkContext.beginPath();inkContext.moveTo(activeGesture.last.x,activeGesture.last.y);inkContext.lineTo(event.clientX,event.clientY);inkContext.strokeStyle=inkConfig.color;inkContext.globalAlpha=inkConfig.opacity;inkContext.lineWidth=inkStrokeWidthPx(inkConfig.strokeWidth,inkConfig.preset);inkContext.lineCap='round';inkContext.lineJoin='round';inkContext.stroke();}
|
|
482
|
+
updatePreviewPath(event.clientX,event.clientY);
|
|
483
|
+
activeGesture.last={x:event.clientX,y:event.clientY};
|
|
484
|
+
}
|
|
485
|
+
function pointerUp(event) {if(!activeGesture || activeGesture.pointerId!==event.pointerId)return;event.preventDefault();event.stopPropagation();var end={x:event.clientX+win.scrollX,y:event.clientY+win.scrollY},last=activeGesture.points[activeGesture.points.length-1];if(Math.hypot(end.x-last.x,end.y-last.y)>0.5){activeGesture.points.push(end);updatePreviewPath(event.clientX,event.clientY);}commitGesture();}
|
|
486
|
+
function pointerCancel(event) {if(activeGesture && (event.pointerId===undefined || activeGesture.pointerId===event.pointerId))cancelGesture();}
|
|
487
|
+
resizeCanvas();rebuildSpatialIndex();
|
|
488
|
+
inkCanvas.addEventListener('pointerdown',pointerDown);inkCanvas.addEventListener('pointermove',pointerMove);inkCanvas.addEventListener('pointerup',pointerUp);inkCanvas.addEventListener('pointercancel',pointerCancel);inkCanvas.addEventListener('lostpointercapture',pointerCancel);
|
|
489
|
+
win.addEventListener('blur',pointerCancel);win.addEventListener('resize',resizeCanvas);
|
|
172
490
|
function render() {
|
|
173
491
|
frame = 0; if (!alive || !options.isCurrent()) return;
|
|
174
|
-
var keep = new Set(), noteGroups = new Map();
|
|
492
|
+
var keep = new Set(), keepInk = new Set(), noteGroups = new Map();
|
|
175
493
|
hitRegions = [];
|
|
494
|
+
var bodyRect=doc.body.getBoundingClientRect() || {left:0,top:0};
|
|
495
|
+
inkSvg.setAttribute('width',String(Math.max(doc.documentElement.scrollWidth,win.innerWidth)));
|
|
496
|
+
inkSvg.setAttribute('height',String(Math.max(doc.documentElement.scrollHeight,win.innerHeight)));
|
|
176
497
|
options.annotations().forEach(function(annotation) {
|
|
177
498
|
var anchor = annotation.anchor;
|
|
178
499
|
if (!validAnchor(anchor) || anchor.href !== options.href || anchor.documentId && options.documentId && anchor.documentId !== options.documentId) return;
|
|
500
|
+
if(annotation.type==='ink' && annotation.epubInk){renderInkAnnotation(annotation,keepInk,bodyRect);return;}
|
|
179
501
|
var range = resolveRange(contents,anchor); if (!range) return;
|
|
180
502
|
var rects = Array.from(range.getClientRects()).filter(function(r){return r.width>0 && r.height>0;});
|
|
181
503
|
if (!rects.length) return;
|
|
@@ -187,6 +509,7 @@
|
|
|
187
509
|
var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
|
|
188
510
|
var style = annotation.markupStyle || annotation.type;
|
|
189
511
|
var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
|
|
512
|
+
if(style === 'highlight')opacity=Math.min(0.35,opacity);
|
|
190
513
|
rects.forEach(function(rect) {
|
|
191
514
|
var mark = doc.createElement('div');
|
|
192
515
|
var bodyRect=doc.body.getBoundingClientRect();
|
|
@@ -199,8 +522,10 @@
|
|
|
199
522
|
if (hasNote) {
|
|
200
523
|
var last = rects[rects.length-1], button = doc.createElement('button');
|
|
201
524
|
button.textContent = '●'; button.setAttribute('aria-label',options.labels().comment || '');
|
|
202
|
-
|
|
203
|
-
var
|
|
525
|
+
var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
|
|
526
|
+
var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
|
|
527
|
+
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';
|
|
528
|
+
var key = column+":"+Math.floor(last.top/44);
|
|
204
529
|
var neighbors=noteGroups.get(key);
|
|
205
530
|
if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
|
|
206
531
|
neighbors.ids.push(annotation.id);
|
|
@@ -211,22 +536,26 @@
|
|
|
211
536
|
group.button.addEventListener('click',function(e){e.stopPropagation();if(options.isCurrent())options.onTap(group.ids[0],group.ids);});
|
|
212
537
|
});
|
|
213
538
|
registry.forEach(function(group,id){if (!keep.has(id)) {group.remove();registry.delete(id);}});
|
|
539
|
+
inkRegistry.forEach(function(group,id){if (!keepInk.has(id)) {group.remove();inkRegistry.delete(id);}});
|
|
214
540
|
}
|
|
215
541
|
function schedule() { if (alive && !frame) frame = win.requestAnimationFrame(render); }
|
|
542
|
+
function rebuildAndSchedule(){if(!alive)return;rebuildSpatialIndex();schedule();}
|
|
543
|
+
function handleResize(){resizeCanvas();rebuildAndSchedule();}
|
|
544
|
+
function handleDocumentLoad(){rebuildAndSchedule();}
|
|
216
545
|
function tapMarkup(event) {
|
|
217
546
|
if(!alive || !options.isCurrent() || String(win.getSelection() || '').length || event.target.closest('[data-papyrus-overlay]'))return;
|
|
218
547
|
var hit=hitRegions.find(function(item){return item.rects.some(function(r){return event.clientX>=r.left && event.clientX<=r.right && event.clientY>=r.top && event.clientY<=r.bottom;});});
|
|
219
548
|
if(hit) {event.stopPropagation();options.onTap(hit.id);}
|
|
220
549
|
}
|
|
221
550
|
doc.addEventListener('click',tapMarkup);
|
|
222
|
-
var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(
|
|
551
|
+
var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(rebuildAndSchedule) : null;
|
|
223
552
|
if (observer) observer.observe(doc.body);
|
|
224
|
-
win.addEventListener('resize',schedule); doc.addEventListener('load',
|
|
225
|
-
if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
|
|
553
|
+
win.addEventListener('resize',handleResize);win.addEventListener('scroll',schedule); doc.addEventListener('load',handleDocumentLoad,true);
|
|
554
|
+
if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(function(){if(alive){rebuildSpatialIndex();schedule();}});
|
|
226
555
|
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',
|
|
556
|
+
return {schedule:schedule, setInkMode:function(config){var preset=['ink','highlight','underline'].indexOf(config?.preset)>=0?config.preset:'ink';inkConfig={enabled:!!config?.enabled,preset:preset,color:/^#[0-9a-f]{6}$/i.test(config?.color)?config.color:'#2563eb',opacity:Number.isFinite(config?.opacity)?Math.max(0,Math.min(1,config.opacity)):1,strokeWidth:Number.isFinite(config?.strokeWidth)?Math.max(0.0025,Math.min(0.02,config.strokeWidth)):0.006};inkCanvas.style.pointerEvents=inkConfig.enabled?'auto':'none';if(!inkConfig.enabled)cancelGesture();}, invalidateLayout:rebuildAndSchedule, 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;cancelGesture();if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',handleResize);win.removeEventListener('scroll',schedule);win.removeEventListener('blur',pointerCancel);doc.removeEventListener('load',handleDocumentLoad,true);doc.removeEventListener('click',tapMarkup);inkCanvas.removeEventListener('pointerdown',pointerDown);inkCanvas.removeEventListener('pointermove',pointerMove);inkCanvas.removeEventListener('pointerup',pointerUp);inkCanvas.removeEventListener('pointercancel',pointerCancel);inkCanvas.removeEventListener('lostpointercapture',pointerCancel);inkCanvas.remove();overlay.remove();doc.body.style.position=originalPosition;registry.clear();inkRegistry.clear();rangeCache.clear();}};
|
|
228
557
|
}
|
|
229
|
-
return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
|
|
558
|
+
return {validAnchor:validAnchor,simplifyPath:simplifyPath,compactGesturePoints:compactGesturePoints,densifyPath:densifyPath,createSpatialIndex:createSpatialIndex,queryNearestTextRect:queryNearestTextRect,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
|
|
230
559
|
});
|
|
231
560
|
|
|
232
561
|
/* @papyrus-epub-annotations:end */
|
|
@@ -237,10 +566,12 @@
|
|
|
237
566
|
let currentType = null;
|
|
238
567
|
let book = null;
|
|
239
568
|
let rendition = null;
|
|
569
|
+
let epubResizeAnchor = null;
|
|
240
570
|
let documentSessionId = null;
|
|
241
571
|
let annotationDocumentId;
|
|
242
572
|
let annotationLabels = {};
|
|
243
573
|
let epubAnnotations = [];
|
|
574
|
+
let epubInkMode = {enabled:false,color:'#2563eb',opacity:1,strokeWidth:0.006};
|
|
244
575
|
const epubAnnotationRenderers = new Map();
|
|
245
576
|
let spineItems = [];
|
|
246
577
|
let textPages = [];
|
|
@@ -649,6 +980,7 @@
|
|
|
649
980
|
};
|
|
650
981
|
|
|
651
982
|
const clearViewer = () => {
|
|
983
|
+
if(epubResizeAnchor){epubResizeAnchor.destroy();epubResizeAnchor=null;}
|
|
652
984
|
epubAnnotationRenderers.forEach(renderer => renderer.destroy());
|
|
653
985
|
epubAnnotationRenderers.clear();
|
|
654
986
|
if (epubInteractionCleanup) {
|
|
@@ -1305,29 +1637,63 @@
|
|
|
1305
1637
|
pageCount,
|
|
1306
1638
|
});
|
|
1307
1639
|
|
|
1640
|
+
if (book.locations?.generate) {
|
|
1641
|
+
const activeBook=book;
|
|
1642
|
+
activeBook.locations.generate(150).then(() => {
|
|
1643
|
+
if (isCurrentLoad() && activeBook===book && epubLocation) {
|
|
1644
|
+
epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
|
|
1645
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
1646
|
+
}
|
|
1647
|
+
}).catch(() => {});
|
|
1648
|
+
}
|
|
1649
|
+
return renderEpubRendition(loadId,generation,null);
|
|
1650
|
+
};
|
|
1651
|
+
|
|
1652
|
+
const renderEpubRendition = async (loadId,generation,target) => {
|
|
1653
|
+
const diagnosticPayload={loadId};
|
|
1654
|
+
const isCurrentLoad=()=>generation===epubLoadGeneration;
|
|
1655
|
+
if(epubInteractionCleanup)epubInteractionCleanup();
|
|
1656
|
+
if(rendition)rendition.destroy();
|
|
1308
1657
|
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
|
-
});
|
|
1658
|
+
rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
|
|
1317
1659
|
sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
|
|
1318
1660
|
|
|
1319
1661
|
if (rendition && rendition.hooks && rendition.hooks.content) {
|
|
1320
1662
|
const session = documentSessionId;
|
|
1321
1663
|
const activeRendition = rendition;
|
|
1322
1664
|
rendition.hooks.content.register((contents) => {
|
|
1665
|
+
const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
|
|
1666
|
+
mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
|
|
1667
|
+
rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
|
|
1668
|
+
blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
|
|
1669
|
+
annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
|
|
1670
|
+
current: () => session === documentSessionId && activeRendition === rendition,
|
|
1671
|
+
interact: () => epubResizeAnchor?.invalidate(),
|
|
1672
|
+
tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
|
|
1673
|
+
turn: direction => {
|
|
1674
|
+
epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
|
|
1675
|
+
if(session !== documentSessionId || activeRendition !== rendition)return;
|
|
1676
|
+
epubResizeAnchor?.invalidate();
|
|
1677
|
+
return direction<0?activeRendition.prev():activeRendition.next();
|
|
1678
|
+
});
|
|
1679
|
+
epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
|
|
1680
|
+
},
|
|
1681
|
+
});
|
|
1682
|
+
const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
|
|
1683
|
+
epubSelectionCleanups.add(gestureCleanup);
|
|
1684
|
+
contents.__papyrusGestureCleanup=gestureCleanup;
|
|
1323
1685
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
1324
1686
|
if (href && window.PapyrusEpubAnnotations) {
|
|
1325
1687
|
const renderer = window.PapyrusEpubAnnotations.install(contents, {
|
|
1326
1688
|
href, get documentId() { return annotationDocumentId; },
|
|
1689
|
+
spineIndex:contents.sectionIndex,
|
|
1327
1690
|
isCurrent: () => session === documentSessionId && activeRendition === rendition,
|
|
1691
|
+
pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
|
|
1328
1692
|
annotations: () => epubAnnotations, labels: () => annotationLabels,
|
|
1693
|
+
onInkCommit: annotation => sendEvent('EPUB_INK_CREATED',{documentSessionId:session,annotation}),
|
|
1329
1694
|
onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
|
|
1330
1695
|
});
|
|
1696
|
+
renderer.setInkMode(epubInkMode);
|
|
1331
1697
|
epubAnnotationRenderers.set(contents,renderer);
|
|
1332
1698
|
}
|
|
1333
1699
|
const frame = contents && contents.document ? contents.document.defaultView.frameElement : null;
|
|
@@ -1343,8 +1709,7 @@
|
|
|
1343
1709
|
typeof selectionWindow.getSelection === 'function'
|
|
1344
1710
|
) {
|
|
1345
1711
|
let selectionTimer = null;
|
|
1346
|
-
|
|
1347
|
-
let lastSelectionAt = 0;
|
|
1712
|
+
|
|
1348
1713
|
const reportSelection = () => {
|
|
1349
1714
|
selectionTimer = null;
|
|
1350
1715
|
if (session !== documentSessionId || activeRendition !== rendition) return;
|
|
@@ -1359,11 +1724,7 @@
|
|
|
1359
1724
|
try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
|
|
1360
1725
|
const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
|
|
1361
1726
|
if (!anchor) return;
|
|
1362
|
-
|
|
1363
|
-
const now = Date.now();
|
|
1364
|
-
if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
|
|
1365
|
-
lastSelectionKey = key;
|
|
1366
|
-
lastSelectionAt = now;
|
|
1727
|
+
if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
1367
1728
|
const payload = {documentSessionId: session, text: anchor.quote,
|
|
1368
1729
|
pageIndex: contents.sectionIndex + 1, anchor};
|
|
1369
1730
|
nativeMenuSelection = {contents, payload};
|
|
@@ -1378,6 +1739,7 @@
|
|
|
1378
1739
|
// The content document still emits selectionchange, so resolve its
|
|
1379
1740
|
// live Range to a CFI before the native menu action can fire.
|
|
1380
1741
|
if (text) reportSelection();
|
|
1742
|
+
else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
|
|
1381
1743
|
};
|
|
1382
1744
|
selectionDocument.addEventListener('selectionchange', handleSelectionChange);
|
|
1383
1745
|
const selectionCleanup = () => {
|
|
@@ -1400,6 +1762,7 @@
|
|
|
1400
1762
|
typeof rendition.hooks.unloaded.register === 'function'
|
|
1401
1763
|
) {
|
|
1402
1764
|
rendition.hooks.unloaded.register((view) => {
|
|
1765
|
+
view?.contents?.__papyrusGestureCleanup?.();
|
|
1403
1766
|
const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
|
|
1404
1767
|
if (typeof cleanup === 'function') cleanup();
|
|
1405
1768
|
const renderer = epubAnnotationRenderers.get(view?.contents);
|
|
@@ -1415,21 +1778,37 @@
|
|
|
1415
1778
|
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
1416
1779
|
const href = spineItems[contents.sectionIndex]?.href;
|
|
1417
1780
|
const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
|
|
1418
|
-
if (!anchor) return;
|
|
1781
|
+
if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
|
|
1419
1782
|
epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
|
|
1420
1783
|
const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
|
|
1421
1784
|
nativeMenuSelection = {contents, payload};
|
|
1422
1785
|
sendEvent('EPUB_TEXT_SELECTED', payload);
|
|
1423
1786
|
});
|
|
1424
|
-
['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
|
|
1425
|
-
if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
1787
|
+
['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
|
|
1788
|
+
if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => name==='relocated'?renderer.schedule():renderer.invalidateLayout());
|
|
1426
1789
|
}));
|
|
1427
1790
|
rendition.on('relocated', (location) => {
|
|
1791
|
+
if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
|
|
1428
1792
|
const start = location && location.start;
|
|
1429
1793
|
let index = start && Number.isInteger(start.index) ? start.index : -1;
|
|
1430
1794
|
if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
|
|
1431
1795
|
if (index < 0 || index >= spineItems.length) return;
|
|
1432
1796
|
epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
|
|
1797
|
+
let pageGeometry;
|
|
1798
|
+
const manager=rendition.manager,layout=manager?.layout;
|
|
1799
|
+
if(epubViewMode==='single' && layout?.divisor===1 && !manager?.settings?.fullsize && manager?.settings?.direction==='ltr'){
|
|
1800
|
+
const visible=manager.visible?.();
|
|
1801
|
+
const view=visible?.find(view=>view.index===index);
|
|
1802
|
+
if(view && manager.container){
|
|
1803
|
+
pageGeometry={direction:'ltr',firstIndex:book.spine.first()?.index,lastIndex:book.spine.last()?.index,pageWidth:layout.pageWidth,offset:manager.container.getBoundingClientRect().left-view.position().left};
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll',pageGeometry);
|
|
1807
|
+
if(epubLocation){
|
|
1808
|
+
epubLocation.cfi=epubResizeAnchor?.cfi() || epubLocation.cfi;
|
|
1809
|
+
if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
|
|
1810
|
+
sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
|
|
1811
|
+
}
|
|
1433
1812
|
const nextPage = index + 1;
|
|
1434
1813
|
if (nextPage === currentPage) return;
|
|
1435
1814
|
currentPage = nextPage;
|
|
@@ -1437,21 +1816,23 @@
|
|
|
1437
1816
|
});
|
|
1438
1817
|
}
|
|
1439
1818
|
|
|
1819
|
+
applyEpubZoom();
|
|
1440
1820
|
sendEpubDiagnostic('epub.display.start', diagnosticPayload);
|
|
1441
1821
|
await withEpubStageTimeout(
|
|
1442
|
-
|
|
1822
|
+
rendition.display(target || spineItems[0]?.href),
|
|
1443
1823
|
'rendition.display',
|
|
1444
1824
|
diagnosticPayload,
|
|
1445
1825
|
);
|
|
1446
1826
|
if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
|
|
1447
1827
|
sendEpubDiagnostic('epub.display.end', diagnosticPayload);
|
|
1828
|
+
const resizeRendition=rendition;
|
|
1829
|
+
epubResizeAnchor=window.PapyrusEpubReader.installResizeAnchor(resizeRendition.manager,()=>resizeRendition.location?.start?.cfi);
|
|
1448
1830
|
if (
|
|
1449
1831
|
rendition &&
|
|
1450
1832
|
rendition.manager &&
|
|
1451
1833
|
typeof rendition.manager.on === 'function' &&
|
|
1452
1834
|
typeof rendition.on === 'function'
|
|
1453
1835
|
) {
|
|
1454
|
-
let touchStart = null;
|
|
1455
1836
|
let lastScrollOffset = -1;
|
|
1456
1837
|
const manager = rendition.manager;
|
|
1457
1838
|
epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
|
|
@@ -1526,48 +1907,19 @@
|
|
|
1526
1907
|
return result;
|
|
1527
1908
|
};
|
|
1528
1909
|
}
|
|
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) => {
|
|
1910
|
+
const handleScroll = event => {
|
|
1539
1911
|
const offsetY = Math.max(0, Number(event && event.top) || 0);
|
|
1540
1912
|
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);
|
|
1913
|
+
if (Math.abs(offsetY-lastScrollOffset)<1) return;
|
|
1914
|
+
lastScrollOffset=offsetY;
|
|
1915
|
+
sendEvent('VIEWER_SCROLL',{offsetY});
|
|
1561
1916
|
};
|
|
1562
|
-
|
|
1563
1917
|
manager.on('scroll', handleScroll);
|
|
1564
1918
|
const handleScrolled = (event) => {
|
|
1565
1919
|
epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
|
|
1566
1920
|
};
|
|
1567
1921
|
manager.on('scrolled', handleScrolled);
|
|
1568
|
-
|
|
1569
|
-
rendition.on('touchmove', handleTouchMove);
|
|
1570
|
-
rendition.on('touchend', handleTouchEnd);
|
|
1922
|
+
|
|
1571
1923
|
epubInteractionCleanup = () => {
|
|
1572
1924
|
epubSelectionCleanups.forEach((cleanup) => cleanup());
|
|
1573
1925
|
epubSelectionCleanups.clear();
|
|
@@ -1576,16 +1928,9 @@
|
|
|
1576
1928
|
if (originalEnqueue) queue.enqueue = originalEnqueue;
|
|
1577
1929
|
if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
|
|
1578
1930
|
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
1931
|
epubScrollDiagnostics = null;
|
|
1585
1932
|
};
|
|
1586
1933
|
}
|
|
1587
|
-
applyEpubZoom();
|
|
1588
|
-
|
|
1589
1934
|
const outline = await withEpubStageTimeout(
|
|
1590
1935
|
buildOutline(),
|
|
1591
1936
|
'outline',
|
|
@@ -1599,6 +1944,8 @@
|
|
|
1599
1944
|
return { pageCount, outline };
|
|
1600
1945
|
};
|
|
1601
1946
|
|
|
1947
|
+
let epubNavigationQueue = Promise.resolve();
|
|
1948
|
+
let epubNavigationRequest = 0;
|
|
1602
1949
|
const displayEpubPage = async (pageIndex) => {
|
|
1603
1950
|
if (!rendition) return;
|
|
1604
1951
|
const item = spineItems[pageIndex];
|
|
@@ -1608,9 +1955,19 @@
|
|
|
1608
1955
|
target,
|
|
1609
1956
|
pageIndex,
|
|
1610
1957
|
});
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1958
|
+
const activeRendition = rendition;
|
|
1959
|
+
const session = documentSessionId;
|
|
1960
|
+
const request = ++epubNavigationRequest;
|
|
1961
|
+
const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
|
|
1962
|
+
if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
|
|
1963
|
+
epubResizeAnchor?.invalidate();
|
|
1964
|
+
await activeRendition.display(target);
|
|
1965
|
+
if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
|
|
1966
|
+
currentPage = pageIndex + 1;
|
|
1967
|
+
sendState();
|
|
1968
|
+
});
|
|
1969
|
+
epubNavigationQueue = navigation;
|
|
1970
|
+
await navigation;
|
|
1614
1971
|
};
|
|
1615
1972
|
|
|
1616
1973
|
const getTextContent = async (pageIndex) => {
|
|
@@ -1712,6 +2069,8 @@
|
|
|
1712
2069
|
};
|
|
1713
2070
|
|
|
1714
2071
|
let nativeMenuSelection = null;
|
|
2072
|
+
let epubViewMode = "single";
|
|
2073
|
+
let epubLocation = null;
|
|
1715
2074
|
|
|
1716
2075
|
const handleCommand = async (message) => {
|
|
1717
2076
|
const { id, kind, payload } = message;
|
|
@@ -1723,7 +2082,13 @@
|
|
|
1723
2082
|
documentSessionId = payload.documentSessionId || null;
|
|
1724
2083
|
epubAnnotations = [];
|
|
1725
2084
|
annotationDocumentId = undefined;
|
|
2085
|
+
epubInkMode = {enabled:false,color:'#2563eb',opacity:1,strokeWidth:0.006};
|
|
1726
2086
|
zoom = 1.0;
|
|
2087
|
+
epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
|
|
2088
|
+
epubLocation = null;
|
|
2089
|
+
nativeMenuSelection = null;
|
|
2090
|
+
epubNavigationRequest += 1;
|
|
2091
|
+
epubNavigationQueue = Promise.resolve();
|
|
1727
2092
|
|
|
1728
2093
|
if (currentType === 'text') {
|
|
1729
2094
|
const result = await loadText(
|
|
@@ -1778,17 +2143,24 @@
|
|
|
1778
2143
|
epubAnnotationRenderers.forEach(renderer => renderer.schedule());
|
|
1779
2144
|
} else {
|
|
1780
2145
|
const annotation = epubAnnotations.find(a => a.id === payload.annotationId);
|
|
1781
|
-
if (!annotation) throw new Error('Annotation not available');
|
|
2146
|
+
if (!annotation || (annotation.anchor.documentId && annotation.anchor.documentId !== annotationDocumentId)) throw new Error('Annotation not available for this document');
|
|
1782
2147
|
// Navigation uses the CFI, never a synthetic percentage/page rectangle.
|
|
1783
2148
|
const navigationSession = documentSessionId;
|
|
1784
2149
|
const activeRendition = rendition;
|
|
1785
2150
|
// Open the confirmed chapter first, resolve its quote, then navigate its actual range.
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
2151
|
+
epubNavigationRequest += 1;
|
|
2152
|
+
const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
|
|
2153
|
+
if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
|
|
2154
|
+
epubResizeAnchor?.invalidate();
|
|
2155
|
+
await activeRendition.display(annotation.anchor.href);
|
|
2156
|
+
if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
|
|
2157
|
+
const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
|
|
2158
|
+
const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
|
|
2159
|
+
if(!range)throw new Error('EPUB anchor unresolved');
|
|
2160
|
+
await activeRendition.display(contents.cfiFromRange(range));
|
|
2161
|
+
});
|
|
2162
|
+
epubNavigationQueue = navigation;
|
|
2163
|
+
await navigation;
|
|
1792
2164
|
}
|
|
1793
2165
|
sendResponse(id,true,null); return;
|
|
1794
2166
|
}
|
|
@@ -1815,12 +2187,61 @@
|
|
|
1815
2187
|
return;
|
|
1816
2188
|
}
|
|
1817
2189
|
|
|
2190
|
+
if (kind === 'epub-turn-page') {
|
|
2191
|
+
if (epubViewMode !== 'single') {sendResponse(id,true,null); return;}
|
|
2192
|
+
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB navigation');
|
|
2193
|
+
const active = rendition;
|
|
2194
|
+
epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
|
|
2195
|
+
if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
|
|
2196
|
+
epubResizeAnchor?.invalidate();
|
|
2197
|
+
return payload.direction < 0 ? active.prev() : active.next();
|
|
2198
|
+
});
|
|
2199
|
+
await epubNavigationQueue;
|
|
2200
|
+
sendResponse(id,true,null); return;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
if (kind === 'set-epub-view-mode') {
|
|
2204
|
+
if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
|
|
2205
|
+
const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
|
|
2206
|
+
{
|
|
2207
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
2208
|
+
if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
|
|
2209
|
+
const target=epubResizeAnchor?.cfi() || epubLocation?.cfi;
|
|
2210
|
+
epubViewMode=next;
|
|
2211
|
+
await renderEpubRendition('layout',epubLoadGeneration,target);
|
|
2212
|
+
});
|
|
2213
|
+
await epubNavigationQueue;
|
|
2214
|
+
}
|
|
2215
|
+
sendResponse(id,true,null);return;
|
|
2216
|
+
}
|
|
2217
|
+
if (kind === 'set-epub-ink-mode') {
|
|
2218
|
+
if(currentType!=='epub'||!documentSessionId||payload.documentSessionId!==documentSessionId)throw new Error('Stale EPUB ink session');
|
|
2219
|
+
epubInkMode={enabled:payload.enabled===true,preset:['ink','highlight','underline'].indexOf(payload.preset)>=0?payload.preset:'ink',color:/^#[0-9a-f]{6}$/i.test(payload.color||'')?payload.color:'#2563eb',opacity:Number.isFinite(payload.opacity)?Math.max(0,Math.min(1,payload.opacity)):1,strokeWidth:Number.isFinite(payload.strokeWidth)?Math.max(0.0025,Math.min(0.02,payload.strokeWidth)):0.006};
|
|
2220
|
+
epubAnnotationRenderers.forEach(renderer=>renderer.setInkMode(epubInkMode));
|
|
2221
|
+
sendResponse(id,true,null);return;
|
|
2222
|
+
}
|
|
2223
|
+
if(kind === 'epub-go-to-location'){
|
|
2224
|
+
if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
|
|
2225
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});
|
|
2226
|
+
await epubNavigationQueue;sendResponse(id,true,null);return;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
1818
2229
|
if (kind === 'set-zoom') {
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2230
|
+
const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
|
|
2231
|
+
if(currentType === 'epub') {
|
|
2232
|
+
const session=documentSessionId;
|
|
2233
|
+
epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
|
|
2234
|
+
if(session!==documentSessionId)return;
|
|
2235
|
+
const active=rendition;
|
|
2236
|
+
const cfi=epubResizeAnchor?.cfi() || epubLocation?.cfi;
|
|
2237
|
+
zoom=requestedZoom;applyEpubZoom();
|
|
2238
|
+
// Let the content CSS update before redisplaying the logical anchor.
|
|
2239
|
+
await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
|
|
2240
|
+
if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
|
|
2241
|
+
});
|
|
2242
|
+
await epubNavigationQueue;
|
|
2243
|
+
} else {zoom=requestedZoom;applyZoom();}
|
|
2244
|
+
sendState();sendResponse(id,true,{zoom});return;
|
|
1824
2245
|
}
|
|
1825
2246
|
|
|
1826
2247
|
if (kind === 'set-rotation') {
|