// Ported from nanobus 4.5.0 (MIT) — https://github.com/choojs/nanobus // Event emitter with a '*' wildcard channel and nanotiming instrumentation. import nanotiming from './timing.js' import { ok, equal } from './assert.js' function assertEventName (eventName, method) { ok( typeof eventName === 'string' || typeof eventName === 'symbol', `nanobus.${method}: eventName should be type string or symbol` ) } export default class Nanobus { constructor (name) { this._name = name || 'nanobus' this._starListeners = [] this._listeners = {} } emit (eventName, ...data) { assertEventName(eventName, 'emit') const emitTiming = nanotiming(this._name + "('" + eventName.toString() + "')") const listeners = this._listeners[eventName] if (listeners && listeners.length > 0) { this._emit(this._listeners[eventName], data) } if (this._starListeners.length > 0) { this._emit(this._starListeners, eventName, data, emitTiming.uuid) } emitTiming() return this } on (eventName, listener) { assertEventName(eventName, 'on') equal(typeof listener, 'function', 'nanobus.on: listener should be type function') if (eventName === '*') { this._starListeners.push(listener) } else { if (!this._listeners[eventName]) this._listeners[eventName] = [] this._listeners[eventName].push(listener) } return this } addListener (eventName, listener) { return this.on(eventName, listener) } prependListener (eventName, listener) { assertEventName(eventName, 'prependListener') equal(typeof listener, 'function', 'nanobus.prependListener: listener should be type function') if (eventName === '*') { this._starListeners.unshift(listener) } else { if (!this._listeners[eventName]) this._listeners[eventName] = [] this._listeners[eventName].unshift(listener) } return this } once (eventName, listener) { assertEventName(eventName, 'once') equal(typeof listener, 'function', 'nanobus.once: listener should be type function') const self = this this.on(eventName, function once (...args) { listener.apply(self, args) self.removeListener(eventName, once) }) return this } prependOnceListener (eventName, listener) { assertEventName(eventName, 'prependOnceListener') equal(typeof listener, 'function', 'nanobus.prependOnceListener: listener should be type function') const self = this this.prependListener(eventName, function once (...args) { listener.apply(self, args) self.removeListener(eventName, once) }) return this } removeListener (eventName, listener) { assertEventName(eventName, 'removeListener') equal(typeof listener, 'function', 'nanobus.removeListener: listener should be type function') if (eventName === '*') { this._starListeners = this._starListeners.slice() return remove(this._starListeners, listener) } else { if (typeof this._listeners[eventName] !== 'undefined') { this._listeners[eventName] = this._listeners[eventName].slice() } return remove(this._listeners[eventName], listener) } function remove (arr, listener) { if (!arr) return const index = arr.indexOf(listener) if (index !== -1) { arr.splice(index, 1) return true } } } removeAllListeners (eventName) { if (eventName) { if (eventName === '*') { this._starListeners = [] } else { this._listeners[eventName] = [] } } else { this._starListeners = [] this._listeners = {} } return this } listeners (eventName) { const listeners = eventName !== '*' ? this._listeners[eventName] : this._starListeners return listeners ? listeners.slice() : [] } _emit (arr, eventName, data, uuid) { if (typeof arr === 'undefined') return if (arr.length === 0) return if (data === undefined) { data = eventName eventName = null } if (eventName) { if (uuid !== undefined) { data = [eventName].concat(data, uuid) } else { data = [eventName].concat(data) } } // Take a copy in case a listener mutates the array while we iterate. const listeners = arr.slice() for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener.apply(listener, data) } } }