text: // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
  
  // This module follows most of the WHATWG Living Standard for the DOM logic.
  // Many parts of the DOM are not implemented in Deno, but the logic for those
  // parts still exists.  This means you will observe a lot of strange structures
  // and impossible logic branches based on what Deno currently supports.
  "use strict";
  
  ((window) => {
    const webidl = window.__bootstrap.webidl;
    const { DOMException } = window.__bootstrap.domException;
    const consoleInternal = window.__bootstrap.console;
    const {
      ArrayPrototypeFilter,
      ArrayPrototypeIncludes,
      ArrayPrototypeIndexOf,
      ArrayPrototypeMap,
      ArrayPrototypePush,
      ArrayPrototypeSlice,
      ArrayPrototypeSplice,
      ArrayPrototypeUnshift,
      Boolean,
      DateNow,
      Error,
      FunctionPrototypeCall,
      Map,
      MapPrototypeGet,
      MapPrototypeSet,
      ObjectCreate,
      ObjectDefineProperty,
      ObjectGetOwnPropertyDescriptor,
      ReflectDefineProperty,
      Symbol,
      SymbolFor,
      SymbolToStringTag,
      TypeError,
    } = window.__bootstrap.primordials;
  
    // accessors for non runtime visible data
  
    function getDispatched(event) {
      return Boolean(event[_dispatched]);
    }
  
    function getPath(event) {
      return event[_path] ?? [];
    }
  
    function getStopImmediatePropagation(event) {
      return Boolean(event[_stopImmediatePropagationFlag]);
    }
  
    function setCurrentTarget(
      event,
      value,
    ) {
      event[_attributes].currentTarget = value;
    }
  
    function setIsTrusted(event, value) {
      event[_isTrusted] = value;
    }
  
    function setDispatched(event, value) {
      event[_dispatched] = value;
    }
  
    function setEventPhase(event, value) {
      event[_attributes].eventPhase = value;
    }
  
    function setInPassiveListener(event, value) {
      event[_inPassiveListener] = value;
    }
  
    function setPath(event, value) {
      event[_path] = value;
    }
  
    function setRelatedTarget(
      event,
      value,
    ) {
      event[_attributes].relatedTarget = value;
    }
  
    function setTarget(event, value) {
      event[_attributes].target = value;
    }
  
    function setStopImmediatePropagation(
      event,
      value,
    ) {
      event[_stopImmediatePropagationFlag] = value;
    }
  
    // Type guards that widen the event type
  
    function hasRelatedTarget(
      event,
    ) {
      return "relatedTarget" in event;
    }
  
    const isTrusted = ObjectGetOwnPropertyDescriptor({
      get isTrusted() {
        return this[_isTrusted];
      },
    }, "isTrusted").get;
  
    const eventInitConverter = webidl.createDictionaryConverter("EventInit", [{
      key: "bubbles",
      defaultValue: false,
      converter: webidl.converters.boolean,
    }, {
      key: "cancelable",
      defaultValue: false,
      converter: webidl.converters.boolean,
    }, {
      key: "composed",
      defaultValue: false,
      converter: webidl.converters.boolean,
    }]);
  
    const _attributes = Symbol("[[attributes]]");
    const _canceledFlag = Symbol("[[canceledFlag]]");
    const _stopPropagationFlag = Symbol("[[stopPropagationFlag]]");
    const _stopImmediatePropagationFlag = Symbol(
      "[[stopImmediatePropagationFlag]]",
    );
    const _inPassiveListener = Symbol("[[inPassiveListener]]");
    const _dispatched = Symbol("[[dispatched]]");
    const _isTrusted = Symbol("[[isTrusted]]");
    const _path = Symbol("[[path]]");
  
    class Event {
      [_attributes] = {};
      [_canceledFlag] = false;
      [_stopPropagationFlag] = false;
      [_stopImmediatePropagationFlag] = false;
      [_inPassiveListener] = false;
      [_dispatched] = false;
      [_isTrusted] = false;
      [_path] = [];
  
      constructor(type, eventInitDict = {}) {
        webidl.requiredArguments(arguments.length, 1, {
          prefix: "Failed to construct 'Event'",
        });
        type = webidl.converters.DOMString(type, {
          prefix: "Failed to construct 'Event'",
          context: "Argument 1",
        });
        const eventInit = eventInitConverter(eventInitDict, {
          prefix: "Failed to construct 'Event'",
          context: "Argument 2",
        });
        this[_attributes] = {
          type,
          ...eventInit,
          currentTarget: null,
          eventPhase: Event.NONE,
          target: null,
          timeStamp: DateNow(),
        };
        ReflectDefineProperty(this, "isTrusted", {
          enumerable: true,
          get: isTrusted,
        });
      }
  
      [SymbolFor("Deno.privateCustomInspect")](inspect) {
        return inspect(consoleInternal.createFilteredInspectProxy({
          object: this,
          evaluate: this instanceof Event,
          keys: EVENT_PROPS,
        }));
      }
  
      get type() {
        return this[_attributes].type;
      }
  
      get target() {
        return this[_attributes].target;
      }
  
      get srcElement() {
        return null;
      }
  
      set srcElement(_) {
        // this member is deprecated
      }
  
      get currentTarget() {
        return this[_attributes].currentTarget;
      }
  
      composedPath() {
        const path = this[_path];
        if (path.length === 0) {
          return [];
        }
  
        if (!this.currentTarget) {
          throw new Error("assertion error");
        }
        const composedPath = [
          {
            item: this.currentTarget,
            itemInShadowTree: false,
            relatedTarget: null,
            rootOfClosedTree: false,
            slotInClosedTree: false,
            target: null,
            touchTargetList: [],
          },
        ];
  
        let currentTargetIndex = 0;
        let currentTargetHiddenSubtreeLevel = 0;
  
        for (let index = path.length - 1; index >= 0; index--) {
          const { item, rootOfClosedTree, slotInClosedTree } = path[index];
  
          if (rootOfClosedTree) {
            currentTargetHiddenSubtreeLevel++;
          }
  
          if (item === this.currentTarget) {
            currentTargetIndex = index;
            break;
          }
  
          if (slotInClosedTree) {
            currentTargetHiddenSubtreeLevel--;
          }
        }
  
        let currentHiddenLevel = currentTargetHiddenSubtreeLevel;
        let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
  
        for (let i = currentTargetIndex - 1; i >= 0; i--) {
          const { item, rootOfClosedTree, slotInClosedTree } = path[i];
  
          if (rootOfClosedTree) {
            currentHiddenLevel++;
          }
  
          if (currentHiddenLevel <= maxHiddenLevel) {
            ArrayPrototypeUnshift(composedPath, {
              item,
              itemInShadowTree: false,
              relatedTarget: null,
              rootOfClosedTree: false,
              slotInClosedTree: false,
              target: null,
              touchTargetList: [],
            });
          }
  
          if (slotInClosedTree) {
            currentHiddenLevel--;
  
            if (currentHiddenLevel < maxHiddenLevel) {
              maxHiddenLevel = currentHiddenLevel;
            }
          }
        }
  
        currentHiddenLevel = currentTargetHiddenSubtreeLevel;
        maxHiddenLevel = currentTargetHiddenSubtreeLevel;
  
        for (let index = currentTargetIndex + 1; index < path.length; index++) {
          const { item, rootOfClosedTree, slotInClosedTree } = path[index];
  
          if (slotInClosedTree) {
            currentHiddenLevel++;
          }
  
          if (currentHiddenLevel <= maxHiddenLevel) {
            ArrayPrototypePush(composedPath, {
              item,
              itemInShadowTree: false,
              relatedTarget: null,
              rootOfClosedTree: false,
              slotInClosedTree: false,
              target: null,
              touchTargetList: [],
            });
          }
  
          if (rootOfClosedTree) {
            currentHiddenLevel--;
  
            if (currentHiddenLevel < maxHiddenLevel) {
              maxHiddenLevel = currentHiddenLevel;
            }
          }
        }
        return ArrayPrototypeMap(composedPath, (p) => p.item);
      }
  
      get NONE() {
        return Event.NONE;
      }
  
      get CAPTURING_PHASE() {
        return Event.CAPTURING_PHASE;
      }
  
      get AT_TARGET() {
        return Event.AT_TARGET;
      }
  
      get BUBBLING_PHASE() {
        return Event.BUBBLING_PHASE;
      }
  
      static get NONE() {
        return 0;
      }
  
      static get CAPTURING_PHASE() {
        return 1;
      }
  
      static get AT_TARGET() {
        return 2;
      }
  
      static get BUBBLING_PHASE() {
        return 3;
      }
  
      get eventPhase() {
        return this[_attributes].eventPhase;
      }
  
      stopPropagation() {
        this[_stopPropagationFlag] = true;
      }
  
      get cancelBubble() {
        return this[_stopPropagationFlag];
      }
  
      set cancelBubble(value) {
        this[_stopPropagationFlag] = webidl.converters.boolean(value);
      }
  
      stopImmediatePropagation() {
        this[_stopPropagationFlag] = true;
        this[_stopImmediatePropagationFlag] = true;
      }
  
      get bubbles() {
        return this[_attributes].bubbles;
      }
  
      get cancelable() {
        return this[_attributes].cancelable;
      }
  
      get returnValue() {
        return !this[_canceledFlag];
      }
  
      set returnValue(value) {
        if (!webidl.converters.boolean(value)) {
          this[_canceledFlag] = true;
        }
      }
  
      preventDefault() {
        if (this[_attributes].cancelable && !this[_inPassiveListener]) {
          this[_canceledFlag] = true;
        }
      }
  
      get defaultPrevented() {
        return this[_canceledFlag];
      }
  
      get composed() {
        return this[_attributes].composed;
      }
  
      get initialized() {
        return true;
      }
  
      get timeStamp() {
        return this[_attributes].timeStamp;
      }
  
      // TODO(lucacasonato): remove when this interface is spec aligned
      [SymbolToStringTag] = "Event";
    }
  
    function defineEnumerableProps(
      Ctor,
      props,
    ) {
      for (const prop of props) {
        ReflectDefineProperty(Ctor.prototype, prop, { enumerable: true });
      }
    }
  
    const EVENT_PROPS = [
      "bubbles",
      "cancelable",
      "composed",
      "currentTarget",
      "defaultPrevented",
      "eventPhase",
      "srcElement",
      "target",
      "returnValue",
      "timeStamp",
      "type",
    ];
  
    defineEnumerableProps(Event, EVENT_PROPS);
  
    // This is currently the only node type we are using, so instead of implementing
    // the whole of the Node interface at the moment, this just gives us the one
    // value to power the standards based logic
    const DOCUMENT_FRAGMENT_NODE = 11;
  
    // DOM Logic Helper functions and type guards
  
    /** Get the parent node, for event targets that have a parent.
     *
     * Ref: https://dom.spec.whatwg.org/#get-the-parent */
    function getParent(eventTarget) {
      return isNode(eventTarget) ? eventTarget.parentNode : null;
    }
  
    function getRoot(eventTarget) {
      return isNode(eventTarget)
        ? eventTarget.getRootNode({ composed: true })
        : null;
    }
  
    function isNode(
      eventTarget,
    ) {
      return Boolean(eventTarget && "nodeType" in eventTarget);
    }
  
    // https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
    function isShadowInclusiveAncestor(
      ancestor,
      node,
    ) {
      while (isNode(node)) {
        if (node === ancestor) {
          return true;
        }
  
        if (isShadowRoot(node)) {
          node = node && getHost(node);
        } else {
          node = getParent(node);
        }
      }
  
      return false;
    }
  
    function isShadowRoot(nodeImpl) {
      return Boolean(
        nodeImpl &&
          isNode(nodeImpl) &&
          nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
          getHost(nodeImpl) != null,
      );
    }
  
    function isSlotable(
      nodeImpl,
    ) {
      return Boolean(isNode(nodeImpl) && "assignedSlot" in nodeImpl);
    }
  
    // DOM Logic functions
  
    /** Append a path item to an event's path.
     *
     * Ref: https://dom.spec.whatwg.org/#concept-event-path-append
     */
    function appendToEventPath(
      eventImpl,
      target,
      targetOverride,
      relatedTarget,
      touchTargets,
      slotInClosedTree,
    ) {
      const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
      const rootOfClosedTree = isShadowRoot(target) &&
        getMode(target) === "closed";
  
      ArrayPrototypePush(getPath(eventImpl), {
        item: target,
        itemInShadowTree,
        target: targetOverride,
        relatedTarget,
        touchTargetList: touchTargets,
        rootOfClosedTree,
        slotInClosedTree,
      });
    }
  
    function dispatch(
      targetImpl,
      eventImpl,
      targetOverride,
    ) {
      let clearTargets = false;
      let activationTarget = null;
  
      setDispatched(eventImpl, true);
  
      targetOverride = targetOverride ?? targetImpl;
      const eventRelatedTarget = hasRelatedTarget(eventImpl)
        ? eventImpl.relatedTarget
        : null;
      let relatedTarget = retarget(eventRelatedTarget, targetImpl);
  
      if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) {
        const touchTargets = [];
  
        appendToEventPath(
          eventImpl,
          targetImpl,
          targetOverride,
          relatedTarget,
          touchTargets,
          false,
        );
  
        const isActivationEvent = eventImpl.type === "click";
  
        if (isActivationEvent && getHasActivationBehavior(targetImpl)) {
          activationTarget = targetImpl;
        }
  
        let slotInClosedTree = false;
        let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl)
          ? targetImpl
          : null;
        let parent = getParent(targetImpl);
  
        // Populate event path
        // https://dom.spec.whatwg.org/#event-path
        while (parent !== null) {
          if (slotable !== null) {
            slotable = null;
  
            const parentRoot = getRoot(parent);
            if (
              isShadowRoot(parentRoot) &&
              parentRoot &&
              getMode(parentRoot) === "closed"
            ) {
              slotInClosedTree = true;
            }
          }
  
          relatedTarget = retarget(eventRelatedTarget, parent);
  
          if (
            isNode(parent) &&
            isShadowInclusiveAncestor(getRoot(targetImpl), parent)
          ) {
            appendToEventPath(
              eventImpl,
              parent,
              null,
              relatedTarget,
              touchTargets,
              slotInClosedTree,
            );
          } else if (parent === relatedTarget) {
            parent = null;
          } else {
            targetImpl = parent;
  
            if (
              isActivationEvent &&
              activationTarget === null &&
              getHasActivationBehavior(targetImpl)
            ) {
              activationTarget = targetImpl;
            }
  
            appendToEventPath(
              eventImpl,
              parent,
              targetImpl,
              relatedTarget,
              touchTargets,
              slotInClosedTree,
            );
          }
  
          if (parent !== null) {
            parent = getParent(parent);
          }
  
          slotInClosedTree = false;
        }
  
        let clearTargetsTupleIndex = -1;
        const path = getPath(eventImpl);
        for (
          let i = path.length - 1;
          i >= 0 && clearTargetsTupleIndex === -1;
          i--
        ) {
          if (path[i].target !== null) {
            clearTargetsTupleIndex = i;
          }
        }
        const clearTargetsTuple = path[clearTargetsTupleIndex];
  
        clearTargets = (isNode(clearTargetsTuple.target) &&
          isShadowRoot(getRoot(clearTargetsTuple.target))) ||
          (isNode(clearTargetsTuple.relatedTarget) &&
            isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
  
        setEventPhase(eventImpl, Event.CAPTURING_PHASE);
  
        for (let i = path.length - 1; i >= 0; --i) {
          const tuple = path[i];
  
          if (tuple.target === null) {
            invokeEventListeners(tuple, eventImpl);
          }
        }
  
        for (let i = 0; i < path.length; i++) {
          const tuple = path[i];
  
          if (tuple.target !== null) {
            setEventPhase(eventImpl, Event.AT_TARGET);
          } else {
            setEventPhase(eventImpl, Event.BUBBLING_PHASE);
          }
  
          if (
            (eventImpl.eventPhase === Event.BUBBLING_PHASE &&
              eventImpl.bubbles) ||
            eventImpl.eventPhase === Event.AT_TARGET
          ) {
            invokeEventListeners(tuple, eventImpl);
          }
        }
      }
  
      setEventPhase(eventImpl, Event.NONE);
      setCurrentTarget(eventImpl, null);
      setPath(eventImpl, []);
      setDispatched(eventImpl, false);
      eventImpl.cancelBubble = false;
      setStopImmediatePropagation(eventImpl, false);
  
      if (clearTargets) {
        setTarget(eventImpl, null);
        setRelatedTarget(eventImpl, null);
      }
  
      // TODO(bartlomieju): invoke activation targets if HTML nodes will be implemented
      // if (activationTarget !== null) {
      //   if (!eventImpl.defaultPrevented) {
      //     activationTarget._activationBehavior();
      //   }
      // }
  
      return !eventImpl.defaultPrevented;
    }
  
    /** Inner invoking of the event listeners where the resolved listeners are
     * called.
     *
     * Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */
    function innerInvokeEventListeners(
      eventImpl,
      targetListeners,
    ) {
      let found = false;
  
      const { type } = eventImpl;
  
      if (!targetListeners || !targetListeners[type]) {
        return found;
      }
  
      // Copy event listeners before iterating since the list can be modified during the iteration.
      const handlers = ArrayPrototypeSlice(targetListeners[type]);
  
      for (let i = 0; i < handlers.length; i++) {
        const listener = handlers[i];
  
        let capture, once, passive;
        if (typeof listener.options === "boolean") {
          capture = listener.options;
          once = false;
          passive = false;
        } else {
          capture = listener.options.capture;
          once = listener.options.once;
          passive = listener.options.passive;
        }
  
        // Check if the event listener has been removed since the listeners has been cloned.
        if (!ArrayPrototypeIncludes(targetListeners[type], listener)) {
          continue;
        }
  
        found = true;
  
        if (
          (eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
          (eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
        ) {
          continue;
        }
  
        if (once) {
          ArrayPrototypeSplice(
            targetListeners[type],
            ArrayPrototypeIndexOf(targetListeners[type], listener),
            1,
          );
        }
  
        if (passive) {
          setInPassiveListener(eventImpl, true);
        }
  
        if (typeof listener.callback === "object") {
          if (typeof listener.callback.handleEvent === "function") {
            listener.callback.handleEvent(eventImpl);
          }
        } else {
          FunctionPrototypeCall(
            listener.callback,
            eventImpl.currentTarget,
            eventImpl,
          );
        }
  
        setInPassiveListener(eventImpl, false);
  
        if (getStopImmediatePropagation(eventImpl)) {
          return found;
        }
      }
  
      return found;
    }
  
    /** Invokes the listeners on a given event path with the supplied event.
     *
     * Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
    function invokeEventListeners(tuple, eventImpl) {
      const path = getPath(eventImpl);
      const tupleIndex = ArrayPrototypeIndexOf(path, tuple);
      for (let i = tupleIndex; i >= 0; i--) {
        const t = path[i];
        if (t.target) {
          setTarget(eventImpl, t.target);
          break;
        }
      }
  
      setRelatedTarget(eventImpl, tuple.relatedTarget);
  
      if (eventImpl.cancelBubble) {
        return;
      }
  
      setCurrentTarget(eventImpl, tuple.item);
  
      innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
    }
  
    function normalizeAddEventHandlerOptions(
      options,
    ) {
      if (typeof options === "boolean" || typeof options === "undefined") {
        return {
          capture: Boolean(options),
          once: false,
          passive: false,
        };
      } else {
        return options;
      }
    }
  
    function normalizeEventHandlerOptions(
      options,
    ) {
      if (typeof options === "boolean" || typeof options === "undefined") {
        return {
          capture: Boolean(options),
        };
      } else {
        return options;
      }
    }
  
    /** Retarget the target following the spec logic.
     *
     * Ref: https://dom.spec.whatwg.org/#retarget */
    function retarget(a, b) {
      while (true) {
        if (!isNode(a)) {
          return a;
        }
  
        const aRoot = a.getRootNode();
  
        if (aRoot) {
          if (
            !isShadowRoot(aRoot) ||
            (isNode(b) && isShadowInclusiveAncestor(aRoot, b))
          ) {
            return a;
          }
  
          a = getHost(aRoot);
        }
      }
    }
  
    // Accessors for non-public data
  
    const eventTargetData = Symbol();
  
    function setEventTargetData(target) {
      target[eventTargetData] = getDefaultTargetData();
    }
  
    function getAssignedSlot(target) {
      return Boolean(target?.[eventTargetData]?.assignedSlot);
    }
  
    function getHasActivationBehavior(target) {
      return Boolean(target?.[eventTargetData]?.hasActivationBehavior);
    }
  
    function getHost(target) {
      return target?.[eventTargetData]?.host ?? null;
    }
  
    function getListeners(target) {
      return target?.[eventTargetData]?.listeners ?? {};
    }
  
    function getMode(target) {
      return target?.[eventTargetData]?.mode ?? null;
    }
  
    function getDefaultTargetData() {
      return {
        assignedSlot: false,
        hasActivationBehavior: false,
        host: null,
        listeners: ObjectCreate(null),
        mode: "",
      };
    }
  
    class EventTarget {
      constructor() {
        this[eventTargetData] = getDefaultTargetData();
      }
  
      addEventListener(
        type,
        callback,
        options,
      ) {
        webidl.requiredArguments(arguments.length, 2, {
          prefix: "Failed to execute 'addEventListener' on 'EventTarget'",
        });
        if (callback === null) {
          return;
        }
  
        options = normalizeAddEventHandlerOptions(options);
        const { listeners } = (this ?? globalThis)[eventTargetData];
  
        if (!(type in listeners)) {
          listeners[type] = [];
        }
  
        for (const listener of listeners[type]) {
          if (
            ((typeof listener.options === "boolean" &&
              listener.options === options.capture) ||
              (typeof listener.options === "object" &&
                listener.options.capture === options.capture)) &&
            listener.callback === callback
          ) {
            return;
          }
        }
        if (options?.signal) {
          const signal = options?.signal;
          if (signal.aborted) {
            // If signal is not null and its aborted flag is set, then return.
            return;
          } else {
            // If listener’s signal is not null, then add the following abort
            // abort steps to it: Remove an event listener.
            signal.addEventListener("abort", () => {
              this.removeEventListener(type, callback, options);
            });
          }
        } else if (options?.signal === null) {
          throw new TypeError("signal must be non-null");
        }
  
        ArrayPrototypePush(listeners[type], { callback, options });
      }
  
      removeEventListener(
        type,
        callback,
        options,
      ) {
        webidl.requiredArguments(arguments.length, 2, {
          prefix: "Failed to execute 'removeEventListener' on 'EventTarget'",
        });
  
        const { listeners } = (this ?? globalThis)[eventTargetData];
        if (callback !== null && type in listeners) {
          listeners[type] = ArrayPrototypeFilter(
            listeners[type],
            (listener) => listener.callback !== callback,
          );
        } else if (callback === null || !listeners[type]) {
          return;
        }
  
        options = normalizeEventHandlerOptions(options);
  
        for (let i = 0; i < listeners[type].length; ++i) {
          const listener = listeners[type][i];
          if (
            ((typeof listener.options === "boolean" &&
              listener.options === options.capture) ||
              (typeof listener.options === "object" &&
                listener.options.capture === options.capture)) &&
            listener.callback === callback
          ) {
            ArrayPrototypeSplice(listeners[type], i, 1);
            break;
          }
        }
      }
  
      dispatchEvent(event) {
        webidl.requiredArguments(arguments.length, 1, {
          prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
        });
        const self = this ?? globalThis;
  
        const { listeners } = self[eventTargetData];
        if (!(event.type in listeners)) {
          setTarget(event, this);
          return true;
        }
  
        if (getDispatched(event)) {
          throw new DOMException("Invalid event state.", "InvalidStateError");
        }
  
        if (event.eventPhase !== Event.NONE) {
          throw new DOMException("Invalid event state.", "InvalidStateError");
        }
  
        return dispatch(self, event);
      }
  
      getParent(_event) {
        return null;
      }
    }
  
    webidl.configurePrototype(EventTarget);
  
    defineEnumerableProps(EventTarget, [
      "addEventListener",
      "removeEventListener",
      "dispatchEvent",
    ]);
  
    class ErrorEvent extends Event {
      #message = "";
      #filename = "";
      #lineno = "";
      #colno = "";
      #error = "";
  
      get message() {
        return this.#message;
      }
      get filename() {
        return this.#filename;
      }
      get lineno() {
        return this.#lineno;
      }
      get colno() {
        return this.#colno;
      }
      get error() {
        return this.#error;
      }
  
      constructor(
        type,
        {
          bubbles,
          cancelable,
          composed,
          message = "",
          filename = "",
          lineno = 0,
          colno = 0,
          error = null,
        } = {},
      ) {
        super(type, {
          bubbles: bubbles,
          cancelable: cancelable,
          composed: composed,
        });
  
        this.#message = message;
        this.#filename = filename;
        this.#lineno = lineno;
        this.#colno = colno;
        this.#error = error;
      }
  
      [SymbolFor("Deno.privateCustomInspect")](inspect) {
        return inspect(consoleInternal.createFilteredInspectProxy({
          object: this,
          evaluate: this instanceof ErrorEvent,
          keys: [
            ...EVENT_PROPS,
            "message",
            "filename",
            "lineno",
            "colno",
            "error",
          ],
        }));
      }
  
      // TODO(lucacasonato): remove when this interface is spec aligned
      [SymbolToStringTag] = "ErrorEvent";
    }
  
    defineEnumerableProps(ErrorEvent, [
      "message",
      "filename",
      "lineno",
      "colno",
      "error",
    ]);
  
    class CloseEvent extends Event {
      #wasClean = "";
      #code = "";
      #reason = "";
  
      get wasClean() {
        return this.#wasClean;
      }
      get code() {
        return this.#code;
      }
      get reason() {
        return this.#reason;
      }
  
      constructor(type, {
        bubbles,
        cancelable,
        composed,
        wasClean = false,
        code = 0,
        reason = "",
      } = {}) {
        super(type, {
          bubbles: bubbles,
          cancelable: cancelable,
          composed: composed,
        });
  
        this.#wasClean = wasClean;
        this.#code = code;
        this.#reason = reason;
      }
  
      [SymbolFor("Deno.privateCustomInspect")](inspect) {
        return inspect(consoleInternal.createFilteredInspectProxy({
          object: this,
          evaluate: this instanceof CloseEvent,
          keys: [
            ...EVENT_PROPS,
            "wasClean",
            "code",
            "reason",
          ],
        }));
      }
    }
  
    class MessageEvent extends Event {
      get source() {
        return null;
      }
  
      constructor(type, eventInitDict) {
        super(type, {
          bubbles: eventInitDict?.bubbles ?? false,
          cancelable: eventInitDict?.cancelable ?? false,
          composed: eventInitDict?.composed ?? false,
        });
  
        this.data = eventInitDict?.data ?? null;
        this.ports = eventInitDict?.ports ?? [];
        this.origin = eventInitDict?.origin ?? "";
        this.lastEventId = eventInitDict?.lastEventId ?? "";
      }
  
      [SymbolFor("Deno.privateCustomInspect")](inspect) {
        return inspect(consoleInternal.createFilteredInspectProxy({
          object: this,
          evaluate: this instanceof MessageEvent,
          keys: [
            ...EVENT_PROPS,
            "data",
            "origin",
            "lastEventId",
          ],
        }));
      }
  
      // TODO(lucacasonato): remove when this interface is spec aligned
      [SymbolToStringTag] = "CloseEvent";
    }
  
    class CustomEvent extends Event {
      #detail = null;
  
      constructor(type, eventInitDict = {}) {
        super(type, eventInitDict);
        webidl.requiredArguments(arguments.length, 1, {
          prefix: "Failed to construct 'CustomEvent'",
        });
        const { detail } = eventInitDict;
        this.#detail = detail;
      }
  
      get detail() {
        return this.#detail;
      }
  
      [SymbolFor("Deno.privateCustomInspect")](inspect) {
        return inspect(consoleInternal.createFilteredInspectProxy({
          object: this,
          evaluate: this instanceof CustomEvent,
          keys: [
            ...EVENT_PROPS,
            "detail",
          ],
        }));
      }
  
      // TODO(lucacasonato): remove when this interface is spec aligned
      [SymbolToStringTag] = "CustomEvent";
    }
  
    ReflectDefineProperty(CustomEvent.prototype, "detail", {
      enumerable: true,
    });
  
    // ProgressEvent could also be used in other DOM progress event emits.
    // Current use is for FileReader.
    class ProgressEvent extends Event {
      constructor(type, eventInitDict = {}) {
        super(type, eventInitDict);
  
        this.lengthComputable = eventInitDict?.lengthComputable ?? false;
        this.loaded = eventInitDict?.loaded ?? 0;
        this.total = eventInitDict?.total ?? 0;
      }
  
      [SymbolFor("Deno.privateCustomInspect")](inspect) {
        return inspect(consoleInternal.createFilteredInspectProxy({
          object: this,
          evaluate: this instanceof ProgressEvent,
          keys: [
            ...EVENT_PROPS,
            "lengthComputable",
            "loaded",
            "total",
          ],
        }));
      }
  
      // TODO(lucacasonato): remove when this interface is spec aligned
      [SymbolToStringTag] = "ProgressEvent";
    }
  
    const _eventHandlers = Symbol("eventHandlers");
  
    function makeWrappedHandler(handler) {
      function wrappedHandler(...args) {
        if (typeof wrappedHandler.handler !== "function") {
          return;
        }
        return FunctionPrototypeCall(wrappedHandler.handler, this, ...args);
      }
      wrappedHandler.handler = handler;
      return wrappedHandler;
    }
  
    // TODO(benjamingr) reuse this here and websocket where possible
    function defineEventHandler(emitter, name, init) {
      // HTML specification section 8.1.5.1
      ObjectDefineProperty(emitter, `on${name}`, {
        get() {
          const map = this[_eventHandlers];
  
          if (!map) return undefined;
          return MapPrototypeGet(map, name)?.handler;
        },
        set(value) {
          if (!this[_eventHandlers]) {
            this[_eventHandlers] = new Map();
          }
          let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name);
          if (handlerWrapper) {
            handlerWrapper.handler = value;
          } else {
            handlerWrapper = makeWrappedHandler(value);
            this.addEventListener(name, handlerWrapper);
            init?.(this);
          }
          MapPrototypeSet(this[_eventHandlers], name, handlerWrapper);
        },
        configurable: true,
        enumerable: true,
      });
    }
  
    window.Event = Event;
    window.EventTarget = EventTarget;
    window.ErrorEvent = ErrorEvent;
    window.CloseEvent = CloseEvent;
    window.MessageEvent = MessageEvent;
    window.CustomEvent = CustomEvent;
    window.ProgressEvent = ProgressEvent;
    window.dispatchEvent = EventTarget.prototype.dispatchEvent;
    window.addEventListener = EventTarget.prototype.addEventListener;
    window.removeEventListener = EventTarget.prototype.removeEventListener;
    window.__bootstrap.eventTarget = {
      EventTarget,
      setEventTargetData,
    };
    window.__bootstrap.event = {
      setIsTrusted,
      setTarget,
      defineEventHandler,
    };
  })(this);
  