/**
 * @file 텍스트-트랙.js
 */
'./text-track-cue-list'에서 TextTrackCueList 가져오기;
import * as Fn from '../utils/fn.js';
import {TextTrackKind, TextTrackMode} from './track-enums';
'../utils/log.js'에서 로그 가져오기;
'글로벌/창'에서 창 가져오기;
'./track.js'에서 트랙 가져오기;
import { isCrossOrigin } from '../utils/url.js';
'@videojs/xhr'에서 XHR 가져오기;
'../utils/merge-options'에서 병합 가져오기;

/**
 * webvtt 파일 내용을 가져와 큐로 구문 분석합니다.
 *
 * @param {문자열} srcContent
 * webVTT 파일 내용
 *
 * @param {TextTrack} 트랙
 * 큐를 추가할 TextTrack. 큐는 srcContent에서 가져옵니다.
 *
 * @사적인
 */
const parseCues = function(srcContent, track) {
  const 파서 = 새 window.WebVTT.Parser(
    창문,
    창.vttjs,
    window.WebVTT.StringDecoder()
  );
  const 오류 = [];

  parser.oncue = 함수(큐) {
    track.addCue(큐);
  };

  parser.onparsingerror = 함수(오류) {
    오류.푸시(오류);
  };

  parser.onflush = 함수() {
    트랙.트리거({
      유형: '로드데이터',
      대상: 추적
    });
  };

  parser.parse(srcContent);
  if (오류. 길이 > 0) {
    if (창.콘솔 && window.console.groupCollapsed) {
      window.console.groupCollapsed(`${track.src}에 대한 텍스트 트랙 구문 분석 오류`);
    }
    오류.forEach((오류) => log.error(오류));
    if (창.콘솔 && window.console.groupEnd) {
      window.console.groupEnd();
    }
  }

  parser.flush();
};

/**
 * 지정된 URL에서 `TextTrack`을 로드합니다.
 *
 * @param {문자열} 소스
 * 트랙을 로드할 URL.
 *
 * @param {TextTrack} 트랙
 * 큐를 추가할 트랙. `url` 끝에 있는 콘텐츠에서 가져옵니다.
 *
 * @사적인
 */
const loadTrack = 함수(src, 트랙) {
  const 선택 = {
    우리: src
  };
  const crossOrigin = isCrossOrigin(src);

  if(크로스오리진) {
    opts.cors = crossOrigin;
  }

  const withCredentials = track.tech_.crossOrigin() === '사용 자격 증명';

  if (withCredentials) {
    opts.withCredentials = withCredentials;
  }

  XHR(opts, Fn.bind(this, function(err, response, responseBody) {
    경우 (오류) {
      return log.error(err, 응답);
    }

    track.loaded_ = 참;

    // vttjs가 로드되었는지 확인합니다. 그렇지 않으면 로드가 완료될 때까지 기다립니다.
    // 참고: 이것은 alt/video.novtt.js 빌드에만 사용됩니다.
    if (typeof window.WebVTT !== '함수') {
      경우 (track.tech_) {
        // eslint 오류를 정의하기 전에 사용을 방지하기 위해 loadHandler를 정의합니다.
        // 여기에 let으로
        track.tech_.any(['vttjsloaded', 'vttjserror'], (이벤트) => {
          if (event.type === 'vttjserror') {
            log.error(`vttjs 로드 실패, ${track.src} 처리 시도 중단`);
            반품;
          }
          return parseCues(responseBody, track);
        });
      }
    } else {
      parseCues(responseBody, track);
    }

  }));
};

/**
 * 단일 `TextTrack`의 표현입니다.
 *
 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
 * @extends 트랙
 */
