@readium/navigator 2.5.8 → 2.6.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/dist/index.js +2573 -1952
- package/dist/index.umd.cjs +41 -40
- package/package.json +1 -2
- package/src/decorations/index.ts +148 -0
- package/src/epub/EpubNavigator.ts +184 -8
- package/src/epub/frame/FrameManager.ts +1 -1
- package/src/epub/fxl/FXLCoordinator.ts +1 -1
- package/src/epub/preferences/EpubDefaults.ts +1 -1
- package/src/epub/preferences/EpubSettings.ts +1 -1
- package/src/helpers/index.ts +0 -1
- package/src/index.ts +1 -0
- package/src/protection/DevToolsDetector.ts +1 -1
- package/src/webpub/WebPubFrameManager.ts +1 -1
- package/src/webpub/WebPubNavigator.ts +149 -5
- package/src/webpub/preferences/WebPubDefaults.ts +1 -1
- package/src/webpub/preferences/WebPubSettings.ts +1 -1
- package/types/src/decorations/index.d.ts +75 -0
- package/types/src/epub/EpubNavigator.d.ts +18 -2
- package/types/src/helpers/index.d.ts +0 -1
- package/types/src/index.d.ts +1 -0
- package/types/src/webpub/WebPubNavigator.d.ts +16 -1
- package/src/helpers/sML.ts +0 -143
- package/types/src/helpers/sML.d.ts +0 -56
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Feature, Link, Locator, Publication, ReadingProgression, LocatorLocations } from "@readium/shared";
|
|
1
|
+
import { Feature, Link, Locator, LocatorText, Publication, ReadingProgression, LocatorLocations } from "@readium/shared";
|
|
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, FrameClickEvent, KeyboardPeripheralEvent, ModuleName, SuspiciousActivityEvent, WebPubModules } from "@readium/navigator-html-injectables";
|
|
5
|
+
import { BasicTextSelection, CommsEventKey, ContextMenuEvent, DecorationActivatedEvent, FrameClickEvent, KeyboardPeripheralEvent, ModuleName, SuspiciousActivityEvent, WebPubModules } from "@readium/navigator-html-injectables";
|
|
6
6
|
import * as path from "path-browserify";
|
|
7
7
|
import { WebPubFrameManager } from "./WebPubFrameManager.ts";
|
|
8
|
+
import { Decoration, DecorableNavigator, DecorationActivationEvent, DecorationObserver, DecoratorConfig, decorationsEqual, resolveDecorationForWire, BUILTIN_DECORATION_TYPES } from "../decorations/index.ts";
|
|
8
9
|
import { ManagerEventKey } from "../epub/EpubNavigator.ts";
|
|
9
10
|
import { getScriptMode } from "../helpers/scriptMode.ts";
|
|
10
11
|
import { WebPubCSS } from "./css/WebPubCSS.ts";
|
|
@@ -26,6 +27,7 @@ export interface WebPubNavigatorConfiguration {
|
|
|
26
27
|
injectables?: IInjectablesConfig;
|
|
27
28
|
contentProtection?: IContentProtectionConfig;
|
|
28
29
|
keyboardPeripherals?: IKeyboardPeripheralsConfig;
|
|
30
|
+
decoratorConfig?: DecoratorConfig;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export interface WebPubNavigatorListeners {
|
|
@@ -58,7 +60,7 @@ const defaultListeners = (listeners: WebPubNavigatorListeners): WebPubNavigatorL
|
|
|
58
60
|
peripheral: listeners.peripheral || (() => {})
|
|
59
61
|
})
|
|
60
62
|
|
|
61
|
-
export class WebPubNavigator extends VisualNavigator implements Configurable<WebPubSettings, WebPubPreferences
|
|
63
|
+
export class WebPubNavigator extends VisualNavigator implements Configurable<WebPubSettings, WebPubPreferences>, DecorableNavigator {
|
|
62
64
|
private readonly pub: Publication;
|
|
63
65
|
private readonly container: HTMLElement;
|
|
64
66
|
private readonly listeners: WebPubNavigatorListeners;
|
|
@@ -80,6 +82,13 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
80
82
|
private readonly _suspiciousActivityListener: ((event: Event) => void) | null = null;
|
|
81
83
|
private readonly _keyboardPeripheralListener: ((event: Event) => void) | null = null;
|
|
82
84
|
|
|
85
|
+
private readonly _decoratorConfig: DecoratorConfig;
|
|
86
|
+
|
|
87
|
+
private _decorations: Map<string, Decoration[]> = new Map();
|
|
88
|
+
private _decorationObservers: Map<string, Set<DecorationObserver>> = new Map();
|
|
89
|
+
private _decorationActivationState: Map<string, boolean> = new Map();
|
|
90
|
+
private _decorationActivationConsumed = false;
|
|
91
|
+
|
|
83
92
|
private webViewport: VisualNavigatorViewport = {
|
|
84
93
|
readingOrder: [],
|
|
85
94
|
progressions: new Map(),
|
|
@@ -112,6 +121,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
112
121
|
|
|
113
122
|
// Initialize content protection with provided config or default values
|
|
114
123
|
this._contentProtection = configuration.contentProtection || {};
|
|
124
|
+
this._decoratorConfig = configuration.decoratorConfig || {};
|
|
115
125
|
|
|
116
126
|
// Merge keyboard peripherals
|
|
117
127
|
this._keyboardPeripherals = this.mergeKeyboardPeripherals(
|
|
@@ -254,6 +264,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
254
264
|
case "_pong":
|
|
255
265
|
this.listeners.frameLoaded(this.framePool.currentFrames[0]!.iframe.contentWindow!);
|
|
256
266
|
this.listeners.positionChanged(this.currentLocation);
|
|
267
|
+
this._reapplyDecorationsToCurrentFrame();
|
|
257
268
|
break;
|
|
258
269
|
case "first_visible_locator":
|
|
259
270
|
const loc = Locator.deserialize(data as string);
|
|
@@ -267,11 +278,23 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
267
278
|
});
|
|
268
279
|
this.listeners.positionChanged(this.currentLocation);
|
|
269
280
|
break;
|
|
270
|
-
case "text_selected":
|
|
271
|
-
|
|
281
|
+
case "text_selected": {
|
|
282
|
+
const selection = data as BasicTextSelection;
|
|
283
|
+
selection.locator = new Locator({ href: this.currentLocation.href, type: this.currentLocation.type, text: new LocatorText({ highlight: selection.text }) });
|
|
284
|
+
this.listeners.textSelected(selection);
|
|
272
285
|
break;
|
|
286
|
+
}
|
|
287
|
+
case "decoration_activated": {
|
|
288
|
+
const handled = this._handleDecorationActivated(data as DecorationActivatedEvent);
|
|
289
|
+
if (handled) this._decorationActivationConsumed = true;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
273
292
|
case "click":
|
|
274
293
|
case "tap":
|
|
294
|
+
if (this._decorationActivationConsumed) {
|
|
295
|
+
this._decorationActivationConsumed = false;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
275
298
|
const edata = data as FrameClickEvent;
|
|
276
299
|
if (edata.interactiveElement) {
|
|
277
300
|
const element = new DOMParser().parseFromString(
|
|
@@ -392,6 +415,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
392
415
|
this.eventListener(key, value);
|
|
393
416
|
};
|
|
394
417
|
}
|
|
418
|
+
this._reapplyDecorationsToCurrentFrame();
|
|
395
419
|
}
|
|
396
420
|
|
|
397
421
|
private async apply() {
|
|
@@ -414,8 +438,128 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
414
438
|
this._navigatorProtector?.destroy();
|
|
415
439
|
this._keyboardPeripheralsManager?.destroy();
|
|
416
440
|
await this.framePool?.destroy();
|
|
441
|
+
this._decorations.clear();
|
|
442
|
+
this._decorationObservers.clear();
|
|
443
|
+
this._decorationActivationState.clear();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// DecorableNavigator
|
|
447
|
+
|
|
448
|
+
public supportsDecorationStyle(styleTypeId: string): boolean {
|
|
449
|
+
return BUILTIN_DECORATION_TYPES.has(styleTypeId) ||
|
|
450
|
+
!!this._decoratorConfig.decorationTemplates?.[styleTypeId];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
public registerDecorationObserver(group: string, observer: DecorationObserver): void {
|
|
454
|
+
if (!this._decorationObservers.has(group))
|
|
455
|
+
this._decorationObservers.set(group, new Set());
|
|
456
|
+
this._decorationObservers.get(group)!.add(observer);
|
|
457
|
+
|
|
458
|
+
this._decorationActivationState.set(group, true);
|
|
459
|
+
this._sendDecorationActivatable(group, true);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
public unregisterDecorationObserver(observer: DecorationObserver): void {
|
|
463
|
+
this._decorationObservers.forEach((set, group) => {
|
|
464
|
+
if (set.has(observer)) {
|
|
465
|
+
set.delete(observer);
|
|
466
|
+
if (set.size === 0) {
|
|
467
|
+
this._decorationActivationState.delete(group);
|
|
468
|
+
this._sendDecorationActivatable(group, false);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
});
|
|
417
472
|
}
|
|
418
473
|
|
|
474
|
+
private _sendDecorationActivatable(group: string, activatable: boolean): void {
|
|
475
|
+
const frame = this.framePool?.currentFrames[0];
|
|
476
|
+
if (frame?.msg) frame.msg.send("decoration_activatable", { group, activatable });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
public applyDecorations(decorations: Decoration[], group: string): void {
|
|
480
|
+
const previous = this._decorations.get(group) ?? [];
|
|
481
|
+
const prevById = new Map(previous.map(d => [d.id, d]));
|
|
482
|
+
const nextById = new Map(decorations.map(d => [d.id, d]));
|
|
483
|
+
|
|
484
|
+
const toRemove: string[] = [];
|
|
485
|
+
const toAdd: Decoration[] = [];
|
|
486
|
+
const toUpdate: Decoration[] = [];
|
|
487
|
+
|
|
488
|
+
for (const [id, prev] of prevById) {
|
|
489
|
+
if (!nextById.has(id)) toRemove.push(id);
|
|
490
|
+
else if (!decorationsEqual(prev, nextById.get(id)!)) toUpdate.push(nextById.get(id)!);
|
|
491
|
+
}
|
|
492
|
+
for (const [id, next] of nextById) {
|
|
493
|
+
if (!prevById.has(id)) toAdd.push(next);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
this._decorations.set(group, decorations);
|
|
497
|
+
this._sendDecorationOps(group, toRemove, toAdd, toUpdate, previous);
|
|
498
|
+
|
|
499
|
+
const activatable = this._decorationActivationState.get(group);
|
|
500
|
+
if (activatable !== undefined) this._sendDecorationActivatable(group, activatable);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private _sendDecorationOps(
|
|
504
|
+
group: string,
|
|
505
|
+
toRemove: string[],
|
|
506
|
+
toAdd: Decoration[],
|
|
507
|
+
toUpdate: Decoration[],
|
|
508
|
+
previous: Decoration[]
|
|
509
|
+
): void {
|
|
510
|
+
const frame = this.framePool?.currentFrames[0];
|
|
511
|
+
if (!frame?.msg) return;
|
|
512
|
+
const href = this.currentLocation.href;
|
|
513
|
+
const prevById = new Map(previous.map(d => [d.id, d]));
|
|
514
|
+
|
|
515
|
+
for (const id of toRemove) {
|
|
516
|
+
const d = prevById.get(id);
|
|
517
|
+
if (!d || d.locator.href !== href) continue;
|
|
518
|
+
frame.msg.send("decorate", { group, action: "remove", decoration: { id } });
|
|
519
|
+
}
|
|
520
|
+
for (const d of toAdd) {
|
|
521
|
+
if (d.locator.href !== href) continue;
|
|
522
|
+
frame.msg.send("decorate", { group, action: "add", decoration: resolveDecorationForWire(d, this._decoratorConfig.decorationTemplates) });
|
|
523
|
+
}
|
|
524
|
+
for (const d of toUpdate) {
|
|
525
|
+
if (d.locator.href !== href) continue;
|
|
526
|
+
frame.msg.send("decorate", { group, action: "update", decoration: resolveDecorationForWire(d, this._decoratorConfig.decorationTemplates) });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private _reapplyDecorationsToCurrentFrame(): void {
|
|
531
|
+
const frame = this.framePool?.currentFrames[0];
|
|
532
|
+
if (!frame?.msg) return;
|
|
533
|
+
const href = this.currentLocation.href;
|
|
534
|
+
|
|
535
|
+
for (const [group, decorations] of this._decorations) {
|
|
536
|
+
const matching = decorations.filter(d => d.locator.href === href);
|
|
537
|
+
if (matching.length === 0) continue;
|
|
538
|
+
frame.msg.send("decorate", { group, action: "clear" });
|
|
539
|
+
for (const d of matching)
|
|
540
|
+
frame.msg.send("decorate", { group, action: "add", decoration: resolveDecorationForWire(d, this._decoratorConfig.decorationTemplates) });
|
|
541
|
+
}
|
|
542
|
+
for (const [group, activatable] of this._decorationActivationState) {
|
|
543
|
+
frame.msg.send("decoration_activatable", { group, activatable });
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private _handleDecorationActivated(data: DecorationActivatedEvent): boolean {
|
|
548
|
+
const observers = this._decorationObservers.get(data.group);
|
|
549
|
+
if (!observers || observers.size === 0) return false;
|
|
550
|
+
|
|
551
|
+
const decoration = (this._decorations.get(data.group) ?? []).find(d => d.id === data.decorationId);
|
|
552
|
+
if (!decoration) return false;
|
|
553
|
+
|
|
554
|
+
const event: DecorationActivationEvent = { decoration, group: data.group, rect: data.rect, point: data.point };
|
|
555
|
+
let anyHandled = false;
|
|
556
|
+
for (const obs of observers)
|
|
557
|
+
if (obs.onDecorationActivated(event)) anyHandled = true;
|
|
558
|
+
return anyHandled;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// End of DecorableNavigator
|
|
562
|
+
|
|
419
563
|
private async changeResource(relative: number): Promise<boolean> {
|
|
420
564
|
if (relative === 0) return false;
|
|
421
565
|
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
ensureExperiment
|
|
15
15
|
} from "../../preferences/guards.ts";
|
|
16
16
|
|
|
17
|
-
import { sMLWithRequest } from "
|
|
17
|
+
import { sMLWithRequest } from "@readium/navigator-html-injectables";
|
|
18
18
|
|
|
19
19
|
export interface IWebPubDefaults {
|
|
20
20
|
fontFamily?: string | null,
|
|
@@ -3,7 +3,7 @@ import { ExperimentKey, TextAlignment } from "../../preferences/Types.ts";
|
|
|
3
3
|
import { WebPubDefaults } from "./WebPubDefaults.ts";
|
|
4
4
|
import { WebPubPreferences } from "./WebPubPreferences.ts";
|
|
5
5
|
|
|
6
|
-
import { sMLWithRequest } from "
|
|
6
|
+
import { sMLWithRequest } from "@readium/navigator-html-injectables";
|
|
7
7
|
|
|
8
8
|
export interface IWebPubSettings {
|
|
9
9
|
fontFamily?: string | null,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Decoration as InjectableDecoration, BuiltinDecorationStyle, HTMLDecorationTemplate as WireHTMLDecorationTemplate } from "@readium/navigator-html-injectables";
|
|
2
|
+
export type { BuiltinDecorationStyle };
|
|
3
|
+
export { DecorationLayout, DecorationStyleType, DecorationWidth } from "@readium/navigator-html-injectables";
|
|
4
|
+
/**
|
|
5
|
+
* Navigator-level decoration template. `element` is a function called once per decoration to
|
|
6
|
+
* generate the HTML string that the injectable will render. The result is resolved on the
|
|
7
|
+
* navigator side (before postMessage) and sanitized by the injectable.
|
|
8
|
+
*/
|
|
9
|
+
export interface HTMLDecorationTemplate extends Omit<WireHTMLDecorationTemplate, 'element'> {
|
|
10
|
+
element: (decoration: Decoration) => string;
|
|
11
|
+
}
|
|
12
|
+
export type DecorationStyle = BuiltinDecorationStyle | HTMLDecorationTemplate;
|
|
13
|
+
export interface Decoration extends Omit<InjectableDecoration, 'style'> {
|
|
14
|
+
style: DecorationStyle;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolves a navigator-level Decoration to a wire-safe plain object for postMessage.
|
|
18
|
+
* For Template styles, calls `element(decoration)` and embeds the resulting HTML string.
|
|
19
|
+
* For registered custom style IDs (found in `decorationTemplates`), resolves the template
|
|
20
|
+
* and converts the style to a Template wire object.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveDecorationForWire(decoration: Decoration, decorationTemplates?: Record<string, HTMLDecorationTemplate>): unknown;
|
|
23
|
+
export interface DecorationActivationEvent {
|
|
24
|
+
decoration: Decoration;
|
|
25
|
+
group: string;
|
|
26
|
+
/** Bounding rect of the activated decoration in navigator container coordinates (CSS pixels). */
|
|
27
|
+
rect?: {
|
|
28
|
+
top: number;
|
|
29
|
+
left: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
/** Tap/click point in navigator container coordinates (CSS pixels). */
|
|
34
|
+
point?: {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface DecorationObserver {
|
|
40
|
+
/**
|
|
41
|
+
* Called when a user activates a decoration (click or tap).
|
|
42
|
+
* Return true to indicate the event was handled — this suppresses normal tap/click navigation.
|
|
43
|
+
*/
|
|
44
|
+
onDecorationActivated(event: DecorationActivationEvent): boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function decorationsEqual(a: Decoration, b: Decoration): boolean;
|
|
47
|
+
/** Configuration for decoration rendering. */
|
|
48
|
+
export interface DecoratorConfig {
|
|
49
|
+
/**
|
|
50
|
+
* Named custom styles. Each key is a style type ID; the value is the template that
|
|
51
|
+
* generates the HTML for decorations of that type. When a decoration's `style.type`
|
|
52
|
+
* matches a key here, the navigator resolves the template and sends it to the
|
|
53
|
+
* injectable as a Template-type decoration.
|
|
54
|
+
*/
|
|
55
|
+
decorationTemplates?: Record<string, HTMLDecorationTemplate>;
|
|
56
|
+
}
|
|
57
|
+
declare const BUILTIN_DECORATION_TYPES: Set<string>;
|
|
58
|
+
export interface DecorableNavigator {
|
|
59
|
+
/**
|
|
60
|
+
* Replaces all decorations for the given group with the provided list.
|
|
61
|
+
* The navigator diffs the new list against the current state and issues
|
|
62
|
+
* add / update / remove / clear commands as needed.
|
|
63
|
+
*/
|
|
64
|
+
applyDecorations(decorations: Decoration[], group: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Returns whether the given style type ID can be rendered by this navigator.
|
|
67
|
+
* Returns true for all built-in types and any IDs registered in DecoratorConfig.
|
|
68
|
+
*/
|
|
69
|
+
supportsDecorationStyle(styleTypeId: string): boolean;
|
|
70
|
+
/** Registers an observer for activation events on the given group. */
|
|
71
|
+
registerDecorationObserver(group: string, observer: DecorationObserver): void;
|
|
72
|
+
/** Unregisters a previously registered observer from all groups. */
|
|
73
|
+
unregisterDecorationObserver(observer: DecorationObserver): void;
|
|
74
|
+
}
|
|
75
|
+
export { BUILTIN_DECORATION_TYPES };
|
|
@@ -3,6 +3,7 @@ import { Configurable, ConfigurableSettings, VisualNavigator, VisualNavigatorVie
|
|
|
3
3
|
import { FramePoolManager } from "./frame/FramePoolManager.ts";
|
|
4
4
|
import { FXLFramePoolManager } from "./fxl/FXLFramePoolManager.ts";
|
|
5
5
|
import { CommsEventKey, ContextMenuEvent, BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
|
|
6
|
+
import { Decoration, DecorationObserver, DecorableNavigator, DecoratorConfig } from "../decorations/index.ts";
|
|
6
7
|
import { FXLFrameManager } from "./fxl/FXLFrameManager.ts";
|
|
7
8
|
import { FrameManager } from "./frame/FrameManager.ts";
|
|
8
9
|
import { IEpubPreferences, EpubPreferences } from "./preferences/EpubPreferences.ts";
|
|
@@ -18,6 +19,7 @@ export interface EpubNavigatorConfiguration {
|
|
|
18
19
|
injectables?: IInjectablesConfig;
|
|
19
20
|
contentProtection?: IContentProtectionConfig;
|
|
20
21
|
keyboardPeripherals?: IKeyboardPeripheralsConfig;
|
|
22
|
+
decoratorConfig?: DecoratorConfig;
|
|
21
23
|
}
|
|
22
24
|
export interface EpubNavigatorListeners {
|
|
23
25
|
frameLoaded: (wnd: Window) => void;
|
|
@@ -34,7 +36,7 @@ export interface EpubNavigatorListeners {
|
|
|
34
36
|
contextMenu: (data: ContextMenuEvent) => void;
|
|
35
37
|
peripheral: (data: KeyboardPeripheralEventData) => void;
|
|
36
38
|
}
|
|
37
|
-
export declare class EpubNavigator extends VisualNavigator implements Configurable<ConfigurableSettings, EpubPreferences
|
|
39
|
+
export declare class EpubNavigator extends VisualNavigator implements Configurable<ConfigurableSettings, EpubPreferences>, DecorableNavigator {
|
|
38
40
|
private readonly pub;
|
|
39
41
|
private readonly container;
|
|
40
42
|
private readonly listeners;
|
|
@@ -60,6 +62,11 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
|
|
|
60
62
|
private readonly _suspiciousActivityListener;
|
|
61
63
|
private readonly _keyboardPeripheralListener;
|
|
62
64
|
private resizeObserver;
|
|
65
|
+
private readonly _decoratorConfig;
|
|
66
|
+
private _decorations;
|
|
67
|
+
private _decorationObservers;
|
|
68
|
+
private _decorationActivationState;
|
|
69
|
+
private _decorationActivationConsumed;
|
|
63
70
|
private reflowViewport;
|
|
64
71
|
constructor(container: HTMLElement, pub: Publication, listeners: EpubNavigatorListeners, positions?: Locator[], initialPosition?: Locator | undefined, configuration?: EpubNavigatorConfiguration);
|
|
65
72
|
static determineLayout(pub: Publication, scroll?: boolean): Layout;
|
|
@@ -90,10 +97,19 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
|
|
|
90
97
|
* to trigger the navigator when user's mouse/keyboard focus is
|
|
91
98
|
* outside the readium-controller navigator. Be careful!
|
|
92
99
|
*/
|
|
93
|
-
eventListener(key: CommsEventKey | ManagerEventKey, data: unknown): void;
|
|
100
|
+
eventListener(key: CommsEventKey | ManagerEventKey, data: unknown, sourceFrame?: FrameManager | FXLFrameManager): void;
|
|
94
101
|
private determineModules;
|
|
95
102
|
private attachListener;
|
|
96
103
|
private apply;
|
|
104
|
+
supportsDecorationStyle(styleTypeId: string): boolean;
|
|
105
|
+
registerDecorationObserver(group: string, observer: DecorationObserver): void;
|
|
106
|
+
unregisterDecorationObserver(observer: DecorationObserver): void;
|
|
107
|
+
private _sendDecorationActivationToFrames;
|
|
108
|
+
applyDecorations(decorations: Decoration[], group: string): void;
|
|
109
|
+
private _sendDecorationOps;
|
|
110
|
+
private _reapplyDecorationsToFrame;
|
|
111
|
+
private _reapplyDecorationsToCurrentFrames;
|
|
112
|
+
private _handleDecorationActivated;
|
|
97
113
|
destroy(): Promise<void>;
|
|
98
114
|
private changeResource;
|
|
99
115
|
private findLastPositionInProgressionRange;
|
package/types/src/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { VisualNavigator, VisualNavigatorViewport, KeyboardPeripheralEventData }
|
|
|
3
3
|
import { Configurable } from "../preferences/Configurable.ts";
|
|
4
4
|
import { BasicTextSelection, CommsEventKey, ContextMenuEvent, FrameClickEvent, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
|
|
5
5
|
import { WebPubFrameManager } from "./WebPubFrameManager.ts";
|
|
6
|
+
import { Decoration, DecorableNavigator, DecorationObserver, DecoratorConfig } from "../decorations/index.ts";
|
|
6
7
|
import { ManagerEventKey } from "../epub/EpubNavigator.ts";
|
|
7
8
|
import { IWebPubPreferences, WebPubPreferences } from "./preferences/WebPubPreferences.ts";
|
|
8
9
|
import { IWebPubDefaults } from "./preferences/WebPubDefaults.ts";
|
|
@@ -16,6 +17,7 @@ export interface WebPubNavigatorConfiguration {
|
|
|
16
17
|
injectables?: IInjectablesConfig;
|
|
17
18
|
contentProtection?: IContentProtectionConfig;
|
|
18
19
|
keyboardPeripherals?: IKeyboardPeripheralsConfig;
|
|
20
|
+
decoratorConfig?: DecoratorConfig;
|
|
19
21
|
}
|
|
20
22
|
export interface WebPubNavigatorListeners {
|
|
21
23
|
frameLoaded: (wnd: Window) => void;
|
|
@@ -31,7 +33,7 @@ export interface WebPubNavigatorListeners {
|
|
|
31
33
|
contextMenu: (data: ContextMenuEvent) => void;
|
|
32
34
|
peripheral: (data: KeyboardPeripheralEventData) => void;
|
|
33
35
|
}
|
|
34
|
-
export declare class WebPubNavigator extends VisualNavigator implements Configurable<WebPubSettings, WebPubPreferences
|
|
36
|
+
export declare class WebPubNavigator extends VisualNavigator implements Configurable<WebPubSettings, WebPubPreferences>, DecorableNavigator {
|
|
35
37
|
private readonly pub;
|
|
36
38
|
private readonly container;
|
|
37
39
|
private readonly listeners;
|
|
@@ -51,6 +53,11 @@ export declare class WebPubNavigator extends VisualNavigator implements Configur
|
|
|
51
53
|
private readonly _keyboardPeripheralsManager;
|
|
52
54
|
private readonly _suspiciousActivityListener;
|
|
53
55
|
private readonly _keyboardPeripheralListener;
|
|
56
|
+
private readonly _decoratorConfig;
|
|
57
|
+
private _decorations;
|
|
58
|
+
private _decorationObservers;
|
|
59
|
+
private _decorationActivationState;
|
|
60
|
+
private _decorationActivationConsumed;
|
|
54
61
|
private webViewport;
|
|
55
62
|
constructor(container: HTMLElement, pub: Publication, listeners: WebPubNavigatorListeners, initialPosition?: Locator | undefined, configuration?: WebPubNavigatorConfiguration);
|
|
56
63
|
load(): Promise<void>;
|
|
@@ -72,6 +79,14 @@ export declare class WebPubNavigator extends VisualNavigator implements Configur
|
|
|
72
79
|
private attachListener;
|
|
73
80
|
private apply;
|
|
74
81
|
destroy(): Promise<void>;
|
|
82
|
+
supportsDecorationStyle(styleTypeId: string): boolean;
|
|
83
|
+
registerDecorationObserver(group: string, observer: DecorationObserver): void;
|
|
84
|
+
unregisterDecorationObserver(observer: DecorationObserver): void;
|
|
85
|
+
private _sendDecorationActivatable;
|
|
86
|
+
applyDecorations(decorations: Decoration[], group: string): void;
|
|
87
|
+
private _sendDecorationOps;
|
|
88
|
+
private _reapplyDecorationsToCurrentFrame;
|
|
89
|
+
private _handleDecorationActivated;
|
|
75
90
|
private changeResource;
|
|
76
91
|
private updateViewport;
|
|
77
92
|
private syncLocation;
|
package/src/helpers/sML.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* (℠)
|
|
3
|
-
* # sML.js | I'm a Simple and Middling Library.
|
|
4
|
-
*
|
|
5
|
-
* * Copyright (c) Satoru MATSUSHIMA - https://github.com/satorumurmur/sML
|
|
6
|
-
* * Licensed under the MIT license. - http://www.opensource.org/licenses/mit-license.php
|
|
7
|
-
*
|
|
8
|
-
* Portions of this code come from the sML library
|
|
9
|
-
* Current version: 1.0.36
|
|
10
|
-
*/
|
|
11
|
-
/// <reference types="user-agent-data-types" />
|
|
12
|
-
|
|
13
|
-
declare interface OSFlags {
|
|
14
|
-
iOS: number[];
|
|
15
|
-
macOS: number[];
|
|
16
|
-
iPadOS: number[];
|
|
17
|
-
WindowsPhone: number[];
|
|
18
|
-
ChromeOS: number[];
|
|
19
|
-
Windows: number[];
|
|
20
|
-
Android: number[];
|
|
21
|
-
Linux: number[];
|
|
22
|
-
Firefox: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
declare interface UAFlags {
|
|
26
|
-
Gecko: number[];
|
|
27
|
-
Firefox: number[];
|
|
28
|
-
Waterfox: number[];
|
|
29
|
-
Opera: number[];
|
|
30
|
-
Silk: number[];
|
|
31
|
-
Blink: number[];
|
|
32
|
-
EdgeHTML: number[];
|
|
33
|
-
Chrome: number[];
|
|
34
|
-
Chromium: number[];
|
|
35
|
-
Phoebe: number[];
|
|
36
|
-
UCBrowser: number[];
|
|
37
|
-
Vivaldi: number[];
|
|
38
|
-
Safari: number[];
|
|
39
|
-
Edge: number[];
|
|
40
|
-
WebKit: number[];
|
|
41
|
-
Trident: number[];
|
|
42
|
-
InternetExplorer: number[];
|
|
43
|
-
Flash: number[];
|
|
44
|
-
Facebook: number[];
|
|
45
|
-
LINE: number[];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
declare type iOSRequest = "mobile" | "desktop" | undefined;
|
|
49
|
-
|
|
50
|
-
// Fallback when global 'navigator' is not available, such as in SSR environments.
|
|
51
|
-
const userAgent = () => typeof navigator === "undefined" ? "" : (navigator.userAgent || "");
|
|
52
|
-
const userAgentData = () => typeof navigator === "undefined" ? undefined : (navigator.userAgentData || undefined);
|
|
53
|
-
|
|
54
|
-
class sMLFactory {
|
|
55
|
-
OS: OSFlags;
|
|
56
|
-
UA: UAFlags;
|
|
57
|
-
Env!: string[];
|
|
58
|
-
|
|
59
|
-
constructor() {
|
|
60
|
-
const NUAD = userAgentData(), NUA = userAgent();
|
|
61
|
-
|
|
62
|
-
const _sV = (V?: string | number) => (typeof V === "string" || typeof V === "number") && V ? String(V).replace(/_/g, ".").split(".").map(I => parseInt(I) || 0) : [];
|
|
63
|
-
const _dV = (Pre="") => {
|
|
64
|
-
if(!Pre) return [];
|
|
65
|
-
const RE = new RegExp("^.*" + Pre + "[ :\\/]?(\\d+([\\._]\\d+)*).*$");
|
|
66
|
-
if(!RE.test(NUA)) return [];
|
|
67
|
-
return _sV(NUA.replace(RE, "$1"));
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
this.OS = ((OS: OSFlags) => {
|
|
71
|
-
if( /(macOS|Mac OS X)/.test(NUA)) {
|
|
72
|
-
if(/\(iP(hone|od touch);/.test(NUA)) OS.iOS = _dV("CPU (?:iPhone )?OS ");
|
|
73
|
-
if( /\(iPad;/.test(NUA)) OS.iOS = OS.iPadOS = _dV("CPU (?:iPhone )?OS ");
|
|
74
|
-
else if( /(macOS|Mac OS X) \d/.test(NUA)) document.ontouchend !== undefined ? OS.iOS = OS.iPadOS = _dV() : OS.macOS = _dV("(?:macOS|Mac OS X) ");
|
|
75
|
-
} else if( /Windows( NT)? \d/.test(NUA)) OS.Windows = (V => V[0] !== 6 || !V[1] ? V : V[1] === 1 ? [7] : V[1] === 2 ? [8] : [8, 1])(_dV("Windows(?: NT)?"));
|
|
76
|
-
else if( /Android \d/.test(NUA)) OS.Android = _dV("Android");
|
|
77
|
-
else if( /CrOS/.test(NUA)) OS.ChromeOS = _dV();
|
|
78
|
-
else if( /X11;/.test(NUA)) OS.Linux = _dV();
|
|
79
|
-
return OS;
|
|
80
|
-
})({} as OSFlags); if(NUAD) NUAD.getHighEntropyValues(["architecture", "model", "platform", "platformVersion", "uaFullVersion"]).then((HEUAD: any) => (OS => { const Pf = HEUAD.platform, PfV = HEUAD.platformVersion; if(!Pf || !PfV) return;
|
|
81
|
-
if( /^i(OS|P(hone|od touch))$/.test(Pf)) OS.iOS = _sV(PfV);
|
|
82
|
-
else if( /^iPad(OS)?$/.test(Pf)) OS.iOS = OS.iPadOS = _sV(PfV);
|
|
83
|
-
else if(/^(macOS|(Mac )?OS X|Mac(Intel)?)$/.test(Pf)) document.ontouchend !== undefined ? OS.iOS = OS.iPadOS = _sV() : OS.macOS = _sV(PfV);
|
|
84
|
-
else if( /^(Microsoft )?Windows$/.test(Pf)) OS.Windows = _sV(PfV);
|
|
85
|
-
else if( /^(Google )?Android$/.test(Pf)) OS.Android = _sV(PfV);
|
|
86
|
-
else if( /^((Google )?Chrome OS|CrOS)$/.test(Pf)) OS.ChromeOS = _sV(PfV);
|
|
87
|
-
else if( /^(Linux|Ubuntu|X11)$/.test(Pf)) OS.Linux = _sV(PfV);
|
|
88
|
-
else return; /**/ Object.keys(this.OS).forEach(Key => delete (this.OS as any)[Key]), Object.assign(this.OS, OS);
|
|
89
|
-
})({} as OSFlags));
|
|
90
|
-
|
|
91
|
-
this.UA = ((UA: UAFlags) => { let _OK = false;
|
|
92
|
-
if(NUAD && Array.isArray(NUAD.brands)) { const BnV = NUAD.brands.reduce((BnV: Record<string, number[]>, _: NavigatorUABrandVersion) => { BnV[_.brand] = [(_.version as any) * 1]; return BnV; }, {});
|
|
93
|
-
if(BnV["Google Chrome"]) _OK = true, UA.Blink = UA.Chromium = BnV["Chromium"] || [], UA.Chrome = BnV["Google Chrome"];
|
|
94
|
-
else if(BnV["Microsoft Edge"]) _OK = true, UA.Blink = UA.Chromium = BnV["Chromium"] || [], UA.Edge = BnV["Microsoft Edge"];
|
|
95
|
-
else if(BnV["Opera"]) _OK = true, UA.Blink = UA.Chromium = BnV["Chromium"] || [], UA.Opera = BnV["Opera"];
|
|
96
|
-
} if(!_OK) {
|
|
97
|
-
if( / Gecko\/\d/.test(NUA)) { UA.Gecko = _dV("rv");
|
|
98
|
-
if( / Waterfox\/\d/.test(NUA)) UA.Waterfox = _dV("Waterfox");
|
|
99
|
-
else if( / Firefox\/\d/.test(NUA)) UA.Firefox = _dV("Firefox");
|
|
100
|
-
} else if( / Edge\/\d/.test(NUA)) { UA.EdgeHTML = _dV("Edge");
|
|
101
|
-
UA.Edge = UA.EdgeHTML;
|
|
102
|
-
} else if(/ Chrom(ium|e)\/\d/.test(NUA)) { UA.Blink = UA.Chromium = (V => V[0] ? V : _dV("Chrome"))(_dV("Chromium"));
|
|
103
|
-
if( / EdgA?\/\d/.test(NUA)) UA.Edge = (V => V[0] ? V : _dV("Edg"))(_dV("EdgA"));
|
|
104
|
-
else if( / OPR\/\d/.test(NUA)) UA.Opera = _dV("OPR");
|
|
105
|
-
else if( / Vivaldi\/\d/.test(NUA)) UA.Vivaldi = _dV("Vivaldi");
|
|
106
|
-
else if( / Silk\/\d/.test(NUA)) UA.Silk = _dV("Silk");
|
|
107
|
-
else if( / UCBrowser\/\d/.test(NUA)) UA.UCBrowser = _dV("UCBrowser");
|
|
108
|
-
else if( / Phoebe\/\d/.test(NUA)) UA.Phoebe = _dV("Phoebe");
|
|
109
|
-
else UA.Chrome = (V => V[0] ? V : UA.Chromium)(_dV("Chrome"));
|
|
110
|
-
} else if( / AppleWebKit\/\d/.test(NUA)) { UA.WebKit = _dV("AppleWebKit");
|
|
111
|
-
if( / CriOS \d/.test(NUA)) UA.Chrome = _dV("CriOS");
|
|
112
|
-
else if( / FxiOS \d/.test(NUA)) UA.Firefox = _dV("FxiOS");
|
|
113
|
-
else if( / EdgiOS\/\d/.test(NUA)) UA.Edge = _dV("EdgiOS");
|
|
114
|
-
else if( / Version\/\d/.test(NUA)) UA.Safari = _dV("Version");
|
|
115
|
-
} else if( / Trident\/\d/.test(NUA)) { UA.Trident = _dV("Trident");
|
|
116
|
-
UA.InternetExplorer = (V => V[0] ? V : _dV("MSIE"))(_dV("rv"));
|
|
117
|
-
}
|
|
118
|
-
} /*+*/ if( /[\[; ]FB(AN|_IAB)\//.test(NUA)) UA.Facebook = _dV("FBAV");
|
|
119
|
-
/*+*/ if( / Line\/\d/.test(NUA)) UA.LINE = _dV("Line");
|
|
120
|
-
return UA;
|
|
121
|
-
})({} as UAFlags);
|
|
122
|
-
|
|
123
|
-
(this.Env as any) = { get: () => [this.OS, this.UA].reduce((Env: string[], OS_UA) => { for(const Par in OS_UA) if((OS_UA as any)[Par]) Env.push(Par); return Env; }, []) };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
class sMLFactoryWithRequest extends sMLFactory {
|
|
128
|
-
get iOSRequest(): iOSRequest {
|
|
129
|
-
const NUAD = userAgentData(), NUA = userAgent();
|
|
130
|
-
|
|
131
|
-
if (this.OS.iOS && !this.OS.iPadOS) {
|
|
132
|
-
return "mobile";
|
|
133
|
-
} else if (this.OS.iPadOS) {
|
|
134
|
-
return (/\(iPad;/.test(NUA) || (NUAD && /^iPad(OS)?$/.test(NUAD.platform))) ? "mobile" : "desktop"
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return undefined;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const sML = new sMLFactory();
|
|
142
|
-
const sMLWithRequest = new sMLFactoryWithRequest();
|
|
143
|
-
export { sML, sMLWithRequest };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* (℠)
|
|
3
|
-
* # sML.js | I'm a Simple and Middling Library.
|
|
4
|
-
*
|
|
5
|
-
* * Copyright (c) Satoru MATSUSHIMA - https://github.com/satorumurmur/sML
|
|
6
|
-
* * Licensed under the MIT license. - http://www.opensource.org/licenses/mit-license.php
|
|
7
|
-
*
|
|
8
|
-
* Portions of this code come from the sML library
|
|
9
|
-
* Current version: 1.0.36
|
|
10
|
-
*/
|
|
11
|
-
declare interface OSFlags {
|
|
12
|
-
iOS: number[];
|
|
13
|
-
macOS: number[];
|
|
14
|
-
iPadOS: number[];
|
|
15
|
-
WindowsPhone: number[];
|
|
16
|
-
ChromeOS: number[];
|
|
17
|
-
Windows: number[];
|
|
18
|
-
Android: number[];
|
|
19
|
-
Linux: number[];
|
|
20
|
-
Firefox: boolean;
|
|
21
|
-
}
|
|
22
|
-
declare interface UAFlags {
|
|
23
|
-
Gecko: number[];
|
|
24
|
-
Firefox: number[];
|
|
25
|
-
Waterfox: number[];
|
|
26
|
-
Opera: number[];
|
|
27
|
-
Silk: number[];
|
|
28
|
-
Blink: number[];
|
|
29
|
-
EdgeHTML: number[];
|
|
30
|
-
Chrome: number[];
|
|
31
|
-
Chromium: number[];
|
|
32
|
-
Phoebe: number[];
|
|
33
|
-
UCBrowser: number[];
|
|
34
|
-
Vivaldi: number[];
|
|
35
|
-
Safari: number[];
|
|
36
|
-
Edge: number[];
|
|
37
|
-
WebKit: number[];
|
|
38
|
-
Trident: number[];
|
|
39
|
-
InternetExplorer: number[];
|
|
40
|
-
Flash: number[];
|
|
41
|
-
Facebook: number[];
|
|
42
|
-
LINE: number[];
|
|
43
|
-
}
|
|
44
|
-
declare type iOSRequest = "mobile" | "desktop" | undefined;
|
|
45
|
-
declare class sMLFactory {
|
|
46
|
-
OS: OSFlags;
|
|
47
|
-
UA: UAFlags;
|
|
48
|
-
Env: string[];
|
|
49
|
-
constructor();
|
|
50
|
-
}
|
|
51
|
-
declare class sMLFactoryWithRequest extends sMLFactory {
|
|
52
|
-
get iOSRequest(): iOSRequest;
|
|
53
|
-
}
|
|
54
|
-
declare const sML: sMLFactory;
|
|
55
|
-
declare const sMLWithRequest: sMLFactoryWithRequest;
|
|
56
|
-
export { sML, sMLWithRequest };
|