@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.
- package/dist/ar-DyHX_uy2-DyHX_uy2-DyHX_uy2.js +7 -0
- package/dist/ar-DyHX_uy2-DyHX_uy2.js +7 -0
- package/dist/da-Dct0PS3E-Dct0PS3E-Dct0PS3E.js +7 -0
- package/dist/da-Dct0PS3E-Dct0PS3E.js +7 -0
- package/dist/fr-C5HEel98-C5HEel98-C5HEel98.js +7 -0
- package/dist/fr-C5HEel98-C5HEel98.js +7 -0
- package/dist/index.js +4330 -2342
- package/dist/index.umd.cjs +1571 -39
- package/dist/it-DFOBoXGy-DFOBoXGy-DFOBoXGy.js +7 -0
- package/dist/it-DFOBoXGy-DFOBoXGy.js +7 -0
- package/dist/pt_PT-Di3sVjze-Di3sVjze-Di3sVjze.js +7 -0
- package/dist/pt_PT-Di3sVjze-Di3sVjze.js +7 -0
- package/dist/sv-BfzAFsVN-BfzAFsVN-BfzAFsVN.js +7 -0
- package/dist/sv-BfzAFsVN-BfzAFsVN.js +7 -0
- package/package.json +1 -1
- package/src/dom/_readium_executionCleanup.js +13 -0
- package/src/dom/_readium_executionPrevention.js +65 -0
- package/src/dom/_readium_webpubExecution.js +4 -0
- package/src/epub/EpubNavigator.ts +26 -2
- package/src/epub/frame/FrameBlobBuilder.ts +37 -131
- package/src/epub/frame/FramePoolManager.ts +34 -5
- package/src/epub/fxl/FXLFramePoolManager.ts +20 -2
- package/src/helpers/minify.ts +14 -0
- package/src/index.ts +2 -1
- package/src/injection/Injectable.ts +85 -0
- package/src/injection/Injector.ts +356 -0
- package/src/injection/epubInjectables.ts +90 -0
- package/src/injection/index.ts +2 -0
- package/src/injection/webpubInjectables.ts +59 -0
- package/src/webpub/WebPubBlobBuilder.ts +19 -80
- package/src/webpub/WebPubFramePoolManager.ts +29 -4
- package/src/webpub/WebPubNavigator.ts +15 -1
- package/types/src/epub/EpubNavigator.d.ts +3 -0
- package/types/src/epub/frame/FrameBlobBuilder.d.ts +7 -4
- package/types/src/epub/frame/FramePoolManager.d.ts +3 -1
- package/types/src/epub/fxl/FXLFramePoolManager.d.ts +3 -1
- package/types/src/helpers/minify.d.ts +12 -0
- package/types/src/index.d.ts +1 -0
- package/types/src/injection/Injectable.d.ts +68 -0
- package/types/src/injection/Injector.d.ts +22 -0
- package/types/src/injection/epubInjectables.d.ts +6 -0
- package/types/src/injection/index.d.ts +2 -0
- package/types/src/injection/webpubInjectables.d.ts +5 -0
- package/types/src/webpub/WebPubBlobBuilder.d.ts +7 -3
- package/types/src/webpub/WebPubFramePoolManager.d.ts +3 -1
- 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,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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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(
|
|
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 =>
|
|
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 =>
|
|
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(
|
|
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
|
-
|
|
8
|
-
|
|
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;
|
package/types/src/index.d.ts
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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
|
-
|
|
8
|
-
|
|
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;
|