1+ /*
2+ Source: https://github.com/ReactiveSets/toubkal
3+
4+ The MIT License (MIT)
5+
6+ Copyright (c) 2013-2016, Reactive Sets
7+
8+ Permission is hereby granted, free of charge, to any person obtaining a copy
9+ of this software and associated documentation files (the "Software"), to deal
10+ in the Software without restriction, including without limitation the rights
11+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+ copies of the Software, and to permit persons to whom the Software is
13+ furnished to do so, subject to the following conditions:
14+
15+ The above copyright notice and this permission notice shall be included in all
16+ copies or substantial portions of the Software.
17+
18+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+ SOFTWARE.
25+ */
26+ 'use strict' ;
27+
28+ var toString = Object . prototype . toString ;
29+
30+ /* -----------------------------------------------------------------------------------------
31+ equals( a, b [, enforce_properties_order, cyclic] )
32+
33+ Returns true if a and b are deeply equal, false otherwise.
34+
35+ Parameters:
36+ - a (Any type): value to compare to b
37+ - b (Any type): value compared to a
38+
39+ Optional Parameters:
40+ - enforce_properties_order (Boolean): true to check if Object properties are provided
41+ in the same order between a and b
42+
43+ - cyclic (Boolean): true to check for cycles in cyclic objects
44+
45+ Implementation:
46+ 'a' is considered equal to 'b' if all scalar values in a and b are strictly equal as
47+ compared with operator '===' except for these two special cases:
48+ - 0 === -0 but are not equal.
49+ - NaN is not === to itself but is equal.
50+
51+ RegExp objects are considered equal if they have the same lastIndex, i.e. both regular
52+ expressions have matched the same number of times.
53+
54+ Functions must be identical, so that they have the same closure context.
55+
56+ "undefined" is a valid value, including in Objects
57+
58+ 106 automated tests.
59+
60+ Provide options for slower, less-common use cases:
61+
62+ - Unless enforce_properties_order is true, if 'a' and 'b' are non-Array Objects, the
63+ order of occurence of their attributes is considered irrelevant:
64+ { a: 1, b: 2 } is considered equal to { b: 2, a: 1 }
65+
66+ - Unless cyclic is true, Cyclic objects will throw:
67+ RangeError: Maximum call stack size exceeded
68+ */
69+ export default function equals ( a , b , enforce_properties_order , cyclic ) {
70+ return a === b // strick equality should be enough unless zero
71+ && a !== 0 // because 0 === -0, requires test by _equals()
72+ || _equals ( a , b ) // handles not strictly equal or zero values
73+ ;
74+
75+ function _equals ( a , b ) {
76+ // a and b have already failed test for strict equality or are zero
77+
78+ var s , l , p , x , y ;
79+
80+ // They should have the same toString() signature
81+ if ( ( s = toString . call ( a ) ) !== toString . call ( b ) ) return false ;
82+
83+ switch ( s ) {
84+ default : // Boolean, Date, String
85+ return a . valueOf ( ) === b . valueOf ( ) ;
86+
87+ case '[object Number]' :
88+ // Converts Number instances into primitive values
89+ // This is required also for NaN test bellow
90+ a = + a ;
91+ b = + b ;
92+
93+ return a ? // a is Non-zero and Non-NaN
94+ a === b
95+ : // a is 0, -0 or NaN
96+ a === a ? // a is 0 or -O
97+ 1 / a === 1 / b // 1/0 !== 1/-0 because Infinity !== -Infinity
98+ : b !== b // NaN, the only Number not equal to itself!
99+ ;
100+ // [object Number]
101+
102+ case '[object RegExp]' :
103+ return a . source == b . source
104+ && a . global == b . global
105+ && a . ignoreCase == b . ignoreCase
106+ && a . multiline == b . multiline
107+ && a . lastIndex == b . lastIndex
108+ ;
109+ // [object RegExp]
110+
111+ case '[object Function]' :
112+ return false ; // functions should be strictly equal because of closure context
113+ // [object Function]
114+
115+ case '[object Array]' :
116+ if ( cyclic && ( x = reference_equals ( a , b ) ) !== null ) return x ; // intentionally duplicated bellow for [object Object]
117+
118+ if ( ( l = a . length ) != b . length ) return false ;
119+ // Both have as many elements
120+
121+ while ( l -- ) {
122+ if ( ( x = a [ l ] ) === ( y = b [ l ] ) && x !== 0 || _equals ( x , y ) ) continue ;
123+
124+ return false ;
125+ }
126+
127+ return true ;
128+ // [object Array]
129+
130+ case '[object Object]' :
131+ if ( cyclic && ( x = reference_equals ( a , b ) ) !== null ) return x ; // intentionally duplicated from above for [object Array]
132+
133+ l = 0 ; // counter of own properties
134+
135+ if ( enforce_properties_order ) {
136+ var properties = [ ] ;
137+
138+ for ( p in a ) {
139+ if ( a . hasOwnProperty ( p ) ) {
140+ properties . push ( p ) ;
141+
142+ if ( ( x = a [ p ] ) === ( y = b [ p ] ) && x !== 0 || _equals ( x , y ) ) continue ;
143+
144+ return false ;
145+ }
146+ }
147+
148+ // Check if 'b' has as the same properties as 'a' in the same order
149+ for ( p in b )
150+ if ( b . hasOwnProperty ( p ) && properties [ l ++ ] != p )
151+ return false ;
152+ } else {
153+ for ( p in a ) {
154+ if ( a . hasOwnProperty ( p ) ) {
155+ ++ l ;
156+
157+ if ( ( x = a [ p ] ) === ( y = b [ p ] ) && x !== 0 || _equals ( x , y ) ) continue ;
158+
159+ return false ;
160+ }
161+ }
162+
163+ // Check if 'b' has as not more own properties than 'a'
164+ for ( p in b )
165+ if ( b . hasOwnProperty ( p ) && -- l < 0 )
166+ return false ;
167+ }
168+
169+ return true ;
170+ // [object Object]
171+ } // switch toString.call( a )
172+ } // _equals()
173+
174+ /* -----------------------------------------------------------------------------------------
175+ reference_equals( a, b )
176+
177+ Helper function to compare object references on cyclic objects or arrays.
178+
179+ Returns:
180+ - null if a or b is not part of a cycle, adding them to object_references array
181+ - true: same cycle found for a and b
182+ - false: different cycle found for a and b
183+
184+ On the first call of a specific invocation of equal(), replaces self with inner function
185+ holding object_references array object in closure context.
186+
187+ This allows to create a context only if and when an invocation of equal() compares
188+ objects or arrays.
189+ */
190+ function reference_equals ( a , b ) {
191+ var object_references = [ ] ;
192+
193+ return ( reference_equals = _reference_equals ) ( a , b ) ;
194+
195+ function _reference_equals ( a , b ) {
196+ var l = object_references . length ;
197+
198+ while ( l -- )
199+ if ( object_references [ l -- ] === b )
200+ return object_references [ l ] === a ;
201+
202+ object_references . push ( a , b ) ;
203+
204+ return null ;
205+ } // _reference_equals()
206+ } // reference_equals()
207+ } // equals()
0 commit comments