/**
 * @file text-track.js
 */
從“./text-track-cue-list”導入 TextTrackCueList;
從 '../utils/fn.js' 導入 * 作為 Fn;
從 './track-enums' 導入 {TextTrackKind, TextTrackMode};
從 '../utils/log.js' 導入日誌;
從“全局/窗口”導入窗口;
從 './track.js' 導入軌道;
從'../utils/url.js'導入{isCrossOrigin};
從“@videojs/xhr”導入 XHR;
從'../utils/merge-options'導入合併;

/**
 * 獲取 webvtt 文件內容並將其解析為提示
 *
 * @param {string} 源內容
 * webVTT 文件內容
 *
 * @param {TextTrack} 軌道
 * TextTrack 添加提示。提示來自 srcContent。
 *
 * @私人的
 */
const parseCues = function(srcContent, track) {
  const parser = new window.WebVTT.Parser(
    窗戶,
    窗口.vttjs,
    窗口.WebVTT.StringDecoder()
  );
  常量錯誤 = [];

  parser.oncue = 函數(提示){
    track.addCue(提示);
  };

  parser.onparsingerror = 函數(錯誤){
    錯誤推送(錯誤);
  };

  parser.onflush = function() {
    track.觸發器({
      類型:'加載數據',
      目標:跟踪
    });
  };

  parser.parse(srcContent);
  如果(錯誤。長度> 0){
    如果(window.console && window.console.groupCollapsed){
      window.console.groupCollapsed(`${track.src}` 的文本軌道解析錯誤);
    }
    errors.forEach((error) => log.error(error));
    如果(window.console && window.console.groupEnd){
      window.console.groupEnd();
    }
  }

  解析器.flush();
};

/**
 * 從指定的 url 加載一個 `TextTrack`。
 *
 * @param {string} 源
 * 加載軌道的 URL。
 *
 * @param {TextTrack} 軌道
 * 跟踪以添加提示。來自 url 末尾的內容。
 *
 * @私人的
 */
const loadTrack = function(src, track) {
  常量選擇= {
    uri: 源代碼
  };
  const crossOrigin = isCrossOrigin(src);

  如果(跨域){
    opts.cors = crossOrigin;
  }

  const withCredentials = track.tech_.crossOrigin() === 'use-credentials';

  如果(withCredentials){
    opts.withCredentials = withCredentials;
  }

  XHR(opts, Fn.bind(this, function(err, response, responseBody) {
    如果(錯誤){
      返回 log.error(err, response);
    }

    track.loaded_ = true;

    // 確保 vttjs 已經加載,否則,等待它加載完成
    // 注意:這僅用於 alt/video.novtt.js 構建
    如果(窗口的類型。== '功能') {
      如果(track.tech_){
        // 為了防止在定義 eslint 之前使用錯誤,我們定義了 loadHandler
        // 作為 let 在這裡
        track.tech_.any(['vttjsloaded', 'vttjserror'], (event) => {
          如果 (event.type === 'vttjserror') {
            log.error(`vttjs 加載失敗,停止嘗試處理 ${track.src}`);
            返回;
          }
          返回 parseCues(responseBody, track);
        });
      }
    }其他{
      parseCues(響應體,軌道);
    }

  }));
};

/**
 * 單個 TextTrack 的表示。
 *
 * @see [規範]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
 * @extends 軌道
 */
