Show:
/**
 * The `proact-flow` provides executing functions in the right order in time.
 * Function execution can be deferred, there are priorities and turns.
 *
 * @module proact-flow
 * @main proact-flow
 */

/**
 * <p>
 *  Constructs the action flow of the ProAct.js; An action flow is a set of actions
 *  executed in the reactive environment, which order is determined by the dependencies
 *  between the reactive properties. The action flow puts on motion the data flow in the reactive
 *  ecosystem. Every change on a property triggers an action flow, which triggers the data flow.
 * </p>
 *  ProAct.Flow is inspired by the Ember's Backburner.js (https://github.com/ebryn/backburner.js).
 *  The defferences are the priority queues and some other optimizations like the the 'once' argument of the {{#crossLink "ProAct.Queue/go:method"}}{{/crossLink}} method.
 *  It doesn't include debouncing and timed defer of actions for now.
 * <p>
 *  ProAct.Flow is used to solve many of the problems in the reactive programming, for example the diamond problem.
 * </p>
 * <p>
 *  It can be used for other purposes too, for example to run rendering in a rendering queue, after all of the property updates.
 * </p>
 * <p>
 *  `ProAct.Flow`, {{#crossLink "ProAct.Queues"}}{{/crossLink}} and {{#crossLink "ProAct.Queue"}}{{/crossLink}} together form the `proact-flow` module of ProAct.
 * </p>
 *
 * TODO ProAct.Flow#start and ProAct.Flow#stop are confusing names - should be renamed to something like 'init' and 'exec'.
 *
 * @constructor
 * @class ProAct.Flow
 * @param {Array} queueNames
 *      Array with the names of the sub-queues of the {{#crossLink "ProAct.Queues"}}{{/crossLink}}es of the flow. The size of this array determines
 *      the number of the sub-queues.
 * @param {Object} options
 *    Various options for the ProAct.Flow.
 *    <p>Available options:</p>
 *    <ul>
 *      <li>start - A callback that will be called every time when the action flow starts.</li>
 *      <li>stop - A callback that will be called every time when the action flow ends.</li>
 *      <li>err - A callback that will be called if an error is thrown in the action flow.</li>
 *      <li>flowInstance - Options object for the current flow instance. The flow instances are of the class {{#crossLink "ProAct.Queues"}}{{/crossLink}}.</li>
 *    </ul>
 */
ProAct.Flow = P.F = function (queueNames, options) {
  this.setQueues(queueNames);

  this.options = options || {};

  this.flowInstance = null;
  this.flowInstances = [];

  this.pauseMode = false;

  try {
    Object.defineProperty(this, 'closingQueue', {
      enumerable: false,
      configurable: false,
      writable: false,
      value: new ProAct.Queue('closing')
    });
  } catch (e) {
    this.closingQueue = new ProAct.Queue('closing');
  }
};

