@readium/navigator 1.3.4 → 2.0.0-beta.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.
Files changed (55) hide show
  1. package/dist/index.js +3732 -2813
  2. package/dist/index.umd.cjs +16 -16
  3. package/package.json +9 -9
  4. package/src/epub/EpubNavigator.ts +184 -7
  5. package/src/epub/css/Properties.ts +376 -0
  6. package/src/epub/css/ReadiumCSS.ts +348 -0
  7. package/src/epub/css/index.ts +2 -0
  8. package/src/epub/frame/FrameBlobBuilder.ts +59 -9
  9. package/src/epub/frame/FrameManager.ts +16 -0
  10. package/src/epub/frame/FramePoolManager.ts +61 -2
  11. package/src/epub/fxl/FXLFramePoolManager.ts +3 -15
  12. package/src/epub/index.ts +3 -1
  13. package/src/epub/preferences/EpubDefaults.ts +154 -0
  14. package/src/epub/preferences/EpubPreferences.ts +183 -0
  15. package/src/epub/preferences/EpubPreferencesEditor.ts +501 -0
  16. package/src/epub/preferences/EpubSettings.ts +212 -0
  17. package/src/epub/preferences/guards.ts +86 -0
  18. package/src/epub/preferences/index.ts +4 -0
  19. package/src/helpers/dimensions.ts +13 -0
  20. package/src/helpers/index.ts +1 -0
  21. package/src/helpers/lineLength.ts +293 -0
  22. package/src/helpers/sML.ts +18 -1
  23. package/src/index.ts +2 -1
  24. package/src/preferences/Configurable.ts +16 -0
  25. package/src/preferences/Preference.ts +272 -0
  26. package/src/preferences/PreferencesEditor.ts +6 -0
  27. package/src/preferences/Types.ts +39 -0
  28. package/src/preferences/index.ts +4 -0
  29. package/types/src/epub/EpubNavigator.d.ts +27 -3
  30. package/types/src/epub/css/Properties.d.ts +177 -0
  31. package/types/src/epub/css/ReadiumCSS.d.ts +32 -0
  32. package/types/src/epub/css/index.d.ts +2 -0
  33. package/types/src/epub/frame/FrameBlobBuilder.d.ts +5 -1
  34. package/types/src/epub/frame/FrameManager.d.ts +4 -0
  35. package/types/src/epub/frame/FramePoolManager.d.ts +8 -1
  36. package/types/src/epub/fxl/FXLFramePoolManager.d.ts +1 -3
  37. package/types/src/epub/index.d.ts +2 -0
  38. package/types/src/epub/preferences/EpubDefaults.d.ts +82 -0
  39. package/types/src/epub/preferences/EpubPreferences.d.ts +86 -0
  40. package/types/src/epub/preferences/EpubPreferencesEditor.d.ts +53 -0
  41. package/types/src/epub/preferences/EpubSettings.d.ts +85 -0
  42. package/types/src/epub/preferences/guards.d.ts +9 -0
  43. package/types/src/epub/preferences/index.d.ts +4 -0
  44. package/types/src/helpers/dimensions.d.ts +7 -0
  45. package/types/src/helpers/index.d.ts +1 -0
  46. package/types/src/helpers/lineLength.d.ts +68 -0
  47. package/types/src/helpers/sML.d.ts +6 -1
  48. package/types/src/index.d.ts +1 -0
  49. package/types/src/preferences/Configurable.d.ts +13 -0
  50. package/types/src/preferences/Preference.d.ts +117 -0
  51. package/types/src/preferences/PreferencesEditor.d.ts +5 -0
  52. package/types/src/preferences/PreferencesSerializer.d.ts +5 -0
  53. package/types/src/preferences/Types.d.ts +24 -0
  54. package/types/src/preferences/index.d.ts +4 -0
  55. package/LICENSE +0 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator",
3
- "version": "1.3.4",
3
+ "version": "2.0.0-beta.1",
4
4
  "type": "module",
5
5
  "description": "Next generation SDK for publications in Web Apps",
6
6
  "author": "readium",
@@ -44,20 +44,20 @@
44
44
  "engines": {
45
45
  "node": ">=18"
46
46
  },
47
+ "scripts": {
48
+ "build": "tsc && vite build"
49
+ },
47
50
  "devDependencies": {
48
51
  "@laynezh/vite-plugin-lib-assets": "^0.5.25",
52
+ "@readium/css": ">=2.0.0-beta.13",
53
+ "@readium/navigator-html-injectables": "workspace:*",
54
+ "@readium/shared": "workspace:*",
49
55
  "@types/path-browserify": "^1.0.3",
50
56
  "css-selector-generator": "^3.6.9",
51
57
  "path-browserify": "^1.0.1",
52
- "@readium/css": "^1.1.0",
53
58
  "tslib": "^2.8.1",
54
59
  "typescript": "^5.6.3",
55
60
  "typescript-plugin-css-modules": "^5.1.0",
56
- "vite": "^4.5.5",
57
- "@readium/navigator-html-injectables": "1.3.2",
58
- "@readium/shared": "1.2.1"
59
- },
60
- "scripts": {
61
- "build": "tsc && vite build"
61
+ "vite": "^4.5.5"
62
62
  }
