(function(JSUS) {
function OBJ(){};
var compatibility = null;
if ('undefined' !== typeof JSUS.compatibility) {
compatibility = JSUS.compatibility();
} |
|
¶ OBJ.equalsChecks for deep equality between two objects, strings or primitive types All nested properties are checked, and if they differ in at least one returns FALSE, otherwise TRUE. Takes care of comparing the following special cases:
Params
o1
object
The first object
o2
object
The second object
Returns
boolean
TRUE if the objects are deeply equal.
|
OBJ.equals = function(o1, o2) {
var type1, type2, primitives, p;
type1 = typeof o1;
type2 = typeof o2;
if (type1 !== type2) return false;
if ('undefined' === type1 || 'undefined' === type2) {
return (o1 === o2);
}
if (o1 === null || o2 === null) {
return (o1 === o2);
}
if (('number' === type1 && isNaN(o1)) &&
('number' === type2 && isNaN(o2))) {
return (isNaN(o1) && isNaN(o2));
} |
Check whether arguments are not objects |
primitives = {number: '', string: '', boolean: ''}
if (type1 in primitives) {
return o1 === o2;
}
if ('function' === type1) {
return o1.toString() === o2.toString();
}
for (p in o1) {
if (o1.hasOwnProperty(p)) {
if ('undefined' === typeof o2[p] &&
'undefined' !== typeof o1[p]) return false;
if (!o2[p] && o1[p]) return false;
switch (typeof o1[p]) {
case 'function':
if (o1[p].toString() !== o2[p].toString()) return false;
default:
if (!OBJ.equals(o1[p], o2[p])) return false;
}
}
} |
Check whether o2 has extra properties TODO: improve, some properties have already been checked! |
for (p in o2) {
if (o2.hasOwnProperty(p)) {
if ('undefined' === typeof o1[p] &&
'undefined' !== typeof o2[p]) return false;
if (!o1[p] && o2[p]) return false;
}
}
return true;
}; |
¶ OBJ.isEmptyReturns TRUE if an object has no own properties Does not check properties of the prototype chain. Params
o
object
The object to check
Returns
boolean
TRUE, if the object has no properties
|
OBJ.isEmpty = function(o) {
var key;
if ('undefined' === typeof o) return true;
for (key in o) {
if (o.hasOwnProperty(key)) {
return false;
}
}
return true;
}; |
¶ OBJ.sizeCounts the number of own properties of an object. Prototype chain properties are excluded. Params
obj
object
The object to check
Returns
number
The number of properties in the object
|
OBJ.size = OBJ.getListSize = function(obj) {
var n, key;
if (!obj) return 0;
if ('number' === typeof obj) return 0;
if ('string' === typeof obj) return 0;
n = 0;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
n++;
}
}
return n;
}; |
¶ OBJ._obj2ArrayExplodes an object into an array of keys and values, according to the specified parameters. A fixed level of recursion can be set. API
private
Params
obj
object
The object to convert in array
keyed
boolean
TRUE, if also property names should be included.
Defaults, FALSE
level
number
Optional. The level of recursion.
Defaults, undefined
Returns
array
The converted object
|
OBJ._obj2Array = function(obj, keyed, level, cur_level) {
var result, key;
if ('object' !== typeof obj) return [obj];
if (level) {
cur_level = ('undefined' !== typeof cur_level) ? cur_level : 1;
if (cur_level > level) return [obj];
cur_level = cur_level + 1;
}
result = [];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (keyed) result.push(key);
if ('object' === typeof obj[key]) {
result = result.concat(OBJ._obj2Array(obj[key], keyed,
level, cur_level));
}
else {
result.push(obj[key]);
}
}
}
return result;
}; |
¶ OBJ.obj2ArrayConverts an object into an array, keys are lost Recursively put the values of the properties of an object into an array and returns it. The level of recursion can be set with the parameter level. By default recursion has no limit, i.e. that the whole object gets totally unfolded into an array. Params
obj
object
The object to convert in array
level
number
Optional. The level of recursion. Defaults,
undefined
Returns
array
The converted object
See
OBJ._obj2Array
See
OBJ.obj2KeyedArray
|
OBJ.obj2Array = function(obj, level) {
return OBJ._obj2Array(obj, false, level);
}; |
¶ OBJ.obj2KeyedArrayConverts an object into array, keys are preserved Creates an array containing all keys and values of an object and returns it. Params
obj
object
The object to convert in array
level
number
Optional. The level of recursion. Defaults,
undefined
Returns
array
The converted object
See
OBJ.obj2Array
|
OBJ.obj2KeyedArray = OBJ.obj2KeyArray = function(obj, level) {
return OBJ._obj2Array(obj, true, level);
}; |
¶ OBJ.keysScans an object an returns all the keys of the properties, into an array. The second paramter controls the level of nested objects to be evaluated. Defaults 0 (nested properties are skipped). Params
obj
object
The object from which extract the keys
level
number
Optional. The level of recursion. Defaults 0
Returns
array
The array containing the extracted keys
See
Object.keys
|
OBJ.keys = OBJ.objGetAllKeys = function(obj, level, curLevel) {
var result, key;
if (!obj) return [];
level = 'number' === typeof level && level >= 0 ? level : 0;
curLevel = 'number' === typeof curLevel && curLevel >= 0 ? curLevel : 0;
result = [];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
result.push(key);
if (curLevel < level) {
if ('object' === typeof obj[key]) {
result = result.concat(OBJ.objGetAllKeys(obj[key],
(curLevel+1)));
}
}
}
}
return result;
}; |
¶ OBJ.implodeSeparates each property into a new object and returns them into an array E.g.
Params
obj
object
The object to implode
Returns
array
result The array containig all the imploded properties
|
OBJ.implode = OBJ.implodeObj = function(obj) {
var result, key, o;
if (!obj) return [];
result = [];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
o = {};
o[key] = obj[key];
result.push(o);
}
}
return result;
}; |
¶ OBJ.cloneCreates a perfect copy of the object passed as parameter Recursively scans all the properties of the object to clone. Properties of the prototype chain are copied as well. Primitive types and special values are returned as they are. Params
obj
object
The object to clone
Returns
object
clone The clone of the object
|
OBJ.clone = function(obj) {
var clone, i, value;
if (!obj) return obj;
if ('number' === typeof obj) return obj;
if ('string' === typeof obj) return obj;
if ('boolean' === typeof obj) return obj;
if (obj === NaN) return obj;
if (obj === Infinity) return obj;
if ('function' === typeof obj) { |
|
clone = function() { return obj.apply(clone, arguments); };
}
else {
clone = Object.prototype.toString.call(obj) === '[object Array]' ?
[] : {};
}
for (i in obj) { |
TODO: index i is being updated, so apply is called on the last element, instead of the correct one. if ('function' === typeof obj[i]) { value = function() { return obj[i].apply(clone, arguments); }; } It is not NULL and it is an object |
if (obj[i] && 'object' === typeof obj[i]) { |
Is an array. |
if (Object.prototype.toString.call(obj[i]) === '[object Array]') {
value = obj[i].slice(0);
} |
Is an object. |
else {
value = OBJ.clone(obj[i]);
}
}
else {
value = obj[i];
}
if (obj.hasOwnProperty(i)) {
clone[i] = value;
}
else { |
We know if object.defineProperty is available. |
if (compatibility && compatibility.defineProperty) {
Object.defineProperty(clone, i, {
value: value,
writable: true,
configurable: true
});
}
else { |
or we try... |
try {
Object.defineProperty(clone, i, {
value: value,
writable: true,
configurable: true
});
}
catch(e) {
clone[i] = value;
}
}
}
}
return clone;
}; |
¶ OBJ.joinPerforms a left join on the keys of two objects Creates a copy of obj1, and in case keys overlap between obj1 and obj2, the values from obj2 are taken. Returns a new object, the original ones are not modified. E.g.
Params
obj1
object
The object where the merge will take place
obj2
object
The merging object
Returns
object
clone The joined object
See
OBJ.merge
|
OBJ.join = function(obj1, obj2) {
var clone, i;
clone = OBJ.clone(obj1);
if (!obj2) return clone;
for (i in clone) {
if (clone.hasOwnProperty(i)) {
if ('undefined' !== typeof obj2[i]) {
if ('object' === typeof obj2[i]) {
clone[i] = OBJ.join(clone[i], obj2[i]);
} else {
clone[i] = obj2[i];
}
}
}
}
return clone;
}; |
¶ OBJ.mergeMerges two objects in one In case keys overlap the values from obj2 are taken. Only own properties are copied. Returns a new object, the original ones are not modified. E.g.
Params
obj1
object
The object where the merge will take place
obj2
object
The merging object
Returns
object
clone The merged object
See
OBJ.join
See
OBJ.mergeOnKey
|
OBJ.merge = function(obj1, obj2) {
var clone, i; |
Checking before starting the algorithm |
if (!obj1 && !obj2) return false;
if (!obj1) return OBJ.clone(obj2);
if (!obj2) return OBJ.clone(obj1);
clone = OBJ.clone(obj1);
for (i in obj2) {
if (obj2.hasOwnProperty(i)) { |
it is an object and it is not NULL |
if ( obj2[i] && 'object' === typeof obj2[i] ) { |
If we are merging an object into a non-object, we need to cast the type of obj1 |
if ('object' !== typeof clone[i]) {
if (Object.prototype.toString.call(obj2[i]) === '[object Array]') {
clone[i] = [];
}
else {
clone[i] = {};
}
}
clone[i] = OBJ.merge(clone[i], obj2[i]);
} else {
clone[i] = obj2[i];
}
}
}
return clone;
}; |
¶ OBJ.mixinAdds all the properties of obj2 into obj1 Original object is modified. Params
obj1
object
The object to which the new properties will be added
obj2
object
The mixin-in object
|
OBJ.mixin = function(obj1, obj2) {
var i;
if (!obj1 && !obj2) return;
if (!obj1) return obj2;
if (!obj2) return obj1;
for (i in obj2) {
obj1[i] = obj2[i];
}
}; |
¶ OBJ.mixoutCopies only non-overlapping properties from obj2 to obj1 Original object is modified Params
obj1
object
The object to which the new properties will be added
obj2
object
The mixin-in object
|
OBJ.mixout = function(obj1, obj2) {
var i;
if (!obj1 && !obj2) return;
if (!obj1) return obj2;
if (!obj2) return obj1;
for (i in obj2) {
if (!obj1[i]) obj1[i] = obj2[i];
}
}; |
¶ OBJ.mixcommonCopies only overlapping properties from obj2 to obj1 Original object is modified Params
obj1
object
The object to which the new properties will be added
obj2
object
The mixin-in object
|
OBJ.mixcommon = function(obj1, obj2) {
var i;
if (!obj1 && !obj2) return;
if (!obj1) return obj2;
if (!obj2) return obj1;
for (i in obj2) {
if (obj1[i]) obj1[i] = obj2[i];
}
}; |
¶ OBJ.mergeOnKeyMerges the properties of obj2 into a new property named 'key' in obj1. Returns a new object, the original ones are not modified. This method is useful when we want to merge into a larger configuration (e.g. with properties min, max, value) object, another one that contains just a subset of properties (e.g. value). Params
obj1
object
The object where the merge will take place
obj2
object
The merging object
key
string
The name of property under which the second object
will be merged
Returns
object
clone The merged object
See
OBJ.merge
|
OBJ.mergeOnKey = function(obj1, obj2, key) {
var clone, i;
clone = OBJ.clone(obj1);
if (!obj2 || !key) return clone;
for (i in obj2) {
if (obj2.hasOwnProperty(i)) {
if (!clone[i] || 'object' !== typeof clone[i]) {
clone[i] = {};
}
clone[i][key] = obj2[i];
}
}
return clone;
}; |
¶ OBJ.subobjCreates a copy of an object containing only the properties passed as second parameter The parameter select can be an array of strings, or the name of a property. Use '.' (dot) to point to a nested property, however if a property with a '.' in the name is found, it will be used first. Params
o
object
The object to dissect
select
string
array
The selection of properties to extract
Returns
object
out The subobject with the properties from the parent
See
OBJ.getNestedValue
|
OBJ.subobj = function(o, select) {
var out, i, key
if (!o) return false;
out = {};
if (!select) return out;
if (!(select instanceof Array)) select = [select];
for (i=0; i < select.length; i++) {
key = select[i];
if (o.hasOwnProperty(key)) {
out[key] = o[key];
}
else if (OBJ.hasOwnNestedProperty(key, o)) {
OBJ.setNestedValue(key, OBJ.getNestedValue(key, o), out);
}
}
return out;
}; |
¶ OBJ.skimCreates a copy of an object with some of the properties removed The parameter Use '.' (dot) to point to a nested property, however if a property with a '.' in the name is found, it will be deleted first. Params
o
object
The object to dissect
remove
string
array
The selection of properties to remove
Returns
object
out The subobject with the properties from the parent
See
OBJ.getNestedValue
|
OBJ.skim = function(o, remove) {
var out, i;
if (!o) return false;
out = OBJ.clone(o);
if (!remove) return out;
if (!(remove instanceof Array)) remove = [remove];
for (i = 0; i < remove.length; i++) {
if (out.hasOwnProperty(i)) {
delete out[i];
}
else {
OBJ.deleteNestedKey(remove[i], out);
}
}
return out;
}; |
¶ OBJ.setNestedValueSets the value of a nested property of an object and returns it. If the object is not passed a new one is created. If the nested property is not existing, a new one is created. Use '.' (dot) to point to a nested property. The original object is modified. Params
str
string
The path to the value
value
mixed
The value to set
Returns
object
boolean
obj The modified object, or FALSE if error
occurrs
See
OBJ.getNestedValue
See
OBJ.deleteNestedKey
|
OBJ.setNestedValue = function(str, value, obj) {
var keys, k;
if (!str) {
JSUS.log('Cannot set value of undefined property', 'ERR');
return false;
}
obj = ('object' === typeof obj) ? obj : {};
keys = str.split('.');
if (keys.length === 1) {
obj[str] = value;
return obj;
}
k = keys.shift();
obj[k] = OBJ.setNestedValue(keys.join('.'), value, obj[k]);
return obj;
}; |
¶ OBJ.getNestedValueReturns the value of a property of an object, as defined by a path string. Use '.' (dot) to point to a nested property. Returns undefined if the nested property does not exist. E.g.
Params
str
string
The path to the value
obj
object
The object from which extract the value
Returns
mixed
The extracted value
See
OBJ.setNestedValue
See
OBJ.deleteNestedKey
|
OBJ.getNestedValue = function(str, obj) {
var keys, k;
if (!obj) return;
keys = str.split('.');
if (keys.length === 1) {
return obj[str];
}
k = keys.shift();
return OBJ.getNestedValue(keys.join('.'), obj[k]);
}; |
¶ OBJ.deleteNestedKeyDeletes a property from an object, as defined by a path string Use '.' (dot) to point to a nested property. The original object is modified. E.g.
Params
str
string
The path string
obj
object
The object from which deleting a property
TRUE,
boolean
if the property was existing, and then deleted
See
OBJ.setNestedValue
See
OBJ.getNestedValue
|
OBJ.deleteNestedKey = function(str, obj) {
var keys, k;
if (!obj) return;
keys = str.split('.');
if (keys.length === 1) {
delete obj[str];
return true;
}
k = keys.shift();
if ('undefined' === typeof obj[k]) {
return false;
}
return OBJ.deleteNestedKey(keys.join('.'), obj[k]);
}; |
¶ OBJ.hasOwnNestedPropertyReturns TRUE if a (nested) property exists Use '.' to specify a nested property. E.g.
Params
str
string
The path of the (nested) property
obj
object
The object to test
Returns
boolean
TRUE, if the (nested) property exists
|
OBJ.hasOwnNestedProperty = function(str, obj) {
var keys, k;
if (!obj) return false;
keys = str.split('.');
if (keys.length === 1) {
return obj.hasOwnProperty(str);
}
k = keys.shift();
return OBJ.hasOwnNestedProperty(keys.join('.'), obj[k]);
}; |
¶ OBJ.splitSplits an object along a specified dimension, and returns all the copies in an array. It creates as many new objects as the number of properties contained in the specified dimension. The object are identical, but for the given dimension, which was split. E.g.
Params
o
object
The object to split
key
sting
The name of the property to split
Returns
object
A copy of the object with split values
|
OBJ.split = function(o, key) {
var out, model, splitValue;
if (!o) return;
if (!key || 'object' !== typeof o[key]) {
return JSUS.clone(o);
}
out = [];
model = JSUS.clone(o);
model[key] = {};
splitValue = function(value) {
var i, copy;
for (i in value) {
copy = JSUS.clone(model);
if (value.hasOwnProperty(i)) {
if ('object' === typeof value[i]) {
out = out.concat(splitValue(value[i]));
}
else {
copy[key][i] = value[i];
out.push(copy);
}
}
}
return out;
};
return splitValue(o[key]);
}; |
¶ OBJ.meltCreates a new object with the specified combination of properties - values The values are assigned cyclically to the properties, so that they do not need to have the same length. E.g.
Params
keys
array
The names of the keys to add to the object
values
array
The values to associate to the keys
Returns
object
A new object with keys and values melted together
|
OBJ.melt = function(keys, values) {
var o = {}, valen = values.length;
for (var i = 0; i < keys.length; i++) {
o[keys[i]] = values[i % valen];
}
return o;
}; |
¶ OBJ.uniqueKeyCreates a random unique key name for a collection User can specify a tentative unique key name, and if already existing an incremental index will be added as suffix to it. Notice: the method does not actually create the key in the object, but it just returns the name. Params
obj
object
The collection for which a unique key will be created
prefixName
string
Optional. A tentative key name. Defaults,
a 15-digit random number
stop
number
Optional. The number of tries before giving up
searching for a unique key name. Defaults, 1000000.
Returns
string
undefined
The unique key name, or undefined if it was not found
|
OBJ.uniqueKey = function(obj, prefixName, stop) {
var name;
var duplicateCounter = 1;
if (!obj) {
JSUS.log('Cannot find unique name in undefined object', 'ERR');
return;
}
prefixName = '' + (prefixName ||
Math.floor(Math.random()*1000000000000000));
stop = stop || 1000000;
name = prefixName;
while (obj[name]) {
name = prefixName + duplicateCounter;
duplicateCounter++;
if (duplicateCounter > stop) {
return;
}
}
return name;
} |
¶ OBJ.augmentPushes the values of the properties of an object into another one User can specifies the subset of keys from both objects that will subject to augmentation. The values of the other keys will not be changed Notice: the method modifies the first input paramteer E.g.
Params
obj1
object
The object whose properties will be augmented
obj2
object
The augmenting object
key
array
Optional. Array of key names common to both objects
taken as the set of properties to augment
|
OBJ.augment = function(obj1, obj2, keys) {
var i, k, keys = keys || OBJ.keys(obj1);
for (i = 0 ; i < keys.length; i++) {
k = keys[i];
if ('undefined' !== typeof obj1[k] &&
Object.prototype.toString.call(obj1[k]) !== '[object Array]') {
obj1[k] = [obj1[k]];
}
if ('undefined' !== obj2[k]) {
if (!obj1[k]) obj1[k] = [];
obj1[k].push(obj2[k]);
}
}
} |
¶ OBJ.pairwiseWalkExecutes a callback on all pairs of attributes with the same name The results of each callback are aggregated in a new object under the same property name. Does not traverse nested objects, and properties of the prototype are excluded. Returns a new object, the original ones are not modified. E.g.
Params
o1
object
The first object
o2
object
The second object
Returns
object
clone The object aggregating the results
|
OBJ.pairwiseWalk = function(o1, o2, cb) {
var i, out;
if (!o1 && !o2) return;
if (!o1) return o2;
if (!o2) return o1;
out = {};
for (i in o1) {
if (o1.hasOwnProperty(i)) {
out[i] = o2.hasOwnProperty(i) ? cb(o1[i], o2[i]) : cb(o1[i]);
}
}
for (i in o2) {
if (o2.hasOwnProperty(i)) {
if ('undefined' === typeof out[i]) {
out[i] = cb(undefined, o2[i]);
}
}
}
return out;
};
JSUS.extend(OBJ);
})('undefined' !== typeof JSUS ? JSUS : module.parent.exports.JSUS);
|