/**
 * @file html5.js
 */
import Tech from './tech.js' ;
import * as Dom from '../utils/dom.js' ;
import * as Url from '../utils/url.js' ;
import log from '../utils/log.js' ;
import * as browser from '../utils/browser.js' ;
import document from 'global/document' ;
import window from 'global/window' ;
import {assign} from '../utils/obj' ;
import mergeOptions from '../utils/merge-options.js' ;
import {toTitleCase} de '../utils/string-cases.js' ;
import {NORMAL as TRACK_TYPES, REMOTE} from '../tracks/track-types' ;
import setupSourceset from './setup-sourceset' ;
import defineLazyProperty de '../utils/define-lazy-property.js' ;
import {silencePromesse} from '../utils/promesse' ;

/**
 * Contrôleur HTML5 Media - Enveloppe pour l'API HTML5 Media
 *
 * @mixes Tech~SourceHandlerAdditions
 * @extends Tech
 */
class Html5 extends Tech {

  /**
  * Créer une instance de ce Tech.
  *
  * @param {Objet} [options]
  *        La mémoire clé/valeur des options du lecteur.
  *
  * @param {Component~ReadyCallback} ready
  *        Fonction de rappel à appeler lorsque le Tech `HTML5` est prêt.
  */
  constructor(options, ready) {
    super(options, ready) ;

    const source = options.source ;
    let crossoriginTracks = false ;

    this.featuresVideoFrameCallback = this.featuresVideoFrameCallback && this.el_.tagName === 'VIDEO' ;

    // Définir la source si elle est fournie
    // 1) Vérifier si la source est nouvelle (si ce n'est pas le cas, nous voulons conserver l'original pour que la lecture ne soit pas interrompue)
    // 2) Vérifier si l'état du réseau de l'étiquette a échoué lors de l'initialisation, et si c'est le cas, réinitialiser la source
    // de toute façon pour que l'erreur soit déclenchée.
    if (source && (this.el_.currentSrc !== source.src || (options.tag && options.tag.initNetworkState_ === 3))) {
      this.setSource(source) ;
    } else {
      this.handleLateInit_(this.el_) ;
    }

    // mise en place d'un jeu de sources après un jeu de sources tardif/init
    if (options.enableSourceset) {
      this.setupSourcesetHandling_() ;
    }

    this.isScrubbing_ = false ;

    if (this.el_.hasChildNodes()) {

      const nodes = this.el_.childNodes ;
      let nodesLength = nodes.length ;
      const removeNodes = [] ;

      while (nodesLength--) {
        const node = nodes[nodesLength] ;
        const nodeName = node.nodeName.toLowerCase() ;

        if (nodeName === 'track') {
          if (!this.featuresNativeTextTracks) {
            // Vider les pistes de balises vidéo afin que le lecteur intégré ne les utilise pas également.
            // Cela peut ne pas être assez rapide pour empêcher les navigateurs HTML5 de lire les balises
            // nous devrons donc désactiver toutes les pistes par défaut si nous le faisons manuellement
            // sous-titres et légendes. videoElement.textTracks
            removeNodes.push(node) ;
          } else {
            // enregistrer HTMLTrackElement et TextTrack dans la liste distante
            this.remoteTextTrackEls().addTrackElement_(node) ;
            this.remoteTextTracks().addTrack(node.track) ;
            this.textTracks().addTrack(node.track) ;
            if (!crossoriginTracks &&
                !this.el_.hasAttribute('crossorigin') &&
                Url.isCrossOrigin(node.src)) {
              crossoriginTracks = true ;
            }
          }
        }
      }

      for (let i = 0 ; i < removeNodes.length ; i++) {
        this.el_.removeChild(removeNodes[i]) ;
      }
    }

    this.proxyNativeTracks_() ;
    if (this.featuresNativeTextTracks && crossoriginTracks) {
      log.warn('Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.\N' +
            ceci peut empêcher le chargement des pistes de texte") ;
    }

    // empêcher Safari iOS de désactiver les pistes de texte de métadonnées pendant la lecture native
    this.restoreMetadataTracksInIOSNativePlayer_() ;

    // Déterminer si les contrôles natifs doivent être utilisés
    // Notre objectif devrait être d'obtenir les contrôles personnalisés sur les mobiles solides partout
    // afin que nous puissions le supprimer. Pour l'instant, cela bloque la personnalisation
    // contrôles sur les ordinateurs portables tactiles comme le Chrome Pixel
    if ((browser.TOUCH_ENABLED || browser.IS_IPHONE ||
        browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
      this.setControls(true) ;
    }

    // sur iOS, nous voulons proxyer `webkitbeginfullscreen` et `webkitendfullscreen`
    // dans un événement `fullscreenchange` (changement d'écran)
    this.proxyWebkitFullscreen_() ;

    this.triggerReady() ;
  }

  /**
   * Dispose de l'élément média `HTML5` et supprime toutes les pistes.
   */
  dispose() {
    if (this.el_ && this.el_.resetSourceset_) {
      this.el_.resetSourceset_() ;
    }
    Html5.disposeMediaElement(this.el_) ;
    this.options_ = null ;

    // tech s'occupera de l'effacement de la liste des pistes émulées
    super.dispose() ;
  }

  /**
   * Modifiez l'élément media de manière à pouvoir détecter le moment où
   * la source est modifiée. Déclenche `sourceset` juste après que la source ait changé
   */
  setupSourcesetHandling_() {
    setupSourceset(this) ;
  }

  /**
   * Lorsqu'une piste de sous-titres est activée dans le lecteur natif Safari d'iOS, toutes les autres pistes de sous-titres sont désactivées
   * sont désactivées (y compris les pistes de métadonnées), ce qui annule toutes leurs pistes de
   * les points de repère associés. Cette opération rétablit les pistes de métadonnées dans l'état où elles se trouvaient avant le plein écran
   * dans ces cas-là, afin de ne pas perdre inutilement des points de repère.
   *
   * @private
   */
  restoreMetadataTracksInIOSNativePlayer_() {
    const textTracks = this.textTracks() ;
    let metadataTracksPreFullscreenState ;

    // capture un instantané de l'état actuel de chaque piste de métadonnées
    const takeMetadataTrackSnapshot = () => {
      metadataTracksPreFullscreenState = [] ;

      for (let i = 0 ; i < textTracks.length ; i++) {
        const track = textTracks[i] ;

        if (track.kind === 'metadata') {
          metadataTracksPreFullscreenState.push({
            la voie ferrée,
            storedMode : track.mode
          }) ;
        }
      }
    };

    // prendre un instantané de l'état initial de chaque piste de métadonnées et mettre à jour l'instantané
    // à chaque fois qu'il y a un événement de "changement" de piste
    takeMetadataTrackSnapshot() ;
    textTracks.addEventListener('change', takeMetadataTrackSnapshot) ;

    this.on('dispose', () => textTracks.removeEventListener('change', takeMetadataTrackSnapshot)) ;

    const restoreTrackMode = () => {
      for (let i = 0 ; i < metadataTracksPreFullscreenState.length ; i++) {
        const storedTrack = metadataTracksPreFullscreenState[i] ;

        if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
          storedTrack.track.mode = storedTrack.storedMode ;
        }
      }
      // nous voulons que ce gestionnaire soit exécuté uniquement lors du premier événement "change"
      textTracks.removeEventListener('change', restoreTrackMode) ;
    };

    // lorsque nous passons en lecture plein écran, nous arrêtons de mettre à jour l'instantané et nous nous concentrons sur les éléments suivants
    // rétablir tous les modes de piste dans leur état d'avant le plein écran
    this.on('webkitbeginfullscreen', () => {
      textTracks.removeEventListener('change', takeMetadataTrackSnapshot) ;

      // supprimer l'écouteur avant de l'ajouter, au cas où il n'aurait pas été supprimé auparavant
      textTracks.removeEventListener('change', restoreTrackMode) ;
      textTracks.addEventListener('change', restoreTrackMode) ;
    }) ;

    // recommencer à mettre à jour l'instantané après avoir quitté le plein écran
    this.on('webkitendfullscreen', () => {
      // supprimer l'écouteur avant de l'ajouter, au cas où il n'aurait pas été supprimé auparavant
      textTracks.removeEventListener('change', takeMetadataTrackSnapshot) ;
      textTracks.addEventListener('change', takeMetadataTrackSnapshot) ;

      // supprimer le gestionnaire restoreTrackMode au cas où il n'aurait pas été déclenché pendant la lecture en plein écran
      textTracks.removeEventListener('change', restoreTrackMode) ;
    }) ;
  }

  /**
   * Tentative de forcer le remplacement des pistes pour le type donné
   *
   * @param {string} type - Type de piste à remplacer, les valeurs possibles étant notamment "Audio",
   * vidéo" et "Texte".
   * @param {boolean} override - Si la valeur est fixée à true, les données audio/vidéo natives seront remplacées,
   * sinon, le son/vidéo natif sera potentiellement utilisé.
   * @private
   */
  overrideNative_(type, override) {
    // S'il n'y a pas de changement de comportement, n'ajoutez/supprimez pas d'écouteurs
    if (override !== this[`featuresNative${type}Tracks`]) {
      retour ;
    }

    const lowerCaseType = type.toLowerCase() ;

    if (this[`${lowerCaseType}TracksListeners_`]) {
      Object.keys(this[`${lowerCaseType}TracksListeners_`]).forEach((eventName) => {
        const elTracks = this.el()[`${lowerCaseType}Tracks`] ;

        elTracks.removeEventListener(eventName, this[`${lowerCaseType}TracksListeners_`][eventName]) ;
      }) ;
    }

    this[`featuresNative${type}Tracks`] = !override ;
    this[`${lowerCaseType}TracksListeners_`] = null ;

    this.proxyNativeTracksForType_(lowerCaseType) ;
  }

  /**
   * Tentative de forcer le remplacement des pistes audio natives.
   *
   * @param {boolean} override - Si la valeur est fixée à true, l'audio natif sera remplacé,
   * sinon le son natif sera potentiellement utilisé.
   */
  overrideNativeAudioTracks(override) {
    this.overrideNative_('Audio', override) ;
  }

  /**
   * Tentative de forcer le remplacement des pistes vidéo natives.
   *
   * @param {boolean} override - Si la valeur est fixée à true, la vidéo native sera remplacée,
   * dans le cas contraire, la vidéo native sera potentiellement utilisée.
   */
  overrideNativeVideoTracks(override) {
    this.overrideNative_('Video', override) ;
  }

  /**
   * Proxy des événements natifs de la liste des pistes pour le type donné vers notre piste
   * si le navigateur dans lequel nous jouons prend en charge ce type de liste de pistes.
   *
   * @param {string} name - Type de piste ; les valeurs incluent "audio", "vidéo" et "texte"
   * @private
   */
  proxyNativeTracksForType_(name) {
    const props = TRACK_TYPES[name] ;
    const elTracks = this.el()[props.getterName] ;
    const techTracks = this[props.getterName]() ;

    if (!this[`featuresNative${props.capitalName}Tracks`] ||
        !elTracks ||
        !elTracks.addEventListener) {
      retour ;
    }
    const listeners = {
      changement : (e) => {
        const event = {
          type : "change",
          cible : techTracks,
          cible actuelle : techTracks,
          srcElement : techTracks
        };

        techTracks.trigger(event) ;

        // s'il s'agit d'un événement de changement de piste de texte, nous devons également notifier l'événement de changement de piste de texte
        // liste des pistes de texte à distance. Cela peut potentiellement provoquer un faux positif
        // si nous recevions un événement de changement sur une piste non distante et que
        // nous avons déclenché l'événement sur la liste des textes distants, qui n'est pas
        // contiennent cette piste. Cependant, les meilleures pratiques impliquent que l'on passe en boucle par le
        // liste des pistes et recherche de la valeur de mode appropriée, donc,
        // cela ne devrait pas poser de problème
        if (name === 'text') {
          this[REMOTE.remoteText.getterName]().trigger(event) ;
        }
      },
      addtrack(e) {
        techTracks.addTrack(e.track) ;
      },
      removetrack(e) {
        techTracks.removeTrack(e.track) ;
      }
    };
    const removeOldTracks = function() {
      const removeTracks = [] ;

      for (let i = 0 ; i < techTracks.length ; i++) {
        let found = false ;

        for (let j = 0 ; j < elTracks.length ; j++) {
          if (elTracks[j] === techTracks[i]) {
            trouvé = vrai ;
            pause ;
          }
        }

        if (!found) {
          removeTracks.push(techTracks[i]) ;
        }
      }

      while (removeTracks.length) {
        techTracks.removeTrack(removeTracks.shift()) ;
      }
    };

    this[props.getterName + 'Listeners_'] = listeners ;

    Object.keys(listeners).forEach((eventName) => {
      const listener = listeners[eventName] ;

      elTracks.addEventListener(eventName, listener) ;
      this.on('dispose', (e) => elTracks.removeEventListener(eventName, listener)) ;
    }) ;

    // Supprimer les pistes (natives) qui ne sont plus utilisées
    this.on('loadstart', removeOldTracks) ;
    this.on('dispose', (e) => this.off('loadstart', removeOldTracks)) ;
  }

  /**
   * Proxy de tous les événements natifs de la liste des pistes vers nos listes de pistes si le navigateur dans lequel nous jouons
   * prend en charge ce type de liste de pistes.
   *
   * @private
   */
  proxyNativeTracks_() {
    TRACK_TYPES.names.forEach((name) => {
      this.proxyNativeTracksForType_(name) ;
    }) ;
  }

  /**
   * Créer l'élément DOM de la Tech `Html5`.
   *
   * @return {Element}
   *         L'élément qui est créé.
   */
  createEl() {
    let el = this.options_.tag ;

    // Vérifier si ce navigateur prend en charge le déplacement de l'élément dans la boîte.
    // Sur l'iPhone, la vidéo s'interrompra si vous déplacez l'élément,
    // Nous devons donc créer un tout nouvel élément.
    // Si nous avons intégré la div player, il n'est pas nécessaire de déplacer l'élément media.
    if (!el ||
        !(this.options_.playerElIngest ||
          this.movingMediaElementInDOM)) {

      // Si la balise originale est toujours présente, la cloner et la supprimer.
      if (el) {
        const clone = el.cloneNode(true) ;

        if (el.parentNode) {
          el.parentNode.insertBefore(clone, el) ;
        }
        Html5.disposeMediaElement(el) ;
        el = clone ;

      } else {
        el = document.createElement('video') ;

        // déterminer si les contrôles natifs doivent être utilisés
        const tagAttributes = this.options_.tag && Dom.getAttributes(this.options_.tag) ;
        const attributes = mergeOptions({}, tagAttributes) ;

        if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
          supprimer les attributs.controls ;
        }

        Dom.setAttributes(
          el,
          assign(attributes, {
            id : this.options_.techId,
            classe : 'vjs-tech'
          })
        ) ;
      }

      el.playerId = this.options_.playerId ;
    }

    if (typeof this.options_.preload !== 'undefined') {
      Dom.setAttribute(el, 'preload', this.options_.preload) ;
    }

    if (this.options_.disablePictureInPicture !== undefined) {
      el.disablePictureInPicture = this.options_.disablePictureInPicture ;
    }

    // Mise à jour des paramètres spécifiques de la balise, au cas où ils auraient été remplacés
    // `autoplay` doit être *dernier* pour que `muted` et `playsinline` soient présents
    // lorsque iOS/Safari ou d'autres navigateurs tentent d'effectuer une lecture automatique.
    const settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'] ;

    for (let i = 0 ; i < settingsAttrs.length ; i++) {
      const attr = settingsAttrs[i] ;
      const value = this.options_[attr] ;

      if (typeof value !== 'undefined') {
        if (value) {
          Dom.setAttribute(el, attr, attr) ;
        } else {
          Dom.removeAttribute(el, attr) ;
        }
        el[attr] = valeur ;
      }
    }

    return el ;
  }

