Spaces:
Sleeping
Sleeping
| // 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 | |
| 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 | |
| } | |
| } | |
| } | |
| exports.Event = Event | |
| // https://dom.spec.whatwg.org/#customevent | |
| exports.CustomEvent = class CustomEvent extends Event { | |
| constructor(type, options = {}) { | |
| super(type, options) | |
| const { detail = null } = options | |
| this._detail = detail | |
| } | |
| // https://dom.spec.whatwg.org/#dom-customevent-detail | |
| get detail() { | |
| return this._detail | |
| } | |
| } | |
| // 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 | |
| } | |
| } | |
| } | |