@readium/navigator 1.3.4 → 2.0.0-beta.10

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 (57) hide show
  1. package/dist/index.js +3974 -2928
  2. package/dist/index.umd.cjs +16 -16
  3. package/package.json +10 -9
  4. package/src/Navigator.ts +11 -0
  5. package/src/epub/EpubNavigator.ts +250 -24
  6. package/src/epub/css/Properties.ts +396 -0
  7. package/src/epub/css/ReadiumCSS.ts +339 -0
  8. package/src/epub/css/index.ts +2 -0
  9. package/src/epub/frame/FrameBlobBuilder.ts +59 -9
  10. package/src/epub/frame/FrameManager.ts +23 -1
  11. package/src/epub/frame/FramePoolManager.ts +62 -4
  12. package/src/epub/fxl/FXLFramePoolManager.ts +23 -16
  13. package/src/epub/index.ts +3 -1
  14. package/src/epub/preferences/EpubDefaults.ts +165 -0
  15. package/src/epub/preferences/EpubPreferences.ts +192 -0
  16. package/src/epub/preferences/EpubPreferencesEditor.ts +534 -0
  17. package/src/epub/preferences/EpubSettings.ts +239 -0
  18. package/src/epub/preferences/guards.ts +86 -0
  19. package/src/epub/preferences/index.ts +4 -0
  20. package/src/helpers/dimensions.ts +13 -0
  21. package/src/helpers/index.ts +1 -0
  22. package/src/helpers/lineLength.ts +241 -0
  23. package/src/helpers/sML.ts +25 -3
  24. package/src/index.ts +2 -1
  25. package/src/preferences/Configurable.ts +16 -0
  26. package/src/preferences/Preference.ts +272 -0
  27. package/src/preferences/PreferencesEditor.ts +6 -0
  28. package/src/preferences/Types.ts +38 -0
  29. package/src/preferences/index.ts +4 -0
  30. package/types/src/Navigator.d.ts +9 -0
  31. package/types/src/epub/EpubNavigator.d.ts +34 -4
  32. package/types/src/epub/css/Properties.d.ts +183 -0
  33. package/types/src/epub/css/ReadiumCSS.d.ts +31 -0
  34. package/types/src/epub/css/index.d.ts +2 -0
  35. package/types/src/epub/frame/FrameBlobBuilder.d.ts +5 -1
  36. package/types/src/epub/frame/FrameManager.d.ts +4 -0
  37. package/types/src/epub/frame/FramePoolManager.d.ts +8 -1
  38. package/types/src/epub/fxl/FXLFramePoolManager.d.ts +4 -4
  39. package/types/src/epub/index.d.ts +2 -0
  40. package/types/src/epub/preferences/EpubDefaults.d.ts +86 -0
  41. package/types/src/epub/preferences/EpubPreferences.d.ts +90 -0
  42. package/types/src/epub/preferences/EpubPreferencesEditor.d.ts +55 -0
  43. package/types/src/epub/preferences/EpubSettings.d.ts +89 -0
  44. package/types/src/epub/preferences/guards.d.ts +9 -0
  45. package/types/src/epub/preferences/index.d.ts +4 -0
  46. package/types/src/helpers/dimensions.d.ts +7 -0
  47. package/types/src/helpers/index.d.ts +1 -0
  48. package/types/src/helpers/lineLength.d.ts +54 -0
  49. package/types/src/helpers/sML.d.ts +6 -1
  50. package/types/src/index.d.ts +1 -0
  51. package/types/src/preferences/Configurable.d.ts +13 -0
  52. package/types/src/preferences/Preference.d.ts +117 -0
  53. package/types/src/preferences/PreferencesEditor.d.ts +5 -0
  54. package/types/src/preferences/PreferencesSerializer.d.ts +5 -0
  55. package/types/src/preferences/Types.d.ts +23 -0
  56. package/types/src/preferences/index.d.ts +4 -0
  57. package/LICENSE +0 -28
