Compare commits
17 Commits
misc-fixes
...
alpha-enum
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fef060501 | ||
|
|
c38d810658 | ||
|
|
f5c48b7bf6 | ||
|
|
d0e08f1d9a | ||
|
|
72ea7b80fd | ||
|
|
35d0c02bc5 | ||
|
|
abd7506b45 | ||
|
|
526b4aa07e | ||
|
|
b5e23963d4 | ||
|
|
2c11eb90d4 | ||
|
|
90e9c79e19 | ||
|
|
1b83631e43 | ||
|
|
547d4e82db | ||
|
|
3377ad5e0d | ||
|
|
1c0df60f05 | ||
|
|
138067dca9 | ||
|
|
844280eaa5 |
@@ -86,6 +86,7 @@
|
||||
openmct.install(openmct.plugins.LADTable());
|
||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||
openmct.install(openmct.plugins.ObjectMigration());
|
||||
openmct.install(openmct.plugins.GoToOriginalAction());
|
||||
openmct.start();
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"node-bourbon": "^4.2.3",
|
||||
"node-sass": "^4.9.2",
|
||||
"painterro": "^0.2.65",
|
||||
"printj": "^1.1.0",
|
||||
"printj": "^1.2.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"request": "^2.69.0",
|
||||
"split": "^1.0.0",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
ng-model="ngModel"
|
||||
ng-show="ngModel.progressPerc !== undefined"></mct-include>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-overlay__button-bar">
|
||||
<button ng-repeat="dialogOption in ngModel.options"
|
||||
class="c-button"
|
||||
@@ -22,4 +23,3 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,6 @@ define(
|
||||
* @returns boolean
|
||||
*/
|
||||
EditorCapability.prototype.inEditContext = function () {
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
@@ -74,7 +73,6 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.isEditContextRoot = function () {
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ define([
|
||||
"./src/actions/MoveAction",
|
||||
"./src/actions/CopyAction",
|
||||
"./src/actions/LinkAction",
|
||||
"./src/actions/GoToOriginalAction",
|
||||
"./src/actions/SetPrimaryLocationAction",
|
||||
"./src/services/LocatingCreationDecorator",
|
||||
"./src/services/LocatingObjectDecorator",
|
||||
@@ -41,7 +40,6 @@ define([
|
||||
MoveAction,
|
||||
CopyAction,
|
||||
LinkAction,
|
||||
GoToOriginalAction,
|
||||
SetPrimaryLocationAction,
|
||||
LocatingCreationDecorator,
|
||||
LocatingObjectDecorator,
|
||||
@@ -104,14 +102,6 @@ define([
|
||||
"linkService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "follow",
|
||||
"name": "Go To Original",
|
||||
"description": "Go to the original, un-linked instance of this object.",
|
||||
"cssClass": "",
|
||||
"category": "contextual",
|
||||
"implementation": GoToOriginalAction
|
||||
},
|
||||
{
|
||||
"key": "locate",
|
||||
"name": "Set Primary Location",
|
||||
|
||||
@@ -1,60 +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(
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Implements the "Go To Original" action, which follows a link back
|
||||
* to an original instance of an object.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @constructor
|
||||
* @private
|
||||
* @memberof platform/entanglement
|
||||
* @param {ActionContext} context the context in which the action
|
||||
* will be performed
|
||||
*/
|
||||
function GoToOriginalAction(context) {
|
||||
this.domainObject = context.domainObject;
|
||||
}
|
||||
|
||||
GoToOriginalAction.prototype.perform = function () {
|
||||
return this.domainObject.getCapability("location").getOriginal()
|
||||
.then(function (originalObject) {
|
||||
var actionCapability =
|
||||
originalObject.getCapability("action");
|
||||
return actionCapability &&
|
||||
actionCapability.perform("navigate");
|
||||
});
|
||||
};
|
||||
|
||||
GoToOriginalAction.appliesTo = function (context) {
|
||||
var domainObject = context.domainObject;
|
||||
return domainObject && domainObject.hasCapability("location") &&
|
||||
domainObject.getCapability("location").isLink();
|
||||
};
|
||||
|
||||
return GoToOriginalAction;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,93 +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(
|
||||
[
|
||||
'../../src/actions/GoToOriginalAction',
|
||||
'../DomainObjectFactory',
|
||||
'../ControlledPromise'
|
||||
],
|
||||
function (GoToOriginalAction, domainObjectFactory, ControlledPromise) {
|
||||
|
||||
describe("The 'go to original' action", function () {
|
||||
var testContext,
|
||||
originalDomainObject,
|
||||
mockLocationCapability,
|
||||
mockOriginalActionCapability,
|
||||
originalPromise,
|
||||
action;
|
||||
|
||||
beforeEach(function () {
|
||||
mockLocationCapability = jasmine.createSpyObj(
|
||||
'location',
|
||||
['isLink', 'isOriginal', 'getOriginal']
|
||||
);
|
||||
mockOriginalActionCapability = jasmine.createSpyObj(
|
||||
'action',
|
||||
['perform', 'getActions']
|
||||
);
|
||||
originalPromise = new ControlledPromise();
|
||||
mockLocationCapability.getOriginal.and.returnValue(originalPromise);
|
||||
mockLocationCapability.isLink.and.returnValue(true);
|
||||
mockLocationCapability.isOriginal.and.callFake(function () {
|
||||
return !mockLocationCapability.isLink();
|
||||
});
|
||||
testContext = {
|
||||
domainObject: domainObjectFactory({
|
||||
capabilities: {
|
||||
location: mockLocationCapability
|
||||
}
|
||||
})
|
||||
};
|
||||
originalDomainObject = domainObjectFactory({
|
||||
capabilities: {
|
||||
action: mockOriginalActionCapability
|
||||
}
|
||||
});
|
||||
|
||||
action = new GoToOriginalAction(testContext);
|
||||
});
|
||||
|
||||
it("is applicable to links", function () {
|
||||
expect(GoToOriginalAction.appliesTo(testContext))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it("is not applicable to originals", function () {
|
||||
mockLocationCapability.isLink.and.returnValue(false);
|
||||
expect(GoToOriginalAction.appliesTo(testContext))
|
||||
.toBeFalsy();
|
||||
});
|
||||
|
||||
it("navigates to original objects when performed", function () {
|
||||
expect(mockOriginalActionCapability.perform)
|
||||
.not.toHaveBeenCalled();
|
||||
action.perform();
|
||||
originalPromise.resolve(originalDomainObject);
|
||||
expect(mockOriginalActionCapability.perform)
|
||||
.toHaveBeenCalledWith('navigate');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -80,15 +80,17 @@ define(['zepto'], function ($) {
|
||||
var newObj;
|
||||
|
||||
seen.push(parent.getId());
|
||||
parentModel.composition.forEach(function (childId, index) {
|
||||
if (!tree[childId] || seen.includes(childId)) {
|
||||
|
||||
parentModel.composition.forEach(function (childId) {
|
||||
let keystring = this.openmct.objects.makeKeyString(childId);
|
||||
|
||||
if (!tree[keystring] || seen.includes(keystring)) {
|
||||
return;
|
||||
}
|
||||
|
||||
newObj = this.instantiate(tree[childId], childId);
|
||||
parent.getCapability("composition").add(newObj);
|
||||
newObj = this.instantiate(tree[keystring], keystring);
|
||||
newObj.getCapability("location")
|
||||
.setPrimaryLocation(tree[childId].location);
|
||||
.setPrimaryLocation(tree[keystring].location);
|
||||
this.deepInstantiate(newObj, tree, seen);
|
||||
}, this);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ define([
|
||||
'./runs/RegisterLegacyTypes',
|
||||
'./services/LegacyObjectAPIInterceptor',
|
||||
'./views/installLegacyViews',
|
||||
'./policies/legacyCompositionPolicyAdapter',
|
||||
'./policies/LegacyCompositionPolicyAdapter',
|
||||
'./actions/LegacyActionAdapter'
|
||||
], function (
|
||||
legacyRegistry,
|
||||
|
||||
@@ -45,22 +45,27 @@ define([
|
||||
view: function (domainObject) {
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new();
|
||||
let scope = $rootScope.$new(true);
|
||||
let legacyObject = convertToLegacyObject(domainObject);
|
||||
let isDestroyed = false;
|
||||
let unlistenToStatus;
|
||||
let element;
|
||||
scope.domainObject = legacyObject;
|
||||
scope.model = legacyObject.getModel();
|
||||
|
||||
let child;
|
||||
let parent;
|
||||
|
||||
return {
|
||||
show: function (container) {
|
||||
parent = container;
|
||||
child = document.createElement('div');
|
||||
parent.appendChild(child);
|
||||
let statusCapability = legacyObject.getCapability('status');
|
||||
unlistenToStatus = statusCapability.listen((newStatus) => {
|
||||
container.classList.remove('s-status-timeconductor-unsynced');
|
||||
child.classList.remove('s-status-timeconductor-unsynced');
|
||||
|
||||
if (newStatus.includes('timeconductor-unsynced')) {
|
||||
container.classList.add('s-status-timeconductor-unsynced');
|
||||
child.classList.add('s-status-timeconductor-unsynced');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -84,12 +89,13 @@ define([
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
element = openmct.$angular.element(child);
|
||||
templateLinker.link(
|
||||
scope,
|
||||
openmct.$angular.element(container),
|
||||
element,
|
||||
legacyView
|
||||
);
|
||||
container.classList.add('u-contents');
|
||||
child.classList.add('u-contents');
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
@@ -103,7 +109,11 @@ define([
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
element.off();
|
||||
element.remove();
|
||||
scope.$destroy();
|
||||
element = null;
|
||||
scope = null;
|
||||
unlistenToStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,15 +41,18 @@ define([
|
||||
let domainObject = selection[0][0].context.item;
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new();
|
||||
let scope = $rootScope.$new(true);
|
||||
let legacyObject = convertToLegacyObject(domainObject);
|
||||
let isDestroyed = false;
|
||||
let element;
|
||||
scope.domainObject = legacyObject;
|
||||
scope.model = legacyObject.getModel();
|
||||
|
||||
|
||||
return {
|
||||
show: function (container) {
|
||||
let child = document.createElement('div');
|
||||
container.appendChild(child);
|
||||
// TODO: implement "gestures" support ?
|
||||
let uses = representation.uses || [];
|
||||
let promises = [];
|
||||
@@ -70,9 +73,10 @@ define([
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
element = openmct.$angular.element(child)
|
||||
templateLinker.link(
|
||||
scope,
|
||||
openmct.$angular.element(container),
|
||||
element,
|
||||
representation
|
||||
);
|
||||
container.style.height = '100%';
|
||||
@@ -89,7 +93,11 @@ define([
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
element.off();
|
||||
element.remove();
|
||||
scope.$destroy();
|
||||
element = null;
|
||||
scope = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,20 @@ define([
|
||||
publicAPI = {};
|
||||
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
|
||||
'get',
|
||||
'mutate'
|
||||
'mutate',
|
||||
'observe',
|
||||
'areIdsEqual'
|
||||
]);
|
||||
|
||||
publicAPI.objects.areIdsEqual.and.callFake(function (id1, id2) {
|
||||
return id1.namespace === id2.namespace && id1.key === id2.key;
|
||||
});
|
||||
|
||||
publicAPI.composition = jasmine.createSpyObj('CompositionAPI', [
|
||||
'checkPolicy'
|
||||
]);
|
||||
publicAPI.composition.checkPolicy.and.returnValue(true);
|
||||
|
||||
publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [
|
||||
'on'
|
||||
]);
|
||||
@@ -119,49 +131,16 @@ define([
|
||||
expect(newComposition[2].key).toEqual('a');
|
||||
})
|
||||
});
|
||||
|
||||
// TODO: Implement add/removal in new default provider.
|
||||
xit('synchronizes changes between instances', function () {
|
||||
var otherComposition = compositionAPI.get(domainObject);
|
||||
var addListener = jasmine.createSpy('addListener');
|
||||
var removeListener = jasmine.createSpy('removeListener');
|
||||
var otherAddListener = jasmine.createSpy('otherAddListener');
|
||||
var otherRemoveListener = jasmine.createSpy('otherRemoveListener');
|
||||
it('supports adding an object to composition', function () {
|
||||
let addListener = jasmine.createSpy('addListener');
|
||||
let mockChildObject = {
|
||||
identifier: {key: 'mock-key', namespace: ''}
|
||||
};
|
||||
composition.on('add', addListener);
|
||||
composition.on('remove', removeListener);
|
||||
otherComposition.on('add', otherAddListener);
|
||||
otherComposition.on('remove', otherRemoveListener);
|
||||
composition.add(mockChildObject);
|
||||
|
||||
return Promise.all([composition.load(), otherComposition.load()])
|
||||
.then(function () {
|
||||
expect(addListener).toHaveBeenCalled();
|
||||
expect(otherAddListener).toHaveBeenCalled();
|
||||
expect(removeListener).not.toHaveBeenCalled();
|
||||
expect(otherRemoveListener).not.toHaveBeenCalled();
|
||||
|
||||
var object = addListener.calls.mostRecent().args[0];
|
||||
composition.remove(object);
|
||||
expect(removeListener).toHaveBeenCalled();
|
||||
expect(otherRemoveListener).toHaveBeenCalled();
|
||||
|
||||
addListener.reset();
|
||||
otherAddListener.reset();
|
||||
composition.add(object);
|
||||
expect(addListener).toHaveBeenCalled();
|
||||
expect(otherAddListener).toHaveBeenCalled();
|
||||
|
||||
removeListener.reset();
|
||||
otherRemoveListener.reset();
|
||||
otherComposition.remove(object);
|
||||
expect(removeListener).toHaveBeenCalled();
|
||||
expect(otherRemoveListener).toHaveBeenCalled();
|
||||
|
||||
addListener.reset();
|
||||
otherAddListener.reset();
|
||||
otherComposition.add(object);
|
||||
expect(addListener).toHaveBeenCalled();
|
||||
expect(otherAddListener).toHaveBeenCalled();
|
||||
});
|
||||
expect(domainObject.composition.length).toBe(4);
|
||||
expect(domainObject.composition[3]).toEqual(mockChildObject.identifier);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -184,7 +163,9 @@ define([
|
||||
key: 'thing'
|
||||
}
|
||||
]);
|
||||
}
|
||||
},
|
||||
add: jasmine.createSpy('add'),
|
||||
remove: jasmine.createSpy('remove')
|
||||
};
|
||||
domainObject = {
|
||||
identifier: {
|
||||
@@ -214,6 +195,25 @@ define([
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Calling add or remove', function () {
|
||||
let mockChildObject;
|
||||
|
||||
beforeEach(function () {
|
||||
mockChildObject = {
|
||||
identifier: {key: 'mock-key', namespace: ''}
|
||||
};
|
||||
composition.add(mockChildObject);
|
||||
});
|
||||
|
||||
it('calls add on the provider', function () {
|
||||
expect(customProvider.add).toHaveBeenCalledWith(domainObject, mockChildObject.identifier);
|
||||
});
|
||||
|
||||
it('calls remove on the provider', function () {
|
||||
composition.remove(mockChildObject);
|
||||
expect(customProvider.remove).toHaveBeenCalledWith(domainObject, mockChildObject.identifier);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic custom composition', function () {
|
||||
|
||||
@@ -75,9 +75,7 @@ define([
|
||||
throw new Error('Event not supported by composition: ' + event);
|
||||
}
|
||||
if (!this.mutationListener) {
|
||||
this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
|
||||
this.domainObject = newDomainObject;
|
||||
})
|
||||
this._synchronize();
|
||||
}
|
||||
if (this.provider.on && this.provider.off) {
|
||||
if (event === 'add') {
|
||||
@@ -134,10 +132,8 @@ define([
|
||||
|
||||
this.listeners[event].splice(index, 1);
|
||||
if (this.listeners[event].length === 0) {
|
||||
if (this.mutationListener) {
|
||||
this.mutationListener();
|
||||
delete this.mutationListener;
|
||||
}
|
||||
this._destroy();
|
||||
|
||||
// Remove provider listener if this is the last callback to
|
||||
// be removed.
|
||||
if (this.provider.off && this.provider.on) {
|
||||
@@ -181,6 +177,9 @@ define([
|
||||
*/
|
||||
CompositionCollection.prototype.add = function (child, skipMutate) {
|
||||
if (!skipMutate) {
|
||||
if (!this.publicAPI.composition.checkPolicy(this.domainObject, child)) {
|
||||
throw `Object of type ${child.type} cannot be added to object of type ${this.domainObject.type}`;
|
||||
}
|
||||
this.provider.add(this.domainObject, child.identifier);
|
||||
} else {
|
||||
this.emit('add', child);
|
||||
@@ -272,6 +271,19 @@ 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
|
||||
|
||||
@@ -48,24 +48,11 @@ define([
|
||||
this.listeningTo = {};
|
||||
this.onMutation = this.onMutation.bind(this);
|
||||
|
||||
this.cannotContainDuplicates = this.cannotContainDuplicates.bind(this);
|
||||
this.cannotContainItself = this.cannotContainItself.bind(this);
|
||||
|
||||
compositionAPI.addPolicy(this.cannotContainDuplicates);
|
||||
compositionAPI.addPolicy(this.cannotContainItself);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
DefaultCompositionProvider.prototype.cannotContainDuplicates = function (parent, child) {
|
||||
return this.appliesTo(parent) &&
|
||||
parent.composition.findIndex((composeeId) => {
|
||||
return composeeId.namespace === child.identifier.namespace &&
|
||||
composeeId.key === child.identifier.key;
|
||||
}) === -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -199,9 +186,18 @@ define([
|
||||
* @memberof module:openmct.CompositionProvider#
|
||||
* @method add
|
||||
*/
|
||||
DefaultCompositionProvider.prototype.add = function (domainObject, child) {
|
||||
throw new Error('Default Provider does not implement adding.');
|
||||
// TODO: this needs to be synchronized via mutation
|
||||
DefaultCompositionProvider.prototype.add = function (parent, childId) {
|
||||
if (!this.includes(parent, childId)) {
|
||||
parent.composition.push(childId);
|
||||
this.publicAPI.objects.mutate(parent, 'composition', parent.composition);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
DefaultCompositionProvider.prototype.includes = function (parent, childId) {
|
||||
return parent.composition.findIndex(composee =>
|
||||
this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
|
||||
};
|
||||
|
||||
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
height: 0; // Chrome 73 overflow bug fix
|
||||
overflow: auto;
|
||||
padding-right: $interiorMargin; // fend off scroll bar
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./components/LadTable.vue',
|
||||
'./components/LADTable.vue',
|
||||
'vue'
|
||||
], function (
|
||||
LadTableComponent,
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<script>
|
||||
import lodash from 'lodash';
|
||||
import LadRow from './LadRow.vue';
|
||||
import LadRow from './LADRow.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<script>
|
||||
import lodash from 'lodash';
|
||||
import LadRow from './LadRow.vue';
|
||||
import LadRow from './LADRow.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
|
||||
77
src/plugins/displayLayout/AlphanumericFormatViewProvider.js
Normal file
77
src/plugins/displayLayout/AlphanumericFormatViewProvider.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/*****************************************************************************
|
||||
* 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([
|
||||
'./components/AlphanumericFormatView.vue',
|
||||
'vue'
|
||||
], function (AlphanumericFormatView, Vue) {
|
||||
|
||||
function AlphanumericFormatViewProvider(openmct, options) {
|
||||
function isTelemetryObject(selectionPath) {
|
||||
let selectedObject = selectionPath[0].context.item;
|
||||
let parentObject = selectionPath[1].context.item;
|
||||
return parentObject &&
|
||||
parentObject.type === 'layout' &&
|
||||
selectedObject &&
|
||||
openmct.telemetry.isTelemetryObject(selectedObject) &&
|
||||
!options.showAsView.includes(selectedObject.type)
|
||||
}
|
||||
|
||||
return {
|
||||
key: 'alphanumeric-format',
|
||||
name: 'Alphanumeric Format',
|
||||
canView: function (selection) {
|
||||
if (selection.length === 0 || selection[0].length === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return selection.every(isTelemetryObject);
|
||||
},
|
||||
view: function (selection) {
|
||||
let component;
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
provide: {
|
||||
openmct
|
||||
},
|
||||
components: {
|
||||
AlphanumericFormatView: AlphanumericFormatView.default
|
||||
},
|
||||
template: '<alphanumeric-format-view></alphanumeric-format-view>',
|
||||
el: element
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
priority: function () {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AlphanumericFormatViewProvider;
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-properties" v-if="isEditing">
|
||||
<div class="c-properties__header">Alphanumeric Format</div>
|
||||
<ul class="c-properties__section" v-if="!multiSelect">
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label" title="Printf formatting for the selected telemetry">
|
||||
<label for="telemetryPrintfFormat">Format</label>
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
<input id="telemetryPrintfFormat" type="text" @change="formatTelemetry" :value="telemetryFormat">
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-properties__row--span-all" v-if="multiSelect">No format to display for multiple items</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
let selectionPath = this.openmct.selection.get()[0];
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
telemetryFormat: selectionPath[0].context.layoutItem.format,
|
||||
multiSelect: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
formatTelemetry(event) {
|
||||
let selectionPath = this.openmct.selection.get()[0];
|
||||
let newFormat = event.currentTarget.value;
|
||||
selectionPath[0].context.updateTelemetryFormat(newFormat);
|
||||
this.telemetryFormat = newFormat;
|
||||
},
|
||||
handleSelection(selection) {
|
||||
if (selection.length === 0 || selection[0].length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.multiSelect = selection.length > 1 ? true : false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
this.openmct.selection.on('change', this.handleSelection);
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.openmct.selection.off('change', this.handleSelection);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -48,7 +48,8 @@
|
||||
:multiSelect="selectedLayoutItems.length > 1"
|
||||
@move="move"
|
||||
@endMove="endMove"
|
||||
@endLineResize='endLineResize'>
|
||||
@endLineResize='endLineResize'
|
||||
@formatChanged='updateTelemetryFormat'>
|
||||
</component>
|
||||
<edit-marquee v-if='showMarquee'
|
||||
:gridSize="gridSize"
|
||||
@@ -269,33 +270,63 @@
|
||||
_.cloneDeep(this.selectedLayoutItems).forEach(selectedItem => {
|
||||
if (selectedItem.type === 'line-view') {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y, selectedItem.x2, selectedItem.y2];
|
||||
this.startingMinX2 = this.startingMinX2 !== undefined ? Math.min(this.startingMinX2, selectedItem.x2) : selectedItem.x2;
|
||||
this.startingMinY2 = this.startingMinY2 !== undefined ? Math.min(this.startingMinY2, selectedItem.y2) : selectedItem.y2;
|
||||
} else {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y];
|
||||
}
|
||||
|
||||
this.startingMinX = this.startingMinX !== undefined ? Math.min(this.startingMinX, selectedItem.x) : selectedItem.x;
|
||||
this.startingMinY = this.startingMinY !== undefined ? Math.min(this.startingMinY, selectedItem.y) : selectedItem.y;
|
||||
});
|
||||
}
|
||||
|
||||
let layoutItems = this.layoutItems.map(item => {
|
||||
if (this.initialPositions[item.id]) {
|
||||
let startingPosition = this.initialPositions[item.id];
|
||||
let [startingX, startingY, startingX2, startingY2] = startingPosition;
|
||||
item.x = startingX + gridDelta[0];
|
||||
item.y = startingY + gridDelta[1];
|
||||
|
||||
if (item.x2) {
|
||||
item.x2 = startingX2 + gridDelta[0];
|
||||
}
|
||||
|
||||
if (item.y2) {
|
||||
item.y2 = startingY2 + gridDelta[1];
|
||||
}
|
||||
this.updateItemPosition(item, gridDelta);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
},
|
||||
updateItemPosition(item, gridDelta) {
|
||||
let startingPosition = this.initialPositions[item.id];
|
||||
let [startingX, startingY, startingX2, startingY2] = startingPosition;
|
||||
|
||||
if (this.startingMinX + gridDelta[0] >= 0) {
|
||||
if (item.x2 !== undefined) {
|
||||
if (this.startingMinX2 + gridDelta[0] >= 0) {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
} else {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.startingMinY + gridDelta[1] >= 0) {
|
||||
if (item.y2 !== undefined) {
|
||||
if (this.startingMinY2 + gridDelta[1] >= 0) {
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
} else {
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (item.x2 !== undefined && this.startingMinX2 + gridDelta[0] >= 0 && this.startingMinX + gridDelta[0] >= 0) {
|
||||
item.x2 = startingX2 + gridDelta[0];
|
||||
}
|
||||
|
||||
if (item.y2 !== undefined && this.startingMinY2 + gridDelta[1] >= 0 && this.startingMinY + gridDelta[1] >= 0) {
|
||||
item.y2 = startingY2 + gridDelta[1];
|
||||
}
|
||||
},
|
||||
endMove() {
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
this.initialPositions = undefined;
|
||||
this.startingMinX = undefined;
|
||||
this.startingMinY = undefined;
|
||||
this.startingMinX2 = undefined;
|
||||
this.startingMinY2 = undefined;
|
||||
},
|
||||
mutate(path, value) {
|
||||
this.openmct.objects.mutate(this.internalDomainObject, path, value);
|
||||
@@ -527,6 +558,11 @@
|
||||
this.layoutItems.splice(itemIndex, 1);
|
||||
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
|
||||
}
|
||||
},
|
||||
updateTelemetryFormat(item, format) {
|
||||
let index = this.layoutItems.indexOf(item);
|
||||
item.format = format;
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
let height = Number.NEGATIVE_INFINITY;
|
||||
|
||||
this.selectedLayoutItems.forEach(item => {
|
||||
if (item.x2) {
|
||||
if (item.x2 !== undefined) {
|
||||
let lineWidth = Math.abs(item.x - item.x2);
|
||||
let lineMinX = Math.min(item.x, item.x2);
|
||||
x = Math.min(lineMinX, x);
|
||||
@@ -126,7 +126,7 @@
|
||||
width = Math.max(item.width + item.x, width);
|
||||
}
|
||||
|
||||
if (item.y2) {
|
||||
if (item.y2 !== undefined) {
|
||||
let lineHeight = Math.abs(item.y - item.y2);
|
||||
let lineMinY = Math.min(item.y, item.y2);
|
||||
y = Math.min(lineMinY, y);
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import printj from 'printj'
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||
DEFAULT_POSITION = [1, 1];
|
||||
@@ -143,6 +144,10 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.item.format) {
|
||||
return printj.sprintf(this.item.format, this.datum[this.valueMetadata.key]);
|
||||
}
|
||||
|
||||
return this.valueFormatter && this.valueFormatter.format(this.datum);
|
||||
},
|
||||
telemetryClass() {
|
||||
@@ -168,6 +173,9 @@
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
},
|
||||
item(newItem) {
|
||||
this.context.layoutItem = newItem;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -194,6 +202,7 @@
|
||||
}.bind(this));
|
||||
},
|
||||
updateView(datum) {
|
||||
// TODO: normalize datum
|
||||
this.datum = datum;
|
||||
},
|
||||
removeSubscription() {
|
||||
@@ -219,10 +228,14 @@
|
||||
this.context = {
|
||||
item: domainObject,
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
index: this.index,
|
||||
updateTelemetryFormat: this.updateTelemetryFormat
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
updateTelemetryFormat(format) {
|
||||
this.$emit('formatChanged', this.item, format);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -25,6 +25,8 @@ import Vue from 'vue'
|
||||
import objectUtils from '../../api/objects/object-utils.js'
|
||||
import DisplayLayoutType from './DisplayLayoutType.js'
|
||||
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'
|
||||
import AlphaNumericFormatViewProvider from './AlphaNumericFormatViewProvider.js'
|
||||
|
||||
export default function DisplayLayoutPlugin(options) {
|
||||
return function (openmct) {
|
||||
openmct.objectViews.addProvider({
|
||||
@@ -76,7 +78,8 @@ export default function DisplayLayoutPlugin(options) {
|
||||
}
|
||||
});
|
||||
openmct.types.addType('layout', DisplayLayoutType());
|
||||
openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct));
|
||||
openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct, options));
|
||||
openmct.inspectorViews.addProvider(new AlphaNumericFormatViewProvider(openmct, options));
|
||||
openmct.composition.addPolicy((parent, child) => {
|
||||
if (parent.type === 'layout' && child.type === 'folder') {
|
||||
return false;
|
||||
|
||||
53
src/plugins/goToOriginalAction/goToOriginalAction.js
Normal file
53
src/plugins/goToOriginalAction/goToOriginalAction.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class GoToOriginalAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Go To Original';
|
||||
this.description = 'Go to the original unlinked instance of this object';
|
||||
|
||||
this._openmct = openmct;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
this._openmct.objects.getOriginalPath(objectPath[0].identifier)
|
||||
.then((originalPath) => {
|
||||
let url = '#/browse/' + originalPath
|
||||
.map(function (o) {
|
||||
return o && this._openmct.objects.makeKeyString(o.identifier);
|
||||
}.bind(this))
|
||||
.reverse()
|
||||
.slice(1)
|
||||
.join('/');
|
||||
|
||||
window.location.href = url;
|
||||
});
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
let parentKeystring = objectPath[1] && this._openmct.objects.makeKeyString(objectPath[1].identifier);
|
||||
|
||||
if (!parentKeystring) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (parentKeystring !== objectPath[0].location);
|
||||
}
|
||||
}
|
||||
28
src/plugins/goToOriginalAction/plugin.js
Normal file
28
src/plugins/goToOriginalAction/plugin.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
import GoToOriginalAction from './goToOriginalAction';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.contextMenu.registerAction(new GoToOriginalAction(openmct));
|
||||
};
|
||||
}
|
||||
@@ -64,35 +64,45 @@ define([
|
||||
Object.keys(panels).forEach(key => {
|
||||
let panel = panels[key];
|
||||
let domainObject = childObjects[key];
|
||||
let identifier = undefined;
|
||||
|
||||
if (isTelemetry(domainObject)) {
|
||||
items.push({
|
||||
width: panel.dimensions[0],
|
||||
height: panel.dimensions[1],
|
||||
x: panel.position[0],
|
||||
y: panel.position[1],
|
||||
identifier: domainObject.identifier,
|
||||
id: uuid(),
|
||||
type: 'telemetry-view',
|
||||
displayMode: 'all',
|
||||
value: openmct.telemetry.getMetadata(domainObject).getDefaultDisplayValue(),
|
||||
stroke: "transparent",
|
||||
fill: "",
|
||||
color: "",
|
||||
size: "13px"
|
||||
// If object is a telemetry point, convert it to a plot and
|
||||
// replace the object in migratedObject composition with the plot.
|
||||
identifier = {
|
||||
key: uuid(),
|
||||
namespace: migratedObject.identifier.namespace
|
||||
};
|
||||
let plotObject = {
|
||||
identifier: identifier,
|
||||
location: domainObject.location,
|
||||
name: domainObject.name,
|
||||
type: "telemetry.plot.overlay"
|
||||
};
|
||||
let plotType = openmct.types.get('telemetry.plot.overlay');
|
||||
plotType.definition.initialize(plotObject);
|
||||
plotObject.composition.push(domainObject.identifier);
|
||||
openmct.objects.mutate(plotObject, 'persisted', Date.now());
|
||||
|
||||
let keyString = openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let clonedComposition = Object.assign([], migratedObject.composition);
|
||||
clonedComposition.forEach((identifier, index) => {
|
||||
if (openmct.objects.makeKeyString(identifier) === keyString) {
|
||||
migratedObject.composition[index] = plotObject.identifier;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
}
|
||||
|
||||
items.push({
|
||||
width: panel.dimensions[0],
|
||||
height: panel.dimensions[1],
|
||||
x: panel.position[0],
|
||||
y: panel.position[1],
|
||||
identifier: domainObject.identifier,
|
||||
identifier: identifier || domainObject.identifier,
|
||||
id: uuid(),
|
||||
type: 'subobject-view',
|
||||
hasFrame: panel.hasFrame
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
migratedObject.configuration.items = items;
|
||||
@@ -167,7 +177,9 @@ define([
|
||||
return [
|
||||
{
|
||||
check(domainObject) {
|
||||
return domainObject.type === 'layout' && domainObject.configuration.layout;
|
||||
return domainObject.type === 'layout' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration.layout;
|
||||
},
|
||||
migrate(domainObject) {
|
||||
let childObjects = {};
|
||||
@@ -186,7 +198,9 @@ define([
|
||||
},
|
||||
{
|
||||
check(domainObject) {
|
||||
return domainObject.type === 'telemetry.fixed' && domainObject.configuration['fixed-display'];
|
||||
return domainObject.type === 'telemetry.fixed' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration['fixed-display'];
|
||||
},
|
||||
migrate(domainObject) {
|
||||
const DEFAULT_GRID_SIZE = [64, 16];
|
||||
@@ -224,6 +238,7 @@ define([
|
||||
{
|
||||
check(domainObject) {
|
||||
return domainObject.type === 'table' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration.table;
|
||||
},
|
||||
migrate(domainObject) {
|
||||
|
||||
@@ -115,11 +115,13 @@ define([
|
||||
|
||||
Collection.prototype.remove = function (model) {
|
||||
var index = this.indexOf(model);
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error('model not found in collection.');
|
||||
}
|
||||
this.models.splice(index, 1);
|
||||
|
||||
this.emit('remove', model, index);
|
||||
this.models.splice(index, 1);
|
||||
};
|
||||
|
||||
Collection.prototype.destroy = function (model) {
|
||||
|
||||
@@ -100,20 +100,34 @@ define([
|
||||
removeTelemetryObject: function (identifier) {
|
||||
var plotObject = this.plot.get('domainObject');
|
||||
if (plotObject.type === 'telemetry.plot.overlay') {
|
||||
var index = _.findIndex(plotObject.configuration.series, function (s) {
|
||||
|
||||
var persistedIndex = _.findIndex(plotObject.configuration.series, function (s) {
|
||||
return _.isEqual(identifier, s.identifier);
|
||||
});
|
||||
this.remove(this.at(index));
|
||||
|
||||
var configIndex = _.findIndex(this.models, function (m) {
|
||||
return _.isEqual(m.domainObject.identifier, identifier);
|
||||
});
|
||||
|
||||
/*
|
||||
when cancelling out of edit mode, the config store and domain object are out of sync
|
||||
thus it is necesarry to check both and remove the models that are no longer in composition
|
||||
*/
|
||||
if (persistedIndex === -1) {
|
||||
this.remove(this.at(configIndex));
|
||||
} else {
|
||||
this.remove(this.at(persistedIndex));
|
||||
// Because this is triggered by a composition change, we have
|
||||
// to defer mutation of our plot object, otherwise we might
|
||||
// mutate an outdated version of the plotObject.
|
||||
setTimeout(function () {
|
||||
var newPlotObject = this.plot.get('domainObject');
|
||||
var cSeries = newPlotObject.configuration.series.slice();
|
||||
cSeries.splice(index, 1);
|
||||
cSeries.splice(persistedIndex, 1);
|
||||
this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
},
|
||||
onSeriesAdd: function (series) {
|
||||
var seriesColor = series.get('color');
|
||||
|
||||
@@ -25,23 +25,11 @@ define([
|
||||
|
||||
function ConfigStore() {
|
||||
this.store = {};
|
||||
this.tracking = {};
|
||||
}
|
||||
|
||||
ConfigStore.prototype.track = function (id) {
|
||||
if (!this.tracking[id]) {
|
||||
this.tracking[id] = 0;
|
||||
}
|
||||
this.tracking[id] += 1;
|
||||
};
|
||||
|
||||
ConfigStore.prototype.untrack = function (id) {
|
||||
this.tracking[id] -= 1;
|
||||
if (this.tracking[id] <= 0) {
|
||||
delete this.tracking[id];
|
||||
ConfigStore.prototype.deleteStore = function (id) {
|
||||
this.store[id].destroy();
|
||||
delete this.store[id];
|
||||
}
|
||||
};
|
||||
|
||||
ConfigStore.prototype.add = function (id, config) {
|
||||
|
||||
@@ -49,7 +49,6 @@ define([
|
||||
};
|
||||
|
||||
PlotOptionsController.prototype.destroy = function () {
|
||||
configStore.untrack(this.configId);
|
||||
this.stopListening();
|
||||
this.unlisten();
|
||||
};
|
||||
@@ -60,7 +59,7 @@ define([
|
||||
this.$timeout(this.setUpScope.bind(this));
|
||||
return;
|
||||
}
|
||||
configStore.track(this.configId);
|
||||
|
||||
this.config = this.$scope.config = config;
|
||||
this.$scope.plotSeries = [];
|
||||
|
||||
|
||||
@@ -282,11 +282,19 @@ define([
|
||||
};
|
||||
|
||||
MCTPlotController.prototype.zoom = function (zoomDirection, zoomFactor) {
|
||||
var currentXaxis = this.$scope.xAxis.get('displayRange'),
|
||||
currentYaxis = this.$scope.yAxis.get('displayRange');
|
||||
|
||||
// when there is no plot data, the ranges can be undefined
|
||||
// in which case we should not perform zoom
|
||||
if (!currentXaxis || !currentYaxis) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.freeze();
|
||||
this.trackHistory();
|
||||
var currentXaxis = this.$scope.xAxis.get('displayRange'),
|
||||
currentYaxis = this.$scope.yAxis.get('displayRange'),
|
||||
xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
|
||||
|
||||
var xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
|
||||
yAxisDist = (currentYaxis.max - currentYaxis.min) * zoomFactor;
|
||||
|
||||
if (zoomDirection === 'in') {
|
||||
@@ -322,12 +330,19 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
let xDisplayRange = this.$scope.xAxis.get('displayRange'),
|
||||
yDisplayRange = this.$scope.yAxis.get('displayRange');
|
||||
|
||||
// when there is no plot data, the ranges can be undefined
|
||||
// in which case we should not perform zoom
|
||||
if (!xDisplayRange || !yDisplayRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.freeze();
|
||||
window.clearTimeout(this.stillZooming);
|
||||
|
||||
let xDisplayRange = this.$scope.xAxis.get('displayRange'),
|
||||
yDisplayRange = this.$scope.yAxis.get('displayRange'),
|
||||
xAxisDist = (xDisplayRange.max - xDisplayRange.min),
|
||||
let xAxisDist = (xDisplayRange.max - xDisplayRange.min),
|
||||
yAxisDist = (yDisplayRange.max - yDisplayRange.min),
|
||||
xDistMouseToMax = xDisplayRange.max - this.positionOverPlot.x,
|
||||
xDistMouseToMin = this.positionOverPlot.x - xDisplayRange.min,
|
||||
|
||||
@@ -148,7 +148,6 @@ define([
|
||||
});
|
||||
configStore.add(configId, config);
|
||||
}
|
||||
configStore.track(configId);
|
||||
return config;
|
||||
};
|
||||
|
||||
@@ -157,7 +156,8 @@ define([
|
||||
};
|
||||
|
||||
PlotController.prototype.destroy = function () {
|
||||
configStore.untrack(this.config.id);
|
||||
configStore.deleteStore(this.config.id);
|
||||
|
||||
this.stopListening();
|
||||
if (this.checkForSize) {
|
||||
clearInterval(this.checkForSize);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
define([
|
||||
'lodash',
|
||||
'./utcTimeSystem/plugin',
|
||||
'./localTimeSystem/plugin',
|
||||
'../../example/generator/plugin',
|
||||
'./autoflow/AutoflowTabularPlugin',
|
||||
'./timeConductor/plugin',
|
||||
@@ -41,10 +42,12 @@ define([
|
||||
'./tabs/plugin',
|
||||
'./LADTable/plugin',
|
||||
'./filters/plugin',
|
||||
'./objectMigration/plugin'
|
||||
'./objectMigration/plugin',
|
||||
'./goToOriginalAction/plugin'
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
LocalTimeSystem,
|
||||
GeneratorPlugin,
|
||||
AutoflowPlugin,
|
||||
TimeConductorPlugin,
|
||||
@@ -63,7 +66,8 @@ define([
|
||||
Tabs,
|
||||
LADTable,
|
||||
Filters,
|
||||
ObjectMigration
|
||||
ObjectMigration,
|
||||
GoToOriginalAction
|
||||
) {
|
||||
var bundleMap = {
|
||||
LocalStorage: 'platform/persistence/local',
|
||||
@@ -79,6 +83,7 @@ define([
|
||||
});
|
||||
|
||||
plugins.UTCTimeSystem = UTCTimeSystem;
|
||||
plugins.LocalTimeSystem = LocalTimeSystem;
|
||||
|
||||
plugins.ImportExport = ImportExport;
|
||||
|
||||
@@ -160,6 +165,7 @@ define([
|
||||
plugins.LADTable = LADTable;
|
||||
plugins.Filters = Filters;
|
||||
plugins.ObjectMigration = ObjectMigration.default;
|
||||
plugins.GoToOriginalAction = GoToOriginalAction.default;
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
||||
@@ -70,17 +70,15 @@ define([
|
||||
*/
|
||||
function onValueInput(event) {
|
||||
var elem = event.target,
|
||||
value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber),
|
||||
value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value),
|
||||
inputIndex = self.valueInputs.indexOf(elem);
|
||||
|
||||
if (elem.tagName.toUpperCase() === 'INPUT') {
|
||||
self.eventEmitter.emit('change', {
|
||||
value: value,
|
||||
property: 'values[' + inputIndex + ']',
|
||||
index: self.index
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.listenTo(this.deleteButton, 'click', this.remove, this);
|
||||
this.listenTo(this.duplicateButton, 'click', this.duplicate, this);
|
||||
@@ -108,8 +106,7 @@ define([
|
||||
Object.values(this.selects).forEach(function (select) {
|
||||
$('.t-configuration', self.domElement).append(select.getDOM());
|
||||
});
|
||||
|
||||
this.listenTo($(this.domElement), 'input', onValueInput);
|
||||
this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput);
|
||||
}
|
||||
|
||||
Condition.prototype.getDOM = function (container) {
|
||||
@@ -167,7 +164,9 @@ define([
|
||||
|
||||
/**
|
||||
* When an operation is selected, create the appropriate value inputs
|
||||
* and add them to the view
|
||||
* and add them to the view. If an operation is of type enum, create
|
||||
* a drop-down menu instead.
|
||||
*
|
||||
* @param {string} operation The key of currently selected operation
|
||||
*/
|
||||
Condition.prototype.generateValueInputs = function (operation) {
|
||||
@@ -176,24 +175,48 @@ define([
|
||||
inputCount,
|
||||
inputType,
|
||||
newInput,
|
||||
index = 0;
|
||||
index = 0,
|
||||
emitChange = false;
|
||||
|
||||
inputArea.html('');
|
||||
this.valueInputs = [];
|
||||
this.config.values = [];
|
||||
|
||||
if (evaluator.getInputCount(operation)) {
|
||||
inputCount = evaluator.getInputCount(operation);
|
||||
inputType = evaluator.getInputType(operation);
|
||||
|
||||
while (index < inputCount) {
|
||||
if (!this.config.values[index]) {
|
||||
this.config.values[index] = (inputType === 'number' ? 0 : '');
|
||||
}
|
||||
if (inputType === 'select') {
|
||||
newInput = $('<select>' + this.generateSelectOptions() + '</select>');
|
||||
emitChange = true;
|
||||
} else {
|
||||
this.config.values[index] = inputType === 'number' ? 0 : '';
|
||||
newInput = $('<input type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>');
|
||||
}
|
||||
|
||||
this.valueInputs.push(newInput.get(0));
|
||||
inputArea.append(newInput);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if (emitChange) {
|
||||
this.eventEmitter.emit('change', {
|
||||
value: Number(newInput[0].options[0].value),
|
||||
property: 'values[0]',
|
||||
index: this.index
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Condition.prototype.generateSelectOptions = function () {
|
||||
let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object);
|
||||
let options = '';
|
||||
telemetryMetadata[this.config.key].enumerations.forEach(enumeration => {
|
||||
options += '<option value="' + enumeration.value + '">'+ enumeration.string + '</option>';
|
||||
});
|
||||
return options;
|
||||
};
|
||||
|
||||
return Condition;
|
||||
|
||||
@@ -24,7 +24,8 @@ define([], function () {
|
||||
*/
|
||||
this.inputTypes = {
|
||||
number: 'number',
|
||||
string: 'text'
|
||||
string: 'text',
|
||||
enum: 'select'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,8 @@ define([], function () {
|
||||
*/
|
||||
this.inputValidators = {
|
||||
number: this.validateNumberInput,
|
||||
string: this.validateStringInput
|
||||
string: this.validateStringInput,
|
||||
enum: this.validateNumberInput
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -201,7 +203,7 @@ define([], function () {
|
||||
return typeof input[0] === 'undefined';
|
||||
},
|
||||
text: 'is undefined',
|
||||
appliesTo: ['string', 'number'],
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is undefined';
|
||||
@@ -212,11 +214,33 @@ define([], function () {
|
||||
return typeof input[0] !== 'undefined';
|
||||
},
|
||||
text: 'is defined',
|
||||
appliesTo: ['string', 'number'],
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is defined';
|
||||
}
|
||||
},
|
||||
enumValueIs: {
|
||||
operation: function (input) {
|
||||
return input[0] === input[1];
|
||||
},
|
||||
text: 'is',
|
||||
appliesTo: ['enum'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
return ' == ' + values[0];
|
||||
}
|
||||
},
|
||||
enumValueIsNot: {
|
||||
operation: function (input) {
|
||||
return input[0] !== input[1];
|
||||
},
|
||||
text: 'is not',
|
||||
appliesTo: ['enum'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
return ' != ' + values[0];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -310,13 +334,16 @@ define([], function () {
|
||||
validator;
|
||||
|
||||
if (cache[object] && typeof cache[object][key] !== 'undefined') {
|
||||
telemetryValue = [cache[object][key]];
|
||||
let value = cache[object][key];
|
||||
telemetryValue = [isNaN(Number(value)) ? value : Number(value)];
|
||||
}
|
||||
|
||||
op = this.operations[operation] && this.operations[operation].operation;
|
||||
input = telemetryValue && telemetryValue.concat(values);
|
||||
validator = op && this.inputValidators[this.operations[operation].appliesTo[0]];
|
||||
|
||||
if (op && input && validator) {
|
||||
if (this.operations[operation].appliesTo.length === 2) {
|
||||
if (this.operations[operation].appliesTo.length > 1) {
|
||||
return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input);
|
||||
} else {
|
||||
return validator(input) && op(input);
|
||||
@@ -372,7 +399,7 @@ define([], function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true only of the given operation applies to a given type
|
||||
* Returns true only if the given operation applies to a given type
|
||||
* @param {string} key The key of the operation
|
||||
* @param {string} type The value type to query
|
||||
* @returns {boolean} True if the condition applies, false otherwise
|
||||
|
||||
@@ -130,7 +130,9 @@ define ([
|
||||
this.telemetryTypesById[objectId] = {};
|
||||
Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) {
|
||||
var type;
|
||||
if (valueMetadata.hints.hasOwnProperty('range')) {
|
||||
if (valueMetadata.enumerations !== undefined) {
|
||||
type = 'enum';
|
||||
} else if (valueMetadata.hints.hasOwnProperty('range')) {
|
||||
type = 'number';
|
||||
} else if (valueMetadata.hints.hasOwnProperty('domain')) {
|
||||
type = 'number';
|
||||
@@ -163,11 +165,18 @@ define ([
|
||||
* @param {datum} datum The new data from the telemetry source
|
||||
* @private
|
||||
*/
|
||||
ConditionManager.prototype.handleSubscriptionCallback = function (objId, datum) {
|
||||
this.subscriptionCache[objId] = datum;
|
||||
ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) {
|
||||
this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum);
|
||||
this.eventEmitter.emit('receiveTelemetry');
|
||||
};
|
||||
|
||||
ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDatum) {
|
||||
return Object.values(this.telemetryMetadataById[objId]).reduce((normalizedDatum, metadatum) => {
|
||||
normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source];
|
||||
return normalizedDatum;
|
||||
}, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for an add event in this Summary Widget's composition.
|
||||
* Sets up subscription handlers and parses its property types.
|
||||
@@ -236,6 +245,7 @@ define ([
|
||||
id.namespace === identifier.namespace;
|
||||
});
|
||||
delete this.compositionObjs[objectId];
|
||||
delete this.subscriptionCache[objectId];
|
||||
this.subscriptions[objectId](); //unsubscribe from telemetry source
|
||||
delete this.subscriptions[objectId];
|
||||
this.eventEmitter.emit('remove', identifier);
|
||||
|
||||
@@ -110,9 +110,11 @@ define([
|
||||
|
||||
type = self.manager.getTelemetryPropertyType(self.config.object, key);
|
||||
|
||||
if (type !== undefined) {
|
||||
self.operationKeys = operations.filter(function (operation) {
|
||||
return self.evaluator.operationAppliesTo(operation, type);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OperationSelect.prototype.destroy = function () {
|
||||
|
||||
@@ -38,7 +38,7 @@ define([
|
||||
return this.openmct.time.getAllTimeSystems().map(function (ts, i) {
|
||||
return {
|
||||
key: ts.key,
|
||||
name: 'UTC',
|
||||
name: ts.name,
|
||||
format: ts.timeFormat,
|
||||
hints: {
|
||||
domain: i
|
||||
@@ -64,7 +64,7 @@ define([
|
||||
// Generally safe assumption is that we have one domain per timeSystem.
|
||||
values: this.getDomains().concat([
|
||||
{
|
||||
name: 'state',
|
||||
name: 'State',
|
||||
key: 'state',
|
||||
source: 'ruleIndex',
|
||||
format: 'enum',
|
||||
|
||||
@@ -174,7 +174,7 @@ define([
|
||||
return typeof input[0] === 'undefined';
|
||||
},
|
||||
text: 'is undefined',
|
||||
appliesTo: ['string', 'number'],
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is undefined';
|
||||
@@ -185,11 +185,33 @@ define([
|
||||
return typeof input[0] !== 'undefined';
|
||||
},
|
||||
text: 'is defined',
|
||||
appliesTo: ['string', 'number'],
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is defined';
|
||||
}
|
||||
},
|
||||
enumValueIs: {
|
||||
operation: function (input) {
|
||||
return input[0] === input[1];
|
||||
},
|
||||
text: 'is',
|
||||
appliesTo: ['enum'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
return ' == ' + values[0];
|
||||
}
|
||||
},
|
||||
enumValueIsNot: {
|
||||
operation: function (input) {
|
||||
return input[0] !== input[1];
|
||||
},
|
||||
text: 'is not',
|
||||
appliesTo: ['enum'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
return ' != ' + values[0];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ define([
|
||||
this.filteredRows.destroy();
|
||||
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
|
||||
this.openmct.time.off('bounds', this.refreshData);
|
||||
this.openmct.time.on('timeSystem', this.refreshData);
|
||||
this.openmct.time.off('timeSystem', this.refreshData);
|
||||
if (this.filterObserver) {
|
||||
this.filterObserver();
|
||||
}
|
||||
|
||||
@@ -47,11 +47,12 @@ define(
|
||||
}
|
||||
|
||||
addOne(item) {
|
||||
let parsedValue = this.getValueForSortColumn(item);
|
||||
// Insert into either in-bounds array, or the future buffer.
|
||||
// Data in the future buffer will be re-evaluated for possible
|
||||
// insertion on next bounds change
|
||||
let beforeStartOfBounds = this.parseTime(item.datum[this.sortOptions.key]) < this.lastBounds.start;
|
||||
let afterEndOfBounds = this.parseTime(item.datum[this.sortOptions.key]) > this.lastBounds.end;
|
||||
let beforeStartOfBounds = parsedValue < this.lastBounds.start;
|
||||
let afterEndOfBounds = parsedValue > this.lastBounds.end;
|
||||
|
||||
if (!afterEndOfBounds && !beforeStartOfBounds) {
|
||||
return super.addOne(item);
|
||||
@@ -135,6 +136,10 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
getValueForSortColumn(row) {
|
||||
return this.parseTime(row.datum[this.sortOptions.key]);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.openmct.time.off('bounds', this.bounds);
|
||||
}
|
||||
|
||||
@@ -116,10 +116,9 @@ define(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const sortOptionsKey = this.sortOptions.key;
|
||||
const testRowValue = testRow.datum[sortOptionsKey];
|
||||
const firstValue = this.rows[0].datum[sortOptionsKey];
|
||||
const lastValue = this.rows[this.rows.length - 1].datum[sortOptionsKey];
|
||||
const testRowValue = this.getValueForSortColumn(testRow);
|
||||
const firstValue = this.getValueForSortColumn(this.rows[0]);
|
||||
const lastValue = this.getValueForSortColumn(this.rows[this.rows.length - 1]);
|
||||
|
||||
lodashFunction = lodashFunction || _.sortedIndex;
|
||||
|
||||
@@ -133,7 +132,7 @@ define(
|
||||
return 0;
|
||||
} else {
|
||||
return lodashFunction(rows, testRow, (thisRow) => {
|
||||
return thisRow.datum[sortOptionsKey];
|
||||
return this.getValueForSortColumn(thisRow);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -147,7 +146,7 @@ define(
|
||||
} else {
|
||||
// Use a custom comparison function to support descending sort.
|
||||
return lodashFunction(rows, testRow, (thisRow) => {
|
||||
const thisRowValue = thisRow.datum[sortOptionsKey];
|
||||
const thisRowValue = this.getValueForSortColumn(thisRow);
|
||||
if (testRowValue === thisRowValue) {
|
||||
return EQUAL;
|
||||
} else if (testRowValue < thisRowValue) {
|
||||
@@ -218,13 +217,19 @@ define(
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
this.emit('remove', removed);
|
||||
}
|
||||
|
||||
getValueForSortColumn(row) {
|
||||
return row.datum[this.sortOptions.key];
|
||||
}
|
||||
|
||||
remove(removedRows) {
|
||||
this.rows = this.rows.filter(row => {
|
||||
return removedRows.indexOf(row) === -1;
|
||||
});
|
||||
|
||||
this.emit('remove', removedRows);
|
||||
}
|
||||
|
||||
@@ -235,6 +240,7 @@ define(
|
||||
clear() {
|
||||
let removedRows = this.rows;
|
||||
this.rows = [];
|
||||
|
||||
this.emit('remove', removedRows);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<!-- Content table -->
|
||||
<div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll" :style="{ 'max-width': widthWithScroll}">
|
||||
<div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth + 'px' }"></div>
|
||||
<table class="c-table__body c-telemetry-table__body"
|
||||
<table class="c-table__body c-telemetry-table__body js-telemetry-table__content"
|
||||
:style="{ height: totalHeight + 'px'}">
|
||||
<tbody>
|
||||
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
|
||||
@@ -284,7 +284,7 @@ import _ from 'lodash';
|
||||
const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 20;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 100;
|
||||
const RESIZE_HOT_ZONE = 10;
|
||||
const MOVE_TRIGGER_WAIT = 500;
|
||||
const VERTICAL_SCROLL_WIDTH = 30;
|
||||
@@ -364,14 +364,15 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
updateVisibleRows() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(()=> {
|
||||
|
||||
let start = 0;
|
||||
let end = VISIBLE_ROW_COUNT;
|
||||
let filteredRows = this.table.filteredRows.getRows();
|
||||
let filteredRowsLength = filteredRows.length;
|
||||
|
||||
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
||||
|
||||
if (filteredRowsLength < VISIBLE_ROW_COUNT) {
|
||||
end = filteredRowsLength;
|
||||
} else {
|
||||
@@ -393,13 +394,18 @@ export default {
|
||||
}
|
||||
this.rowOffset = start;
|
||||
this.visibleRows = filteredRows.slice(start, end);
|
||||
|
||||
this.updatingView = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
calculateFirstVisibleRow() {
|
||||
return Math.floor(this.scrollable.scrollTop / this.rowHeight);
|
||||
let scrollTop = this.scrollable.scrollTop;
|
||||
return Math.floor(scrollTop / this.rowHeight);
|
||||
},
|
||||
calculateLastVisibleRow() {
|
||||
let bottomScroll = this.scrollable.scrollTop + this.scrollable.offsetHeight;
|
||||
return Math.floor(bottomScroll / this.rowHeight);
|
||||
let scrollBottom = this.scrollable.scrollTop + this.scrollable.offsetHeight;
|
||||
return Math.ceil(scrollBottom / this.rowHeight);
|
||||
},
|
||||
updateHeaders() {
|
||||
this.headers = this.table.configuration.getVisibleHeaders();
|
||||
@@ -444,9 +450,6 @@ export default {
|
||||
this.table.sortBy(this.sortOptions);
|
||||
},
|
||||
scroll () {
|
||||
if (!this.processingScroll) {
|
||||
this.processingScroll = true;
|
||||
requestAnimationFrame(()=> {
|
||||
this.updateVisibleRows();
|
||||
this.synchronizeScrollX();
|
||||
|
||||
@@ -457,57 +460,59 @@ export default {
|
||||
// Auto-scroll will be re-enabled if user scrolls to bottom again.
|
||||
this.autoScroll = false;
|
||||
}
|
||||
this.processingScroll = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
shouldSnapToBottom() {
|
||||
return this.scrollable.scrollTop >= (this.scrollable.scrollHeight - this.scrollable.offsetHeight - AUTO_SCROLL_TRIGGER_HEIGHT);
|
||||
},
|
||||
scrollToBottom() {
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight;
|
||||
this.scrollable.scrollTop = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
synchronizeScrollX() {
|
||||
this.headersHolderEl.scrollLeft = this.scrollable.scrollLeft;
|
||||
},
|
||||
filterChanged(columnKey) {
|
||||
this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
|
||||
this.setHeight();
|
||||
},
|
||||
clearFilter(columnKey) {
|
||||
this.filters[columnKey] = '';
|
||||
this.table.filteredRows.setColumnFilter(columnKey, '');
|
||||
this.setHeight();
|
||||
},
|
||||
rowsAdded (rows) {
|
||||
this.setHeight();
|
||||
|
||||
let sizingRow;
|
||||
if (Array.isArray(rows)) {
|
||||
sizingRow = rows[0];
|
||||
} else {
|
||||
sizingRow = rows;
|
||||
}
|
||||
|
||||
if (!this.sizingRows[sizingRow.objectKeyString]) {
|
||||
this.sizingRows[sizingRow.objectKeyString] = sizingRow;
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
}
|
||||
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(()=> {
|
||||
this.updateVisibleRows();
|
||||
if (this.autoScroll) {
|
||||
this.$nextTick().then(this.scrollToBottom);
|
||||
}
|
||||
this.updatingView = false;
|
||||
});
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
this.updateVisibleRows();
|
||||
},
|
||||
rowsRemoved (rows) {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(()=> {
|
||||
this.setHeight();
|
||||
this.updateVisibleRows();
|
||||
this.updatingView = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Calculates height based on total number of rows, and sets table height.
|
||||
*/
|
||||
setHeight() {
|
||||
let filteredRowsLength = this.table.filteredRows.getRows().length;
|
||||
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
||||
// Set element height directly to avoid having to wait for Vue to update DOM
|
||||
// which causes subsequent scroll to use an out of date height.
|
||||
this.contentTable.style.height = this.totalHeight + 'px';
|
||||
},
|
||||
exportAsCSV() {
|
||||
const headerKeys = Object.keys(this.headers);
|
||||
@@ -595,7 +600,11 @@ export default {
|
||||
this.calculateTableSize();
|
||||
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
|
||||
// Need to preserve scroll position in this case.
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
} else {
|
||||
this.scrollable.scrollTop = scrollTop;
|
||||
}
|
||||
width = el.clientWidth;
|
||||
height = el.clientHeight;
|
||||
}
|
||||
@@ -608,6 +617,9 @@ export default {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
this.rowsAdded = _.throttle(this.rowsAdded, 200);
|
||||
this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
|
||||
this.scroll = _.throttle(this.scroll, 100);
|
||||
|
||||
this.table.on('object-added', this.addObject);
|
||||
this.table.on('object-removed', this.removeObject);
|
||||
@@ -621,6 +633,7 @@ export default {
|
||||
//Default sort
|
||||
this.sortOptions = this.table.filteredRows.sortBy();
|
||||
this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w');
|
||||
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
|
||||
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
|
||||
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
|
||||
|
||||
|
||||
@@ -54,10 +54,11 @@
|
||||
|
||||
&:after {
|
||||
// App logo
|
||||
top: 0;
|
||||
right: 15%;
|
||||
bottom: 0;
|
||||
left: 15%;
|
||||
$d: 25%;
|
||||
top: $d;
|
||||
right: $d;
|
||||
bottom: $d;
|
||||
left: $d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,12 +76,20 @@
|
||||
|
||||
&__image,
|
||||
&__text {
|
||||
height: 50%;
|
||||
flex: 1 1 0;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__image {
|
||||
height: 35%;
|
||||
}
|
||||
|
||||
&__text {
|
||||
height: 65%;
|
||||
overflow: auto;
|
||||
> * + * {
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&--licenses {
|
||||
@@ -107,7 +116,7 @@
|
||||
|
||||
h1, h2, h3 {
|
||||
font-weight: normal;
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: .25em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
||||
@@ -393,6 +393,11 @@ select {
|
||||
color: $colorMenuIc;
|
||||
font-size: 1em;
|
||||
margin-right: $interiorMargin;
|
||||
min-width: 1em;
|
||||
}
|
||||
|
||||
&:not([class]):before {
|
||||
content: ''; // Add this element so that menu items without an icon still indent properly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,3 +33,4 @@
|
||||
@import "table";
|
||||
@import "legacy";
|
||||
@import "legacy-plots";
|
||||
@import "legacy-messages";
|
||||
|
||||
@@ -34,10 +34,10 @@ export default {
|
||||
this.currentObject = this.object;
|
||||
this.updateView();
|
||||
this.$el.addEventListener('dragover', this.onDragOver);
|
||||
this.$el.addEventListener('drop', this.addObjectToParent);
|
||||
this.$el.addEventListener('drop', this.editIfEditable, {
|
||||
capture: true
|
||||
});
|
||||
this.$el.addEventListener('drop', this.addObjectToParent);
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
@@ -57,6 +57,10 @@ export default {
|
||||
this.removeSelectable();
|
||||
delete this.removeSelectable;
|
||||
}
|
||||
|
||||
if (this.composition) {
|
||||
this.composition._destroy();
|
||||
}
|
||||
},
|
||||
invokeEditModeHandler(editMode) {
|
||||
this.currentView.onEditModeChange(editMode);
|
||||
@@ -70,6 +74,13 @@ export default {
|
||||
if (!this.currentObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.composition = this.openmct.composition.get(this.currentObject);
|
||||
if (this.composition) {
|
||||
this.composition._synchronize();
|
||||
this.loadComposition();
|
||||
}
|
||||
|
||||
this.viewContainer = document.createElement('div');
|
||||
this.viewContainer.classList.add('c-object-view','u-contents');
|
||||
this.$el.append(this.viewContainer);
|
||||
@@ -112,6 +123,10 @@ export default {
|
||||
delete this.removeSelectable;
|
||||
}
|
||||
|
||||
if (this.composition) {
|
||||
this.composition._destroy();
|
||||
}
|
||||
|
||||
this.currentObject = object;
|
||||
this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => {
|
||||
this.currentObject = mutatedObject;
|
||||
@@ -120,6 +135,9 @@ export default {
|
||||
this.viewKey = viewKey;
|
||||
this.updateView(immediatelySelect);
|
||||
},
|
||||
loadComposition() {
|
||||
return this.composition.load();
|
||||
},
|
||||
getSelectionContext() {
|
||||
if (this.currentView.getSelectionContext) {
|
||||
return this.currentView.getSelectionContext();
|
||||
@@ -133,10 +151,12 @@ export default {
|
||||
}
|
||||
},
|
||||
addObjectToParent(event) {
|
||||
if (this.hasComposableDomainObject(event)) {
|
||||
if (this.hasComposableDomainObject(event) && this.composition) {
|
||||
let composableDomainObject = this.getComposableDomainObject(event);
|
||||
this.currentObject.composition.push(composableDomainObject.identifier);
|
||||
this.openmct.objects.mutate(this.currentObject, 'composition', this.currentObject.composition);
|
||||
this.loadComposition().then(() => {
|
||||
this.composition.add(composableDomainObject);
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
@@ -155,6 +175,7 @@ export default {
|
||||
editIfEditable(event) {
|
||||
let provider = this.getViewProvider();
|
||||
if (provider &&
|
||||
provider.canEdit &&
|
||||
provider.canEdit(this.currentObject) &&
|
||||
!this.openmct.editor.isEditing()) {
|
||||
this.openmct.editor.edit();
|
||||
|
||||
@@ -25,10 +25,6 @@ import _ from 'lodash';
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
if (_.isEqual(this.selection, selection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection = selection;
|
||||
|
||||
if (this.selectedViews) {
|
||||
@@ -38,10 +34,6 @@ import _ from 'lodash';
|
||||
this.$el.innerHTML = '';
|
||||
}
|
||||
|
||||
if (selection.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedViews = this.openmct.inspectorViews.get(selection);
|
||||
this.selectedViews.forEach(selectedView => {
|
||||
let viewContainer = document.createElement('div');
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="c-about c-about--splash">
|
||||
<div v-if="!branding.aboutHtml" class="c-about__image c-splash-image"></div>
|
||||
|
||||
<div class="c-about__image c-splash-image"></div>
|
||||
<div class="c-about__text s-text">
|
||||
<div v-if="branding.aboutHtml" class="s-text l-content" v-html="branding.aboutHtml"></div>
|
||||
<div class="c-about__text__element" v-if="branding.aboutHtml" v-html="branding.aboutHtml"></div>
|
||||
<div class="c-about__text__element">
|
||||
<h1 class="l-title s-title">Open MCT</h1>
|
||||
<div class="l-description s-description">
|
||||
<p>Open MCT, Copyright © 2014-2019, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.</p>
|
||||
@@ -20,6 +20,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
|
||||
@@ -70,17 +70,18 @@
|
||||
this.domainObject = newObject;
|
||||
});
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
|
||||
if (this.openmct.composition.get(this.node.object)) {
|
||||
this.hasChildren = true;
|
||||
}
|
||||
|
||||
this.openmct.router.on('change:path', this.highlightIfNavigated);
|
||||
},
|
||||
destroy() {
|
||||
destroyed() {
|
||||
this.openmct.router.off('change:path', this.highlightIfNavigated);
|
||||
if (this.composition) {
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
this.openmct.router.off('change:path', this.highlightIfNavigated);
|
||||
delete this.composition;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
|
||||
this.objectPath.forEach(object => {
|
||||
if (object) {
|
||||
this.$once('hook:destroy',
|
||||
this.$once('hook:destroyed',
|
||||
this.openmct.objects.observe(object, '*', updateObject.bind(this, object)))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
this.notebookEnabled = true;
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
destroyed() {
|
||||
this.view.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ const webpackConfig = {
|
||||
"bourbon": "bourbon.scss",
|
||||
"vue": path.join(__dirname, "node_modules/vue/dist/vue.js"),
|
||||
"d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
|
||||
"printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
|
||||
"styles": path.join(__dirname, "src/styles-new")
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user