/**
* @file middleware.js
* @module middleware
*/
import { assign } from '../utils/obj.js' ;
import {toTitleCase} de '../utils/string-cases.js' ;
const middlewares = {} ;
const middlewareInstances = {} ;
export const TERMINATOR = {} ;
/**
* Un objet middleware est un objet JavaScript ordinaire qui possède des méthodes qui
* correspondent aux méthodes {@link Tech} trouvées dans les listes de méthodes autorisées
* {@link module:middleware.allowedGetters|getters},
* {@link module:middleware.allowedSetters|setters}, et
* {@link module:middleware.allowedMediators|mediators}.
*
* @typedef {Objet} MiddlewareObject
*/
/**
* Une fonction de fabrique d'intergiciel qui doit renvoyer un
* {@link module:middleware~MiddlewareObject|MiddlewareObject}.
*
* Cette usine sera appelée pour chaque joueur en cas de besoin, avec le joueur
* transmis en tant qu'argument.
*
* @callback MiddlewareFactory
* @param {Player} player
* Un lecteur Video.js.
*/
/**
* Définir un logiciel intermédiaire que le lecteur doit utiliser au moyen d'une fonction d'usine
* qui renvoie un objet intermédiaire.
*
* @param {string} type
* Le type de MIME à rechercher ou `"*"` pour tous les types de MIME.
*
* @param {MiddlewareFactory} middleware
* Une fonction d'usine d'intergiciel qui sera exécutée pour
* les types de correspondance.
*/
export function use(type, middleware) {
middlewares[type] = middlewares[type] || [] ;
middlewares[type].push(middleware) ;
}
/**
* Permet d'obtenir les intermédiaires par type (ou tous les intermédiaires).
*
* @param {string} type
* Le type de MIME à rechercher ou `"*"` pour tous les types de MIME.
*
* @return {Fonction[]|undefined}
* Un tableau d'intergiciels ou `undefined` s'il n'y en a pas.
*/
export function getMiddleware(type) {
if (type) {
return middlewares[type] ;
}
retourner les middlewares ;
}
/**
* Définit de manière asynchrone une source à l'aide d'un intergiciel en parcourant tous les
* et appeler `setSource` sur chacun d'entre eux, en leur transmettant l'élément
* la valeur précédente renvoyée à chaque fois.
*
* @param {Player} player
* Une instance {@link Player}.
*
* @param {Tech~SourceObject} src
* Un objet source.
*
* @param {Fonction}
* Le prochain logiciel intermédiaire à exécuter.
*/
export function setSource(player, src, next) {
player.setTimeout(() => setSourceHelper(src, middlewares[src.type], next, player), 1) ;
}
/**
* Lorsque la technologie est définie, elle est transmise à la méthode `setTech` de chaque middleware.
*
* @param {Objet[]} middleware
* Un tableau d'instances d'intergiciels.
*
* @param {Tech} tech
* Une technologie Video.js.
*/
export function setTech(middleware, tech) {
middleware.forEach((mw) => mw.setTech && mw.setTech(tech)) ;
}
/**
* Appelle d'abord un getter sur le tech, à travers chaque middleware
* de droite à gauche vers le joueur.
*
* @param {Objet[]} middleware
* Un tableau d'instances d'intergiciels.
*
* @param {Tech} tech
* La technologie actuelle.
*
* @param {string} method
* Un nom de méthode.
*
* @return {Mixed}
* La valeur finale de la technologie après que l'intergiciel l'a interceptée.
*/
export function get(middleware, tech, method) {
return middleware.reduceRight(middlewareIterator(method), tech[method]()) ;
}
/**
* Prend l'argument donné au lecteur et appelle la méthode setter sur chaque
* middleware de gauche à droite jusqu'à la technologie.
*
* @param {Objet[]} middleware
* Un tableau d'instances d'intergiciels.
*
* @param {Tech} tech
* La technologie actuelle.
*
* @param {string} method
* Un nom de méthode.
*
* @param {Mixed} arg
* La valeur à définir sur le tech.
*
* @return {Mixed}
* La valeur de retour de la `méthode` de la `technique`.
*/
export function set(middleware, tech, method, arg) {
return tech[method](middleware.reduce(middlewareIterator(method), arg)) ;
}
/**
* Prend l'argument donné au joueur et appelle la version `call` de la fonction
* sur chaque logiciel intermédiaire, de gauche à droite.
*
* Ensuite, appeler la méthode passée sur le tech et renvoyer le résultat inchangé
* au joueur, par l'intermédiaire d'un logiciel intermédiaire, cette fois-ci de droite à gauche.
*
* @param {Objet[]} middleware
* Un tableau d'instances d'intergiciels.
*
* @param {Tech} tech
* La technologie actuelle.
*
* @param {string} method
* Un nom de méthode.
*
* @param {Mixed} arg
* La valeur à définir sur le tech.
*
* @return {Mixed}
* La valeur de retour de la `méthode` de la `tech`, quelle que soit la valeur de retour de la `tech`
* les valeurs de retour des logiciels intermédiaires.
*/
export function mediate(middleware, tech, method, arg = null) {
const callMethod = 'call' + toTitleCase(method) ;
const middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg) ;
const terminated = middlewareValue === TERMINATOR ;
// déprécié. La valeur de retour `null` devrait plutôt renvoyer TERMINATOR à
// éviter toute confusion si une méthode techs renvoie en fait un résultat nul.
const returnValue = terminated ? null : tech[method](middlewareValue) ;
executeRight(middleware, method, returnValue, terminated) ;
returnValue ;
}
/**
* Enumération de getters autorisés dont les clés sont des noms de méthodes.
*
* @type {Objet}
*/
export const allowedGetters = {
mis en mémoire tampon : 1,
l'heure actuelle : 1,
durée: 1,
en sourdine : 1,
a joué : 1,
pausé : 1,
recherchable : 1,
volume : 1,
terminé : 1
};
/**
* Enumération de paramètres autorisés dont les clés sont des noms de méthodes.
*
* @type {Objet}
*/
export const allowedSetters = {
setCurrentTime : 1,
setMuted : 1,
setVolume : 1
};
/**
* Enumération de médiateurs autorisés dont les clés sont des noms de méthodes.
*
* @type {Objet}
*/
export const allowedMediators = {
jouer : 1,
pause : 1
};
function middlewareIterator(method) {
return (value, mw) => {
// si l'intergiciel précédent s'est terminé, transmettre la fin de l'intergiciel
if (value === TERMINATOR) {
retour TERMINATOR ;
}
if (mw[method]) {
return mw[method](value) ;
}
valeur de retour ;
};
}
function executeRight(mws, method, value, terminated) {
for (let i = mws.length - 1 ; i >= 0 ; i--) {
const mw = mws[i] ;
if (mw[method]) {
mw[method](terminated, value) ;
}
}
}
/**
* Efface le cache de l'intergiciel d'un lecteur.
*
* @param {Player} player
* Une instance {@link Player}.
*/
export function clearCacheForPlayer(player) {
middlewareInstances[player.id()] = null ;
}
/**
* {
* [playerId] : [[mwFactory, mwInstance], ...]
* }
*
* @private
*/
function getOrCreateFactory(player, mwFactory) {
const mws = middlewareInstances[player.id()] ;
let mw = null ;
if (mws === undefined || mws === null) {
mw = mwFactory(player) ;
middlewareInstances[player.id()] = [[mwFactory, mw]] ;
retour mw ;
}
for (let i = 0 ; i < mws.length ; i++) {
const [mwf, mwi] = mws[i] ;
if (mwf !== mwFactory) {
continuer ;
}
mw = mwi ;
}
if (mw === null) {
mw = mwFactory(player) ;
mws.push([mwFactory, mw]) ;
}
retour mw ;
}
function setSourceHelper(src = {}, middleware = [], next, player, acc = [], lastRun = false) {
const [mwFactory, ...mwrest] = middleware ;
// si mwFactory est une chaîne de caractères, nous sommes à la croisée des chemins
if (typeof mwFactory === 'string') {
setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun) ;
// si nous avons une mwFactory, appelons-la avec le joueur pour obtenir le mw,
// puis appeler la méthode setSource du mw
else if (mwFactory) {
const mw = getOrCreateFactory(player, mwFactory) ;
// si setSource n'est pas présent, sélectionner implicitement cet intergiciel
if (!mw.setSource) {
acc.push(mw) ;
return setSourceHelper(src, mwrest, next, player, acc, lastRun) ;
}
mw.setSource(assign({}, src), function(err, _src) {
// il s'est passé quelque chose, essayer l'intergiciel suivant au niveau actuel
// s'assurer d'utiliser l'ancien src
if (err) {
return setSourceHelper(src, mwrest, next, player, acc, lastRun) ;
}
// nous avons réussi, il faut maintenant aller plus loin
acc.push(mw) ;
// s'il s'agit du même type, poursuivre la chaîne en cours
// sinon, nous voulons descendre dans la nouvelle chaîne
setSourceHelper(
_src,
src.type === _src.type ? mwrest : middlewares[_src.type],
suivant,
joueur,
acc,
dernière exécution
) ;
}) ;
else if (mwrest.length) {
setSourceHelper(src, mwrest, next, player, acc, lastRun) ;
} else if (lastRun) {
next(src, acc) ;
} else {
setSourceHelper(src, middlewares['*'], next, player, acc, true) ;
}
}