123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- // Smart comparison of three.js objects.
- // Identifies significant differences between two objects.
- // Performs deep comparison.
- // Comparison stops after the first difference is found.
- // Provides an explanation for the failure.
- function SmartComparer() {
- 'use strict';
- // Diagnostic message, when comparison fails.
- var message;
- return {
- areEqual: areEqual,
- getDiagnostic: function () {
- return message;
- }
- };
- // val1 - first value to compare (typically the actual value)
- // val2 - other value to compare (typically the expected value)
- function areEqual( val1, val2 ) {
- // Values are strictly equal.
- if ( val1 === val2 ) return true;
- // Null or undefined values.
- /* jshint eqnull:true */
- if ( val1 == null || val2 == null ) {
- if ( val1 != val2 ) {
- return makeFail( 'One value is undefined or null', val1, val2 );
- }
- // Both null / undefined.
- return true;
- }
- // Don't compare functions.
- if ( isFunction( val1 ) && isFunction( val2 ) ) return true;
- // Array comparison.
- var arrCmp = compareArrays( val1, val2 );
- if ( arrCmp !== undefined ) return arrCmp;
- // Has custom equality comparer.
- if ( val1.equals ) {
- if ( val1.equals( val2 ) ) return true;
- return makeFail( 'Comparison with .equals method returned false' );
- }
- // Object comparison.
- var objCmp = compareObjects( val1, val2 );
- if ( objCmp !== undefined ) return objCmp;
- // if (JSON.stringify( val1 ) == JSON.stringify( val2 ) ) return true;
- // Object differs (unknown reason).
- return makeFail( 'Values differ', val1, val2 );
- }
- function isFunction( value ) {
- // The use of `Object#toString` avoids issues with the `typeof` operator
- // in Safari 8 which returns 'object' for typed array constructors, and
- // PhantomJS 1.9 which returns 'function' for `NodeList` instances.
- var tag = isObject( value ) ? Object.prototype.toString.call( value ) : '';
- return tag == '[object Function]' || tag == '[object GeneratorFunction]';
- }
- function isObject( value ) {
- // Avoid a V8 JIT bug in Chrome 19-20.
- // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
- var type = typeof value;
- return !! value && ( type == 'object' || type == 'function' );
- }
- function compareArrays( val1, val2 ) {
- var isArr1 = Array.isArray( val1 );
- var isArr2 = Array.isArray( val2 );
- // Compare type.
- if ( isArr1 !== isArr2 ) return makeFail( 'Values are not both arrays' );
- // Not arrays. Continue.
- if ( ! isArr1 ) return undefined;
- // Compare length.
- var N1 = val1.length;
- var N2 = val2.length;
- if ( N1 !== val2.length ) return makeFail( 'Array length differs', N1, N2 );
- // Compare content at each index.
- for ( var i = 0; i < N1; i ++ ) {
- var cmp = areEqual( val1[ i ], val2[ i ] );
- if ( ! cmp ) return addContext( 'array index "' + i + '"' );
- }
- // Arrays are equal.
- return true;
- }
- function compareObjects( val1, val2 ) {
- var isObj1 = isObject( val1 );
- var isObj2 = isObject( val2 );
- // Compare type.
- if ( isObj1 !== isObj2 ) return makeFail( 'Values are not both objects' );
- // Not objects. Continue.
- if ( ! isObj1 ) return undefined;
- // Compare keys.
- var keys1 = Object.keys( val1 );
- var keys2 = Object.keys( val2 );
- for ( var i = 0, l = keys1.length; i < l; i ++ ) {
- if ( keys2.indexOf( keys1[ i ] ) < 0 ) {
- return makeFail( 'Property "' + keys1[ i ] + '" is unexpected.' );
- }
- }
- for ( var i = 0, l = keys2.length; i < l; i ++ ) {
- if ( keys1.indexOf( keys2[ i ] ) < 0 ) {
- return makeFail( 'Property "' + keys2[ i ] + '" is missing.' );
- }
- }
- // Keys are the same. For each key, compare content until a difference is found.
- var hadDifference = false;
- for ( var i = 0, l = keys1.length; i < l; i ++ ) {
- var key = keys1[ i ];
- if ( key === "uuid" || key === "id" ) {
- continue;
- }
- var prop1 = val1[ key ];
- var prop2 = val2[ key ];
- // Compare property content.
- var eq = areEqual( prop1, prop2 );
- // In case of failure, an message should already be set.
- // Add context to low level message.
- if ( ! eq ) {
- addContext( 'property "' + key + '"' );
- hadDifference = true;
- }
- }
- return ! hadDifference;
- }
- function makeFail( msg, val1, val2 ) {
- message = msg;
- if ( arguments.length > 1 ) message += " (" + val1 + " vs " + val2 + ")";
- return false;
- }
- function addContext( msg ) {
- // There should already be a validation message. Add more context to it.
- message = message || "Error";
- message += ", at " + msg;
- return false;
- }
- }
- export { SmartComparer };
|