Compare commits
	
		
			16 Commits
		
	
	
		
			release/2.
			...
			mutation-o
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f8884cef62 | ||
|   | 3262b810b3 | ||
|   | 790a929d66 | ||
|   | 2920e58b07 | ||
|   | 1613975048 | ||
|   | dae58e0ad9 | ||
|   | a5826f5d45 | ||
|   | 4480956bab | ||
|   | dd4d55b422 | ||
|   | 705ec5b753 | ||
|   | 48177082f1 | ||
|   | 4c65062ce9 | ||
|   | c4d9b1cf41 | ||
|   | 56dbbd894a | ||
|   | f3d14be034 | ||
|   | 018f3e7749 | 
| @@ -215,7 +215,7 @@ define([ | ||||
|          * @memberof module:openmct.MCT# | ||||
|          * @name objects | ||||
|          */ | ||||
|         this.objects = new api.ObjectAPI(); | ||||
|         this.objects = new api.ObjectAPI(this.types); | ||||
|  | ||||
|         /** | ||||
|          * An interface for retrieving and interpreting telemetry data associated | ||||
|   | ||||
| @@ -60,7 +60,8 @@ define([ | ||||
|             var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId()), | ||||
|                 keystring = utils.makeKeyString(newStyleObject.identifier); | ||||
|  | ||||
|             this.eventEmitter.emit(keystring + ":*", newStyleObject); | ||||
|             this.eventEmitter.emit(keystring + ':$_synchronize_model', newStyleObject); | ||||
|             this.eventEmitter.emit(keystring + ':*', newStyleObject); | ||||
|             this.eventEmitter.emit('mutation', newStyleObject); | ||||
|         }.bind(this); | ||||
|  | ||||
|   | ||||
| @@ -21,9 +21,11 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     'lodash' | ||||
|     'lodash', | ||||
|     '../objects/MutableDomainObject' | ||||
| ], function ( | ||||
|     _ | ||||
|     _, | ||||
|     MutableDomainObject | ||||
| ) { | ||||
|     /** | ||||
|      * A CompositionCollection represents the list of domain objects contained | ||||
| @@ -60,6 +62,17 @@ define([ | ||||
|         }; | ||||
|         this.onProviderAdd = this.onProviderAdd.bind(this); | ||||
|         this.onProviderRemove = this.onProviderRemove.bind(this); | ||||
|         this.mutables = {}; | ||||
|  | ||||
|         if (this.domainObject instanceof MutableDomainObject.default && | ||||
|             this.publicAPI.objects.isMutable(this.domainObject)) { | ||||
|             this.returnMutables = true; | ||||
|             this.domainObject.$observe('$_destroy', () => { | ||||
|                 Object.values(this.mutables).forEach(mutable => { | ||||
|                     mutable.$destroy(); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -74,9 +87,6 @@ define([ | ||||
|         if (!this.listeners[event]) { | ||||
|             throw new Error('Event not supported by composition: ' + event); | ||||
|         } | ||||
|         if (!this.mutationListener) { | ||||
|             this._synchronize(); | ||||
|         } | ||||
|         if (this.provider.on && this.provider.off) { | ||||
|             if (event === 'add') { | ||||
|                 this.provider.on( | ||||
| @@ -132,8 +142,6 @@ define([ | ||||
|  | ||||
|         this.listeners[event].splice(index, 1); | ||||
|         if (this.listeners[event].length === 0) { | ||||
|             this._destroy(); | ||||
|  | ||||
|             // Remove provider listener if this is the last callback to | ||||
|             // be removed. | ||||
|             if (this.provider.off && this.provider.on) { | ||||
| @@ -182,6 +190,13 @@ define([ | ||||
|             } | ||||
|             this.provider.add(this.domainObject, child.identifier); | ||||
|         } else { | ||||
|             if (this.returnMutables && this.publicAPI.objects.isMutable(child)) { | ||||
|                 let keyString = this.publicAPI.objects.makeKeyString(child.identifier); | ||||
|                 if (this.publicAPI.objects.isMutable(child)) { | ||||
|                     child = this.publicAPI.objects.mutable(child); | ||||
|                     this.mutables[keyString] = child; | ||||
|                 } | ||||
|             } | ||||
|             this.emit('add', child); | ||||
|         } | ||||
|     }; | ||||
| @@ -195,6 +210,7 @@ define([ | ||||
|      * @name load | ||||
|      */ | ||||
|     CompositionCollection.prototype.load = function () { | ||||
|         this.cleanUpMutables(); | ||||
|         return this.provider.load(this.domainObject) | ||||
|             .then(function (children) { | ||||
|                 return Promise.all(children.map((c) => this.publicAPI.objects.get(c))); | ||||
| @@ -225,6 +241,13 @@ define([ | ||||
|         if (!skipMutate) { | ||||
|             this.provider.remove(this.domainObject, child.identifier); | ||||
|         } else { | ||||
|             if (this.returnMutables && this.publicAPI.objects.isMutable(child)) { | ||||
|                 let keyString = this.publicAPI.objects.makeKeyString(child); | ||||
|                 if (this.mutables[keyString] !== undefined) { | ||||
|                     this.mutables[keyString].$destroy(); | ||||
|                     delete this.mutables[keyString]; | ||||
|                 } | ||||
|             } | ||||
|             this.emit('remove', child); | ||||
|         } | ||||
|     }; | ||||
| @@ -271,19 +294,6 @@ define([ | ||||
|         this.remove(child, true); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype._synchronize = function () { | ||||
|         this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => { | ||||
|             this.domainObject = JSON.parse(JSON.stringify(newDomainObject)); | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype._destroy = function () { | ||||
|         if (this.mutationListener) { | ||||
|             this.mutationListener(); | ||||
|             delete this.mutationListener; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Emit events. | ||||
|      * @private | ||||
| @@ -298,5 +308,11 @@ define([ | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype.cleanUpMutables = function () { | ||||
|         Object.values(this.mutables).forEach(mutable => { | ||||
|             mutable.$destroy(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     return CompositionCollection; | ||||
| }); | ||||
|   | ||||
							
								
								
									
										105
									
								
								src/api/objects/MutableDomainObject.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/api/objects/MutableDomainObject.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2019, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| import _ from 'lodash'; | ||||
| import utils from './object-utils.js'; | ||||
|  | ||||
| const ANY_OBJECT_EVENT = 'mutation'; | ||||
|  | ||||
| class MutableDomainObject { | ||||
|     constructor(eventEmitter) { | ||||
|         Object.defineProperties(this, { | ||||
|             _eventEmitter: { | ||||
|                 value: eventEmitter, | ||||
|                 // Property should not be serialized | ||||
|                 enumerable: false | ||||
|             }, | ||||
|             _observers: { | ||||
|                 value: [], | ||||
|                 // Property should not be serialized | ||||
|                 enumerable: false | ||||
|             }, | ||||
|             isMutable: { | ||||
|                 value: true, | ||||
|                 // Property should not be serialized | ||||
|                 enumerable: false | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     $observe(path, callback) { | ||||
|         var fullPath = qualifiedEventName(this, path); | ||||
|         var eventOff = | ||||
|             this._eventEmitter.off.bind(this._eventEmitter, fullPath, callback); | ||||
|  | ||||
|         this._eventEmitter.on(fullPath, callback); | ||||
|         this._observers.push(eventOff); | ||||
|  | ||||
|         return eventOff; | ||||
|     } | ||||
|     $set(path, value) { | ||||
|         _.set(this, path, value); | ||||
|         _.set(this, 'modified', Date.now()); | ||||
|  | ||||
|         //Emit secret synchronization event first, so that all objects are in sync before subsequent events fired. | ||||
|         this._eventEmitter.emit(qualifiedEventName(this, '$_synchronize_model'), this); | ||||
|  | ||||
|         //Emit a general "any object" event | ||||
|         this._eventEmitter.emit(ANY_OBJECT_EVENT, this); | ||||
|         //Emit wildcard event, with path so that callback knows what changed | ||||
|         this._eventEmitter.emit(qualifiedEventName(this, '*'), this, path, value); | ||||
|  | ||||
|         //Emit events specific to properties affected | ||||
|         let parentPropertiesList = path.split('.'); | ||||
|         for (let index = parentPropertiesList.length; index > 0; index--) { | ||||
|             let parentPropertyPath = parentPropertiesList.slice(0, index).join('.'); | ||||
|             this._eventEmitter.emit(qualifiedEventName(this, parentPropertyPath), _.get(this, parentPropertyPath)); | ||||
|         } | ||||
|  | ||||
|         //TODO: Emit events for listeners of child properties when parent changes. | ||||
|         // Do it at observer time - also register observers for parent attribute path. | ||||
|     } | ||||
|     $destroy() { | ||||
|         this._observers.forEach(observer => observer()); | ||||
|         delete this._eventEmitter; | ||||
|         delete this._observers; | ||||
|         this._eventEmitter.emit(qualifiedEventName(this, '$_destroy')); | ||||
|     } | ||||
|  | ||||
|     static createMutable(object, mutationTopic) { | ||||
|         let mutable = Object.create(new MutableDomainObject(mutationTopic)); | ||||
|         Object.assign(mutable, object); | ||||
|         mutable.$observe('$_synchronize_model', (updatedObject) => { | ||||
|             let clone = JSON.parse(JSON.stringify(updatedObject)); | ||||
|             let deleted = _.difference(Object.keys(updatedObject), Object.keys(updatedObject)); | ||||
|             deleted.forEach((propertyName) => delete mutable[propertyName]); | ||||
|             Object.assign(mutable, clone); | ||||
|         }) | ||||
|         return mutable; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function qualifiedEventName(object, eventName) { | ||||
|     var keystring = utils.makeKeyString(object.identifier); | ||||
|  | ||||
|     return [keystring, eventName].join(':'); | ||||
| } | ||||
|  | ||||
| export default MutableDomainObject; | ||||
| @@ -1,102 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './object-utils.js', | ||||
|     'lodash' | ||||
| ], function ( | ||||
|     utils, | ||||
|     _ | ||||
| ) { | ||||
|     var ANY_OBJECT_EVENT = "mutation"; | ||||
|  | ||||
|     /** | ||||
|      * The MutableObject wraps a DomainObject and provides getters and | ||||
|      * setters for | ||||
|      * @param eventEmitter | ||||
|      * @param object | ||||
|      * @interface MutableObject | ||||
|      */ | ||||
|     function MutableObject(eventEmitter, object) { | ||||
|         this.eventEmitter = eventEmitter; | ||||
|         this.object = object; | ||||
|         this.unlisteners = []; | ||||
|     } | ||||
|  | ||||
|     function qualifiedEventName(object, eventName) { | ||||
|         var keystring = utils.makeKeyString(object.identifier); | ||||
|  | ||||
|         return [keystring, eventName].join(':'); | ||||
|     } | ||||
|  | ||||
|     MutableObject.prototype.stopListening = function () { | ||||
|         this.unlisteners.forEach(function (unlisten) { | ||||
|             unlisten(); | ||||
|         }); | ||||
|         this.unlisteners = []; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Observe changes to this domain object. | ||||
|      * @param {string} path the property to observe | ||||
|      * @param {Function} callback a callback to invoke when new values for | ||||
|      *        this property are observed | ||||
|      * @method on | ||||
|      * @memberof module:openmct.MutableObject# | ||||
|      */ | ||||
|     MutableObject.prototype.on = function (path, callback) { | ||||
|         var fullPath = qualifiedEventName(this.object, path); | ||||
|         var eventOff = | ||||
|             this.eventEmitter.off.bind(this.eventEmitter, fullPath, callback); | ||||
|  | ||||
|         this.eventEmitter.on(fullPath, callback); | ||||
|         this.unlisteners.push(eventOff); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Modify this domain object. | ||||
|      * @param {string} path the property to modify | ||||
|      * @param {*} value the new value for this property | ||||
|      * @method set | ||||
|      * @memberof module:openmct.MutableObject# | ||||
|      */ | ||||
|     MutableObject.prototype.set = function (path, value) { | ||||
|         _.set(this.object, path, value); | ||||
|         _.set(this.object, 'modified', Date.now()); | ||||
|  | ||||
|         var handleRecursiveMutation = function (newObject) { | ||||
|             this.object = newObject; | ||||
|         }.bind(this); | ||||
|  | ||||
|         //Emit wildcard event | ||||
|         this.eventEmitter.emit(qualifiedEventName(this.object, '*'), this.object); | ||||
|         //Emit a general "any object" event | ||||
|         this.eventEmitter.emit(ANY_OBJECT_EVENT, this.object); | ||||
|  | ||||
|         this.eventEmitter.on(qualifiedEventName(this.object, '*'), handleRecursiveMutation); | ||||
|         //Emit event specific to property | ||||
|         this.eventEmitter.emit(qualifiedEventName(this.object, path), value); | ||||
|         this.eventEmitter.off(qualifiedEventName(this.object, '*'), handleRecursiveMutation); | ||||
|     }; | ||||
|  | ||||
|     return MutableObject; | ||||
| }); | ||||
| @@ -23,14 +23,14 @@ | ||||
| define([ | ||||
|     'lodash', | ||||
|     './object-utils', | ||||
|     './MutableObject', | ||||
|     './MutableDomainObject', | ||||
|     './RootRegistry', | ||||
|     './RootObjectProvider', | ||||
|     'EventEmitter' | ||||
| ], function ( | ||||
|     _, | ||||
|     utils, | ||||
|     MutableObject, | ||||
|     MutableDomainObject, | ||||
|     RootRegistry, | ||||
|     RootObjectProvider, | ||||
|     EventEmitter | ||||
| @@ -43,7 +43,8 @@ define([ | ||||
|      * @memberof module:openmct | ||||
|      */ | ||||
|  | ||||
|     function ObjectAPI() { | ||||
|     function ObjectAPI(typeRegistry) { | ||||
|         this.typeRegistry = typeRegistry; | ||||
|         this.eventEmitter = new EventEmitter(); | ||||
|         this.providers = {}; | ||||
|         this.rootRegistry = new RootRegistry(); | ||||
| @@ -157,6 +158,19 @@ define([ | ||||
|         return provider.get(identifier); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Will fetch object, returning it as a MutableDomainObject IF the object is mutable. | ||||
|      */ | ||||
|     ObjectAPI.prototype.getAsMutable = function (identifier) { | ||||
|         return this.get(identifier).then((object) => { | ||||
|             if (this.isMutable(object)) { | ||||
|                 return this.mutable(object); | ||||
|             } else { | ||||
|                 return object; | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     ObjectAPI.prototype.delete = function () { | ||||
|         throw new Error('Delete not implemented'); | ||||
|     }; | ||||
| @@ -177,6 +191,20 @@ define([ | ||||
|         this.rootRegistry.addRoot(key); | ||||
|     }; | ||||
|  | ||||
|     ObjectAPI.prototype.mutable = function (object) { | ||||
|         if (!this.isMutable) { | ||||
|             throw `Error: Attempted to create mutable from immutable object ${object.name}`; | ||||
|         } | ||||
|         return MutableDomainObject.default.createMutable(object, this.eventEmitter); | ||||
|     } | ||||
|  | ||||
|     ObjectAPI.prototype.isMutable = function (object) { | ||||
|         // Checking for mutability is a bit broken right now. This is an 80% solution, | ||||
|         // but does not work in many cases. | ||||
|         const type = this.typeRegistry.get(object.type); | ||||
|         return type && type.definition.creatable === true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Modify a domain object. | ||||
|      * @param {module:openmct.DomainObject} object the object to mutate | ||||
| @@ -186,9 +214,17 @@ define([ | ||||
|      * @memberof module:openmct.ObjectAPI# | ||||
|      */ | ||||
|     ObjectAPI.prototype.mutate = function (domainObject, path, value) { | ||||
|         var mutableObject = | ||||
|             new MutableObject(this.eventEmitter, domainObject); | ||||
|         return mutableObject.set(path, value); | ||||
|         if (!this.isMutable(domainObject)) { | ||||
|             throw `Error: Attempted to mutate immutable object ${domainObject.name}`; | ||||
|         } | ||||
|         console.warn('DEPRECATION WARNING: The .mutate() function in the Object API is now deprecated. Please use mutable() '); | ||||
|         if (domainObject instanceof MutableDomainObject.default) { | ||||
|             domainObject.$set(path, value); | ||||
|         } else { | ||||
|             let mutable = this.mutable(domainObject); | ||||
|             mutable.$set(path, value); | ||||
|             mutable.$destroy(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -201,10 +237,14 @@ define([ | ||||
|      * @memberof module:openmct.ObjectAPI# | ||||
|      */ | ||||
|     ObjectAPI.prototype.observe = function (domainObject, path, callback) { | ||||
|         var mutableObject = | ||||
|             new MutableObject(this.eventEmitter, domainObject); | ||||
|         mutableObject.on(path, callback); | ||||
|         return mutableObject.stopListening.bind(mutableObject); | ||||
|         console.warn('DEPRECATION WARNING: The .observe() function in the Object API is now deprecated. Please use mutable() '); | ||||
|         if (domainObject instanceof MutableDomainObject.default) { | ||||
|             return domainObject.$observe(path, callback); | ||||
|         } else { | ||||
|             let mutable = this.mutable(domainObject); | ||||
|             mutable.$observe(path, callback); | ||||
|             return () => mutable.$destroy(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										121
									
								
								src/api/objects/ObjectAPISpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/api/objects/ObjectAPISpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2019, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './ObjectAPI' | ||||
| ], function ( | ||||
|     ObjectAPI | ||||
| ) { | ||||
|     fdescribe('The Object API', function () { | ||||
|         describe('Mutable Object', function () { | ||||
|             let testObject; | ||||
|             let mutable; | ||||
|             let objectAPI; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 objectAPI = new ObjectAPI(); | ||||
|                 testObject = { | ||||
|                     identifier: { | ||||
|                         namespace: 'test-namespace', | ||||
|                         key: 'test-key' | ||||
|                     }, | ||||
|                     otherAttribute: 'other-attribute-value', | ||||
|                     objectAttribute: { | ||||
|                         embeddedObject: { | ||||
|                             embeddedKey: 'embedded-value' | ||||
|                         } | ||||
|                     } | ||||
|                 }; | ||||
|                 mutable = objectAPI.mutable(testObject); | ||||
|             }); | ||||
|  | ||||
|             it('retains own properties', function () { | ||||
|                 expect(mutable.hasOwnProperty('identifier')).toBe(true); | ||||
|                 expect(mutable.hasOwnProperty('otherAttribute')).toBe(true); | ||||
|                 expect(mutable.identifier).toEqual(testObject.identifier); | ||||
|                 expect(mutable.otherAttribute).toEqual(testObject.otherAttribute); | ||||
|             }); | ||||
|  | ||||
|             it('is identical to original object when serialized', function () { | ||||
|                 expect(JSON.stringify(mutable)).toEqual(JSON.stringify(testObject)); | ||||
|             }); | ||||
|  | ||||
|             it('is identical to original object when serialized', function () { | ||||
|                 expect(JSON.stringify(mutable)).toEqual(JSON.stringify(testObject)); | ||||
|             }); | ||||
|  | ||||
|             describe('uses events', function () { | ||||
|                 let testObjectDuplicate; | ||||
|                 let mutableSecondInstance; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     // Duplicate object to guarantee we are not sharing object instance, which would invalidate test | ||||
|                     testObjectDuplicate = JSON.parse(JSON.stringify(testObject)); | ||||
|                     mutableSecondInstance = objectAPI.mutable(testObjectDuplicate); | ||||
|                 }); | ||||
|  | ||||
|                 it('to stay synchronized when mutated', function () { | ||||
|                     mutable.$set('otherAttribute', 'new-attribute-value'); | ||||
|                     expect(mutableSecondInstance.otherAttribute).toBe('new-attribute-value'); | ||||
|                 }); | ||||
|  | ||||
|                 it('to indicate when a property changes', function () { | ||||
|                     let mutationCallback = jasmine.createSpy('mutation-callback'); | ||||
|  | ||||
|                     return new Promise(function (resolve) { | ||||
|                         mutationCallback.and.callFake(resolve); | ||||
|                         mutableSecondInstance.observe('otherAttribute', mutationCallback); | ||||
|                         mutable.$set('otherAttribute', 'some-new-value') | ||||
|                     }).then(function () { | ||||
|                         expect(mutationCallback).toHaveBeenCalledWith('some-new-value'); | ||||
|                     }); | ||||
|                 }); | ||||
|  | ||||
|                 it('to indicate when a child property has changed', function () { | ||||
|                     let embeddedKeyCallback = jasmine.createSpy('embeddedKeyCallback'); | ||||
|                     let embeddedObjectCallback = jasmine.createSpy('embeddedObjectCallback'); | ||||
|                     let objectAttributeCallback = jasmine.createSpy('objectAttribute'); | ||||
|  | ||||
|                     return new Promise(function (resolve) { | ||||
|                         objectAttributeCallback.and.callFake(resolve); | ||||
|  | ||||
|                         mutableSecondInstance.observe('objectAttribute.embeddedObject.embeddedKey', embeddedKeyCallback); | ||||
|                         mutableSecondInstance.observe('objectAttribute.embeddedObject', embeddedObjectCallback); | ||||
|                         mutableSecondInstance.observe('objectAttribute', objectAttributeCallback); | ||||
|  | ||||
|                         mutable.$set('objectAttribute.embeddedObject.embeddedKey', 'updated-embedded-value'); | ||||
|                     }).then(function () { | ||||
|                         expect(embeddedKeyCallback).toHaveBeenCalledWith('updated-embedded-value'); | ||||
|                         expect(embeddedObjectCallback).toHaveBeenCalledWith({ | ||||
|                             embeddedKey: 'updated-embedded-value' | ||||
|                         }); | ||||
|                         expect(objectAttributeCallback).toHaveBeenCalledWith({ | ||||
|                             embeddedObject: { | ||||
|                                 embeddedKey: 'updated-embedded-value' | ||||
|                             } | ||||
|                         }); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     }) | ||||
| }); | ||||
| @@ -23,7 +23,7 @@ | ||||
|  | ||||
| <template> | ||||
|     <tr @contextmenu.prevent="showContextMenu"> | ||||
|         <td>{{name}}</td> | ||||
|         <td>{{domainObject.name}}</td> | ||||
|         <td>{{timestamp}}</td> | ||||
|         <td :class="valueClass"> | ||||
|             {{value}} | ||||
| @@ -50,7 +50,6 @@ export default { | ||||
|         currentObjectPath.unshift(this.domainObject); | ||||
|  | ||||
|         return { | ||||
|             name: this.domainObject.name, | ||||
|             timestamp: '---', | ||||
|             value: '---', | ||||
|             valueClass: '', | ||||
| @@ -70,9 +69,6 @@ export default { | ||||
|                 this.valueClass = ''; | ||||
|             } | ||||
|         }, | ||||
|         updateName(name){ | ||||
|             this.name = name; | ||||
|         }, | ||||
|         updateTimeSystem(timeSystem) { | ||||
|             this.value = '---'; | ||||
|             this.timestamp = '---'; | ||||
| @@ -98,14 +94,6 @@ export default { | ||||
|             .telemetry | ||||
|             .limitEvaluator(this.domainObject); | ||||
|  | ||||
|         this.stopWatchingMutation = openmct | ||||
|             .objects | ||||
|             .observe( | ||||
|                 this.domainObject, | ||||
|                 '*', | ||||
|                 this.updateName | ||||
|             ); | ||||
|  | ||||
|         this.openmct.time.on('timeSystem', this.updateTimeSystem); | ||||
|  | ||||
|         this.timestampKey = this.openmct.time.timeSystem().key; | ||||
| @@ -126,7 +114,6 @@ export default { | ||||
|             .then((array) => this.updateValues(array[array.length - 1])); | ||||
|     }, | ||||
|     destroyed() { | ||||
|         this.stopWatchingMutation(); | ||||
|         this.unsubscribe(); | ||||
|         this.openmct.off('timeSystem', this.updateTimeSystem); | ||||
|     } | ||||
|   | ||||
| @@ -176,9 +176,8 @@ | ||||
|  | ||||
|     export default { | ||||
|         data() { | ||||
|             let domainObject = JSON.parse(JSON.stringify(this.domainObject)); | ||||
|             return { | ||||
|                 internalDomainObject: domainObject, | ||||
|                 internalDomainObject: this.domainObject, | ||||
|                 initSelectIndex: undefined, | ||||
|                 selection: [] | ||||
|             }; | ||||
| @@ -566,9 +565,6 @@ | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
|             this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', function (obj) { | ||||
|                 this.internalDomainObject = JSON.parse(JSON.stringify(obj)); | ||||
|             }.bind(this)); | ||||
|             this.openmct.selection.on('change', this.setSelection); | ||||
|             this.initializeItems(); | ||||
|             this.composition = this.openmct.composition.get(this.internalDomainObject); | ||||
| @@ -580,7 +576,6 @@ | ||||
|             this.openmct.selection.off('change', this.setSelection); | ||||
|             this.composition.off('add', this.addChild); | ||||
|             this.composition.off('remove', this.removeChild); | ||||
|             this.unlisten(); | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|   | ||||
| @@ -113,10 +113,13 @@ | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
|             this.openmct.objects.get(this.item.identifier) | ||||
|             this.openmct.objects.getAsMutable(this.item.identifier) | ||||
|                 .then(this.setObject); | ||||
|         }, | ||||
|         destroyed() { | ||||
|             if (this.domainObject.$destroy) { | ||||
|                 this.domainObject.$destroy(); | ||||
|             } | ||||
|             if (this.removeSelectable) { | ||||
|                 this.removeSelectable(); | ||||
|             } | ||||
|   | ||||
| @@ -248,12 +248,13 @@ | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
|             this.openmct.objects.get(this.item.identifier) | ||||
|             this.openmct.objects.getAsMutable(this.item.identifier) | ||||
|                 .then(this.setObject); | ||||
|             this.openmct.time.on("bounds", this.refreshData); | ||||
|         }, | ||||
|         destroyed() { | ||||
|             this.removeSubscription(); | ||||
|             this.domainObject.$destroy(); | ||||
|  | ||||
|             if (this.removeSelectable) { | ||||
|                 this.removeSelectable(); | ||||
|   | ||||
| @@ -152,24 +152,25 @@ | ||||
|             }, | ||||
|             getGlobalFiltersToRemove(keyString) { | ||||
|                 let filtersToRemove = new Set(); | ||||
|                 if (this.children[keyString]){ | ||||
|                     this.children[keyString].metadataWithFilters.forEach(metadatum => { | ||||
|                         let keepFilter = false | ||||
|                         Object.keys(this.children).forEach(childKeyString => { | ||||
|                             if (childKeyString !== keyString) { | ||||
|                                 let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key); | ||||
|  | ||||
|                 this.children[keyString].metadataWithFilters.forEach(metadatum => { | ||||
|                     let keepFilter = false | ||||
|                     Object.keys(this.children).forEach(childKeyString => { | ||||
|                         if (childKeyString !== keyString) { | ||||
|                             let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key); | ||||
|  | ||||
|                             if (filterMatched) { | ||||
|                                 keepFilter = true; | ||||
|                                 return; | ||||
|                                 if (filterMatched) { | ||||
|                                     keepFilter = true; | ||||
|                                     return; | ||||
|                                 } | ||||
|                             } | ||||
|                         }); | ||||
|  | ||||
|                         if (!keepFilter) { | ||||
|                             filtersToRemove.add(metadatum.key); | ||||
|                         } | ||||
|                     }); | ||||
|  | ||||
|                     if (!keepFilter) { | ||||
|                         filtersToRemove.add(metadatum.key); | ||||
|                     } | ||||
|                 }); | ||||
|                 } | ||||
|  | ||||
|                 return Array.from(filtersToRemove); | ||||
|             }, | ||||
| @@ -234,16 +235,14 @@ | ||||
|             this.composition.on('add', this.addChildren); | ||||
|             this.composition.on('remove', this.removeChildren); | ||||
|             this.composition.load(); | ||||
|             this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters); | ||||
|             this.unobserveGlobalFilters = this.openmct.objects.observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters); | ||||
|             this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject); | ||||
|             this.unobserve = this.providedObject.$observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters); | ||||
|             this.unobserveGlobalFilters = this.providedObject.$observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters); | ||||
|         }, | ||||
|         beforeDestroy() { | ||||
|             this.composition.off('add', this.addChildren); | ||||
|             this.composition.off('remove', this.removeChildren); | ||||
|             this.unobserve(); | ||||
|             this.unobserveGlobalFilters(); | ||||
|             this.unobserveAllMutation(); | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|   | ||||
| @@ -629,9 +629,6 @@ export default { | ||||
|                 return size; | ||||
|             } | ||||
|         }, | ||||
|         updateDomainObject(newDomainObject) { | ||||
|             this.domainObject = newDomainObject; | ||||
|         }, | ||||
|         moveContainer(toIndex, event) { | ||||
|             let containerId = event.dataTransfer.getData('containerid'); | ||||
|             let container = this.containers.filter(c => c.id === containerId)[0]; | ||||
| @@ -664,14 +661,10 @@ export default { | ||||
|         this.composition.on('add', this.addFrame); | ||||
|  | ||||
|         this.RemoveAction = new RemoveAction(this.openmct); | ||||
|  | ||||
|         this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.composition.off('remove', this.removeChildObject); | ||||
|         this.composition.off('add', this.addFrame); | ||||
|  | ||||
|         this.unobserve(); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -108,7 +108,7 @@ export default { | ||||
|     }, | ||||
|     mounted() { | ||||
|         if (this.frame.domainObjectIdentifier) { | ||||
|             this.openmct.objects.get(this.frame.domainObjectIdentifier).then((object)=>{ | ||||
|             this.openmct.objects.getAsMutable(this.frame.domainObjectIdentifier).then((object)=>{ | ||||
|                 this.setDomainObject(object); | ||||
|             }); | ||||
|         } | ||||
| @@ -116,6 +116,10 @@ export default { | ||||
|         this.dragGhost = document.getElementById('js-fl-drag-ghost'); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         if (this.domainObject.$destroy) { | ||||
|             this.domainObject.$destroy(); | ||||
|         } | ||||
|  | ||||
|         if (this.unsubscribeSelection) { | ||||
|             this.unsubscribeSelection(); | ||||
|         } | ||||
|   | ||||
| @@ -34,11 +34,8 @@ define([ | ||||
|             this.columns = {}; | ||||
|  | ||||
|             this.removeColumnsForObject = this.removeColumnsForObject.bind(this); | ||||
|             this.objectMutated = this.objectMutated.bind(this); | ||||
|             //Make copy of configuration, otherwise change detection is impossible if shared instance is being modified. | ||||
|             this.oldConfiguration = JSON.parse(JSON.stringify(this.getConfiguration())); | ||||
|  | ||||
|             this.unlistenFromMutation = openmct.objects.observe(domainObject, '*', this.objectMutated); | ||||
|             this.unlistenFromMutation = domainObject.$observe('configuration', configuration => this.updateListeners(configuration)); | ||||
|         } | ||||
|  | ||||
|         getConfiguration() { | ||||
| @@ -60,15 +57,8 @@ define([ | ||||
|          * @private | ||||
|          * @param {*} object | ||||
|          */ | ||||
|         objectMutated(object) { | ||||
|             //Synchronize domain object reference. Duplicate object otherwise change detection becomes impossible. | ||||
|             this.domainObject = object; | ||||
|             //Was it the configuration that changed? | ||||
|             if (object.configuration !== undefined && !_.eq(object.configuration, this.oldConfiguration)) { | ||||
|                 //Make copy of configuration, otherwise change detection is impossible if shared instance is being modified. | ||||
|                 this.oldConfiguration = JSON.parse(JSON.stringify(this.getConfiguration())); | ||||
|                 this.emit('change', object.configuration); | ||||
|             } | ||||
|         updateListeners(configuration) { | ||||
|             this.emit('change', configuration); | ||||
|         } | ||||
|  | ||||
|         addSingleColumnForObject(telemetryObject, column, position) { | ||||
|   | ||||
| @@ -23,11 +23,13 @@ | ||||
| define( | ||||
|     [ | ||||
|         'EventEmitter', | ||||
|         'lodash' | ||||
|         'lodash', | ||||
|         '../api/objects/MutableDomainObject.js' | ||||
|     ], | ||||
|     function ( | ||||
|         EventEmitter, | ||||
|         _ | ||||
|         _, | ||||
|         MutableDomainObject | ||||
|     ) { | ||||
|  | ||||
|         /** | ||||
| @@ -75,6 +77,10 @@ define( | ||||
|                 this.selected = [selectable]; | ||||
|             } | ||||
|  | ||||
|             if (this.temporaryMutables) { | ||||
|                 this.temporaryMutables.forEach(mutable => mutable.$destroy()); | ||||
|             } | ||||
|  | ||||
|             this.emit('change', this.selected); | ||||
|         }; | ||||
|  | ||||
| @@ -233,12 +239,6 @@ define( | ||||
|             element.addEventListener('click', capture, true); | ||||
|             element.addEventListener('click', selectCapture); | ||||
|  | ||||
|             if (context.item) { | ||||
|                 var unlisten = this.openmct.objects.observe(context.item, "*", function (newItem) { | ||||
|                     context.item = newItem; | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             if (select) { | ||||
|                 element.click(); | ||||
|             } | ||||
| @@ -246,10 +246,6 @@ define( | ||||
|             return function () { | ||||
|                 element.removeEventListener('click', capture, true); | ||||
|                 element.removeEventListener('click', selectCapture); | ||||
|  | ||||
|                 if (unlisten) { | ||||
|                     unlisten(); | ||||
|                 } | ||||
|             }; | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -68,12 +68,6 @@ export default { | ||||
|         }; | ||||
|     }, | ||||
|     mounted() { | ||||
|         if (this.observedObject) { | ||||
|             let removeListener = this.openmct.objects.observe(this.observedObject, '*', (newObject) => { | ||||
|                 this.observedObject = newObject; | ||||
|             }); | ||||
|             this.$once('hook:destroyed', removeListener); | ||||
|         } | ||||
|         this.previewAction = new PreviewAction(this.openmct); | ||||
|     }, | ||||
|     computed: { | ||||
|   | ||||
| @@ -59,10 +59,6 @@ export default { | ||||
|                 delete this.removeSelectable; | ||||
|             } | ||||
|  | ||||
|             if (this.composition) { | ||||
|                 this.composition._destroy(); | ||||
|             } | ||||
|  | ||||
|             this.openmct.objectViews.off('clearData', this.clearData); | ||||
|         }, | ||||
|         invokeEditModeHandler(editMode) { | ||||
| @@ -80,7 +76,6 @@ export default { | ||||
|              | ||||
|             this.composition = this.openmct.composition.get(this.currentObject); | ||||
|             if (this.composition) { | ||||
|                 this.composition._synchronize(); | ||||
|                 this.loadComposition(); | ||||
|             } | ||||
|  | ||||
| @@ -130,20 +125,17 @@ export default { | ||||
|                 delete this.removeSelectable; | ||||
|             } | ||||
|  | ||||
|             if (this.composition) { | ||||
|                 this.composition._destroy(); | ||||
|             } | ||||
|  | ||||
|             this.currentObject = object; | ||||
|  | ||||
|             this.composition = this.openmct.composition.get(this.currentObject); | ||||
|             if (this.composition) { | ||||
|                 this.loadComposition(); | ||||
|             } | ||||
|  | ||||
|             if (currentObjectPath) { | ||||
|                 this.currentObjectPath = currentObjectPath; | ||||
|             } | ||||
|  | ||||
|             this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => { | ||||
|                 this.currentObject = mutatedObject; | ||||
|             }); | ||||
|  | ||||
|             this.viewKey = viewKey; | ||||
|             this.updateView(immediatelySelect); | ||||
|         }, | ||||
|   | ||||
| @@ -118,9 +118,6 @@ export default { | ||||
|             } | ||||
|  | ||||
|             if (this.parentObject) { | ||||
|                 this.mutationUnobserver = this.openmct.objects.observe(this.parentObject, '*', (updatedModel) => { | ||||
|                     this.parentObject = updatedModel; | ||||
|                 }); | ||||
|                 this.composition = this.openmct.composition.get(this.parentObject); | ||||
|  | ||||
|                 if (this.composition) { | ||||
| @@ -141,8 +138,7 @@ export default { | ||||
|         }, | ||||
|         addElement(element) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(element.identifier); | ||||
|             this.elementsCache[keyString] =  | ||||
|                 JSON.parse(JSON.stringify(element)); | ||||
|             this.elementsCache[keyString] = element; | ||||
|             this.applySearch(this.currentSearch); | ||||
|         }, | ||||
|         reorderElements() { | ||||
| @@ -182,9 +178,6 @@ export default { | ||||
|         this.openmct.editor.off('isEditing', this.setEditState); | ||||
|         this.openmct.selection.off('change', this.showSelection); | ||||
|  | ||||
|         if (this.mutationUnobserver) { | ||||
|             this.mutationUnobserver(); | ||||
|         } | ||||
|         if (this.compositionUnlistener) { | ||||
|             this.compositionUnlistener(); | ||||
|         } | ||||
|   | ||||
| @@ -77,8 +77,9 @@ const PLACEHOLDER_OBJECT = {}; | ||||
|                 this.showSaveMenu = false; | ||||
|             }, | ||||
|             updateName(event) { | ||||
|                 // TODO: handle isssues with contenteditable text escaping. | ||||
|                 if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) { | ||||
|                     this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText); | ||||
|                     this.domainObject.$set('name', event.target.innerText); | ||||
|                 } else { | ||||
|                     event.target.innerText = this.domainObject.name; | ||||
|                 } | ||||
| @@ -223,20 +224,7 @@ const PLACEHOLDER_OBJECT = {}; | ||||
|                 this.isEditing = isEditing; | ||||
|             }); | ||||
|         }, | ||||
|         watch: { | ||||
|             domainObject() { | ||||
|                 if (this.mutationObserver) { | ||||
|                     this.mutationObserver(); | ||||
|                 } | ||||
|                 this.mutationObserver = this.openmct.objects.observe(this.domainObject, '*', (domainObject) => { | ||||
|                     this.domainObject = domainObject; | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         beforeDestroy: function () { | ||||
|             if (this.mutationObserver) { | ||||
|                 this.mutationObserver(); | ||||
|             } | ||||
|             document.removeEventListener('click', this.closeViewAndSaveMenu); | ||||
|             window.removeEventListener('click', this.promptUserbeforeNavigatingAway); | ||||
|         } | ||||
|   | ||||
| @@ -207,6 +207,7 @@ | ||||
|             getAllChildren() { | ||||
|                 this.isLoading = true; | ||||
|                 this.openmct.objects.get('ROOT') | ||||
|                     .then(root => this.openmct.objects.mutable(root)) | ||||
|                     .then(root => { | ||||
|                         return this.openmct.composition.get(root).load() | ||||
|                     }) | ||||
| @@ -223,8 +224,18 @@ | ||||
|                     }); | ||||
|             }, | ||||
|             getFilteredChildren() { | ||||
|                 if (this.filteredTreeItems) { | ||||
|                     this.filteredTreeItems.forEach(filteredTreeItem => filteredTreeItem.destroy()); | ||||
|                 } | ||||
|  | ||||
|                 this.searchService.query(this.searchValue).then(children => { | ||||
|                     this.filteredTreeItems = children.hits.map(child => { | ||||
|                     this.filteredTreeItems = children.hits | ||||
|                         .map(child => {  | ||||
|                             if (this.openmct.objects.isMutable(child)) { | ||||
|                                 this.openmct.objects.mutable(child); | ||||
|                             } | ||||
|                         }) | ||||
|                         .map(child => { | ||||
|                          | ||||
|                         let context = child.object.getCapability('context'), | ||||
|                             object = child.object.useCapability('adapter'), | ||||
|   | ||||
| @@ -66,11 +66,7 @@ | ||||
|             // TODO: set isAlias per tree-item | ||||
|  | ||||
|             this.domainObject = this.node.object; | ||||
|             let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => { | ||||
|                 this.domainObject = newObject; | ||||
|             }); | ||||
|  | ||||
|             this.$once('hook:destroyed', removeListener); | ||||
|             if (this.openmct.composition.get(this.node.object)) { | ||||
|                 this.hasChildren = true; | ||||
|             } | ||||
| @@ -82,6 +78,7 @@ | ||||
|             if (this.composition) { | ||||
|                 this.composition.off('add', this.addChild); | ||||
|                 this.composition.off('remove', this.removeChild); | ||||
|                 this.children.forEach(child => child.object.$destroy()); | ||||
|                 delete this.composition; | ||||
|             } | ||||
|         }, | ||||
| @@ -101,6 +98,9 @@ | ||||
|         }, | ||||
|         methods: { | ||||
|             addChild (child) { | ||||
|                 if (this.openmct.objects.isMutable(child)) { | ||||
|                     child = this.openmct.objects.mutable(child); | ||||
|                 } | ||||
|                 this.children.push({ | ||||
|                     id: this.openmct.objects.makeKeyString(child.identifier), | ||||
|                     object: child, | ||||
| @@ -110,8 +110,16 @@ | ||||
|             }, | ||||
|             removeChild(identifier) { | ||||
|                 let removeId = this.openmct.objects.makeKeyString(identifier); | ||||
|                 let removed = []; | ||||
|                 this.children = this.children | ||||
|                     .filter(c => c.id !== removeId); | ||||
|                     .filter(c => { | ||||
|                         if(c.id !== removeId) { | ||||
|                             removed.push(c); | ||||
|                             return true | ||||
|                         } | ||||
|                         return false; | ||||
|                     }); | ||||
|                 removed.forEach(removedChild => removedChild.object.$destroy()); | ||||
|             }, | ||||
|             finishLoading () { | ||||
|                 this.isLoading = false; | ||||
|   | ||||
| @@ -7,13 +7,14 @@ define([ | ||||
|     return function install(openmct) { | ||||
|         let navigateCall = 0; | ||||
|         let browseObject; | ||||
|         let unobserve = undefined; | ||||
|         let mutable; | ||||
|         let currentObjectPath; | ||||
|  | ||||
|         openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot); | ||||
|  | ||||
|         openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => { | ||||
|             let navigatePath = results[1]; | ||||
|             clearMutationListeners(); | ||||
|             navigateToPath(navigatePath, params.view); | ||||
|         }); | ||||
|  | ||||
| @@ -27,10 +28,17 @@ define([ | ||||
|         }); | ||||
|  | ||||
|         function viewObject(object, viewProvider) { | ||||
|             if (mutable) { | ||||
|                 mutable.$destroy(); | ||||
|                 mutable = undefined; | ||||
|             } | ||||
|             if (openmct.objects.isMutable(object)) { | ||||
|                 mutable = openmct.objects.mutable(object); | ||||
|             } | ||||
|             currentObjectPath = openmct.router.path; | ||||
|  | ||||
|             openmct.layout.$refs.browseObject.show(object, viewProvider.key, true, currentObjectPath); | ||||
|             openmct.layout.$refs.browseBar.domainObject = object; | ||||
|             openmct.layout.$refs.browseObject.show(mutable || object, viewProvider.key, true, currentObjectPath); | ||||
|             openmct.layout.$refs.browseBar.domainObject = mutable || object; | ||||
|             openmct.layout.$refs.browseBar.viewKey = viewProvider.key; | ||||
|         } | ||||
|  | ||||
| @@ -38,37 +46,26 @@ define([ | ||||
|             navigateCall++; | ||||
|             let currentNavigation = navigateCall; | ||||
|  | ||||
|             if (unobserve) { | ||||
|                 unobserve(); | ||||
|                 unobserve = undefined; | ||||
|             } | ||||
|  | ||||
|             //Split path into object identifiers | ||||
|             if (!Array.isArray(path)) { | ||||
|                 path = path.split('/'); | ||||
|             } | ||||
|  | ||||
|             return pathToObjects(path).then((objects)=>{ | ||||
|             return pathToObjects(path).then((objects) => { | ||||
|                 if (currentNavigation !== navigateCall) { | ||||
|                     return; // Prevent race. | ||||
|                 } | ||||
|  | ||||
|                 let navigatedObject = objects[objects.length - 1]; | ||||
|  | ||||
|                 // FIXME: this is a hack to support create action, intended to | ||||
|                 // expose the current routed path.  We need to rewrite the | ||||
|                 // navigation service and router to expose a clear and minimal | ||||
|                 // API for this. | ||||
|                 openmct.router.path = objects.reverse(); | ||||
|                 objects = objects.reverse(); | ||||
|                 openmct.router.path = objects; | ||||
|  | ||||
|                 unobserve = this.openmct.objects.observe(openmct.router.path[0], '*', (newObject) => { | ||||
|                     openmct.router.path[0] = newObject; | ||||
|                 }); | ||||
|                 browseObject = objects[0]; | ||||
|                 openmct.layout.$refs.browseBar.domainObject = browseObject; | ||||
|  | ||||
|                 openmct.layout.$refs.browseBar.domainObject = navigatedObject; | ||||
|                 browseObject = navigatedObject; | ||||
|  | ||||
|                 if (!navigatedObject) { | ||||
|                 if (!browseObject) { | ||||
|                     openmct.layout.$refs.browseObject.clear(); | ||||
|                     return; | ||||
|                 } | ||||
| @@ -78,12 +75,12 @@ define([ | ||||
|  | ||||
|                 document.title = browseObject.name; //change document title to current object in main view | ||||
|  | ||||
|                 if (currentProvider && currentProvider.canView(navigatedObject)) { | ||||
|                     viewObject(navigatedObject,  currentProvider); | ||||
|                 if (currentProvider && currentProvider.canView(browseObject)) { | ||||
|                     viewObject(browseObject,  currentProvider); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 let defaultProvider = openmct.objectViews.get(navigatedObject)[0]; | ||||
|                 let defaultProvider = openmct.objectViews.get(browseObject)[0]; | ||||
|                 if (defaultProvider) { | ||||
|                     openmct.router.updateParams({ | ||||
|                         view: defaultProvider.key | ||||
| @@ -99,7 +96,7 @@ define([ | ||||
|  | ||||
|         function pathToObjects(path) { | ||||
|             return Promise.all(path.map((keyString)=>{ | ||||
|                 return openmct.objects.get(keyString); | ||||
|                 return openmct.objects.getAsMutable(keyString); | ||||
|             })); | ||||
|         } | ||||
|  | ||||
| @@ -117,5 +114,15 @@ define([ | ||||
|                     }); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         function clearMutationListeners() { | ||||
|             if (openmct.router.path !== undefined) { | ||||
|                 openmct.router.path.forEach((pathObject) => { | ||||
|                     if (pathObject.$destroy) { | ||||
|                         pathObject.$destroy(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -81,8 +81,8 @@ | ||||
|                 } | ||||
|             }, | ||||
|             observeObject(domainObject, id) { | ||||
|                 let unobserveObject = this.openmct.objects.observe(domainObject, '*', function(newObject) { | ||||
|                     this.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject)); | ||||
|                 let unobserveObject = domainObject.$observe('*', function(newObject) { | ||||
|                     this.domainObjectsById[id].newObject = newObject; | ||||
|                     this.updateToolbarAfterMutation(); | ||||
|                 }.bind(this)); | ||||
|                 this.unObserveObjects.push(unobserveObject); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user