1 var comb = exports;
  2 
  3 var merge = function(target, source) {
  4     var name, s;
  5     for (name in source) {
  6         s = source[name];
  7         if (!(name in target) || (target[name] !== s)) {
  8             target[name] = s;
  9         }
 10     }
 11     return target;
 12 };
 13 
 14 /**
 15  * Merges objects together
 16  * NOTE: this function takes a variable number of objects to merge
 17  *
 18  * @example
 19  *
 20  * var myObj = {};
 21  * comb.merge(myObj, {test : true});
 22  *
 23  * myObj.test => true
 24  *
 25  * comb.merge(myObj, {test : false}, {test2 : false}, {test3 : "hello", test4 : "world"});
 26  * myObj.test => false
 27  * myObj.test2 => false
 28  * myObj.test3 => "hello"
 29  * myObj.test4 => "world"
 30  *
 31  *
 32  * @param {Object} obj the object to merge into
 33  * @param {Obejct} props variable number of objects to merge into the obj
 34  *
 35  * @returns {Object} the merged object
 36  */
 37 comb.merge = function(obj, props) {
 38     if (!obj) {
 39         obj = {};
 40     }
 41     for (var i = 1, l = arguments.length; i < l; i++) {
 42         merge(obj, arguments[i]);
 43     }
 44     return obj; // Object
 45 };
 46 
 47 /**
 48  * Extends the prototype of an object if it exists otherwise it extends the object.
 49  *
 50  * @example
 51  *
 52  *  var MyObj = function(){};
 53  *  MyObj.prototype.test = true;
 54  *  comb.extend(MyObj, {test2 : false, test3 : "hello", test4 : "world"});
 55  *
 56  *  var myObj = new MyObj();
 57  *
 58  *  myObj.test => true
 59  *  myObj.test2 => false
 60  *  myObj.test3 => "hello"
 61  *  myObj.test4 => "world"
 62  *
 63  *  var myObj2 = {};
 64  *  myObj2.test = true;
 65  *  comb.extend(myObj2, {test2 : false, test3 : "hello", test4 : "world"});
 66  *
 67  *  myObj2.test => true
 68  *  myObj2.test2 => false
 69  *  myObj2.test3 => "hello"
 70  *  myObj2.test4 => "world"
 71  *
 72  *
 73  * @param {Object} parent the parent object to extend
 74  * @param {Object} extend the extension object to mixin to the parent
 75  *
 76  * @returns {Object} returns the extended object
 77  */
 78 comb.extend = function(parent, extend){
 79     var proto = parent.prototype || parent;
 80     return exports.merge(proto, extend);
 81 };
 82 
 83 /**
 84  * Determines if obj is an object
 85  *
 86  * @param {Anything} obj the thing to test if it is an object
 87  *
 88  * @returns {Boolean} true if it is an object false otherwise
 89  */
 90 comb.isObject = function(obj) {
 91     var undef;
 92     return obj != null && obj != undef && typeof obj == "object";
 93 };
 94 
 95 /**
 96  * Determins if an object is just a hash and not a qualified Object such as number
 97  * 
 98  * @example
 99  *    comb.isHash({}) => true
100  *    comb.isHash({1 : 2, a : "b"}) => true
101  *    comb.isHash(new Date()) => false
102  *    comb.isHash(new String()) => false
103  *    comb.isHash(new Number()) => false
104  *    comb.isHash(new Boolean()) => false
105  *    comb.isHash() => false
106  *    comb.isHash("") => false
107  *    comb.isHash(1) => false
108  *    comb.isHash(false) => false
109  *    comb.isHash(true) => false
110  * @param {Anything} obj the thing to test if it is a hash
111  *
112  * @returns {Boolean} true if it is a hash false otherwise
113  */
114 comb.isHash = function(obj){
115     var ret = comb.isObject(obj);
116     return ret && obj.constructor === Object;
117 }
118 
119 /**
120  * Determines if an object is empty
121  *
122  * @example
123  *
124  * comb.isEmpty({}) => true
125  * comg.isEmpty({a : 1}) => false
126  *
127  * @param object the object to test
128  */
129 comb.isEmpty = function(object) {
130     if (object) {
131         for (var i in object) {
132             if (object.hasOwnProperty(i)) {
133                 return false;
134             }
135         }
136     }
137     return true;
138 }
139