event-bus.js

import {VISUALIZER_EVENTS, PRIVATE_EVENTS} from './constants';
import {hasValue, getKey} from './utils';

/**
 * Class representing a Wizart event with strict validation of event types.
 * @class
 * @memberof WizartDeploymentKit
 */
class WizartEvent {

    /**
     * Creates an instance of WizartEvent.
     * @param {VisualizerEvent} eventName - The name of the event.
     * @param {*} data - The event payload data.
     * @throws Will throw an error if the event name is not recognized.
     */
    constructor(eventName, data={}) {
        if (!VISUALIZER_EVENTS.hasOwnProperty(eventName) && !hasValue(PRIVATE_EVENTS, eventName)) {
            console.error(`Unknown event type ${eventName}`);
        }
        this.eventName = eventName;
        this.data = data;
    }
}

/**
 * @namespace EventBus
 * @memberof WizartDeploymentKit
 */

/**
 * @ignore
 */
const subscribers_ = new Map();

/**
 * @ignore
 */
const pushEvent = (visualizer, event) => {
    if (!(event instanceof WizartEvent)) {
        console.error('Event is not instance of WizartEvent class.');
    }
    visualizer._pushEvent(event.eventName, event.data);
}

/**
 * Subscribes a callback function to an event, optionally for a specific event and/or visualizer instance.
 * @function subscribe
 * @memberof WizartDeploymentKit.EventBus
 * @param {Function} callback - The callback function to execute when the event is triggered.
 * @param {VisualizerEvent|null} eventName - Optional event name to subscribe to.
 * @param {Visualizer|null} visualizer - Optional specific visualizer instance to filter events.
 * @returns {symbol} A unique subscription ID for managing the subscription.
 * @throws Will throw an error if callback is not a function or if visualizer is not a valid instance.
 * 
 * @example
 * const subscriptionId = WizartDeploymentKit.EventBus.subscribe((event) => {
 *         console.log(event);
 *     }, 
 *     
 *     // if you want to handle specific event.
 *     WizartDeploymentKit.VISUALIZER_EVENTS.USER_SESSION_STARTED,
 * 
 *     // if you want to handle event of specific visualizer instance
 *     visualizer
 * );
 */
const subscribe = (callback, eventName = null, visualizer = null) => {
    if (typeof callback !== 'function') {
        console.error(`Passed callback is not a function.`);
    }

    if (visualizer !== null && !visualizer._isInstanceOfVisualizer) {
        console.error(`Wrong value passed for visualizer argument`);
    }

    if (eventName !== null && !hasValue(VISUALIZER_EVENTS, eventName) && !hasValue(PRIVATE_EVENTS, eventName)) {
        console.error(`Unknow event type ${eventName}.`);
    }

    const subscriptionId = Symbol();

    if (visualizer !== null && visualizer._isInstanceOfVisualizer) {
        visualizer._registerLocalSubscription(subscriptionId);
    }

    subscribers_.set(subscriptionId, { callback, eventName, visualizer });
    return subscriptionId;
}

/**
 * Unsubscribes from an event using the provided subscription ID.
 * @function unsubscribe
 * @memberof WizartDeploymentKit.EventBus
 * @param {symbol} subscriptionId - The unique ID of the subscription to remove.
 * 
 * @example
 * WizartDeploymentKit.EventBus.unsubscribe(subscriptionId);
 */
const unsubscribe = (subscriptionId) => {
    subscribers_.delete(subscriptionId);
}

/**
 * @ignore
 */
const initEventListener_ = () => {
    const WIZART_EVENT_NAME = 'wizart_analytics';
    window.addEventListener("message", (event) => {
        const { data={}, source } = event;
        event.stopPropagation();
        let wizartEventName = data.analyticsEventName;
        if (!wizartEventName && hasValue(PRIVATE_EVENTS, data.eventName)) {
            wizartEventName = data.eventName;
        }

        if (wizartEventName) {
            subscribers_.forEach((subscriber, subscriptionId) => {
                const { callback, eventName, visualizer } = subscriber;
                const isEventMatch = !eventName || eventName === wizartEventName;
                const isVisualizerMatch = !visualizer || (visualizer.isLoaded && source === visualizer.iframe.contentWindow);

                if (isEventMatch && isVisualizerMatch) {
                    callback(new WizartEvent(getKey(VISUALIZER_EVENTS, wizartEventName), data.payload));
                }
            });
        }
    });
};
initEventListener_();

const EventBus = {
    pushEvent,
    subscribe,
    unsubscribe
};

export {
    EventBus,
    WizartEvent
}