/**
Advanced Object prototype functions
@author Tilak Patidar <tilakpatidar@gmail.com>
@constructor
*/
var _ = require("underscore");
var ObjectX = function() {
};
if (!Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
if (!Object.freeze || typeof Object.freeze !== 'function') {
throw Error('ES5 support required');
}
// from ES5
var O = Object,
OP = O.prototype,
create = O.create,
defineProperty = O.defineProperty,
defineProperties = O.defineProperties,
getOwnPropertyNames = O.getOwnPropertyNames,
getOwnPropertyDescriptor = O.getOwnPropertyDescriptor,
getPrototypeOf = O.getPrototypeOf,
freeze = O.freeze,
isFrozen = O.isFrozen,
isSealed = O.isSealed,
seal = O.seal,
isExtensible = O.isExtensible,
preventExtensions = O.preventExtensions,
hasOwnProperty = OP.hasOwnProperty,
toString = OP.toString,
isArray = Array.isArray,
slice = Array.prototype.slice;
// Utility functions; some exported
function defaults(dst, src) {
getOwnPropertyNames(src).forEach(function(k) {
if (!hasOwnProperty.call(dst, k)) defineProperty(
dst, k, getOwnPropertyDescriptor(src, k)
);
});
return dst;
};
var isObject = function(o) {
return o === Object(o)
};
var isPrimitive = function(o) {
return o !== Object(o)
};
var isFunction = function(f) {
return typeof(f) === 'function'
};
var signatureOf = function(o) {
return toString.call(o)
};
var HASWEAKMAP = (function() { // paranoia check
try {
var wm = new WeakMap();
wm.set(wm, wm);
return wm.get(wm) === wm;
} catch (e) {
return false;
}
})();
// exported
function is(x, y) {
return x === y ?
x !== 0 ? true :
(1 / x === 1 / y) // +-0
:
(x !== x && y !== y); // NaN
};
function isnt(x, y) {
return !is(x, y)
};
var defaultCK = {
descriptors: true,
extensibility: true,
enumerator: getOwnPropertyNames
};
function equals(x, y, ck) {
var vx, vy;
if (HASWEAKMAP) {
vx = new WeakMap();
vy = new WeakMap();
}
ck = defaults(ck || {}, defaultCK);
return (function _equals(x, y) {
if (isPrimitive(x)) return is(x, y);
if (isFunction(x)) return is(x, y);
// check deeply
var sx = signatureOf(x),
sy = signatureOf(y);
var i, l, px, py, sx, sy, kx, ky, dx, dy, dk, flt;
if (sx !== sy) return false;
switch (sx) {
case '[object Array]':
case '[object Object]':
if (ck.extensibility) {
if (isExtensible(x) !== isExtensible(y)) return false;
if (isSealed(x) !== isSealed(y)) return false;
if (isFrozen(x) !== isFrozen(y)) return false;
}
if (vx) {
if (vx.has(x)) {
// console.log('circular ref found');
return vy.has(y);
}
vx.set(x, true);
vy.set(y, true);
}
px = ck.enumerator(x);
py = ck.enumerator(y);
if (ck.filter) {
flt = function(k) {
var d = getOwnPropertyDescriptor(this, k);
return ck.filter(d, k, this);
};
px = px.filter(flt, x);
py = py.filter(flt, y);
}
if (px.length != py.length) return false;
px.sort();
py.sort();
for (i = 0, l = px.length; i < l; ++i) {
kx = px[i];
ky = py[i];
if (kx !== ky) return false;
dx = getOwnPropertyDescriptor(x, ky);
dy = getOwnPropertyDescriptor(y, ky);
if ('value' in dx) {
if (!_equals(dx.value, dy.value)) return false;
} else {
if (dx.get && dx.get !== dy.get) return false;
if (dx.set && dx.set !== dy.set) return false;
}
if (ck.descriptors) {
if (dx.enumerable !== dy.enumerable) return false;
if (ck.extensibility) {
if (dx.writable !== dy.writable)
return false;
if (dx.configurable !== dy.configurable)
return false;
}
}
}
return true;
case '[object RegExp]':
case '[object Date]':
case '[object String]':
case '[object Number]':
case '[object Boolean]':
return '' + x === '' + y;
default:
throw TypeError(sx + ' not supported');
}
})(x, y);
}
// Install
var obj2specs = function(src) {
var specs = create(null);
getOwnPropertyNames(src).forEach(function(k) {
specs[k] = {
value: src[k],
configurable: true,
writable: true,
enumerable: false
};
});
return specs;
};
var defaultProperties = function(dst, descs) {
getOwnPropertyNames(descs).forEach(function(k) {
if (!hasOwnProperty.call(dst, k)) defineProperty(
dst, k, descs[k]
);
});
return dst;
};
/**
Returns deep copy of any Object. Similar to java's Object.clone
@static
@param {Object} src
*/
ObjectX.clone = function clone(src, deep, ck) {
var wm;
if (deep && HASWEAKMAP) {
wm = new WeakMap();
}
ck = defaults(ck || {}, defaultCK);
return (function _clone(src) {
// primitives and functions
if (isPrimitive(src)) return src;
if (isFunction(src)) return src;
var sig = signatureOf(src);
switch (sig) {
case '[object Array]':
case '[object Object]':
if (wm) {
if (wm.has(src)) {
// console.log('circular ref found');
return src;
}
wm.set(src, true);
}
var isarray = isArray(src);
var dst = isarray ? [] : create(getPrototypeOf(src));
ck.enumerator(src).forEach(function(k) {
// Firefox forbids defineProperty(obj, 'length' desc)
if (isarray && k === 'length') {
dst.length = src.length;
} else {
if (ck.descriptors) {
var desc = getOwnPropertyDescriptor(src, k);
if (ck.filter && !ck.filter(desc, k, src)) return;
if (deep && 'value' in desc)
desc.value = _clone(src[k]);
defineProperty(dst, k, desc);
} else {
dst[k] = _clone(src[k]);
}
}
});
if (ck.extensibility) {
if (!isExtensible(src)) preventExtensions(dst);
if (isSealed(src)) seal(dst);
if (isFrozen(src)) freeze(dst);
}
return dst;
case '[object RegExp]':
case '[object Date]':
case '[object String]':
case '[object Number]':
case '[object Boolean]':
return deep ? new src.constructor(src.valueOf()) : src;
default:
throw TypeError(sig + ' is not supported');
}
})(src);
};
/**
Check if two objects are equivalent.
@param {Object} a
@param {Object} b
@static
@returns {boolean}
*/
ObjectX.isEquivalent = function isEquivalent(a, b) {
var anil = false;
var bnil = false;
if (a === undefined || a === null) {
anil = true;
}
if (b === undefined || b === null) {
bnil = true;
}
if (anil && bnil) {
return true;
} else if (anil === true && bnil === false) {
return false;
} else if (bnil === true && anil === false) {
return false;
}
if (a.constructor.name !== b.constructor.name) {
return false;
}
if (typeof a === "number") {
if (a === b) {
return true;
} else {
return false;
}
} else if (typeof a === "string") {
if (a === b) {
return true;
} else {
return false;
}
} else if (a.constructor.name === "Array" && b.constructor.name === "Array") {
a.sort();
b.sort();
}
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (typeof a[propName] === "object") {
if (!isEquivalent(a[propName], b[propName])) {
return false;
}
} else {
if (a[propName] !== b[propName]) {
return false;
}
}
}
// If we made it this far, objects
// are considered equivalent
return true;
};
/**
Returns values of Object.
@static
@returns {Array}
*/
ObjectX.values = function values(dic) {
var li = []
_.each(dic, function(e, key) {
li.push(key);
});
return li;
};
module.exports = ObjectX;