mirror of
https://github.com/nasa/openmct.git
synced 2025-06-17 14:48:13 +00:00
* [Composition] provide ids, sync via mutation Composition provides ids, and we sync things via mutation. This simplifies the composition provider interface some, and also fixes some issues with the previous default composition provider related to #1322 fixes #1253 * [Style] Fix style, update jsdoc Fix style, update jsdoc, clean up composition api changes for Fixes #1322 * [Style] Tidy and JSDoc * [Composition] Utilize new composition API Ensures that composition provided by new API works in old API. Some functionality is not present in both places, but for the time being this is sufficient. https://github.com/nasa/openmct/pull/1332 * [Utils] add tests, fix bugs Add tests to objectUtils to ensure correctness. This caught a bug where character escapes were not properly applied or removed. As a result, any missing object that contained a colon in it's key would cause an infinite loop and cause the application to crash. Bug discovered in VISTA integration. * [Style] Fix style * [Roots] Depend on new api for ROOT Depend on new API for ROOT model, ensuring consistency when fetching ROOT model. * [Style] Remove commented code
This commit is contained in:
committed by
Andrew Henry
parent
79b4f9a0f4
commit
73b3ae7264
@ -21,20 +21,26 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'EventEmitter',
|
||||
'lodash',
|
||||
'../objects/object-utils'
|
||||
], function (
|
||||
EventEmitter,
|
||||
_,
|
||||
objectUtils
|
||||
) {
|
||||
|
||||
|
||||
/**
|
||||
* A CompositionCollection represents the list of domain objects contained
|
||||
* by another domain object. It provides methods for loading this
|
||||
* list asynchronously, and for modifying this list.
|
||||
* list asynchronously, modifying this list, and listening for changes to
|
||||
* this list.
|
||||
*
|
||||
* Usage:
|
||||
* ```javascript
|
||||
* var myViewComposition = MCT.composition.get(myViewObject);
|
||||
* myViewComposition.on('add', addObjectToView);
|
||||
* myViewComposition.on('remove', removeObjectFromView);
|
||||
* myViewComposition.load(); // will trigger `add` for all loaded objects.
|
||||
* ```
|
||||
*
|
||||
* @interface CompositionCollection
|
||||
* @param {module:openmct.DomainObject} domainObject the domain object
|
||||
@ -44,20 +50,42 @@ define([
|
||||
* @param {module:openmct.CompositionAPI} api the composition API, for
|
||||
* policy checks
|
||||
* @memberof module:openmct
|
||||
* @augments EventEmitter
|
||||
*/
|
||||
function CompositionCollection(domainObject, provider, api) {
|
||||
EventEmitter.call(this);
|
||||
function CompositionCollection(domainObject, provider, publicAPI) {
|
||||
this.domainObject = domainObject;
|
||||
this.provider = provider;
|
||||
this.api = api;
|
||||
if (this.provider.on) {
|
||||
this.publicAPI = publicAPI;
|
||||
this.listeners = {
|
||||
add: [],
|
||||
remove: [],
|
||||
load: []
|
||||
};
|
||||
this.onProviderAdd = this.onProviderAdd.bind(this);
|
||||
this.onProviderRemove = this.onProviderRemove.bind(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listen for changes to this composition. Supports 'add', 'remove', and
|
||||
* 'load' events.
|
||||
*
|
||||
* @param event event to listen for, either 'add', 'remove' or 'load'.
|
||||
* @param callback to trigger when event occurs.
|
||||
* @param [context] context to use when invoking callback, optional.
|
||||
*/
|
||||
CompositionCollection.prototype.on = function (event, callback, context) {
|
||||
if (!this.listeners[event]) {
|
||||
throw new Error('Event not supported by composition: ' + event);
|
||||
}
|
||||
|
||||
if (event === 'add') {
|
||||
this.provider.on(
|
||||
this.domainObject,
|
||||
'add',
|
||||
this.onProviderAdd,
|
||||
this
|
||||
);
|
||||
} if (event === 'remove') {
|
||||
this.provider.on(
|
||||
this.domainObject,
|
||||
'remove',
|
||||
@ -65,62 +93,55 @@ define([
|
||||
this
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CompositionCollection.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
CompositionCollection.prototype.onProviderAdd = function (child) {
|
||||
this.add(child, true);
|
||||
};
|
||||
|
||||
CompositionCollection.prototype.onProviderRemove = function (child) {
|
||||
this.remove(child, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the index of a domain object within this composition. If the
|
||||
* domain object is not contained here, -1 will be returned.
|
||||
*
|
||||
* A call to [load]{@link module:openmct.CompositionCollection#load}
|
||||
* must have resolved before using this method.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} child the domain object for which
|
||||
* an index should be retrieved
|
||||
* @returns {number} the index of that domain object
|
||||
* @memberof module:openmct.CompositionCollection#
|
||||
* @name indexOf
|
||||
*/
|
||||
CompositionCollection.prototype.indexOf = function (child) {
|
||||
return _.findIndex(this.loadedChildren, function (other) {
|
||||
return objectUtils.equals(child, other);
|
||||
this.listeners[event].push({
|
||||
callback: callback,
|
||||
context: context
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the index of a domain object within this composition.
|
||||
* Remove a listener. Must be called with same exact parameters as
|
||||
* `off`.
|
||||
*
|
||||
* A call to [load]{@link module:openmct.CompositionCollection#load}
|
||||
* must have resolved before using this method.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} child the domain object for which
|
||||
* containment should be checked
|
||||
* @returns {boolean} true if the domain object is contained here
|
||||
* @memberof module:openmct.CompositionCollection#
|
||||
* @name contains
|
||||
* @param event
|
||||
* @param callback
|
||||
* @param [context]
|
||||
*/
|
||||
CompositionCollection.prototype.contains = function (child) {
|
||||
return this.indexOf(child) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a domain object can be added to this composition.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} child the domain object to add
|
||||
* @memberof module:openmct.CompositionCollection#
|
||||
* @name canContain
|
||||
*/
|
||||
CompositionCollection.prototype.canContain = function (domainObject) {
|
||||
return this.api.checkPolicy(this.domainObject, domainObject);
|
||||
CompositionCollection.prototype.off = function (event, callback, context) {
|
||||
if (!this.listeners[event]) {
|
||||
throw new Error('Event not supported by composition: ' + event);
|
||||
}
|
||||
|
||||
var index = _.findIndex(this.listeners[event], function (l) {
|
||||
return l.callback === callback && l.context === context;
|
||||
});
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error('Tried to remove a listener that does not exist');
|
||||
}
|
||||
|
||||
this.listeners[event].splice(index, 1);
|
||||
if (this.listeners[event].length === 0) {
|
||||
// Remove provider listener if this is the last callback to
|
||||
// be removed.
|
||||
if (event === 'add') {
|
||||
this.provider.off(
|
||||
this.domainObject,
|
||||
'add',
|
||||
this.onProviderAdd,
|
||||
this
|
||||
);
|
||||
} else if (event === 'remove') {
|
||||
this.provider.off(
|
||||
this.domainObject,
|
||||
'remove',
|
||||
this.onProviderRemove,
|
||||
this
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -136,23 +157,10 @@ define([
|
||||
* @name add
|
||||
*/
|
||||
CompositionCollection.prototype.add = function (child, skipMutate) {
|
||||
if (!this.loadedChildren) {
|
||||
throw new Error("Must load composition before you can add!");
|
||||
}
|
||||
if (!this.canContain(child)) {
|
||||
throw new Error("This object cannot contain that object.");
|
||||
}
|
||||
if (this.contains(child)) {
|
||||
if (skipMutate) {
|
||||
return; // don't add twice, don't error.
|
||||
}
|
||||
throw new Error("Unable to add child: already in composition");
|
||||
}
|
||||
this.loadedChildren.push(child);
|
||||
this.emit('add', child);
|
||||
if (!skipMutate) {
|
||||
// add after we have added.
|
||||
this.provider.add(this.domainObject, child);
|
||||
this.provider.add(this.domainObject, child.identifier);
|
||||
} else {
|
||||
this.emit('add', child);
|
||||
}
|
||||
};
|
||||
|
||||
@ -167,12 +175,11 @@ define([
|
||||
CompositionCollection.prototype.load = function () {
|
||||
return this.provider.load(this.domainObject)
|
||||
.then(function (children) {
|
||||
this.loadedChildren = [];
|
||||
children.map(function (c) {
|
||||
this.add(c, true);
|
||||
}, this);
|
||||
return Promise.all(children.map(this.onProviderAdd, this));
|
||||
}.bind(this))
|
||||
.then(function (children) {
|
||||
this.emit('load');
|
||||
return this.loadedChildren.slice();
|
||||
return children;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
@ -189,42 +196,44 @@ define([
|
||||
* @name remove
|
||||
*/
|
||||
CompositionCollection.prototype.remove = function (child, skipMutate) {
|
||||
if (!this.contains(child)) {
|
||||
if (skipMutate) {
|
||||
return;
|
||||
}
|
||||
throw new Error("Unable to remove child: not found in composition");
|
||||
}
|
||||
var index = this.indexOf(child);
|
||||
var removed = this.loadedChildren.splice(index, 1)[0];
|
||||
this.emit('remove', index, child);
|
||||
if (!skipMutate) {
|
||||
// trigger removal after we have internally removed it.
|
||||
this.provider.remove(this.domainObject, removed);
|
||||
this.provider.remove(this.domainObject, child.identifier);
|
||||
} else {
|
||||
this.emit('remove', child);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop using this composition collection. This will release any resources
|
||||
* associated with this collection.
|
||||
* @name destroy
|
||||
* @memberof module:openmct.CompositionCollection#
|
||||
* Handle adds from provider.
|
||||
* @private
|
||||
*/
|
||||
CompositionCollection.prototype.destroy = function () {
|
||||
if (this.provider.off) {
|
||||
this.provider.off(
|
||||
this.domainObject,
|
||||
'add',
|
||||
this.onProviderAdd,
|
||||
this
|
||||
);
|
||||
this.provider.off(
|
||||
this.domainObject,
|
||||
'remove',
|
||||
this.onProviderRemove,
|
||||
this
|
||||
);
|
||||
}
|
||||
CompositionCollection.prototype.onProviderAdd = function (childId) {
|
||||
return this.publicAPI.objects.get(childId).then(function (child) {
|
||||
this.add(child, true);
|
||||
return child;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle removal from provider.
|
||||
* @private
|
||||
*/
|
||||
CompositionCollection.prototype.onProviderRemove = function (child) {
|
||||
this.remove(child, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit events.
|
||||
* @private
|
||||
*/
|
||||
CompositionCollection.prototype.emit = function (event, payload) {
|
||||
this.listeners[event].forEach(function (l) {
|
||||
if (l.context) {
|
||||
l.callback.call(l.context, payload);
|
||||
} else {
|
||||
l.callback(payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return CompositionCollection;
|
||||
|
Reference in New Issue
Block a user