cryptalk/client/source/vendor/castrato.js

300 lines
8.0 KiB
JavaScript
Raw Normal View History

2021-11-03 15:57:54 -04:00
/**
* Licensed under the MIT License
*
* Copyright (c) 2014 Pehr Boman (github.com/unkelpehr)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/** @license Licenced under MIT - castrato - ©2014 Pehr Boman <github.com/unkelpehr> */
let
/**
* Contains the next unique node id.
*
* @property index
* @type {Integer}
* @private
*/
index = 0,
/**
* Contains all subscriptions
*
* @property subs
* @type {Object}
* @private
*/
subs = {},
/**
* Contains all emits that has been done with the `persistent` parameter set to `true`.
*
* @property emits
* @type {Object}
* @private
*/
emits = {},
/**
* An empty function that does not accept any arguments.
*
* @property noop
* @type {function}
* @private
*/
noop = function () {};
/**
* Creates a new entry in the `subs` object.
*
* @method on
* @private
* @param {Integer} fromId The unique subscriber ID.
* @param {String} event The event to subscribe to.
* @param {Function} handler A function to execute when the event is triggered.
*/
function on (fromId, event, handler, once) {
let i, item, subscription = [fromId, handler, handler.length > 1];
// Create if needed a namespace for this event and push the subscription.
(subs[event] || (subs[event] = [])).push(subscription);
// If it exists a persistent event that matches that which is currently being bound;
// loop through and each of them and emit to this handler.
if (emits[event]) {
i = 0;
subscription = [subscription];
while ((item = emits[event][i++])) {
emit(
0, // `persistent`
0, // `event`
item[0], // `data`
item[1], // `handler`
subscription // `explicitSubs`
);
if (once) {
break;
}
}
}
}
/**
* Removes all event handlers originating from `fromId` and optionally filter by handler.
*
* @method off
* @private
* @param {Integer} fromId The unique subscriber ID.
* @param {String} event The event to unsubscribe from.
* @param {Function} [handler=null] The original handler that was attached to this event. If not passed, all subscriptions will be removed.
*/
function off (fromId, event, handler) {
let sub,
i = 0,
toSubs = subs[event];
if (toSubs) {
while ((sub = toSubs[i++])) {
if (sub[0] === fromId && (!handler || handler === sub[1])) {
toSubs.splice(--i, 1);
}
}
}
}
/**
* Loops through all subscriptions, calling all handlers attached to given `event`.
*
* @method emit
* @private
* @param {Integer} fromId The unique subscriber ID.
* @param {String} event The event to emit
* @param {Object} [data=undefined] Parameters to pass along to the event handler.
* @param {Function} [handler=undefined] A function to execute when all event handlers has returned.
*/
function emit (persistent, event, data, callback, explicitSubs) {
let sub,
toSubs = explicitSubs || subs[event] || [],
total = toSubs.length,
left,
loop,
answers = [],
done;
// Add any wildcard subscriptions to the target subscriptions.
if (subs['*']) {
toSubs = toSubs.concat(subs['*']);
}
// Wildcard subscriptions shouldn't be counted as subscribers when passed to a possible emit callback.
loop = left = toSubs.length;
// Don't continue setup for calling all the subscribers if there isn't any.
if (loop) {
// If the emit function does not include a callback;
// we still have to set `done` to `noop` so that event callbacks
// does not try to execute something that is not a function.
done = !callback ? noop : function (data) {
if (data) {
answers.push(data);
}
if (!--left) {
callback(answers, total);
callback = 0;
}
};
// Execute all handlers that are bound to this event.
// Passing `done` if the handler expects it - otherwise decrementing the `left` variable.
while ((sub = toSubs[--loop])) {
sub[1](data, sub[2] ? done : left--, event);
}
}
// `func` get destructed when called.
// It has to be called at least once - even if no one was subscribing.
// Execute it if it still exists.
if (!left && callback) {
callback(answers, total);
}
// Save this emit if the `persistent` parameter is set to `true`.
if (persistent) {
(emits[event] || (emits[event] = [])).push([data, callback]);
}
}
/**
* Castrato entrypoint
*
* @constructor
* @returns {Castrato}
*/
function Castrato () {
this.nodeId = index++;
return this;
}
/**
* Execute all handlers attached to the given event.
*
* @method emit
* @param {String} event The event to emit
* @param {Object} [data=undefined] Parameters to pass along to the event handler.
* @param {Function} [func=undefined] A function to execute when all event handlers has returned.
* @return {Castrato} `this`
* @example
* $.emit('something');
* $.emit('something', { foo: 'bar' });
* $.emit('something', { foo: 'bar' }, function (data, subscribers) {
* console.log('Emit done, a total of ' + subscribers + ' subscribers returned: ', data);
* });
*/
Castrato.prototype.emit = function (persistent, event, data, handler) {
// emit('something', { data: true }, function () {});
if (persistent !== true && persistent !== false) {
handler = data;
data = event;
event = persistent;
persistent = false;
}
emit(persistent, event, data, handler);
return this;
};
/**
* Attach an event handler function for an event.
*
* @method on
* @param {String} event The event to subscribe to.
* @param {Function} handler A function to execute when the event is triggered.
* @return {Castrato} `this`
* @example
* $.on('something', function (data) {
* console.log('Got something!', data);
* });
*/
Castrato.prototype.on = function (event, handler) {
on(this.nodeId, event, handler);
return this;
};
/**
* Attach an event handler function for an event which will only be fired once.
*
* @method once
* @param {String} event The event to subscribe to.
* @param {Function} handler A function to execute when the event is triggered.
* @return {Castrato} `this`
* @example
* $.once('something', function (data) {
* console.log('Got something!', data);
* });
*/
Castrato.prototype.once = function (event, handler) {
on(this.nodeId, event, function wrapper (data, done) {
off(this.nodeId, event, wrapper);
handler(data, (handler.length > 1) ? done : done());
}, true);
return this;
};
/**
* Removes an event handler function for an event.
*
* @method off
* @param {String} event The event to unsubscribe from.
* @param {Function} [handler=null] The original handler that was attached to this event. If not passed, all subscriptions will be removed.
* @return {Castrato} `this`
* @example
* $.off('something');
* $.off('something else', handler);
*/
Castrato.prototype.off = function (event, handler) {
off(this.nodeId, event, handler);
return this;
};
// Only used in testing.
// Should get removed in production (and will be removed in the minified version)
Castrato.prototype.destroy = function () {
this.nodeId = 0;
index = 0;
subs = {};
emits = {};
return this;
};
// Always return instance?
/**
* @type {Castrato}
*/
let castrato = new Castrato();
// Export both named and default
export default castrato;
export { castrato };