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

function Provider () {
  this.stored = {};
}
function StreamProvider () {
  P.R.Provider.call(this);
}
function FunctionProvider () {
  P.R.Provider.call(this);
}
function ProObjectProvider () {
  P.R.Provider.call(this);
}

function streamConstructArgs (args) {
  var queueName;
  if (args.length === 2) {
    queueName = args[0];
    args[0] = args[1];
  }
  return [queueName].concat(args);
}

P.U.ex(ProAct.Registry, {

  /**
   * Constructs a `ProAct.Registry.Provider`.
   * The {{#crossLink "ProAct.Registry"}}{{/crossLink}} uses registered providers as storage for different objects.
   * <p>
   *  Every provider has one or more namespaces in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} it is registered to.
   * </p>
   * <p>
   *  Every provider knows how to store its type of obects, how to make them, or delete them.
   * </p>
   *
   * @namespace ProAct.Registry
   * @class Provider
   * @constructor
   * @static
   */
  Provider: Provider,

  /**
   * Constructs a `ProAct.Registry.StreamProvider`. The {{#crossLink "ProAct.Registry"}}{{/crossLink}} uses registered stream providers as storage for {{#crossLink "ProAct.Stream"}}{{/crossLink}}s.
   *
   * @namespace ProAct.Registry
   * @class StreamProvider
   * @constructor
   * @extends ProAct.Registry.Provider
   * @static
   */
  StreamProvider: StreamProvider,

  /**
   * Constructs a `ProAct.Registry.FunctionProvider`.
   * The {{#crossLink "ProAct.Registry"}}{{/crossLink}} uses registered function providers as storage for Functions.
   * <p>
   *  The function provider doesn't have implementation for creation of new functions, only for storing, readin and removing them.
   * </p>
   *
   * @namespace ProAct.Registry
   * @class FunctionProvider
   * @constructor
   * @extends ProAct.Registry.Provider
   * @static
   */
  FunctionProvider: FunctionProvider,

  /**
   * Constructs a `ProAct.Registry.ProObjectProvider`.
   * The {{#crossLink "ProAct.Registry"}}{{/crossLink}} uses registered function providers as storage for objects with reactive {{#crossLink "ProAct.Property"}}{{/crossLink}} instances.
   *
   * @namespace ProAct.Registry
   * @class ProObjectProvider
   * @constructor
   * @extends ProAct.Registry.Provider
   * @static
   */
  ProObjectProvider: ProObjectProvider
});

