UNPKG

ste-core

Version:
265 lines (264 loc) 7.95 kB
"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;