ste-core
Version:
Core files for the Strongly Typed Events project.
265 lines (264 loc) • 7.95 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SubscriptionChangeEventDispatcher = exports.DispatcherBase = void 0;
const DispatcherWrapper_1 = require("./DispatcherWrapper");
const Subscription_1 = require("../events/Subscription");
const EventManagement_1 = require("../management/EventManagement");
/**
* Base class for implementation of the dispatcher. It facilitates the subscribe
* and unsubscribe methods based on generic handlers. The TEventType specifies
* the type of event that should be exposed. Use the asEvent to expose the
* dispatcher as event.
*
* @export
* @abstract
* @class DispatcherBase
* @implements {ISubscribable<TEventHandler>}
* @template TEventHandler The type of event handler.
*/
class DispatcherBase {
constructor() {
/**
* The subscriptions.
*
* @protected
*
* @memberOf DispatcherBase
*/
this._subscriptions = new Array();
}
/**
* Returns the number of subscriptions.
*
* @readonly
* @type {number}
* @memberOf DispatcherBase
*/
get count() {
return this._subscriptions.length;
}
/**
* Triggered when subscriptions are changed (added or removed).
*
* @readonly
* @type {ISubscribable<SubscriptionChangeEventHandler>}
* @memberOf DispatcherBase
*/
get onSubscriptionChange() {
if (this._onSubscriptionChange == null) {
this._onSubscriptionChange = new SubscriptionChangeEventDispatcher();
}
return this._onSubscriptionChange.asEvent();
}
/**
* Subscribe to the event dispatcher.
*
* @param {TEventHandler} fn The event handler that is called when the event is dispatched.
* @returns A function that unsubscribes the event handler from the event.
*
* @memberOf DispatcherBase
*/
subscribe(fn) {
if (fn) {
this._subscriptions.push(this.createSubscription(fn, false));
this.triggerSubscriptionChange();
}
return () => {
this.unsubscribe(fn);
};
}
/**
* Subscribe to the event dispatcher.
*
* @param {TEventHandler} fn The event handler that is called when the event is dispatched.
* @returns A function that unsubscribes the event handler from the event.
*
* @memberOf DispatcherBase
*/
sub(fn) {
return this.subscribe(fn);
}
/**
* Subscribe once to the event with the specified name.
*
* @param {TEventHandler} fn The event handler that is called when the event is dispatched.
* @returns A function that unsubscribes the event handler from the event.
*
* @memberOf DispatcherBase
*/
one(fn) {
if (fn) {
this._subscriptions.push(this.createSubscription(fn, true));
this.triggerSubscriptionChange();
}
return () => {
this.unsubscribe(fn);
};
}
/**
* Checks it the event has a subscription for the specified handler.
*
* @param {TEventHandler} fn The event handler.
*
* @memberOf DispatcherBase
*/
has(fn) {
if (!fn)
return false;
return this._subscriptions.some((sub) => sub.handler == fn);
}
/**
* Unsubscribes the handler from the dispatcher.
*
* @param {TEventHandler} fn The event handler.
*
* @memberOf DispatcherBase
*/
unsubscribe(fn) {
if (!fn)
return;
let changes = false;
for (let i = 0; i < this._subscriptions.length; i++) {
if (this._subscriptions[i].handler == fn) {
this._subscriptions.splice(i, 1);
changes = true;
break;
}
}
if (changes) {
this.triggerSubscriptionChange();
}
}
/**
* Unsubscribes the handler from the dispatcher.
*
* @param {TEventHandler} fn The event handler.
*
* @memberOf DispatcherBase
*/
unsub(fn) {
this.unsubscribe(fn);
}
/**
* Generic dispatch will dispatch the handlers with the given arguments.
*
* @protected
* @param {boolean} executeAsync `True` if the even should be executed async.
* @param {*} scope The scope of the event. The scope becomes the `this` for handler.
* @param {IArguments} args The arguments for the event.
* @returns {(IPropagationStatus | null)} The propagation status, or if an `executeAsync` is used `null`.
*
* @memberOf DispatcherBase
*/
_dispatch(executeAsync, scope, args) {
//execute on a copy because of bug #9
for (let sub of [...this._subscriptions]) {
let ev = new EventManagement_1.EventManagement(() => this.unsub(sub.handler));
let nargs = Array.prototype.slice.call(args);
nargs.push(ev);
let s = sub;
s.execute(executeAsync, scope, nargs);
//cleanup subs that are no longer needed
this.cleanup(sub);
if (!executeAsync && ev.propagationStopped) {
return { propagationStopped: true };
}
}
if (executeAsync) {
return null;
}
return { propagationStopped: false };
}
/**
* Creates a subscription.
*
* @protected
* @param {TEventHandler} handler The handler.
* @param {boolean} isOnce True if the handler should run only one.
* @returns {ISubscription<TEventHandler>} The subscription.
*
* @memberOf DispatcherBase
*/
createSubscription(handler, isOnce) {
return new Subscription_1.Subscription(handler, isOnce);
}
/**
* Cleans up subs that ran and should run only once.
*
* @protected
* @param {ISubscription<TEventHandler>} sub The subscription.
*
* @memberOf DispatcherBase
*/
cleanup(sub) {
let changes = false;
if (sub.isOnce && sub.isExecuted) {
let i = this._subscriptions.indexOf(sub);
if (i > -1) {
this._subscriptions.splice(i, 1);
changes = true;
}
}
if (changes) {
this.triggerSubscriptionChange();
}
}
/**
* Creates an event from the dispatcher. Will return the dispatcher
* in a wrapper. This will prevent exposure of any dispatcher methods.
*
* @returns {ISubscribable<TEventHandler>}
*
* @memberOf DispatcherBase
*/
asEvent() {
if (this._wrap == null) {
this._wrap = new DispatcherWrapper_1.DispatcherWrapper(this);
}
return this._wrap;
}
/**
* Clears the subscriptions.
*
* @memberOf DispatcherBase
*/
clear() {
if (this._subscriptions.length != 0) {
this._subscriptions.splice(0, this._subscriptions.length);
this.triggerSubscriptionChange();
}
}
/**
* Triggers the subscription change event.
*
* @private
*
* @memberOf DispatcherBase
*/
triggerSubscriptionChange() {
if (this._onSubscriptionChange != null) {
this._onSubscriptionChange.dispatch(this.count);
}
}
}
exports.DispatcherBase = DispatcherBase;
/**
* Dispatcher for subscription changes.
*
* @export
* @class SubscriptionChangeEventDispatcher
* @extends {DispatcherBase<SubscriptionChangeEventHandler>}
*/
class SubscriptionChangeEventDispatcher extends DispatcherBase {
/**
* Dispatches the event.
*
* @param {number} count The currrent number of subscriptions.
*
* @memberOf SubscriptionChangeEventDispatcher
*/
dispatch(count) {
this._dispatch(false, this, arguments);
}
}
exports.SubscriptionChangeEventDispatcher = SubscriptionChangeEventDispatcher;