@openfin/web-utils 0.42.19 → 0.42.21
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/out/custom-elements/of-view.d.ts +6 -2
- package/out/custom-elements/of-view.js +57 -12
- package/out/utils/main.d.ts +1 -0
- package/out/utils/main.js +1 -0
- package/out/utils/utils.d.ts +2 -0
- package/out/utils/utils.js +50 -0
- package/package.json +1 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
declare const mixin: (Base: typeof HTMLElement) => {
|
|
2
2
|
new (): {
|
|
3
|
+
"__#1@#titleObserver": MutationObserver | null;
|
|
4
|
+
"__#1@#initObservers"(): void;
|
|
5
|
+
"__#1@#clearObservers"(): void;
|
|
6
|
+
readonly lastKnownUrl: string | undefined;
|
|
3
7
|
connectedCallback(): void;
|
|
4
|
-
|
|
8
|
+
readonly iframe: HTMLIFrameElement | null;
|
|
9
|
+
title: string;
|
|
5
10
|
brokerUrl: string | null;
|
|
6
11
|
name: string | null;
|
|
7
12
|
forceFrameName: string | null;
|
|
@@ -26,7 +31,6 @@ declare const mixin: (Base: typeof HTMLElement) => {
|
|
|
26
31
|
readonly offsetWidth: number;
|
|
27
32
|
outerText: string;
|
|
28
33
|
spellcheck: boolean;
|
|
29
|
-
title: string;
|
|
30
34
|
translate: boolean;
|
|
31
35
|
attachInternals(): ElementInternals;
|
|
32
36
|
click(): void;
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import { encodeOptions } from '../utils/options-encoder';
|
|
2
|
+
import { getTitleObserver, isSameOrigin } from '../utils/utils';
|
|
2
3
|
const mixin = (Base) => class _OfViewElement extends Base {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.#titleObserver = null;
|
|
7
|
+
}
|
|
8
|
+
#titleObserver;
|
|
9
|
+
#initObservers() {
|
|
10
|
+
// we can only monitor the DOM if the iframe is same-origin
|
|
11
|
+
const iframe = this.iframe;
|
|
12
|
+
if (iframe &&
|
|
13
|
+
iframe.contentDocument?.head &&
|
|
14
|
+
iframe.contentWindow &&
|
|
15
|
+
window.top &&
|
|
16
|
+
isSameOrigin(iframe.contentWindow.window, window.top)) {
|
|
17
|
+
this.#titleObserver = getTitleObserver(iframe.contentDocument.head, (title) => this.dispatchEvent(new CustomEvent('page-title-updated', { detail: { title } })));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
#clearObservers() {
|
|
21
|
+
if (this.#titleObserver) {
|
|
22
|
+
this.#titleObserver.disconnect();
|
|
23
|
+
this.#titleObserver = null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
get lastKnownUrl() {
|
|
27
|
+
return this.iframe?.contentDocument?.location.href;
|
|
28
|
+
}
|
|
3
29
|
connectedCallback() {
|
|
4
30
|
if (!this.name || !this.uuid) {
|
|
5
31
|
throw new Error('<of-view> Name or uuid attribute missing');
|
|
@@ -7,21 +33,28 @@ const mixin = (Base) => class _OfViewElement extends Base {
|
|
|
7
33
|
if (!this.src) {
|
|
8
34
|
throw new Error(`<of-view> missing 'src' attribute.`);
|
|
9
35
|
}
|
|
10
|
-
if (!this
|
|
11
|
-
|
|
12
|
-
|
|
36
|
+
if (!this.iframe) {
|
|
37
|
+
const iframe = document.createElement('iframe');
|
|
38
|
+
iframe.addEventListener('load', () => {
|
|
39
|
+
// reload observers every navigation
|
|
40
|
+
this.#initObservers();
|
|
41
|
+
});
|
|
42
|
+
iframe.addEventListener('unload', () => {
|
|
43
|
+
this.#clearObservers();
|
|
44
|
+
});
|
|
45
|
+
iframe.src = this.src;
|
|
13
46
|
if (this.allow) {
|
|
14
|
-
|
|
47
|
+
iframe.allow = this.allow;
|
|
15
48
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
49
|
+
iframe.style.height = '100%';
|
|
50
|
+
iframe.style.width = '100%';
|
|
51
|
+
iframe.style.border = 'none';
|
|
19
52
|
if (this.forceFrameName) {
|
|
20
53
|
// if forceFrameName is set, the consumer is intentionally breaking auto-connection
|
|
21
|
-
|
|
54
|
+
iframe.setAttribute('name', this.forceFrameName);
|
|
22
55
|
}
|
|
23
56
|
else {
|
|
24
|
-
|
|
57
|
+
iframe.setAttribute('name', encodeOptions({
|
|
25
58
|
brokerUrl: this.brokerUrl,
|
|
26
59
|
name: this.name,
|
|
27
60
|
uuid: this.uuid,
|
|
@@ -29,11 +62,23 @@ const mixin = (Base) => class _OfViewElement extends Base {
|
|
|
29
62
|
contextGroup: this.contextGroup
|
|
30
63
|
}, 'of-frame'));
|
|
31
64
|
}
|
|
32
|
-
|
|
33
|
-
this.appendChild(
|
|
65
|
+
iframe.setAttribute('id', this.name);
|
|
66
|
+
this.appendChild(iframe);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
get iframe() {
|
|
70
|
+
return this.querySelector(`iframe[id="${this.name}"]`);
|
|
71
|
+
}
|
|
72
|
+
get title() {
|
|
73
|
+
return this.getAttribute('title') ?? this.iframe?.title ?? '';
|
|
74
|
+
}
|
|
75
|
+
set title(val) {
|
|
76
|
+
this.setAttribute('title', val);
|
|
77
|
+
// keep iframe title attribute in sync with of-view
|
|
78
|
+
if (this.iframe) {
|
|
79
|
+
this.iframe.title = val;
|
|
34
80
|
}
|
|
35
81
|
}
|
|
36
|
-
#iframe;
|
|
37
82
|
get brokerUrl() {
|
|
38
83
|
return this.getAttribute('of-broker');
|
|
39
84
|
}
|
package/out/utils/main.d.ts
CHANGED
package/out/utils/main.js
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const isSameOrigin = (windowA, windowB) => {
|
|
2
|
+
try {
|
|
3
|
+
return windowA.origin === windowB.origin;
|
|
4
|
+
}
|
|
5
|
+
catch (error) {
|
|
6
|
+
// CORS errors on access of window proxy origin means the windows are in different origins.
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
export const getTitleObserver = (documentHead, setter) => {
|
|
11
|
+
const observer = new MutationObserver((mutations) => {
|
|
12
|
+
let shouldEmit = false;
|
|
13
|
+
mutations.forEach((mutation) => {
|
|
14
|
+
// title DOM structure looks like this <title><text_node /><title> and all mutations boil down to:
|
|
15
|
+
// - mutation.target is <text_node>: text_node changed
|
|
16
|
+
// - mutation.target is <title> : text_node either added or removed
|
|
17
|
+
// - mutation.target is <head> : title is either added or removed
|
|
18
|
+
// check easy cases first and early return
|
|
19
|
+
// case 1
|
|
20
|
+
if (mutation.target.parentNode?.nodeName === 'TITLE') {
|
|
21
|
+
shouldEmit = true;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// case 2
|
|
25
|
+
if (mutation.target.nodeName === 'TITLE') {
|
|
26
|
+
shouldEmit = true;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// case 3 - check removed nodes first
|
|
30
|
+
mutation.removedNodes.forEach((node) => {
|
|
31
|
+
if (node.nodeName === 'TITLE' || node.parentNode?.nodeName === 'TITLE') {
|
|
32
|
+
shouldEmit = true;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
mutation.addedNodes.forEach((node) => {
|
|
36
|
+
if (node.nodeName === 'TITLE' || node.parentNode?.nodeName === 'TITLE') {
|
|
37
|
+
shouldEmit = true;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
// don't use mutation record for values, just grab a new title elem from DOM
|
|
42
|
+
const title = documentHead.querySelector('title')?.textContent ?? '';
|
|
43
|
+
if (shouldEmit) {
|
|
44
|
+
setter(title);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
observer.observe(documentHead, { childList: true, subtree: true, characterData: true });
|
|
48
|
+
return observer;
|
|
49
|
+
};
|
|
50
|
+
// TODO: getFavIconObserver()
|