類 TextTrack 擴展軌道 {

  /**
   * 創建此類的一個實例。
   *
   * @param {Object} 選項={}
   * 選項名稱和值的對象
   *
   * @param {Tech} options.tech
   * 對擁有此 TextTrack 的技術的引用。
   *
   * @param {TextTrack~Kind} [options.kind='subtitles']
   * 一個有效的文本軌道類型。
   *
   * @param {TextTrack~Mode} [options.mode='disabled']
   * 一個有效的文本軌道模式。
   *
   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
   * 此 TextTrack 的唯一 ID。
   *
   * @param {string} [options.label='']
   * 此軌道的菜單標籤。
   *
   * @param {string} [options.language='']
   * 有效的雙字符語言代碼。
   *
   * @param {string} [options.srclang='']
   * 有效的雙字符語言代碼。一個替代方案,但被取消優先級
   * `options.language` 版本
   *
   * @param {string} [options.src]
   * TextTrack 提示的 url。
   *
   * @param {boolean} [options.default]
   * 如果此軌道應默認打開或關閉。
   */
  構造函數(選項={}){
    如果(!options.tech){
      throw new Error('未提供技術。');
    }

    常量設置=合併(選項,{
      種類:TextTrackKind[options.kind] || “字幕”,
      語言:options.language ||選項.srclang || ''
    });
    讓模式 = TextTrackMode[設置.模式] || '禁用';
    const default_ = settings.default;

    if (settings.kind === 'metadata' || settings.kind === 'chapters') {
      模式='隱藏';
    }
    超級(設置);

    this.tech_ = settings.tech;

    this.cues_ = [];
    this.activeCues_ = [];

    這個。預加載 _ = 這個。技術預加載文本軌道!== 假的;

    const cues = new TextTrackCueList(this.cues_);
    const activeCues = new TextTrackCueList(this.activeCues_);
    讓改變=假;

    this.timeupdateHandler = Fn.bind(this, function(event = {}) {
      如果(this.tech_.isDisposed()){
        返回;
      }

      如果(!this.tech_.isReady_){
        如果(事件類型!=='timeupdate'){
          this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
        }

        返回;
      }

      // 訪問 this.activeCues 以獲得更新自身的副作用
      // 由於其作為 getter 函數的性質。不要刪除或提示會
      // 停止更新!
      // 使用 setter 防止 uglify 刪除(pure_getters 規則)
      this.activeCues = this.activeCues;
      如果(改變){
        this.trigger('cuechange');
        改變=假;
      }

      如果(事件類型!=='timeupdate'){
        this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
      }

    });

    const disposeHandler = () => {
      this.stopTracking();
    };

    this.tech_.one('dispose', disposeHandler);
    如果(模式!=='禁用'){
      這個.startTracking();
    }

    Object.defineProperties(這個,{
      /**
       * @memberof TextTrack
       * @member {boolean} 默認值
       * 如果此軌道默認設置為打開或關閉。之後無法更改
       * 創作。
       * @實例
       *
       * @只讀
       */
      默認: {
        得到() {
          返回默認_;
        },
        放() {}
      },

      /**
       * @memberof TextTrack
       * @member {string} 模式
       * 將此 TextTrack 的模式設置為有效的 {@link TextTrack~Mode}。將要
       * 如果設置為無效模式則不設置。
       * @實例
       *
       * @fires TextTrack#modechange
       */
      模式: {
        得到() {
          返回方式;
        },
        設置(新模式){
          如果(!文本跟踪模式 [新模式]) {
            返回;
          }
          如果(模式===新模式){
            返回;
          }

          模式=新模式;
          如果(!這個。預加載 _ && 模式!== '禁用' && 這個 .cu.cu.length === 0) {
            // 按需加載。
            loadTrack(this.src, this);
          }
          this.stopTracking();

          如果(模式!=='禁用'){
            這個.startTracking();
          }
          /**
           * 在此軌道上模式更改時觸發的事件。這允許
           * 保存此軌道以相應操作的 TextTrackList。
           *
           * > 注意:這不是規範的一部分!
           *
           * @event TextTrack#modechange
           * @type {EventTarget~Event}
           */
          this.trigger('modechange');

        }
      },

      /**
       * @memberof TextTrack
       * @member {TextTrackCueList} 提示
       * 此 TextTrack 的文本軌道提示列表。
       * @實例
       */
      線索:{
        得到() {
          如果(!this.loaded_){
            返回空值;
          }

          返回提示;
        },
        放() {}
      },

      /**
       * @memberof TextTrack
       * @member {TextTrackCueList} activeCues
       * 當前為此 TextTrack 激活的列表文本軌道提示。
       * @實例
       */
      活動線索:{
        得到() {
          如果(!this.loaded_){
            返回空值;
          }

          // 沒事做
          如果(this.cues.length === 0){
            返回活動提示;
          }

          const ct = this.tech_.currentTime();
          const 活動 = [];

          對於(讓我 = 0,升 = 這個。線索。長度;  < 我; 我 ++){
            const cue = this.cues[i];

            如果(cue.startTime <= ct && cue.endTime >= ct){
              主動推送(提示);
            } else if (cue.startTime === cue.endTime &&
                       cue.startTime <= ct &&
                       cue.startTime + 0.5 >= ct) {
              主動推送(提示);
            }
          }

          改變=假;

          如果(活動。長度!== 這個. 活動 _. 長度) {
            改變=真;
          }其他{
            為 (讓我 = 0; 我 < 活躍. 長度; 我 ++) {
              如果 (this.activeCues_.indexOf(active[i]) === -1) {
                改變=真;
              }
            }
          }

          this.activeCues_ = active;
          activeCues.setCues_(this.activeCues_);

          返回活動提示;
        },

        ///!\ 保持這個設置器為空(請參閱上面的時間更新處理程序)
        放() {}
      }
    });

    如果(設置.src){
      this.src = settings.src;
      如果(!this.preload_){
        // 曲目將按需加載。
        // 就像我們為了其他目的而加載一樣。
        this.loaded_ = true;
      }
      如果(這個。預加載 _ ||(設置。== '字幕' && 設置。種!== '字幕')) {
        loadTrack(this.src, this);
      }
    }其他{
      this.loaded_ = true;
    }
  }

  開始跟踪(){
    // 基於帶有 requestAnimationFram 回退的 requestVideoFrameCallback 的更精確的提示
    this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
    // 如果 rVFC/rAF 停止(後台窗口,視頻 el 中的音頻),還要聽 timeupdate
    this.tech_.on('timeupdate', this.timeupdateHandler);
  }

  停止跟踪(){
    如果(this.rvf_){
      this.tech_.cancelVideoFrameCallback(this.rvf_);
      this.rvf_ = 未定義;
    }
    this.tech_.off('timeupdate', this.timeupdateHandler);
  }

  /**
   * 將提示添加到提示的內部列表。
   *
   * @param {TextTrack~Cue} 提示
   * 添加到我們內部列表的提示
   */
  添加提示(原始提示){
    讓 cue = originalCue;

    如果(窗口 .vttjs &&!(原始提示執行個體視窗. VTTJ.VTTCUE)) {
      cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);

      for (const prop in originalCue) {
        如果(!(提示中的道具)){
          cue[prop] = originalCue[prop];
        }
      }

      // 確保複製了 `id`
      cue.id = originalCue.id;
      cue.originalCue_ = originalCue;
    }

    const tracks = this.tech_.textTracks();

    對於(讓我 = 0; 我 < 跟踪。長度; 我 ++){
      如果(曲目[i]!==這個){
        tracks[i].removeCue(提示);
      }
    }

    this.cues_.push(cue);
    this.cues.setCues_(this.cues_);
  }

  /**
   * 從我們的內部列表中刪除一個提示
   *
   * @param {TextTrack~Cue} removeCue
   * 從我們的內部列表中刪除的提示
   */
  removeCue(removeCue){
    讓我 = this.cues_.length;

    當我 - ) {
      const cue = this.cues_[i];

      如果 (cue === removeCue || (cue.originalCue_ && cue.originalCue_ === removeCue)) {
        this.cues_.splice(i, 1);
        this.cues.setCues_(this.cues_);
        休息;
      }
    }
  }
}

/**
 * cuechange - 音軌中的一個或多個提示已激活或停止激活。
 */
TextTrack.prototype.allowedEvents_ = {
  cuechange: 'cuechange'
};

導出默認 TextTrack;