const kHandler = Symbol('handler') const kListeners = Symbol('listeners') const kOrigin = Symbol('origin') const kOther = Symbol('other') class PostMessageEventEmitter { constructor(other, origin) { if (typeof other.postMessage !== 'function') { throw new TypeError('Expected other.postMessage to be a function') } if (typeof origin !== 'string') { throw new TypeError('Expected origin to be a string') } this[kListeners] = {} this[kOrigin] = origin this[kOther] = other this[kHandler] = (ev) => { // Check origin of message if (ev.origin !== this[kOrigin]) return if (ev.source !== this[kOther]) return // Check message data if (ev.data === null) return if (typeof ev.data !== 'object') return if (typeof ev.data.name !== 'string') return // Emit event if (this[kListeners][ev.data.name]) { for (const fn of this[kListeners][ev.data.name]) { Promise.resolve().then(() => fn(ev.data.name, ev.data.data)) } } } window.addEventListener('message', this[kHandler]) } on(name, fn) { if (typeof name !== 'string') throw new TypeError('Expected name to be a string') if (typeof fn !== 'function') throw new TypeError('Expected fn to be a function') if (this[kListeners][name]) { this[kListeners][name].push(fn) } else { this[kListeners][name] = [fn] } } emit(name, data) { if (typeof name !== 'string') throw new TypeError('Expected name to be a string') this[kOther].postMessage({name, data}, this[kOrigin]) } stop() { window.removeEventListener('message', this[kHandler]) } } module.exports = PostMessageEventEmitter