@readium/navigator 2.6.1 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/dist/Navigator.js +1 -0
- package/dist/_virtual/index.js +1 -0
- package/dist/_virtual/preload-helper.js +1 -0
- package/dist/audio/AudioNavigator.js +1 -0
- package/dist/audio/AudioPoolManager.js +1 -0
- package/dist/audio/engine/PreservePitchProcessor.js +215 -0
- package/dist/audio/engine/PreservePitchWorklet.js +1 -0
- package/dist/audio/engine/WebAudioEngine.js +1 -0
- package/dist/audio/preferences/AudioDefaults.js +1 -0
- package/dist/audio/preferences/AudioPreferences.js +1 -0
- package/dist/audio/preferences/AudioPreferencesEditor.js +1 -0
- package/dist/audio/preferences/AudioSettings.js +1 -0
- package/dist/audio/protection/AudioNavigatorProtector.js +1 -0
- package/dist/css/Properties.js +1 -0
- package/dist/dom/_readium_cssSelectorGenerator.js +1 -0
- package/dist/dom/_readium_executionCleanup.js +14 -0
- package/dist/dom/_readium_executionPrevention.js +66 -0
- package/dist/dom/_readium_webpubExecution.js +5 -0
- package/dist/epub/EpubNavigator.js +1 -0
- package/dist/epub/css/Properties.js +1 -0
- package/dist/epub/css/ReadiumCSS.js +1 -0
- package/dist/epub/frame/FrameBlobBuilder.js +1 -0
- package/dist/epub/frame/FrameComms.js +1 -0
- package/dist/epub/frame/FrameManager.js +1 -0
- package/dist/epub/frame/FramePoolManager.js +1 -0
- package/dist/epub/fxl/FXLCoordinator.js +1 -0
- package/dist/epub/fxl/FXLFrameManager.js +1 -0
- package/dist/epub/fxl/FXLFramePoolManager.js +1 -0
- package/dist/epub/fxl/FXLPeripherals.js +4 -0
- package/dist/epub/fxl/FXLPeripheralsDebug.js +1 -0
- package/dist/epub/fxl/FXLSpreader.js +1 -0
- package/dist/epub/preferences/EpubDefaults.js +1 -0
- package/dist/epub/preferences/EpubPreferences.js +1 -0
- package/dist/epub/preferences/EpubPreferencesEditor.js +1 -0
- package/dist/epub/preferences/EpubSettings.js +1 -0
- package/dist/helpers/dimensions.js +1 -0
- package/dist/helpers/dist/sML.js +1 -0
- package/dist/helpers/lineLength.js +1 -0
- package/dist/helpers/minify.js +1 -0
- package/dist/helpers/scriptMode.js +1 -0
- package/dist/index.js +1 -11006
- package/dist/injection/Injector.js +1 -0
- package/dist/injection/epubInjectables.js +1 -0
- package/dist/injection/webpubInjectables.js +1 -0
- package/dist/{ReadiumCSS-after-D7unrNI9.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/ReadiumCSS-after.css.js} +2 -5
- package/dist/{ReadiumCSS-before-DwBLxUVH.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/ReadiumCSS-before.css.js} +2 -5
- package/dist/{ReadiumCSS-default-AIAk8uwU.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/ReadiumCSS-default.css.js} +2 -5
- package/dist/{ReadiumCSS-ebpaj_fonts_patch-Dt2XliTg.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/ReadiumCSS-ebpaj_fonts_patch.css.js} +2 -5
- package/dist/{ReadiumCSS-after-XUKPAxfT.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/cjk-horizontal/ReadiumCSS-after.css.js} +2 -5
- package/dist/{ReadiumCSS-before-8FMq19-x.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/cjk-horizontal/ReadiumCSS-before.css.js} +2 -5
- package/dist/{ReadiumCSS-default-N65xNiIp.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/cjk-horizontal/ReadiumCSS-default.css.js} +2 -5
- package/dist/{ReadiumCSS-after-ClF4TBzj.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/cjk-vertical/ReadiumCSS-after.css.js} +2 -5
- package/dist/{ReadiumCSS-before-BNTwR8Qm.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/cjk-vertical/ReadiumCSS-before.css.js} +2 -5
- package/dist/{ReadiumCSS-default-BesyZHRU.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/cjk-vertical/ReadiumCSS-default.css.js} +2 -5
- package/dist/{ReadiumCSS-after-d5mC4cme.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/rtl/ReadiumCSS-after.css.js} +2 -5
- package/dist/{ReadiumCSS-before-CG-KmDa3.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/rtl/ReadiumCSS-before.css.js} +2 -5
- package/dist/{ReadiumCSS-default-BhdLiyWp.js → node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/rtl/ReadiumCSS-default.css.js} +2 -5
- package/dist/node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/dist/webPub/ReadiumCSS-webPub.css.js +274 -0
- package/dist/node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/vars/colors.json.js +1 -0
- package/dist/node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/vars/experiments.json.js +1 -0
- package/dist/node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/vars/fontStacks.json.js +1 -0
- package/dist/node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/vars/i18n.json.js +1 -0
- package/dist/node_modules/.pnpm/@readium_css@2.0.5/node_modules/@readium/css/css/vars/settings.json.js +1 -0
- package/dist/node_modules/.pnpm/path-browserify@1.0.1/node_modules/path-browserify/index.js +1 -0
- package/dist/peripherals/KeyboardConditionBridge.js +1 -0
- package/dist/peripherals/KeyboardPeripherals.js +1 -0
- package/dist/preferences/Preference.js +1 -0
- package/dist/preferences/Types.js +1 -0
- package/dist/preferences/guards.js +1 -0
- package/dist/protection/AutomationDetector.js +1 -0
- package/dist/protection/ContextMenuProtector.js +1 -0
- package/dist/protection/CopyProtector.js +1 -0
- package/dist/protection/DevToolsDetector.js +1 -0
- package/dist/protection/DragAndDropProtector.js +1 -0
- package/dist/protection/IframeEmbeddingDetector.js +1 -0
- package/dist/protection/NavigatorProtector.js +1 -0
- package/dist/protection/PrintProtector.js +15 -0
- package/dist/protection/utils/WorkerConsole.js +12 -0
- package/dist/protection/utils/console.js +1 -0
- package/dist/protection/utils/match.js +1 -0
- package/dist/protection/utils/platform.js +1 -0
- package/dist/webpub/WebPubBlobBuilder.js +2 -0
- package/dist/webpub/WebPubFrameManager.js +1 -0
- package/dist/webpub/WebPubFramePoolManager.js +1 -0
- package/dist/webpub/WebPubNavigator.js +1 -0
- package/dist/webpub/css/Properties.js +1 -0
- package/dist/webpub/css/WebPubCSS.js +1 -0
- package/dist/webpub/preferences/WebPubDefaults.js +1 -0
- package/dist/webpub/preferences/WebPubPreferences.js +1 -0
- package/dist/webpub/preferences/WebPubPreferencesEditor.js +1 -0
- package/dist/webpub/preferences/WebPubSettings.js +1 -0
- package/package.json +16 -14
- package/src/Navigator.ts +4 -1
- package/src/audio/AudioNavigator.ts +8 -8
- package/src/decorations/index.ts +24 -127
- package/src/epub/EpubNavigator.ts +149 -20
- package/src/epub/frame/FrameManager.ts +8 -3
- package/src/epub/frame/FramePoolManager.ts +5 -2
- package/src/epub/fxl/FXLCoordinator.ts +1 -1
- package/src/epub/fxl/FXLFrameManager.ts +1 -1
- package/src/epub/preferences/EpubDefaults.ts +1 -1
- package/src/epub/preferences/EpubSettings.ts +1 -1
- package/src/protection/DevToolsDetector.ts +1 -1
- package/src/webpub/WebPubFrameManager.ts +9 -3
- package/src/webpub/WebPubFramePoolManager.ts +5 -2
- package/src/webpub/WebPubNavigator.ts +100 -19
- package/src/webpub/preferences/WebPubDefaults.ts +1 -1
- package/src/webpub/preferences/WebPubSettings.ts +1 -1
- package/types/src/Navigator.d.ts +3 -1
- package/types/src/decorations/index.d.ts +5 -59
- package/types/src/epub/EpubNavigator.d.ts +13 -3
- package/types/src/epub/frame/FrameManager.d.ts +2 -1
- package/types/src/epub/frame/FramePoolManager.d.ts +2 -1
- package/types/src/epub/fxl/FXLFramePoolManager.d.ts +1 -1
- package/types/src/webpub/WebPubFrameManager.d.ts +2 -1
- package/types/src/webpub/WebPubFramePoolManager.d.ts +2 -1
- package/types/src/webpub/WebPubNavigator.d.ts +12 -3
- package/dist/ar-DyHX_uy2.js +0 -7
- package/dist/da-Dct0PS3E.js +0 -7
- package/dist/fr-C5HEel98.js +0 -7
- package/dist/index.umd.cjs +0 -5615
- package/dist/it-DFOBoXGy.js +0 -7
- package/dist/pt_PT-Di3sVjze.js +0 -7
- package/dist/sv-BfzAFsVN.js +0 -7
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Layout, Link, Locator, LocatorText, Profile, Publication, ReadingProgression } from "@readium/shared";
|
|
1
|
+
import { Layout, Link, Locator, LocatorText, Profile, Publication, ReadingProgression, Timeline, TimelineItem, getCssSelector, getHtmlId } from "@readium/shared";
|
|
2
2
|
import { Configurable, ConfigurableSettings, LineLengths, ProgressionRange, 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, DecorationActivatedEvent, FXLModules, ModuleLibrary, ModuleName, ReflowableModules, BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent, KeyboardPeripheralEvent } from "@readium/navigator-html-injectables";
|
|
6
|
-
import { Decoration,
|
|
5
|
+
import { CommsEventKey, ContextMenuEvent, DecorationActivatedEvent, DecorationPointerEnterData, DecorationPointerLeaveData, FXLModules, ModuleLibrary, ModuleName, ReflowableModules, BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent, KeyboardPeripheralEvent } from "@readium/navigator-html-injectables";
|
|
6
|
+
import { Decoration, OnDecorationActivatedEvent, OnDecorationPointerEnterEvent, OnDecorationPointerLeaveEvent, DecorationObserver, DecorableNavigator, DecoratorConfig, decorationsEqual, resolveDecorationForWire, supportsDecorationStyle as canRenderDecorationStyle, DecorationStyleType } from "../decorations/index.ts";
|
|
7
7
|
import * as path from "path-browserify";
|
|
8
8
|
import { FXLFrameManager } from "./fxl/FXLFrameManager.ts";
|
|
9
9
|
import { FrameManager } from "./frame/FrameManager.ts";
|
|
@@ -36,6 +36,7 @@ export interface EpubNavigatorConfiguration {
|
|
|
36
36
|
export interface EpubNavigatorListeners {
|
|
37
37
|
frameLoaded: (wnd: Window) => void;
|
|
38
38
|
positionChanged: (locator: Locator) => void;
|
|
39
|
+
timelineItemChanged: (item: TimelineItem | undefined) => void;
|
|
39
40
|
tap: (e: FrameClickEvent) => boolean; // Return true to prevent handling here
|
|
40
41
|
click: (e: FrameClickEvent) => boolean; // Return true to prevent handling here
|
|
41
42
|
zoom: (scale: number) => void;
|
|
@@ -53,6 +54,7 @@ export interface EpubNavigatorListeners {
|
|
|
53
54
|
const defaultListeners = (listeners: EpubNavigatorListeners): EpubNavigatorListeners => ({
|
|
54
55
|
frameLoaded: listeners.frameLoaded || (() => {}),
|
|
55
56
|
positionChanged: listeners.positionChanged || (() => {}),
|
|
57
|
+
timelineItemChanged: listeners.timelineItemChanged || (() => {}),
|
|
56
58
|
tap: listeners.tap || (() => false),
|
|
57
59
|
click: listeners.click || (() => false),
|
|
58
60
|
zoom: listeners.zoom || (() => {}),
|
|
@@ -73,6 +75,8 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
73
75
|
private framePool!: FramePoolManager | FXLFramePoolManager;
|
|
74
76
|
private positions!: Locator[];
|
|
75
77
|
private currentLocation!: Locator;
|
|
78
|
+
private _currentTimelineItem: TimelineItem | undefined;
|
|
79
|
+
private _timelineAugmented = false;
|
|
76
80
|
private lastLocationInView: Locator | undefined;
|
|
77
81
|
private currentProgression: ReadingProgression;
|
|
78
82
|
private _layout: Layout;
|
|
@@ -99,7 +103,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
99
103
|
|
|
100
104
|
private _decorations: Map<string, Decoration[]> = new Map();
|
|
101
105
|
private _decorationObservers: Map<string, Set<DecorationObserver>> = new Map();
|
|
106
|
+
private _decorationHoveredDecorations: Map<string, Decoration> = new Map();
|
|
102
107
|
private _decorationActivationState: Map<string, boolean> = new Map();
|
|
108
|
+
private _decorationHoverState: Map<string, boolean> = new Map();
|
|
103
109
|
private _decorationActivationConsumed = false;
|
|
104
110
|
|
|
105
111
|
private reflowViewport: VisualNavigatorViewport = {
|
|
@@ -270,7 +276,11 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
270
276
|
cssProperties,
|
|
271
277
|
this._injector,
|
|
272
278
|
this._contentProtection,
|
|
273
|
-
this._keyboardPeripherals
|
|
279
|
+
this._keyboardPeripherals,
|
|
280
|
+
(href) => this.pub.timeline.segmentsForHref(href)
|
|
281
|
+
.flatMap(item => item.references)
|
|
282
|
+
.map(ref => { const h = ref.indexOf('#'); return h >= 0 ? ref.slice(h + 1) : ''; })
|
|
283
|
+
.filter(Boolean)
|
|
274
284
|
);
|
|
275
285
|
}
|
|
276
286
|
|
|
@@ -436,6 +446,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
436
446
|
case "_pong":
|
|
437
447
|
this.listeners.frameLoaded(this._cframes[0]!.iframe.contentWindow!);
|
|
438
448
|
this.listeners.positionChanged(this.currentLocation);
|
|
449
|
+
this._notifyTimelineChange(this.currentLocation);
|
|
439
450
|
if (sourceFrame) {
|
|
440
451
|
const frames = this._cframes.filter(f => !!f) as (FrameManager | FXLFrameManager)[];
|
|
441
452
|
const i = frames.indexOf(sourceFrame);
|
|
@@ -456,6 +467,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
456
467
|
text: loc?.text
|
|
457
468
|
});
|
|
458
469
|
this.listeners.positionChanged(this.currentLocation);
|
|
470
|
+
this._notifyTimelineChange(this.currentLocation);
|
|
459
471
|
break;
|
|
460
472
|
case "text_selected": {
|
|
461
473
|
const selection = data as BasicTextSelection;
|
|
@@ -476,6 +488,12 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
476
488
|
if (handled) this._decorationActivationConsumed = true;
|
|
477
489
|
break;
|
|
478
490
|
}
|
|
491
|
+
case "decoration_pointer_enter":
|
|
492
|
+
this._handleDecorationPointerEnter(data as DecorationPointerEnterData);
|
|
493
|
+
break;
|
|
494
|
+
case "decoration_pointer_leave":
|
|
495
|
+
this._handleDecorationPointerLeave(data as DecorationPointerLeaveData);
|
|
496
|
+
break;
|
|
479
497
|
case "click":
|
|
480
498
|
case "tap":
|
|
481
499
|
if (this._decorationActivationConsumed) {
|
|
@@ -645,9 +663,8 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
645
663
|
|
|
646
664
|
// DecorableNavigator
|
|
647
665
|
|
|
648
|
-
public supportsDecorationStyle(styleTypeId: string): boolean {
|
|
649
|
-
return
|
|
650
|
-
!!this._decoratorConfig.decorationTemplates?.[styleTypeId];
|
|
666
|
+
public supportsDecorationStyle(styleTypeId: DecorationStyleType | string): boolean {
|
|
667
|
+
return canRenderDecorationStyle(styleTypeId, this._decoratorConfig.decorationTemplates);
|
|
651
668
|
}
|
|
652
669
|
|
|
653
670
|
public registerDecorationObserver(group: string, observer: DecorationObserver): void {
|
|
@@ -655,19 +672,34 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
655
672
|
this._decorationObservers.set(group, new Set());
|
|
656
673
|
this._decorationObservers.get(group)!.add(observer);
|
|
657
674
|
|
|
658
|
-
//
|
|
659
|
-
|
|
660
|
-
|
|
675
|
+
// Enable activation if the observer handles it
|
|
676
|
+
if (observer.onDecorationActivated) {
|
|
677
|
+
this._decorationActivationState.set(group, true);
|
|
678
|
+
this._sendDecorationActivationToFrames(group, true);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Enable hover if the observer declares hover callbacks
|
|
682
|
+
if (observer.onDecorationPointerEnter || observer.onDecorationPointerLeave) {
|
|
683
|
+
this._decorationHoverState.set(group, true);
|
|
684
|
+
this._sendDecorationHoverToFrames(group, true);
|
|
685
|
+
}
|
|
661
686
|
}
|
|
662
687
|
|
|
663
688
|
public unregisterDecorationObserver(observer: DecorationObserver): void {
|
|
664
689
|
this._decorationObservers.forEach((set, group) => {
|
|
665
|
-
if (set.has(observer))
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
690
|
+
if (!set.has(observer)) return;
|
|
691
|
+
set.delete(observer);
|
|
692
|
+
|
|
693
|
+
const stillActivatable = [...set].some(o => o.onDecorationActivated);
|
|
694
|
+
if (this._decorationActivationState.has(group) && !stillActivatable) {
|
|
695
|
+
this._decorationActivationState.delete(group);
|
|
696
|
+
this._sendDecorationActivationToFrames(group, false);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const stillHoverable = [...set].some(o => o.onDecorationPointerEnter || o.onDecorationPointerLeave);
|
|
700
|
+
if (this._decorationHoverState.has(group) && !stillHoverable) {
|
|
701
|
+
this._decorationHoverState.delete(group);
|
|
702
|
+
this._sendDecorationHoverToFrames(group, false);
|
|
671
703
|
}
|
|
672
704
|
});
|
|
673
705
|
}
|
|
@@ -679,6 +711,13 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
679
711
|
});
|
|
680
712
|
}
|
|
681
713
|
|
|
714
|
+
private _sendDecorationHoverToFrames(group: string, hoverable: boolean): void {
|
|
715
|
+
const frames = this._cframes.filter(f => !!f) as (FrameManager | FXLFrameManager)[];
|
|
716
|
+
frames.forEach(f => {
|
|
717
|
+
if (f.msg) f.msg.send("decoration_hoverable", { group, hoverable });
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
682
721
|
public applyDecorations(decorations: Decoration[], group: string): void {
|
|
683
722
|
const previous = this._decorations.get(group) ?? [];
|
|
684
723
|
const prevById = new Map(previous.map(d => [d.id, d]));
|
|
@@ -706,6 +745,10 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
706
745
|
if (activatable !== undefined) {
|
|
707
746
|
this._sendDecorationActivationToFrames(group, activatable);
|
|
708
747
|
}
|
|
748
|
+
const hoverable = this._decorationHoverState.get(group);
|
|
749
|
+
if (hoverable !== undefined) {
|
|
750
|
+
this._sendDecorationHoverToFrames(group, hoverable);
|
|
751
|
+
}
|
|
709
752
|
}
|
|
710
753
|
|
|
711
754
|
private _sendDecorationOps(
|
|
@@ -751,6 +794,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
751
794
|
for (const [group, activatable] of this._decorationActivationState) {
|
|
752
795
|
frame.msg.send("decoration_activatable", { group, activatable });
|
|
753
796
|
}
|
|
797
|
+
for (const [group, hoverable] of this._decorationHoverState) {
|
|
798
|
+
frame.msg.send("decoration_hoverable", { group, hoverable });
|
|
799
|
+
}
|
|
754
800
|
}
|
|
755
801
|
|
|
756
802
|
private _reapplyDecorationsToCurrentFrames(): void {
|
|
@@ -769,13 +815,36 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
769
815
|
const decoration = (this._decorations.get(data.group) ?? []).find(d => d.id === data.decorationId);
|
|
770
816
|
if (!decoration) return false;
|
|
771
817
|
|
|
772
|
-
const event:
|
|
818
|
+
const event: OnDecorationActivatedEvent = { decoration, group: data.group, rect: data.rect, point: data.point };
|
|
773
819
|
let anyHandled = false;
|
|
774
820
|
for (const obs of observers)
|
|
775
|
-
if (obs.onDecorationActivated(event)) anyHandled = true;
|
|
821
|
+
if (obs.onDecorationActivated?.(event)) anyHandled = true;
|
|
776
822
|
return anyHandled;
|
|
777
823
|
}
|
|
778
824
|
|
|
825
|
+
private _handleDecorationPointerEnter(data: DecorationPointerEnterData): void {
|
|
826
|
+
const observers = this._decorationObservers.get(data.group);
|
|
827
|
+
if (!observers || observers.size === 0) return;
|
|
828
|
+
const decoration = (this._decorations.get(data.group) ?? []).find(d => d.id === data.decorationId);
|
|
829
|
+
if (!decoration) return;
|
|
830
|
+
this._decorationHoveredDecorations.set(data.group, decoration);
|
|
831
|
+
const event: OnDecorationPointerEnterEvent = { decoration, group: data.group, rect: data.rect, point: data.point };
|
|
832
|
+
for (const obs of observers)
|
|
833
|
+
obs.onDecorationPointerEnter?.(event);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
private _handleDecorationPointerLeave(data: DecorationPointerLeaveData): void {
|
|
837
|
+
const observers = this._decorationObservers.get(data.group);
|
|
838
|
+
if (!observers || observers.size === 0) return;
|
|
839
|
+
const decoration = (this._decorations.get(data.group) ?? []).find(d => d.id === data.decorationId)
|
|
840
|
+
?? this._decorationHoveredDecorations.get(data.group);
|
|
841
|
+
this._decorationHoveredDecorations.delete(data.group);
|
|
842
|
+
if (!decoration) return;
|
|
843
|
+
const event: OnDecorationPointerLeaveEvent = { decoration, group: data.group, rect: data.rect, point: data.point };
|
|
844
|
+
for (const obs of observers)
|
|
845
|
+
obs.onDecorationPointerLeave?.(event);
|
|
846
|
+
}
|
|
847
|
+
|
|
779
848
|
// End of Decoration
|
|
780
849
|
|
|
781
850
|
public async destroy() {
|
|
@@ -790,7 +859,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
790
859
|
await this.framePool?.destroy();
|
|
791
860
|
this._decorations.clear();
|
|
792
861
|
this._decorationObservers.clear();
|
|
862
|
+
this._decorationHoveredDecorations.clear();
|
|
793
863
|
this._decorationActivationState.clear();
|
|
864
|
+
this._decorationHoverState.clear();
|
|
794
865
|
}
|
|
795
866
|
|
|
796
867
|
private async changeResource(relative: number): Promise<boolean> {
|
|
@@ -829,6 +900,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
829
900
|
// positionChanged via _pong; on back-then-forward to an
|
|
830
901
|
// already-loaded frame no _pong fires, so dispatch explicitly here.
|
|
831
902
|
this.listeners.positionChanged(this.currentLocation);
|
|
903
|
+
this._notifyTimelineChange(this.currentLocation);
|
|
832
904
|
return true;
|
|
833
905
|
}
|
|
834
906
|
|
|
@@ -931,6 +1003,10 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
931
1003
|
this.lastLocationInView = nearestPositions.last;
|
|
932
1004
|
this.updateViewport(progression);
|
|
933
1005
|
this.listeners.positionChanged(this.currentLocation);
|
|
1006
|
+
const locatorForTimeline = progression.fragmentId
|
|
1007
|
+
? this.currentLocation.copyWithLocations({ fragments: [`#${progression.fragmentId}`] })
|
|
1008
|
+
: this.currentLocation;
|
|
1009
|
+
this._notifyTimelineChange(locatorForTimeline);
|
|
934
1010
|
await this.framePool.update(this.pub, this.currentLocation, this.determineModules());
|
|
935
1011
|
}
|
|
936
1012
|
|
|
@@ -1033,9 +1109,54 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
1033
1109
|
return this.pub;
|
|
1034
1110
|
}
|
|
1035
1111
|
|
|
1112
|
+
get timeline(): Timeline {
|
|
1113
|
+
const t = this.pub.timeline;
|
|
1114
|
+
if (!this._timelineAugmented && this.positions?.length) {
|
|
1115
|
+
const positions = this.positions;
|
|
1116
|
+
// We have no way to tell which position within an href corresponds to which
|
|
1117
|
+
// toc entry (fragment ids aren't resolvable against the positions list). So
|
|
1118
|
+
// only the first *leaf* entry for a given href without a fragment match gets
|
|
1119
|
+
// the resource's start position; every other leaf entry sharing that href is
|
|
1120
|
+
// left without a position rather than being given one we can't actually derive.
|
|
1121
|
+
// Container items (chapters with their own toc children) are skipped entirely
|
|
1122
|
+
// here — otherwise the chapter itself would claim the one derivable slot before
|
|
1123
|
+
// any of its displayed children get a turn.
|
|
1124
|
+
const assignedHrefs = new Set<string>();
|
|
1125
|
+
t.augment((item, link) => {
|
|
1126
|
+
if (item.children?.length) return {};
|
|
1127
|
+
const hashIndex = link.href.indexOf('#');
|
|
1128
|
+
const bare = hashIndex >= 0 ? link.href.slice(0, hashIndex) : link.href;
|
|
1129
|
+
const fragment = hashIndex >= 0 ? link.href.slice(hashIndex + 1) : undefined;
|
|
1130
|
+
const entries = positions.filter(p => p.href === bare);
|
|
1131
|
+
if (!entries.length) return {};
|
|
1132
|
+
const atFragment = fragment
|
|
1133
|
+
? entries.find(p => p.locations.fragments[0] === fragment)
|
|
1134
|
+
: undefined;
|
|
1135
|
+
let candidate: Locator | undefined;
|
|
1136
|
+
if (atFragment) {
|
|
1137
|
+
candidate = atFragment;
|
|
1138
|
+
} else if (!assignedHrefs.has(bare)) {
|
|
1139
|
+
assignedHrefs.add(bare);
|
|
1140
|
+
candidate = entries.reduce((min, p) =>
|
|
1141
|
+
(p.locations.position ?? Infinity) < (min.locations.position ?? Infinity) ? p : min
|
|
1142
|
+
);
|
|
1143
|
+
} else {
|
|
1144
|
+
candidate = undefined;
|
|
1145
|
+
}
|
|
1146
|
+
if (!candidate) return {};
|
|
1147
|
+
return {
|
|
1148
|
+
position: candidate.locations.position,
|
|
1149
|
+
scroll: atFragment?.locations.progression,
|
|
1150
|
+
};
|
|
1151
|
+
});
|
|
1152
|
+
this._timelineAugmented = true;
|
|
1153
|
+
}
|
|
1154
|
+
return t;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1036
1157
|
private async loadLocator(locator: Locator, cb: (ok: boolean) => void) {
|
|
1037
1158
|
let done = false;
|
|
1038
|
-
let cssSelector = (
|
|
1159
|
+
let cssSelector = getCssSelector(locator.locations);
|
|
1039
1160
|
if(locator.text?.highlight) {
|
|
1040
1161
|
done = await new Promise<boolean>((res, _) => {
|
|
1041
1162
|
// Attempt to go to a highlighted piece of text in the resource
|
|
@@ -1067,7 +1188,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
1067
1188
|
// This sanity check has to be performed because we're still passing non-locator class
|
|
1068
1189
|
// locator objects to this function. This is not good and should eventually be forbidden
|
|
1069
1190
|
// or the locator should be deserialized sometime before this function.
|
|
1070
|
-
const hid = (
|
|
1191
|
+
const hid = getHtmlId(locator.locations);
|
|
1071
1192
|
if(hid)
|
|
1072
1193
|
done = await new Promise<boolean>((res, _) => {
|
|
1073
1194
|
// Attempt to go to an HTML ID in the resource
|
|
@@ -1113,4 +1234,12 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
1113
1234
|
public goLink(link: Link, animated: boolean, cb: (ok: boolean) => void): void {
|
|
1114
1235
|
return this.go(link.locator, animated, cb);
|
|
1115
1236
|
}
|
|
1237
|
+
|
|
1238
|
+
private _notifyTimelineChange(locator: Locator): void {
|
|
1239
|
+
const item = this.timeline.locate(locator);
|
|
1240
|
+
if (item !== this._currentTimelineItem) {
|
|
1241
|
+
this._currentTimelineItem = item;
|
|
1242
|
+
this.listeners.timelineItemChanged(item);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1116
1245
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Loader, ModuleName } from "@readium/navigator-html-injectables";
|
|
2
2
|
import { FrameComms } from "./FrameComms.ts";
|
|
3
|
-
import type { ReadiumWindow } from "
|
|
4
|
-
import { sML } from "@readium/
|
|
3
|
+
import type { ReadiumWindow } from "@readium/navigator-html-injectables";
|
|
4
|
+
import { sML } from "@readium/helpers";
|
|
5
5
|
import type { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator.ts";
|
|
6
6
|
import { KeyboardConditionBridge } from "../../peripherals/KeyboardConditionBridge.ts";
|
|
7
7
|
|
|
@@ -21,7 +21,8 @@ export class FrameManager {
|
|
|
21
21
|
constructor(
|
|
22
22
|
source: string,
|
|
23
23
|
contentProtectionConfig: IContentProtectionConfig = {},
|
|
24
|
-
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
|
|
24
|
+
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
|
|
25
|
+
private readonly timelineFragmentIds: string[] = []
|
|
25
26
|
) {
|
|
26
27
|
this.frame = document.createElement("iframe");
|
|
27
28
|
this.frame.sandbox.value = "allow-same-origin allow-scripts";
|
|
@@ -141,6 +142,10 @@ export class FrameManager {
|
|
|
141
142
|
// Apply content protection synchronously
|
|
142
143
|
this.applyContentProtection();
|
|
143
144
|
|
|
145
|
+
// Send timeline fragment IDs so the snapper can set up observers
|
|
146
|
+
if (this.timelineFragmentIds.length > 0)
|
|
147
|
+
this.comms?.send("timeline_entries", this.timelineFragmentIds);
|
|
148
|
+
|
|
144
149
|
const remove = () => {
|
|
145
150
|
this.frame.style.removeProperty("visibility");
|
|
146
151
|
this.frame.style.removeProperty("aria-hidden");
|
|
@@ -21,6 +21,7 @@ export class FramePoolManager {
|
|
|
21
21
|
private readonly injector: Injector | null = null;
|
|
22
22
|
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
23
23
|
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
24
|
+
private readonly getFragmentIds: (href: string) => string[];
|
|
24
25
|
|
|
25
26
|
constructor(
|
|
26
27
|
container: HTMLElement,
|
|
@@ -28,7 +29,8 @@ export class FramePoolManager {
|
|
|
28
29
|
cssProperties?: { [key: string]: string },
|
|
29
30
|
injector?: Injector | null,
|
|
30
31
|
contentProtectionConfig?: IContentProtectionConfig,
|
|
31
|
-
keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig
|
|
32
|
+
keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig,
|
|
33
|
+
getFragmentIds?: (href: string) => string[]
|
|
32
34
|
) {
|
|
33
35
|
this.container = container;
|
|
34
36
|
this.positions = positions;
|
|
@@ -36,6 +38,7 @@ export class FramePoolManager {
|
|
|
36
38
|
this.injector = injector ?? null;
|
|
37
39
|
this.contentProtectionConfig = contentProtectionConfig || {};
|
|
38
40
|
this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
|
|
41
|
+
this.getFragmentIds = getFragmentIds ?? (() => []);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
async destroy() {
|
|
@@ -171,7 +174,7 @@ export class FramePoolManager {
|
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
// Create <iframe>
|
|
174
|
-
const fm = new FrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
|
|
177
|
+
const fm = new FrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href));
|
|
175
178
|
if(href !== newHref) await fm.hide(); // Avoid unecessary hide
|
|
176
179
|
this.container.appendChild(fm.iframe);
|
|
177
180
|
await fm.load(modules);
|
|
@@ -2,7 +2,7 @@ import { Loader, ModuleName } from "@readium/navigator-html-injectables";
|
|
|
2
2
|
import { Page, ReadingProgression } from "@readium/shared";
|
|
3
3
|
import { FrameComms } from "../frame/FrameComms.ts";
|
|
4
4
|
import { FXLPeripherals } from "./FXLPeripherals.ts";
|
|
5
|
-
import type { ReadiumWindow } from "
|
|
5
|
+
import type { ReadiumWindow } from "@readium/navigator-html-injectables";
|
|
6
6
|
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator.ts";
|
|
7
7
|
import { KeyboardConditionBridge } from "../../peripherals/KeyboardConditionBridge.ts";
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
withFallback
|
|
20
20
|
} from "../../preferences/guards.ts";
|
|
21
21
|
|
|
22
|
-
import { sMLWithRequest } from "@readium/
|
|
22
|
+
import { sMLWithRequest } from "@readium/helpers";
|
|
23
23
|
|
|
24
24
|
export interface IEpubDefaults {
|
|
25
25
|
backgroundColor?: string | null,
|
|
@@ -3,7 +3,7 @@ import { ExperimentKey, TextAlignment } from "../../preferences/Types.ts";
|
|
|
3
3
|
import { EpubDefaults } from "./EpubDefaults.ts";
|
|
4
4
|
import { EpubPreferences } from "./EpubPreferences.ts";
|
|
5
5
|
|
|
6
|
-
import { sMLWithRequest } from "@readium/
|
|
6
|
+
import { sMLWithRequest } from "@readium/helpers";
|
|
7
7
|
|
|
8
8
|
export interface IEpubSettings {
|
|
9
9
|
backgroundColor?: string | null,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Loader, ModuleName } from "@readium/navigator-html-injectables";
|
|
2
2
|
import { FrameComms } from "../epub/frame/FrameComms.ts";
|
|
3
|
-
import type { ReadiumWindow } from "
|
|
4
|
-
import { sML } from "@readium/
|
|
3
|
+
import type { ReadiumWindow } from "@readium/navigator-html-injectables";
|
|
4
|
+
import { sML } from "@readium/helpers";
|
|
5
5
|
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../Navigator.ts";
|
|
6
6
|
import { KeyboardConditionBridge } from "../peripherals/KeyboardConditionBridge.ts";
|
|
7
7
|
|
|
@@ -20,7 +20,8 @@ export class WebPubFrameManager {
|
|
|
20
20
|
constructor(
|
|
21
21
|
source: string,
|
|
22
22
|
contentProtectionConfig: IContentProtectionConfig = {},
|
|
23
|
-
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
|
|
23
|
+
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
|
|
24
|
+
private readonly timelineFragmentIds: string[] = []
|
|
24
25
|
) {
|
|
25
26
|
this.frame = document.createElement("iframe");
|
|
26
27
|
this.frame.classList.add("readium-navigator-iframe");
|
|
@@ -138,6 +139,11 @@ export class WebPubFrameManager {
|
|
|
138
139
|
this.comms?.send("focus", undefined, () => {
|
|
139
140
|
// Apply content protection synchronously
|
|
140
141
|
this.applyContentProtection();
|
|
142
|
+
|
|
143
|
+
// Send timeline fragment IDs so the snapper can set up observers
|
|
144
|
+
if (this.timelineFragmentIds.length > 0)
|
|
145
|
+
this.comms?.send("timeline_entries", this.timelineFragmentIds);
|
|
146
|
+
|
|
141
147
|
const remove = () => {
|
|
142
148
|
this.frame.style.removeProperty("visibility");
|
|
143
149
|
this.frame.style.removeProperty("aria-hidden");
|
|
@@ -17,19 +17,22 @@ export class WebPubFramePoolManager {
|
|
|
17
17
|
private readonly injector?: Injector | null = null;
|
|
18
18
|
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
19
19
|
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
20
|
+
private readonly getFragmentIds: (href: string) => string[];
|
|
20
21
|
|
|
21
22
|
constructor(
|
|
22
23
|
container: HTMLElement,
|
|
23
24
|
cssProperties?: { [key: string]: string },
|
|
24
25
|
injector?: Injector | null,
|
|
25
26
|
contentProtectionConfig: IContentProtectionConfig = {},
|
|
26
|
-
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
|
|
27
|
+
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
|
|
28
|
+
getFragmentIds?: (href: string) => string[]
|
|
27
29
|
) {
|
|
28
30
|
this.container = container;
|
|
29
31
|
this.currentCssProperties = cssProperties;
|
|
30
32
|
this.injector = injector;
|
|
31
33
|
this.contentProtectionConfig = contentProtectionConfig;
|
|
32
34
|
this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
|
|
35
|
+
this.getFragmentIds = getFragmentIds ?? (() => []);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
async destroy() {
|
|
@@ -154,7 +157,7 @@ export class WebPubFramePoolManager {
|
|
|
154
157
|
this.blobs.set(href, blobURL);
|
|
155
158
|
}
|
|
156
159
|
|
|
157
|
-
const fm = new WebPubFrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
|
|
160
|
+
const fm = new WebPubFrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href));
|
|
158
161
|
if(href !== newHref) await fm.hide();
|
|
159
162
|
this.container.appendChild(fm.iframe);
|
|
160
163
|
await fm.load(modules);
|