@readium/navigator 2.2.0 → 2.2.2

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 (48) hide show
  1. package/dist/index.js +1912 -1330
  2. package/dist/index.umd.cjs +218 -17
  3. package/package.json +1 -1
  4. package/src/css/Properties.ts +47 -0
  5. package/src/css/index.ts +1 -0
  6. package/src/epub/css/Properties.ts +10 -48
  7. package/src/epub/preferences/EpubDefaults.ts +1 -1
  8. package/src/epub/preferences/EpubPreferences.ts +1 -1
  9. package/src/epub/preferences/EpubPreferencesEditor.ts +30 -23
  10. package/src/index.ts +2 -1
  11. package/src/preferences/Types.ts +40 -0
  12. package/src/{epub/preferences → preferences}/guards.ts +5 -6
  13. package/src/preferences/index.ts +2 -1
  14. package/src/webpub/WebPubBlobBuilder.ts +26 -4
  15. package/src/webpub/WebPubFrameManager.ts +16 -0
  16. package/src/webpub/WebPubFramePoolManager.ts +49 -2
  17. package/src/webpub/WebPubNavigator.ts +103 -11
  18. package/src/webpub/css/Properties.ts +71 -0
  19. package/src/webpub/css/WebPubCSS.ts +42 -0
  20. package/src/webpub/css/WebPubStylesheet.ts +204 -0
  21. package/src/webpub/css/index.ts +3 -0
  22. package/src/webpub/index.ts +3 -1
  23. package/src/webpub/preferences/WebPubDefaults.ts +61 -0
  24. package/src/webpub/preferences/WebPubPreferences.ts +88 -0
  25. package/src/webpub/preferences/WebPubPreferencesEditor.ts +199 -0
  26. package/src/webpub/preferences/WebPubSettings.ts +90 -0
  27. package/src/webpub/preferences/index.ts +4 -0
  28. package/types/src/css/Properties.d.ts +20 -0
  29. package/types/src/css/index.d.ts +1 -0
  30. package/types/src/epub/css/Properties.d.ts +1 -21
  31. package/types/src/index.d.ts +1 -0
  32. package/types/src/preferences/Types.d.ts +8 -0
  33. package/types/src/preferences/guards.d.ts +9 -0
  34. package/types/src/preferences/index.d.ts +1 -0
  35. package/types/src/webpub/WebPubBlobBuilder.d.ts +5 -1
  36. package/types/src/webpub/WebPubFrameManager.d.ts +4 -0
  37. package/types/src/webpub/WebPubFramePoolManager.d.ts +8 -1
  38. package/types/src/webpub/WebPubNavigator.d.ts +30 -3
  39. package/types/src/webpub/css/Properties.d.ts +36 -0
  40. package/types/src/webpub/css/WebPubCSS.d.ts +10 -0
  41. package/types/src/webpub/css/WebPubStylesheet.d.ts +1 -0
  42. package/types/src/webpub/css/index.d.ts +3 -0
  43. package/types/src/webpub/index.d.ts +2 -0
  44. package/types/src/webpub/preferences/WebPubDefaults.d.ts +32 -0
  45. package/types/src/webpub/preferences/WebPubPreferences.d.ts +36 -0
  46. package/types/src/webpub/preferences/WebPubPreferencesEditor.d.ts +28 -0
  47. package/types/src/webpub/preferences/WebPubSettings.d.ts +35 -0
  48. package/types/src/webpub/preferences/index.d.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "type": "module",
5
5
  "description": "Next generation SDK for publications in Web Apps",
6
6
  "author": "readium",