ProAct.Registry.Provider.prototype = {

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

  /**
   * Creates and stores an instance of the object this `ProAct.Registry.Provider` manages.
   * <p>
   *  For the creation is used the {{#crossLink "ProAct.Registry.Provider/provide:method"}}{{/crossLink}} method.
   * </p>
   *
   * @for ProAct.Registry.Provider
   * @instance
   * @method make
   * @param {String} key
   *      The key on which the new instance will be stored.
   * @param {Array} options
   *      Array containing options for the creation process. For example the exact sub-type of the object to create (optional).
   * @param [...]
   *      Parameters passed to the constructor when the new instance is created.
   * @return {Object}
   *      The newly created and stored object.
   */
  make: function (key, options) {
    var provided, args = slice.call(arguments, 1);
    this.stored[key] = provided = this.provide.apply(this, args);
    return provided;
  },

  /**
   * Stores an instance of an object this `ProAct.Registry.Provider` manages.
   *
   * @for ProAct.Registry.Provider
   * @instance
   * @method store
   * @param {String} key
   *      The key on which the <i>object</i> will be stored.
   * @param {Object} object
   *      The object to store.
   * @return {Object}
   *      The stored object.
   */
  store: function (key, object) { return this.stored[key] = object; },

  /**
   * Reads a stored instance.
   *
   * @for ProAct.Registry.Provider
   * @instance
   * @method get
   * @param {String} key
   *      The key to read.
   * @return {Object}
   *      The stored object corresponding to the passed <i>key</i> or undefined if there is no such object.
   */
  get: function (key) { return this.stored[key]; },

  /**
   * Deletes a stored instance.
   *
   * @for ProAct.Registry.Provider
   * @instance
   * @method delete
   * @param {String} key
   *      The key to delete.
   * @return {Object}
   *      The stored object corresponding to the passed <i>key</i> or undefined if there is no such object.
   */
  del: function(key) {
    var deleted = this.get(key);
    delete this.stored[key];
    return deleted;
  },

  /**
   * A callback called by the {{#crossLink "ProAct.Registry"}}{{/crossLink}} when <i>this</i> `ProAct.Registry.Provider` is registered.
   *
   * @for ProAct.Registry.Provider
   * @instance
   * @protected
   * @abstract
   * @method registered
   * @param {ProAct.Registry} registery
   *      The registry in which <i>this</i> is being registered.
   */
  registered: function (registry) {},

  /**
   * An object containing all the available sub-types constructions of the managed by <i>this</i> class.
   * <p>
   *  Should always have a 'basic' field for the default construction operation.
   * </p>
   *
   * @namespace ProAct.Registry.Provider
   * @class types
   * @static
   */
  types: {

    /**
     * Defines default construction logic for the managed object.
     * <p>
     *  For example if we have a `FooProvider`, this method will be something like:
     *  <pre>
     *    return new Foo();
     *  </pre>
     * </p>
     * <p>
     *  It is abstract and must be overridden by the extenders, or an Error will be thrown.
     * </p>
     *
     * @for ProAct.Registry.Provider.types
     * @protected
     * @instance
     * @abstract
     * @method basic
     * @return {Object}
     *      An isntance of the managed class of objects.
     */
    basic: function () { throw new Error('Abstract: implement!'); }
  },

  /**
   * Provides a new instance of the managed by <i>this</i> `ProAct.Registry.Provider` object.
   *
   * @for ProAct.Registry.Provider
   * @instance
   * @method provide
   * @param {Array} options
   *      An array containing the key of the object to create and store.
   *      It may contain data to pass to the constructor of the object.
   * @param [...]
   *      Arguments that should be passed to the constructor.
   * @return {Object}
   *      An isntance of the managed class of objects.
   */
  provide: function (options) {
    if (options) {
      var type = options[0],
          regexp, matched, args,
          argumentData = slice.call(arguments, 1);
      if (type && !(type === 'basic')) {
        regexp = new RegExp("(\\w*)\\(([\\s\\S]*)\\)");
        matched = regexp.exec(type);
        args = matched[2];
        if (args) {
          args = args.split(',');
        }
        type = matched[1];
        if (type && this.types[type]) {
          return this.types[type].apply(this, [args].concat(argumentData));
        }
      }
    }

    return this.types.basic.apply(this, arguments);
  }
};

