File: /var/dev/farhangmoaser/web/node_modules/should-equal/index.js
var getType = require('should-type');
var format = require('./format');
var hasOwnProperty = Object.prototype.hasOwnProperty;
function makeResult(r, path, reason, a, b) {
var o = {result: r};
if(!r) {
o.path = path;
o.reason = reason;
o.a = a;
o.b = b;
}
return o;
}
var EQUALS = makeResult(true);
function typeToString(t) {
return t.type + (t.cls ? '(' + t.cls + (t.sub ? ' ' + t.sub : '') + ')' : '');
}
var REASON = {
PLUS_0_AND_MINUS_0: '+0 is not equal to -0',
DIFFERENT_TYPES: 'A has type %s and B has type %s',
NAN_NUMBER: 'NaN is not equal to any number',
EQUALITY: 'A is not equal to B',
EQUALITY_PROTOTYPE: 'A and B have different prototypes',
WRAPPED_VALUE: 'A wrapped value is not equal to B wrapped value',
FUNCTION_SOURCES: 'function A is not equal to B by source code value (via .toString call)',
MISSING_KEY: '%s has no key %s',
CIRCULAR_VALUES: 'A has circular reference that was visited not in the same time as B',
SET_MAP_MISSING_KEY: 'Set/Map missing key',
MAP_VALUE_EQUALITY: 'Values of the same key in A and B is not equal'
};
function eqInternal(a, b, opts, stackA, stackB, path) {
var r = EQUALS;
function result(comparison, reason) {
return makeResult(comparison, path, reason, a, b);
}
function checkPropertyEquality(property) {
return eqInternal(a[property], b[property], opts, stackA, stackB, path.concat([property]));
}
// equal a and b exit early
if(a === b) {
// check for +0 !== -0;
return result(a !== 0 || (1 / a == 1 / b), REASON.PLUS_0_AND_MINUS_0);
}
var l, p;
var typeA = getType(a),
typeB = getType(b);
// if objects has different types they are not equals
var typeDifferents = typeA.type !== typeB.type || typeA.cls !== typeB.cls;
if(typeDifferents || ((opts.checkSubType && typeA.sub !== typeB.sub) || !opts.checkSubType)) {
return result(false, format(REASON.DIFFERENT_TYPES, typeToString(typeA), typeToString(typeB)));
}
//early checks for types
switch(typeA.type) {
case 'number':
// NaN !== NaN
return (a !== a) ? result(b !== b, REASON.NAN_NUMBER)
// but treat `+0` vs. `-0` as not equal
: (a === 0 ? result(1 / a === 1 / b, REASON.PLUS_0_AND_MINUS_0) : result(a === b, REASON.EQUALITY));
case 'boolean':
case 'string':
return result(a === b, REASON.EQUALITY);
case 'function':
var fA = a.toString(), fB = b.toString();
r = eqInternal(fA, fB, opts, stackA, stackB, path);
if(!r.result) {
r.reason = REASON.FUNCTION_SOURCES;
return r;
}
break;//check user properties
case 'object':
// additional checks for object instances
switch(typeA.cls) {
// check regexp flags
// TODO add es6 flags
case 'regexp':
p = ['source', 'global', 'multiline', 'lastIndex', 'ignoreCase'];
while(p.length) {
r = checkPropertyEquality(p.shift());
if(!r.result) return r;
}
break;//check user properties
//check by timestamp only
case 'date':
if(+a !== +b) {
return result(false, REASON.EQUALITY);
}
break;//check user properties
//primitive type wrappers
case 'number':
case 'boolean':
case 'string':
r = eqInternal(a.valueOf(), b.valueOf(), opts, stackA, stackB, path);
if(!r.result) {
r.reason = REASON.WRAPPED_VALUE;
return r;
}
break;//check user properties
//node buffer
case 'buffer':
//if length different it is obviously different
r = checkPropertyEquality('length');
if(!r.result) return r;
l = a.length;
while(l--) {
r = checkPropertyEquality(l);
if(!r.result) return r;
}
//we do not check for user properties because
//node Buffer have some strange hidden properties
return EQUALS;
case 'error':
//check defined properties
p = ['name', 'message'];
while(p.length) {
r = checkPropertyEquality(p.shift());
if(!r.result) return r;
}
break;//check user properties
case 'array':
case 'arguments':
case 'typed-array':
r = checkPropertyEquality('length');
if(!r.result) return r;
break;//check user properties
case 'array-buffer':
r = checkPropertyEquality('byteLength');
if(!r.result) return r;
break;//check user properties
case 'map':
case 'set':
r = checkPropertyEquality('size');
if(!r.result) return r;
stackA.push(a);
stackB.push(b);
var itA = a.entries();
var nextA = itA.next();
while(!nextA.done) {
var key = nextA.value[0];
//first check for primitive key if we can do light check
//using .has and .get
if(getType(key).type != 'object') {
if(b.has(key)) {
if(typeA.cls == 'map') {
//for map we also check its value to be equal
var value = b.get(key);
r = eqInternal(nextA.value[1], value, opts, stackA, stackB, path);
if(!r.result) {
r.a = nextA.value;
r.b = value;
r.reason = REASON.MAP_VALUE_EQUALITY;
break;
}
}
} else {
r = result(false, REASON.SET_MAP_MISSING_KEY);
r.a = key;
r.b = key;
break;
}
} else {
//heavy check
//we search by iterator for key equality using equal
var itB = b.entries();
var nextB = itB.next();
while(!nextB.done) {
//first check for keys
r = eqInternal(nextA.value[0], nextB.value[0], opts, stackA, stackB, path);
if(!r.result) {
r.reason = REASON.SET_MAP_MISSING_KEY;
r.a = key;
r.b = key;
} else {
if(typeA.cls == 'map') {
r = eqInternal(nextA.value[1], nextB.value[1], opts, stackA, stackB, path);
if(!r.result) {
r.a = nextA.value;
r.b = nextB.value;
r.reason = REASON.MAP_VALUE_EQUALITY;
}
}
break;
}
nextB = itB.next();
}
}
if(!r.result) {
break;
}
nextA = itA.next();
}
stackA.pop();
stackB.pop();
if(!r.result) {
r.reason = REASON.SET_MAP_MISSING_ENTRY;
return r;
}
break; //check user properties
}
}
// compare deep objects and arrays
// stacks contain references only
//
l = stackA.length;
while(l--) {
if(stackA[l] == a) {
return result(stackB[l] == b, REASON.CIRCULAR_VALUES);
}
}
// add `a` and `b` to the stack of traversed objects
stackA.push(a);
stackB.push(b);
var key;
for(key in b) {
if(hasOwnProperty.call(b, key)) {
r = result(hasOwnProperty.call(a, key), format(REASON.MISSING_KEY, 'A', key));
if(!r.result) {
break;
}
if(r.result) {
r = checkPropertyEquality(key);
if(!r.result) {
break;
}
}
}
}
if(r.result) {
// ensure both objects have the same number of properties
for(key in a) {
if(hasOwnProperty.call(a, key)) {
r = result(hasOwnProperty.call(b, key), format(REASON.MISSING_KEY, 'B', key));
if(!r.result) {
return r;
}
}
}
}
stackA.pop();
stackB.pop();
if(!r.result) return r;
var prototypesEquals = false, canComparePrototypes = false;
if(opts.checkProtoEql) {
if(Object.getPrototypeOf) {//TODO should i check prototypes for === or use eq?
prototypesEquals = Object.getPrototypeOf(a) === Object.getPrototypeOf(b);
canComparePrototypes = true;
} else if(a.__proto__ && b.__proto__) {
prototypesEquals = a.__proto__ === b.__proto__;
canComparePrototypes = true;
}
if(canComparePrototypes && !prototypesEquals) {
r = result(prototypesEquals, REASON.EQUALITY_PROTOTYPE);
r.showReason = true;
if(!r.result) {
return r;
}
}
}
return EQUALS;
}
var defaultOptions = {
checkProtoEql: true,
checkSubType: true
};
function eq(a, b, opts) {
opts = opts || {};
if(typeof opts.checkProtoEql !== 'boolean')
opts.checkProtoEql = defaultOptions.checkProtoEql;
if(typeof opts.checkSubType !== 'boolean')
opts.checkSubType = defaultOptions.checkSubType;
var r = eqInternal(a, b, opts, [], [], []);
return r;
}
module.exports = eq;
eq.r = REASON;