Page MenuHomec4science

broadcast-channel.js
No OneTemporary

File Metadata

Created
Wed, Feb 5, 12:40

broadcast-channel.js

import { isPromise } from './util.js';
import { chooseMethod } from './method-chooser.js';
import { fillOptionsWithDefaults } from './options.js';
export var BroadcastChannel = function BroadcastChannel(name, options) {
this.name = name;
if (ENFORCED_OPTIONS) {
options = ENFORCED_OPTIONS;
}
this.options = fillOptionsWithDefaults(options);
this.method = chooseMethod(this.options); // isListening
this._iL = false;
/**
* _onMessageListener
* setting onmessage twice,
* will overwrite the first listener
*/
this._onML = null;
/**
* _addEventListeners
*/
this._addEL = {
message: [],
internal: []
};
/**
* Unsend message promises
* where the sending is still in progress
* @type {Set<Promise>}
*/
this._uMP = new Set();
/**
* _beforeClose
* array of promises that will be awaited
* before the channel is closed
*/
this._befC = [];
/**
* _preparePromise
*/
this._prepP = null;
_prepareChannel(this);
}; // STATICS
/**
* used to identify if someone overwrites
* window.BroadcastChannel with this
* See methods/native.js
*/
BroadcastChannel._pubkey = true;
/**
* clears the tmp-folder if is node
* @return {Promise<boolean>} true if has run, false if not node
*/
export function clearNodeFolder(options) {
options = fillOptionsWithDefaults(options);
var method = chooseMethod(options);
if (method.type === 'node') {
return method.clearNodeFolder().then(function () {
return true;
});
} else {
return Promise.resolve(false);
}
}
/**
* if set, this method is enforced,
* no mather what the options are
*/
var ENFORCED_OPTIONS;
export function enforceOptions(options) {
ENFORCED_OPTIONS = options;
} // PROTOTYPE
BroadcastChannel.prototype = {
postMessage: function postMessage(msg) {
if (this.closed) {
throw new Error('BroadcastChannel.postMessage(): ' + 'Cannot post message after channel has closed');
}
return _post(this, 'message', msg);
},
postInternal: function postInternal(msg) {
return _post(this, 'internal', msg);
},
set onmessage(fn) {
var time = this.method.microSeconds();
var listenObj = {
time: time,
fn: fn
};
_removeListenerObject(this, 'message', this._onML);
if (fn && typeof fn === 'function') {
this._onML = listenObj;
_addListenerObject(this, 'message', listenObj);
} else {
this._onML = null;
}
},
addEventListener: function addEventListener(type, fn) {
var time = this.method.microSeconds();
var listenObj = {
time: time,
fn: fn
};
_addListenerObject(this, type, listenObj);
},
removeEventListener: function removeEventListener(type, fn) {
var obj = this._addEL[type].find(function (obj) {
return obj.fn === fn;
});
_removeListenerObject(this, type, obj);
},
close: function close() {
var _this = this;
if (this.closed) {
return;
}
this.closed = true;
var awaitPrepare = this._prepP ? this._prepP : Promise.resolve();
this._onML = null;
this._addEL.message = [];
return awaitPrepare // wait until all current sending are processed
.then(function () {
return Promise.all(Array.from(_this._uMP));
}) // run before-close hooks
.then(function () {
return Promise.all(_this._befC.map(function (fn) {
return fn();
}));
}) // close the channel
.then(function () {
return _this.method.close(_this._state);
});
},
get type() {
return this.method.type;
},
get isClosed() {
return this.closed;
}
};
/**
* Post a message over the channel
* @returns {Promise} that resolved when the message sending is done
*/
function _post(broadcastChannel, type, msg) {
var time = broadcastChannel.method.microSeconds();
var msgObj = {
time: time,
type: type,
data: msg
};
var awaitPrepare = broadcastChannel._prepP ? broadcastChannel._prepP : Promise.resolve();
return awaitPrepare.then(function () {
var sendPromise = broadcastChannel.method.postMessage(broadcastChannel._state, msgObj); // add/remove to unsend messages list
broadcastChannel._uMP.add(sendPromise);
sendPromise["catch"]().then(function () {
return broadcastChannel._uMP["delete"](sendPromise);
});
return sendPromise;
});
}
function _prepareChannel(channel) {
var maybePromise = channel.method.create(channel.name, channel.options);
if (isPromise(maybePromise)) {
channel._prepP = maybePromise;
maybePromise.then(function (s) {
// used in tests to simulate slow runtime
/*if (channel.options.prepareDelay) {
await new Promise(res => setTimeout(res, this.options.prepareDelay));
}*/
channel._state = s;
});
} else {
channel._state = maybePromise;
}
}
function _hasMessageListeners(channel) {
if (channel._addEL.message.length > 0) return true;
if (channel._addEL.internal.length > 0) return true;
return false;
}
function _addListenerObject(channel, type, obj) {
channel._addEL[type].push(obj);
_startListening(channel);
}
function _removeListenerObject(channel, type, obj) {
channel._addEL[type] = channel._addEL[type].filter(function (o) {
return o !== obj;
});
_stopListening(channel);
}
function _startListening(channel) {
if (!channel._iL && _hasMessageListeners(channel)) {
// someone is listening, start subscribing
var listenerFn = function listenerFn(msgObj) {
channel._addEL[msgObj.type].forEach(function (obj) {
if (msgObj.time >= obj.time) {
obj.fn(msgObj.data);
}
});
};
var time = channel.method.microSeconds();
if (channel._prepP) {
channel._prepP.then(function () {
channel._iL = true;
channel.method.onMessage(channel._state, listenerFn, time);
});
} else {
channel._iL = true;
channel.method.onMessage(channel._state, listenerFn, time);
}
}
}
function _stopListening(channel) {
if (channel._iL && !_hasMessageListeners(channel)) {
// noone is listening, stop subscribing
channel._iL = false;
var time = channel.method.microSeconds();
channel.method.onMessage(channel._state, null, time);
}
}

Event Timeline