import { warn } from './debug' import { addEvent, removeEvent } from './dom' interface Events { [name: string]: [WithFnFunction, Object][] } interface EventTypes { [type: string]: string } interface WithFnFunction extends Function { fn?: Function } export class EventEmitter { events: Events eventTypes: EventTypes constructor(names: string[]) { this.events = {} this.eventTypes = {} this.registerType(names) } on(type: string, fn: Function, context: Object = this) { this.hasType(type) if (!this.events[type]) { this.events[type] = [] } this.events[type].push([fn, context]) return this } once(type: string, fn: Function, context: Object = this) { this.hasType(type) const magic = (...args: any[]) => { this.off(type, magic) const ret = fn.apply(context, args) if (ret === true) { return ret } } magic.fn = fn this.on(type, magic) return this } off(type?: string, fn?: Function) { if (!type && !fn) { this.events = {} return this } if (type) { this.hasType(type) if (!fn) { this.events[type] = [] return this } let events = this.events[type] if (!events) { return this } let count = events.length while (count--) { if ( events[count][0] === fn || (events[count][0] && events[count][0].fn === fn) ) { events.splice(count, 1) } } return this } } trigger(type: string, ...args: any[]) { this.hasType(type) let events = this.events[type] if (!events) { return } let len = events.length let eventsCopy = [...events] let ret for (let i = 0; i < len; i++) { let event = eventsCopy[i] let [fn, context] = event if (fn) { ret = fn.apply(context, args) if (ret === true) { return ret } } } } registerType(names: string[]) { names.forEach((type: string) => { this.eventTypes[type] = type }) } destroy() { this.events = {} this.eventTypes = {} } private hasType(type: string) { const types = this.eventTypes const isType = types[type] === type if (!isType) { warn( `EventEmitter has used unknown event type: "${type}", should be oneof [` + `${Object.keys(types).map((_) => JSON.stringify(_))}` + `]` ) } } } interface EventData { name: string handler(e: UIEvent): void capture?: boolean } export class EventRegister { constructor( public wrapper: HTMLElement | Window, public events: EventData[] ) { this.addDOMEvents() } destroy() { this.removeDOMEvents() this.events = [] } private addDOMEvents() { this.handleDOMEvents(addEvent) } private removeDOMEvents() { this.handleDOMEvents(removeEvent) } private handleDOMEvents(eventOperation: Function) { const wrapper = this.wrapper this.events.forEach((event: EventData) => { eventOperation(wrapper, event.name, this, !!event.capture) }) } private handleEvent(e: UIEvent) { const eventType = e.type this.events.some((event: EventData) => { if (event.name === eventType) { event.handler(e) return true } return false }) } }