@@ -5,6 +5,7 @@ import FrameBlobBuider from "../frame/FrameBlobBuilder";
5
5
  import { FXLFrameManager } from "./FXLFrameManager";
6
6
  import { FXLPeripherals } from "./FXLPeripherals";
7
7
  import { FXLSpreader } from "./FXLSpreader";
8
+ import { VisualNavigatorViewport } from "../../Navigator";
8
9
 
9
10
  const UPPER_BOUNDARY = 8;
10
11
  const LOWER_BOUNDARY = 5;
@@ -39,7 +40,6 @@ export class FXLFramePoolManager {
39
40
  private readonly spreadPresentation: Spread;
40
41
  private orientationInternal = -1; // Portrait = 1, Landscape = 0, Unknown = -1
41
42
  private containerHeightCached: number;
42
- private readonly resizeBoundHandler: EventListenerOrEventListenerObject;
43
43
  private resizeTimeout: number | undefined;
44
44
  // private readonly pages: FXLFrameManager[] = [];
45
45
  public readonly peripherals: FXLPeripherals;
@@ -57,10 +57,6 @@ export class FXLFramePoolManager {
57
57
  // NEW
58
58
  this.spreader = new FXLSpreader(this.pub);
59
59
  this.containerHeightCached = container.clientHeight;
60
- this.resizeBoundHandler = this.nativeResizeHandler.bind(this);
61
-
62
- this.ownerWindow.addEventListener("resize", this.resizeBoundHandler);
63
- this.ownerWindow.addEventListener("orientationchange", this.resizeBoundHandler);
64
60
 
65
61
  this.bookElement = document.createElement("div");
66
62
  this.bookElement.ariaLabel = "Book";
@@ -101,10 +97,6 @@ export class FXLFramePoolManager {
101
97
  return this.peripherals.pan.touchID > 0;
102
98
  }
103
99
 
104
- private nativeResizeHandler(_: Event) {
105
- this.resizeHandler(true);
106
- }
107
-
108
100
  /**
109
101
  * When window resizes, resize slider components as well
110
102
  */
@@ -259,8 +251,8 @@ export class FXLFramePoolManager {
259
251
  return this.spreader.nLandscape;
260
252
  }
261
253
 
262
- public setPerPage(perPage: number) {
263
- if(perPage === 0) {
254
+ public setPerPage(perPage: number | null) {
255
+ if(perPage === null) {
264
256
  // TODO this mode is auto
265
257
  this.spread = true;
266
258
  } else if(perPage === 1) {
@@ -376,9 +368,6 @@ export class FXLFramePoolManager {
376
368
 
377
369
 
378
370
  async destroy() {
379
- this.ownerWindow.removeEventListener("resize", this.resizeBoundHandler);
380
- this.ownerWindow.removeEventListener("orientationchange", this.resizeBoundHandler);
381
-
382
371
  // Wait for all in-progress loads to complete
383
372
  let iit = this.inprogress.values();
384
373
  let inp = iit.next();
@@ -614,7 +603,25 @@ export class FXLFramePoolManager {
614
603
  return ret as DOMRect;
615
604
  }
616
605
 
617
- get currentNumbers(): number[] {
606
+ get viewport(): VisualNavigatorViewport {
607
+ const viewport: VisualNavigatorViewport = {
608
+ readingOrder: [],
609
+ progressions: new Map(),
610
+ positions: null
611
+ };
612
+ const currentSpread = this.spreader.currentSpread(this.currentSlide, this.perPage);
613
+ currentSpread.forEach(link => {
614
+ viewport.readingOrder.push(link.href);
615
+ viewport.progressions.set(link.href, { start: 0, end: 1 }); // FXL always uses [0,1] progression
616
+ });
617
+
618
+ // Set positions using currentNumbers
619
+ viewport.positions = this.getCurrentNumbers();
620
+
621
+ return viewport;
622
+ }
623
+
624
+ private getCurrentNumbers(): number[] {
618
625
  if(this.perPage < 2) {
619
626
  const link = this.pub.readingOrder.items[this.currentSlide];
620
627
  return [link.properties?.otherProperties["number"]];
@@ -629,4 +636,4 @@ export class FXLFramePoolManager {
629
636
  deselect() {
630
637
  this.currentFrames?.forEach(f => f?.deselect());
631
638
  }
632
- }
639
+ }
package/src/epub/index.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from "./EpubNavigator";
2
2
  export * from "./frame";
3
- export * from "./fxl";
3
+ export * from "./fxl";
4
+ export * from "./preferences";
5
+ export * from "./css";
@@ -0,0 +1,165 @@
1
+ import {
2
+ fontSizeRangeConfig,
3
+ fontWeightRangeConfig,
4
+ fontWidthRangeConfig,
5
+ LayoutStrategy,
6
+ TextAlignment,
7
+ Theme
8
+ } from "../../preferences/Types";
9
+
10
+ import {
11
+ ensureBoolean,
12
+ ensureEnumValue,
13
+ ensureFilter,
14
+ ensureLessThanOrEqual,
15
+ ensureMoreThanOrEqual,
16
+ ensureNonNegative,
17
+ ensureString,
18
+ ensureValueInRange,
19
+ withFallback
20
+ } from "./guards";
21
+
22
+ import { sMLWithRequest } from "../../helpers";
23
+
24
+ export interface IEpubDefaults {
25
+ backgroundColor?: string | null,
26
+ blendFilter?: boolean | null,
27
+ columnCount?: number | null,
28
+ constraint?: number | null,
29
+ darkenFilter?: boolean | number | null,
30
+ deprecatedFontSize?: boolean | null,
31
+ fontFamily?: string | null,
32
+ fontSize?: number | null,
33
+ fontSizeNormalize?: boolean | null,
34
+ fontOpticalSizing?: boolean | null,
35
+ fontWeight?: number | null,
36
+ fontWidth?: number | null,
37
+ hyphens?: boolean | null,
38
+ invertFilter?: boolean | number | null,
39
+ invertGaijiFilter?: boolean | number | null,
40
+ iOSPatch?: boolean | null,
41
+ iPadOSPatch?: boolean | null,
42
+ layoutStrategy?: LayoutStrategy | null,
43
+ letterSpacing?: number | null,
44
+ ligatures?: boolean | null,
45
+ lineHeight?: number | null,
46
+ linkColor?: string | null,
47
+ maximalLineLength?: number | null,
48
+ minimalLineLength?: number | null,
49
+ noRuby?: boolean | null,
50
+ optimalLineLength?: number | null,
51
+ pageGutter?: number | null,
52
+ paragraphIndent?: number | null,
53
+ paragraphSpacing?: number | null,
54
+ scroll?: boolean | null,
55
+ scrollPaddingTop?: number | null,
56
+ scrollPaddingBottom?: number | null,
57
+ // scrollPaddingLeft?: number | null,
58
+ // scrollPaddingRight?: number | null,
59
+ selectionBackgroundColor?: string | null,
60
+ selectionTextColor?: string | null,
61
+ textAlign?: TextAlignment | null,
62
+ textColor?: string | null,
63
+ textNormalization?: boolean | null,
64
+ theme?: Theme | null,
65
+ visitedColor?: string | null,
66
+ wordSpacing?: number | null
67
+ }
68
+
69
+ export class EpubDefaults {
70
+ backgroundColor: string | null;
71
+ blendFilter: boolean | null;
72
+ columnCount: number | null;
73
+ constraint: number;
74
+ darkenFilter: boolean | number | null;
75
+ deprecatedFontSize?: boolean | null;
76
+ fontFamily: string | null;
77
+ fontSize: number | null;
78
+ fontSizeNormalize: boolean | null;
79
+ fontOpticalSizing: boolean | null;
80
+ fontWeight: number | null;
81
+ fontWidth: number | null;
82
+ hyphens: boolean | null;
83
+ invertFilter: boolean | number | null;
84
+ invertGaijiFilter: boolean | number | null;
85
+ iOSPatch: boolean;
86
+ iPadOSPatch: boolean;
87
+ layoutStrategy: LayoutStrategy | null;
88
+ letterSpacing: number | null;
89
+ ligatures: boolean | null;
90
+ lineHeight: number | null;
91
+ linkColor: string | null;
92
+ maximalLineLength: number | null;
93
+ minimalLineLength: number | null;
94
+ noRuby: boolean | null;
95
+ optimalLineLength: number;
96
+ pageGutter: number | null;
97
+ paragraphIndent: number | null;
98
+ paragraphSpacing: number | null;
99
+ scroll: boolean | null;
100
+ scrollPaddingTop: number | null;
101
+ scrollPaddingBottom: number | null;
102
+ // scrollPaddingLeft: number | null;
103
+ // scrollPaddingRight: number | null;
104
+ selectionBackgroundColor: string | null;
105
+ selectionTextColor: string | null;
106
+ textAlign: TextAlignment | null;
107
+ textColor: string | null;
108
+ textNormalization: boolean | null;
109
+ theme: Theme | null;
110
+ visitedColor: string | null;
111
+ wordSpacing: number | null;
112
+
113
+ constructor(defaults: IEpubDefaults) {
114
+ this.backgroundColor = ensureString(defaults.backgroundColor) || null;
115
+ this.blendFilter = ensureBoolean(defaults.blendFilter) ?? false;
116
+ this.constraint = ensureNonNegative(defaults.constraint) || 0;
117
+ this.columnCount = ensureNonNegative(defaults.columnCount) || null;
118
+ this.darkenFilter = ensureFilter(defaults.darkenFilter) ?? false;
119
+ this.deprecatedFontSize = ensureBoolean(defaults.deprecatedFontSize);
120
+ if (this.deprecatedFontSize === false || this.deprecatedFontSize === null) {
121
+ this.deprecatedFontSize = !CSS.supports("zoom", "1");
122
+ }
123
+ this.fontFamily = ensureString(defaults.fontFamily) || null;
124
+ this.fontSize = ensureValueInRange(defaults.fontSize, fontSizeRangeConfig.range) || 1;
125
+ this.fontSizeNormalize = ensureBoolean(defaults.fontSizeNormalize) ?? false;
126
+ this.fontOpticalSizing = ensureBoolean(defaults.fontOpticalSizing) ?? null;
127
+ this.fontWeight = ensureValueInRange(defaults.fontWeight, fontWeightRangeConfig.range) || null;
128
+ this.fontWidth = ensureValueInRange(defaults.fontWidth,fontWidthRangeConfig.range) || null;
129
+ this.hyphens = ensureBoolean(defaults.hyphens) ?? null;
130
+ this.invertFilter = ensureFilter(defaults.invertFilter) ?? false;
131
+ this.invertGaijiFilter = ensureFilter(defaults.invertGaijiFilter) ?? false;
132
+ this.iOSPatch = defaults.iOSPatch === false
133
+ ? false
134
+ : ((sMLWithRequest.OS.iOS || sMLWithRequest.OS.iPadOS) && sMLWithRequest.iOSRequest === "mobile");
135
+ this.iPadOSPatch = defaults.iPadOSPatch === false
136
+ ? false
137
+ : (sMLWithRequest.OS.iPadOS && sMLWithRequest.iOSRequest === "desktop");
138
+ this.layoutStrategy = ensureEnumValue<LayoutStrategy>(defaults.layoutStrategy, LayoutStrategy) || LayoutStrategy.lineLength;
139
+ this.letterSpacing = ensureNonNegative(defaults.letterSpacing) || null;
140
+ this.ligatures = ensureBoolean(defaults.ligatures) ?? null;
141
+ this.lineHeight = ensureNonNegative(defaults.lineHeight) || null;
142
+ this.linkColor = ensureString(defaults.linkColor) || null;
143
+ this.noRuby = ensureBoolean(defaults.noRuby) ?? false;
144
+ this.pageGutter = withFallback(ensureNonNegative(defaults.pageGutter), 20);
145
+ this.paragraphIndent = ensureNonNegative(defaults.paragraphIndent) ?? null;
146
+ this.paragraphSpacing = ensureNonNegative(defaults.paragraphSpacing) ?? null;
147
+ this.scroll = ensureBoolean(defaults.scroll) ?? false;
148
+ this.scrollPaddingTop = ensureNonNegative(defaults.scrollPaddingTop) ?? null;
149
+ this.scrollPaddingBottom = ensureNonNegative(defaults.scrollPaddingBottom) ?? null;
150
+ // this.scrollPaddingLeft = ensureNonNegative(defaults.scrollPaddingLeft) ?? null;
151
+ // this.scrollPaddingRight = ensureNonNegative(defaults.scrollPaddingRight) ?? null;
152
+ this.selectionBackgroundColor = ensureString(defaults.selectionBackgroundColor) || null;
153
+ this.selectionTextColor = ensureString(defaults.selectionTextColor) || null;
154
+ this.textAlign = ensureEnumValue<TextAlignment>(defaults.textAlign, TextAlignment) || null;
155
+ this.textColor = ensureString(defaults.textColor) || null;
156
+ this.textNormalization = ensureBoolean(defaults.textNormalization) ?? false;
157
+ this.theme = ensureEnumValue<Theme>(defaults.theme, Theme) || null;
158
+ this.visitedColor = ensureString(defaults.visitedColor) || null;
159
+ this.wordSpacing = ensureNonNegative(defaults.wordSpacing) || null;
160
+
161
+ this.optimalLineLength = ensureNonNegative(defaults.optimalLineLength) || 65;
162
+ this.maximalLineLength = withFallback(ensureMoreThanOrEqual(defaults.maximalLineLength, this.optimalLineLength), 80);
163
+ this.minimalLineLength = withFallback(ensureLessThanOrEqual(defaults.minimalLineLength, this.optimalLineLength), 40);
164
+ }
165
+ }
@@ -0,0 +1,192 @@
1
+ import { ConfigurablePreferences } from "../../preferences/Configurable";
2
+
3
+ import {
4
+ LayoutStrategy,
5
+ TextAlignment,
6
+ Theme,
7
+ fontSizeRangeConfig,
8
+ fontWeightRangeConfig,
9
+ fontWidthRangeConfig
10
+ } from "../../preferences/Types";
11
+
12
+ import {
13
+ ensureBoolean,
14
+ ensureEnumValue,
15
+ ensureFilter,
16
+ ensureNonNegative,
17
+ ensureString,
18
+ ensureValueInRange
19
+ } from "./guards";
20
+
21
+ export interface IEpubPreferences {
22
+ backgroundColor?: string | null,
23
+ blendFilter?: boolean | null,
24
+ columnCount?: number | null,
25
+ constraint?: number | null,
26
+ darkenFilter?: boolean | number | null,
27
+ deprecatedFontSize?: boolean | null,
28
+ fontFamily?: string | null,
29
+ fontSize?: number | null,
30
+ fontSizeNormalize?: boolean | null,
31
+ fontOpticalSizing?: boolean | null,
32
+ fontWeight?: number | null,
33
+ fontWidth?: number | null,
34
+ hyphens?: boolean | null,
35
+ invertFilter?: boolean | number | null,
36
+ invertGaijiFilter?: boolean | number | null,
37
+ iOSPatch?: boolean | null,
38
+ iPadOSPatch?: boolean | null,
39
+ layoutStrategy?: LayoutStrategy | null,
40
+ letterSpacing?: number | null,
41
+ ligatures?: boolean | null,
42
+ lineHeight?: number | null,
43
+ linkColor?: string | null,
44
+ maximalLineLength?: number | null,
45
+ minimalLineLength?: number | null,
46
+ noRuby?: boolean | null,
47
+ optimalLineLength?: number | null,
48
+ pageGutter?: number | null,
49
+ paragraphIndent?: number | null,
50
+ paragraphSpacing?: number | null,
51
+ scroll?: boolean | null,
52
+ scrollPaddingTop?: number | null,
53
+ scrollPaddingBottom?: number | null,
54
+ // scrollPaddingLeft?: number | null,
55
+ // scrollPaddingRight?: number | null,
56
+ selectionBackgroundColor?: string | null,
57
+ selectionTextColor?: string | null,
58
+ textAlign?: TextAlignment | null,
59
+ textColor?: string | null,
60
+ textNormalization?: boolean | null,
61
+ theme?: Theme | null,
62
+ visitedColor?: string | null,
63
+ wordSpacing?: number | null
64
+ }
65
+
66
+ export class EpubPreferences implements ConfigurablePreferences {
67
+ backgroundColor?: string | null;
68
+ blendFilter?: boolean | null;
69
+ constraint?: number | null;
70
+ columnCount?: number | null;
71
+ darkenFilter?: boolean | number | null;
72
+ deprecatedFontSize?: boolean | null;
73
+ fontFamily?: string | null;
74
+ fontSize?: number | null;
75
+ fontSizeNormalize?: boolean | null;
76
+ fontOpticalSizing?: boolean | null;
77
+ fontWeight?: number | null;
78
+ fontWidth?: number | null;
79
+ hyphens?: boolean | null;
80
+ invertFilter?: boolean | number | null;
81
+ invertGaijiFilter?: boolean | number | null;
82
+ iOSPatch?: boolean | null;
83
+ iPadOSPatch?: boolean | null;
84
+ layoutStrategy?: LayoutStrategy | null;
85
+ letterSpacing?: number | null;
86
+ ligatures?: boolean | null;
87
+ lineHeight?: number | null;
88
+ linkColor?: string | null;
89
+ maximalLineLength?: number | null;
90
+ minimalLineLength?: number | null;
91
+ noRuby?: boolean | null;
92
+ optimalLineLength?: number | null;
93
+ pageGutter?: number | null;
94
+ paragraphIndent?: number | null;
95
+ paragraphSpacing?: number | null;
96
+ scroll?: boolean | null;
97
+ scrollPaddingTop?: number | null;
98
+ scrollPaddingBottom?: number | null;
99
+ // scrollPaddingLeft?: number | null;
100
+ // scrollPaddingRight?: number | null;
101
+ selectionBackgroundColor?: string | null;
102
+ selectionTextColor?: string | null;
103
+ textAlign?: TextAlignment | null;
104
+ textColor?: string | null;
105
+ textNormalization?: boolean | null;
106
+ theme?: Theme | null;
107
+ visitedColor?: string | null;
108
+ wordSpacing?: number | null;
109
+
110
+ constructor(preferences: IEpubPreferences = {}) {
111
+ this.backgroundColor = ensureString(preferences.backgroundColor);
112
+ this.blendFilter = ensureBoolean(preferences.blendFilter);
113
+ this.constraint = ensureNonNegative(preferences.constraint);
114
+ this.columnCount = ensureNonNegative(preferences.columnCount);
115
+ this.darkenFilter = ensureFilter(preferences.darkenFilter);
116
+ this.deprecatedFontSize = ensureBoolean(preferences.deprecatedFontSize);
117
+ this.fontFamily = ensureString(preferences.fontFamily);
118
+ this.fontSize = ensureValueInRange(preferences.fontSize, fontSizeRangeConfig.range);
119
+ this.fontSizeNormalize = ensureBoolean(preferences.fontSizeNormalize);
120
+ this.fontOpticalSizing = ensureBoolean(preferences.fontOpticalSizing);
121
+ this.fontWeight = ensureValueInRange(preferences.fontWeight, fontWeightRangeConfig.range);
122
+ this.fontWidth = ensureValueInRange(preferences.fontWidth,fontWidthRangeConfig.range);
123
+ this.hyphens = ensureBoolean(preferences.hyphens);
124
+ this.invertFilter = ensureFilter(preferences.invertFilter);
125
+ this.invertGaijiFilter = ensureFilter(preferences.invertGaijiFilter);
126
+ this.iOSPatch = ensureBoolean(preferences.iOSPatch);
127
+ this.iPadOSPatch = ensureBoolean(preferences.iPadOSPatch);
128
+ this.layoutStrategy = ensureEnumValue<LayoutStrategy>(preferences.layoutStrategy, LayoutStrategy);
129
+ this.letterSpacing = ensureNonNegative(preferences.letterSpacing);
130
+ this.ligatures = ensureBoolean(preferences.ligatures);
131
+ this.lineHeight = ensureNonNegative(preferences.lineHeight);
132
+ this.linkColor = ensureString(preferences.linkColor);
133
+ this.noRuby = ensureBoolean(preferences.noRuby);
134
+ this.pageGutter = ensureNonNegative(preferences.pageGutter);
135
+ this.paragraphIndent = ensureNonNegative(preferences.paragraphIndent);
136
+ this.paragraphSpacing = ensureNonNegative(preferences.paragraphSpacing);
137
+ this.scroll = ensureBoolean(preferences.scroll);
138
+ this.scrollPaddingTop = ensureNonNegative(preferences.scrollPaddingTop);
139
+ this.scrollPaddingBottom = ensureNonNegative(preferences.scrollPaddingBottom);
140
+ // this.scrollPaddingLeft = ensureNonNegative(preferences.scrollPaddingLeft);
141
+ // this.scrollPaddingRight = ensureNonNegative(preferences.scrollPaddingRight);
142
+ this.selectionBackgroundColor = ensureString(preferences.selectionBackgroundColor);
143
+ this.selectionTextColor = ensureString(preferences.selectionTextColor);
144
+ this.textAlign = ensureEnumValue<TextAlignment>(preferences.textAlign, TextAlignment);
145
+ this.textColor = ensureString(preferences.textColor);
146
+ this.textNormalization = ensureBoolean(preferences.textNormalization);
147
+ this.theme = ensureEnumValue<Theme>(preferences.theme, Theme);
148
+ this.visitedColor = ensureString(preferences.visitedColor);
149
+ this.wordSpacing = ensureNonNegative(preferences.wordSpacing);
150
+
151
+ this.optimalLineLength = ensureNonNegative(preferences.optimalLineLength);
152
+ this.maximalLineLength = ensureNonNegative(preferences.maximalLineLength);
153
+ this.minimalLineLength = ensureNonNegative(preferences.minimalLineLength);
154
+ }
155
+
156
+ static serialize(preferences: EpubPreferences): string {
157
+ const { ...properties } = preferences;
158
+ return JSON.stringify(properties);
159
+ }
160
+
161
+ static deserialize(preferences: string): EpubPreferences | null {
162
+ try {
163
+ const parsedPreferences = JSON.parse(preferences);
164
+ return new EpubPreferences(parsedPreferences);
165
+ } catch (error) {
166
+ console.error("Failed to deserialize preferences:", error);
167
+ return null;
168
+ }
169
+ }
170
+
171
+ merging(other: ConfigurablePreferences): ConfigurablePreferences {
172
+ const merged: IEpubPreferences = { ...this };
173
+ for (const key of Object.keys(other) as (keyof IEpubPreferences)[]) {
174
+ if (
175
+ other[key] !== undefined &&
176
+ (
177
+ key !== "maximalLineLength" ||
178
+ other[key] === null ||
179
+ (other[key] >= (other.optimalLineLength ?? merged.optimalLineLength ?? 65))
180
+ ) &&
181
+ (
182
+ key !== "minimalLineLength" ||
183
+ other[key] === null ||
184
+ (other[key] <= (other.optimalLineLength ?? merged.optimalLineLength ?? 65))
185
+ )
186
+ ) {
187
+ merged[key] = other[key];
188
+ }
189
+ }
190
+ return new EpubPreferences(merged);
191
+ }
192
+ }