  /**
   * Ceci sera déclenché si l'événement loadstart s'est déjà déclenché, avant que videojs ne soit
   * prêt. Voici deux exemples connus de cas où cela peut se produire :
   * 1. Si nous chargeons l'objet de lecture après le chargement
   * 2. Le média est déjà en train de lire (souvent avec la lecture automatique) puis
   *
   * Cette fonction déclenchera un autre chargement pour que videojs puisse rattraper son retard.
   *
   * @fires Tech#loadstart
   *
   * @return {undefined}
   *         ne renvoie rien.
   */
  handleLateInit_(el) {
    if (el.networkState === 0 || el.networkState === 3) {
      // L'élément vidéo n'a pas encore commencé à charger la source
      // ou n'a pas trouvé de source
      retour ;
    }

    if (el.readyState === 0) {
      // L'état du réseau est défini de manière synchrone MAIS loadstart est déclenché au moment de l'activation de l'état du réseau
      // fin de la pile actuelle, généralement avant setInterval(fn, 0).
      // A ce stade, nous savons que loadstart s'est peut-être déjà déclenché ou qu'il est en cours d'exécution
      // sur le point de se déclencher, et dans tous les cas, le joueur ne l'a pas encore vu.
      // Nous ne voulons pas déclencher prématurément le démarrage de la charge et provoquer un
      // double loadstart donc nous attendons de voir si cela se produit entre-temps
      // et la boucle suivante, et la déclenche si ce n'est pas le cas.
      // CEPENDANT, nous voulons aussi nous assurer qu'il se déclenche avant loadedmetadata
      // ce qui pourrait également se produire entre maintenant et la prochaine boucle, nous allons donc
      // à surveiller également.
      let loadstartFired = false ;
      const setLoadstartFired = function() {
        loadstartFired = true ;
      };

      this.on('loadstart', setLoadstartFired) ;

      const triggerLoadstart = function() {
        // Nous avons manqué le démarrage de la charge d'origine. Assurez-vous que le lecteur
        // voit loadstart avant loadedmetadata
        if (!loadstartFired) {
          this.trigger('loadstart') ;
        }
      };

      this.on('loadedmetadata', triggerLoadstart) ;

      this.ready(function() {
        this.off('loadstart', setLoadstartFired) ;
        this.off('loadedmetadata', triggerLoadstart) ;

        if (!loadstartFired) {
          // Le démarrage natif de la charge nous a manqué. Tirez tout de suite.
          this.trigger('loadstart') ;
        }
      }) ;

      retour ;
    }

    // A partir de là, nous savons que loadstart s'est déjà déclenché et que nous l'avons manqué.
    // Les autres événements readyState ne posent pas autant de problèmes si nous doublons les événements readyState avec les événements readyState
    //, et ne se donnera donc pas autant de mal que loadstart pour empêcher les
    // que si nous ne trouvons pas de raison de le faire.
    const eventsToTrigger = ['loadstart'] ;

    // loadedmetadata : nouvellement égal à HAVE_METADATA (1) ou supérieur
    eventsToTrigger.push('loadedmetadata') ;

    // données chargées : nouvellement augmentées à HAVE_CURRENT_DATA (2) ou plus
    if (el.readyState >= 2) {
      eventsToTrigger.push('loadeddata') ;
    }

    // canplay : nouvellement augmenté à HAVE_FUTURE_DATA (3) ou supérieur
    if (el.readyState >= 3) {
      eventsToTrigger.push('canplay') ;
    }

    // canplaythrough : nouvellement égal à HAVE_ENOUGH_DATA (4)
    if (el.readyState >= 4) {
      eventsToTrigger.push('canplaythrough') ;
    }

    // Nous devons encore donner au joueur le temps d'ajouter des récepteurs d'événements
    this.ready(function() {
      eventsToTrigger.forEach(function(type) {
        this.trigger(type) ;
      }, this) ;
    }) ;
  }

