/**
* @file text-track-display.js
* /
importar componente desde '../componente';
importar * como Fn desde '../utils/fn.js';
importar * como Dom desde '../utils/dom.js';
importar ventana desde 'global/window';
const grisoscuro = '#222';
const gris claro = '#ccc';
const fontMap = {
monoespaciado: 'monoespaciado',
sansserif: 'sans-serif',
serif: 'serif',
monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
monospaceSerif: '"Courier New", monospace',
proporcionalSansSerif: 'sans-serif',
proporcionalSerif: 'serif',
casual: '"Comic Sans MS", Impacto, fantasía',
escritura: '"Monotype Corsiva", cursiva',
versalitas: '"Andale Mono", "Lucida Console", monoespacio, sans-serif'
};
/**
* Construya un color rgba a partir de un código de color hexadecimal dado.
*
* @param {número} color
* Número hexadecimal para el color, como #f0e o #f604e2.
*
* @param {número} opacidad
* Valor de opacidad, 0.0 - 1.0.
*
* @return {cadena}
* El color rgba que se creó, como 'rgba(255, 0, 0, 0.3)'.
* /
función de exportación constructColor(color, opacidad) {
dejar maleficio;
if (color.longitud === 4) {
// el color se parece a "#f0e"
hexadecimal = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
} else if (color.longitud === 7) {
// el color se parece a "#f604e2"
hexadecimal = color.rebanada(1);
} else {
throw new Error('Código de color proporcionado no válido, ' + color + '; debe formatearse como, por ejemplo, #f0e o #f604e2.');
}
devuelve 'rgba(' +
parseInt(hexadecimal.segmento(0, 2), 16) + ',' +
parseInt(hexadecimal.segmento(2, 4), 16) + ',' +
parseInt(hexadecimal.segmento(4, 6), 16) + ',' +
opacidad + ')';
}
/**
* Intente actualizar el estilo de un elemento DOM. Algunos cambios de estilo arrojarán un error,
* particularmente en IE8. Deberían ser noops.
*
* @param {Elemento} el
* El elemento DOM al que se le aplicará estilo.
*
* @param {cadena} estilo
* La propiedad CSS del elemento al que se debe aplicar estilo.
*
* regla @param {cadena}
* La regla de estilo que se debe aplicar a la propiedad.
*
* @privado
* /
function probarActualizarEstilo(el, estilo, regla) {
intentar {
el.style[estilo] = regla;
} catch (e) {
// Satisface linter.
devolver;
}
}
/**
* El componente para mostrar señales de pista de texto.
*
* Componente @extiende
* /
clase TextTrackDisplay 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.
*
* @param {Componente~ReadyCallback} [listo]
* La función para llamar cuando `TextTrackDisplay` está listo.
* /
constructor(jugador, opciones, listo) {
super(jugador, opciones, listo);
const actualizarDisplayHandler = (e) => this.updateDisplay(e);
jugador.on('loadstart', (e) => this.toggleDisplay(e));
player.on('texttrackchange', updateDisplayHandler);
player.on('metadatos cargados', (e) => this.preselectTrack(e));
// Esto solía llamarse durante el inicio del jugador, pero estaba causando un error
// si una pista debe mostrarse de forma predeterminada y la pantalla aún no se ha cargado.
// Probablemente debería moverse a un cargador de pistas externo cuando admitamos
// pistas que no necesitan visualización.
player.ready(Fn.bind(this, function() {
si (jugador.tech_ && player.tech_.featuresNativeTextTracks) {
esto. ocultar ();
devolver;
}
player.on('cambio de pantalla completa', updateDisplayHandler);
player.on('redimensionarjugador', actualizarDisplayHandler);
window.addEventListener('cambio de orientación', updateDisplayHandler);
jugador.on('disponer', () => window.removeEventListener('cambio de orientación', updateDisplayHandler));
const pistas = this.options_.playerOptions.tracks || [];
para (sea i = 0; i < pistas.longitud; i++) {
this.player_.addRemoteTextTrack(pistas[i], verdadero);
}
this.preselectTrack();
}));
}
/**
* Preseleccione una pista siguiendo esta precedencia:
*: coincide con el idioma y el tipo del {@link TextTrack} seleccionado previamente
*: coincide solo con el idioma del {@link TextTrack} seleccionado anteriormente
* - es la primera pista de subtítulos predeterminada
* - es la primera pista de descripciones predeterminada
*
* @escucha Player#loadstart
* /
preseleccionar pista() {
modos const = {subtítulos: 1, subtítulos: 1};
const trackList = this.player_.textTracks();
const userPref = this.player_.cache_.selectedLanguage;
let firstDesc;
let firstCaptions;
let PreferredTrack;
para (sea i = 0; i < trackList.longitud; i++) {
const pista = trackList[i];
si (
usuarioPref && userPref.habilitado &&
pref.usuario.idioma && userPref.idioma === pista.idioma &&
track.kind en modos
) {
// Elija siempre la pista que coincida con el idioma y el tipo
if (pista.tipo === userPref.tipo) {
pista preferida = pista;
// o elige la primera pista que coincida con el idioma
} más si (! pista preferida) {
pista preferida = pista;
}
// borrar todo si se hizo clic en offTextTrackMenuItem
} más si (usuarioPref && !usuarioPref.habilitado) {
pistapreferida = null;
primerDesc = nulo;
primeros subtítulos = nulo;
} más si (pista.predeterminado) {
if (pista.tipo === 'descripciones' && !primerDesc) {
primerDesc = pista;
} else if (pista.tipo en modos && !primeros subtítulos) {
primeros subtítulos = pista;
}
}
}
// La pista preferida coincide con la preferencia del usuario y toma
// precedencia sobre todas las demás pistas.
// Entonces, muestra la pista preferida antes de la primera pista predeterminada
// y la pista de subtítulos/títulos antes de la pista de descripciones
if (pista preferida) {
preferidoTrack.mode = 'mostrando';
} else if (primeros subtítulos) {
firstCaptions.mode = 'mostrando';
} más si (primerDesc) {
firstDesc.mode = 'mostrando';
}
}
/**
* Cambia la visualización de {@link TextTrack} del estado actual al otro estado.
* Solo hay dos estados:
* - 'mostrado'
* - 'oculto'
*
* @escucha Player#loadstart
* /
alternar pantalla () {
si (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
esto. ocultar ();
} else {
este espectáculo();
}
}
/**
* Cree el elemento DOM del {@link Component}.
*
* @return {Elemento}
* El elemento que se creó.
* /
crearEl() {
return super.createEl('div', {
className: 'vjs-text-track-display'
}, {
'traducir': 'sí',
'aria-en vivo': 'apagado',
'aria-atomic': 'verdadero'
});
}
/**
* Borrar todos los {@link TextTrack} mostrados.
* /
borrar pantalla () {
if (tipo de ventana.WebVTT === 'función') {
ventana.WebVTT.processCues(ventana, [], this.el_);
}
}
/**
* Actualizar el TextTrack mostrado cuando un {@link Player#texttrackchange} o
* se dispara un {@link Player#fullscreenchange}.
*
* @escucha Player#texttrackchange
* @escucha jugador#cambio de pantalla completa
* /
actualizarPantalla() {
const pistas = this.player_.textTracks();
const allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;
this.clearDisplay();
if (permitir Múltiples Mostrar Pistas) {
const mostrandoTracks = [];
para (sea i = 0; i < pistas.longitud; ++ yo) {
const pista = pistas[i];
if (track.mode !== 'mostrando') {
continuar;
}
mostrandoTracks.push(pista);
}
this.updateForTrack(mostrandoTracks);
devolver;
}
// Modelo de priorización de visualización de pistas: si se 'muestran' varias pistas,
// muestra la primera pista de 'subtítulos' o 'captions' que está 'mostrando',
// de lo contrario, muestra la primera pista de 'descripciones' que está 'mostrando'
let descripcionesTrack = null;
let captionsSubtitlesTrack = null;
let i = pistas.longitud;
mientras yo--) {
const pista = pistas[i];
if (track.mode === 'mostrando') {
if (pista.tipo === 'descripciones') {
descripcionesTrack = track;
} else {
captionsSubtitlesTrack = pista;
}
}
}
if (captionsSubtitlesTrack) {
if (this.getAttribute('aria-live') !== 'off') {
this.setAttribute('aria-live', 'off');
}
this.updateForTrack(captionsSubtitlesTrack);
} else if (descripcionesPista) {
if (this.getAttribute('aria-live') !== 'asertivo') {
this.setAttribute('aria-live', 'asertivo');
}
this.updateForTrack(descriptionsTrack);
}
}
/**
* Estilo {@Link TextTrack} activeCues según {@Link TextTrackSettings}.
*
* @param {TextTrack} pista
* Objeto de pista de texto que contiene señales activas para diseñar.
* /
actualizarDisplayState(pista) {
const overrides = this.player_.textTrackSettings.getValues();
const cues = track.activeCues;
let i = cues.longitud;
mientras yo--) {
const señal = señal[i];
si (! señal) {
continuar;
}
const cueDiv = cue.displayState;
if (anula.color) {
cueDiv.firstChild.style.color = overrides.color;
}
if (anula.textOpacity) {
intentarActualizarEstilo(
cueDiv.primer hijo,
'color',
construirColor(
anula.color || '#fff',
overrides.textOpacity
)
);
}
if (anula.backgroundColor) {
cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
}
if (anula.backgroundOpacity) {
intentarActualizarEstilo(
cueDiv.primer hijo,
'color de fondo',
construirColor(
overrides.backgroundColor || '#000',
overrides.backgroundOpacity
)
);
}
if (anula.windowColor) {
if (anula.windowOpacity) {
intentarActualizarEstilo(
cueDiv,
'color de fondo',
constructColor(overrides.windowColor, overrides.windowOpacity)
);
} else {
cueDiv.style.backgroundColor = overrides.windowColor;
}
}
if (anula.edgeStyle) {
if (overrides.edgeStyle === 'sombra paralela') {
cueDiv.firstChild.style.textShadow = `2px 2px 3px ${gris oscuro}, 2px 2px 4px ${gris oscuro}, 2px 2px 5px ${gris oscuro}`;
} else if (overrides.edgeStyle === 'elevado') {
cueDiv.firstChild.style.textShadow = `1px 1px ${darkGray}, 2px 2px ${darkGray}, 3px 3px ${darkGray}`;
} else if (overrides.edgeStyle === 'deprimido') {
cueDiv.firstChild.style.textShadow = `1px 1px ${lightGray}, 0 1px ${lightGray}, -1px -1px ${darkGray}, 0 -1px ${darkGray}`;
} else if (overrides.edgeStyle === 'uniforme') {
cueDiv.firstChild.style.textShadow = `0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}`;
}
}
if (anula.fontPercent && overrides.fontPercent !== 1) {
const fontSize = window.parseFloat(cueDiv.style.fontSize);
cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px';
cueDiv.style.height = 'auto';
cueDiv.style.top = 'auto';
}
si (anula.fontFamily && overrides.fontFamily !== 'predeterminado') {
if (overrides.fontFamily === 'small-caps') {
cueDiv.firstChild.style.fontVariant = 'small-caps';
} else {
cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
}
}
}
}
/**
* Agregue un {@link TextTrack} a la {@link Tech}s {@link TextTrackList}.
*
* @param {TextTrack|TextTrack[]} pistas
* Objeto de pista de texto o matriz de pista de texto para agregar a la lista.
* /
updateForTrack(pistas) {
if (!Array.isArray(pistas)) {
pistas = [pistas];
}
if (tipo de ventana.WebVTT !== 'función' ||
pistas.cada((pista)=> {
return !track.activeCues;
})) {
devolver;
}
señales constantes = [];
// empuja todas las señales de pista activas
para (sea i = 0; i < pistas.longitud; ++ yo) {
const pista = pistas[i];
para (sea j = 0; j < track.activeCues.longitud; ++j) {
cues.push(pista.activeCues[j]);
}
}
// elimina todas las señales antes de procesar las nuevas
ventana.WebVTT.processCues(ventana, señales, this.el_);
// agrega una clase única a cada pista de texto de idioma & agregue estilo de configuración si es necesario
para (sea i = 0; i < pistas.longitud; ++ yo) {
const pista = pistas[i];
para (sea j = 0; j < track.activeCues.longitud; ++j) {
const cueEl = track.activeCues[j].displayState;
Dom.addClass(cueEl, 'vjs-text-track-cue');
Dom.addClass(cueEl, 'vjs-text-track-cue-' + ((track.language) ? track.language : i));
if (pista.idioma) {
Dom.setAttribute(cueEl, 'lang', pista.idioma);
}
}
if (this.player_.textTrackSettings) {
this.updateDisplayState(pista);
}
}
}
}
Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
exportar TextTrackDisplay predeterminado;