    /**
     * @file text-track-display.js
     */
    import Component from '../component';
    import * as Fn from '../utils/fn.js';
    import window from 'global/window';

    const darkGray = '#222';
    const lightGray = '#ccc';
    const fontMap = {
      monospace: 'monospace',
      sansSerif: 'sans-serif',
      serif: 'serif',
      monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
      monospaceSerif: '"Courier New", monospace',
      proportionalSansSerif: 'sans-serif',
      proportionalSerif: 'serif',
      casual: '"Comic Sans MS", Impact, fantasy',
      script: '"Monotype Corsiva", cursive',
      smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
    };

    /**
     * Construct an rgba color from a given hex color code.
     *
     * @param {number} color
     *        Hex number for color, like #f0e.
     *
     * @param {number} opacity
     *        Value for opacity, 0.0 - 1.0.
     *
     * @return {string}
     *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
     *
     * @private
     */
    function constructColor(color, opacity) {
      return 'rgba(' +
        // color looks like "#f0e"
        parseInt(color[1] + color[1], 16) + ',' +
        parseInt(color[2] + color[2], 16) + ',' +
        parseInt(color[3] + color[3], 16) + ',' +
        opacity + ')';
    }

    /**
     * Try to update the style of a DOM element. Some style changes will throw an error,
     * particularly in IE8. Those should be noops.
     *
     * @param {Element} el
     *        The DOM element to be styled.
     *
     * @param {string} style
     *        The CSS property on the element that should be styled.
     *
     * @param {string} rule
     *        The style rule that should be applied to the property.
     *
     * @private
     */
    function tryUpdateStyle(el, style, rule) {
      try {
        el.style[style] = rule;
      } catch (e) {

        // Satisfies linter.
        return;
      }
    }

    /**
     * The component for displaying text track cues.
     *
     * @extends Component
     */
    class TextTrackDisplay extends Component {

      /**
       * Creates an instance of this class.
       *
       * @param {Player} player
       *        The `Player` that this class should be attached to.
       *
       * @param {Object} [options]
       *        The key/value store of player options.
       *
       * @param {Component~ReadyCallback} [ready]
       *        The function to call when `TextTrackDisplay` is ready.
       */
      constructor(player, options, ready) {
        super(player, options, ready);

        player.on('loadstart', Fn.bind(this, this.toggleDisplay));
        player.on('texttrackchange', Fn.bind(this, this.updateDisplay));
        player.on('loadstart', Fn.bind(this, this.preselectTrack));

        // This used to be called during player init, but was causing an error
        // if a track should show by default and the display hadn't loaded yet.
        // Should probably be moved to an external track loader when we support
        // tracks that don't need a display.
        player.ready(Fn.bind(this, function() {
          if (player.tech_ && player.tech_.featuresNativeTextTracks) {
            this.hide();
            return;
          }

          player.on('fullscreenchange', Fn.bind(this, this.updateDisplay));

          const tracks = this.options_.playerOptions.tracks || [];

          for (let i = 0; i < tracks.length; i++) {
            this.player_.addRemoteTextTrack(tracks[i], true);
          }

          this.preselectTrack();
        }));
      }

      /**
      * Preselect a track following this precedence:
      * - matches the previously selected {@link TextTrack}'s language and kind
      * - matches the previously selected {@link TextTrack}'s language only
      * - is the first default captions track
      * - is the first default descriptions track
      *
      * @listens Player#loadstart
      */
      preselectTrack() {
        const modes = {captions: 1, subtitles: 1};
        const trackList = this.player_.textTracks();
        const userPref = this.player_.cache_.selectedLanguage;
        let firstDesc;
        let firstCaptions;
        let preferredTrack;

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

          if (userPref && userPref.enabled &&
            userPref.language === track.language) {
            // Always choose the track that matches both language and kind
            if (track.kind === userPref.kind) {
              preferredTrack = track;
            // or choose the first track that matches language
            } else if (!preferredTrack) {
              preferredTrack = track;
            }

          // clear everything if offTextTrackMenuItem was clicked
          } else if (userPref && !userPref.enabled) {
            preferredTrack = null;
            firstDesc = null;
            firstCaptions = null;

          } else if (track.default) {
            if (track.kind === 'descriptions' && !firstDesc) {
              firstDesc = track;
            } else if (track.kind in modes && !firstCaptions) {
              firstCaptions = track;
            }
          }
        }

        // The preferredTrack matches the user preference and takes
        // precendence over all the other tracks.
        // So, display the preferredTrack before the first default track
        // and the subtitles/captions track before the descriptions track
        if (preferredTrack) {
          preferredTrack.mode = 'showing';
        } else if (firstCaptions) {
          firstCaptions.mode = 'showing';
        } else if (firstDesc) {
          firstDesc.mode = 'showing';
        }
      }

      /**
       * Turn display of {@link TextTrack}'s from the current state into the other state.
       * There are only two states:
       * - 'shown'
       * - 'hidden'
       *
       * @listens Player#loadstart
       */
      toggleDisplay() {
        if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
          this.hide();
        } else {
          this.show();
        }
      }

      /**
       * Create the {@link Component}'s DOM element.
       *
       * @return {Element}
       *         The element that was created.
       */
      createEl() {
        return super.createEl('div', {
          className: 'vjs-text-track-display'
        }, {
          'aria-live': 'off',
          'aria-atomic': 'true'
        });
      }

      /**
       * Clear all displayed {@link TextTrack}s.
       */
      clearDisplay() {
        if (typeof window.WebVTT === 'function') {
          window.WebVTT.processCues(window, [], this.el_);
        }
      }

      /**
       * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
       * a {@link Player#fullscreenchange} is fired.
       *
       * @listens Player#texttrackchange
       * @listens Player#fullscreenchange
       */
      updateDisplay() {
        const tracks = this.player_.textTracks();

        this.clearDisplay();

        // Track display prioritization model: if multiple tracks are 'showing',
        //  display the first 'subtitles' or 'captions' track which is 'showing',
        //  otherwise display the first 'descriptions' track which is 'showing'

        let descriptionsTrack = null;
        let captionsSubtitlesTrack = null;
        let i = tracks.length;

        while (i--) {
          const track = tracks[i];

          if (track.mode === 'showing') {
            if (track.kind === 'descriptions') {
              descriptionsTrack = track;
            } else {
              captionsSubtitlesTrack = track;
            }
          }
        }

        if (captionsSubtitlesTrack) {
          if (this.getAttribute('aria-live') !== 'off') {
            this.setAttribute('aria-live', 'off');
          }
          this.updateForTrack(captionsSubtitlesTrack);
        } else if (descriptionsTrack) {
          if (this.getAttribute('aria-live') !== 'assertive') {
            this.setAttribute('aria-live', 'assertive');
          }
          this.updateForTrack(descriptionsTrack);
        }
      }

      /**
       * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
       *
       * @param {TextTrack} track
       *        Text track object to be added to the list.
       */
      updateForTrack(track) {
        if (typeof window.WebVTT !== 'function' || !track.activeCues) {
          return;
        }

        const overrides = this.player_.textTrackSettings.getValues();
        const cues = [];

        for (let i = 0; i < track.activeCues.length; i++) {
          cues.push(track.activeCues[i]);
        }

        window.WebVTT.processCues(window, cues, this.el_);

        let i = cues.length;

        while (i--) {
          const cue = cues[i];

          if (!cue) {
            continue;
          }

          const cueDiv = cue.displayState;

          if (overrides.color) {
            cueDiv.firstChild.style.color = overrides.color;
          }
          if (overrides.textOpacity) {
            tryUpdateStyle(cueDiv.firstChild,
                           'color',
                           constructColor(overrides.color || '#fff',
                                          overrides.textOpacity));
          }
          if (overrides.backgroundColor) {
            cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
          }
          if (overrides.backgroundOpacity) {
            tryUpdateStyle(cueDiv.firstChild,
                           'backgroundColor',
                           constructColor(overrides.backgroundColor || '#000',
                                          overrides.backgroundOpacity));
          }
          if (overrides.windowColor) {
            if (overrides.windowOpacity) {
              tryUpdateStyle(cueDiv,
                             'backgroundColor',
                             constructColor(overrides.windowColor, overrides.windowOpacity));
            } else {
              cueDiv.style.backgroundColor = overrides.windowColor;
            }
          }
          if (overrides.edgeStyle) {
            if (overrides.edgeStyle === 'dropshadow') {
              cueDiv.firstChild.style.textShadow = `2px 2px 3px ${darkGray}, 2px 2px 4px ${darkGray}, 2px 2px 5px ${darkGray}`;
            } else if (overrides.edgeStyle === 'raised') {
              cueDiv.firstChild.style.textShadow = `1px 1px ${darkGray}, 2px 2px ${darkGray}, 3px 3px ${darkGray}`;
            } else if (overrides.edgeStyle === 'depressed') {
              cueDiv.firstChild.style.textShadow = `1px 1px ${lightGray}, 0 1px ${lightGray}, -1px -1px ${darkGray}, 0 -1px ${darkGray}`;
            } else if (overrides.edgeStyle === 'uniform') {
              cueDiv.firstChild.style.textShadow = `0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}`;
            }
          }
          if (overrides.fontPercent && overrides.fontPercent !== 1) {
            const fontSize = window.parseFloat(cueDiv.style.fontSize);

            cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px';
            cueDiv.style.height = 'auto';
            cueDiv.style.top = 'auto';
            cueDiv.style.bottom = '2px';
          }
          if (overrides.fontFamily && overrides.fontFamily !== 'default') {
            if (overrides.fontFamily === 'small-caps') {
              cueDiv.firstChild.style.fontVariant = 'small-caps';
            } else {
              cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
            }
          }
        }
      }

    }

    Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
    export default TextTrackDisplay;