/**
* @archivo reproductor.js
* /
// Componente de subclases
importar componente desde './component.js';
importar {versión} desde '../../package.json';
importar documento desde 'global/document';
importar ventana desde 'global/window';
importar eventos desde './mixins/evented';
importar {isEvented, addEventedCallback} desde './mixins/evented';
importar * como Eventos desde './utils/events.js';
importar * como Dom desde './utils/dom.js';
importar * como Fn desde './utils/fn.js';
importar * como Guid desde './utils/guid.js';
importar * como navegador desde './utils/browser.js';
importar {IE_VERSION, IS_CHROME, IS_WINDOWS} desde './utils/browser.js';
registro de importación, { createLogger } desde './utils/log.js';
importar {toTitleCase, titleCaseEquals} desde './utils/string-cases.js';
importar { createTimeRange } desde './utils/time-ranges.js';
importar {bufferedPercent} desde './utils/buffer.js';
importar * como hoja de estilo desde './utils/stylesheet.js';
importar FullscreenApi desde './fullscreen-api.js';
importar MediaError desde './media-error.js';
importar safeParseTuple desde 'safe-json-parse/tuple';
importar {asignar} desde './utils/obj';
importar mergeOptions desde './utils/merge-options.js';
importar {silencePromise, isPromise} desde './utils/promise';
importar textTrackConverter desde './tracks/text-track-list-converter.js';
importar ModalDialog desde './modal-dialog';
importar tecnología desde './tech/tech.js';
importar * como middleware desde './tech/middleware.js';
importar {TODO como TRACK_TYPES} desde './tracks/track-types';
importar filterSource desde './utils/filter-source';
importar {getMimetype, findMimetype} desde './utils/mimetypes';
importar {hooks} desde './utils/hooks';
importar {isObject} desde './utils/obj';
importar código clave desde 'código clave';
// Las siguientes importaciones se utilizan solo para garantizar que los módulos correspondientes
// siempre se incluyen en el paquete video.js. La importación de los módulos
// ejecútelos y se registrarán solos con video.js.
importar './tech/loader.js';
importar './imagen-del-cartel.js';
import './tracks/text-track-display.js';
importar './loading-spinner.js';
importar './gran-play-button.js';
importar './cerrar-boton.js';
importar './barra-de-control/barra-de-control.js';
importar './error-display.js';
import './tracks/text-track-settings.js';
importar './resize-manager.js';
importar './live-tracker.js';
// Importar tecnología Html5, al menos para desechar la etiqueta de video original.
importar './tech/html5.js';
// Los siguientes eventos tecnológicos simplemente se reactivan
// en el jugador cuando suceden
constante TECH_EVENTS_RETRIGGER = [
/**
* Activado mientras el agente de usuario está descargando datos multimedia.
*
* @event Jugador#progreso
* @type {Objetivo del evento~Evento}
* /
/**
* Vuelva a activar el evento "progreso" que activó {@link Tech}.
*
* @privado
* @método Player#handleTechProgress_
* @fires Player#progreso
* @escucha tecnología#progreso
* /
'progreso',
/**
* Se dispara cuando se cancela la carga de un audio/video.
*
* @reproductor de eventos#abortar
* @type {Objetivo del evento~Evento}
* /
/**
* Vuelva a activar el evento `abortar` que fue activado por {@link Tech}.
*
* @privado
* @método Player#handleTechAbort_
* @fires Player#abortar
* @escucha Tech#abort
* /
'abortar',
/**
* Se activa cuando el navegador intencionalmente no obtiene datos multimedia.
*
* @reproductor de eventos#suspender
* @type {Objetivo del evento~Evento}
* /
/**
* Vuelva a activar el evento `suspender` que fue activado por {@link Tech}.
*
* @privado
* @método Player#handleTechSuspend_
* @despedir jugador#suspender
* @escucha Tech#suspender
* /
'suspender',
/**
* Se activa cuando la lista de reproducción actual está vacía.
*
* @event Jugador #vaciado
* @type {Objetivo del evento~Evento}
* /
/**
* Vuelva a activar el evento "vaciado" que activó {@link Tech}.
*
* @privado
* @method Player#handleTechEmptied_
* @fires Jugador #vaciado
* @escucha Tech#emptied
* /
'vaciado',
/**
* Se activa cuando el navegador intenta obtener datos multimedia, pero los datos no están disponibles.
*
* @event jugador # estancado
* @type {Objetivo del evento~Evento}
* /
/**
* Vuelva a activar el evento `stalled` que fue activado por {@link Tech}.
*
* @privado
* @method Player#handleTechStalled_
* @fires Player#stalled
* @escucha Tech#stalled
* /
'estancado',
/**
* Se dispara cuando el navegador ha cargado metadatos para el audio/video.
*
* @reproductor de eventos#metadatos cargados
* @type {Objetivo del evento~Evento}
* /
/**
* Vuelva a activar el evento `loadedmetadata` que fue activado por {@link Tech}.
*
* @privado
* @method Player#handleTechLoadedmetadata_
* @fires Player#metadatos cargados
* @escucha Tech#loadedmetadata
* /
'metadatos cargados',
/**
* Se dispara cuando el navegador ha cargado el cuadro actual del audio/video.
*
* @reproductor del evento#datos cargados
* @tipo {evento}
* /
/**
* Vuelva a activar el evento `loadeddata` que fue activado por {@link Tech}.
*
* @privado
* @método Player#handleTechLoaddeddata_
* @fires Player#loadeddata
* @escucha Tech#loadeddata
* /
'datos cargados',
/**
* Se dispara cuando la posición de reproducción actual ha cambiado.
*
* @event Player#timeupdate
* @tipo {evento}
* /
/**
* Vuelva a activar el evento `timeupdate` que fue activado por {@link Tech}.
*
* @privado
* @método Player#handleTechTimeUpdate_
* @fires jugador #actualización de tiempo
* @escucha Tech#timeupdate
* /
'actualización de tiempo',
/**
* Se dispara cuando cambian las dimensiones intrínsecas del video
*
* @reproductor de eventos#redimensionar
* @tipo {evento}
* /
/**
* Vuelva a activar el evento de "cambio de tamaño" que activó {@link Tech}.
*
* @privado
* @method Player#handleTechResize_
* @fires jugador #redimensionar
* @escucha Tech#redimensionar
* /
'redimensionar',
/**
* Se dispara cuando se ha cambiado el volumen
*
* @reproductor de eventos#cambio de volumen
* @tipo {evento}
* /
/**
* Vuelva a activar el evento `cambio de volumen` que fue activado por {@link Tech}.
*
* @privado
* @method Player#handleTechVolumechange_
* @fires Player#cambio de volumen
* @escucha Tech#cambio de volumen
* /
'cambio de volumen',
/**
* Se dispara cuando se ha cambiado la pista de texto
*
* @reproductor de eventos#texttrackchange
* @tipo {evento}
* /
/**
* Vuelva a activar el evento `texttrackchange` que fue activado por {@link Tech}.
*
* @privado
* @method Player#handleTechTexttrackchange_
* @fires Player#texttrackchange
* @escucha Tech#texttrackchange
* /
'cambio de pista de texto'
];
// eventos en cola cuando la tasa de reproducción es cero
// esto es un hash con el único propósito de mapear nombres de eventos que no están en mayúsculas y minúsculas
// a nombres de funciones en mayúsculas y minúsculas
constante TECH_EVENTS_QUEUE = {
Poder jugar: 'Poder jugar',
puede jugar a través de: 'Puede reproducir',
jugando: 'Jugando',
buscado: 'buscado'
};
constante BREAKPOINT_ORDER = [
'diminuto',
'xpequeño',
'pequeño',
'medio',
'grande',
'grande',
'enorme'
];
const BREAKPOINT_CLASSES = {};
// grep: vjs-layout-tiny
// grep: vjs-layout-x-pequeño
// grep: vjs-layout-pequeño
// grep: vjs-layout-medium
// grep: vjs-layout-grande
// grep: vjs-layout-x-grande
// grep: vjs-layout-enorme
BREAKPOINT_ORDER.forEach(k => {
const v = k.charAt(0) === 'x' ? `x-${k.subcadena(1)}` : k;
BREAKPOINT_CLASSES[k] = `vjs-layout-${v}`;
});
constante DEFAULT_BREAKPOINTS = {
diminuto: 210,
xpequeño: 320,
pequeño: 425,
medio: 768,
grande: 1440,
extra grande: 2560,
enorme: Infinidad
};
/**
* Se crea una instancia de la clase `Player` cuando cualquiera de los métodos de configuración de Video.js
* se utilizan para inicializar un video.
*
* Una vez que se ha creado una instancia, se puede acceder a ella globalmente de dos maneras:
* 1. Llamando a `videojs('example_video_1');`
* 2. Usándolo directamente a través de `videojs.players.example_video_1;`
*
* Componente @extiende
* /
El jugador de clase extiende el componente {
/**
* Crear una instancia de esta clase.
*
* Etiqueta @param {Elemento}
* El elemento DOM de video original utilizado para configurar las opciones.
*
* @param {Objeto} [opciones]
* Objeto de nombres y valores de opciones.
*
* @param {Componente~ReadyCallback} [listo]
* Función de devolución de llamada lista.
* /
constructor(etiqueta, opciones, listo) {
// Asegúrese de que existe la identificación de la etiqueta
etiqueta.id = etiqueta.id || opciones.id || `vjs_video_${Guid.nuevoGUID()}`;
// Establecer opciones
// El argumento de opciones anula las opciones establecidas en la etiqueta de video
// que anula las opciones establecidas globalmente.
// Esta última parte coincide con el orden de carga
// (la etiqueta debe existir antes que Player)
opciones = asignar (Player.getTagSettings (etiqueta), opciones);
// Retrasar la inicialización de los niños porque necesitamos configurar
// primero las propiedades del jugador, y no puede usar `this` antes de `super()`
opciones.initChildren = falso;
// Lo mismo con la creación del elemento
opciones.createEl = false;
// no mezclar automáticamente la mezcla con eventos
options.evented = false;
// no queremos que el reproductor informe de la actividad táctil sobre sí mismo
// ver enableTouchActivity en Componente
opciones.reportTouchActivity = falso;
// Si el idioma no está configurado, obtenga el atributo lang más cercano
if (!opciones.idioma) {
if (tipo de etiqueta.más cercano === 'función') {
const más cercano = etiqueta.más cercano('[idioma]');
si (más cercano && más cercano.getAttribute) {
opciones.idioma = más cercano.getAttribute('idioma');
}
} else {
let elemento = etiqueta;
mientras (elemento && elemento.tiponodo === 1) {
if (Dom.getAttributes(elemento).hasOwnProperty('lang')) {
opciones.idioma = elemento.getAttribute('idioma');
romper;
}
elemento = elemento.NodoPadre;
}
}
}
// Ejecutar la inicialización del componente base con nuevas opciones
super(nulo, opciones, listo);
// Crear métodos enlazados para detectores de documentos.
this.boundDocumentFullscreenChange_ = (e) => this.documentFullscreenChange_(e);
this.boundFullWindowOnEscKey_ = (e) => this.fullWindowOnEscKey(e);
this.boundUpdateStyleEl_ = (e) => this.updateStyleEl_(e);
this.boundApplyInitTime_ = (e) => this.applyInitTime_(e);
this.boundUpdateCurrentBreakpoint_ = (e) => this.updateCurrentBreakpoint_(e);
this.boundHandleTechClick_ = (e) => this.handleTechClick_(e);
this.boundHandleTechDoubleClick_ = (e) => this.handleTechDoubleClick_(e);
this.boundHandleTechTouchStart_ = (e) => this.handleTechTouchStart_(e);
this.boundHandleTechTouchMove_ = (e) => this.handleTechTouchMove_(e);
this.boundHandleTechTouchEnd_ = (e) => this.handleTechTouchEnd_(e);
this.boundHandleTechTap_ = (e) => este.handleTechTap_(e);
// por defecto isFullscreen_ a falso
this.isFullscreen_ = falso;
// crear registrador
este.log = createLogger(este.id_);
// Mantener nuestra propia referencia a la API de pantalla completa para que se pueda simular en las pruebas
esto.fsApi_ = FullscreenApi;
// Realiza un seguimiento cuando un técnico cambia el cartel
this.isPosterFromTech_ = falso;
// Contiene información de devolución de llamada que se pone en cola cuando la tasa de reproducción es cero
// y está ocurriendo una búsqueda
this.queuedCallbacks_ = [];
// Desactive el acceso a la API porque estamos cargando una nueva tecnología que podría cargarse de forma asíncrona
esto.estáListo_ = falso;
// Estado inicial hasStarted_
this.hasStarted_ = false;
// Estado inicial userActive_
este.usuarioActivo_ = falso;
// Inicialización de depuración habilitada_
this.debugEnabled_ = falso;
// Estado de inicio audioOnlyMode_
this.audioOnlyMode_ = falso;
// Estado de inicio audioPosterMode_
este.audioPosterMode_ = falso;
// Estado inicial audioOnlyCache_
this.audioOnlyCache_ = {
altura del jugador: nulo,
niños ocultos: []
};
// si el objeto de la opción global fue volado accidentalmente por
// alguien, fianza temprano con un error informativo
if (!estas.opciones_ ||
!this.options_.techOrder ||
!this.options_.techOrder.longitud) {
throw new Error('No se especificó orden de tecnología. Sobrescribiste ' +
'videojs.options en lugar de simplemente cambiar el ' +
'¿propiedades que desea anular?');
}
// Almacenar la etiqueta original utilizada para establecer opciones
esta.etiqueta = etiqueta;
// Almacenar los atributos de la etiqueta utilizados para restaurar el elemento html5
this.tagAttributes = etiqueta && Dom.getAttributes(etiqueta);
// Actualizar idioma actual
este.idioma(esta.opciones_.idioma);
// Actualizar idiomas admitidos
if (opciones.idiomas) {
// Normalizar los idiomas de las opciones del jugador a minúsculas
const idiomasABajar = {};
Object.getOwnPropertyNames(opciones.idiomas).forEach(función(nombre) {
languagesToLower[nombre.toLowerCase()] = opciones.languages[nombre];
});
this.languages_ = languagesToLower;
} else {
this.languages_ = Player.prototype.options_.languages;
}
esto.resetCache_();
// Establecer cartel
this.poster_ = opciones.poster || '';
// Establecer controles
this.controls_ = !!opciones.controles;
// Configuraciones de etiquetas originales almacenadas en opciones
// ahora elimine inmediatamente para que los controles nativos no parpadeen.
// La tecnología HTML5 puede volver a activarlo si nativeControlsForTouch es verdadero
etiqueta.controles = falso;
etiqueta.removeAttribute('controles');
este.cambiandoSrc_ = falso;
this.playCallbacks_ = [];
this.playTerpressedQueue_ = [];
// el atributo anula la opción
if (etiqueta.hasAttribute('reproducción automática')) {
this.reproducción automática (verdadero);
} else {
// de lo contrario, use el setter para validar y
// establecer el valor correcto.
esta.reproducción automática(esta.opciones_.reproducción automática);
}
// comprobar complementos
si (opciones.complementos) {
Object.keys(options.plugins).forEach((nombre) => {
if (tipo de este [nombre] !== 'función') {
lanzar un nuevo error (`el complemento "${name}" no existe`);
}
});
}
/*
* Almacenar el estado interno de fregado
*
* @privado
* @return {Boolean} True si el usuario está limpiando
* /
esto.fregar_ = falso;
esto.el_ = esto.createEl();
// Convierta esto en un objeto con eventos y use `el_` como su bus de eventos.
evento(esto, {eventBusKey: 'el_'});
// escuchar los controladores de cambio de pantalla completa del documento y del reproductor para que recibamos esos eventos
// antes de que un usuario pueda recibirlos para que podamos actualizar isFullscreen adecuadamente.
// asegúrese de escuchar los eventos de cambio de pantalla completa antes que todo lo demás para asegurarse de que
// nuestro método isFullscreen se actualiza correctamente tanto para los componentes internos como para los externos.
si (esto.fsApi_.requestFullscreen) {
Events.on(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
this.on(this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
}
si (este.fluido_) {
this.on(['playerreset', 'resize'], this.boundUpdateStyleEl_);
}
// También queremos pasar las opciones del reproductor original a cada componente y complemento
// también para que no necesiten volver a buscar opciones en el reproductor más adelante.
// También necesitamos hacer otra copia de this.options_ para no terminar con
// un bucle infinito.
const playerOptionsCopy = mergeOptions(this.options_);
// Cargar complementos
si (opciones.complementos) {
Object.keys(options.plugins).forEach((nombre) => {
este[nombre](opciones.complementos[nombre]);
});
}
// Habilite el modo de depuración para activar el evento de depuración para todos los complementos.
si (opciones.depuración) {
esto. depurar (verdadero);
}
this.options_.playerOptions = playerOptionsCopy;
este.middleware_ = [];
this.playbackRates(opciones.playbackRates);
esto.initChildren();
// Establecer isAudio en función de si se utilizó o no una etiqueta de audio
this.isAudio(tag.nodeName.toLowerCase() === 'audio');
// Actualizar controles className. No se puede hacer esto cuando los controles están inicialmente
// establecido porque el elemento aún no existe.
if (esto.controles()) {
this.addClass('vjs-controls-enabled');
} else {
this.addClass('vjs-controls-disabled');
}
// Establecer la etiqueta ARIA y el rol de la región según el tipo de jugador
this.el_.setAttribute('rol', 'región');
si (esto.esAudio()) {
this.el_.setAttribute('aria-label', this.localize('Reproductor de audio'));
} else {
this.el_.setAttribute('aria-label', this.localize('Video Player'));
}
si (esto.esAudio()) {
this.addClass('vjs-audio');
}
si (esto.flexNotSupported_()) {
this.addClass('vjs-no-flex');
}
// HACER: Haz esto más inteligente. Cambiar el estado del usuario entre tocar/mouse
// usar eventos, ya que los dispositivos pueden tener eventos táctiles y de mouse.
// HACER: Haga que esta verificación se realice nuevamente cuando la ventana cambie entre monitores
// (Ver https://github.com/videojs/video.js/issues/5683)
si (navegador.TOUCH_ENABLED) {
this.addClass('vjs-touch-enabled');
}
// iOS Safari ha roto el manejo de desplazamiento
si (!navegador.IS_IOS) {
this.addClass('vjs-workinghover');
}
// Hacer que el jugador sea fácilmente localizable por ID
Player.players[this.id_] = esto;
// Agregue una clase de versión principal para ayudar a css en los complementos
const majorVersion = version.split('.')[0];
this.addClass(`vjs-v${versión principal}`);
// Cuando el reproductor se inicializa por primera vez, active la actividad para que los componentes
// como la barra de control se muestra si es necesario
este.usuarioActivo(verdadero);
this.reportUserActivity();
this.one('reproducir', (e) => this.listenForUserActivity_(e));
this.on('clic en el escenario', (e) => this.handleStageClick_(e));
this.on('teclado abajo', (e) => this.handleKeyDown(e));
this.on('cambio de idioma', (e) => this.handleLanguagechange(e));
this.breakpoints(this.options_.breakpoints);
this.responsive(this.options_.responsive);
// Llamar a ambos métodos de modo de audio después de que el reproductor esté completamente
// configuración para poder escuchar los eventos desencadenados por ellos
this.on('listo', () => {
// Llamar primero al método audioPosterMode para que
// audioOnlyMode puede tener prioridad cuando ambas opciones se establecen en verdadero
this.audioPosterMode(this.options_.audioPosterMode);
this.audioOnlyMode(this.options_.audioOnlyMode);
});
}
/**
* Destruye el reproductor de video y realiza cualquier limpieza necesaria.
*
* Esto es especialmente útil si está agregando y eliminando videos dinámicamente
* hacia/desde el DOM.
*
* @fires jugador #dispose
* /
disponer () {
/**
* Llamado cuando el jugador está siendo eliminado.
*
* @reproductor de eventos#dispose
* @type {Objetivo del evento~Evento}
* /
this.trigger('dispose');
// evita que se llame dos veces a dispose
this.off('dispose');
// Asegúrese de que todos los detectores de documentos específicos del reproductor no estén vinculados. Esto es
Events.off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
Events.off(document, 'keydown', this.boundFullWindowOnEscKey_);
if (this.styleEl_ && this.styleEl_.parentNode) {
this.styleEl_.parentNode.removeChild(this.styleEl_);
este.estiloEl_ = nulo;
}
// Elimina la referencia a este jugador
Player.players[this.id_] = null;
si (esta.etiqueta && este.etiqueta.jugador) {
esta.etiqueta.jugador = nulo;
}
si (esto.el_ && este.el_.jugador) {
this.el_.jugador = nulo;
}
si (esta.tecnología_) {
this.tech_.dispose();
this.isPosterFromTech_ = falso;
este.poster_ = '';
}
if (this.playerElIngest_) {
this.playerElIngest_ = null;
}
si (esta.etiqueta) {
esta.etiqueta = nulo;
}
middleware.clearCacheForPlayer(esto);
// eliminar todos los controladores de eventos para las listas de pistas
// todas las pistas y los oyentes de pista se eliminan en
// desechar tecnología
TRACK_TYPES.names.forEach((nombre) => {
const props = TRACK_TYPES[nombre];
const list = this[props.getterName]();
// si no es una lista nativa
// tenemos que eliminar manualmente los detectores de eventos
si (lista && lista.off) {
lista.off();
}
});
// el .el_ real se elimina aquí, o se reemplaza si
super.dispose({restoreEl: this.options_.restoreEl});
}
/**
* Crea el elemento DOM del `Player`.
*
* @return {Elemento}
* El elemento DOM que se crea.
* /
crearEl() {
let etiqueta = this.tag;
deja que el;
let playerElIngest = this.playerElIngest_ = tag.parentNode && etiqueta.parentNode.hasAttribute && tag.parentNode.hasAttribute('datos-vjs-jugador');
const divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
if (jugadorElIngesto) {
el = this.el_ = etiqueta.parentNode;
} si no (!divEmbed) {
el = esto.el_ = super.createEl('div');
}
// Copia todos los atributos de la etiqueta, incluidos el ID y la clase
// ID ahora hará referencia al cuadro del reproductor, no a la etiqueta de video
const attrs = Dom.getAttributes(etiqueta);
si (divEmbed) {
el = esto.el_ = etiqueta;
etiqueta = esto.etiqueta = documento.createElement('video');
while (el.niños.longitud) {
etiqueta.appendChild(el.firstChild);
}
if (!Dom.hasClass(el, 'video-js')) {
Dom.addClass(el, 'video-js');
}
el.appendChild(etiqueta);
playerElIngest = this.playerElIngest_ = el;
// mueve las propiedades desde nuestro elemento `video-js` personalizado
// a nuestro nuevo elemento `video`. Esto moverá cosas como
// `src` o `controls` que se configuraron a través de js antes del jugador
// fue inicializado.
Objeto.claves(el).forEach((k) => {
intentar {
etiqueta[k] = el[k];
} catch (e) {
// tenemos una propiedad como HTML externo que en realidad no podemos copiar, ignórela
}
});
}
// establece tabindex en -1 para eliminar el elemento de video del orden de enfoque
etiqueta.setAttribute('tabindex', '-1');
atributos.tabindex = '-1';
// Solución alternativa para #4583 (JAWS+IE no anuncia BPB ni botón de reproducción), y
// por el mismo problema con Chrome (en Windows) con JAWS.
// Consulte https://github.com/FreedomScientific/VFO-standards-support/issues/78
// Tenga en cuenta que no podemos detectar si se está utilizando JAWS, pero este atributo ARIA
// no cambia el comportamiento de IE11 o Chrome si no se usa JAWS
si (IE_VERSION || (IS_CHROME && ES_WINDOWS)) {
tag.setAttribute('rol', 'aplicación');
attrs.role = 'aplicación';
}
// Elimina los atributos de ancho/alto de la etiqueta para que CSS pueda hacerlo 100% ancho/alto
etiqueta.removeAttribute('ancho');
etiqueta.removeAttribute('altura');
if ('ancho' en atributos) {
eliminar atributos.ancho;
}
if ('altura' en atributos) {
eliminar attrs.height;
}
Object.getOwnPropertyNames(attrs).forEach(function(attr) {
// no copiar sobre el atributo de clase al elemento del jugador cuando estamos en un div incrustado
// la clase ya está configurada correctamente en el caso de divEmbed
// y queremos asegurarnos de que la clase `video-js` no se pierda
si (!(divIncrustar && atributo === 'clase')) {
el.setAttribute(atributo, atributos[atributo]);
}
si (divEmbed) {
etiqueta.setAttribute(atributo, atributos[atributo]);
}
});
// Actualizar id/clase de etiqueta para usar como tecnología de reproducción de HTML5
// Podría pensar que deberíamos hacer esto después de incrustarlo en el contenedor, por lo que la clase .vjs-tech
// no parpadea al 100% de ancho/alto, pero la clase solo se aplica con el padre .video-js
etiqueta.playerId = etiqueta.id;
etiqueta.id += '_html5_api';
etiqueta.className = 'vjs-tech';
// Hacer que el jugador se pueda encontrar en los elementos
tag.jugador = el.jugador = esto;
// El estado predeterminado del video está en pausa
this.addClass('vjs-paused');
// Agregue un elemento de estilo en el reproductor que usaremos para establecer el ancho/alto
// del jugador de una manera que CSS aún puede anular, al igual que el
// elemento de vídeo
si (ventana.VIDEOJS_NO_DYNAMIC_STYLE! == verdadero) {
this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
const defaultsStyleEl = Dom.$('.vjs-styles-defaults');
const cabeza = Dom.$('cabeza');
head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
}
esto.llenar_ = falso;
este.fluido_ = falso;
// Pase las opciones de ancho/alto/relación de aspecto que actualizarán el estilo el
este.ancho(esta.opciones_.ancho);
esta.altura(esta.opciones_.altura);
this.fill(this.options_.fill);
este.fluido(esta.opciones_.fluido);
this.aspectRatio(this.options_.aspectRatio);
// admite crossOrigin y crossorigin para reducir la confusión y los problemas relacionados con el nombre
this.crossOrigin(this.options_.crossOrigin || this.options_.crossorigin);
// Ocultar cualquier enlace dentro de la etiqueta de video/audio,
// porque IE no los oculta completamente de los lectores de pantalla.
const enlaces = etiqueta.getElementsByTagName('a');
para (sea i = 0; i < enlaces.longitud; i++) {
const linkEl = enlaces.item(i);
Dom.addClass(linkEl, 'vjs-hidden');
linkEl.setAttribute('oculto', 'oculto');
}
// insertElFirst parece hacer que el estado de la red parpadee de 3 a 2, por lo que
// realizar un seguimiento del original para más adelante para que podamos saber si la fuente falló originalmente
etiqueta.initNetworkState_ = etiqueta.networkState;
// Envolver la etiqueta de video en el contenedor div (el/box)
if (etiqueta.nodopadre && !playerElIngest) {
etiqueta.parentNode.insertBefore(el, etiqueta);
}
// inserta la etiqueta como el primer hijo del elemento jugador
// luego agréguelo manualmente a la matriz de niños para que this.addChild
// funcionará correctamente para otros componentes
//
// Rompe el iPhone, corregido en la configuración de HTML5.
Dom.prependTo(etiqueta, el);
this.children_.unshift(etiqueta);
// Establezca lang attr en el reproductor para garantizar que CSS :lang() sea coherente con el reproductor
// si se ha configurado en algo diferente al documento
this.el_.setAttribute('lang', this.language_);
this.el_.setAttribute('traducir', 'no');
esto.el_ = el;
volver el;
}
/**
* Obtener o establecer la opción crossOrigin del `Player`. Para el reproductor HTML5, este
* establece la propiedad `crossOrigin` en `< video> ` etiqueta para controlar el CORS
* comportamiento.
*
* @ver [Atributos de elemento de video]{@enlace https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-crossorigin}
*
* @param {cadena} [valor]
* El valor para establecer el crossOrigin del `Player`. Si un argumento es
* proporcionado, debe ser uno de `anonymous` o `use-credentials`.
*
* @return {cadena|indefinido}
* - El valor crossOrigin actual del `Player` al obtener.
* - indefinido al configurar
* /
origencruz(valor) {
si (! valor) {
devuelve esto.techGet_('crossOrigin');
}
if (valor !== 'anonimo' && valor !== 'usar-credenciales') {
log.warn(`crossOrigin debe ser "anónimo" o "usar-credenciales", dado "${valor}"`);
devolver;
}
this.techCall_('setCrossOrigin', valor);
devolver;
}
/**
* Un getter/setter para el ancho del `Jugador`. Devuelve el valor configurado del jugador.
* Para obtener el ancho actual, use `currentWidth()`.
*
* @param {número} [valor]
* El valor para establecer el ancho del `Jugador`.
*
* @return {número}
* El ancho actual del `Player` al obtener.
* /
ancho (valor) {
return this.dimension('ancho', valor);
}
/**
* Un getter/setter para la altura del `Jugador`. Devuelve el valor configurado del jugador.
* Para obtener la altura actual, use `currentheight ()`.
*
* @param {número} [valor]
* El valor para establecer la altura del `Jugador`.
*
* @return {número}
* La altura actual del `Jugador` al obtener.
* /
altura (valor) {
return this.dimension('altura', valor);
}
/**
* Un getter/setter para el ancho del `Jugador` & altura.
*
* @param {cadena} dimensión
* Esta cadena puede ser:
* - 'ancho'
* - 'altura'
*
* @param {número} [valor]
* Valor de la dimensión especificada en el primer argumento.
*
* @return {número}
* El valor de los argumentos de dimensión al obtener (ancho/alto).
* /
dimensión(dimensión, valor) {
const privDimension = dimensión + '_';
si (valor === indefinido) {
devuelve esta[dimensiónprivada] || 0;
}
if (valor === '' || valor === 'auto') {
// Si se da una cadena vacía, restablece la dimensión para que sea automática
esta[dimensiónprivada] = indefinido;
this.updateStyleEl_();
devolver;
}
const parsedVal = parseFloat(valor);
if (esNaN(valor analizado)) {
log.error(`Valor incorrecto "${valor}" suministrado para ${dimensión}`);
devolver;
}
this[privDimension] = parsedVal;
this.updateStyleEl_();
}
/**
* Un getter/setter/toggler para vjs-fluid `className` en el `Player`.
*
* Activar esto desactivará el modo de llenado.
*
* @param {booleano} [booleano]
* - Un valor de true agrega la clase.
* - Un valor de falso elimina la clase.
* - Ningún valor será captador.
*
* @return {booleano|indefinido}
* - El valor del fluido al llegar.
* - `indefinido` al configurar.
* /
fluido(bool) {
si (bool === indefinido) {
volver !!este.fluido_;
}
este.fluido_ = !!bool;
si (es un evento (esto)) {
this.off(['playerreset', 'resize'], this.boundUpdateStyleEl_);
}
si (bool) {
this.addClass('vjs-fluido');
esto.llenar(falso);
addEventedCallback(esto, () => {
this.on(['playerreset', 'resize'], this.boundUpdateStyleEl_);
});
} else {
this.removeClass('vjs-fluido');
}
this.updateStyleEl_();
}
/**
* Un getter/setter/toggler para vjs-fill `className` en el `Player`.
*
* Activar esto desactivará el modo fluido.
*
* @param {booleano} [booleano]
* - Un valor de true agrega la clase.
* - Un valor de falso elimina la clase.
* - Ningún valor será captador.
*
* @return {booleano|indefinido}
* - El valor del fluido al llegar.
* - `indefinido` al configurar.
* /
llenar (bool) {
si (bool === indefinido) {
volver !!esto.llenar_;
}
this.fill_ = !!bool;
si (bool) {
this.addClass('vjs-fill');
este.fluido(falso);
} else {
this.removeClass('vjs-fill');
}
}
/**
* Obtener/Establecer la relación de aspecto
*
* @param {cadena} [proporción]
* Relación de aspecto para el jugador
*
* @return {cadena|indefinido}
* devuelve la relación de aspecto actual al obtener
* /
/**
* Un getter/setter para la relación de aspecto del `Player`.
*
* @param {cadena} [proporción]
* El valor para establecer la relación de aspecto del 'Reproductor'.
*
* @return {cadena|indefinido}
* - La relación de aspecto actual del `Player` al obtener.
* - indefinido al configurar
* /
relación de aspecto (relación) {
si (proporción === indefinido) {
devuelve esto.aspectRatio_;
}
// Comprobar el formato ancho:alto
si (!(/^\d+\:\d+$/).prueba(proporción)) {
throw new Error('Valor incorrecto proporcionado para la relación de aspecto. El formato debe ser ancho:alto, por ejemplo 16:9.');
}
this.aspectRatio_ = relación;
// Suponemos que si establece una relación de aspecto desea el modo fluido,
// porque en modo fijo podrías calcular el ancho y la altura tú mismo.
este.fluido(verdadero);
this.updateStyleEl_();
}
/**
* Actualizar estilos del elemento `Player` (alto, ancho y relación de aspecto).
*
* @privado
* @escucha Tech#loadedmetadata
* /
actualizarEstiloEl_() {
si (ventana.VIDEOJS_NO_DYNAMIC_STYLE === verdadero) {
const ancho = tipo de esto.ancho_ === 'número' ? este.ancho_ : este.opciones_.ancho;
altura const = tipo de this.height_ === 'número'? esta.altura_ : esta.opciones_.altura;
const techEl = this.tech_ && esto.tech_.el();
si (tecnología) {
si (ancho > = 0) {
techEl.ancho = ancho;
}
si (altura > = 0) {
techEl.altura = altura;
}
}
devolver;
}
dejar ancho;
dejar altura;
dejar relación de aspecto;
dejar idClase;
// La relación de aspecto se usa directamente o para calcular el ancho y la altura.
if (this.aspectRatio_ !== indefinido && this.aspectRatio_ !== 'auto') {
// Usar cualquier relación de aspecto que se haya establecido específicamente
relación de aspecto = this.aspectRatio_;
} más si (this.videoWidth() > 0) {
// De lo contrario, intente obtener la relación de aspecto de los metadatos del video
relación de aspecto = this.videoWidth() + ':' + this.videoHeight();
} else {
// O use un valor predeterminado. El elemento de video es 2:1, pero 16:9 es más común.
relación de aspecto = '16:9';
}
// Obtenga la relación como un decimal que podemos usar para calcular dimensiones
const ratioParts = aspectRatio.split(':');
const ratioMultiplier = ratioParts[1] / ratioParts[0];
if (this.width_ !== indefinido) {
// Usa cualquier ancho que se haya establecido específicamente
ancho = este.ancho_;
} else if (this.height_ !== indefinido) {
// O calcule el ancho a partir de la relación de aspecto si se ha establecido una altura
ancho = this.height_ / ratioMultiplier;
} else {
// O use los metadatos del video, o use el valor predeterminado de video el de 300
ancho = this.videoWidth() || 300;
}
if (this.height_ !== indefinido) {
// Usar cualquier altura que se haya establecido específicamente
altura = esta.altura_;
} else {
// De lo contrario, calcule la altura a partir de la relación y el ancho
alto = ancho * ratioMultiplier;
}
// Asegúrese de que la clase CSS sea válida comenzando con un carácter alfabético
if ((/^[^a-zA-Z]/).test(this.id())) {
idClass = 'dimensiones-' + this.id();
} else {
idClass = this.id() + '-dimensiones';
}
// Asegúrese de que la clase correcta aún esté en el reproductor para el elemento de estilo
this.addClass(idClass);
hoja de estilo.setTextContent(this.styleEl_, `
.${idClase} {
ancho: ${ancho}px;
altura: ${altura}px;
}
.${idClass}.vjs-fluid:not(.vjs-audio-only-mode) {
padding-top: ${ratioMultiplier * 100}%;
}
`);
}
/**
* Cargar/Crear una instancia de reproducción {@link Tech} incluido el elemento
* y métodos API. Luego agregue el elemento `Tech` en `Player` como un elemento secundario.
*
* @param {string} techName
* nombre de la tecnología de reproducción
*
* @param {cadena} fuente
* fuente de vídeo
*
* @privado
* /
loadTech_(nombreTecnología, fuente) {
// Pausa y elimina la tecnología de reproducción actual
si (esta.tecnología_) {
esto.descargaTecnología_();
}
const titleTechName = toTitleCase(techName);
const camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
// deshacerse de la etiqueta de video HTML5 tan pronto como estemos usando otra tecnología
if (titleTechName !== 'Html5' && esta.etiqueta) {
Tech.getTech('Html5').disposeMediaElement(this.tag);
esta.etiqueta.jugador = nulo;
esta.etiqueta = nulo;
}
this.techName_ = titleTechName;
// Desactive el acceso a la API porque estamos cargando una nueva tecnología que podría cargarse de forma asíncrona
esto.estáListo_ = falso;
let reproducción automática = this.autoplay();
// si la reproducción automática es una cadena (o `true` con normalizeAutoplay: true) le pasamos falso a la tecnología
// porque el reproductor manejará la reproducción automática en `loadstart`
if (tipo de this.autoplay() === 'cadena' || this.autoplay() === verdadero && this.options_.normalizeReproducción automática) {
reproducción automática = falso;
}
// Obtenga opciones específicas de tecnología de las opciones del reproductor y agregue la fuente y el elemento principal para usar.
const techOptions = {
fuente,
auto-reproducción,
'nativeControlsForTouch': this.options_.nativeControlsForTouch,
'IdJugador': this.id(),
'techId': `${this.id()}_${camelTechName}_api`,
'playsinline': this.options_.playsinline,
'precargar': this.options_.preload,
'bucle': this.options_.loop,
'disablePictureInPicture': this.options_.disablePictureInPicture,
'silenciado': this.options_.muted,
'cartel': este.cartel(),
'idioma': este.idioma(),
'playerElIngest': this.playerElIngest_ || FALSO,
'vtt.js': this.options_['vtt.js'],
'canOverridePoster': !!this.options_.techCanOverridePoster,
'enableSourceset': this.options_.enableSourceset,
'Promesa': this.options_.Promise
};
TRACK_TYPES.names.forEach((nombre) => {
const props = TRACK_TYPES[nombre];
techOptions[props.getterName] = this[props.privateName];
});
asignar(techOptions, this.options_[titleTechName]);
asignar(techOptions, this.options_[camelTechName]);
asignar(opcionestecnológicas, this.options_[techName.toLowerCase()]);
si (esta.etiqueta) {
techOptions.tag = this.tag;
}
si (fuente && fuente.src === this.cache_.src && this.cache_.currentTime > 0) {
techOptions.startTime = this.cache_.currentTime;
}
// Inicializar instancia de tecnología
const TechClass = Tech.getTech(techName);
if (!ClaseTecnológica) {
throw new Error(`No existe ninguna tecnología llamada '${titleTechName}'! '${titleTechName}' debe registrarse usando videojs.registerTech()'`);
}
this.tech_ = new TechClass(techOptions);
// player.triggerReady siempre es asíncrono, así que no es necesario que sea asíncrono
this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);
textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
// Escuche todos los eventos definidos por HTML5 y actívelos en el reproductor
TECH_EVENTS_RETRIGGER.forEach((evento) => {
this.on(this.tech_, event, (e) => this[`handleTech${toTitleCase(evento)}_`](e));
});
Objeto.keys(TECH_EVENTS_QUEUE).forEach((evento) => {
this.on(this.tech_, event, (eventObj) => {
if (this.tech_.playbackRate() === 0 && esta.tecnología_.buscando()) {
this.queuedCallbacks_.push({
devolución de llamada: this[`handleTech${TECH_EVENTS_QUEUE[event]}_`].bind(this),
evento: eventObj
});
devolver;
}
this[`handleTech${TECH_EVENTS_QUEUE[evento]}_`](eventObj);
});
});
this.on(this.tech_, 'loadstart', (e) => this.handleTechLoadStart_(e));
this.on(this.tech_, 'sourceset', (e) => this.handleTechSourceset_(e));
this.on(this.tech_, 'esperando', (e) => this.handleTechWaiting_(e));
this.on(this.tech_, 'terminado', (e) => this.handleTechEnded_(e));
this.on(this.tech_, 'buscando', (e) => this.handleTechSeeking_(e));
this.on(this.tech_, 'play', (e) => this.handleTechPlay_(e));
this.on(this.tech_, 'firstplay', (e) => this.handleTechFirstPlay_(e));
this.on(this.tech_, 'pause', (e) => this.handleTechPause_(e));
this.on(this.tech_, 'durationchange', (e) => this.handleTechDurationChange_(e));
this.on(this.tech_, 'cambio de pantalla completa', (e, data) => this.handleTechFullscreenChange_(e, datos));
this.on(this.tech_, 'error de pantalla completa', (e, err) => this.handleTechFullscreenError_(e, err));
this.on(this.tech_, 'enterpictureinpicture', (e) => this.handleTechEnterPictureInPicture_(e));
this.on(this.tech_, 'dejar imagen en imagen', (e) => this.handleTechLeavePictureInPicture_(e));
this.on(this.tech_, 'error', (e) => this.handleTechError_(e));
this.on(this.tech_, 'posterchange', (e) => this.handleTechPosterChange_(e));
this.on(this.tech_, 'textdata', (e) => this.handleTechTextData_(e));
this.on(this.tech_, 'ratechange', (e) => this.handleTechRateChange_(e));
this.on(this.tech_, 'loadedmetadata', this.boundUpdateStyleEl_);
this.usingNativeControls(this.techGet_('controls'));
si (esto.controles() && !this.usingNativeControls()) {
this.addTechControlsListeners_();
}
// Agregue el elemento tecnológico en el DOM si aún no estaba allí
// Asegúrese de no insertar el elemento de video original si usa Html5
if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
Dom.prependTo(this.tech_.el(), this.el());
}
// Deshágase de la referencia de la etiqueta de video original después de cargar la primera tecnología
si (esta.etiqueta) {
esta.etiqueta.jugador = nulo;
esta.etiqueta = nulo;
}
}
/**
* Descargue y elimine la reproducción actual {@link Tech}.
*
* @privado
* /
descargarTecnología_() {
// Guarde las pistas de texto actuales para que podamos reutilizar las mismas pistas de texto con la siguiente tecnología
TRACK_TYPES.names.forEach((nombre) => {
const props = TRACK_TYPES[nombre];
this[props.privateName] = this[props.getterName]();
});
this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
esto.estáListo_ = falso;
this.tech_.dispose();
this.tech_ = false;
si (este.esPosterFromTech_) {
este.poster_ = '';
this.trigger('cambio de cartel');
}
this.isPosterFromTech_ = falso;
}
/**
* Devolver una referencia al {@link Tech} actual.
* Imprimirá una advertencia por defecto sobre el peligro de usar la tecnología directamente
* pero cualquier argumento que se pase silenciará la advertencia.
*
* @param {*} [seguridad]
* Cualquier cosa pasó para silenciar la advertencia.
*
* @return {Tecnología}
* La tecnología
* /
tecnología (seguridad) {
if (seguridad === indefinido) {
log.warn('Usar la tecnología directamente puede ser peligroso. Espero que sepas lo que estás haciendo.\n' +
'Consulte https://github.com/videojs/video.js/issues/2617 para obtener más información.\n');
}
devolver esto.tech_;
}
/**
* Configure oyentes de clic y toque para el elemento de reproducción
*
* - En computadoras de escritorio: un clic en el video cambiará la reproducción
* - En dispositivos móviles: un clic en el video alterna los controles
* que se hace alternando el estado del usuario entre activo y
* inactivo
* - Un toque puede indicar que un usuario se ha vuelto activo o se ha vuelto inactivo
* Por ejemplo, un toque rápido en una película de iPhone debería revelar los controles. Otro
* el toque rápido debería ocultarlos nuevamente (lo que indica que el usuario está inactivo)
* estado de visualización)
* - Además de esto, todavía queremos que el usuario sea considerado inactivo después de
* unos segundos de inactividad.
*
* > Nota: la única parte de la interacción de iOS que no podemos imitar con esta configuración
* es mantener presionado el elemento de video que cuenta como actividad para
* mantenga los controles visibles, pero eso no debería ser un problema. Un toque y espera
* en cualquier control aún mantendrá al usuario activo
*
* @privado
* /
addTechControlsListeners_() {
// Asegúrese de eliminar todos los oyentes anteriores en caso de que nos llamen varias veces.
this.removeTechControlsListeners_();
this.on(this.tech_, 'click', this.boundHandleTechClick_);
this.on(this.tech_, 'dblclick', this.boundHandleTechDoubleClick_);
// Si los controles estaban ocultos, no queremos que eso cambie sin un evento de toque
// así que comprobaremos si los controles ya se mostraban antes de informar al usuario
// actividad
this.on(this.tech_, 'touchstart', this.boundHandleTechTouchStart_);
this.on(this.tech_, 'touchmove', this.boundHandleTechTouchMove_);
this.on(this.tech_, 'touchend', this.boundHandleTechTouchEnd_);
// El oyente del toque debe ir después del oyente del extremo táctil porque el oyente del toque
// el oyente cancela cualquier actividad de usuario informada al configurar userActive (falso)
this.on(this.tech_, 'toque', this.boundHandleTechTap_);
}
/**
* Elimine los oyentes utilizados para los controles de clic y toque. Esto es necesario para
* alternar a los controles deshabilitados, donde un toque/toque no debería hacer nada.
*
* @privado
* /
removeTechControlsListeners_() {
// No queremos usar simplemente `this.off()` porque podría haber otros necesarios
// oyentes agregados por técnicos que amplían esto.
this.off(this.tech_, 'toque', this.boundHandleTechTap_);
this.off(this.tech_, 'touchstart', this.boundHandleTechTouchStart_);
this.off(this.tech_, 'touchmove', this.boundHandleTechTouchMove_);
this.off(this.tech_, 'touchend', this.boundHandleTechTouchEnd_);
this.off(this.tech_, 'click', this.boundHandleTechClick_);
this.off(this.tech_, 'dblclick', this.boundHandleTechDoubleClick_);
}
/**
* El jugador espera a que la tecnología esté lista
*
* @privado
* /
manejarTechReady_() {
this.triggerReady();
// Mantener el mismo volumen que antes
if (este.caché_.volumen) {
this.techCall_('setVolume', this.cache_.volume);
}
// Mire si el técnico encontró un póster de mayor resolución mientras cargaba
this.handleTechPosterChange_();
// Actualizar la duración si está disponible
this.handleTechDurationChange_();
}
/**
* Vuelva a activar el evento `loadstart` que fue activado por {@link Tech}. Esto
* la función también activará {@link Player#firstplay} si es el primer inicio de carga
* para un vídeo.
*
* @fires Player#loadstart
* @fires jugador #firstplay
* @escucha Tech#loadstart
* @privado
* /
handleTechLoadStart_() {
// HACER: Actualice para usar el evento 'vaciado' en su lugar. Ver #1277.
this.removeClass('vjs-terminado');
this.removeClass('búsqueda de vjs');
// restablecer el estado de error
este.error(nulo);
// Actualizar la duración
this.handleTechDurationChange_();
// Si ya se está reproduciendo, queremos activar un evento firstplay ahora.
// El evento firstplay se basa en los eventos play y loadstart
// que puede ocurrir en cualquier orden para una nueva fuente
if (!this.paused()) {
/**
* Activado cuando el agente de usuario comienza a buscar datos de medios
*
* @reproductor de eventos#loadstart
* @type {Objetivo del evento~Evento}
* /
this.trigger('loadstart');
this.trigger('primera reproducción');
} else {
// restablecer el estado hasStarted
this.hasStarted(false);
this.trigger('loadstart');
}
// la reproducción automática ocurre después del inicio de carga para el navegador,
// entonces imitamos ese comportamiento
this.manualAutoplay_(this.autoplay() === verdadero && this.options_.normalizeReproducción automática? 'reproducir': this.autoplay());
}
/**
* Manejar valores de cadena de reproducción automática, en lugar del típico booleano
* valores que debe manejar el técnico. Tenga en cuenta que esto no es
* parte de cualquier especificación. Los valores válidos y lo que hacen pueden ser
* encontrado en el captador de reproducción automática en Player#autoplay()
* /
manualAutoplay_(tipo) {
if (!this.tech_ || tipo de tipo !== 'cadena') {
devolver;
}
// Guarde el valor silenciado() original, establezca silenciado en verdadero e intente reproducir().
// Al rechazar la promesa, restaurar silenciado desde el valor guardado
const resolverSilenciado = () => {
const previamente silenciado = this.muted();
esto.silenciado(verdadero);
const restaurar silenciado = () => {
this.muted(anteriormente silenciado);
};
// restaurar silenciado al terminar la reproducción
this.playTerpressedQueue_.push(restoreMuted);
const mutedPromise = this.play();
if (!isPromise(mutedPromise)) {
devolver;
}
return Promesa silenciada.catch(err => {
restaurarSilenciado();
throw new Error(`Rechazo en manualAutoplay. Restauración del valor silenciado. ${err? error: ''}`);
});
};
dejar prometer;
// si está silenciado, el valor predeterminado es verdadero
// lo unico que podemos hacer es llamar al play
si (escriba === 'cualquiera' && !esto.silenciado()) {
promesa = this.play();
si (esPromesa(promesa)) {
promesa = promesa.catch(resolveMuted);
}
} else if (escriba === 'silenciado' && !esto.silenciado()) {
promesa = resolverSilenciado();
} else {
promesa = this.play();
}
if (!isPromise(promesa)) {
devolver;
}
volver promesa.entonces(() => {
this.trigger({tipo: 'reproducción automática-éxito', reproducción automática: tipo});
}).atrapar(() => {
this.trigger({tipo: 'autoplay-failure', autoplay: tipo});
});
}
/**
* Actualizar las cachés de fuentes internas para que devolvamos la fuente correcta de
* `src()`, `currentSource()` y `currentSources()`.
*
* > Nota: `currentSources` no se actualizará si la fuente que se pasa existe
* en el caché `currentSources` actual.
*
*
* @param {Tech~SourceObject} srcObj
* Una fuente de cadena u objeto para actualizar nuestros cachés.
* /
updateSourceCaches_(srcObj = '') {
let src = srcObj;
dejar tipo = '';
if (tipo de src !== 'cadena') {
src = srcObj.src;
tipo = srcObj.tipo;
}
// asegúrese de que todos los cachés estén configurados con los valores predeterminados
// para evitar la verificación nula
this.cache_.source = this.cache_.source || {};
this.cache_.sources = this.cache_.sources || [];
// intenta obtener el tipo de src que se pasó
si (fuente && !tipo) {
tipo = findMimetype(this, src);
}
// actualiza siempre la memoria caché `currentSource`
this.cache_.source = mergeOptions({}, srcObj, {src, type});
const matchingSources = this.cache_.sources.filter((s) => s.src && s.src === src);
const fuenteElFuentes = [];
const sourceEls = this.$$('fuente');
const coincidenciaSourceEls = [];
para (sea i = 0; i < fuenteEls.longitud; i++) {
const sourceObj = Dom.getAttributes(sourceEls[i]);
sourceElSources.push(sourceObj);
si (origenObj.src && sourceObj.src === src) {
haciendo coincidirSourceEls.push(sourceObj.src);
}
}
// si tenemos els fuente coincidente pero fuentes no coincidentes
// el caché de origen actual no está actualizado
if (matchingSourceEls.longitud && !fuentescoincidencias.longitud) {
this.cache_.sources = sourceElSources;
// si no tenemos una fuente coincidente o una fuente els, establezca el
// fuentes de la memoria caché a la memoria caché `currentSource`
} else if (!fuentescoincidencias.longitud) {
this.cache_.sources = [this.cache_.source];
}
// actualizar el caché tecnológico `src`
this.cache_.src = src;
}
/**
* *EXPERIMENTAL* Se activa cuando la fuente se configura o cambia en {@link Tech}
* haciendo que el elemento multimedia se vuelva a cargar.
*
* Se disparará para la fuente inicial y cada fuente subsiguiente.
* Este evento es un evento personalizado de Video.js y lo activa {@link Tech}.
*
* El objeto de evento para este evento contiene una propiedad `src` que contendrá la fuente
* que estaba disponible cuando se activó el evento. Esto generalmente solo es necesario si Video.js
* está cambiando de tecnología mientras se cambiaba la fuente.
*
* También se dispara cuando se llama a `cargar` en el reproductor (o elemento multimedia)
* porque el {@enlace https://html.spec.whatwg.org/multipage/media.html#dom-media-load|especificación para `carga`}
* dice que el algoritmo de selección de recursos debe cancelarse y reiniciarse.
* En este caso, es muy probable que la propiedad `src` se establezca en el
* cadena vacía `""` para indicar que no sabemos cuál será la fuente pero
* que está cambiando.
*
* *Este evento aún es experimental y puede cambiar en versiones menores.*
* __Para usar esto, pasa la opción `enableSourceset` al reproductor.__
*
* @reproductor de eventos#conjuntofuente
* @type {Objetivo del evento~Evento}
* @prop {cadena} src
* La URL de origen disponible cuando se activó el `sourceset`.
* Será una cadena vacía si no podemos saber cuál es la fuente
* pero sepa que la fuente cambiará.
* /
/**
* Vuelva a activar el evento `sourceset` que fue activado por {@link Tech}.
*
* @fires Player#sourceset
* @escucha Tech#sourceset
* @privado
* /
handleTechSourceset_(evento) {
// solo actualice el caché de origen cuando el origen
// no se actualizó usando la API del reproductor
if (!este.cambiandoSrc_) {
let updateSourceCaches = (src) => this.updateSourceCaches_(src);
const playerSrc = this.currentSource().src;
const eventSrc = event.src;
// si tenemos un playerSrc que no es un blob y un tech src que es un blob
if (jugadorSrc && !(/^blob:/).test(playerSrc) && (/^blob:/).prueba(eventSrc)) {
// si tanto la fuente de tecnología como la fuente del jugador se actualizaron, asumimos
// Algo así como @videojs/http-streaming hizo el conjunto de fuentes y salteó la actualización del caché de fuentes.
if (!this.lastSource_ || (this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc)) {
updateSourceCaches = () => {};
}
}
// actualice la fuente a la fuente inicial de inmediato
// en algunos casos será una cadena vacía
updateSourceCaches(eventSrc);
// si el `sourceset` `src` era una cadena vacía
// espera un `loadstart` para actualizar el caché a `currentSrc`.
// Si ocurre un conjunto de fuentes antes de un `loadstart`, restablecemos el estado
si (!evento.src) {
this.tech_.any(['sourceset', 'loadstart'], (e) => {
// si un conjunto de fuentes ocurre antes de un `loadstart` allí
// no hay nada que hacer como `handleTechSourceset_`
// se volverá a llamar y esto se manejará allí.
if (e.type === 'conjunto de fuentes') {
devolver;
}
const techSrc = this.techGet('currentSrc');
this.lastSource_.tech = techSrc;
this.updateSourceCaches_(techSrc);
});
}
}
this.lastSource_ = {jugador: this.currentSource().src, tech: event.src};
este.disparador({
src: evento.src,
tipo: 'conjunto de fuentes'
});
}
/**
* Agregar/eliminar la clase vjs-has-started
*
* @fires jugador #firstplay
*
* solicitud @param {booleano}
* - verdadero: agrega la clase
* - falso: eliminar la clase
*
* @return {booleano}
* el valor booleano de hasStarted_
* /
ha comenzado (solicitud) {
si (solicitud === indefinido) {
// actúa como captador, si no tenemos ninguna solicitud de cambio
return this.hasStarted_;
}
if (solicitud === this.hasStarted_) {
devolver;
}
this.hasStarted_ = solicitud;
if (esto.haComenzado_) {
this.addClass('vjs-ha-comenzado');
this.trigger('primera reproducción');
} else {
this.removeClass('vjs-ha-comenzado');
}
}
/**
* Activado cada vez que el medio comienza o reanuda la reproducción
*
* @ver [Especificación]{@enlace https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
* @fires Jugador#jugar
* @escucha Tech#play
* @privado
* /
handleTechPlay_() {
this.removeClass('vjs-terminado');
this.removeClass('vjs-paused');
this.addClass('vjs-jugando');
// oculta el cartel cuando el usuario presiona reproducir
this.hasStarted(verdadero);
/**
* Se activa cada vez que ocurre un evento {@link Tech#play}. Indica que
* la reproducción ha comenzado o se ha reanudado.
*
* @reproductor de eventos#reproducir
* @type {Objetivo del evento~Evento}
* /
this.trigger('reproducir');
}
/**
* Vuelva a activar el evento `ratechange` que fue activado por {@link Tech}.
*
* Si había algún evento en cola mientras la velocidad de reproducción era cero, disparar
* esos eventos ahora.
*
* @privado
* @método Player#handleTechRateChange_
* @fires Jugador#cambio de tasa
* @escucha Tech#ratechange
* /
handleTechRateChange_() {
if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
this.queuedCallbacks_.forEach((en cola) => en cola.devolución de llamada(en cola.evento));
this.queuedCallbacks_ = [];
}
this.cache_.lastPlaybackRate = this.tech_.playbackRate();
/**
* Se dispara cuando se cambia la velocidad de reproducción del audio/video
*
* @event Player#ratechange
* @tipo {evento}
* /
this.trigger('cambio de tasa');
}
/**
* Vuelva a activar el evento "en espera" que activó {@link Tech}.
*
* @fires Player#esperando
* @escucha Tech#esperando
* @privado
* /
handleTechWaiting_() {
this.addClass('vjs-esperando');
/**
* Un cambio de readyState en el elemento DOM ha provocado que se detenga la reproducción.
*
* @reproductor de eventos#esperando
* @type {Objetivo del evento~Evento}
* /
this.trigger('esperando');
// Los navegadores pueden emitir un evento de actualización de tiempo después de un evento de espera. A fin de evitar
// eliminación prematura de la clase de espera, esperar a que cambie el tiempo.
const timeWhenWaiting = this.currentTime();
const timeUpdateListener = () => {
if (timeWhenWaiting !== this.currentTime()) {
this.removeClass('vjs-esperando');
this.off('actualización de hora', escucha de actualización de hora);
}
};
this.on('actualización de hora', escucha de actualización de hora);
}
/**
* Vuelva a activar el evento `canplay` que fue activado por {@link Tech}.
* > Nota: Esto no es consistente entre navegadores. Ver #1351
*
* @fires Jugador#puedejugar
* @escucha Tech#canplay
* @privado
* /
handleTechCanPlay_() {
this.removeClass('vjs-esperando');
/**
* El medio tiene un estado listo de HAVE_FUTURE_DATA o superior.
*
* @event Jugador#puedejugar
* @type {Objetivo del evento~Evento}
* /
this.trigger('puede jugar');
}
/**
* Vuelva a activar el evento `canplaythrough` que fue activado por {@link Tech}.
*
* @fires Player#canplaythrough
* @escucha Tech#canplaythrough
* @privado
* /
handleTechCanPlayThrough_() {
this.removeClass('vjs-esperando');
/**
* El medio tiene un estado listo de TENER_ENOUGH_DATA o superior. Esto significa que el
* El archivo multimedia completo se puede reproducir sin almacenamiento en búfer.
*
* @event Player#canplaythrough
* @type {Objetivo del evento~Evento}
* /
this.trigger('puedejugar');
}
/**
* Vuelva a activar el evento `reproduciendo` que fue activado por {@link Tech}.
*
* @fires Jugador#jugando
* @escucha Tech#playing
* @privado
* /
manejarTecnologíaJugando_() {
this.removeClass('vjs-esperando');
/**
* El medio ya no está bloqueado para la reproducción y ha comenzado a reproducirse.
*
* @event Jugador#jugando
* @type {Objetivo del evento~Evento}
* /
this.trigger('jugando');
}
/**
* Vuelva a activar el evento `buscando` que fue activado por {@link Tech}.
*
* @fires Jugador#buscando
* @escucha Tecnología#buscando
* @privado
* /
handleTechSeeking_() {
this.addClass('búsqueda de vjs');
/**
* Se dispara cada vez que el jugador salta a un nuevo tiempo
*
* @evento Jugador#buscando
* @type {Objetivo del evento~Evento}
* /
this.trigger('buscando');
}
/**
* Vuelva a activar el evento `buscado` que fue activado por {@link Tech}.
*
* @fires Player#buscado
* @escucha Tech#buscado
* @privado
* /
handleTechSeeked_() {
this.removeClass('búsqueda de vjs');
this.removeClass('vjs-terminado');
/**
* Se dispara cuando el jugador ha terminado de saltar a un nuevo tiempo
*
* Jugador de @event#buscado
* @type {Objetivo del evento~Evento}
* /
this.trigger('buscado');
}
/**
* Vuelva a activar el evento `firstplay` que fue activado por {@link Tech}.
*
* @fires jugador #firstplay
* @escucha Tech#firstplay
* @deprecated A partir de 6.0, el evento firstplay está obsoleto.
* A partir de la versión 6.0, pasar la opción `starttime` al jugador y el evento firstplay quedan obsoletos.
* @privado
* /
handleTechFirstPlay_() {
// Si se especifica el primer atributo de hora de inicio
// luego comenzaremos en el desplazamiento dado en segundos
if (esta.opciones_.hora de inicio) {
log.warn('Pasar la opción `starttime` al jugador quedará obsoleto en 6.0');
this.currentTime(this.options_.starttime);
}
this.addClass('vjs-ha-comenzado');
/**
* Se activa la primera vez que se reproduce un video. No es parte de la especificación HLS, y esto es
* Probablemente no sea la mejor implementación todavía, así que utilícelo con moderación. Si no tienes un
* motivo para evitar la reproducción, use `myPlayer.one('play');` en su lugar.
*
* @reproductor del evento#firstplay
* @deprecated A partir de 6.0, el evento firstplay está obsoleto.
* @type {Objetivo del evento~Evento}
* /
this.trigger('primera reproducción');
}
/**
* Vuelva a activar el evento de "pausa" que activó {@link Tech}.
*
* @fires jugador #pausa
* @escucha Tech#pausa
* @privado
* /
manejarPausaTecnológica_() {
this.removeClass('vjs-jugando');
this.addClass('vjs-paused');
/**
* Disparado cada vez que los medios han sido pausados
*
* @reproductor de eventos#pausa
* @type {Objetivo del evento~Evento}
* /
this.trigger('pausa');
}
/**
* Vuelva a activar el evento `finalizado` que activó {@link Tech}.
*
* @fires Player#finalizado
* @escucha Tech#finalizado
* @privado
* /
handleTechEnded_() {
this.addClass('vjs-terminado');
this.removeClass('vjs-esperando');
if (esta.opciones_.bucle) {
this.currentTime(0);
este juego();
} else if (!this.paused()) {
esto.pausa();
}
/**
* Activado cuando se alcanza el final del recurso de medios (currentTime == duración)
*
* @event Player#finalizado
* @type {Objetivo del evento~Evento}
* /
this.trigger('terminado');
}
/**
* Activado cuando la duración del recurso de medios se conoce o cambia por primera vez
*
* @escucha Tech#durationchange
* @privado
* /
handleTechDurationChange_() {
this.duration(this.techGet_('duration'));
}
/**
* Manejar un clic en el elemento multimedia para reproducir/pausar
*
* @param {EventTarget~Evento} evento
* el evento que provocó que esta función se activara
*
* @escucha Tech#click
* @privado
* /
handleTechClick_(evento) {
// Cuando los controles están deshabilitados, un clic no debería alternar la reproducción porque
// el clic se considera un control
if (!this.controls_) {
devolver;
}
si (
this.options_ === indefinido ||
this.options_.userActions === indefinido ||
this.options_.userActions.click === indefinido ||
this.options_.userActions.click !== false
) {
si (
this.options_ !== undefined &&
this.options_.userActions !== undefined &&
typeof this.options_.userActions.click === 'función'
) {
this.options_.userActions.click.call(este, evento);
} else if (esto.en pausa()) {
silencioPromise(this.play());
} else {
esto.pausa();
}
}
}
/**
* Haz doble clic en el elemento multimedia para entrar/salir de la pantalla completa
*
* @param {EventTarget~Evento} evento
* el evento que provocó que esta función se activara
*
* @escucha Tech#dblclick
* @privado
* /
handleTechDoubleClick_(evento) {
if (!this.controls_) {
devolver;
}
// no queremos alternar el estado de pantalla completa
// al hacer doble clic dentro de una barra de control o un modal
const inAllowedEls = Array.prototype.some.call(
this.$$('.vjs-control-bar, .vjs-modal-dialog'),
el => el.contains(evento.objetivo)
);
if (!inAllowedEls) {
/*
* options.userActions.doubleClick
*
* Si es `indefinido` o `verdadero`, hacer doble clic cambia a pantalla completa si los controles están presentes
* Establecer en `falso` para deshabilitar el manejo de doble clic
* Establecido en una función para sustituir un controlador de doble clic externo
* /
si (
this.options_ === indefinido ||
this.options_.userActions === indefinido ||
this.options_.userActions.doubleClick === indefinido ||
this.options_.userActions.doubleClick !== false
) {
si (
this.options_ !== undefined &&
this.options_.userActions !== undefined &&
typeof this.options_.userActions.doubleClick === 'función'
) {
this.options_.userActions.doubleClick.call(este, evento);
} más si (this.isFullscreen()) {
this.exitFullscreen();
} else {
this.requestFullscreen();
}
}
}
}
/**
* Manejar un toque en el elemento multimedia. Cambiará al usuario
* estado de actividad, que oculta y muestra los controles.
*
* @escucha Tech#tap
* @privado
* /
handleTechTap_() {
este.usuarioActivo(!este.usuarioActivo());
}
/**
* Manija táctil para comenzar
*
* @escucha Tech#touchstart
* @privado
* /
manejarTechTouchStart_() {
this.userWasActive = this.userActive();
}
/**
* Manija táctil para mover
*
* @escucha Tech#touchmove
* @privado
* /
handleTechTouchMove_() {
if (este.usuarioEstabaActivo) {
this.reportUserActivity();
}
}
/**
* Manejar toque para terminar
*
* @param {EventTarget~Evento} evento
* el evento touchend que activó
* esta función
*
* @escucha Tech#touchend
* @privado
* /
handleTechTouchEnd_(evento) {
// Impide que los eventos del mouse también sucedan
if (evento.cancelable) {
event.preventDefault();
}
}
/**
* Los eventos de clics nativos en SWF no se activan en IE11, Win8.1RT
* use eventos de clic de etapa activados desde dentro del SWF en su lugar
*
* @privado
* @escucha stageclick
* /
handleStageClick_() {
this.reportUserActivity();
}
/**
* @privado
* /
alternar Clase de pantalla completa_ () {
si (this.isFullscreen()) {
this.addClass('vjs-pantalla completa');
} else {
this.removeClass('vjs-pantalla completa');
}
}
/**
* cuando se desencadena el evento fschange del documento, llama a esto
* /
documentFullscreenChange_(e) {
const targetPlayer = e.target.player;
// si otro jugador estaba en pantalla completa
// hacer una verificación nula para targetPlayer porque los antiguos firefox pondrían el documento como e.target
si (jugador objetivo && ¡jugador objetivo! == esto) {
devolver;
}
const el = esto.el();
let isFs = document[this.fsApi_.fullscreenElement] === el;
si (!esFs && el.coincidencias) {
isFs = el.matches(':' + this.fsApi_.fullscreen);
} más si (!isFs && el.msMatchesSelector) {
isFs = el.msMatchesSelector(':' + this.fsApi_.fullscreen);
}
this.isFullscreen(isFs);
}
/**
* Manejar el cambio de pantalla completa de Tech
*
* @param {EventTarget~Evento} evento
* el evento de cambio de pantalla completa que activó esta función
*
* @param {Objeto} datos
* los datos que se enviaron con el evento
*
* @privado
* @escucha Tech#fullscreenchange
* @fires Player#cambio de pantalla completa
* /
handleTechFullscreenChange_(evento, datos) {
si (datos) {
si (data.nativeIOSFullscreen) {
this.addClass('vjs-ios-native-fs');
this.tech_.one('webkitendfullscreen', () => {
this.removeClass('vjs-ios-native-fs');
});
}
this.isFullscreen(datos.isFullscreen);
}
}
handleTechFullscreenError_(evento, error) {
this.trigger('error de pantalla completa', err);
}
/**
* @privado
* /
alternarImagenEnClaseDeImagen_() {
if (esto.esEnImagenEnImagen()) {
this.addClass('vjs-imagen-en-imagen');
} else {
this.removeClass('vjs-imagen-en-imagen');
}
}
/**
* Handle Tech Ingrese Picture-in-Picture.
*
* @param {EventTarget~Evento} evento
* el evento enterpictureinpicture que activó esta función
*
* @privado
* @escucha Tech#enterpictureinpicture
* /
handleTechEnterPictureInPicture_(evento) {
esto.isInPictureInPicture(verdadero);
}
/**
* Manejar Tech Leave Picture-in-Picture.
*
* @param {EventTarget~Evento} evento
* el evento LeavePictureInPicture que activó esta función
*
* @privado
* @escucha Tech#dejarimagenenimagen
* /
handleTechLeavePictureInPicture_(evento) {
esto.isInPictureInPicture(falso);
}
/**
* Se dispara cuando ocurre un error durante la carga de un audio/video.
*
* @privado
* @escucha Tech#error
* /
handleTechError_() {
const error = this.tech_.error();
este.error(error);
}
/**
* Vuelva a activar el evento `textdata` que fue activado por {@link Tech}.
*
* @fires Player#textdata
* @escucha Tech#textdata
* @privado
* /
handleTechTextData_() {
dejar datos = nulo;
if (argumentos.longitud > 1) {
datos = argumentos[1];
}
/**
* Se dispara cuando recibimos un evento de datos de texto de tecnología
*
* @reproductor de eventos#datos de texto
* @type {Objetivo del evento~Evento}
* /
this.trigger('datos de texto', datos);
}
/**
* Obtener objeto para valores almacenados en caché.
*
* @return {Objeto}
* obtener el caché de objetos actual
* /
obtenerCaché() {
devolver esto.cache_;
}
/**
* Restablece el objeto de caché interno.
*
* El uso de esta función fuera del constructor del jugador o el método de reinicio puede
* tener efectos secundarios no deseados.
*
* @privado
* /
resetCache_() {
esto.cache_ = {
// En este momento, el tiempo actual no está _realmente_ en caché porque siempre está
// recuperado de la tecnología (ver: hora actual). Sin embargo, para completar,
// lo establecemos en cero aquí para asegurarnos de que si comenzamos a almacenar en caché
// lo reiniciamos junto con todo lo demás.
tiempo actual: 0,
tiempo de inicio: 0,
inactividadTiempo de espera: esto.opciones_.inactividadTiempo de espera,
duración: Yaya,
último volumen: 1,
lastPlaybackRate: this.defaultPlaybackRate(),
medios: nulo,
origen: '',
fuente: {},
fuentes: [],
tasas de reproducción: [],
volumen: 1
};
}
/**
* Pasar valores a la tecnología de reproducción
*
* @param {cadena} [método]
* el método para llamar
*
* @param {Objeto} argumento
* el argumento para pasar
*
* @privado
* /
techCall_(método, arg) {
// Si aún no está listo, llame al método cuando lo esté
esto.listo(función() {
if (método en middleware.allowedSetters) {
return middleware.set(this.middleware_, this.tech_, method, arg);
} else if (method in middleware.allowedMediators) {
return middleware.mediate(this.middleware_, this.tech_, method, arg);
}
intentar {
si (esta.tecnología_) {
this.tech_[método](arg);
}
} catch (e) {
registro (e);
tirar e;
}
}, true);
}
/**
* Recibir llamadas no puede esperar a la tecnología y, a veces, no es necesario.
*
* Método @param {cadena}
* Método tecnológico
*
* @return {Función|indefinido}
* el método o indefinido
*
* @privado
* /
techGet_(método) {
if (!esta.tecnología_ || !esta.tecnología_.estáListo_) {
devolver;
}
if (método en middleware.allowedGetters) {
return middleware.get(this.middleware_, this.tech_, method);
} else if (method in middleware.allowedMediators) {
return middleware.mediate(this.middleware_, this.tech_, method);
}
// A Flash le gusta morir y recargar cuando lo ocultas o lo cambias de posición.
// En estos casos, los métodos del objeto desaparecen y obtenemos errores.
// HACER: ¿Es necesario para otras tecnologías además de Flash?
// Cuando eso suceda, detectaremos los errores e informaremos al equipo técnico que ya no está listo.
intentar {
devuelve this.tech_[método]();
} catch (e) {
// Al crear bibliotecas tecnológicas adicionales, es posible que aún no se haya definido un método esperado
if (this.tech_[método] === indefinido) {
log(`Video.js: método ${method} no definido para la tecnología de reproducción ${this.techName_}.`, e);
tirar e;
}
// Cuando un método no está disponible en el objeto, arroja un TypeError
if (e.nombre === 'TypeError') {
log(`Video.js: ${método} no disponible en el elemento de tecnología de reproducción ${this.techName_}.`, e);
this.tech_.isReady_ = falso;
tirar e;
}
// Si el error es desconocido, simplemente inicie sesión y lance
registro (e);
tirar e;
}
}
/**
* Intente iniciar la reproducción en la primera oportunidad.
*
* @return {Promesa|indefinido}
* Devuelve una promesa si el navegador admite Promises (o una
* se pasó como una opción). Esta promesa se resolverá el
* el valor de retorno del juego. Si esto no está definido, cumplirá con el
* cadena de promesa de lo contrario, la cadena de promesa se cumplirá cuando
* se cumple la promesa del juego.
* /
jugar() {
const PromiseClass = this.options_.Promise || ventana.Promesa;
si (PromesaClase) {
devolver nueva PromiseClass((resolver) => {
this.play_(resolver);
});
}
devolver esto.play_();
}
/**
* La lógica real para jugar, toma una devolución de llamada que se resolverá en el
* valor de retorno del juego. Esto nos permite resolver la promesa de juego si hay
* es uno en los navegadores modernos.
*
* @privado
* @param {Función} [devolución de llamada]
* La devolución de llamada que debe llamarse cuando los técnicos juegan realmente se llama
* /
play_(devolución de llamada = promesa de silencio) {
this.playCallbacks_.push(devolución de llamada);
const isSrcReady = Boolean(!this. ChangingSrc_ && (este.src() || este.currentSrc()));
// trata las llamadas a play_ algo así como la función de evento `one`
si (esto.waitToPlay_) {
this.off(['ready', 'loadstart'], this.waitToPlay_);
this.waitToPlay_ = null;
}
// si el jugador/tecnología no está listo o el src mismo no está listo
// poner en cola una llamada para jugar en `ready` o `loadstart`
if (!this.isReady_ || !isSrcReady) {
this.waitToPlay_ = (e) => {
este juego_();
};
this.one(['ready', 'loadstart'], this.waitToPlay_);
// si estamos en Safari, existe una gran posibilidad de que loadstart se active después del período de tiempo del gesto
// en ese caso, necesitamos preparar el elemento de video llamando a cargar para que esté listo a tiempo
si (! isSrcReady && (navegador.IS_ANY_SAFARI || navegador.IS_IOS)) {
esto.load();
}
devolver;
}
// Si el reproductor/técnico está listo y tenemos una fuente, podemos intentar la reproducción.
const val = this.techGet_('jugar');
// la reproducción finalizó si el valor devuelto es nulo
si (val === nulo) {
this.runPlayTerpressedQueue_();
} else {
this.runPlayCallbacks_(val);
}
}
/**
* Estas funciones se ejecutarán cuando finalice el juego. si jugar
* runPlayCallbacks_ se ejecuta, esta función no se ejecutará. Esto nos permite
* para diferenciar entre una jugada terminada y una llamada a jugar real.
* /
ejecutarReproducirColaTerminada_() {
const cola = this.playTerpressedQueue_.slice(0);
this.playTerpressedQueue_ = [];
cola.paraCada(función(q) {
q();
});
}
/**
* Cuando se retrasa una devolución de llamada para jugar, tenemos que ejecutar estos
* devoluciones de llamada cuando el juego se llama realmente en la tecnología. Esta función
* ejecuta las devoluciones de llamada que se retrasaron y acepta el valor devuelto
* de la tecnología.
*
* @param {indefinido|Promesa} val
* El valor de retorno de la tecnología.
* /
ejecutarReproducirCallbacks_(val) {
const devoluciones de llamada = this.playCallbacks_.slice(0);
this.playCallbacks_ = [];
// Limpiar la cola finalizada del juego ya que terminamos un juego real.
this.playTerpressedQueue_ = [];
devoluciones de llamada.forEach(función(cb) {
cb(valor);
});
}
/**
* Pausa la reproducción del video
*
* @return {Jugador}
* Una referencia al objeto del jugador en el que se invocó esta función
* /
pausa() {
this.techCall_('pausa');
}
/**
* Compruebe si el reproductor está en pausa o aún no ha jugado
*
* @return {booleano}
* - falso: si el medio se está reproduciendo actualmente
* - verdadero: si los medios no se están reproduciendo actualmente
* /
en pausa() {
// El estado inicial de pausa debe ser verdadero (en Safari es falso)
return (this.techGet_('paused') === false) ? falso verdadero;
}
/**
* Obtenga un objeto TimeRange que represente los rangos de tiempo actuales que el usuario
* ha jugado.
*
* @return {Intervalo de tiempo}
* Un objeto de rango de tiempo que representa todos los incrementos de tiempo que han
* se ha jugado.
* /
jugó() {
devuelve esto.techGet_('reproducido') || crear rango de tiempo (0, 0);
}
/**
* Devuelve si el usuario está "fregando" o no. fregar es
* cuando el usuario ha hecho clic en el controlador de la barra de progreso y está
* arrastrándolo a lo largo de la barra de progreso.
*
* @param {booleano} [es fregar]
* si el usuario está o no está fregando
*
* @return {booleano}
* El valor de fregar al obtener
* /
fregando(esFregando) {
if (typeof isScrubbing === 'indefinido') {
devolver esto.fregar_;
}
this.scrubbing_ = !!isScrubbing;
this.techCall_('setScrubbing', this.scrubbing_);
si (está fregando) {
this.addClass('vjs-fregado');
} else {
this.removeClass('vjs-scrubbing');
}
}
/**
* Obtener o establecer la hora actual (en segundos)
*
* @param {número|cadena} [segundos]
* El tiempo para buscar en segundos
*
* @return {número}
* - la hora actual en segundos al obtener
* /
horaActual(segundos) {
if (tipo de segundos !== 'indefinido') {
si (segundos < 0) {
segundos = 0;
}
if (!this.isReady_ || this. ChangingSrc_ || !this.tech_ || !this.tech_.isReady_) {
this.cache_.initTime = segundos;
this.off('canplay', this.boundApplyInitTime_);
this.one('canplay', this.boundApplyInitTime_);
devolver;
}
this.techCall_('setCurrentTime', segundos);
this.cache_.initTime = 0;
devolver;
}
// almacena en caché la última hora actual y regresa. por defecto a 0 segundos
//
// El almacenamiento en caché de currentTime está destinado a evitar una gran cantidad de lecturas en la tecnología
// horaActual al realizar la limpieza, pero puede que no proporcione muchos beneficios de rendimiento después de todo.
// Debe ser probado. También algo tiene que leer la hora actual real o el caché
// nunca se actualiza.
this.cache_.currentTime = (this.techGet_('currentTime') || 0);
devuelve this.cache_.currentTime;
}
/**
* Aplicar el valor de initTime almacenado en caché como currentTime.
*
* @privado
* /
aplicarTiempoInicio_() {
this.currentTime(this.cache_.initTime);
}
/**
* Normalmente obtiene la duración del video en segundos;
* en todos los casos de uso, excepto en los más raros, NO se pasará un argumento al método
*
* > **NOTA**: El video debe haber comenzado a cargarse antes de que se pueda determinar la duración.
* conocido y, según el comportamiento de precarga, es posible que no se conozca hasta que comience el video
* jugando.
*
* @fires Player#cambio de duración
*
* @param {número} [segundos]
* La duración del video a configurar en segundos
*
* @return {número}
* - La duración del video en segundos al obtener
* /
duración (segundos) {
si (segundos === indefinido) {
// devuelve NaN si no se conoce la duración
devolver this.cache_.duration !== indefinido ? this.cache_.duration : Yaya;
}
segundos = parseFloat(segundos);
// Estandarizar en Infinity para señalar que el video está en vivo
si (segundos < 0) {
segundos = infinito;
}
if (segundos! == this.cache_.duration) {
// Almacenar en caché el último valor establecido para una limpieza optimizada (esp. Destello)
// HACER: ¿Requerido para tecnologías que no sean Flash?
this.cache_.duration = segundos;
si (segundos === infinito) {
this.addClass('vjs-live');
} else {
this.removeClass('vjs-live');
}
if (!isNaN(segundos)) {
// No active el cambio de duración a menos que se conozca el valor de duración.
// @ver [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
/**
* @event Player#cambio de duración
* @type {Objetivo del evento~Evento}
* /
this.trigger('cambio de duración');
}
}
}
/**
* Calcula cuánto tiempo queda en el video. no parte
* de la API de video nativa.
*
* @return {número}
* El tiempo restante en segundos
* /
tiempo restante() {
devuelve this.duration() - this.currentTime();
}
/**
* Una función de tiempo restante que está destinada a ser utilizada cuando
* el tiempo debe mostrarse directamente al usuario.
*
* @return {número}
* El tiempo restante redondeado en segundos
* /
visualización de tiempo restante () {
return Math.floor(this.duration()) - Math.floor(this.currentTime());
}
//
// Algo así como una matriz de partes del video que se han descargado.
/**
* Obtenga un objeto TimeRange con una matriz de los tiempos del video
* que han sido descargados. Si sólo desea el porcentaje de la
* video que ha sido descargado, use bufferedPercent.
*
* @ver [Especificación en búfer]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
*
* @return {Intervalo de tiempo}
* Un objeto TimeRange simulado (siguiendo las especificaciones de HTML)
* /
almacenado en búfer () {
let buffered = this.techGet_('buffered');
if (!buffered || !buffered.length) {
almacenado en búfer = createTimeRange(0, 0);
}
retorno amortiguado;
}
/**
* Obtenga el porcentaje (como decimal) del video que se descargó.
* Este método no forma parte de la API de video HTML nativo.
*
* @return {número}
* Un decimal entre 0 y 1 que representa el porcentaje
* que está almacenado en búfer 0 siendo 0% y 1 siendo 100%
* /
porcentajebuffered() {
return bufferedPercent(this.buffered(), this.duration());
}
/**
* Obtenga la hora de finalización del último rango de tiempo almacenado en búfer
* Esto se usa en la barra de progreso para encapsular todos los rangos de tiempo.
*
* @return {número}
* El final del último rango de tiempo almacenado en el búfer
* /
bufferedEnd() {
const buffered = this.buffered();
const duracion = this.duration();
let end = buffered.end(buffered.length - 1);
si (fin > duración) {
fin = duración;
}
fin de retorno;
}
/**
* Obtener o establecer el volumen actual de los medios
*
* @param {número} [porcentaje como decimal]
* El nuevo volumen como porcentaje decimal:
* - 0 está silenciado/0%/apagado
* - 1.0 es 100%/lleno
* - 0.5 es la mitad del volumen o 50%
*
* @return {número}
* El volumen actual como porcentaje al obtener
* /
volumen (porcentaje como decimal) {
vamos vol;
if (porcentaje como decimal !== indefinido) {
// Forzar valor entre 0 y 1
vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
this.cache_.volume = vol;
this.techCall_('setVolume', vol);
si (vol. > 0) {
this.lastVolume_(vol);
}
devolver;
}
// Predeterminado a 1 al devolver el volumen actual.
vol = parseFloat(this.techGet_('volumen'));
volver (isNaN(vol)) ? 1 : volumen;
}
/**
* Obtenga el estado silenciado actual, o active o desactive el silencio
*
* @param {booleano} [silenciado]
* - verdadero para silenciar
* - falso para desactivar el silencio
*
* @return {booleano}
* - verdadero si el silencio está activado y se está activando
* - falso si el silencio está desactivado y se activa
* /
silenciado (silenciado) {
si (silenciado! == indefinido) {
this.techCall_('setMuted', silenciado);
devolver;
}
devuelve esto.techGet_('silenciado') || FALSO;
}
/**
* Obtener el estado de silenciado predeterminado actual, o activar o desactivar el silenciado predeterminado. por defecto silenciado
* indica el estado de silencio en la reproducción inicial.
*
* ```js
* var myPlayer = videojs('algún-id-del-jugador');
*
* myPlayer.src("http://www.example.com/path/to/video.mp4");
*
* // obtener, debe ser falso
* console.log(myPlayer.defaultMuted());
* // establecido en verdadero
* myPlayer.defaultMuted(true);
* // obtener debe ser verdadero
* console.log(myPlayer.defaultMuted());
* ```
*
* @param {booleano} [predeterminado silenciado]
* - verdadero para silenciar
* - falso para desactivar el silencio
*
* @return {booleano|Jugador}
* - verdadero si defaultMuted está activado y obteniendo
* - falso si defaultMuted está desactivado y obteniendo
* - Una referencia al reproductor actual al configurar
* /
predeterminado silenciado (predeterminado silenciado) {
if (predeterminado silenciado !== indefinido) {
devuelve this.techCall_('setDefaultMuted', defaultMuted);
}
devuelve esto.techGet_('defaultMuted') || FALSO;
}
/**
* Obtenga el último volumen, o configúrelo
*
* @param {número} [porcentaje como decimal]
* El nuevo último volumen como porcentaje decimal:
* - 0 está silenciado/0%/apagado
* - 1.0 es 100%/lleno
* - 0.5 es la mitad del volumen o 50%
*
* @return {número}
* el valor actual de lastVolume como porcentaje al obtener
*
* @privado
* /
lastVolume_(percentAsDecimal) {
if (porcentaje como decimal !== indefinido && porcentaje como decimal !== 0) {
this.cache_.lastVolume = percentAsDecimal;
devolver;
}
devuelve this.cache_.lastVolume;
}
/**
* Comprobar si la tecnología actual admite pantalla completa nativa
* (por ejemplo, con controles integrados como iOS)
*
* @return {booleano}
* si se admite la pantalla completa nativa
* /
admite pantalla completa () {
devuelve esto.techGet_('supportsFullScreen') || FALSO;
}
/**
* Compruebe si el jugador está en modo de pantalla completa o dígale al jugador que
* está o no está en modo de pantalla completa.
*
* > NOTA: A partir de la última especificación de HTML5, isFullscreen ya no es oficial
* propiedad y en su lugar se utiliza document.fullscreenElement. Pero isFullscreen es
* sigue siendo una propiedad valiosa para el funcionamiento interno del jugador.
*
* @param {booleano} [isFS]
* Establecer el estado actual de pantalla completa de los jugadores
*
* @return {booleano}
* - verdadero si la pantalla completa está activada y obteniendo
* - falso si la pantalla completa está desactivada y se está poniendo
* /
esPantalla completa(isFS) {
if (isFS !== indefinido) {
const oldValue = this.isFullscreen_;
this.isFullscreen_ = Boolean(isFS);
// si cambiamos el estado de pantalla completa y estamos en modo prefijado, active el cambio de pantalla completa
// este es el único lugar donde activamos eventos de cambio de pantalla completa para navegadores más antiguos
// el modo de ventana completa se trata como un evento con prefijo y también obtendrá un evento de cambio de pantalla completa
if (this.isFullscreen_ !== valorAntiguo && this.fsApi_.prefijado) {
/**
* @reproductor de eventos#cambio de pantalla completa
* @type {Objetivo del evento~Evento}
* /
this.trigger('cambio de pantalla completa');
}
this.toggleFullscreenClass_();
devolver;
}
devuelve esto.isFullscreen_;
}
/**
* Aumentar el tamaño del video a pantalla completa
* En algunos navegadores, la pantalla completa no es compatible de forma nativa, por lo que ingresa
* "modo de ventana completa", donde el video llena la ventana del navegador.
* En navegadores y dispositivos que admiten pantalla completa nativa, a veces el
* Se mostrarán los controles predeterminados del navegador y no el aspecto personalizado de Video.js.
* Esto incluye la mayoría de los dispositivos móviles (iOS, Android) y versiones anteriores de
* Safari.
*
* @param {Objeto} [opciones de pantalla completa]
* Anular las opciones de pantalla completa del jugador
*
* @fires Player#cambio de pantalla completa
* /
solicitud de pantalla completa (opciones de pantalla completa) {
const PromiseClass = this.options_.Promise || ventana.Promesa;
si (PromesaClase) {
const self = esto;
return new PromiseClass((resolver, rechazar) => {
función offHandler() {
self.off('error de pantalla completa', manejador de errores);
self.off('cambio de pantalla completa', controlador de cambios);
}
función changeHandler() {
offHandler();
resolver();
}
función manejador de errores (e, err) {
offHandler();
rechazar (error);
}
self.one('cambio de pantalla completa', changeHandler);
self.one('error de pantalla completa', manejador de errores);
const promesa = self.requestFullscreenHelper_(fullscreenOptions);
si (promesa) {
promesa.entonces(offHandler, offHandler);
prometer.entonces(resolver, rechazar);
}
});
}
devolver esto.requestFullscreenHelper_();
}
requestFullscreenHelper_(opciones de pantalla completa) {
dejar fsOptions;
// Solo pase las opciones de pantalla completa a requestFullscreen en navegadores que cumplan con las especificaciones.
// Use la opción predeterminada o configurada por el jugador a menos que se pase directamente a este método.
if (!this.fsApi_.prefijado) {
fsOptions = this.options_.pantalla completa && this.options_.fullscreen.options || {};
if (opciones de pantalla completa! == indefinido) {
fsOptions = opciones de pantalla completa;
}
}
// Este método funciona de la siguiente manera:
// 1. si hay una API de pantalla completa disponible, utilícela
// 1. solicitud de llamada Pantalla completa con opciones potenciales
// 2. si recibimos una promesa de arriba, utilícela para actualizar isFullscreen()
// 2. De lo contrario, si la tecnología admite pantalla completa, llame a `enterFullScreen`.
// Esto se usa particularmente para iPhone, iPads más antiguos y navegadores que no son safari en iOS.
// 3. de lo contrario, use el modo "ventana completa"
si (esto.fsApi_.requestFullscreen) {
const promesa = this.el_[this.fsApi_.requestFullscreen](fsOptions);
si (promesa) {
promesa.entonces(() => this.isFullscreen(verdadero), () => this.isFullscreen(falso));
}
promesa de devolución;
} else if (this.tech_.supportsFullScreen() && !this.options_.preferFullWindow === true) {
// no podemos tomar los controles de video.js a pantalla completa pero podemos ir a pantalla completa
// con controles nativos
this.techCall_('ingresar a pantalla completa');
} else {
// la pantalla completa no es compatible, por lo que estiraremos el elemento de video para
// llenar la ventana gráfica
this.enterFullWindow();
}
}
/**
* Devolver el video a su tamaño normal luego de haber estado en modo pantalla completa
*
* @fires Player#cambio de pantalla completa
* /
salir de pantalla completa() {
const PromiseClass = this.options_.Promise || ventana.Promesa;
si (PromesaClase) {
const self = esto;
return new PromiseClass((resolver, rechazar) => {
función offHandler() {
self.off('error de pantalla completa', manejador de errores);
self.off('cambio de pantalla completa', controlador de cambios);
}
función changeHandler() {
offHandler();
resolver();
}
función manejador de errores (e, err) {
offHandler();
rechazar (error);
}
self.one('cambio de pantalla completa', changeHandler);
self.one('error de pantalla completa', manejador de errores);
const promesa = self.exitFullscreenHelper_();
si (promesa) {
promesa.entonces(offHandler, offHandler);
// asigna la promesa a nuestros métodos de resolución/rechazo
prometer.entonces(resolver, rechazar);
}
});
}
devuelve esto.exitFullscreenHelper_();
}
exitFullscreenHelper_() {
si (esto.fsApi_.requestFullscreen) {
const promesa = documento[this.fsApi_.exitFullscreen]();
si (promesa) {
// estamos dividiendo la promesa aquí, así que queremos atrapar el
// error potencial para que esta cadena no tenga errores no controlados
silencioPromesa(promesa.entonces(() => this.isFullscreen(falso)));
}
promesa de devolución;
} else if (this.tech_.supportsFullScreen() && !this.options_.preferFullWindow === true) {
this.techCall_('salir de pantalla completa');
} else {
this.exitFullWindow();
}
}
/**
* Cuando no se admite la pantalla completa, podemos estirar la
* contenedor de video tan ancho como el navegador nos lo permita.
*
* @fires Player#enterFullWindow
* /
ingresarVentanaCompleta() {
this.isFullscreen(verdadero);
this.isFullWindow = verdadero;
// Almacenar el valor de desbordamiento del documento original para volver cuando la pantalla completa está desactivada
this.docOrigOverflow = document.documentElement.style.overflow;
// Agregar oyente para la tecla esc para salir de pantalla completa
Events.on(document, 'keydown', this.boundFullWindowOnEscKey_);
// Ocultar las barras de desplazamiento
document.documentElement.style.overflow = 'oculto';
// Aplicar estilos de pantalla completa
Dom.addClass(document.body, 'vjs-full-window');
/**
* @reproductor de eventos#enterFullWindow
* @type {Objetivo del evento~Evento}
* /
this.trigger('enterFullWindow');
}
/**
* Verifique la llamada para salir de la ventana completa o
* pantalla completa en la tecla ESC
*
* evento @param {cadena}
* Evento para verificar si se presiona una tecla
* /
fullWindowOnEscKey(evento) {
if (keycode.isEventKey(evento, 'Esc')) {
if (this.isFullscreen() === verdadero) {
si (!esta.esVentanaCompleta) {
this.exitFullscreen();
} else {
this.exitFullWindow();
}
}
}
}
/**
* Salir de la ventana completa
*
* @fires Player#exitFullWindow
* /
salirVentanaCompleta() {
this.isFullscreen(falso);
this.isFullWindow = falso;
Events.off(document, 'keydown', this.boundFullWindowOnEscKey_);
// Mostrar barras de desplazamiento.
document.documentElement.style.overflow = this.docOrigOverflow;
// Eliminar estilos de pantalla completa
Dom.removeClass(document.body, 'vjs-full-window');
// Cambiar el tamaño de la caja, el controlador y el póster a los tamaños originales
// this.positionAll();
/**
* @reproductor de eventos#exitFullWindow
* @type {Objetivo del evento~Evento}
* /
this.trigger('exitFullWindow');
}
/**
* Deshabilitar el modo de imagen en imagen.
*
* valor @param {booleano}
* - true desactivará el modo Picture-in-Picture
* - false habilitará el modo Picture-in-Picture
* /
deshabilitarImagenEnImagen(valor) {
si (valor === indefinido) {
devuelve esto.techGet_('disablePictureInPicture');
}
this.techCall_('setDisablePictureInPicture', valor);
this.options_.disablePictureInPicture = valor;
this.trigger('deshabilitar imagen en imagen cambiada');
}
/**
* Verifique si el reproductor está en modo Picture-in-Picture o dígale al reproductor que
* está o no está en el modo Picture-in-Picture.
*
* @param {booleano} [isPiP]
* Establecer el estado actual de Picture-in-Picture de los jugadores
*
* @return {booleano}
* - verdadero si Picture-in-Picture está activado y recibiendo
* - falso si Picture-in-Picture está desactivado y
* /
esEnImagenEnImagen(isPiP) {
if (esPiP !== indefinido) {
this.isInPictureInPicture_ = !!isPiP;
this.togglePictureInPictureClass_();
devolver;
}
volver !!this.isInPictureInPicture_;
}
/**
* Cree una ventana de video flotante siempre encima de otras ventanas para que los usuarios puedan
* continuar consumiendo medios mientras interactúan con otros sitios de contenido, o
* aplicaciones en su dispositivo.
*
* @ver [Especificación]{@enlace https://wicg.github.io/picture-in-picture}
*
* @fires Player#enterpictureinpicture
*
* @return {Promesa}
* Una promesa con una ventana Picture-in-Picture.
* /
solicitudImagenEnImagen() {
if ('imagenEnImagenHabilitada' en el documento && this.disablePictureInPicture() === false) {
/**
* Este evento se dispara cuando el jugador entra en el modo de imagen en imagen
*
* @reproductor de eventos#enterpictureinpicture
* @type {Objetivo del evento~Evento}
* /
devuelve this.techGet_('requestPictureInPicture');
}
}
/**
* Salga del modo Imagen en imagen.
*
* @ver [Especificación]{@enlace https://wicg.github.io/picture-in-picture}
*
* @fires Player#dejarimagenenimagen
*
* @return {Promesa}
* Una promesa.
* /
salirImagenEnImagen() {
if ('imagenEnImagenHabilitada' en el documento) {
/**
* Este evento se activa cuando el jugador deja la imagen en modo imagen
*
* @reproductor de eventos#dejarimagenenimagen
* @type {Objetivo del evento~Evento}
* /
volver documento.exitPictureInPicture();
}
}
/**
* Llamado cuando este jugador tiene el foco y se presiona una tecla, o cuando
* cualquier Componente de este reproductor recibe una pulsación de tecla que no maneja.
* Esto permite teclas de acceso rápido para todo el jugador (ya sea como se define a continuación, u opcionalmente
* por una función externa).
*
* @param {EventTarget~Evento} evento
* El evento `keydown` que hizo que se llamara a esta función.
*
* @escucha tecla abajo
* /
handleKeyDown(evento) {
const {userActions} = this.options_;
// Rescatar si las teclas de acceso rápido no están configuradas.
if (! acciones de usuario || ! acciones de usuario. teclas rápidas) {
devolver;
}
// Función que determina si excluir o no un elemento de
// manejo de hotkeys.
const excluirElemento = (el) => {
const tagName = el.tagName.toLowerCase();
// La primera y más sencilla prueba es para elementos `contenidos`.
si (el.isContentEditable) {
devolver verdadero;
}
// Las entradas que coincidan con estos tipos aún activarán el manejo de teclas rápidas como
// no son entradas de texto.
const tipos de entrada permitidos = [
'botón',
'caja',
'oculto',
'radio',
'reiniciar',
'entregar'
];
if (etiquetaNombre === 'entrada') {
return allowInputTypes.indexOf(el.type) === -1;
}
// La prueba final es por nombre de etiqueta. Estas etiquetas se excluirán por completo.
const etiquetasexcluidas = ['textarea'];
devuelve etiquetasexcluidas.indexOf(tagName) !== -1;
};
// Rescatar si el usuario se centra en un elemento de formulario interactivo.
if (excludeElement(this.el_.ownerDocument.activeElement)) {
devolver;
}
if (typeof userActions.hotkeys === 'función') {
userActions.hotkeys.call(este, evento);
} else {
this.handleHotkeys(evento);
}
}
/**
* Llamado cuando este reproductor recibe un evento de tecla de acceso directo.
* Las teclas de acceso rápido compatibles con todo el jugador son:
*
* f - alternar pantalla completa
* m - alternar silencio
* k o Espacio - alternar reproducción/pausa
*
* @param {EventTarget~Evento} evento
* El evento `keydown` que hizo que se llamara a esta función.
* /
handleHotkeys(evento) {
const hotkeys = this.options_.userActions ? this.options_.userActions.hotkeys: {};
// establece fullscreenKey, muteKey, playPauseKey desde `hotkeys`, usa los valores predeterminados si no está configurado
constante {
tecla de pantalla completa = keydownEvent => keycode.isEventKey(keydownEvent, 'f'),
muteKey = keydownEvent => keycode.isEventKey(keydownEvent, 'm'),
playPauseKey = keydownEvent => (keycode.isEventKey(keydownEvent, 'k') || keycode.isEventKey(keydownEvent, 'Space'))
} = teclas de acceso rápido;
if (fullscreenKey.call(este, evento)) {
event.preventDefault();
event.stopPropagation();
const FSToggle = Component.getComponent('FullscreenToggle');
if (document[this.fsApi_.fullscreenEnabled] !== false) {
FSToggle.prototype.handleClick.call(este, evento);
}
} else if (muteKey.call(este, evento)) {
event.preventDefault();
event.stopPropagation();
const MuteToggle = Component.getComponent('MuteToggle');
MuteToggle.prototype.handleClick.call(este, evento);
} else if (playPauseKey.call(this, event)) {
event.preventDefault();
event.stopPropagation();
const PlayToggle = Component.getComponent('PlayToggle');
PlayToggle.prototype.handleClick.call(este, evento);
}
}
/**
* Comprueba si el jugador puede jugar un tipo de mimo dado
*
* @ver https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
*
* @param {cadena} tipo
* El tipo MIME a comprobar
*
* @return {cadena}
* 'probablemente', 'tal vez' o '' (cadena vacía)
* /
canPlayType(tipo) {
dejar puede;
// Recorre cada tecnología de reproducción en el orden de las opciones
for (sea i = 0, j = this.options_.techOrder; i < j.longitud; i++) {
const techName = j[i];
let tech = Tech.getTech(techName);
// Admite el comportamiento anterior de los técnicos que se registran como componentes.
// Eliminar una vez que se elimine ese comportamiento obsoleto.
si (! tecnología) {
tech = Component.getComponent(techName);
}
// Comprobar si la tecnología actual está definida antes de continuar
si (! tecnología) {
log.error(`La tecnología "${techName}" no está definida. Se omitió la verificación de soporte del navegador para esa tecnología.`);
continuar;
}
// Comprobar si el navegador soporta esta tecnología
si (tech.isSupported()) {
can = tech.canPlayType(tipo);
si puede) {
lata de retorno;
}
}
}
devolver '';
}
/**
* Seleccione la fuente según el pedido tecnológico o el pedido de la fuente
* Utiliza la selección de orden de origen si `options.sourceOrder` es veraz. De lo contrario,
* por defecto a la selección de orden de tecnología
*
* @param {Array} fuentes
* Las fuentes de un recurso multimedia
*
* @return {Objeto|booleano}
* Objeto de origen y orden técnico o falso
* /
seleccionarFuente(fuentes) {
// Obtener solo las tecnologías especificadas en `techOrder` que existen y son compatibles con el
// plataforma actual
tecnicos constantes =
this.options_.techOrder
.map((nombreTecnológico) => {
return [NombreTecnología, Tech.getTech(NombreTecnología)];
})
.filter(([nombreTecnología, tecnología]) => {
// Comprobar si la tecnología actual está definida antes de continuar
si (tecnología) {
// Comprobar si el navegador soporta esta tecnología
volver tech.isSupported();
}
log.error(`La tecnología "${techName}" no está definida. Se omitió la verificación de soporte del navegador para esa tecnología.`);
falso retorno;
});
// Iterar sobre cada elemento `innerArray` una vez por elemento `outerArray` y ejecutar
// `probador` con ambos. Si `tester` devuelve un valor no falso, salga temprano y regrese
// ese valor.
const findFirstPassingTechSourcePair = function(outerArray, innerArray, tester) {
dejar encontrado;
matrizexterna.algunos((elecciónexterna) => {
return matrizInterior.algunos((ElecciónInterior) => {
encontrado = probador(elecciónexterna, eleccióninterna);
si se encuentra) {
devolver verdadero;
}
});
});
devolución encontrada;
};
let foundSourceAndTech;
volteo constante = (fn) => (a, b) => fn(b,a);
buscador constante = ([techName, tech], fuente) => {
if (tech.canPlaySource(fuente, this.options_[techName.toLowerCase()])) {
return {fuente, tecnología: techName};
}
};
// Dependiendo de la veracidad de `options.sourceOrder`, intercambiamos el orden de las tecnologías y las fuentes
// para seleccionar de ellos en función de su prioridad.
if (this.options_.sourceOrder) {
// Ordenar primero la fuente
foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
} else {
// Ordenar primero en tecnología
foundSourceAndTech = findFirstPassingTechSourcePair(tecnologías, fuentes, buscador);
}
volver encontradoSourceAndTech || FALSO;
}
/**
* Ejecuta la configuración de la fuente y obtiene la lógica
*
* @param {Tech~SourceObject|Tech~SourceObject[]|cadena} [fuente]
* Un SourceObject, una matriz de SourceObjects o una cadena de referencia
* una URL a una fuente de medios. Es _muy recomendable_ que un objeto
* o matriz de objetos se utiliza aquí, por lo que la selección de fuente
* los algoritmos pueden tener en cuenta el `tipo`.
*
* Si no se proporciona, este método actúa como captador.
* @param {booleano} esReintentar
* Indica si se está llamando internamente como resultado de un reintento
*
* @return {cadena|indefinido}
* Si falta el argumento `fuente`, devuelve la fuente actual
* URL. De lo contrario, no devuelve nada o indefinido.
* /
handleSrc_(fuente, esReintentar) {
// uso del captador
if (tipo de fuente === 'indefinido') {
devolver this.cache_.src || '';
}
// Restablece el comportamiento de reintento para la nueva fuente
si (esto.resetRetryOnError_) {
this.resetRetryOnError_();
}
// filtrar las fuentes inválidas y convertir nuestra fuente en
// una matriz de objetos de origen
const fuentes = filterSource(fuente);
// si se pasó una fuente, entonces no es válida porque
// se filtró a una matriz de longitud cero. entonces tenemos que
// mostrar un error
if (!fuentes.longitud) {
this.setTimeout(función() {
este.error({ código: 4, mensaje: this.options_.notSupportedMessage });
}, 0);
devolver;
}
// fuentes iniciales
este.cambiandoSrc_ = verdadero;
// Solo actualice la lista de fuentes en caché si no estamos volviendo a intentar una nueva fuente después de un error,
// ya que en ese caso queremos incluir las fuentes fallidas en el caché
si (! esReintentar) {
this.cache_.sources = fuentes;
}
this.updateSourceCaches_(fuentes[0]);
// middlewareSource es la fuente después de haber sido modificada por middleware
middleware.setSource(esto, fuentes[0], (middlewareSource, mws) => {
este.middleware_ = mws;
// dado que sourceSet es asíncrono, tenemos que actualizar el caché nuevamente después de seleccionar una fuente ya que
// la fuente que se selecciona podría estar fuera de servicio debido a la actualización de caché por encima de esta devolución de llamada.
si (! esReintentar) {
this.cache_.sources = fuentes;
}
this.updateSourceCaches_(middlewareSource);
const err = this.src_(middlewareSource);
si (err) {
if (fuentes.longitud > 1) {
devuelve this.handleSrc_(sources.slice(1));
}
este.cambiandoSrc_ = falso;
// Necesitamos envolver esto en un tiempo de espera para darle a la gente la oportunidad de agregar controladores de eventos de error
this.setTimeout(función() {
este.error({ código: 4, mensaje: this.options_.notSupportedMessage });
}, 0);
// no pudimos encontrar una tecnología apropiada, pero aún notifiquemos al delegado que esto es todo
// esto necesita un mejor comentario sobre por qué es necesario
this.triggerReady();
devolver;
}
middleware.setTech(mws, this.tech_);
});
// Pruebe con otra fuente disponible si esta falla antes de la reproducción.
if (this.options_.retryOnError && fuentes.longitud > 1) {
constante reintento = () => {
// Eliminar el modal de error
este.error(nulo);
this.handleSrc_(sources.slice(1), true);
};
const dejar de escuchar errores = () => {
this.off('error', reintentar);
};
this.one('error', reintentar);
this.one('playing', stopListeningForErrors);
this.resetRetryOnError_ = () => {
this.off('error', reintentar);
this.off('reproduciendo', dejar de escuchar errores);
};
}
}
/**
* Obtener o establecer la fuente de video.
*
* @param {Tech~SourceObject|Tech~SourceObject[]|cadena} [fuente]
* Un SourceObject, una matriz de SourceObjects o una cadena de referencia
* una URL a una fuente de medios. Es _muy recomendable_ que un objeto
* o matriz de objetos se utiliza aquí, por lo que la selección de fuente
* los algoritmos pueden tener en cuenta el `tipo`.
*
* Si no se proporciona, este método actúa como captador.
*
* @return {cadena|indefinido}
* Si falta el argumento `fuente`, devuelve la fuente actual
* URL. De lo contrario, no devuelve nada o indefinido.
* /
src(fuente) {
devuelve esto.handleSrc_(fuente, falso);
}
/**
* Establecer el objeto de origen en la tecnología, devuelve un valor booleano que indica si
* hay una tecnología que puede reproducir la fuente o no
*
* @param {Tech~SourceObject} fuente
* El objeto de origen para establecer en el Tech
*
* @return {booleano}
* - Verdadero si no hay tecnología para reproducir esta fuente
* - Falso en caso contrario
*
* @privado
* /
src_(fuente) {
const sourceTech = this.selectSource([fuente]);
si (!fuenteTecnología) {
devolver verdadero;
}
if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
este.cambiandoSrc_ = verdadero;
// carga esta tecnología con la fuente elegida
this.loadTech_(sourceTech.tech, sourceTech.source);
esto.tech_.ready(() => {
este.cambiandoSrc_ = falso;
});
falso retorno;
}
// esperar hasta que el técnico esté listo para establecer la fuente
// y configurarlo sincrónicamente si es posible (#2326)
esto.listo(función() {
// El método tecnológico setSource se agregó con controladores de origen
// para que los técnicos más antiguos no lo admitan
// Necesitamos verificar el prototipo directo para el caso donde las subclases
// de la tecnología no admite controladores de origen
if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
this.techCall_('establecerFuente', fuente);
} else {
this.techCall_('src', source.src);
}
este.cambiandoSrc_ = falso;
}, true);
falso retorno;
}
/**
* Comience a cargar los datos src.
* /
carga() {
this.techCall_('cargar');
}
/**
* Restablecer el reproductor. Carga la primera tecnología en el techOrder,
* elimina todas las pistas de texto en la `tecnología` existente,
* y llama a `reset` en `tech`.
* /
reiniciar() {
const PromiseClass = this.options_.Promise || ventana.Promesa;
if (this.paused() || !PromiseClass) {
esto.doReset_();
} else {
const playPromise = this.play();
silencioPromesa(reproducirPromesa.entonces(() => esto.doReset_()));
}
}
hacerReset_() {
si (esta.tecnología_) {
this.tech_.clearTracks('texto');
}
esto.resetCache_();
este.cartel('');
this.loadTech_(this.options_.techOrder[0], nulo);
this.techCall_('restablecer');
esto.resetControlBarUI_();
si (es un evento (esto)) {
this.trigger('reinicio del jugador');
}
}
/**
* Reinicie la interfaz de usuario de la barra de control llamando a submétodos que reinician
* todos los componentes de la barra de control
* /
resetControlBarUI_() {
esto.resetProgressBar_();
this.resetPlaybackRate_();
this.resetVolumeBar_();
}
/**
* Restablecer el progreso de la tecnología para que la barra de progreso se restablezca en la interfaz de usuario
* /
resetProgressBar_() {
this.currentTime(0);
const { visualización de duración, visualización de tiempo restante } = this.controlBar || {};
if (visualización de duración) {
duraciónDisplay.updateContent();
}
si (visualización de tiempo restante) {
restanteTimeDisplay.updateContent();
}
}
/**
* Restablecer relación de reproducción
* /
resetPlaybackRate_() {
this.playbackRate(this.defaultPlaybackRate());
this.handleTechRateChange_();
}
/**
* Restablecer barra de volumen
* /
resetVolumeBar_() {
este.volumen(1.0);
this.trigger('cambio de volumen');
}
/**
* Devuelve todos los objetos de origen actuales.
*
* @return {Tecnología~ObjetoOrigen[]}
* Los objetos fuente actuales
* /
fuentesactuales() {
const fuente = this.currentSource();
const fuentes = [];
// asumir `{}` o `{ src }`
if (Objeto.claves(origen).longitud !== 0) {
fuentes.push(fuente);
}
devolver this.cache_.sources || fuentes;
}
/**
* Devuelve el objeto fuente actual.
*
* @return {Tecnología~ObjetoOrigen}
* El objeto fuente actual
* /
fuente actual() {
devolver this.cache_.source || {};
}
/**
* Devuelve la URL completa del valor de origen actual, por ejemplo, http://mysite.com/video.mp4
* Se puede usar junto con `currentType` para ayudar a reconstruir el objeto de origen actual.
*
* @return {cadena}
* La fuente actual
* /
fuenteActual() {
devolver this.currentSource() && this.currentSource().src || '';
}
/**
* Obtener el tipo de fuente actual, por ejemplo, video/mp4
* Esto puede permitirle reconstruir el objeto de origen actual para que pueda cargar el mismo
* fuente y tecnología más tarde
*
* @return {cadena}
* El tipo MIME de origen
* /
tipoactual() {
devolver this.currentSource() && this.currentSource().type || '';
}
/**
* Obtener o establecer el atributo de precarga
*
* @param {booleano} [valor]
* - verdadero significa que debemos precargar
* - falso significa que no debemos precargar
*
* @return {cadena}
* El valor del atributo de precarga al obtener
* /
precarga(valor) {
si (valor! == indefinido) {
this.techCall_('setPreload', valor);
this.options_.preload = valor;
devolver;
}
devuelve this.techGet_('preload');
}
/**
* Obtener o configurar la opción de reproducción automática. Cuando esto es un valor booleano, lo hará
* modificar el atributo en la tecnología. Cuando se trata de una cadena, el atributo en
* la tecnología se eliminará y `Player` se encargará de la reproducción automática en los inicios de carga.
*
* @param {booleano|cadena} [valor]
* - verdadero: reproducción automática usando el comportamiento del navegador
* - falso: no reproducir automáticamente
* - 'reproducir': llamar a reproducir () en cada inicio de carga
* - 'silenciado': llama a muted() y luego a play() en cada inicio de carga
* - 'cualquiera': llama a play() en cada inicio de carga. si eso falla, llama a muted() y luego a play().
* - *: los valores que no sean los enumerados aquí se establecerán como "reproducción automática" en verdadero
*
* @return {booleano|cadena}
* El valor actual de reproducción automática al obtener
* /
reproducción automática (valor) {
// uso del captador
si (valor === indefinido) {
devolver esto.opciones_.reproducción automática || FALSO;
}
deja que techAutoplay;
// si el valor es una cadena válida, ajústelo a eso, o normalice `true` a 'play', si es necesario
if (tipo de valor === 'cadena' && (/(cualquiera|reproducir|silenciado)/).prueba(valor) || valor === cierto && this.options_.normalizeReproducción automática) {
this.options_.autoplay = valor;
this.manualAutoplay_(typeof value === 'string' ? value : 'play');
techAutoplay = false;
// cualquier valor falso establece la reproducción automática en falso en el navegador,
// hagamos lo mismo
} si no (! valor) {
this.options_.autoplay = falso;
// cualquier otro valor (es decir, verdadero) establece la reproducción automática en verdadero
} else {
this.options_.autoplay = true;
}
techAutoplay = tipo de techAutoplay === 'indefinido' ? this.options_.autoplay : techAutoplay;
// si no tenemos un técnico entonces no hacemos cola
// una llamada setAutoplay en tecnología lista. Hacemos esto porque el
// la opción de reproducción automática se pasará en el constructor y nosotros
// no es necesario configurarlo dos veces
si (esta.tecnología_) {
this.techCall_('setAutoplay', techAutoplay);
}
}
/**
* Establecer o deshabilitar el atributo playsinline.
* Playsinline le dice al navegador que prefiere la reproducción que no sea a pantalla completa.
*
* @param {booleano} [valor]
* - verdadero significa que deberíamos intentar jugar en línea por defecto
* - falso significa que debemos usar el modo de reproducción predeterminado del navegador,
* que en la mayoría de los casos está en línea. iOS Safari es una excepción notable
* y se reproduce a pantalla completa de forma predeterminada.
*
* @return {cadena|Jugador}
* - el valor actual de playsinline
* - el reproductor al configurar
*
* @ver [Especificación]{@enlace https://html.spec.whatwg.org/#attr-video-playsinline}
* /
juegosenlinea(valor) {
si (valor! == indefinido) {
this.techCall_('setPlaysinline', value);
this.options_.playsinline = valor;
devolver esto;
}
devuelve this.techGet_('playsinline');
}
/**
* Obtener o establecer el atributo de bucle en el elemento de video.
*
* @param {booleano} [valor]
* - verdadero significa que debemos repetir el video
* - falso significa que no debemos repetir el video
*
* @return {booleano}
* El valor actual del bucle al obtener
* /
bucle (valor) {
si (valor! == indefinido) {
this.techCall_('setLoop', valor);
this.options_.loop = valor;
devolver;
}
devuelve esto.techGet_('bucle');
}
/**
* Obtenga o configure la URL de origen de la imagen del póster
*
* @fires Player#posterchange
*
* @param {cadena} [origen]
* URL de origen de la imagen del póster
*
* @return {cadena}
* El valor actual del cartel al obtener
* /
cartel (origen) {
si (origen === indefinido) {
devolver este.poster_;
}
// La forma correcta de eliminar un póster es configurarlo como una cadena vacía
// otros valores falsos arrojarán errores
si (!origen) {
origen = '';
}
if (src === este.poster_) {
devolver;
}
// actualiza la variable del cartel interno
este.poster_ = src;
// actualiza el cartel de la tecnología
this.techCall_('setPoster', src);
this.isPosterFromTech_ = falso;
// alerta a los componentes de que se ha configurado el cartel
/**
* Este evento se activa cuando se cambia la imagen del póster en el reproductor.
*
* @reproductor del evento#cambio de cartel
* @type {Objetivo del evento~Evento}
* /
this.trigger('cambio de cartel');
}
/**
* Algunas tecnologías (por ejemplo, YouTube) pueden proporcionar una fuente de póster en un
* manera asíncrona. Queremos que el componente de póster use este
* fuente del cartel para que cubra los controles de la tecnología.
* (Botón de reproducción de YouTube). Sin embargo, solo queremos usar esto
* fuente si el usuario del reproductor no ha configurado un póster a través de
* las API normales.
*
* @fires Player#posterchange
* @escucha Tech#posterchange
* @privado
* /
handleTechPosterChange_() {
if ((!this.poster_ || this.options_.techCanOverridePoster) && esta.tecnología_ && este.tech_.poster) {
const nuevoPoster = this.tech_.poster() || '';
if (nuevoPoster !== this.poster_) {
this.poster_ = newPoster;
this.isPosterFromTech_ = verdadero;
// Informar a los componentes que el cartel ha cambiado
this.trigger('cambio de cartel');
}
}
}
/**
* Obtener o establecer si se muestran o no los controles.
*
* @fires Player#controlenabled
*
* @param {booleano} [booleano]
* - true para activar los controles
* - falso para apagar los controles
*
* @return {booleano}
* El valor actual de los controles al obtener
* /
controles(bool) {
si (bool === indefinido) {
return !!this.controls_;
}
bool = !! bool;
// No active un evento de cambio a menos que realmente haya cambiado
if (this.controls_ === bool) {
devolver;
}
esto.controles_ = bool;
if (esto.usandoNativeControls()) {
this.techCall_('setControls', bool);
}
if (esto.controles_) {
this.removeClass('vjs-controls-disabled');
this.addClass('vjs-controls-enabled');
/**
* @reproductor de eventos#controles habilitados
* @type {Objetivo del evento~Evento}
* /
this.trigger('controles habilitados');
if (!this.usingNativeControls()) {
this.addTechControlsListeners_();
}
} else {
this.removeClass('vjs-controls-enabled');
this.addClass('vjs-controls-disabled');
/**
* @event Player#controlsdisabled
* @type {Objetivo del evento~Evento}
* /
this.trigger('controles deshabilitados');
if (!this.usingNativeControls()) {
this.removeTechControlsListeners_();
}
}
}
/**
* Activar/desactivar los controles nativos. Los controles nativos son los controles integrados en
* dispositivos (por ejemplo, controles de iPhone predeterminados) u otras tecnologías
* (por ejemplo, controles de Vimeo)
* **Esto solo debe ser configurado por la tecnología actual, porque solo la tecnología sabe
* si puede admitir controles nativos **
*
* @fires Player#usingnativecontrols
* @dispara al jugador#usandocontrolespersonalizados
*
* @param {booleano} [booleano]
* - true para activar los controles nativos
* - falso para desactivar los controles nativos
*
* @return {booleano}
* El valor actual de los controles nativos al obtener
* /
usandoNativeControls(bool) {
si (bool === indefinido) {
devuelve !!this.usingNativeControls_;
}
bool = !! bool;
// No active un evento de cambio a menos que realmente haya cambiado
if (this.usingNativeControls_ === bool) {
devolver;
}
this.usingNativeControls_ = bool;
if (esto.usandoNativeControls_) {
this.addClass('vjs-using-native-controls');
/**
* el jugador está usando los controles nativos del dispositivo
*
* @reproductor de eventos#usandocontrolesnativos
* @type {Objetivo del evento~Evento}
* /
this.trigger('usando controles nativos');
} else {
this.removeClass('vjs-using-native-controls');
/**
* el jugador está usando los controles HTML personalizados
*
* @reproductor de eventos#usandocontrolespersonalizados
* @type {Objetivo del evento~Evento}
* /
this.trigger('usando controles personalizados');
}
}
/**
* Establecer u obtener el MediaError actual
*
* @fires Player#error
*
* @param {Error de Medios|cadena|número} [err]
* Un MediaError o una cadena/número para convertir
* en un MediaError
*
* @return {Error de Medios|null}
* El MediaError actual al obtener (o nulo)
* /
error (err) {
si (err === indefinido) {
devolver este.error_ || nulo;
}
// permite que los ganchos modifiquen el objeto de error
ganchos('antes del error').forEach((funcióngancho) => {
const newErr = hookFunction(this, err);
si (!(
(esObjeto(nuevoErr) && !Array.isArray(nuevoErr)) ||
typeof newErr === 'cadena' ||
typeof newErr === 'número' ||
nuevoErr === nulo
)) {
this.log.error('Por favor, devuelva un valor que MediaError espera en los ganchos beforeerror');
devolver;
}
err = newErr;
});
// Suprimir el primer mensaje de error de fuente no compatible hasta
// la interacción del usuario
si (this.options_.suppressNotSupportedError &&
errar && err.código === 4
) {
const triggerSuppressedError = function() {
este.error(err);
};
this.options_.suppressNotSupportedError = falso;
this.any(['click', 'touchstart'], triggerSuppressedError);
this.one('loadstart', function() {
this.off(['click', 'touchstart'], triggerSuppressedError);
});
devolver;
}
// restaurar a los valores predeterminados
si (err === nulo) {
this.error_ = err;
this.removeClass('vjs-error');
if (esta.visualización de error) {
this.errorDisplay.close();
}
devolver;
}
this.error_ = new MediaError(err);
// agrega el nombre de clase vjs-error al jugador
this.addClass('vjs-error');
// registrar el nombre del tipo de error y cualquier mensaje
// IE11 registra "[objeto objeto]" y requiere que expanda el mensaje para ver el objeto de error
log.error(`(CÓDIGO:${this.error_.code} ${MediaError.errorTypes[this.error_.code]})`, this.error_.message, this.error_);
/**
* @event Player#error
* @type {Objetivo del evento~Evento}
* /
this.trigger('error');
// notifica a los ganchos del error por jugador
ganchos('error').forEach((funcióngancho) => hookFunction(esto, esto.error_));
devolver;
}
/**
* Informar de la actividad del usuario
*
* evento @param {Objeto}
* Objeto de evento
* /
reportUserActivity(evento) {
this.userActivity_ = verdadero;
}
/**
* Obtener/establecer si el usuario está activo
*
* @fires Jugador#usuarioactivo
* @fires Player#userinactive
*
* @param {booleano} [booleano]
* - verdadero si el usuario está activo
* - falso si el usuario está inactivo
*
* @return {booleano}
* El valor actual de userActive al obtener
* /
usuarioActivo(bool) {
si (bool === indefinido) {
devolver este.userActive_;
}
bool = !! bool;
if (bool === this.userActive_) {
devolver;
}
este.usuarioActivo_ = bool;
si (este.usuarioActivo_) {
this.userActivity_ = verdadero;
this.removeClass('vjs-usuario-inactivo');
this.addClass('vjs-usuario-activo');
/**
* @event Jugador#usuarioactivo
* @type {Objetivo del evento~Evento}
* /
this.trigger('usuario activo');
devolver;
}
// Chrome/Safari/IE tienen errores donde cuando cambias el cursor puede
// activa un evento de movimiento del ratón. Esto causa un problema cuando te escondes.
// el cursor cuando el usuario está inactivo, y un movimiento del mouse señala al usuario
// actividad. Haciendo imposible pasar al modo inactivo. Específicamente
// esto sucede en pantalla completa cuando realmente necesitamos ocultar el cursor.
//
// Cuando esto se resuelva en TODOS los navegadores, se puede eliminar
// https://code.google.com/p/chromium/issues/detail?id=103041
si (esta.tecnología_) {
this.tech_.one('movimiento del ratón', function(e) {
e.detener la propagación();
e.preventDefault();
});
}
this.userActivity_ = false;
this.removeClass('vjs-user-active');
this.addClass('vjs-usuario-inactivo');
/**
* @event Jugador#usuarioinactivo
* @type {Objetivo del evento~Evento}
* /
this.trigger('usuarioinactivo');
}
/**
* Escuche la actividad del usuario según el valor del tiempo de espera
*
* @privado
* /
escucharActividadDeUsuario_() {
dejar mouseInProgress;
let ultimoMovimientoX;
let lastMoveY;
const handleActivity = Fn.bind(this, this.reportUserActivity);
const manejarMouseMouse = function(e) {
// #1068 - Prevenir el spam de movimiento del mouse
// Error de Chrome: https://code.google.com/p/chromium/issues/detail?id=366970
if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
ultimaMovidaX = e.pantallaX;
ultimoMovimientoY = e.pantallaY;
manejarActividad();
}
};
const manejarMouseDown = function() {
manejarActividad();
// Mientras estén tocando el dispositivo o tengan el mouse presionado,
// los consideramos activos incluso si no mueven el dedo o el mouse.
// Entonces queremos continuar actualizando que están activos
this.clearInterval(mouseInProgress);
// Establecer userActivity=true ahora y establecer el intervalo al mismo tiempo
// ya que el intervalo de verificación de actividad (250) debería garantizar que nunca perdamos el
// próxima actividadComprobar
mouseInProgress = this.setInterval(handleActivity, 250);
};
const handleMouseUpAndMouseLeave = función (evento) {
manejarActividad();
// Detener el intervalo que mantiene la actividad si el mouse/toque está presionado
this.clearInterval(mouseInProgress);
};
// Cualquier movimiento del mouse se considerará actividad del usuario
this.on('mousedown', handleMouseDown);
this.on('movemouse', handleMouseMove);
this.on('mouseup', handleMouseUpAndMouseLeave);
this.on('mouseleave', handleMouseUpAndMouseLeave);
const controlBar = this.getChild('controlBar');
// Corrige el error en Android & iOS donde al tocar la barra de progreso (cuando se muestra la barra de control)
// la barra de control ya no estaría oculta por el tiempo de espera predeterminado.
si (barra de control && !navegador.IS_IOS && !navegador.IS_ANDROID) {
controlBar.on('mouseenter', function(evento) {
if (este.jugador().opciones_.inactividadTiempo de espera!== 0) {
this.player().cache_.inactivityTimeout = this.player().options_.inactivityTimeout;
}
este.jugador().opciones_.inactividadTiempo de espera = 0;
});
controlBar.on('mouseleave', function(evento) {
this.player().options_.inactivityTimeout = this.player().cache_.inactivityTimeout;
});
}
// Escuche la navegación del teclado
// No debería necesitar usar el intervalo inProgress debido a la repetición de la tecla
this.on('keydown', handleActivity);
this.on('keyup', handleActivity);
// Ejecutar un intervalo cada 250 milisegundos en lugar de meter todo en
// la función mousemove/touchmove en sí, para evitar la degradación del rendimiento.
// `this.reportUserActivity` simplemente establece this.userActivity_ en verdadero, lo que
// luego es recogido por este ciclo
// http://ejohn.org/blog/aprendiendo-de-twitter/
dejar inactividadTiempo de espera;
este.setInterval(función() {
// Comprobar para ver si ha ocurrido actividad de ratón/táctil
if (!this.userActivity_) {
devolver;
}
// Restablecer el rastreador de actividad
this.userActivity_ = false;
// Si el estado del usuario estaba inactivo, establezca el estado en activo
este.usuarioActivo(verdadero);
// Borrar cualquier tiempo de espera de inactividad existente para volver a iniciar el temporizador
this.clearTimeout(inactividadTiempo de espera);
const timeout = this.options_.inactivityTimeout;
si (tiempo de espera < = 0) {
devolver;
}
// En < se acabó el tiempo> milisegundos, si no ha ocurrido más actividad, el
// el usuario será considerado inactivo
inactividadTiempo de espera = this.setTimeout (función () {
// Proteger contra el caso en el que inactiveTimeout puede desencadenar solo
// antes de que el bucle de verificación de actividad detecte la siguiente actividad del usuario
// causando un parpadeo
if (!this.userActivity_) {
este.usuarioActivo(falso);
}
}, timeout);
}, 250);
}
/**
* Obtiene o establece la velocidad de reproducción actual. Una tasa de reproducción de
* 1,0 representa la velocidad normal y 0,5 indicaría la mitad de la velocidad
* reproducción, por ejemplo.
*
* @ver https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
*
* @param {número} [tasa]
* Nueva tasa de reproducción para establecer.
*
* @return {número}
* La tasa de reproducción actual al obtener o 1.0
* /
tasa de reproducción (tasa) {
if (tasa !== indefinido) {
// NOTA: this.cache_.lastPlaybackRate se establece desde el controlador técnico
// que esta registrado arriba
this.techCall_('setPlaybackRate', rate);
devolver;
}
si (esta.tecnología_ && this.tech_.featuresRate de reproducción) {
devolver this.cache_.lastPlaybackRate || this.techGet_('tasa de reproducción');
}
volver 1.0;
}
/**
* Obtiene o establece la velocidad de reproducción predeterminada actual. Una tasa de reproducción predeterminada de
* 1,0 representa la velocidad normal y 0,5 indicaría reproducción a media velocidad, por ejemplo.
* defaultPlaybackRate solo representará cuál era la tasa de reproducción inicial de un video, no
* no es la tasa de reproducción actual.
*
* @ver https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
*
* @param {número} [tasa]
* Nueva tasa de reproducción predeterminada para establecer.
*
* @return {número|Jugador}
* - La velocidad de reproducción predeterminada al obtener o 1.0
* - el reproductor al configurar
* /
tasa de reproducción predeterminada (tasa) {
if (tasa !== indefinido) {
devuelve this.techCall_('setDefaultPlaybackRate', rate);
}
si (esta.tecnología_ && this.tech_.featuresRate de reproducción) {
devuelve this.techGet_('defaultPlaybackRate');
}
volver 1.0;
}
/**
* Obtiene o establece la bandera de audio
*
* @param {booleano} booleano
* - señales verdaderas de que se trata de un reproductor de audio
* - señales falsas de que esto no es un reproductor de audio
*
* @return {booleano}
* El valor actual de isAudio al obtener
* /
esAudio(bool) {
si (booleano! == indefinido) {
this.isAudio_ = !!bool;
devolver;
}
volver !!this.isAudio_;
}
enableAudioOnlyUI_() {
// Actualice el estilo inmediatamente para mostrar la barra de control para que podamos obtener su altura
this.addClass('vjs-audio-only-mode');
const jugadorNiños = esto.niños();
const controlBar = this.getChild('ControlBar');
const controlBarHeight = controlBar && controlBar.currentHeight();
// Ocultar todos los componentes del reproductor excepto la barra de control. Componentes de la barra de control
// necesarios solo para video están ocultos con CSS
jugadorNiños.paraCada(niño => {
if (hijo === barra de control) {
devolver;
}
si (niño.el_ && !child.hasClass('vjs-hidden')) {
niño.hide();
this.audioOnlyCache_.hiddenChildren.push(child);
}
});
this.audioOnlyCache_.playerHeight = this.currentHeight();
// Establecer la altura del jugador igual que la barra de control
this.height(controlBarHeight);
this.trigger('audioonlymodechange');
}
deshabilitarAudioOnlyUI_() {
this.removeClass('vjs-audio-only-mode');
// Mostrar los componentes del reproductor que antes estaban ocultos
this.audioOnlyCache_.hiddenChildren.forEach(child => niño.mostrar());
// Restablecer la altura del jugador
esta.altura(esta.audioOnlyCache_.playerHeight);
this.trigger('audioonlymodechange');
}
/**
* Obtenga el estado actual de audioOnlyMode o establezca audioOnlyMode en verdadero o falso.
*
* Establecer esto en `true` ocultará todos los componentes del reproductor excepto la barra de control,
* así como los componentes de la barra de control necesarios solo para video.
*
* @param {booleano} [valor]
* El valor para configurar audioOnlyMode.
*
* @return {Promesa|booleano}
* Se devuelve una promesa al establecer el estado y un valor booleano al obtener
* el estado actual
* /
AudioOnlyMode(valor) {
if (typeof value !== 'boolean' || value === this.audioOnlyMode_) {
devolver este.audioOnlyMode_;
}
this.audioOnlyMode_ = valor;
const PromiseClass = this.options_.Promise || ventana.Promesa;
si (PromesaClase) {
// Habilitar el modo de solo audio
si (valor) {
const exitPromises = [];
// Pantalla completa y PiP no son compatibles con audioOnlyMode, así que salga si es necesario.
if (esto.esEnImagenEnImagen()) {
exitPromises.push(this.exitPictureInPicture());
}
si (this.isFullscreen()) {
exitPromises.push(this.exitFullscreen());
}
if (this.audioPosterMode()) {
exitPromises.push(this.audioPosterMode(false));
}
return PromiseClass.all(exitPromises).then(() => this.enableAudioOnlyUI_());
}
// Deshabilitar el modo de solo audio
devuelve PromiseClass.resolve().then(() => this.disableAudioOnlyUI_());
}
si (valor) {
if (esto.esEnImagenEnImagen()) {
this.exitPictureInPicture();
}
si (this.isFullscreen()) {
this.exitFullscreen();
}
this.enableAudioOnlyUI_();
} else {
this.disableAudioOnlyUI_();
}
}
enablePosterModeUI_() {
// Oculte el elemento de video y muestre la imagen del póster para habilitar posterModeUI
const tech = this.tech_ && esta.tecnología_;
tech.hide();
this.addClass('vjs-audio-poster-mode');
this.trigger('cambio de modo de póster de audio');
}
desactivarPosterModeUI_() {
// Mostrar el elemento de video y ocultar la imagen del póster para deshabilitar posterModeUI
const tech = this.tech_ && esta.tecnología_;
tech.show();
this.removeClass('vjs-audio-poster-mode');
this.trigger('cambio de modo de póster de audio');
}
/**
* Obtenga el estado actual de audioPosterMode o establezca audioPosterMode en verdadero o falso
*
* @param {booleano} [valor]
* El valor para configurar audioPosterMode.
*
* @return {Promesa|booleano}
* Se devuelve una promesa al establecer el estado y un valor booleano al obtener
* el estado actual
* /
audioPosterMode(valor) {
if (typeof value !== 'boolean' || value === this.audioPosterMode_) {
devolver este.audioPosterMode_;
}
this.audioPosterMode_ = valor;
const PromiseClass = this.options_.Promise || ventana.Promesa;
si (PromesaClase) {
si (valor) {
if (este.audioOnlyMode()) {
const audioOnlyModePromise = this.audioOnlyMode(false);
devolver audioOnlyModePromise.then(() => {
// habilite el modo de póster de audio después de que el modo de solo audio esté deshabilitado
this.enablePosterModeUI_();
});
}
devuelve PromiseClass.resolve().then(() => {
// habilitar el modo de cartel de audio
this.enablePosterModeUI_();
});
}
devuelve PromiseClass.resolve().then(() => {
// deshabilitar el modo de cartel de audio
this.disablePosterModeUI_();
});
}
si (valor) {
if (este.audioOnlyMode()) {
este.audioOnlyMode(falso);
}
this.enablePosterModeUI_();
devolver;
}
this.disablePosterModeUI_();
}
/**
* Un método auxiliar para agregar un {@link TextTrack} a nuestro
* {@enlace TextTrackList}.
*
* Además de la configuración de W3C, permitimos agregar información adicional a través de opciones.
*
* @ver http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
*
* @param {cadena} [tipo]
* el tipo de TextTrack que está agregando
*
* @param {cadena} [etiqueta]
* la etiqueta para dar la etiqueta TextTrack
*
* @param {cadena} [idioma]
* el idioma a configurar en el TextTrack
*
* @return {TextTrack|indefinido}
* el TextTrack que se agregó o no se definió
* si no hay tecnología
* /
addTextTrack(tipo, etiqueta, idioma) {
si (esta.tecnología_) {
devuelve this.tech_.addTextTrack(tipo, etiqueta, idioma);
}
}
/**
* Cree un {@link TextTrack} remoto y un {@link HTMLTrackElement}.
* Cuando manualCleanup se establece en falso, la pista se eliminará automáticamente
* sobre cambios de fuente.
*
* @param {Objeto} opciones
* Opciones para pasar a {@link HTMLTrackElement} durante la creación. Ver
* {@link HTMLTrackElement} para las propiedades del objeto que debe usar.
*
* @param {boolean} [manualCleanup=true] si se establece en false, TextTrack será
* eliminado en un cambio de fuente
*
* @return {HtmlTrackElement}
* el HTMLTrackElement que se creó y agregó
* al HtmlTrackElementList y al control remoto
* Lista de pistas de texto
*
* @deprecated El valor predeterminado del parámetro "manualCleanup" será predeterminado
* a "falso" en las próximas versiones de Video.js
* /
addRemoteTextTrack(opciones, limpiezamanual) {
si (esta.tecnología_) {
devuelve this.tech_.addRemoteTextTrack(opciones, manualCleanup);
}
}
/**
* Eliminar un {@link TextTrack} remoto del respectivo
* {@link TextTrackList} y {@link HtmlTrackElementList}.
*
* @param {Objeto} pista
* Remoto {@link TextTrack} para eliminar
*
* @return {indefinido}
* no devuelve nada
* /
removeRemoteTextTrack(obj = {}) {
let {pista} = obj;
si (!pista) {
pista = obj;
}
// desestructurar la entrada en un objeto con un argumento de seguimiento, por defecto a arguments[0]
// predeterminado todo el argumento a un objeto vacío si no se pasó nada en
si (esta.tecnología_) {
devuelve this.tech_.removeRemoteTextTrack(pista);
}
}
/**
* Obtiene métricas de calidad de reproducción de medios disponibles según lo especificado por los medios de W3C
* API de calidad de reproducción.
*
* @ver [Especificaciones]{@enlace https://wicg.github.io/media-playback-quality}
*
* @return {Objeto|indefinido}
* Un objeto con métricas de calidad de reproducción de medios admitidas o indefinido si hay
* no es tecnología o la tecnología no es compatible.
* /
getVideoPlaybackQuality() {
devuelve this.techGet_('getVideoPlaybackQuality');
}
/**
* Obtener ancho de video
*
* @return {número}
* ancho de video actual
* /
ancho de video() {
devolver esto.tech_ && este.tech_.videoWidth && this.tech_.videoWidth() || 0;
}
/**
* Obtener altura de video
*
* @return {número}
* altura actual del video
* /
altura del video() {
devolver esto.tech_ && esta.tecnología_.videoHeight && esta.tecnología_.videoHeight() || 0;
}
/**
* El código de idioma del jugador.
*
* Cambiar el idioma activará
* [cambio de idioma]{@link Player#event:cambio de idioma}
* qué componentes pueden usar para actualizar el texto de control.
* ClickableComponent actualizará su texto de control de forma predeterminada en
* [cambio de idioma]{@link Player#event:cambio de idioma}.
*
* @fires Jugador#cambiodeidioma
*
* @param {cadena} [código]
* el código de idioma para configurar el reproductor
*
* @return {cadena}
* El código de idioma actual al obtener
* /
Código de lenguaje) {
si (código === indefinido) {
devuelve este.idioma_;
}
if (this.language_ !== String(code).toLowerCase()) {
this.language_ = String(code).toLowerCase();
// durante la primera inicialización, es posible que algunas cosas no se realicen
si (es un evento (esto)) {
/**
* se activa cuando cambia el idioma del reproductor
*
* @event Player#cambio de idioma
* @type {Objetivo del evento~Evento}
* /
this.trigger('cambio de idioma');
}
}
}
/**
* Obtenga el diccionario de idioma del jugador
* Combinar cada vez, porque un complemento recién agregado puede llamar a videojs.addLanguage() en cualquier momento
* Los idiomas especificados directamente en las opciones del reproductor tienen prioridad
*
* @return {Array}
* Una variedad de idiomas admitidos
* /
idiomas() {
return mergeOptions(Player.prototype.options_.languages, this.languages_);
}
/**
* devuelve un objeto JavaScript que representa la pista actual
* información. **NO lo devuelve como JSON**
*
* @return {Objeto}
* Objeto que representa la información actual de la pista
* /
aJSON() {
const opciones = mergeOptions(this.options_);
const pistas = opciones.pistas;
opciones.pistas = [];
para (sea i = 0; i < pistas.longitud; i++) {
dejar pista = pistas[i];
// fusión profunda de pistas y anulación del reproductor para que no haya referencias circulares
pista = mergeOptions(pista);
pista.jugador = indefinido;
opciones.pistas[i] = pista;
}
opciones de devolución;
}
/**
* Crea un cuadro de diálogo modal simple (una instancia de {@link ModalDialog}
* componente) que inmediatamente superpone al jugador con arbitrario
* contenido y se elimina cuando se cierra.
*
* @param {cadena|Función|Elemento|Array|null} contenido
* Igual que el parámetro del mismo nombre de {@link ModalDialog#content}.
* El uso más sencillo es proporcionar una cadena o DOM
* elemento.
*
* @param {Objeto} [opciones]
* Opciones adicionales que se pasarán al {@link ModalDialog}.
*
* @return {ModalDialog}
* el {@link ModalDialog} que se creó
* /
createModal(contenido, opciones) {
opciones = opciones || {};
opciones.contenido = contenido || '';
const modal = new ModalDialog(esto, opciones);
this.addChild(modal);
modal.on('disponer', () => {
this.removeChild(modal);
});
modal.open();
retorno modal;
}
/**
* Cambiar las clases de punto de interrupción cuando el jugador cambia de tamaño.
*
* @privado
* /
actualizarCurrentBreakpoint_() {
if (!this.responsive()) {
devolver;
}
const currentBreakpoint = this.currentBreakpoint();
const currentWidth = this.currentWidth();
para (sea i = 0; i < BREAKPOINT_ORDER.longitud; i++) {
const candidatoBreakpoint = BREAKPOINT_ORDER[i];
const maxWidth = this.breakpoints_[candidateBreakpoint];
si (ancho actual < = ancho máximo) {
// El punto de interrupción actual no cambió, no hay nada que hacer.
if (punto de ruptura actual === punto de ruptura candidato) {
devolver;
}
// Solo elimina una clase si hay un punto de interrupción actual.
si (punto de interrupción actual) {
this.removeClass(BREAKPOINT_CLASSES[currentBreakpoint]);
}
this.addClass(BREAKPOINT_CLASSES[candidateBreakpoint]);
this.breakpoint_ = candidatoBreakpoint;
romper;
}
}
}
/**
* Elimina el punto de interrupción actual.
*
* @privado
* /
removeCurrentBreakpoint_() {
const className = this.currentBreakpointClass();
this.breakpoint_ = '';
si (nombre de la clase) {
this.removeClass(nombreClase);
}
}
/**
* Obtener o establecer puntos de interrupción en el reproductor.
*
* Llamar a este método con un objeto o `true` eliminará cualquier anterior
* Puntos de interrupción personalizados y comenzar de nuevo desde los valores predeterminados.
*
* @param {Objeto|booleano} [puntos de interrupción]
* Si se da un objeto, se puede utilizar para proporcionar personalizado
* puntos de ruptura. Si se da `true`, establecerá puntos de interrupción predeterminados.
* Si no se proporciona este argumento, simplemente devolverá el actual
* puntos de ruptura.
*
* @param {número} [puntos de interrupción.pequeño]
* El ancho máximo para la clase "vjs-layout-tiny".
*
* @param {número} [puntos de interrupción.xsmall]
* El ancho máximo para la clase "vjs-layout-x-small".
*
* @param {número} [puntos de interrupción.pequeño]
* El ancho máximo para la clase "vjs-layout-small".
*
* @param {número} [puntos de interrupción.medio]
* El ancho máximo para la clase "vjs-layout-medium".
*
* @param {número} [puntos de interrupción.grande]
* El ancho máximo para la clase "vjs-layout-large".
*
* @param {número} [puntos de interrupción.xlarge]
* El ancho máximo para la clase "vjs-layout-x-large".
*
* @param {número} [puntos de ruptura.enormes]
* El ancho máximo para la clase "vjs-layout-huge".
*
* @return {Objeto}
* Un objeto que asigna nombres de puntos de interrupción a valores de ancho máximo.
* /
puntos de interrupción (puntos de interrupción) {
// Usado como captador.
if (puntos de ruptura === indefinido) {
volver asignar (this.breakpoints_);
}
this.breakpoint_ = '';
this.breakpoints_ = asignar({}, DEFAULT_BREAKPOINTS, breakpoints);
// Cuando cambian las definiciones de punto de interrupción, necesitamos actualizar el actual
// punto de interrupción seleccionado.
this.updateCurrentBreakpoint_();
// Clona los puntos de interrupción antes de regresar.
volver asignar (this.breakpoints_);
}
/**
* Obtenga o establezca una bandera que indique si este jugador debe o no ajustar
* su interfaz de usuario basada en sus dimensiones.
*
* valor @param {booleano}
* Debería ser "verdadero" si el jugador debe ajustar su interfaz de usuario en función de su
* dimensiones; de lo contrario, debería ser `falso`.
*
* @return {booleano}
* Será "verdadero" si este jugador debe ajustar su interfaz de usuario en función de su
* dimensiones; de lo contrario, será `falso`.
* /
sensible (valor) {
// Usado como captador.
si (valor === indefinido) {
devuelve esto.responsive_;
}
valor = booleano(valor);
constante actual = this.responsive_;
// Nada ha cambiado.
if (valor === actual) {
devolver;
}
// El valor realmente cambió, configúrelo.
this.responsive_ = valor;
// Comenzar a escuchar los puntos de interrupción y establecer el punto de interrupción inicial si el
// el reproductor ahora responde.
si (valor) {
this.on('playerresize', this.boundUpdateCurrentBreakpoint_);
this.updateCurrentBreakpoint_();
// Deja de escuchar los puntos de interrupción si el reproductor ya no responde.
} else {
this.off('playerresize', this.boundUpdateCurrentBreakpoint_);
this.removeCurrentBreakpoint_();
}
valor de retorno;
}
/**
* Obtenga el nombre del punto de interrupción actual, si corresponde.
*
* @return {cadena}
* Si actualmente hay un punto de interrupción establecido, devuelve la clave del
* Objeto de puntos de interrupción que lo empareja. De lo contrario, devuelve una cadena vacía.
* /
punto de interrupción actual () {
devolver este.breakpoint_;
}
/**
* Obtener el nombre de la clase de punto de interrupción actual.
*
* @return {cadena}
* El nombre de la clase coincidente (por ejemplo, `"vjs-layout-tiny"` o
* `"vjs-layout-large"`) para el punto de interrupción actual. Cadena vacía si
* no hay ningún punto de interrupción actual.
* /
claseBreakpointActual() {
devuelve BREAKPOINT_CLASSES[this.breakpoint_] || '';
}
/**
* Un objeto que describe una sola pieza de medios.
*
* Se conservarán las propiedades que no formen parte de la descripción de este tipo; entonces,
* esto también puede verse como un mecanismo genérico de almacenamiento de metadatos.
*
* @ver {@enlace https://wicg.github.io/mediasession/#the-mediametadata-interface}
* @typedef {Objeto} Reproductor ~ MediaObject
*
* @propiedad {cadena} [álbum]
* Sin usar, excepto si este objeto se pasa a `MediaSession`
*API.
*
* @propiedad {cadena} [artista]
* Sin usar, excepto si este objeto se pasa a `MediaSession`
*API.
*
* @propiedad {Objeto[]} [obra de arte]
* Sin usar, excepto si este objeto se pasa a `MediaSession`
*API. Si no se especifica, se completará a través del `cartel`, si
* disponible.
*
* @propiedad {cadena} [póster]
* URL de una imagen que se mostrará antes de la reproducción.
*
* @property {Tech~SourceObject|Tech~SourceObject[]|string} [src]
* Un solo objeto de origen, una matriz de objetos de origen o una cadena
* hacer referencia a una URL a una fuente de medios. Es _muy recomendable_
* que aquí se usa un objeto o una matriz de objetos, por lo que la fuente
* los algoritmos de selección pueden tener en cuenta el `tipo`.
*
* @propiedad {cadena} [título]
* Sin usar, excepto si este objeto se pasa a `MediaSession`
*API.
*
* @propiedad {Objeto[]} [pistas de texto]
* Una matriz de objetos que se utilizarán para crear pistas de texto, siguiendo
* el {@enlace https://www.w3.org/TR/html50/embedded-content-0.html#the-track-element|formato de elemento de pista nativo}.
* Para facilitar su eliminación, se crearán como texto "remoto"
* rastrea y configura para limpiar automáticamente los cambios de fuente.
*
* Estos objetos pueden tener propiedades como `src`, `kind`, `label`,
* y `idioma`, consulte {@link Tech#createRemoteTextTrack}.
* /
/**
* Rellene el reproductor con {@link Player~MediaObject|MediaObject}.
*
* @param {Reproductor~MediaObject} medios
* Un objeto mediático.
*
* @param {Función} lista
* Una devolución de llamada para ser llamada cuando el jugador esté listo.
* /
loadMedia(medios, listo) {
if (! medios || tipo de medios! == 'objeto') {
devolver;
}
esto.reset();
// Clonar el objeto de medios para que no se pueda mutar desde el exterior.
this.cache_.media = mergeOptions(media);
const {arte, poster, src, textTracks} = this.cache_.media;
// Si no se proporciona `artwork`, créelo usando `poster`.
si (! ilustraciones && cartel) {
this.cache_.media.artwork = [{
origen: cartel,
tipo: getMimetype(póster)
}];
}
si (fuente) {
this.src(src);
}
si (cartel) {
este.cartel(cartel);
}
if (Array.isArray(textTracks)) {
Pistas de texto.forEach(tt => this.addRemoteTextTrack(tt, false));
}
esto.listo(listo);
}
/**
* Obtén un clon del {@link Player~MediaObject} actual para este reproductor.
*
* Si no se ha utilizado el método `loadMedia`, intentará devolver un
* {@link Player~MediaObject} según el estado actual del reproductor.
*
* @return {Reproductor~MediaObject}
* /
obtenerMedios() {
if (!this.cache_.media) {
const poster = this.poster();
const src = this.currentSources();
const textTracks = Array.prototype.map.call(this.remoteTextTracks(), (tt) => ({
amable: tt. amable,
etiqueta: tt.etiqueta,
idioma: tt.idioma,
src: tt.src
}));
const media = {origen, pistas de texto};
si (cartel) {
media.poster = cartel;
medios.arte = [{
src: media.poster,
tipo: getMimetype(media.poster)
}];
}
medios de retorno;
}
devuelve mergeOptions(this.cache_.media);
}
/**
* Obtiene la configuración de la etiqueta
*
* Etiqueta @param {Elemento}
* La etiqueta del jugador
*
* @return {Objeto}
* Un objeto que contiene todas las configuraciones
* para una etiqueta de jugador
* /
getTagSettings estáticos (etiqueta) {
const baseOptions = {
fuentes: [],
pistas: []
};
const tagOptions = Dom.getAttributes(tag);
const dataSetup = tagOptions['data-setup'];
if (Dom.hasClass(etiqueta, 'vjs-fill')) {
tagOptions.fill = verdadero;
}
if (Dom.hasClass(etiqueta, 'vjs-fluido')) {
tagOptions.fluido = verdadero;
}
// Comprobar si existe el atributo de configuración de datos.
si (configuración de datos! == nulo) {
// Opciones de análisis JSON
// Si es una cadena vacía, conviértala en un objeto json analizable.
const [err, datos] = safeParseTuple(dataSetup || '{}');
si (err) {
registro.error(err);
}
asignar (opciones de etiqueta, datos);
}
asignar (opciones base, opciones de etiqueta);
// Obtener la configuración de los hijos de la etiqueta
si (etiqueta.hasChildNodes()) {
const niños = etiqueta.childNodes;
for (sea i = 0, j = niños.longitud; i < j; i++) {
const hijo = hijos[i];
// Cambiar mayúsculas y minúsculas: http://ejohn.org/blog/nodename-case-sensitivity/
const childName = child.nodeName.toLowerCase();
if (childName === 'fuente') {
baseOptions.sources.push(Dom.getAttributes(child));
} else if (childName === 'pista') {
baseOptions.tracks.push(Dom.getAttributes(child));
}
}
}
volver opciones base;
}
/**
* Determinar si flexbox es compatible o no
*
* @return {booleano}
* - verdadero si se admite flexbox
* - falso si flexbox no es compatible
* /
flexNotSupported_() {
const elem = documento.createElement('i');
// Nota: En realidad, no usamos flexBasis (o flexOrder), pero es uno de los más
// características comunes de flexión en las que podemos confiar cuando comprobamos la compatibilidad con flexión.
volver! ('base flexible' en elem.style ||
'webkitFlexBasis' en elem.style ||
'mozFlexBasis' en elem.style ||
'msFlexBasis' en elem.style ||
// Específico de IE10 (2012 flex spec), disponible para completar
'msFlexOrder' en elem.estilo);
}
/**
* Establecer el modo de depuración para habilitar/deshabilitar los registros a nivel de información.
*
* @param {booleano} habilitado
* @fires jugador#debugon
* @fires Player#debugoff
* /
depurar (habilitado) {
si (habilitado === indefinido) {
devolver esto.debugEnabled_;
}
si (habilitado) {
this.trigger('debugon');
this.previousLogLevel_ = this.log.level;
this.log.level('depurar');
this.debugEnabled_ = verdadero;
} else {
this.trigger('debugoff');
este.log.level(this.anteriorLogLevel_);
this.previousLogLevel_ = indefinido;
this.debugEnabled_ = falso;
}
}
/**
* Establecer u obtener tasas de reproducción actuales.
* Toma una matriz y actualiza el menú de tasas de reproducción con los nuevos elementos.
* Pase una matriz vacía para ocultar el menú.
* Los valores que no sean matrices se ignoran.
*
* @fires Player#playbackrateschange
* @param {número[]} nuevasTasas
* Las nuevas tasas a las que debería actualizarse el menú de tasas de reproducción.
* Una matriz vacía ocultará el menú
* @return {number[]} Cuando se usa como captador, devolverá las tasas de reproducción actuales
* /
tasas de reproducción (tasas nuevas) {
if (nuevasTasas === indefinido) {
devolver this.cache_.playbackRates;
}
// ignora cualquier valor que no sea una matriz
if (!Array.isArray(nuevasTasas)) {
devolver;
}
// ignora cualquier matriz que no solo contenga números
if (!nuevasTarifas.cada((tarifa) => tipo de tasa === 'número')) {
devolver;
}
this.cache_.playbackRates = newRates;
/**
* se dispara cuando se cambian las tasas de reproducción en un reproductor
*
* @event Jugador#cambio de tasas de reproducción
* @type {Objetivo del evento~Evento}
* /
this.trigger('cambio de tasas de reproducción');
}
}
/**
* Obtener la {@link VideoTrackList}
* @enlace https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
*
* @return {Lista de pistas de vídeo}
* la lista de pistas de video actual
*
* @método Player.prototype.videoTracks
* /
/**
* Obtener la {@link AudioTrackList}
* @enlace https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
*
* @return {Lista de pistas de audio}
* la lista de pistas de audio actual
*
* @método Player.prototype.audioTracks
* /
/**
* Obtén la {@link TextTrackList}
*
* @enlace http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
*
* @return {TextTrackList}
* la lista de pistas de texto actual
*
* @método Player.prototype.textTracks
* /
/**
* Obtener el {@link TextTrackList} remoto
*
* @return {TextTrackList}
* La lista de pistas de texto remoto actual
*
* @método Player.prototype.remoteTextTracks
* /
/**
* Obtener las pistas remotas {@link HtmlTrackElementList}.
*
* @return {HtmlTrackElementList}
* La lista de elementos de pista de texto remoto actual
*
* @método Player.prototype.remoteTextTrackEls
* /
TRACK_TYPES.names.forEach(función(nombre) {
const props = TRACK_TYPES[nombre];
Player.prototype[props.getterName] = function() {
si (esta.tecnología_) {
devuelve this.tech_[props.getterName]();
}
// si aún no tenemos loadTech_, creamos {video,audio,text}Tracks_
// estos se pasarán al técnico durante la carga
this[props.privateName] = this[props.privateName] || nuevos accesorios.ListClass();
devolver este [props.privateName];
};
});
/**
* Obtener o establecer la opción de origen cruzado del `Jugador`. Para el reproductor HTML5, este
* establece la propiedad `crossOrigin` en `< video> ` etiqueta para controlar el CORS
* comportamiento.
*
* @ver [Atributos de elemento de video]{@enlace https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-crossorigin}
*
* @param {cadena} [valor]
* El valor para establecer el origen cruzado del `Jugador`. Si un argumento es
* proporcionado, debe ser uno de `anonymous` o `use-credentials`.
*
* @return {cadena|indefinido}
* - El valor de origen cruzado actual del `Jugador` al obtener.
* - indefinido al configurar
* /
Player.prototype.crossorigin = Player.prototype.crossOrigin;
/**
* Enumeración global de jugadores.
*
* Las claves son los ID de los jugadores y los valores son {@link Player}
* instancia o `null` para jugadores desechados.
*
* @type {Objeto}
* /
Jugador.jugadores = {};
const navegador = ventana.navegador;
/*
* Opciones de instancia de jugador, surgieron usando opciones
* opciones = Jugador.prototipo.opciones_
* Hacer cambios en opciones, no aquí.
*
* @type {Objeto}
* @privado
* /
Jugador.prototipo.opciones_ = {
// Orden predeterminado de la tecnología de respaldo
Orden técnico: Tech.defaultTechOrder_,
html5: {},
// tiempo de espera de inactividad predeterminado
tiempo de inactividad: 2000,
// tasas de reproducción predeterminadas
tasas de reproducción: [],
// Agregar selección de tasa de reproducción agregando tasas
// 'tasas de reproducción': [0.5, 1, 1.5, 2],
liveui: falso,
// Conjuntos de control incluidos
niños: [
'cargador de medios',
'imagen del cartel',
'textTrackDisplay',
'cargandoSpinner',
'botón de reproducción grande',
'rastreador en vivo',
'barra de control',
'visualización de errores',
'Configuración de pista de texto',
'resizeManager'
],
idioma: navegador && (navegador.idiomas && navegador.idiomas[0] || navegador.idiomausuario || navegador.idioma) || 'en',
// locales y sus traducciones de idiomas
idiomas: {},
// Mensaje predeterminado para mostrar cuando no se puede reproducir un video.
Mensaje no admitido: 'No se encontró una fuente compatible para este medio.',
normalizarReproducción automática: falso,
pantalla completa: {
opciones: {
UI de navegación: 'ocultar'
}
},
puntos de interrupción: {},
responsivo: falso,
AudioOnlyMode: falso,
audioPosterMode: falso
};
[
/**
* Devuelve si el jugador está o no en el estado "terminado".
*
* @return {Boolean} Verdadero si el jugador está en el estado finalizado, falso en caso contrario.
* @método Player#finalizado
* /
'terminado',
/**
* Devuelve si el jugador está o no en el estado de "búsqueda".
*
* @return {Boolean} Verdadero si el jugador está en el estado de búsqueda, falso si no.
* @método Jugador#buscando
* /
'buscando',
/**
* Devuelve los TimeRanges de los medios que están disponibles actualmente
* por querer.
*
* @return {TimeRanges} los intervalos buscables de la línea de tiempo de los medios
* @método Player#buscable
* /
'buscable',
/**
* Devuelve el estado actual de la actividad de la red para el elemento, desde
* los códigos en la lista a continuación.
* - NETWORK_EMPTY (valor numérico 0)
* El elemento aún no ha sido inicializado. Todos los atributos están en
* sus estados iniciales.
* - NETWORK_IDLE (valor numérico 1)
* El algoritmo de selección de recursos del elemento está activo y tiene
* seleccionó un recurso, pero en realidad no está usando la red en
* esta vez.
* - NETWORK_LOADING (valor numérico 2)
* El agente de usuario está tratando activamente de descargar datos.
* - NETWORK_NO_SOURCE (valor numérico 3)
* El algoritmo de selección de recursos del elemento está activo, pero tiene
* aún no se ha encontrado un recurso para usar.
*
* @ver https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
* @return {number} el estado actual de la actividad de la red
* @método jugador#estado de la red
* /
'estado de la red',
/**
* Devuelve un valor que expresa el estado actual del elemento
* con respecto a la representación de la posición de reproducción actual, desde el
* códigos en la lista a continuación.
* - NO TENER_NADA (valor numérico 0)
* No hay información disponible sobre el recurso de los medios.
* - TENER_METADATA (valor numérico 1)
* Se ha obtenido suficiente del recurso que la duración de la
* el recurso está disponible.
* - TENER_DATOS_ACTUALES (valor numérico 2)
* Los datos para la posición de reproducción actual inmediata están disponibles.
* - TENER_DATOS_FUTUROS (valor numérico 3)
* Los datos para la posición de reproducción actual inmediata están disponibles, como
* así como datos suficientes para que el agente de usuario avance el actual
* posición de reproducción en la dirección de reproducción.
* - TENER_SUFICIENTE_DATOS (valor numérico 4)
* El agente de usuario estima que hay suficientes datos disponibles para
* reproducción para continuar sin interrupciones.
*
* @ver https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
* @return {number} el estado actual de reproducción de reproducción
* @method Player#readyState
* /
'estadolisto'
].forEach(función(fn) {
Jugador.prototipo[fn] = función() {
devolver esto.techGet_(fn);
};
});
TECH_EVENTS_RETRIGGER.forEach(función(evento) {
Player.prototype[`handleTech${toTitleCase(evento)}_`] = función() {
devuelve este disparador (evento);
};
});
/**
* Activado cuando el jugador tiene información inicial de duración y dimensión
*
* @reproductor de eventos#metadatos cargados
* @type {Objetivo del evento~Evento}
* /
/**
* Activado cuando el reproductor ha descargado datos en la posición de reproducción actual
*
* @reproductor del evento#datos cargados
* @type {Objetivo del evento~Evento}
* /
/**
* Se dispara cuando la posición de reproducción actual ha cambiado *
* Durante la reproducción, se dispara cada 15-250 milisegundos, según el
* tecnología de reproducción en uso.
*
* @event Player#timeupdate
* @type {Objetivo del evento~Evento}
* /
/**
* Disparado cuando cambia el volumen
*
* @reproductor de eventos#cambio de volumen
* @type {Objetivo del evento~Evento}
* /
/**
* Informa si un jugador tiene o no un complemento disponible.
*
* Esto no informa si el complemento se ha inicializado alguna vez o no.
* en este reproductor. Para eso, [usingPlugin]{@link Player#usingPlugin}.
*
* @método Player#hasPlugin
* @param {cadena} nombre
* El nombre de un complemento.
*
* @return {booleano}
* Si este reproductor tiene disponible o no el complemento solicitado.
* /
/**
* Informa si un jugador está usando o no un complemento por nombre.
*
* Para complementos básicos, esto solo informa si el complemento ha sido _alguna vez_
* inicializado en este reproductor.
*
* @método Player#usingPlugin
* @param {cadena} nombre
* El nombre de un complemento.
*
* @return {booleano}
* Si este reproductor está utilizando o no el complemento solicitado.
* /
Component.registerComponent('Jugador', Jugador);
exportar reproductor predeterminado;