Home Reference Source

src/EventHandler.js

/**
EventListener holds information about listeners on an object with the EventHandler
*/
const EventListener = class {
	constructor(eventName, callback, once = false) {
		this.eventName = eventName
		this.callback = callback
		this.once = once
	}
	matches(eventName) {
		return this.eventName === EventHandler.ALL_EVENTS || eventName === this.eventName
	}
	distributeEvent(eventName, ...params) {
		if (this.matches(eventName)) {
			this.callback(eventName, ...params)
			return true
		}
		return false
	}
}

/**
EventHandler is the base class that implements event distribution
*/
const EventHandler = class {
	/** Send an event to listeners */
	trigger(eventName, ...params) {
		const listenersToRemove = []
		for (const listener of this.listeners) {
			if (listener.distributeEvent(eventName, ...params) && listener.once) {
				listenersToRemove.push(listener)
			}
		}
		for (const listener of listenersToRemove) {
			this.removeListener(listener.callback, listener.eventName)
		}
	}

	/**
	@param {function(eventName: string, eventSource: EventHandler)} callback often includes more parameters that are specific to the event
	@param {Object|Symbol} [eventName=EventHandler.ALL_EVENTS] a string or Symbol indicating the event to watch, defaults to ALL_EVENTS
	@param {bool} [once=false] If true then the listener is removed after receiving one event
	*/
	addListener(callback, eventName = EventHandler.ALL_EVENTS, once = false) {
		this.listeners.push(new EventListener(eventName, callback, once))
	}

	removeListener(callback, eventName = null) {
		let remove = false
		for (let i = 0; i < this.listeners.length; i++) {
			remove = false
			if (this.listeners[i].callback === callback) {
				if (eventName == null) {
					remove = true
				} else if (this.listeners[i].matches(eventName)) {
					remove = true
				}
			}
			if (remove) {
				this.listeners.splice(i, 1)
				i -= 1
			}
		}
	}

	/** @return {EventListener[]} */
	get listeners() {
		if (typeof this._listeners == 'undefined') {
			this._listeners = []
		}
		return this._listeners
	}

	cleanup() {
		if (typeof this._listeners !== 'undefined') {
			this._listeners.length = 0
		}
	}
}
EventHandler.ALL_EVENTS = Symbol('all events')

export default EventHandler