Initial commit
This commit is contained in:
+257
@@ -0,0 +1,257 @@
|
||||
/*!
|
||||
* deep-eql
|
||||
* Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var type = require('type-detect');
|
||||
|
||||
/*!
|
||||
* Buffer.isBuffer browser shim
|
||||
*/
|
||||
|
||||
var Buffer;
|
||||
try { Buffer = require('buffer').Buffer; }
|
||||
catch(ex) {
|
||||
Buffer = {};
|
||||
Buffer.isBuffer = function() { return false; }
|
||||
}
|
||||
|
||||
/*!
|
||||
* Primary Export
|
||||
*/
|
||||
|
||||
module.exports = deepEqual;
|
||||
|
||||
/**
|
||||
* Assert super-strict (egal) equality between
|
||||
* two objects of any type.
|
||||
*
|
||||
* @param {Mixed} a
|
||||
* @param {Mixed} b
|
||||
* @param {Array} memoised (optional)
|
||||
* @return {Boolean} equal match
|
||||
*/
|
||||
|
||||
function deepEqual(a, b, m) {
|
||||
if (sameValue(a, b)) {
|
||||
return true;
|
||||
} else if ('date' === type(a)) {
|
||||
return dateEqual(a, b);
|
||||
} else if ('regexp' === type(a)) {
|
||||
return regexpEqual(a, b);
|
||||
} else if (Buffer.isBuffer(a)) {
|
||||
return bufferEqual(a, b);
|
||||
} else if ('arguments' === type(a)) {
|
||||
return argumentsEqual(a, b, m);
|
||||
} else if (!typeEqual(a, b)) {
|
||||
return false;
|
||||
} else if (('object' !== type(a) && 'object' !== type(b))
|
||||
&& ('array' !== type(a) && 'array' !== type(b))) {
|
||||
return sameValue(a, b);
|
||||
} else {
|
||||
return objectEqual(a, b, m);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Strict (egal) equality test. Ensures that NaN always
|
||||
* equals NaN and `-0` does not equal `+0`.
|
||||
*
|
||||
* @param {Mixed} a
|
||||
* @param {Mixed} b
|
||||
* @return {Boolean} equal match
|
||||
*/
|
||||
|
||||
function sameValue(a, b) {
|
||||
if (a === b) return a !== 0 || 1 / a === 1 / b;
|
||||
return a !== a && b !== b;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compare the types of two given objects and
|
||||
* return if they are equal. Note that an Array
|
||||
* has a type of `array` (not `object`) and arguments
|
||||
* have a type of `arguments` (not `array`/`object`).
|
||||
*
|
||||
* @param {Mixed} a
|
||||
* @param {Mixed} b
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function typeEqual(a, b) {
|
||||
return type(a) === type(b);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compare two Date objects by asserting that
|
||||
* the time values are equal using `saveValue`.
|
||||
*
|
||||
* @param {Date} a
|
||||
* @param {Date} b
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function dateEqual(a, b) {
|
||||
if ('date' !== type(b)) return false;
|
||||
return sameValue(a.getTime(), b.getTime());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compare two regular expressions by converting them
|
||||
* to string and checking for `sameValue`.
|
||||
*
|
||||
* @param {RegExp} a
|
||||
* @param {RegExp} b
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function regexpEqual(a, b) {
|
||||
if ('regexp' !== type(b)) return false;
|
||||
return sameValue(a.toString(), b.toString());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Assert deep equality of two `arguments` objects.
|
||||
* Unfortunately, these must be sliced to arrays
|
||||
* prior to test to ensure no bad behavior.
|
||||
*
|
||||
* @param {Arguments} a
|
||||
* @param {Arguments} b
|
||||
* @param {Array} memoize (optional)
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function argumentsEqual(a, b, m) {
|
||||
if ('arguments' !== type(b)) return false;
|
||||
a = [].slice.call(a);
|
||||
b = [].slice.call(b);
|
||||
return deepEqual(a, b, m);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get enumerable properties of a given object.
|
||||
*
|
||||
* @param {Object} a
|
||||
* @return {Array} property names
|
||||
*/
|
||||
|
||||
function enumerable(a) {
|
||||
var res = [];
|
||||
for (var key in a) res.push(key);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Simple equality for flat iterable objects
|
||||
* such as Arrays or Node.js buffers.
|
||||
*
|
||||
* @param {Iterable} a
|
||||
* @param {Iterable} b
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function iterableEqual(a, b) {
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
var i = 0;
|
||||
var match = true;
|
||||
|
||||
for (; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Extension to `iterableEqual` specifically
|
||||
* for Node.js Buffers.
|
||||
*
|
||||
* @param {Buffer} a
|
||||
* @param {Mixed} b
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function bufferEqual(a, b) {
|
||||
if (!Buffer.isBuffer(b)) return false;
|
||||
return iterableEqual(a, b);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Block for `objectEqual` ensuring non-existing
|
||||
* values don't get in.
|
||||
*
|
||||
* @param {Mixed} object
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function isValue(a) {
|
||||
return a !== null && a !== undefined;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Recursively check the equality of two objects.
|
||||
* Once basic sameness has been established it will
|
||||
* defer to `deepEqual` for each enumerable key
|
||||
* in the object.
|
||||
*
|
||||
* @param {Mixed} a
|
||||
* @param {Mixed} b
|
||||
* @return {Boolean} result
|
||||
*/
|
||||
|
||||
function objectEqual(a, b, m) {
|
||||
if (!isValue(a) || !isValue(b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.prototype !== b.prototype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var i;
|
||||
if (m) {
|
||||
for (i = 0; i < m.length; i++) {
|
||||
if ((m[i][0] === a && m[i][1] === b)
|
||||
|| (m[i][0] === b && m[i][1] === a)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m = [];
|
||||
}
|
||||
|
||||
try {
|
||||
var ka = enumerable(a);
|
||||
var kb = enumerable(b);
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ka.sort();
|
||||
kb.sort();
|
||||
|
||||
if (!iterableEqual(ka, kb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m.push([ a, b ]);
|
||||
|
||||
var key;
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
key = ka[i];
|
||||
if (!deepEqual(a[key], b[key], m)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user