All files / src eventEmitter.ts

0% Statements 0/37
0% Branches 0/26
0% Functions 0/11
0% Lines 0/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103                                                                                                                                                                                                             
/*!
 * Copyright (c) Microsoft. All rights reserved.
 * Licensed under the MIT license. See LICENSE file in the project.
 */

// taken from TableSorter https://github.com/Microsoft/PowerBI-visuals-TableSorter
type Handler = (...args: any[]) => any
 
interface DestroyObject {
	destroy: (key: string, handler?: Handler) => void
}
interface EventNameSpace {
	event: string
	namespace: string
}

/**
 * A mixin that adds support for event emitting
 */
export class EventEmitter {
	private listeners = new Map<string, Handler[]>()
 
	/**
	 * Adds an event listener for the given event
	 */
	public on(key: string, handler?: Handler): DestroyObject | undefined {
		if (!this.listeners.has(key)) {
			this.listeners.set(key, [])
		}
		const listeners = this.listeners.get(key)
		if (listeners && handler) {
			listeners.push(handler)
			return {
				destroy: () => {
					this.off(key, handler)
				},
			}
		}
	}

	/**
	 * Removes an event listener for the given event
	 */
	public off(key: string, handler?: Handler): void {
		if (handler) {
			const { namespace, event } = this.getNamespaceAndEvent(key)
			if (namespace && !event) {
				Object.keys(this.listeners).forEach((otherKey) => {
					const { namespace: otherNamespace } =
						this.getNamespaceAndEvent(otherKey)
					if (otherNamespace === namespace) {
						this.listeners.delete(otherKey)
					}
				})
			} else {
				const listeners = this.listeners.get(key)
				if (listeners) {
					const idx = listeners.indexOf(handler)
					if (idx >= 0) {
						listeners.splice(idx, 1)
					}
				}
			}
		}
	}

	/**
	 * Raises the given event
	 */
	public emit(name: string, ...args: any[]): void {
		const keys = [...this.listeners.keys()]
		keys.forEach((otherKey) => {
			const { event } = this.getNamespaceAndEvent(otherKey)
			const handlers = this.listeners.get(otherKey)
			if (handlers && event === name) {
				handlers.forEach((l) => l.apply(this, args))
			}
		})
	}

	/**
	 * Returns the namespace and event name
	 * @param eventKey - The event key, either "<event>" or "<event>.<namespace>"
	 */
	private getNamespaceAndEvent(eventKey: string): EventNameSpace {
		if (eventKey) {
			const [event, namespace] = eventKey.split('.')
			return {
				event,
				namespace,
			}
		}
		return {
			event: '',
			namespace: '',
		}
	}
}
 
export const eventEmitter = (): EventEmitter => {
	return new EventEmitter()
}