Source: EventBus.js

(function() {
    /**
     * central event bus
     * @augments Help4.jscore.Base
     */
    Help4.EventBus = class extends Help4.jscore.Base {
        /** @override */
        constructor() {
            super({
                statics: {
                    _subscribers: {init: [], destroy: false},
                    TYPES:        {init: Help4.EventBus.TYPES, destroy: false}
                }
            });
        }

        /**
         * @typedef {Object} Help4.EventBus.Types
         * @property {'controllerStart'} controllerStart
         * @property {'controllerDestroy'} controllerDestroy
         * @property {'controllerOpen'} controllerOpen
         * @property {'controllerClose'} controllerClose
         * @property {'controllerAfterNavigate'} controllerAfterNavigate
         * @property {'controllerPlaybackServiceReady'} controllerPlaybackServiceReady
         * @property {'controllerEditMode'} controllerEditMode
         * @property {'controllerPlaybackActive'} controllerPlaybackActive
         * @property {'autoStartTour'} autoStartTour
         * @property {'hotspotAssignStart'} hotspotAssignStart
         * @property {'hotspotAssignStop'} hotspotAssignStop
         * @property {'hotspotAssignPostpone'} hotspotAssignPostpone
         * @property {'hotspotAssignResume'} hotspotAssignResume
         * @property {'hotspotOver'} hotspotOver
         * @property {'hotspotOut'} hotspotOut
         * @property {'hotspotClick'} hotspotClick
         * @property {'bubbleOver'} bubbleOver
         * @property {'bubbleOut'} bubbleOut
         * @property {'bubbleOpen'} bubbleOpen
         * @property {'carouselTileOver'} carouselTileOver
         * @property {'carouselTileOut'} carouselTileOut
         * @property {'carouselTileClick'} carouselTileClick
         * @property {'dragdrop'} dragdrop
         * @property {'selectionTileSelect'} selectionTileSelect
         * @property {'selectionTileDeselect'} selectionTileDeselect
         * @property {'lightboxClose'} lightboxClose
         * @property {'hotkey'} hotkey
         * @property {'widgetStart'} widgetStart
         * @property {'widgetVisibility'} widgetVisibility
         * @property {'widgetActivate'} widgetActivate
         * @property {'widgetStatus'} widgetStatus
         * @property {'xhrStatus'} xhrStatus
         * @property {'controlCreate'} controlCreate
         * @property {'controlDestroy'} controlDestroy
         * @property {'tourOpen'} tourOpen
         * @property {'tourClose'} tourClose
         */
        /**
         * @memberof Help4.EventBus
         * @type {Help4.EventBus.Types}
         */
        static TYPES = {
            controllerStart: 'controllerStart',
            controllerDestroy: 'controllerDestroy',
            controllerOpen: 'controllerOpen',
            controllerClose: 'controllerClose',
            controllerAfterNavigate: 'controllerAfterNavigate',
            controllerPlaybackServiceReady: 'controllerPlaybackServiceReady',
            controllerEditMode: 'controllerEditMode',
            controllerPlaybackActive: 'controllerPlaybackActive',
            autoStartTour: 'autoStartTour',

            hotspotAssignStart: 'hotspotAssignStart',
            hotspotAssignStop: 'hotspotAssignStop',
            hotspotAssignPostpone: 'hotspotAssignPostpone',
            hotspotAssignResume: 'hotspotAssignResume',
            hotspotOver: 'hotspotOver',
            hotspotOut: 'hotspotOut',
            hotspotClick: 'hotspotClick',

            bubbleOver: 'bubbleOver',
            bubbleOut: 'bubbleOut',
            bubbleOpen: 'bubbleOpen',

            carouselTileOver: 'carouselTileOver',
            carouselTileOut: 'carouselTileOut',
            carouselTileClick: 'carouselTileClick',

            dragdrop: 'dragdrop',

            selectionTileSelect: 'selectionTileSelect',
            selectionTileDeselect: 'selectionTileDeselect',

            lightboxClose: 'lightboxClose',
            lightboxCheckbox: 'lightboxCheckbox',
            hotkey: 'hotkey',

            widgetStart: 'widgetStart',
            widgetVisibility: 'widgetVisibility',
            widgetActivate: 'widgetActivate',
            widgetStatus: 'widgetStatus',

            xhrStatus: 'xhrStatus',

            controlCreate: 'controlCreate',
            controlDestroy: 'controlDestroy',

            tourOpen: 'tourOpen',
            tourClose: 'tourClose'
        };

        /** destroys the EventBus */
        destroy() {
            // some calls might still arrive due to async execution flows
            // provide them an API that does not crash but does nothing
            this.subscribe = () => {};
            this.unsubscribe = () => {};
            this.fire = () => {};

            super.destroy();
        }

        /**
         * @param {Help4.observer.EventBusObserver.EventBusSubscriber} subscriber
         * @returns {Help4.EventBus}
         * @throws {TypeError}
         */
        subscribe(subscriber) {
            if (subscriber instanceof Help4.observer.EventBusObserver.EventBusSubscriber) {
                const {_subscribers} = this;
                if ((_subscribers?.indexOf(subscriber) ?? -1) < 0) _subscribers.push(subscriber);
            } else {
                throw new TypeError('Invalid subscriber for EventBus! Need to be an EventBusObserver.EventBusSubscriber!');
            }
            return this;
        }

        /**
         * @param {Help4.observer.EventBusObserver.EventBusSubscriber} subscriber
         * @returns {Help4.EventBus}
         */
        unsubscribe(subscriber) {
            const {_subscribers} = this;
            const i = _subscribers?.indexOf(subscriber) ?? -1;
            if (i >= 0) _subscribers.splice(i, 1);
            return this;
        }

        /**
         * @param {Object} event
         * @returns {Help4.EventBus}
         */
        fire(event) {
            this._subscribers.forEach(subscriber => setTimeout(() => subscriber.onEvent(event), 1));
            return this;
        }
    }
})();