API Docs for: 1.0.0
Show:

File: src/gallery-object-extras/js/object-extras.js

"use strict";

/**
 * @module gallery-object-extras
 */

/**
 * <p>Augments Y.Object with the same higher-order functions that
 * array-extras adds to Y.Array.  Note that, unlike Y.Array, iteration
 * order for objects is arbitrary, so be careful when applying
 * non-commutative operations!</p>
 * 
 * @main gallery-object-extras
 * @class Object~extras
 */

Y.mix(Y.Object,
{
	/**
	 * Executes the supplied function on each item in the object.
	 * Iteration stops if the supplied function does not return a truthy
	 * value.  The function receives the value, the key, and the object
	 * itself as parameters (in that order).
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method every
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {Function} the function to execute on each item
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Boolean} true if every item in the array returns true from the supplied function, false otherwise
	 */
	every: function(o, f, c, proto)
	{
		for (var k in o)
		{
			if ((proto || o.hasOwnProperty(k)) && !f.call(c, o[k], k, o))
			{
				return false;
			}
		}

		return true;
	},

	/**
	 * Executes the supplied function on each item in the object.  Returns
	 * a new object containing the items for which the supplied function
	 * returned a truthy value.  The function receives the value, the key,
	 * and the object itself as parameters (in that order).
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method filter
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {Function} the function to execute on each item
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Object} object of items for which the supplied function returned a truthy value (empty if it never returned a truthy value)
	 */
	filter: function(o, f, c, proto)
	{
		var result = {};

		for (var k in o)
		{
			var v = o[k];
			if ((proto || o.hasOwnProperty(k)) && f.call(c, v, k, o))
			{
				result[k] = v;
			}
		}

		return result;
	},

	/**
	 * Executes the supplied function on each item in the object, searching
	 * for the first item that matches the supplied function.  The function
	 * receives the value, the key, and the object itself as parameters (in
	 * that order).
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method find
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {Function} the function to execute on each item
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Mixed} the first item for which the supplied function returns true, or null if it never returns true
	 */
	find: function(o, f, c, proto)
	{
		for (var k in o)
		{
			var v = o[k];
			if ((proto || o.hasOwnProperty(k)) && f.call(c, v, k, o))
			{
				return v;
			}
		}

		return null;
	},

	/**
	 * Executes the supplied function on each item in the object, searching
	 * for the first item that matches the supplied function.
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method keyOf
	 * @static
	 * @param o {Object} the object to iterate
	 * @param v {Mixed} the value to search for
	 * @param proto {Boolean} include prototype properties
	 * @return {String} key of an item strictly equal to v, or null if not found
	 */
	keyOf: function(o, v, proto)
	{
		for (var k in o)
		{
			if ((proto || o.hasOwnProperty(k)) && o[k] === v)
			{
				return k;
			}
		}

		return null;
	},

	/**
	 * Executes a named method on each item in the object. Items that do
	 * not have a function by that name will be skipped.
	 *
	 * @method invoke
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {String} the function to invoke
	 * @param args* {Any} any number of additional args are passed as parameters to the execution of the named method
	 * @return {Object} all return values, mapped according to the item key
	 */
	invoke: function(o, f)
	{
		var args = Y.Array(arguments, 2, true),
			result = {};

		for (var k in o)
		{
			var v = o[k];
			if (o.hasOwnProperty(k) && Y.Lang.isFunction(v[f]))
			{
				result[k] = v[f].apply(v, args);
			}
		}

		return result;
	},

	/**
	 * Executes the supplied function on each item in the object and
	 * returns a new object with the results.  The function receives the
	 * value, the key, and the object itself as parameters (in that order).
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method map
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {String} the function to invoke
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Object} all return values, mapped according to the item key
	 */
	map: function(o, f, c, proto)
	{
		var result = {};

		for (var k in o)
		{
			if (proto || o.hasOwnProperty(k))
			{
				result[k] = f.call(c, o[k], k, o);
			}
		}

		return result;
	},

	/**
	 * Partitions an object into two new objects, one with the items for
	 * which the supplied function returns true, and one with the items
	 * for which the function returns false.  The function receives the
	 * value, the key, and the object itself as parameters (in that order).
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method partition
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {Function} the function to execute on each item
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Object} object with two properties: matches and rejects. Each is an object containing the items that were selected or rejected by the test function (or an empty object if none).
	 */
	partition: function(o, f, c, proto)
	{
		var result =
		{
			matches: {},
			rejects: {}
		};

		for (var k in o)
		{
			var v = o[k];
			if (proto || o.hasOwnProperty(k))
			{
				var set = f.call(c, v, k, o) ? result.matches : result.rejects;
				set[k]  = v;
			}
		}

		return result;
	},

	/**
	 * Executes the supplied function on each item in the object, folding
	 * the object into a single value.  The function receives the value
	 * returned by the previous iteration (or the initial value if this is
	 * the first iteration), the value being iterated, the key, and the
	 * object itself as parameters (in that order).  The function must
	 * return the updated value.
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method reduce
	 * @static
	 * @param o {Object} the object to iterate
	 * @param init {Mixed} the initial value
	 * @param f {String} the function to invoke
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Mixed} final result from iteratively applying the given function to each item in the object
	 */
	reduce: function(o, init, f, c, proto)
	{
		var result = init;

		for (var k in o)
		{
			if (proto || o.hasOwnProperty(k))
			{
				result = f.call(c, result, o[k], k, o);
			}
		}

		return result;
	},

	/**
	 * Executes the supplied function on each item in the object.  Returns
	 * a new object containing the items for which the supplied function
	 * returned a falsey value.  The function receives the value, the key,
	 * and the object itself as parameters (in that order).
	 *
	 * By default, only properties owned by obj are enumerated. To include
	 * prototype properties, set the proto parameter to true.
	 *
	 * @method reject
	 * @static
	 * @param o {Object} the object to iterate
	 * @param f {Function} the function to execute on each item
	 * @param c {Object} optional context object
	 * @param proto {Boolean} include prototype properties
	 * @return {Object} object of items for which the supplied function returned a falsey value (empty if it never returned a falsey value)
	 */
	reject: function(o, f, c, proto)
	{
		return Y.Object.filter(o, function(v, k, o)
		{
			return !f.call(c, v, k, o);
		},
		c, proto);
	},

	/**
	 * Creates an object by pairing the corresponding elements of two arrays.
	 *
	 * @method zip
	 * @static
	 * @param a1 {Array} the keys which must be strings
	 * @param a2 {Array} the values
	 * @return {Object} object formed by pairing each element of the first array with an item in the second array having the corresponding index
	 */
	zip: function(a1, a2)
	{
		var result = {};

		Y.Array.each(a1, function(v, i)
		{
			result[ v.toString() ] = a2[i];
		});

		return result;
	},

	/**
	 * Extracts a value from an object, given a string containing a valid
	 * JavaScript expression.  In a browser, this uses eval().  In Adobe
	 * AIR, it parses the string, thereby restricting the syntax to only
	 * dot notation.
	 *
	 * @method evalGet
	 * @static
	 * @param o {Object} the object from which to extract a value
	 * @param s {String} the expression
	 * @return {mixed} requested value
	 */
	evalGet: function(o, s)
	{
		if (s.indexOf(';') >= 0)
		{
			throw 'Semi-colon detected in evalGet. Security error.';
		}

		if (!window.air)
		{
			var tmp;
			eval('tmp=o'+s);
			return tmp;
		}

		s = s.split('.');
		s.shift();	// expression must begin with a dot

		var tmp = o;
		while (s.length > 0)
		{
			tmp = tmp[ s.shift() ];
		}

		return tmp;
	},

	/**
	 * Sets a value inside an object, given a string containing a valid
	 * JavaScript expression.  In a browser, this uses eval().  In Adobe
	 * AIR, it parses the string, thereby restricting the syntax to only
	 * dot notation.
	 *
	 * @method evalSet
	 * @static
	 * @param o {Object} the object in which to set a value
	 * @param s {String} the expression
	 * @param v {mixed} the value to set
	 */
	evalSet: function(o, s, v)
	{
		if (s.indexOf(';') >= 0)
		{
			throw 'Semi-colon detected in evalSet. Security error.';
		}

		if (!window.air)
		{
			eval('o'+s+'=v');
			return;
		}

		s = s.split('.');
		s.shift();	// expression must begin with a dot

		var tmp = o;
		while (s.length > 1)
		{
			tmp = tmp[ s.shift() ];
		}

		tmp[ s.shift() ] = v;
	}
});

/**
 * Executes the supplied function on each item in the object, starting at
 * the end and folding the object into a single value.  The function
 * receives the value returned by the previous iteration (or the initial
 * value if this is the first iteration), the value being iterated, the
 * key, and the object itself as parameters (in that order).  The function
 * must return the updated value.
 *
 * By default, only properties owned by obj are enumerated. To include
 * prototype properties, set the proto parameter to true.
 * 
 * Since the order of iteration is undefined for objects, this is identical
 * to `reduce`.
 *
 * @method reduceRight
 * @static
 * @param o {Object} the object to iterate
 * @param init {Mixed} the initial value
 * @param f {String} the function to invoke
 * @param c {Object} optional context object
 * @param proto {Boolean} include prototype properties
 * @return {Mixed} final result from iteratively applying the given function to each item in the object
 */
Y.Object.reduceRight = Y.Object.reduce;