/**
 * @file menú-botón.js
 * /
importar botón desde '../button.js';
importar componente desde '../component.js';
importar menú desde './menu.js';
importar * como Dom desde '../utils/dom.js';
importar * como Eventos desde '../utils/events.js';
importar {toTitleCase} desde '../utils/string-cases.js';
importar { IS_IOS } desde '../utils/browser.js';
importar documento desde 'global/document';
importar código clave desde 'código clave';

/**
 * Una clase `MenuButton` para cualquier ventana emergente {@link Menu}.
 *
 * Componente @extiende
 * /
class MenuButton extiende Componente {

  /**
   * 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.menuButton_ = new Button(jugador, opciones);

    este.menuButton_.controlText(este.controlText_);
    this.menuButton_.el_.setAttribute('aria-haspopup', 'true');

    // Agregar valores de buildCSSClass al botón, no al envoltorio
    const buttonClass = Button.prototype.buildCSSClass();

    this.menuButton_.el_.className = this.buildCSSClass() + ' ' + buttonClass;
    this.menuButton_.removeClass('vjs-control');

    this.addChild(this.menuButton_);

    esta.actualización();

    esto.habilitado_ = verdadero;

    const handleClick = (e) => this.handleClick(e);

    this.handleMenuKeyUp_ = (e) => this.handleMenuKeyUp(e);

    this.on(this.menuButton_, 'tocar', handleClick);
    this.on(this.menuButton_, 'clic', handleClick);
    this.on(this.menuButton_, 'keydown', (e) => this.handleKeyDown(e));
    this.on(this.menuButton_, 'mouseenter', () => {
      this.addClass('vjs-hover');
      este.menú.mostrar();
      Events.on(document, 'keyup', this.handleMenuKeyUp_);
    });
    this.on('mouseleave', (e) => this.handleMouseLeave(e));
    this.on('teclado abajo', (e) => this.handleSubmenuKeyDown(e));
  }

  /**
   * Actualice el menú según el estado actual de sus elementos.
   * /
  actualizar() {
    const menu = this.createMenu();

    si (este.menú) {
      este.menu.dispose();
      this.removeChild(this.menu);
    }

    este.menu = menú;
    this.addChild(menú);

    /**
     * Seguimiento del estado del botón de menú
     *
     * @tipo {Booleano}
     * @privado
     * /
    this.buttonPressed_ = false;
    this.menuButton_.el_.setAttribute('aria-expanded', 'false');

    si (este.elementos && este.artículos.longitud < = this.hideThreshold_) {
      esto. ocultar ();
      this.menu.contentEl_.removeAttribute('rol');

    } else {
      este espectáculo();
      this.menu.contentEl_.setAttribute('rol', 'menú');
    }
  }

  /**
   * Cree el menú y agréguele todos los elementos.
   *
   * @return {Menú}
   * El menú construido
   * /
  crearMenu() {
    const menu = new Menu(this.player_, { menuButton: this });

    /**
     * Ocultar el menú si el número de elementos es menor o igual a este umbral. esto por defecto
     * a 0 y cada vez que agreguemos elementos que se pueden ocultar en el menú, lo incrementaremos. Listamos
     * aquí porque cada vez que ejecutamos `createMenu` necesitamos restablecer el valor.
     *
     * @protegido
     * @teclea un número}
     * /
    este.hideThreshold_ = 0;

    // Agregar un elemento de la lista de títulos en la parte superior
    if (esta.opciones_.título) {
      const tituloEl = Dom.createEl('li', {
        className: 'vjs-menú-título',
        textContent: toTitleCase(this.options_.title),
        índice de tabulación: -1
      });

      const titleComponent = new Component(this.player_, {el: titleEl});

      menu.addItem(componentetítulo);
    }

    this.items = this.createItems();

    si (este.elementos) {
      // Agregar elementos de menú al menú
      para (sea i = 0; i < this.items.length; i++) {
        menu.addItem(this.items[i]);
      }
    }

    menú de retorno;
  }

  /**
   * Crear la lista de elementos del menú. Específico para cada subclase.
   *
   * @abstracto
   * /
  crear elementos () {}

  /**
   * Crear el elemento DOM `MenuButtons`.
   *
   * @return {Elemento}
   * El elemento que se crea.
   * /
  crearEl() {
    return super.createEl('div', {
      nombre de clase: this.buildWrapperCSSClass()
    }, {
    });
  }

  /**
   * Permitir que los subcomponentes apilen nombres de clase CSS para el elemento contenedor
   *
   * @return {cadena}
   * El envoltorio construido DOM `className`
   * /
  buildWrapperCSSClass() {
    let menuButtonClass = 'vjs-menu-button';

    // Si se pasa la opción en línea, queremos usar estilos diferentes por completo.
    if (this.options_.inline === verdadero) {
      menuButtonClass += '-en línea';
    } else {
      menuButtonClass += '-popup';
    }

    // HACER: Arregle el CSS para que esto no sea necesario
    const buttonClass = Button.prototype.buildCSSClass();

    return `vjs-menu-button ${menuButtonClass} ${buttonClass} ${super.buildCSSClass()}`;
  }

  /**
   * Construye el DOM predeterminado `className`.
   *
   * @return {cadena}
   * El DOM `className` para este objeto.
   * /
  construirClaseCSS() {
    let menuButtonClass = 'vjs-menu-button';

    // Si se pasa la opción en línea, queremos usar estilos diferentes por completo.
    if (this.options_.inline === verdadero) {
      menuButtonClass += '-en línea';
    } else {
      menuButtonClass += '-popup';
    }

    return `vjs-menu-button ${menuButtonClass} ${super.buildCSSClass()}`;
  }

  /**
   * Obtener o establecer el texto de control localizado que se utilizará para la accesibilidad.
   *
   * > NOTA: Esto vendrá del elemento interno `menuButton_`.
   *
   * @param {cadena} [texto]
   * Texto de control para el elemento.
   *
   * @param {Elemento} [el=this.menuButton_.el()]
   * Elemento para configurar el título.
   *
   * @return {cadena}
   * - El texto de control al obtener
   * /
  controlText(texto, el = this.menuButton_.el()) {
    devuelve este.menuButton_.controlText(texto, el);
  }

  /**
   * Deseche el &quot;botón de menú&quot; y todos los componentes secundarios.
   * /
  disponer () {
    this.handleMouseLeave();
    super.dispose();
  }

  /**
   * Manejar un clic en un `MenuButton`.
   * Consulte {@link ClickableComponent#handleClick} para conocer las instancias en las que se llama.
   *
   * @param {EventTarget~Evento} evento
   * El evento `keydown`, `top` o `click` que hizo que esta función se desactivara
   * llamado.
   *
   * @escucha toca
   * @escucha clic
   * /
  handleClick(evento) {
    if (este.botonPresionado_) {
      este.unpressButton();
    } else {
      este.presioneBoton();
    }
  }

  /**
   * Manejar `mouseleave` para `MenuButton`.
   *
   * @param {EventTarget~Evento} evento
   * El evento `mouseleave` que provocó la llamada de esta función.
   *
   * @escucha mouseleave
   * /
  handleMouseLeave(evento) {
    this.removeClass('vjs-hover');
    Events.off(document, 'keyup', this.handleMenuKeyUp_);
  }

  /**
   * Establecer el enfoque en el botón real, no en este elemento
   * /
  enfocar() {
    este.menuButton_.focus();
  }

  /**
   * Eliminar el foco del botón real, no de este elemento
   * /
  difuminar() {
    este.menuButton_.blur();
  }

  /**
   * Manejar las teclas de tabulador, escape, flecha abajo y flecha arriba para `MenuButton`. Ver
   * {@link ClickableComponent#handleKeyDown} para instancias donde se llama esto.
   *
   * @param {EventTarget~Evento} evento
   * El evento `keydown` que hizo que se llamara a esta función.
   *
   * @escucha tecla abajo
   * /
  handleKeyDown(evento) {

    // Escape o Tab despresiona el 'botón'
    if (keycode.isEventKey(evento, 'Esc') || keycode.isEventKey(evento, 'Tab')) {
      if (este.botonPresionado_) {
        este.unpressButton();
      }

      // No prevenga Predeterminado para la tecla Tabulador: aún queremos perder el foco
      if (!keycode.isEventKey(evento, 'Tab')) {
        event.preventDefault();
        // Vuelva a poner el foco en el botón del botón de menú
        este.menuButton_.focus();
      }
    // Flecha arriba o flecha abajo también 'presiona' el botón para abrir el menú
    } else if (keycode.isEventKey(evento, 'Arriba') || keycode.isEventKey(evento, 'Abajo')) {
      if (!this.buttonPressed_) {
        event.preventDefault();
        este.presioneBoton();
      }
    }
  }

  /**
   * Manejar un evento `keyup` en un `MenuButton`. El oyente para esto se agrega en
   * el constructor.
   *
   * @param {EventTarget~Evento} evento
   * Evento de pulsación de tecla
   *
   * @escucha teclado
   * /
  handleMenuKeyUp(evento) {
    // Escape oculta el menú emergente
    if (keycode.isEventKey(evento, 'Esc') || keycode.isEventKey(evento, 'Tab')) {
      this.removeClass('vjs-hover');
    }
  }

  /**
   * Este nombre de método ahora se delega a `handleSubmenuKeyDown`. Esto significa
   * cualquiera que llame a `handleSubmenuKeyPress` no verá sus llamadas a métodos
   * para de trabajar.
   *
   * @param {EventTarget~Evento} evento
   * El evento que provocó la llamada de esta función.
   * /
  handleSubmenuKeyPress(evento) {
    this.handleSubmenuKeyDown(evento);
  }

  /**
   * Manejar un evento `keydown` en un submenú. El oyente para esto se agrega en
   * el constructor.
   *
   * @param {EventTarget~Evento} evento
   * Evento de pulsación de tecla
   *
   * @escucha tecla abajo
   * /
  handleSubmenuKeyDown(evento) {
    // Escape o Tab despresiona el 'botón'
    if (keycode.isEventKey(evento, 'Esc') || keycode.isEventKey(evento, 'Tab')) {
      if (este.botonPresionado_) {
        este.unpressButton();
      }
      // No prevenga Predeterminado para la tecla Tabulador: aún queremos perder el foco
      if (!keycode.isEventKey(evento, 'Tab')) {
        event.preventDefault();
        // Vuelva a poner el foco en el botón del botón de menú
        este.menuButton_.focus();
      }
    } else {
      // NOTA: Este es un caso especial en el que no pasamos desapercibidos
      // eventos keydown hasta el controlador de componentes, porque es
      // acaba de entender el manejo del teclado del `MenuItem`
      // en el `Menú` que ya pasa las teclas no utilizadas.
    }
  }

  /**
   * Poner el `MenuButton` actual en un estado presionado.
   * /
  presiona el botón() {
    si (esto.habilitado_) {
      this.buttonPressed_ = verdadero;
      este.menú.mostrar();
      this.menu.lockMostrando();
      this.menuButton_.el_.setAttribute('aria-expanded', 'true');

      // establezca el enfoque en el submenú, excepto en iOS, donde resulta en
      // comportamiento de desplazamiento no deseado cuando el jugador está en un iframe
      si (IS_IOS && Dom.isInFrame()) {
        //Regresa temprano para que el menu no este enfocado
        devolver;
      }

      este.menú.focus();
    }
  }

  /**
   * Saca el `MenuButton` actual de un estado presionado.
   * /
  despresionarBoton() {
    si (esto.habilitado_) {
      this.buttonPressed_ = false;
      this.menu.unlockMostrando();
      este.menu.hide();
      this.menuButton_.el_.setAttribute('aria-expanded', 'false');
    }
  }

  /**
   * Deshabilite el `MenuButton`. No permita que se haga clic en él.
   * /
  desactivar() {
    este.unpressButton();

    esto.habilitado_ = falso;
    this.addClass('vjs-disabled');

    este.menuButton_.disable();
  }

  /**
   * Habilite el `MenuButton`. Permita que se haga clic en él.
   * /
  permitir() {
    esto.habilitado_ = verdadero;
    this.removeClass('vjs-disabled');

    este.menuButton_.enable();
  }
}

Componente.registerComponent('MenuButton', MenuButton);
exportar el botón de menú predeterminado;