Show:
/**
 * @module proact-core
 */


/**
 * ActorUtil provides methods that can be used to make the Actor to 'act'.
 * The Actor is ProAct.js version of the base `Observable` object. Various types
 * of listeners can be attached to it and used to observe its `actions`.
 *
 * On the other hand the `Actor` should do something or `act`, because something
 * has to be observed after all.
 *
 * The `ActorUtil` contains a set of methods that help implementing these `acts`.
 *
 * For example the we can trigger events/values in the `Streams`. This is thier `act`.
 * This triggering can be implemented with ease using the methods defined in `ActorUtil`.
 *
 * Another example is `Properties` - they can be set or updated by the reactive flow -> they should react.
 *
 * So `ActorUtil` provides the `Actors` with helpful methods for `acting` and `reacting`.
 *
 * All these methods use the {{#crossLink "ProAct.Flow"}}{{/crossLink}} to defer the changes the right way.
 * And the using the `flow` these methods handle the dependencies between the `Actors`.
 *
 * Use the methods in the `ActorUtil` to implement your `Actor's` `actions` and `reactions`.
 *
 * @namespace ProAct
 * @private
 * @class ActorUtil
 * @extensionfor ProAct.Actor
 * @static
 */
ActorUtil = {

  /**
   * Updating/notifying method that can be applied to an {{#crossLink "ProAct.Actor"}}{{/crossLink}}
   *
   * This method defers the update and the notifications into {{#crossLink "ProAct.flow"}}{{/crossLink}}.
   *
   * If the state of the caller is {{#crossLink "ProAct.States.destroyed)"}}{{/crossLink}}, an exception will be thrown.
   * If the state of the caller is {{#crossLink "ProAct.States.closed)"}}{{/crossLink}}, nothing will happen.
   *
   * Examples:
   *
   * You can implement a stream and in it's `trigger` method use this:
   * ```
   *   ActorUtil.update.call(this, event);
   * ```
   * This way the event will be triggered into the stream and all the listeners to the stream will be notified.
   * For this to work you'll have to override the `makeEvent` method of the stream to return the unmodified source - no state/no event generation,
   * the event will just go through.
   *
   *
   * If you want to implement a statefull `Actor` like a `property`, you can set a state in it and just notify all the
   * observing `Actors` with this method.
   *
   *
   * @method update
   * @protected
   * @param {Object} [source] The event/value, causing the update -> can be null : no source.
   * @param {Object} [actions] For which actions should notify -> can be null : default actions.
   * @param {Object} [eventData] Data for creating the updating event -> can be null : no data.
   * @return {Object} The calling object.
   */
  update: function (source, actions, eventData) {
    if (this.state === ProAct.States.destroyed) {
      throw new Error('You can not trigger actions on destroyed actors!');
    }

    if (this.state === ProAct.States.closed) {
      return;
    }

    var actor = this;
    if (!P.flow.isRunning()) {
      P.flow.run(function () {
        ActorUtil.doUpdate.call(actor, source, actions, eventData);
      });
    } else {
      ActorUtil.doUpdate.call(actor, source, actions, eventData);
    }
    return this;
  },

  /**
   * Contains the real notify/update logic defered by {{#crossLink "ProAct.ActorUtil/update:method"}}{{/crossLink}} into the flow.
   * It is private method, should not be used - use `update`.
   *
   * @method doUpdate
   * @private
   * @param {Object} [source] The event/value, causing the update -> can be null : no source.
   * @param {Object} [actions] For which actions should notify -> can be null : default actions.
   * @param {Object} [eventData] Data for creating the updating event -> can be null : no data.
   * @return {Object} The calling object.
   */
  doUpdate: function (source, actions, eventData) {
    if (!actions) {
      actions = this.defaultActions();
    }

    var ln, i, j,
        listener,
        listeners,
        length,
        event;

    if (P.U.isString(actions)) {
      listeners = this.listeners[actions];
    } else {
      while (actions.indexOf('close') !== -1) {
        P.U.remove(actions, 'close');
      }

      listeners = [];
      ln = actions.length;

      if (this.parent === null && actions.length === 0) {
        return this;
      }

      for (i = 0; i < ln; i++) {
        listenersForAction = this.listeners[actions[i]];

        if (listenersForAction) {
          for (j = 0; j < listenersForAction.length; j++) {
            if (listenersForAction[j].destroyed || listenersForAction[j].closed) {
              this.off(actions[i], listenersForAction[j]);
              continue;
            }
          }
          listeners = listeners.concat(listenersForAction);
        }
      }
    }

    if (listeners.length === 0 && this.parent === null && actions !== 'close') {
      return this;
    }

    if (actions === 'close' && !this.canClose()) {
      return this;
    }

    length = listeners.length;
    event = this.makeEvent(source, eventData);

    for (i = 0; i < length; i++) {
      listener = listeners[i];
      if (!listener) {
        throw new Error('Invalid null listener for actions : ' + actions);
      }

      if (P.U.isString(actions) && listener.destroyed) {
        this.off(actions, listener);
        continue;
      }

      this.defer(event, listener);

      if (listener.property) {
        ActorUtil.doUpdate.call(listener.property, event);
      }
    }

    if (this.parent && this.parent.call) {
      this.defer(event, this.parent);
    }

    if (actions === 'close') {
      P.flow.pushClose(this, this.doClose);
    }

    return this;
  }
};
P.U.defValProp(ProAct, 'ActorUtil', false, false, false, ActorUtil);