@readium/navigator 2.11.0 → 2.12.0
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/divina/DivinaNavigator.js +1 -1
- package/dist/epub/EpubNavigator.js +1 -1
- package/dist/epub/frame/FrameComms.js +1 -1
- package/dist/epub/frame/FrameManager.js +1 -1
- package/dist/epub/frame/FramePoolManager.js +1 -1
- package/dist/epub/fxl/FXLFrameManager.js +1 -1
- package/dist/helpers/serialize.js +1 -0
- package/dist/index.js +1 -1
- package/dist/preferences/guards.js +1 -1
- package/dist/readaloud/GuidedNavigationProvider.js +1 -0
- package/dist/readaloud/ReadAloudNavigator.js +1 -0
- package/dist/readaloud/ReadingUnit.js +1 -0
- package/dist/readaloud/SpeechProgress.js +1 -0
- package/dist/readaloud/preferences/ReadAloudDefaults.js +1 -0
- package/dist/readaloud/preferences/ReadAloudPreferences.js +1 -0
- package/dist/readaloud/preferences/ReadAloudPreferencesEditor.js +1 -0
- package/dist/readaloud/preferences/ReadAloudSettings.js +1 -0
- package/dist/webpub/WebPubFrameManager.js +1 -1
- package/dist/webpub/WebPubFramePoolManager.js +1 -1
- package/dist/webpub/WebPubNavigator.js +1 -1
- package/package.json +8 -6
- package/src/divina/DivinaNavigator.ts +1 -0
- package/src/epub/EpubNavigator.ts +127 -21
- package/src/epub/frame/FrameComms.ts +42 -7
- package/src/epub/frame/FrameManager.ts +20 -7
- package/src/epub/frame/FramePoolManager.ts +5 -0
- package/src/epub/fxl/FXLFrameManager.ts +30 -8
- package/src/helpers/serialize.ts +9 -0
- package/src/index.ts +1 -0
- package/src/preferences/guards.ts +11 -0
- package/src/readaloud/GuidedNavigationProvider.ts +87 -0
- package/src/readaloud/ReadAloudNavigator.ts +949 -0
- package/src/readaloud/ReadingUnit.ts +154 -0
- package/src/readaloud/SpeechProgress.ts +60 -0
- package/src/readaloud/index.ts +17 -0
- package/src/readaloud/preferences/ReadAloudDefaults.ts +24 -0
- package/src/readaloud/preferences/ReadAloudPreferences.ts +104 -0
- package/src/readaloud/preferences/ReadAloudPreferencesEditor.ts +115 -0
- package/src/readaloud/preferences/ReadAloudSettings.ts +44 -0
- package/src/readaloud/preferences/index.ts +4 -0
- package/src/webpub/WebPubFrameManager.ts +24 -7
- package/src/webpub/WebPubFramePoolManager.ts +5 -0
- package/src/webpub/WebPubNavigator.ts +114 -10
- package/types/src/epub/EpubNavigator.d.ts +26 -1
- package/types/src/epub/frame/FrameComms.d.ts +8 -1
- package/types/src/epub/frame/FrameManager.d.ts +3 -0
- package/types/src/epub/frame/FramePoolManager.d.ts +2 -0
- package/types/src/epub/fxl/FXLFrameManager.d.ts +5 -0
- package/types/src/helpers/serialize.d.ts +1 -0
- package/types/src/index.d.ts +1 -0
- package/types/src/preferences/guards.d.ts +1 -0
- package/types/src/readaloud/GuidedNavigationProvider.d.ts +28 -0
- package/types/src/readaloud/ReadAloudNavigator.d.ts +158 -0
- package/types/src/readaloud/ReadingUnit.d.ts +58 -0
- package/types/src/readaloud/SpeechProgress.d.ts +19 -0
- package/types/src/readaloud/index.d.ts +6 -0
- package/types/src/readaloud/preferences/ReadAloudDefaults.d.ts +11 -0
- package/types/src/readaloud/preferences/ReadAloudPreferences.d.ts +58 -0
- package/types/src/readaloud/preferences/ReadAloudPreferencesEditor.d.ts +37 -0
- package/types/src/readaloud/preferences/ReadAloudSettings.d.ts +26 -0
- package/types/src/readaloud/preferences/index.d.ts +4 -0
- package/types/src/webpub/WebPubFrameManager.d.ts +4 -0
- package/types/src/webpub/WebPubFramePoolManager.d.ts +2 -0
- package/types/src/webpub/WebPubNavigator.d.ts +25 -2
|
@@ -2,7 +2,7 @@ import { Feature, Link, Locator, LocatorText, Publication, ReadingProgression, L
|
|
|
2
2
|
import { VisualNavigator, VisualNavigatorViewport, ProgressionRange, KeyboardPeripheralEventData } from "../Navigator.ts";
|
|
3
3
|
import { Configurable } from "../preferences/Configurable.ts";
|
|
4
4
|
import { WebPubFramePoolManager } from "./WebPubFramePoolManager.ts";
|
|
5
|
-
import { BasicTextSelection, CommsEventKey, ContextMenuEvent, DecorationActivatedEvent, DecorationPointerEnterData, DecorationPointerLeaveData, FrameClickEvent, KeyboardPeripheralEvent, ModuleName, SuspiciousActivityEvent, WebPubModules } from "@readium/navigator-html-injectables";
|
|
5
|
+
import { BasicTextSelection, CommsEventKey, ContextMenuEvent, DecorationActivatedEvent, DecorationPointerEnterData, DecorationPointerLeaveData, FrameClickEvent, KeyboardPeripheralEvent, ModuleName, SuspiciousActivityEvent, TextLayout, PointedPieceRequest, PointedPieceResponse, WebPubModules } from "@readium/navigator-html-injectables";
|
|
6
6
|
import * as path from "path-browserify";
|
|
7
7
|
import { WebPubFrameManager } from "./WebPubFrameManager.ts";
|
|
8
8
|
import { Decoration, DecorableNavigator, OnDecorationActivatedEvent, OnDecorationPointerEnterEvent, OnDecorationPointerLeaveEvent, DecorationObserver, DecoratorConfig, decorationsEqual, resolveDecorationForWire, supportsDecorationStyle as canRenderDecorationStyle, DecorationStyleType } from "../decorations/index.ts";
|
|
@@ -46,6 +46,9 @@ export interface WebPubNavigatorListeners {
|
|
|
46
46
|
peripheral: (data: KeyboardPeripheralEventData) => void;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Expired comms callbacks are dropped, never called.
|
|
50
|
+
const POINTED_PIECE_TIMEOUT = 1000;
|
|
51
|
+
|
|
49
52
|
const defaultListeners = (listeners: WebPubNavigatorListeners): WebPubNavigatorListeners => ({
|
|
50
53
|
frameLoaded: listeners.frameLoaded || (() => {}),
|
|
51
54
|
positionChanged: listeners.positionChanged || (() => {}),
|
|
@@ -102,6 +105,11 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
102
105
|
private _decorationHoverState: Map<string, boolean> = new Map();
|
|
103
106
|
private _decorationActivationConsumed = false;
|
|
104
107
|
private _decorationResizeSelectors: Set<string>;
|
|
108
|
+
private _textLayoutWatch?: { href: string; pieces: unknown[]; cb: (layout: TextLayout) => void };
|
|
109
|
+
private _textLayoutFrame?: WebPubFrameManager;
|
|
110
|
+
private _navigationLocked = false;
|
|
111
|
+
private readonly _pointedPieceRequests = new Map<string, (index: number) => void>();
|
|
112
|
+
private _pointedPieceRequestId = 0;
|
|
105
113
|
|
|
106
114
|
private webViewport: VisualNavigatorViewport = {
|
|
107
115
|
readingOrder: [],
|
|
@@ -283,14 +291,25 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
283
291
|
) ?? false;
|
|
284
292
|
}
|
|
285
293
|
|
|
286
|
-
public eventListener(key: CommsEventKey | ManagerEventKey, data: unknown) {
|
|
294
|
+
public eventListener(key: CommsEventKey | ManagerEventKey, data: unknown, sourceFrame?: WebPubFrameManager) {
|
|
287
295
|
switch (key) {
|
|
288
296
|
case "_pong":
|
|
289
297
|
this.listeners.frameLoaded(this.framePool.currentFrames[0]!.iframe.contentWindow!);
|
|
290
298
|
this.listeners.positionChanged(this.currentLocation);
|
|
291
299
|
this._notifyTimelineChange(this.currentLocation);
|
|
292
300
|
this._reapplyDecorationsToCurrentFrame();
|
|
301
|
+
this._watchTextLayoutInCurrentFrame(true);
|
|
302
|
+
this._sendNavigationLock();
|
|
303
|
+
break;
|
|
304
|
+
case "text_layout":
|
|
305
|
+
// A frame being hidden after moving to another resource lays out again, collapsed.
|
|
306
|
+
if (sourceFrame && sourceFrame === this._textLayoutFrame && this.currentLocation.href.split("#")[0] === this._textLayoutWatch?.href) this._textLayoutWatch.cb(data as TextLayout);
|
|
293
307
|
break;
|
|
308
|
+
case "pointed_piece": {
|
|
309
|
+
const { id, index } = data as PointedPieceResponse;
|
|
310
|
+
this._pointedPieceRequests.get(id)?.(index);
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
294
313
|
case "first_visible_locator":
|
|
295
314
|
const loc = Locator.deserialize(data as string);
|
|
296
315
|
if(!loc) break;
|
|
@@ -340,7 +359,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
340
359
|
) {
|
|
341
360
|
const origHref = element.attributes.getNamedItem("href")?.value!;
|
|
342
361
|
if (origHref.startsWith("#")) {
|
|
343
|
-
this.go(this.currentLocation.copyWithLocations({
|
|
362
|
+
if (!this._navigationLocked) this.go(this.currentLocation.copyWithLocations({
|
|
344
363
|
fragments: [origHref.substring(1)]
|
|
345
364
|
}), false, () => { });
|
|
346
365
|
} else if(
|
|
@@ -374,7 +393,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
374
393
|
|
|
375
394
|
const link = this.pub.readingOrder.findWithHref(hrefToCheck);
|
|
376
395
|
if (link) {
|
|
377
|
-
this.goLink(link, false, () => { });
|
|
396
|
+
if (!this._navigationLocked) this.goLink(link, false, () => { });
|
|
378
397
|
} else {
|
|
379
398
|
console.warn(`Internal link not found in readingOrder: ${hrefToCheck}`);
|
|
380
399
|
this.listeners.handleLocator(new Link({
|
|
@@ -400,9 +419,13 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
400
419
|
case "zoom":
|
|
401
420
|
this.listeners.zoom(data as number);
|
|
402
421
|
break;
|
|
403
|
-
case "progress":
|
|
422
|
+
case "progress": {
|
|
423
|
+
// The previous resource's frame can still report once moving to another, which would take its progress.
|
|
424
|
+
const frame = sourceFrame && this.framePool._frameFor(this.currentLocation.href.split("#")[0]);
|
|
425
|
+
if (sourceFrame && sourceFrame !== frame) break;
|
|
404
426
|
this.syncLocation(data as ProgressionRange);
|
|
405
427
|
break;
|
|
428
|
+
}
|
|
406
429
|
case "content_protection":
|
|
407
430
|
const activity = data as SuspiciousActivityEvent;
|
|
408
431
|
this.listeners.contentProtection(activity.type, activity);
|
|
@@ -442,12 +465,15 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
442
465
|
}
|
|
443
466
|
|
|
444
467
|
private attachListener() {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
468
|
+
const vframes = this.framePool.currentFrames.filter(f => !!f) as WebPubFrameManager[];
|
|
469
|
+
vframes.forEach(f => {
|
|
470
|
+
if(f.msg) f.msg.listener = (key: CommsEventKey | ManagerEventKey, value: unknown) => {
|
|
471
|
+
this.eventListener(key, value, f);
|
|
472
|
+
}
|
|
473
|
+
})
|
|
450
474
|
this._reapplyDecorationsToCurrentFrame();
|
|
475
|
+
this._watchTextLayoutInCurrentFrame();
|
|
476
|
+
this._sendNavigationLock();
|
|
451
477
|
}
|
|
452
478
|
|
|
453
479
|
private async apply() {
|
|
@@ -619,6 +645,84 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
619
645
|
}
|
|
620
646
|
}
|
|
621
647
|
|
|
648
|
+
/**
|
|
649
|
+
* Reports where the text of `locators`, all in one resource, starts each line along the scroll axis,
|
|
650
|
+
* with the viewport. Reported again whenever that resource is scrolled or laid out again.
|
|
651
|
+
* Replaces any previous watch, and returns a function that stops this one.
|
|
652
|
+
*/
|
|
653
|
+
public watchTextLayout(locators: Locator[], cb: (layout: TextLayout) => void): () => void {
|
|
654
|
+
const watch = {
|
|
655
|
+
href: locators[0]?.href.split("#")[0] ?? "",
|
|
656
|
+
pieces: locators.map(locator => [locator.text?.serialize(), getCssSelector(locator.locations)]),
|
|
657
|
+
cb,
|
|
658
|
+
};
|
|
659
|
+
this._textLayoutWatch = watch;
|
|
660
|
+
this._watchTextLayoutInCurrentFrame(true);
|
|
661
|
+
return () => {
|
|
662
|
+
if (this._textLayoutWatch !== watch) return;
|
|
663
|
+
this._textLayoutWatch = undefined;
|
|
664
|
+
this._watchTextLayoutInCurrentFrame();
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Locks or unlocks navigating in the content: scrolling and following its links.
|
|
670
|
+
* Calls to the navigator still move it.
|
|
671
|
+
*/
|
|
672
|
+
public lockNavigation(locked: boolean): void {
|
|
673
|
+
this._navigationLocked = locked;
|
|
674
|
+
this._sendNavigationLock();
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
private _sendNavigationLock(): void {
|
|
678
|
+
this.framePool?.currentFrames[0]?.msg?.send("lock_navigation", this._navigationLocked);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Returns the first of `pieces` under the point of `event`, else the first containing the element it targets,
|
|
683
|
+
* in the resource of the frame it comes from. Undefined when none does.
|
|
684
|
+
*/
|
|
685
|
+
public async findPointedPiece(event: FrameClickEvent, pieces: Locator[]): Promise<Locator | undefined> {
|
|
686
|
+
const frames = this.framePool.currentFrames.filter(f => !!f) as WebPubFrameManager[];
|
|
687
|
+
const i = frames.findIndex(frame => frame.source === event.targetFrameSrc);
|
|
688
|
+
const href = i >= 0 ? this.viewport.readingOrder[i] : undefined;
|
|
689
|
+
if (!href || !event.cssSelector) return undefined;
|
|
690
|
+
const candidates = pieces.filter(piece => piece.href.split("#")[0] === href);
|
|
691
|
+
if (candidates.length === 0) return undefined;
|
|
692
|
+
|
|
693
|
+
const id = `${++this._pointedPieceRequestId}`;
|
|
694
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
695
|
+
const index = await new Promise<number>(resolve => {
|
|
696
|
+
this._pointedPieceRequests.set(id, resolve);
|
|
697
|
+
timeout = setTimeout(() => resolve(-1), POINTED_PIECE_TIMEOUT);
|
|
698
|
+
const request: PointedPieceRequest = {
|
|
699
|
+
id,
|
|
700
|
+
cssSelector: event.cssSelector!,
|
|
701
|
+
x: event.x,
|
|
702
|
+
y: event.y,
|
|
703
|
+
pieces: candidates.map(piece => [piece.text?.serialize(), getCssSelector(piece.locations)])
|
|
704
|
+
};
|
|
705
|
+
// The reply is posted before the ack, so a pending request on ack got no reply.
|
|
706
|
+
const sent = frames[i].msg?.send("pointed_piece", request, () => resolve(-1));
|
|
707
|
+
if (!sent) resolve(-1);
|
|
708
|
+
});
|
|
709
|
+
clearTimeout(timeout);
|
|
710
|
+
this._pointedPieceRequests.delete(id);
|
|
711
|
+
return candidates[index];
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// The previous frame stops watching, so only the displayed resource reports its layout.
|
|
715
|
+
private _watchTextLayoutInCurrentFrame(resend = false): void {
|
|
716
|
+
const frame = this.framePool?.currentFrames[0];
|
|
717
|
+
const watch = this._textLayoutWatch;
|
|
718
|
+
const target = watch && frame && this.currentLocation?.href.split("#")[0] === watch.href ? frame : undefined;
|
|
719
|
+
if (target === this._textLayoutFrame && !resend) return;
|
|
720
|
+
const previous = this._textLayoutFrame;
|
|
721
|
+
if (previous && previous !== target && !previous.isDestroyed) previous.msg?.send("watch_text_layout", []);
|
|
722
|
+
this._textLayoutFrame = target;
|
|
723
|
+
if (target && watch) target.msg?.send("watch_text_layout", watch.pieces);
|
|
724
|
+
}
|
|
725
|
+
|
|
622
726
|
private _handleDecorationActivated(data: DecorationActivatedEvent): boolean {
|
|
623
727
|
const observers = this._decorationObservers.get(data.group);
|
|
624
728
|
if (!observers || observers.size === 0) return false;
|
|
@@ -2,7 +2,7 @@ import { Layout, Link, Locator, Publication, ReadingProgression, Timeline, Timel
|
|
|
2
2
|
import { Configurable, ConfigurableSettings, VisualNavigator, VisualNavigatorViewport } from "../index.ts";
|
|
3
3
|
import { FramePoolManager } from "./frame/FramePoolManager.ts";
|
|
4
4
|
import { FXLFramePoolManager } from "./fxl/FXLFramePoolManager.ts";
|
|
5
|
-
import { CommsEventKey, ContextMenuEvent, BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
|
|
5
|
+
import { CommsEventKey, ContextMenuEvent, BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent, TextLayout } from "@readium/navigator-html-injectables";
|
|
6
6
|
import { Decoration, DecorationObserver, DecorableNavigator, DecoratorConfig, DecorationStyleType } from "../decorations/index.ts";
|
|
7
7
|
import { FXLFrameManager } from "./fxl/FXLFrameManager.ts";
|
|
8
8
|
import { FrameManager } from "./frame/FrameManager.ts";
|
|
@@ -81,6 +81,11 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
|
|
|
81
81
|
private _decorationHoverState;
|
|
82
82
|
private _decorationActivationConsumed;
|
|
83
83
|
private _decorationResizeSelectors;
|
|
84
|
+
private _textLayoutWatch?;
|
|
85
|
+
private _textLayoutFrame?;
|
|
86
|
+
private _navigationLocked;
|
|
87
|
+
private readonly _pointedPieceRequests;
|
|
88
|
+
private _pointedPieceRequestId;
|
|
84
89
|
private reflowViewport;
|
|
85
90
|
constructor(container: HTMLElement, pub: Publication, listeners: EpubNavigatorListeners, positions?: Locator[], initialPosition?: Locator | undefined, configuration?: EpubNavigatorConfiguration);
|
|
86
91
|
static determineLayout(pub: Publication, scroll?: boolean): Layout;
|
|
@@ -126,6 +131,26 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
|
|
|
126
131
|
private _sendDecorationOps;
|
|
127
132
|
private _reapplyDecorationsToFrame;
|
|
128
133
|
private _reapplyDecorationsToCurrentFrames;
|
|
134
|
+
/**
|
|
135
|
+
* Reports where the text of `locators`, all in one reflowable resource, starts each line or column
|
|
136
|
+
* along the scroll axis, with the viewport. Reported again whenever that resource is scrolled or laid out again.
|
|
137
|
+
* Replaces any previous watch, and returns a function that stops this one.
|
|
138
|
+
*/
|
|
139
|
+
watchTextLayout(locators: Locator[], cb: (layout: TextLayout) => void): () => void;
|
|
140
|
+
/**
|
|
141
|
+
* Locks or unlocks navigating in the content: scrolling, swiping, tapping its edges and following its links.
|
|
142
|
+
* Calls to the navigator still move it.
|
|
143
|
+
*/
|
|
144
|
+
lockNavigation(locked: boolean): void;
|
|
145
|
+
private _sendNavigationLock;
|
|
146
|
+
/** The left page's width when `targetFrameSrc` is the right page (screen-wise) of a fixed-layout spread, else 0. */
|
|
147
|
+
private _spreadOffsetX;
|
|
148
|
+
/**
|
|
149
|
+
* Returns the first of `pieces` under the point of `event`, else the first containing the element it targets,
|
|
150
|
+
* in the resource of the frame it comes from. Undefined when none does.
|
|
151
|
+
*/
|
|
152
|
+
findPointedPiece(event: FrameClickEvent, pieces: Locator[]): Promise<Locator | undefined>;
|
|
153
|
+
private _watchTextLayoutInCurrentFrame;
|
|
129
154
|
private _handleDecorationActivated;
|
|
130
155
|
private _handleDecorationPointerEnter;
|
|
131
156
|
private _handleDecorationPointerLeave;
|
|
@@ -4,15 +4,22 @@ export type FrameCommsListener = (key: CommsEventKey | ManagerEventKey, value: u
|
|
|
4
4
|
export declare class FrameComms {
|
|
5
5
|
private readonly wnd;
|
|
6
6
|
private readonly registry;
|
|
7
|
-
private
|
|
7
|
+
private gc;
|
|
8
8
|
private readonly origin;
|
|
9
9
|
readonly channelId: string;
|
|
10
10
|
private _ready;
|
|
11
11
|
private _listener;
|
|
12
12
|
private listenerBuffer;
|
|
13
|
+
private visibleTime;
|
|
14
|
+
private visibleSince;
|
|
13
15
|
set listener(listener: FrameCommsListener);
|
|
14
16
|
clearListener(): void;
|
|
15
17
|
constructor(wnd: Window, origin: string);
|
|
18
|
+
private startGc;
|
|
19
|
+
private stopGc;
|
|
20
|
+
private visibleNow;
|
|
21
|
+
private handleVisibility;
|
|
22
|
+
private visibilityHandler;
|
|
16
23
|
halt(): void;
|
|
17
24
|
resume(): void;
|
|
18
25
|
private handle;
|
|
@@ -15,6 +15,7 @@ export declare class FrameManager {
|
|
|
15
15
|
private pendingUnwatchSelectors;
|
|
16
16
|
private conditionBridge?;
|
|
17
17
|
private currModules;
|
|
18
|
+
private readonly serialize;
|
|
18
19
|
constructor(source: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, timelineFragmentIds?: string[], resizeWatchSelectors?: string[]);
|
|
19
20
|
load(modules: ModuleName[]): Promise<Window>;
|
|
20
21
|
private applyContentProtection;
|
|
@@ -22,7 +23,9 @@ export declare class FrameManager {
|
|
|
22
23
|
removeResizeTarget(selector: string): void;
|
|
23
24
|
destroy(): Promise<void>;
|
|
24
25
|
hide(): Promise<void>;
|
|
26
|
+
private hideNow;
|
|
25
27
|
show(atProgress?: number): Promise<void>;
|
|
28
|
+
private showNow;
|
|
26
29
|
setCSSProperties(properties: {
|
|
27
30
|
[key: string]: string;
|
|
28
31
|
}): void;
|
|
@@ -33,5 +33,7 @@ export declare class FramePoolManager {
|
|
|
33
33
|
[key: string]: string;
|
|
34
34
|
}): void;
|
|
35
35
|
get currentFrames(): (FrameManager | undefined)[];
|
|
36
|
+
/** The pooled frame of `href`, whether or not it's shown. */
|
|
37
|
+
_frameFor(href: string): FrameManager | undefined;
|
|
36
38
|
get currentBounds(): DOMRect;
|
|
37
39
|
}
|
|
@@ -21,8 +21,10 @@ export declare class FXLFrameManager {
|
|
|
21
21
|
private loadPromise;
|
|
22
22
|
private showPromise;
|
|
23
23
|
private viewportSize;
|
|
24
|
+
private readonly serialize;
|
|
24
25
|
constructor(peripherals: FXLPeripherals, direction: ReadingProgression, debugHref: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, resizeWatchSelectors?: string[]);
|
|
25
26
|
load(modules: ModuleName[], source: string): Promise<Window>;
|
|
27
|
+
private loadNow;
|
|
26
28
|
loadPageSize(): {
|
|
27
29
|
width: number;
|
|
28
30
|
height: number;
|
|
@@ -30,13 +32,16 @@ export declare class FXLFrameManager {
|
|
|
30
32
|
update(page?: Page): void;
|
|
31
33
|
destroy(): Promise<void>;
|
|
32
34
|
unload(): Promise<void>;
|
|
35
|
+
private unloadNow;
|
|
33
36
|
deselect(): void;
|
|
34
37
|
unfocus(): Promise<void>;
|
|
38
|
+
private unfocusNow;
|
|
35
39
|
private applyContentProtection;
|
|
36
40
|
addResizeTarget(selector: string): void;
|
|
37
41
|
removeResizeTarget(selector: string): void;
|
|
38
42
|
private cachedPage;
|
|
39
43
|
show(page: Page): Promise<void>;
|
|
44
|
+
private showNow;
|
|
40
45
|
activate(): Promise<void>;
|
|
41
46
|
get element(): HTMLDivElement;
|
|
42
47
|
get iframe(): HTMLIFrameElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createSerializer(): <T>(operation: () => Promise<T>) => Promise<T>;
|
package/types/src/index.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export declare function ensureEnumValue<T extends string>(value: T | null | unde
|
|
|
7
7
|
export declare function ensureFilter(filter: boolean | number | null | undefined): boolean | number | null | undefined;
|
|
8
8
|
export declare function ensureNonNegative(value: number | null | undefined): number | null | undefined;
|
|
9
9
|
export declare function ensureValueInRange(value: number | null | undefined, range: [number, number]): number | null | undefined;
|
|
10
|
+
export declare function ensureDecorationStyle<T extends object>(value: T | false | null | undefined): T | false | null | undefined;
|
|
10
11
|
export declare function withFallback<T>(value: T | null | undefined, defaultValue: T | null): T | null;
|
|
11
12
|
export declare function ensureExperiment(experimentsInput: ExperimentKey[] | null | undefined): ExperimentKey[] | null | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GuidedNavigationObject, Link, Publication } from "@readium/shared";
|
|
2
|
+
export interface GuidedNavigationProvider {
|
|
3
|
+
/**
|
|
4
|
+
* Guided Navigation objects of a resource, with every textref qualified by the resource's href.
|
|
5
|
+
* Resolves to undefined when the resource has nothing to read.
|
|
6
|
+
*/
|
|
7
|
+
guideFor(link: Link): Promise<GuidedNavigationObject[] | undefined>;
|
|
8
|
+
}
|
|
9
|
+
export interface PublicationGuidedNavigationProviderOptions {
|
|
10
|
+
/** Generates from the resource's markup without using the publication's Guided Navigation document. */
|
|
11
|
+
generateFromMarkup?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Uses the publication's Guided Navigation document when its text can be located in the resource,
|
|
15
|
+
* and generates one from the resource's markup otherwise, or always with `generateFromMarkup`.
|
|
16
|
+
* Only the objects of the resource are kept from a document covering the whole publication.
|
|
17
|
+
*/
|
|
18
|
+
export declare class PublicationGuidedNavigationProvider implements GuidedNavigationProvider {
|
|
19
|
+
private readonly publication;
|
|
20
|
+
private readonly options;
|
|
21
|
+
constructor(publication: Publication, options?: PublicationGuidedNavigationProviderOptions);
|
|
22
|
+
guideFor(link: Link): Promise<GuidedNavigationObject[] | undefined>;
|
|
23
|
+
private generate;
|
|
24
|
+
private objectsIn;
|
|
25
|
+
private resourceOf;
|
|
26
|
+
private imageOf;
|
|
27
|
+
private isLocatable;
|
|
28
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Layout, Locator } from "@readium/shared";
|
|
2
|
+
import { FrameClickEvent, TextLayout } from "@readium/navigator-html-injectables";
|
|
3
|
+
import { ReadiumSpeechPlaybackEngine, ReadiumSpeechPlaybackState, ReadiumSpeechVoice } from "@readium/speech";
|
|
4
|
+
import { Navigator, VisualNavigatorViewport } from "../Navigator.ts";
|
|
5
|
+
import { DecorableNavigator } from "../decorations/index.ts";
|
|
6
|
+
import { GuidedNavigationProvider } from "./GuidedNavigationProvider.ts";
|
|
7
|
+
import { IReadAloudDefaults, IReadAloudPreferences, ReadAloudPreferences, ReadAloudPreferencesEditor, ReadAloudSettings } from "./preferences/index.ts";
|
|
8
|
+
export type ReadAloudState = ReadiumSpeechPlaybackState;
|
|
9
|
+
export interface ReadAloudVoicesOptions {
|
|
10
|
+
/** BCP 47 tags, matched by base language. */
|
|
11
|
+
languages?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface ReadAloudUtterance {
|
|
14
|
+
text: string;
|
|
15
|
+
/** One locator per piece of the utterance, each in its own resource. */
|
|
16
|
+
locators: Locator[];
|
|
17
|
+
}
|
|
18
|
+
export interface ReadAloudListeners {
|
|
19
|
+
stateChanged?: (state: ReadAloudState) => void;
|
|
20
|
+
utteranceChanged?: (utterance: ReadAloudUtterance) => void;
|
|
21
|
+
wordChanged?: (locator: Locator, word: string) => void;
|
|
22
|
+
error?: (error: unknown) => void;
|
|
23
|
+
}
|
|
24
|
+
export interface ReadAloudConfiguration {
|
|
25
|
+
/** Defaults to a `WebSpeechEngine`. */
|
|
26
|
+
engine?: ReadiumSpeechPlaybackEngine;
|
|
27
|
+
provider?: GuidedNavigationProvider;
|
|
28
|
+
preferences?: IReadAloudPreferences;
|
|
29
|
+
defaults?: IReadAloudDefaults;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the reader can move away while reading aloud, which then stops following until back.
|
|
32
|
+
* Otherwise navigating in the content is locked while playing. Defaults to true.
|
|
33
|
+
*/
|
|
34
|
+
detachable?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads a publication aloud with a speech engine, alongside the navigator displaying it.
|
|
38
|
+
* The spoken utterance and word are highlighted, and the navigator follows them.
|
|
39
|
+
*/
|
|
40
|
+
export declare class ReadAloudNavigator {
|
|
41
|
+
private readonly navigator;
|
|
42
|
+
private readonly listeners;
|
|
43
|
+
private readonly speech;
|
|
44
|
+
private readonly pool;
|
|
45
|
+
private readonly units;
|
|
46
|
+
private readonly unsubscribers;
|
|
47
|
+
private readonly _defaults;
|
|
48
|
+
private _preferences;
|
|
49
|
+
private _settings;
|
|
50
|
+
private _preferencesEditor;
|
|
51
|
+
private utteranceLocators;
|
|
52
|
+
private wordLocator?;
|
|
53
|
+
private pausedBefore?;
|
|
54
|
+
private turning?;
|
|
55
|
+
private followedHref?;
|
|
56
|
+
private following?;
|
|
57
|
+
private detached;
|
|
58
|
+
private navigationLocked;
|
|
59
|
+
private shownHref?;
|
|
60
|
+
private watchedPieces;
|
|
61
|
+
private unwatchLayout?;
|
|
62
|
+
private lines;
|
|
63
|
+
private viewport?;
|
|
64
|
+
private readonly progress;
|
|
65
|
+
private breakTimer?;
|
|
66
|
+
private unit?;
|
|
67
|
+
private loading;
|
|
68
|
+
private loadPaused;
|
|
69
|
+
private loadToken;
|
|
70
|
+
private settleReady?;
|
|
71
|
+
private lastState;
|
|
72
|
+
constructor(navigator: Navigator & Partial<DecorableNavigator> & {
|
|
73
|
+
readonly viewport?: VisualNavigatorViewport;
|
|
74
|
+
readonly layout?: Layout;
|
|
75
|
+
watchTextLayout?(locators: Locator[], cb: (layout: TextLayout) => void): () => void;
|
|
76
|
+
findPointedPiece?(event: FrameClickEvent, pieces: Locator[]): Promise<Locator | undefined>;
|
|
77
|
+
lockNavigation?(locked: boolean): void;
|
|
78
|
+
}, listeners?: ReadAloudListeners, configuration?: ReadAloudConfiguration);
|
|
79
|
+
/** Whether the reader can move away while reading aloud. Otherwise the app keeps its own navigation from moving while playing. */
|
|
80
|
+
readonly detachable: boolean;
|
|
81
|
+
get state(): ReadAloudState;
|
|
82
|
+
/**
|
|
83
|
+
* Starts reading from `from`, or resumes when paused.
|
|
84
|
+
* Without `from`, an idle reader starts from the navigator's current position.
|
|
85
|
+
*/
|
|
86
|
+
play(from?: Locator): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Starts reading from the utterance under a click or tap in a displayed resource, loading that resource when needed.
|
|
89
|
+
* Resolves false when the press isn't on content read aloud, or is on an interactive element, so the caller handles it.
|
|
90
|
+
*/
|
|
91
|
+
readFromPointer(event: FrameClickEvent): Promise<boolean>;
|
|
92
|
+
pause(): void;
|
|
93
|
+
stop(): void;
|
|
94
|
+
/** Moves to the next utterance, continuing into the next reading unit at the end of this one. */
|
|
95
|
+
next(): Promise<boolean>;
|
|
96
|
+
/** Moves to the previous utterance, continuing into the previous reading unit at the start of this one. */
|
|
97
|
+
previous(): Promise<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* The available voices, only those of `languages` when given,
|
|
100
|
+
* ranked the way the default voice is picked: preferred region first, then quality.
|
|
101
|
+
*/
|
|
102
|
+
getVoices(options?: ReadAloudVoicesOptions): Promise<ReadiumSpeechVoice[]>;
|
|
103
|
+
setVoice(voice: ReadiumSpeechVoice | string): void;
|
|
104
|
+
getCurrentVoice(): ReadiumSpeechVoice | null;
|
|
105
|
+
get settings(): Readonly<ReadAloudSettings>;
|
|
106
|
+
get preferencesEditor(): ReadAloudPreferencesEditor;
|
|
107
|
+
submitPreferences(preferences: ReadAloudPreferences): Promise<void>;
|
|
108
|
+
destroy(): Promise<void>;
|
|
109
|
+
private listen;
|
|
110
|
+
private start;
|
|
111
|
+
private resumePaused;
|
|
112
|
+
private speechReady;
|
|
113
|
+
private untilReady;
|
|
114
|
+
private movePaused;
|
|
115
|
+
private loadUnit;
|
|
116
|
+
private startIndex;
|
|
117
|
+
private indexAfterRebuild;
|
|
118
|
+
private pointedIndex;
|
|
119
|
+
private piecesOf;
|
|
120
|
+
private speakFrom;
|
|
121
|
+
private pausesAtPages;
|
|
122
|
+
private turnTo;
|
|
123
|
+
private staysPaused;
|
|
124
|
+
private pauseTurning;
|
|
125
|
+
private pausesBeforeUnit;
|
|
126
|
+
private pausesBetween;
|
|
127
|
+
private hrefsOf;
|
|
128
|
+
private notifyUtterance;
|
|
129
|
+
private pageLocator;
|
|
130
|
+
private decorate;
|
|
131
|
+
private applyDecorations;
|
|
132
|
+
private clearHighlights;
|
|
133
|
+
private follow;
|
|
134
|
+
private attach;
|
|
135
|
+
private isDetached;
|
|
136
|
+
private watchLayout;
|
|
137
|
+
private laidOut;
|
|
138
|
+
private isHidden;
|
|
139
|
+
private lineStart;
|
|
140
|
+
private spokenPoint;
|
|
141
|
+
private utteranceStart;
|
|
142
|
+
private wordPoint;
|
|
143
|
+
private pointAt;
|
|
144
|
+
private pointLocator;
|
|
145
|
+
private armBreak;
|
|
146
|
+
private clearBreak;
|
|
147
|
+
private sendsBoundaries;
|
|
148
|
+
private voiceName;
|
|
149
|
+
private speed;
|
|
150
|
+
private static collapsed;
|
|
151
|
+
private static rawOffset;
|
|
152
|
+
private locatorFor;
|
|
153
|
+
private layout;
|
|
154
|
+
private static speechPreferences;
|
|
155
|
+
private setLoading;
|
|
156
|
+
private lockNavigation;
|
|
157
|
+
private notifyState;
|
|
158
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { GuidedNavigationObject, Link, Publication } from "@readium/shared";
|
|
2
|
+
import { Navigator, VisualNavigatorViewport } from "../Navigator.ts";
|
|
3
|
+
import { GuidedNavigationProvider } from "./GuidedNavigationProvider.ts";
|
|
4
|
+
/** Reading order links whose Guided Navigation objects are read as one sequence. */
|
|
5
|
+
export interface ReadingUnit {
|
|
6
|
+
links: Link[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Splits the publication into reading units: the displayed spread for fixed layouts, Divina excepted,
|
|
10
|
+
* and a single resource otherwise. Spreads are only known to the navigator, so it's moved to find them;
|
|
11
|
+
* FXL navigators update their viewport synchronously, so it's read without waiting for the callback.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ReadingUnits {
|
|
14
|
+
private readonly navigator;
|
|
15
|
+
private readonly spreads;
|
|
16
|
+
constructor(navigator: Navigator & {
|
|
17
|
+
readonly viewport?: VisualNavigatorViewport;
|
|
18
|
+
});
|
|
19
|
+
/** The unit containing `href`. */
|
|
20
|
+
around(href: string): ReadingUnit | undefined;
|
|
21
|
+
/** The first link of the unit following `unit`, without moving the navigator. */
|
|
22
|
+
linkAfter(unit: ReadingUnit): Link | undefined;
|
|
23
|
+
/** The last link of the unit preceding `unit`, without moving the navigator. */
|
|
24
|
+
linkBefore(unit: ReadingUnit): Link | undefined;
|
|
25
|
+
after(unit: ReadingUnit): ReadingUnit | undefined;
|
|
26
|
+
before(unit: ReadingUnit): ReadingUnit | undefined;
|
|
27
|
+
/** Whether `href` is a page of the displayed spread. */
|
|
28
|
+
displays(href: string): boolean;
|
|
29
|
+
private move;
|
|
30
|
+
private displayedFrom;
|
|
31
|
+
private indexOf;
|
|
32
|
+
}
|
|
33
|
+
export interface StitchedUnit {
|
|
34
|
+
/** The unit's Guided Navigation objects, concatenated in reading order. */
|
|
35
|
+
guided: GuidedNavigationObject[];
|
|
36
|
+
/** Links whose objects couldn't be obtained, left out of `guided`. */
|
|
37
|
+
failures: {
|
|
38
|
+
link: Link;
|
|
39
|
+
error: unknown;
|
|
40
|
+
}[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Keeps the Guided Navigation objects of the resources around the unit being read,
|
|
44
|
+
* fetching those within LOWER_BOUNDARY reading order items and releasing those beyond UPPER_BOUNDARY.
|
|
45
|
+
*/
|
|
46
|
+
export declare class GuidedNavigationPool {
|
|
47
|
+
private readonly publication;
|
|
48
|
+
private readonly provider;
|
|
49
|
+
private readonly guides;
|
|
50
|
+
constructor(publication: Publication, provider: GuidedNavigationProvider);
|
|
51
|
+
/**
|
|
52
|
+
* Concatenates the Guided Navigation objects of every link in the unit, in reading order,
|
|
53
|
+
* fetching at most `concurrency` resources at once, then fetches the unit's neighbours.
|
|
54
|
+
*/
|
|
55
|
+
stitch(unit: ReadingUnit, concurrency?: number): Promise<StitchedUnit>;
|
|
56
|
+
private update;
|
|
57
|
+
private guideFor;
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReadiumSpeechUtterance } from "@readium/speech";
|
|
2
|
+
/** Times the utterance being spoken, and measures each voice's speed on the utterances it finishes. */
|
|
3
|
+
export declare class SpeechProgress {
|
|
4
|
+
private readonly speeds;
|
|
5
|
+
private utterance?;
|
|
6
|
+
private startedAt;
|
|
7
|
+
private pausedAt?;
|
|
8
|
+
start(utterance: ReadiumSpeechUtterance): void;
|
|
9
|
+
pause(): void;
|
|
10
|
+
resume(): void;
|
|
11
|
+
/** Measures the speed of `voice` once it has spoken `utterance` through. */
|
|
12
|
+
end(utterance: ReadiumSpeechUtterance | null, voice: string, rate: number): void;
|
|
13
|
+
reset(): void;
|
|
14
|
+
get speaking(): boolean;
|
|
15
|
+
/** Seconds spent speaking the current utterance. */
|
|
16
|
+
elapsed(): number;
|
|
17
|
+
/** Characters per second of `voice` at `rate`. */
|
|
18
|
+
speed(voice: string, rate: number): number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './GuidedNavigationProvider.ts';
|
|
2
|
+
export * from './ReadAloudNavigator.ts';
|
|
3
|
+
export * from './preferences/index.ts';
|
|
4
|
+
export { defaultContextualizations, skippableRoles, filterByLanguages, filterOutNoveltyVoices, filterOutVeryLowQualityVoices, getQualityValue, groupVoicesByLanguage, sortAlphabetically, sortByQuality, sortVoicesByRegions } from "@readium/speech";
|
|
5
|
+
export type { ExtractionFormat, LanguageMode, ReadiumSpeechPlaybackEngine, ReadiumSpeechVoice, Segmentation, VerbosityPreset } from "@readium/speech";
|
|
6
|
+
export type { GndRole } from "@readium/guided-navigation";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IReadAloudPreferences, ReadAloudAutoPause, ReadAloudDecorationStyle } from "./ReadAloudPreferences.ts";
|
|
2
|
+
export type IReadAloudDefaults = IReadAloudPreferences;
|
|
3
|
+
export declare class ReadAloudDefaults {
|
|
4
|
+
/** Defaults of the speech fields, validated by speech itself. */
|
|
5
|
+
speech: IReadAloudPreferences;
|
|
6
|
+
autoPause: ReadAloudAutoPause;
|
|
7
|
+
speakInContentLanguage: boolean;
|
|
8
|
+
utteranceStyle: ReadAloudDecorationStyle;
|
|
9
|
+
wordStyle: ReadAloudDecorationStyle;
|
|
10
|
+
constructor(defaults?: IReadAloudDefaults);
|
|
11
|
+
}
|