/**
 * @file src/js/event-target.js
 */
import * as Events from './utils/events.js';
從“全局/窗口”導入窗口;

/**
 * `EventTarget` 是一個可以與 DOM `EventTarget` 具有相同 API 的類。它
 * 添加了環繞冗長函數的速記函數。例如:
 * `on` 函數是 `addEventListener` 的包裝器。
 *
 * @see [EventTarget 規範]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
 * @class 事件目標
 */
const EventTarget = function() {};

/**
 * 自定義 DOM 事件。
 *
 * @typedef {Object} EventTarget~Event
 * @see [屬性]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
 */

/**
 * 所有事件偵聽器都應遵循以下格式。
 *
 * @callback EventTarget~EventListener
 * @this {EventTarget}
 *
 * @param {EventTarget~Event} 事件
 * 觸發此功能的事件
 *
 * @param {對象} [哈希]
 * 事件期間發送的數據哈希
 */

/**
 * 包含事件名稱作為鍵和布爾值作為值的對象。
 *
 * > 注意:如果在此處將事件名稱設置為真值 {@link EventTarget#trigger}
 * 將具有額外的功能。有關詳細信息,請參閱該函數。
 *
 * @property EventTarget.prototype.allowedEvents_
 * @私人的
 */
EventTarget.prototype.allowedEvents_ = {};

/**
 * 向 `EventTarget` 的實例添加一個 `event listener`。一個“事件監聽器”是
 * 當具有特定名稱的事件被觸發時將被調用的函數。
 *
 * @param {string|string[]} 類型
 * 事件名稱或事件名稱數組。
 *
 * @param {EventTarget~EventListener} fn
 * 使用 `EventTarget` 調用的函數
 */
EventTarget.prototype.on = function(type, fn) {
  // 在調用 Events.on 之前移除 addEventListener 別名
  // 這樣我們就不會陷入無限循環
  const ael = this.addEventListener;

  this.addEventListener = () => {};
  事件.on(這個,類型,fn);
  this.addEventListener = ael;
};

/**
 * {@link EventTarget#on} 的別名。允許“EventTarget”模仿
 * 標準的 DOM API。
 *
 * @功能
 * @see {@link EventTarget#on}
 */
EventTarget.prototype.addEventListener = EventTarget.prototype.on;

/**
 * 從 `EventTarget` 的實例中刪除特定事件的 `event listener`。
 * 這使得 `event listener` 將不再被調用
 * 命名事件發生。
 *
 * @param {string|string[]} 類型
 * 事件名稱或事件名稱數組。
 *
 * @param {EventTarget~EventListener} fn
 * 要刪除的函數。
 */
EventTarget.prototype.off = function(type, fn) {
  Events.off(這個,類型,fn);
};

/**
 * {@link EventTarget#off} 的別名。允許“EventTarget”模仿
 * 標準的 DOM API。
 *
 * @功能
 * @see {@link EventTarget#off}
 */
EventTarget.prototype.removeEventListener = EventTarget.prototype.off;

/**
 * 此函數將添加一個僅觸發一次的“事件偵聽器”。之後
 * 第一次觸發它將被刪除。這就像添加一個“事件監聽器”
 * 與 {@link EventTarget#on} 一起調用 {@link EventTarget#off}。
 *
 * @param {string|string[]} 類型
 * 事件名稱或事件名稱數組。
 *
 * @param {EventTarget~EventListener} fn
 * 為每個事件名稱調用一次的函數。
 */
EventTarget.prototype.one = function(type, fn) {
  // 移除 addEventListener 別名 Events.on
  // 這樣我們就不會陷入無限循環
  const ael = this.addEventListener;

  this.addEventListener = () => {};
  Events.one(這個,類型,fn);
  this.addEventListener = ael;
};

EventTarget.prototype.any = function(type, fn) {
  // 移除 addEventListener 別名 Events.on
  // 這樣我們就不會陷入無限循環
  const ael = this.addEventListener;

  this.addEventListener = () => {};
  Events.any(這個,類型,fn);
  this.addEventListener = ael;
};

/**
 * 此函數導致事件發生。這將導致任何“事件監聽器”
 * 正在等待那個事件,被調用。如果沒有“事件監聽器”
 * 對於一個事件,那麼什麼都不會發生。
 *
 * 如果正在觸發的“事件”的名稱在“EventTarget.allowedEvents_”中。
 * 觸發器還會調用 `on` + `uppercaseEventName` 函數。
 *
 * 例子:
 * 'click' 在 `EventTarget.allowedEvents_` 中,因此,觸發器將嘗試調用
 * `onClick` 如果存在。
 *
 * @param {string|EventTarget~Event|Object} 事件
 * 事件名稱、“Event”或鍵類型設置為的對象
 * 事件名稱。
 */
EventTarget.prototype.trigger = 函數(事件){
  const type = event.type ||事件;

  //棄用
  // 在未來的版本中,我們應該默認目標為 `this`
  // 類似於我們將目標默認為 `elem` 的方式
  // `Events.trigger`。現在默認的“目標”將是
  // `document` 由於調用了 `Event.fixEvent`。
  如果(事件類型==='字符串'){
    事件={類型};
  }
  event = Events.fixEvent(事件);

  如果(this.allowedEvents_[type] && this['on' + type]){
    這個['on' + type](事件);
  }

  Events.trigger(這個,事件);
};

/**
 * {@link EventTarget#trigger} 的別名。允許“EventTarget”模仿
 * 標準的 DOM API。
 *
 * @功能
 * @see {@link EventTarget#trigger}
 */
EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;

讓EVENT_MAP;

EventTarget.prototype.queueTrigger = 函數(事件){
  // 只有在使用時才設置 EVENT_MAP
  如果(!EVENT_MAP){
    EVENT_MAP = 新地圖();
  }

  const type = event.type ||事件;
  讓 map = EVENT_MAP.get(this);

  如果(!地圖){
    地圖=新地圖();
    EVENT_MAP.set(this, map);
  }

  const oldTimeout = map.get(type);

  地圖.刪除(類型);
  window.clearTimeout(oldTimeout);

  const timeout = window.setTimeout(() => {
    地圖.刪除(類型);
    // 如果我們清除了當前目標的所有超時,則刪除它的地圖
    如果(地圖大小=== 0){
      地圖=空;
      EVENT_MAP.delete(這個);
    }

    這個。觸發器(事件);
  }, 0);

  map.set(類型,超時);
};

導出默認事件目標;