@actualwave/messageport-dispatcher
Version:
Cross-domain EventDispatcher for MessagePort interface
218 lines (173 loc) • 6.03 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var eventDispatcher = require('@actualwave/event-dispatcher');
var hasOwn = require('@actualwave/has-own');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var hasOwn__default = /*#__PURE__*/_interopDefaultLegacy(hasOwn);
class MessagePortTarget {
constructor(sender, receiver) {
this.sender = sender || [];
this.receiver = receiver || [];
if (!(this.sender instanceof Array)) {
this.sender = [this.sender];
}
if (!(this.receiver instanceof Array)) {
this.receiver = [this.receiver];
}
}
/*
@param data
@param origin
*/
postMessage(...args) {
this.sender.forEach(item => item.postMessage(...args));
}
addEventListener(type, handler) {
this.receiver.forEach(item => item.addEventListener(type, handler));
}
removeEventListener(type, handler) {
this.receiver.forEach(item => item.removeEventListener(type, handler));
}
}
/**
* Created by Oleg Galaburda on 09.02.16.
*/
const createId = () => `MP/${Math.ceil(Math.random() * 10000)}/${Date.now()}`;
/**
* If toJSON method implemented on object, it will be called instead of converting to JSON string.
* This was made to utilize structured cloning algorithm for raw objects.
* https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
* In this case developer is responsible for converting linked objects.
* @param object
* @returns {Object}
*/
const toRawData = object => {
if (typeof object.toJSON === 'function') {
return object.toJSON();
}
return JSON.stringify(object);
};
/**
*
* @param data {Object|String}
* @returns {Object}
*/
const parseRawData = data => {
let object; // keep it undefined in case of error
if (eventDispatcher.isObject(data)) {
return data;
}
try {
return JSON.parse(data);
} catch (error) {// this isn't an event we are waiting for.
}
return object;
};
/**
* Created by Oleg Galaburda on 09.02.16.
*/
class MessagePortEvent {
constructor(event, dispatcherId) {
this.event = event;
this.dispatcherId = dispatcherId;
}
toJSON() {
return {
event: toRawData(this.event),
dispatcherId: this.dispatcherId
};
}
}
const isMessagePortEvent = object => eventDispatcher.isObject(object) && hasOwn__default['default'](object, 'dispatcherId') && hasOwn__default['default'](object, 'event');
const parseMessagePortEvent = object => {
const result = parseRawData(object);
if (result && isMessagePortEvent(result)) {
const {
event,
dispatcherId
} = result;
return new MessagePortEvent(parseRawData(event), dispatcherId);
}
return null;
};
/**
* Created by Oleg Galaburda on 09.02.16.
*/
class MessagePortDispatcher {
constructor(target = null, customPostMessageHandler = null, receiverEventPreprocessor = null, senderEventPreprocessor = null) {
this.dispatcherId = createId();
this.targetOrigin = '*';
this.target = target || self;
this.customPostMessageHandler = customPostMessageHandler;
this.senderEventPreprocessor = senderEventPreprocessor;
this.sender = eventDispatcher.createEventDispatcher();
this.receiver = eventDispatcher.createEventDispatcher(receiverEventPreprocessor);
this.target.addEventListener('message', event => this._postMessageListener(event));
}
addEventListener(eventType, listener, priority) {
this.receiver.addEventListener(eventType, listener, priority);
}
hasEventListener(eventType) {
return this.receiver.hasEventListener(eventType);
}
removeEventListener(eventType, listener) {
this.receiver.removeEventListener(eventType, listener);
}
removeAllEventListeners(eventType) {
this.receiver.removeAllEventListeners(eventType);
}
dispatchEvent(eventType, data, transferList) {
let event = eventDispatcher.getEvent(eventType, data);
if (this.senderEventPreprocessor) {
event = this.senderEventPreprocessor.call(this, event);
}
const eventJson = toRawData(new MessagePortEvent(event, this.dispatcherId));
return this._postMessageHandler(eventJson, transferList);
}
/**
* @private
*/
_postMessageHandler(data, transferList) {
const handler = this.customPostMessageHandler;
if (handler) {
return handler.call(this, data, this.targetOrigin, transferList);
}
return this.target.postMessage(data, this.targetOrigin, transferList);
}
/**
* @private
*/
_postMessageListener(event) {
// INFO .nativeEvent react-native thing, it contains event object coming from WebView
event = event.nativeEvent || event;
const message = parseMessagePortEvent(event.data);
if (message) {
if (message.dispatcherId === this.dispatcherId) {
this.sender.dispatchEvent(message.event);
} else {
this.receiver.dispatchEvent(message.event);
}
}
}
}
const createMessagePortDispatcher = (target, customPostMessageHandler, receiverEventPreprocessor, senderEventPreprocessor) => new MessagePortDispatcher(target, customPostMessageHandler, receiverEventPreprocessor, senderEventPreprocessor);
const factory = (getTarget, dispatcher = null) => () => {
if (!dispatcher) {
dispatcher = createMessagePortDispatcher(getTarget());
}
return dispatcher;
};
const getForSelf = factory(() => self);
const getForParent = factory(() => parent);
const getForTop = factory(() => top);
exports.MessagePortDispatcher = MessagePortDispatcher;
exports.MessagePortEvent = MessagePortEvent;
exports.MessagePortTarget = MessagePortTarget;
exports.createMessagePortDispatcher = createMessagePortDispatcher;
exports.default = MessagePortDispatcher;
exports.getForParent = getForParent;
exports.getForSelf = getForSelf;
exports.getForTop = getForTop;
exports.isMessagePortEvent = isMessagePortEvent;
exports.parseMessagePortEvent = parseMessagePortEvent;
//# sourceMappingURL=index.js.map