@readium/navigator 2.10.2 → 2.11.0
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/audio/AudioNavigator.js +1 -1
- package/dist/audio/AudioPoolManager.js +1 -1
- package/dist/audio/engine/WebAudioEngine.js +1 -1
- package/dist/divina/DivinaNavigator.js +1 -0
- package/dist/divina/DivinaPageManager.js +1 -0
- package/dist/divina/DivinaPagedPresenter.js +1 -0
- package/dist/divina/DivinaPeripherals.js +1 -0
- package/dist/divina/DivinaScrolledPresenter.js +1 -0
- package/dist/divina/DivinaSpreader.js +1 -0
- package/dist/divina/DivinaVariantSelector.js +1 -0
- package/dist/divina/preferences/DivinaDefaults.js +1 -0
- package/dist/divina/preferences/DivinaPreferences.js +1 -0
- package/dist/divina/preferences/DivinaPreferencesEditor.js +1 -0
- package/dist/divina/preferences/DivinaSettings.js +1 -0
- package/dist/divina/protection/DivinaNavigatorProtector.js +1 -0
- package/dist/epub/EpubNavigator.js +1 -1
- package/dist/epub/frame/FrameBlobBuilder.js +3 -1
- package/dist/epub/frame/FrameManager.js +1 -1
- package/dist/epub/frame/FramePoolManager.js +1 -1
- package/dist/epub/fxl/FXLFrameManager.js +1 -1
- package/dist/epub/fxl/FXLFramePoolManager.js +1 -1
- package/dist/epub/fxl/FXLPeripherals.js +2 -2
- package/dist/index.js +1 -1
- package/dist/peripherals/KeyboardConditionBridge.js +1 -1
- package/dist/peripherals/KeyboardPeripherals.js +1 -1
- package/dist/webpub/WebPubFrameManager.js +1 -1
- package/dist/webpub/WebPubFramePoolManager.js +1 -1
- package/dist/webpub/WebPubNavigator.js +1 -1
- package/package.json +5 -5
- package/src/audio/AudioNavigator.ts +107 -5
- package/src/audio/AudioPoolManager.ts +14 -8
- package/src/audio/engine/AudioEngine.ts +34 -1
- package/src/audio/engine/WebAudioEngine.ts +48 -3
- package/src/divina/DivinaNavigator.ts +704 -0
- package/src/divina/DivinaPageManager.ts +322 -0
- package/src/divina/DivinaPagedPresenter.ts +508 -0
- package/src/divina/DivinaPeripherals.ts +630 -0
- package/src/divina/DivinaScrolledPresenter.ts +516 -0
- package/src/divina/DivinaSpreader.ts +202 -0
- package/src/divina/DivinaVariantSelector.ts +77 -0
- package/src/divina/index.ts +8 -0
- package/src/divina/preferences/DivinaDefaults.ts +37 -0
- package/src/divina/preferences/DivinaPreferences.ts +77 -0
- package/src/divina/preferences/DivinaPreferencesEditor.ts +96 -0
- package/src/divina/preferences/DivinaSettings.ts +38 -0
- package/src/divina/preferences/index.ts +4 -0
- package/src/divina/protection/DivinaNavigatorProtector.ts +44 -0
- package/src/epub/EpubNavigator.ts +73 -7
- package/src/epub/frame/FrameBlobBuilder.ts +144 -39
- package/src/epub/frame/FrameManager.ts +7 -9
- package/src/epub/frame/FramePoolManager.ts +106 -48
- package/src/epub/fxl/FXLFrameManager.ts +69 -26
- package/src/epub/fxl/FXLFramePoolManager.ts +98 -53
- package/src/epub/fxl/FXLPeripherals.ts +22 -1
- package/src/index.ts +1 -0
- package/src/peripherals/KeyboardConditionBridge.ts +15 -5
- package/src/peripherals/KeyboardPeripherals.ts +13 -7
- package/src/webpub/WebPubFrameManager.ts +7 -9
- package/src/webpub/WebPubFramePoolManager.ts +5 -2
- package/src/webpub/WebPubNavigator.ts +44 -0
- package/types/src/audio/AudioNavigator.d.ts +29 -0
- package/types/src/audio/engine/AudioEngine.d.ts +27 -1
- package/types/src/audio/engine/WebAudioEngine.d.ts +14 -2
- package/types/src/divina/DivinaNavigator.d.ts +119 -0
- package/types/src/divina/DivinaPageManager.d.ts +66 -0
- package/types/src/divina/DivinaPagedPresenter.d.ts +122 -0
- package/types/src/divina/DivinaPeripherals.d.ts +121 -0
- package/types/src/divina/DivinaScrolledPresenter.d.ts +125 -0
- package/types/src/divina/DivinaSpreader.d.ts +65 -0
- package/types/src/divina/DivinaVariantSelector.d.ts +30 -0
- package/types/src/divina/index.d.ts +8 -0
- package/types/src/divina/preferences/DivinaDefaults.d.ts +18 -0
- package/types/src/divina/preferences/DivinaPreferences.d.ts +33 -0
- package/types/src/divina/preferences/DivinaPreferencesEditor.d.ts +20 -0
- package/types/src/divina/preferences/DivinaSettings.d.ts +26 -0
- package/types/src/divina/preferences/index.d.ts +4 -0
- package/types/src/divina/protection/DivinaNavigatorProtector.d.ts +14 -0
- package/types/src/epub/EpubNavigator.d.ts +7 -1
- package/types/src/epub/frame/FrameBlobBuilder.d.ts +19 -3
- package/types/src/epub/frame/FramePoolManager.d.ts +5 -1
- package/types/src/epub/fxl/FXLFrameManager.d.ts +2 -0
- package/types/src/epub/fxl/FXLFramePoolManager.d.ts +8 -2
- package/types/src/epub/fxl/FXLPeripherals.d.ts +3 -0
- package/types/src/index.d.ts +1 -0
- package/types/src/peripherals/KeyboardPeripherals.d.ts +1 -0
- package/types/src/webpub/WebPubNavigator.d.ts +2 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ModuleName } from "@readium/navigator-html-injectables";
|
|
2
2
|
import { Locator, Publication } from "@readium/shared";
|
|
3
|
-
import
|
|
3
|
+
import FrameBlobBuilder, { UpgradeInsecureRequests } from "./FrameBlobBuilder.ts";
|
|
4
4
|
import { FrameManager } from "./FrameManager.ts";
|
|
5
5
|
import { Injector } from "../../injection/Injector.ts";
|
|
6
6
|
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator.ts";
|
|
7
7
|
|
|
8
|
-
const UPPER_BOUNDARY =
|
|
9
|
-
const LOWER_BOUNDARY =
|
|
8
|
+
const UPPER_BOUNDARY = 10;
|
|
9
|
+
const LOWER_BOUNDARY = 5;
|
|
10
10
|
|
|
11
11
|
export class FramePoolManager {
|
|
12
12
|
private readonly container: HTMLElement;
|
|
@@ -14,15 +14,18 @@ export class FramePoolManager {
|
|
|
14
14
|
private _currentFrame: FrameManager | undefined;
|
|
15
15
|
private currentCssProperties: { [key: string]: string } | undefined;
|
|
16
16
|
private readonly pool: Map<string, FrameManager> = new Map();
|
|
17
|
-
private readonly blobs: Map<string,
|
|
18
|
-
private readonly inprogress: Map<string, Promise<
|
|
17
|
+
private readonly blobs: Map<string, FrameBlobBuilder> = new Map();
|
|
18
|
+
private readonly inprogress: Map<string, Promise<unknown>> = new Map();
|
|
19
19
|
private pendingUpdates: Map<string, { inPool: boolean }> = new Map();
|
|
20
20
|
private currentBaseURL: string | undefined;
|
|
21
21
|
private readonly injector: Injector | null = null;
|
|
22
22
|
private readonly contentProtectionConfig: IContentProtectionConfig;
|
|
23
23
|
private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
|
|
24
|
+
private updateSequence = 0;
|
|
25
|
+
private forcedUpdate: Promise<void> | undefined;
|
|
24
26
|
private resizeWatchSelectors: string[];
|
|
25
27
|
private readonly getFragmentIds: (href: string) => string[];
|
|
28
|
+
private readonly upgradeInsecureRequests: UpgradeInsecureRequests;
|
|
26
29
|
|
|
27
30
|
constructor(
|
|
28
31
|
container: HTMLElement,
|
|
@@ -32,7 +35,8 @@ export class FramePoolManager {
|
|
|
32
35
|
contentProtectionConfig?: IContentProtectionConfig,
|
|
33
36
|
keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig,
|
|
34
37
|
getFragmentIds?: (href: string) => string[],
|
|
35
|
-
resizeWatchSelectors: string[] = []
|
|
38
|
+
resizeWatchSelectors: string[] = [],
|
|
39
|
+
upgradeInsecureRequests?: UpgradeInsecureRequests
|
|
36
40
|
) {
|
|
37
41
|
this.container = container;
|
|
38
42
|
this.positions = positions;
|
|
@@ -42,6 +46,7 @@ export class FramePoolManager {
|
|
|
42
46
|
this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
|
|
43
47
|
this.getFragmentIds = getFragmentIds ?? (() => []);
|
|
44
48
|
this.resizeWatchSelectors = [...resizeWatchSelectors];
|
|
49
|
+
this.upgradeInsecureRequests = upgradeInsecureRequests ?? 'always';
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
addResizeTarget(selector: string): void {
|
|
@@ -61,7 +66,7 @@ export class FramePoolManager {
|
|
|
61
66
|
// Wait for all in-progress loads to complete
|
|
62
67
|
let iit = this.inprogress.values();
|
|
63
68
|
let inp = iit.next();
|
|
64
|
-
const inprogressPromises: Promise<
|
|
69
|
+
const inprogressPromises: Promise<unknown>[] = [];
|
|
65
70
|
while(inp.value) {
|
|
66
71
|
inprogressPromises.push(inp.value);
|
|
67
72
|
inp = iit.next();
|
|
@@ -81,10 +86,8 @@ export class FramePoolManager {
|
|
|
81
86
|
this.pool.clear();
|
|
82
87
|
|
|
83
88
|
// Revoke all blobs
|
|
84
|
-
this.blobs.forEach(v =>
|
|
85
|
-
|
|
86
|
-
URL.revokeObjectURL(v);
|
|
87
|
-
});
|
|
89
|
+
this.blobs.forEach(v => v.reset());
|
|
90
|
+
this.blobs.clear();
|
|
88
91
|
|
|
89
92
|
// Clean up injector if it exists
|
|
90
93
|
this.injector?.dispose();
|
|
@@ -96,6 +99,9 @@ export class FramePoolManager {
|
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
async update(pub: Publication, locator: Locator, modules: ModuleName[], force=false) {
|
|
102
|
+
// Updates arriving mid-rebuild must not cancel it, or the old frame is gone with no replacement
|
|
103
|
+
while (this.forcedUpdate) await this.forcedUpdate;
|
|
104
|
+
const updateSequence = ++this.updateSequence;
|
|
99
105
|
let i = this.positions.findIndex(l => l.locations.position === locator.locations.position);
|
|
100
106
|
if(i < 0) throw Error(`Locator not found in position list: ${locator.locations.position} > ${this.positions.reduce<number>((acc, l) => l.locations.position || 0 > acc ? l.locations.position || 0 : acc, 0) }`);
|
|
101
107
|
const newHref = this.positions[i].href;
|
|
@@ -125,39 +131,33 @@ export class FramePoolManager {
|
|
|
125
131
|
this.pool.delete(href);
|
|
126
132
|
if(this.pendingUpdates.has(href))
|
|
127
133
|
this.pendingUpdates.set(href, { inPool: false });
|
|
134
|
+
// Note that we don't reset the blob here, unlike in the FXL pool.
|
|
135
|
+
// This is because FXL tends to have a ton more blobs. Maybe we'll adjust
|
|
136
|
+
// this at a later point with a much larger boundary for resets to deal
|
|
137
|
+
// with extremely long/large reflowable publications.
|
|
138
|
+
// Reflowable publication resources also tend to be much larger documents,
|
|
139
|
+
// so they're more expensive to preprocess with the FrameBlobBuilder.
|
|
128
140
|
});
|
|
129
141
|
|
|
130
142
|
// Check if base URL of publication has changed
|
|
131
143
|
if(this.currentBaseURL !== undefined && pub.baseURL !== this.currentBaseURL) {
|
|
132
144
|
// Revoke all blobs
|
|
133
|
-
this.blobs.forEach(v =>
|
|
134
|
-
this.injector?.releaseBlobUrl?.(v);
|
|
135
|
-
URL.revokeObjectURL(v);
|
|
136
|
-
});
|
|
145
|
+
this.blobs.forEach(v => v.reset());
|
|
137
146
|
this.blobs.clear();
|
|
138
147
|
}
|
|
139
148
|
this.currentBaseURL = pub.baseURL;
|
|
140
149
|
|
|
150
|
+
if(force) {
|
|
151
|
+
this.blobs.forEach(v => v.reset());
|
|
152
|
+
this.blobs.clear();
|
|
153
|
+
this.pendingUpdates.clear();
|
|
154
|
+
}
|
|
155
|
+
|
|
141
156
|
const creator = async (href: string) => {
|
|
142
|
-
if(force) {
|
|
143
|
-
// Revoke all blobs so that CSSProperties are not stale
|
|
144
|
-
// When using force, we switch scroll/paginated
|
|
145
|
-
// If this property is not up to date, it creates issues
|
|
146
|
-
// when navigating backwards, where paginated will go the
|
|
147
|
-
// start of the resource instead of the end due to the
|
|
148
|
-
// corrupted width ColumnSnapper (injectables) gets on init
|
|
149
|
-
this.blobs.forEach(v => {
|
|
150
|
-
this.injector?.releaseBlobUrl?.(v);
|
|
151
|
-
URL.revokeObjectURL(v);
|
|
152
|
-
});
|
|
153
|
-
this.blobs.clear();
|
|
154
|
-
this.pendingUpdates.clear();
|
|
155
|
-
}
|
|
156
157
|
if(this.pendingUpdates.has(href) && this.pendingUpdates.get(href)?.inPool === false) {
|
|
157
|
-
const
|
|
158
|
-
if(
|
|
159
|
-
|
|
160
|
-
URL.revokeObjectURL(url);
|
|
158
|
+
const v = this.blobs.get(href);
|
|
159
|
+
if(v) {
|
|
160
|
+
v.reset();
|
|
161
161
|
this.blobs.delete(href);
|
|
162
162
|
this.pendingUpdates.delete(href);
|
|
163
163
|
}
|
|
@@ -176,44 +176,92 @@ export class FramePoolManager {
|
|
|
176
176
|
const itm = pub.readingOrder.findWithHref(href);
|
|
177
177
|
if(!itm) return; // TODO throw?
|
|
178
178
|
if(!this.blobs.has(href)) {
|
|
179
|
-
|
|
179
|
+
this.blobs.set(href, new FrameBlobBuilder(
|
|
180
180
|
pub,
|
|
181
181
|
this.currentBaseURL || "",
|
|
182
182
|
itm,
|
|
183
183
|
{
|
|
184
184
|
cssProperties: this.currentCssProperties,
|
|
185
|
-
injector: this.injector
|
|
185
|
+
injector: this.injector,
|
|
186
|
+
upgradeInsecureRequests: this.upgradeInsecureRequests
|
|
186
187
|
}
|
|
187
|
-
);
|
|
188
|
-
const blobURL = await blobBuilder.build();
|
|
189
|
-
this.blobs.set(href, blobURL);
|
|
188
|
+
));
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
// Create <iframe>
|
|
193
|
-
const fm = new FrameManager(this.blobs.get(href)
|
|
192
|
+
const fm = new FrameManager(await this.blobs.get(href)!.build(), this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href), this.resizeWatchSelectors);
|
|
194
193
|
if(href !== newHref) await fm.hide(); // Avoid unecessary hide
|
|
195
194
|
this.container.appendChild(fm.iframe);
|
|
196
195
|
await fm.load(modules);
|
|
197
196
|
this.pool.set(href, fm);
|
|
197
|
+
return fm;
|
|
198
198
|
}
|
|
199
|
+
|
|
200
|
+
// Target frame is awaited
|
|
199
201
|
try {
|
|
200
|
-
await Promise.all(creation.map(href => creator(href)));
|
|
202
|
+
await Promise.all(creation.filter(href => href === newHref).map(href => creator(href)));
|
|
201
203
|
} catch (error) {
|
|
202
204
|
reject(error);
|
|
205
|
+
return;
|
|
203
206
|
}
|
|
204
207
|
|
|
208
|
+
if (updateSequence !== this.updateSequence) {
|
|
209
|
+
resolve();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
Promise.all(creation.filter(href => href !== newHref && !this.inprogress.has(href)).map(async href => {
|
|
214
|
+
const c = creator(href);
|
|
215
|
+
this.inprogress.set(href, c);
|
|
216
|
+
try {
|
|
217
|
+
await c;
|
|
218
|
+
} finally {
|
|
219
|
+
if (this.inprogress.get(href) === c) this.inprogress.delete(href);
|
|
220
|
+
}
|
|
221
|
+
})).catch(error => console.warn("Failed preloading frame", error));
|
|
222
|
+
|
|
205
223
|
// Update current frame
|
|
206
|
-
const newFrame = this.pool.get(newHref)
|
|
224
|
+
const newFrame = this.pool.get(newHref);
|
|
225
|
+
if (!newFrame) {
|
|
226
|
+
resolve();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
207
230
|
if(newFrame?.source !== this._currentFrame?.source || force) {
|
|
208
231
|
await this._currentFrame?.hide(); // Hide current frame. It's possible it no longer even exists in the DOM at this point
|
|
209
|
-
|
|
210
|
-
|
|
232
|
+
|
|
233
|
+
// Resolve if there is a concurrent update that has started since this one began
|
|
234
|
+
if (updateSequence !== this.updateSequence) {
|
|
235
|
+
resolve();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// If user is speeding through the publication, this can get destroyed
|
|
240
|
+
const currentFrame = this.pool.get(newHref);
|
|
241
|
+
if (!currentFrame) {
|
|
242
|
+
resolve();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await currentFrame.load(modules); // In order to ensure modules match the latest configuration
|
|
247
|
+
|
|
248
|
+
// Resolve if there is a concurrent update that has started since this one began
|
|
249
|
+
if (updateSequence !== this.updateSequence) {
|
|
250
|
+
resolve();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Check if it was destroyed, again
|
|
255
|
+
const latestFrame = this.pool.get(newHref);
|
|
256
|
+
if (!latestFrame) {
|
|
257
|
+
resolve();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
211
260
|
|
|
212
261
|
// Update progression if necessary and show the new frame
|
|
213
|
-
|
|
214
|
-
await newFrame.show(locator.locations.progression); // Show/activate new frame
|
|
262
|
+
await latestFrame.show(locator.locations.progression); // Show/activate new frame
|
|
215
263
|
|
|
216
|
-
this._currentFrame =
|
|
264
|
+
this._currentFrame = latestFrame;
|
|
217
265
|
|
|
218
266
|
// Safari retains focus on the hidden iframe; transfer it to the new
|
|
219
267
|
// frame only if no meaningful element in the parent document owns focus.
|
|
@@ -227,8 +275,18 @@ export class FramePoolManager {
|
|
|
227
275
|
});
|
|
228
276
|
|
|
229
277
|
this.inprogress.set(newHref, progressPromise); // Add the job to the in progress map
|
|
230
|
-
|
|
231
|
-
|
|
278
|
+
if (force) {
|
|
279
|
+
const forced = progressPromise.catch(() => {});
|
|
280
|
+
this.forcedUpdate = forced;
|
|
281
|
+
forced.then(() => {
|
|
282
|
+
if (this.forcedUpdate === forced) this.forcedUpdate = undefined;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
await progressPromise; // Wait on the job to finish...
|
|
287
|
+
} finally {
|
|
288
|
+
this.inprogress.delete(newHref); // Delete it from the in progress map!
|
|
289
|
+
}
|
|
232
290
|
}
|
|
233
291
|
|
|
234
292
|
setCSSProperties(properties: { [key: string]: string }) {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Loader, ModuleName } from "@readium/navigator-html-injectables";
|
|
2
2
|
import { Page, ReadingProgression } from "@readium/shared";
|
|
3
3
|
import { FrameComms } from "../frame/FrameComms.ts";
|
|
4
|
-
import { FXLPeripherals } from "./FXLPeripherals.ts";
|
|
4
|
+
import { FXLPeripherals, isTypedOMSupported } from "./FXLPeripherals.ts";
|
|
5
5
|
import type { ReadiumWindow } from "@readium/navigator-html-injectables";
|
|
6
6
|
import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator.ts";
|
|
7
7
|
import { KeyboardConditionBridge } from "../../peripherals/KeyboardConditionBridge.ts";
|
|
8
8
|
|
|
9
9
|
export class FXLFrameManager {
|
|
10
10
|
private frame: HTMLIFrameElement;
|
|
11
|
+
private frameIsAppended = false;
|
|
11
12
|
private loader: Loader | undefined;
|
|
12
13
|
public source: string;
|
|
13
14
|
private comms: FrameComms | undefined;
|
|
@@ -24,6 +25,7 @@ export class FXLFrameManager {
|
|
|
24
25
|
public debugHref: string;
|
|
25
26
|
private loadPromise: Promise<Window> | undefined;
|
|
26
27
|
private showPromise: Promise<void> | undefined;
|
|
28
|
+
private viewportSize: { width: number, height: number } | undefined = undefined;
|
|
27
29
|
|
|
28
30
|
constructor(
|
|
29
31
|
peripherals: FXLPeripherals,
|
|
@@ -45,7 +47,7 @@ export class FXLFrameManager {
|
|
|
45
47
|
this.frame.classList.add("blank");
|
|
46
48
|
this.frame.scrolling = "no";
|
|
47
49
|
this.frame.style.visibility = "hidden";
|
|
48
|
-
this.frame.
|
|
50
|
+
this.frame.ariaHidden = "true";
|
|
49
51
|
this.frame.style.display = "none";
|
|
50
52
|
this.frame.style.position = "absolute";
|
|
51
53
|
this.frame.style.pointerEvents = "none";
|
|
@@ -56,15 +58,17 @@ export class FXLFrameManager {
|
|
|
56
58
|
this.frame.dataset.originalHref = debugHref;
|
|
57
59
|
this.source = "about:blank";
|
|
58
60
|
|
|
59
|
-
// NEW
|
|
60
61
|
this.wrapper = document.createElement("div");
|
|
61
62
|
this.wrapper.style.position = "relative";
|
|
63
|
+
this.wrapper.style.contain = "strict";
|
|
62
64
|
this.wrapper.style.float = this.wrapper.style.cssFloat = direction === ReadingProgression.rtl ? "right" : "left";
|
|
63
|
-
|
|
64
|
-
this.wrapper.appendChild(this.frame);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
async load(modules: ModuleName[], source: string): Promise<Window> {
|
|
68
|
+
if(!this.frameIsAppended) {
|
|
69
|
+
this.wrapper.appendChild(this.frame);
|
|
70
|
+
this.frameIsAppended = true;
|
|
71
|
+
}
|
|
68
72
|
if(this.source === source && this.loadPromise/* && this.loaded*/) {
|
|
69
73
|
if([...this.currModules].sort().join("|") === [...modules].sort().join("|")) {
|
|
70
74
|
return this.loadPromise;
|
|
@@ -110,6 +114,7 @@ export class FXLFrameManager {
|
|
|
110
114
|
|
|
111
115
|
// Parses the page size from the viewport meta tag of the loaded resource.
|
|
112
116
|
loadPageSize(): { width: number, height: number } {
|
|
117
|
+
if(this.viewportSize) return this.viewportSize;
|
|
113
118
|
const wnd = this.frame.contentWindow!;
|
|
114
119
|
|
|
115
120
|
// Try to get the page size from the viewport meta tag
|
|
@@ -126,8 +131,10 @@ export class FXLFrameManager {
|
|
|
126
131
|
else if(match[1] === "height")
|
|
127
132
|
height = Number.parseFloat(match[2]);
|
|
128
133
|
}
|
|
129
|
-
if(width > 0 && height > 0)
|
|
130
|
-
|
|
134
|
+
if(width > 0 && height > 0) {
|
|
135
|
+
this.viewportSize = { width, height };
|
|
136
|
+
return this.viewportSize;
|
|
137
|
+
}
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
// Otherwise get it from the size of the loaded content
|
|
@@ -140,25 +147,47 @@ export class FXLFrameManager {
|
|
|
140
147
|
update(page?: Page) {
|
|
141
148
|
if(!this.loaded) return;
|
|
142
149
|
const dimensions = this.loadPageSize();
|
|
143
|
-
this.frame.style.height = `${dimensions.height}px`;
|
|
144
|
-
this.frame.style.width = `${dimensions.width}px`;
|
|
145
150
|
const ratio = Math.min(this.wrapper.clientWidth / dimensions.width, this.wrapper.clientHeight / dimensions.height);
|
|
146
|
-
|
|
151
|
+
if(isTypedOMSupported()) {
|
|
152
|
+
this.frame.attributeStyleMap.set("width", CSS.px(dimensions.width));
|
|
153
|
+
this.frame.attributeStyleMap.set("height", CSS.px(dimensions.height));
|
|
154
|
+
this.frame.attributeStyleMap.set("transform", new CSSTransformValue([
|
|
155
|
+
new CSSScale(ratio, ratio),
|
|
156
|
+
]));
|
|
157
|
+
} else {
|
|
158
|
+
this.frame.style.height = `${dimensions.height}px`;
|
|
159
|
+
this.frame.style.width = `${dimensions.width}px`;
|
|
160
|
+
this.frame.style.transform = `scale(${ratio})`;
|
|
161
|
+
}
|
|
147
162
|
const bcr = this.frame.getBoundingClientRect();
|
|
148
163
|
const hdiff = this.wrapper.clientHeight - bcr.height;
|
|
149
|
-
|
|
150
|
-
if(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
|
|
165
|
+
if(isTypedOMSupported()) {
|
|
166
|
+
this.frame.attributeStyleMap.set("top", CSS.px(hdiff / 2));
|
|
167
|
+
if(page === Page.left) {
|
|
168
|
+
const wdiff = this.wrapper.clientWidth - bcr.width;
|
|
169
|
+
this.frame.attributeStyleMap.set("left", CSS.px(wdiff));
|
|
170
|
+
} else if(page === Page.center) {
|
|
171
|
+
const wdiff = this.wrapper.clientWidth - bcr.width;
|
|
172
|
+
this.frame.attributeStyleMap.set("left", CSS.px(wdiff / 2));
|
|
173
|
+
} else {
|
|
174
|
+
this.frame.attributeStyleMap.set("left", CSS.px(0));
|
|
175
|
+
}
|
|
156
176
|
} else {
|
|
157
|
-
this.frame.style.
|
|
177
|
+
this.frame.style.top = `${hdiff / 2}px`;
|
|
178
|
+
if(page === Page.left) {
|
|
179
|
+
const wdiff = this.wrapper.clientWidth - bcr.width;
|
|
180
|
+
this.frame.style.left = `${wdiff}px`;
|
|
181
|
+
} else if(page === Page.center) {
|
|
182
|
+
const wdiff = this.wrapper.clientWidth - bcr.width;
|
|
183
|
+
this.frame.style.left = `${wdiff / 2}px`;
|
|
184
|
+
} else {
|
|
185
|
+
this.frame.style.left = "0px";
|
|
186
|
+
}
|
|
158
187
|
}
|
|
159
188
|
|
|
160
189
|
this.frame.style.removeProperty("visibility");
|
|
161
|
-
this.frame.
|
|
190
|
+
this.frame.ariaHidden = null;
|
|
162
191
|
this.frame.style.removeProperty("pointer-events");
|
|
163
192
|
this.frame.classList.remove("blank");
|
|
164
193
|
this.frame.classList.add("loaded");
|
|
@@ -175,7 +204,7 @@ export class FXLFrameManager {
|
|
|
175
204
|
if(!this.loaded) return;
|
|
176
205
|
this.deselect();
|
|
177
206
|
this.frame.style.visibility = "hidden";
|
|
178
|
-
this.frame.
|
|
207
|
+
this.frame.ariaHidden = "true";
|
|
179
208
|
this.frame.style.pointerEvents = "none";
|
|
180
209
|
this.frame.classList.add("blank");
|
|
181
210
|
this.frame.classList.remove("loaded");
|
|
@@ -193,6 +222,8 @@ export class FXLFrameManager {
|
|
|
193
222
|
this.source = "about:blank";
|
|
194
223
|
this.frame.contentWindow!.location.replace("about:blank");
|
|
195
224
|
this.frame.style.display = "none";
|
|
225
|
+
this.frame.style.width = "0px";
|
|
226
|
+
this.frame.style.height = "0px";
|
|
196
227
|
});
|
|
197
228
|
}
|
|
198
229
|
|
|
@@ -326,15 +357,27 @@ export class FXLFrameManager {
|
|
|
326
357
|
}
|
|
327
358
|
|
|
328
359
|
set width(width: number) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
360
|
+
if(isTypedOMSupported()) {
|
|
361
|
+
const current = this.wrapper.attributeStyleMap.get("width");
|
|
362
|
+
if(current instanceof CSSUnitValue && current.unit === "percent" && current.value === width) return;
|
|
363
|
+
this.wrapper.attributeStyleMap.set("width", CSS.percent(width));
|
|
364
|
+
} else {
|
|
365
|
+
const newWidth = `${width}%`;
|
|
366
|
+
if(this.wrapper.style.width === newWidth) return;
|
|
367
|
+
this.wrapper.style.width = newWidth;
|
|
368
|
+
}
|
|
332
369
|
}
|
|
333
370
|
|
|
334
371
|
set height(height: number) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
372
|
+
if(isTypedOMSupported()) {
|
|
373
|
+
const current = this.wrapper.attributeStyleMap.get("height");
|
|
374
|
+
if(current instanceof CSSUnitValue && current.unit === "px" && current.value === height) return;
|
|
375
|
+
this.wrapper.attributeStyleMap.set("height", CSS.px(height));
|
|
376
|
+
} else {
|
|
377
|
+
const newHeight = `${height}px`;
|
|
378
|
+
if(this.wrapper.style.height === newHeight) return;
|
|
379
|
+
this.wrapper.style.height = newHeight;
|
|
380
|
+
}
|
|
338
381
|
}
|
|
339
382
|
|
|
340
383
|
get window() {
|