  /**
   * Permet de déterminer si l'on procède à un nettoyage ou non.
   * Ceci est utilisé pour décider si nous devons utiliser `fastSeek` ou non.
   * `fastSeek` est utilisé pour fournir un jeu d'astuces sur les navigateurs Safari.
   *
   * @param {boolean} isScrubbing
   *                  - vrai pour nous sommes en train de nettoyer
   *                  - faux car nous ne frottons plus
   */
  setScrubbing(isScrubbing) {
    this.isScrubbing_ = isScrubbing ;
  }

  /**
   * Obtenir que l'on frotte ou non.
   *
   * @return {boolean} isScrubbing
   *                  - vrai pour nous sommes en train de nettoyer
   *                  - faux car nous ne frottons plus
   */
  scrubbing() {
    return this.isScrubbing_ ;
  }

  /**
   * Fixe l'heure actuelle pour la technologie `HTML5`.
   *
   * @param {number} seconds
   *        Régler l'heure actuelle du média sur cette valeur.
   */
  setCurrentTime(seconds) {
    essayez {
      if (this.isScrubbing_ && this.el_.fastSeek && browser.IS_ANY_SAFARI) {
        this.el_.fastSeek(seconds) ;
      } else {
        this.el_.currentTime = seconds ;
      }
    } catch (e) {
      log(e, 'La vidéo n'est pas prête. (Video.js)') ;
      // this.warning(VideoJS.warnings.videoNotReady) ;
    }
  }

  /**
   * Obtenir la durée actuelle de l'élément multimédia HTML5.
   *
   * @return {number}
   *         La durée du média ou 0 s'il n'y a pas de durée.
   */
  duration() {
    // Android Chrome indiquera la durée comme étant l'infini pour la VOD HLS jusqu'à ce qu'elle soit terminée
    // la lecture a commencé, ce qui déclenche par erreur l'affichage en direct.
    // Retourne NaN si la lecture n'a pas commencé et déclenche une mise à jour de la durée une fois
    // la durée peut être connue de manière fiable.
    si (
      this.el_.duration === Infinity &&
      navigateur.IS_ANDROID &&
      navigateur.IS_CHROME &&
      this.el_.currentTime === 0
    ) {
      // Attendez la première `timeupdate` avec currentTime > 0 - il peut y avoir des `timeupdate`
      // plusieurs avec 0
      const checkProgress = () => {
        if (this.el_.currentTime > 0) {
          // Déclenchement d'un changement de durée pour une véritable vidéo en direct
          if (this.el_.duration === Infinity) {
            this.trigger('durationchange') ;
          }
          this.off('timeupdate', checkProgress) ;
        }
      };

      this.on('timeupdate', checkProgress) ;
      renvoie NaN ;
    }
    return this.el_.duration || NaN ;
  }

  /**
   * Obtenir la largeur actuelle de l'élément HTML5 media.
   *
   * @return {number}
   *         La largeur de l'élément HTML5 media.
   */
  width() {
    return this.el_.offsetWidth ;
  }

  /**
   * Obtenir la hauteur actuelle de l'élément HTML5 media.
   *
   * @return {number}
   *         La hauteur de l'élément HTML5 media.
   */
  height() {
    return this.el_.offsetHeight ;
  }

  /**
   * Proxy iOS `webkitbeginfullscreen` et `webkitendfullscreen` en
   * événement `fullscreenchange`.
   *
   * @private
   * @fires fullscreenchange
   * @listens webkitendfullscreen
   * @listens webkitbeginfullscreen
   * @listens webkitbeginfullscreen
   */
  proxyWebkitFullscreen_() {
    if ( !('webkitDisplayingFullscreen' in this.el_)) {
      retour ;
    }

    const endFn = function() {
      this.trigger('fullscreenchange', { isFullscreen : false }) ;
      // Safari définit parfois des contols sur l'élément vidéo lorsqu'il existe en plein écran.
      if (this.el_.controls && !this.options_.nativeControlsForTouch && this.controls()) {
        this.el_.controls = false ;
      }
    };

    const beginFn = function() {
      if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
        this.one('webkitendfullscreen', endFn) ;

        this.trigger('fullscreenchange', {
          isFullscreen : true,
          // mettre un drapeau au cas où un autre technicien déclencherait un changement d'écran complet
          nativeIOSFullscreen : true
        }) ;
      }
    };

    this.on('webkitbeginfullscreen', beginFn) ;
    this.on('dispose', () => {
      this.off('webkitbeginfullscreen', beginFn) ;
      this.off('webkitendfullscreen', endFn) ;
    }) ;
  }

  /**
   * Vérifier si le plein écran est pris en charge par l'appareil de lecture actuel.
   *
   * @return {boolean}
   *         - Vrai si le plein écran est pris en charge.
   *         - Faux si le plein écran n'est pas pris en charge.
   */
  supportsFullScreen() {
    if (typeof this.el_.webkitEnterFullScreen === 'function') {
      const userAgent = window.navigator && window.navigator.userAgent || '' ;

      // Semble être cassé dans Chromium/Chrome && Safari dans Leopard
      if ((/Android/).test(userAgent) || !(/Chrome|Mac OS X 10.5/).test(userAgent)) {
        retourner vrai ;
      }
    }
    retourner faux ;
  }

  /**
   * Demande que le Tech `HTML5` entre en plein écran.
   */
  enterFullScreen() {
    const video = this.el_ ;

    if (video.paused && video.networkState <= video.HAVE_METADATA) {
      // tentative d'amorcer l'élément vidéo pour un accès programmatique
      // ce n'est pas nécessaire sur le bureau mais cela ne devrait pas nuire
      silencePromesse(this.el_.play()) ;

      // lecture et pause synchronisées pendant la transition vers le plein écran
      // peut faire entrer les appareils iOS ~6.1 dans une boucle de lecture/pause
      this.setTimeout(function() {
        video.pause() ;
        essayez {
          video.webkitEnterFullScreen() ;
        } catch (e) {
          this.trigger('fullscreenerror', e) ;
        }
      }, 0) ;
    } else {
      essayez {
        video.webkitEnterFullScreen() ;
      } catch (e) {
        this.trigger('fullscreenerror', e) ;
      }
    }
  }

