/**
 * @archivo buscar-bar.js
 * /
importar control deslizante desde '../../slider/slider.js';
importar componente desde '../../component.js';
importar {IS_IOS, IS_ANDROID} desde '../../utils/browser.js';
importar * como Dom desde '../../utils/dom.js';
importar * como Fn desde '../../utils/fn.js';
importar formatTime desde '../../utils/format-time.js';
importar {silencePromise} desde '../../utils/promise';
importar código clave desde 'código clave';
importar documento desde 'global/document';

importar './load-progress-bar.js';
importar './play-progress-bar.js';
importar './mouse-time-display.js';

// El número de segundos que las funciones `step*` mueven la línea de tiempo.
constante PASO_SEGUNDOS = 5;

// El multiplicador de STEP_SECONDS que PgUp/PgDown mueven la línea de tiempo.
const PAGE_KEY_MULTIPLIER = 12;

/**
 * Barra de búsqueda y contenedor para las barras de progreso. Utiliza {@link PlayProgressBar}
 * como su `bar`.
 *
 * @extiende el control deslizante
 * /
clase SeekBar extiende el control deslizante {

  /**
   * Crea una instancia de esta clase.
   *
   * @param {Jugador} jugador
   * El `Jugador` al que se debe adjuntar esta clase.
   *
   * @param {Objeto} [opciones]
   * El almacén de clave/valor de las opciones del jugador.
   * /
  constructor(jugador, opciones) {
    super(jugador, opciones);
    this.setEventHandlers_();
  }

  /**
   * Establece los controladores de eventos
   *
   * @privado
   * /
  setEventHandlers_() {
    this.update_ = Fn.bind(this, this.update);
    this.update = Fn.throttle(this.update_, Fn.UPDATE_REFRESH_INTERVAL);

    this.on(this.player_, ['finalizado', 'durationchange', 'timeupdate'], this.update);
    si (this.player_.liveTracker) {
      this.on(this.player_.liveTracker, 'liveedgechange', this.update);
    }

    // al jugar, asegurémonos de actualizar sin problemas la barra de progreso del juego
    // a través de un intervalo
    this.updateInterval = null;

    this.enableIntervalHandler_ = (e) => this.enableInterval_(e);
    this.disableIntervalHandler_ = (e) => this.disableInterval_(e);

    this.on(this.player_, ['jugando'], this.enableIntervalHandler_);

    this.on(this.player_, ['finalizado', 'pausa', 'esperando'], this.disableIntervalHandler_);

    // no necesitamos actualizar el progreso de la reproducción si el documento está oculto,
    // también, esto hace que la CPU se dispare y finalmente bloquee la página en IE11.
    if ('oculto' en el documento && 'estado de visibilidad' en el documento) {
      this.on(documento, 'cambio de visibilidad', this.toggleVisibility_);
    }
  }

  alternarVisibilidad_(e) {
    if (document.visibilityState === 'oculto') {
      this.cancelNamedAnimationFrame('SeekBar#update');
      this.cancelNamedAnimationFrame('Slider#update');
      this.disableInterval_(e);
    } else {
      si (!este.jugador_.terminado() && !este.jugador_.pausado()) {
        this.enableInterval_();
      }

      // acabamos de volver a la página y es posible que alguien esté mirando, así que actualice lo antes posible
      esta.actualización();
    }
  }

  habilitarIntervalo_() {
    if (este.intervalo de actualización) {
      devolver;

    }
    this.updateInterval = this.setInterval(this.update, Fn.UPDATE_REFRESH_INTERVAL);
  }

  deshabilitarIntervalo_(e) {
    si (this.player_.liveTracker && this.player_.liveTracker.isLive() && mi && e.type !== 'terminado') {
      devolver;
    }

    if (!this.updateInterval) {
      devolver;
    }

    este.clearInterval(este.updateInterval);
    this.updateInterval = null;
  }

  /**
   * Crear el elemento DOM del 'Componente'
   *
   * @return {Elemento}
   * El elemento que se creó.
   * /
  crearEl() {
    return super.createEl('div', {
      className: 'vjs-progreso-titular'
    }, {
      'aria-label': this.localize('Barra de progreso')
    });
  }

  /**
   * Esta función actualiza la barra de progreso de reproducción y la accesibilidad.
   * atributos a todo lo que se pasa.
   *
   * @param {Objetivo de evento~Evento} [evento]
   * El evento 'timeupdate' o 'finalizado' que hizo que esto se ejecutara.
   *
   * @escucha Player#timeupdate
   *
   * @return {número}
   * El porcentaje actual en un número de 0-1
   * /
  actualizar (evento) {
    // ignora las actualizaciones mientras la pestaña está oculta
    if (document.visibilityState === 'oculto') {
      devolver;
    }

    porcentaje constante = super.update();

    this.requestNamedAnimationFrame('SeekBar#update', () => {
      const horaActual = this.player_.ended() ?
        this.player_.duration() : this.getCurrentTime_();
      const liveTracker = this.player_.liveTracker;
      let duración = this.player_.duration();

      si (rastreador en vivo && liveTracker.isLive()) {
        duración = this.player_.liveTracker.liveCurrentTime();
      }

      if (this.percent_ !== percent) {
        // valor legible por máquina de la barra de progreso (porcentaje completado)
        this.el_.setAttribute('aria-valuenow', (porcentaje * 100).toFixed(2));
        this.percent_ = porcentaje;
      }

      if (this.currentTime_ !== currentTime || this.duration_ !== duración) {
        // valor legible por humanos de la barra de progreso (tiempo completo)
        this.el_.setAttribute(
          'aria-valuetext',
          esto.localizar(
            'tiempo de la barra de progreso: horaActual={1} duración={2}',
            [formatoTiempo(horaActual, duración),
              formatTime(duración, duración)],
            '{1 de 2}'
          )
        );

        this.currentTime_ = currentTime;
        this.duration_ = duración;
      }

      // actualizar la información sobre herramientas de la barra de progreso con la hora actual
      si (esta.barra) {
        this.bar.update(Dom.getBoundingClientRect(this.el()), this.getProgress());
      }
    });

    porcentaje de retorno;
  }

  /**
   * Evite que liveThreshold haga que las búsquedas parezcan
   * no están sucediendo desde la perspectiva del usuario.
   *
   * @param {número} ct
   * tiempo actual para buscar
   * /
  usuarioSeek_(ct) {
    si (this.player_.liveTracker && this.player_.liveTracker.isLive()) {
      this.player_.liveTracker.nextSeekedFromUser();
    }

    this.player_.currentTime(ct);
  }

  /**
   * Obtenga el valor de la hora actual, pero permite un lavado suave,
   * cuando el jugador no puede seguir el ritmo.
   *
   * @return {número}
   * El valor de tiempo actual para mostrar
   *
   * @privado
   * /
  getHoraActual_() {
    volver (this.player_.scrubbing()) ?
      this.player_.getCache().currentTime :
      this.player_.currentTime();
  }

  /**
   * Obtenga el porcentaje de contenido multimedia reproducido hasta el momento.
   *
   * @return {número}
   * El porcentaje de medios reproducidos hasta ahora (0 a 1).
   * /
  obtenerPorcentaje() {
    const horaActual = this.getCurrentTime_();
    sea por ciento;
    const liveTracker = this.player_.liveTracker;

    si (rastreador en vivo && liveTracker.isLive()) {
      porcentaje = (horaActual - liveTracker.seekableStart()) / liveTracker.liveWindow();

      // evita que el porcentaje cambie en el borde vivo
      si (liveTracker.atLiveEdge()) {
        porcentaje = 1;
      }
    } else {
      porcentaje = horaActual / this.player_.duration();
    }

    porcentaje de retorno;
  }

  /**
   * Maneje el mouse hacia abajo en la barra de búsqueda
   *
   * @param {EventTarget~Evento} evento
   * El evento `mousedown` que causó que esto se ejecutara.
   *
   * @escucha mousedown
   * /
  manejarRatónAbajo(evento) {
    if (!Dom.isSingleLeftClick(evento)) {
      devolver;
    }

    // Detener la propagación de eventos para evitar el doble disparo en el progreso de control.js
    event.stopPropagation();

    this.videoWasPlaying = !this.player_.paused();
    este.jugador_.pausa();

    super.handleMouseDown(evento);
  }

  /**
   * Manejar el movimiento del mouse en la barra de búsqueda
   *
   * @param {EventTarget~Evento} evento
   * El evento `mousemove` que causó que esto se ejecutara.
   * @param {boolean} mouseDown este es un indicador que debe establecerse en verdadero si `handleMouseMove` se llama directamente. Nos permite omitir cosas que no deberían suceder si vienen con el mouse hacia abajo, pero deberían suceder en el controlador de movimiento del mouse normal. Predeterminado a falso
   *
   * @escucha movimiento del ratón
   * /
  handleMouseMove(evento, mouseDown = false) {
    if (!Dom.isSingleLeftClick(evento)) {
      devolver;
    }

    si (!mouseDown && !este.jugador_.fregado()) {
      this.player_.scrubbing(verdadero);
    }

    dejar nuevoTiempo;
    const distancia = this.calculateDistance(evento);
    const liveTracker = this.player_.liveTracker;

    if (!liveTracker || !liveTracker.isLive()) {
      newTime = distancia * this.player_.duration();

      // No dejes que el video termine mientras se limpia.
      if (newTime === this.player_.duration()) {
        nuevoTiempo = nuevoTiempo - 0.1;
      }
    } else {

      si (distancia > = 0,99) {
        LiveTracker.seekToLiveEdge();
        devolver;
      }
      const seekableStart = liveTracker.seekableStart();
      const seekableEnd = liveTracker.liveCurrentTime();

      newTime = seekableStart + (distancia * liveTracker.liveWindow());

      // No dejes que el video termine mientras se limpia.
      si (nuevoTiempo > = fin buscable) {
        newTime = fin buscable;
      }

      // Compensar las diferencias de precisión para que el tiempo actual no sea menor
      // que inicio buscable
      si (nuevoTiempo < = inicio buscable) {
        nuevoTiempo = inicioBuscable + 0.1;
      }

      // En Android seekableEnd puede ser Infinito a veces,
      // esto hará que newTime sea Infinity, que es
      // no es una hora actual válida.
      if (nuevoTiempo === Infinito) {
        devolver;
      }
    }

    // Establecer una nueva hora (dígale al jugador que busque una nueva hora)
    this.userSeek_(newTime);
  }

  permitir() {
    super.enable();
    const mouseTimeDisplay = this.getChild('mouseTimeDisplay');

    if (!mouseTimeDisplay) {
      devolver;
    }

    mouseTimeDisplay.show();
  }

  desactivar() {
    super.disable();
    const mouseTimeDisplay = this.getChild('mouseTimeDisplay');

    if (!mouseTimeDisplay) {
      devolver;
    }

    mouseTimeDisplay.hide();
  }

  /**
   * Manejar el mouse hacia arriba en la barra de búsqueda
   *
   * @param {EventTarget~Evento} evento
   * El evento `mouseup` que causó que esto se ejecutara.
   *
   * @escucha mouseup
   * /
  manejarMouseUp(evento) {
    super.handleMouseUp(evento);

    // Detener la propagación de eventos para evitar el doble disparo en el progreso de control.js
    si (evento) {
      event.stopPropagation();
    }
    this.player_.scrubbing(falso);

    /**
     * Active la actualización de la hora porque hemos terminado de buscar y la hora ha cambiado.
     * Esto es particularmente útil si el reproductor está en pausa para cronometrar las pantallas de tiempo.
     *
     * @event Tech#timeupdate
     * @type {Objetivo del evento~Evento}
     * /
    this.player_.trigger({ type: 'timeupdate', target: this, manualmente Triggered: true });
    if (este.videoEstabaReproduciendo) {
      PromesaSilencio(este.jugador_.jugar());
    } else {
      // Terminamos de buscar y el tiempo ha cambiado.
      // Si el reproductor está en pausa, asegúrese de mostrar la hora correcta en la barra de búsqueda.
      esto.actualizar_();
    }
  }

  /**
   * Avance más rápido para usuarios de solo teclado
   * /
  un paso adelante() {
    this.userSeek_(this.player_.currentTime() + STEP_SECONDS);
  }

  /**
   * Muévase más rápido y rebobine para usuarios de solo teclado
   * /
  Paso atrás() {
    this.userSeek_(this.player_.currentTime() - STEP_SECONDS);
  }

  /**
   * Cambia el estado de reproducción del reproductor
   * Esto se llama cuando se usa enter o espacio en la barra de búsqueda
   *
   * @param {EventTarget~Evento} evento
   * El evento `keydown` que provocó que se llamara a esta función
   *
   * /
  manejarAcción(evento) {
    if (este.jugador_.pausado()) {
      este.jugador_.jugar();
    } else {
      este.jugador_.pausa();
    }
  }

  /**
   * Se llama cuando esta SeekBar tiene el foco y se presiona una tecla.
   * Soporta las siguientes claves:
   *
   * La tecla Espacio o Intro activa un evento de clic
   * La tecla de inicio se mueve al inicio de la línea de tiempo
   * La tecla Finalizar se mueve al final de la línea de tiempo
   * Las teclas de dígitos &quot;0&quot; a &quot;9&quot; se mueven a 0%, 10% ... 80%, 90% de la línea de tiempo
   * La tecla PageDown retrocede un paso más grande que ArrowDown
   * La tecla PageUp avanza un gran paso
   *
   * @param {EventTarget~Evento} evento
   * El evento `keydown` que hizo que se llamara a esta función.
   *
   * @escucha tecla abajo
   * /
  handleKeyDown(evento) {
    const liveTracker = this.player_.liveTracker;

    if (keycode.isEventKey(evento, 'Espacio') || keycode.isEventKey(evento, 'Entrar')) {
      event.preventDefault();
      event.stopPropagation();
      this.handleAction(evento);
    } else if (keycode.isEventKey(evento, 'Inicio')) {
      event.preventDefault();
      event.stopPropagation();
      este.userSeek_(0);
    } else if (keycode.isEventKey(evento, 'Fin')) {
      event.preventDefault();
      event.stopPropagation();
      si (rastreador en vivo && liveTracker.isLive()) {
        this.userSeek_(liveTracker.liveCurrentTime());
      } else {
        this.userSeek_(this.player_.duration());
      }
    } else if (/^[0-9]$/.test(keycode(evento))) {
      event.preventDefault();
      event.stopPropagation();
      const gotoFraction = (keycode.codes[keycode(evento)] - keycode.codes['0']) * 10.0 / 100.0;

      si (rastreador en vivo && liveTracker.isLive()) {
        this.userSeek_(liveTracker.seekableStart() + (liveTracker.liveWindow() * gotoFraction));
      } else {
        this.userSeek_(this.player_.duration() * gotoFraction);
      }
    } else if (keycode.isEventKey(event, 'PgDn')) {
      event.preventDefault();
      event.stopPropagation();
      this.userSeek_(this.player_.currentTime() - (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
    } else if (keycode.isEventKey(event, 'PgUp')) {
      event.preventDefault();
      event.stopPropagation();
      this.userSeek_(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
    } else {
      // Pasar el manejo de keydown para claves no compatibles
      super.handleKeyDown(evento);
    }
  }

  disponer () {
    this.disableInterval_();

    this.off(this.player_, ['finalizado', 'durationchange', 'timeupdate'], this.update);
    si (this.player_.liveTracker) {
      this.off(this.player_.liveTracker, 'liveedgechange', this.update);
    }

    this.off(this.player_, ['jugando'], this.enableIntervalHandler_);
    this.off(this.player_, ['finalizado', 'pausa', 'esperando'], this.disableIntervalHandler_);

    // no necesitamos actualizar el progreso de la reproducción si el documento está oculto,
    // también, esto hace que la CPU se dispare y finalmente bloquee la página en IE11.
    if ('oculto' en el documento && 'estado de visibilidad' en el documento) {
      this.off(document, 'visibilitychange', this.toggleVisibility_);
    }

    super.dispose();
  }
}

/**
 * Opciones predeterminadas para `SeekBar`
 *
 * @type {Objeto}
 * @privado
 * /
SeekBar.prototipo.opciones_ = {
  niños: [
    'cargar barra de progreso',
    'playProgressBar'
  ],
  barName: 'playProgressBar'
};

// La información sobre herramientas de MouseTimeDisplay no debe agregarse a un reproductor en dispositivos móviles
if (!IS_IOS && !IS_ANDROID) {
  SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
}

Componente.registerComponent('BuscarBar', BuscarBar);
exportar SeekBar por defecto;