63
- }
63
+ }
@@ -1,5 +1,5 @@
1
1
  import { EPUBLayout, Link, Locator, Publication, ReadingProgression } from "@readium/shared";
2
- import { VisualNavigator } from "../";
2
+ import { Configurable, ConfigurablePreferences, ConfigurableSettings, LineLengths, VisualNavigator } from "../";
3
3
  import { FramePoolManager } from "./frame/FramePoolManager";
4
4
  import { FXLFramePoolManager } from "./fxl/FXLFramePoolManager";
5
5
  import { CommsEventKey, FXLModules, ModuleLibrary, ModuleName, ReflowableModules } from "@readium/navigator-html-injectables";
@@ -7,9 +7,21 @@ import { BasicTextSelection, FrameClickEvent } from "@readium/navigator-html-inj
7
7
  import * as path from "path-browserify";
8
8
  import { FXLFrameManager } from "./fxl/FXLFrameManager";
9
9
  import { FrameManager } from "./frame/FrameManager";
10
+ import { IEpubPreferences, EpubPreferences } from "./preferences/EpubPreferences";
11
+ import { IEpubDefaults, EpubDefaults } from "./preferences/EpubDefaults";
12
+ import { EpubSettings } from "./preferences";
13
+ import { EpubPreferencesEditor } from "./preferences/EpubPreferencesEditor";
14
+ import { ReadiumCSS } from "./css/ReadiumCSS";
15
+ import { RSProperties, UserProperties } from "./css/Properties";
16
+ import { getContentWidth } from "../helpers/dimensions";
10
17
 
11
18
  export type ManagerEventKey = "zoom";
12
19
 
20
+ export interface EpubNavigatorConfiguration {
21
+ preferences: IEpubPreferences;
22
+ defaults: IEpubDefaults;
23
+ }
24
+
13
25
  export interface EpubNavigatorListeners {
14
26
  frameLoaded: (wnd: Window) => void;
15
27
  positionChanged: (locator: Locator) => void;
@@ -35,7 +47,7 @@ const defaultListeners = (listeners: EpubNavigatorListeners): EpubNavigatorListe
35
47
  textSelected: listeners.textSelected || (() => {})
36
48
  })
37
49
 
