mirror of
https://github.com/nasa/openmct.git
synced 2025-06-26 19:12:02 +00:00
Compare commits
1 Commits
notebook-s
...
stacked-pl
Author | SHA1 | Date | |
---|---|---|---|
317cb68b53 |
@ -33,7 +33,8 @@ define([
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
},
|
||||
filters: ['equals']
|
||||
},
|
||||
{
|
||||
key: "cos",
|
||||
@ -99,10 +100,10 @@ define([
|
||||
|
||||
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
||||
return _.extend(
|
||||
{},
|
||||
domainObject.telemetry,
|
||||
METADATA_BY_TYPE[domainObject.type]
|
||||
);
|
||||
{},
|
||||
domainObject.telemetry,
|
||||
METADATA_BY_TYPE[domainObject.type]
|
||||
);
|
||||
};
|
||||
|
||||
return GeneratorMetadataProvider;
|
||||
|
@ -1,9 +1,9 @@
|
||||
<span class="h-indicator" ng-controller="DialogLaunchController">
|
||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
|
||||
<div class="c-indicator c-indicator--clickable icon-box-with-arrow s-status-available"><span class="label c-indicator__label">
|
||||
<button ng-click="launchProgress(true)">Known</button>
|
||||
<button ng-click="launchProgress(false)">Unknown</button>
|
||||
<button ng-click="launchError()">Error</button>
|
||||
<button ng-click="launchInfo()">Info</button>
|
||||
<div class="ls-indicator icon-box-with-arrow s-status-available"><span class="label">
|
||||
<a ng-click="launchProgress(true)">Known</a>
|
||||
<a ng-click="launchProgress(false)">Unknown</a>
|
||||
<a ng-click="launchError()">Error</a>
|
||||
<a ng-click="launchInfo()">Info</a>
|
||||
</span></div>
|
||||
</span>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<span class="h-indicator" ng-controller="NotificationLaunchController">
|
||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
|
||||
<div class="c-indicator c-indicator--clickable icon-bell s-status-available"><span class="label c-indicator__label">
|
||||
<button ng-click="newInfo()">Success</button>
|
||||
<button ng-click="newError()">Error</button>
|
||||
<button ng-click="newAlert()">Alert</button>
|
||||
<button ng-click="newProgress()">Progress</button>
|
||||
<div class="ls-indicator icon-bell s-status-available"><span class="label">
|
||||
<a ng-click="newInfo()">Success</a>
|
||||
<a ng-click="newError()">Error</a>
|
||||
<a ng-click="newAlert()">Alert</a>
|
||||
<a ng-click="newProgress()">Progress</a>
|
||||
</span></div>
|
||||
</span>
|
||||
|
10
index.html
10
index.html
@ -50,6 +50,7 @@
|
||||
openmct.install(openmct.plugins.Generator());
|
||||
openmct.install(openmct.plugins.ExampleImagery());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.install(openmct.plugins.ImportExport());
|
||||
openmct.install(openmct.plugins.AutoflowView({
|
||||
type: "telemetry.panel"
|
||||
}));
|
||||
@ -78,10 +79,13 @@
|
||||
]
|
||||
}));
|
||||
openmct.install(openmct.plugins.SummaryWidget());
|
||||
openmct.install(openmct.plugins.NotebookSVC());
|
||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||
openmct.install(openmct.plugins.Notebook());
|
||||
openmct.install(openmct.plugins.FolderView());
|
||||
openmct.install(openmct.plugins.Tabs());
|
||||
openmct.install(openmct.plugins.FlexibleLayout());
|
||||
openmct.install(openmct.plugins.LADTable());
|
||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
||||
openmct.install(openmct.plugins.ObjectMigration());
|
||||
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
||||
openmct.start();
|
||||
</script>
|
||||
</html>
|
||||
|
@ -1,10 +1,9 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "1.0.0-beta",
|
||||
"version": "0.14.0-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"acorn": "6.2.0",
|
||||
"angular": "1.4.14",
|
||||
"angular-route": "1.4.14",
|
||||
"babel-eslint": "8.2.6",
|
||||
@ -56,7 +55,7 @@
|
||||
"node-bourbon": "^4.2.3",
|
||||
"node-sass": "^4.9.2",
|
||||
"painterro": "^0.2.65",
|
||||
"printj": "^1.2.1",
|
||||
"printj": "^1.1.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"request": "^2.69.0",
|
||||
"split": "^1.0.0",
|
||||
|
@ -9,17 +9,17 @@
|
||||
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"
|
||||
<div class="c-overlay__button-bar">
|
||||
<button ng-repeat="dialogOption in ngModel.options"
|
||||
class="c-button"
|
||||
ng-click="dialogOption.callback()">
|
||||
{{dialogOption.label}}
|
||||
</button>
|
||||
<button class="c-button c-button--major"
|
||||
{{dialogOption.label}}
|
||||
</button>
|
||||
<button class="c-button c-button--major"
|
||||
ng-if="ngModel.primaryOption"
|
||||
ng-click="ngModel.primaryOption.callback()">
|
||||
{{ngModel.primaryOption.label}}
|
||||
</button>
|
||||
{{ngModel.primaryOption.label}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -64,6 +64,7 @@ define(
|
||||
* @returns boolean
|
||||
*/
|
||||
EditorCapability.prototype.inEditContext = function () {
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
@ -73,6 +74,7 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.isEditContextRoot = function () {
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
|
@ -77,19 +77,14 @@ define([], function () {
|
||||
return promiseFn().then(nextFn);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Clear any existing persistence calls for object with given ID. This ensures only the most recent persistence
|
||||
* call is executed. This should prevent stale objects being persisted and overwriting fresh ones.
|
||||
*/
|
||||
if (this.isScheduled(id)) {
|
||||
this.clearTransactionsFor(id);
|
||||
}
|
||||
|
||||
this.clearTransactionFns[id] =
|
||||
this.transactionService.addToTransaction(
|
||||
chain(onCommit, release),
|
||||
chain(onCancel, release)
|
||||
);
|
||||
if (!this.isScheduled(id)) {
|
||||
this.clearTransactionFns[id] =
|
||||
this.transactionService.addToTransaction(
|
||||
chain(onCommit, release),
|
||||
chain(onCancel, release)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -93,33 +93,24 @@ define(
|
||||
expect(mockOnCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("Adds callbacks to transaction", function () {
|
||||
beforeEach(function () {
|
||||
spyOn(manager, 'clearTransactionsFor');
|
||||
manager.clearTransactionsFor.and.callThrough();
|
||||
});
|
||||
it("ignores subsequent calls for the same object", function () {
|
||||
manager.addToTransaction(
|
||||
testId,
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(mockTransactionService.addToTransaction.calls.count())
|
||||
.toEqual(1);
|
||||
});
|
||||
|
||||
it("and clears pending calls if same object", function () {
|
||||
manager.addToTransaction(
|
||||
testId,
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(manager.clearTransactionsFor).toHaveBeenCalledWith(testId);
|
||||
});
|
||||
|
||||
it("and does not clear pending calls if different object", function () {
|
||||
manager.addToTransaction(
|
||||
'other-id',
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(manager.clearTransactionsFor).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect(mockTransactionService.addToTransaction.calls.count()).toEqual(2);
|
||||
});
|
||||
it("accepts subsequent calls for other objects", function () {
|
||||
manager.addToTransaction(
|
||||
'other-id',
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(mockTransactionService.addToTransaction.calls.count())
|
||||
.toEqual(2);
|
||||
});
|
||||
|
||||
it("does not remove callbacks from the transaction", function () {
|
||||
|
@ -20,8 +20,8 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
|
||||
<div class="c-indicator {{ngModel.getCssClass()}}"
|
||||
<div class="ls-indicator {{ngModel.getCssClass()}}"
|
||||
title="{{ngModel.getDescription()}}"
|
||||
ng-show="ngModel.getText().length > 0">
|
||||
<span class="label c-indicator__label">{{ngModel.getText()}}</span>
|
||||
<span class="label">{{ngModel.getText()}}</span>
|
||||
</div>
|
||||
|
@ -54,7 +54,6 @@ define(
|
||||
if (isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
var removeSelectable = openmct.selection.selectable(
|
||||
element[0],
|
||||
scope.$eval(attrs.mctSelectable),
|
||||
|
@ -1,8 +1,8 @@
|
||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
|
||||
<div ng-show="notifications.length > 0" class="c-indicator c-indicator--clickable s-status-{{highest.severity}} icon-bell"
|
||||
<div ng-show="notifications.length > 0" class="ls-indicator s-status-{{highest.severity}} icon-bell"
|
||||
ng-controller="NotificationIndicatorController">
|
||||
<span class="label c-indicator__label">
|
||||
<button ng-click="showNotificationsList()">
|
||||
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span></button>
|
||||
</span><span class="c-indicator__count">{{notifications.length}}</span>
|
||||
<span class="label">
|
||||
<a ng-click="showNotificationsList()">
|
||||
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span></a>
|
||||
</span><span class="count">{{notifications.length}}</span>
|
||||
</div>
|
||||
|
@ -43,10 +43,23 @@ define([], function () {
|
||||
var mutationTopic = topic('mutation');
|
||||
mutationTopic.listen(function (domainObject) {
|
||||
var persistence = domainObject.getCapability('persistence');
|
||||
var wasActive = transactionService.isActive();
|
||||
cacheService.put(domainObject.getId(), domainObject.getModel());
|
||||
|
||||
if (hasChanged(domainObject)) {
|
||||
persistence.persist();
|
||||
|
||||
if (!wasActive) {
|
||||
transactionService.startTransaction();
|
||||
}
|
||||
|
||||
transactionService.addToTransaction(
|
||||
persistence.persist.bind(persistence),
|
||||
persistence.refresh.bind(persistence)
|
||||
);
|
||||
|
||||
if (!wasActive) {
|
||||
transactionService.commit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -24,27 +24,22 @@ define(
|
||||
["../../src/runs/TransactingMutationListener"],
|
||||
function (TransactingMutationListener) {
|
||||
|
||||
describe("TransactingMutationListener", function () {
|
||||
xdescribe("TransactingMutationListener", function () {
|
||||
var mockTopic,
|
||||
mockMutationTopic,
|
||||
mockCacheService,
|
||||
mockTransactionService,
|
||||
mockDomainObject,
|
||||
mockModel,
|
||||
mockPersistence;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTopic = jasmine.createSpy('topic');
|
||||
mockMutationTopic =
|
||||
jasmine.createSpyObj('mutation', ['listen']);
|
||||
mockCacheService =
|
||||
jasmine.createSpyObj('cacheService', [
|
||||
'put'
|
||||
]);
|
||||
mockTransactionService =
|
||||
jasmine.createSpyObj('transactionService', [
|
||||
'isActive',
|
||||
'startTransaction',
|
||||
'addToTransaction',
|
||||
'commit'
|
||||
]);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
@ -57,24 +52,18 @@ define(
|
||||
);
|
||||
|
||||
mockTopic.and.callFake(function (t) {
|
||||
expect(t).toBe('mutation');
|
||||
return mockMutationTopic;
|
||||
return (t === 'mutation') && mockMutationTopic;
|
||||
});
|
||||
|
||||
mockDomainObject.getId.and.returnValue('mockId');
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
expect(c).toBe('persistence');
|
||||
return mockPersistence;
|
||||
return (c === 'persistence') && mockPersistence;
|
||||
});
|
||||
mockModel = {};
|
||||
mockDomainObject.getModel.and.returnValue(mockModel);
|
||||
|
||||
mockPersistence.persisted.and.returnValue(true);
|
||||
|
||||
return new TransactingMutationListener(
|
||||
mockTopic,
|
||||
mockTransactionService,
|
||||
mockCacheService
|
||||
mockTransactionService
|
||||
);
|
||||
});
|
||||
|
||||
@ -83,27 +72,48 @@ define(
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("calls persist if the model has changed", function () {
|
||||
mockModel.persisted = Date.now();
|
||||
[false, true].forEach(function (isActive) {
|
||||
var verb = isActive ? "is" : "isn't";
|
||||
|
||||
//Mark the model dirty by setting the mutated date later than the last persisted date.
|
||||
mockModel.modified = mockModel.persisted + 1;
|
||||
function onlyWhenInactive(expectation) {
|
||||
return isActive ? expectation.not : expectation;
|
||||
}
|
||||
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
describe("when a transaction " + verb + " active", function () {
|
||||
var innerVerb = isActive ? "does" : "doesn't";
|
||||
|
||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||
});
|
||||
beforeEach(function () {
|
||||
mockTransactionService.isActive.and.returnValue(isActive);
|
||||
});
|
||||
|
||||
it("does not call persist if the model has not changed", function () {
|
||||
mockModel.persisted = Date.now();
|
||||
describe("and mutation occurs", function () {
|
||||
beforeEach(function () {
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
});
|
||||
|
||||
mockModel.modified = mockModel.persisted;
|
||||
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
it(innerVerb + " start a new transaction", function () {
|
||||
onlyWhenInactive(
|
||||
expect(mockTransactionService.startTransaction)
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(mockPersistence.persist).not.toHaveBeenCalled();
|
||||
it("adds to the active transaction", function () {
|
||||
expect(mockTransactionService.addToTransaction)
|
||||
.toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it(innerVerb + " immediately commit", function () {
|
||||
onlyWhenInactive(
|
||||
expect(mockTransactionService.commit)
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ define([
|
||||
"./src/actions/MoveAction",
|
||||
"./src/actions/CopyAction",
|
||||
"./src/actions/LinkAction",
|
||||
"./src/actions/GoToOriginalAction",
|
||||
"./src/actions/SetPrimaryLocationAction",
|
||||
"./src/services/LocatingCreationDecorator",
|
||||
"./src/services/LocatingObjectDecorator",
|
||||
@ -40,6 +41,7 @@ define([
|
||||
MoveAction,
|
||||
CopyAction,
|
||||
LinkAction,
|
||||
GoToOriginalAction,
|
||||
SetPrimaryLocationAction,
|
||||
LocatingCreationDecorator,
|
||||
LocatingObjectDecorator,
|
||||
@ -102,6 +104,14 @@ 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",
|
||||
|
60
platform/entanglement/src/actions/GoToOriginalAction.js
Normal file
60
platform/entanglement/src/actions/GoToOriginalAction.js
Normal file
@ -0,0 +1,60 @@
|
||||
/*****************************************************************************
|
||||
* 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;
|
||||
}
|
||||
);
|
||||
|
93
platform/entanglement/test/actions/GoToOriginalActionSpec.js
Normal file
93
platform/entanglement/test/actions/GoToOriginalActionSpec.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*****************************************************************************
|
||||
* 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');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -49,7 +49,7 @@ define(
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getCssClass = function () {
|
||||
return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable";
|
||||
return "t-indicator-clock icon-clock no-collapse float-right";
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getText = function () {
|
||||
|
@ -64,30 +64,12 @@ define(['zepto'], function ($) {
|
||||
var tree = this.generateNewIdentifiers(objTree);
|
||||
var rootId = tree.rootId;
|
||||
var rootObj = this.instantiate(tree.openmct[rootId], rootId);
|
||||
var newStyleParent = parent.useCapability('adapter');
|
||||
var newStyleRootObj = rootObj.useCapability('adapter');
|
||||
|
||||
if (this.openmct.composition.checkPolicy(newStyleParent, newStyleRootObj)) {
|
||||
// Instantiate all objects in tree with their newly generated ids,
|
||||
// adding each to its rightful parent's composition
|
||||
rootObj.getCapability("location").setPrimaryLocation(parent.getId());
|
||||
this.deepInstantiate(rootObj, tree.openmct, []);
|
||||
parent.getCapability("composition").add(rootObj);
|
||||
} else {
|
||||
var dialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: "We're sorry, but you cannot import that object type into this object.",
|
||||
buttons: [
|
||||
{
|
||||
label: "Ok",
|
||||
emphasis: true,
|
||||
callback: function () {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
// Instantiate all objects in tree with their newly genereated ids,
|
||||
// adding each to its rightful parent's composition
|
||||
rootObj.getCapability("location").setPrimaryLocation(parent.getId());
|
||||
this.deepInstantiate(rootObj, tree.openmct, []);
|
||||
parent.getCapability("composition").add(rootObj);
|
||||
};
|
||||
|
||||
ImportAsJSONAction.prototype.deepInstantiate = function (parent, tree, seen) {
|
||||
@ -98,17 +80,15 @@ define(['zepto'], function ($) {
|
||||
var newObj;
|
||||
|
||||
seen.push(parent.getId());
|
||||
|
||||
parentModel.composition.forEach(function (childId) {
|
||||
let keystring = this.openmct.objects.makeKeyString(childId);
|
||||
|
||||
if (!tree[keystring] || seen.includes(keystring)) {
|
||||
parentModel.composition.forEach(function (childId, index) {
|
||||
if (!tree[childId] || seen.includes(childId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
newObj = this.instantiate(tree[keystring], keystring);
|
||||
newObj = this.instantiate(tree[childId], childId);
|
||||
parent.getCapability("composition").add(newObj);
|
||||
newObj.getCapability("location")
|
||||
.setPrimaryLocation(tree[keystring].location);
|
||||
.setPrimaryLocation(tree[childId].location);
|
||||
this.deepInstantiate(newObj, tree, seen);
|
||||
}, this);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ define(
|
||||
}
|
||||
|
||||
CouchIndicator.prototype.getCssClass = function () {
|
||||
return "c-indicator--clickable icon-database " + this.state.statusClass;
|
||||
return "icon-database " + this.state.statusClass;
|
||||
};
|
||||
|
||||
CouchIndicator.prototype.getGlyphClass = function () {
|
||||
|
@ -84,7 +84,7 @@ define(
|
||||
}
|
||||
|
||||
ElasticIndicator.prototype.getCssClass = function () {
|
||||
return "c-indicator--clickable icon-database";
|
||||
return "icon-database";
|
||||
};
|
||||
ElasticIndicator.prototype.getGlyphClass = function () {
|
||||
return this.state.glyphClass;
|
||||
|
@ -41,7 +41,7 @@ define(
|
||||
}
|
||||
|
||||
LocalStorageIndicator.prototype.getCssClass = function () {
|
||||
return "c-indicator--clickable icon-database s-status-caution";
|
||||
return "icon-database s-status-caution";
|
||||
};
|
||||
LocalStorageIndicator.prototype.getGlyphClass = function () {
|
||||
return 'caution';
|
||||
|
@ -246,21 +246,12 @@ define([
|
||||
this.branding = BrandingAPI.default;
|
||||
|
||||
this.legacyRegistry = defaultRegistry;
|
||||
|
||||
// Plugin's that are installed by default
|
||||
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(PreviewPlugin.default());
|
||||
this.install(LegacyIndicatorsPlugin());
|
||||
this.install(LicensesPlugin.default());
|
||||
this.install(RemoveActionPlugin.default());
|
||||
this.install(this.plugins.ImportExport());
|
||||
this.install(this.plugins.FolderView());
|
||||
this.install(this.plugins.Tabs());
|
||||
this.install(this.plugins.FlexibleLayout());
|
||||
this.install(this.plugins.LADTable());
|
||||
this.install(this.plugins.GoToOriginalAction());
|
||||
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
this.install(buildInfoPlugin(BUILD_CONSTANTS));
|
||||
|
@ -26,7 +26,6 @@ const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "li
|
||||
export default class LegacyContextMenuAction {
|
||||
constructor(openmct, LegacyAction) {
|
||||
this.openmct = openmct;
|
||||
this.key = LegacyAction.definition.key;
|
||||
this.name = LegacyAction.definition.name;
|
||||
this.description = LegacyAction.definition.description;
|
||||
this.cssClass = LegacyAction.definition.cssClass;
|
||||
@ -34,25 +33,20 @@ export default class LegacyContextMenuAction {
|
||||
}
|
||||
|
||||
invoke(objectPath) {
|
||||
this.openmct.objects.getRoot().then((root) => {
|
||||
let pathWithRoot = objectPath.slice();
|
||||
pathWithRoot.push(root);
|
||||
let context = {
|
||||
category: 'contextual',
|
||||
domainObject: this.openmct.legacyObject(objectPath)
|
||||
}
|
||||
let legacyAction = new this.LegacyAction(context);
|
||||
|
||||
let context = {
|
||||
category: 'contextual',
|
||||
domainObject: this.openmct.legacyObject(pathWithRoot)
|
||||
}
|
||||
let legacyAction = new this.LegacyAction(context);
|
||||
|
||||
if (!legacyAction.getMetadata) {
|
||||
let metadata = Object.create(this.LegacyAction.definition);
|
||||
metadata.context = context;
|
||||
legacyAction.getMetadata = function () {
|
||||
return metadata;
|
||||
}.bind(legacyAction);
|
||||
}
|
||||
legacyAction.perform();
|
||||
});
|
||||
if (!legacyAction.getMetadata) {
|
||||
let metadata = Object.create(this.LegacyAction.definition);
|
||||
metadata.context = context;
|
||||
legacyAction.getMetadata = function () {
|
||||
return metadata;
|
||||
}.bind(legacyAction);
|
||||
}
|
||||
legacyAction.perform();
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
|
@ -36,7 +36,7 @@ define([
|
||||
'./runs/RegisterLegacyTypes',
|
||||
'./services/LegacyObjectAPIInterceptor',
|
||||
'./views/installLegacyViews',
|
||||
'./policies/LegacyCompositionPolicyAdapter',
|
||||
'./policies/legacyCompositionPolicyAdapter',
|
||||
'./actions/LegacyActionAdapter'
|
||||
], function (
|
||||
legacyRegistry,
|
||||
|
@ -45,30 +45,15 @@ define([
|
||||
view: function (domainObject) {
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new(true);
|
||||
let scope = $rootScope.$new();
|
||||
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) => {
|
||||
child.classList.remove('s-status-timeconductor-unsynced');
|
||||
|
||||
if (newStatus.includes('timeconductor-unsynced')) {
|
||||
child.classList.add('s-status-timeconductor-unsynced');
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: implement "gestures" support ?
|
||||
let uses = legacyView.uses || [];
|
||||
let promises = [];
|
||||
@ -89,13 +74,12 @@ define([
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
element = openmct.$angular.element(child);
|
||||
templateLinker.link(
|
||||
scope,
|
||||
element,
|
||||
openmct.$angular.element(container),
|
||||
legacyView
|
||||
);
|
||||
child.classList.add('u-contents');
|
||||
container.classList.add('u-contents');
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
@ -108,16 +92,8 @@ define([
|
||||
link();
|
||||
}
|
||||
},
|
||||
onClearData() {
|
||||
scope.$broadcast('clearData');
|
||||
},
|
||||
destroy: function () {
|
||||
element.off();
|
||||
element.remove();
|
||||
scope.$destroy();
|
||||
element = null;
|
||||
scope = null;
|
||||
unlistenToStatus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -25,7 +25,7 @@ define([
|
||||
cssClass: representation.cssClass,
|
||||
description: representation.description,
|
||||
canView: function (selection) {
|
||||
if (selection.length !== 1 || selection[0].length === 0) {
|
||||
if (selection.length === 0 || selection[0].length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -41,18 +41,15 @@ define([
|
||||
let domainObject = selection[0][0].context.item;
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new(true);
|
||||
let scope = $rootScope.$new();
|
||||
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 = [];
|
||||
@ -73,10 +70,9 @@ define([
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
element = openmct.$angular.element(child)
|
||||
templateLinker.link(
|
||||
scope,
|
||||
element,
|
||||
openmct.$angular.element(container),
|
||||
representation
|
||||
);
|
||||
container.style.height = '100%';
|
||||
@ -93,11 +89,7 @@ define([
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
element.off();
|
||||
element.remove();
|
||||
scope.$destroy();
|
||||
element = null;
|
||||
scope = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ export default class Editor extends EventEmitter {
|
||||
super();
|
||||
this.editing = false;
|
||||
this.openmct = openmct;
|
||||
document.addEventListener('drop', (event) => {
|
||||
if (!this.isEditing()) {
|
||||
this.edit();
|
||||
}
|
||||
}, {capture: true});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,20 +22,8 @@ define([
|
||||
publicAPI = {};
|
||||
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
|
||||
'get',
|
||||
'mutate',
|
||||
'observe',
|
||||
'areIdsEqual'
|
||||
'mutate'
|
||||
]);
|
||||
|
||||
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'
|
||||
]);
|
||||
@ -103,7 +91,7 @@ define([
|
||||
beforeEach(function () {
|
||||
listener = jasmine.createSpy('reorderListener');
|
||||
composition.on('reorder', listener);
|
||||
|
||||
|
||||
return composition.load();
|
||||
});
|
||||
it('', function () {
|
||||
@ -131,16 +119,49 @@ define([
|
||||
expect(newComposition[2].key).toEqual('a');
|
||||
})
|
||||
});
|
||||
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.add(mockChildObject);
|
||||
|
||||
expect(domainObject.composition.length).toBe(4);
|
||||
expect(domainObject.composition[3]).toEqual(mockChildObject.identifier);
|
||||
// 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');
|
||||
composition.on('add', addListener);
|
||||
composition.on('remove', removeListener);
|
||||
otherComposition.on('add', otherAddListener);
|
||||
otherComposition.on('remove', otherRemoveListener);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -163,9 +184,7 @@ define([
|
||||
key: 'thing'
|
||||
}
|
||||
]);
|
||||
},
|
||||
add: jasmine.createSpy('add'),
|
||||
remove: jasmine.createSpy('remove')
|
||||
}
|
||||
};
|
||||
domainObject = {
|
||||
identifier: {
|
||||
@ -195,25 +214,6 @@ 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,7 +75,9 @@ define([
|
||||
throw new Error('Event not supported by composition: ' + event);
|
||||
}
|
||||
if (!this.mutationListener) {
|
||||
this._synchronize();
|
||||
this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
|
||||
this.domainObject = newDomainObject;
|
||||
})
|
||||
}
|
||||
if (this.provider.on && this.provider.off) {
|
||||
if (event === 'add') {
|
||||
@ -132,8 +134,10 @@ define([
|
||||
|
||||
this.listeners[event].splice(index, 1);
|
||||
if (this.listeners[event].length === 0) {
|
||||
this._destroy();
|
||||
|
||||
if (this.mutationListener) {
|
||||
this.mutationListener();
|
||||
delete this.mutationListener;
|
||||
}
|
||||
// Remove provider listener if this is the last callback to
|
||||
// be removed.
|
||||
if (this.provider.off && this.provider.on) {
|
||||
@ -177,9 +181,6 @@ 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);
|
||||
@ -271,19 +272,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
|
||||
|
@ -48,11 +48,24 @@ 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
|
||||
*/
|
||||
@ -186,18 +199,9 @@ define([
|
||||
* @memberof module:openmct.CompositionProvider#
|
||||
* @method add
|
||||
*/
|
||||
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.add = function (domainObject, child) {
|
||||
throw new Error('Default Provider does not implement adding.');
|
||||
// TODO: this needs to be synchronized via mutation
|
||||
};
|
||||
|
||||
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
|
||||
|
@ -49,9 +49,6 @@ class ContextMenuAPI {
|
||||
* a single sentence or short paragraph) of this kind of view
|
||||
* @property {string} cssClass the CSS class to apply to labels for this
|
||||
* view (to add icons, for instance)
|
||||
* @property {string} key unique key to identify the context menu action
|
||||
* (used in custom context menu eg table rows, to identify which actions to include)
|
||||
* @property {boolean} hideInDefaultMenu optional flag to hide action from showing in the default context menu (tree item)
|
||||
*/
|
||||
/**
|
||||
* @method appliesTo
|
||||
@ -75,21 +72,12 @@ class ContextMenuAPI {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_showContextMenuForObjectPath(objectPath, x, y, actionsToBeIncluded) {
|
||||
|
||||
_showContextMenuForObjectPath(objectPath, x, y) {
|
||||
let applicableActions = this._allActions.filter((action) => {
|
||||
|
||||
if (actionsToBeIncluded) {
|
||||
if (action.appliesTo === undefined && actionsToBeIncluded.includes(action.key)) {
|
||||
return true;
|
||||
}
|
||||
return action.appliesTo(objectPath, actionsToBeIncluded) && actionsToBeIncluded.includes(action.key);
|
||||
} else {
|
||||
if (action.appliesTo === undefined) {
|
||||
return true;
|
||||
}
|
||||
return action.appliesTo(objectPath) && !action.hideInDefaultMenu;
|
||||
if (action.appliesTo === undefined) {
|
||||
return true;
|
||||
}
|
||||
return action.appliesTo(objectPath);
|
||||
});
|
||||
|
||||
if (this._activeContextMenu) {
|
||||
|
@ -28,7 +28,7 @@ define(['zepto', './res/indicator-template.html'],
|
||||
this.openmct = openmct;
|
||||
this.element = $(indicatorTemplate)[0];
|
||||
|
||||
this.textElement = this.element.querySelector('.js-indicator-text');
|
||||
this.textElement = this.element.querySelector('.indicator-text');
|
||||
|
||||
//Set defaults
|
||||
this.text('New Indicator');
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div class="c-indicator c-indicator--clickable c-indicator--simple" title="">
|
||||
<span class="label js-indicator-text c-indicator__label"></span>
|
||||
<div class="ls-indicator" title="">
|
||||
<span class="label indicator-text"></span>
|
||||
</div>
|
||||
|
@ -93,7 +93,7 @@
|
||||
&.message-severity-error:before {
|
||||
@include legacyMessage();
|
||||
content: $glyph-icon-alert-triangle;
|
||||
color: $colorWarningHi;
|
||||
color: $colorWarningLo;
|
||||
}
|
||||
|
||||
// Messages in a list
|
||||
|
@ -69,7 +69,6 @@
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__top-bar {
|
||||
@ -93,7 +92,6 @@
|
||||
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
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ define([
|
||||
canEdit: function (domainObject) {
|
||||
return domainObject.type === 'LadTableSet';
|
||||
},
|
||||
view: function (domainObject, isEditing, objectPath) {
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
|
||||
return {
|
||||
@ -49,8 +49,7 @@ define([
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
objectPath
|
||||
domainObject
|
||||
},
|
||||
el: element,
|
||||
template: '<lad-table-set></lad-table-set>'
|
||||
|
@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./components/LADTable.vue',
|
||||
'./components/LadTable.vue',
|
||||
'vue'
|
||||
], function (
|
||||
LadTableComponent,
|
||||
@ -38,7 +38,7 @@ define([
|
||||
canEdit: function (domainObject) {
|
||||
return domainObject.type === 'LadTable';
|
||||
},
|
||||
view: function (domainObject, isEditing, objectPath) {
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
|
||||
return {
|
||||
@ -49,8 +49,7 @@ define([
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
objectPath
|
||||
domainObject
|
||||
},
|
||||
el: element,
|
||||
template: '<lad-table-component></lad-table-component>'
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
@ -22,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<tr @contextmenu.prevent="showContextMenu">
|
||||
<tr>
|
||||
<td>{{name}}</td>
|
||||
<td>{{timestamp}}</td>
|
||||
<td :class="valueClass">
|
||||
@ -36,25 +35,15 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
const CONTEXT_MENU_ACTIONS = [
|
||||
'viewHistoricalData',
|
||||
'remove'
|
||||
];
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'objectPath'],
|
||||
inject: ['openmct'],
|
||||
props: ['domainObject'],
|
||||
data() {
|
||||
let currentObjectPath = this.objectPath.slice();
|
||||
currentObjectPath.unshift(this.domainObject);
|
||||
|
||||
return {
|
||||
name: this.domainObject.name,
|
||||
timestamp: '---',
|
||||
value: '---',
|
||||
valueClass: '',
|
||||
currentObjectPath
|
||||
valueClass: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -84,15 +73,11 @@ export default {
|
||||
.request(this.domainObject, {strategy: 'latest'})
|
||||
.then((array) => this.updateValues(array[array.length - 1]));
|
||||
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
this.limitEvaluator = openmct
|
||||
.telemetry
|
||||
|
@ -41,10 +41,10 @@
|
||||
|
||||
<script>
|
||||
import lodash from 'lodash';
|
||||
import LadRow from './LADRow.vue';
|
||||
import LadRow from './LadRow.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject', 'objectPath'],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
components: {
|
||||
LadRow
|
||||
},
|
||||
|
@ -52,7 +52,7 @@
|
||||
|
||||
<script>
|
||||
import lodash from 'lodash';
|
||||
import LadRow from './LADRow.vue';
|
||||
import LadRow from './LadRow.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
|
@ -1,39 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class ClearDataAction {
|
||||
constructor(openmct, appliesToObjects) {
|
||||
this.name = 'Clear Data';
|
||||
this.description = 'Clears current data for object, unsubscribes and resubscribes to data';
|
||||
|
||||
this._openmct = openmct;
|
||||
this._appliesToObjects = appliesToObjects;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
this._openmct.objectViews.emit('clearData', objectPath[0]);
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
let contextualDomainObject = objectPath[0];
|
||||
|
||||
return this._appliesToObjects.filter(type => contextualDomainObject.type === type).length;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<template>
|
||||
<div class="c-indicator c-indicator--clickable icon-session">
|
||||
<span class="label c-indicator__label">
|
||||
<button @click="globalClearEmit">Clear All Data</button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
methods: {
|
||||
globalClearEmit() {
|
||||
this.openmct.objectViews.emit('clearData');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,54 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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([
|
||||
'./components/globalClearIndicator.vue',
|
||||
'./clearDataAction',
|
||||
'vue'
|
||||
], function (
|
||||
GlobaClearIndicator,
|
||||
ClearDataAction,
|
||||
Vue
|
||||
) {
|
||||
return function plugin(appliesToObjects) {
|
||||
appliesToObjects = appliesToObjects || [];
|
||||
|
||||
return function install(openmct) {
|
||||
let component = new Vue ({
|
||||
provide: {
|
||||
openmct
|
||||
},
|
||||
components: {
|
||||
GlobalClearIndicator: GlobaClearIndicator.default
|
||||
},
|
||||
template: '<GlobalClearIndicator></GlobalClearIndicator>'
|
||||
}),
|
||||
indicator = {
|
||||
element: component.$mount().$el
|
||||
};
|
||||
|
||||
openmct.indicators.add(indicator);
|
||||
|
||||
openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
|
||||
};
|
||||
};
|
||||
});
|
@ -1,62 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
import ClearDataActionPlugin from '../plugin.js';
|
||||
import ClearDataAction from '../clearDataAction.js';
|
||||
|
||||
describe('When the Clear Data Plugin is installed,', function () {
|
||||
var mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']),
|
||||
mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']),
|
||||
mockContextMenuProvider = jasmine.createSpyObj('contextMenu', ['registerAction']),
|
||||
openmct = {
|
||||
objectViews: mockObjectViews,
|
||||
indicators: mockIndicatorProvider,
|
||||
contextMenu: mockContextMenuProvider,
|
||||
install: function (plugin) {
|
||||
plugin(this);
|
||||
}
|
||||
},
|
||||
mockObjectPath = [
|
||||
{name: 'mockObject1'},
|
||||
{name: 'mockObject2'}
|
||||
];
|
||||
|
||||
it('Global Clear Indicator is installed', function () {
|
||||
openmct.install(ClearDataActionPlugin([]));
|
||||
|
||||
expect(mockIndicatorProvider.add).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Clear Data context menu action is installed', function () {
|
||||
openmct.install(ClearDataActionPlugin([]));
|
||||
|
||||
expect(mockContextMenuProvider.registerAction).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('clear data action emits a clearData event when invoked', function () {
|
||||
let action = new ClearDataAction(openmct);
|
||||
|
||||
action.invoke(mockObjectPath);
|
||||
|
||||
expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
|
||||
});
|
||||
});
|
@ -1,78 +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([
|
||||
'./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 (domainObject, isEditing, objectPath) {
|
||||
let component;
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
provide: {
|
||||
openmct,
|
||||
objectPath
|
||||
},
|
||||
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;
|
||||
});
|
@ -1,90 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-properties" v-if="isEditing">
|
||||
<div class="c-properties__header">Alphanumeric Format</div>
|
||||
<ul class="c-properties__section">
|
||||
<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"
|
||||
:placeholder="nonMixedFormat ? '' : 'Mixed'"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
let selectionPath = this.openmct.selection.get()[0];
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
telemetryFormat: undefined,
|
||||
nonMixedFormat: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
formatTelemetry(event) {
|
||||
let newFormat = event.currentTarget.value;
|
||||
this.openmct.selection.get().forEach(selectionPath => {
|
||||
selectionPath[0].context.updateTelemetryFormat(newFormat);
|
||||
});
|
||||
this.telemetryFormat = newFormat;
|
||||
},
|
||||
handleSelection(selection) {
|
||||
if (selection.length === 0 || selection[0].length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let format = selection[0][0].context.layoutItem.format;
|
||||
this.nonMixedFormat = selection.every(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.format === format;
|
||||
});
|
||||
|
||||
this.telemetryFormat = this.nonMixedFormat ? format : '';
|
||||
}
|
||||
},
|
||||
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,8 +48,7 @@
|
||||
:multiSelect="selectedLayoutItems.length > 1"
|
||||
@move="move"
|
||||
@endMove="endMove"
|
||||
@endLineResize='endLineResize'
|
||||
@formatChanged='updateTelemetryFormat'>
|
||||
@endLineResize='endLineResize'>
|
||||
</component>
|
||||
<edit-marquee v-if='showMarquee'
|
||||
:gridSize="gridSize"
|
||||
@ -202,7 +201,7 @@
|
||||
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
|
||||
}
|
||||
},
|
||||
inject: ['openmct', 'options', 'objectPath'],
|
||||
inject: ['openmct', 'options'],
|
||||
props: ['domainObject'],
|
||||
components: components,
|
||||
methods: {
|
||||
@ -270,63 +269,33 @@
|
||||
_.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]) {
|
||||
this.updateItemPosition(item, gridDelta);
|
||||
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];
|
||||
}
|
||||
}
|
||||
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);
|
||||
@ -558,11 +527,6 @@
|
||||
this.layoutItems.splice(itemIndex, 1);
|
||||
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
|
||||
}
|
||||
},
|
||||
updateTelemetryFormat(item, format) {
|
||||
let index = _.findIndex(this.layoutItems, 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 !== undefined) {
|
||||
if (item.x2) {
|
||||
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 !== undefined) {
|
||||
if (item.y2) {
|
||||
let lineHeight = Math.abs(item.y - item.y2);
|
||||
let lineMinY = Math.min(item.y, item.y2);
|
||||
y = Math.min(lineMinY, y);
|
||||
|
@ -22,12 +22,11 @@
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
:title="domainObject && domainObject.name"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
<object-frame v-if="domainObject"
|
||||
:domain-object="domainObject"
|
||||
:object-path="currentObjectPath"
|
||||
:object-path="objectPath"
|
||||
:has-frame="item.hasFrame"
|
||||
:show-edit-view="false"
|
||||
ref="objectFrame">
|
||||
@ -71,7 +70,7 @@
|
||||
hasFrame: hasFrameByDefault(domainObject.type)
|
||||
};
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
@ -81,7 +80,7 @@
|
||||
data() {
|
||||
return {
|
||||
domainObject: undefined,
|
||||
currentObjectPath: []
|
||||
objectPath: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@ -100,7 +99,7 @@
|
||||
methods: {
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.currentObjectPath = [this.domainObject].concat(this.objectPath.slice());
|
||||
this.objectPath = [this.domainObject].concat(this.openmct.router.path);
|
||||
this.$nextTick(function () {
|
||||
let childContext = this.$refs.objectFrame.getSelectionContext();
|
||||
childContext.item = domainObject;
|
||||
|
@ -27,8 +27,7 @@
|
||||
@endMove="() => $emit('endMove')">
|
||||
<div class="c-telemetry-view"
|
||||
:style="styleObject"
|
||||
v-if="domainObject"
|
||||
@contextmenu.prevent="showContextMenu">
|
||||
v-if="domainObject">
|
||||
<div v-if="showLabel"
|
||||
class="c-telemetry-view__label">
|
||||
<div class="c-telemetry-view__label-text">{{ domainObject.name }}</div>
|
||||
@ -80,11 +79,9 @@
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import printj from 'printj'
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
CONTEXT_MENU_ACTIONS = ['viewHistoricalData'];
|
||||
DEFAULT_POSITION = [1, 1];
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
@ -105,7 +102,7 @@
|
||||
size: "13px"
|
||||
};
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
@ -146,10 +143,6 @@
|
||||
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() {
|
||||
@ -165,8 +158,7 @@
|
||||
return {
|
||||
datum: undefined,
|
||||
formats: undefined,
|
||||
domainObject: undefined,
|
||||
currentObjectPath: undefined
|
||||
domainObject: undefined
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -176,9 +168,6 @@
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
},
|
||||
item(newItem) {
|
||||
this.context.layoutItem = newItem;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -187,8 +176,7 @@
|
||||
let options = {
|
||||
start: bounds.start,
|
||||
end: bounds.end,
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
size: 1
|
||||
};
|
||||
this.openmct.telemetry.request(this.domainObject, options)
|
||||
.then(data => {
|
||||
@ -221,30 +209,19 @@
|
||||
},
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.requestHistoricalData();
|
||||
this.subscribeToObject();
|
||||
|
||||
this.currentObjectPath = this.objectPath.slice();
|
||||
this.currentObjectPath.unshift(this.domainObject);
|
||||
|
||||
this.context = {
|
||||
item: domainObject,
|
||||
layoutItem: this.item,
|
||||
index: this.index,
|
||||
updateTelemetryFormat: this.updateTelemetryFormat
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
updateTelemetryFormat(format) {
|
||||
this.$emit('formatChanged', this.item, format);
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -25,8 +25,6 @@ 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({
|
||||
@ -37,7 +35,7 @@ export default function DisplayLayoutPlugin(options) {
|
||||
canEdit: function (domainObject) {
|
||||
return domainObject.type === 'layout';
|
||||
},
|
||||
view: function (domainObject, isEditing, objectPath) {
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
return {
|
||||
show(container) {
|
||||
@ -49,14 +47,13 @@ export default function DisplayLayoutPlugin(options) {
|
||||
provide: {
|
||||
openmct,
|
||||
objectUtils,
|
||||
options,
|
||||
objectPath
|
||||
options
|
||||
},
|
||||
el: container,
|
||||
data () {
|
||||
return {
|
||||
domainObject: domainObject
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -79,8 +76,7 @@ export default function DisplayLayoutPlugin(options) {
|
||||
}
|
||||
});
|
||||
openmct.types.addType('layout', DisplayLayoutType());
|
||||
openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct, options));
|
||||
openmct.inspectorViews.addProvider(new AlphaNumericFormatViewProvider(openmct, options));
|
||||
openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct));
|
||||
openmct.composition.addPolicy((parent, child) => {
|
||||
if (parent.type === 'layout' && child.type === 'folder') {
|
||||
return false;
|
||||
|
@ -42,10 +42,7 @@
|
||||
|
||||
<!-- Checkbox list, NOT editing -->
|
||||
<template v-if="filter.possibleValues && !isEditing">
|
||||
<span
|
||||
v-if="persistedFilters[filter.comparator]">
|
||||
{{persistedFilters[filter.comparator].join(', ')}}
|
||||
</span>
|
||||
<span>{{persistedFilters[filter.comparator].join(', ')}}</span>
|
||||
</template>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -63,37 +63,22 @@ export default {
|
||||
|
||||
if (filterValue && filterValue[comparator]) {
|
||||
if (value === false) {
|
||||
let filteredValueName = filterValue[comparator].filter(v => v !== valueName);
|
||||
|
||||
if (filteredValueName.length === 0) {
|
||||
delete this.updatedFilters[key];
|
||||
} else {
|
||||
filterValue[comparator] = filteredValueName;
|
||||
}
|
||||
filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName);
|
||||
} else {
|
||||
filterValue[comparator].push(valueName);
|
||||
}
|
||||
} else {
|
||||
if (!this.updatedFilters[key]) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
this.updatedFilters[key] = {};
|
||||
}
|
||||
this.$set(this.updatedFilters[key], comparator, [value ? valueName : undefined]);
|
||||
this.updatedFilters[key][comparator] = [value ? valueName : undefined];
|
||||
}
|
||||
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
},
|
||||
updateTextFilter(key, comparator, value) {
|
||||
if (value.trim() === '') {
|
||||
if (this.updatedFilters[key]) {
|
||||
delete this.updatedFilters[key];
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.updatedFilters[key]) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
this.$set(this.updatedFilters[key], comparator, '');
|
||||
this.updatedFilters[key] = {};
|
||||
}
|
||||
this.updatedFilters[key][comparator] = value;
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
|
@ -59,18 +59,14 @@ export default {
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
this.$delete(this.children, keyString);
|
||||
delete this.persistedFilters[keyString];
|
||||
this.mutateConfigurationFilters();
|
||||
this.persistFilters(keyString);
|
||||
},
|
||||
persistFilters(keyString, userSelects) {
|
||||
this.persistedFilters[keyString] = userSelects;
|
||||
this.mutateConfigurationFilters();
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
|
||||
},
|
||||
updatePersistedFilters(filters) {
|
||||
this.persistedFilters = filters;
|
||||
},
|
||||
mutateConfigurationFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
|
@ -113,8 +113,7 @@
|
||||
|
||||
&__container-holder {
|
||||
display: flex;
|
||||
flex: 1 1 100%; // Must be 100% to work
|
||||
overflow: auto;
|
||||
flex: 1 1 100%; // Must needs to be 100% to work
|
||||
|
||||
// Columns by default
|
||||
flex-direction: row;
|
||||
|
@ -1,54 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class GoToOriginalAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Go To Original';
|
||||
this.key = 'goToOriginal';
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,28 +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.
|
||||
*****************************************************************************/
|
||||
import GoToOriginalAction from './goToOriginalAction';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.contextMenu.registerAction(new GoToOriginalAction(openmct));
|
||||
};
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<div class="c-ne__embed">
|
||||
<div class="c-ne__embed__snap-thumb"
|
||||
v-if="embed.snapshot">
|
||||
<img v-bind:src="embed.snapshot.src">
|
||||
</div>
|
||||
<div class="c-ne__embed__info">
|
||||
<div class="c-ne__embed__name">
|
||||
<a class="c-ne__embed__link"
|
||||
:href="objectLink"
|
||||
:class="embed.cssClass">
|
||||
{{embed.name}}
|
||||
</a>
|
||||
<a class="c-ne__embed__context-available icon-arrow-down"></a>
|
||||
</div>
|
||||
<div class="c-ne__embed__time" v-if="embed.snapshot">
|
||||
{{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import objectLinkMixin from '../../../ui/mixins/object-link';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'formatTime'],
|
||||
props: ['embed'],
|
||||
mixins: [objectLinkMixin]
|
||||
}
|
||||
</script>
|
@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<li class="c-notebook__entry c-ne has-local-controls"
|
||||
@dragover.prevent
|
||||
@drop.prevent="onTreeItemDrop">
|
||||
<div class="c-ne__time-and-content">
|
||||
<div class="c-ne__time">
|
||||
<span>{{formatTime(entry.createdOn, 'YYYY-MM-DD')}}</span>
|
||||
<span>{{formatTime(entry.createdOn, 'HH:mm:ss')}}</span>
|
||||
</div>
|
||||
<div class="c-ne__content">
|
||||
<div class="c-ne__text c-input-inline"
|
||||
contenteditable="true"
|
||||
ref="contenteditable"
|
||||
@blur="updateEntry"
|
||||
v-html="entry.text">
|
||||
</div>
|
||||
<div class="c-ne__embeds">
|
||||
<notebook-embed
|
||||
v-for="embed in entry.embeds"
|
||||
:key="embed.id"
|
||||
:embed="embed"
|
||||
:objectPath="embed.objectPath">
|
||||
</notebook-embed>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-ne__local-controls--hidden">
|
||||
<button class="c-click-icon c-click-icon--major icon-trash"
|
||||
title="Delete this entry"
|
||||
@click="deleteEntry">
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NotebookEmbed from './embed.vue';
|
||||
|
||||
export default {
|
||||
inject: ['formatTime'],
|
||||
props: ['entry'],
|
||||
components: {
|
||||
NotebookEmbed
|
||||
},
|
||||
methods: {
|
||||
updateEntry(event) {
|
||||
this.$emit('update-entry', this.entry.id, event.target.innerText);
|
||||
},
|
||||
deleteEntry() {
|
||||
this.$emit('delete-entry', this.entry.id);
|
||||
},
|
||||
onTreeItemDrop(event) {
|
||||
this.$emit('drop-embed', this.entry.id, event);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.entry.text && !this.entry.embeds.length) {
|
||||
this.$refs.contenteditable.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,198 +0,0 @@
|
||||
<template>
|
||||
<div class="c-notebook"
|
||||
@dragover.stop
|
||||
@drop.stop>
|
||||
<div class="c-notebook__head">
|
||||
<search class="c-notebook__search"
|
||||
:value="searchValue"
|
||||
@input="searchEntries"
|
||||
@clear="searchEntries">
|
||||
</search>
|
||||
|
||||
<div class="c-notebook__controls ">
|
||||
<select class="c-notebook__controls__time" v-model="timeFrame">
|
||||
<option value="0" :selected="timeFrame==='0'">Show all</option>
|
||||
<option value="1" :selected="timeFrame==='1'">Last hour</option>
|
||||
<option value="8" :selected="timeFrame==='8'">Last 8 hours</option>
|
||||
<option value="24" :selected="timeFrame==='24'">Last 24 hours</option>
|
||||
</select>
|
||||
<select class="c-notebook__controls__time" v-model="sortOrder">
|
||||
<option value="newest" :selected="sortOrder === 'newest'">Newest first</option>
|
||||
<option value="oldest" :selected="sortOrder === 'oldest'">Oldest first</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-notebook__drag-area icon-plus"
|
||||
@click="newEntry">
|
||||
<span class="c-notebook__drag-area__label">To start a new entry, click here or drag and drop any object</span>
|
||||
</div>
|
||||
<div class="c-notebook__entries">
|
||||
<ul>
|
||||
<entry
|
||||
v-for="entry in entries"
|
||||
:key="entry.id"
|
||||
:entry="entry"
|
||||
@update-entry="persistEntry"
|
||||
@delete-entry="deleteEntry"
|
||||
@drop-embed="addEmbed">
|
||||
</entry>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Search from '../../../ui/components/search.vue';
|
||||
import Entry from './entry.vue';
|
||||
import Moment from 'moment';
|
||||
import searchVue from '../../../ui/components/search.vue';
|
||||
|
||||
function formatTime(unixTime, format) {
|
||||
return Moment(unixTime).format(format)
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'providedDomainObject'],
|
||||
components: {
|
||||
Search,
|
||||
Entry
|
||||
},
|
||||
provide: {
|
||||
formatTime
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject: this.providedDomainObject,
|
||||
timeFrame: '0',
|
||||
searchValue: '',
|
||||
sortOrder: this.providedDomainObject.configuration.sortOrder
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchEntries(value) {
|
||||
this.searchValue = value;
|
||||
},
|
||||
updateDomainObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
},
|
||||
newEntry() {
|
||||
this.searchValue = '';
|
||||
|
||||
let date = Date.now(),
|
||||
entry = {'id': 'entry-' + date, 'createdOn': date, 'embeds':[]},
|
||||
entries = this.domainObject.entries,
|
||||
lastEntry = entries[entries.length - 1];
|
||||
|
||||
if (lastEntry && !lastEntry.text && !lastEntry.embeds.length) {
|
||||
let lastEntryComponent = this.$children.find(entryComponent => {
|
||||
if (entryComponent.entry) {
|
||||
return entryComponent.entry.id === lastEntry.id;
|
||||
}
|
||||
});
|
||||
|
||||
lastEntryComponent.$refs.contenteditable.focus();
|
||||
} else {
|
||||
entries.push(entry);
|
||||
|
||||
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
|
||||
}
|
||||
},
|
||||
findEntry(entryId) {
|
||||
return this.domainObject.entries.findIndex(entry => {
|
||||
return entry.id === entryId;
|
||||
})
|
||||
},
|
||||
persistEntry(entryId, text) {
|
||||
let entryPos = this.findEntry(entryId);
|
||||
|
||||
this.openmct.objects.mutate(this.domainObject, `entries[${entryPos}].text`, text);
|
||||
},
|
||||
deleteEntry(entryId) {
|
||||
let entryPos = this.findEntry(entryId),
|
||||
entries = this.domainObject.entries;
|
||||
|
||||
if (entryPos !== -1) {
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: 'This action will permanently delete this entry. Do you wish to continue?',
|
||||
buttons: [
|
||||
{
|
||||
label: "Ok",
|
||||
emphasis: true,
|
||||
callback: () => {
|
||||
entries.splice(entryPos, 1);
|
||||
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
|
||||
dialog.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
},
|
||||
applySearch(entries) {
|
||||
return entries.filter((entry) => {
|
||||
if (entry.text.includes(this.searchValue)) {
|
||||
return entry;
|
||||
}
|
||||
});
|
||||
},
|
||||
addEmbed(entryId, event) {
|
||||
var data = event.dataTransfer.getData('openmct/domain-object-path');
|
||||
|
||||
if (data) {
|
||||
var objectPath = JSON.parse(data),
|
||||
domainObject = objectPath[0],
|
||||
domainObjectKey = domainObject.identifier.key,
|
||||
domainObjectType = this.openmct.types.get(domainObject.type),
|
||||
cssClass = domainObjectType && domainObjectType.definition ?
|
||||
domainObjectType.definition.cssClass : 'icon-object-unknown',
|
||||
entryPos = this.findEntry(entryId),
|
||||
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
|
||||
newEmbed = {
|
||||
id: '' + Date.now(),
|
||||
domainObject: domainObject,
|
||||
objectPath: objectPath,
|
||||
type: domainObjectKey,
|
||||
cssClass: cssClass,
|
||||
name: domainObject.name,
|
||||
snapshot: ''
|
||||
};
|
||||
|
||||
currentEntryEmbeds.push(newEmbed);
|
||||
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].embeds', currentEntryEmbeds);
|
||||
}
|
||||
},
|
||||
onDrop() {
|
||||
console.log('droped');
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
entries() {
|
||||
let entries = [...this.domainObject.entries];
|
||||
|
||||
if (this.searchValue !== '') {
|
||||
entries = this.applySearch(entries);
|
||||
}
|
||||
|
||||
if (this.sortOrder === 'newest') {
|
||||
return entries.reverse();
|
||||
} else {
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,67 +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([
|
||||
'vue',
|
||||
'./components/notebook.vue'
|
||||
], function (
|
||||
Vue,
|
||||
Notebook
|
||||
) {
|
||||
function NotebookViewProvider(openmct) {
|
||||
return {
|
||||
key: 'notebook',
|
||||
name: 'Notebook',
|
||||
cssClass: 'icon-notebook',
|
||||
canView: function (domainObject) {
|
||||
return domainObject.type === 'notebook';
|
||||
},
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
components: {
|
||||
Notebook: Notebook.default
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
providedDomainObject: domainObject
|
||||
},
|
||||
el: element,
|
||||
template: '<notebook></notebook>'
|
||||
});
|
||||
},
|
||||
destroy: function (element) {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
};
|
||||
},
|
||||
priority: function () {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
return NotebookViewProvider;
|
||||
});
|
@ -1,47 +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([
|
||||
'./notebook-view-provider'
|
||||
], function (
|
||||
NotebookViewProvider
|
||||
) {
|
||||
return function plugin() {
|
||||
|
||||
return function install(openmct) {
|
||||
openmct.objectViews.addProvider(new NotebookViewProvider(openmct));
|
||||
|
||||
openmct.types.addType('notebook', {
|
||||
name: "Notebook SVC",
|
||||
creatable: true,
|
||||
description: "Create and save timestamped notes with embedded object snapshots.",
|
||||
cssClass: 'icon-notebook',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.configuration = {
|
||||
sortOrder: 'oldest'
|
||||
};
|
||||
domainObject.entries = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
@ -35,7 +35,7 @@ define([
|
||||
|
||||
installed = true;
|
||||
|
||||
openmct.legacyRegistry.register('notebook-old', {
|
||||
openmct.legacyRegistry.register('notebook', {
|
||||
name: 'Notebook Plugin',
|
||||
extensions: {
|
||||
types: [
|
||||
@ -73,10 +73,10 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
openmct.legacyRegistry.enable('notebook-old');
|
||||
openmct.legacyRegistry.enable('notebook');
|
||||
|
||||
openmct.objectViews.addProvider({
|
||||
key: 'notebook-old',
|
||||
key: 'notebook-vue',
|
||||
name: 'Notebook View',
|
||||
cssClass: 'icon-notebook',
|
||||
canView: function (domainObject) {
|
||||
|
@ -64,45 +64,35 @@ define([
|
||||
Object.keys(panels).forEach(key => {
|
||||
let panel = panels[key];
|
||||
let domainObject = childObjects[key];
|
||||
let identifier = undefined;
|
||||
|
||||
if (isTelemetry(domainObject)) {
|
||||
// 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;
|
||||
}
|
||||
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"
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
width: panel.dimensions[0],
|
||||
height: panel.dimensions[1],
|
||||
x: panel.position[0],
|
||||
y: panel.position[1],
|
||||
identifier: domainObject.identifier,
|
||||
id: uuid(),
|
||||
type: 'subobject-view',
|
||||
hasFrame: panel.hasFrame
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
width: panel.dimensions[0],
|
||||
height: panel.dimensions[1],
|
||||
x: panel.position[0],
|
||||
y: panel.position[1],
|
||||
identifier: identifier || domainObject.identifier,
|
||||
id: uuid(),
|
||||
type: 'subobject-view',
|
||||
hasFrame: panel.hasFrame
|
||||
});
|
||||
});
|
||||
|
||||
migratedObject.configuration.items = items;
|
||||
@ -177,9 +167,7 @@ define([
|
||||
return [
|
||||
{
|
||||
check(domainObject) {
|
||||
return domainObject.type === 'layout' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration.layout;
|
||||
return domainObject.type === 'layout' && domainObject.configuration.layout;
|
||||
},
|
||||
migrate(domainObject) {
|
||||
let childObjects = {};
|
||||
@ -198,9 +186,7 @@ define([
|
||||
},
|
||||
{
|
||||
check(domainObject) {
|
||||
return domainObject.type === 'telemetry.fixed' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration['fixed-display'];
|
||||
return domainObject.type === 'telemetry.fixed' && domainObject.configuration['fixed-display'];
|
||||
},
|
||||
migrate(domainObject) {
|
||||
const DEFAULT_GRID_SIZE = [64, 16];
|
||||
@ -238,7 +224,6 @@ define([
|
||||
{
|
||||
check(domainObject) {
|
||||
return domainObject.type === 'table' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration.table;
|
||||
},
|
||||
migrate(domainObject) {
|
||||
|
@ -115,22 +115,10 @@
|
||||
width: (tickWidth + 30) + 'px'
|
||||
}">
|
||||
|
||||
<div class="gl-plot-label gl-plot-y-label" ng-if="!yKeyOptions">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{ yAxis.get('label') }}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-label gl-plot-y-label" ng-if="yKeyOptions.length > 1 && series.length === 1">
|
||||
<select class="gl-plot-y-label__select"
|
||||
ng-model="yAxisLabel" ng-change="plot.toggleYAxisLabel(yAxisLabel, yKeyOptions, series[0])">
|
||||
<option ng-repeat="option in yKeyOptions"
|
||||
value="{{option.name}}"
|
||||
ng-selected="option.name === yAxisLabel">
|
||||
{{option.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<mct-ticks axis="yAxis">
|
||||
<div ng-repeat="tick in ticks track by tick.text"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
|
@ -43,16 +43,12 @@
|
||||
<div class="l-view-section">
|
||||
<div class="c-loading--overlay loading"
|
||||
ng-show="!!currentRequest.pending"></div>
|
||||
<div class="gl-plot child-frame u-inspectable"
|
||||
<div class="gl-plot child-frame"
|
||||
ng-repeat="telemetryObject in telemetryObjects"
|
||||
ng-class="{
|
||||
's-status-timeconductor-unsynced': telemetryObject
|
||||
.getCapability('status')
|
||||
.get('timeconductor-unsynced')
|
||||
}"
|
||||
mct-selectable="{
|
||||
item: telemetryObject.useCapability('adapter'),
|
||||
oldItem: telemetryObject
|
||||
}">
|
||||
<mct-overlay-plot domain-object="telemetryObject"></mct-overlay-plot>
|
||||
</div>
|
||||
|
@ -115,13 +115,11 @@ define([
|
||||
|
||||
Collection.prototype.remove = function (model) {
|
||||
var index = this.indexOf(model);
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error('model not found in collection.');
|
||||
}
|
||||
|
||||
this.emit('remove', model, index);
|
||||
this.models.splice(index, 1);
|
||||
this.emit('remove', model, index);
|
||||
};
|
||||
|
||||
Collection.prototype.destroy = function (model) {
|
||||
|
@ -377,19 +377,6 @@ define([
|
||||
delete this.unsubscribe;
|
||||
}
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the plot series, unsubscribes and resubscribes
|
||||
* @public
|
||||
*/
|
||||
refresh: function () {
|
||||
this.reset();
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
delete this.unsubscribe;
|
||||
}
|
||||
this.fetch();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -100,33 +100,19 @@ define([
|
||||
removeTelemetryObject: function (identifier) {
|
||||
var plotObject = this.plot.get('domainObject');
|
||||
if (plotObject.type === 'telemetry.plot.overlay') {
|
||||
|
||||
var persistedIndex = _.findIndex(plotObject.configuration.series, function (s) {
|
||||
var index = _.findIndex(plotObject.configuration.series, function (s) {
|
||||
return _.isEqual(identifier, s.identifier);
|
||||
});
|
||||
|
||||
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(persistedIndex, 1);
|
||||
this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
|
||||
}.bind(this));
|
||||
}
|
||||
this.remove(this.at(index));
|
||||
// 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);
|
||||
this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
onSeriesAdd: function (series) {
|
||||
|
@ -25,11 +25,23 @@ define([
|
||||
|
||||
function ConfigStore() {
|
||||
this.store = {};
|
||||
this.tracking = {};
|
||||
}
|
||||
|
||||
ConfigStore.prototype.deleteStore = function (id) {
|
||||
this.store[id].destroy();
|
||||
delete this.store[id];
|
||||
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];
|
||||
this.store[id].destroy();
|
||||
delete this.store[id];
|
||||
}
|
||||
};
|
||||
|
||||
ConfigStore.prototype.add = function (id, config) {
|
||||
|
@ -49,6 +49,7 @@ define([
|
||||
};
|
||||
|
||||
PlotOptionsController.prototype.destroy = function () {
|
||||
configStore.untrack(this.configId);
|
||||
this.stopListening();
|
||||
this.unlisten();
|
||||
};
|
||||
@ -59,7 +60,7 @@ define([
|
||||
this.$timeout(this.setUpScope.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
configStore.track(this.configId);
|
||||
this.config = this.$scope.config = config;
|
||||
this.$scope.plotSeries = [];
|
||||
|
||||
|
@ -93,8 +93,6 @@ define([
|
||||
this.$scope.series = this.config.series.models;
|
||||
this.$scope.legend = this.config.legend;
|
||||
|
||||
this.$scope.yAxisLabel = this.config.yAxis.get('label');
|
||||
|
||||
this.cursorGuideVertical = this.$element[0].querySelector('.js-cursor-guide--v');
|
||||
this.cursorGuideHorizontal = this.$element[0].querySelector('.js-cursor-guide--h');
|
||||
this.cursorGuide = false;
|
||||
@ -105,35 +103,9 @@ define([
|
||||
this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this);
|
||||
this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this);
|
||||
this.listenTo(this.$scope, 'plot:reinitializeCanvas', this.initCanvas, this);
|
||||
|
||||
this.listenTo(this.config.xAxis, 'change:displayRange', this.onXAxisChange, this);
|
||||
this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
|
||||
|
||||
this.setUpYAxisOptions();
|
||||
};
|
||||
|
||||
MCTPlotController.prototype.setUpYAxisOptions = function () {
|
||||
if (this.$scope.series.length === 1) {
|
||||
let metadata = this.$scope.series[0].metadata;
|
||||
|
||||
this.$scope.yKeyOptions = metadata
|
||||
.valuesForHints(['range'])
|
||||
.map(function (o) {
|
||||
return {
|
||||
name: o.name,
|
||||
key: o.key
|
||||
};
|
||||
});
|
||||
|
||||
// set yAxisLabel if none is set yet
|
||||
if (this.$scope.yAxisLabel === 'none') {
|
||||
let yKey = this.$scope.series[0].model.yKey,
|
||||
yKeyModel = this.$scope.yKeyOptions.filter(o => o.key === yKey)[0];
|
||||
|
||||
this.$scope.yAxisLabel = yKeyModel.name;
|
||||
}
|
||||
} else {
|
||||
this.$scope.yKeyOptions = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
MCTPlotController.prototype.onXAxisChange = function (displayBounds) {
|
||||
@ -310,19 +282,11 @@ 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 xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
|
||||
var currentXaxis = this.$scope.xAxis.get('displayRange'),
|
||||
currentYaxis = this.$scope.yAxis.get('displayRange'),
|
||||
xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
|
||||
yAxisDist = (currentYaxis.max - currentYaxis.min) * zoomFactor;
|
||||
|
||||
if (zoomDirection === 'in') {
|
||||
@ -358,19 +322,12 @@ 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 xAxisDist = (xDisplayRange.max - xDisplayRange.min),
|
||||
let xDisplayRange = this.$scope.xAxis.get('displayRange'),
|
||||
yDisplayRange = this.$scope.yAxis.get('displayRange'),
|
||||
xAxisDist = (xDisplayRange.max - xDisplayRange.min),
|
||||
yAxisDist = (yDisplayRange.max - yDisplayRange.min),
|
||||
xDistMouseToMax = xDisplayRange.max - this.positionOverPlot.x,
|
||||
xDistMouseToMin = this.positionOverPlot.x - xDisplayRange.min,
|
||||
@ -521,13 +478,5 @@ define([
|
||||
this.cursorGuide = !this.cursorGuide;
|
||||
};
|
||||
|
||||
MCTPlotController.prototype.toggleYAxisLabel = function (label, options, series) {
|
||||
let yAxisObject = options.filter(o => o.name === label)[0];
|
||||
|
||||
if (yAxisObject) {
|
||||
series.emit('change:yKey', yAxisObject.key);
|
||||
}
|
||||
};
|
||||
|
||||
return MCTPlotController;
|
||||
});
|
||||
|
@ -63,11 +63,8 @@ define([
|
||||
|
||||
$scope.pending = 0;
|
||||
|
||||
this.clearData = this.clearData.bind(this);
|
||||
|
||||
this.listenTo($scope, 'user:viewport:change:end', this.onUserViewportChangeEnd, this);
|
||||
this.listenTo($scope, '$destroy', this.destroy, this);
|
||||
this.listenTo($scope, 'clearData', this.clearData);
|
||||
|
||||
this.config = this.getConfig(this.$scope.domainObject);
|
||||
this.listenTo(this.config.series, 'add', this.addSeries, this);
|
||||
@ -77,13 +74,14 @@ define([
|
||||
this.followTimeConductor();
|
||||
|
||||
this.newStyleDomainObject = $scope.domainObject.useCapability('adapter');
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.newStyleDomainObject.identifier);
|
||||
|
||||
this.filterObserver = this.openmct.objects.observe(
|
||||
this.newStyleDomainObject,
|
||||
'configuration.filters',
|
||||
this.updateFiltersAndResubscribe.bind(this)
|
||||
);
|
||||
|
||||
this.listenTo($scope, 'filters:update', this.onFiltersUpdate, this);
|
||||
}
|
||||
|
||||
eventHelpers.extend(PlotController.prototype);
|
||||
@ -152,6 +150,7 @@ define([
|
||||
});
|
||||
configStore.add(configId, config);
|
||||
}
|
||||
configStore.track(configId);
|
||||
return config;
|
||||
};
|
||||
|
||||
@ -160,8 +159,7 @@ define([
|
||||
};
|
||||
|
||||
PlotController.prototype.destroy = function () {
|
||||
configStore.deleteStore(this.config.id);
|
||||
|
||||
configStore.untrack(this.config.id);
|
||||
this.stopListening();
|
||||
if (this.checkForSize) {
|
||||
clearInterval(this.checkForSize);
|
||||
@ -267,10 +265,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
PlotController.prototype.clearData = function () {
|
||||
this.config.series.forEach(function (series) {
|
||||
series.refresh();
|
||||
});
|
||||
PlotController.prototype.onFiltersUpdate = function ($event, updatedFilters) {
|
||||
this.updateFiltersAndResubscribe(updatedFilters);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -37,6 +37,11 @@ define([
|
||||
this.exportImageService = exportImageService;
|
||||
this.$scope = $scope;
|
||||
this.cursorGuide = false;
|
||||
this.domainObject = $scope.domainObject.useCapability('adapter');
|
||||
|
||||
var currentFilters = this.domainObject.configuration.filters;
|
||||
|
||||
$scope.currentFilters = this.currentFilters;
|
||||
|
||||
$scope.telemetryObjects = [];
|
||||
|
||||
@ -79,15 +84,6 @@ define([
|
||||
$scope.$broadcast('plot:tickWidth', _.max(tickWidthMap));
|
||||
}
|
||||
}
|
||||
|
||||
function compositionReorder(reorderPlan) {
|
||||
let oldComposition = telemetryObjects.slice();
|
||||
|
||||
reorderPlan.forEach((reorder) => {
|
||||
telemetryObjects[reorder.newIndex] = oldComposition[reorder.oldIndex];
|
||||
});
|
||||
}
|
||||
|
||||
thisRequest.pending += 1;
|
||||
openmct.objects.get(domainObject.getId())
|
||||
.then(function (obj) {
|
||||
@ -98,12 +94,10 @@ define([
|
||||
composition = openmct.composition.get(obj);
|
||||
composition.on('add', addChild);
|
||||
composition.on('remove', removeChild);
|
||||
composition.on('reorder', compositionReorder);
|
||||
composition.load();
|
||||
unlisten = function () {
|
||||
composition.off('add', addChild);
|
||||
composition.off('remove', removeChild);
|
||||
composition.off('reorder', compositionReorder);
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -121,6 +115,12 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
function onFilterUpdate(filters) {
|
||||
$scope.$broadcast('filters:update', filters);
|
||||
|
||||
currentFilters = filters;
|
||||
}
|
||||
|
||||
$scope.$watch('domainObject', onDomainObjectChange);
|
||||
$scope.$watch('domainObject.getModel().composition', onCompositionChange);
|
||||
|
||||
@ -140,6 +140,12 @@ define([
|
||||
$scope.$on('plot:highlight:update', function ($e, point) {
|
||||
$scope.$broadcast('plot:highlight:set', point);
|
||||
});
|
||||
|
||||
var unobserve = openmct.objects.observe(this.domainObject, 'configuration.filters', onFilterUpdate);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
unobserve();
|
||||
});
|
||||
}
|
||||
|
||||
StackedPlotController.prototype.exportJPG = function () {
|
||||
|
@ -23,7 +23,6 @@
|
||||
define([
|
||||
'lodash',
|
||||
'./utcTimeSystem/plugin',
|
||||
'./localTimeSystem/plugin',
|
||||
'../../example/generator/plugin',
|
||||
'./autoflow/AutoflowTabularPlugin',
|
||||
'./timeConductor/plugin',
|
||||
@ -41,15 +40,11 @@ define([
|
||||
'./flexibleLayout/plugin',
|
||||
'./tabs/plugin',
|
||||
'./LADTable/plugin',
|
||||
'./notebook-svc/plugin',
|
||||
'./filters/plugin',
|
||||
'./objectMigration/plugin',
|
||||
'./goToOriginalAction/plugin',
|
||||
'./clearData/plugin'
|
||||
'./objectMigration/plugin'
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
LocalTimeSystem,
|
||||
GeneratorPlugin,
|
||||
AutoflowPlugin,
|
||||
TimeConductorPlugin,
|
||||
@ -67,11 +62,8 @@ define([
|
||||
FlexibleLayout,
|
||||
Tabs,
|
||||
LADTable,
|
||||
NotebookSVC,
|
||||
Filters,
|
||||
ObjectMigration,
|
||||
GoToOriginalAction,
|
||||
ClearData
|
||||
ObjectMigration
|
||||
) {
|
||||
var bundleMap = {
|
||||
LocalStorage: 'platform/persistence/local',
|
||||
@ -87,7 +79,6 @@ define([
|
||||
});
|
||||
|
||||
plugins.UTCTimeSystem = UTCTimeSystem;
|
||||
plugins.LocalTimeSystem = LocalTimeSystem;
|
||||
|
||||
plugins.ImportExport = ImportExport;
|
||||
|
||||
@ -161,17 +152,14 @@ define([
|
||||
plugins.SummaryWidget = SummaryWidget;
|
||||
plugins.TelemetryMean = TelemetryMean;
|
||||
plugins.URLIndicator = URLIndicatorPlugin;
|
||||
// plugins.Notebook = Notebook;
|
||||
plugins.Notebook = Notebook;
|
||||
plugins.DisplayLayout = DisplayLayoutPlugin.default;
|
||||
plugins.FolderView = FolderView;
|
||||
plugins.Tabs = Tabs;
|
||||
plugins.FlexibleLayout = FlexibleLayout;
|
||||
plugins.LADTable = LADTable;
|
||||
plugins.NotebookSVC = NotebookSVC;
|
||||
plugins.Filters = Filters;
|
||||
plugins.ObjectMigration = ObjectMigration.default;
|
||||
plugins.GoToOriginalAction = GoToOriginalAction.default;
|
||||
plugins.ClearData = ClearData;
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
@ -23,7 +23,6 @@
|
||||
export default class RemoveAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Remove';
|
||||
this.key = 'remove';
|
||||
this.description = 'Remove this object from its containing object.';
|
||||
this.cssClass = "icon-trash";
|
||||
|
||||
|
@ -70,14 +70,16 @@ define([
|
||||
*/
|
||||
function onValueInput(event) {
|
||||
var elem = event.target,
|
||||
value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value),
|
||||
value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber),
|
||||
inputIndex = self.valueInputs.indexOf(elem);
|
||||
|
||||
self.eventEmitter.emit('change', {
|
||||
value: value,
|
||||
property: 'values[' + inputIndex + ']',
|
||||
index: self.index
|
||||
});
|
||||
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);
|
||||
@ -106,7 +108,8 @@ define([
|
||||
Object.values(this.selects).forEach(function (select) {
|
||||
$('.t-configuration', self.domElement).append(select.getDOM());
|
||||
});
|
||||
this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput);
|
||||
|
||||
this.listenTo($(this.domElement), 'input', onValueInput);
|
||||
}
|
||||
|
||||
Condition.prototype.getDOM = function (container) {
|
||||
@ -164,9 +167,7 @@ define([
|
||||
|
||||
/**
|
||||
* When an operation is selected, create the appropriate value inputs
|
||||
* and add them to the view. If an operation is of type enum, create
|
||||
* a drop-down menu instead.
|
||||
*
|
||||
* and add them to the view
|
||||
* @param {string} operation The key of currently selected operation
|
||||
*/
|
||||
Condition.prototype.generateValueInputs = function (operation) {
|
||||
@ -175,49 +176,25 @@ define([
|
||||
inputCount,
|
||||
inputType,
|
||||
newInput,
|
||||
index = 0,
|
||||
emitChange = false;
|
||||
index = 0;
|
||||
|
||||
inputArea.html('');
|
||||
this.valueInputs = [];
|
||||
this.config.values = [];
|
||||
|
||||
if (evaluator.getInputCount(operation)) {
|
||||
inputCount = evaluator.getInputCount(operation);
|
||||
inputType = evaluator.getInputType(operation);
|
||||
|
||||
while (index < inputCount) {
|
||||
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>');
|
||||
if (!this.config.values[index]) {
|
||||
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,8 +24,7 @@ define([], function () {
|
||||
*/
|
||||
this.inputTypes = {
|
||||
number: 'number',
|
||||
string: 'text',
|
||||
enum: 'select'
|
||||
string: 'text'
|
||||
};
|
||||
|
||||
/**
|
||||
@ -35,8 +34,7 @@ define([], function () {
|
||||
*/
|
||||
this.inputValidators = {
|
||||
number: this.validateNumberInput,
|
||||
string: this.validateStringInput,
|
||||
enum: this.validateNumberInput
|
||||
string: this.validateStringInput
|
||||
};
|
||||
|
||||
/**
|
||||
@ -203,7 +201,7 @@ define([], function () {
|
||||
return typeof input[0] === 'undefined';
|
||||
},
|
||||
text: 'is undefined',
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
appliesTo: ['string', 'number'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is undefined';
|
||||
@ -214,33 +212,11 @@ define([], function () {
|
||||
return typeof input[0] !== 'undefined';
|
||||
},
|
||||
text: 'is defined',
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
appliesTo: ['string', 'number'],
|
||||
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];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -334,16 +310,13 @@ define([], function () {
|
||||
validator;
|
||||
|
||||
if (cache[object] && typeof cache[object][key] !== 'undefined') {
|
||||
let value = cache[object][key];
|
||||
telemetryValue = [isNaN(Number(value)) ? value : Number(value)];
|
||||
telemetryValue = [cache[object][key]];
|
||||
}
|
||||
|
||||
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 > 1) {
|
||||
if (this.operations[operation].appliesTo.length === 2) {
|
||||
return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input);
|
||||
} else {
|
||||
return validator(input) && op(input);
|
||||
@ -399,7 +372,7 @@ define([], function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true only if the given operation applies to a given type
|
||||
* Returns true only of 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,9 +130,7 @@ define ([
|
||||
this.telemetryTypesById[objectId] = {};
|
||||
Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) {
|
||||
var type;
|
||||
if (valueMetadata.enumerations !== undefined) {
|
||||
type = 'enum';
|
||||
} else if (valueMetadata.hints.hasOwnProperty('range')) {
|
||||
if (valueMetadata.hints.hasOwnProperty('range')) {
|
||||
type = 'number';
|
||||
} else if (valueMetadata.hints.hasOwnProperty('domain')) {
|
||||
type = 'number';
|
||||
@ -165,18 +163,11 @@ define ([
|
||||
* @param {datum} datum The new data from the telemetry source
|
||||
* @private
|
||||
*/
|
||||
ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) {
|
||||
this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum);
|
||||
ConditionManager.prototype.handleSubscriptionCallback = function (objId, datum) {
|
||||
this.subscriptionCache[objId] = datum;
|
||||
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.
|
||||
@ -245,7 +236,6 @@ 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,11 +110,9 @@ define([
|
||||
|
||||
type = self.manager.getTelemetryPropertyType(self.config.object, key);
|
||||
|
||||
if (type !== undefined) {
|
||||
self.operationKeys = operations.filter(function (operation) {
|
||||
return self.evaluator.operationAppliesTo(operation, type);
|
||||
});
|
||||
}
|
||||
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: ts.name,
|
||||
name: 'UTC',
|
||||
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', 'enum'],
|
||||
appliesTo: ['string', 'number'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is undefined';
|
||||
@ -185,33 +185,11 @@ define([
|
||||
return typeof input[0] !== 'undefined';
|
||||
},
|
||||
text: 'is defined',
|
||||
appliesTo: ['string', 'number', 'enum'],
|
||||
appliesTo: ['string', 'number'],
|
||||
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];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,8 +7,7 @@
|
||||
}">
|
||||
<div class="c-drop-hint"
|
||||
@drop="onDrop"
|
||||
@dragenter="dragenter"
|
||||
@dragleave="dragleave">
|
||||
ref="dropHint">
|
||||
</div>
|
||||
<div class="c-tabs-view__empty-message"
|
||||
v-if="!tabsList.length > 0">Drag objects here to add them to this view.</div>
|
||||
@ -198,6 +197,13 @@ export default {
|
||||
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
|
||||
let dropHint = this.$refs.dropHint;
|
||||
|
||||
if (dropHint) {
|
||||
dropHint.addEventListener('dragenter', this.dragenter);
|
||||
dropHint.addEventListener('dragleave', this.dragleave);
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
@ -206,6 +212,12 @@ export default {
|
||||
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
},
|
||||
beforeDestroy() {
|
||||
let dropHint = this.$refs.dropHint;
|
||||
|
||||
dropHint.removeEventListener('dragenter', this.dragenter);
|
||||
dropHint.removeEventListener('dragleave', this.dragleave);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -37,7 +37,7 @@ define([
|
||||
key: 'table-configuration',
|
||||
name: 'Telemetry Table Configuration',
|
||||
canView: function (selection) {
|
||||
if (selection.length !== 1 || selection[0].length === 0) {
|
||||
if (selection.length === 0 || selection[0].length === 0) {
|
||||
return false;
|
||||
}
|
||||
let object = selection[0][0].context.item;
|
||||
|
@ -26,7 +26,6 @@ define([
|
||||
'./collections/BoundedTableRowCollection',
|
||||
'./collections/FilteredTableRowCollection',
|
||||
'./TelemetryTableRow',
|
||||
'./TelemetryTableColumn',
|
||||
'./TelemetryTableConfiguration'
|
||||
], function (
|
||||
EventEmitter,
|
||||
@ -34,7 +33,6 @@ define([
|
||||
BoundedTableRowCollection,
|
||||
FilteredTableRowCollection,
|
||||
TelemetryTableRow,
|
||||
TelemetryTableColumn,
|
||||
TelemetryTableConfiguration
|
||||
) {
|
||||
class TelemetryTable extends EventEmitter {
|
||||
@ -49,8 +47,6 @@ define([
|
||||
this.telemetryObjects = [];
|
||||
this.outstandingRequests = 0;
|
||||
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||
this.paused = false;
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
||||
this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
|
||||
@ -98,6 +94,8 @@ define([
|
||||
this.tableComposition.load().then((composition) => {
|
||||
|
||||
composition = composition.filter(this.isTelemetryObject);
|
||||
|
||||
this.configuration.addColumnsForAllObjects(composition);
|
||||
composition.forEach(this.addTelemetryObject);
|
||||
|
||||
this.tableComposition.on('add', this.addTelemetryObject);
|
||||
@ -107,7 +105,7 @@ define([
|
||||
}
|
||||
|
||||
addTelemetryObject(telemetryObject) {
|
||||
this.addColumnsForObject(telemetryObject, true);
|
||||
this.configuration.addColumnsForObject(telemetryObject, true);
|
||||
this.requestDataFor(telemetryObject);
|
||||
this.subscribeTo(telemetryObject);
|
||||
this.telemetryObjects.push(telemetryObject);
|
||||
@ -146,17 +144,14 @@ define([
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let columnMap = this.getColumnMapForObject(keyString);
|
||||
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||
this.processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator);
|
||||
|
||||
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
this.boundedRows.add(telemetryRows);
|
||||
}).finally(() => {
|
||||
this.decrementOutstandingRequests();
|
||||
});
|
||||
}
|
||||
|
||||
processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator) {
|
||||
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
this.boundedRows.add(telemetryRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ -196,19 +191,6 @@ define([
|
||||
}, {});
|
||||
}
|
||||
|
||||
addColumnsForObject(telemetryObject) {
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
|
||||
metadataValues.forEach(metadatum => {
|
||||
let column = this.createColumn(metadatum);
|
||||
this.configuration.addSingleColumnForObject(telemetryObject, column);
|
||||
});
|
||||
}
|
||||
|
||||
createColumn(metadatum) {
|
||||
return new TelemetryTableColumn(this.openmct, metadatum);
|
||||
}
|
||||
|
||||
subscribeTo(telemetryObject) {
|
||||
let subscribeOptions = this.buildOptionsFromConfiguration(telemetryObject);
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
@ -220,17 +202,10 @@ define([
|
||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.paused) {
|
||||
this.processRealtimeDatum(datum, columnMap, keyString, limitEvaluator);
|
||||
}
|
||||
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
}, subscribeOptions);
|
||||
}
|
||||
|
||||
processRealtimeDatum(datum, columnMap, keyString, limitEvaluator) {
|
||||
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
}
|
||||
|
||||
isTelemetryObject(domainObject) {
|
||||
return domainObject.hasOwnProperty('telemetry');
|
||||
}
|
||||
@ -259,24 +234,12 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.paused = true;
|
||||
this.boundedRows.unsubscribeFromBounds();
|
||||
}
|
||||
|
||||
unpause() {
|
||||
this.paused = false;
|
||||
this.boundedRows.subscribeToBounds();
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.boundedRows.destroy();
|
||||
this.filteredRows.destroy();
|
||||
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
|
||||
this.openmct.time.off('bounds', this.refreshData);
|
||||
this.openmct.time.off('timeSystem', this.refreshData);
|
||||
|
||||
this.openmct.time.on('timeSystem', this.refreshData);
|
||||
if (this.filterObserver) {
|
||||
this.filterObserver();
|
||||
}
|
||||
|
@ -21,11 +21,10 @@
|
||||
*****************************************************************************/
|
||||
define(function () {
|
||||
class TelemetryTableColumn {
|
||||
constructor (openmct, metadatum, options = {selectable: false}) {
|
||||
constructor (openmct, metadatum) {
|
||||
this.metadatum = metadatum;
|
||||
this.formatter = openmct.telemetry.getValueFormatter(metadatum);
|
||||
this.titleValue = this.metadatum.name;
|
||||
this.selectable = options.selectable;
|
||||
}
|
||||
|
||||
getKey() {
|
||||
@ -56,7 +55,8 @@ define(function () {
|
||||
return formattedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return TelemetryTableColumn;
|
||||
});
|
||||
|
@ -22,8 +22,9 @@
|
||||
|
||||
define([
|
||||
'lodash',
|
||||
'EventEmitter'
|
||||
], function (_, EventEmitter) {
|
||||
'EventEmitter',
|
||||
'./TelemetryTableColumn'
|
||||
], function (_, EventEmitter, TelemetryTableColumn) {
|
||||
|
||||
class TelemetryTableConfiguration extends EventEmitter {
|
||||
constructor(domainObject, openmct) {
|
||||
@ -33,6 +34,7 @@ define([
|
||||
this.openmct = openmct;
|
||||
this.columns = {};
|
||||
|
||||
this.addColumnsForObject = this.addColumnsForObject.bind(this);
|
||||
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.
|
||||
@ -46,7 +48,6 @@ define([
|
||||
configuration.hiddenColumns = configuration.hiddenColumns || {};
|
||||
configuration.columnWidths = configuration.columnWidths || {};
|
||||
configuration.columnOrder = configuration.columnOrder || [];
|
||||
configuration.cellFormat = configuration.cellFormat || {};
|
||||
configuration.autosize = configuration.autosize === undefined ? true : configuration.autosize;
|
||||
|
||||
return configuration;
|
||||
@ -64,18 +65,26 @@ define([
|
||||
//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)) {
|
||||
if (!_.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);
|
||||
}
|
||||
}
|
||||
|
||||
addSingleColumnForObject(telemetryObject, column, position) {
|
||||
addColumnsForAllObjects(objects) {
|
||||
objects.forEach(object => this.addColumnsForObject(object, false));
|
||||
}
|
||||
|
||||
addColumnsForObject(telemetryObject) {
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
this.columns[objectKeyString] = this.columns[objectKeyString] || [];
|
||||
position = position || this.columns[objectKeyString].length;
|
||||
this.columns[objectKeyString].splice(position, 0, column);
|
||||
this.columns[objectKeyString] = [];
|
||||
|
||||
metadataValues.forEach(metadatum => {
|
||||
let column = new TelemetryTableColumn(this.openmct, metadatum);
|
||||
this.columns[objectKeyString].push(column);
|
||||
});
|
||||
}
|
||||
|
||||
removeColumnsForObject(objectIdentifier) {
|
||||
|
@ -29,7 +29,7 @@ define([], function () {
|
||||
this.limitEvaluator = limitEvaluator;
|
||||
this.objectKeyString = objectKeyString;
|
||||
}
|
||||
|
||||
|
||||
getFormattedDatum(headers) {
|
||||
return Object.keys(headers).reduce((formattedDatum, columnKey) => {
|
||||
formattedDatum[columnKey] = this.getFormattedValue(columnKey);
|
||||
@ -42,19 +42,12 @@ define([], function () {
|
||||
return column && column.getFormattedValue(this.datum[key]);
|
||||
}
|
||||
|
||||
getCellComponentName(key) {
|
||||
let column = this.columns[key];
|
||||
return column &&
|
||||
column.getCellComponentName &&
|
||||
column.getCellComponentName();
|
||||
}
|
||||
|
||||
getRowClass() {
|
||||
if (!this.rowClass) {
|
||||
getRowLimitClass() {
|
||||
if (!this.rowLimitClass) {
|
||||
let limitEvaluation = this.limitEvaluator.evaluate(this.datum);
|
||||
this.rowClass = limitEvaluation && limitEvaluation.cssClass;
|
||||
this.rowLimitClass = limitEvaluation && limitEvaluation.cssClass;
|
||||
}
|
||||
return this.rowClass;
|
||||
return this.rowLimitClass;
|
||||
}
|
||||
|
||||
getCellLimitClasses() {
|
||||
@ -62,16 +55,12 @@ define([], function () {
|
||||
this.cellLimitClasses = Object.values(this.columns).reduce((alarmStateMap, column) => {
|
||||
let limitEvaluation = this.limitEvaluator.evaluate(this.datum, column.getMetadatum());
|
||||
alarmStateMap[column.getKey()] = limitEvaluation && limitEvaluation.cssClass;
|
||||
|
||||
|
||||
return alarmStateMap;
|
||||
}, {});
|
||||
}
|
||||
return this.cellLimitClasses;
|
||||
}
|
||||
|
||||
getContextMenuActions() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,4 +78,4 @@ define([], function () {
|
||||
}
|
||||
|
||||
return TelemetryTableRow;
|
||||
});
|
||||
});
|
@ -22,10 +22,12 @@
|
||||
|
||||
define([
|
||||
'./components/table.vue',
|
||||
'../../exporters/CSVExporter',
|
||||
'./TelemetryTable',
|
||||
'vue'
|
||||
], function (
|
||||
TableComponent,
|
||||
CSVExporter,
|
||||
TelemetryTable,
|
||||
Vue
|
||||
) {
|
||||
@ -48,7 +50,8 @@ define([
|
||||
canEdit(domainObject) {
|
||||
return domainObject.type === 'table';
|
||||
},
|
||||
view(domainObject, isEditing, objectPath) {
|
||||
view(domainObject) {
|
||||
let csvExporter = new CSVExporter.default();
|
||||
let table = new TelemetryTable(domainObject, openmct);
|
||||
let component;
|
||||
return {
|
||||
@ -64,19 +67,16 @@ define([
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
table,
|
||||
objectPath
|
||||
csvExporter,
|
||||
table
|
||||
},
|
||||
el: element,
|
||||
template: '<table-component :isEditing="isEditing" :enableMarking="true"></table-component>'
|
||||
template: '<table-component :isEditing="isEditing"></table-component>'
|
||||
});
|
||||
},
|
||||
onEditModeChange(isEditing) {
|
||||
component.isEditing = isEditing;
|
||||
},
|
||||
onClearData() {
|
||||
table.refreshData();
|
||||
},
|
||||
destroy: function (element) {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
|
@ -31,9 +31,9 @@ define(
|
||||
) {
|
||||
|
||||
class BoundedTableRowCollection extends SortedTableRowCollection {
|
||||
constructor(openmct) {
|
||||
constructor (openmct) {
|
||||
super();
|
||||
|
||||
|
||||
this.futureBuffer = new SortedTableRowCollection();
|
||||
this.openmct = openmct;
|
||||
|
||||
@ -43,17 +43,15 @@ define(
|
||||
this.sortByTimeSystem(openmct.time.timeSystem());
|
||||
|
||||
this.lastBounds = openmct.time.bounds();
|
||||
|
||||
this.subscribeToBounds();
|
||||
openmct.time.on('bounds', this.bounds);
|
||||
}
|
||||
|
||||
addOne(item) {
|
||||
let parsedValue = this.getValueForSortColumn(item);
|
||||
addOne (item) {
|
||||
// Insert into either in-bounds array, or the future buffer.
|
||||
// Data in the future buffer will be re-evaluated for possible
|
||||
// Data in the future buffer will be re-evaluated for possible
|
||||
// insertion on next bounds change
|
||||
let beforeStartOfBounds = parsedValue < this.lastBounds.start;
|
||||
let afterEndOfBounds = parsedValue > this.lastBounds.end;
|
||||
let beforeStartOfBounds = this.parseTime(item.datum[this.sortOptions.key]) < this.lastBounds.start;
|
||||
let afterEndOfBounds = this.parseTime(item.datum[this.sortOptions.key]) > this.lastBounds.end;
|
||||
|
||||
if (!afterEndOfBounds && !beforeStartOfBounds) {
|
||||
return super.addOne(item);
|
||||
@ -88,13 +86,13 @@ define(
|
||||
* @fires TelemetryCollection#discarded
|
||||
* @param bounds
|
||||
*/
|
||||
bounds(bounds) {
|
||||
bounds (bounds) {
|
||||
let startChanged = this.lastBounds.start !== bounds.start;
|
||||
let endChanged = this.lastBounds.end !== bounds.end;
|
||||
|
||||
|
||||
let startIndex = 0;
|
||||
let endIndex = 0;
|
||||
|
||||
|
||||
let discarded = [];
|
||||
let added = [];
|
||||
let testValue = {
|
||||
@ -137,21 +135,9 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
getValueForSortColumn(row) {
|
||||
return this.parseTime(row.datum[this.sortOptions.key]);
|
||||
}
|
||||
|
||||
unsubscribeFromBounds() {
|
||||
destroy() {
|
||||
this.openmct.time.off('bounds', this.bounds);
|
||||
}
|
||||
|
||||
subscribeToBounds() {
|
||||
this.openmct.time.on('bounds', this.bounds);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unsubscribeFromBounds();
|
||||
}
|
||||
}
|
||||
return BoundedTableRowCollection;
|
||||
});
|
||||
return BoundedTableRowCollection;
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ define(
|
||||
if (rowsAdded.length > 0) {
|
||||
this.emit('add', rowsAdded);
|
||||
}
|
||||
this.dupeCheck = true;
|
||||
this.dupeCheck = true;
|
||||
} else {
|
||||
let wasAdded = this.addOne(rows);
|
||||
if (wasAdded) {
|
||||
@ -115,10 +115,11 @@ define(
|
||||
if (this.rows.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const testRowValue = this.getValueForSortColumn(testRow);
|
||||
const firstValue = this.getValueForSortColumn(this.rows[0]);
|
||||
const lastValue = this.getValueForSortColumn(this.rows[this.rows.length - 1]);
|
||||
|
||||
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];
|
||||
|
||||
lodashFunction = lodashFunction || _.sortedIndex;
|
||||
|
||||
@ -132,7 +133,7 @@ define(
|
||||
return 0;
|
||||
} else {
|
||||
return lodashFunction(rows, testRow, (thisRow) => {
|
||||
return this.getValueForSortColumn(thisRow);
|
||||
return thisRow.datum[sortOptionsKey];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -146,7 +147,7 @@ define(
|
||||
} else {
|
||||
// Use a custom comparison function to support descending sort.
|
||||
return lodashFunction(rows, testRow, (thisRow) => {
|
||||
const thisRowValue = this.getValueForSortColumn(thisRow);
|
||||
const thisRowValue = thisRow.datum[sortOptionsKey];
|
||||
if (testRowValue === thisRowValue) {
|
||||
return EQUAL;
|
||||
} else if (testRowValue < thisRowValue) {
|
||||
@ -205,7 +206,7 @@ define(
|
||||
this.emit('sort');
|
||||
}
|
||||
// Return duplicate to avoid direct modification of underlying object
|
||||
return Object.assign({}, this.sortOptions);
|
||||
return Object.assign({}, this.sortOptions);
|
||||
}
|
||||
|
||||
removeAllRowsForObject(objectKeyString) {
|
||||
@ -217,32 +218,25 @@ 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);
|
||||
}
|
||||
|
||||
getRows() {
|
||||
getRows () {
|
||||
return this.rows;
|
||||
}
|
||||
|
||||
clear() {
|
||||
let removedRows = this.rows;
|
||||
this.rows = [];
|
||||
|
||||
this.emit('remove', removedRows);
|
||||
}
|
||||
}
|
||||
return SortedTableRowCollection;
|
||||
});
|
||||
return SortedTableRowCollection;
|
||||
});
|
||||
|
@ -1,176 +0,0 @@
|
||||
<template>
|
||||
<div v-if="filterNames.length > 0"
|
||||
:title=title
|
||||
class="c-filter-indication"
|
||||
:class="{ 'c-filter-indication--mixed': mixed }">
|
||||
<span class="c-filter-indication__mixed">{{ label }}</span>
|
||||
<span v-for="(name, index) in filterNames"
|
||||
class="c-filter-indication__label">
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
.c-filter-indication {
|
||||
@include userSelectNone();
|
||||
background: $colorFilterBg;
|
||||
color: $colorFilterFg;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.9em;
|
||||
margin-top: $interiorMarginSm;
|
||||
padding: 2px;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:before {
|
||||
font-family: symbolsfont-12px;
|
||||
content: $glyph-icon-filter;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
&__mixed {
|
||||
font-weight: bold;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
&--mixed {
|
||||
.c-filter-indication__mixed {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
+ .c-filter-indication__label {
|
||||
&:before {
|
||||
content: ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const FILTER_INDICATOR_LABEL = 'Filters:';
|
||||
const FILTER_INDICATOR_LABEL_MIXED = 'Mixed Filters:';
|
||||
const FILTER_INDICATOR_TITLE = 'Data filters are being applied to this view.';
|
||||
const FILTER_INDICATOR_TITLE_MIXED = 'A mix of data filter values are being applied to this view.';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'table'],
|
||||
data() {
|
||||
return {
|
||||
filterNames: [],
|
||||
filteredTelemetry: {},
|
||||
mixed: false,
|
||||
label: '',
|
||||
title: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isTelemetryObject(domainObject) {
|
||||
return domainObject.hasOwnProperty('telemetry');
|
||||
},
|
||||
setFilterNames() {
|
||||
let names = [];
|
||||
|
||||
this.composition && this.composition.load().then((domainObjects) => {
|
||||
domainObjects.forEach(telemetryObject => {
|
||||
let keyString= this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let filters = this.filteredTelemetry[keyString];
|
||||
this.telemetryKeyStrings.add(keyString);
|
||||
|
||||
if (filters !== undefined) {
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
Object.keys(filters).forEach(key => {
|
||||
metadataValues.forEach(metadaum => {
|
||||
|
||||
if (key === metadaum.key) {
|
||||
names.push(metadaum.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
this.filterNames = Array.from(new Set(names));
|
||||
});
|
||||
},
|
||||
handleConfigurationChanges(configuration) {
|
||||
if (!_.eq(this.filteredTelemetry, configuration.filters)) {
|
||||
this.updateFilters(configuration.filters || {});
|
||||
}
|
||||
},
|
||||
checkFiltersForMixedValues() {
|
||||
let valueToCompare = this.filteredTelemetry[Object.keys(this.filteredTelemetry)[0]];
|
||||
let mixed = false;
|
||||
|
||||
Object.values(this.filteredTelemetry).forEach(value => {
|
||||
if (!_.isEqual(valueToCompare, value)) {
|
||||
mixed = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// If the filtered telemetry is not mixed at this point, check the number of available objects
|
||||
// with the number of filtered telemetry. If they are not equal, the filters must be mixed.
|
||||
if (mixed === false && _.size(this.filteredTelemetry) !== this.telemetryKeyStrings.size) {
|
||||
mixed = true;
|
||||
}
|
||||
|
||||
this.mixed = mixed;
|
||||
},
|
||||
setLabels() {
|
||||
if (this.mixed) {
|
||||
this.label = FILTER_INDICATOR_LABEL_MIXED;
|
||||
this.title = FILTER_INDICATOR_TITLE_MIXED;
|
||||
} else {
|
||||
this.label = FILTER_INDICATOR_LABEL;
|
||||
this.title = FILTER_INDICATOR_TITLE;
|
||||
}
|
||||
},
|
||||
updateFilters(filters) {
|
||||
this.filteredTelemetry = JSON.parse(JSON.stringify(filters));
|
||||
this.setFilterNames();
|
||||
this.updateIndicatorLabel();
|
||||
},
|
||||
addChildren(child) {
|
||||
let keyString = this.openmct.objects.makeKeyString(child.identifier);
|
||||
this.telemetryKeyStrings.add(keyString);
|
||||
this.updateIndicatorLabel();
|
||||
},
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
this.telemetryKeyStrings.delete(keyString);
|
||||
this.updateIndicatorLabel();
|
||||
},
|
||||
updateIndicatorLabel() {
|
||||
this.checkFiltersForMixedValues();
|
||||
this.setLabels();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let filters = this.table.configuration.getConfiguration().filters || {};
|
||||
this.telemetryKeyStrings = new Set();
|
||||
this.composition = this.openmct.composition.get(this.table.configuration.domainObject);
|
||||
|
||||
if (this.composition) {
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
}
|
||||
|
||||
this.table.configuration.on('change', this.handleConfigurationChanges);
|
||||
this.updateFilters(filters);
|
||||
},
|
||||
destroyed() {
|
||||
this.table.configuration.off('change', this.handleConfigurationChanges);
|
||||
|
||||
if (this.composition) {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,71 +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.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<td @click="selectCell($event.currentTarget, columnKey)" :title="formattedValue">{{formattedValue}}</td>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
row: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
columnKey: {
|
||||
type: String,
|
||||
require: true
|
||||
},
|
||||
objectPath: {
|
||||
type: Array,
|
||||
require: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectCell(element, columnKey) {
|
||||
if (this.isSelectable) {
|
||||
this.openmct.selection.select([{
|
||||
element: element,
|
||||
context: {
|
||||
type: 'table-cell',
|
||||
row: this.row.objectKeyString,
|
||||
column: columnKey
|
||||
}
|
||||
},{
|
||||
element: this.openmct.layout.$refs.browseObject.$el,
|
||||
context: {
|
||||
item: this.objectPath[0]
|
||||
}
|
||||
}], false);
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
formattedValue() {
|
||||
return this.row.getFormattedValue(this.columnKey);
|
||||
},
|
||||
isSelectable() {
|
||||
return this.row.columns[this.columnKey].selectable;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -23,8 +23,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TelemetryTableColumn from '../TelemetryTableColumn';
|
||||
|
||||
export default {
|
||||
inject: ['tableConfiguration', 'openmct'],
|
||||
data() {
|
||||
@ -45,7 +43,7 @@ export default {
|
||||
this.tableConfiguration.updateConfiguration(this.configuration);
|
||||
},
|
||||
addObject(domainObject) {
|
||||
this.addColumnsForObject(domainObject, true);
|
||||
this.tableConfiguration.addColumnsForObject(domainObject, true);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
},
|
||||
removeObject(objectIdentifier) {
|
||||
@ -58,17 +56,6 @@ export default {
|
||||
toggleAutosize() {
|
||||
this.configuration.autosize = !this.configuration.autosize;
|
||||
this.tableConfiguration.updateConfiguration(this.configuration);
|
||||
},
|
||||
addColumnsForAllObjects(objects) {
|
||||
objects.forEach(object => this.addColumnsForObject(object, false));
|
||||
},
|
||||
addColumnsForObject(telemetryObject) {
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
|
||||
metadataValues.forEach(metadatum => {
|
||||
let column = new TelemetryTableColumn(this.openmct, metadatum);
|
||||
this.tableConfiguration.addSingleColumnForObject(telemetryObject, column);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -78,7 +65,7 @@ export default {
|
||||
|
||||
compositionCollection.load()
|
||||
.then((composition) => {
|
||||
this.addColumnsForAllObjects(composition);
|
||||
this.tableConfiguration.addColumnsForAllObjects(composition);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
|
||||
compositionCollection.on('add', this.addObject);
|
||||
|
@ -20,55 +20,26 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<tr :style="{ top: rowTop }"
|
||||
class="noselect"
|
||||
:class="[
|
||||
rowClass,
|
||||
{'is-selected': marked}
|
||||
]"
|
||||
v-on="listeners">
|
||||
<component v-for="(title, key) in headers"
|
||||
<tr :style="{ top: rowTop }" :class="rowLimitClass">
|
||||
<td v-for="(title, key) in headers"
|
||||
:key="key"
|
||||
:is="componentList[key]"
|
||||
:columnKey="key"
|
||||
:style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
||||
:class="[cellLimitClasses[key], selectableColumns[key] ? 'is-selectable' : '']"
|
||||
:objectPath="objectPath"
|
||||
:row="row">
|
||||
</component>
|
||||
:title="formattedRow[key]"
|
||||
:class="cellLimitClasses[key]">{{formattedRow[key]}}</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.noselect {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome and Opera */
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TableCell from './table-cell.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'objectPath'],
|
||||
data: function () {
|
||||
return {
|
||||
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
|
||||
rowClass: this.row.getRowClass(),
|
||||
cellLimitClasses: this.row.getCellLimitClasses(),
|
||||
componentList: Object.keys(this.headers).reduce((components, header) => {
|
||||
components[header] = this.row.getCellComponentName(header) || 'table-cell';
|
||||
return components
|
||||
}, {}),
|
||||
selectableColumns : Object.keys(this.row.columns).reduce((selectable, columnKeys) => {
|
||||
selectable[columnKeys] = this.row.columns[columnKeys].selectable;
|
||||
return selectable;
|
||||
}, {})
|
||||
formattedRow: this.row.getFormattedDatum(this.headers),
|
||||
rowLimitClass: this.row.getRowLimitClass(),
|
||||
cellLimitClasses: this.row.getCellLimitClasses()
|
||||
}
|
||||
},
|
||||
props: {
|
||||
@ -84,10 +55,6 @@ export default {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
objectPath: {
|
||||
type: Array,
|
||||
required: false
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number,
|
||||
required: false,
|
||||
@ -102,11 +69,6 @@ export default {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
marked: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -114,54 +76,9 @@ export default {
|
||||
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
|
||||
},
|
||||
formatRow: function (row) {
|
||||
this.rowClass = row.getRowClass();
|
||||
this.formattedRow = row.getFormattedDatum(this.headers);
|
||||
this.rowLimitClass = row.getRowLimitClass();
|
||||
this.cellLimitClasses = row.getCellLimitClasses();
|
||||
},
|
||||
markRow: function (event) {
|
||||
let keyCtrlModifier = false;
|
||||
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
keyCtrlModifier = true;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
this.$emit('markMultipleConcurrent', this.rowIndex);
|
||||
} else {
|
||||
if (this.marked) {
|
||||
this.$emit('unmark', this.rowIndex, keyCtrlModifier);
|
||||
} else {
|
||||
this.$emit('mark', this.rowIndex, keyCtrlModifier);
|
||||
}
|
||||
}
|
||||
},
|
||||
selectCell(element, columnKey) {
|
||||
if (this.selectableColumns[columnKey]) {
|
||||
//TODO: This is a hack. Cannot get parent this way.
|
||||
this.openmct.selection.select([{
|
||||
element: element,
|
||||
context: {
|
||||
type: 'table-cell',
|
||||
row: this.row.objectKeyString,
|
||||
column: columnKey
|
||||
}
|
||||
},{
|
||||
element: this.openmct.layout.$refs.browseObject.$el,
|
||||
context: {
|
||||
item: this.openmct.router.path[0]
|
||||
}
|
||||
}], false);
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
showContextMenu: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.openmct.objects.get(this.row.objectKeyString).then((domainObject) => {
|
||||
let contextualObjectPath = this.objectPath.slice();
|
||||
contextualObjectPath.unshift(domainObject);
|
||||
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(contextualObjectPath, event.x, event.y, this.row.getContextMenuActions());
|
||||
});
|
||||
}
|
||||
},
|
||||
// TODO: use computed properties
|
||||
@ -171,22 +88,6 @@ export default {
|
||||
handler: 'formatRow',
|
||||
deep: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
TableCell
|
||||
},
|
||||
computed: {
|
||||
listeners() {
|
||||
let listenersObject = {
|
||||
click: this.markRow
|
||||
}
|
||||
|
||||
if (this.row.getContextMenuActions().length) {
|
||||
listenersObject.contextmenu = this.showContextMenu;
|
||||
}
|
||||
|
||||
return listenersObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,135 +20,92 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-table-wrapper">
|
||||
<div class="c-table-control-bar c-control-bar">
|
||||
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
:class="{'loading': loading}">
|
||||
<div class="c-table__control-bar c-control-bar">
|
||||
<button class="c-button icon-download labeled"
|
||||
v-if="allowExport"
|
||||
v-on:click="exportAllDataAsCSV()"
|
||||
title="Export This View's Data">
|
||||
<span class="c-button__label">Export Table Data</span>
|
||||
v-on:click="exportAsCSV()"
|
||||
title="Export This View's Data">
|
||||
<span class="c-button__label">Export As CSV</span>
|
||||
</button>
|
||||
<button class="c-button icon-download labeled"
|
||||
v-if="allowExport"
|
||||
v-show="markedRows.length"
|
||||
v-on:click="exportMarkedDataAsCSV()"
|
||||
title="Export Marked Rows As CSV">
|
||||
<span class="c-button__label">Export Marked Rows</span>
|
||||
</button>
|
||||
<button class="c-button icon-x labeled"
|
||||
v-show="markedRows.length"
|
||||
v-on:click="unmarkAllRows()"
|
||||
title="Unmark All Rows">
|
||||
<span class="c-button__label">Unmark All Rows</span>
|
||||
</button>
|
||||
<div v-if="enableMarking"
|
||||
class="c-separator">
|
||||
</div>
|
||||
<button v-if="enableMarking"
|
||||
class="c-button icon-pause pause-play labeled"
|
||||
:class=" paused ? 'icon-play is-paused' : 'icon-pause'"
|
||||
v-on:click="togglePauseByButton()"
|
||||
:title="paused ? 'Continue Data Flow' : 'Pause Data Flow'">
|
||||
<span class="c-button__label">
|
||||
{{paused ? 'Play' : 'Pause'}}
|
||||
</span>
|
||||
</button>
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
|
||||
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
:class="{
|
||||
'loading': loading,
|
||||
'paused' : paused
|
||||
}">
|
||||
|
||||
<div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}"><slot></slot></div>
|
||||
|
||||
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
||||
<!-- Headers table -->
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
||||
<table class="c-table__headers c-telemetry-table__headers">
|
||||
<thead>
|
||||
<tr class="c-telemetry-table__headers__labels">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@sort="allowSorting && sortBy(key)"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:sortOptions="sortOptions"
|
||||
:isEditing="isEditing"
|
||||
><span class="c-telemetry-table__headers__label">{{title}}</span>
|
||||
</table-column-header>
|
||||
</tr>
|
||||
<tr class="c-telemetry-table__headers__filter">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:isEditing="isEditing"
|
||||
>
|
||||
<search class="c-table__search"
|
||||
v-model="filters[key]"
|
||||
v-on:input="filterChanged(key)"
|
||||
v-on:clear="clearFilter(key)" />
|
||||
</table-column-header>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<!-- 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 js-telemetry-table__content"
|
||||
:style="{ height: totalHeight + 'px'}">
|
||||
<tbody>
|
||||
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
|
||||
:headers="headers"
|
||||
:columnWidths="columnWidths"
|
||||
:rowIndex="rowIndex"
|
||||
:objectPath="objectPath"
|
||||
:rowOffset="rowOffset"
|
||||
:rowHeight="rowHeight"
|
||||
:row="row"
|
||||
:marked="row.marked"
|
||||
@mark="markRow"
|
||||
@unmark="unmarkRow"
|
||||
@markMultipleConcurrent="markMultipleConcurrentRows">
|
||||
</telemetry-table-row>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Sizing table -->
|
||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
|
||||
<tr>
|
||||
<template v-for="(title, key) in headers">
|
||||
<th :key="key" :style="{ width: configuredColumnWidths[key] + 'px', 'max-width': configuredColumnWidths[key] + 'px'}">{{title}}</th>
|
||||
</template>
|
||||
</tr>
|
||||
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
|
||||
:key="objectKeyString"
|
||||
:headers="headers"
|
||||
:columnWidths="configuredColumnWidths"
|
||||
:row="sizingRowData">
|
||||
</telemetry-table-row>
|
||||
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
||||
<!-- Headers table -->
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
||||
<table class="c-table__headers c-telemetry-table__headers">
|
||||
<thead>
|
||||
<tr class="c-telemetry-table__headers__labels">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@sort="sortBy(key)"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:sortOptions="sortOptions"
|
||||
:isEditing="isEditing"
|
||||
><span class="c-telemetry-table__headers__label">{{title}}</span>
|
||||
</table-column-header>
|
||||
</tr>
|
||||
<tr class="c-telemetry-table__headers__filter">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:isEditing="isEditing"
|
||||
>
|
||||
<search class="c-table__search"
|
||||
v-model="filters[key]"
|
||||
v-on:input="filterChanged(key)"
|
||||
v-on:clear="clearFilter(key)" />
|
||||
</table-column-header>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<telemetry-filter-indicator></telemetry-filter-indicator>
|
||||
</div>
|
||||
</div><!-- closes c-table-wrapper -->
|
||||
<!-- 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"
|
||||
:style="{ height: totalHeight + 'px'}">
|
||||
<tbody>
|
||||
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
|
||||
:headers="headers"
|
||||
:columnWidths="columnWidths"
|
||||
:rowIndex="rowIndex"
|
||||
:rowOffset="rowOffset"
|
||||
:rowHeight="rowHeight"
|
||||
:row="row">
|
||||
</telemetry-table-row>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Sizing table -->
|
||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
|
||||
<tr>
|
||||
<template v-for="(title, key) in headers">
|
||||
<th :key="key" :style="{ width: configuredColumnWidths[key] + 'px', 'max-width': configuredColumnWidths[key] + 'px'}">{{title}}</th>
|
||||
</template>
|
||||
</tr>
|
||||
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
|
||||
:headers="headers"
|
||||
:columnWidths="configuredColumnWidths"
|
||||
:row="sizingRowData">
|
||||
</telemetry-table-row>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -174,7 +131,7 @@
|
||||
display: block;
|
||||
flex: 1 0 auto;
|
||||
width: 100px;
|
||||
vertical-align: middle; // This is crucial to hiding 4px height injected by browser by default
|
||||
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
||||
}
|
||||
|
||||
td {
|
||||
@ -259,10 +216,6 @@
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
height: 18px; // Needed when a row has empty values in its cells
|
||||
|
||||
&.is-selected {
|
||||
background-color: $colorSelectedBg;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
@ -313,10 +266,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.paused {
|
||||
border: 1px solid #ff9900;
|
||||
}
|
||||
|
||||
/******************************* LEGACY */
|
||||
.s-status-taking-snapshot,
|
||||
.overlay.snapshot {
|
||||
@ -330,14 +279,12 @@
|
||||
import TelemetryTableRow from './table-row.vue';
|
||||
import search from '../../../ui/components/search.vue';
|
||||
import TableColumnHeader from './table-column-header.vue';
|
||||
import TelemetryFilterIndicator from './TelemetryFilterIndicator.vue';
|
||||
import CSVExporter from '../../../exporters/CSVExporter.js';
|
||||
import _ from 'lodash';
|
||||
|
||||
const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 100;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 20;
|
||||
const RESIZE_HOT_ZONE = 10;
|
||||
const MOVE_TRIGGER_WAIT = 500;
|
||||
const VERTICAL_SCROLL_WIDTH = 30;
|
||||
@ -346,30 +293,13 @@ export default {
|
||||
components: {
|
||||
TelemetryTableRow,
|
||||
TableColumnHeader,
|
||||
search,
|
||||
TelemetryFilterIndicator
|
||||
search
|
||||
},
|
||||
inject: ['table', 'openmct', 'objectPath'],
|
||||
inject: ['table', 'openmct', 'csvExporter'],
|
||||
props: {
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
allowExport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
allowFiltering: {
|
||||
'type': Boolean,
|
||||
'default': true
|
||||
},
|
||||
allowSorting: {
|
||||
'type': Boolean,
|
||||
'default': true
|
||||
},
|
||||
enableMarking: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -398,10 +328,7 @@ export default {
|
||||
dropOffsetLeft: undefined,
|
||||
isDropTargetActive: false,
|
||||
isAutosizeEnabled: configuration.autosize,
|
||||
scrollW: 0,
|
||||
markCounter: 0,
|
||||
paused: false,
|
||||
markedRows: []
|
||||
scrollW: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -437,48 +364,42 @@ 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;
|
||||
let start = 0;
|
||||
let end = VISIBLE_ROW_COUNT;
|
||||
let filteredRows = this.table.filteredRows.getRows();
|
||||
let filteredRowsLength = filteredRows.length;
|
||||
|
||||
if (filteredRowsLength < VISIBLE_ROW_COUNT) {
|
||||
end = filteredRowsLength;
|
||||
} else {
|
||||
let firstVisible = this.calculateFirstVisibleRow();
|
||||
let lastVisible = this.calculateLastVisibleRow();
|
||||
let totalVisible = lastVisible - firstVisible;
|
||||
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
||||
|
||||
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
|
||||
start = firstVisible - Math.floor(numberOffscreen / 2);
|
||||
end = lastVisible + Math.ceil(numberOffscreen / 2);
|
||||
if (filteredRowsLength < VISIBLE_ROW_COUNT) {
|
||||
end = filteredRowsLength;
|
||||
} else {
|
||||
let firstVisible = this.calculateFirstVisibleRow();
|
||||
let lastVisible = this.calculateLastVisibleRow();
|
||||
let totalVisible = lastVisible - firstVisible;
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
end = Math.min(VISIBLE_ROW_COUNT, filteredRowsLength);
|
||||
} else if (end >= filteredRowsLength) {
|
||||
end = filteredRowsLength;
|
||||
start = end - VISIBLE_ROW_COUNT + 1;
|
||||
}
|
||||
}
|
||||
this.rowOffset = start;
|
||||
this.visibleRows = filteredRows.slice(start, end);
|
||||
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
|
||||
start = firstVisible - Math.floor(numberOffscreen / 2);
|
||||
end = lastVisible + Math.ceil(numberOffscreen / 2);
|
||||
|
||||
this.updatingView = false;
|
||||
});
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
end = Math.min(VISIBLE_ROW_COUNT, filteredRowsLength);
|
||||
} else if (end >= filteredRowsLength) {
|
||||
end = filteredRowsLength;
|
||||
start = end - VISIBLE_ROW_COUNT + 1;
|
||||
}
|
||||
}
|
||||
this.rowOffset = start;
|
||||
this.visibleRows = filteredRows.slice(start, end);
|
||||
},
|
||||
calculateFirstVisibleRow() {
|
||||
let scrollTop = this.scrollable.scrollTop;
|
||||
return Math.floor(scrollTop / this.rowHeight);
|
||||
return Math.floor(this.scrollable.scrollTop / this.rowHeight);
|
||||
},
|
||||
calculateLastVisibleRow() {
|
||||
let scrollBottom = this.scrollable.scrollTop + this.scrollable.offsetHeight;
|
||||
return Math.ceil(scrollBottom / this.rowHeight);
|
||||
let bottomScroll = this.scrollable.scrollTop + this.scrollable.offsetHeight;
|
||||
return Math.floor(bottomScroll / this.rowHeight);
|
||||
},
|
||||
updateHeaders() {
|
||||
this.headers = this.table.configuration.getVisibleHeaders();
|
||||
@ -522,92 +443,81 @@ export default {
|
||||
}
|
||||
this.table.sortBy(this.sortOptions);
|
||||
},
|
||||
scroll () {
|
||||
this.updateVisibleRows();
|
||||
this.synchronizeScrollX();
|
||||
scroll() {
|
||||
if (!this.processingScroll) {
|
||||
this.processingScroll = true;
|
||||
requestAnimationFrame(()=> {
|
||||
this.updateVisibleRows();
|
||||
this.synchronizeScrollX();
|
||||
|
||||
if (this.shouldSnapToBottom()) {
|
||||
this.autoScroll = true;
|
||||
} else {
|
||||
// If user scrolls away from bottom, disable auto-scroll.
|
||||
// Auto-scroll will be re-enabled if user scrolls to bottom again.
|
||||
this.autoScroll = false;
|
||||
if (this.shouldSnapToBottom()) {
|
||||
this.autoScroll = true;
|
||||
} else {
|
||||
// If user scrolls away from bottom, disable auto-scroll.
|
||||
// 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 = Number.MAX_SAFE_INTEGER;
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight;
|
||||
},
|
||||
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();
|
||||
|
||||
rowsAdded(rows) {
|
||||
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.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(()=> {
|
||||
this.updateVisibleRows();
|
||||
if (this.autoScroll) {
|
||||
this.$nextTick().then(this.scrollToBottom);
|
||||
}
|
||||
this.updatingView = false;
|
||||
});
|
||||
}
|
||||
|
||||
this.updateVisibleRows();
|
||||
},
|
||||
rowsRemoved (rows) {
|
||||
this.setHeight();
|
||||
this.updateVisibleRows();
|
||||
rowsRemoved(rows) {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(()=> {
|
||||
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(data) {
|
||||
exportAsCSV() {
|
||||
const headerKeys = Object.keys(this.headers);
|
||||
|
||||
this.csvExporter.export(data, {
|
||||
const justTheData = this.table.filteredRows.getRows()
|
||||
.map(row => row.getFormattedDatum(this.headers));
|
||||
this.csvExporter.export(justTheData, {
|
||||
filename: this.table.domainObject.name + '.csv',
|
||||
headers: headerKeys
|
||||
});
|
||||
},
|
||||
exportAllDataAsCSV() {
|
||||
const justTheData = this.table.filteredRows.getRows()
|
||||
.map(row => row.getFormattedDatum(this.headers));
|
||||
|
||||
this.exportAsCSV(justTheData);
|
||||
},
|
||||
exportMarkedDataAsCSV() {
|
||||
const data = this.table.filteredRows.getRows()
|
||||
.filter(row => row.marked === true)
|
||||
.map(row => row.getFormattedDatum(this.headers));
|
||||
|
||||
this.exportAsCSV(data);
|
||||
},
|
||||
outstandingRequests(loading) {
|
||||
this.loading = loading;
|
||||
},
|
||||
@ -685,133 +595,23 @@ 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;
|
||||
}
|
||||
this.scrollable.scrollTop = scrollTop;
|
||||
width = el.clientWidth;
|
||||
height = el.clientHeight;
|
||||
}
|
||||
scrollTop = this.scrollable.scrollTop;
|
||||
}, RESIZE_POLL_INTERVAL);
|
||||
},
|
||||
clearRowsAndRerender() {
|
||||
this.visibleRows = [];
|
||||
this.$nextTick().then(this.updateVisibleRows);
|
||||
},
|
||||
pause(pausedByButton) {
|
||||
if (pausedByButton) {
|
||||
this.pausedByButton = true;
|
||||
}
|
||||
this.paused = true;
|
||||
this.table.pause();
|
||||
},
|
||||
unpause(unpausedByButton) {
|
||||
if (unpausedByButton) {
|
||||
this.paused = false;
|
||||
this.table.unpause();
|
||||
this.markedRows = [];
|
||||
this.pausedByButton = false;
|
||||
} else {
|
||||
if (!this.pausedByButton) {
|
||||
this.paused = false;
|
||||
this.table.unpause();
|
||||
this.markedRows = [];
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
togglePauseByButton() {
|
||||
if (this.paused) {
|
||||
this.unpause(true);
|
||||
} else {
|
||||
this.pause(true);
|
||||
}
|
||||
},
|
||||
undoMarkedRows(unpause) {
|
||||
this.markedRows.forEach(r => r.marked = false);
|
||||
this.markedRows = [];
|
||||
},
|
||||
unmarkRow(rowIndex) {
|
||||
this.undoMarkedRows();
|
||||
this.unpause();
|
||||
},
|
||||
markRow(rowIndex, keyModifier) {
|
||||
if (!this.enableMarking) {
|
||||
return;
|
||||
}
|
||||
|
||||
let insertMethod = 'unshift';
|
||||
|
||||
if (this.markedRows.length && !keyModifier) {
|
||||
this.undoMarkedRows();
|
||||
insertMethod = 'push';
|
||||
}
|
||||
|
||||
let markedRow = this.visibleRows[rowIndex];
|
||||
|
||||
this.$set(markedRow, 'marked', true);
|
||||
this.pause();
|
||||
|
||||
this.markedRows[insertMethod](markedRow);
|
||||
},
|
||||
unmarkAllRows(skipUnpause) {
|
||||
this.markedRows.forEach(row => row.marked = false);
|
||||
this.markedRows = [];
|
||||
this.unpause();
|
||||
},
|
||||
markMultipleConcurrentRows(rowIndex) {
|
||||
if (!this.enableMarking) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.markedRows.length) {
|
||||
this.markRow(rowIndex);
|
||||
} else {
|
||||
if (this.markedRows.length > 1) {
|
||||
this.markedRows.forEach((r,i) => {
|
||||
if (i !== 0) {
|
||||
r.marked = false;
|
||||
}
|
||||
});
|
||||
this.markedRows.splice(1);
|
||||
}
|
||||
let lastRowToBeMarked = this.visibleRows[rowIndex];
|
||||
|
||||
let allRows = this.table.filteredRows.getRows(),
|
||||
firstRowIndex = allRows.indexOf(this.markedRows[0]),
|
||||
lastRowIndex = allRows.indexOf(lastRowToBeMarked);
|
||||
|
||||
//supports backward selection
|
||||
if (lastRowIndex < firstRowIndex) {
|
||||
let temp = lastRowIndex;
|
||||
|
||||
lastRowIndex = firstRowIndex;
|
||||
firstRowIndex = temp - 1;
|
||||
}
|
||||
|
||||
for (var i = firstRowIndex + 1; i <= lastRowIndex; i++) {
|
||||
let row = allRows[i];
|
||||
row.marked = true;
|
||||
this.markedRows.push(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
this.csvExporter = new CSVExporter();
|
||||
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);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
this.table.on('refresh', this.clearRowsAndRerender);
|
||||
|
||||
this.table.filteredRows.on('add', this.rowsAdded);
|
||||
this.table.filteredRows.on('remove', this.rowsRemoved);
|
||||
@ -821,7 +621,6 @@ 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');
|
||||
|
||||
@ -837,7 +636,6 @@ export default {
|
||||
this.table.off('object-added', this.addObject);
|
||||
this.table.off('object-removed', this.removeObject);
|
||||
this.table.off('outstanding-requests', this.outstandingRequests);
|
||||
this.table.off('refresh', this.clearRowsAndRerender);
|
||||
|
||||
this.table.filteredRows.off('add', this.rowsAdded);
|
||||
this.table.filteredRows.off('remove', this.rowsRemoved);
|
||||
|
6
src/plugins/telemetryTable/table-row.html
Normal file
6
src/plugins/telemetryTable/table-row.html
Normal file
@ -0,0 +1,6 @@
|
||||
<tr :style="{ top: rowTop }" :class="rowLimitClass">
|
||||
<td v-for="(title, key, headerIndex) in headers"
|
||||
:style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}"
|
||||
:title="formattedRow[key]"
|
||||
:class="cellLimitClasses[key]">{{formattedRow[key]}}</td>
|
||||
</tr>
|
@ -167,7 +167,6 @@ export default {
|
||||
this.xAxis.scale(this.xScale);
|
||||
this.xAxis.tickFormat(utcMultiTimeFormat);
|
||||
this.axisElement.call(this.xAxis);
|
||||
this.setScale();
|
||||
},
|
||||
getActiveFormatter() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user