/**
* @archivo modal-dialog.js
* /
importar * como Dom desde './utils/dom';
importar componente desde './componente';
importar ventana desde 'global/window';
importar documento desde 'global/document';
importar código clave desde 'código clave';
const MODAL_CLASS_NAME = 'vjs-modal-dialog';
/**
* El `ModalDialog` se muestra sobre el video y sus controles, lo que bloquea
* interacción con el jugador hasta que se cierre.
*
* Los cuadros de diálogo modales incluyen un botón "Cerrar" y se cerrarán cuando ese botón
* está activado - o cuando se presiona ESC en cualquier lugar.
*
* Componente @extiende
* /
clase ModalDialog extiende Componente {
/**
* Crear 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.
*
* @param {Mixto} [opciones.contenido=indefinido]
* Proporcionar contenido personalizado para este modal.
*
* @param {cadena} [opciones.descripción]
* Una descripción de texto para el modal, principalmente para la accesibilidad.
*
* @param {booleano} [opciones.fillAlways=false]
* Normalmente, los modales se llenan automáticamente solo la primera vez
* ellos abren. Esto le dice al modal que actualice su contenido.
* cada vez que se abre.
*
* @param {cadena} [opciones.etiqueta]
* Una etiqueta de texto para el modal, principalmente para la accesibilidad.
*
* @param {booleano} [opciones.pauseOnOpen=true]
* Si es `verdadero`, la reproducción se pausará si se reproduce cuando
* el modal se abre, y se reanuda cuando se cierra.
*
* @param {booleano} [opciones.temporal=verdadero]
* Si es `verdadero`, el modal solo se puede abrir una vez; será
* eliminado tan pronto como esté cerrado.
*
* @param {booleano} [opciones.uncloseable=false]
* Si `verdadero`, el usuario no podrá cerrar el modal
* a través de la interfaz de usuario de las formas normales. El cierre programático es
* aun posible.
* /
constructor(jugador, opciones) {
super(jugador, opciones);
this.handleKeyDown_ = (e) => this.handleKeyDown(e);
esto.cerrar_ = (e) => esto.cerrar(e);
this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = false;
this.closeable(!this.options_.uncloseable);
este.contenido(esta.opciones_.contenido);
// Asegúrese de que contentEl esté definido DESPUÉS de inicializar cualquier elemento secundario
// porque solo queremos los contenidos del modal en el contentEl
// (no los elementos de la interfaz de usuario como el botón de cerrar).
this.contentEl_ = Dom.createEl('div', {
className: `${MODAL_CLASS_NAME}-contenido`
}, {
rol: 'documento'
});
this.descEl_ = Dom.createEl('p', {
className: `${MODAL_CLASS_NAME}-descripción vjs-control-text`,
id: this.el().getAttribute('aria-descrito por')
});
Dom.textContent(this.descEl_, this.description());
this.el_.appendChild(this.descEl_);
this.el_.appendChild(this.contentEl_);
}
/**
* Crear el elemento DOM de `ModalDialog`
*
* @return {Elemento}
* El elemento DOM que se crea.
* /
crearEl() {
return super.createEl('div', {
nombre de clase: this.buildCSSClass(),
índice de tabulación: -1
}, {
'aria-descrita por': `${this.id()}_description`,
'aria-oculto': 'verdadero',
'aria-etiqueta': this.label(),
'rol': 'diálogo'
});
}
disponer () {
this.contentEl_ = null;
esto.descEl_ = nulo;
this.previouslyActiveEl_ = null;
super.dispose();
}
/**
* Construye el DOM predeterminado `className`.
*
* @return {cadena}
* El DOM `className` para este objeto.
* /
construirClaseCSS() {
devuelve `${MODAL_CLASS_NAME} vjs-hidden ${super.buildCSSClass()}`;
}
/**
* Devuelve la cadena de etiquetas para este modal. Se utiliza principalmente para accesibilidad.
*
* @return {cadena}
* la etiqueta localizada o cruda de este modal.
* /
etiqueta() {
return this.localize(this.options_.label || 'Ventana modal');
}
/**
* Devuelve la cadena de descripción para este modal. Utilizado principalmente para
* accesibilidad.
*
* @return {cadena}
* La descripción localizada o cruda de este modal.
* /
descripción() {
let desc = this.options_.description || this.localize('Esta es una ventana modal.');
// Agregue un mensaje de cierre universal si el modal se puede cerrar.
if (esto.cerrable()) {
desc += ' ' + this.localize('Este modal se puede cerrar presionando la tecla Escape o activando el botón de cerrar.');
}
volver desc;
}
/**
* Abre el modal.
*
* @fires ModalDialog#beforemodalopen
* @incendios ModalDialog#modalopen
* /
abierto() {
if (!esto.abierto_) {
const jugador = este.jugador();
/**
* Activado justo antes de que se abra un `ModalDialog`.
*
* @event ModalDialog#beforemodalopen
* @type {Objetivo del evento~Evento}
* /
this.trigger('beforemodalopen');
this.opened_ = true;
// Rellene el contenido si el modal nunca se ha abierto antes y
// nunca se ha llenado.
if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
esto.llenar();
}
// Si el jugador estaba jugando, pausarlo y tomar nota de su anterior
// estado de reproducción.
this.wasPlaying_ = !player.paused();
if (this.options_.pauseOnOpen && this.wasPlaying_) {
jugador.pausa();
}
this.on('keydown', this.handleKeyDown_);
// Ocultar controles y anotar si estaban habilitados.
this.hadControls_ = player.controls();
jugador.controles (falso);
este espectáculo();
this.conditionalFocus_();
this.el().setAttribute('aria-hidden', 'false');
/**
* Activado justo después de abrir un `ModalDialog`.
*
* @event ModalDialog#modalopen
* @type {Objetivo del evento~Evento}
* /
this.trigger('modalopen');
this.hasBeenOpened_ = true;
}
}
/**
* Si `ModalDialog` está actualmente abierto o cerrado.
*
* @param {booleano} [valor]
* Si se da, abrirá (`true`) o cerrará (`false`) el modal.
*
* @return {booleano}
* el estado abierto actual del modaldialog
* /
abierto (valor) {
if (tipo de valor === 'booleano') {
este valor ? 'abierto cerrado']();
}
devuelve esto.abierto_;
}
/**
* Cierra el modal, no hace nada si `ModalDialog` está
* no abierto.
*
* @fires ModalDialog#beforemodalclose
* @fires ModalDialog#modalclose
* /
cerca() {
if (!esto.abierto_) {
devolver;
}
const jugador = este.jugador();
/**
* Activado justo antes de que se cierre un `ModalDialog`.
*
* @event ModalDialog#beforemodalclose
* @type {Objetivo del evento~Evento}
* /
this.trigger('antes del cierre modal');
esto.abierto_ = falso;
si (esto.estabaJugando_ && this.options_.pauseOnOpen) {
jugador.jugar();
}
this.off('keydown', this.handleKeyDown_);
if (this.hadControls_) {
jugador.controles(verdadero);
}
esto. ocultar ();
this.el().setAttribute('aria-hidden', 'true');
/**
* Activado justo después de que se cierra un `ModalDialog`.
*
* @event ModalDialog#modalclose
* @type {Objetivo del evento~Evento}
* /
this.trigger('modalclose');
this.condicionalBlur_();
if (esta.opciones_.temporal) {
esto.dispose();
}
}
/**
* Verifique si `ModalDialog` se puede cerrar a través de la interfaz de usuario.
*
* @param {booleano} [valor]
* Si se da como valor booleano, establecerá la opción `cierrable`.
*
* @return {booleano}
* Devuelve el valor final de la opción cerrable.
* /
cerrable(valor) {
if (tipo de valor === 'booleano') {
const cerrable = this.closeable_ = !!valor;
let close = this.getChild('closeButton');
// Si esto se está cerrando y no tiene un botón de cierre, agregue uno.
si (cerrable && !cerca) {
// El botón de cerrar debe ser un elemento secundario del modal, no su
// elemento de contenido, así que cambie temporalmente el elemento de contenido.
const temp = this.contentEl_;
this.contentEl_ = this.el_;
close = this.addChild('closeButton', {controlText: 'Cerrar diálogo modal'});
this.contentEl_ = temp;
this.on(cerrar, 'cerrar', this.close_);
}
// Si esto se hace imposible de cerrar y tiene un botón de cierre, elimínelo.
si (! cerrable && cerca) {
this.off(cerrar, 'cerrar', this.close_);
this.removeChild(cerrar);
cerrar.dispose();
}
}
devuelve esto.cerrable_;
}
/**
* Rellene el elemento de contenido del modal con la opción "contenido" del modal.
* El elemento de contenido se vaciará antes de que se produzca este cambio.
* /
llenar() {
esto.llenarCon(este.contenido());
}
/**
* Rellene el elemento de contenido del modal con contenido arbitrario.
* El elemento de contenido se vaciará antes de que se produzca este cambio.
*
* @fires ModalDialog#beforemodalfill
* @fires ModalDialog#modalfill
*
* @param {Mixto} [contenido]
* Se aplican las mismas reglas que se aplican a la opción `contenido`.
* /
llenar con (contenido) {
const contenidoEl = this.contentEl();
const parentEl = contentEl.parentNode;
const nextSiblingEl = contentEl.nextSibling;
/**
* Activado justo antes de que `ModalDialog` se llene de contenido.
*
* @event ModalDialog#beforemodalfill
* @type {Objetivo del evento~Evento}
* /
this.trigger('antesdelrellenomodal');
this.hasBeenFilled_ = true;
// Separar el elemento de contenido del DOM antes de realizar
// manipulación para evitar modificar el DOM en vivo varias veces.
parentEl.removeChild(contentEl);
esto.vacío();
Dom.insertContent(contenidoEl, contenido);
/**
* Activado justo después de que un `ModalDialog` esté lleno de contenido.
*
* @event ModalDialog#modalfill
* @type {Objetivo del evento~Evento}
* /
this.trigger('modalfill');
// Vuelva a inyectar el elemento de contenido rellenado.
if (siguienteSiblingEl) {
parentEl.insertBefore(contentEl, nextSiblingEl);
} else {
padreEl.appendChild(contenidoEl);
}
// asegúrese de que el botón de cerrar sea el último en el diálogo DOM
const closeButton = this.getChild('closeButton');
si (botóncerrar) {
parentEl.appendChild(closeButton.el_);
}
}
/**
* Vacía el elemento de contenido. Esto sucede cada vez que se llena el modal.
*
* @fires ModalDialog#beforemodalempty
* @fires ModalDialog#modalempty
* /
vacío() {
/**
* Activado justo antes de que se vacíe un `ModalDialog`.
*
* @event ModalDialog#beforemodalempty
* @type {Objetivo del evento~Evento}
* /
this.trigger('beforemodalempty');
Dom.emptyEl(this.contentEl());
/**
* Activado justo después de vaciar un `ModalDialog`.
*
* @event ModalDialog#modalempty
* @type {Objetivo del evento~Evento}
* /
this.trigger('modalempty');
}
/**
* Obtiene o establece el contenido modal, que se normaliza antes de ser
* renderizado en el DOM.
*
* Esto no actualiza el DOM ni llena el modal, pero se llama durante
* ese proceso.
*
* @param {Mixto} [valor]
* Si está definido, establece el valor de contenido interno que se utilizará en el
* próxima(s) llamada(s) a `llenar`. Este valor se normaliza antes de ser
* insertado. Para "borrar" el valor del contenido interno, pase `null`.
*
* @return {Mixto}
* El contenido actual del diálogo modal
* /
contenido (valor) {
if (tipo de valor! == 'indefinido') {
this.content_ = valor;
}
devolver este.content_;
}
/**
* enfocar condicionalmente el diálogo modal si el foco estaba previamente en el jugador.
*
* @privado
* /
enfoque condicional_() {
const activeEl = documento.activeElement;
const jugadorEl = este.jugador_.el_;
this.previouslyActiveEl_ = null;
if (jugadorEl.contains(activoEl) || jugadorEl === activoEl) {
this.previouslyActiveEl_ = activeEl;
este.enfoque();
}
}
/**
* desenfocar condicionalmente el elemento y volver a enfocar el último elemento enfocado
*
* @privado
* /
condicionalBlur_() {
if (this.previouslyActiveEl_) {
this.previouslyActiveEl_.focus();
this.previouslyActiveEl_ = null;
}
}
/**
* Manejador de teclas. Se adjunta cuando el modal está enfocado.
*
* @escucha tecla abajo
* /
handleKeyDown(evento) {
// No permita que las pulsaciones de teclado salgan del cuadro de diálogo modal.
event.stopPropagation();
if (keycode.isEventKey(evento, 'Escape') && esto.cerrable()) {
event.preventDefault();
esto.cerrar();
devolver;
}
// salir temprano si no es una tecla de tabulación
if (!keycode.isEventKey(evento, 'Tab')) {
devolver;
}
const focusableEls = this.focusableEls_();
const activeEl = this.el_.querySelector(':foco');
dejar focusIndex;
para (sea i = 0; i < enfocableEls.longitud; i++) {
if (activeEl === focusableEls[i]) {
índice de enfoque = i;
romper;
}
}
if (document.activeElement === this.el_) {
índice de enfoque = 0;
}
if (evento.shiftKey && índice de enfoque === 0) {
focusableEls[focusableEls.longitud - 1].focus();
event.preventDefault();
} más si (!event.shiftKey && focusIndex === focusableEls.longitud - 1) {
enfocableEls[0].focus();
event.preventDefault();
}
}
/**
* obtener todos los elementos enfocables
*
* @privado
* /
enfocableEls_() {
const allChildren = this.el_.querySelectorAll('*');
return Array.prototype.filter.call(allChildren, (hijo) => {
return ((instancia secundaria de ventana.HTMLAnchorElement ||
instancia secundaria de ventana.HTMLAreaElement) && niño.hasAttribute('href')) ||
((instancia secundaria de ventana.HTMLInputElement ||
instancia secundaria de ventana.HTMLSelectElement ||
instancia secundaria de ventana.HTMLTextAreaElement ||
instancia secundaria de ventana.HTMLButtonElement) && !child.hasAttribute('deshabilitado')) ||
(instancia secundaria de ventana.HTMLIFrameElement ||
instancia secundaria de ventana.HTMLObjectElement ||
instancia secundaria de ventana.HTMLEmbedElement) ||
(hijo.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1) ||
(child.hasAttribute('contenteditable'));
});
}
}
/**
* Opciones predeterminadas para las opciones predeterminadas de `ModalDialog`.
*
* @type {Objeto}
* @privado
* /
ModalDialog.prototipo.opciones_ = {
pauseOnOpen: verdadero,
temporal: verdadero
};
Componente.registerComponent('ModalDialog', ModalDialog);
exportar ModalDialog predeterminado;