클래스 TextTrack 확장 트랙 {

  /**
   * 이 클래스의 인스턴스를 만듭니다.
   *
   * @param {객체} 옵션={}
   * 옵션 이름 및 값의 개체
   *
   * @param {기술} options.tech
   * 이 TextTrack을 소유한 기술에 대한 참조입니다.
   *
   * @param {TextTrack~Kind} [options.kind='자막']
   * 유효한 텍스트 트랙 종류입니다.
   *
   * @param {TextTrack~Mode} [options.mode='비활성화됨']
   * 유효한 텍스트 트랙 모드.
   *
   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
   * 이 TextTrack의 고유 ID입니다.
   *
   * @param {문자열} [옵션.라벨='']
   * 이 트랙의 메뉴 레이블입니다.
   *
   * @param {문자열} [옵션.언어='']
   * 유효한 두 문자 언어 코드입니다.
   *
   * @param {문자열} [options.srclang='']
   * 유효한 두 문자 언어 코드입니다. 대안이지만 우선 순위가 낮음
   * `options.language` 버전
   *
   * @param {문자열} [옵션.src]
   * TextTrack 큐에 대한 URL.
   *
   * @param {부울} [옵션.기본값]
   * 이 트랙이 기본적으로 켜짐 또는 꺼짐인지 여부.
   */
  생성자(옵션 = {}) {
    if (!options.tech) {
      throw new Error('기술이 제공되지 않았습니다.');
    }

    const 설정 = 병합(옵션, {
      친절한: TextTrackKind[options.kind] || '자막',
      언어: options.language || options.srclang || ''
    });
    let 모드 = TextTrackMode[settings.mode] || '장애가 있는';
    const default_ = 설정.기본;

    if (settings.kind === 'metadata' || settings.kind === '챕터') {
      모드 = '숨김';
    }
    슈퍼(설정);

    this.tech_ = 설정.기술;

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

    this.preload_ = this.tech_.preloadTextTracks !== 거짓;

    const cues = new TextTrackCueList(this.cues_);
    const activeCues = new TextTrackCueList(this.activeCues_);
    변경하자 = 거짓;

    this.timeupdateHandler = Fn.bind(this, function(event = {}) {
      if (this.tech_.isDisposed()) {
        반품;
      }

      if (!this.tech_.isReady_) {
        if (event.type !== 'timeupdate') {
          this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
        }

        반품;
      }

      // 업데이트 자체의 부작용에 대한 this.activeCues 액세스
      // getter 함수로서의 특성 때문입니다. 제거하지 마십시오.
      // 업데이트 중지!
      // uglify에서 삭제되지 않도록 setter를 사용합니다(pure_getters 규칙).
      this.activeCues = this.activeCues;
      경우 (변경) {
        this.trigger('큐체인지');
        변경 = 거짓;
      }

      if (event.type !== 'timeupdate') {
        this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
      }

    });

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

    this.tech_.one('dispose', disposeHandler);
    if (모드 !== '비활성화') {
      this.startTracking();
    }

    Object.defineProperties(이것, {
      /**
       * @memberof TextTrack
       * @member {부울} 기본값
       * 이 트랙이 기본적으로 켜짐 또는 꺼짐으로 설정된 경우. 이후 변경 불가
       * 창조.
       * @사례
       *
       * @읽기전용
       */
      기본: {
        얻다() {
          default_ 반환;
        },
        세트() {}
      },

      /**
       * @memberof TextTrack
       * @member {문자열} 모드
       * 이 TextTrack의 모드를 유효한 {@link TextTrack~Mode}로 설정합니다. 할 것이다
       * 유효하지 않은 모드로 설정하면 설정되지 않습니다.
       * @사례
       *
       * @fires TextTrack#modechange
       */
      모드: {
        얻다() {
          반환 모드;
        },
        설정(신규 모드) {
          if (!TextTrackMode[newMode]) {
            반품;
          }
          if (모드 === newMode) {
            반품;
          }

          모드 = newMode;
          if (!this.preload_ && 모드 !== '비활성화' && this.cues.length === 0) {
            // 주문형 로드.
            loadTrack(this.src, this);
          }
          this.stopTracking();

          if (모드 !== '비활성화') {
            this.startTracking();
          }
          /**
           * 이 트랙에서 모드가 변경될 때 발생하는 이벤트입니다. 이를 통해
           * 이 트랙을 보유하고 있는 TextTrackList가 적절하게 작동합니다.
           *
           * > 메모: 이것은 사양의 일부가 아닙니다!
           *
           * @event TextTrack#modechange
           * @type {이벤트대상~이벤트}
           */
          this.trigger('모드 변경');

        }
      },

      /**
       * @memberof TextTrack
       * @member {TextTrackCueList} 큐
       * 이 TextTrack에 대한 텍스트 트랙 큐 목록입니다.
       * @사례
       */
      단서: {
        얻다() {
          if (!this.loaded_) {
            null을 반환합니다.
          }

          반환 신호;
        },
        세트() {}
      },

      /**
       * @memberof TextTrack
       * @member {TextTrackCueList} 활성 큐
       * 이 TextTrack에 대해 현재 활성화된 목록 텍스트 트랙 큐.
       * @사례
       */
      활성 큐: {
        얻다() {
          if (!this.loaded_) {
            null을 반환합니다.
          }

          // 할 것이 없다
          if (this.cues.length === 0) {
            활성 큐를 반환합니다.
          }

          const ct = this.tech_.currentTime();
          const 활성 = [];

          for (let i = 0, l = this.cues.length; i < 엘; i++) {
            const cue = this.cues[i];

            if (cue.startTime < = CT && cue.endTime > = CT) {
              active.push(큐);
            } 그렇지 않으면 (cue.startTime === cue.endTime &&
                       cue.startTime < = CT &&
                       cue.startTime + 0.5 > = CT) {
              active.push(큐);
            }
          }

          변경 = 거짓;

          if (active.length !== this.activeCues_.length) {
            변경됨 = 참;
          } else {
            에 대한 (하자 i = 0; i < 활성 길이; i++) {
              if (this.activeCues_.indexOf(active[i]) === -1) {
                변경됨 = 참;
              }
            }
          }

          this.activeCues_ = 활성;
          activeCues.setCues_(this.activeCues_);

          활성 큐를 반환합니다.
        },

        // /!\ 이 setter를 비워 둡니다(위의 timeupdate 핸들러 참조).
        세트() {}
      }
    });

    경우 (settings.src) {
      this.src = 설정.src;
      if (!this.preload_) {
        // 트랙은 주문형으로 로드됩니다.
        // 다른 목적을 위해 로드된 것처럼 작동합니다.
        this.loaded_ = 참;
      }
      if (this.preload_ || (settings.kind !== '자막' && settings.kind !== '캡션')) {
        loadTrack(this.src, this);
      }
    } else {
      this.loaded_ = 참;
    }
  }

  시작 추적() {
    // requestAnimationFram 폴백이 있는 requestVideoFrameCallback을 기반으로 하는 보다 정확한 신호
    this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
    // 또한 rVFC/rAF가 중지되는 경우 timeupdate를 듣습니다(창은 백그라운드, 오디오는 비디오 엘).
    this.tech_.on('timeupdate', this.timeupdateHandler);
  }

  stopTracking() {
    경우 (이.rvf_) {
      this.tech_.cancelVideoFrameCallback(this.rvf_);
      this.rvf_ = 정의되지 않음;
    }
    this.tech_.off('timeupdate', this.timeupdateHandler);
  }

  /**
   * 내부 큐 목록에 큐를 추가합니다.
   *
   * @param {TextTrack~Cue} 큐
   * 내부 목록에 추가할 단서
   */
  addCue(originalCue) {
    let cue = originalCue;

    만약 (window.vttjs && !(window.vttjs.VTTCue의 원본 큐 인스턴스)) {
      cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);

      for (originalCue의 const 소품) {
        if (!(큐의 소품)) {
          cue[prop] = originalCue[prop];
        }
      }

      // `id`가 복사되었는지 확인합니다.
      cue.id = originalCue.id;
      cue.originalCue_ = originalCue;
    }

    const 트랙 = this.tech_.textTracks();

    에 대한 (하자 i = 0; i < 트랙.길이; i++) {
      if (트랙[i] !== 이것) {
        track[i].removeCue(cue);
      }
    }

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

  /**
   * 내부 목록에서 큐 제거
   *
   * @param {TextTrack~Cue} removeCue
   * 내부 목록에서 제거할 신호
   */
  removeCue(제거 큐) {
    let i = this.cues_.length;

    동안 (i--) {
      const cue = this.cues_[i];

      if (cue === removeCue || (cue.originalCue_ && cue.originalCue_ === removeCue)) {
        this.cues_.splice(i, 1);
        this.cues.setCues_(this.cues_);
        부서지다;
      }
    }
  }
}

/**
 * cuechange - 트랙에 있는 하나 이상의 큐가 활성화되었거나 활성이 중지되었습니다.
 */
TextTrack.prototype.allowedEvents_ = {
  cuechange: '큐체인지'
};

기본 TextTrack 내보내기;