function createListener(fn, that, type, once) { return { fn, that, type, once, bound: that ? fn.bind(that) : fn, } } export function EventEmitterMixin(Superclass) { return class EventEmitter extends Superclass { constructor(...args) { super(...args); this.$$events = Object.create(null); } addListener(typesOrType, fn, that) { return this.on(typesOrType, fn, that); } // Notify ALL the listeners! emit(type, ...args) { const typeListeners = this.$$events[type]; if (typeListeners && typeListeners.length > 0) { this.emitToListeners(typeListeners, args); } } emitToListeners(listeners, args) { for (let i = 0; i < listeners.length; ++i) { const {once, type, fn, bound, that} = listeners[i]; // Remove if only once. if (once) { this.offSingleEvent(type, fn); } // Fast path... if (0 === args.length) { bound() } else if (1 === args.length) { bound(args[0]); } else if (2 === args.length) { bound(args[0], args[1]); } else if (3 === args.length) { bound(args[0], args[1], args[2]); } else if (4 === args.length) { bound(args[0], args[1], args[2], args[3]); } else if (5 === args.length) { bound(args[0], args[1], args[2], args[3], args[4]); } // Slow path... else { fn.apply(that, args); } } } off(typesOrType, fn) { if (!Array.isArray(typesOrType)) { typesOrType = [typesOrType]; } for (let i = 0; i < typesOrType.length; i++) { const type = typesOrType[i]; this.offSingleEvent(type, fn); } return this; } offSingleEvent(type, fn) { if ('function' !== typeof fn) { // Only type. if (type in this.$$events) { this.$$events[type] = []; } return; } // Function. if (!(type in this.$$events)) { return; } this.$$events[type] = this.$$events[type].filter((listener) => { return listener.fn !== fn; }); } on(typesOrType, fn, that = undefined) { this._on(typesOrType, fn, that, false); return this; } _on(typesOrType, fn, that, once) { if (!Array.isArray(typesOrType)) { typesOrType = [typesOrType]; } for (let i = 0; i < typesOrType.length; i++) { const type = typesOrType[i]; this.onSingleEvent(type, fn, that, once); } } once(types, fn, that = undefined) { this._on(types, fn, that, true); return this; } onSingleEvent(type, fn, that, once) { const listener = createListener(fn, that, type, once); if (!(type in this.$$events)) { this.$$events[type] = []; } this.$$events[type].push(listener); } removeListener(...args) { return this.off(...args); } } }