import {ErrorCode, ExceptionIdMap} from '../../exception/exceptionDesc.js';
const logModule = 'as-event';
/**
* TComponents Namespace
* @namespace TComponents
* @public
*/
/**
* @description Event manager base class
* @class TComponents.Eventing_A
* @memberof TComponents
* @example
* const eventingInstance = new TComponents.Eventing_A();
*/
export class Eventing_A {
constructor() {
/** @type {Object<string, Function[]>} */
this.events = {};
}
/**
* @description Subscribe to an event
* @member TComponents.Eventing_A#on
* @method
* @param {string} eventName - Name of the triggering event
* @param {Function} callback - Function to be called when the event is triggered
* @param {boolean} [strict=true] - If true (default), checking whether the function has been added is done by function object comparison,
* otherwise, the comparison is done only by function.name
*/
on(eventName, callback, strict = true) {
if (typeof callback !== 'function') throw new Error('callback is not a valid function');
const handlers = this.events[eventName] || [];
if (!strict && handlers.some((cb) => cb.name === callback.name)) return;
if (handlers.some((h) => h === callback || (h && h._original && h._original === callback))) return;
handlers.push(callback);
this.events[eventName] = handlers;
}
/**
* @description Unsubscribe from an event
* @member TComponents.Eventing_A#off
* @method
* @param {string} eventName - Name of the triggering event
* @param {Function} callback - Function to be removed from the event's callbacks
* @returns {boolean} - True if the callback was removed, false if it was not found
*/
off(eventName, callback) {
const handlers = this.events[eventName];
if (!handlers || handlers.length === 0) {
return false;
}
const index = handlers.findIndex((h) => h === callback || (h && h._original && h._original === callback));
if (index > -1) {
handlers.splice(index, 1);
return true;
}
return false;
}
/**
* @description Subscribe to an event, but the callback is called only once
* @member TComponents.Eventing_A#once
* @method
* @param {string} eventName - Name of the triggering event
* @param {Function} callback - Function to be called when the event is triggered
* @returns {boolean} - True if the callback was added, false if it was already added
*/
once(eventName, callback) {
if (typeof callback !== 'function') throw new Error('callback is not a valid function');
const handlers = this.events[eventName] || [];
if (handlers.some((h) => h === callback || (h && h._original && h._original === callback))) return false;
const onceCallback = (...data) => {
try {
callback(...data);
} finally {
this.off(eventName, onceCallback);
}
};
onceCallback._original = callback;
handlers.push(onceCallback);
this.events[eventName] = handlers;
return true;
}
/**
* @description Trigger all callbacks subscribed to an event
* @member TComponent.Eventing_A#trigger
* @method
* @param {string} eventName - Name of the event to be triggered
* @param {...any} data - Data passed to the callback as input parameters
*/
trigger(eventName, ...data) {
const handlers = this.events[eventName];
if (!handlers || handlers.length === 0) {
return;
}
const toCall = handlers.slice();
toCall.forEach((callback) => {
try {
callback(...data);
} catch (e) {
Logger.e(logModule, ErrorCode.EventHandlerError, `Error in event handler for ${eventName}:`, e);
}
});
}
/**
* @description Get the number of callbacks subscribed to an event
* @member TComponents.Eventing_A#count
* @method
* @param {string} eventName - Name of the triggering event
* @returns {number} - Number of callbacks subscribed to the event
*/
count(eventName) {
return this.events[eventName] ? this.events[eventName].length : 0;
}
/**
* @description Clean up a specific event
* @member TComponents.Eventing_A#cleanEvent
* @method
* @param {string} eventName - The event name you want to clean
*/
cleanEvent(eventName) {
if (this.events[eventName]) {
const handlers = this.events[eventName];
handlers.forEach((callback) => {
this.off(eventName, callback);
});
// Optionally, remove the event from the events registry
delete this.events[eventName];
}
}
/**
* @description Clean up all registered events except those in the ignore list
* @member TComponents.Eventing_A#cleanUpEvents
* @method
* @param {string[]} [ignores=['before:init', 'after:render', 'before:destroy']] - Event list you don't want to clean
*/
cleanUpEvents(ignores = ['before:init', 'after:render', 'before:destroy']) {
for (const eventName in this.events) {
if (!ignores.includes(eventName) && Object.prototype.hasOwnProperty.call(this.events, eventName)) {
const handlers = this.events[eventName];
handlers.forEach((callback) => {
this.off(eventName, callback);
});
}
}
}
/**
* @description Check if an event has been registered already
* @member TComponents.Eventing_A#hasEvent
* @method
* @param {string} eventName - Name of the triggering event
* @returns {boolean} - True if the event has been registered already, false otherwise
*/
hasEvent(eventName) {
return Object.prototype.hasOwnProperty.call(this.events, eventName);
}
/**
* @description Check if any event has been registered already
* @member TComponents.Eventing_A#anyEvent
* @method
* @returns {boolean} - True if any event has been registered already, false otherwise
*/
anyEvent() {
return Object.keys(this.events).length > 0;
}
}