ProAct.Registry.StreamProvider.prototype = P.U.ex(Object.create(P.R.Provider.prototype), {

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

  /**
   * A callback called by the {{#crossLink "ProAct.Registry"}}{{/crossLink}}
   * when <i>this</i> `ProAct.Registry.StreamProvider` is registered.
   * <p>
   *  It adds the methods <i>s</i> and <i>stream</i> to the
   *  {{#crossLink "ProAct.Registry"}}{{/crossLink}}, which are aliases
   *  of <i>this</i>' {{#crossLink "ProAct.Registry.StreamProvider/get:method"}}{{/crossLink}} method.
   * </p>
   *
   * @for ProAct.Registry.StreamProvider
   * @protected
   * @instance
   * @method registered
   * @param {ProAct.Registry} registry
   *      The registry in which <i>this</i> is being registered.
   */
  registered: function (registry) {
    registry.s = registry.stream = P.U.bind(this, this.get);
  },

  /**
   * An object containing all the available sub-types constructions of the managed by <i>this</i> class.
   *
   * @for ProAct.Registry.StreamProvider
   * @namespace ProAct.Registry.StreamProvider
   * @class types
   * @type Object
   * @property types
   */
  types: {

    /**
     * Constructs a simple {{#crossLink "ProAct.Stream"}}{{/crossLink}}
     * <p>
     *  <pre>
     *    return new ProAct.Stream();
     *  </pre>
     * </p>
     *
     * @for ProAct.Registry.StreamProvider.types
     * @protected
     * @instance
     * @method basic
     * @return {ProAct.Stream}
     *      An isntance of {{#crossLink "ProAct.Stream"}}{{/crossLink}}.
     */
    basic: function (args) { return P.stream(undefined, undefined, undefined, args[0]); },

    /**
     * Constructs a {{#crossLink "ProAct.DelayedStream"}}{{/crossLink}}
     * <p>
     *  <pre>
     *    return new ProAct.DelayedStream(delay);
     *  </pre>
     * </p>
     *
     * @for ProAct.Registry.StreamProvider.types
     * @protected
     * @instance
     * @method delayed
     * @param {Array} args
     *      An array of arguments - the first element of which is the <i>delay</i> of the stream to construct.
     * @return {ProAct.DelayedStream}
     *      An isntance of {{#crossLink "ProAct.DelayedStream"}}{{/crossLink}}.
     */
    delayed: function (args) {
      var args = streamConstructArgs(args);
      return new P.DBS(args[0], parseInt(args[1], 10));
    },

    /**
     * Constructs a {{#crossLink "ProAct.SizeBufferedStream"}}{{/crossLink}}
     * <p>
     *  <pre>
     *    return new ProAct.SizeBufferedStream(size);
     *  </pre>
     * </p>
     *
     * @for ProAct.Registry.StreamProvider.types
     * @protected
     * @instance
     * @method size
     * @param {Array} args
     *      An array of arguments - the first element of which is the <i>size</i> of the stream to construct.
     * @return {ProAct.SizeBufferedStream}
     *      An isntance of {{#crossLink "ProAct.SizeBufferedStream"}}{{/crossLink}}.
     */
    size: function (args) {
      var args = streamConstructArgs(args);
      return new P.SBS(args[0], parseInt(args[1], 10));
    },

    /**
     * Constructs a {{#crossLink "ProAct.DebouncingStream"}}{{/crossLink}}
     * <p>
     *  <pre>
     *    return new ProAct.DebouncingStream(delay);
     *  </pre>
     * </p>
     *
     * @for ProAct.Registry.StreamProvider.types
     * @protected
     * @instance
     * @method debouncing
     * @param {Array} args
     *      An array of arguments - the first element of which is the <i>delay</i> of the stream to construct.
     * @return {ProAct.DebouncingStream}
     *      An isntance of {{#crossLink "ProAct.DebouncingStream"}}{{/crossLink}}.
     */
    debouncing: function (args) {
      var args = streamConstructArgs(args);
      return new P.DDS(args[0], parseInt(args[1], 10));
    },

    /**
     * Constructs a {{#crossLink "ProAct.ThrottlingStream"}}{{/crossLink}}
     * <p>
     *  <pre>
     *    return new ProAct.ThrottlingStream(delay);
     *  </pre>
     * </p>
     *
     * @for ProAct.Registry.StreamProvider.types
     * @protected
     * @instance
     * @method throttling
     * @param {Array} args
     *      An array of arguments - the first element of which is the <i>delay</i> of the stream to construct.
     * @return {ProAct.ThrottlingStream}
     *      An isntance of {{#crossLink "ProAct.ThrottlingStream"}}{{/crossLink}}.
     */
    throttling: function (args) {
      var args = streamConstructArgs(args);
      return new P.TDS(args[0], parseInt(args[1], 10));
    }
  }
});

var higher = {
  split: function (provider, action, data) {
    var keys = data.split(action),
        ln = keys.length, i,
        functions = [];
    for (i = 0; i < ln; i++) {
      functions.push(provider.get(keys[i].trim()));
    }

    return functions;
  },
  accumulator: function (functions, initial, computation) {
    return function () {
      var i, ln = functions.length, result = initial;
      for (i = 0; i < ln; i++) {
        result = computation(result, functions[i].apply(null, arguments));
      }
      return result;
    };
  },
  or: function (tillNow, argument) {
    return tillNow || argument;
  },
  and: function (tillNow, argument) {
    return tillNow && argument;
  }
};

ProAct.Registry.FunctionProvider.prototype = P.U.ex(Object.create(P.R.Provider.prototype), {

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

  // Private stuff
  predefinedActions: {
    map: 'mapping',
    filter: 'filtering',
    acc: 'accumulation'
  },

  /**
   * Reads a stored instance.
   * <p>
   *  If stored instance is not found and the key is in the form:
   *  actions(arg) - it is searched in the predefined lambdas, for example:
   *  <pre>
   *    map(+)
   *  </pre>
   * </p>
   *
   * @for ProAct.Registry.FunctionProvider
   * @instance
   * @method get
   * @param {String} key
   *      The key to read.
   * @return {Object}
   *      The stored object corresponding to the passed <i>key</i> or
   *      predefined lambda or undefined if there is no such object.
   */
  get: function (key) {
    var func,
        reg, matched,
        action, args,
        i, ln;

    if (key.indexOf('OR') !== -1) {
      return higher.accumulator(higher.split(this, 'OR', key), false, higher.or);
    } else if (key.indexOf('AND') !== -1) {
      return higher.accumulator(higher.split(this, 'AND', key), true, higher.and);
    } else if (key.indexOf('!') === 0) {
      func = this.get(key.substring(1));
      return function () {
        return !func.apply(null, arguments);
      };
    }

    func = this.stored[key];
    if (!func) {
      reg = new RegExp("(\\w*)\\(([\\s\\S]*)\\)");
      matched = reg.exec(key);
      if (matched) {
        action = matched[1], args = matched[2],
        func = dsl.predefined[this.predefinedActions[action]][args];
      }
    }

    return func;
  }
});

