<commit_before>'use strict';

var enumeration = require('./enumeration'),
  singleton = require('./singleton'),
  collection = require('./collection'),
  promiseDone = require('./promiseDone');

/**
 * @name Entry
 * @typedef {Object} Entry
 */

/**
 * @name SequencerState
 * @typedef {Object} SequencerState
 */

/**
 * @name SequencerStates
 * @typedef {Object} SequencerStates
 * @property {SequencerState} pristine
 * @property {SequencerState} playing
 * @property {SequencerState} paused
 * @property {SequencerState} stopped
 */

/**
 * @type {SequencerStates}
 */
var States = enumeration(['pristine', 'playing', 'paused', 'stopped']);

/**
 * @name sequencerConfig
 * @typedef {Object} sequencerConfig
 * @property {function(?Entry):Entry} nextEntryProducer
 * @property {function(Video, ?Video)} comingNext
 * @property {function({entry: Entry, endingSoon: function, ending: function}):PlaybackSlot} playbackSlotProducer
 * @property {function(SequencerState, SequencerState)} stateChanged
 */

/**
 * @param {sequencerConfig} config
 * @return Sequencer
 */
function sequencer(config) {

  var _config = config,

    _state = singleton({
      init: States.pristine,
      changedListener: function(prevState, state) {
        if (state === States.playing) {
          forEachSlot(function(slot) {
            slot.proceed();
          });
        } else if (state === States.paused) {
          forEachSlot(function(slot) {
            slot.suspend();
          });
        }

        _config.stateChanged(prevState, state);
      }
    }),

    _endingSlots = collection({
      addedListener: function(slot) {
        slot.end()
          .then(function() {
            _endingSlots.remove(slot);
          })
          .then(stoppedGuard);
      }
    }),

    _preloadingSlot = singleton({
      changedListener: function(prevSlot, slot) {
        if (prevSlot) prevSlot.end().then(stoppedGuard);

        if (slot) {
          // load the slot and retry in case of loading error until a working entry is found
          promiseDone(
            slot.load()
              .catch(function() {
                if (slot === _preloadingSlot.get()) {
                  _preloadingSlot.clear();
                  var nextEntry = _config.nextEntryProducer(slot.entry);
                  if (nextEntry) {
                    preload(nextEntry);
                  }
                }
              }));
        }
      }
    }),

    _skippingSlot = singleton({
      changedListener: function(prevSlot) {
        if (prevSlot) _endingSlots.add(prevSlot);
      }
    }),

    _playingSlot = singleton({
      changedListener: function(prevSlot, slot) {
        if (prevSlot) _endingSlots.add(prevSlot);

        if (slot) {
          slot.start();
          var nextEntry = _config.nextEntryProducer(slot.entry);
          if (nextEntry) {
            preload(nextEntry);
          }
        }
      }
    });

  function forEachSlot(callback) {
    [_preloadingSlot, _skippingSlot, _playingSlot]
      .forEach(function(singleton) {
        if (singleton.get()) callback(singleton.get())
      });

    _endingSlots.forEach(callback);
  }

  /**
   * Have to be called anytime a slot ends. It makes sure the state of the sequencer is set to stopped if there
   * is not more valid entry to playing or about to play.
   */
  function stoppedGuard() {
    var count = 0;
    forEachSlot(function count() {
      count++;
    });
    if (count === 0) {
      _state.set(States.stopped);
    }
  }
<commit_msg>fixed doc typo and improved function name<commit_after>'use strict';

var enumeration = require('./enumeration'),
  singleton = require('./singleton'),
  collection = require('./collection'),
  promiseDone = require('./promiseDone');

/**
 * @name Entry
 * @typedef {Object} Entry
 */

/**
 * @name SequencerState
 * @typedef {Object} SequencerState
 */

/**
 * @name SequencerStates
 * @typedef {Object} SequencerStates
 * @property {SequencerState} pristine
 * @property {SequencerState} playing
 * @property {SequencerState} paused
 * @property {SequencerState} stopped
 */

/**
 * @type {SequencerStates}
 */
var States = enumeration(['pristine', 'playing', 'paused', 'stopped']);

/**
 * @name sequencerConfig
 * @typedef {Object} sequencerConfig
 * @property {function(?Entry):Entry} nextEntryProducer
 * @property {function(Video, ?Video)} comingNext
 * @property {function({entry: Entry, endingSoon: function, ending: function}):PlaybackSlot} playbackSlotProducer
 * @property {function(SequencerState, SequencerState)} stateChanged
 */

/**
 * @param {sequencerConfig} config
 * @return Sequencer
 */
function sequencer(config) {

  var _config = config,

    _state = singleton({
      init: States.pristine,
      changedListener: function(prevState, state) {
        if (state === States.playing) {
          forEachSlot(function(slot) {
            slot.proceed();
          });
        } else if (state === States.paused) {
          forEachSlot(function(slot) {
            slot.suspend();
          });
        }

        _config.stateChanged(prevState, state);
      }
    }),

    _endingSlots = collection({
      addedListener: function(slot) {
        slot.end()
          .then(function() {
            _endingSlots.remove(slot);
          })
          .then(probeStateOnSlotEnded);
      }
    }),

    _preloadingSlot = singleton({
      changedListener: function(prevSlot, slot) {
        if (prevSlot) prevSlot.end().then(probeStateOnSlotEnded);

        if (slot) {
          // load the slot and retry in case of loading error until a working entry is found
          promiseDone(
            slot.load()
              .catch(function() {
                if (slot === _preloadingSlot.get()) {
                  _preloadingSlot.clear();
                  var nextEntry = _config.nextEntryProducer(slot.entry);
                  if (nextEntry) {
                    preload(nextEntry);
                  }
                }
              }));
        }
      }
    }),

    _skippingSlot = singleton({
      changedListener: function(prevSlot) {
        if (prevSlot) _endingSlots.add(prevSlot);
      }
    }),

    _playingSlot = singleton({
      changedListener: function(prevSlot, slot) {
        if (prevSlot) _endingSlots.add(prevSlot);

        if (slot) {
          slot.start();
          var nextEntry = _config.nextEntryProducer(slot.entry);
          if (nextEntry) {
            preload(nextEntry);
          }
        }
      }
    });

  function forEachSlot(callback) {
    [_preloadingSlot, _skippingSlot, _playingSlot]
      .forEach(function(singleton) {
        if (singleton.get()) callback(singleton.get())
      });

    _endingSlots.forEach(callback);
  }

  /**
   * Have to be called anytime a slot ends. It makes sure the state of the sequencer is set to stopped if there
   * is not more valid entry playing or about to play.
   */
  function probeStateOnSlotEnded() {
    var count = 0;
    forEachSlot(function count() {
      count++;
    });
    if (count === 0) {
      _state.set(States.stopped);
    }
  }

{'fraction_non_alphanumeric': 0.1281761716544325, 'fraction_numerical': 0.000564652738565782, 'mean_word_length': 2.3388312912346843, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7111354', 'n_tokens_mistral': 2069, 'n_tokens_neox': 1978, 'n_words': 457}