| 'use strict' |
| |
| module.exports = shallow |
| shallow.strict = strict |
| |
| // TODO a future version of this that drops node 0.x support will |
| // check for Symbol.iterator, and handle anything that can be passed |
| // to Array.from() |
| |
| var hasSet = typeof Set === 'function' |
| |
| function isSet (object) { |
| return hasSet && (object instanceof Set) |
| } |
| |
| function isMap (object) { |
| return hasSet && (object instanceof Map) |
| } |
| |
| function setSame (a, b) { |
| var ret = a.size === b.size |
| if (a.size && ret) { |
| a.forEach(function (entry) { |
| if (ret) |
| ret = b.has(entry) |
| }) |
| } |
| return ret |
| } |
| |
| function mapSame (a, b, ca, cb, fn) { |
| var ret = a.size === b.size |
| if (a.size && ret) { |
| a.forEach(function (value, key) { |
| if (ret) |
| ret = b.has(key) |
| if (ret) |
| ret = fn(value, b.get(key), ca, cb) |
| }) |
| } |
| return ret |
| } |
| |
| function isArguments (object) { |
| return Object.prototype.toString.call(object) === '[object Arguments]' |
| } |
| |
| function arrayFrom (obj) { |
| return Array.isArray(obj) ? obj |
| : Array.from ? Array.from(obj) |
| : Array.prototype.slice.call(obj) |
| } |
| |
| function strict (a, b) { |
| return deeper(a, b, [], []) |
| } |
| |
| function deeper (a, b, ca, cb) { |
| return a === b ? true |
| : a !== a ? b !== b |
| : typeof a !== 'object' || typeof b !== 'object' ? false |
| : a === null || b === null ? false |
| : Buffer.isBuffer(a) && Buffer.isBuffer(b) ? bufferSame(a, b) |
| : a instanceof Date && b instanceof Date ? a.getTime() === b.getTime() |
| : a instanceof RegExp && b instanceof RegExp ? regexpSame(a, b) |
| : isArguments(a) ? |
| isArguments(b) && deeper(arrayFrom(a), arrayFrom(b), ca, cb) |
| : isArguments(b) ? false |
| : a.constructor !== b.constructor ? false |
| : isSet(a) && isSet(b) ? setSame(a, b) |
| : isMap(a) && isMap(b) ? mapSame(a, b, ca, cb, deeper) |
| : deeperObj(a, b, Object.keys(a), Object.keys(b), ca, cb) |
| } |
| |
| function deeperObj (a, b, ka, kb, ca, cb) { |
| // don't bother with stack acrobatics if there's nothing there |
| return ka.length === 0 && kb.length === 0 ? true |
| : ka.length !== kb.length ? false |
| : deeperObj_(a, b, ka, kb, ca, cb) |
| } |
| |
| function deeperObj_ (a, b, ka, kb, ca, cb) { |
| var ret = true |
| |
| var cal = ca.length |
| |
| var go = true |
| while (cal-- && go) { |
| if (ca[cal] === a) { |
| ret = cb[cal] === b |
| go = false |
| } |
| } |
| |
| if (go) { |
| ca.push(a) |
| cb.push(b) |
| |
| ka.sort() |
| kb.sort() |
| |
| for (var j = ka.length - 1; j >= 0 && ret; j--) { |
| if (ka[j] !== kb[j]) |
| ret = false |
| } |
| |
| if (ret) { |
| var key |
| for (var k = ka.length - 1; k >= 0 && ret; k--) { |
| key = ka[k] |
| if (!deeper(a[key], b[key], ca, cb)) |
| ret = false |
| } |
| } |
| |
| if (ret) { |
| ca.pop() |
| cb.pop() |
| } |
| } |
| |
| return ret |
| } |
| |
| function shallow (a, b) { |
| return shallower(a, b, [], []) |
| } |
| |
| function regexpSame (a, b) { |
| return a.source === b.source && |
| a.global === b.global && |
| a.multiline === b.multiline && |
| a.lastIndex === b.lastIndex && |
| a.ignoreCase === b.ignoreCase |
| } |
| |
| function bufferSame (a, b) { |
| var ret |
| if (a.equals) { |
| ret = a.equals(b) |
| } else if (a.length !== b.length) { |
| ret = false |
| } else { |
| ret = true |
| for (var j = 0; j < a.length && ret; j++) { |
| if (a[j] != b[j]) |
| ret = false |
| } |
| } |
| return ret |
| } |
| |
| function shallower (a, b, ca, cb) { |
| return typeof a !== 'object' && typeof b !== 'object' && a == b ? true |
| : a === null || b === null ? a == b |
| : a !== a ? b !== b |
| : typeof a !== 'object' || typeof b !== 'object' ? false |
| : Buffer.isBuffer(a) && Buffer.isBuffer(b) ? bufferSame(a, b) |
| : a instanceof Date && b instanceof Date ? a.getTime() === b.getTime() |
| : a instanceof RegExp && b instanceof RegExp ? regexpSame(a, b) |
| : isArguments(a) || isArguments(b) ? |
| shallower(arrayFrom(a), arrayFrom(b), ca, cb) |
| : isSet(a) && isSet(b) ? setSame(a, b) |
| : isMap(a) && isMap(b) ? mapSame(a, b, ca, cb, shallower) |
| : shallowerObj(a, b, Object.keys(a), Object.keys(b), ca, cb) |
| } |
| |
| function shallowerObj (a, b, ka, kb, ca, cb) { |
| // don't bother with stack acrobatics if there's nothing there |
| return ka.length === 0 && kb.length === 0 ? true |
| : ka.length !== kb.length ? false |
| : shallowerObj_(a, b, ka, kb, ca, cb) |
| } |
| |
| function shallowerObj_ (a, b, ka, kb, ca, cb) { |
| var ret |
| |
| var cal = ca.length |
| var go = true |
| while (cal-- && go) { |
| if (ca[cal] === a) { |
| ret = cb[cal] === b |
| go = false |
| } |
| } |
| |
| if (go) { |
| ca.push(a) |
| cb.push(b) |
| |
| ka.sort() |
| kb.sort() |
| |
| ret = true |
| for (var k = ka.length - 1; k >= 0 && ret; k--) { |
| if (ka[k] !== kb[k]) |
| ret = false |
| } |
| |
| if (ret) { |
| var key |
| for (var l = ka.length - 1; l >= 0 && ret; l--) { |
| key = ka[l] |
| if (!shallower(a[key], b[key], ca, cb)) |
| ret = false |
| } |
| } |
| |
| if (ret) { |
| ca.pop() |
| cb.pop() |
| } |
| } |
| |
| return ret |
| } |