@readium/navigator 2.2.4 → 2.2.6
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 +1300 -1233
- package/dist/index.umd.cjs +20 -78
- package/package.json +4 -4
- package/src/epub/css/Properties.ts +10 -1
- package/src/epub/css/ReadiumCSS.ts +3 -0
- package/src/epub/frame/FrameBlobBuilder.ts +39 -11
- package/src/epub/frame/FrameManager.ts +1 -0
- package/src/epub/fxl/FXLFrameManager.ts +1 -0
- package/src/epub/preferences/EpubDefaults.ts +8 -2
- package/src/epub/preferences/EpubSettings.ts +6 -2
- package/src/preferences/Types.ts +6 -0
- package/src/preferences/guards.ts +12 -0
- package/src/webpub/WebPubNavigator.ts +9 -1
- package/src/webpub/css/Properties.ts +26 -1
- package/src/webpub/css/WebPubCSS.ts +8 -1
- package/src/webpub/preferences/WebPubDefaults.ts +7 -2
- package/src/webpub/preferences/WebPubSettings.ts +6 -2
- package/types/src/epub/css/Properties.d.ts +3 -1
- package/types/src/epub/preferences/EpubDefaults.d.ts +3 -1
- package/types/src/epub/preferences/EpubSettings.d.ts +3 -1
- package/types/src/preferences/Types.d.ts +14 -0
- package/types/src/preferences/guards.d.ts +2 -0
- package/types/src/webpub/css/Properties.d.ts +11 -1
- package/types/src/webpub/css/WebPubCSS.d.ts +3 -1
- package/types/src/webpub/preferences/WebPubDefaults.d.ts +3 -1
- package/types/src/webpub/preferences/WebPubSettings.d.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@readium/navigator",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Next generation SDK for publications in Web Apps",
|
|
6
6
|
"author": "readium",
|
|
@@ -49,16 +49,16 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@laynezh/vite-plugin-lib-assets": "^2.1.0",
|
|
52
|
-
"@readium/css": "2.0.0-beta.
|
|
52
|
+
"@readium/css": "2.0.0-beta.24",
|
|
53
53
|
"@readium/navigator-html-injectables": "workspace:*",
|
|
54
54
|
"@readium/shared": "workspace:*",
|
|
55
55
|
"@types/path-browserify": "^1.0.3",
|
|
56
56
|
"css-selector-generator": "^3.6.9",
|
|
57
57
|
"path-browserify": "^1.0.1",
|
|
58
58
|
"tslib": "^2.8.1",
|
|
59
|
-
"typescript": "^5.9.
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
60
|
"typescript-plugin-css-modules": "^5.2.0",
|
|
61
61
|
"user-agent-data-types": "^0.4.2",
|
|
62
|
-
"vite": "^7.
|
|
62
|
+
"vite": "^7.2.4"
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
1
|
+
import { ExperimentKey, experiments, TextAlignment } from "../../preferences/Types";
|
|
2
2
|
import {
|
|
3
3
|
BodyHyphens,
|
|
4
4
|
BoxSizing,
|
|
@@ -214,6 +214,7 @@ export interface IRSProperties {
|
|
|
214
214
|
textColor?: string | null;
|
|
215
215
|
typeScale?: TypeScale | null;
|
|
216
216
|
visitedColor?: string | null;
|
|
217
|
+
experiments?: Array<ExperimentKey> | null;
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
export class RSProperties extends Properties {
|
|
@@ -258,6 +259,7 @@ export class RSProperties extends Properties {
|
|
|
258
259
|
textColor: string | null;
|
|
259
260
|
typeScale: TypeScale | null;
|
|
260
261
|
visitedColor: string | null;
|
|
262
|
+
experiments: Array<ExperimentKey> | null;
|
|
261
263
|
|
|
262
264
|
constructor(props: IRSProperties) {
|
|
263
265
|
super();
|
|
@@ -302,6 +304,7 @@ export class RSProperties extends Properties {
|
|
|
302
304
|
this.textColor = props.textColor ?? null;
|
|
303
305
|
this.typeScale = props.typeScale ?? null;
|
|
304
306
|
this.visitedColor = props.visitedColor ?? null;
|
|
307
|
+
this.experiments = props.experiments ?? null;
|
|
305
308
|
}
|
|
306
309
|
|
|
307
310
|
toCSSProperties(): { [key: string]: string; } {
|
|
@@ -349,6 +352,12 @@ export class RSProperties extends Properties {
|
|
|
349
352
|
if (this.typeScale) cssProperties["--RS__typeScale"] = this.toUnitless(this.typeScale);
|
|
350
353
|
if (this.visitedColor) cssProperties["--RS__visitedColor"] = this.visitedColor;
|
|
351
354
|
|
|
355
|
+
if (this.experiments) {
|
|
356
|
+
this.experiments.forEach((exp) => {
|
|
357
|
+
cssProperties["--RS__" + exp] = experiments[exp].value;
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
|
|
352
361
|
return cssProperties;
|
|
353
362
|
}
|
|
354
363
|
}
|
|
@@ -54,6 +54,9 @@ export class ReadiumCSS {
|
|
|
54
54
|
if (settings.scrollPaddingTop !== this.rsProperties.scrollPaddingTop)
|
|
55
55
|
this.rsProperties.scrollPaddingTop = settings.scrollPaddingTop;
|
|
56
56
|
|
|
57
|
+
if (settings.experiments !== this.rsProperties.experiments)
|
|
58
|
+
this.rsProperties.experiments = settings.experiments;
|
|
59
|
+
|
|
57
60
|
// This has to be updated before pagination
|
|
58
61
|
// otherwise the metrics won’t be correct for line length
|
|
59
62
|
this.lineLengths.update({
|
|
@@ -50,7 +50,10 @@ const cssSelectorGenerator = (doc: Document) => scriptify(doc, cached("css-selec
|
|
|
50
50
|
|
|
51
51
|
// Note: we aren't blocking some of the events right now to try and be as nonintrusive as possible.
|
|
52
52
|
// For a more comprehensive implementation, see https://github.com/hackademix/noscript/blob/3a83c0e4a506f175e38b0342dad50cdca3eae836/src/content/syncFetchPolicy.js#L142
|
|
53
|
+
// The snippet of code at the beginning of this source is an attempt at defence against JS using persistent storage
|
|
53
54
|
const rBefore = (doc: Document) => scriptify(doc, cached("JS-Before", () => blobify(stripJS(`
|
|
55
|
+
const noop=()=>{},emptyObj={},emptyPromise=()=>Promise.resolve(void 0),fakeStorage={getItem:noop,setItem:noop,removeItem:noop,clear:noop,key:noop,length:0};["localStorage","sessionStorage"].forEach((e=>Object.defineProperty(window,e,{get:()=>fakeStorage,configurable:!0}))),Object.defineProperty(document,"cookie",{get:()=>"",set:noop,configurable:!0}),Object.defineProperty(window,"indexedDB",{get:()=>{},configurable:!0}),Object.defineProperty(window,"caches",{get:()=>emptyObj,configurable:!0}),Object.defineProperty(navigator,"storage",{get:()=>({persist:emptyPromise,persisted:emptyPromise,estimate:()=>Promise.resolve({quota:0,usage:0})}),configurable:!0}),Object.defineProperty(navigator,"serviceWorker",{get:()=>({register:emptyPromise,getRegistration:emptyPromise,ready:emptyPromise()}),configurable:!0});
|
|
56
|
+
|
|
54
57
|
window._readium_blockedEvents = [];
|
|
55
58
|
window._readium_blockEvents = true;
|
|
56
59
|
window._readium_eventBlocker = (e) => {
|
|
@@ -78,6 +81,24 @@ const rAfter = (doc: Document) => scriptify(doc, cached("JS-After", () => blobif
|
|
|
78
81
|
});`
|
|
79
82
|
), "text/javascript")));
|
|
80
83
|
|
|
84
|
+
const csp = (domains: string[]) => {
|
|
85
|
+
const d = domains.join(" ");
|
|
86
|
+
return [
|
|
87
|
+
// 'self' is useless because the document is loaded from a blob: URL
|
|
88
|
+
`upgrade-insecure-requests`,
|
|
89
|
+
`default-src ${d} blob:`,
|
|
90
|
+
`connect-src 'none'`, // No fetches to anywhere. TODO: change?
|
|
91
|
+
`script-src ${d} blob: 'unsafe-inline'`, // JS scripts
|
|
92
|
+
`style-src ${d} blob: 'unsafe-inline'`, // CSS styles
|
|
93
|
+
`img-src ${d} blob: data:`, // Images
|
|
94
|
+
`font-src ${d} blob: data:`, // Fonts
|
|
95
|
+
`object-src ${d} blob:`, // Despite not being recommended, still necessary in EPUBs for <object>
|
|
96
|
+
`child-src ${d}`, // <iframe>, web workers
|
|
97
|
+
`form-action 'none'`, // No form submissions
|
|
98
|
+
//`report-uri ?`,
|
|
99
|
+
].join("; ");
|
|
100
|
+
};
|
|
101
|
+
|
|
81
102
|
export default class FrameBlobBuider {
|
|
82
103
|
private readonly item: Link;
|
|
83
104
|
private readonly burl: string;
|
|
@@ -93,7 +114,7 @@ export default class FrameBlobBuider {
|
|
|
93
114
|
|
|
94
115
|
public async build(fxl = false): Promise<string> {
|
|
95
116
|
if(!this.item.mediaType.isHTML) {
|
|
96
|
-
if(this.item.mediaType.isBitmap) {
|
|
117
|
+
if(this.item.mediaType.isBitmap || this.item.mediaType.equals(MediaType.SVG)) {
|
|
97
118
|
return this.buildImageFrame();
|
|
98
119
|
} else
|
|
99
120
|
throw Error("Unsupported frame mediatype " + this.item.mediaType.string);
|
|
@@ -115,7 +136,7 @@ export default class FrameBlobBuider {
|
|
|
115
136
|
const details = perror.querySelector("div");
|
|
116
137
|
throw new Error(`Failed parsing item ${this.item.href}: ${details?.textContent || perror.textContent}`);
|
|
117
138
|
}
|
|
118
|
-
return this.finalizeDOM(doc, this.burl, this.item.mediaType, fxl, this.cssProperties);
|
|
139
|
+
return this.finalizeDOM(doc, this.pub.baseURL, this.burl, this.item.mediaType, fxl, this.cssProperties);
|
|
119
140
|
}
|
|
120
141
|
|
|
121
142
|
private buildImageFrame(): string {
|
|
@@ -126,7 +147,7 @@ export default class FrameBlobBuider {
|
|
|
126
147
|
simg.alt = this.item.title || "";
|
|
127
148
|
simg.decoding = "async";
|
|
128
149
|
doc.body.appendChild(simg);
|
|
129
|
-
return this.finalizeDOM(doc, this.burl, this.item.mediaType, true);
|
|
150
|
+
return this.finalizeDOM(doc, this.pub.baseURL, this.burl, this.item.mediaType, true);
|
|
130
151
|
}
|
|
131
152
|
|
|
132
153
|
// Has JS that may have side-effects when the document is loaded, without any user interaction
|
|
@@ -159,7 +180,7 @@ export default class FrameBlobBuider {
|
|
|
159
180
|
}
|
|
160
181
|
}
|
|
161
182
|
|
|
162
|
-
private finalizeDOM(doc: Document, base: string | undefined, mediaType: MediaType, fxl = false, cssProperties?: { [key: string]: string }): string {
|
|
183
|
+
private finalizeDOM(doc: Document, root: string | undefined, base: string | undefined, mediaType: MediaType, fxl = false, cssProperties?: { [key: string]: string }): string {
|
|
163
184
|
if(!doc) return "";
|
|
164
185
|
|
|
165
186
|
// Inject styles
|
|
@@ -233,8 +254,8 @@ export default class FrameBlobBuider {
|
|
|
233
254
|
) {
|
|
234
255
|
document.documentElement.dir = this.pub.metadata.effectiveReadingProgression;
|
|
235
256
|
} */
|
|
236
|
-
|
|
237
|
-
if(base !== undefined) {
|
|
257
|
+
|
|
258
|
+
if (base !== undefined) {
|
|
238
259
|
// Set all URL bases. Very convenient!
|
|
239
260
|
const b = doc.createElement("base");
|
|
240
261
|
b.href = base;
|
|
@@ -244,17 +265,24 @@ export default class FrameBlobBuider {
|
|
|
244
265
|
|
|
245
266
|
// Inject script to prevent in-publication scripts from executing until we want them to
|
|
246
267
|
const hasExecutable = this.hasExecutable(doc);
|
|
247
|
-
if(hasExecutable) doc.head.firstChild!.before(rBefore(doc));
|
|
268
|
+
if (hasExecutable) doc.head.firstChild!.before(rBefore(doc));
|
|
248
269
|
doc.head.firstChild!.before(cssSelectorGenerator(doc)); // CSS selector utility
|
|
249
|
-
if(hasExecutable) doc.head.appendChild(rAfter(doc)); // Another execution prevention script
|
|
270
|
+
if (hasExecutable) doc.head.appendChild(rAfter(doc)); // Another execution prevention script
|
|
271
|
+
|
|
272
|
+
// Add CSP
|
|
273
|
+
const meta = doc.createElement("meta");
|
|
274
|
+
meta.httpEquiv = "Content-Security-Policy";
|
|
275
|
+
meta.content = csp(root ? [root] : []);
|
|
276
|
+
meta.dataset.readium = "true";
|
|
277
|
+
doc.head.firstChild!.before(meta);
|
|
250
278
|
|
|
251
279
|
|
|
252
280
|
// Make blob from doc
|
|
253
281
|
return URL.createObjectURL(
|
|
254
282
|
new Blob([new XMLSerializer().serializeToString(doc)], {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
283
|
+
type: mediaType.isHTML
|
|
284
|
+
? mediaType.string
|
|
285
|
+
: "application/xhtml+xml", // Fallback to XHTML
|
|
258
286
|
})
|
|
259
287
|
);
|
|
260
288
|
}
|
|
@@ -16,6 +16,7 @@ export class FrameManager {
|
|
|
16
16
|
|
|
17
17
|
constructor(source: string) {
|
|
18
18
|
this.frame = document.createElement("iframe");
|
|
19
|
+
this.frame.sandbox.value = "allow-same-origin allow-scripts";
|
|
19
20
|
this.frame.classList.add("readium-navigator-iframe");
|
|
20
21
|
this.frame.style.visibility = "hidden";
|
|
21
22
|
this.frame.style.setProperty("aria-hidden", "true");
|
|
@@ -23,6 +23,7 @@ export class FXLFrameManager {
|
|
|
23
23
|
this.peripherals = peripherals;
|
|
24
24
|
this.debugHref = debugHref;
|
|
25
25
|
this.frame = document.createElement("iframe");
|
|
26
|
+
this.frame.sandbox.value = "allow-same-origin allow-scripts";
|
|
26
27
|
this.frame.classList.add("readium-navigator-iframe");
|
|
27
28
|
this.frame.classList.add("blank");
|
|
28
29
|
this.frame.scrolling = "no";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ExperimentKey,
|
|
2
3
|
fontSizeRangeConfig,
|
|
3
4
|
fontWeightRangeConfig,
|
|
4
5
|
fontWidthRangeConfig,
|
|
@@ -8,13 +9,14 @@ import {
|
|
|
8
9
|
import {
|
|
9
10
|
ensureBoolean,
|
|
10
11
|
ensureEnumValue,
|
|
12
|
+
ensureExperiment,
|
|
11
13
|
ensureFilter,
|
|
12
14
|
ensureLessThanOrEqual,
|
|
13
15
|
ensureMoreThanOrEqual,
|
|
14
16
|
ensureNonNegative,
|
|
15
17
|
ensureString,
|
|
16
18
|
ensureValueInRange,
|
|
17
|
-
withFallback
|
|
19
|
+
withFallback
|
|
18
20
|
} from "../../preferences/guards";
|
|
19
21
|
|
|
20
22
|
import { sMLWithRequest } from "../../helpers";
|
|
@@ -59,7 +61,8 @@ export interface IEpubDefaults {
|
|
|
59
61
|
textColor?: string | null,
|
|
60
62
|
textNormalization?: boolean | null,
|
|
61
63
|
visitedColor?: string | null,
|
|
62
|
-
wordSpacing?: number | null
|
|
64
|
+
wordSpacing?: number | null,
|
|
65
|
+
experiments?: Array<ExperimentKey> | null
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export class EpubDefaults {
|
|
@@ -103,6 +106,7 @@ export class EpubDefaults {
|
|
|
103
106
|
textNormalization: boolean | null;
|
|
104
107
|
visitedColor: string | null;
|
|
105
108
|
wordSpacing: number | null;
|
|
109
|
+
experiments: Array<ExperimentKey> | null;
|
|
106
110
|
|
|
107
111
|
constructor(defaults: IEpubDefaults) {
|
|
108
112
|
this.backgroundColor = ensureString(defaults.backgroundColor) || null;
|
|
@@ -153,5 +157,7 @@ export class EpubDefaults {
|
|
|
153
157
|
this.optimalLineLength = ensureNonNegative(defaults.optimalLineLength) || 65;
|
|
154
158
|
this.maximalLineLength = withFallback(ensureMoreThanOrEqual(defaults.maximalLineLength, this.optimalLineLength), 80);
|
|
155
159
|
this.minimalLineLength = withFallback(ensureLessThanOrEqual(defaults.minimalLineLength, this.optimalLineLength), 40);
|
|
160
|
+
|
|
161
|
+
this.experiments = ensureExperiment(defaults.experiments) || null;
|
|
156
162
|
}
|
|
157
163
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfigurableSettings } from "../../preferences/Configurable";
|
|
2
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
2
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
3
3
|
import { EpubDefaults } from "./EpubDefaults";
|
|
4
4
|
import { EpubPreferences } from "./EpubPreferences";
|
|
5
5
|
|
|
@@ -45,7 +45,8 @@ export interface IEpubSettings {
|
|
|
45
45
|
textColor?: string | null,
|
|
46
46
|
textNormalization?: boolean | null,
|
|
47
47
|
visitedColor?: string | null,
|
|
48
|
-
wordSpacing?: number | null
|
|
48
|
+
wordSpacing?: number | null,
|
|
49
|
+
experiments?: Array<ExperimentKey> | null
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
export class EpubSettings implements ConfigurableSettings {
|
|
@@ -89,6 +90,7 @@ export class EpubSettings implements ConfigurableSettings {
|
|
|
89
90
|
textNormalization: boolean | null;
|
|
90
91
|
visitedColor: string | null;
|
|
91
92
|
wordSpacing: number | null;
|
|
93
|
+
experiments: Array<ExperimentKey> | null;
|
|
92
94
|
|
|
93
95
|
constructor(preferences: EpubPreferences, defaults: EpubDefaults) {
|
|
94
96
|
this.backgroundColor = preferences.backgroundColor || defaults.backgroundColor || null;
|
|
@@ -229,5 +231,7 @@ export class EpubSettings implements ConfigurableSettings {
|
|
|
229
231
|
: defaults.wordSpacing !== undefined
|
|
230
232
|
? defaults.wordSpacing
|
|
231
233
|
: null;
|
|
234
|
+
|
|
235
|
+
this.experiments = defaults.experiments || null;
|
|
232
236
|
}
|
|
233
237
|
}
|
package/src/preferences/Types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ExperimentKey, experiments } from './Types';
|
|
2
|
+
|
|
1
3
|
export function ensureLessThanOrEqual<T extends number | null | undefined>(value: T, compareTo: T): T | undefined {
|
|
2
4
|
if (value === undefined || value === null) {
|
|
3
5
|
return value;
|
|
@@ -83,3 +85,13 @@ export function ensureValueInRange(value: number | null | undefined, range: [num
|
|
|
83
85
|
export function withFallback<T>(value: T | null | undefined, defaultValue: T | null): T | null {
|
|
84
86
|
return value === undefined ? defaultValue : value;
|
|
85
87
|
}
|
|
88
|
+
|
|
89
|
+
export function ensureExperiment(experimentsInput: ExperimentKey[] | null | undefined): ExperimentKey[] | null | undefined {
|
|
90
|
+
if (experimentsInput === undefined) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
if (experimentsInput === null) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return experimentsInput.filter(exp => exp in experiments);
|
|
97
|
+
}
|
|
@@ -8,12 +8,13 @@ import { WebPubFrameManager } from "./WebPubFrameManager";
|
|
|
8
8
|
|
|
9
9
|
import { ManagerEventKey } from "../epub/EpubNavigator";
|
|
10
10
|
import { WebPubCSS } from "./css/WebPubCSS";
|
|
11
|
-
import { WebUserProperties } from "./css/Properties";
|
|
11
|
+
import { WebUserProperties, WebRSProperties } from "./css/Properties";
|
|
12
12
|
import { IWebPubPreferences, WebPubPreferences } from "./preferences/WebPubPreferences";
|
|
13
13
|
import { IWebPubDefaults, WebPubDefaults } from "./preferences/WebPubDefaults";
|
|
14
14
|
import { WebPubSettings } from "./preferences/WebPubSettings";
|
|
15
15
|
import { IPreferencesEditor } from "../preferences/PreferencesEditor";
|
|
16
16
|
import { WebPubPreferencesEditor } from "./preferences/WebPubPreferencesEditor";
|
|
17
|
+
|
|
17
18
|
export interface WebPubNavigatorConfiguration {
|
|
18
19
|
preferences: IWebPubPreferences;
|
|
19
20
|
defaults: IWebPubDefaults;
|
|
@@ -74,6 +75,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
74
75
|
this._defaults = new WebPubDefaults(configuration.defaults);
|
|
75
76
|
this._settings = new WebPubSettings(this._preferences, this._defaults, this.hasDisplayTransformability);
|
|
76
77
|
this._css = new WebPubCSS({
|
|
78
|
+
rsProperties: new WebRSProperties({ experiments: this._settings.experiments || null }),
|
|
77
79
|
userProperties: new WebUserProperties({ zoom: this._settings.zoom })
|
|
78
80
|
});
|
|
79
81
|
|
|
@@ -135,6 +137,12 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
|
|
|
135
137
|
private compileCSSProperties(css: WebPubCSS) {
|
|
136
138
|
const properties: { [key: string]: string } = {};
|
|
137
139
|
|
|
140
|
+
// Include RS properties (i.e. experiments)
|
|
141
|
+
for (const [key, value] of Object.entries(css.rsProperties.toCSSProperties())) {
|
|
142
|
+
properties[key] = value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Include user properties
|
|
138
146
|
for (const [key, value] of Object.entries(css.userProperties.toCSSProperties())) {
|
|
139
147
|
properties[key] = value;
|
|
140
148
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
1
|
+
import { ExperimentKey, experiments, TextAlignment } from "../../preferences/Types";
|
|
2
2
|
import { BodyHyphens, Ligatures, Properties } from "../../css/Properties";
|
|
3
3
|
|
|
4
4
|
export interface IWebUserProperties {
|
|
@@ -77,3 +77,28 @@ export class WebUserProperties extends Properties {
|
|
|
77
77
|
return cssProperties;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
export interface IWebRSProperties {
|
|
82
|
+
experiments: Array<ExperimentKey> | null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class WebRSProperties extends Properties {
|
|
86
|
+
experiments: Array<ExperimentKey> | null;
|
|
87
|
+
|
|
88
|
+
constructor(props: IWebRSProperties) {
|
|
89
|
+
super();
|
|
90
|
+
this.experiments = props.experiments ?? null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
toCSSProperties() {
|
|
94
|
+
const cssProperties: { [key: string]: string } = {};
|
|
95
|
+
|
|
96
|
+
if (this.experiments) {
|
|
97
|
+
this.experiments.forEach((exp) => {
|
|
98
|
+
cssProperties["--RS__" + exp] = experiments[exp].value;
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return cssProperties;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { WebPubSettings } from "../preferences/WebPubSettings";
|
|
2
|
-
import { IWebUserProperties, WebUserProperties } from "./Properties";
|
|
2
|
+
import { IWebUserProperties, WebRSProperties, WebUserProperties } from "./Properties";
|
|
3
3
|
|
|
4
4
|
export interface IWebPubCSS {
|
|
5
|
+
rsProperties: WebRSProperties;
|
|
5
6
|
userProperties: WebUserProperties;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export class WebPubCSS {
|
|
10
|
+
rsProperties: WebRSProperties;
|
|
9
11
|
userProperties: WebUserProperties;
|
|
10
12
|
|
|
11
13
|
constructor(props: IWebPubCSS) {
|
|
14
|
+
this.rsProperties = props.rsProperties;
|
|
12
15
|
this.userProperties = props.userProperties;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
update(settings: WebPubSettings) {
|
|
19
|
+
if (settings.experiments) {
|
|
20
|
+
this.rsProperties.experiments = settings.experiments;
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
const updated: IWebUserProperties = {
|
|
17
24
|
a11yNormalize: settings.textNormalization,
|
|
18
25
|
bodyHyphens: typeof settings.hyphens !== "boolean"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ExperimentKey,
|
|
2
3
|
fontWeightRangeConfig,
|
|
3
4
|
TextAlignment,
|
|
4
5
|
zoomRangeConfig
|
|
@@ -9,7 +10,8 @@ import {
|
|
|
9
10
|
ensureEnumValue,
|
|
10
11
|
ensureNonNegative,
|
|
11
12
|
ensureValueInRange,
|
|
12
|
-
ensureString
|
|
13
|
+
ensureString,
|
|
14
|
+
ensureExperiment
|
|
13
15
|
} from "../../preferences/guards";
|
|
14
16
|
|
|
15
17
|
import { sMLWithRequest } from "../../helpers";
|
|
@@ -29,7 +31,8 @@ export interface IWebPubDefaults {
|
|
|
29
31
|
textAlign?: TextAlignment | null,
|
|
30
32
|
textNormalization?: boolean | null,
|
|
31
33
|
wordSpacing?: number | null,
|
|
32
|
-
zoom?: number | null
|
|
34
|
+
zoom?: number | null,
|
|
35
|
+
experiments?: Array<ExperimentKey> | null,
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
export class WebPubDefaults {
|
|
@@ -48,6 +51,7 @@ export class WebPubDefaults {
|
|
|
48
51
|
textNormalization: boolean | null;
|
|
49
52
|
wordSpacing: number | null;
|
|
50
53
|
zoom: number;
|
|
54
|
+
experiments: Array<ExperimentKey> | null;
|
|
51
55
|
|
|
52
56
|
constructor(defaults: IWebPubDefaults) {
|
|
53
57
|
this.fontFamily = ensureString(defaults.fontFamily) || null;
|
|
@@ -69,5 +73,6 @@ export class WebPubDefaults {
|
|
|
69
73
|
this.textNormalization = ensureBoolean(defaults.textNormalization) ?? false;
|
|
70
74
|
this.wordSpacing = ensureNonNegative(defaults.wordSpacing) || null;
|
|
71
75
|
this.zoom = ensureValueInRange(defaults.zoom, zoomRangeConfig.range) || 1;
|
|
76
|
+
this.experiments = ensureExperiment(defaults.experiments) ?? null;
|
|
72
77
|
}
|
|
73
78
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfigurableSettings } from "../../preferences/Configurable";
|
|
2
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
2
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
3
3
|
import { WebPubDefaults } from "./WebPubDefaults";
|
|
4
4
|
import { WebPubPreferences } from "./WebPubPreferences";
|
|
5
5
|
|
|
@@ -20,7 +20,8 @@ export interface IWebPubSettings {
|
|
|
20
20
|
textAlign?: TextAlignment | null,
|
|
21
21
|
textNormalization?: boolean | null,
|
|
22
22
|
wordSpacing?: number | null,
|
|
23
|
-
zoom?: number | null
|
|
23
|
+
zoom?: number | null,
|
|
24
|
+
experiments?: Array<ExperimentKey> | null,
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export class WebPubSettings implements ConfigurableSettings {
|
|
@@ -39,6 +40,7 @@ export class WebPubSettings implements ConfigurableSettings {
|
|
|
39
40
|
textNormalization: boolean | null = null;
|
|
40
41
|
wordSpacing: number | null = null;
|
|
41
42
|
zoom: number | null;
|
|
43
|
+
experiments: Array<ExperimentKey> | null;
|
|
42
44
|
|
|
43
45
|
constructor(preferences: WebPubPreferences, defaults: WebPubDefaults, hasDisplayTransformability: boolean) {
|
|
44
46
|
if (hasDisplayTransformability) {
|
|
@@ -103,5 +105,7 @@ export class WebPubSettings implements ConfigurableSettings {
|
|
|
103
105
|
: defaults.zoom !== undefined
|
|
104
106
|
? defaults.zoom
|
|
105
107
|
: null;
|
|
108
|
+
|
|
109
|
+
this.experiments = defaults.experiments || null;
|
|
106
110
|
}
|
|
107
111
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
1
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
2
2
|
import { BodyHyphens, BoxSizing, FontOpticalSizing, FontWidth, Ligatures, Properties, TypeScale, View } from "../../css/Properties";
|
|
3
3
|
export interface IUserProperties {
|
|
4
4
|
advancedSettings?: boolean | null;
|
|
@@ -113,6 +113,7 @@ export interface IRSProperties {
|
|
|
113
113
|
textColor?: string | null;
|
|
114
114
|
typeScale?: TypeScale | null;
|
|
115
115
|
visitedColor?: string | null;
|
|
116
|
+
experiments?: Array<ExperimentKey> | null;
|
|
116
117
|
}
|
|
117
118
|
export declare class RSProperties extends Properties {
|
|
118
119
|
backgroundColor: string | null;
|
|
@@ -154,6 +155,7 @@ export declare class RSProperties extends Properties {
|
|
|
154
155
|
textColor: string | null;
|
|
155
156
|
typeScale: TypeScale | null;
|
|
156
157
|
visitedColor: string | null;
|
|
158
|
+
experiments: Array<ExperimentKey> | null;
|
|
157
159
|
constructor(props: IRSProperties);
|
|
158
160
|
toCSSProperties(): {
|
|
159
161
|
[key: string]: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
1
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
2
2
|
export interface IEpubDefaults {
|
|
3
3
|
backgroundColor?: string | null;
|
|
4
4
|
blendFilter?: boolean | null;
|
|
@@ -38,6 +38,7 @@ export interface IEpubDefaults {
|
|
|
38
38
|
textNormalization?: boolean | null;
|
|
39
39
|
visitedColor?: string | null;
|
|
40
40
|
wordSpacing?: number | null;
|
|
41
|
+
experiments?: Array<ExperimentKey> | null;
|
|
41
42
|
}
|
|
42
43
|
export declare class EpubDefaults {
|
|
43
44
|
backgroundColor: string | null;
|
|
@@ -78,5 +79,6 @@ export declare class EpubDefaults {
|
|
|
78
79
|
textNormalization: boolean | null;
|
|
79
80
|
visitedColor: string | null;
|
|
80
81
|
wordSpacing: number | null;
|
|
82
|
+
experiments: Array<ExperimentKey> | null;
|
|
81
83
|
constructor(defaults: IEpubDefaults);
|
|
82
84
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfigurableSettings } from "../../preferences/Configurable";
|
|
2
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
2
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
3
3
|
import { EpubDefaults } from "./EpubDefaults";
|
|
4
4
|
import { EpubPreferences } from "./EpubPreferences";
|
|
5
5
|
export interface IEpubSettings {
|
|
@@ -41,6 +41,7 @@ export interface IEpubSettings {
|
|
|
41
41
|
textNormalization?: boolean | null;
|
|
42
42
|
visitedColor?: string | null;
|
|
43
43
|
wordSpacing?: number | null;
|
|
44
|
+
experiments?: Array<ExperimentKey> | null;
|
|
44
45
|
}
|
|
45
46
|
export declare class EpubSettings implements ConfigurableSettings {
|
|
46
47
|
backgroundColor: string | null;
|
|
@@ -81,5 +82,6 @@ export declare class EpubSettings implements ConfigurableSettings {
|
|
|
81
82
|
textNormalization: boolean | null;
|
|
82
83
|
visitedColor: string | null;
|
|
83
84
|
wordSpacing: number | null;
|
|
85
|
+
experiments: Array<ExperimentKey> | null;
|
|
84
86
|
constructor(preferences: EpubPreferences, defaults: EpubDefaults);
|
|
85
87
|
}
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import RCSSExperiments from "@readium/css/css/vars/experiments.json";
|
|
2
|
+
export type ExperimentKey = keyof typeof RCSSExperiments;
|
|
3
|
+
export declare const experiments: {
|
|
4
|
+
experimentalHeaderFiltering: {
|
|
5
|
+
description: string;
|
|
6
|
+
scope: string;
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
9
|
+
experimentalZoom: {
|
|
10
|
+
description: string;
|
|
11
|
+
scope: string;
|
|
12
|
+
value: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
1
15
|
export declare enum TextAlignment {
|
|
2
16
|
start = "start",
|
|
3
17
|
left = "left",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ExperimentKey } from './Types';
|
|
1
2
|
export declare function ensureLessThanOrEqual<T extends number | null | undefined>(value: T, compareTo: T): T | undefined;
|
|
2
3
|
export declare function ensureMoreThanOrEqual<T extends number | null | undefined>(value: T, compareTo: T): T | undefined;
|
|
3
4
|
export declare function ensureString(value: string | null | undefined): string | null | undefined;
|
|
@@ -7,3 +8,4 @@ export declare function ensureFilter(filter: boolean | number | null | undefined
|
|
|
7
8
|
export declare function ensureNonNegative(value: number | null | undefined): number | null | undefined;
|
|
8
9
|
export declare function ensureValueInRange(value: number | null | undefined, range: [number, number]): number | null | undefined;
|
|
9
10
|
export declare function withFallback<T>(value: T | null | undefined, defaultValue: T | null): T | null;
|
|
11
|
+
export declare function ensureExperiment(experimentsInput: ExperimentKey[] | null | undefined): ExperimentKey[] | null | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
1
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
2
2
|
import { BodyHyphens, Ligatures, Properties } from "../../css/Properties";
|
|
3
3
|
export interface IWebUserProperties {
|
|
4
4
|
a11yNormalize?: boolean | null;
|
|
@@ -38,3 +38,13 @@ export declare class WebUserProperties extends Properties {
|
|
|
38
38
|
[key: string]: string;
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
export interface IWebRSProperties {
|
|
42
|
+
experiments: Array<ExperimentKey> | null;
|
|
43
|
+
}
|
|
44
|
+
export declare class WebRSProperties extends Properties {
|
|
45
|
+
experiments: Array<ExperimentKey> | null;
|
|
46
|
+
constructor(props: IWebRSProperties);
|
|
47
|
+
toCSSProperties(): {
|
|
48
|
+
[key: string]: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { WebPubSettings } from "../preferences/WebPubSettings";
|
|
2
|
-
import { WebUserProperties } from "./Properties";
|
|
2
|
+
import { WebRSProperties, WebUserProperties } from "./Properties";
|
|
3
3
|
export interface IWebPubCSS {
|
|
4
|
+
rsProperties: WebRSProperties;
|
|
4
5
|
userProperties: WebUserProperties;
|
|
5
6
|
}
|
|
6
7
|
export declare class WebPubCSS {
|
|
8
|
+
rsProperties: WebRSProperties;
|
|
7
9
|
userProperties: WebUserProperties;
|
|
8
10
|
constructor(props: IWebPubCSS);
|
|
9
11
|
update(settings: WebPubSettings): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextAlignment } from "../../preferences/Types";
|
|
1
|
+
import { ExperimentKey, TextAlignment } from "../../preferences/Types";
|
|
2
2
|
export interface IWebPubDefaults {
|
|
3
3
|
fontFamily?: string | null;
|
|
4
4
|
fontWeight?: number | null;
|
|
@@ -15,6 +15,7 @@ export interface IWebPubDefaults {
|
|
|
15
15
|
textNormalization?: boolean | null;
|
|
16
16
|
wordSpacing?: number | null;
|
|
17
17
|
zoom?: number | null;
|
|
18
|
+
experiments?: Array<ExperimentKey> | null;
|
|
18
19
|
}
|
|
19
20
|
export declare class WebPubDefaults {
|
|
20
21
|
fontFamily: string | null;
|
|
@@ -32,5 +33,6 @@ export declare class WebPubDefaults {
|
|
|
32
33
|
textNormalization: boolean | null;
|
|
33
34
|
wordSpacing: number | null;
|
|
34
35
|
zoom: number;
|
|
36
|
+
experiments: Array<ExperimentKey> | null;
|
|
35
37
|
constructor(defaults: IWebPubDefaults);
|
|
36
38
|
}
|