38
- export class EpubNavigator extends VisualNavigator {
50
+ export class EpubNavigator extends VisualNavigator implements Configurable<ConfigurableSettings, ConfigurablePreferences> {
39
51
  private readonly pub: Publication;
40
52
  private readonly container: HTMLElement;
41
53
  private readonly listeners: EpubNavigatorListeners;
@@ -46,16 +58,55 @@ export class EpubNavigator extends VisualNavigator {
46
58
  private currentProgression: ReadingProgression;
47
59
  public readonly layout: EPUBLayout;
48
60
 
49
- constructor(container: HTMLElement, pub: Publication, listeners: EpubNavigatorListeners, positions: Locator[] = [], initialPosition: Locator | undefined = undefined) {
61
+ private _preferences: EpubPreferences;
62
+ private _defaults: EpubDefaults;
63
+ private _settings: EpubSettings;
64
+ private _css: ReadiumCSS;
65
+ private _preferencesEditor: EpubPreferencesEditor | null = null;
66
+
67
+ private resizeObserver: ResizeObserver;
68
+
69
+ constructor(container: HTMLElement, pub: Publication, listeners: EpubNavigatorListeners, positions: Locator[] = [], initialPosition: Locator | undefined = undefined, configuration: EpubNavigatorConfiguration = { preferences: {}, defaults: {} }) {
50
70
  super();
51
71
  this.pub = pub;
52
72
  this.layout = EpubNavigator.determineLayout(pub);
53
- this.currentProgression = pub.metadata.effectiveReadingProgression;
54
73
  this.container = container;
55
74
  this.listeners = defaultListeners(listeners);
56
75
  this.currentLocation = initialPosition!;
57
76
  if (positions.length)
58
77
  this.positions = positions;
78
+
79
+ this._preferences = new EpubPreferences(configuration.preferences);
80
+ this._defaults = new EpubDefaults(configuration.defaults);
81
+ this._settings = new EpubSettings(this._preferences, this._defaults);
82
+ this._css = new ReadiumCSS({
83
+ rsProperties: new RSProperties({}),
84
+ userProperties: new UserProperties({}),
85
+ lineLengths: new LineLengths({
86
+ optimalChars: this._settings.optimalLineLength,
87
+ minChars: this._settings.minimalLineLength,
88
+ maxChars: this._settings.maximalLineLength,
89
+ pageGutter: this._settings.pageGutter,
90
+ fontFace: this._settings.fontFamily,
91
+ letterSpacing: this._settings.letterSpacing,
92
+ wordSpacing: this._settings.wordSpacing,
93
+ // sample: this.pub.metadata.description
94
+ }),
95
+ container: container,
96
+ constraint: this._settings.constraint
97
+ });
98
+
99
+ this.currentProgression = this.layout === EPUBLayout.reflowable
100
+ ? (this._settings.scroll
101
+ ? ReadingProgression.ttb
102
+ : pub.metadata.effectiveReadingProgression)
103
+ : pub.metadata.effectiveReadingProgression;
104
+
105
+ // We use a resizeObserver cos’ the container parent may not be the width of
106
+ // the document/window e.g. app using a docking system with left and right panels.
107
+ // If we observe this.container, that won’t obviously work since we set its width.
108
+ this.resizeObserver = new ResizeObserver(() => this.ownerWindow.requestAnimationFrame(async () => await this.resizeHandler()));
109
+ this.resizeObserver.observe(this.container.parentElement || document.documentElement);
59
110
  }
60
111
 
61
112
  public static determineLayout(pub: Publication): EPUBLayout {
@@ -79,13 +130,139 @@ export class EpubNavigator extends VisualNavigator {
79
130
  this.framePool.listener = (key: CommsEventKey | ManagerEventKey, data: unknown) => {
80
131
  this.eventListener(key, data);
81
132
  }
82
- } else
83
- this.framePool = new FramePoolManager(this.container, this.positions);
133
+ } else {
134
+ await this.updateCSS(false);
135
+ const cssProperties = this.compileCSSProperties(this._css);
136
+ this.framePool = new FramePoolManager(this.container, this.positions, cssProperties);
137
+ }
138
+
84
139
  if(this.currentLocation === undefined)
85
140
  this.currentLocation = this.positions[0];
141
+
142
+ await this.resizeHandler();
86
143
  await this.apply();
87
144
  }
88
145
 
146
+ public get settings(): Readonly<EpubSettings> {
147
+ if (this.layout === EPUBLayout.fixed) {
148
+ return Object.freeze({ ...this._settings });
149
+ } else {
150
+ // Given all the nasty issues moving auto-pagination to EpubSettings creates
151
+ // Especially as it’s tied to ReadiumCSS in the first place and could be
152
+ // problematic if you intend to use something else,
153
+ // we return the properties with columnCount overridden
154
+ const columnCount = this._css.userProperties.colCount || this._css.rsProperties.colCount || this._settings.columnCount;
155
+ return Object.freeze({ ...this._settings, columnCount: columnCount });
156
+ }
157
+ }
158
+
159
+ public get preferencesEditor() {
160
+ if (this._preferencesEditor === null) {
161
+ // Note: we pass this.settings instead of this._settings to ensure the columnCount is correct
162
+ this._preferencesEditor = new EpubPreferencesEditor(this._preferences, this.settings, this.pub.metadata);
163
+ }
164
+ return this._preferencesEditor;
165
+ }
166
+
167
+ public async submitPreferences(preferences: EpubPreferences) {
168
+ this._preferences = this._preferences.merging(preferences) as EpubPreferences;
169
+ await this.applyPreferences();
170
+ }
171
+
172
+ private async applyPreferences() {
173
+ const oldSettings = this._settings;
174
+ this._settings = new EpubSettings(this._preferences, this._defaults);
175
+
176
+ if (this._preferencesEditor !== null) {
177
+ // Note: we pass this.settings instead of this._settings to ensure the columnCount is correct
178
+ this._preferencesEditor = new EpubPreferencesEditor(this._preferences, this.settings, this.pub.metadata);
179
+ }
180
+
181
+ if (this.layout === EPUBLayout.fixed) {
182
+ this.handleFXLPrefs(oldSettings, this._settings);
183
+ } else {
184
+ await this.updateCSS(true);
185
+ }
186
+ }
187
+
188
+ // TODO: fit, etc.
189
+ private handleFXLPrefs(from: EpubSettings, to: EpubSettings) {
190
+ if (from.columnCount !== to.columnCount) {
191
+ (this.framePool as FXLFramePoolManager).setPerPage(to.columnCount);
192
+ }
193
+ }
194
+
195
+ private async updateCSS(commit: boolean) {
196
+ this._css.update(this._settings);
197
+
198
+ if (commit) await this.commitCSS(this._css);
199
+ };
200
+
201
+ private compileCSSProperties(css: ReadiumCSS) {
202
+ const properties: { [key: string]: string } = {};
203
+
204
+ for (const [key, value] of Object.entries(css.rsProperties.toCSSProperties())) {
205
+ properties[key] = value;
206
+ }
207
+
208
+ for (const [key, value] of Object.entries(css.userProperties.toCSSProperties())) {
209
+ properties[key] = value;
210
+ }
211
+
212
+ return properties;
213
+ }
214
+
215
+ private async commitCSS(css: ReadiumCSS) {
216
+ // Since we’re updating the CSS properties in injectables by removing
217
+ // the existing properties that are not inside this object first,
218
+ // then adding all from it, we don’t compare the previous properties here
219
+ const properties = this.compileCSSProperties(css);
220
+
221
+ (this.framePool as FramePoolManager).setCSSProperties(properties);
222
+
223
+ if (
224
+ this._css.userProperties.view === "paged" &&
225
+ this.readingProgression === ReadingProgression.ttb
226
+ ) {
227
+ await this.setReadingProgression(this.pub.metadata.effectiveReadingProgression);
228
+ } else if (
229
+ this._css.userProperties.view === "scroll" &&
230
+ (this.readingProgression === ReadingProgression.ltr || this.readingProgression === ReadingProgression.rtl)
231
+ ) {
232
+ await this.setReadingProgression(ReadingProgression.ttb);
233
+ }
234
+
235
+ this._css.setContainerWidth();
236
+ }
237
+
238
+ async resizeHandler() {
239
+ // We check the parentElement cos we want to remove constraint from the container
240
+ // and the container may not be the entire width of the document/window
241
+ const parentEl = this.container.parentElement || document.documentElement;
242
+
243
+ if (this.layout === EPUBLayout.fixed) {
244
+ this.container.style.width = `${ getContentWidth(parentEl) - this._settings.constraint }px`;
245
+ (this.framePool as FXLFramePoolManager).resizeHandler();
246
+ } else {
247
+ // for reflow ReadiumCSS gets the width from columns + line-lengths
248
+ // but we need to check whether colCount has changed to commit new CSS
249
+ const oldColCount = this._css.userProperties.colCount;
250
+ const oldLineLength = this._css.userProperties.lineLength;
251
+ this._css.resizeHandler();
252
+ if (
253
+ this._css.userProperties.view !== "scroll" &&
254
+ oldColCount !== this._css.userProperties.colCount ||
255
+ oldLineLength !== this._css.userProperties.lineLength
256
+ ) {
257
+ await this.commitCSS(this._css);
258
+ }
259
+ }
260
+ }
261
+
262
+ get ownerWindow() {
263
+ return this.container.ownerDocument.defaultView || window;
264
+ }
265
+
89
266
  /**
90
267
  * Exposed to the public to compensate for lack of implemented readium conveniences
91
268
  * TODO remove when settings management is incorporated
@@ -442,7 +619,7 @@ export class EpubNavigator extends VisualNavigator {
442
619
 
443
620
  // TODO: This is temporary until user settings are implemented.
444
621
  public async setReadingProgression(newProgression: ReadingProgression) {
445
- if(this.currentProgression === newProgression) return;
622
+ if(this.currentProgression === newProgression || !this.framePool) return;
446
623
  this.currentProgression = newProgression;
447
624
  await this.framePool.update(this.pub, this.currentLocator, this.determineModules(), true);
448
625
  this.attachListener();
@@ -0,0 +1,376 @@
1
+ import { TextAlignment, Theme } from "../../preferences/Types";
2
+
3
+ export type BodyHyphens = "auto" | "none";
4
+ export type BoxSizing = "content-box" | "border-box";
5
+ export type FontOpticalSizing = "auto" | "none";
6
+ export type FontWidth = "ultra-condensed" | "extra-condensed" | "condensed" | "semi-condensed" | "normal" | "semi-expanded" | "expanded" | "extra-expanded" | "ultra-expanded" | number;
7
+ export type Ligatures = "common-ligatures" | "none";
8
+ export type TypeScale = 1 | 1.067 | 1.125 | 1.2 | 1.25 | 1.333 | 1.414 | 1.5 | 1.618;
9
+ export type View = "paged" | "scroll";
10
+
11
+ abstract class Properties {
12
+ constructor() {}
13
+
14
+ protected toFlag(name: string) {
15
+ return `readium-${ name }-on`;
16
+ }
17
+
18
+ protected toUnitless(value: number) {
19
+ return value.toString();
20
+ }
21
+
22
+ protected toPercentage(value: number, ratio: boolean = false) {
23
+ if (ratio || value > 0 && value <= 1) {
24
+ return `${ Math.round(value * 100) }%`;
25
+ } else {
26
+ return `${ value }%`;
27
+ }
28
+ }
29
+
30
+ protected toVw(value: number) {
31
+ const percentage = Math.round(value * 100);
32
+ return `${ Math.min(percentage, 100) }vw`;
33
+ }
34
+
35
+ protected toVh(value: number) {
36
+ const percentage = Math.round(value * 100);
37
+ return `${ Math.min(percentage, 100) }vh`;
38
+ }
39
+
40
+ protected toPx(value: number) {
41
+ return `${ value }px`;
42
+ }
43
+
44
+ protected toRem(value: number) {
45
+ return `${ value }rem`;
46
+ }
47
+
48
+ abstract toCSSProperties(): { [key: string]: string };
49
+ }
50
+
51
+ export interface IUserProperties {
52
+ advancedSettings?: boolean | null;
53
+ a11yNormalize?: boolean | null;
54
+ appearance?: Theme | null;
55
+ backgroundColor?: string | null;
56
+ blendFilter?: boolean | null;
57
+ bodyHyphens?: BodyHyphens | null;
58
+ colCount?: number | null;
59
+ darkenFilter?: boolean | number | null;
60
+ deprecatedFontSize?: boolean | null;
61
+ fontFamily?: string | null;
62
+ fontOpticalSizing?: FontOpticalSizing | null;
63
+ fontOverride?: boolean | null;
64
+ fontSize?: number | null;
65
+ fontSizeNormalize?: boolean | null;
66
+ fontWeight?: number | null;
67
+ fontWidth?: FontWidth | null;
68
+ invertFilter?: boolean | number | null;
69
+ invertGaijiFilter?: boolean | number | null;
70
+ iPadOSPatch?: boolean | null;
71
+ letterSpacing?: number | null;
72
+ ligatures?: Ligatures | null;
73
+ lineHeight?: number | null;
74
+ lineLength?: number | null;
75
+ linkColor?: string | null;
76
+ noRuby?: boolean | null;
77
+ paraIndent?: number | null;
78
+ paraSpacing?: number | null;
79
+ selectionBackgroundColor?: string | null;
80
+ selectionTextColor?: string | null;
81
+ textAlign?: TextAlignment | null;
82
+ textColor?: string | null;
83
+ view?: View | null;
84
+ visitedColor?: string | null;
85
+ wordSpacing?: number | null;
86
+ }
87
+
88
+ export class UserProperties extends Properties {
89
+ a11yNormalize: boolean | null;
90
+ appearance: Theme | null;
91
+ backgroundColor: string | null;
92
+ blendFilter: boolean | null;
93
+ bodyHyphens: BodyHyphens | null;
94
+ colCount: number | null | undefined;
95
+ darkenFilter: boolean | number | null;
96
+ deprecatedFontSize: boolean | null;
97
+ fontFamily: string | null;
98
+ fontOpticalSizing: FontOpticalSizing | null;
99
+ fontOverride: boolean | null;
100
+ fontSize: number | null;
101
+ fontSizeNormalize: boolean | null;
102
+ fontWeight: number | null;
103
+ fontWidth: FontWidth | null;
104
+ invertFilter: boolean | number | null;
105
+ invertGaijiFilter: boolean | number | null;
106
+ iPadOSPatch: boolean | null;
107
+ letterSpacing: number | null;
108
+ ligatures: Ligatures | null;
109
+ lineHeight: number | null;
110
+ lineLength: number | null;
111
+ linkColor: string | null;
112
+ noRuby: boolean | null;
113
+ paraIndent: number | null;
114
+ paraSpacing: number | null;
115
+ selectionBackgroundColor?: string | null;
116
+ selectionTextColor?: string | null;
117
+ textAlign: TextAlignment | null;
118
+ textColor: string | null;
119
+ view: View | null;
120
+ visitedColor: string | null;
121
+ wordSpacing: number | null;
122
+
123
+ constructor(props: IUserProperties) {
124
+ super();
125
+ this.a11yNormalize = props.a11yNormalize ?? null;
126
+ this.appearance = props.appearance ?? null;
127
+ this.backgroundColor = props.backgroundColor ?? null;
128
+ this.blendFilter = props.blendFilter ?? null;
129
+ this.bodyHyphens = props.bodyHyphens ?? null;
130
+ this.colCount = props.colCount ?? null;
131
+ this.darkenFilter = props.darkenFilter ?? null;
132
+ this.deprecatedFontSize = props.deprecatedFontSize ?? null;
133
+ this.fontFamily = props.fontFamily ?? null;
134
+ this.fontOpticalSizing = props.fontOpticalSizing ?? null;
135
+ this.fontOverride = props.fontOverride ?? null;
136
+ this.fontSize = props.fontSize ?? null;
137
+ this.fontSizeNormalize = props.fontSizeNormalize ?? null;
138
+ this.fontWeight = props.fontWeight ?? null;
139
+ this.fontWidth = props.fontWidth ?? null;
140
+ this.invertFilter = props.invertFilter ?? null;
141
+ this.invertGaijiFilter = props.invertGaijiFilter ?? null;
142
+ this.iPadOSPatch = props.iPadOSPatch ?? null;
143
+ this.letterSpacing = props.letterSpacing ?? null;
144
+ this.ligatures = props.ligatures ?? null;
145
+ this.lineHeight = props.lineHeight ?? null;
146
+ this.lineLength = props.lineLength ?? null;
147
+ this.linkColor = props.linkColor ?? null;
148
+ this.noRuby = props.noRuby ?? null;
149
+ this.paraIndent = props.paraIndent ?? null;
150
+ this.paraSpacing = props.paraSpacing ?? null;
151
+ this.selectionBackgroundColor = props.selectionBackgroundColor ?? null;
152
+ this.selectionTextColor = props.selectionTextColor ?? null;
153
+ this.textAlign = props.textAlign ?? null;
154
+ this.textColor = props.textColor ?? null;
155
+ this.view = props.view ?? null;
156
+ this.visitedColor = props.visitedColor ?? null;
157
+ this.wordSpacing = props.wordSpacing ?? null;
158
+ }
159
+
160
+ toCSSProperties() {
161
+ const cssProperties: { [key: string]: string } = {};
162
+
163
+ if (this.a11yNormalize) cssProperties["--USER__a11yNormalize"] = this.toFlag("a11y");
164
+ if (this.appearance) cssProperties["--USER__appearance"] = this.toFlag(this.appearance);
165
+ if (this.backgroundColor) cssProperties["--USER__backgroundColor"] = this.backgroundColor;
166
+ if (this.blendFilter) cssProperties["--USER__blendFilter"] = this.toFlag("blend");
167
+ if (this.bodyHyphens) cssProperties["--USER__bodyHyphens"] = this.bodyHyphens;
168
+ if (this.colCount) cssProperties["--USER__colCount"] = this.toUnitless(this.colCount);
169
+ if (this.darkenFilter === true) {
170
+ cssProperties["--USER__darkenFilter"] = this.toFlag("darken");
171
+ } else if (typeof this.darkenFilter === "number") {
172
+ cssProperties["--USER__darkenFilter"] = this.toPercentage(this.darkenFilter);
173
+ }
174
+ if (this.deprecatedFontSize) cssProperties["--USER__fontSizeImplementation"] = this.toFlag("deprecatedFontSize");
175
+ if (this.fontFamily) cssProperties["--USER__fontFamily"] = this.fontFamily;
176
+ if (this.fontOpticalSizing != null) cssProperties["--USER__fontOpticalSizing"] = this.fontOpticalSizing;
177
+ if (this.fontOverride) cssProperties["--USER__fontOverride"] = this.toFlag("font");
178
+ if (this.fontSize != null) cssProperties["--USER__fontSize"] = this.toPercentage(this.fontSize, true);
179
+ if (this.fontSizeNormalize) cssProperties["--USER__fontSizeNormalize"] = this.toFlag("normalize");
180
+ if (this.fontWeight != null) cssProperties["--USER__fontWeight"] = this.toUnitless(this.fontWeight);
181
+ if (this.fontWidth != null) {
182
+ cssProperties["--USER__fontWidth"] = typeof this.fontWidth === "string"
183
+ ? this.fontWidth
184
+ : this.toUnitless(this.fontWidth);
185
+ }
186
+ if (this.invertFilter === true) {
187
+ cssProperties["--USER__invertFilter"] = this.toFlag("invert");
188
+ } else if (typeof this.invertFilter === "number") {
189
+ cssProperties["--USER__invertFilter"] = this.toPercentage(this.invertFilter);
190
+ }
191
+ if (this.invertGaijiFilter === true) {
192
+ cssProperties["--USER__invertGaiji"] = this.toFlag("invertGaiji");
193
+ } else if (typeof this.invertGaijiFilter === "number") {
194
+ cssProperties["--USER__invertGaiji"] = this.toPercentage(this.invertGaijiFilter);
195
+ }
196
+ if (this.iPadOSPatch) cssProperties["--USER__iPadOSPatch"] = this.toFlag("iPadOSPatch");
197
+ if (this.letterSpacing != null) cssProperties["--USER__letterSpacing"] = this.toRem(this.letterSpacing);
198
+ if (this.ligatures) cssProperties["--USER__ligatures"] = this.ligatures;
199
+ if (this.lineHeight != null) cssProperties["--USER__lineHeight"] = this.toUnitless(this.lineHeight);
200
+ if (this.lineLength != null) cssProperties["--USER__lineLength"] = this.toPx(this.lineLength);
201
+ if (this.linkColor) cssProperties["--USER__linkColor"] = this.linkColor;
202
+ if (this.noRuby) cssProperties["--USER__noRuby"] = this.toFlag("noRuby");
203
+ if (this.paraIndent != null) cssProperties["--USER__paraIndent"] = this.toRem(this.paraIndent);
204
+ if (this.paraSpacing != null) cssProperties["--USER__paraSpacing"] = this.toRem(this.paraSpacing);
205
+ if (this.selectionBackgroundColor) cssProperties["--USER__selectionBackgroundColor"] = this.selectionBackgroundColor;
206
+ if (this.selectionTextColor) cssProperties["--USER__selectionTextColor"] = this.selectionTextColor;
207
+ if (this.textAlign) cssProperties["--USER__textAlign"] = this.textAlign;
208
+ if (this.textColor) cssProperties["--USER__textColor"] = this.textColor;
209
+ if (this.view) cssProperties["--USER__view"] = this.toFlag(this.view);
210
+ if (this.visitedColor) cssProperties["--USER__visitedColor"] = this.visitedColor;
211
+ if (this.wordSpacing != null) cssProperties["--USER__wordSpacing"] = this.toRem(this.wordSpacing);
212
+
213
+ return cssProperties;
214
+ }
215
+ }
216
+
217
+ export interface IRSProperties {
218
+ backgroundColor?: string | null;
219
+ baseFontFamily?: string | null;
220
+ baseFontSize?: number | null;
221
+ baseLineHeight?: number | null;
222
+ boxSizingMedia?: BoxSizing | null;
223
+ boxSizingTable?: BoxSizing | null;
224
+ colWidth?: string | null;
225
+ colCount?: number | null;
226
+ colGap?: number | null;
227
+ codeFontFamily?: string | null;
228
+ compFontFamily?: string | null;
229
+ defaultLineLength?: number | null;
230
+ flowSpacing?: number | null;
231
+ humanistTf?: string | null;
232
+ linkColor?: string | null;
233
+ maxMediaWidth?: number | null;
234
+ maxMediaHeight?: number | null;
235
+ modernTf?: string | null;
236
+ monospaceTf?: string | null;
237
+ noVerticalPagination?: boolean | null;
238
+ oldStyleTf?: string | null;
239
+ pageGutter?: number | null;
240
+ paraIndent?: number | null;
241
+ paraSpacing?: number | null;
242
+ primaryColor?: string | null;
243
+ sansSerifJa?: string | null;
244
+ sansSerifJaV?: string | null;
245
+ sansTf?: string | null;
246
+ secondaryColor?: string | null;
247
+ selectionBackgroundColor?: string | null;
248
+ selectionTextColor?: string | null;
249
+ serifJa?: string | null;
250
+ serifJaV?: string | null;
251
+ textColor?: string | null;
252
+ typeScale?: TypeScale | null;
253
+ visitedColor?: string | null;
254
+ }
255
+
256
+ export class RSProperties extends Properties {
257
+ backgroundColor: string | null;
258
+ baseFontFamily: string | null;
259
+ baseFontSize: number | null;
260
+ baseLineHeight: number | null;
261
+ boxSizingMedia: BoxSizing | null;
262
+ boxSizingTable: BoxSizing | null;
263
+ colWidth: string | null;
264
+ colCount: number | null;
265
+ colGap: number | null;
266
+ codeFontFamily: string | null;
267
+ compFontFamily: string | null;
268
+ defaultLineLength: number | null;
269
+ flowSpacing: number | null;
270
+ humanistTf: string | null;
271
+ linkColor: string | null;
272
+ maxMediaWidth: number | null;
273
+ maxMediaHeight: number | null;
274
+ modernTf: string | null;
275
+ monospaceTf: string | null;
276
+ noVerticalPagination: boolean | null;
277
+ oldStyleTf: string | null;
278
+ pageGutter: number | null;
279
+ paraIndent: number | null;
280
+ paraSpacing: number | null;
281
+ primaryColor: string | null;
282
+ sansSerifJa: string | null;
283
+ sansSerifJaV: string | null;
284
+ sansTf: string | null;
285
+ secondaryColor: string | null;
286
+ selectionBackgroundColor: string | null;
287
+ selectionTextColor: string | null;
288
+ serifJa: string | null;
289
+ serifJaV: string | null;
290
+ textColor: string | null;
291
+ typeScale: TypeScale | null;
292
+ visitedColor: string | null;
293
+
294
+ constructor(props: IRSProperties) {
295
+ super();
296
+ this.backgroundColor = props.backgroundColor ?? null;
297
+ this.baseFontFamily = props.baseFontFamily ?? null;
298
+ this.baseFontSize = props.baseFontSize ?? null;
299
+ this.baseLineHeight = props.baseLineHeight ?? null;
300
+ this.boxSizingMedia = props.boxSizingMedia ?? null;
301
+ this.boxSizingTable = props.boxSizingTable ?? null;
302
+ this.colWidth = props.colWidth ?? null;
303
+ this.colCount = props.colCount ?? null;
304
+ this.colGap = props.colGap ?? null;
305
+ this.codeFontFamily = props.codeFontFamily ?? null;
306
+ this.compFontFamily = props.compFontFamily ?? null;
307
+ this.defaultLineLength = props.defaultLineLength ?? null;
308
+ this.flowSpacing = props.flowSpacing ?? null;
309
+ this.humanistTf = props.humanistTf ?? null;
310
+ this.linkColor = props.linkColor ?? null;
311
+ this.maxMediaWidth = props.maxMediaWidth ?? null;
312
+ this.maxMediaHeight = props.maxMediaHeight ?? null;
313
+ this.modernTf = props.modernTf ?? null;
314
+ this.monospaceTf = props.monospaceTf ?? null;
315
+ this.noVerticalPagination = props.noVerticalPagination ?? null;
316
+ this.oldStyleTf = props.oldStyleTf ?? null;
317
+ this.pageGutter = props.pageGutter ?? null;
318
+ this.paraIndent = props.paraIndent ?? null;
319
+ this.paraSpacing = props.paraSpacing ?? null;
320
+ this.primaryColor = props.primaryColor ?? null;
321
+ this.sansSerifJa = props.sansSerifJa ?? null;
322
+ this.sansSerifJaV = props.sansSerifJaV ?? null;
323
+ this.sansTf = props.sansTf ?? null;
324
+ this.secondaryColor = props.secondaryColor ?? null;
325
+ this.selectionBackgroundColor = props.selectionBackgroundColor ?? null;
326
+ this.selectionTextColor = props.selectionTextColor ?? null;
327
+ this.serifJa = props.serifJa ?? null;
328
+ this.serifJaV = props.serifJaV ?? null;
329
+ this.textColor = props.textColor ?? null;
330
+ this.typeScale = props.typeScale ?? null;
331
+ this.visitedColor = props.visitedColor ?? null;
332
+ }
333
+
334
+ toCSSProperties(): { [key: string]: string; } {
335
+ const cssProperties: { [key: string]: string } = {};
336
+
337
+ if (this.backgroundColor) cssProperties["--RS__backgroundColor"] = this.backgroundColor;
338
+ if (this.baseFontFamily) cssProperties["--RS__baseFontFamily"] = this.baseFontFamily;
339
+ if (this.baseFontSize != null) cssProperties["--RS__baseFontSize"] = this.toRem(this.baseFontSize);
340
+ if (this.baseLineHeight != null) cssProperties["--RS__baseLineHeight"] = this.toUnitless(this.baseLineHeight);
341
+ if (this.boxSizingMedia) cssProperties["--RS__boxSizingMedia"] = this.boxSizingMedia;
342
+ if (this.boxSizingTable) cssProperties["--RS__boxSizingTable"] = this.boxSizingTable;
343
+ if (this.colWidth != null) cssProperties["--RS__colWidth"] = this.colWidth;
344
+ if (this.colCount != null) cssProperties["--RS__colCount"] = this.toUnitless(this.colCount);
345
+ if (this.colGap != null) cssProperties["--RS__colGap"] = this.toPx(this.colGap);
346
+ if (this.codeFontFamily) cssProperties["--RS__codeFontFamily"] = this.codeFontFamily;
347
+ if (this.compFontFamily) cssProperties["--RS__compFontFamily"] = this.compFontFamily;
348
+ if (this.defaultLineLength != null) cssProperties["--RS__defaultLineLength"] = this.toPx(this.defaultLineLength);
349
+ if (this.flowSpacing != null) cssProperties["--RS__flowSpacing"] = this.toRem(this.flowSpacing);
350
+ if (this.humanistTf) cssProperties["--RS__humanistTf"] = this.humanistTf;
351
+ if (this.linkColor) cssProperties["--RS__linkColor"] = this.linkColor;
352
+ if (this.maxMediaWidth) cssProperties["--RS__maxMediaWidth"] = this.toVw(this.maxMediaWidth);
353
+ if (this.maxMediaHeight) cssProperties["--RS__maxMediaHeight"] = this.toVh(this.maxMediaHeight);
354
+ if (this.modernTf) cssProperties["--RS__modernTf"] = this.modernTf;
355
+ if (this.monospaceTf) cssProperties["--RS__monospaceTf"] = this.monospaceTf;
356
+ if (this.noVerticalPagination) cssProperties["--RS__disablePagination"] = this.toFlag("noVerticalPagination");
357
+ if (this.oldStyleTf) cssProperties["--RS__oldStyleTf"] = this.oldStyleTf;
358
+ if (this.pageGutter != null) cssProperties["--RS__pageGutter"] = this.toPx(this.pageGutter);
359
+ if (this.paraIndent != null) cssProperties["--RS__paraIndent"] = this.toRem(this.paraIndent);
360
+ if (this.paraSpacing != null) cssProperties["--RS__paraSpacing"] = this.toRem(this.paraSpacing);
361
+ if (this.primaryColor) cssProperties["--RS__primaryColor"] = this.primaryColor;
362
+ if (this.sansSerifJa) cssProperties["--RS__sans-serif-ja"] = this.sansSerifJa;
363
+ if (this.sansSerifJaV) cssProperties["--RS__sans-serif-ja-v"] = this.sansSerifJaV;
364
+ if (this.sansTf) cssProperties["--RS__sansTf"] = this.sansTf;
365
+ if (this.secondaryColor) cssProperties["--RS__secondaryColor"] = this.secondaryColor;
366
+ if (this.selectionBackgroundColor) cssProperties["--RS__selectionBackgroundColor"] = this.selectionBackgroundColor;
367
+ if (this.selectionTextColor) cssProperties["--RS__selectionTextColor"] = this.selectionTextColor;
368
+ if (this.serifJa) cssProperties["--RS__serif-ja"] = this.serifJa;
369
+ if (this.serifJaV) cssProperties["--RS__serif-ja-v"] = this.serifJaV;
370
+ if (this.textColor) cssProperties["--RS__textColor"] = this.textColor;
371
+ if (this.typeScale) cssProperties["--RS__typeScale"] = this.toUnitless(this.typeScale);
372
+ if (this.visitedColor) cssProperties["--RS__visitedColor"] = this.visitedColor;
373
+
374
+ return cssProperties;
375
+ }
376
+ }