text: 'use strict';
  
  function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  
  var videojs = _interopDefault(require('video.js'));
  
  /**
   * Validates a number of seconds to use as the auto-advance delay.
   *
   * @private
   * @param   {number} s
   *          The number to check
   *
   * @return  {boolean}
   *          Whether this is a valid second or not
   */
  var validSeconds = function validSeconds(s) {
    return typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
  };
  
  /**
   * Resets the auto-advance behavior of a player.
   *
   * @param {Player} player
   *        The player to reset the behavior on
   */
  var reset = function reset(player) {
    var aa = player.playlist.autoadvance_;
  
    if (aa.timeout) {
      player.clearTimeout(aa.timeout);
    }
  
    if (aa.trigger) {
      player.off('ended', aa.trigger);
    }
  
    aa.timeout = null;
    aa.trigger = null;
  };
  
  /**
   * Sets up auto-advance behavior on a player.
   *
   * @param  {Player} player
   *         the current player
   *
   * @param  {number} delay
   *         The number of seconds to wait before each auto-advance.
   *
   * @return {undefined}
   *         Used to short circuit function logic
   */
  var setup = function setup(player, delay) {
    reset(player);
  
    // Before queuing up new auto-advance behavior, check if `seconds` was
    // called with a valid value.
    if (!validSeconds(delay)) {
      player.playlist.autoadvance_.delay = null;
      return;
    }
  
    player.playlist.autoadvance_.delay = delay;
  
    player.playlist.autoadvance_.trigger = function () {
  
      // This calls setup again, which will reset the existing auto-advance and
      // set up another auto-advance for the next "ended" event.
      var cancelOnPlay = function cancelOnPlay() {
        return setup(player, delay);
      };
  
      // If there is a "play" event while we're waiting for an auto-advance,
      // we need to cancel the auto-advance. This could mean the user seeked
      // back into the content or restarted the content. This is reproducible
      // with an auto-advance > 0.
      player.one('play', cancelOnPlay);
  
      player.playlist.autoadvance_.timeout = player.setTimeout(function () {
        reset(player);
        player.off('play', cancelOnPlay);
        player.playlist.next();
      }, delay * 1000);
    };
  
    player.one('ended', player.playlist.autoadvance_.trigger);
  };
  
  /**
   * Removes all remote text tracks from a player.
   *
   * @param  {Player} player
   *         The player to clear tracks on
   */
  var clearTracks = function clearTracks(player) {
    var tracks = player.remoteTextTracks();
    var i = tracks && tracks.length || 0;
  
    // This uses a `while` loop rather than `forEach` because the
    // `TextTrackList` object is a live DOM list (not an array).
    while (i--) {
      player.removeRemoteTextTrack(tracks[i]);
    }
  };
  
  /**
   * Plays an item on a player's playlist.
   *
   * @param  {Player} player
   *         The player to play the item on
   *
   * @param  {Object} item
   *         A source from the playlist.
   *
   * @return {Player}
   *         The player that is now playing the item
   */
  var playItem = function playItem(player, item) {
    var replay = !player.paused() || player.ended();
  
    player.trigger('beforeplaylistitem', item);
    player.poster(item.poster || '');
    player.src(item.sources);
    clearTracks(player);
  
    player.ready(function () {
      (item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
      player.trigger('playlistitem', item);
  
      if (replay) {
        player.play();
      }
  
      setup(player, player.playlist.autoadvance_.delay);
    });
  
    return player;
  };
  
  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };
  
  
  
  
  
  var asyncGenerator = function () {
    function AwaitValue(value) {
      this.value = value;
    }
  
    function AsyncGenerator(gen) {
      var front, back;
  
      function send(key, arg) {
        return new Promise(function (resolve, reject) {
          var request = {
            key: key,
            arg: arg,
            resolve: resolve,
            reject: reject,
            next: null
          };
  
          if (back) {
            back = back.next = request;
          } else {
            front = back = request;
            resume(key, arg);
          }
        });
      }
  
      function resume(key, arg) {
        try {
          var result = gen[key](arg);
          var value = result.value;
  
          if (value instanceof AwaitValue) {
            Promise.resolve(value.value).then(function (arg) {
              resume("next", arg);
            }, function (arg) {
              resume("throw", arg);
            });
          } else {
            settle(result.done ? "return" : "normal", result.value);
          }
        } catch (err) {
          settle("throw", err);
        }
      }
  
      function settle(type, value) {
        switch (type) {
          case "return":
            front.resolve({
              value: value,
              done: true
            });
            break;
  
          case "throw":
            front.reject(value);
            break;
  
          default:
            front.resolve({
              value: value,
              done: false
            });
            break;
        }
  
        front = front.next;
  
        if (front) {
          resume(front.key, front.arg);
        } else {
          back = null;
        }
      }
  
      this._invoke = send;
  
      if (typeof gen.return !== "function") {
        this.return = undefined;
      }
    }
  
    if (typeof Symbol === "function" && Symbol.asyncIterator) {
      AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
        return this;
      };
    }
  
    AsyncGenerator.prototype.next = function (arg) {
      return this._invoke("next", arg);
    };
  
    AsyncGenerator.prototype.throw = function (arg) {
      return this._invoke("throw", arg);
    };
  
    AsyncGenerator.prototype.return = function (arg) {
      return this._invoke("return", arg);
    };
  
    return {
      wrap: function (fn) {
        return function () {
          return new AsyncGenerator(fn.apply(this, arguments));
        };
      },
      await: function (value) {
        return new AwaitValue(value);
      }
    };
  }();
  
  /**
   * Given two sources, check to see whether the two sources are equal.
   * If both source urls have a protocol, the protocols must match, otherwise, protocols
   * are ignored.
   *
   * @private
   * @param {string|Object} source1
   *        The first source
   *
   * @param {string|Object} source2
   *        The second source
   *
   * @return {boolean}
   *         The result
   */
  var sourceEquals = function sourceEquals(source1, source2) {
    var src1 = source1;
    var src2 = source2;
  
    if ((typeof source1 === 'undefined' ? 'undefined' : _typeof(source1)) === 'object') {
      src1 = source1.src;
    }
    if ((typeof source2 === 'undefined' ? 'undefined' : _typeof(source2)) === 'object') {
      src2 = source2.src;
    }
  
    if (/^\/\//.test(src1)) {
      src2 = src2.slice(src2.indexOf('//'));
    }
    if (/^\/\//.test(src2)) {
      src1 = src1.slice(src1.indexOf('//'));
    }
  
    return src1 === src2;
  };
  
  /**
   * Look through an array of playlist items for a specific `source`;
   * checking both the value of elements and the value of their `src`
   * property.
   *
   * @private
   * @param   {Array} arr
   *          An array of playlist items to look through
   *
   * @param   {string} src
   *          The source to look for
   *
   * @return  {number}
   *          The index of that source or -1
   */
  var indexInSources = function indexInSources(arr, src) {
    for (var i = 0; i < arr.length; i++) {
      var sources = arr[i].sources;
  
      if (Array.isArray(sources)) {
        for (var j = 0; j < sources.length; j++) {
          var source = sources[j];
  
          if (source && sourceEquals(source, src)) {
            return i;
          }
        }
      }
    }
  
    return -1;
  };
  
  /**
   * Randomize the contents of an array.
   *
   * @private
   * @param  {Array} arr
   *         An array.
   *
   * @return {Array}
   *         The same array that was passed in.
   */
  var randomize = function randomize(arr) {
    var index = -1;
    var lastIndex = arr.length - 1;
  
    while (++index < arr.length) {
      var rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
      var value = arr[rand];
  
      arr[rand] = arr[index];
      arr[index] = value;
    }
  
    return arr;
  };
  
  /**
   * Factory function for creating new playlist implementation on the given player.
   *
   * API summary:
   *
   * playlist(['a', 'b', 'c']) // setter
   * playlist() // getter
   * playlist.currentItem() // getter, 0
   * playlist.currentItem(1) // setter, 1
   * playlist.next() // 'c'
   * playlist.previous() // 'b'
   * playlist.first() // 'a'
   * playlist.last() // 'c'
   * playlist.autoadvance(5) // 5 second delay
   * playlist.autoadvance() // cancel autoadvance
   *
   * @param  {Player} player
   *         The current player
   *
   * @param  {Array=} initialList
   *         If given, an initial list of sources with which to populate
   *         the playlist.
   *
   * @param  {number=}  initialIndex
   *         If given, the index of the item in the list that should
   *         be loaded first. If -1, no video is loaded. If omitted, The
   *         the first video is loaded.
   *
   * @return {Function}
   *         Returns the playlist function specific to the given player.
   */
  function factory(player, initialList) {
    var initialIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  
    var list = null;
    var changing = false;
  
    /**
     * Get/set the playlist for a player.
     *
     * This function is added as an own property of the player and has its
     * own methods which can be called to manipulate the internal state.
     *
     * @param  {Array} [newList]
     *         If given, a new list of sources with which to populate the
     *         playlist. Without this, the function acts as a getter.
     *
     * @param  {number}  [newIndex]
     *         If given, the index of the item in the list that should
     *         be loaded first. If -1, no video is loaded. If omitted, The
     *         the first video is loaded.
     *
     * @return {Array}
     *         The playlist
     */
    var playlist = player.playlist = function (newList) {
      var newIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  
      if (changing) {
        throw new Error('do not call playlist() during a playlist change');
      }
  
      if (Array.isArray(newList)) {
  
        // @todo - Simplify this to `list.slice()` for v5.
        var previousPlaylist = Array.isArray(list) ? list.slice() : null;
  
        list = newList.slice();
  
        // Mark the playlist as changing during the duringplaylistchange lifecycle.
        changing = true;
  
        player.trigger({
          type: 'duringplaylistchange',
          nextIndex: newIndex,
          nextPlaylist: list,
          previousIndex: playlist.currentIndex_,
  
          // @todo - Simplify this to simply pass along `previousPlaylist` for v5.
          previousPlaylist: previousPlaylist || []
        });
  
        changing = false;
  
        if (newIndex !== -1) {
          playlist.currentItem(newIndex);
        }
  
        // The only time the previous playlist is null is the first call to this
        // function. This allows us to fire the `duringplaylistchange` event
        // every time the playlist is populated and to maintain backward
        // compatibility by not firing the `playlistchange` event on the initial
        // population of the list.
        //
        // @todo - Remove this condition in preparation for v5.
        if (previousPlaylist) {
          player.setTimeout(function () {
            player.trigger('playlistchange');
          }, 0);
        }
      }
  
      // Always return a shallow clone of the playlist list.
      return list.slice();
    };
  
    // On a new source, if there is no current item, disable auto-advance.
    player.on('loadstart', function () {
      if (playlist.currentItem() === -1) {
        reset(player);
      }
    });
  
    playlist.currentIndex_ = -1;
    playlist.player_ = player;
    playlist.autoadvance_ = {};
    playlist.repeat_ = false;
  
    /**
     * Get or set the current item in the playlist.
     *
     * During the duringplaylistchange event, acts only as a getter.
     *
     * @param  {number} [index]
     *         If given as a valid value, plays the playlist item at that index.
     *
     * @return {number}
     *         The current item index.
     */
    playlist.currentItem = function (index) {
  
      // If the playlist is changing, only act as a getter.
      if (changing) {
        return playlist.currentIndex_;
      }
  
      if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
        playlist.currentIndex_ = index;
        playItem(playlist.player_, list[playlist.currentIndex_]);
      } else {
        playlist.currentIndex_ = playlist.indexOf(playlist.player_.currentSrc() || '');
      }
  
      return playlist.currentIndex_;
    };
  
    /**
     * Checks if the playlist contains a value.
     *
     * @param  {string|Object|Array} value
     *         The value to check
     *
     * @return {boolean}
     *         The result
     */
    playlist.contains = function (value) {
      return playlist.indexOf(value) !== -1;
    };
  
    /**
     * Gets the index of a value in the playlist or -1 if not found.
     *
     * @param  {string|Object|Array} value
     *         The value to find the index of
     *
     * @return {number}
     *         The index or -1
     */
    playlist.indexOf = function (value) {
      if (typeof value === 'string') {
        return indexInSources(list, value);
      }
  
      var sources = Array.isArray(value) ? value : value.sources;
  
      for (var i = 0; i < sources.length; i++) {
        var source = sources[i];
  
        if (typeof source === 'string') {
          return indexInSources(list, source);
        } else if (source.src) {
          return indexInSources(list, source.src);
        }
      }
  
      return -1;
    };
  
    /**
     * Get the index of the current item in the playlist. This is identical to
     * calling `currentItem()` with no arguments.
     *
     * @return {number}
     *         The current item index.
     */
    playlist.currentIndex = function () {
      return playlist.currentItem();
    };
  
    /**
     * Get the index of the last item in the playlist.
     *
     * @return {number}
     *         The index of the last item in the playlist or -1 if there are no
     *         items.
     */
    playlist.lastIndex = function () {
      return list.length - 1;
    };
  
    /**
     * Get the index of the next item in the playlist.
     *
     * @return {number}
     *         The index of the next item in the playlist or -1 if there is no
     *         current item.
     */
    playlist.nextIndex = function () {
      var current = playlist.currentItem();
  
      if (current === -1) {
        return -1;
      }
  
      var lastIndex = playlist.lastIndex();
  
      // When repeating, loop back to the beginning on the last item.
      if (playlist.repeat_ && current === lastIndex) {
        return 0;
      }
  
      // Don't go past the end of the playlist.
      return Math.min(current + 1, lastIndex);
    };
  
    /**
     * Get the index of the previous item in the playlist.
     *
     * @return {number}
     *         The index of the previous item in the playlist or -1 if there is
     *         no current item.
     */
    playlist.previousIndex = function () {
      var current = playlist.currentItem();
  
      if (current === -1) {
        return -1;
      }
  
      // When repeating, loop back to the end of the playlist.
      if (playlist.repeat_ && current === 0) {
        return playlist.lastIndex();
      }
  
      // Don't go past the beginning of the playlist.
      return Math.max(current - 1, 0);
    };
  
    /**
     * Plays the first item in the playlist.
     *
     * @return {Object|undefined}
     *         Returns undefined and has no side effects if the list is empty.
     */
    playlist.first = function () {
      if (changing) {
        return;
      }
  
      if (list.length) {
        return list[playlist.currentItem(0)];
      }
  
      playlist.currentIndex_ = -1;
    };
  
    /**
     * Plays the last item in the playlist.
     *
     * @return {Object|undefined}
     *         Returns undefined and has no side effects if the list is empty.
     */
    playlist.last = function () {
      if (changing) {
        return;
      }
  
      if (list.length) {
        return list[playlist.currentItem(playlist.lastIndex())];
      }
  
      playlist.currentIndex_ = -1;
    };
  
    /**
     * Plays the next item in the playlist.
     *
     * @return {Object|undefined}
     *         Returns undefined and has no side effects if on last item.
     */
    playlist.next = function () {
      if (changing) {
        return;
      }
  
      var index = playlist.nextIndex();
  
      if (index !== playlist.currentIndex_) {
        return list[playlist.currentItem(index)];
      }
    };
  
    /**
     * Plays the previous item in the playlist.
     *
     * @return {Object|undefined}
     *         Returns undefined and has no side effects if on first item.
     */
    playlist.previous = function () {
      if (changing) {
        return;
      }
  
      var index = playlist.previousIndex();
  
      if (index !== playlist.currentIndex_) {
        return list[playlist.currentItem(index)];
      }
    };
  
    /**
     * Set up auto-advance on the playlist.
     *
     * @param  {number} [delay]
     *         The number of seconds to wait before each auto-advance.
     */
    playlist.autoadvance = function (delay) {
      setup(playlist.player_, delay);
    };
  
    /**
     * Sets `repeat` option, which makes the "next" video of the last video in
     * the playlist be the first video in the playlist.
     *
     * @param  {boolean} [val]
     *         The value to set repeat to
     *
     * @return {boolean}
     *         The current value of repeat
     */
    playlist.repeat = function (val) {
      if (val === undefined) {
        return playlist.repeat_;
      }
  
      if (typeof val !== 'boolean') {
        videojs.log.error('videojs-playlist: Invalid value for repeat', val);
        return;
      }
  
      playlist.repeat_ = !!val;
      return playlist.repeat_;
    };
  
    /**
     * Sorts the playlist array.
     *
     * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
     * @fires playlistsorted
     *
     * @param {Function} compare
     *        A comparator function as per the native Array method.
     */
    playlist.sort = function (compare) {
  
      // Bail if the array is empty.
      if (!list.length) {
        return;
      }
  
      list.sort(compare);
  
      // If the playlist is changing, don't trigger events.
      if (changing) {
        return;
      }
  
      /**
       * Triggered after the playlist is sorted internally.
       *
       * @event playlistsorted
       * @type {Object}
       */
      player.trigger('playlistsorted');
    };
  
    /**
     * Reverses the playlist array.
     *
     * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
     * @fires playlistsorted
     */
    playlist.reverse = function () {
  
      // Bail if the array is empty.
      if (!list.length) {
        return;
      }
  
      list.reverse();
  
      // If the playlist is changing, don't trigger events.
      if (changing) {
        return;
      }
  
      /**
       * Triggered after the playlist is sorted internally.
       *
       * @event playlistsorted
       * @type {Object}
       */
      player.trigger('playlistsorted');
    };
  
    /**
     * Shuffle the contents of the list randomly.
     *
     * @see   {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
     * @fires playlistsorted
     * @todo  Make the `rest` option default to `true` in v5.0.0.
     * @param {Object} [options]
     *        An object containing shuffle options.
     *
     * @param {boolean} [options.rest = false]
     *        By default, the entire playlist is randomized. However, this may
     *        not be desirable in all cases, such as when a user is already
     *        watching a video.
     *
     *        When `true` is passed for this option, it will only shuffle
     *        playlist items after the current item. For example, when on the
     *        first item, will shuffle the second item and beyond.
     */
    playlist.shuffle = function () {
      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          rest = _ref.rest;
  
      var index = 0;
      var arr = list;
  
      // When options.rest is true, start randomization at the item after the
      // current item.
      if (rest) {
        index = playlist.currentIndex_ + 1;
        arr = list.slice(index);
      }
  
      // Bail if the array is empty or too short to shuffle.
      if (arr.length <= 1) {
        return;
      }
  
      randomize(arr);
  
      // When options.rest is true, splice the randomized sub-array back into
      // the original array.
      if (rest) {
        var _list;
  
        (_list = list).splice.apply(_list, [index, arr.length].concat(arr));
      }
  
      // If the playlist is changing, don't trigger events.
      if (changing) {
        return;
      }
  
      /**
       * Triggered after the playlist is sorted internally.
       *
       * @event playlistsorted
       * @type {Object}
       */
      player.trigger('playlistsorted');
    };
  
    // If an initial list was given, populate the playlist with it.
    if (Array.isArray(initialList)) {
      playlist(initialList.slice(), initialIndex);
  
      // If there is no initial list given, silently set an empty array.
    } else {
      list = [];
    }
  
    return playlist;
  }
  
  // Video.js 5/6 cross-compatible.
  var registerPlugin = videojs.registerPlugin || videojs.plugin;
  
  /**
   * The video.js playlist plugin. Invokes the playlist-maker to create a
   * playlist function on the specific player.
   *
   * @param {Array} list
   *        a list of sources
   *
   * @param {number} item
   *        The index to start at
   */
  var plugin = function plugin(list, item) {
    factory(this, list, item);
  };
  
  registerPlugin('playlist', plugin);
  
  module.exports = plugin;
  