Test case details

Preparation Code

if (!Array.prototype.find) {             Array.prototype.find = function (callback, thisArg) {             'use strict';             var arr = this,                 arrLen = arr.length,                 i;             for (i = 0; i < arrLen; i += 1) {                 if (callback.call(thisArg, arr[i], i, arr)) {                     return arr[i];                 }             }             return undefined;         };     }             var gms = {};         gms.Observer = function( object, property, callback, context, options ) {             if( typeof object !== 'object' ) {                 return;         }             if( ! ( this instanceof gms.Observer )) {                 return new gms.Observer( object, property, callback, context, options );         }             this.object    = object;         this.property  = property;         this.callback  = callback;         this.context   = context;         this.options   = options || {};             this.sync      = ( options ) ? options.sync : false;             // Store the observe function to call ( sync or async )         this.observeFunction = ( this.sync ) ? gms.Observe.propertySync : gms.Observe.property;             if( this.sync ) {                 this.useNative = false;         } else {                 this.useNative = ( options && options.useNative !== undefined ) ? options.useNative : Object.observe !== undefined;         }             // this.useNative = false;                 // If we've already created the observer, let's use that         var observer = this._observerAlreadyAttached();         if( observer && observer.length > 0 ){                 return observer;         }                 if( property.indexOf( '.' ) > -1 ) {                 return this._observeChildren( );         }             this._observingArray = (                 object[ property ] instanceof Array ||                 object instanceof Array && property.indexOf( '@each' ) === 0         );             this._attachObservers();             return this._attachCallbackFunction();         };         gms.Observer.prototype = {             /**          * Attaches the appropriate observes ( Object.observe or getter/setter )          * @return {null}          */             _attachObservers: function() {                     if( this._observingArray ){                             // If the property starts with @each, and our object is an array, use that                         if( ! ( this.property.indexOf( '@each' === 0 ) && this.object instanceof Array ) ) {                                 this.object = this.object[ this.property ];                         }                             if( this.useNative ) {                                 this._addArrayObjectObserver( this.object );                         }                             else {                                 this._addArrayMethods( this.object );                                 this._addSetters( );                         }                         }  else {                             if( this.useNative ) {                                 this._addObjectObserver();                         }                             else {                                 this._addSetters( );                         }                 }         },             /**          * Handles observing arrays          * @param  {Object}   object   - The object to observe          * @param  {String}   property - The array property          * @param  {String}   eachProp - The property to observe on the array          * @return {null}          */         _observeArray: function( object, property, eachProp ) {                     var array = ( property === '@each' ) ? object : object[ property ],                         observers = [];                     var observer = this.observeFunction.call( gms.Observe, object, property, this.callback, this.context, this.options );                     observers.push( observer );                     // Add array each observers                 if( eachProp ) {                             this._addEachProperties( array, eachProp );                             array.__eachObservers__[ eachProp ].push( this._createObserver() );                             var eachObservers = [];                             array.forEach( function( item ) {                                 eachObservers.push(                                         this.observeFunction.call( gms.Observe, item, eachProp, this.callback, this.context, this.options )                                 );                         }, this );                             observers = observers.concat( eachObservers );                 }                     return observers;             },             /**          * Adds a property to observe on each item of the array          * @param {Array} array     - The array to observe          * @param {String} eachProp - The property to observe on each array item          */         _addEachProperties: function( array, eachProp ) {                     if( ! array.__eachObservers__ ) {                         Object.defineProperty( array, '__eachObservers__', {                                 enumerable : true,                                 writable   : true,                                 value: {}                         } );                 }                     if( ! array.__eachObservers__[ eachProp ] ) {                         Object.defineProperty( array.__eachObservers__, eachProp, {                                 enumerable : true,                                 writable   : true,                                 value      : []                         } );                 }             },             /**          * Attaches the callback function to the object.property          * @return { Observer} - The Observer created          */         _attachCallbackFunction: function() {                     // If we're observing an array, we want the __observes__ to be an array,                 // since there's no property name for an object                 var __observersValue__ = ( this._observingArray ) ? [] : {};                     // Create the __observers__ property                 if( this.object.__observers__  === undefined ) {                             Object.defineProperty( this.object, '__observers__', {                                 enumerable : false,                                 value      : __observersValue__,                                 writable   : true                         } );                 }                     // Create the __observers__[ property ] array to store the observers                 if( !this._observingArray && this.object.__observers__[ this.property ] === undefined ) {                             var observers = this.object.__observers__;                             Object.defineProperty( observers, this.property, {                                 enumerable : true,                                 value      : [],                                 writable   : true                         } );                     }                     // Create the Observer actual observer                 var observer = this._createObserver();                     // Add it to the object.__observers__ array                 if( !this._observingArray ){                         this.object.__observers__[ this.property ].push ( observer );                 } else {                         this.object.__observers__.push ( observer );                 }                     return observer;         },             /**          * Observe child properties in dot notation ( object, 'prop.subProp' )          * @optional {Object} object - The object to observe          * @optional {String} properties - A dot separated list of properties to observe          * @return {Array}                         - Array of observers          */         _observeChildren: function( object, properties ) {                     object = object || this.object;                     var parts     = ( properties ) ? properties.split( '.' ) : this.property.split( '.' ),                         property  = parts.shift(),                             observers = [], observer;                     var observeLastProperty = this.options.observeLastProperty;                     // Use the right observe function ( sync/async                     if( parts[ 0 ] === '@each' || property === '@each'  ) {                         observer = this._addEachObservers( object, property, parts );                 }                     else if( property === '@any' ) {                             return this._addAnyObservers( parts.join( '.' ) );                 }                     else {                             object[ property ] = object[ property ] || {};                             // Observe the first child                         observer = this.observeFunction.call( gms.Observe, object, property, this.callback, this.context, this.options );                 }                     // Add the observer to the array                 observers.push( observer );                     // Get recursive                 if( parts.length > 1 ){                         var childObservers = this._observeChildren( object[ property ], parts.join('.') );                         observers = observers.concat( childObservers );                 }                     // Observe the last child                 else {                             if( property  !== '@any' ) {                                 object = object[ property ];                         }                             property = parts[ 0 ];                             // Clone the options object so we can set a property on it                         var optionsClone   = ( this.options !== undefined ) ? goog.object.clone( this.options ) : { };                             if( observeLastProperty ){                                 optionsClone.lastProperty = true;                         }                             observer = this.observeFunction.call( gms.Observe, object, parts.join( '.' ), this.callback, this.context, optionsClone );                         observers.push( observer );                 }                     return observers;         },             /**          * Adds an .@each observer to an array          * @param {Object} object - The object to observe          * @param {String} property - The property          * @param  {Array}      parts    - An array of all the child observers          * @return {Observer}            - The observer added          */         _addEachObservers: function( object, property, parts ) {                     var eachObserver = ( parts[ 0 ] === '@each' ) ? parts.slice( 1 ).join( '.' ) : parts.join( '.' );                     object[ property ] = object[ property ] || [];                     var observer = this._observeArray( object, property, eachObserver );                     if( parts[ 0 ] === '@each' ){                         parts.splice( 0, 1 );                 }                     return observer;         },             /**          * Adds an .@any observer to an object          * @param  {String}     property - The property          * @return {Array}               - The observers added          */         _addAnyObservers: function( property ) {                     var observers = [];                     if( this.sync && ! this.options.ignoreAnySyncWarning ){                         console.warn( '----------------' );                         console.log( 'You are synchronously observing @any on an object.' );                         console.log( 'Your callback will fire when any property currently defined on the object changes' );                         console.log( 'But will not fire when new properties are added.' );                         console.log( 'To acheive both, you must observe @any asynchronously.' );                         console.log( 'To suppress this message pass ignoreAnySyncWarning: true as an Observer options argument');                         console.warn( '----------------' );                 }                     for( var prop in this.object ) {                             var observer = this.observeFunction.call( gms.Observe, this.object, prop, this.callback, this.context, this.options );                         observers.push( observer );                             // If we're observing a property on @any item, do it here!                         if( property !== '' && property !== '@any' ) {                                 observer = this.observeFunction.call( gms.Observe, this.object[ prop ], property, this.callback, this.context, this.options );                                 observers.push( observer );                         }                 }                     return observers;         },             /**          * Creates the Observer object          * @return {Observer} - The Observer object          */         _createObserver: function() {                     // TODO: maybe make this its own class?                 return {                         callback      : this.callback,                         context       : this.context,                         sync          : this.sync,                         object        : this.object,                         property      : this.property,                         nativeObserve : this.useNative,                         options       : this.options                 };         },             /**          * Creates the change object that gets returned to the callback          * @param {Object} object   - The object listening for the change          * @param {String} property - The property changed          * @param {*} oldValue      - The previous value          * @return {change}         - the Change object to return to the callback          */         _createChange: function( object, property, oldValue, args ) {                     // TODO: maybe make this its own class?                 return {                         type         : 'update',                         object       : object,                         name         : property,                         oldValue     : oldValue,                         args         : args,                         currentValue : object[ property ]                 };         },             _createArrayChange: function( array, item, type, oldValue, args ) {                     var change = {                         type         : type,                         object       : array,                         index        : array.indexOf( item ),                         oldValue     : oldValue,                         currentValue : array[ array.indexOf( item ) ],                         args         : args                 };                     if( type === 'delete' ) {                         change.oldValue = item;                 }                     return change;         },             /**          * Takes a change object returned by the native Object.observe and adds some properties to it          * @param  {Object} change - The change returned by O.o          * @return {Change}        - The new change object          */         _cloneNativeChange: function( change, args ) {                     // TODO: maybe make this its own class?                 return {                         type         : 'update',                         object       : change.object,                         name         : change.name,                         oldValue     : change.oldValue,                         args         : args,                         currentValue : change.object[ change.name ]                 };         },             /**          * Takes a change object returned by the native Object.observe( array ) and adds some properties to it          * @param  {Object} change - The change returned by O.o          * @return {Change}        - The new change object          */         _cloneNativeArrayChange: function( change, args ) {                     // TODO: maybe make this its own class?                 return {                         type         : change.type,                         object       : change.object,                         index        : change.name,                         oldValue     : change.oldValue,                         args         : args,                         currentValue : change.object[ change.name ]                 };         },             /**          **** Native Object.observe Methods *******          -----------------------------------         */             /**          * Uses the native Object.observe          */         _addObjectObserver: function() {                     var observedProperty = this.property,                         _this = this;                     Object.observe( this.object, function( changes ) {                             // We only care if an object has been updated, added, or deleted                         changes = changes.filter( function( c ) {                                 return [ 'add', 'update', 'delete' ].indexOf( c.type ) > -1;                         });                             _this._objectObserverCallback( changes, observedProperty  );                 });         },             /**          * Users the native Object.observe to observe changes to an array          * @param {Array} array - The array to observe          */         _addArrayObjectObserver: function( array ) {                     var _this = this;                     Object.observe( array, function( change ) {                         _this._arrayObjectObserverCallback( change  );                     });             },             /**          * The callback fired by the native Object.observe          * @param  {Object} changed          - The change object          * @param  {String} observedProperty - The property observed          * @return {null}          */         _objectObserverCallback: function( changed, observedProperty ) {                     changed.forEach( function( change ) {                         this._forEachChangedObject( change, observedProperty );                 }, this );         },             /**          * Callback fired by the array Object.observe          * @param  {Array} changed - Array of changes          * @return {null}          */         _arrayObjectObserverCallback: function( changed ) {                     changed.forEach( function( change ) {                         this._forEachArrayChangedObject( change );                 }, this );         },             /**          * Looks through all the changed objects for one matching our property          * @param  {[type]} change [description]          * @return {[type]}        [description]          */         _forEachChangedObject: function( change, observedProperty ) {                     var property  = observedProperty,                         object    = change.object,                         observers = object.__observers__;                     if( change.name === property || observedProperty === '@any' ) {                             observers[ property ].forEach( function( observer ){                                 change = this._cloneNativeChange( change, observer.options );                                 gms.Observe._onObservedPropChange( observer, change );                         }, this );                     }         },             /**          * Handles adding array changes to the observer queue          * Only adds to queue when an actual item in the array has changed, not an array property ( like length )          * @param  {Change} change - A single change object          * @return {null}          */         _forEachArrayChangedObject: function( change ) {                     var array    = change.object,                         observers = array.__observers__;                     if( ! isNaN( change.name ) ) {                             this._reattachEachObservers( array, change.object[ change.name ] );                             observers.forEach( function( observer ){                                 change = this._cloneNativeArrayChange( change, observer.options );                                 gms.Observe._onObservedPropChange( observer, change );                         }, this );                     }         },             /**          **** Non-Native Observer Methods ****          *------------------------------------          */             /**          * Manually creates setters and getter functions          */         _addSetters: function( ){                     var object   = this.object,                         property = this.property;                     if( property === '@any' ) {                         return this._addAnyObservers(  this.property );                 }                     // Check if __ private __ property is defined                 if( ! object.hasOwnProperty( '__' + property + '__' ) ) {                             this._addPrivateProp( object, property );                             // Then redefine the propertyerty                         var setter = object.__lookupSetter__( property ),                                 _this = this;                             Object.defineProperty( object, property, {                                     configurable: true,                                     get: function( ) {                                         return this[ '__' + property + '__' ];                                 },                                     set: function( value ) {                                             var oldValue = this[ property ];                                             if( value !== oldValue ) {                                                     if( setter ) {                                                         setter( value );                                                 } else {                                                         object[ '__' + property + '__' ] = value;                                                 }                                                     return _this._set.call( _this, this, property, oldValue );                                         }                                     }                             });                     }         },             /**          * Function called after an observed property is changed, creates a queue of callbacks to fire          * @param {Object} object   The observed object          * @param {String} property The changed property name          * @param {*}      oldValue The previous value of the property          */         _set: function( object, property, oldValue ) {                     if( object.__observers__ && object.__observers__[ property ] ) {                             object.__observers__[ property ].forEach( function( observer ){                                     var changed = this._createChange( object, property, oldValue, observer.options );                                 gms.Observe._onObservedPropChange( observer, changed );                             }, this );                 }         },             /**          * Creates proxies for array methods push, splice, pop and sort; which trigger _onArrayUpdated when called          * @param {Array} array - The array to add the methods to          */         _addArrayMethods: function( array ) {                     var _this = this;                     array.push = function( ) {                         var pushed = Array.prototype.push.apply( this, arguments );                         _this._onArrayUpdated( this, 'add', Array.prototype.slice.call( arguments, [ this[ pushed ] ] ) );                         return pushed;                 };                     array.splice = function( ) {                         var spliced = Array.prototype.splice.apply( this, arguments );                             if( arguments[ 2 ] !== undefined ){                                 _this._onArrayUpdated( this, 'add', [ arguments[ 2 ] ] );                         } else {                                 _this._onArrayUpdated( this, 'delete', spliced );                         }                         return spliced;                 };                     array.pop = function( ) {                         var popped = Array.prototype.pop.apply( this, arguments );                         _this._onArrayUpdated( this, 'delete', popped );                         return popped;                 };                     array.sort = function( ) {                         var sorted = Array.prototype.sort.apply( this, arguments );                         _this._onArrayUpdated( this, 'change', sorted );                         return sorted;                 };         },             /**          * Called by the proxied array methods          * @param  {Array} array        - The array updated          * @param  {String} type         - The type of update [ 'add', 'delete', 'update' ]          * @param  {Array} changedItems - The items that were added, deleted, or updated          * @return {null}          */         _onArrayUpdated: function( array, type, changedItems ) {                     if( array['__observers__'] ) {                             // If we're observing properties on each array item                         // Observe them on the new items we're adding                         if( type === 'add' && array.__eachObservers__ ){                                 this._reattachEachObservers( array, changedItems );                         }                             for( var i = 0, l = array['__observers__'].length; i < l; i++ ) {                                     var observer = array['__observers__'][ i ];                                     var changes = [];                                     if( changedItems instanceof Array  ) {                                         changedItems.forEach( function( item ) {                                                 var change = this._createArrayChange( array, item, type, observer.options );                                                 changes.push( change );                                         }, this );                                 }                                     else {                                         changes.push( this._createArrayChange( array, changedItems, type, observer.options ) );                                 }                                     gms.Observe._onObservedPropChange( observer, changes );                             }                 }         },             /**          * Reattaches the each observer to items in an array          * @param  {Array} array - The array          * @param  {Array} items - An array of items to attach          * @return {null}          */         _reattachEachObservers: function( array, items ) {                     for( var observedProp in array.__eachObservers__ ){                             var observers = array.__eachObservers__[ observedProp ];                             observers.forEach( function( observer ){                                     if( items instanceof Array ){                                             items.forEach( function( arrayItem ){                                                 this.observeFunction.call( gms.Observe, arrayItem, observedProp, observer.callback, observer.context, observer.args );                                         }, this );                                 }                                     else {                                         this.observeFunction.call( gms.Observe, items, observedProp, observer.callback, observer.context, observer.args );                                 }                             }, this );                 }         },             /**          * Creates the internal __property__ on the object          * @param {Object} object   The object          * @param {String} property The property          */         _addPrivateProp: function( object, property ) {                     var getter = object.__lookupGetter__( property );                     if( getter ) {                             Object.defineProperty( object, '__' + property + '__', {                                 enumerable : false,                                 get        : getter                         });                     }                     else {                             Object.defineProperty( object, '__' + property + '__', {                                 enumerable : false,                                 value      : object[ property ],                                 writable   : true                         });                 }         },             _observerAlreadyAttached: function( ) {                     if( this.object.__observers__ && this.object.__observers__[ this.property ] ){                         return this.object.__observers__[ this.property ].filter( function( observer ) {                           return this.callback === observer.callback && this.context === observer.context;                         }, this );                 }                     return false;             }     };             gms.Observe = {             // The callback queue         _queue: [],             /**          * Observes a single property          * @param  {Object} object     - The object to observe          * @param  {String} property   - The name of the property          * @param  {Function} callback - The callback function          * @param  {Object} context    - The context to run the callback          * @param {Object} options     - Optional options          * @return {Observer}          - The Observer object          */         property: function( object, property, callback, context, options ) {                     if( typeof property !== 'string' ) {                             if( Array.isArray( property ) ){                                 return this.properties.apply( this, arguments );                         }                             throw 'You must pass a string to Observe.property';                 }                     return new gms.Observer( object, property, callback, context, options );             },             /**          * Observe a single property synchronously          * @param  {Object}   object   - The object to observe          * @param  {String}   property - The property to observe          * @param  {Function} callback - The callback function          * @param  {Object} context    - The context to run the callback          * @param {Object} options     - Optional options          * @return {Observer}          - The observer object          */         propertySync: function( object, property, callback, context, options ) {                     options      = options || {};                 options.sync = true;                     return this.property( object, property, callback, context, options );         },             /**          * Observe multiple properties on an object          * @param  {Object}   object     - The object to observer          * @param  {Array}    properties - An array of properties          * @param  {Function} callback   - The callback to fire          * @param  {Object} context      - The context to run the callback          * @param {Object} options       - Optional options          * @return {Array}               - The created observers          */         properties: function( object, properties, callback, context, options ) {                     if( ! ( properties instanceof Array ) ) {                         throw 'You must pass an array to Observe.properties';                 }                     var observers = [];                     properties.forEach( function( property ){                         observers.push( this.property( object, property, callback, context, options ) );                 }, this );                     return observers;         },             /**          * Observe multiple properties on an object synchronously          * @param  {Object}   object     - The object to observer          * @param  {Array}    properties - An array of properties          * @param  {Function} callback   - The callback to fire          * @param  {Object} context      - The context to run the callback          * @param {Object} options       - Optional options          * @return {Array}               - The created observers          */         propertiesSync: function( object, properties, callback, context, options ) {                     var observers = [];                     properties.forEach( function( property ){                         observers.push( this.propertySync( object, property, callback, context, options ) );                 }, this );                     return observers;         },             /**          * Creates observers to fire the observer only when the last property in the chain changes          * @param  {Object}   object   - The object to observe          * @param  {String}   property - The property          * @param  {Function} callback - The callback          * @param  {Object}   context  - The context to run the callback with          * @param {Object} options     - Optional options          * @return {Observer}            The Observer          */         lastProperty: function( object, property, callback, context, options ) {                     options = options || {};                 options.observeLastProperty = true;                     this.property( object, property, callback, context, options );         },             /**          * Creates observers to fire the observer synchronously only when the last property in the chain changes          * @param  {Object}   object   - The object to observe          * @param  {String}   property - The property          * @param  {Function} callback - The callback          * @param  {Object}   context  - The context to run the callback with          * @param {Object} options     - Optional options          * @return {Observer}            The Observer          */         lastPropertySync: function( object, property, callback, context, options ) {                     options = options || {};                 options.observeLastProperty = true;                     this.propertySync( object, property, callback, context, options );         },             /**          * Stop observing a property          * @param  {Observer|Array} observer - The observer(s) to stop          * @return {null}          */         stop: function( observer ) {                     if( observer instanceof Array ) {                         observer.forEach( this._removeObserver, this );                 } else {                         this._removeObserver( observer );                 }         },             /**          * Remove an observer from an object          * @param  {Object}   object   - The object          * @param  {String}   property - The property to stop observing          * @param  {Function} callback - The callback          * @param  {Object}   context  - The context the callback runs in          * @return {null}          */         remove: function( object, property, callback, context ) {                     var observers     = object.__observers__,                             // Find the observers to remove                         observersToRemove = observers[ property ].filter( function( o ) {                          return o.callback === callback && o.context === context;                         });                     this.stop( observersToRemove );         },             /**          * Pause an observer          * @param {Observer} observer - The observer to pause          */         pause: function( observer ) {                 observer.paused = true;         },             /**          * Resume a paused observer          * @param {Observer} observer      - The observer to resume          * @param {Boolean} fireLastChange - Whether to fire the callback for the last change          */         resume: function( observer, fireLastChange ) {                     observer.paused = false;                     if( fireLastChange && observer.__lastChange__ ) {                         this._onObservedPropChange( observer, observer.__lastChange__ );                 }                     delete observer.__lastChange__;         },             removeAllFromObject: function( object ) {                     for( var prop in object.__observers__ ){                         object.__observers__[ prop ].forEach( this._removeObserver, this );                 }             },                     /**          * Adds an observer to the queue and starts resets a timer to fire the queue          * @param  {Observer} observer - The Observer to add          * @param  {Array} changes     - Array of changes          * @return {null}          */         _pushObserverToQueue: function( observer, changes ) {                     var item = this._queue.find( function( item ){                         return item.callback === observer.callback && item.context === observer.context;                 } );                     if( item ) {                         item.changes = item.changes.concat( changes );                 } else {                             this._queue.push( {                                 callback : observer.callback,                                 context  : observer.context,                                 changes  : changes                         });                 }                     window.clearTimeout( this._queueTimer );                 this._queueTimer = window.setTimeout( this._fireQueuedCallbacks.bind( this ), 2 );             },             /**          * Fires the queued callbacks from the changed/setters          * @return {null}          */         _fireQueuedCallbacks: function() {                     for( var i = 0, l = this._queue.length; i < l; i++ ) {                             var item = this._queue.shift();                             if( item ) {                                 item.callback.call( item.context, item.changes, item.options );                         }                 }                     // If things got added while we were running the _queue, run it again!                 if( this._queue.length > 0 ) {                         this._fireQueuedCallbacks();                 }         },                 /**          * Fired when an observed property changes          * Handles reattaching child observers and queuing/firing callbacks          * @param  {Observer} observer   - The observer that changed          * @param  {Array|Object} change - The change object(s)          * @return {null}          */         _onObservedPropChange: function( observer, change ) {                     // Always make changes an array                 var changes = ( change instanceof Array ) ? change: [ change ];                     this._reattachOnChange( changes );                     if( observer.paused ) {                         observer.__lastChange__ = change;                         return;                 }                     if( observer.options && observer.options.observeLastProperty && ! observer.options.lastProperty ) {                         return;                 }                     if( observer.sync ){                         observer.callback.call( observer.context, changes, observer.options );                 } else {                         gms.Observe._pushObserverToQueue( observer, changes );                 }         },             /**          * Reattaches all child observers to a changed object          * @param  {Array} changes - Array of changes          * @return {null}          */         _reattachOnChange: function( changes ) {                     changes.forEach( function( change ){                         this._reattachChildObservers( change.object, change.name, change.oldValue );                 }, this );             },             _reattachChildObservers: function( object, property, oldValue ) {                     if( typeof object !== 'object' || object === null ) {                         return;                 }                     if( !oldValue || oldValue.__observers__ === undefined ) {                         return;                 }                     var observersToRemove = [];                     // Loop through all the old observers                 for( var observedProp in oldValue.__observers__ ) {                             var observers = oldValue.__observers__[ observedProp ];                             // Reattach them all                         observers.forEach( function( observer ) {                                     if( observer.context === oldValue ) {                                         observer.context = object[ property ];                                 }                                     // Use the right observe function ( sync/async )                                 var observeFunction = ( observer.sync ) ? this.propertySync : this.property;                                     // Reattach the observers to this property                                 observeFunction.call( this, object[ property ], observedProp, observer.callback, observer.context );                                     // Add this observer to the list to remove                                 observersToRemove.push( observer );                             }, this );                             this._reattachChildObservers( object[ property ], observedProp, oldValue[ observedProp ] );                 }                     // Remove all the old observers                 observersToRemove.forEach( this._removeObserver, this );         },                 /**          * Removes a single observer          * @param  {Observer} observer - The observer to remove          * @return {null}          */         _removeObserver: function( observer ) {                     if( ! observer ) {                         return;                 }                     var object    = observer.object,                         property  = observer.property;                     if( ! object ) {                         return;                 }                     var observerIndex = object.__observers__[ property ].findIndex( function( o ) {                   return o === observer;                 });                     object.__observers__[ property ].splice( observerIndex, 1 );                     // If there are no observers left, stop observing the object                 var hasNoKeys = Object.keys( object.__observers__ ).length === 0;                     if( hasNoKeys ) {                         if( observer.nativeObserve ) {                                 Object.unobserve( observer.object, observer.callback );                         }                 }         },     };             var object = {     a: 1,     b: 2,     c: 3,     d: 4,     e: 5     };         var changes;     var callbackFunction = function( changes ) {        changes = changes;        console.log( changes );     debugger;     deferred.resolve();     }

Test cases

Test #1

// async test gms.Observe.property( object, 'a', callbackFunction ); object.a = 2;

Test #2

// async test debugger; gms.Observe.property( object, 'a', callbackFunction, null, { useNative: false } ); object.a = 2;