@@ -0,0 +1,47 @@
1
+ export type BodyHyphens = "auto" | "none";
2
+ export type BoxSizing = "content-box" | "border-box";
3
+ export type FontOpticalSizing = "auto" | "none";
4
+ export type FontWidth = "ultra-condensed" | "extra-condensed" | "condensed" | "semi-condensed" | "normal" | "semi-expanded" | "expanded" | "extra-expanded" | "ultra-expanded" | number;
5
+ export type Ligatures = "common-ligatures" | "none";
6
+ export type TypeScale = 1 | 1.067 | 1.125 | 1.2 | 1.25 | 1.333 | 1.414 | 1.5 | 1.618;
7
+ export type View = "paged" | "scroll";
8
+
9
+ export abstract class Properties {
10
+ constructor() {}
11
+
12
+ protected toFlag(name: string) {
13
+ return `readium-${ name }-on`;
14
+ }
15
+
16
+ protected toUnitless(value: number) {
17
+ return value.toString();
18
+ }
19
+
20
+ protected toPercentage(value: number, ratio: boolean = false) {
21
+ if (ratio || value > 0 && value <= 1) {
22
+ return `${ Math.round(value * 100) }%`;
23
+ } else {
24
+ return `${ value }%`;
25
+ }
26
+ }
27
+
28
+ protected toVw(value: number) {
29
+ const percentage = Math.round(value * 100);
30
+ return `${ Math.min(percentage, 100) }vw`;
31
+ }
32
+
33
+ protected toVh(value: number) {
34
+ const percentage = Math.round(value * 100);
35
+ return `${ Math.min(percentage, 100) }vh`;
36
+ }
37
+
38
+ protected toPx(value: number) {
39
+ return `${ value }px`;
40
+ }
41
+
42
+ protected toRem(value: number) {
43
+ return `${ value }rem`;
44
+ }
45
+
46
+ abstract toCSSProperties(): { [key: string]: string };
47
+ }
@@ -0,0 +1 @@
1
+ export * from "./Properties";
@@ -1,52 +1,14 @@
1
1
  import { TextAlignment } 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
- }
2
+ import {
3
+ BodyHyphens,
4
+ BoxSizing,
5
+ FontOpticalSizing,
6
+ FontWidth,
7
+ Ligatures,
8
+ Properties,
9
+ TypeScale,
10
+ View
11
+ } from "../../css/Properties";
50
12
 
51
13
  export interface IUserProperties {
52
14
  advancedSettings?: boolean | null;
@@ -15,7 +15,7 @@ import {
15
15
  ensureString,
16
16
  ensureValueInRange,
17
17
  withFallback
18
- } from "./guards";
18
+ } from "../../preferences/guards";
19
19
 
20
20
  import { sMLWithRequest } from "../../helpers";
21
21
 
@@ -14,7 +14,7 @@ import {
14
14
  ensureNonNegative,
15
15
  ensureString,
16
16
  ensureValueInRange
17
- } from "./guards";
17
+ } from "../../preferences/guards";
18
18
 
19
19
  export interface IEpubPreferences {
20
20
  backgroundColor?: string | null,
@@ -5,9 +5,16 @@ import { EpubSettings } from "./EpubSettings";
5
5
  import { BooleanPreference, EnumPreference, Preference, RangePreference } from "../../preferences/Preference";
6
6
  import {
7
7
  TextAlignment,
8
+ filterRangeConfig,
8
9
  fontSizeRangeConfig,
9
10
  fontWeightRangeConfig,
10
- fontWidthRangeConfig
11
+ fontWidthRangeConfig,
12
+ letterSpacingRangeConfig,
13
+ lineHeightRangeConfig,
14
+ lineLengthRangeConfig,
15
+ paragraphIndentRangeConfig,
16
+ paragraphSpacingRangeConfig,
17
+ wordSpacingRangeConfig
11
18
  } from "../../preferences/Types";
12
19
 
13
20
  import defaultColors from "@readium/css/css/vars/colors.json";
@@ -85,8 +92,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
85
92
  onChange: (newValue: number | boolean | null | undefined) => {
86
93
  this.updatePreference("darkenFilter", newValue || null);
87
94
  },
88
- supportedRange: [0, 100],
89
- step: 1
95
+ supportedRange: filterRangeConfig.range,
96
+ step: filterRangeConfig.step
90
97
  });
91
98
  }
92
99
 
@@ -192,8 +199,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
192
199
  onChange: (newValue: number | boolean | null | undefined) => {
193
200
  this.updatePreference("invertFilter", newValue || null);
194
201
  },
195
- supportedRange: [0, 100],
196
- step: 1
202
+ supportedRange: filterRangeConfig.range,
203
+ step: filterRangeConfig.step
197
204
  });
198
205
  }
199
206
 
@@ -205,8 +212,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
205
212
  onChange: (newValue: number | boolean | null | undefined) => {
206
213
  this.updatePreference("invertGaijiFilter", newValue || null);
207
214
  },
