@readium/navigator 2.2.7 → 2.2.8

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 (46) hide show
  1. package/dist/ar-DyHX_uy2-DyHX_uy2-DyHX_uy2.js +7 -0
  2. package/dist/ar-DyHX_uy2-DyHX_uy2.js +7 -0
  3. package/dist/da-Dct0PS3E-Dct0PS3E-Dct0PS3E.js +7 -0
  4. package/dist/da-Dct0PS3E-Dct0PS3E.js +7 -0
  5. package/dist/fr-C5HEel98-C5HEel98-C5HEel98.js +7 -0
  6. package/dist/fr-C5HEel98-C5HEel98.js +7 -0
  7. package/dist/index.js +4330 -2342
  8. package/dist/index.umd.cjs +1571 -39
  9. package/dist/it-DFOBoXGy-DFOBoXGy-DFOBoXGy.js +7 -0
  10. package/dist/it-DFOBoXGy-DFOBoXGy.js +7 -0
  11. package/dist/pt_PT-Di3sVjze-Di3sVjze-Di3sVjze.js +7 -0
  12. package/dist/pt_PT-Di3sVjze-Di3sVjze.js +7 -0
  13. package/dist/sv-BfzAFsVN-BfzAFsVN-BfzAFsVN.js +7 -0
  14. package/dist/sv-BfzAFsVN-BfzAFsVN.js +7 -0
  15. package/package.json +1 -1
  16. package/src/dom/_readium_executionCleanup.js +13 -0
  17. package/src/dom/_readium_executionPrevention.js +65 -0
  18. package/src/dom/_readium_webpubExecution.js +4 -0
  19. package/src/epub/EpubNavigator.ts +26 -2
  20. package/src/epub/frame/FrameBlobBuilder.ts +37 -131
  21. package/src/epub/frame/FramePoolManager.ts +34 -5
  22. package/src/epub/fxl/FXLFramePoolManager.ts +20 -2
  23. package/src/helpers/minify.ts +14 -0
  24. package/src/index.ts +2 -1
  25. package/src/injection/Injectable.ts +85 -0
  26. package/src/injection/Injector.ts +356 -0
  27. package/src/injection/epubInjectables.ts +90 -0
  28. package/src/injection/index.ts +2 -0
  29. package/src/injection/webpubInjectables.ts +59 -0
  30. package/src/webpub/WebPubBlobBuilder.ts +19 -80
  31. package/src/webpub/WebPubFramePoolManager.ts +29 -4
  32. package/src/webpub/WebPubNavigator.ts +15 -1
  33. package/types/src/epub/EpubNavigator.d.ts +3 -0
  34. package/types/src/epub/frame/FrameBlobBuilder.d.ts +7 -4
  35. package/types/src/epub/frame/FramePoolManager.d.ts +3 -1
  36. package/types/src/epub/fxl/FXLFramePoolManager.d.ts +3 -1
  37. package/types/src/helpers/minify.d.ts +12 -0
  38. package/types/src/index.d.ts +1 -0
  39. package/types/src/injection/Injectable.d.ts +68 -0
  40. package/types/src/injection/Injector.d.ts +22 -0
  41. package/types/src/injection/epubInjectables.d.ts +6 -0
  42. package/types/src/injection/index.d.ts +2 -0
  43. package/types/src/injection/webpubInjectables.d.ts +5 -0
  44. package/types/src/webpub/WebPubBlobBuilder.d.ts +7 -3
  45. package/types/src/webpub/WebPubFramePoolManager.d.ts +3 -1
  46. package/types/src/webpub/WebPubNavigator.d.ts +3 -0
