text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.EventHooks = void 0;
  const array_1 = require("./array");
  const momentos = new WeakMap();
  /**
   * Event emitter which allows listeners to return a value.
   *
   * This is beneficial for the themes since it allows plugins to modify the HTML output
   * without doing unsafe text replacement.
   *
   * This class is functionally nearly identical to the {@link EventEmitter} class with
   * two exceptions.
   * 1. The {@link EventEmitter} class only `await`s return values from its listeners, it
   *    does not return them to the emitter.
   * 2. This class requires listeners to by synchronous, unless `R` is specified as to be
   *    a promise or other deferred type.
   *
   * @example
   * ```ts
   * const x = new EventHooks<{ a: [string] }, string>()
   * x.on('a', a => a.repeat(123)) // ok, returns a string
   * x.on('b', console.log) // error, 'b' is not assignable to 'a'
   * x.on('a' a => 1) // error, returns a number but expected a string
   * ```
   */
  class EventHooks {
      constructor() {
          // Function is *usually* not a good type to use, but here it lets us specify stricter
          // contracts in the methods while not casting everywhere this is used.
          this._listeners = new Map();
      }
      /**
       * Starts listening to an event.
       * @param event the event to listen to.
       * @param listener function to be called when an this event is emitted.
       * @param order optional order to insert this hook with.
       */
      on(event, listener, order = 0) {
          const list = (this._listeners.get(event) || []).slice();
          (0, array_1.insertOrderSorted)(list, { listener, order });
          this._listeners.set(event, list);
      }
      /**
       * Listens to a single occurrence of an event.
       * @param event the event to listen to.
       * @param listener function to be called when an this event is emitted.
       * @param order optional order to insert this hook with.
       */
      once(event, listener, order = 0) {
          const list = (this._listeners.get(event) || []).slice();
          (0, array_1.insertOrderSorted)(list, { listener, once: true, order });
          this._listeners.set(event, list);
      }
      /**
       * Stops listening to an event.
       * @param event the event to stop listening to.
       * @param listener the function to remove from the listener array.
       */
      off(event, listener) {
          const listeners = this._listeners.get(event);
          if (listeners) {
              const index = listeners.findIndex((lo) => lo.listener === listener);
              if (index > -1) {
                  listeners.splice(index, 1);
              }
          }
      }
      /**
       * Emits an event to all currently subscribed listeners.
       * @param event the event to emit.
       * @param args any arguments required for the event.
       */
      emit(event, ...args) {
          var _a;
          const listeners = ((_a = this._listeners.get(event)) === null || _a === void 0 ? void 0 : _a.slice()) || [];
          this._listeners.set(event, listeners.filter(({ once }) => !once));
          return listeners.map(({ listener }) => listener(...args));
      }
      saveMomento() {
          const momento = {};
          const save = new Map();
          for (const [key, val] of this._listeners) {
              save.set(key, [...val]);
          }
          momentos.set(momento, save);
          return momento;
      }
      restoreMomento(momento) {
          const saved = momentos.get(momento);
          if (saved) {
              this._listeners.clear();
              for (const [key, val] of saved) {
                  this._listeners.set(key, [...val]);
              }
          }
          else {
              throw new Error("Momento not found.");
          }
      }
  }
  exports.EventHooks = EventHooks;
  