  /**
   * Demande que le Tech `HTML5` quitte le plein écran.
   */
  exitFullScreen() {
    if (!this.el_.webkitDisplayingFullscreen) {
      this.trigger('fullscreenerror', new Error('La vidéo n'est pas en plein écran')) ;
      retour ;
    }

    this.el_.webkitExitFullScreen() ;
  }

  /**
   * Créer une fenêtre vidéo flottante toujours au-dessus des autres fenêtres afin que les utilisateurs puissent
   * continuer à consommer des médias tout en interagissant avec d'autres sites de contenu, ou
   * sur leur appareil.
   *
   * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
   *
   * @return {Promesse}
   *         Une promesse avec une fenêtre d'image dans l'image.
   */
  requestPictureInPicture() {
    return this.el_.requestPictureInPicture() ;
  }

  /**
   * RequestVideoFrameCallback native si elle est prise en charge par le navigateur/la technologie, ou fallback
   * N'utilisez pas rVCF sur Safari lorsque le DRM est en cours de lecture, car il ne se déclenche pas
   * Doit être vérifié après le constructeur
   * Il s'agira d'un faux positif pour les sources claires chargées après une source Fairplay
   *
   * @param {fonction} fonction cb à appeler
   * @return {number} id of request
   */
  requestVideoFrameCallback(cb) {
    if (this.featuresVideoFrameCallback && !this.el_.webkitKeys) {
      return this.el_.requestVideoFrameCallback(cb) ;
    }
    return super.requestVideoFrameCallback(cb) ;
  }

  /**
   * Native ou fallback requestVideoFrameCallback
   *
   * @param {number} id id de la demande à annuler
   */
  cancelVideoFrameCallback(id) {
    if (this.featuresVideoFrameCallback && !this.el_.webkitKeys) {
      this.el_.cancelVideoFrameCallback(id) ;
    } else {
      super.cancelVideoFrameCallback(id) ;
    }
  }

  /**
   * Un getter/setter pour l'objet source de `Html5` Tech.
   * > Remarque : Veuillez utiliser {@link Html5#setSource}
   *
   * @param {Tech~SourceObject} [src]
   *        L'objet source que vous voulez placer sur l'élément techs `HTML5`.
   *
   * @return {Tech~SourceObject|undefined}
   *         - L'objet source actuel lorsqu'aucune source n'est transmise.
   *         - indéfinie lors de la mise en place
   *
   * @deprecated Depuis la version 5.
   */
  src(src) {
    if (src === undefined) {
      return this.el_.src ;
    }

    // Le réglage de src par `src` au lieu de `setSrc` sera déprécié
    this.setSrc(src) ;
  }

  /**
   * Réinitialisez le technicien en supprimant toutes les sources et en appelant ensuite
   * {@link Html5.resetMediaElement}.
   */
  reset() {
    Html5.resetMediaElement(this.el_) ;
  }

  /**
   * Obtenir la source actuelle sur le Tech `HTML5`. Renvoie au retour de la source de
   * l'élément HTML5 media.
   *
   * @return {Tech~SourceObject}
   *         L'objet source actuel de la technologie HTML5. Avec un repli sur la
   *         source d'éléments.
   */
  currentSrc() {
    if (this.currentSource_) {
      return this.currentSource_.src ;
    }
    return this.el_.currentSrc ;
  }

  /**
   * Définir l'attribut de contrôle pour l'élément HTML5 media.
   *
   * @param {string} val
   *        Valeur à attribuer à l'attribut controls
   */
  setControls(val) {
    this.el_.controls = !!val ;
  }

  /**
   * Crée et renvoie un objet {@link TextTrack} distant.
   *
   * @param {string} kind
   *        type de `TextTrack` (sous-titres, légendes, descriptions, chapitres ou métadonnées)
   *
   * @param {string} [label]
   *        Étiquette pour identifier la piste de texte
   *
   * @param {string} [langue]
   *        Abréviation linguistique à deux lettres
   *
   * @return {TextTrack}
   *         La piste de texte qui est créée.
   */
  addTextTrack(kind, label, language) {
    if (!this.featuresNativeTextTracks) {
      return super.addTextTrack(kind, label, language) ;
    }

    return this.el_.addTextTrack(kind, label, language) ;
  }

  /**
   * Crée soit un TextTrack natif, soit un TextTrack émulé dépendant de l'application
   * sur la valeur de `featuresNativeTextTracks`
   *
   * @param {Objet} options
   *        L'objet doit contenir les options permettant d'initialiser la piste de texte.
   *
   * @param {string} [options.kind]
   *        type de `TextTrack` (sous-titres, légendes, descriptions, chapitres ou métadonnées).
   *
   * @param {string} [options.label]
   *        Étiquette pour identifier la piste de texte
   *
   * @param {string} [options.language]
   *        Abréviation linguistique de deux lettres.
   *
   * @param {boolean} [options.default]
   *        Cette piste est activée par défaut.
   *
   * @param {string} [options.id]
   *        L'identifiant interne à attribuer à cette piste.
   *
   * @param {string} [options.src]
   *        Une source url pour le titre.
   *
   * @return {HTMLTrackElement}
   *         L'élément de piste qui est créé.
   */
  createRemoteTextTrack(options) {
    if (!this.featuresNativeTextTracks) {
      return super.createRemoteTextTrack(options) ;
    }
    const htmlTrackElement = document.createElement('track') ;

    if (options.kind) {
      htmlTrackElement.kind = options.kind ;
    }
    if (options.label) {
      htmlTrackElement.label = options.label ;
    }
    if (options.language || options.srclang) {
      htmlTrackElement.srclang = options.language || options.srclang ;
    }
    if (options.default) {
      htmlTrackElement.default = options.default ;
    }
    if (options.id) {
      htmlTrackElement.id = options.id ;
    }
    if (options.src) {
      htmlTrackElement.src = options.src ;
    }

    return htmlTrackElement ;
  }

  /**
   * Crée un objet de piste de texte distant et renvoie un élément de piste html.
   *
   * @param {Object} options L'objet doit contenir des valeurs pour
   * type, langue, étiquette et src (emplacement du fichier WebVTT)
   * @param {boolean} [manualCleanup=true] si la valeur est false, le TextTrack sera nettoyé à la main
   * automatiquement supprimée de l'élément vidéo lorsque la source change
   * @return {HTMLTrackElement} Un élément de piste Html.
   * Il peut s'agir d'un {@link HTMLTrackElement} émulé ou natif.
   * @deprecated La valeur par défaut du paramètre "manualCleanup" sera par défaut
   * à "false" dans les prochaines versions de Video.js
   */
  addRemoteTextTrack(options, manualCleanup) {
    const htmlTrackElement = super.addRemoteTextTrack(options, manualCleanup) ;

    if (this.featuresNativeTextTracks) {
      this.el().appendChild(htmlTrackElement) ;
    }

    return htmlTrackElement ;
  }

  /**
   * Supprimer la `TextTrack` distante de l'objet `TextTrackList`
   *
   * @param {TextTrack} track
   *        objet `TextTrack` à supprimer
   */
  removeRemoteTextTrack(track) {
    super.removeRemoteTextTrack(track) ;

    if (this.featuresNativeTextTracks) {
      const tracks = this.$$('track') ;

      let i = tracks.length ;

      while (i--) {
        if (track === tracks[i] || track === tracks[i].track) {
          this.el().removeChild(tracks[i]) ;
        }
      }
    }
  }

  /**
   * Obtient les mesures de qualité de lecture des médias disponibles, telles que spécifiées par la norme Media
   * Qualité de lecture API.
   *
   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
   *
   * @return {Object}
   *         Un objet contenant les mesures de qualité de lecture des médias prises en charge
   */
  getVideoPlaybackQuality() {
    if (typeof this.el().getVideoPlaybackQuality === 'function') {
      return this.el().getVideoPlaybackQuality() ;
    }

    const videoPlaybackQuality = {} ;

    if (typeof this.el().webkitDroppedFrameCount !== 'undefined') &&
        typeof this.el().webkitDecodedFrameCount !== 'undefined') {
      videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount ;
      videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount ;
    }

    if (window.performance && typeof window.performance.now === 'function') {
      videoPlaybackQuality.creationTime = window.performance.now() ;
    } else if (window.performance &&
               fenêtre.performance.timing &&
               typeof window.performance.timing.navigationStart === 'number') {
      videoPlaybackQuality.creationTime =
        window.Date.now() - window.performance.timing.navigationStart ;
    }

    return videoPlaybackQuality ;
  }
}

/* Test du support HTML5 ---------------------------------------------------- */

/**
 * Élément permettant de tester les capacités média du navigateur HTML5
 *
 * @type {Element}
 * @constant
 * @private
 */
defineLazyProperty(Html5, 'TEST_VID', function() {
  if (!Dom.isReal()) {
    retour ;
  }
  const video = document.createElement('video') ;
  const track = document.createElement('track') ;

  track.kind = 'captions' ;
  track.srclang = 'en' ;
  track.label = 'English' ;
  video.appendChild(track) ;

  retour vidéo ;
}) ;

/**
 * Vérifier si les médias HTML5 sont pris en charge par ce navigateur/appareil.
 *
 * @return {boolean}
 *         - True si les médias HTML5 sont pris en charge.
 *         - Faux si le média HTML5 n'est pas pris en charge.
 */
Html5.isSupported = function() {
  // IE sans Media Player est un menteur ! (#984)
  essayez {
    Html5.TEST_VID.volume = 0.5 ;
  } catch (e) {
    retourner faux ;
  }

  return ! !(Html5.TEST_VID && Html5.TEST_VID.canPlayType) ;
};

/**
 * Vérifier si le technicien peut prendre en charge le type donné
 *
 * @param {string} type
 *        Le type d'image à vérifier
 * @return {string} 'probablement', 'peut-être', ou '' (chaîne vide)
 */
Html5.canPlayType = function(type) {
  return Html5.TEST_VID.canPlayType(type) ;
};

/**
 * Vérifier si le technicien peut prendre en charge la source donnée
 *
 * @param {Objet} srcObj
 *        L'objet source
 * @param {Objet} options
 *        Les options transmises au tech
 * @return {string} 'probablement', 'peut-être', ou '' (chaîne vide)
 */
Html5.canPlaySource = function(srcObj, options) {
  return Html5.canPlayType(srcObj.type) ;
};

/**
 * Vérifier si le volume peut être modifié dans ce navigateur/appareil.
 * De nombreux appareils mobiles ne permettent pas de modifier le volume.
 * Plus précisément, il ne peut pas être modifié à partir de 1 sur iOS.
 *
 * @return {boolean}
 *         - Vrai si le volume peut être contrôlé
 *         - Faux sinon
 */
Html5.canControlVolume = function() {
  // IE se trompera si Windows Media Player n'est pas installé #3315
  essayez {
    const volume = Html5.TEST_VID.volume ;

    Html5.TEST_VID.volume = (volume / 2) + 0,1 ;

    const canControl = volume !== Html5.TEST_VID.volume ;

    // Avec l'introduction d'iOS 15, il y a des cas où le volume est lu comme
    // modifié mais revient à son état d'origine au début du prochain tick.
    // Déterminer si le volume peut être contrôlé sur iOS,
    // un délai d'attente est fixé et le volume est vérifié de manière asynchrone.
    // Comme `features` ne fonctionne pas actuellement de manière asynchrone, la valeur est définie manuellement.
    if (canControl && browser.IS_IOS) {
      window.setTimeout(() => {
        if (Html5 && Html5.prototype) {
          Html5.prototype.featuresVolumeControl = volume !== Html5.TEST_VID.volume ;
        }
      }) ;

      // La valeur par défaut d'iOS est false, qui sera mise à jour dans le timeout ci-dessus.
      retourner faux ;
    }

    return canControl ;
  } catch (e) {
    retourner faux ;
  }
};

/**
 * Vérifier si le volume peut être mis en sourdine dans ce navigateur/appareil.
 * Certains appareils, par exemple iOS, ne permettent pas de modifier le volume
 * mais permet l'inhibition/la désactivation.
 *
 * @return {bolean}
 *      - Vrai si le volume peut être mis en sourdine
 *      - Faux sinon
 */
Html5.canMuteVolume = function() {
  essayez {
    const muted = Html5.TEST_VID.muted ;

    // dans certaines versions d'iOS, la propriété "muted" ne fonctionne pas toujours
    // travail, nous voulons donc définir à la fois la propriété et l'attribut
    Html5.TEST_VID.muted = !muted ;
    if (Html5.TEST_VID.muted) {
      Dom.setAttribute(Html5.TEST_VID, 'muted', 'muted') ;
    } else {
      Dom.removeAttribute(Html5.TEST_VID, 'muted', 'muted') ;
    }
    return muted !== Html5.TEST_VID.muted ;
  } catch (e) {
    retourner faux ;
  }
};

/**
 * Vérifiez si la vitesse de lecture peut être modifiée dans ce navigateur/appareil.
 *
 * @return {boolean}
 *         - Vrai si la vitesse de lecture peut être contrôlée
 *         - Faux sinon
 */
Html5.canControlPlaybackRate = function() {
  // L'API de taux de lecture est implémentée dans Android Chrome, mais ne fait rien
  // https://github.com/videojs/video.js/issues/3180
  if (browser.IS_ANDROID && browser.IS_CHROME && browser.CHROME_VERSION < 58) {
    retourner faux ;
  }
  // IE se trompera si Windows Media Player n'est pas installé #3315
  essayez {
    const playbackRate = Html5.TEST_VID.playbackRate ;

    Html5.TEST_VID.playbackRate = (playbackRate / 2) + 0.1 ;
    return playbackRate !== Html5.TEST_VID.playbackRate ;
  } catch (e) {
    retourner faux ;
  }
};

/**
 * Vérifier si l'on peut remplacer les attributs d'un élément vidéo/audio, avec
 * Object.defineProperty.
 *
 * @return {boolean}
 *         - True si les attributs intégrés peuvent être remplacés
 *         - Faux sinon
 */
Html5.canOverrideAttributes = function() {
  // si nous ne pouvons pas écraser la propriété src/innerHTML, il n'y a pas de support
  // iOS 7 safari par exemple ne peut pas faire cela.
  essayez {
    const noop = () => {} ;

    Object.defineProperty(document.createElement('video'), 'src', {get : noop, set : noop}) ;
    Object.defineProperty(document.createElement('audio'), 'src', {get : noop, set : noop}) ;
    Object.defineProperty(document.createElement('video'), 'innerHTML', {get : noop, set : noop}) ;
    Object.defineProperty(document.createElement('audio'), 'innerHTML', {get : noop, set : noop}) ;
  } catch (e) {
    retourner faux ;
  }

  retourner vrai ;
};

/**
 * Vérifier si les `TextTrack`s natifs sont supportés par ce navigateur/appareil.
 *
 * @return {boolean}
 *         - True si les `TextTrack`s natifs sont supportés.
 *         - Faux sinon
 */
Html5.supportsNativeTextTracks = function() {
  return browser.IS_ANY_SAFARI || (browser.IS_IOS && browser.IS_CHROME) ;
};

/**
 * Vérifier si les `VideoTrack`s natifs sont supportés par ce navigateur/appareil
 *
 * @return {boolean}
 *        - True si les `VideoTrack`s natifs sont supportés.
 *        - Faux sinon
 */
Html5.supportsNativeVideoTracks = function() {
  return ! !(Html5.TEST_VID && Html5.TEST_VID.videoTracks) ;
};

/**
 * Vérifier si les `AudioTrack`s natifs sont supportés par ce navigateur/appareil
 *
 * @return {boolean}
 *        - True si les `AudioTrack`s natifs sont supportés.
 *        - Faux sinon
 */
Html5.supportsNativeAudioTracks = function() {
  return ! !(Html5.TEST_VID && Html5.TEST_VID.audioTracks) ;
};

/**
 * Une série d'événements disponibles sur la technologie Html5.
 *
 * @private
 * @type {Array}
 */
Html5.Events = [
  'loadstart',
  suspendre",
  'abort',
  'erreur',
  vide",
  "bloqué",
  'loadedmetadata',
  'loadeddata',
  'canplay',
  'canplaythrough',
  'jouant',
  attente",
  recherche",
  recherchée",
  terminé",
  'durationchange',
  'timeupdate',
  'progrès',
  'play',
  pause",
  'ratechange',
  'redimensionner',
  volumechange"
] ;

/**
 * Booléen indiquant si la `Tech` prend en charge le contrôle du volume.
 *
 * @type {boolean}
 * @default {@link Html5.canControlVolume}
 */
/**
 * Booléen indiquant si la `Tech` supporte le volume muting.
 *
 * @type {bolean}
 * @default {@link Html5.canMuteVolume}
 */

/**
 * Booléen indiquant si la `Technologie` permet de modifier la vitesse à laquelle le média est utilisé
 * joue. Exemples :
 *   - Régler le lecteur pour qu'il joue 2x (deux fois) plus vite
 *   - Régler le lecteur pour qu'il joue 0,5x (moitié) plus vite
 *
 * @type {boolean}
 * @default {@link Html5.canControlPlaybackRate}
 */

/**
 * Booléen indiquant si la `Tech` supporte l'événement `sourceset`.
 *
 * @type {boolean}
 * @défaut
 */
/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement les `TextTrack`s natifs.
 *
 * @type {boolean}
 * @default {@link Html5.supportsNativeTextTracks}
 */
/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement les `VideoTrack`s natifs.
 *
 * @type {boolean}
 * @default {@link Html5.supportsNativeVideoTracks}
 */
/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement les `AudioTrack`s natifs.
 *
 * @type {boolean}
 * @default {@link Html5.supportsNativeAudioTracks}
 */
[
  ["featuresMuteControl", "canMuteVolume"],
  ["featuresPlaybackRate", "canControlPlaybackRate"],
  ['featuresSourceset', 'canOverrideAttributes'],
  ["featuresNativeTextTracks", "supportsNativeTextTracks"],
  ["featuresNativeVideoTracks", "supportsNativeVideoTracks"],
  ['featuresNativeAudioTracks', 'supportsNativeAudioTracks']]
].forEach(function([key, fn]) {
  defineLazyProperty(Html5.prototype, key, () => Html5[fn](), true) ;
}) ;