ProAct.Registry.ProObjectProvider.prototype = P.U.ex(Object.create(P.R.Provider.prototype), {

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

  /**
   * A callback called by the {{#crossLink "ProAct.Registry"}}{{/crossLink}}
   * when <i>this</i> `ProAct.Registry.ProObjectProvider` is registered.
   * <p>
   *  It adds the methods <i>po</i> and <i>proObject</i> to the {{#crossLink "ProAct.Registry"}}{{/crossLink}},
   *  which are aliases of <i>this</i>' {{#crossLink "ProAct.Registry.ProObjectProvider/get:method"}}{{/crossLink}} method.
   * </p>
   * <p>
   *  It adds the method <i>prob</i> to the {{#crossLink "ProAct.Registry"}}{{/crossLink}},
   *  which is alias of <i>this</i>' {{#crossLink "ProAct.Registry.ProObjectProvider/make:method"}}{{/crossLink}} method.
   * </p>
   *
   * @for ProAct.Registry.StreamProvider
   * @protected
   * @instance
   * @protected
   * @method registered
   * @param {ProAct.Registry} registery
   *      The registry in which <i>this</i> is being registered.
   */
  registered: function (registry) {
    registry.po = registry.proObject = P.U.bind(this, this.get);
    registry.prob = P.U.bind(this, function (key, val, meta) {
      return this.make(key, null, val, meta);
    });
  },

  /**
   * An object containing all the available sub-types constructions of the managed by <i>this</i> class.
   *
   * @for ProAct.Registry.ProObjectProvider
   * @namespace ProAct.Registry.ProObjectProvider
   * @class types
   * @type Object
   * @property types
   */
  types: {
    stat: function (options, value, meta) {
      return P.P.value(value, meta);
    },

    /**
     * Constructs a ProAct.js reactive object from original one, using {{#crossLink "ProAct/prob:method"}}{{/crossLink}}
     * <p>
     *  <pre>
     *    return new ProAct.prob(value, meta);
     *  </pre>
     * </p>
     *
     * @for ProAct.Registry.ProObjectProvider.types
     * @instance
     * @method basic
     * @protected
     * @param {Array} options
     *      Array containing options for the creation process.
     * @param {Object} value
     *      The object/value to make reactive.
     * @param {Object|String} meta
     *      Meta-data used to help in the reactive object creation.
     * @return {Object}
     *      A ractive object.
     */
    basic: function (options, value, meta) {
      return P.prob(value, meta);
    }
  }
});

streamProvider = new P.R.StreamProvider();
functionProvider = new P.R.FunctionProvider();
proObjectProvider = new P.R.ProObjectProvider();

/**
 * The {{#crossLink "ProAct.Registry"}}{{/crossLink}} instance used by ProAct's by default.
 * <p>
 *  It has a {{#crossLink "ProAct.Registry.StreamProvider"}}{{/crossLink}} registered on the <i>s</i> namespace.
 * </p>
 * <p>
 *  It has a {{#crossLink "ProAct.Registry.ProObjectProvider"}}{{/crossLink}} registered on the <i>po</i> and <i>obj</i> namespaces.
 * </p>
 * <p>
 *  It has a {{#crossLink "ProAct.Registry.FunctionProvider"}}{{/crossLink}} registered on the <i>f</i> and <i>l</i> namespaces.
 * </p>
 * <p>
 *  Override this instance or register your own providers in it to extend the ProAct.js DSL.
 * </p>
 *
 * @property registry
 * @type ProAct.Registry
 * @for ProAct
 * @static
 */
ProAct.registry = new P.R()
  .register('s', streamProvider)
  .register('po', proObjectProvider)
  .register('obj', proObjectProvider)
  .register('f', functionProvider)
  .register('l', functionProvider);