@readium/navigator 1.2.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/LICENSE +28 -0
- package/README.MD +11 -0
- package/dist/assets/AccessibleDfA.otf +0 -0
- package/dist/assets/iAWriterDuospace-Regular.ttf +0 -0
- package/dist/index.js +6263 -0
- package/dist/index.umd.cjs +107 -0
- package/package.json +65 -0
- package/src/Navigator.ts +66 -0
- package/src/audio/engine/AudioEngine.ts +136 -0
- package/src/audio/engine/WebAudioEngine.ts +286 -0
- package/src/audio/engine/index.ts +2 -0
- package/src/audio/index.ts +1 -0
- package/src/epub/EpubNavigator.ts +507 -0
- package/src/epub/frame/FrameBlobBuilder.ts +211 -0
- package/src/epub/frame/FrameComms.ts +142 -0
- package/src/epub/frame/FrameManager.ts +134 -0
- package/src/epub/frame/FramePoolManager.ts +179 -0
- package/src/epub/frame/index.ts +3 -0
- package/src/epub/fxl/FXLCoordinator.ts +152 -0
- package/src/epub/fxl/FXLFrameManager.ts +286 -0
- package/src/epub/fxl/FXLFramePoolManager.ts +632 -0
- package/src/epub/fxl/FXLPeripherals.ts +587 -0
- package/src/epub/fxl/FXLPeripheralsDebug.ts +46 -0
- package/src/epub/fxl/FXLSpreader.ts +95 -0
- package/src/epub/fxl/index.ts +5 -0
- package/src/epub/index.ts +3 -0
- package/src/helpers/sML.ts +120 -0
- package/src/index.ts +3 -0
- package/types/src/Navigator.d.ts +41 -0
- package/types/src/audio/engine/AudioEngine.d.ts +114 -0
- package/types/src/audio/engine/WebAudioEngine.d.ts +107 -0
- package/types/src/audio/engine/index.d.ts +2 -0
- package/types/src/audio/index.d.ts +1 -0
- package/types/src/epub/EpubNavigator.d.ts +66 -0
- package/types/src/epub/frame/FrameBlobBuilder.d.ts +13 -0
- package/types/src/epub/frame/FrameComms.d.ts +26 -0
- package/types/src/epub/frame/FrameManager.d.ts +21 -0
- package/types/src/epub/frame/FramePoolManager.d.ts +17 -0
- package/types/src/epub/frame/index.d.ts +3 -0
- package/types/src/epub/fxl/FXLCoordinator.d.ts +37 -0
- package/types/src/epub/fxl/FXLFrameManager.d.ts +41 -0
- package/types/src/epub/fxl/FXLFramePoolManager.d.ts +93 -0
- package/types/src/epub/fxl/FXLPeripherals.d.ts +97 -0
- package/types/src/epub/fxl/FXLPeripheralsDebug.d.ts +13 -0
- package/types/src/epub/fxl/FXLSpreader.d.ts +12 -0
- package/types/src/epub/fxl/index.d.ts +5 -0
- package/types/src/epub/index.d.ts +3 -0
- package/types/src/helpers/sML.d.ts +51 -0
- package/types/src/index.d.ts +3 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import sML from "../../helpers/sML";
|
|
2
|
+
|
|
3
|
+
export interface Point {
|
|
4
|
+
X: number;
|
|
5
|
+
Y: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export enum HorizontalThird {
|
|
9
|
+
Left,
|
|
10
|
+
Center,
|
|
11
|
+
Right
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export enum VerticalThird {
|
|
15
|
+
Top,
|
|
16
|
+
Middle,
|
|
17
|
+
Bottom
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface NinthPoint {
|
|
21
|
+
X: HorizontalThird | null;
|
|
22
|
+
Y: VerticalThird | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BibiEvent {
|
|
26
|
+
Target: EventTarget | null;
|
|
27
|
+
Coord: Point | null;
|
|
28
|
+
Ratio: Point | null;
|
|
29
|
+
Division: NinthPoint | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class FXLCoordinator {
|
|
33
|
+
HTML: HTMLElement;
|
|
34
|
+
Head: HTMLHeadElement;
|
|
35
|
+
Body: HTMLElement;
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this.HTML = document.documentElement;
|
|
39
|
+
this.Head = document.head;
|
|
40
|
+
this.Body = document.body;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
getElementCoord(El: any) {
|
|
45
|
+
var Coord = { X: El["offsetLeft"], Y: El["offsetTop"] };
|
|
46
|
+
while(El.offsetParent) El = El.offsetParent, Coord.X += El["offsetLeft"], Coord.Y += El["offsetTop"];
|
|
47
|
+
return Coord;
|
|
48
|
+
}
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
private outerWidth = 0;
|
|
52
|
+
private outerHeight = 0;
|
|
53
|
+
refreshOuterPixels(_: DOMRect) {
|
|
54
|
+
if(sML.OS.iOS) return; // No need on iOS
|
|
55
|
+
this.outerHeight = window.outerHeight - window.innerHeight;
|
|
56
|
+
if(sML.OS.Android && sML.UA.Chrome) {
|
|
57
|
+
if(window.screen.height > window.innerHeight)
|
|
58
|
+
// This is a hack: since outer/inner are zero, we assume there's a
|
|
59
|
+
// top (chrome url bar) and bottom (android controls) bar and divide
|
|
60
|
+
// by 1.5 because the top bar is roughtly 2x height of the bottom one
|
|
61
|
+
this.outerHeight = (window.screen.height - window.innerHeight) / 1.5;
|
|
62
|
+
}
|
|
63
|
+
this.outerWidth = window.outerWidth - window.innerWidth;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getBibiEventCoord(Eve: TouchEvent | MouseEvent, touch=0): Point {
|
|
67
|
+
const Coord: Point = { X:0, Y:0 };
|
|
68
|
+
if(/^touch/.test(Eve.type)) {
|
|
69
|
+
Coord.X = (Eve as TouchEvent).touches[touch].screenX;
|
|
70
|
+
Coord.Y = (Eve as TouchEvent).touches[touch].screenY;
|
|
71
|
+
} else {
|
|
72
|
+
Coord.X = (Eve as MouseEvent).screenX;
|
|
73
|
+
Coord.Y = (Eve as MouseEvent).screenY;
|
|
74
|
+
}
|
|
75
|
+
if(((Eve.target as HTMLElement).ownerDocument?.documentElement || (Eve.target as HTMLDocument).documentElement) === this.HTML) {
|
|
76
|
+
Coord.X -= (this.HTML.scrollLeft + this.Body.scrollLeft);
|
|
77
|
+
Coord.Y -= (this.HTML.scrollTop + this.Body.scrollTop);
|
|
78
|
+
} else {
|
|
79
|
+
/*
|
|
80
|
+
var Item = Eve.target.ownerDocument.documentElement.Item;
|
|
81
|
+
ItemCoord = this.getElementCoord(Item);
|
|
82
|
+
if(!Item.PrePaginated && !Item.Outsourcing) ItemCoord.X += settings.S["item-padding-left"], ItemCoord.Y += settings.S["item-padding-top"];
|
|
83
|
+
Coord.X = (Coord.X + ItemCoord.X - R.Main.scrollLeft) * R.Main.Transformation.Scale + R.Main.Transformation.Translation.X;
|
|
84
|
+
Coord.Y = (Coord.Y + ItemCoord.Y - R.Main.scrollTop ) * R.Main.Transformation.Scale + R.Main.Transformation.Translation.Y;
|
|
85
|
+
*/
|
|
86
|
+
}
|
|
87
|
+
Coord.X -= this.outerWidth;
|
|
88
|
+
Coord.Y -= this.outerHeight;
|
|
89
|
+
return Coord;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getTouchDistance(Eve: TouchEvent) {
|
|
93
|
+
if (Eve.touches.length !== 2) return 0;
|
|
94
|
+
const x1 = Eve.touches[0].screenX - this.outerWidth;
|
|
95
|
+
const y1 = Eve.touches[0].screenY - this.outerHeight;
|
|
96
|
+
const x2 = Eve.touches[1].screenX - this.outerWidth;
|
|
97
|
+
const y2 = Eve.touches[1].screenY - this.outerHeight;
|
|
98
|
+
return Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2)));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getTouchCenter(Eve: TouchEvent): Point | null {
|
|
102
|
+
if (Eve.touches.length !== 2) return null;
|
|
103
|
+
const subL = (this.HTML.scrollLeft + this.Body.scrollLeft);
|
|
104
|
+
const subT = (this.HTML.scrollTop + this.Body.scrollTop);
|
|
105
|
+
const x1 = Eve.touches[0].screenX - this.outerWidth - subL;
|
|
106
|
+
const y1 = Eve.touches[0].screenY - this.outerHeight - subT;
|
|
107
|
+
const x2 = Eve.touches[1].screenX - this.outerWidth - subL;
|
|
108
|
+
const y2 = Eve.touches[1].screenY - this.outerHeight - subT;
|
|
109
|
+
return { X: (x1 + x2) / 2, Y: (y1 + y2) / 2 };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getBibiEvent(Eve: Event): BibiEvent {
|
|
113
|
+
if(!Eve) return {
|
|
114
|
+
Coord: null,
|
|
115
|
+
Division: null,
|
|
116
|
+
Ratio: null,
|
|
117
|
+
Target: null
|
|
118
|
+
};
|
|
119
|
+
const Coord = this.getBibiEventCoord(Eve as TouchEvent | MouseEvent);
|
|
120
|
+
let FlipperWidth = 0.3; // TODO flipper-width
|
|
121
|
+
const Ratio = {
|
|
122
|
+
X: Coord.X / window.innerWidth,
|
|
123
|
+
Y: Coord.Y / window.innerHeight
|
|
124
|
+
};
|
|
125
|
+
let BorderT, BorderB, BorderL, BorderR;
|
|
126
|
+
if(FlipperWidth < 1) { // Ratio
|
|
127
|
+
BorderL = BorderT = FlipperWidth;
|
|
128
|
+
BorderR = BorderB = 1 - FlipperWidth;
|
|
129
|
+
} else { // Pixel to Ratio
|
|
130
|
+
BorderL = FlipperWidth / window.innerWidth;
|
|
131
|
+
BorderT = FlipperWidth / window.innerHeight;
|
|
132
|
+
BorderR = 1 - BorderL;
|
|
133
|
+
BorderB = 1 - BorderT;
|
|
134
|
+
}
|
|
135
|
+
const Division: NinthPoint = {
|
|
136
|
+
X: null,
|
|
137
|
+
Y: null
|
|
138
|
+
};
|
|
139
|
+
if(Ratio.X < BorderL) Division.X = HorizontalThird.Left;
|
|
140
|
+
else if(BorderR < Ratio.X) Division.X = HorizontalThird.Right;
|
|
141
|
+
else Division.X = HorizontalThird.Center;
|
|
142
|
+
if(Ratio.Y < BorderT) Division.Y = VerticalThird.Top;
|
|
143
|
+
else if(BorderB < Ratio.Y) Division.Y = VerticalThird.Bottom;
|
|
144
|
+
else Division.Y = VerticalThird.Middle;
|
|
145
|
+
return {
|
|
146
|
+
Target: Eve.target,
|
|
147
|
+
Coord: Coord,
|
|
148
|
+
Ratio: Ratio,
|
|
149
|
+
Division: Division
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { Loader, ModuleName } from "@readium/navigator-html-injectables";
|
|
2
|
+
import { Page, ReadingProgression } from "@readium/shared";
|
|
3
|
+
import { FrameComms } from "../frame/FrameComms";
|
|
4
|
+
import { FXLPeripherals } from "./FXLPeripherals";
|
|
5
|
+
import { ReadiumWindow } from "../../../../navigator-html-injectables/types/src/helpers/dom";
|
|
6
|
+
|
|
7
|
+
export class FXLFrameManager {
|
|
8
|
+
private frame: HTMLIFrameElement;
|
|
9
|
+
private loader: Loader | undefined;
|
|
10
|
+
public source: string;
|
|
11
|
+
private comms: FrameComms | undefined;
|
|
12
|
+
private readonly peripherals: FXLPeripherals;
|
|
13
|
+
|
|
14
|
+
private currModules: ModuleName[] = [];
|
|
15
|
+
|
|
16
|
+
// NEW
|
|
17
|
+
public wrapper: HTMLDivElement;
|
|
18
|
+
public debugHref: string;
|
|
19
|
+
private loadPromise: Promise<Window> | undefined;
|
|
20
|
+
private showPromise: Promise<void> | undefined;
|
|
21
|
+
|
|
22
|
+
constructor(peripherals: FXLPeripherals, direction: ReadingProgression, debugHref: string) {
|
|
23
|
+
this.peripherals = peripherals;
|
|
24
|
+
this.debugHref = debugHref;
|
|
25
|
+
this.frame = document.createElement("iframe");
|
|
26
|
+
this.frame.classList.add("readium-navigator-iframe");
|
|
27
|
+
this.frame.classList.add("blank");
|
|
28
|
+
this.frame.scrolling = "no";
|
|
29
|
+
this.frame.style.visibility = "hidden";
|
|
30
|
+
this.frame.style.setProperty("aria-hidden", "true");
|
|
31
|
+
this.frame.style.display = "none";
|
|
32
|
+
this.frame.style.position = "absolute";
|
|
33
|
+
this.frame.style.pointerEvents = "none";
|
|
34
|
+
this.frame.style.transformOrigin = "0 0";
|
|
35
|
+
this.frame.style.transform = "scale(1)";
|
|
36
|
+
this.frame.style.background = "#fff";
|
|
37
|
+
this.frame.style.touchAction = "none";
|
|
38
|
+
this.frame.dataset.originalHref = debugHref;
|
|
39
|
+
this.source = "about:blank";
|
|
40
|
+
|
|
41
|
+
// NEW
|
|
42
|
+
this.wrapper = document.createElement("div");
|
|
43
|
+
this.wrapper.style.position = "relative";
|
|
44
|
+
this.wrapper.style.float = this.wrapper.style.cssFloat = direction === ReadingProgression.rtl ? "right" : "left";
|
|
45
|
+
|
|
46
|
+
this.wrapper.appendChild(this.frame);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async load(modules: ModuleName[], source: string): Promise<Window> {
|
|
50
|
+
if(this.source === source && this.loadPromise/* && this.loaded*/) {
|
|
51
|
+
if([...this.currModules].sort().join("|") === [...modules].sort().join("|")) {
|
|
52
|
+
return this.loadPromise;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if(this.loaded && this.source !== source) {
|
|
56
|
+
this.window.stop();
|
|
57
|
+
}
|
|
58
|
+
this.source = source;
|
|
59
|
+
this.loadPromise = new Promise((res, rej) => {
|
|
60
|
+
if(this.loader && this.loaded) {
|
|
61
|
+
const wnd = this.frame.contentWindow!;
|
|
62
|
+
// Check if currently loaded modules are equal
|
|
63
|
+
if([...this.currModules].sort().join("|") === [...modules].sort().join("|")) {
|
|
64
|
+
try { res(wnd); this.loadPromise = undefined; } catch (error) { };
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// TODO
|
|
68
|
+
this.comms?.halt();
|
|
69
|
+
this.loader.destroy();
|
|
70
|
+
this.loader = new Loader(wnd as ReadiumWindow, modules);
|
|
71
|
+
this.currModules = modules;
|
|
72
|
+
this.comms = undefined;
|
|
73
|
+
try { res(wnd); this.loadPromise = undefined; } catch (error) {}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.frame.addEventListener("load", () => {
|
|
77
|
+
const wnd = this.frame.contentWindow!;
|
|
78
|
+
this.loader = new Loader(wnd as ReadiumWindow, modules);
|
|
79
|
+
this.currModules = modules;
|
|
80
|
+
this.peripherals.observe(this.wrapper);
|
|
81
|
+
this.peripherals.observe(wnd);
|
|
82
|
+
try { res(wnd); } catch (error) {};
|
|
83
|
+
}, { once: true });
|
|
84
|
+
this.frame.addEventListener("error", (e) => {
|
|
85
|
+
try { rej(e.error); this.loadPromise = undefined; } catch (error) {};
|
|
86
|
+
}, { once: true });
|
|
87
|
+
this.frame.style.removeProperty("display");
|
|
88
|
+
this.frame.contentWindow!.location.replace(this.source);
|
|
89
|
+
});
|
|
90
|
+
return this.loadPromise;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Parses the page size from the viewport meta tag of the loaded resource.
|
|
94
|
+
loadPageSize(): { width: number, height: number } {
|
|
95
|
+
const wnd = this.frame.contentWindow!;
|
|
96
|
+
|
|
97
|
+
// Try to get the page size from the viewport meta tag
|
|
98
|
+
const viewport = wnd.document.head.querySelector(
|
|
99
|
+
"meta[name=viewport]"
|
|
100
|
+
) as HTMLMetaElement;
|
|
101
|
+
if (viewport) {
|
|
102
|
+
const regex = /(\w+) *= *([^\s,]+)/g;
|
|
103
|
+
let match;
|
|
104
|
+
let width = 0, height = 0;
|
|
105
|
+
while ((match = regex.exec(viewport.content))) {
|
|
106
|
+
if(match[1] === "width")
|
|
107
|
+
width = Number.parseFloat(match[2]);
|
|
108
|
+
else if(match[1] === "height")
|
|
109
|
+
height = Number.parseFloat(match[2]);
|
|
110
|
+
}
|
|
111
|
+
if(width > 0 && height > 0)
|
|
112
|
+
return { width, height };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Otherwise get it from the size of the loaded content
|
|
116
|
+
return {
|
|
117
|
+
width: wnd.document.body.scrollWidth,
|
|
118
|
+
height: wnd.document.body.scrollHeight
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
update(page?: Page) {
|
|
123
|
+
if(!this.loaded) return;
|
|
124
|
+
const dimensions = this.loadPageSize();
|
|
125
|
+
this.frame.style.height = `${dimensions.height}px`;
|
|
126
|
+
this.frame.style.width = `${dimensions.width}px`;
|
|
127
|
+
const ratio = Math.min(this.wrapper.clientWidth / dimensions.width, this.wrapper.clientHeight / dimensions.height);
|
|
128
|
+
this.frame.style.transform = `scale(${ratio})`;
|
|
129
|
+
const bcr = this.frame.getBoundingClientRect();
|
|
130
|
+
const hdiff = this.wrapper.clientHeight - bcr.height;
|
|
131
|
+
this.frame.style.top = `${hdiff / 2}px`;
|
|
132
|
+
if(page === Page.left) {
|
|
133
|
+
const wdiff = this.wrapper.clientWidth - bcr.width;
|
|
134
|
+
this.frame.style.left = `${wdiff}px`;
|
|
135
|
+
} else if(page === Page.center) {
|
|
136
|
+
const wdiff = this.wrapper.clientWidth - bcr.width;
|
|
137
|
+
this.frame.style.left = `${wdiff / 2}px`;
|
|
138
|
+
} else {
|
|
139
|
+
this.frame.style.left = "0px";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.frame.style.removeProperty("visibility");
|
|
143
|
+
this.frame.style.removeProperty("aria-hidden");
|
|
144
|
+
this.frame.style.removeProperty("pointer-events");
|
|
145
|
+
this.frame.classList.remove("blank");
|
|
146
|
+
this.frame.classList.add("loaded");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async destroy() {
|
|
150
|
+
await this.unfocus();
|
|
151
|
+
this.loader?.destroy();
|
|
152
|
+
this.wrapper.remove();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async unload() {
|
|
156
|
+
if(!this.loaded) return;
|
|
157
|
+
this.deselect();
|
|
158
|
+
this.frame.style.visibility = "hidden";
|
|
159
|
+
this.frame.style.setProperty("aria-hidden", "true");
|
|
160
|
+
this.frame.style.pointerEvents = "none";
|
|
161
|
+
this.frame.classList.add("blank");
|
|
162
|
+
this.frame.classList.remove("loaded");
|
|
163
|
+
this.comms?.halt();
|
|
164
|
+
this.loader?.destroy();
|
|
165
|
+
this.comms = undefined;
|
|
166
|
+
this.frame.blur();
|
|
167
|
+
return new Promise<void>((res, rej) => {
|
|
168
|
+
this.frame.addEventListener("load", () => {
|
|
169
|
+
try { this.showPromise = undefined; res(); } catch (error) {};
|
|
170
|
+
}, { once: true });
|
|
171
|
+
this.frame.addEventListener("error", (e) => {
|
|
172
|
+
try { this.showPromise = undefined; rej(e.error); } catch (error) {};
|
|
173
|
+
}, { once: true });
|
|
174
|
+
this.source = "about:blank";
|
|
175
|
+
this.frame.contentWindow!.location.replace("about:blank");
|
|
176
|
+
this.frame.style.display = "none";
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
deselect() {
|
|
181
|
+
this.frame.contentWindow?.getSelection()?.removeAllRanges();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async unfocus(): Promise<void> {
|
|
185
|
+
if(this.frame.parentElement) {
|
|
186
|
+
if(this.comms === undefined) return;
|
|
187
|
+
return new Promise((res, _) => {
|
|
188
|
+
this.comms?.send("unfocus", undefined, (_: boolean) => {
|
|
189
|
+
this.comms?.halt();
|
|
190
|
+
this.showPromise = undefined;
|
|
191
|
+
res();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
} else
|
|
195
|
+
this.comms?.halt();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private cachedPage: Page | undefined = undefined;
|
|
199
|
+
async show(page: Page): Promise<void> {
|
|
200
|
+
if(!this.frame.parentElement) {
|
|
201
|
+
console.warn("Trying to show frame that is not attached to the DOM");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if(!this.loaded) {
|
|
205
|
+
this.showPromise = undefined;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if(this.showPromise) {
|
|
209
|
+
if(this.cachedPage !== page) {
|
|
210
|
+
this.update(page); // TODO fix that this can theoretically happen before the page is fully loaded
|
|
211
|
+
this.cachedPage = page;
|
|
212
|
+
}
|
|
213
|
+
return this.showPromise;
|
|
214
|
+
};
|
|
215
|
+
// this.update(page);
|
|
216
|
+
this.cachedPage = page;
|
|
217
|
+
if(this.comms) this.comms.resume();
|
|
218
|
+
else this.comms = new FrameComms(this.frame.contentWindow!, this.source);
|
|
219
|
+
this.showPromise = new Promise<void>((res, _) => {
|
|
220
|
+
this.comms!.send("focus", undefined, (_: boolean) => {
|
|
221
|
+
// this.showPromise = undefined; Don't do this
|
|
222
|
+
this.update(this.cachedPage);
|
|
223
|
+
res();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
return this.showPromise;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async activate(): Promise<void> {
|
|
230
|
+
return new Promise<void>((res, _) => {
|
|
231
|
+
if(!this.comms) return res(); // TODO: investigate when this is the case
|
|
232
|
+
this.comms?.send("activate", undefined, () => {
|
|
233
|
+
res();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
get element() {
|
|
239
|
+
return this.wrapper;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
get iframe() {
|
|
243
|
+
return this.frame;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
get realSize() {
|
|
247
|
+
return this.frame.getBoundingClientRect();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
get loaded() {
|
|
251
|
+
return this.frame.contentWindow && this.frame.contentWindow.location.href !== "about:blank";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
set width(width: number) {
|
|
255
|
+
const newWidth = `${width}%`;
|
|
256
|
+
if(this.wrapper.style.width === newWidth) return;
|
|
257
|
+
this.wrapper.style.width = newWidth;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
set height(height: number) {
|
|
261
|
+
const newHeight = `${height}px`;
|
|
262
|
+
if(this.wrapper.style.height === newHeight) return;
|
|
263
|
+
this.wrapper.style.height = newHeight;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
get window() {
|
|
267
|
+
if(!this.frame.contentWindow) throw Error("Trying to use frame window when it doesn't exist");
|
|
268
|
+
return this.frame.contentWindow;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
get atLeft() {
|
|
272
|
+
return this.window.scrollX < 5;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
get atRight() {
|
|
276
|
+
return this.window.scrollX > this.window.document.scrollingElement!.scrollWidth - this.window.innerWidth - 5
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
get msg() {
|
|
280
|
+
return this.comms;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
get ldr() {
|
|
284
|
+
return this.loader;
|
|
285
|
+
}
|
|
286
|
+
}
|