Html5.prototype.featuresVolumeControl = Html5.canControlVolume() ;

/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement l'élément media
 * se déplaçant dans le DOM. iOS s'interrompt si l'on déplace l'élément média, il faut donc lui donner la valeur suivante
 * faux là. Partout ailleurs, cela devrait être vrai.
 *
 * @type {boolean}
 * @défaut
 */
Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS ;

// TODO : Commentaire précédent : Ne semble plus être utilisé. Peut probablement être retiré.
// Est-ce vrai ?
/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement le redimensionnement automatique des médias
 * lors du passage en plein écran.
 *
 * @type {boolean}
 * @défaut
 */
Html5.prototype.featuresFullscreenResize = true ;

/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement l'événement de progression.
 * Si cette valeur est fausse, les événements manuels de type `progress` seront déclenchés à la place.
 *
 * @type {boolean}
 * @défaut
 */
Html5.prototype.featuresProgressEvents = true ;

/**
 * Booléen indiquant si la technologie `HTML5` supporte actuellement l'événement timeupdate.
 * Si c'est faux, les événements manuels `timeupdate` seront déclenchés à la place.
 *
 * @défaut
 */
Html5.prototype.featuresTimeupdateEvents = true ;

/**
 * Si l'el HTML5 supporte `requestVideoFrameCallback`
 *
 * @type {boolean}
 */
