@readium/navigator 2.2.9 → 2.3.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 +2786 -1791
- package/dist/index.umd.cjs +79 -86
- package/package.json +2 -3
- package/src/Navigator.ts +76 -0
- package/src/epub/EpubNavigator.ts +93 -10
- package/src/epub/css/Properties.ts +8 -8
- package/src/epub/css/ReadiumCSS.ts +7 -5
- package/src/epub/frame/FrameManager.ts +38 -2
- package/src/epub/frame/FramePoolManager.ts +9 -2
- package/src/epub/fxl/FXLFrameManager.ts +31 -2
- package/src/epub/fxl/FXLFramePoolManager.ts +9 -3
- package/src/epub/preferences/EpubDefaults.ts +6 -6
- package/src/epub/preferences/EpubPreferences.ts +6 -6
- package/src/epub/preferences/EpubPreferencesEditor.ts +1 -3
- package/src/epub/preferences/EpubSettings.ts +5 -7
- package/src/helpers/lineLength.ts +4 -6
- package/src/injection/epubInjectables.ts +11 -3
- package/src/injection/webpubInjectables.ts +12 -2
- package/src/peripherals/KeyboardPeripherals.ts +53 -0
- package/src/protection/AutomationDetector.ts +66 -0
- package/src/protection/ContextMenuProtector.ts +46 -0
- package/src/protection/DevToolsDetector.ts +291 -0
- package/src/protection/IframeEmbeddingDetector.ts +73 -0
- package/src/protection/NavigatorProtector.ts +95 -0
- package/src/protection/PrintProtector.ts +58 -0
- package/src/protection/utils/WorkerConsole.ts +84 -0
- package/src/protection/utils/console.ts +16 -0
- package/src/protection/utils/match.ts +18 -0
- package/src/protection/utils/platform.ts +22 -0
- package/src/webpub/WebPubFrameManager.ts +38 -5
- package/src/webpub/WebPubFramePoolManager.ts +9 -2
- package/src/webpub/WebPubNavigator.ts +91 -7
- package/types/src/Navigator.d.ts +14 -0
- package/types/src/epub/EpubNavigator.d.ts +14 -2
- package/types/src/epub/css/Properties.d.ts +4 -0
- package/types/src/epub/frame/FrameManager.d.ts +5 -1
- package/types/src/epub/frame/FramePoolManager.d.ts +4 -1
- package/types/src/epub/fxl/FXLFrameManager.d.ts +5 -1
- package/types/src/epub/fxl/FXLFramePoolManager.d.ts +4 -2
- package/types/src/epub/preferences/EpubDefaults.d.ts +4 -0
- package/types/src/epub/preferences/EpubPreferences.d.ts +4 -0
- package/types/src/epub/preferences/EpubPreferencesEditor.d.ts +2 -0
- package/types/src/epub/preferences/EpubSettings.d.ts +4 -0
- package/types/src/helpers/lineLength.d.ts +2 -3
- package/types/src/injection/epubInjectables.d.ts +2 -2
- package/types/src/injection/webpubInjectables.d.ts +2 -1
- package/types/src/peripherals/KeyboardPeripherals.d.ts +13 -0
- package/types/src/protection/AutomationDetector.d.ts +14 -0
- package/types/src/protection/ContextMenuProtector.d.ts +11 -0
- package/types/src/protection/DevToolsDetector.d.ts +75 -0
- package/types/src/protection/IframeEmbeddingDetector.d.ts +14 -0
- package/types/src/protection/NavigatorProtector.d.ts +12 -0
- package/types/src/protection/PrintProtector.d.ts +13 -0
- package/types/src/protection/utils/WorkerConsole.d.ts +15 -0
- package/types/src/protection/utils/console.d.ts +6 -0
- package/types/src/protection/utils/match.d.ts +8 -0
- package/types/src/protection/utils/platform.d.ts +4 -0
- package/types/src/webpub/WebPubFrameManager.d.ts +5 -1
- package/types/src/webpub/WebPubFramePoolManager.d.ts +4 -1
- package/types/src/webpub/WebPubNavigator.d.ts +13 -1
- package/dist/assets/AccessibleDfA-Bold.woff2 +0 -0
- package/dist/assets/AccessibleDfA-Italic.woff2 +0 -0
- package/dist/assets/AccessibleDfA-Regular.woff +0 -0
- package/dist/assets/AccessibleDfA-Regular.woff2 +0 -0
- package/dist/assets/iAWriterDuospace-Regular.ttf +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@readium/navigator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Next generation SDK for publications in Web Apps",
|
|
6
6
|
"author": "readium",
|
|
@@ -50,8 +50,7 @@
|
|
|
50
50
|
"generate:css-selector": "node scripts/generate-css-selector.js"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@
|
|
54
|
-
"@readium/css": "2.0.0-beta.24",
|
|
53
|
+
"@readium/css": "^2.0.0",
|
|
55
54
|
"@readium/navigator-html-injectables": "workspace:*",
|
|
56
55
|
"@readium/shared": "workspace:*",
|
|
57
56
|
"@types/path-browserify": "^1.0.3",
|
package/src/Navigator.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { Link, Locator, Publication, ReadingProgression } from "@readium/shared";
|
|
2
|
+
import {
|
|
3
|
+
ContentProtectionConfig,
|
|
4
|
+
PrintProtectionConfig,
|
|
5
|
+
KeyboardPeripheral,
|
|
6
|
+
KeyCombo,
|
|
7
|
+
DEV_TOOLS,
|
|
8
|
+
SELECT_ALL,
|
|
9
|
+
PRINT,
|
|
10
|
+
SAVE
|
|
11
|
+
} from "@readium/navigator-html-injectables";
|
|
2
12
|
|
|
3
13
|
type cbb = (ok: boolean) => void;
|
|
4
14
|
|
|
15
|
+
export type IKeyboardPeripheralsConfig = Array<Omit<KeyboardPeripheral, 'type'> & {
|
|
16
|
+
type: Exclude<string, 'developer_tools' | 'select_all' | 'print' | 'save'>;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
5
19
|
export interface ProgressionRange {
|
|
6
20
|
start: number;
|
|
7
21
|
end: number;
|
|
@@ -13,6 +27,12 @@ export interface VisualNavigatorViewport {
|
|
|
13
27
|
positions: number[] | null; // Range of visible positions
|
|
14
28
|
}
|
|
15
29
|
|
|
30
|
+
export interface IContentProtectionConfig extends ContentProtectionConfig {
|
|
31
|
+
protectPrinting?: PrintProtectionConfig;
|
|
32
|
+
checkAutomation?: boolean;
|
|
33
|
+
checkIFrameEmbedding?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
export abstract class Navigator {
|
|
17
37
|
abstract get publication(): Publication; // Publication rendered by this navigator.
|
|
18
38
|
abstract get currentLocator(): Locator; // Current position (detailed) in the publication. Can be used to save a bookmark to the current position.
|
|
@@ -43,6 +63,62 @@ export abstract class Navigator {
|
|
|
43
63
|
* Destroy all resources associated with this navigator. Synonymous with "unmount"
|
|
44
64
|
*/
|
|
45
65
|
abstract destroy(): void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Merges keyboard peripherals from content protection config with user-provided peripherals
|
|
69
|
+
* Content protection peripherals are added first for priority, then user peripherals are added only if they don't conflict
|
|
70
|
+
*/
|
|
71
|
+
protected mergeKeyboardPeripherals(
|
|
72
|
+
config: IContentProtectionConfig,
|
|
73
|
+
keyboardPeripherals: IKeyboardPeripheralsConfig = []
|
|
74
|
+
): IKeyboardPeripheralsConfig {
|
|
75
|
+
const peripherals: IKeyboardPeripheralsConfig = [];
|
|
76
|
+
|
|
77
|
+
// Filter out any peripherals with reserved content protection types
|
|
78
|
+
const filteredUserPeripherals = keyboardPeripherals.filter(
|
|
79
|
+
peripheral => !['developer_tools', 'select_all', 'print', 'save'].includes(peripheral.type)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Add content protection peripherals first for priority
|
|
83
|
+
if (config.disableSelectAll) {
|
|
84
|
+
peripherals.push(SELECT_ALL);
|
|
85
|
+
}
|
|
86
|
+
if (config.disableSave) {
|
|
87
|
+
peripherals.push(SAVE);
|
|
88
|
+
}
|
|
89
|
+
if (config.monitorDevTools) {
|
|
90
|
+
peripherals.push(DEV_TOOLS);
|
|
91
|
+
}
|
|
92
|
+
if (config.protectPrinting?.disable) {
|
|
93
|
+
peripherals.push(PRINT);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Add user peripherals with conflicting combos removed
|
|
97
|
+
for (const userPeripheral of filteredUserPeripherals) {
|
|
98
|
+
// Filter out combos that conflict with existing peripherals
|
|
99
|
+
const filteredCombos = userPeripheral.keyCombos.filter((userCombo: KeyCombo) =>
|
|
100
|
+
!peripherals.some(existingPeripheral =>
|
|
101
|
+
existingPeripheral.keyCombos.some((existingCombo: KeyCombo) =>
|
|
102
|
+
userCombo.keyCode === existingCombo.keyCode &&
|
|
103
|
+
userCombo.ctrl === existingCombo.ctrl &&
|
|
104
|
+
userCombo.shift === existingCombo.shift &&
|
|
105
|
+
userCombo.alt === existingCombo.alt &&
|
|
106
|
+
userCombo.meta === existingCombo.meta
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Add the peripheral with filtered combos if any remain
|
|
112
|
+
if (filteredCombos.length > 0) {
|
|
113
|
+
peripherals.push({
|
|
114
|
+
...userPeripheral,
|
|
115
|
+
keyCombos: filteredCombos
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return peripherals;
|
|
121
|
+
}
|
|
46
122
|
}
|
|
47
123
|
|
|
48
124
|
export abstract class VisualNavigator extends Navigator {
|
|
@@ -2,8 +2,8 @@ import { Layout, Link, Locator, Profile, Publication, ReadingProgression } from
|
|
|
2
2
|
import { Configurable, ConfigurablePreferences, ConfigurableSettings, LineLengths, ProgressionRange, VisualNavigator, VisualNavigatorViewport } from "../";
|
|
3
3
|
import { FramePoolManager } from "./frame/FramePoolManager";
|
|
4
4
|
import { FXLFramePoolManager } from "./fxl/FXLFramePoolManager";
|
|
5
|
-
import { CommsEventKey, FXLModules, ModuleLibrary, ModuleName, ReflowableModules } from "@readium/navigator-html-injectables";
|
|
6
|
-
import { BasicTextSelection, FrameClickEvent } from "@readium/navigator-html-injectables";
|
|
5
|
+
import { CommsEventKey, ContextMenuEvent, FXLModules, KeyboardEventData, ModuleLibrary, ModuleName, ReflowableModules } from "@readium/navigator-html-injectables";
|
|
6
|
+
import { BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
|
|
7
7
|
import * as path from "path-browserify";
|
|
8
8
|
import { FXLFrameManager } from "./fxl/FXLFrameManager";
|
|
9
9
|
import { FrameManager } from "./frame/FrameManager";
|
|
@@ -17,6 +17,9 @@ import { getContentWidth } from "../helpers/dimensions";
|
|
|
17
17
|
import { Injector } from "../injection/Injector";
|
|
18
18
|
import { createReadiumEpubRules } from "../injection/epubInjectables";
|
|
19
19
|
import { IInjectablesConfig } from "../injection/Injectable";
|
|
20
|
+
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../Navigator";
|
|
21
|
+
import { NavigatorProtector, NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT } from "../protection/NavigatorProtector";
|
|
22
|
+
import { KeyboardPeripherals, NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT } from "../peripherals/KeyboardPeripherals";
|
|
20
23
|
|
|
21
24
|
export type ManagerEventKey = "zoom";
|
|
22
25
|
|
|
@@ -24,6 +27,8 @@ export interface EpubNavigatorConfiguration {
|
|
|
24
27
|
preferences: IEpubPreferences;
|
|
25
28
|
defaults: IEpubDefaults;
|
|
26
29
|
injectables?: IInjectablesConfig;
|
|
30
|
+
contentProtection?: IContentProtectionConfig;
|
|
31
|
+
keyboardPeripherals?: IKeyboardPeripheralsConfig;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
export interface EpubNavigatorListeners {
|
|
@@ -35,8 +40,11 @@ export interface EpubNavigatorListeners {
|
|
|
35
40
|
miscPointer: (amount: number) => void;
|
|
36
41
|
scroll: (delta: number) => void;
|
|
37
42
|
customEvent: (key: string, data: unknown) => void;
|
|
38
|
-
handleLocator: (locator: Locator) => boolean; //
|
|
43
|
+
handleLocator: (locator: Locator) => boolean; // Return true to prevent handling here
|
|
39
44
|
textSelected: (selection: BasicTextSelection) => void;
|
|
45
|
+
contentProtection: (type: string, data: SuspiciousActivityEvent) => void;
|
|
46
|
+
contextMenu: (data: ContextMenuEvent) => void;
|
|
47
|
+
peripheral: (data: KeyboardEventData) => void;
|
|
40
48
|
// showToc: () => void;
|
|
41
49
|
}
|
|
42
50
|
|
|
@@ -50,7 +58,10 @@ const defaultListeners = (listeners: EpubNavigatorListeners): EpubNavigatorListe
|
|
|
50
58
|
scroll: listeners.scroll || (() => {}),
|
|
51
59
|
customEvent: listeners.customEvent || (() => {}),
|
|
52
60
|
handleLocator: listeners.handleLocator || (() => false),
|
|
53
|
-
textSelected: listeners.textSelected || (() => {})
|
|
61
|
+
textSelected: listeners.textSelected || (() => {}),
|
|
62
|
+
contentProtection: listeners.contentProtection || (() => {}),
|
|
63
|
+
contextMenu: listeners.contextMenu || (() => {}),
|
|
64
|
+
peripheral: listeners.peripheral || (() => {}),
|
|
54
65
|
})
|
|
55
66
|
|
|
56
67
|
export class EpubNavigator extends VisualNavigator implements Configurable<ConfigurableSettings, ConfigurablePreferences> {
|
|
@@ -70,6 +81,12 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
70
81
|
private _css: ReadiumCSS;
|
|
71
82
|
private _preferencesEditor: EpubPreferencesEditor | null = null;
|
|
72
83
|
private readonly _injector: Injector | null = null;
|
|
84
|
+
private readonly _contentProtection: IContentProtectionConfig;
|
|
85
|
+
private readonly _keyboardPeripherals: IKeyboardPeripheralsConfig;
|
|
86
|
+
private readonly _navigatorProtector: NavigatorProtector | null = null;
|
|
87
|
+
private readonly _keyboardPeripheralsManager: KeyboardPeripherals | null = null;
|
|
88
|
+
private readonly _suspiciousActivityListener: ((event: Event) => void) | null = null;
|
|
89
|
+
private readonly _keyboardPeripheralListener: ((event: Event) => void) | null = null;
|
|
73
90
|
|
|
74
91
|
private resizeObserver: ResizeObserver;
|
|
75
92
|
|
|
@@ -98,7 +115,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
98
115
|
optimalChars: this._settings.optimalLineLength,
|
|
99
116
|
minChars: this._settings.minimalLineLength,
|
|
100
117
|
maxChars: this._settings.maximalLineLength,
|
|
101
|
-
|
|
118
|
+
padding: this._settings.scroll
|
|
119
|
+
? (this._settings.scrollPaddingLeft || 0) + (this._settings.scrollPaddingRight || 0)
|
|
120
|
+
: (this._settings.pageGutter || 0) * 2,
|
|
102
121
|
fontFace: this._settings.fontFamily,
|
|
103
122
|
letterSpacing: this._settings.letterSpacing,
|
|
104
123
|
wordSpacing: this._settings.wordSpacing,
|
|
@@ -112,13 +131,55 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
112
131
|
this.currentProgression = pub.metadata.effectiveReadingProgression;
|
|
113
132
|
|
|
114
133
|
// Combine Readium rules with user-provided injectables
|
|
115
|
-
const readiumRules = createReadiumEpubRules(pub.metadata);
|
|
134
|
+
const readiumRules = createReadiumEpubRules(pub.metadata, pub.readingOrder.items);
|
|
116
135
|
const userConfig = configuration.injectables || { rules: [], allowedDomains: [] };
|
|
117
136
|
|
|
118
137
|
this._injector = new Injector({
|
|
119
138
|
rules: [...readiumRules, ...userConfig.rules],
|
|
120
139
|
allowedDomains: userConfig.allowedDomains
|
|
121
140
|
});
|
|
141
|
+
|
|
142
|
+
this._contentProtection = configuration.contentProtection || {};
|
|
143
|
+
|
|
144
|
+
// Merge keyboard peripherals
|
|
145
|
+
this._keyboardPeripherals = this.mergeKeyboardPeripherals(
|
|
146
|
+
this._contentProtection,
|
|
147
|
+
configuration.keyboardPeripherals || []
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Initialize navigator protection if any protection is configured
|
|
151
|
+
if (this._contentProtection.disableContextMenu ||
|
|
152
|
+
this._contentProtection.checkAutomation ||
|
|
153
|
+
this._contentProtection.checkIFrameEmbedding ||
|
|
154
|
+
this._contentProtection.monitorDevTools ||
|
|
155
|
+
this._contentProtection.protectPrinting?.disable) {
|
|
156
|
+
this._navigatorProtector = new NavigatorProtector(this._contentProtection);
|
|
157
|
+
|
|
158
|
+
// Listen for custom events from NavigatorProtector
|
|
159
|
+
this._suspiciousActivityListener = (event: Event) => {
|
|
160
|
+
const { type, ...activity } = (event as CustomEvent).detail;
|
|
161
|
+
if (type === "context_menu") {
|
|
162
|
+
this.listeners.contextMenu(activity as ContextMenuEvent);
|
|
163
|
+
} else {
|
|
164
|
+
this.listeners.contentProtection(type, activity);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
window.addEventListener(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, this._suspiciousActivityListener);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Initialize keyboard peripherals separately (works independently of protection)
|
|
171
|
+
if (this._keyboardPeripherals.length > 0) {
|
|
172
|
+
this._keyboardPeripheralsManager = new KeyboardPeripherals({
|
|
173
|
+
keyboardPeripherals: this._keyboardPeripherals
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Listen for keyboard peripheral events from main window
|
|
177
|
+
this._keyboardPeripheralListener = (event: Event) => {
|
|
178
|
+
const activity = (event as CustomEvent).detail;
|
|
179
|
+
this.listeners.peripheral(activity);
|
|
180
|
+
};
|
|
181
|
+
window.addEventListener(NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT, this._keyboardPeripheralListener);
|
|
182
|
+
}
|
|
122
183
|
|
|
123
184
|
// We use a resizeObserver cos’ the container parent may not be the width of
|
|
124
185
|
// the document/window e.g. app using a docking system with left and right panels.
|
|
@@ -154,7 +215,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
154
215
|
this.container,
|
|
155
216
|
this.positions,
|
|
156
217
|
this.pub,
|
|
157
|
-
this._injector
|
|
218
|
+
this._injector,
|
|
219
|
+
this._contentProtection,
|
|
220
|
+
this._keyboardPeripherals
|
|
158
221
|
);
|
|
159
222
|
this.framePool.listener = (key: CommsEventKey | ManagerEventKey, data: unknown) => {
|
|
160
223
|
this.eventListener(key, data);
|
|
@@ -166,7 +229,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
166
229
|
this.container,
|
|
167
230
|
this.positions,
|
|
168
231
|
cssProperties,
|
|
169
|
-
this._injector
|
|
232
|
+
this._injector,
|
|
233
|
+
this._contentProtection,
|
|
234
|
+
this._keyboardPeripherals
|
|
170
235
|
);
|
|
171
236
|
}
|
|
172
237
|
|
|
@@ -434,6 +499,16 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
434
499
|
case "progress":
|
|
435
500
|
this.syncLocation(data as ProgressionRange);
|
|
436
501
|
break;
|
|
502
|
+
case "content_protection":
|
|
503
|
+
const activity = data as SuspiciousActivityEvent;
|
|
504
|
+
this.listeners.contentProtection(activity.type, activity);
|
|
505
|
+
break;
|
|
506
|
+
case "context_menu":
|
|
507
|
+
this.listeners.contextMenu(data as ContextMenuEvent);
|
|
508
|
+
break;
|
|
509
|
+
case "keyboard_peripherals":
|
|
510
|
+
this.listeners.peripheral(data as KeyboardEventData);
|
|
511
|
+
break;
|
|
437
512
|
case "log":
|
|
438
513
|
console.log(this._cframes[0]?.source?.split("/")[3], ...(data as any[]));
|
|
439
514
|
break;
|
|
@@ -482,6 +557,14 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
482
557
|
}
|
|
483
558
|
|
|
484
559
|
public async destroy() {
|
|
560
|
+
if (this._suspiciousActivityListener) {
|
|
561
|
+
window.removeEventListener(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, this._suspiciousActivityListener);
|
|
562
|
+
}
|
|
563
|
+
if (this._keyboardPeripheralListener) {
|
|
564
|
+
window.removeEventListener(NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT, this._keyboardPeripheralListener);
|
|
565
|
+
}
|
|
566
|
+
this._navigatorProtector?.destroy();
|
|
567
|
+
this._keyboardPeripheralsManager?.destroy();
|
|
485
568
|
await this.framePool?.destroy();
|
|
486
569
|
}
|
|
487
570
|
|
|
@@ -711,7 +794,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
711
794
|
|
|
712
795
|
private async loadLocator(locator: Locator, cb: (ok: boolean) => void) {
|
|
713
796
|
let done = false;
|
|
714
|
-
let cssSelector = (typeof locator.locations.getCssSelector ===
|
|
797
|
+
let cssSelector = (typeof locator.locations.getCssSelector === "function") && locator.locations.getCssSelector();
|
|
715
798
|
if(locator.text?.highlight) {
|
|
716
799
|
done = await new Promise<boolean>((res, _) => {
|
|
717
800
|
// Attempt to go to a highlighted piece of text in the resource
|
|
@@ -743,7 +826,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
|
|
|
743
826
|
// This sanity check has to be performed because we're still passing non-locator class
|
|
744
827
|
// locator objects to this function. This is not good and should eventually be forbidden
|
|
745
828
|
// or the locator should be deserialized sometime before this function.
|
|
746
|
-
const hid = (typeof locator.locations.htmlId ===
|
|
829
|
+
const hid = (typeof locator.locations.htmlId === "function") && locator.locations.htmlId();
|
|
747
830
|
if(hid)
|
|
748
831
|
done = await new Promise<boolean>((res, _) => {
|
|
749
832
|
// Attempt to go to an HTML ID in the resource
|
|
@@ -203,8 +203,8 @@ export interface IRSProperties {
|
|
|
203
203
|
sansSerifJaV?: string | null;
|
|
204
204
|
sansTf?: string | null;
|
|
205
205
|
scrollPaddingBottom?: number | null;
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
scrollPaddingLeft?: number | null;
|
|
207
|
+
scrollPaddingRight?: number | null;
|
|
208
208
|
scrollPaddingTop?: number | null;
|
|
209
209
|
secondaryColor?: string | null;
|
|
210
210
|
selectionBackgroundColor?: string | null;
|
|
@@ -248,8 +248,8 @@ export class RSProperties extends Properties {
|
|
|
248
248
|
sansSerifJaV: string | null;
|
|
249
249
|
sansTf: string | null;
|
|
250
250
|
scrollPaddingBottom: number | null;
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
scrollPaddingLeft: number | null;
|
|
252
|
+
scrollPaddingRight: number | null;
|
|
253
253
|
scrollPaddingTop: number | null;
|
|
254
254
|
secondaryColor: string | null;
|
|
255
255
|
selectionBackgroundColor: string | null;
|
|
@@ -290,8 +290,8 @@ export class RSProperties extends Properties {
|
|
|
290
290
|
this.paraSpacing = props.paraSpacing ?? null;
|
|
291
291
|
this.primaryColor = props.primaryColor ?? null;
|
|
292
292
|
this.scrollPaddingBottom = props.scrollPaddingBottom ?? null;
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
this.scrollPaddingLeft = props.scrollPaddingLeft ?? null;
|
|
294
|
+
this.scrollPaddingRight = props.scrollPaddingRight ?? null;
|
|
295
295
|
this.scrollPaddingTop = props.scrollPaddingTop ?? null;
|
|
296
296
|
this.sansSerifJa = props.sansSerifJa ?? null;
|
|
297
297
|
this.sansSerifJaV = props.sansSerifJaV ?? null;
|
|
@@ -340,8 +340,8 @@ export class RSProperties extends Properties {
|
|
|
340
340
|
if (this.sansSerifJaV) cssProperties["--RS__sans-serif-ja-v"] = this.sansSerifJaV;
|
|
341
341
|
if (this.sansTf) cssProperties["--RS__sansTf"] = this.sansTf;
|
|
342
342
|
if (this.scrollPaddingBottom != null) cssProperties["--RS__scrollPaddingBottom"] = this.toPx(this.scrollPaddingBottom);
|
|
343
|
-
|
|
344
|
-
|
|
343
|
+
if (this.scrollPaddingLeft != null) cssProperties["--RS__scrollPaddingLeft"] = this.toPx(this.scrollPaddingLeft);
|
|
344
|
+
if (this.scrollPaddingRight != null) cssProperties["--RS__scrollPaddingRight"] = this.toPx(this.scrollPaddingRight);
|
|
345
345
|
if (this.scrollPaddingTop != null) cssProperties["--RS__scrollPaddingTop"] = this.toPx(this.scrollPaddingTop);
|
|
346
346
|
if (this.secondaryColor) cssProperties["--RS__secondaryColor"] = this.secondaryColor;
|
|
347
347
|
if (this.selectionBackgroundColor) cssProperties["--RS__selectionBackgroundColor"] = this.selectionBackgroundColor;
|
|
@@ -45,11 +45,11 @@ export class ReadiumCSS {
|
|
|
45
45
|
if (settings.scrollPaddingBottom !== this.rsProperties.scrollPaddingBottom)
|
|
46
46
|
this.rsProperties.scrollPaddingBottom = settings.scrollPaddingBottom;
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
if (settings.scrollPaddingLeft !== this.rsProperties.scrollPaddingLeft)
|
|
49
|
+
this.rsProperties.scrollPaddingLeft = settings.scrollPaddingLeft;
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
if (settings.scrollPaddingRight !== this.rsProperties.scrollPaddingRight)
|
|
52
|
+
this.rsProperties.scrollPaddingRight = settings.scrollPaddingRight;
|
|
53
53
|
|
|
54
54
|
if (settings.scrollPaddingTop !== this.rsProperties.scrollPaddingTop)
|
|
55
55
|
this.rsProperties.scrollPaddingTop = settings.scrollPaddingTop;
|
|
@@ -62,7 +62,9 @@ export class ReadiumCSS {
|
|
|
62
62
|
this.lineLengths.update({
|
|
63
63
|
fontFace: settings.fontFamily,
|
|
64
64
|
letterSpacing: settings.letterSpacing,
|
|
65
|
-
|
|
65
|
+
padding: settings.scroll
|
|
66
|
+
? (settings.scrollPaddingLeft || 0) + (settings.scrollPaddingRight || 0)
|
|
67
|
+
: (settings.pageGutter || 0) * 2,
|
|
66
68
|
wordSpacing: settings.wordSpacing,
|
|
67
69
|
optimalChars: settings.optimalLineLength,
|
|
68
70
|
minChars: settings.minimalLineLength,
|
|
@@ -2,6 +2,7 @@ import { Loader, ModuleName } from "@readium/navigator-html-injectables";
|
|
|
2
2
|
import { FrameComms } from "./FrameComms";
|
|
3
3
|
import { ReadiumWindow } from "../../../../navigator-html-injectables/types/src/helpers/dom";
|
|
4
4
|
import { sML } from "../../helpers";
|
|
5
|
+
import type { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
export class FrameManager {
|
|
@@ -11,10 +12,15 @@ export class FrameManager {
|
|
|
11
12
|
private comms: FrameComms | undefined;
|
|
12
13
|
private hidden: boolean = true;
|
|
13
14
|
private destroyed: boolean = false;
|
|
14
|
-
|
|
15
|
+
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
16
|
+
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
15
17
|
private currModules: ModuleName[] = [];
|
|
16
18
|
|
|
17
|
-
constructor(
|
|
19
|
+
constructor(
|
|
20
|
+
source: string,
|
|
21
|
+
contentProtectionConfig: IContentProtectionConfig = {},
|
|
22
|
+
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
|
|
23
|
+
) {
|
|
18
24
|
this.frame = document.createElement("iframe");
|
|
19
25
|
this.frame.sandbox.value = "allow-same-origin allow-scripts";
|
|
20
26
|
this.frame.classList.add("readium-navigator-iframe");
|
|
@@ -25,6 +31,11 @@ export class FrameManager {
|
|
|
25
31
|
this.frame.style.pointerEvents = "none";
|
|
26
32
|
this.frame.style.transition = "visibility 0s, opacity 0.1s linear";
|
|
27
33
|
this.source = source;
|
|
34
|
+
|
|
35
|
+
// Use the provided content protection config directly without overriding defaults
|
|
36
|
+
this.contentProtectionConfig = { ...contentProtectionConfig };
|
|
37
|
+
this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
|
|
38
|
+
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
async load(modules: ModuleName[]): Promise<Window> {
|
|
@@ -57,6 +68,28 @@ export class FrameManager {
|
|
|
57
68
|
});
|
|
58
69
|
}
|
|
59
70
|
|
|
71
|
+
private applyContentProtection() {
|
|
72
|
+
if (!this.comms) this.comms!.resume();
|
|
73
|
+
|
|
74
|
+
// Send content protection config
|
|
75
|
+
this.comms!.send("peripherals_protection", this.contentProtectionConfig);
|
|
76
|
+
|
|
77
|
+
// Send keyboard peripherals separately
|
|
78
|
+
if (this.keyboardPeripheralsConfig && this.keyboardPeripheralsConfig.length > 0) {
|
|
79
|
+
this.comms!.send("keyboard_peripherals", this.keyboardPeripheralsConfig);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Apply scroll protection
|
|
83
|
+
if (this.contentProtectionConfig.monitorScrollingExperimental) {
|
|
84
|
+
this.comms!.send("scroll_protection", {});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Apply print protection if configured
|
|
88
|
+
if (this.contentProtectionConfig.protectPrinting) {
|
|
89
|
+
this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
60
93
|
async destroy() {
|
|
61
94
|
await this.hide();
|
|
62
95
|
this.loader?.destroy();
|
|
@@ -91,6 +124,9 @@ export class FrameManager {
|
|
|
91
124
|
return new Promise((res, _) => {
|
|
92
125
|
this.comms?.send("activate", undefined, () => {
|
|
93
126
|
this.comms?.send("focus", undefined, () => {
|
|
127
|
+
// Apply content protection synchronously
|
|
128
|
+
this.applyContentProtection();
|
|
129
|
+
|
|
94
130
|
const remove = () => {
|
|
95
131
|
this.frame.style.removeProperty("visibility");
|
|
96
132
|
this.frame.style.removeProperty("aria-hidden");
|
|
@@ -3,6 +3,7 @@ import { Locator, Publication } from "@readium/shared";
|
|
|
3
3
|
import FrameBlobBuider from "./FrameBlobBuilder";
|
|
4
4
|
import { FrameManager } from "./FrameManager";
|
|
5
5
|
import { Injector } from "../../injection/Injector";
|
|
6
|
+
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
|
|
6
7
|
|
|
7
8
|
const UPPER_BOUNDARY = 5;
|
|
8
9
|
const LOWER_BOUNDARY = 3;
|
|
@@ -18,17 +19,23 @@ export class FramePoolManager {
|
|
|
18
19
|
private pendingUpdates: Map<string, { inPool: boolean }> = new Map();
|
|
19
20
|
private currentBaseURL: string | undefined;
|
|
20
21
|
private readonly injector: Injector | null = null;
|
|
22
|
+
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
23
|
+
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
21
24
|
|
|
22
25
|
constructor(
|
|
23
26
|
container: HTMLElement,
|
|
24
27
|
positions: Locator[],
|
|
25
28
|
cssProperties?: { [key: string]: string },
|
|
26
|
-
injector?: Injector | null
|
|
29
|
+
injector?: Injector | null,
|
|
30
|
+
contentProtectionConfig?: IContentProtectionConfig,
|
|
31
|
+
keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig
|
|
27
32
|
) {
|
|
28
33
|
this.container = container;
|
|
29
34
|
this.positions = positions;
|
|
30
35
|
this.currentCssProperties = cssProperties;
|
|
31
36
|
this.injector = injector ?? null;
|
|
37
|
+
this.contentProtectionConfig = contentProtectionConfig || {};
|
|
38
|
+
this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
async destroy() {
|
|
@@ -164,7 +171,7 @@ export class FramePoolManager {
|
|
|
164
171
|
}
|
|
165
172
|
|
|
166
173
|
// Create <iframe>
|
|
167
|
-
const fm = new FrameManager(this.blobs.get(href)
|
|
174
|
+
const fm = new FrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
|
|
168
175
|
if(href !== newHref) await fm.hide(); // Avoid unecessary hide
|
|
169
176
|
this.container.appendChild(fm.iframe);
|
|
170
177
|
await fm.load(modules);
|
|
@@ -3,6 +3,7 @@ import { Page, ReadingProgression } from "@readium/shared";
|
|
|
3
3
|
import { FrameComms } from "../frame/FrameComms";
|
|
4
4
|
import { FXLPeripherals } from "./FXLPeripherals";
|
|
5
5
|
import { ReadiumWindow } from "../../../../navigator-html-injectables/types/src/helpers/dom";
|
|
6
|
+
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
|
|
6
7
|
|
|
7
8
|
export class FXLFrameManager {
|
|
8
9
|
private frame: HTMLIFrameElement;
|
|
@@ -10,7 +11,8 @@ export class FXLFrameManager {
|
|
|
10
11
|
public source: string;
|
|
11
12
|
private comms: FrameComms | undefined;
|
|
12
13
|
private readonly peripherals: FXLPeripherals;
|
|
13
|
-
|
|
14
|
+
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
15
|
+
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
14
16
|
private currModules: ModuleName[] = [];
|
|
15
17
|
|
|
16
18
|
// NEW
|
|
@@ -19,9 +21,18 @@ export class FXLFrameManager {
|
|
|
19
21
|
private loadPromise: Promise<Window> | undefined;
|
|
20
22
|
private showPromise: Promise<void> | undefined;
|
|
21
23
|
|
|
22
|
-
constructor(
|
|
24
|
+
constructor(
|
|
25
|
+
peripherals: FXLPeripherals,
|
|
26
|
+
direction: ReadingProgression,
|
|
27
|
+
debugHref: string,
|
|
28
|
+
contentProtectionConfig: IContentProtectionConfig = {},
|
|
29
|
+
keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
|
|
30
|
+
) {
|
|
23
31
|
this.peripherals = peripherals;
|
|
24
32
|
this.debugHref = debugHref;
|
|
33
|
+
// Use the provided content protection config directly without overriding defaults
|
|
34
|
+
this.contentProtectionConfig = { ...contentProtectionConfig };
|
|
35
|
+
this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
|
|
25
36
|
this.frame = document.createElement("iframe");
|
|
26
37
|
this.frame.sandbox.value = "allow-same-origin allow-scripts";
|
|
27
38
|
this.frame.classList.add("readium-navigator-iframe");
|
|
@@ -196,6 +207,23 @@ export class FXLFrameManager {
|
|
|
196
207
|
this.comms?.halt();
|
|
197
208
|
}
|
|
198
209
|
|
|
210
|
+
private applyContentProtection() {
|
|
211
|
+
if (!this.comms) this.comms!.resume();
|
|
212
|
+
|
|
213
|
+
// Send content protection config
|
|
214
|
+
this.comms!.send("peripherals_protection", this.contentProtectionConfig);
|
|
215
|
+
|
|
216
|
+
// Send keyboard peripherals separately
|
|
217
|
+
if (this.keyboardPeripheralsConfig && this.keyboardPeripheralsConfig.length > 0) {
|
|
218
|
+
this.comms!.send("keyboard_peripherals", this.keyboardPeripheralsConfig);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Apply print protection if configured
|
|
222
|
+
if (this.contentProtectionConfig.protectPrinting) {
|
|
223
|
+
this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
199
227
|
private cachedPage: Page | undefined = undefined;
|
|
200
228
|
async show(page: Page): Promise<void> {
|
|
201
229
|
if(!this.frame.parentElement) {
|
|
@@ -221,6 +249,7 @@ export class FXLFrameManager {
|
|
|
221
249
|
this.comms!.send("focus", undefined, (_: boolean) => {
|
|
222
250
|
// this.showPromise = undefined; Don't do this
|
|
223
251
|
this.update(this.cachedPage);
|
|
252
|
+
this.applyContentProtection();
|
|
224
253
|
res();
|
|
225
254
|
});
|
|
226
255
|
});
|
|
@@ -5,7 +5,7 @@ import FrameBlobBuider from "../frame/FrameBlobBuilder";
|
|
|
5
5
|
import { FXLFrameManager } from "./FXLFrameManager";
|
|
6
6
|
import { FXLPeripherals } from "./FXLPeripherals";
|
|
7
7
|
import { FXLSpreader, Orientation, Spread } from "./FXLSpreader";
|
|
8
|
-
import { VisualNavigatorViewport } from "../../Navigator";
|
|
8
|
+
import { VisualNavigatorViewport, IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
|
|
9
9
|
import { Injector } from "../../injection/Injector";
|
|
10
10
|
|
|
11
11
|
const UPPER_BOUNDARY = 8;
|
|
@@ -28,6 +28,8 @@ export class FXLFramePoolManager {
|
|
|
28
28
|
private currentBaseURL: string | undefined;
|
|
29
29
|
private previousFrames: FXLFrameManager[] = [];
|
|
30
30
|
private readonly injector: Injector | null = null;
|
|
31
|
+
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
32
|
+
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
31
33
|
|
|
32
34
|
// NEW
|
|
33
35
|
private readonly bookElement: HTMLDivElement;
|
|
@@ -50,12 +52,16 @@ export class FXLFramePoolManager {
|
|
|
50
52
|
container: HTMLElement,
|
|
51
53
|
positions: Locator[],
|
|
52
54
|
pub: Publication,
|
|
53
|
-
injector?: Injector | null
|
|
55
|
+
injector?: Injector | null,
|
|
56
|
+
contentProtectionConfig?: IContentProtectionConfig,
|
|
57
|
+
keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig,
|
|
54
58
|
) {
|
|
55
59
|
this.container = container;
|
|
56
60
|
this.positions = positions;
|
|
57
61
|
this.pub = pub;
|
|
58
62
|
this.injector = injector ?? null;
|
|
63
|
+
this.contentProtectionConfig = contentProtectionConfig || {};
|
|
64
|
+
this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
|
|
59
65
|
this.spreadPresentation = pub.metadata.otherMetadata?.spread || Spread.auto;
|
|
60
66
|
|
|
61
67
|
if(this.pub.metadata.effectiveReadingProgression !== ReadingProgression.rtl && this.pub.metadata.effectiveReadingProgression !== ReadingProgression.ltr)
|
|
@@ -82,7 +88,7 @@ export class FXLFramePoolManager {
|
|
|
82
88
|
|
|
83
89
|
this.pub.readingOrder.items.forEach((link) => {
|
|
84
90
|
// Create <iframe>
|
|
85
|
-
const fm = new FXLFrameManager(this.peripherals, this.pub.metadata.effectiveReadingProgression, link.href);
|
|
91
|
+
const fm = new FXLFrameManager(this.peripherals, this.pub.metadata.effectiveReadingProgression, link.href, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
|
|
86
92
|
this.spineElement.appendChild(fm.element);
|
|
87
93
|
|
|
88
94
|
// this.pages.push(fm);
|
|
@@ -53,8 +53,8 @@ export interface IEpubDefaults {
|
|
|
53
53
|
scroll?: boolean | null,
|
|
54
54
|
scrollPaddingTop?: number | null,
|
|
55
55
|
scrollPaddingBottom?: number | null,
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
scrollPaddingLeft?: number | null,
|
|
57
|
+
scrollPaddingRight?: number | null,
|
|
58
58
|
selectionBackgroundColor?: string | null,
|
|
59
59
|
selectionTextColor?: string | null,
|
|
60
60
|
textAlign?: TextAlignment | null,
|
|
@@ -97,8 +97,8 @@ export class EpubDefaults {
|
|
|
97
97
|
scroll: boolean | null;
|
|
98
98
|
scrollPaddingTop: number | null;
|
|
99
99
|
scrollPaddingBottom: number | null;
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
scrollPaddingLeft: number | null;
|
|
101
|
+
scrollPaddingRight: number | null;
|
|
102
102
|
selectionBackgroundColor: string | null;
|
|
103
103
|
selectionTextColor: string | null;
|
|
104
104
|
textAlign: TextAlignment | null;
|
|
@@ -144,8 +144,8 @@ export class EpubDefaults {
|
|
|
144
144
|
this.scroll = ensureBoolean(defaults.scroll) ?? false;
|
|
145
145
|
this.scrollPaddingTop = ensureNonNegative(defaults.scrollPaddingTop) ?? null;
|
|
146
146
|
this.scrollPaddingBottom = ensureNonNegative(defaults.scrollPaddingBottom) ?? null;
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
this.scrollPaddingLeft = ensureNonNegative(defaults.scrollPaddingLeft) ?? null;
|
|
148
|
+
this.scrollPaddingRight = ensureNonNegative(defaults.scrollPaddingRight) ?? null;
|
|
149
149
|
this.selectionBackgroundColor = ensureString(defaults.selectionBackgroundColor) || null;
|
|
150
150
|
this.selectionTextColor = ensureString(defaults.selectionTextColor) || null;
|
|
151
151
|
this.textAlign = ensureEnumValue<TextAlignment>(defaults.textAlign, TextAlignment) || null;
|