/**
* @file slider.js
*/
import Component from '../component.js' ;
import * as Dom from '../utils/dom.js' ;
import {assign} from '../utils/obj' ;
import {IS_CHROME} from '../utils/browser.js' ;
import clamp from '../utils/clamp.js' ;
import keycode from 'keycode' ;
/**
* Fonctionnalité de base d'un curseur. Peut être vertical ou horizontal.
* Par exemple, la barre de volume ou la barre de recherche d'une vidéo est un curseur.
*
* @extends Component
*/
class Slider extends Component {
/**
* Créer une instance de cette classe
*
* @param {Player} player
* Le `Player` auquel cette classe doit être attachée.
*
* @param {Objet} [options]
* La mémoire clé/valeur des options du lecteur.
*/
constructor(player, options) {
super(player, options) ;
this.handleMouseDown_ = (e) => this.handleMouseDown(e) ;
this.handleMouseUp_ = (e) => this.handleMouseUp(e) ;
this.handleKeyDown_ = (e) => this.handleKeyDown(e) ;
this.handleClick_ = (e) => this.handleClick(e) ;
this.handleMouseMove_ = (e) => this.handleMouseMove(e) ;
this.update_ = (e) => this.update(e) ;
// Définir les noms des propriétés à la barre pour qu'ils correspondent à la classe enfant Slider recherchée
this.bar = this.getChild(this.options_.barName) ;
// Définir une classe horizontale ou verticale sur le curseur en fonction du type de curseur
this.vertical(!!this.options_.vertical) ;
this.enable() ;
}
/**
* Les contrôles sont-ils activés ou non pour ce curseur ?
*
* @return {boolean}
* true si les contrôles sont activés, false sinon
*/
enabled() {
return this.enabled_ ;
}
/**
* Activer les contrôles de ce curseur s'ils sont désactivés
*/
enable() {
if (this.enabled()) {
retour ;
}
this.on('mousedown', this.handleMouseDown_) ;
this.on('touchstart', this.handleMouseDown_) ;
this.on('keydown', this.handleKeyDown_) ;
this.on('click', this.handleClick_) ;
// TODO : obsolète, controlsvisible ne semble pas être déclenché
this.on(this.player_, 'controlsvisible', this.update) ;
if (this.playerEvent) {
this.on(this.player_, this.playerEvent, this.update) ;
}
this.removeClass('disabled') ;
this.setAttribute('tabindex', 0) ;
this.enabled_ = true ;
}
/**
* Désactiver les contrôles de ce curseur s'ils sont activés
*/
disable() {
if (!this.enabled()) {
retour ;
}
const doc = this.bar.el_.ownerDocument ;
this.off('mousedown', this.handleMouseDown_) ;
this.off('touchstart', this.handleMouseDown_) ;
this.off('keydown', this.handleKeyDown_) ;
this.off('click', this.handleClick_) ;
this.off(this.player_, 'controlsvisible', this.update_) ;
this.off(doc, 'mousemove', this.handleMouseMove_) ;
this.off(doc, 'mouseup', this.handleMouseUp_) ;
this.off(doc, 'touchmove', this.handleMouseMove_) ;
this.off(doc, 'touchend', this.handleMouseUp_) ;
this.removeAttribute('tabindex') ;
this.addClass('disabled') ;
if (this.playerEvent) {
this.off(this.player_, this.playerEvent, this.update) ;
}
this.enabled_ = false ;
}
/**
* Créer l'élément DOM `Slider`.
*
* @param {string} type
* Type d'élément à créer.
*
* @param {Objet} [props={}]
* Liste des propriétés sous forme d'objet.
*
* @param {Objet} [attributes={}]
* liste d'attributs sous forme d'objets.
*
* @return {Element}
* L'élément qui est créé.
*/
createEl(type, props = {}, attributes = {}) {
// Ajouter la classe de l'élément "slider" à toutes les sous classes
props.className = props.className + ' vjs-slider' ;
props = assign({
tabIndex : 0
}, props) ;
attributs = assign({
rôle" : 'slider',
aria-valuenow" : 0,
aria-valuemin" : 0,
aria-valuemax" : 100,
'tabIndex' : 0
}, attributs) ;
return super.createEl(type, props, attributes) ;
}
/**
* Gère les événements `mousedown` ou `touchstart` sur le `Slider`.
*
* @param {EventTarget~Event} event
* événement `mousedown` ou `touchstart` qui a déclenché cette fonction
*
* @listens mousedown
* @listens touchstart
* @fires Slider#slideractive
*/
handleMouseDown(event) {
const doc = this.bar.el_.ownerDocument ;
if (event.type === 'mousedown') {
event.preventDefault() ;
}
// Ne pas appeler preventDefault() au démarrage du contact dans Chrome
// pour éviter les avertissements de la console. Utiliser le style "touch-action : none"
// pour éviter un défilement intempestif.
// https://developers.google.com/web/updates/2017/01/scrolling-intervention
if (event.type === 'touchstart' && !IS_CHROME) {
event.preventDefault() ;
}
Dom.blockTextSelection() ;
this.addClass('vjs-sliding') ;
/**
* Déclenché lorsque le curseur est actif
*
* @event Slider#slideractive
* @type {EventTarget~Event}
*/
this.trigger('slideractive') ;
this.on(doc, 'mousemove', this.handleMouseMove_) ;
this.on(doc, 'mouseup', this.handleMouseUp_) ;
this.on(doc, 'touchmove', this.handleMouseMove_) ;
this.on(doc, 'touchend', this.handleMouseUp_) ;
this.handleMouseMove(event, true) ;
}
/**
* Gère les événements `mousemove`, `touchmove` et `mousedown` sur ce `Slider`.
* Les événements `mousemove` et `touchmove` ne déclencheront cette fonction que pendant
* `mousedown` et `touchstart`. Ceci est dû à {@link Slider#handleMouseDown} et à {@link Slider#handleMouseDown}
* {@link Slider#handleMouseUp}.
*
* @param {EventTarget~Event} event
* événement `mousedown`, `mousemove`, `touchstart`, ou `touchmove` qui a été déclenché
* cette fonction
* @param {boolean} mouseDown c'est un drapeau qui doit être mis à true si `handleMouseMove` est appelé directement. Cela nous permet d'ignorer les choses qui ne devraient pas se produire si elles proviennent de la souris vers le bas, mais qui devraient se produire avec un gestionnaire de mouvement de souris normal. La valeur par défaut est false.
*
* @listens mousemove
* @listens touchmove
*/
handleMouseMove(event) {}
/**
* Gère les événements `mouseup` ou `touchend` sur le `Slider`.
*
* @param {EventTarget~Event} event
* l'événement `mouseup` ou `touchend` qui a déclenché cette fonction.
*
* @listens touchend
* @listens mouseup
* @fires Slider#sliderinactive
*/
handleMouseUp() {
const doc = this.bar.el_.ownerDocument ;
Dom.unblockTextSelection() ;
this.removeClass('vjs-sliding') ;
/**
* Déclenché lorsque le curseur n'est plus actif.
*
* @event Slider#sliderinactive
* @type {EventTarget~Event}
*/
this.trigger('sliderinactive') ;
this.off(doc, 'mousemove', this.handleMouseMove_) ;
this.off(doc, 'mouseup', this.handleMouseUp_) ;
this.off(doc, 'touchmove', this.handleMouseMove_) ;
this.off(doc, 'touchend', this.handleMouseUp_) ;
this.update() ;
}
/**
* Mettre à jour la barre de progression du `Slider`.
*
* @return {number}
* Le pourcentage de progression que la barre de progression représente en tant que
* nombre de 0 à 1.
*/
update() {
// Dans l'init du VolumeBar, nous avons un setTimeout pour la mise à jour qui se déclenche et se met à jour
// à la fin de la pile d'exécution. Le joueur est détruit avant cela
// la mise à jour provoquera une erreur
// S'il n'y a pas de bar...
if (!this.el_ || !this.bar) {
retour ;
}
// la progression est comprise entre 0 et 1
// et n'arrondir qu'à quatre décimales, car nous arrondissons à deux décimales ci-dessous
const progress = this.getProgress() ;
if (progress === this.progress_) {
retour de la progression ;
}
this.progress_ = progress ;
this.requestNamedAnimationFrame('Slider#update', () => {
// Définir la largeur ou la hauteur de la nouvelle barre
const sizeKey = this.vertical() ? 'height' : 'width' ;
// Conversion en pourcentage pour la valeur css
this.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%' ;
}) ;
retour de la progression ;
}
/**
* Obtenir le pourcentage de la barre qui doit être remplie
* mais serré et arrondi.
*
* @return {number}
* pourcentage de remplissage du curseur
*/
getProgress() {
return Number(clamp(this.getPercent(), 0, 1).toFixed(4)) ;
}
/**
* Calculer la distance pour le curseur
*
* @param {EventTarget~Event} event
* L'événement qui a provoqué l'exécution de cette fonction.
*
* @return {number}
* La position actuelle du curseur.
* - position.x pour les `Slider` verticaux
* - position.y pour les `Slider` horizontaux
*/
calculateDistance(event) {
const position = Dom.getPointerPosition(this.el_, event) ;
if (this.vertical()) {
return position.y ;
}
retour position.x ;
}
/**
* Gère un événement `keydown` sur le `Slider`. Veille sur la gauche, la droite, le haut et le bas
* touches fléchées. Cette fonction ne sera appelée que lorsque le curseur est mis au point. Voir
* {@link Slider#handleFocus} et {@link Slider#handleBlur}.
*
* @param {EventTarget~Event} event
* l'événement `keydown` qui a provoqué l'exécution de cette fonction.
*
* @listens keydown
*/
handleKeyDown(event) {
// Flèches de gauche et de bas
if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
event.preventDefault() ;
event.stopPropagation() ;
this.stepBack() ;
// Flèches vers le haut et vers la droite
} else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
event.preventDefault() ;
event.stopPropagation() ;
this.stepForward() ;
} else {
// Transmettre la gestion des touches non prises en charge vers le haut
super.handleKeyDown(event) ;
}
}
/**
* Écoute des événements de clic sur le curseur, utilisé pour empêcher les clics
* de remonter vers les éléments parents tels que les menus de boutons.
*
* @param {Objet} événement
* Événement à l'origine de l'exécution de cet objet
*/
handleClick(event) {
event.stopPropagation() ;
event.preventDefault() ;
}
/**
* Obtenir/paramétrer si le curseur est horizontal ou vertical
*
* @param {boolean} [bool]
* - true si le curseur est vertical,
* - faux est horizontal
*
* @return {boolean}
* - vrai si le curseur est vertical, et en obtenant
* - false si le curseur est horizontal, et en obtenant
*/
vertical(bool) {
if (bool === undefined) {
return this.vertical_ || false ;
}
this.vertical_ = !!bool ;
if (this.vertical_) {
this.addClass('vjs-slider-vertical') ;
} else {
this.addClass('vjs-slider-horizontal') ;
}
}
}
Component.registerComponent('Slider', Slider) ;
exporter le curseur par défaut ;