/**
 * @file src/js/event-target.js
 */
import * as Events from './utils/events.js';
'글로벌/창'에서 창 가져오기;

/**
 * `EventTarget`은 DOM `EventTarget`과 동일한 API를 가질 수 있는 클래스입니다. 그것
 * 긴 함수를 감싸는 속기 함수를 추가합니다. 예:
 * `on` 함수는 `addEventListener` 주변의 래퍼입니다.
 *
 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
 * @클래스 이벤트 타겟
 */
const EventTarget = function() {};

/**
 * 커스텀 DOM 이벤트.
 *
 * @typedef {객체} EventTarget~Event
 * @see [속성]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
 */

/**
 * 모든 이벤트 리스너는 다음 형식을 따라야 합니다.
 *
 * @callback EventTarget~EventListener
 * @이 {이벤트 대상}
 *
 * @param {EventTarget~Event} 이벤트
 * 이 함수를 트리거한 이벤트
 *
 * @param {객체} [해시]
 * 이벤트 중에 전송된 데이터의 해시
 */

/**
 * 이벤트 이름을 키로, 부울 값을 값으로 포함하는 개체입니다.
 *
 * > 메모: 여기에서 이벤트 이름이 true 값으로 설정된 경우 {@link EventTarget#trigger}
 * 추가 기능이 있습니다. 자세한 내용은 해당 기능을 참조하십시오.
 *
 * @property EventTarget.prototype.allowedEvents_
 * @사적인
 */
EventTarget.prototype.allowedEvents_ = {};

/**
 * `EventTarget`의 인스턴스에 `이벤트 리스너`를 추가합니다. `이벤트 리스너`는
 * 특정 이름의 이벤트가 발생했을 때 호출되는 함수.
 *
 * @param {문자열|문자열[]} 유형
 * 이벤트 이름 또는 이벤트 이름의 배열.
 *
 * @param {EventTarget~EventListener} fn
 * `EventTarget`으로 호출하는 함수
 */
EventTarget.prototype.on = 기능(유형, fn) {
  // Events.on을 호출하기 전에 addEventListener 별칭을 제거합니다.
  // 무한 유형 루프에 빠지지 않도록
  const ael = this.addEventListener;

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

/**
 * {@link EventTarget#on}의 별칭입니다. `EventTarget`이 모방하도록 허용
 * 표준 DOM API.
 *
 * @기능
 * @see {@link EventTarget#on}
 */
EventTarget.prototype.addEventListener = EventTarget.prototype.on;

/**
 * `EventTarget` 인스턴스에서 특정 이벤트에 대한 `이벤트 리스너`를 제거합니다.
 * 이렇게 하면 `이벤트 리스너`가 더 이상 호출되지 않습니다.
 * 명명된 이벤트가 발생합니다.
 *
 * @param {문자열|문자열[]} 유형
 * 이벤트 이름 또는 이벤트 이름의 배열.
 *
 * @param {EventTarget~EventListener} fn
 * 제거하는 기능입니다.
 */
EventTarget.prototype.off = 기능(유형, fn) {
  Events.off(this, type, fn);
};

/**
 * {@link EventTarget#off}의 별칭. `EventTarget`이 모방하도록 허용
 * 표준 DOM API.
 *
 * @기능
 * @see {@link EventTarget#off}
 */
EventTarget.prototype.removeEventListener = EventTarget.prototype.off;

/**
 * 이 함수는 한 번만 트리거되는 `이벤트 리스너`를 추가합니다. 후
 * 첫 번째 트리거는 제거됩니다. 이것은 `이벤트 리스너`를 추가하는 것과 같습니다.
 * {@link EventTarget#on} 자체에서 {@link EventTarget#off}를 호출하는 {@link EventTarget#on} 사용.
 *
 * @param {문자열|문자열[]} 유형
 * 이벤트 이름 또는 이벤트 이름의 배열.
 *
 * @param {EventTarget~EventListener} fn
 * 각 이벤트 이름에 대해 한 번씩 호출되는 함수.
 */
EventTarget.prototype.one = 기능(유형, fn) {
  // addEventListener 앨리어싱 Events.on을 제거합니다.
  // 무한 유형 루프에 빠지지 않도록
  const ael = this.addEventListener;

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

EventTarget.prototype.any = 함수(유형, fn) {
  // addEventListener 앨리어싱 Events.on을 제거합니다.
  // 무한 유형 루프에 빠지지 않도록
  const ael = this.addEventListener;

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

/**
 * 이 함수는 이벤트를 발생시킵니다. 그러면 '이벤트 리스너'가 발생합니다.
 * 해당 이벤트를 기다리는 중 호출됩니다. `이벤트 리스너`가 없는 경우
 * 이벤트의 경우 아무 일도 일어나지 않습니다.
 *
 * 트리거되는 `Event`의 이름이 `EventTarget.allowedEvents_`에 있는 경우.
 * 트리거는 `on` + `uppercaseEventName` 함수도 호출합니다.
 *
 * 예:
 * '클릭'은 `EventTarget.allowedEvents_`에 있으므로 트리거가 호출을 시도합니다.
 * 존재하는 경우 'onClick'.
 *
 * @param {string|EventTarget~Event|Object} 이벤트
 * 이벤트의 이름, `Event` 또는 다음으로 설정된 유형의 키가 있는 객체
 * 이벤트 이름.
 */
EventTarget.prototype.trigger = 함수(이벤트) {
  상수 유형 = event.type || 이벤트;

  // 지원 중단
  // 향후 버전에서는 기본 대상을 `this`로 지정해야 합니다.
  // 대상을 `elem`으로 기본 설정하는 방법과 비슷합니다.
  // `이벤트.트리거`. 현재 기본 `target`은
  // `Event.fixEvent` 호출로 인한 `document`.
  if (이벤트 유형 === '문자열') {
    이벤트 = {유형};
  }
  이벤트 = Events.fixEvent(이벤트);

  if (this.allowedEvents_[유형] && this['on' + 유형]) {
    this['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이 사용될 경우에만 설정
  if (!EVENT_MAP) {
    EVENT_MAP = 새 지도();
  }

  상수 유형 = event.type || 이벤트;
  let map = EVENT_MAP.get(이);

  if (!맵) {
    지도 = 새 지도();
    EVENT_MAP.set(이것, 지도);
  }

  const oldTimeout = map.get(유형);

  지도.삭제(유형);
  window.clearTimeout(oldTimeout);

  const timeout = window.setTimeout(() => {
    지도.삭제(유형);
    // 현재 대상에 대한 모든 시간 제한을 지운 경우 해당 맵을 삭제합니다.
    if (지도 크기 === 0) {
      지도 = null;
      EVENT_MAP.delete(이);
    }

    this.trigger(이벤트);
  }, 0);

  map.set(타입, 타임아웃);
};

기본 EventTarget 내보내기;