@@ -0,0 +1,90 @@
1
+ import { IInjectableRule, IInjectable } from "../injection/Injectable";
2
+ import { stripJS, stripCSS } from "../helpers/minify";
3
+ import { Metadata, Layout } from "@readium/shared";
4
+
5
+ import readiumCSSAfter from "@readium/css/css/dist/ReadiumCSS-after.css?raw";
6
+ import readiumCSSBefore from "@readium/css/css/dist/ReadiumCSS-before.css?raw";
7
+ import readiumCSSDefault from "@readium/css/css/dist/ReadiumCSS-default.css?raw";
8
+
9
+ import cssSelectorGeneratorContent from "../dom/_readium_cssSelectorGenerator.js?raw";
10
+ import executionPreventionContent from "../dom/_readium_executionPrevention.js?raw";
11
+ import onloadProxyContent from "../dom/_readium_executionCleanup.js?raw";
12
+
13
+ /**
14
+ * Creates injectable rules for EPUB content documents
15
+ */
16
+ export function createReadiumEpubRules(metadata: Metadata): IInjectableRule[] {
17
+ const isFixedLayout = metadata.effectiveLayout === Layout.fixed;
18
+
19
+ // Core injectables that should be prepended
20
+ const prependInjectables: IInjectable[] = [
21
+ // CSS Selector Generator - always injected
22
+ {
23
+ id: "css-selector-generator",
24
+ as: "script",
25
+ target: "head",
26
+ blob: new Blob([stripJS(cssSelectorGeneratorContent)], { type: "text/javascript" })
27
+ },
28
+ // Execution Prevention - conditional (has executable scripts)
29
+ {
30
+ id: "execution-prevention",
31
+ as: "script",
32
+ target: "head",
33
+ blob: new Blob([stripJS(executionPreventionContent)], { type: "text/javascript" }),
34
+ condition: (doc: Document) => !!(doc.querySelector("script") || doc.querySelector("body[onload]:not(body[onload=''])"))
35
+ }
36
+ ];
37
+
38
+ // Core injectables that should be appended
39
+ const appendInjectables: IInjectable[] = [
40
+ // Onload Proxy - conditional (has executable scripts)
41
+ {
42
+ id: "onload-proxy",
43
+ as: "script",
44
+ target: "head",
45
+ blob: new Blob([stripJS(onloadProxyContent)], { type: "text/javascript" }),
46
+ condition: (doc: Document) => !!(doc.querySelector("script") || doc.querySelector("body[onload]:not(body[onload=''])"))
47
+ }
48
+ ];
49
+
50
+ // Only add Readium CSS for reflowable documents
51
+ if (!isFixedLayout) {
52
+ // Readium CSS Before - prepended for reflowable
53
+ prependInjectables.unshift({
54
+ id: "readium-css-before",
55
+ as: "link",
56
+ target: "head",
57
+ blob: new Blob([stripCSS(readiumCSSBefore)], { type: "text/css" }),
58
+ rel: "stylesheet"
59
+ });
60
+
61
+ // Readium CSS Default and After - appended for reflowable
62
+ appendInjectables.unshift(
63
+ // Readium CSS Default - only for reflowable AND no existing styles
64
+ {
65
+ id: "readium-css-default",
66
+ as: "link",
67
+ target: "head",
68
+ blob: new Blob([stripCSS(readiumCSSDefault)], { type: "text/css" }),
69
+ rel: "stylesheet",
70
+ condition: (doc: Document) => !(doc.querySelector("link[rel='stylesheet']") || doc.querySelector("style") || doc.querySelector("[style]:not([style=''])"))
71
+ },
72
+ // Readium CSS After - only for reflowable
73
+ {
74
+ id: "readium-css-after",
75
+ as: "link",
76
+ target: "head",
77
+ blob: new Blob([stripCSS(readiumCSSAfter)], { type: "text/css" }),
78
+ rel: "stylesheet"
79
+ }
80
+ );
81
+ }
82
+
83
+ return [
84
+ {
85
+ resources: [/\.xhtml$/, /\.html$/],
86
+ prepend: prependInjectables,
87
+ append: appendInjectables
88
+ }
89
+ ];
90
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Injectable";
2
+ export * from "./Injector";
@@ -0,0 +1,59 @@
1
+ import { IInjectableRule, IInjectable } from "../injection/Injectable";
2
+ import { stripJS, stripCSS } from "../helpers/minify";
3
+
4
+ import readiumCSSWebPub from "@readium/css/css/dist/webPub/ReadiumCSS-webPub.css?raw";
5
+
6
+ import cssSelectorGeneratorContent from "../dom/_readium_cssSelectorGenerator.js?raw";
7
+ import webpubExecutionContent from "../dom/_readium_webpubExecution.js?raw";
8
+ import onloadProxyContent from "../dom/_readium_executionCleanup.js?raw";
9
+
10
+ /**
11
+ * Creates injectable rules for WebPub content documents
12
+ */
13
+ export function createReadiumWebPubRules(): IInjectableRule[] {
14
+ // Core injectables that should be prepended
15
+ const prependInjectables: IInjectable[] = [
16
+ // CSS Selector Generator - always injected
17
+ {
18
+ id: "css-selector-generator",
19
+ as: "script",
20
+ target: "head",
21
+ blob: new Blob([stripJS(cssSelectorGeneratorContent)], { type: "text/javascript" })
22
+ },
23
+ // WebPub Execution - always injected (sets up event blocking to false)
24
+ {
25
+ id: "webpub-execution",
26
+ as: "script",
27
+ target: "head",
28
+ blob: new Blob([stripJS(webpubExecutionContent)], { type: "text/javascript" })
29
+ }
30
+ ];
31
+
32
+ // Core injectables that should be appended
33
+ const appendInjectables: IInjectable[] = [
34
+ // Onload Proxy - conditional (has executable scripts)
35
+ {
36
+ id: "onload-proxy",
37
+ as: "script",
38
+ target: "head",
39
+ blob: new Blob([stripJS(onloadProxyContent)], { type: "text/javascript" }),
40
+ condition: (doc: Document) => !!(doc.querySelector("script") || doc.querySelector("body[onload]:not(body[onload=''])"))
41
+ },
42
+ // Readium CSS WebPub - always injected
43
+ {
44
+ id: "readium-css-webpub",
45
+ as: "link",
46
+ target: "head",
47
+ blob: new Blob([stripCSS(readiumCSSWebPub)], { type: "text/css" }),
48
+ rel: "stylesheet"
49
+ }
50
+ ];
51
+
52
+ return [
53
+ {
54
+ resources: [/\.xhtml$/, /\.html$/],
55
+ prepend: prependInjectables,
56
+ append: appendInjectables
57
+ }
58
+ ];
59
+ }
@@ -1,78 +1,27 @@
1
1
  import { Link, Publication } from "@readium/shared";
2
-
3
- // Readium CSS imports
4
- // The "?inline" query is to prevent some bundlers from injecting these into the page (e.g. vite)
5
- // @ts-ignore
6
- import readiumCSSWebPub from "@readium/css/css/dist/webPub/ReadiumCSS-webPub.css?inline";
7
-
8
- // Import the pre-built CSS selector generator
9
- // This has to be injected because you need to be in the iframe's context for it to work properly
10
- import cssSelectorGeneratorContent from "../dom/_readium_cssSelectorGenerator.js?raw";
11
-
12
- // Utilities
13
- const blobify = (source: string, type: string) => URL.createObjectURL(new Blob([source], { type }));
14
- const stripJS = (source: string) => source.replace(/\/\/.*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\n/g, "").replace(/\s+/g, " ");
15
- const stripCSS = (source: string) => source.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '').replace(/ {2,}/g, ' ')
16
- // Fully resolve absolute local URLs created by bundlers since it's going into a blob
17
- .replace(/url\((?!(https?:)?\/\/)("?)\/([^\)]+)/g, `url($2${window.location.origin}/$3`);
18
- const scriptify = (doc: Document, source: string) => {
19
- const s = doc.createElement("script");
20
- s.dataset.readium = "true";
21
- s.src = source.startsWith("blob:") ? source : blobify(source, "text/javascript");
22
- return s;
23
- }
24
- const styleify = (doc: Document, source: string) => {
25
- const s = doc.createElement("link");
26
- s.dataset.readium = "true";
27
- s.rel = "stylesheet";
28
- s.type = "text/css";
29
- s.href = source.startsWith("blob:") ? source : blobify(source, "text/css");
30
- return s;
31
- }
32
-
33
- type CacheFunction = () => string;
34
- const resourceBlobCache = new Map<string, string>();
35
- const cached = (key: string, cacher: CacheFunction) => {
36
- if(resourceBlobCache.has(key)) return resourceBlobCache.get(key)!;
37
- const value = cacher();
38
- resourceBlobCache.set(key, value);
39
- return value;
40
- };
41
-
42
- const cssSelectorGenerator = (doc: Document) => scriptify(doc, cached("css-selector-generator", () => blobify(
43
- cssSelectorGeneratorContent,
44
- "text/javascript"
45
- )));
46
-
47
- const readiumPropertiesScript = `
48
- window._readium_blockedEvents = [];
49
- window._readium_blockEvents = false; // WebPub doesn't need event blocking
50
- window._readium_eventBlocker = null;
51
- `;
52
-
53
- const rBefore = (doc: Document) => scriptify(doc, cached("webpub-js-before", () => blobify(stripJS(readiumPropertiesScript), "text/javascript")));
54
- const rAfter = (doc: Document) => scriptify(doc, cached("webpub-js-after", () => blobify(stripJS(`
55
- if(window.onload) window.onload = new Proxy(window.onload, {
56
- apply: function(target, receiver, args) {
57
- if(!window._readium_blockEvents) {
58
- Reflect.apply(target, receiver, args);
59
- return;
60
- }
61
- _readium_blockedEvents.push([0, target, receiver, args]);
62
- }
63
- });`), "text/javascript")));
2
+ import { Injector } from "../injection/Injector";
64
3
 
65
4
  export class WebPubBlobBuilder {
66
5
  private readonly item: Link;
67
6
  private readonly burl: string;
68
7
  private readonly pub: Publication;
69
8
  private readonly cssProperties?: { [key: string]: string };
70
-
71
- constructor(pub: Publication, baseURL: string, item: Link, cssProperties?: { [key: string]: string }) {
9
+ private readonly injector: Injector | null = null;
10
+
11
+ constructor(
12
+ pub: Publication,
13
+ baseURL: string,
14
+ item: Link,
15
+ options: {
16
+ cssProperties?: { [key: string]: string };
17
+ injector?: Injector | null;
18
+ }
19
+ ) {
72
20
  this.pub = pub;
73
21
  this.item = item;
74
22
  this.burl = item.toURL(baseURL) || "";
75
- this.cssProperties = cssProperties;
23
+ this.cssProperties = options.cssProperties;
24
+ this.injector = options.injector ?? null;
76
25
  }
77
26
 
78
27
  public async build(): Promise<string> {
@@ -96,14 +45,12 @@ export class WebPubBlobBuilder {
96
45
  const details = perror.querySelector("div");
97
46
  throw new Error(`Failed parsing item ${this.item.href}: ${details?.textContent || perror.textContent}`);
98
47
  }
99
- return this.finalizeDOM(doc, this.burl, this.item.mediaType, txt, this.cssProperties);
100
- }
101
48
 
102
- private hasExecutable(doc: Document): boolean {
103
- return (
104
- !!doc.querySelector("script") ||
105
- !!doc.querySelector("body[onload]:not(body[onload=''])")
106
- );
49
+ // Apply resource injections if injection service is provided
50
+ if (this.injector) {
51
+ await this.injector.injectForDocument(doc, this.item);
52
+ }
53
+ return this.finalizeDOM(doc, this.burl, this.item.mediaType, txt, this.cssProperties);
107
54
  }
108
55
 
109
56
  private setProperties(cssProperties: { [key: string]: string }, doc: Document) {
@@ -116,9 +63,6 @@ export class WebPubBlobBuilder {
116
63
  private finalizeDOM(doc: Document, base: string | undefined, mediaType: any, txt?: string, cssProperties?: { [key: string]: string }): string {
117
64
  if(!doc) return "";
118
65
 
119
- // ReadiumCSS WebPub
120
- doc.head.appendChild(styleify(doc, cached("ReadiumCSS-webpub", () => blobify(stripCSS(readiumCSSWebPub), "text/css"))));
121
-
122
66
  if (cssProperties) {
123
67
  this.setProperties(cssProperties, doc);
124
68
  }
@@ -134,11 +78,6 @@ export class WebPubBlobBuilder {
134
78
  doc.head.firstChild!.before(b);
135
79
  }
136
80
 
137
- const hasExecutable = this.hasExecutable(doc);
138
- if(hasExecutable) doc.head.firstChild!.before(rBefore(doc));
139
- doc.head.firstChild!.before(cssSelectorGenerator(doc));
140
- if(hasExecutable) doc.head.appendChild(rAfter(doc));
141
-
142
81
  // Serialize properly based on content type
143
82
  let serializedContent: string;
144
83
 
@@ -2,6 +2,7 @@ import { ModuleName } from "@readium/navigator-html-injectables";
2
2
  import { Locator, Publication } from "@readium/shared";
3
3
  import { WebPubBlobBuilder } from "./WebPubBlobBuilder";
4
4
  import { WebPubFrameManager } from "./WebPubFrameManager";
5
+ import { Injector } from "../injection/Injector";
5
6
 
6
7
  export class WebPubFramePoolManager {
7
8
  private readonly container: HTMLElement;
@@ -12,10 +13,16 @@ export class WebPubFramePoolManager {
12
13
  private readonly inprogress: Map<string, Promise<void>> = new Map();
13
14
  private pendingUpdates: Map<string, { inPool: boolean }> = new Map();
14
15
  private currentBaseURL: string | undefined;
16
+ private readonly injector?: Injector | null = null;
15
17
 
16
- constructor(container: HTMLElement, cssProperties?: { [key: string]: string }) {
18
+ constructor(
19
+ container: HTMLElement,
20
+ cssProperties?: { [key: string]: string },
21
+ injector?: Injector | null
22
+ ) {
17
23
  this.container = container;
18
24
  this.currentCssProperties = cssProperties;
25
+ this.injector = injector;
19
26
  }
20
27
 
21
28
  async destroy() {
@@ -42,9 +49,15 @@ export class WebPubFramePoolManager {
42
49
  this.pool.clear();
43
50
 
44
51
  // Revoke all blobs
45
- this.blobs.forEach(v => URL.revokeObjectURL(v));
52
+ this.blobs.forEach(v => {
53
+ this.injector?.releaseBlobUrl?.(v);
54
+ URL.revokeObjectURL(v);
55
+ });
46
56
  this.blobs.clear();
47
57
 
58
+ // Clean up injector if it exists
59
+ this.injector?.dispose();
60
+
48
61
  // Empty container of elements
49
62
  this.container.childNodes.forEach(v => {
50
63
  if(v.nodeType === Node.ELEMENT_NODE || v.nodeType === Node.TEXT_NODE) v.remove();
@@ -87,7 +100,10 @@ export class WebPubFramePoolManager {
87
100
  });
88
101
 
89
102
  if(this.currentBaseURL !== undefined && pub.baseURL !== this.currentBaseURL) {
90
- this.blobs.forEach(v => URL.revokeObjectURL(v));
103
+ this.blobs.forEach(v => {
104
+ this.injector?.releaseBlobUrl?.(v);
105
+ URL.revokeObjectURL(v);
106
+ });
91
107
  this.blobs.clear();
92
108
  }
93
109
  this.currentBaseURL = pub.baseURL;
@@ -97,6 +113,7 @@ export class WebPubFramePoolManager {
97
113
  if(this.pendingUpdates.has(href) && this.pendingUpdates.get(href)?.inPool === false) {
98
114
  const url = this.blobs.get(href);
99
115
  if(url) {
116
+ this.injector?.releaseBlobUrl?.(url);
100
117
  URL.revokeObjectURL(url);
101
118
  this.blobs.delete(href);
102
119
  this.pendingUpdates.delete(href);
@@ -117,7 +134,15 @@ export class WebPubFramePoolManager {
117
134
  const itm = pub.readingOrder.findWithHref(href);
118
135
  if(!itm) return;
119
136
  if(!this.blobs.has(href)) {
120
- const blobBuilder = new WebPubBlobBuilder(pub, this.currentBaseURL || "", itm, this.currentCssProperties);
137
+ const blobBuilder = new WebPubBlobBuilder(
138
+ pub,
139
+ this.currentBaseURL || "",
140
+ itm,
141
+ {
142
+ cssProperties: this.currentCssProperties,
143
+ injector: this.injector
144
+ }
145
+ );
121
146
  const blobURL = await blobBuilder.build();
122
147
  this.blobs.set(href, blobURL);
123
148
  }
@@ -14,10 +14,14 @@ 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
+ import { Injector } from "../injection/Injector";
18
+ import { createReadiumWebPubRules } from "../injection/webpubInjectables";
19
+ import { IInjectablesConfig } from "../injection/Injectable";
17
20
 
18
21
  export interface WebPubNavigatorConfiguration {
19
22
  preferences: IWebPubPreferences;
20
23
  defaults: IWebPubDefaults;
24
+ injectables?: IInjectablesConfig;
21
25
  }
22
26
 
23
27
  export interface WebPubNavigatorListeners {
@@ -57,6 +61,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
57
61
  private _settings: WebPubSettings;
58
62
  private _css: WebPubCSS;
59
63
  private _preferencesEditor: WebPubPreferencesEditor | null = null;
64
+ private readonly _injector: Injector | null = null;
60
65
 
61
66
  private webViewport: VisualNavigatorViewport = {
62
67
  readingOrder: [],
@@ -79,6 +84,15 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
79
84
  userProperties: new WebUserProperties({ zoom: this._settings.zoom })
80
85
  });
81
86
 
87
+ // Combine WebPub rules with user-provided injectables
88
+ const webpubRules = createReadiumWebPubRules();
89
+ const userConfig = configuration.injectables || { rules: [], allowedDomains: [] };
90
+
91
+ this._injector = new Injector({
92
+ rules: [...webpubRules, ...userConfig.rules],
93
+ allowedDomains: userConfig.allowedDomains
94
+ });
95
+
82
96
  // Initialize current location
83
97
  if (initialPosition && typeof initialPosition.copyWithLocations === 'function') {
84
98
  this.currentLocation = initialPosition;
@@ -95,7 +109,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
95
109
  public async load() {
96
110
  await this.updateCSS(false);
97
111
  const cssProperties = this.compileCSSProperties(this._css);
98
- this.framePool = new WebPubFramePoolManager(this.container, cssProperties);
112
+ this.framePool = new WebPubFramePoolManager(this.container, cssProperties, this._injector);
99
113
 
100
114
  await this.apply();
101
115
  }
@@ -10,10 +10,12 @@ import { IEpubPreferences, EpubPreferences } from "./preferences/EpubPreferences
10
10
  import { IEpubDefaults } from "./preferences/EpubDefaults";
11
11
  import { EpubSettings } from "./preferences";
12
12
  import { EpubPreferencesEditor } from "./preferences/EpubPreferencesEditor";
13
+ import { IInjectablesConfig } from "../injection/Injectable";
13
14
  export type ManagerEventKey = "zoom";
14
15
  export interface EpubNavigatorConfiguration {
15
16
  preferences: IEpubPreferences;
16
17
  defaults: IEpubDefaults;
18
+ injectables?: IInjectablesConfig;
17
19
  }
18
20
  export interface EpubNavigatorListeners {
19
21
  frameLoaded: (wnd: Window) => void;
@@ -42,6 +44,7 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
42
44
  private _settings;
43
45
  private _css;
44
46
  private _preferencesEditor;
47
+ private readonly _injector;
45
48
  private resizeObserver;
46
49
  private reflowViewport;
47
50
  constructor(container: HTMLElement, pub: Publication, listeners: EpubNavigatorListeners, positions?: Locator[], initialPosition?: Locator | undefined, configuration?: EpubNavigatorConfiguration);
@@ -1,17 +1,20 @@
1
1
  import { Link, Publication } from "@readium/shared";
2
+ import { Injector } from "../../injection/Injector";
2
3
  export default class FrameBlobBuider {
3
4
  private readonly item;
4
5
  private readonly burl;
5
6
  private readonly pub;
6
7
  private readonly cssProperties?;
7
- constructor(pub: Publication, baseURL: string, item: Link, cssProperties?: {
8
- [key: string]: string;
8
+ private readonly injector;
9
+ constructor(pub: Publication, baseURL: string, item: Link, options: {
10
+ cssProperties?: {
11
+ [key: string]: string;
12
+ };
13
+ injector?: Injector | null;
9
14
  });
10
15
  build(fxl?: boolean): Promise<string>;
11
16
  private buildHtmlFrame;
12
17
  private buildImageFrame;
13
- private hasExecutable;
14
- private hasStyle;
15
18
  private setProperties;
16
19
  private finalizeDOM;
17
20
  }
@@ -1,6 +1,7 @@
1
1
  import { ModuleName } from "@readium/navigator-html-injectables";
2
2
  import { Locator, Publication } from "@readium/shared";
3
3
  import { FrameManager } from "./FrameManager";
4
+ import { Injector } from "../../injection/Injector";
4
5
  export declare class FramePoolManager {
5
6
  private readonly container;
6
7
  private readonly positions;
@@ -11,9 +12,10 @@ export declare class FramePoolManager {
11
12
  private readonly inprogress;
12
13
  private pendingUpdates;
13
14
  private currentBaseURL;
15
+ private readonly injector;
14
16
  constructor(container: HTMLElement, positions: Locator[], cssProperties?: {
15
17
  [key: string]: string;
16
- });
18
+ }, injector?: Injector | null);
17
19
  destroy(): Promise<void>;
18
20
  update(pub: Publication, locator: Locator, modules: ModuleName[], force?: boolean): Promise<void>;
19
21
  setCSSProperties(properties: {
@@ -4,6 +4,7 @@ import { FrameCommsListener } from "../frame";
4
4
  import { FXLFrameManager } from "./FXLFrameManager";
5
5
  import { FXLPeripherals } from "./FXLPeripherals";
6
6
  import { VisualNavigatorViewport } from "../../Navigator";
7
+ import { Injector } from "../../injection/Injector";
7
8
  export declare class FXLFramePoolManager {
8
9
  private readonly container;
9
10
  private readonly positions;
@@ -14,6 +15,7 @@ export declare class FXLFramePoolManager {
14
15
  private readonly delayedTimeout;
15
16
  private currentBaseURL;
16
17
  private previousFrames;
18
+ private readonly injector;
17
19
  private readonly bookElement;
18
20
  readonly spineElement: HTMLDivElement;
19
21
  private readonly pub;
@@ -28,7 +30,7 @@ export declare class FXLFramePoolManager {
28
30
  private containerHeightCached;
29
31
  private resizeTimeout;
30
32
  readonly peripherals: FXLPeripherals;
31
- constructor(container: HTMLElement, positions: Locator[], pub: Publication);
33
+ constructor(container: HTMLElement, positions: Locator[], pub: Publication, injector?: Injector | null);
32
34
  private _listener;
33
35
  set listener(listener: FrameCommsListener);
34
36
  get listener(): FrameCommsListener;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Utilities for processing CSS and JavaScript content
3
+ */
4
+ /**
5
+ * Minifies JavaScript by removing comments and normalizing whitespace
6
+ */
7
+ export declare const stripJS: (source: string) => string;
8
+ /**
9
+ * Minifies CSS by removing comments and normalizing whitespace
10
+ * Note: URL resolution should be handled by the caller with correct context
11
+ */
12
+ export declare const stripCSS: (source: string) => string;
@@ -5,3 +5,4 @@ export * from './audio';
5
5
  export * from './helpers';
6
6
  export * from './preferences';
7
7
  export * from './css';
8
+ export * from './injection';
@@ -0,0 +1,68 @@
1
+ import { Link } from "@readium/shared";
2
+ type ForbiddenAttributes = "type" | "rel" | "href" | "src";
3
+ type AllowedAttributes = {
4
+ [K in string]: K extends ForbiddenAttributes ? never : (string | boolean | undefined);
5
+ } & {
6
+ [K in ForbiddenAttributes]?: never;
7
+ };
8
+ export interface IBaseInjectable {
9
+ id?: string;
10
+ target?: "head" | "body";
11
+ type?: string;
12
+ condition?: (doc: Document) => boolean;
13
+ attributes?: AllowedAttributes;
14
+ }
15
+ export interface IScriptInjectable extends IBaseInjectable {
16
+ as: "script";
17
+ rel?: never;
18
+ }
19
+ export interface ILinkInjectable extends IBaseInjectable {
20
+ as: "link";
21
+ rel: string;
22
+ }
23
+ export interface IUrlInjectable {
24
+ url: string;
25
+ }
26
+ export interface IBlobInjectable {
27
+ blob: Blob;
28
+ }
29
+ export type IInjectable = (IScriptInjectable | ILinkInjectable) & (IUrlInjectable | IBlobInjectable);
30
+ /**
31
+ * Defines a rule for resource injection, specifying which resources to inject into which documents.
32
+ */
33
+ export interface IInjectableRule {
34
+ /**
35
+ * List of resource URLs or patterns that this rule applies to.
36
+ * Can be exact URLs or patterns with wildcards.
37
+ */
38
+ resources: Array<string | RegExp>;
39
+ /**
40
+ * Resources to inject at the beginning of the target (in array order)
41
+ */
42
+ prepend?: IInjectable[];
43
+ /**
44
+ * Resources to inject at the end of the target (in array order)
45
+ */
46
+ append?: IInjectable[];
47
+ }
48
+ export interface IInjectablesConfig {
49
+ rules: IInjectableRule[];
50
+ allowedDomains?: string[];
51
+ }
52
+ export interface IInjector {
53
+ /**
54
+ * Injects resources into a document based on matching rules
55
+ * @param doc The document to inject resources into
56
+ * @param link The link being loaded, used to match against injection rules
57
+ */
58
+ injectForDocument(doc: Document, link: Link): Promise<void>;
59
+ /**
60
+ * Cleans up any resources used by the injector
61
+ */
62
+ dispose(): void;
63
+ /**
64
+ * Get the list of allowed domains
65
+ */
66
+ getAllowedDomains(): string[];
67
+ }
68
+ export {};
@@ -0,0 +1,22 @@
1
+ import { IInjector, IInjectablesConfig } from "./Injectable";
2
+ import { Link } from "@readium/shared";
3
+ export declare class Injector implements IInjector {
4
+ private readonly blobStore;
5
+ private readonly createdBlobUrls;
6
+ private readonly rules;
7
+ private readonly allowedDomains;
8
+ private injectableIdCounter;
9
+ constructor(config: IInjectablesConfig);
10
+ dispose(): void;
11
+ getAllowedDomains(): string[];
12
+ injectForDocument(doc: Document, link: Link): Promise<void>;
13
+ private matchesRule;
14
+ private getOrCreateBlobUrl;
15
+ releaseBlobUrl(url: string): Promise<void>;
16
+ private getResourceUrl;
17
+ private createPreloadLink;
18
+ private createElement;
19
+ private applyRule;
20
+ private processInjectable;
21
+ private isValidUrl;
22
+ }
@@ -0,0 +1,6 @@
1
+ import { IInjectableRule } from "../injection/Injectable";
2
+ import { Metadata } from "@readium/shared";
3
+ /**
4
+ * Creates injectable rules for EPUB content documents
5
+ */
6
+ export declare function createReadiumEpubRules(metadata: Metadata): IInjectableRule[];
@@ -0,0 +1,2 @@
1
+ export * from "./Injectable";
2
+ export * from "./Injector";
@@ -0,0 +1,5 @@
1
+ import { IInjectableRule } from "../injection/Injectable";
2
+ /**
3
+ * Creates injectable rules for WebPub content documents
4
+ */
5
+ export declare function createReadiumWebPubRules(): IInjectableRule[];
@@ -1,15 +1,19 @@
1
1
  import { Link, Publication } from "@readium/shared";
2
+ import { Injector } from "../injection/Injector";
2
3
  export declare class WebPubBlobBuilder {
3
4
  private readonly item;
4
5
  private readonly burl;
5
6
  private readonly pub;
6
7
  private readonly cssProperties?;
7
- constructor(pub: Publication, baseURL: string, item: Link, cssProperties?: {
8
- [key: string]: string;
8
+ private readonly injector;
9
+ constructor(pub: Publication, baseURL: string, item: Link, options: {
10
+ cssProperties?: {
11
+ [key: string]: string;
12
+ };
13
+ injector?: Injector | null;
9
14
  });
10
15
  build(): Promise<string>;
11
16
  private buildHtmlFrame;
12
- private hasExecutable;
13
17
  private setProperties;
14
18
  private finalizeDOM;
15
19
  private serializeAsHTML;