P.F.prototype = {

  /**
   * Reference to the constructor of this object.
   *
   * @property constructor
   * @type ProAct.Flow
   * @final
   * @for ProAct.Flow
   */
  constructor: ProAct.Flow,

  /**
   * Puts the `ProAct.Flow` in running mode, meaning actions can be defered in it.
   * <p>
   *  It creates a new flow instance - instance of {{#crossLink "ProAct.Queues"}}{{/crossLink}} and
   *  if there was a running instance, it is set to be the previous inctance.
   * </p>
   * <p>
   *  If a <i>start</i> callback was passed when this `ProAct.Flow` was being created,
   *  it is called with the new flow instance.
   * </p>
   * <p>
   *  `begin` is alias of this method.
   * </p>
   *
   * @for ProAct.Flow
   * @instance
   * @method start
   */
  start: function () {
    var queues = this.flowInstance,
        options = this.options,
        start = options && options.start,
        queueNames = this.queueNames;

    if (queues) {
      this.flowInstances.push(queues);
    }

    this.flowInstance = new P.Queues(queueNames, options.flowInstance);

    if (start) {
      start(this.flowInstance);
    }
  },

  /**
   * Appends a new queue name to the list of <i>this</i>' queues.
   * <p>
   *  When a new <i>flowInstance</i> is created the updated list will be used.
   * </p>
   *
   * @for ProAct.Flow
   * @instance
   * @method addQueue
   * @param {String} queueName
   *      The queue name to add.
   */
  addQueue: function (queueName) {
    this.queueNames.push(queueName);
  },

  /**
   * Sets the queue names of <i>this</i> flow.
   * <p>
   *  When a new <i>flowInstance</i> is created the new list will be used.
   * </p>
   *
   * @for ProAct.Flow
   * @instance
   * @method setQueues
   * @param {Array} queueNames
   *      Array with the names of the sub-queues of the {{#crossLink "ProAct.Queues"}}{{/crossLink}}es of the flow.
   *      The size of this array determines the number of the sub-queues.
   */
  setQueues: function (queueNames) {
    if (!queueNames) {
      queueNames = ['proq'];
    }
    this.queueNames = queueNames;
  },

  /**
   * Starts an action flow consisting of all the actions defered after the
   * last call of {{#crossLink "ProAct.Flow/start:method"}}{{/crossLink}} and then stops the `ProAct.Flow`.
   *
   * <p>
   *  If there is a current action flow instance, it is flushed, using the
   *  {{#crossLink "ProAct.Queues/go:method"}}{{/crossLink}} method.
   * </p>
   * <p>
   *  If there was aprevious flow instance, it is set to be the current one.
   * </p>
   * <p>
   *  If a callback for 'stop' was specified in the <i>options</i> on creation,
   *  it is called with the flushed instance.
   * </p>
   * <p>
   *  When the flow is started you put actions in order or with priority,
   *  and if you want to execute them and stop it, you call this method.
   * </p>
   * <p>
   *  `end` is an alias for this method.
   * </p>
   *
   * @for ProAct.Flow
   * @instance
   * @method stop
   */
  stop: function () {
    var queues = this.flowInstance,
        options = this.options,
        stop = options && options.stop,
        nextQueues;

    if (queues) {
      try {
        queues.go();
      } finally {
        this.flowInstance = null;

        if (this.flowInstances.length) {
          nextQueues = this.flowInstances.pop();
          this.flowInstance = nextQueues;
        }

        if (stop) {
          stop(queues);
        }
        this.closingQueue.go();
      }
    }
  },

  /**
   * Puts the flow in <i>pause mode</i>.
   * When the flow is paused actions that should be defered to be run in it
   * are skipped.
   *
   * @for ProAct.Flow
   * @instance
   * @method pause
   */
  pause: function () {
    this.pauseMode = true;
  },

  /**
   * Resumes the action flow if it is paused.
   * The flow becomes active again and actions can be pushed into it.
   *
   * @for ProAct.Flow
   * @instance
   * @method resume
   */
  resume: function () {
    this.pauseMode = false;
  },

  /**
   * Starts the action flow, executes the passed callback, in the passed context,
   * and then stops the action flow, executing all the pushed by the <i>callback</i> actions.
   * <p>
   *  This means that you are guaranteed that you have a running action flow for the actions
   *  that should be pushed to a flow in the <i>callback</i>.
   * </p>
   * <p>
   *  `go` and `flush` are aliases of this method.
   * </p>
   *
   * @for ProAct.Flow
   * @instance
   * @method run
   * @param {Object} context
   *      The value of <i>this</i> bound to the <i>callback</i> when it is executed.
   * @param {Function} callback
   *      The callback that will be invoked in a new running `ProAct.Flow`.
   */
  run: function (context, callback) {
    var options = this.options,
        err = options.err;

    this.start();
    if (!callback) {
      callback = context;
      context = null;
    }

    try {
      if (err) {
        try {
          callback.call(context);
        } catch (e) {
          if (!e.fromFlow) {
            e.fromFlow = true;
            err(e);
          } else {
            throw e;
          }
        }
      } else {
        callback.call(context);
      }
    } finally {
      this.stop();
    }
  },

  /**
   * Checks if there is an active {{#crossLink "ProAct.Queues"}}{{/crossLink}} instance in this `ProAct.Flow`.
   *
   * TODO This should be named 'isActive'.
   *
   * @for ProAct.Flow
   * @instance
   * @method isRunning
   */
  isRunning: function () {
    return this.flowInstance !== null && this.flowInstance !== undefined;
  },

  /**
   * Checks if this `ProAct.Flow` is paused.
   *
   * @for ProAct.Flow
   * @instance
   * @method isPaused
   */
  isPaused: function () {
    return this.isRunning() && this.pauseMode;
  },

  /**
   * Pushes an action to the flow.
   * This method can defer in the flow the same action multiple times.
   * <p>
   *  `defer`, `enque` and `add` are aliases of this method.
   * </p>
   * <p>
   *  If the flow is paused, the action will not be defered.
   * </p>
   *
   * TODO Errors should be put in constants!
   *
   * @for ProAct.Flow
   * @instance
   * @method push
   * @param {String} queueName
   *      The name of the queue to defer the action in.
   *      <p>
   *        On the place of this argument the context can be passed and the queue to push in
   *        becomes the first queue available.
   *      </p>
   * @param {Object} context
   *      The context of the action.
   *      It can be null.
   *      <p>
   *        If the method is called with a Function context, the context becomes the action.
   *        This way the method can be called with only one parameter for actions without context.
   *      </p>
   * @param {Function} action
   *      The action to defer into the flow.
   *      <p>
   *        If there is no context and the action is passed in place of the context,
   *        this parameter can hold the arguments of the action.
   *      </p>
   * @param {Array} args
   *      Arguments to be passed to the action when it is executed.
   * @throws {Error} <i>Not in running flow!</i>, if there is no action flow instance.
   */
  push: function (queueName, context, action, args) {
    if (!this.flowInstance) {
      throw new Error('Not in running flow!');
    }
    if (!this.isPaused()) {
      this.flowInstance.push(queueName, context, action, args);
    }
  },

  /**
   * Defers an action to the flow only once per run.
   * <p>
   *  If the action is pushed for the second time using this method, instead of
   *  adding it, its set to be executed later then all the actions that were defered only once, using this method.
   * </p>
   * <p>
   *  `deferOnce`, `enqueOnce` and `addOnce` are aliases of this method.
   * </p>
   * <p>
   *  If the flow is paused, the action will not be defered.
   * </p>
   *
   * @for ProAct.Flow
   * @instance
   * @method pushOnce
   * @param {String} queueName
   *      The name of the queue to defer the action in.
   *      <p>
   *        On the place of this argument the context can be passed and the queue to push in
   *        becomes the first queue of the sub-queues.
   *      </p>
   * @param {Object} context
   *      The context of the action.
   *      It can be null.
   *      <p>
   *        If the method is called with a Function context, the context becomes the action.
   *        This way the method can be called with only one parameter for actions without context.
   *      </p>
   * @param {Function} action
   *      The action to defer.
   *      <p>
   *        If there is no context and the action is passed in place of the context,
   *        this parameter can hold the arguments of the action.
   *      </p>
   * @param {Array} args
   *      Arguments to be passed to the action when it is executed.
   * @throws {Error} <i>Not in running flow!</i>, if there is no action flow instance.
   */
  pushOnce: function (queueName, context, action, args) {
    if (!this.flowInstance) {
      throw new Error('Not in running flow!');
    }
    if (!this.isPaused()) {
      this.flowInstance.pushOnce(queueName, context, action, args);
    }
  },

  pushClose: function (context, action, args) {
    this.closingQueue.pushOnce(context, action, args);
  }
};

/**
 * The {{#crossLink "ProAct.Flow"}}{{/crossLink}} instance used by ProAct's property updates by default.
 * <p>
 *  It defines only one queue - the default one <i>proq</i>.
 * </p>
 * <p>
 *  Override this instance if you are creating a framework or toolset over ProAct.js.
 * </p>
 *
 * @property flow
 * @type ProAct.Flow
 * @for ProAct
 * @final
 */
ProAct.flow = new ProAct.Flow(['proq'], {
  err: function (e) {
    console.log(e);
  },
  flowInstance: {
    queue: {
      err: function (queue, e) {
        e.queue = queue;
        console.log(e);
      }
    }
  }
});

P.F.prototype.begin = P.F.prototype.start;
P.F.prototype.end = P.F.prototype.stop;
P.F.prototype.defer = P.F.prototype.enque = P.F.prototype.add = P.F.prototype.push;
P.F.prototype.deferOnce = P.F.prototype.enqueOnce = P.F.prototype.addOnce = P.F.prototype.pushOnce;
P.F.prototype.flush = P.F.prototype.go = P.F.prototype.run;