-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
112 lines (95 loc) · 2.29 KB
/
index.js
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
function toJSON(value) {
try {
return JSON.stringify(value);
} catch (err) {
return false;
}
}
function fromJSON(value) {
try {
return JSON.parse(value);
} catch (err) {
return false;
}
}
/**
* @class IFrameCommunicator
* @example
* ```
* const com = new IFrameCommunicator();
* com.on('ready', () => document.querySelector('root').style.backgroundColor = 'blue');
* com.ready();
* ```
*/
export default class IFrameCommunicator {
/**
* @param {string} id
* @param {array} initialDeps
*/
constructor(id, initialDeps) {
this.id = id;
this.remainingDeps = initialDeps;
this.listeners = {};
const eventListenerName = ('addEventListener' in window) ? 'addEventListener' : 'attachEvent';
window[eventListenerName]('message', (event) => {
if (!event || !event.data) {
return;
}
const data = fromJSON(event.data);
if (data.type === 'ping') {
event.source.postMessage(toJSON({
type: 'pong',
id: this.id
}), '*');
}
const index = this.remainingDeps.indexOf(data.id);
if (index >= 0) {
this.remainingDeps.splice(index, 1);
if (this.remainingDeps.length === 0) {
this.fire('ready');
}
}
});
}
/**
* @param {string} name
* @param {function} callback
* @return {IFrameCommunicator}
*/
on(name, callback) {
this.listeners[name] = this.listeners[name] || [];
this.listeners[name].push(callback);
return this;
}
/**
* @param {string} name
* @param {...mixed} args
* @return {boolean}
*/
fire(name, ...args) {
if (!this.listeners[name]) {
return false;
}
this.listeners[name].forEach((callback) => {
callback(...args);
});
return true;
}
/**
* Sends a "message" event with the `type` `ping` to all `window.frames` and
* thier frames, and so on...
*
* @return {void}
*/
ready() {
const bubble = (currentWindow = window.top) => {
for (let index = 0; index < currentWindow.frames.length; index + 1) {
if (currentWindow.frames[index] !== window) {
currentWindow.frames[index].postMessage(toJSON({ type: 'ping', id: this.id }), '*');
}
bubble(currentWindow.frames[index]);
}
};
bubble();
}
}