// Event state const BUBBLES = 0x1 const CANCELABLE = 0x2 const COMPOSED = 0x4 const CANCELED = 0x8 const DISPATCH = 0x10 const STOP = 0x20 // EventTarget state const CAPTURE = 0x1 const PASSIVE = 0x2 const ONCE = 0x4 // https://dom.spec.whatwg.org/#event exports.Event = class Event { // https://dom.spec.whatwg.org/#dom-event-event constructor(type, options = {}) { const { bubbles = false, cancelable = false, composed = false } = options this._type = type this._target = null this._state = 0 if (bubbles) this._state |= BUBBLES if (cancelable) this._state |= CANCELABLE if (composed) this._state |= COMPOSED } // https://dom.spec.whatwg.org/#dom-event-type get type() { return this._type } // https://dom.spec.whatwg.org/#dom-event-target get target() { return this._target } // https://dom.spec.whatwg.org/#dom-event-currenttarget get currentTarget() { return null } // https://dom.spec.whatwg.org/#dom-event-bubbles get bubbles() { return (this._state & BUBBLES) !== 0 } // https://dom.spec.whatwg.org/#dom-event-cancelable get cancelable() { return (this._state & CANCELABLE) !== 0 } // https://dom.spec.whatwg.org/#dom-event-composed get composed() { return (this._state & COMPOSED) !== 0 } // https://dom.spec.whatwg.org/#dom-event-defaultprevented get defaultPrevented() { return (this._state & CANCELED) !== 0 } // https://dom.spec.whatwg.org/#dom-event-istrusted get isTrusted() { return false } // https://dom.spec.whatwg.org/#dom-event-preventdefault preventDefault() { if (this._state & CANCELABLE) this._state |= CANCELED } // https://dom.spec.whatwg.org/#dom-event-stoppropagation stopPropagation() {} // https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation stopImmediatePropagation() { this._state |= STOP } toJSON() { return { type: this.type, target: this.target, bubbles: this.bubbles, cancelable: this.cancelable, composed: this.composed, defaultPrevented: this.defaultPrevented, isTrusted: this.isTrusted } } [Symbol.for('bare.inspect')]() { return { __proto__: { constructor: Event }, type: this.type, target: this.target, bubbles: this.bubbles, cancelable: this.cancelable, composed: this.composed, defaultPrevented: this.defaultPrevented, isTrusted: this.isTrusted } } } // https://dom.spec.whatwg.org/#eventtarget exports.EventTarget = class EventTarget { // https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget constructor() { this._listeners = new Map() } // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener addEventListener(type, callback = null, options = {}) { if (typeof options === 'boolean') options = { capture: options } const { capture = false, passive = false, once = false, signal = null } = options if (signal !== null && signal.aborted) return if (callback === null) return const listener = new EventListener( type, callback, capture, passive, once, signal ) const listeners = this._listeners.get(type) if (listeners === undefined) this._listeners.set(type, listener) else { for (const existing of listeners) { if (callback === existing.callback && capture === existing.capture) { return // Duplicate listener } } listener.link(listeners) if (signal !== null) { signal.addEventListener('abort', onabort) function onabort() { listener.unlink() } } } } // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener removeEventListener(type, callback = null, options = {}) { if (typeof options === 'boolean') options = { capture: options } const { capture = false } = options const listeners = this._listeners.get(type) if (listeners === undefined) return for (const existing of listeners) { if (callback === existing.callback && capture === existing.capture) { const next = existing.unlink() if (listeners === existing) this._listeners.set(type, next) return } } } // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent dispatchEvent(event) { event._target = this event._state |= DISPATCH const listeners = this._listeners.get(event.type) try { if (listeners === undefined) return true for (const listener of listeners) { // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke if (listener.once) listener.unlink() let callback = listener.callback let context = this if (typeof callback === 'object') { context = callback callback = callback.handleEvent } Reflect.apply(callback, context, [event]) if (event._state & STOP) break } return (event._state & CANCELED) === 0 } finally { event._state &= ~DISPATCH event._state &= ~STOP } } toJSON() { return {} } [Symbol.for('bare.inspect')]() { return { __proto__: { constructor: EventTarget } } } } // https://dom.spec.whatwg.org/#concept-event-listener class EventListener { constructor(type, callback, capture, passive, once, signal) { this._type = type this._callback = callback this._signal = signal this._state = 0 if (capture) this._state |= CAPTURE if (passive) this._state |= PASSIVE if (once) this._state |= ONCE this._previous = this this._next = this } get type() { return this._type } get callback() { return this._callback } get capture() { return (this._state & CAPTURE) !== 0 } get passive() { return (this._state & PASSIVE) !== 0 } get once() { return (this._state & ONCE) !== 0 } get removed() { return this._previous === this && this._next === this } link(listener) { const next = this._next const previous = listener._previous this._next = listener listener._previous = this previous._next = next next._previous = previous return listener } unlink() { if (this.removed) return this const next = this._next const previous = this._previous this._next = this this._previous = this previous._next = next next._previous = previous return next } *[Symbol.iterator]() { let current = this while (true) { const next = current._next yield current if (next === this) break current = next } } toJSON() { return { type: this.type, capture: this.capture, passive: this.passive, once: this.once, removed: this.removed } } [Symbol.for('bare.inspect')]() { return { __proto__: { constructor: EventListener }, type: this.type, callback: this.callback, capture: this.capture, passive: this.passive, once: this.once, removed: this.removed } } }