208
- supportedRange: [0, 100],
209
- step: 1
215
+ supportedRange: filterRangeConfig.range,
216
+ step: filterRangeConfig.step
210
217
  });
211
218
  }
212
219
 
@@ -240,8 +247,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
240
247
  onChange: (newValue: number | null | undefined) => {
241
248
  this.updatePreference("letterSpacing", newValue || null);
242
249
  },
243
- supportedRange: [0, 1],
244
- step: .125
250
+ supportedRange: letterSpacingRangeConfig.range,
251
+ step: letterSpacingRangeConfig.step
245
252
  });
246
253
  }
247
254
 
@@ -266,8 +273,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
266
273
  onChange: (newValue: number | null | undefined) => {
267
274
  this.updatePreference("lineHeight", newValue || null);
268
275
  },
269
- supportedRange: [1, 2],
270
- step: .1
276
+ supportedRange: lineHeightRangeConfig.range,
277
+ step: lineHeightRangeConfig.step
271
278
  });
272
279
  }
273
280
 
@@ -290,8 +297,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
290
297
  onChange: (newValue: number | null | undefined) => {
291
298
  this.updatePreference("maximalLineLength", newValue);
292
299
  },
293
- supportedRange: [20, 100],
294
- step: 1
300
+ supportedRange: lineLengthRangeConfig.range,
301
+ step: lineLengthRangeConfig.step
295
302
  });
296
303
  }
297
304
 
@@ -303,8 +310,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
303
310
  onChange: (newValue: number | null | undefined) => {
304
311
  this.updatePreference("minimalLineLength", newValue);
305
312
  },
306
- supportedRange: [20, 100],
307
- step: 1
313
+ supportedRange: lineLengthRangeConfig.range,
314
+ step: lineLengthRangeConfig.step
308
315
  });
309
316
  }
310
317
 
@@ -327,8 +334,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
327
334
  onChange: (newValue: number | null | undefined) => {
328
335
  this.updatePreference("optimalLineLength", newValue as number);
329
336
  },
330
- supportedRange: [20, 100],
331
- step: 1
337
+ supportedRange: lineLengthRangeConfig.range,
338
+ step: lineLengthRangeConfig.step
332
339
  });
333
340
  }
334
341
 
@@ -351,8 +358,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
351
358
  onChange: (newValue: number | null | undefined) => {
352
359
  this.updatePreference("paragraphIndent", newValue || null);
353
360
  },
354
- supportedRange: [0, 3],
355
- step: .25
361
+ supportedRange: paragraphIndentRangeConfig.range,
362
+ step: paragraphIndentRangeConfig.step
356
363
  });
357
364
  }
358
365
 
@@ -364,8 +371,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
364
371
  onChange: (newValue: number | null | undefined) => {
365
372
  this.updatePreference("paragraphSpacing", newValue || null);
366
373
  },
367
- supportedRange: [0, 3],
368
- step: .25
374
+ supportedRange: paragraphSpacingRangeConfig.range,
375
+ step: paragraphSpacingRangeConfig.step
369
376
  });
370
377
  }
371
378
 
@@ -501,8 +508,8 @@ export class EpubPreferencesEditor implements IPreferencesEditor {
501
508
  onChange: (newValue: number | null | undefined) => {
502
509
  this.updatePreference("wordSpacing", newValue || null);
503
510
  },
504
- supportedRange: [0, 2],
505
- step: 0.125
511
+ supportedRange: wordSpacingRangeConfig.range,
512
+ step: wordSpacingRangeConfig.step
506
513
  });
507
514
  }
508
515
  }
package/src/index.ts CHANGED
@@ -3,4 +3,5 @@ export * from './webpub';
3
3
  export * from './epub';
4
4
  export * from './audio';
5
5
  export * from './helpers';
6
- export * from './preferences';
6
+ export * from './preferences';
7
+ export * from './css';
@@ -10,6 +10,11 @@ export type RangeConfig = {
10
10
  step: number
11
11
  }
12
12
 
