/**
 * @file text-track.js
 * /
importar TextTrackCueList desde './text-track-cue-list';
importar * como Fn desde '../utils/fn.js';
importar {TextTrackKind, TextTrackMode} desde './track-enums';
importar registro desde '../utils/log.js';
importar ventana desde 'global/window';
importar pista desde './track.js';
importar { isCrossOrigin } desde '../utils/url.js';
importar XHR desde '@videojs/xhr';
importar combinación desde '../utils/merge-options';

/**
 * Toma el contenido de un archivo webvtt y lo analiza en señales
 *
 * @param {cadena} srcContent
 * contenido del archivo webVTT
 *
 * @param {TextTrack} pista
 * TextTrack para agregar pistas. Las señales provienen de srcContent.
 *
 * @privado
 * /
const parseCues = function(srcContent, track) {
  const analizador = nueva ventana.WebVTT.Parser(
    ventana,
    ventana.vttjs,
    ventana.WebVTT.StringDecoder()
  );
  const errores = [];

  analizador.oncue = function(señal) {
    pista.addCue(cue);
  };

  analizador.onparsingerror = función (error) {
    errores.push(error);
  };

  analizador.onflush = function() {
    pista.disparador({
      tipo: 'datos cargados',
      objetivo: pista
    });
  };

  analizador.parse(srcContent);
  if (errores.longitud > 0) {
    si (ventana.consola && window.console.groupCollapsed) {
      window.console.groupCollapsed(`Text Track errores de análisis para ${track.src}`);
    }
    errores.forEach((error) => log.error(error));
    si (ventana.consola && ventana.consola.groupEnd) {
      ventana.consola.groupEnd();
    }
  }

  analizador.flush();
};

/**
 * Cargue un `TextTrack` desde una URL específica.
 *
 * @param {cadena} src
 * Url desde donde cargar la pista.
 *
 * @param {TextTrack} pista
 * Pista para agregar señales. Viene del contenido al final de `url`.
 *
 * @privado
 * /
const loadTrack = function(origen, pista) {
  const opciones = {
    uri: origen
  };
  const crossOrigin = isCrossOrigin(src);

  si (origen cruzado) {
    opts.cors = crossOrigin;
  }

  const withCredentials = track.tech_.crossOrigin() === 'usar-credenciales';

  si (con Credenciales) {
    opts.withCredentials = withCredentials;
  }

  XHR(opts, Fn.bind(this, function(err, respuesta, cuerpo de respuesta) {
    si (err) {
      return log.error(err, respuesta);
    }

    pista.cargado_ = verdadero;

    // Asegúrese de que vttjs se haya cargado, de lo contrario, espere hasta que termine de cargarse
    // NOTA: esto solo se usa para la compilación alt/video.novtt.js
    if (tipo de ventana.WebVTT !== 'función') {
      si (pista.tech_) {
        // para evitar el uso antes de definir el error eslint, definimos loadHandler
        // como dejar aquí
        track.tech_.any(['vttjsloaded', 'vttjserror'], (evento) => {
          if (evento.tipo === 'vttjserror') {
            log.error(`vttjs no se pudo cargar, dejó de intentar procesar ${track.src}`);
            devolver;
          }
          return parseCues(cuerpo de respuesta, pista);
        });
      }
    } else {
      parseCues(cuerpo de respuesta, pista);
    }

  }));
};

/**
 * Una representación de un solo `TextTrack`.
 *
 * @ver [Especificación]{@enlace https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
 * @extiende Pista
 * /
clase TextTrack extiende Pista {

  /**
   * Crear una instancia de esta clase.
   *
   * @param {Objeto} opciones={}
   * Objeto de nombres y valores de opciones
   *
   * @param {Tecnología} opciones.tecnología
   * Una referencia a la tecnología propietaria de este TextTrack.
   *
   * @param {TextTrack~Tipo} [opciones.tipo='subtítulos']
   * Un tipo de pista de texto válido.
   *
   * @param {TextTrack~Mode} [opciones.modo='deshabilitado']
   * Un modo de seguimiento de texto válido.
   *
   * @param {cadena} [opciones.id='vjs_track_' + Guid.nuevoGUID()]
   * Una identificación única para este TextTrack.
   *
   * @param {cadena} [opciones.etiqueta='']
   * La etiqueta del menú para esta pista.
   *
   * @param {cadena} [opciones.idioma='']
   * Un código de idioma válido de dos caracteres.
   *
   * @param {cadena} [opciones.srclang='']
   * Un código de idioma válido de dos caracteres. Una alternativa, pero despriorizada
   * versión de `options.language`
   *
   * @param {cadena} [opciones.src]
   * Una url a las señales de TextTrack.
   *
   * @param {booleano} [opciones.predeterminado]
   * Si esta pista debería estar activada o desactivada de forma predeterminada.
   * /
  constructor(opciones = {}) {
    if (!opciones.tecnología) {
      throw new Error('No se proporcionó una tecnología.');
    }

    configuración constante = fusionar (opciones, {
      amable: TextTrackKind[opciones.tipo] || 'subtitulos',
      idioma: opciones.idioma || opciones.srclang || ''
    });
    let mode = TextTrackMode[settings.mode] || 'desactivado';
    const predeterminado_ = configuración.predeterminado;

    if (configuraciones.tipo === 'metadatos' || configuraciones.tipo === 'capítulos') {
      modo = 'oculto';
    }
    super(configuraciones);

    this.tech_ = configuración.tech;

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

    this.preload_ = this.tech_.preloadTextTracks !== falso;

    const cues = nueva TextTrackCueList(this.cues_);
    const activeCues = new TextTrackCueList(this.activeCues_);
    dejar cambiado = falso;

    this.timeupdateHandler = Fn.bind(this, function(event = {}) {
      si (this.tech_.isDisposed()) {
        devolver;
      }

      si (!esta.tecnología_.estáListo_) {
        if (evento.tipo !== 'actualización de tiempo') {
          this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
        }

        devolver;
      }

      // Accediendo a this.activeCues para los efectos secundarios de actualizarse a sí mismo
      // debido a su naturaleza como función getter. No elimine o las señales se
      // deja de actualizar!
      // Usar el setter para evitar la eliminación de uglify (regla pure_getters)
      this.activeCues = this.activeCues;
      si (cambiado) {
        this.trigger('cambio de señal');
        cambiado = falso;
      }

      if (evento.tipo !== 'actualización de tiempo') {
        this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
      }

    });

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

    this.tech_.one('dispose', disposeHandler);
    if (modo !== 'deshabilitado') {
      this.startTracking();
    }

    Objeto.defineProperties(esto, {
      /**
       * @miembro de TextTrack
       * @member {booleano} por defecto
       * Si esta pista se configuró como activada o desactivada de forma predeterminada. No se puede cambiar después
       * creación.
       * @instancia
       *
       * @solo lectura
       * /
      por defecto: {
        conseguir() {
          volver por defecto_;
        },
        colocar() {}
      },

      /**
       * @miembro de TextTrack
       * modo @member {cadena}
       * Configure el modo de este TextTrack en un {@link TextTrack~Mode} válido. Voluntad
       * no se establecerá si se establece en un modo no válido.
       * @instancia
       *
       * @fires TextTrack#cambio de modo
       * /
      modo: {
        conseguir() {
          modo de retorno;
        },
        establecer (nuevo modo) {
          if (!TextTrackMode[nuevoModo]) {
            devolver;
          }
          if (modo === nuevoModo) {
            devolver;
          }

          modo = nuevoModo;
          si (!esta.precarga_ && modo !== 'deshabilitado' && this.cues.length === 0) {
            // Carga bajo demanda.
            loadTrack(este.src, este);
          }
          this.stopTracking();

          if (modo !== 'deshabilitado') {
            this.startTracking();
          }
          /**
           * Un evento que se activa cuando cambia el modo en esta pista. Esto permite
           * la TextTrackList que contiene esta pista para actuar en consecuencia.
           *
           * > Nota: ¡Esto no forma parte de la especificación!
           *
           * @event TextTrack#cambio de modo
           * @type {Objetivo del evento~Evento}
           * /
          this.trigger('cambio de modo');

        }
      },

      /**
       * @miembro de TextTrack
       * @member {TextTrackCueList} señales
       * La lista de pistas de la pista de texto para este TextTrack.
       * @instancia
       * /
      señales: {
        conseguir() {
          if (!this.loaded_) {
            devolver nulo;
          }

          señales de retorno;
        },
        colocar() {}
      },

      /**
       * @miembro de TextTrack
       * @member {TextTrackCueList} activeCues
       * La lista de pistas de texto que están actualmente activas para este TextTrack.
       * @instancia
       * /
      señales activas: {
        conseguir() {
          if (!this.loaded_) {
            devolver nulo;
          }

          // Nada que hacer
          if (this.cues.length === 0) {
            devuelve las señales activas;
          }

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

          for (sea i = 0, l = this.cues.length; i < yo; i++) {
            const señal = this.cues[i];

            si (cue.startTime < = ct && cue.endTime > = ct) {
              activo.push(cue);
            } else if (cue.startTime === cue.endTime &&
                       cue.startTime < = ct &&
                       cue.startTime + 0.5 > = ct) {
              activo.push(cue);
            }
          }

          cambiado = falso;

          if (activo.longitud !== this.activeCues_.longitud) {
            cambiado = verdadero;
          } else {
            para (sea i = 0; i < longitud.activa; i++) {
              if (this.activeCues_.indexOf(active[i]) === -1) {
                cambiado = verdadero;
              }
            }
          }

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

          devuelve las señales activas;
        },

        // /!\ Mantenga este setter vacío (consulte el controlador de actualización de tiempo anterior)
        colocar() {}
      }
    });

    si (configuraciones.src) {
      this.src = configuración.src;
      si (!esta.precarga_) {
        // Las pistas se cargarán a pedido.
        // Actuar como si estuviéramos cargados para otros propósitos.
        esto.cargado_ = verdadero;
      }
      if (this.preload_ || (settings.kind !== 'subtítulos' && settings.kind !== 'subtítulos')) {
        loadTrack(este.src, este);
      }
    } else {
      esto.cargado_ = verdadero;
    }
  }

  iniciarseguimiento() {
    // Señales más precisas basadas en requestVideoFrameCallback con un respaldo de requestAnimationFram
    this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
    // También escuche la actualización de tiempo en caso de que rVFC/rAF se detenga (ventana en segundo plano, audio en video el)
    this.tech_.on('timeupdate', this.timeupdateHandler);
  }

  detener el seguimiento () {
    si (esto.rvf_) {
      this.tech_.cancelVideoFrameCallback(this.rvf_);
      esto.rvf_ = indefinido;
    }
    this.tech_.off('timeupdate', this.timeupdateHandler);
  }

  /**
   * Agregar una señal a la lista interna de señales.
   *
   * @param {TextTrack~Cue} entrada
   * La señal para agregar a nuestra lista interna
   * /
  addCue(originalCue) {
    let cue = originalCue;

    si (ventana.vttjs && !(instancia Cue original de window.vttjs.VTTCue)) {
      cue = nueva ventana.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);

      for (const prop en originalCue) {
        if (!(prop en señal)) {
          señal[prop] = originalCue[prop];
        }
      }

      // asegúrese de que `id` se copia
      cue.id = originalCue.id;
      cue.originalCue_ = originalCue;
    }

    const pistas = this.tech_.textTracks();

    para (sea i = 0; i < pistas.longitud; i++) {
      if (pistas[i] !== esto) {
        pistas[i].removeCue(cue);
      }
    }

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

  /**
   * Eliminar una señal de nuestra lista interna
   *
   * @param {TextTrack~Cue} removeCue
   * La señal para eliminar de nuestra lista interna
   * /
  eliminarCue(eliminarCue) {
    let i = this.cues_.length;

    mientras yo--) {
      const señal = this.cues_[i];

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

/**
 * cuechange - Una o más señales en la pista se activaron o dejaron de estar activas.
 * /
TextTrack.prototype.allowedEvents_ = {
  cambio de señal: 'cambio de señal'
};

exportar TextTrack predeterminado;