Source: screensaver/ss_runner.js

/*
 *  Copyright (c) 2015-2017, Michael A. Updike All rights reserved.
 *  Licensed under the BSD-3-Clause
 *  https://opensource.org/licenses/BSD-3-Clause
 *  https://github.com/opus1269/photo-screen-saver/blob/master/LICENSE.md
 */
window.app = window.app || {};

/**
 * Control the running of a {@link app.Screensaver}
 * @namespace
 */
app.SSRunner = (function() {
  'use strict';

  new ExceptionHandler();

  /**
   * Instance variables
   * @type {Object}
   * @property {boolean} started - true if slideshow started
   * @property {int} replaceIdx - page to replace with next photo
   * @property {int} lastSelected - last selected page
   * @property {int} waitTime - wait time when looking for photo in milliSecs
   * @property {boolean} interactive - is interaction allowed
   * @property {boolean} paused - is screensaver paused
   * @property {number} timeOutId - id of setTimeout
   * @private
   * @memberOf app.SSRunner
   */
  const _VARS = {
    started: false,
    replaceIdx: -1,
    lastSelected: -1,
    waitTime: 30000,
    interactive: false,
    paused: false,
    timeOutId: 0,
  };

  /**
   * Stop the animation
   * @private
   * @memberOf app.SSRunner
   */
  function _stop() {
    window.clearTimeout(_VARS.timeOutId);
  }

  /**
   * Restart the slideshow
   * @param {?int} [newIdx=null] optional idx to use for current idx
   * @private
   * @memberOf app.SSRunner
   */
  function _restart(newIdx = null) {
    const transTime = Chrome.Storage.get('transitionTime');
    if (transTime) {
      app.SSRunner.setWaitTime(transTime.base * 1000);
    }
    _runShow(newIdx);
  }

  /**
   * Increment the slide show manually
   * @param {?int} [newIdx=null] optional idx to use for current idx
   * @private
   * @memberOf app.SSRunner
   */
  function _step(newIdx = null) {
    if (app.SSRunner.isPaused()) {
      app.SSRunner.togglePaused(newIdx);
      app.SSRunner.togglePaused();
    } else {
      _stop();
      _restart(newIdx);
    }
  }

  /**
   * Self called at fixed time intervals to cycle through the photos
   * @param {?int} [newIdx=null] override selected
   * @private
   * @memberOf app.SSRunner
   */
  function _runShow(newIdx = null) {
    if (app.Screensaver.noPhotos()) {
      // no usable photos to show
      return;
    }

    const selected = app.SSViews.getSelectedIndex();
    const viewLen = app.SSViews.getCount();
    let curIdx = (newIdx === null) ? selected : newIdx;
    curIdx = !app.SSRunner.isStarted() ? 0 : curIdx;
    let nextIdx = (curIdx === viewLen - 1) ? 0 : curIdx + 1;

    if (!app.SSRunner.isStarted()) {
      // special case for first page. neon-animated-pages is configured
      // to run the entry animation for the first selection
      nextIdx = 0;
    }

    nextIdx = app.SSFinder.getNext(nextIdx);
    if (nextIdx !== -1) {
      // the next photo is ready

      if (!app.SSRunner.isStarted()) {
        _VARS.started = true;
        app.SSTime.setTime();
      }

      // setup photo
      const view = app.SSViews.get(nextIdx);
      view.render();

      // track the photo history
      app.SSHistory.add(newIdx, nextIdx, _VARS.replaceIdx);

      // update selected so the animation runs
      _VARS.lastSelected = selected;
      app.SSViews.setSelectedIndex(nextIdx);

      if (newIdx === null) {
        // load next photo from master array
        app.SSFinder.replacePhoto(_VARS.replaceIdx);
        _VARS.replaceIdx = _VARS.lastSelected;
      }
    }

    // set the next timeout, then call ourselves - runs unless interrupted
    _VARS.timeOutId = window.setTimeout(() => {
      _runShow();
    }, _VARS.waitTime);
  }

  return {
    /**
     * Start the slideshow
     * @param {int} [delay=2000] - delay before start
     * @memberOf app.SSRunner
     */
    start: function(delay = 2000) {
      const transTime = Chrome.Storage.get('transitionTime');
      if (transTime) {
        app.SSRunner.setWaitTime(transTime.base * 1000);
      }
      _VARS.interactive = Chrome.Storage.get('interactive');

      app.SSHistory.initialize();

      // start slide show. slight delay at beginning so we have a smooth start
      window.setTimeout(_runShow, delay);
    },

    /**
     * Get wait time between _runShow calls
     * @returns {int} current wait time
     * @memberOf app.SSRunner
     */
    getWaitTime: function() {
      return _VARS.waitTime;
    },

    /**
     * Set wait time between _runShow calls in milliSecs
     * @param {int} waitTime - wait time for next attempt to get photo
     * @memberOf app.SSRunner
     */
    setWaitTime: function(waitTime) {
      _VARS.waitTime = waitTime;
    },

    /**
     * Set last selected index
     * @param {int} lastSelected - last index in {@link app.SSViews}
     * @memberOf app.SSRunner
     */
    setLastSelected: function(lastSelected) {
      _VARS.lastSelected = lastSelected;
    },

    /**
     * Set last selected index
     * @param {int} idx - replace index in {@link app.SSViews}
     * @memberOf app.SSRunner
     */
    setReplaceIdx: function(idx) {
      _VARS.replaceIdx = idx;
    },

    /**
     * Has the first page run
     * @returns {boolean} if animation has started
     * @memberOf app.SSRunner
     */
    isStarted: function() {
      return _VARS.started;
    },
    /**
     * Is interactive mode allowed
     * @returns {boolean} true if allowed
     * @memberOf app.SSRunner
     */
    isInteractive: function() {
      return _VARS.interactive;
    },

    /**
     * Are we paused
     * @returns {boolean} true if paused
     * @memberOf app.SSRunner
     */
    isPaused: function() {
      return _VARS.paused;
    },

    /**
     * Is the given idx a part of the current animation pair
     * @param {int} idx - index into {@link app.SSViews}
     * @returns {boolean} if selected or last selected
     * @memberOf app.SSRunner
     */
    isCurrentPair: function(idx) {
      const selected = app.SSViews.getSelectedIndex();
      return ((idx === selected) || (idx === _VARS.lastSelected));
    },

    /**
     * Toggle paused state of the slideshow
     * @param {?int} [newIdx=null] optional idx to use for current idx
     * @memberOf app.SSRunner
     */
    togglePaused: function(newIdx = null) {
      if (_VARS.started) {
        _VARS.paused = !_VARS.paused;
        app.Screensaver.setPaused(_VARS.paused);
        if (_VARS.paused) {
          _stop();
        } else {
          _restart(newIdx);
        }
      }
    },

    /**
     * Forward one slide
     * @memberOf app.SSRunner
     */
    forward: function() {
      if (_VARS.started) {
        _step();
      }
    },

    /**
     * Backup one slide
     * @memberOf app.SSRunner
     */
    back: function() {
      if (_VARS.started) {
        const nextStep = app.SSHistory.back();
        if (nextStep !== null) {
          _step(nextStep);
        }
      }
    },
  };
})();