1 /**
  2  * @fileOverview Helper methods for Objects
  3  * @author <a href="http://www.bobs-bits.com">Stephen Reay</a>
  4  */
  5 
  6 if (!Object.size) {
  7 	/**
  8 	 * Calculate the size of an Object
  9 	 * @param object the object to get the size of
 10 	 * @returns {Number} the size of the object
 11 	 */
 12 	Object.size = function size(object) {
 13 		if (object.length) {
 14 			return object.length;
 15 		}
 16 		if (object.__count__) {
 17 			return object.__count__;
 18 		}
 19 		var count = 0;
 20 
 21 		for (var i in object) {
 22 			if (object.hasOwnProperty(i)) {
 23 				count++;
 24 			}
 25 		}
 26 		return count;
 27 	};
 28 }
 29 
 30 if (!Object.update) {
 31 	/**
 32 	 * Updates an Object with key:value pairs from another Object
 33 	 * @param {Object} object the Object to update
 34 	 * @param {Object} updates the new key:value pairs to update the original Object with
 35 	 * @returns {Object} the original Object updated with new key:value pairs
 36 	 * @see Object.merge
 37 	 * @example
 38 	 * var obj = {'cow':'moo', 'dog':'woof'};
 39 	 * Object.update(obj, {'cow':'milk','sheep':'wool'}); //{'cow':'milk', 'dog':'woof', 'sheep':'wool'}
 40 	 */
 41 	Object.update = function update(object, updates) {
 42 		if(Object.isUndefined(updates)) {
 43 			return object;
 44 		}
 45 		for (var att in updates) {
 46 			if (updates.hasOwnProperty(att)) {
 47 				if (object.hasOwnProperty(att) && Object.isObject(object[att]) && Object.isObject(updates[att])) {
 48 					Object.update(object[att], updates[att]);
 49 				}
 50 				else {
 51 					object[att] = updates[att];
 52 				}
 53 			}
 54 		}
 55 		
 56 		return object;
 57 	};
 58 }
 59 
 60 if (!Object.merge) {
 61 	/**
 62 	 * Creates a new Object, by combining the key:value pairs from two Objects
 63 	 * @param {Object} object the first of the Objects to merge
 64 	 * @param {Object} updates the second of the Objects to merge
 65 	 * @returns {Object} new Object updated with new key:value pairs
 66 	 * @see Object.update
 67 	 * @example
 68 	 * var obj = {'cow':'moo', 'dog':'woof'};
 69 	 * var obj2 = Object.merge(obj, {'cow':'milk','sheep':'wool'});
 70 	 * //obj2 == {'cow':'milk', 'dog':'woof', 'sheep':'wool'}
 71 	 * //obj is unchanged
 72 	 */
 73 	Object.merge = function merge(object, updates) {
 74 		var newObj = {};
 75 		for (var att in object) {
 76 			if (object.hasOwnProperty(att)) {
 77 				newObj[att] = object[att];
 78 			}
 79 		}
 80 		return Object.update(newObj, updates);
 81 	};
 82 }
 83 
 84 if (!Object.prototype.inherits) {
 85 	/**
 86 	 * Extend a Class with another's prototype to mimic Class inheritance
 87 	 * @param {Class} parentClass the Class to inherit from
 88 	 * @param [1...n] Arguments to initiate the parent Class with
 89 	 * @author <a href="http://www.coolpage.com/developer/javascript/Correct%20OOP%20for%20Javascript.html">Shelby H. Moore III</a>
 90 	 * @see Function.prototype.inherits
 91 	 * @example
 92 	 * function ClassA(arg) {
 93 	 *     this.prop = arg || false;
 94 	 * }
 95 	 * 
 96 	 * ClassB.inherits(ClassA);
 97 	 * function ClassB(arg) {
 98 	 *     this.inherits(ClassA);
 99 	 *     this.prop2 = arg || 'default';
100 	 * }
101 	 */
102 	Object.prototype.inherits = function inherits(parentClass) {
103 		if (Object.isFunction(parentClass)) {
104 		 	if (this.constructor === parentClass) {
105 		 		throw new ReferenceError("A Class can't inherit from itself");
106 		 	}
107 		 	if (arguments.length > 1) {
108 				parentClass.apply(this, Array.prototype.slice.call(arguments, 1));
109 			}
110 			else {
111 				parentClass.call(this);
112 			}	 	
113 		}
114 	};
115 }
116 
117 if (!Object.isObject) {
118 	/**
119 	 * Convenience method to check if an object is an Object
120 	 * @param object the object to check the type of
121 	 * @returns {Boolean} true if object is an Object, false if not
122 	 */
123 	Object.isObject = function isObject(object) {
124 		return !Object.isUndefined(object) && object.constructor === Object;
125 	};
126 }
127 
128 if (!Object.isArray) {
129 	/**
130 	 * Convenience method to check if an object is an Array
131 	 * @param object the object to check the type of
132 	 * @returns {Boolean} true if object is an Array, false if not
133 	 */
134 	Object.isArray = function isArray(object) {
135 		return !Object.isUndefined(object) && object.constructor === Array;
136 	};
137 }
138 
139 if (!Object.isString) {
140 	/**
141 	 * Convenience method to check if an object is a String
142 	 * @param object the object to check the type of
143 	 * @returns {Boolean} true if object is a String, false if not
144 	 */
145 	Object.isString = function isString(object) {
146 		return !Object.isUndefined(object) && object.constructor === String;
147 	};
148 }
149 
150 if (!Object.isNumber) {
151 	/**
152 	 * Convenience method to check if an object is a Number
153 	 * @param object the object to check the type of
154 	 * @returns {Boolean} true if object is a Number, false if not
155 	 */
156 	Object.isNumber = function isNumber(object) {
157 		return !Object.isUndefined(object) && object.constructor === Number;
158 	};
159 }
160 
161 if (!Object.isDate) {
162 	/**
163 	 * Convenience method to check if an object is a Number
164 	 * @param object the object to check the type of
165 	 * @returns {Boolean} true if object is a Number, false if not
166 	 */
167 	Object.isDate = function isDate(object) {
168 		return !Object.isUndefined(object) && object.constructor === Date;
169 	};
170 }
171 
172 if (!Object.isRegExp) {
173 	/**
174 	 * Convenience method to check if an object is a Regular Expression
175 	 * @param object the object to check the type of
176 	 * @returns {Boolean} true if object is a Regular Expression, false if not
177 	 */
178 	Object.isRegExp = function isRegExp(object) {
179 		return !Object.isUndefined(object) && object.constructor === RegExp;
180 	};
181 }
182 
183 if (!Object.isFunction) {
184 	/**
185 	 * Convenience method to check if an object is a Function
186 	 * @param object the object to check the type of
187 	 * @returns {Boolean} true if object is a Function, false if not
188 	 */
189 	Object.isFunction = function isFunction(object) {
190 		return !Object.isUndefined(object) && object.constructor === Function;
191 	};
192 }
193 
194 if (!Object.isBoolean) {
195 	/**
196 	 * Convenience method to check if an object is a Boolean
197 	 * @param object the object to check the type of
198 	 * @returns {Boolean} true if object is a Boolean, false if not
199 	 */
200 	Object.isBoolean = function isBoolean(object) {
201 		return !Object.isUndefined(object) && object.constructor === Boolean;
202 	};
203 }
204 
205 if (!Object.isElement) {
206 	/**
207 	 * Convenience method to check if an object is a DOM Element
208 	 * @param object the object to check the type of
209 	 * @returns {Boolean} true if object is a DOM Element, false if not
210 	 */
211 	Object.isElement = function isElement(object, tag) {
212 		return !Object.isUndefined(object) && object.nodeType === 1 && (Object.isUndefined(tag) || object.nodeName.toLowerCase() === tag.toLowerCase());
213 	};
214 }
215 
216 if (!Object.isUndefined) {	
217 	/**
218 	 * Convenience method to check if an object is defined
219 	 * @param object the object to check
220 	 * @returns {Boolean} true if object is undefined, false if object is defined
221 	 */
222 	Object.isUndefined = function isUndefined(object) {
223 		return typeof object === 'undefined';
224 	};
225 }
226 
227 if (!Object.toArray) {
228 	/**
229 	 * Convert an array-like object into a true Array
230 	 * @param {Array-like} array array-like object to create true Array from
231 	 * @returns {Array} new Array created from original object
232 	 * @example
233 	 * function foo() {
234 	 *     var args = Object.toArray(arguments); ['moo','cow']
235 	 *     args.forEach(someOtherFunc);
236 	 * }
237 	 * foo('moo', 'cow');
238 	 */
239 	Object.toArray = function toArray(object) {
240 		if (Object.isArray(object)) {
241 			return object;
242 		}
243 		try { //This might bomb in Explorer!
244 			return Array.prototype.slice.call(object, 0);
245 		}
246 		catch(e) {
247 			return Array.prototype.map.call(object, function(a) {
248 				return a;
249 			});
250 		}
251 	};
252 }
253 
254 if (!Object.forEach) {
255 	/**
256 	 * Executes a provided function once per object element.
257 	 * @param {Object} object the Object to iterate over
258 	 * @param {Function} callback function to execute for each element
259 	 * @param {Object} [context] to use as this when executing callback
260 	 * @see Array#forEach
261 	 * @example
262 	 * Object.forEach({'moo':'cow', 'woof':'dog'}, myFunc);
263 	 */	
264 	Object.forEach = function forEach(object, callback, context) {
265 		if (!Object.isFunction(callback)) {
266 			throw new TypeError();
267 		}
268 		
269 		for (var i in object) {
270 			if (object.hasOwnProperty(i)) {
271 				callback.call(context, object[i], i, object);
272 			}
273 		}
274 	};
275 }
276 	
277 if (!Object.map) {
278 	/**
279 	 * Creates a new Object with the results of calling a provided function on every element in this Object
280 	 * @param {Object} object the Object to iterate over
281 	 * @param {Function} callback function that produces an element of the new Object from an element of the current one
282 	 * @param {Object} [context] to use as this when executing callback
283 	 * @returns {Object} new Object with the results of calling a provided function on every element in this Object
284 	 * @see Array#map
285 	 * @example
286 	 * function makePseudoPlural(single) {
287 	 *     return single.replace(/o/g, "e");
288 	 * }
289 	 * var singles = {"human":"foot", "dinner":"goose", "target":"moose"];
290 	 * var plurals = Object.map(singles, makePseudoPlural); //{"human":"feet", "dinner":"geese", "target":"meese"}
291 	 */
292 	Object.map = function map(object, callback, context) {
293 		if (!Object.isFunction(callback)) {
294 			throw new TypeError();
295 		}
296 		
297 		var obj = {};
298 		
299 		for (var i in object) {
300 			if (object.hasOwnProperty(i)) {
301 				obj[i] = callback.call(context, object[i], i, object);
302 			}
303 		}
304 		
305 		return obj;
306 	};
307 }
308 
309 if (!Object.every) {
310 	/**
311 	 * Tests whether all elements in the Object pass the test implemented by the provided function
312 	 * @param {Object} object the Object to iterate over
313 	 * @param {Function} callback function to test for each element
314 	 * @param {Object} [context] to use as this when executing callback
315 	 * @returns {Boolean} true if all elements pass the test implented by callback, false if not
316 	 * @see Array#every
317 	 */	
318 	Object.every = function every(object, callback, context) {
319 		if (!Object.isFunction(callback)) {
320 			throw new TypeError();
321 		}
322 
323 		for (var i in object) {
324 			if (object.hasOwnProperty(i) && !callback.call(context, object[i], i, object)) {
325 				return false;
326 			}
327 		}
328 		return true;
329 	};
330 }
331 
332 if (!Object.some) {
333 	/**
334 	 * Tests whether some element in the array passes the test implemented by the provided function
335 	 * @param {Object} object the Object to iterate over
336 	 * @param {Function} callback function to test for each element
337 	 * @param {Object} [context] to use as this when executing callback
338 	 * @returns {Boolean} true if some element passes the test implented by callback, false if not
339 	 * @see Array#some
340 	 */
341 	Object.some = function some(object, callback, context) {
342 		if (!Object.isFunction(callback)) {
343 			throw new TypeError();
344 		}
345 
346 		for (var i in object) {
347 			if (object.hasOwnProperty(i) && callback.call(context, object[i], i, object)) {
348 				return true;
349 			}
350 			return true;
351 		}
352 		return false;
353 	};
354 }
355 
356 if (!Object.filter) {
357 	/**
358 	 * Creates a new Object with all elements that pass the test implemented by the provided function.
359 	 * @param {Object} object the Object to iterate over
360 	 * @param {Function} callback function to test each element of the array
361 	 * @param {Object} [context] to use as this when executing callback
362 	 * @returns {Object} a new Object with all elements that pass the test implemented by the provided function
363 	 * @see Array#filter
364 	 */
365 	Object.filter = function filter(object, callback, context) {
366 		if (!Object.isFunction(callback)) {
367 			throw new TypeError();
368 		}
369 		
370 		var obj = {};
371 		
372 		for (var i in object) {
373 			if (object.hasOwnProperty(i)) {
374 				var val = object[i];
375 				if (callback.call(context, val, i, object)) {
376 					obj[i] = val;
377 				}
378 			}
379 		}
380 		
381 		return obj;
382 	};
383 }	
384 
385 if (!Object.serialize) {
386 	/**
387 	 * Serialize an Object into a string. If the value of a property is an array, the entries will be added multiple times
388 	 * @param {Object} object the data object to serialize
389 	 * @param {String} [seperator='&'] the seperator to use between the values in the generated string
390 	 * @returns {String} the serialized version of the data
391 	 */
392 	Object.serialize = function serialize(object, seperator) {
393 		seperator = seperator || '&';
394 		var serializedString = "";
395 		for (var i in object) {
396 			if (object.hasOwnProperty(i) && !Object.isFunction(object[i])) {
397 				if (serializedString.length > 0) {
398 					serializedString += seperator;
399 				}
400 				if (Object.isArray(object[i])) {
401 					serializedString += (i + '=' + object[i].join(seperator + i + '='));
402 				}
403 				else {
404 					serializedString += (i + '=' + object[i]);
405 				}
406 			}
407 		}
408 		
409 		return serializedString;
410 	};
411 }
412 
413 if (!Object.getKeys) {
414 	/**
415 	 * Get the keys (property names) from an Object as an Array
416 	 * @param {Object} object the Object to get the keys (property names) from
417 	 * @returns {Array} the keys (property names) from the object
418 	 */
419 	Object.getKeys = function getKeys(object) {
420 		var newArr = [];
421 		for (var i in object) {
422 			if (object.hasOwnProperty(i)) {
423 				newArr.push(i);
424 			}
425 		}
426 	
427 		return newArr;
428 	};
429 }
430 
431 if (!Object.getValues) {
432 	/**
433 	 * Get the values from an Object as an Array
434 	 * @param {Object} object the Object to get the values from
435 	 * @returns {Array} the values from the object
436 	 */
437 	Object.getValues = function getValues(object) {
438 		var newArr = [];
439 		for (var i in object) {
440 			if (object.hasOwnProperty(i)) {
441 				newArr.push(object[i]);
442 			}
443 		}
444 	
445 		return newArr;
446 	};
447 }