Html5.prototype.featuresVideoFrameCallback = ! !(Html5.TEST_VID && Html5.TEST_VID.requestVideoFrameCallback) ;

// Détection des fonctionnalités HTML5 et corrections pour les appareils --------------------------------- //
let canPlayType ;

Html5.patchCanPlayType = function() {

  // Android 4.0 et supérieur peut lire HLS dans une certaine mesure, mais il signale qu'il n'est pas en mesure de le faire
  // Firefox et Chrome rapportent correctement
  if (browser.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX && !browser.IS_CHROME) {
    canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType ;
    Html5.TEST_VID.constructor.prototype.canPlayType = function(type) {
      const mpegurlRE = /^application/(?:x-|vnd.apple\.)mpegurl/i ;

      if (type && mpegurlRE.test(type)) {
        retourner "peut-être" ;
      }
      return canPlayType.call(this, type) ;
    };
  }
};

Html5.unpatchCanPlayType = function() {
  const r = Html5.TEST_VID.constructor.prototype.canPlayType ;

  if (canPlayType) {
    Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType ;
  }
  retour r ;
};

// par défaut, patcher l'élément média
Html5.patchCanPlayType() ;

Html5.disposeMediaElement = function(el) {
  if (!el) {
    retour ;
  }

  if (el.parentNode) {
    el.parentNode.removeChild(el) ;
  }

  // supprimer toute piste enfant ou nœud source pour empêcher leur chargement
  while (el.hasChildNodes()) {
    el.removeChild(el.firstChild) ;
  }

  // supprimer toute référence à src. ne pas mettre `src=''` car cela provoque un avertissement
  // dans firefox
  el.removeAttribute('src') ;

  // forcer l'élément média à mettre à jour son état de chargement en appelant load()
  // cependant IE sur Windows 7N a un bug qui provoque une erreur, il faut donc un try/catch (#793)
  if (typeof el.load === 'function') {
    // envelopper dans un iife pour ne pas être désoptimisé (#1060#discussion_r10324473)
    (function() {
      essayez {
        el.load() ;
      } catch (e) {
        // non pris en charge
      }
    }()) ;
  }
};

Html5.resetMediaElement = function(el) {
  if (!el) {
    retour ;
  }

  const sources = el.querySelectorAll('source') ;
  let i = sources.length ;

  while (i--) {
    el.removeChild(sources[i]) ;
  }

  // supprimer toute référence src.
  // ne pas mettre `src=''` car cela provoque une erreur
  el.removeAttribute('src') ;

  if (typeof el.load === 'function') {
    // envelopper dans un iife pour ne pas être désoptimisé (#1060#discussion_r10324473)
    (function() {
      essayez {
        el.load() ;
      } catch (e) {
        // satisfaire le linter
      }
    }()) ;
  }
};

/* enveloppement des propriétés des éléments HTML5 natifs ----------------------------------- */
// Envelopper les attributs booléens natifs avec des getters qui vérifient à la fois la propriété et l'attribut
// La liste est la suivante :
// muted, defaultMuted, autoplay, controls, loop, playsinline
[
  /**
   * Récupère la valeur de `muted` de l'élément media. `muted` indique
   * que le volume du support doit être réglé sur silencieux. Cela ne change pas grand-chose
   * l'attribut `volume`.
   *
   * @method Html5#muted
   * @return {boolean}
   *         - True si la valeur de `volume` doit être ignorée et l'audio mis en silencieux.
   *         - False si la valeur de `volume` doit être utilisée.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
   */
  en sourdine",

  /**
   * Obtenir la valeur de `defaultMuted` de l'élément media. `defaultMuted` indique
   * si le média doit être mis en sourdine ou non. Ne modifie que l'état par défaut du
   * media. `muted` et `defaultMuted` peuvent avoir des valeurs différentes. {@link Html5#muted} indique la valeur de l'attribut
   * état actuel.
   *
   * @method Html5#defaultMuted
   * @return {boolean}
   *         - La valeur de `defaultMuted` de l'élément media.
   *         - True indique que le média doit être mis en sourdine.
   *         - False indique que le média ne doit pas démarrer en sourdine
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
   */
  'defaultMuted',

  /**
   * Récupère la valeur de `autoplay` de l'élément media. `autoplay` indique
   * que la lecture du média doit commencer dès que la page est prête.
   *
   * @method Html5#autoplay
   * @return {boolean}
   *         - La valeur de `autoplay` de l'élément media.
   *         - True indique que le média doit démarrer dès le chargement de la page.
   *         - False indique que le média ne doit pas démarrer dès le chargement de la page.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
   */
  'autoplay',

  /**
   * Obtenir la valeur de `controls` de l'élément media. `controls` indique
   * si les contrôles médias natifs doivent être affichés ou masqués.
   *
   * @method Html5#controls
   * @return {boolean}
   *         - La valeur de `controls` de l'élément media.
   *         - True indique que les contrôles natifs doivent être affichés.
   *         - False indique que les contrôles natifs doivent être cachés.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
   */
  contrôles",

  /**
   * Récupère la valeur de `loop` de l'élément média. `loop` indique
   * que le média doit revenir au début du média et continuer la lecture une fois que le média est terminé
   * il arrive au bout.
   *
   * @method Html5#loop
   * @return {boolean}
   *         - La valeur de `loop` de l'élément média.
   *         - True indique que la lecture doit revenir au début une fois que
   *           la fin d'un média est atteinte.
   *         - Faux indique que la lecture ne doit pas revenir en boucle au début lorsque le bouton
   *           l'extrémité du support est atteinte.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
   */
  'loop' (boucle),

  /**
   * Obtenir la valeur de `playsinline` de l'élément media. `playsinline` indique
   * au navigateur que la lecture en mode non-plein écran est préférée lorsque la lecture en mode plein écran est possible
   * est la valeur native par défaut, comme dans Safari d'iOS.
   *
   * @method Html5#playsinline
   * @return {boolean}
   *         - La valeur de `playsinline` de l'élément media.
   *         - True indique que le média doit être lu en ligne.
   *         - False indique que le média ne doit pas être lu en ligne.
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
   */
  joue en ligne
].forEach(function(prop) {
  Html5.prototype[prop] = function() {
    return this.el_[prop] || this.el_.hasAttribute(prop) ;
  };
}) ;

// Envelopper les attributs booléens natifs avec des fixateurs qui fixent à la fois la propriété et l'attribut
// La liste est la suivante :
// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
// setControls est un cas particulier ci-dessus
[
  /**
   * Fixe la valeur de `muted` sur l'élément media. `muted` indique que l'élément
   * le niveau audio doit être silencieux.
   *
   * @method Html5#setMuted
   * @param {boolean} muted
   *        - Vrai si l'audio doit être réglé sur silencieux
   *        - Faux sinon
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
   */
  en sourdine",

  /**
   * Fixe la valeur de `defaultMuted` sur l'élément media. `defaultMuted` indique que la valeur courante de l'élément
   * le niveau audio doit être silencieux, mais n'affecte le niveau de sourdine que lors de la première lecture.
   *
   * @method Html5.prototype.setDefaultMuted
   * @param {boolean} defaultMuted
   *        - Vrai si l'audio doit être réglé sur silencieux
   *        - Faux sinon
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
   */
  'defaultMuted',

  /**
   * Fixe la valeur de `autoplay` sur l'élément media. `autoplay` indique
   * que la lecture du média doit commencer dès que la page est prête.
   *
   * @method Html5#setAutoplay
   * @param {boolean} autoplay
   *         - True indique que le média doit démarrer dès le chargement de la page.
   *         - False indique que le média ne doit pas démarrer dès le chargement de la page.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
   */
  'autoplay',

  /**
   * Fixe la valeur de `loop` sur l'élément média. `loop` indique
   * que le média doit revenir au début du média et continuer la lecture une fois que le média est terminé
   * il arrive au bout.
   *
   * @method Html5#setLoop
   * @param {boolean} loop
   *         - True indique que la lecture doit revenir au début une fois que
   *           la fin d'un média est atteinte.
   *         - Faux indique que la lecture ne doit pas revenir en boucle au début lorsque le bouton
   *           l'extrémité du support est atteinte.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
   */
  'loop' (boucle),

  /**
   * Fixe la valeur de `playsinline` de l'élément media. `playsinline` indique
   * au navigateur que la lecture en mode non-plein écran est préférée lorsque la lecture en mode plein écran est possible
   * est la valeur native par défaut, comme dans Safari d'iOS.
   *
   * @method Html5#setPlaysinline
   * @param {boolean} playsinline
   *         - True indique que le média doit être lu en ligne.
   *         - False indique que le média ne doit pas être lu en ligne.
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
   */
  joue en ligne
].forEach(function(prop) {
  Html5.prototype['set' + toTitleCase(prop)] = function(v) {
    this.el_[prop] = v ;

    if (v) {
      this.el_.setAttribute(prop, prop) ;
    } else {
      this.el_.removeAttribute(prop) ;
    }
  };
}) ;

