/**
 * @file modal-dialog.js
 */
從 './utils/dom' 導入 * 作為 Dom;
從 './component' 導入組件;
從“全局/窗口”導入窗口;
從“全局/文檔”導入文檔;
從“鍵碼”導入鍵碼;

const MODAL_CLASS_NAME = 'vjs-modal-dialog';

/**
 * `ModalDialog` 顯示在視頻及其控件上,它會阻止
 * 與播放器的互動,直到它被關閉。
 *
 *模態對話框包括一個“關閉”按鈕,當該按鈕時將關閉
 * 被激活 - 或者當 ESC 在任何地方被按下時。
 *
 * @extends 組件
 */
類 ModalDialog 擴展組件 {

  /**
   * 創建此類的一個實例。
   *
   * @param {Player} 播放器
   * 此類應附加到的 `Player`。
   *
   * @param {對象} [選項]
   * 播放器選項的鍵/值存儲。
   *
   * @param {混合} [options.content=undefined]
   * 為此模態提供自定義內容。
   *
   * @param {string} [選項.描述]
   * 模式的文本描述,主要用於輔助功能。
   *
   * @param {boolean} [options.fillAlways=false]
   * 通常,模態框只會在第一次自動填充
   * 他們打開。這告訴模態刷新它的內容
   * 每次打開。
   *
   * @param {string} [選項.標籤]
   * 模式的文本標籤,主要用於可訪問性。
   *
   * @param {boolean} [options.pauseOnOpen=true]
   * 如果為 `true`,播放將暫停
   * 模式打開,並在關閉時恢復。
   *
   * @param {boolean} [options.temporary=true]
   * 如果為 `true`,模態框只能打開一次;這將是
   *關閉後立即處理。
   *
   * @param {boolean} [options.uncloseable=false]
   * 如果為 `true`,用戶將無法關閉模態
   * 以正常方式通過 UI。程序化關閉是
   * 仍有可能。
   */
  構造函數(播放器,選項){
    超級(播放器,選項);

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

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

    // 確保在任何子項初始化後定義了 contentEl
    // 因為我們只想要 contentEl 中模態的內容
    //(不是像關閉按鈕這樣的 UI 元素)。
    this.contentEl_ = Dom.createEl('div', {
      類名:`${MODAL_CLASS_NAME}-內容`
    },{
      角色:'文檔'
    });

    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 元素。
   */
  創建El() {
    返回 super.createEl('div', {
      類名:this.buildCSSClass(),
      選項卡索引:-1
    },{
      'aria-describedby': `${this.id()}_description`,
      'aria-hidden':'真實',
      'aria-label': this.label(),
      '角色':'對話'
    });
  }

  處置(){
    this.contentEl_ = null;
    this.descEl_ = null;
    this.previouslyActiveEl_ = null;

    super.dispose();
  }

  /**
   * 構建默認的 DOM `className`。
   *
   * @return {字符串}
   * 此對象的 DOM `className`。
   */
  buildCSSClass() {
    返回`${MODAL_CLASS_NAME} vjs-hidden ${super.buildCSSClass()}`;
  }

  /**
   * 返回此模式的標籤字符串。主要用於可訪問性。
   *
   * @return {字符串}
   * 此模態的本地化或原始標籤。
   */
  標籤() {
    return this.localize(this.options_.label || 'Modal Window');
  }

  /**
   * 返回此模式的描述字符串。主要用於
   * 無障礙。
   *
   * @return {字符串}
   * 此模態的本地化或原始描述。
   */
  描述() {
    讓 desc = this.options_.description || this.localize('這是一個模態窗口。');

    // 如果模態是可關閉的,則附加通用可關閉性消息。
    如果(this.closeable()){
      desc += ' ' + this.localize('可以通過按 Escape 鍵或激活關閉按鈕來關閉此模式。');
    }

    返回描述;
  }

  /**
   * 打開模式。
   *
   * @fires ModalDialog#beforemodalopen
   * @fires ModalDialog#modalopen
   */
  打開() {
    如果(!this.opened_){
      const player = this.player();

      /**
        * 在 `ModalDialog` 打開之前觸發。
        *
        * @event ModalDialog#beforemodalopen
        * @type {EventTarget~Event}
        */
      this.trigger('beforemodalopen');
      this.opened_ = true;

      // 如果模式之前從未打開過,則填充內容
      // 從未被填充。
      如果(this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_){
        這個.填充();
      }

      // 如果玩家正在玩,暫停它並記下它之前的
      //播放狀態。
      this.wasPlaying_ = !player.paused();

      如果(this.options_.pauseOnOpen && this.wasPlaying_){
        播放器.暫停();
      }

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

      // 隱藏控件並註意它們是否已啟用。
      this.hadControls_ = player.controls();
      player.controls(false);

      這個。顯示();
      this.conditionalFocus_();
      this.el().setAttribute('aria-hidden', 'false');

      /**
        * 在 `ModalDialog` 打開後觸發。
        *
        * @event ModalDialog#modalopen
        * @type {EventTarget~Event}
        */
      this.trigger('modalopen');
      this.hasBeenOpened_ = true;
    }
  }

  /**
   * 如果 `ModalDialog` 當前打開或關閉。
   *
   * @param {boolean} [值]
   * 如果給定,它將打開(`true`)或關閉(`false`)模態。
   *
   * @return {布爾值}
   * 模態對話框的當前打開狀態
   */
  打開(值){
    如果(類型值==='布爾值'){
      這個[價值? '開關']();
    }
    返回this.opened_;
  }

  /**
   * 關閉模式,如果 `ModalDialog` 是什麼都不做
   * 沒開。
   *
   * @fires ModalDialog#beforemodalclose
   * @fires ModalDialog#modalclose
   */
  關閉() {
    如果(!this.opened_){
      返回;
    }
    const player = this.player();

    /**
      * 在 `ModalDialog` 關閉之前觸發。
      *
      * @event ModalDialog#beforemodalclose
      * @type {EventTarget~Event}
      */
    this.trigger('beforemodalclose');
    this.opened_ = false;

    如果(this.wasPlaying_ && this.options_.pauseOnOpen){
      播放器.play();
    }

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

    如果(this.hadControls_){
      播放器控制(真);
    }

    這個。隱藏();
    this.el().setAttribute('aria-hidden', 'true');

    /**
      * 在 `ModalDialog` 關閉後觸發。
      *
      * @event ModalDialog#modalclose
      * @type {EventTarget~Event}
      */
    this.trigger('modalclose');
    this.conditionalBlur_();

    如果(this.options_.temporary){
      這個。處置();
    }
  }

  /**
   * 檢查 `ModalDialog` 是否可以通過 UI 關閉。
   *
   * @param {boolean} [值]
   * 如果作為布爾值給出,它將設置 `closeable` 選項。
   *
   * @return {布爾值}
   * 返回可關閉選項的最終值。
   */
  可關閉(值){
    如果(類型值==='布爾值'){
      const closeable = this.closeable_ = !!value;
      讓 close = this.getChild('closeButton');

      // 如果這是可關閉的並且沒有關閉按鈕,請添加一個。
      如果(可關閉的&&!關閉){

        // 關閉按鈕應該是模態的子元素——而不是它的
        // 內容元素,所以暫時改變內容元素。
        const temp = this.contentEl_;

        this.contentEl_ = this.el_;
        close = this.addChild('closeButton', {controlText:'關閉模態對話框'});
        this.contentEl_ = temp;
        this.on(close, 'close', this.close_);
      }

      // 如果這是不可關閉的並且有一個關閉按鈕,請將其刪除。
      如果(!關閉&&關閉){
        this.off(close, 'close', this.close_);
        這個.removeChild(關閉);
        關閉.處置();
      }
    }
    返回this.closeable_;
  }

  /**
   * 使用模態的“內容”選項填充模態的內容元素。
   * 內容元素將在此更改發生之前清空。
   */
  充滿() {
    this.fillWith(this.content());
  }

  /**
   * 用任意內容填充模式的內容元素。
   * 內容元素將在此更改發生之前清空。
   *
   * @fires ModalDialog#beforemodalfill
   * @fires ModalDialog#modalfill
   *
   * @param {混合} [內容]
   * 適用於此的規則與適用於 `content` 選項的規則相同。
   */
  填充(內容){
    const contentEl = this.contentEl();
    const parentEl = contentEl.parentNode;
    const nextSiblingEl = contentEl.nextSibling;

    /**
      * 在 `ModalDialog` 被內容填充之前觸發。
      *
      * @event ModalDialog#beforemodalfill
      * @type {EventTarget~Event}
      */
    this.trigger('beforemodalfill');
    this.hasBeenFilled_ = true;

    // 在執行之前從 DOM 中分離內容元素
    // 避免多次修改動態 DOM 的操作。
    parentEl.removeChild(contentEl);
    這個.empty();
    Dom.insertContent(contentEl, content);
    /**
     * 在 `ModalDialog` 充滿內容後觸發。
     *
     * @event ModalDialog#modalfill
     * @type {EventTarget~Event}
     */
    this.trigger('modalfill');

    // 重新註入重新填充的內容元素。
    如果(下一個兄弟姐妹){
      parentEl.insertBefore(contentEl, nextSiblingEl);
    }其他{
      parentEl.appendChild(contentEl);
    }

    // 確保關閉按鈕在對話框 DOM 中位於最後
    const closeButton = this.getChild('closeButton');

    如果(關閉按鈕){
      parentEl.appendChild(closeButton.el_);
    }
  }

  /**
   * 清空內容元素。只要模態被填充,就會發生這種情況。
   *
   * @fires ModalDialog#beforemodalempty
   * @fires ModalDialog#modalempty
   */
  空的() {
    /**
    * 在 `ModalDialog` 清空之前觸發。
    *
    * @event ModalDialog#beforemodalempty
    * @type {EventTarget~Event}
    */
    this.trigger('beforemodalempty');
    Dom.emptyEl(this.contentEl());

    /**
    * 在 `ModalDialog` 被清空後觸發。
    *
    * @event ModalDialog#modalempty
    * @type {EventTarget~Event}
    */
    this.trigger('modalempty');
  }

  /**
   * 獲取或設置模態內容,模態內容在被規範化之前
   * 渲染到 DOM 中。
   *
   * 這不會更新 DOM 或填充模式,但會在
   *那個過程。
   *
   * @param {混合} [值]
   * 如果已定義,則設置要在
   * 下一次調用 `fill`。該值在被歸一化之前
   * 插入。要“清除”內部內容值,請傳遞“null”。
   *
   * @return {混合}
   * 模態對話框的當前內容
   */
  內容(價值){
    如果(類型值!=='未定義'){
      this.content_ = 值;
    }
    返回 this.content_;
  }

  /**
   * 如果焦點先前在播放器上,則有條件地聚焦模式對話框。
   *
   * @私人的
   */
  conditionalFocus_() {
    const activeEl = document.activeElement;
    const playerEl = this.player_.el_;

    this.previouslyActiveEl_ = null;

    如果 (playerEl.contains(activeEl) || playerEl === activeEl) {
      this.previouslyActiveEl_ = activeEl;

      這個.焦點();
    }
  }

  /**
   * 有條件地模糊元素並重新聚焦最後聚焦的元素
   *
   * @私人的
   */
  條件模糊_() {
    如果(this.previouslyActiveEl_){
      this.previouslyActiveEl_.focus();
      this.previouslyActiveEl_ = null;
    }
  }

  /**
   * 按鍵處理程序。聚焦模式時附加。
   *
   * @listens 按鍵
   */
  handleKeyDown(事件){

    // 不允許按鍵超出模態對話框。
    事件.stopPropagation();

    if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
      事件.preventDefault();
      這個。關閉();
      返回;
    }

    // 如果不是 tab 鍵則提前退出
    如果 (!keycode.isEventKey(event, 'Tab')) {
      返回;
    }

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

    for (let i = 0; i < focusableEls.length; i++) {
      如果 (activeEl === focusableEls[i]) {
        焦點指數=我;
        休息;
      }
    }

    如果(document.activeElement === this.el_){
      焦點指數 = 0;
    }

    如果 (event.shiftKey && focusIndex === 0) {
      focusableEls[focusableEls.length - 1].focus();
      事件.preventDefault();
    } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
      focusableEls[0].focus();
      事件.preventDefault();
    }
  }

  /**
   * 獲取所有可聚焦元素
   *
   * @私人的
   */
  focusableEls_() {
    const allChildren = this.el_.querySelectorAll('*');

    返回 Array.prototype.filter.call(allChildren, (child) => {
      返回((子instanceof window.HTMLAnchorElement ||
               child instanceof window.HTMLAreaElement) && child.hasAttribute('href')) ||
             ((窗口的子實例。HTMLInputElement ||
               window.HTMLSelectElement 的子實例 ||
               window.HTMLTextAreaElement 的子實例 ||
               child instanceof window.HTMLButtonElement) && !child.hasAttribute('disabled')) ||
             (child instanceof window.HTMLIFrameElement ||
               window.HTMLObjectElement 的子實例 ||
               window.HTMLEmbedElement 的子實例)||
             (child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1) ||
             (child.hasAttribute('contenteditable'));
    });
  }
}

/**
 * `ModalDialog` 默認選項的默認選項。
 *
 * @type {對象}
 * @私人的
 */
ModalDialog.prototype.options_ = {
  暫停打開:真,
  臨時:真
};

Component.registerComponent('ModalDialog', ModalDialog);
導出默認的 ModalDialog;