13
+ export const filterRangeConfig: RangeConfig = {
14
+ range: [0, 100],
15
+ step: 1
16
+ }
17
+
13
18
  export const fontSizeRangeConfig: RangeConfig = {
14
19
  range: [0.7, 4],
15
20
  step: 0.05
@@ -23,4 +28,39 @@ export const fontWeightRangeConfig: RangeConfig = {
23
28
  export const fontWidthRangeConfig: RangeConfig = {
24
29
  range: [50, 250],
25
30
  step: 10
31
+ }
32
+
33
+ export const letterSpacingRangeConfig: RangeConfig = {
34
+ range: [0, 1],
35
+ step: .125
36
+ }
37
+
38
+ export const lineHeightRangeConfig: RangeConfig = {
39
+ range: [1, 2],
40
+ step: .1
41
+ }
42
+
43
+ export const lineLengthRangeConfig: RangeConfig = {
44
+ range: [20, 100],
45
+ step: 1
46
+ }
47
+
48
+ export const paragraphIndentRangeConfig: RangeConfig = {
49
+ range: [0, 3],
50
+ step: .25
51
+ }
52
+
53
+ export const paragraphSpacingRangeConfig: RangeConfig = {
54
+ range: [0, 3],
55
+ step: .25
56
+ }
57
+
58
+ export const wordSpacingRangeConfig: RangeConfig = {
59
+ range: [0, 2],
60
+ step: .125
61
+ }
62
+
63
+ export const zoomRangeConfig: RangeConfig = {
64
+ range: [0.7, 4],
65
+ step: 0.05
26
66
  }
@@ -29,14 +29,13 @@ export function ensureString(value: string | null | undefined): string | null |
29
29
  }
30
30
 
31
31
  export function ensureBoolean(value: boolean | null | undefined): boolean | null | undefined {
32
- return typeof value === "boolean"
33
- ? value
34
- : value === undefined || value === null
35
- ? value
32
+ return typeof value === "boolean"
33
+ ? value
34
+ : value === undefined || value === null
35
+ ? value
36
36
  : undefined;
37
37
  }
38
38
 
39
-
40
39
  export function ensureEnumValue<T extends string>(value: T | null | undefined, enumType: Record<T, string>): T | null | undefined {
41
40
  if (value === undefined) {
42
41
  return undefined;
@@ -83,4 +82,4 @@ export function ensureValueInRange(value: number | null | undefined, range: [num
83
82
 
84
83
  export function withFallback<T>(value: T | null | undefined, defaultValue: T | null): T | null {
85
84
  return value === undefined ? defaultValue : value;
86
- }
85
+ }
@@ -1,4 +1,5 @@
1
1
  export * from "./Configurable";
2
2
  export * from "./Preference";
3
3
  export * from "./PreferencesEditor";
4
- export * from "./Types";
4
+ export * from "./Types";
5
+ export * from "./guards";
@@ -1,4 +1,5 @@
1
1
  import { Link, Publication } from "@readium/shared";
2
+ import { webPubStylesheet } from "./css/WebPubStylesheet";
2
3
 
3
4
  // Utilities (matching FrameBlobBuilder pattern)
4
5
  const blobify = (source: string, type: string) => URL.createObjectURL(new Blob([source], { type }));
@@ -9,6 +10,12 @@ const scriptify = (doc: Document, source: string) => {
9
10
  s.src = source.startsWith("blob:") ? source : blobify(source, "text/javascript");
10
11
  return s;
11
12
  }
13
+ const styleify = (doc: Document, source: string) => {
14
+ const s = doc.createElement("style");
15
+ s.dataset.readium = "true";
16
+ s.textContent = source;
17
+ return s;
18
+ }
12
19
 
13
20
  type CacheFunction = () => string;
14
21
  const resourceBlobCache = new Map<string, string>();
@@ -46,11 +53,13 @@ export class WebPubBlobBuilder {
46
53
  private readonly item: Link;
47
54
  private readonly burl: string;
48
55
  private readonly pub: Publication;
56
+ private readonly cssProperties?: { [key: string]: string };
49
57
 
50
- constructor(pub: Publication, baseURL: string, item: Link) {
58
+ constructor(pub: Publication, baseURL: string, item: Link, cssProperties?: { [key: string]: string }) {
51
59
  this.pub = pub;
52
60
  this.item = item;
53
61
  this.burl = item.toURL(baseURL) || "";
62
+ this.cssProperties = cssProperties;
54
63
  }
55
64
 
56
65
  public async build(): Promise<string> {
@@ -74,7 +83,7 @@ export class WebPubBlobBuilder {
74
83
  const details = perror.querySelector("div");
75
84
  throw new Error(`Failed parsing item ${this.item.href}: ${details?.textContent || perror.textContent}`);
76
85
  }
77
- return this.finalizeDOM(doc, this.burl, this.item.mediaType, txt);
86
+ return this.finalizeDOM(doc, this.burl, this.item.mediaType, txt, this.cssProperties);
78
87
  }
79
88
 
80
89
  private hasExecutable(doc: Document): boolean {
@@ -84,9 +93,23 @@ export class WebPubBlobBuilder {
84
93
  );
85
94
  }
86
95
 
87
- private finalizeDOM(doc: Document, base: string | undefined, mediaType: any, txt?: string): string {
96
+ private setProperties(cssProperties: { [key: string]: string }, doc: Document) {
97
+ for (const key in cssProperties) {
98
+ const value = cssProperties[key];
99
+ if (value) doc.documentElement.style.setProperty(key, value);
100
+ }
101
+ }
102
+
103
+ private finalizeDOM(doc: Document, base: string | undefined, mediaType: any, txt?: string, cssProperties?: { [key: string]: string }): string {
88
104
  if(!doc) return "";
89
105
 
106
+ // Add WebPubCSS stylesheet at end of head (like EPUB ReadiumCSS-after)
107
+ const webPubStyle = styleify(doc, webPubStylesheet);
108
+ doc.head.appendChild(webPubStyle);
109
+ if (cssProperties) {
110
+ this.setProperties(cssProperties, doc);
111
+ }
112
+
90
113
  doc.body.querySelectorAll("img").forEach((img) => {
91
114
  img.setAttribute("fetchpriority", "high");
92
115
  });
@@ -98,7 +121,6 @@ export class WebPubBlobBuilder {
98
121
  doc.head.firstChild!.before(b);
99
122
  }
100
123
 
101
-
102
124
  const hasExecutable = this.hasExecutable(doc);
103
125
  if(hasExecutable) doc.head.firstChild!.before(rBefore(doc));
104
126
  doc.head.firstChild!.before(cssSelectorGenerator(doc));
@@ -8,6 +8,7 @@ export class WebPubFrameManager {
8
8
  private loader: Loader | undefined;
9
9
  public readonly source: string;
10
10
  private comms: FrameComms | undefined;
11
+ private hidden: boolean = true;
11
12
  private destroyed: boolean = false;
12
13
 
13
14
  private currModules: ModuleName[] = [];
@@ -69,6 +70,7 @@ export class WebPubFrameManager {
69
70
  this.frame.style.setProperty("aria-hidden", "true");
70
71
  this.frame.style.opacity = "0";
71
72
  this.frame.style.pointerEvents = "none";
73
+ this.hidden = true;
72
74
 
73
75
  if(this.frame.parentElement) {
74
76
  if(this.comms === undefined || !this.comms.ready) return;
@@ -97,6 +99,7 @@ export class WebPubFrameManager {
97
99
  this.frame.style.removeProperty("aria-hidden");
98
100
  this.frame.style.removeProperty("opacity");
99
101
  this.frame.style.removeProperty("pointer-events");
102
+ this.hidden = false;
100
103
 
101
104
  if (sML.UA.WebKit) {
102
105
  this.comms?.send("force_webkit_recalc", undefined);
@@ -115,6 +118,19 @@ export class WebPubFrameManager {
115
118
  });
116
119
  }
117
120
 
121
+ setCSSProperties(properties: { [key: string]: string }) {
122
+ if(this.destroyed || !this.frame.contentWindow) return;
123
+
124
+ // We need to resume and halt postMessage to update the properties
125
+ // if the frame is hidden since it's been halted in hide()
126
+ if (this.hidden) {
127
+ if (this.comms) this.comms?.resume();
128
+ else this.comms = new FrameComms(this.frame.contentWindow!, this.source);
129
+ }
130
+ this.comms?.send("update_properties", properties);
131
+ if (this.hidden) this.comms?.halt();
132
+ }
133
+
118
134
  get iframe() {
119
135
  if(this.destroyed) throw Error("Trying to use frame when it doesn't exist");
120
136
  return this.frame;
@@ -6,13 +6,16 @@ import { WebPubFrameManager } from "./WebPubFrameManager";
6
6
  export class WebPubFramePoolManager {
7
7
  private readonly container: HTMLElement;
8
8
  private _currentFrame: WebPubFrameManager | undefined;
9
+ private currentCssProperties: { [key: string]: string } | undefined;
9
10
  private readonly pool: Map<string, WebPubFrameManager> = new Map();
10
11
  private readonly blobs: Map<string, string> = new Map();
11
12
  private readonly inprogress: Map<string, Promise<void>> = new Map();
13
+ private pendingUpdates: Map<string, { inPool: boolean }> = new Map();
12
14
  private currentBaseURL: string | undefined;
13
15
 
14
- constructor(container: HTMLElement) {
16
+ constructor(container: HTMLElement, cssProperties?: { [key: string]: string }) {
15
17
  this.container = container;
18
+ this.currentCssProperties = cssProperties;
16
19
  }
17
20
 
18
21
  async destroy() {
@@ -90,11 +93,22 @@ export class WebPubFramePoolManager {
90
93
  this.currentBaseURL = pub.baseURL;
91
94
 
92
95
  const creator = async (href: string) => {
96
+ // Check if blob needs to be recreated due to CSS property changes
97
+ if(this.pendingUpdates.has(href) && this.pendingUpdates.get(href)?.inPool === false) {
98
+ const url = this.blobs.get(href);
99
+ if(url) {
100
+ URL.revokeObjectURL(url);
101
+ this.blobs.delete(href);
102
+ this.pendingUpdates.delete(href);
103
+ }
104
+ }
105
+
93
106
  if(this.pool.has(href)) {
94
107
  const fm = this.pool.get(href)!;
95
108
  if(!this.blobs.has(href)) {
96
109
  await fm.destroy();
97
110
  this.pool.delete(href);
111
+ this.pendingUpdates.delete(href);
98
112
  } else {
99
113
  await fm.load(modules);
100
114
  return;
@@ -103,7 +117,7 @@ export class WebPubFramePoolManager {
103
117
  const itm = pub.readingOrder.findWithHref(href);
104
118
  if(!itm) return;
105
119
  if(!this.blobs.has(href)) {
106
- const blobBuilder = new WebPubBlobBuilder(pub, this.currentBaseURL || "", itm);
120
+ const blobBuilder = new WebPubBlobBuilder(pub, this.currentBaseURL || "", itm, this.currentCssProperties);
107
121
  const blobURL = await blobBuilder.build();
108
122
  this.blobs.set(href, blobURL);
109
123
  }
@@ -139,6 +153,39 @@ export class WebPubFramePoolManager {
139
153
  this.inprogress.delete(newHref);
140
154
  }
141
155
 
156
+ setCSSProperties(properties: { [key: string]: string }) {
157
+ const deepCompare = (obj1: { [key: string]: string }, obj2: { [key: string]: string }) => {
158
+ const keys1 = Object.keys(obj1);
159
+ const keys2 = Object.keys(obj2);
160
+
161
+ if (keys1.length !== keys2.length) {
162
+ return false;
163
+ }
164
+
165
+ for (const key of keys1) {
166
+ if (obj1[key] !== obj2[key]) {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ return true;
172
+ };
173
+
174
+ // If CSSProperties have changed, we update the currentCssProperties,
175
+ // and set the CSS Properties to all frames already in the pool
176
+ // We also need to invalidate the blobs and recreate them with the new properties.
177
+ // We do that in update, by updating them when needed (they are added into the pool)
178
+ // so that we do not invalidate and recreate blobs over and over again.
179
+ if(!deepCompare(this.currentCssProperties || {}, properties)) {
180
+ this.currentCssProperties = properties;
181
+ this.pool.forEach((frame) => {
182
+ frame.setCSSProperties(properties);
183
+ });
184
+ for (const href of this.blobs.keys()) {
185
+ this.pendingUpdates.set(href, { inPool: this.pool.has(href) });
186
+ }
187
+ }
188
+ }
142
189
  get currentFrames(): (WebPubFrameManager | undefined)[] {
143
190
  return [this._currentFrame];
144
191
  }