messageport-observable
Version:
This provides some magic wrappers for [MessagePort][1] objects and things that resemble them (windows/iframes, workers, etc.). The wrapped objects still have the same API as MessagePorts, but also have some additional features.
1,202 lines (953 loc) • 35.1 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.MessagePortObservable = {})));
}(this, (function (exports) { 'use strict';
var array = Array.isArray;
var _function = function isFunction(arg) {
return typeof arg === 'function';
};
var object = function isObject(arg) {
var type = typeof arg;
return Boolean(arg) && (type === 'object' || type === 'function');
};
var stamp = function isStamp(arg) {
return _function(arg) && _function(arg.compose);
};
// More proper implementation would be
// isDescriptor(obj) || isStamp(obj)
// but there is no sense since stamp is function and function is object.
var composable = object;
var assign = Object.assign;
var plainObject = function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' &&
Object.getPrototypeOf(value) === Object.prototype;
};
/**
* The 'src' argument plays the command role.
* The returned values is always of the same type as the 'src'.
* @param dst The object to merge into
* @param src The object to merge from
* @returns {*}
*/
function mergeOne(dst, src) {
if (src === undefined) return dst;
// According to specification arrays must be concatenated.
// Also, the '.concat' creates a new array instance. Overrides the 'dst'.
if (array(src)) return (array(dst) ? dst : []).concat(src);
// Now deal with non plain 'src' object. 'src' overrides 'dst'
// Note that functions are also assigned! We do not deep merge functions.
if (!plainObject(src)) return src;
// See if 'dst' is allowed to be mutated.
// If not - it's overridden with a new plain object.
var returnValue = object(dst) ? dst : {};
var keys = Object.keys(src);
for (var i = 0; i < keys.length; i += 1) {
var key = keys[i];
var srcValue = src[key];
// Do not merge properties with the 'undefined' value.
if (srcValue !== undefined) {
var dstValue = returnValue[key];
// Recursive calls to mergeOne() must allow only plain objects or arrays in dst
var newDst = plainObject(dstValue) || array(srcValue) ? dstValue : {};
// deep merge each property. Recursion!
returnValue[key] = mergeOne(newDst, srcValue);
}
}
return returnValue;
}
var merge = function (dst) {
for (var i = 1; i < arguments.length; i++) {
dst = mergeOne(dst, arguments[i]);
}
return dst;
};
var slice = Array.prototype.slice;
/**
* Creates new factory instance.
* @returns {Function} The new factory function.
*/
function createFactory() {
return function Stamp(options) {
var descriptor = Stamp.compose || {};
// Next line was optimized for most JS VMs. Please, be careful here!
var obj = {__proto__: descriptor.methods}; // jshint ignore:line
merge(obj, descriptor.deepProperties);
assign(obj, descriptor.properties);
Object.defineProperties(obj, descriptor.propertyDescriptors || {});
if (!descriptor.initializers || descriptor.initializers.length === 0) return obj;
if (options === undefined) options = {};
var inits = descriptor.initializers;
var length = inits.length;
for (var i = 0; i < length; i += 1) {
var initializer = inits[i];
if (_function(initializer)) {
var returnedValue = initializer.call(obj, options,
{instance: obj, stamp: Stamp, args: slice.apply(arguments)});
obj = returnedValue === undefined ? obj : returnedValue;
}
}
return obj;
};
}
/**
* Returns a new stamp given a descriptor and a compose function implementation.
* @param {Descriptor} [descriptor={}] The information about the object the stamp will be creating.
* @param {Compose} composeFunction The "compose" function implementation.
* @returns {Stamp}
*/
function createStamp(descriptor, composeFunction) {
var Stamp = createFactory();
if (descriptor.staticDeepProperties) {
merge(Stamp, descriptor.staticDeepProperties);
}
if (descriptor.staticProperties) {
assign(Stamp, descriptor.staticProperties);
}
if (descriptor.staticPropertyDescriptors) {
Object.defineProperties(Stamp, descriptor.staticPropertyDescriptors);
}
var composeImplementation = _function(Stamp.compose) ? Stamp.compose : composeFunction;
Stamp.compose = function _compose() {
return composeImplementation.apply(this, arguments);
};
assign(Stamp.compose, descriptor);
return Stamp;
}
function concatAssignFunctions(dstObject, srcArray, propName) {
if (!array(srcArray)) return;
var length = srcArray.length;
var dstArray = dstObject[propName] || [];
dstObject[propName] = dstArray;
for (var i = 0; i < length; i += 1) {
var fn = srcArray[i];
if (_function(fn) && dstArray.indexOf(fn) < 0) {
dstArray.push(fn);
}
}
}
function combineProperties(dstObject, srcObject, propName, action) {
if (!object(srcObject[propName])) return;
if (!object(dstObject[propName])) dstObject[propName] = {};
action(dstObject[propName], srcObject[propName]);
}
function deepMergeAssign(dstObject, srcObject, propName) {
combineProperties(dstObject, srcObject, propName, merge);
}
function mergeAssign(dstObject, srcObject, propName) {
combineProperties(dstObject, srcObject, propName, assign);
}
/**
* Mutates the dstDescriptor by merging the srcComposable data into it.
* @param {Descriptor} dstDescriptor The descriptor object to merge into.
* @param {Composable} [srcComposable] The composable
* (either descriptor or stamp) to merge data form.
*/
function mergeComposable(dstDescriptor, srcComposable) {
var srcDescriptor = (srcComposable && srcComposable.compose) || srcComposable;
mergeAssign(dstDescriptor, srcDescriptor, 'methods');
mergeAssign(dstDescriptor, srcDescriptor, 'properties');
deepMergeAssign(dstDescriptor, srcDescriptor, 'deepProperties');
mergeAssign(dstDescriptor, srcDescriptor, 'propertyDescriptors');
mergeAssign(dstDescriptor, srcDescriptor, 'staticProperties');
deepMergeAssign(dstDescriptor, srcDescriptor, 'staticDeepProperties');
mergeAssign(dstDescriptor, srcDescriptor, 'staticPropertyDescriptors');
mergeAssign(dstDescriptor, srcDescriptor, 'configuration');
deepMergeAssign(dstDescriptor, srcDescriptor, 'deepConfiguration');
concatAssignFunctions(dstDescriptor, srcDescriptor.initializers, 'initializers');
concatAssignFunctions(dstDescriptor, srcDescriptor.composers, 'composers');
}
/**
* Given the list of composables (stamp descriptors and stamps) returns
* a new stamp (composable factory function).
* @typedef {Function} Compose
* @param {...(Composable)} [arguments] The list of composables.
* @returns {Stamp} A new stamp (aka composable factory function)
*/
var compose = function compose() {
var descriptor = {};
var composables = [];
if (composable(this)) {
mergeComposable(descriptor, this);
composables.push(this);
}
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (composable(arg)) {
mergeComposable(descriptor, arg);
composables.push(arg);
}
}
var stamp$$1 = createStamp(descriptor, compose);
var composers = descriptor.composers;
if (array(composers) && composers.length > 0) {
for (var j = 0; j < composers.length; j += 1) {
var composer = composers[j];
var returnedValue = composer({stamp: stamp$$1, composables: composables});
stamp$$1 = stamp(returnedValue) ? returnedValue : stamp$$1;
}
}
return stamp$$1;
};
/**
* The Stamp Descriptor
* @typedef {Function|Object} Descriptor
* @returns {Stamp} A new stamp based on this Stamp
* @property {Object} [methods] Methods or other data used as object instances' prototype
* @property {Array<Function>} [initializers] List of initializers called for each object instance
* @property {Array<Function>} [composers] List of callbacks called each time a composition happens
* @property {Object} [properties] Shallow assigned properties of object instances
* @property {Object} [deepProperties] Deeply merged properties of object instances
* @property {Object} [staticProperties] Shallow assigned properties of Stamps
* @property {Object} [staticDeepProperties] Deeply merged properties of Stamps
* @property {Object} [configuration] Shallow assigned properties of Stamp arbitrary metadata
* @property {Object} [deepConfiguration] Deeply merged properties of Stamp arbitrary metadata
* @property {Object} [propertyDescriptors] ES5 Property Descriptors applied to object instances
* @property {Object} [staticPropertyDescriptors] ES5 Property Descriptors applied to Stamps
*/
/**
* The Stamp factory function
* @typedef {Function} Stamp
* @returns {*} Instantiated object
* @property {Descriptor} compose - The Stamp descriptor and composition function
*/
/**
* A composable object - stamp or descriptor
* @typedef {Stamp|Descriptor} Composable
*/
function createShortcut(propName) {
return function (arg) {
var param = {};
param[propName] = arg;
return this && this.compose ? this.compose(param) : compose(param);
};
}
var properties = createShortcut('properties');
var staticProperties = createShortcut('staticProperties');
var configuration = createShortcut('configuration');
var deepProperties = createShortcut('deepProperties');
var staticDeepProperties = createShortcut('staticDeepProperties');
var deepConfiguration = createShortcut('deepConfiguration');
var initializers = createShortcut('initializers');
var shortcut = compose({
staticProperties: {
methods: createShortcut('methods'),
props: properties,
properties: properties,
statics: staticProperties,
staticProperties: staticProperties,
conf: configuration,
configuration: configuration,
deepProps: deepProperties,
deepProperties: deepProperties,
deepStatics: staticDeepProperties,
staticDeepProperties: staticDeepProperties,
deepConf: deepConfiguration,
deepConfiguration: deepConfiguration,
init: initializers,
initializers: initializers,
composers: createShortcut('composers'),
propertyDescriptors: createShortcut('propertyDescriptors'),
staticPropertyDescriptors: createShortcut('staticPropertyDescriptors')
}
});
var concat = Array.prototype.concat;
function extractFunctions() {
var fns = concat.apply([], arguments).filter(_function);
return fns.length === 0 ? undefined : fns;
}
function standardiseDescriptor(descr) {
if (!object(descr)) return descr;
var methods = descr.methods;
var properties = descr.properties;
var props = descr.props;
var initializers = descr.initializers;
var init = descr.init;
var composers = descr.composers;
var deepProperties = descr.deepProperties;
var deepProps = descr.deepProps;
var pd = descr.propertyDescriptors;
var staticProperties = descr.staticProperties;
var statics = descr.statics;
var staticDeepProperties = descr.staticDeepProperties;
var deepStatics = descr.deepStatics;
var spd = descr.staticPropertyDescriptors;
var configuration = descr.configuration;
var conf = descr.conf;
var deepConfiguration = descr.deepConfiguration;
var deepConf = descr.deepConf;
var p = object(props) || object(properties) ?
assign({}, props, properties) : undefined;
var dp = object(deepProps) ? merge({}, deepProps) : undefined;
dp = object(deepProperties) ? merge(dp, deepProperties) : dp;
var sp = object(statics) || object(staticProperties) ?
assign({}, statics, staticProperties) : undefined;
var sdp = object(deepStatics) ? merge({}, deepStatics) : undefined;
sdp = object(staticDeepProperties) ? merge(sdp, staticDeepProperties) : sdp;
var c = object(conf) || object(configuration) ?
assign({}, conf, configuration) : undefined;
var dc = object(deepConf) ? merge({}, deepConf) : undefined;
dc = object(deepConfiguration) ? merge(dc, deepConfiguration) : dc;
var ii = extractFunctions(init, initializers);
var cc = extractFunctions(composers);
var descriptor = {};
if (methods) descriptor.methods = methods;
if (p) descriptor.properties = p;
if (ii) descriptor.initializers = ii;
if (cc) descriptor.composers = cc;
if (dp) descriptor.deepProperties = dp;
if (sp) descriptor.staticProperties = sp;
if (sdp) descriptor.staticDeepProperties = sdp;
if (pd) descriptor.propertyDescriptors = pd;
if (spd) descriptor.staticPropertyDescriptors = spd;
if (c) descriptor.configuration = c;
if (dc) descriptor.deepConfiguration = dc;
return descriptor;
}
function stampit() {
var length = arguments.length, args = [];
for (var i = 0; i < length; i += 1) {
var arg = arguments[i];
args.push(stamp(arg) ? arg : standardiseDescriptor(arg));
}
return compose.apply(this || baseStampit, args); // jshint ignore:line
}
var baseStampit = shortcut.compose({
staticProperties: {
create: function () { return this.apply(this, arguments); },
compose: stampit // infecting
}
});
var shortcuts = shortcut.compose.staticProperties;
for (var prop in shortcuts) stampit[prop] = shortcuts[prop].bind(baseStampit);
stampit.compose = stampit.bind();
var it = stampit;
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var zenObservable$1 = createCommonjsModule(function (module, exports) {
(function(fn, name) { fn(exports, module); })(function(exports, module) { // === Symbol Support ===
function hasSymbol(name) {
return typeof Symbol === "function" && Boolean(Symbol[name]);
}
function getSymbol(name) {
return hasSymbol(name) ? Symbol[name] : "@@" + name;
}
// Ponyfill Symbol.observable for interoperability with other libraries
if (typeof Symbol === "function" && !Symbol.observable) {
Symbol.observable = Symbol("observable");
}
// === Abstract Operations ===
function getMethod(obj, key) {
var value = obj[key];
if (value == null)
return undefined;
if (typeof value !== "function")
throw new TypeError(value + " is not a function");
return value;
}
function getSpecies(obj) {
var ctor = obj.constructor;
if (ctor !== undefined) {
ctor = ctor[getSymbol("species")];
if (ctor === null) {
ctor = undefined;
}
}
return ctor !== undefined ? ctor : Observable;
}
function addMethods(target, methods) {
Object.keys(methods).forEach(function(k) {
var desc = Object.getOwnPropertyDescriptor(methods, k);
desc.enumerable = false;
Object.defineProperty(target, k, desc);
});
}
function cleanupSubscription(subscription) {
// Assert: observer._observer is undefined
var cleanup = subscription._cleanup;
if (!cleanup)
return;
// Drop the reference to the cleanup function so that we won't call it
// more than once
subscription._cleanup = undefined;
// Call the cleanup function
cleanup();
}
function subscriptionClosed(subscription) {
return subscription._observer === undefined;
}
function closeSubscription(subscription) {
if (subscriptionClosed(subscription))
return;
subscription._observer = undefined;
cleanupSubscription(subscription);
}
function cleanupFromSubscription(subscription) {
return function() { subscription.unsubscribe(); };
}
function Subscription(observer, subscriber) {
// Assert: subscriber is callable
// The observer must be an object
if (Object(observer) !== observer)
throw new TypeError("Observer must be an object");
this._cleanup = undefined;
this._observer = observer;
var start = getMethod(observer, "start");
if (start)
start.call(observer, this);
if (subscriptionClosed(this))
return;
observer = new SubscriptionObserver(this);
try {
// Call the subscriber function
var cleanup$0 = subscriber.call(undefined, observer);
// The return value must be undefined, null, a subscription object, or a function
if (cleanup$0 != null) {
if (typeof cleanup$0.unsubscribe === "function")
cleanup$0 = cleanupFromSubscription(cleanup$0);
else if (typeof cleanup$0 !== "function")
throw new TypeError(cleanup$0 + " is not a function");
this._cleanup = cleanup$0;
}
} catch (e) {
// If an error occurs during startup, then attempt to send the error
// to the observer
observer.error(e);
return;
}
// If the stream is already finished, then perform cleanup
if (subscriptionClosed(this))
cleanupSubscription(this);
}
addMethods(Subscription.prototype = {}, {
get closed() { return subscriptionClosed(this) },
unsubscribe: function() { closeSubscription(this); },
});
function SubscriptionObserver(subscription) {
this._subscription = subscription;
}
addMethods(SubscriptionObserver.prototype = {}, {
get closed() { return subscriptionClosed(this._subscription) },
next: function(value) {
var subscription = this._subscription;
// If the stream is closed, then return undefined
if (subscriptionClosed(subscription))
return undefined;
var observer = subscription._observer;
var m = getMethod(observer, "next");
// If the observer doesn't support "next", then return undefined
if (!m)
return undefined;
// Send the next value to the sink
return m.call(observer, value);
},
error: function(value) {
var subscription = this._subscription;
// If the stream is closed, throw the error to the caller
if (subscriptionClosed(subscription))
throw value;
var observer = subscription._observer;
subscription._observer = undefined;
try {
var m$0 = getMethod(observer, "error");
// If the sink does not support "error", then throw the error to the caller
if (!m$0)
throw value;
value = m$0.call(observer, value);
} catch (e) {
try { cleanupSubscription(subscription); }
finally { throw e }
}
cleanupSubscription(subscription);
return value;
},
complete: function(value) {
var subscription = this._subscription;
// If the stream is closed, then return undefined
if (subscriptionClosed(subscription))
return undefined;
var observer = subscription._observer;
subscription._observer = undefined;
try {
var m$1 = getMethod(observer, "complete");
// If the sink does not support "complete", then return undefined
value = m$1 ? m$1.call(observer, value) : undefined;
} catch (e) {
try { cleanupSubscription(subscription); }
finally { throw e }
}
cleanupSubscription(subscription);
return value;
},
});
function Observable(subscriber) {
// The stream subscriber must be a function
if (typeof subscriber !== "function")
throw new TypeError("Observable initializer must be a function");
this._subscriber = subscriber;
}
addMethods(Observable.prototype, {
subscribe: function(observer) { for (var args = [], __$0 = 1; __$0 < arguments.length; ++__$0) args.push(arguments[__$0]);
if (typeof observer === 'function') {
observer = {
next: observer,
error: args[0],
complete: args[1],
};
}
return new Subscription(observer, this._subscriber);
},
forEach: function(fn) { var __this = this;
return new Promise(function(resolve, reject) {
if (typeof fn !== "function")
return Promise.reject(new TypeError(fn + " is not a function"));
__this.subscribe({
_subscription: null,
start: function(subscription) {
if (Object(subscription) !== subscription)
throw new TypeError(subscription + " is not an object");
this._subscription = subscription;
},
next: function(value) {
var subscription = this._subscription;
if (subscription.closed)
return;
try {
return fn(value);
} catch (err) {
reject(err);
subscription.unsubscribe();
}
},
error: reject,
complete: resolve,
});
});
},
map: function(fn) { var __this = this;
if (typeof fn !== "function")
throw new TypeError(fn + " is not a function");
var C = getSpecies(this);
return new C(function(observer) { return __this.subscribe({
next: function(value) {
if (observer.closed)
return;
try { value = fn(value); }
catch (e) { return observer.error(e) }
return observer.next(value);
},
error: function(e) { return observer.error(e) },
complete: function(x) { return observer.complete(x) },
}); });
},
filter: function(fn) { var __this = this;
if (typeof fn !== "function")
throw new TypeError(fn + " is not a function");
var C = getSpecies(this);
return new C(function(observer) { return __this.subscribe({
next: function(value) {
if (observer.closed)
return;
try { if (!fn(value)) return undefined }
catch (e) { return observer.error(e) }
return observer.next(value);
},
error: function(e) { return observer.error(e) },
complete: function() { return observer.complete() },
}); });
},
reduce: function(fn) { var __this = this;
if (typeof fn !== "function")
throw new TypeError(fn + " is not a function");
var C = getSpecies(this);
var hasSeed = arguments.length > 1;
var hasValue = false;
var seed = arguments[1];
var acc = seed;
return new C(function(observer) { return __this.subscribe({
next: function(value) {
if (observer.closed)
return;
var first = !hasValue;
hasValue = true;
if (!first || hasSeed) {
try { acc = fn(acc, value); }
catch (e) { return observer.error(e) }
} else {
acc = value;
}
},
error: function(e) { observer.error(e); },
complete: function() {
if (!hasValue && !hasSeed) {
observer.error(new TypeError("Cannot reduce an empty sequence"));
return;
}
observer.next(acc);
observer.complete();
},
}); });
},
flatMap: function(fn) { var __this = this;
if (typeof fn !== "function")
throw new TypeError(fn + " is not a function");
var C = getSpecies(this);
return new C(function(observer) {
var completed = false;
var subscriptions = [];
// Subscribe to the outer Observable
var outer = __this.subscribe({
next: function(value) {
if (fn) {
try {
value = fn(value);
} catch (x) {
observer.error(x);
return;
}
}
// Subscribe to the inner Observable
Observable.from(value).subscribe({
_subscription: null,
start: function(s) { subscriptions.push(this._subscription = s); },
next: function(value) { observer.next(value); },
error: function(e) { observer.error(e); },
complete: function() {
var i = subscriptions.indexOf(this._subscription);
if (i >= 0)
subscriptions.splice(i, 1);
closeIfDone();
}
});
},
error: function(e) {
return observer.error(e);
},
complete: function() {
completed = true;
closeIfDone();
}
});
function closeIfDone() {
if (completed && subscriptions.length === 0)
observer.complete();
}
return function() {
subscriptions.forEach(function(s) { return s.unsubscribe(); });
outer.unsubscribe();
};
});
},
});
Object.defineProperty(Observable.prototype, getSymbol("observable"), {
value: function() { return this },
writable: true,
configurable: true,
});
addMethods(Observable, {
from: function(x) {
var C = typeof this === "function" ? this : Observable;
if (x == null)
throw new TypeError(x + " is not an object");
var method = getMethod(x, getSymbol("observable"));
if (method) {
var observable$0 = method.call(x);
if (Object(observable$0) !== observable$0)
throw new TypeError(observable$0 + " is not an object");
if (observable$0.constructor === C)
return observable$0;
return new C(function(observer) { return observable$0.subscribe(observer); });
}
if (hasSymbol("iterator") && (method = getMethod(x, getSymbol("iterator")))) {
return new C(function(observer) {
for (var __$0 = (method.call(x))[Symbol.iterator](), __$1; __$1 = __$0.next(), !__$1.done;) { var item$0 = __$1.value;
observer.next(item$0);
if (observer.closed)
return;
}
observer.complete();
});
}
if (Array.isArray(x)) {
return new C(function(observer) {
for (var i$0 = 0; i$0 < x.length; ++i$0) {
observer.next(x[i$0]);
if (observer.closed)
return;
}
observer.complete();
});
}
throw new TypeError(x + " is not observable");
},
of: function() { for (var items = [], __$0 = 0; __$0 < arguments.length; ++__$0) items.push(arguments[__$0]);
var C = typeof this === "function" ? this : Observable;
return new C(function(observer) {
for (var i$1 = 0; i$1 < items.length; ++i$1) {
observer.next(items[i$1]);
if (observer.closed)
return;
}
observer.complete();
});
},
});
Object.defineProperty(Observable, getSymbol("species"), {
get: function() { return this },
configurable: true,
});
exports.Observable = Observable;
}, "*");
});
var zenObservable = zenObservable$1.Observable;
var toConsumableArray = function (arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
} else {
return Array.from(arr);
}
};
/**
* This is a "lightweight" (is it?) wrapper around MessagePort / Window / Worker
* objects (things that have a postMessage method).
*/
/**
* This is used to ensure that when the wrapped object is set, method bindings
* happen
*/
var wrapper = it().props({ isWrapped: true }).propertyDescriptors({
wrapped: {
enumerable: true,
configurable: true,
get: function get$$1() {
return null;
},
set: function set$$1(obj) {
if (!obj) throw new Error("Cannot set wrapped object to falsy");
delete this.wrapped;
Object.defineProperty(this, 'wrapped', {
value: obj,
writable: true,
configurable: true,
enumerable: true
});
}
}
}).init(function (_, _ref) {
var instance = _ref.instance,
stamp = _ref.stamp;
instance.wrapper = stamp;
}).methods({
unwrap: function unwrap() {
if (!this.wrapped) throw new Error("No wrapped object in this wrapper");
return this.wrapped.isWrapped ? this.wrapped.unwrap() : this.wrapped;
}
});
function filteringPropertyDescriptor(type) {
var attribute = 'on' + type;
return {
enumerable: true,
configurable: false,
get: function get$$1() {
return this.wrapped[attribute];
},
set: function set$$1(listener) {
var eventFilter = this.eventFilters[type];
if (eventFilter) {
this.wrapped[attribute] = function (event) {
if (eventFilter.call(this, event)) listener.call(this, event);
};
} else {
this.wrapped[attribute] = listener;
}
}
};
}
// Use a WeakMap if poss. That way, if the messageport loses the ref to
// the listener on its own, there's no memory leak
var mapImpl = typeof WeakMap === 'function' ? WeakMap : Map;
/**
* This is stamp returns an object that wraps event handlers so that they only
* fire when the given filters apply
*/
var filteringPort = wrapper.init(function (_, _ref2) {
var instance = _ref2.instance,
stamp = _ref2.stamp;
instance.eventFilters = {};
instance.eventListeners = {};
}).methods({
filter: function filter() {
var newFilter = void 0,
type = void 0;
if (arguments.length === 1) {
type = 'message';
newFilter = arguments[0];
} else {
type = arguments[0];
newFilter = arguments[1];
}
var clone = this.wrapper(this);
clone.autostart = false;
clone.eventFilters[type] = newFilter;
return clone;
},
addEventListener: function addEventListener(type, listener, options) {
if (!this.eventListeners[type]) this.eventListeners[type] = new mapImpl();
// This ensures that we are only notified about events that haven't been
// filtered out
if (!this.eventListeners[type].has(listener)) {
var eventFilter = this.eventFilters[type];
var wrappedListener = void 0;
if (eventFilter) {
wrappedListener = function wrappedListener(event) {
if (eventFilter(event)) {
typeof listener.handleEvent === 'function' ? listener.handleEvent(event) : listener(event);
}
};
} else {
wrappedListener = listener;
}
this.wrapped.addEventListener(type, wrappedListener, options);
this.eventListeners[type].set(listener, wrappedListener);
}
},
removeEventListener: function removeEventListener(type, listener, options) {
if (this.eventListeners[type] && this.eventListeners[type].has(listener)) {
this.wrapped.removeEventListener(type, this.eventListeners[type].get(listener), options);
this.eventListeners[type].delete(listener);
}
}
}).propertyDescriptors({
onmessage: filteringPropertyDescriptor('message'),
onmessageerror: filteringPropertyDescriptor('messageerror')
});
var observablePort = it().props({
autostart: true
}).init(function (_, _ref3) {
var instance = _ref3.instance;
// Add standardised observable accessor, if poss.
if (typeof Symbol === 'function' && Symbol.observable) instance[Symbol.observable] = function () {
return instance.observable;
};
}).propertyDescriptors({
observable: {
enumerable: true,
configurable: true,
get: function get$$1() {
var _this = this;
var observable = new zenObservable(function (observer) {
var messageCb = observer.next.bind(observer);
var messageErrorCb = observer.error.bind(observer);
_this.addEventListener('message', messageCb);
_this.addEventListener('messageerror', messageErrorCb);
if (_this.autostart && _this.start) _this.start();
return function () {
_this.removeEventListener('message', messageCb);
_this.removeEventListener('messageerror', messageErrorCb);
};
});
delete this.observable;
Object.defineProperty(this, 'observable', {
value: observable,
writable: true,
configurable: true,
enumerable: true
});
return observable;
}
}
}).methods({
subscribe: function subscribe() {
var _observable;
return (_observable = this.observable).subscribe.apply(_observable, arguments);
},
postMessageWithReply: function postMessageWithReply(message, listener) {
var messageChannel = new MessageChannel(),
replyPort = this.wrapper(messageChannel.port1);
this.postMessage(message, [messageChannel.port2]);
if (listener) listener(replyPort);else return replyPort;
},
postObservable: function postObservable(observable) {
var _this2 = this;
var splat = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var close = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var complete = close && typeof this.close === 'function' ? function () {
return _this2.close();
} : undefined;
var next = splat ? function (args) {
return _this2.postMessage.apply(_this2, toConsumableArray(args));
} : this.postMessage.bind(this);
return zenObservable.from(observable).subscribe(next, complete, complete);
},
postMessageWithObservable: function postMessageWithObservable(message, observable) {
var splat = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var messageChannel = new MessageChannel(),
postPort = this.wrapper(messageChannel.port1);
this.postMessage(message, [messageChannel.port2]);
return postPort.postObservable(observable, splat, true);
},
subscribeWithPort: function subscribeWithPort(listener) {
var wrapper = this.wrapper;
return this.subscribe(function (event) {
var port = event.ports[0];
var wrappedPort = port ? wrapper(port) : null;
listener(event, wrappedPort);
});
},
subscribeAndPostReplies: function subscribeAndPostReplies(listener) {
var splat = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
return this.subscribeWithPort(function (event, replyPort) {
var response = listener(event);
if (response && replyPort) replyPort.postObservable(response, splat, true);
}, splat);
}
});
var filteringObservablePort = filteringPort.compose(observablePort);
/**
* A generic wrapper around MessagePort objects (incl. workers)
*/
var wrapPort = filteringObservablePort.init(function (port, _ref4) {
var instance = _ref4.instance;
if (!port) throw new Error("No port given");
instance.wrapped = port;
var _arr = ['postMessage', 'start', 'close'];
for (var _i = 0; _i < _arr.length; _i++) {
var method = _arr[_i];
if (typeof port[method] === 'function') instance[method] = port[method].bind(port);
}
});
/**
* A MessagePort-alike interface for windows. Adds the following:
*
* - filters to ensure that all events sent and received have an origin setting.
* - shims the postMessage method so that it looks like the MessagePort one
*/
var wrapWindow = filteringObservablePort.init(function (options, _ref5) {
var instance = _ref5.instance;
if (!options.window) throw new Error("No window given");
if (!options.origin || options.origin === "") throw new Error("No origin given");
// Override the wrapper variable so that subsequently created ports don't
// use this constructor. This can be provided as a parameter if you want to
// compose in some stuff.
instance.wrapper = options.wrapPort ? options.wrapPort : wrapPort;
instance.wrapped = options.window;
instance.origin = options.origin;
// Set up initial filters if a specific origin is given.
if (instance.origin !== '*') {
instance.eventFilters['message'] = instance.eventFilters['messageerror'] = function (event) {
return event.origin === options.origin;
};
}
}).methods({
// Provide a compliant postMessage
postMessage: function postMessage(message, transferList) {
this.wrapped.postMessage(message, this.origin, transferList);
}
});
exports.wrapPort = wrapPort;
exports.wrapWindow = wrapWindow;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=messageport-observable.js.map