-
Notifications
You must be signed in to change notification settings - Fork 85
/
iframe-controller.ts
121 lines (110 loc) · 3.84 KB
/
iframe-controller.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* eslint-disable no-param-reassign */
/* eslint-disable no-unused-expressions */
import { ViewController, createDuplicateIframeWarning, createURL, createModalNotReadyError } from '@magic-sdk/provider';
/**
* Magic `<iframe>` overlay styles. These base styles enable `<iframe>` UI
* to render above all other DOM content.
*/
const overlayStyles: Partial<CSSStyleDeclaration> = {
display: 'none',
position: 'fixed',
top: '0',
right: '0',
width: '100%',
height: '100%',
borderRadius: '0',
border: 'none',
zIndex: '2147483647',
};
/**
* Apply iframe styles to the given element.
* @param elem - An element to apply styles using CSSOM.
*/
function applyOverlayStyles(elem: HTMLElement) {
for (const [cssProperty, value] of Object.entries(overlayStyles)) {
/* eslint-disable-next-line no-param-reassign */
(elem.style as any)[cssProperty as any] = value;
}
}
/**
* Checks if the given query params are associated with an active `<iframe>`
* instance.
*
* @param parameters - The unique, encoded query parameters to check for
* duplicates against.
*/
function checkForSameSrcInstances(parameters: string) {
const iframes: HTMLIFrameElement[] = [].slice.call(document.querySelectorAll('.magic-iframe'));
return Boolean(iframes.find((iframe) => iframe.src.includes(parameters)));
}
/**
* View controller for the Magic `<iframe>` overlay.
*/
export class IframeController extends ViewController {
private iframe!: Promise<HTMLIFrameElement>;
private activeElement: any = null;
/**
* Initializes the underlying `<iframe>` element.
* Initializes the underlying `Window.onmessage` event listener.
*/
protected init() {
(this as any).test = 'hello';
this.iframe = new Promise((resolve) => {
const onload = () => {
if (!checkForSameSrcInstances(encodeURIComponent(this.parameters))) {
const iframe = document.createElement('iframe');
iframe.classList.add('magic-iframe');
iframe.dataset.magicIframeLabel = createURL(this.endpoint).host;
iframe.title = 'Secure Modal';
iframe.src = createURL(`/send?params=${encodeURIComponent(this.parameters)}`, this.endpoint).href;
iframe.allow = 'identity-credentials-get; clipboard-read; clipboard-write';
applyOverlayStyles(iframe);
document.body.appendChild(iframe);
resolve(iframe);
} else {
createDuplicateIframeWarning().log();
}
};
// Check DOM state and load...
if (['loaded', 'interactive', 'complete'].includes(document.readyState)) {
onload();
} else {
// ...or check load events to load
window.addEventListener('load', onload, false);
}
});
window.addEventListener('message', (event: MessageEvent) => {
if (event.origin === this.endpoint) {
if (event.data && event.data.msgType && this.messageHandlers.size) {
// If the response object is undefined, we ensure it's at least an
// empty object before passing to the event listener.
/* istanbul ignore next */
event.data.response = event.data.response ?? {};
for (const handler of this.messageHandlers.values()) {
handler(event);
}
}
}
});
}
protected async showOverlay() {
const iframe = await this.iframe;
iframe.style.display = 'block';
this.activeElement = document.activeElement;
iframe.focus();
}
protected async hideOverlay() {
const iframe = await this.iframe;
iframe.style.display = 'none';
if (this.activeElement?.focus) this.activeElement.focus();
this.activeElement = null;
}
protected async _post(data: any) {
const iframe = await this.iframe;
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage(data, this.endpoint);
} else {
throw createModalNotReadyError();
}
}
}