/**
 * @file 모달-dialog.js
 */
import * as Dom from './utils/dom';
'./component'에서 컴포넌트 가져오기;
'글로벌/창'에서 창 가져오기;
'글로벌/문서'에서 문서 가져오기;
'키코드'에서 키코드 가져오기;

const MODAL_CLASS_NAME = 'vjs-modal-dialog';

/**
 * `ModalDialog`는 동영상과 컨트롤 위에 표시되며
 * 플레이어가 닫힐 때까지 플레이어와 상호 작용합니다.
 *
 * 모달 대화 상자에는 "닫기" 버튼이 포함되어 있으며 해당 버튼을 누르면 닫힙니다.
 * 활성화 - 또는 ESC를 아무데나 눌렀을 때.
 *
 * @extends 컴포넌트
 */
클래스 ModalDialog 확장 구성 요소 {

  /**
   * 이 클래스의 인스턴스를 만듭니다.
   *
   * @param {플레이어} 플레이어
   * 이 클래스가 연결되어야 하는 `Player`.
   *
   * @param {객체} [옵션]
   * 플레이어 옵션의 키/값 저장소.
   *
   * @param {혼합} [options.content=정의되지 않음]
   * 이 모달에 대한 맞춤형 콘텐츠를 제공합니다.
   *
   * @param {문자열} [옵션 설명]
   * 주로 접근성을 위한 모달에 대한 텍스트 설명입니다.
   *
   * @param {부울} [options.fillAlways=false]
   * 일반적으로 모달은 처음에만 자동으로 채워집니다.
   * 그들은 열립니다. 이것은 모달에게 내용을 새로 고치도록 지시합니다.
   * 열 때마다.
   *
   * @param {문자열} [옵션.레이블]
   * 주로 접근성을 위한 모달용 텍스트 레이블입니다.
   *
   * @param {부울} [options.pauseOnOpen=true]
   * 'true'인 경우 다음 시간에 재생하면 재생이 일시 중지됩니다.
   * 모달이 열리고 닫히면 다시 시작됩니다.
   *
   * @param {부울} [옵션.임시=참]
   * 'true'이면 모달을 한 번만 열 수 있습니다. 그것은 될 것이다
   * 마감되는 대로 폐기합니다.
   *
   * @param {부울} [options.uncloseable=false]
   * `true`이면 사용자가 모달을 닫을 수 없습니다.
   * 일반적인 방법으로 UI를 통해. 프로그래밍 방식 종료는
   * 여전히 가능합니다.
   */
  생성자(플레이어, 옵션) {
    super(플레이어, 옵션);

    this.handleKeyDown_ = (e) => this.handleKeyDown(e);
    this.close_ = (e) => this.close(e);
    this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = 거짓;

    this.closeable(!this.options_.uncloseable);
    this.content(this.options_.content);

    // 자식이 초기화된 후에 contentEl이 정의되었는지 확인합니다.
    // contentEl에 있는 모달의 내용만 원하기 때문에
    // (닫기 버튼과 같은 UI 요소가 아님).
    this.contentEl_ = Dom.createEl('div', {
      클래스 이름: `${MODAL_CLASS_NAME}-content`
    }, {
      역할: '문서'
    });

    this.descEl_ = Dom.createEl('p', {
      className: `${MODAL_CLASS_NAME}-description vjs-control-text`,
      id: this.el().getAttribute('aria-describedby')
    });

    Dom.textContent(this.descEl_, this.description());
    this.el_.appendChild(this.descEl_);
    this.el_.appendChild(this.contentEl_);
  }

  /**
   * `ModalDialog`의 DOM 요소 생성
   *
   * @return {요소}
   * 생성되는 DOM 요소.
   */
  createEl() {
    return super.createEl('div', {
      className: this.buildCSSClass(),
      탭 인덱스: -1
    }, {
      'aria-describedby': `${this.id()}_description`,
      'aria-hidden': '참',
      '아리아 라벨': this.label(),
      '역할': '대화'
    });
  }

  폐기() {
    this.contentEl_ = null;
    this.descEl_ = null;
    this.previousActiveEl_ = null;

    super.dispose();
  }

  /**
   * 기본 DOM `className`을 빌드합니다.
   *
   * @return {문자열}
   * 이 개체의 DOM `className`입니다.
   */
  buildCSSClass() {
    `${MODAL_CLASS_NAME} vjs-hidden ${super.buildCSSClass()}` 반환;
  }

  /**
   * 이 모달의 레이블 문자열을 반환합니다. 주로 접근성에 사용됩니다.
   *
   * @return {문자열}
   * 이 모달의 현지화 또는 원시 레이블.
   */
  라벨() {
    return this.localize(this.options_.label || '모달 창');
  }

  /**
   * 이 모달에 대한 설명 문자열을 반환합니다. 주로 사용
   * 접근성.
   *
   * @return {문자열}
   * 이 모달의 현지화된 설명 또는 초기 설명입니다.
   */
  설명() {
    let desc = this.options_.description || this.localize('모달 창입니다.');

    // 모달이 닫힐 수 있는 경우 범용 닫기 가능성 메시지를 추가합니다.
    if (this.closeable()) {
      desc += ' ' + this.localize('Esc 키를 누르거나 닫기 버튼을 활성화하여 이 모달을 닫을 수 있습니다.');
    }

    반환 설명;
  }

  /**
   * 모달을 엽니다.
   *
   * @fires ModalDialog#beforemodalopen
   * @fires ModalDialog#modalopen
   */
  열려 있는() {
    if (!this.opened_) {
      const player = this.player();

      /**
        * `ModalDialog`가 열리기 직전에 실행됩니다.
        *
        * @event ModalDialog#beforemodalopen
        * @type {이벤트대상~이벤트}
        */
      this.trigger('beforemodalopen');
      this.opened_ = 참;

      // 모달이 이전에 열린 적이 없는 경우 콘텐츠를 채우고
      // 채워진 적이 없습니다.
      if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
        this.fill();
      }

      // 플레이어가 재생 중인 경우 일시 중지하고 이전
      // 재생 상태.
      this.wasPlaying_ = !player.paused();

      if (this.options_.pauseOnOpen && this.wasPlaying_) {
        player.pause();
      }

      this.on('keydown', this.handleKeyDown_);

      // 컨트롤을 숨기고 활성화되었는지 확인합니다.
      this.hadControls_ = player.controls();
      player.controls(거짓);

      this.show();
      this.conditionalFocus_();
      this.el().setAttribute('aria-hidden', 'false');

      /**
        * `ModalDialog`가 열린 직후에 시작됩니다.
        *
        * @event 모달대화상자#modalopen
        * @type {이벤트대상~이벤트}
        */
      this.trigger('modalopen');
      this.hasBeenOpened_ = 참;
    }
  }

  /**
   * `ModalDialog`가 현재 열려 있거나 닫혀 있는지 여부.
   *
   * @param {부울} [값]
   * 주어진 경우 모달을 열거나(`true`) 닫습니다(`false`).
   *
   * @return {부울}
   * 모달 대화 상자의 현재 열린 상태
   */
  열림(값) {
    if (typeof 값 === '부울') {
      이[값 ? '열기' : '닫기']();
    }
    반환 this.opened_;
  }

  /**
   * 모달을 닫고 `ModalDialog`가
   * 열려 있지 않음.
   *
   * @fires ModalDialog#beforemodalclose
   * @fires ModalDialog#modalclose
   */
  닫다() {
    if (!this.opened_) {
      반품;
    }
    const player = this.player();

    /**
      * `ModalDialog`가 닫히기 직전에 시작됩니다.
      *
      * @event ModalDialog#beforemodalclose
      * @type {이벤트대상~이벤트}
      */
    this.trigger('beforemodalclose');
    this.opened_ = 거짓;

    if (this.wasPlaying_ && this.options_.pauseOnOpen) {
      플레이어.플레이();
    }

    this.off('키다운', this.handleKeyDown_);

    if (this.hadControls_) {
      player.controls(true);
    }

    this.hide();
    this.el().setAttribute('aria-hidden', 'true');

    /**
      * `ModalDialog`가 닫힌 직후에 시작됩니다.
      *
      * @event 모달대화상자#modalclose
      * @type {이벤트대상~이벤트}
      */
    this.trigger('modalclose');
    this.conditionalBlur_();

    if (this.options_.temporary) {
      this.dispose();
    }
  }

  /**
   * UI를 통해 `ModalDialog`를 닫을 수 있는지 확인하십시오.
   *
   * @param {부울} [값]
   * 부울로 주어지면 `closeable` 옵션을 설정합니다.
   *
   * @return {부울}
   * 닫을 수 있는 옵션의 최종 값을 반환합니다.
   */
  닫기 가능(값) {
    if (typeof 값 === '부울') {
      const closeable = this.closeable_ = !!value;
      let close = this.getChild('closeButton');

      // 닫을 수 있게 만들고 닫기 버튼이 없으면 추가합니다.
      if (닫을 수 있는 && !닫다) {

        // 닫기 버튼은 모달의 자식이어야 합니다.
        // 콘텐츠 요소이므로 일시적으로 콘텐츠 요소를 변경합니다.
        const temp = this.contentEl_;

        this.contentEl_ = this.el_;
        닫기 = this.addChild('closeButton', {controlText: '모달 대화상자 닫기'});
        this.contentEl_ = 임시;
        this.on(닫기, '닫기', this.close_);
      }

      // 닫을 수 없게 만들고 닫기 버튼이 있으면 제거합니다.
      if (!닫을 수 있는 && 닫다) {
        this.off(닫기, '닫기', this.close_);
        this.removeChild(닫기);
        닫기.처리();
      }
    }
    this.closeable_을 반환합니다.
  }

  /**
   * 모달의 콘텐츠 요소를 모달의 "콘텐츠" 옵션으로 채웁니다.
   * 이 변경 사항이 적용되기 전에 콘텐츠 요소가 비워집니다.
   */
  채우다() {
    this.fillWith(this.content());
  }

  /**
   * 임의의 콘텐츠로 모달의 콘텐츠 요소를 채웁니다.
   * 이 변경 사항이 적용되기 전에 콘텐츠 요소가 비워집니다.
   *
   * @fires ModalDialog#beforemodalfill
   * @fires ModalDialog#modalfill
   *
   * @param {혼합} [내용]
   * '콘텐츠' 옵션에 적용되는 것과 동일한 규칙이 적용됩니다.
   */
  채우기(내용) {
    const contentEl = this.contentEl();
    const parentEl = contentEl.parentNode;
    const nextSiblingEl = contentEl.nextSibling;

    /**
      * `ModalDialog`가 콘텐츠로 채워지기 직전에 시작됩니다.
      *
      * @event ModalDialog#beforemodalfill
      * @type {이벤트대상~이벤트}
      */
    this.trigger('beforemodalfill');
    this.hasBeenFilled_ = 참;

    // 수행하기 전에 DOM에서 콘텐츠 요소를 분리합니다.
    // 라이브 DOM을 여러 번 수정하지 않도록 조작합니다.
    parentEl.removeChild(contentEl);
    this.empty();
    Dom.insertContent(contentEl, 콘텐츠);
    /**
     * `ModalDialog`가 콘텐츠로 채워진 직후에 시작됩니다.
     *
     * @event 모달대화상자#modalfill
     * @type {이벤트대상~이벤트}
     */
    this.trigger('modalfill');

    // 다시 채워진 콘텐츠 요소를 다시 삽입합니다.
    경우 (nextSiblingEl) {
      parentEl.insertBefore(contentEl, nextSiblingEl);
    } else {
      parentEl.appendChild(contentEl);
    }

    // 닫기 버튼이 대화 DOM에서 마지막인지 확인합니다.
    const closeButton = this.getChild('closeButton');

    if(닫기버튼) {
      parentEl.appendChild(closeButton.el_);
    }
  }

  /**
   * 콘텐츠 요소를 비웁니다. 이것은 모달이 채워질 때마다 발생합니다.
   *
   * @fires ModalDialog#beforemodalempty
   * @fires ModalDialog#modalempty
   */
  비어 있는() {
    /**
    * `ModalDialog`가 비워지기 직전에 시작됩니다.
    *
    * @event ModalDialog#beforemodalempty
    * @type {이벤트대상~이벤트}
    */
    this.trigger('beforemodalempty');
    Dom.emptyEl(this.contentEl());

    /**
    * `ModalDialog`가 비워진 직후에 시작됩니다.
    *
    * @event ModalDialog#modalempty
    * @type {이벤트대상~이벤트}
    */
    this.trigger('modalempty');
  }

  /**
   * 정규화되기 전에 모달 콘텐츠를 가져오거나 설정합니다.
   * DOM으로 렌더링됩니다.
   *
   * 이것은 DOM을 업데이트하거나 모달을 채우지는 않지만 호출되는 동안 호출됩니다.
   * 그 과정.
   *
   * @param {혼합} [값]
   * 정의된 경우에 사용할 내부 콘텐츠 값을 설정합니다.
   * `fill`에 대한 다음 호출(들). 이 값은 정규화되기 전에
   * 삽입. 내부 콘텐츠 값을 "지우려면" `null`을 전달합니다.
   *
   * @return {혼합}
   * 모달 대화 상자의 현재 내용
   */
  내용(값) {
    if (값 유형 !== '정의되지 않음') {
      this.content_ = 값;
    }
    this.content_를 반환합니다.
  }

  /**
   * 이전에 플레이어에 포커스가 있는 경우 조건부로 모달 대화 상자에 포커스를 맞춥니다.
   *
   * @사적인
   */
  conditionalFocus_() {
    const activeEl = document.activeElement;
    const playerEl = this.player_.el_;

    this.previousActiveEl_ = null;

    if (playerEl.contains(activeEl) || playerEl === activeEl) {
      this.previousActiveEl_ = 활성 El;

      this.focus();
    }
  }

  /**
   * 조건부로 요소를 흐리게 처리하고 마지막으로 초점을 맞춘 요소의 초점을 다시 맞춥니다.
   *
   * @사적인
   */
  conditionalBlur_() {
    if (this.previousActiveEl_) {
      this.previousActiveEl_.focus();
      this.previousActiveEl_ = null;
    }
  }

  /**
   * 키 다운 핸들러. 모달에 포커스가 있을 때 첨부됩니다.
   *
   * @listens 키다운
   */
  handleKeyDown(이벤트) {

    // 키다운이 모달 대화 상자 밖으로 도달하지 않도록 합니다.
    event.stopPropagation();

    if (keycode.isEventKey(이벤트, '탈출') && this.closeable()) {
      event.preventDefault();
      this.close();
      반품;
    }

    // 탭 키가 아니면 일찍 종료
    if (!keycode.isEventKey(event, 'Tab')) {
      반품;
    }

    const focusableEls = this.focusableEls_();
    const activeEl = this.el_.querySelector(':focus');
    let focusIndex;

    에 대한 (하자 i = 0; i < focusableEls.length; i++) {
      if (activeEl === focusableEls[i]) {
        focusIndex = i;
        부서지다;
      }
    }

    if (document.activeElement === this.el_) {
      포커스 인덱스 = 0;
    }

    if (이벤트.쉬프트키 && 포커스 인덱스 === 0) {
      focusableEls[focusableEls.length - 1].focus();
      event.preventDefault();
    } 그렇지 않으면 (!event.shiftKey && focusIndex === focusableEls.length - 1) {
      focusableEls[0].focus();
      event.preventDefault();
    }
  }

  /**
   * 모든 포커스 가능 요소 가져오기
   *
   * @사적인
   */
  focusableEls_() {
    const allChildren = this.el_.querySelectorAll('*');

    return Array.prototype.filter.call(allChildren, (자식) => {
      return ((window.HTMLAnchorElement의 하위 인스턴스 ||
               window.HTMLAreaElement의 하위 인스턴스) && child.hasAttribute('href')) ||
             ((window.HTMLInputElement의 하위 인스턴스 ||
               window.HTMLSelectElement의 자식 인스턴스 ||
               window.HTMLTextAreaElement의 자식 인스턴스 ||
               window.HTMLButtonElement의 하위 인스턴스) && !child.hasAttribute('비활성화됨')) ||
             (window.HTMLIFrameElement의 하위 인스턴스 ||
               window.HTMLObjectElement의 자식 인스턴스 ||
               window.HTMLEmbedElement의 자식 인스턴스) ||
             (자식.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1) ||
             (child.hasAttribute('contenteditable'));
    });
  }
}

/**
 * `ModalDialog` 기본 옵션에 대한 기본 옵션.
 *
 * @type {객체}
 * @사적인
 */
ModalDialog.prototype.options_ = {
  pauseOnOpen: 참,
  임시: 사실
};

Component.registerComponent('ModalDialog', ModalDialog);
기본 ModalDialog 내보내기;