// Envelopper les propriétés natives avec un getter
// La liste est la suivante
// paused, currentTime, buffered, volume, poster, preload, error, seeking
// recherchable, terminé, playbackRate, defaultPlaybackRate, disablePictureInPicture
// joué, networkState, readyState, videoWidth, videoHeight, crossOrigin
[
  /**
   * Obtenir la valeur de `paused` de l'élément média. `paused` indique si l'élément media
   * est en pause ou non.
   *
   * @method Html5#paused
   * @return {boolean}
   *         La valeur de `paused` de l'élément média.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
   */
  pausé",

  /**
   * Récupère la valeur de `currentTime` de l'élément media. `currentTime` indique
   * la seconde à laquelle se trouve le média en cours de lecture.
   *
   * @method Html5#currentTime
   * @return {number}
   *         La valeur de `currentTime` de l'élément média.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
   */
  'currentTime',

  /**
   * Récupère la valeur de `buffered` de l'élément media. `buffered` est un élément de type `TimeRange`
   * qui représente les parties du média qui sont déjà téléchargées et
   * disponibles pour la lecture.
   *
   * @method Html5#buffered
   * @return {TimeRange}
   *         La valeur de `buffered` de l'élément média.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
   */
  'buffered',

  /**
   * Obtenir la valeur de `volume` de l'élément média. `volume` indique
   * le volume de lecture actuel de l'audio pour un média. `volume` sera une valeur de 0
   * (silencieux) à 1 (le plus fort et par défaut).
   *
   * @method Html5#volume
   * @return {number}
   *         La valeur de `volume` de l'élément média. La valeur sera comprise entre 0 et 1.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
   */
  volume",

  /**
   * Obtenir la valeur de `poster` de l'élément média. `poster` indique
   * l'url d'un fichier image qui peut/veut être affiché lorsqu'aucune donnée média n'est disponible.
   *
   * @method Html5#poster
   * @return {string}
   *         La valeur de `poster` de l'élément média. La valeur sera une url vers un
   *         image.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
   */
  poster",

  /**
   * Récupère la valeur de `preload` de l'élément media. `preload` indique
   * ce qui doit être téléchargé avant d'interagir avec les médias. Il peut présenter les caractéristiques suivantes
   * valeurs :
   * - none : rien ne doit être téléchargé
   * - métadonnées : l'affiche et les premières images du média peuvent être téléchargées afin d'obtenir des informations sur le contenu du média
   *   les dimensions des médias et autres métadonnées
   * - auto : permet de télécharger le média et les métadonnées du média avant la fin de l'opération
   *    l'interaction
   *
   * @method Html5#preload
   * @return {string}
   *         La valeur de `preload` de l'élément média. Sera "none", "metadata",
   *         ou "auto".
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
   */
  'preload',

  /**
   * Récupère la valeur de l'élément `error` de l'élément media. `error` indique toute
   * MediaError qui a pu se produire pendant la lecture. Si l'erreur renvoie un résultat nul, il n'y a pas de
   * erreur actuelle.
   *
   * @method Html5#error
   * @return {MediaError|null}
   *         La valeur de `error` de l'élément media. Il y aura une `MediaError` s'il y a une
   *         est une erreur courante et null sinon.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
   */
  'erreur',

  /**
   * Récupère la valeur de `seeking` de l'élément media. `seeking` indique si l'élément
   * les médias sont actuellement à la recherche d'un nouveau poste ou non.
   *
   * @method Html5#seeking
   * @return {boolean}
   *         - La valeur de `seeking` de l'élément média.
   *         - Vrai indique que le média est actuellement à la recherche d'un nouveau poste.
   *         - Faux indique que le média n'est pas à la recherche d'un nouveau poste pour le moment.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
   */
  recherche",

  /**
   * Récupère la valeur de `seekable` de l'élément media. `seekable` renvoie une valeur
   * objet `TimeRange` indiquant les plages de temps qui peuvent actuellement être `recherchées`.
   *
   * @method Html5#seekable
   * @return {TimeRange}
   *         La valeur de `seekable` de l'élément média. Un objet `TimeRange` (plage de temps)
   *         indiquant les plages de temps actuelles qui peuvent être recherchées.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
   */
  'cherchable',

  /**
   * Récupère la valeur de `ended` de l'élément media. `ended` indique si
   * les médias ont atteint la fin ou non.
   *
   * @method Html5#ended
   * @return {boolean}
   *         - La valeur de `ended` de l'élément média.
   *         - True indique que le média est terminé.
   *         - Faux indique que le média n'est pas terminé.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
   */
  terminé",

  /**
   * Obtenir la valeur de `playbackRate` de l'élément media. `playbackRate` indique
   * la vitesse à laquelle le média est en cours de lecture. Exemples :
   *   - si playbackRate est réglé sur 2, le média sera lu deux fois plus vite.
   *   - si playbackRate est fixé à 0,5, le média sera lu deux fois moins vite.
   *
   * @method Html5#playbackRate
   * @return {number}
   *         La valeur de `playbackRate` de l'élément media. Un nombre indiquant
   *         la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
   */
  taux de lecture",

  /**
   * Obtenir la valeur de `defaultPlaybackRate` de l'élément media. `defaultPlaybackRate` indique
   * la vitesse à laquelle le média est en cours de lecture. Cette valeur n'indique pas l'état actuel de la
   * `playbackRate` après le début de la lecture, utilisez {@link Html5#playbackRate} pour cela.
   *
   * Exemples:
   *   - si defaultPlaybackRate est fixé à 2, la lecture du média sera deux fois plus rapide.
   *   - si defaultPlaybackRate est fixé à 0,5, le média sera lu deux fois moins vite.
   *
   * @method Html5.prototype.defaultPlaybackRate
   * @return {number}
   *         La valeur de `defaultPlaybackRate` de l'élément média. Un nombre indiquant
   *         la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
   */
  'defaultPlaybackRate',

  /**
   * Obtenir la valeur de "disablePictureInPicture" de l'élément vidéo.
   *
   * @method Html5#disablePictureInPicture
   * @return {boolean} value
   *         - La valeur de `disablePictureInPicture` de l'élément vidéo.
   *         - True indique que la vidéo ne peut pas être lue en mode image dans l'image
   *         - Faux indique que la vidéo peut être lue en mode image dans l'image
   *
   * @see [Spec]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
   */
  'disablePictureInPicture',

  /**
   * Récupère la valeur de `played` de l'élément media. `played` retourne un `TimeRange`
   * représentant les points de la chronologie des médias qui ont été lus.
   *
   * @method Html5#played
   * @return {TimeRange}
   *         La valeur de `played` de l'élément média. Un objet `TimeRange` indiquant
   *         les plages de temps qui ont été jouées.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
   */
  jouée",

  /**
   * Obtenir la valeur de `networkState` de l'élément média. `networkState` indique
   * l'état actuel du réseau. Il renvoie une énumération à partir de la liste suivante :
   * - 0 : NETWORK_EMPTY
   * - 1 : NETWORK_IDLE
   * - 2 : NETWORK_LOADING
   * - 3 : NETWORK_NO_SOURCE
   *
   * @method Html5#networkState
   * @return {number}
   *         La valeur de `networkState` de l'élément média. Il s'agira d'un nombre
   *         à partir de la liste figurant dans la description.
   *
   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
   */
  'networkState',

  /**
   * Obtenir la valeur de `readyState` de l'élément media. `readyState` indique
   * l'état actuel de l'élément multimédia. Il renvoie une énumération de la liste
   * liste suivante :
   * - 0 : HAVE_RIEN
   * - 1 : HAVE_MÉTADONNÉES
   * - 2 : HAVE_CURRENT_DATA
   * - 3 : HAVE_FUTURE_DATA
   * - 4 : HAVE_ASOUGH_DATA
   *
   * @method Html5#readyState
   * @return {number}
   *         La valeur de `readyState` de l'élément média. Il s'agira d'un nombre
   *         à partir de la liste figurant dans la description.
   *
   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
   */
  'readyState',

  /**
   * Obtenir la valeur de `videoWidth` de l'élément vidéo. `videoWidth` indique
   * la largeur actuelle de la vidéo en pixels css.
   *
   * @method Html5#videoWidth
   * @return {number}
   *         La valeur de `videoWidth` de l'élément vidéo. Il s'agira d'un nombre
   *         en pixels css.
   *
   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
   */
  'videoWidth',

  /**
   * Obtenir la valeur de `videoHeight` de l'élément vidéo. `videoHeight` indique
   * la hauteur actuelle de la vidéo en pixels css.
   *
   * @method Html5#videoHeight
   * @return {number}
   *         La valeur de `videoHeight` de l'élément vidéo. Il s'agira d'un nombre
   *         en pixels css.
   *
   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
   */
  hauteur de la vidéo",

  /**
   * Obtenir la valeur de `crossOrigin` de l'élément média. `crossOrigin` indique
   * au navigateur qui doit envoyer les cookies en même temps que les demandes de
   * différents actifs/listes de lecture
   *
   * @method Html5#crossOrigin
   * @return {string}
   *         - anonyme indique que le média ne doit pas envoyer de cookies.
   *         - use-credentials indique que le média doit envoyer des cookies avec les requêtes.
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
   */
  crossOrigin' (origine croisée)
].forEach(function(prop) {
  Html5.prototype[prop] = function() {
    return this.el_[prop] ;
  };
}) ;

