/**
 * @Datei src/js/event-target.js
 * /
import * as Events aus './utils/events.js';
import window from 'global/window';

/**
 * eventTarget" ist eine Klasse, die die gleiche API wie das DOM "EventTarget" haben kann. Es
 * fügt Shorthand-Funktionen hinzu, die längere Funktionen umschließen. Zum Beispiel:
 * die Funktion "on" ist ein Wrapper um "addEventListener".
 *
 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
 * @class EventTarget
 * /
const EventTarget = function() {};

/**
 * Ein benutzerdefiniertes DOM-Ereignis.
 *
 * @typedef {Object} EreignisZiel~Ereignis
 * @see [Eigenschaften]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
 * /

/**
 * Alle Ereignis-Listener sollten dem folgenden Format folgen.
 *
 * @callback EventTarget~EventListener
 * @this {EventTarget}
 *
 * @param {EventTarget~Event} event
 *        das Ereignis, das diese Funktion ausgelöst hat
 *
 * @param {Object} [Raute]
 *        hash der während des Ereignisses gesendeten Daten
 * /

/**
 * Ein Objekt mit Ereignisnamen als Schlüssel und Booleschen Werten.
 *
 * > HINWEIS: Wenn ein Ereignisname hier auf einen wahren Wert gesetzt wird {@link EventTarget#trigger}
 *         wird zusätzliche Funktionen haben. Weitere Informationen finden Sie in dieser Funktion.
 *
 * @property EventTarget.prototype.allowedEvents_
 * @privat
 * /
EventTarget.prototype.allowedEvents_ = {};

/**
 * Fügt einen "Ereignis-Listener" zu einer Instanz eines "EventTarget" hinzu. Ein `Ereignis-Hörer` ist ein
 * funktion, die aufgerufen wird, wenn ein Ereignis mit einem bestimmten Namen ausgelöst wird.
 *
 * @param {string|string[]} type
 *        Ein Ereignisname oder ein Array von Ereignisnamen.
 *
 * @param {EventTarget~EventListener} fn
 *        Die Funktion, die mit `EventTarget` aufgerufen wird
 * /
EventTarget.prototype.on = function(type, fn) {
  // Entfernen Sie den Alias addEventListener vor dem Aufruf von Events.on
  // damit wir nicht in eine unendliche Typ-Schleife geraten
  const ael = this.addEventListener;

  this.addEventListener = () => {};
  Events.on(this, type, fn);
  this.addEventListener = ael;
};

/**
 * Ein Alias von {@link EventTarget#on}. Ermöglicht `EventTarget` die Nachahmung
 * die Standard-DOM-API.
 *
 * @Funktion
 * @siehe {@link EventTarget#on}
 * /
EventTarget.prototype.addEventListener = EventTarget.prototype.on;

/**
 * Entfernt einen `Ereignis-Listener` für ein bestimmtes Ereignis aus einer Instanz von `EventTarget`.
 * Dadurch wird der `Ereignis-Hörer` nicht mehr aufgerufen, wenn die
 * genanntes Ereignis eintritt.
 *
 * @param {string|string[]} type
 *        Ein Ereignisname oder ein Array von Ereignisnamen.
 *
 * @param {EventTarget~EventListener} fn
 *        Die zu entfernende Funktion.
 * /
EventTarget.prototype.off = function(type, fn) {
  Events.off(this, type, fn);
};

/**
 * Ein Alias von {@link EventTarget#off}. Ermöglicht `EventTarget` die Nachahmung
 * die Standard-DOM-API.
 *
 * @Funktion
 * @siehe {@link EventTarget#off}
 * /
EventTarget.prototype.removeEventListener = EventTarget.prototype.off;

/**
 * Diese Funktion fügt einen `Ereignis-Hörer` hinzu, der nur einmal ausgelöst wird. Nachdem die
 * ersten Auslöser wird er entfernt. Dies ist wie das Hinzufügen eines "Ereignis-Hörers"
 * mit {@link EventTarget#on}, das selbst {@link EventTarget#off} aufruft.
 *
 * @param {string|string[]} type
 *        Ein Ereignisname oder ein Array von Ereignisnamen.
 *
 * @param {EventTarget~EventListener} fn
 *        Die Funktion, die für jeden Ereignisnamen einmal aufgerufen wird.
 * /
EventTarget.prototype.one = function(type, fn) {
  // Entfernen Sie den addEventListener aliasing Events.on
  // damit wir nicht in eine unendliche Typ-Schleife geraten
  const ael = this.addEventListener;

  this.addEventListener = () => {};
  Events.one(this, type, fn);
  this.addEventListener = ael;
};

EventTarget.prototype.any = function(type, fn) {
  // Entfernen Sie den addEventListener aliasing Events.on
  // damit wir nicht in eine unendliche Typ-Schleife geraten
  const ael = this.addEventListener;

  this.addEventListener = () => {};
  Events.any(this, type, fn);
  this.addEventListener = ael;
};

/**
 * Mit dieser Funktion wird ein Ereignis ausgelöst. Dies führt dann dazu, dass alle `Ereignis-Hörer`
 * die auf dieses Ereignis warten, um aufgerufen zu werden. Wenn es keine `Ereigniszuhörer` gibt
 * für ein Ereignis, dann wird nichts passieren.
 *
 * Wenn der Name des Ereignisses, das ausgelöst wird, in `EventTarget.allowedEvents_` enthalten ist.
 * Der Auslöser ruft auch die Funktion `on` + `uppercaseEventName` auf.
 *
 * Beispiel:
 * click' ist in `EventTarget.allowedEvents_`, also wird der Auslöser versuchen, die
 * onClick", wenn sie existiert.
 *
 * @param {string|EventTarget~Event|Object} event
 *        Der Name des Ereignisses, ein `Event` oder ein Objekt mit einem Schlüssel vom Typ, der auf
 *        einen Ereignisnamen.
 * /
EventTarget.prototype.trigger = function(event) {
  const type = event.type || event;

  // Verwerfung
  // In einer zukünftigen Version sollten wir das Ziel auf `das` setzen
  // ähnlich wie bei der Vorgabe des Ziels `elem` in
  // `Events.trigger`. Im Moment ist das Standard-Ziel
  // `Dokument` aufgrund des Aufrufs `Event.fixEvent`.
  if (typeof event === 'string') {
    ereignis = {Typ};
  }
  event = Events.fixEvent(event);

  if (this.allowedEvents_[type] && this['on' + type]) {
    this['on' + type](event);
  }

  Events.trigger(this, event);
};

/**
 * Ein Alias von {@link EventTarget#trigger}. Ermöglicht `EventTarget` die Nachahmung
 * die Standard-DOM-API.
 *
 * @Funktion
 * @siehe {@link EventTarget#trigger}
 * /
EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;

lassen Sie EVENT_MAP;

EventTarget.prototype.queueTrigger = function(event) {
  // EVENT_MAP nur einrichten, wenn es verwendet werden soll
  if (!EVENT_MAP) {
    EVENT_MAP = new Map();
  }

  const type = event.type || event;
  let map = EVENT_MAP.get(this);

  if (!map) {
    map = new Map();
    EVENT_MAP.set(this, map);
  }

  const oldTimeout = map.get(type);

  map.delete(type);
  window.clearTimeout(oldTimeout);

  const timeout = window.setTimeout(() => {
    map.delete(type);
    // wenn wir alle Zeitüberschreitungen für das aktuelle Ziel gelöscht haben, löschen wir seine Karte
    if (map.size === 0) {
      karte = null;
      EVENT_MAP.delete(this);
    }

    this.trigger(event);
  }, 0);

  map.set(type, timeout);
};

standard-EventTarget exportieren;