{"size": 5932, "ext": "js", "max_stars_repo_path": "node_modules/@sinonjs/samsam/lib/deep-equal.js", "max_stars_repo_name": "samuolis/tbexpressbackend", "max_stars_repo_stars_event_min_datetime": "2020-03-25T21:08:58.000Z", "max_stars_repo_stars_event_max_datetime": "2020-03-25T21:08:58.000Z", "max_issues_repo_path": "node_modules/@sinonjs/samsam/lib/deep-equal.js", "max_issues_repo_name": "samuolis/tbexpressbackend", "max_issues_repo_issues_event_min_datetime": "2020-04-30T00:46:34.000Z", "max_issues_repo_issues_event_max_datetime": "2022-02-12T04:51:33.000Z", "max_forks_repo_path": "node_modules/@sinonjs/samsam/lib/deep-equal.js", "max_forks_repo_name": "samuolis/tbexpressbackend", "max_forks_repo_forks_event_min_datetime": "2018-11-08T14:45:37.000Z", "max_forks_repo_forks_event_max_datetime": "2019-01-29T08:48:23.000Z"}
{"max_stars_count": 1, "max_issues_count": 6, "max_forks_count": 3, "avg_line_length": 32.2391304348, "max_line_length": 76, "alphanum_fraction": 0.5230950775}
"use strict";

var getClass = require("./get-class");
var identical = require("./identical");
var isArguments = require("./is-arguments");
var isDate = require("./is-date");
var isElement = require("./is-element");
var isNaN = require("./is-nan");
var isObject = require("./is-object");
var isSet = require("./is-set");
var isSubset = require("./is-subset");
var getClassName = require("./get-class-name");

var every = Array.prototype.every;
var getTime = Date.prototype.getTime;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var indexOf = Array.prototype.indexOf;
var keys = Object.keys;

/**
 * @name samsam.deepEqual
 * @param Object first
 * @param Object second
 *
 * Deep equal comparison. Two values are "deep equal" if:
 *
 *   - They are equal, according to samsam.identical
 *   - They are both date objects representing the same time
 *   - They are both arrays containing elements that are all deepEqual
 *   - They are objects with the same set of properties, and each property
 *     in ``first`` is deepEqual to the corresponding property in ``second``
 *
 * Supports cyclic objects.
 */
function deepEqualCyclic(first, second) {
    // used for cyclic comparison
    // contain already visited objects
    var objects1 = [];
    var objects2 = [];
    // contain pathes (position in the object structure)
    // of the already visited objects
    // indexes same as in objects arrays
    var paths1 = [];
    var paths2 = [];
    // contains combinations of already compared objects
    // in the manner: { "$1['ref']$2['ref']": true }
    var compared = {};

    // does the recursion for the deep equal check
    return (function deepEqual(obj1, obj2, path1, path2) {
        var type1 = typeof obj1;
        var type2 = typeof obj2;

        // == null also matches undefined
        if (
            obj1 === obj2 ||
            isNaN(obj1) ||
            isNaN(obj2) ||
            obj1 == null ||
            obj2 == null ||
            type1 !== "object" ||
            type2 !== "object"
        ) {
            return identical(obj1, obj2);
        }

        // Elements are only equal if identical(expected, actual)
        if (isElement(obj1) || isElement(obj2)) {
            return false;
        }

        var isDate1 = isDate(obj1);
        var isDate2 = isDate(obj2);
        if (isDate1 || isDate2) {
            if (
                !isDate1 ||
                !isDate2 ||
                getTime.call(obj1) !== getTime.call(obj2)
            ) {
                return false;
            }
        }

        if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
            if (obj1.toString() !== obj2.toString()) {
                return false;
            }
        }

        if (obj1 instanceof Error && obj2 instanceof Error) {
            if (obj1.stack !== obj2.stack) {
                return false;
            }
        }

        var class1 = getClass(obj1);
        var class2 = getClass(obj2);
        var keys1 = keys(obj1);
        var keys2 = keys(obj2);
        var name1 = getClassName(obj1);
        var name2 = getClassName(obj2);

        if (isArguments(obj1) || isArguments(obj2)) {
            if (obj1.length !== obj2.length) {
                return false;
            }
        } else {
            if (
                type1 !== type2 ||
                class1 !== class2 ||
                keys1.length !== keys2.length ||
                (name1 && name2 && name1 !== name2)
            ) {
                return false;
            }
        }

        if (isSet(obj1) || isSet(obj2)) {
            if (!isSet(obj1) || !isSet(obj2) || obj1.size !== obj2.size) {
                return false;
            }

            return isSubset(obj1, obj2, deepEqual);
        }

        return every.call(keys1, function(key) {
            if (!hasOwnProperty.call(obj2, key)) {
                return false;
            }

            var value1 = obj1[key];
            var value2 = obj2[key];
            var isObject1 = isObject(value1);
            var isObject2 = isObject(value2);
            // determines, if the objects were already visited
            // (it's faster to check for isObject first, than to
            // get -1 from getIndex for non objects)
            var index1 = isObject1 ? indexOf.call(objects1, value1) : -1;
            var index2 = isObject2 ? indexOf.call(objects2, value2) : -1;
            // determines the new paths of the objects
            // - for non cyclic objects the current path will be extended
            //   by current property name
            // - for cyclic objects the stored path is taken
            var newPath1 =
                index1 !== -1
                    ? paths1[index1]
                    : path1 + "[" + JSON.stringify(key) + "]";
            var newPath2 =
                index2 !== -1
                    ? paths2[index2]
                    : path2 + "[" + JSON.stringify(key) + "]";
            var combinedPath = newPath1 + newPath2;

            // stop recursion if current objects are already compared
            if (compared[combinedPath]) {
                return true;
            }

            // remember the current objects and their paths
            if (index1 === -1 && isObject1) {
                objects1.push(value1);
                paths1.push(newPath1);
            }
            if (index2 === -1 && isObject2) {
                objects2.push(value2);
                paths2.push(newPath2);
            }

            // remember that the current objects are already compared
            if (isObject1 && isObject2) {
                compared[combinedPath] = true;
            }

            // End of cyclic logic

            // neither value1 nor value2 is a cycle
            // continue with next level
            return deepEqual(value1, value2, newPath1, newPath2);
        });
    })(first, second, "$1", "$2");
}

module.exports = deepEqualCyclic;