// Envelopper les propriétés natives avec un setter dans ce format :
// set + toTitleCase(name)
// La liste est la suivante :
// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate,
// setDisablePictureInPicture, setCrossOrigin
[
  /**
   * Fixe la valeur de `volume` sur l'élément média. `volume` indique la valeur actuelle de
   * le niveau audio en pourcentage sous forme décimale. Cela signifie que 1 correspond à 100 %, 0,5 à 50 % et
   * et ainsi de suite.
   *
   * @method Html5#setVolume
   * @param {number} percentAsDecimal
   *        Le pourcentage de volume sous forme décimale. La plage valide est comprise entre 0 et 1.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
   */
  volume",

  /**
   * Fixe la valeur de `src` sur l'élément media. `src` indique le nom de l'élément
   * {@link Tech~SourceObject} pour le média.
   *
   * @method Html5#setSrc
   * @param {Tech~SourceObject} src
   *        L'objet source à définir comme source courante.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
   */
  'src',

  /**
   * Fixe la valeur de `poster` sur l'élément media. `poster` est l'url de
   * un fichier image qui peut/veut être affiché lorsqu'aucune donnée média n'est disponible.
   *
   * @method Html5#setPoster
   * @param {string} poster
   *        L'url d'une image qui doit être utilisée comme `poster` pour le média
   *        élément.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
   */
  poster",

  /**
   * Fixe la valeur de `preload` sur l'élément media. `preload` indique
   * ce qui doit être téléchargé avant d'interagir avec les médias. Il peut présenter les caractéristiques suivantes
   * valeurs :
   * - none : rien ne doit être téléchargé
   * - métadonnées : l'affiche et les premières images du média peuvent être téléchargées afin d'obtenir des informations sur le contenu du média
   *   les dimensions des médias et autres métadonnées
   * - auto : permet de télécharger le média et les métadonnées du média avant la fin de l'opération
   *    l'interaction
   *
   * @method Html5#setPreload
   * @param {string} preload
   *         La valeur de `preload` à définir sur l'élément média. Doit être "none", "metadata",
   *         ou "auto".
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
   */
  'preload',

  /**
   * Fixe la valeur de `playbackRate` sur l'élément media. `playbackRate` indique
   * la vitesse à laquelle le média doit être lu. Exemples :
   *   - si playbackRate est réglé sur 2, le média sera lu deux fois plus vite.
   *   - si playbackRate est fixé à 0,5, le média sera lu deux fois moins vite.
   *
   * @method Html5#setPlaybackRate
   * @return {number}
   *         La valeur de `playbackRate` de l'élément media. Un nombre indiquant
   *         la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
   */
  taux de lecture",

  /**
   * Fixe la valeur de `defaultPlaybackRate` sur l'élément média. `defaultPlaybackRate` indique
   * la vitesse à laquelle le média doit être lu lors du démarrage initial. Modification de cette valeur
   * après le démarrage d'une vidéo n'aura aucun effet. Vous devriez plutôt utiliser {@link Html5#setPlaybackRate}.
   *
   * Exemple de valeurs :
   *   - si playbackRate est réglé sur 2, le média sera lu deux fois plus vite.
   *   - si playbackRate est fixé à 0,5, le média sera lu deux fois moins vite.
   *
   * @method Html5.prototype.setDefaultPlaybackRate
   * @return {number}
   *         La valeur de `defaultPlaybackRate` de l'élément média. Un nombre indiquant
   *         la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
   *
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
   */
  'defaultPlaybackRate',

  /**
   * Empêche le navigateur de proposer un menu contextuel "image dans l'image"
   * ou de demander une image dans l'image automatiquement dans certains cas.
   *
   * @method Html5#setDisablePictureInPicture
   * @param {boolean} value
   *         La valeur vraie désactive le mode Image dans l'image.
   *
   * @see [Spec]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
   */
  'disablePictureInPicture',

  /**
   * Fixe la valeur de `crossOrigin` de l'élément média. `crossOrigin` indique
   * au navigateur qui doit envoyer les cookies en même temps que les demandes de
   * différents actifs/listes de lecture
   *
   * @method Html5#setCrossOrigin
   * @param {string} crossOrigin
   *         - anonyme indique que le média ne doit pas envoyer de cookies.
   *         - use-credentials indique que le média doit envoyer des cookies avec les requêtes.
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
   */
  crossOrigin' (origine croisée)
].forEach(function(prop) {
  Html5.prototype['set' + toTitleCase(prop)] = function(v) {
    this.el_[prop] = v ;
  };
}) ;

// envelopper les fonctions natives dans une fonction
// La liste est la suivante :
// pause, chargement, lecture
[
  /**
   * Une enveloppe autour de la fonction `pause` des éléments de média. Ceci appellera la fonction `HTML5`
   * la fonction `pause` des éléments de média.
   *
   * @method Html5#pause
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
   */
  pause",

  /**
   * Une enveloppe autour de la fonction `load` des éléments de média. Ceci appellera le `HTML5`s
   * fonction `load` de l'élément média.
   *
   * @method Html5#load
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
   */
  'load',

  /**
   * Une enveloppe autour de la fonction `play` des éléments de média. Ceci appellera le `HTML5`s
   * fonction `play` de l'élément média.
   *
   * @method Html5#play
   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
   */
  jouer"
].forEach(function(prop) {
  Html5.prototype[prop] = function() {
    return this.el_[prop]() ;
  };
}) ;

Tech.withSourceHandlers(Html5) ;

/**
 * Gestionnaire de source natif pour Html5, qui transmet simplement la source à l'élément média.
 *
 * @property {Tech~SourceObject} source
 *        L'objet source
 *
 * @property {Html5} tech
 *        L'instance de la technologie HTML5.
 */
Html5.nativeSourceHandler = {} ;

/**
 * Vérifie si l'élément multimédia peut lire le type de fichier mime donné.
 *
 * @param {string} type
 *        Le type d'image à vérifier
 *
 * @return {string}
 *         "probablement", "peut-être" ou '' (chaîne vide)
 */
Html5.nativeSourceHandler.canPlayType = function(type) {
  // IE sans MediaPlayer provoque une erreur (#519)
  essayez {
    return Html5.TEST_VID.canPlayType(type) ;
  } catch (e) {
    retourner '' ;
  }
};

/**
 * Vérifier si l'élément média peut gérer une source de manière native.
 *
 * @param {Tech~SourceObject} source
 *         L'objet source
 *
 * @param {Objet} [options]
 *         Options à transmettre au technicien.
 *
 * @return {string}
 *         'probablement', 'peut-être' ou '' (chaîne vide).
 */
Html5.nativeSourceHandler.canHandleSource = function(source, options) {

  // Si un type a été fourni, nous devons nous y fier
  if (source.type) {
    return Html5.nativeSourceHandler.canPlayType(source.type) ;

  // S'il n'y a pas de type, on revient à la vérification de "video/[EXTENSION]"
  } else if (source.src) {
    const ext = Url.getFileExtension(source.src) ;

    return Html5.nativeSourceHandler.canPlayType(`video/${ext}`) ;
  }

  retourner '' ;
};

/**
 * Transmettre la source à l'élément multimédia natif.
 *
 * @param {Tech~SourceObject} source
 *        L'objet source
 *
 * @param {Html5} tech
 *        L'instance de la technologie Html5
 *
 * @param {Objet} [options]
 *        Les options à transmettre à la source
 */
Html5.nativeSourceHandler.handleSource = function(source, tech, options) {
  tech.setSrc(source.src) ;
};

/**
 * Un noop pour la fonction native dispose, car le nettoyage n'est pas nécessaire.
 */
Html5.nativeSourceHandler.dispose = function() {} ;

// Enregistrer le gestionnaire de sources natives
Html5.registerSourceHandler(Html5.nativeSourceHandler) ;

Tech.registerTech('Html5', Html5) ;
exporter par défaut Html5 ;