mirror of
https://github.com/nasa/openmct.git
synced 2025-06-27 19:38:53 +00:00
Compare commits
37 Commits
refresh-ap
...
yield-requ
Author | SHA1 | Date | |
---|---|---|---|
15926cc9a2 | |||
dad9f12a5c | |||
aa5edb0b83 | |||
d647e287fd | |||
b315803180 | |||
b27317631b | |||
7998ee3f61 | |||
953a9daafb | |||
e7d94d9447 | |||
336babdb15 | |||
0b4624fc8f | |||
62c54e732d | |||
e63393b0c4 | |||
63f9cd449f | |||
02c97ae10b | |||
5b499368ae | |||
dd556c3335 | |||
54220f547b | |||
93d967c2b3 | |||
1a71708b63 | |||
1226459c6f | |||
d7c9c9cb98 | |||
2131ef2397 | |||
ab425ed04e | |||
514181c2fd | |||
757ff1c85a | |||
e7f21654e0 | |||
c895869cbf | |||
5d40a55a20 | |||
54477f2bb9 | |||
eb894894b5 | |||
3f06bc5c48 | |||
c3238e50ee | |||
d83e919541 | |||
5fcf0bd7de | |||
48c22369a1 | |||
6506077f4d |
@ -195,6 +195,7 @@
|
|||||||
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
||||||
{indicator: true}
|
{indicator: true}
|
||||||
));
|
));
|
||||||
|
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
|
||||||
openmct.start();
|
openmct.start();
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
@ -21,32 +21,24 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
"moment-timezone",
|
|
||||||
"./src/indicators/ClockIndicator",
|
|
||||||
"./src/services/TickerService",
|
"./src/services/TickerService",
|
||||||
"./src/services/TimerService",
|
"./src/services/TimerService",
|
||||||
"./src/controllers/ClockController",
|
|
||||||
"./src/controllers/TimerController",
|
"./src/controllers/TimerController",
|
||||||
"./src/controllers/RefreshingController",
|
"./src/controllers/RefreshingController",
|
||||||
"./src/actions/StartTimerAction",
|
"./src/actions/StartTimerAction",
|
||||||
"./src/actions/RestartTimerAction",
|
"./src/actions/RestartTimerAction",
|
||||||
"./src/actions/StopTimerAction",
|
"./src/actions/StopTimerAction",
|
||||||
"./src/actions/PauseTimerAction",
|
"./src/actions/PauseTimerAction",
|
||||||
"./res/templates/clock.html",
|
|
||||||
"./res/templates/timer.html"
|
"./res/templates/timer.html"
|
||||||
], function (
|
], function (
|
||||||
MomentTimezone,
|
|
||||||
ClockIndicator,
|
|
||||||
TickerService,
|
TickerService,
|
||||||
TimerService,
|
TimerService,
|
||||||
ClockController,
|
|
||||||
TimerController,
|
TimerController,
|
||||||
RefreshingController,
|
RefreshingController,
|
||||||
StartTimerAction,
|
StartTimerAction,
|
||||||
RestartTimerAction,
|
RestartTimerAction,
|
||||||
StopTimerAction,
|
StopTimerAction,
|
||||||
PauseTimerAction,
|
PauseTimerAction,
|
||||||
clockTemplate,
|
|
||||||
timerTemplate
|
timerTemplate
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
@ -73,16 +65,6 @@ define([
|
|||||||
"value": "YYYY/MM/DD HH:mm:ss"
|
"value": "YYYY/MM/DD HH:mm:ss"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"indicators": [
|
|
||||||
{
|
|
||||||
"implementation": ClockIndicator,
|
|
||||||
"depends": [
|
|
||||||
"tickerService",
|
|
||||||
"CLOCK_INDICATOR_FORMAT"
|
|
||||||
],
|
|
||||||
"priority": "preferred"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": [
|
"services": [
|
||||||
{
|
{
|
||||||
"key": "tickerService",
|
"key": "tickerService",
|
||||||
@ -99,14 +81,6 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"controllers": [
|
"controllers": [
|
||||||
{
|
|
||||||
"key": "ClockController",
|
|
||||||
"implementation": ClockController,
|
|
||||||
"depends": [
|
|
||||||
"$scope",
|
|
||||||
"tickerService"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "TimerController",
|
"key": "TimerController",
|
||||||
"implementation": TimerController,
|
"implementation": TimerController,
|
||||||
@ -126,12 +100,6 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"views": [
|
"views": [
|
||||||
{
|
|
||||||
"key": "clock",
|
|
||||||
"type": "clock",
|
|
||||||
"editable": false,
|
|
||||||
"template": clockTemplate
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "timer",
|
"key": "timer",
|
||||||
"type": "timer",
|
"type": "timer",
|
||||||
@ -186,70 +154,6 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"types": [
|
"types": [
|
||||||
{
|
|
||||||
"key": "clock",
|
|
||||||
"name": "Clock",
|
|
||||||
"cssClass": "icon-clock",
|
|
||||||
"description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.",
|
|
||||||
"priority": 101,
|
|
||||||
"features": [
|
|
||||||
"creation"
|
|
||||||
],
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"key": "clockFormat",
|
|
||||||
"name": "Display Format",
|
|
||||||
"control": "composite",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"control": "select",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"value": "YYYY/MM/DD hh:mm:ss",
|
|
||||||
"name": "YYYY/MM/DD hh:mm:ss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "YYYY/DDD hh:mm:ss",
|
|
||||||
"name": "YYYY/DDD hh:mm:ss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "hh:mm:ss",
|
|
||||||
"name": "hh:mm:ss"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cssClass": "l-inline"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"control": "select",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"value": "clock12",
|
|
||||||
"name": "12hr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "clock24",
|
|
||||||
"name": "24hr"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cssClass": "l-inline"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "timezone",
|
|
||||||
"name": "Timezone",
|
|
||||||
"control": "autocomplete",
|
|
||||||
"options": MomentTimezone.tz.names()
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"model": {
|
|
||||||
"clockFormat": [
|
|
||||||
"YYYY/MM/DD hh:mm:ss",
|
|
||||||
"clock12"
|
|
||||||
],
|
|
||||||
"timezone": "UTC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "timer",
|
"key": "timer",
|
||||||
"name": "Timer",
|
"name": "Timer",
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
<!--
|
|
||||||
Open MCT, Copyright (c) 2009-2016, 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.
|
|
||||||
-->
|
|
||||||
<div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
|
|
||||||
<div class="c-clock__timezone">
|
|
||||||
{{clock.zone()}}
|
|
||||||
</div>
|
|
||||||
<div class="c-clock__value">
|
|
||||||
{{clock.text()}}
|
|
||||||
</div>
|
|
||||||
<div class="c-clock__ampm">
|
|
||||||
{{clock.ampm()}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,110 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2009-2016, 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([
|
|
||||||
'moment',
|
|
||||||
'moment-timezone'
|
|
||||||
],
|
|
||||||
function (
|
|
||||||
moment,
|
|
||||||
momentTimezone
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller for views of a Clock domain object.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @memberof platform/features/clock
|
|
||||||
* @param {angular.Scope} $scope the Angular scope
|
|
||||||
* @param {platform/features/clock.TickerService} tickerService
|
|
||||||
* a service used to align behavior with clock ticks
|
|
||||||
*/
|
|
||||||
function ClockController($scope, tickerService) {
|
|
||||||
var lastTimestamp,
|
|
||||||
unlisten,
|
|
||||||
timeFormat,
|
|
||||||
zoneName,
|
|
||||||
self = this;
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
var m = zoneName
|
|
||||||
? moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp);
|
|
||||||
self.zoneAbbr = m.zoneAbbr();
|
|
||||||
self.textValue = timeFormat && m.format(timeFormat);
|
|
||||||
self.ampmValue = m.format("A"); // Just the AM or PM part
|
|
||||||
}
|
|
||||||
|
|
||||||
function tick(timestamp) {
|
|
||||||
lastTimestamp = timestamp;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateModel(model) {
|
|
||||||
var baseFormat;
|
|
||||||
if (model !== undefined) {
|
|
||||||
baseFormat = model.clockFormat[0];
|
|
||||||
|
|
||||||
self.use24 = model.clockFormat[1] === 'clock24';
|
|
||||||
timeFormat = self.use24
|
|
||||||
? baseFormat.replace('hh', "HH") : baseFormat;
|
|
||||||
// If wrong timezone is provided, the UTC will be used
|
|
||||||
zoneName = momentTimezone.tz.names().includes(model.timezone)
|
|
||||||
? model.timezone : "UTC";
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull in the model (clockFormat and timezone) from the domain object model
|
|
||||||
$scope.$watch('model', updateModel);
|
|
||||||
|
|
||||||
// Listen for clock ticks ... and stop listening on destroy
|
|
||||||
unlisten = tickerService.listen(tick);
|
|
||||||
$scope.$on('$destroy', unlisten);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the clock's time zone, as displayable text.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
ClockController.prototype.zone = function () {
|
|
||||||
return this.zoneAbbr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current time, as displayable text.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
ClockController.prototype.text = function () {
|
|
||||||
return this.textValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the text to display to qualify a time as AM or PM.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
ClockController.prototype.ampm = function () {
|
|
||||||
return this.use24 ? '' : this.ampmValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
return ClockController;
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,65 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2009-2016, 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(
|
|
||||||
['moment'],
|
|
||||||
function (moment) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicator that displays the current UTC time in the status area.
|
|
||||||
* @implements {Indicator}
|
|
||||||
* @memberof platform/features/clock
|
|
||||||
* @param {platform/features/clock.TickerService} tickerService
|
|
||||||
* a service used to align behavior with clock ticks
|
|
||||||
* @param {string} indicatorFormat format string for timestamps
|
|
||||||
* shown in this indicator
|
|
||||||
*/
|
|
||||||
function ClockIndicator(tickerService, indicatorFormat) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.text = "";
|
|
||||||
|
|
||||||
tickerService.listen(function (timestamp) {
|
|
||||||
self.text = moment.utc(timestamp)
|
|
||||||
.format(indicatorFormat) + " UTC";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ClockIndicator.prototype.getGlyphClass = function () {
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
ClockIndicator.prototype.getCssClass = function () {
|
|
||||||
return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable";
|
|
||||||
};
|
|
||||||
|
|
||||||
ClockIndicator.prototype.getText = function () {
|
|
||||||
return this.text;
|
|
||||||
};
|
|
||||||
|
|
||||||
ClockIndicator.prototype.getDescription = function () {
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
return ClockIndicator;
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,107 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2009-2017, 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/controllers/ClockController"],
|
|
||||||
function (ClockController) {
|
|
||||||
|
|
||||||
// Wed, 03 Jun 2015 17:56:14 GMT
|
|
||||||
var TEST_TIMESTAMP = 1433354174000;
|
|
||||||
|
|
||||||
describe("A clock view's controller", function () {
|
|
||||||
var mockScope,
|
|
||||||
mockTicker,
|
|
||||||
mockUnticker,
|
|
||||||
controller;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
|
|
||||||
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
|
|
||||||
mockUnticker = jasmine.createSpy('unticker');
|
|
||||||
|
|
||||||
mockTicker.listen.and.returnValue(mockUnticker);
|
|
||||||
|
|
||||||
controller = new ClockController(mockScope, mockTicker);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("watches for model (clockFormat and timezone) from the domain object model", function () {
|
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
|
||||||
"model",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("subscribes to clock ticks", function () {
|
|
||||||
expect(mockTicker.listen)
|
|
||||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("unsubscribes to ticks when destroyed", function () {
|
|
||||||
// Make sure $destroy is being listened for...
|
|
||||||
expect(mockScope.$on.calls.mostRecent().args[0]).toEqual('$destroy');
|
|
||||||
expect(mockUnticker).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// ...and makes sure that its listener unsubscribes from ticker
|
|
||||||
mockScope.$on.calls.mostRecent().args[1]();
|
|
||||||
expect(mockUnticker).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("formats using the format string from the model", function () {
|
|
||||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
|
||||||
mockScope.$watch.calls.mostRecent().args[1]({
|
|
||||||
"clockFormat": [
|
|
||||||
"YYYY-DDD hh:mm:ss",
|
|
||||||
"clock24"
|
|
||||||
],
|
|
||||||
"timezone": "Canada/Eastern"
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.zone()).toEqual("EDT");
|
|
||||||
expect(controller.text()).toEqual("2015-154 13:56:14");
|
|
||||||
expect(controller.ampm()).toEqual("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("formats 12-hour time", function () {
|
|
||||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
|
||||||
mockScope.$watch.calls.mostRecent().args[1]({
|
|
||||||
"clockFormat": [
|
|
||||||
"YYYY-DDD hh:mm:ss",
|
|
||||||
"clock12"
|
|
||||||
],
|
|
||||||
"timezone": ""
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(controller.zone()).toEqual("UTC");
|
|
||||||
expect(controller.text()).toEqual("2015-154 05:56:14");
|
|
||||||
expect(controller.ampm()).toEqual("PM");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not throw exceptions when model is undefined", function () {
|
|
||||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
|
||||||
expect(function () {
|
|
||||||
mockScope.$watch.calls.mostRecent().args[1](undefined);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,58 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2009-2016, 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/indicators/ClockIndicator"],
|
|
||||||
function (ClockIndicator) {
|
|
||||||
|
|
||||||
// Wed, 03 Jun 2015 17:56:14 GMT
|
|
||||||
var TEST_TIMESTAMP = 1433354174000,
|
|
||||||
TEST_FORMAT = "YYYY-DDD HH:mm:ss";
|
|
||||||
|
|
||||||
describe("The clock indicator", function () {
|
|
||||||
var mockTicker,
|
|
||||||
mockUnticker,
|
|
||||||
indicator;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
|
|
||||||
mockUnticker = jasmine.createSpy('unticker');
|
|
||||||
|
|
||||||
mockTicker.listen.and.returnValue(mockUnticker);
|
|
||||||
|
|
||||||
indicator = new ClockIndicator(mockTicker, TEST_FORMAT);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("displays the current time", function () {
|
|
||||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
|
||||||
expect(indicator.getText()).toEqual("2015-154 17:56:14 UTC");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("implements the Indicator interface", function () {
|
|
||||||
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
|
|
||||||
expect(indicator.getText()).toEqual(jasmine.any(String));
|
|
||||||
expect(indicator.getDescription()).toEqual(jasmine.any(String));
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -30,8 +30,8 @@ define([
|
|||||||
|
|
||||||
return function ImportExportPlugin() {
|
return function ImportExportPlugin() {
|
||||||
return function (openmct) {
|
return function (openmct) {
|
||||||
ExportAsJSONAction.appliesTo = function (context) {
|
ExportAsJSONAction.prototype.appliesTo = function (context) {
|
||||||
return openmct.$injector.get('policyService')
|
return this.openmct.$injector.get('policyService')
|
||||||
.allow("creation", context.domainObject.getCapability("type")
|
.allow("creation", context.domainObject.getCapability("type")
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,7 @@ define(
|
|||||||
],
|
],
|
||||||
function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
|
function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
|
||||||
|
|
||||||
xdescribe("The export JSON action", function () {
|
describe("The export JSON action", function () {
|
||||||
|
|
||||||
var context,
|
var context,
|
||||||
action,
|
action,
|
||||||
@ -102,7 +102,7 @@ define(
|
|||||||
expect(action).toBeDefined();
|
expect(action).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't export non-creatable objects in tree", function () {
|
xit("doesn't export non-creatable objects in tree", function () {
|
||||||
var nonCreatableType = {
|
var nonCreatableType = {
|
||||||
hasFeature:
|
hasFeature:
|
||||||
function (feature) {
|
function (feature) {
|
||||||
@ -149,7 +149,7 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can export self-containing objects", function () {
|
xit("can export self-containing objects", function () {
|
||||||
var parent = domainObjectFactory({
|
var parent = domainObjectFactory({
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
model: {
|
model: {
|
||||||
@ -191,7 +191,7 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exports links to external objects as new objects", function () {
|
xit("exports links to external objects as new objects", function () {
|
||||||
var parent = domainObjectFactory({
|
var parent = domainObjectFactory({
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
model: {
|
model: {
|
||||||
|
@ -27,7 +27,7 @@ define(
|
|||||||
],
|
],
|
||||||
function (ImportAsJSONAction, domainObjectFactory) {
|
function (ImportAsJSONAction, domainObjectFactory) {
|
||||||
|
|
||||||
xdescribe("The import JSON action", function () {
|
describe("The import JSON action", function () {
|
||||||
|
|
||||||
var context = {};
|
var context = {};
|
||||||
var action,
|
var action,
|
||||||
@ -146,7 +146,7 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can import self-containing objects", function () {
|
xit("can import self-containing objects", function () {
|
||||||
var compDomainObject = domainObjectFactory({
|
var compDomainObject = domainObjectFactory({
|
||||||
name: 'compObject',
|
name: 'compObject',
|
||||||
model: { name: 'compObject'},
|
model: { name: 'compObject'},
|
||||||
@ -198,7 +198,7 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("assigns new ids to each imported object", function () {
|
xit("assigns new ids to each imported object", function () {
|
||||||
dialogService.getUserInput.and.returnValue(Promise.resolve(
|
dialogService.getUserInput.and.returnValue(Promise.resolve(
|
||||||
{
|
{
|
||||||
selectFile: {
|
selectFile: {
|
||||||
|
@ -358,6 +358,20 @@ ObjectAPI.prototype.applyGetInterceptors = function (identifier, domainObject) {
|
|||||||
return domainObject;
|
return domainObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return relative url path from a given object path
|
||||||
|
* eg: #/browse/mine/cb56f6bf-c900-43b7-b923-2e3b64b412db/6e89e858-77ce-46e4-a1ad-749240286497/....
|
||||||
|
* @param {Array} objectPath
|
||||||
|
* @returns {string} relative url for object
|
||||||
|
*/
|
||||||
|
ObjectAPI.prototype.getRelativePath = function (objectPath) {
|
||||||
|
return objectPath
|
||||||
|
.map(p => this.makeKeyString(p.identifier))
|
||||||
|
.reverse()
|
||||||
|
.join('/')
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify a domain object.
|
* Modify a domain object.
|
||||||
* @param {module:openmct.DomainObject} object the object to mutate
|
* @param {module:openmct.DomainObject} object the object to mutate
|
||||||
|
@ -10,28 +10,37 @@ const cssClasses = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Overlay extends EventEmitter {
|
class Overlay extends EventEmitter {
|
||||||
constructor(options) {
|
constructor({
|
||||||
|
buttons,
|
||||||
|
autoHide = true,
|
||||||
|
dismissable = true,
|
||||||
|
element,
|
||||||
|
onDestroy,
|
||||||
|
size
|
||||||
|
} = {}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.dismissable = options.dismissable !== false;
|
|
||||||
this.container = document.createElement('div');
|
this.container = document.createElement('div');
|
||||||
this.container.classList.add('l-overlay-wrapper', cssClasses[options.size]);
|
this.container.classList.add('l-overlay-wrapper', cssClasses[size]);
|
||||||
|
|
||||||
|
this.autoHide = autoHide;
|
||||||
|
this.dismissable = dismissable !== false;
|
||||||
|
|
||||||
this.component = new Vue({
|
this.component = new Vue({
|
||||||
provide: {
|
|
||||||
dismiss: this.dismiss.bind(this),
|
|
||||||
element: options.element,
|
|
||||||
buttons: options.buttons,
|
|
||||||
dismissable: this.dismissable
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
OverlayComponent: OverlayComponent
|
OverlayComponent: OverlayComponent
|
||||||
},
|
},
|
||||||
|
provide: {
|
||||||
|
dismiss: this.dismiss.bind(this),
|
||||||
|
element,
|
||||||
|
buttons,
|
||||||
|
dismissable: this.dismissable
|
||||||
|
},
|
||||||
template: '<overlay-component></overlay-component>'
|
template: '<overlay-component></overlay-component>'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.onDestroy) {
|
if (onDestroy) {
|
||||||
this.once('destroy', options.onDestroy);
|
this.once('destroy', onDestroy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,10 @@ class OverlayAPI {
|
|||||||
*/
|
*/
|
||||||
showOverlay(overlay) {
|
showOverlay(overlay) {
|
||||||
if (this.activeOverlays.length) {
|
if (this.activeOverlays.length) {
|
||||||
this.activeOverlays[this.activeOverlays.length - 1].container.classList.add('invisible');
|
const previousOverlay = this.activeOverlays[this.activeOverlays.length - 1];
|
||||||
|
if (previousOverlay.autoHide) {
|
||||||
|
previousOverlay.container.classList.add('invisible');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.activeOverlays.push(overlay);
|
this.activeOverlays.push(overlay);
|
||||||
|
@ -115,6 +115,7 @@ export class TelemetryCollection extends EventEmitter {
|
|||||||
|
|
||||||
this._requestHistoricalTelemetry();
|
this._requestHistoricalTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a historical provider exists, then historical requests will be made
|
* If a historical provider exists, then historical requests will be made
|
||||||
* @private
|
* @private
|
||||||
@ -126,20 +127,25 @@ export class TelemetryCollection extends EventEmitter {
|
|||||||
|
|
||||||
let historicalData;
|
let historicalData;
|
||||||
|
|
||||||
|
this.options.onPartialResponse = this._processNewTelemetry.bind(this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.requestAbort = new AbortController();
|
this.requestAbort = new AbortController();
|
||||||
this.options.signal = this.requestAbort.signal;
|
this.options.signal = this.requestAbort.signal;
|
||||||
historicalData = await this.historicalProvider.request(this.domainObject, this.options);
|
historicalData = await this.historicalProvider.request(this.domainObject, this.options);
|
||||||
this.requestAbort = undefined;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error requesting telemetry data...');
|
if (error.name !== 'AbortError') {
|
||||||
this.requestAbort = undefined;
|
console.error('Error requesting telemetry data...');
|
||||||
this._error(error);
|
this._error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.requestAbort = undefined;
|
||||||
|
|
||||||
this._processNewTelemetry(historicalData);
|
this._processNewTelemetry(historicalData);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This uses the built in subscription function from Telemetry API
|
* This uses the built in subscription function from Telemetry API
|
||||||
* @private
|
* @private
|
||||||
@ -342,6 +348,8 @@ export class TelemetryCollection extends EventEmitter {
|
|||||||
this.boundedTelemetry = [];
|
this.boundedTelemetry = [];
|
||||||
this.futureBuffer = [];
|
this.futureBuffer = [];
|
||||||
|
|
||||||
|
this.emit('clear');
|
||||||
|
|
||||||
this._requestHistoricalTelemetry();
|
this._requestHistoricalTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class ImageExporter {
|
|||||||
element.id = oldId;
|
element.id = oldId;
|
||||||
},
|
},
|
||||||
removeContainer: true // Set to false to debug what html2canvas renders
|
removeContainer: true // Set to false to debug what html2canvas renders
|
||||||
}).then(function (canvas) {
|
}).then(canvas => {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
@ -105,9 +105,10 @@ class ImageExporter {
|
|||||||
|
|
||||||
return canvas.toBlob(blob => resolve({ blob }), mimeType);
|
return canvas.toBlob(blob => resolve({ blob }), mimeType);
|
||||||
});
|
});
|
||||||
}, function (error) {
|
}).catch(error => {
|
||||||
console.log('error capturing image', error);
|
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
|
|
||||||
|
console.error('error capturing image', error);
|
||||||
const errorDialog = overlays.dialog({
|
const errorDialog = overlays.dialog({
|
||||||
iconClass: 'error',
|
iconClass: 'error',
|
||||||
message: 'Image was not captured successfully!',
|
message: 'Image was not captured successfully!',
|
||||||
|
78
src/plugins/clearData/ClearDataAction.js
Normal file
78
src/plugins/clearData/ClearDataAction.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
function inSelectionPath(openmct, domainObject) {
|
||||||
|
const domainObjectIdentifier = domainObject.identifier;
|
||||||
|
|
||||||
|
return openmct.selection.get().some(selectionPath => {
|
||||||
|
return selectionPath.some(objectInPath => {
|
||||||
|
const objectInPathIdentifier = objectInPath.context.item.identifier;
|
||||||
|
|
||||||
|
return openmct.objects.areIdsEqual(objectInPathIdentifier, domainObjectIdentifier);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ClearDataAction {
|
||||||
|
constructor(openmct, appliesToObjects) {
|
||||||
|
this.name = 'Clear Data for Object';
|
||||||
|
this.key = 'clear-data-action';
|
||||||
|
this.description = 'Clears current data for object, unsubscribes and resubscribes to data';
|
||||||
|
this.cssClass = 'icon-clear-data';
|
||||||
|
|
||||||
|
this._openmct = openmct;
|
||||||
|
this._appliesToObjects = appliesToObjects;
|
||||||
|
}
|
||||||
|
invoke(objectPath) {
|
||||||
|
let domainObject = null;
|
||||||
|
if (objectPath) {
|
||||||
|
domainObject = objectPath[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
this._openmct.objectViews.emit('clearData', domainObject);
|
||||||
|
}
|
||||||
|
appliesTo(objectPath) {
|
||||||
|
if (!objectPath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextualDomainObject = objectPath[0];
|
||||||
|
// first check to see if this action applies to this sort of object at all
|
||||||
|
const appliesToThisObject = this._appliesToObjects.some(type => {
|
||||||
|
return contextualDomainObject.type === type;
|
||||||
|
});
|
||||||
|
if (!appliesToThisObject) {
|
||||||
|
// we've selected something not applicable
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectInSelectionPath = inSelectionPath(this._openmct, contextualDomainObject);
|
||||||
|
if (objectInSelectionPath) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// if this it doesn't match up, check to see if we're in a composition (i.e., layout)
|
||||||
|
const routerPath = this._openmct.router.path[0];
|
||||||
|
|
||||||
|
return routerPath.type === 'layout';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'./components/globalClearIndicator.vue',
|
'./components/globalClearIndicator.vue',
|
||||||
'./clearDataAction',
|
'./ClearDataAction',
|
||||||
'vue'
|
'vue'
|
||||||
], function (
|
], function (
|
||||||
GlobaClearIndicator,
|
GlobaClearIndicator,
|
||||||
|
140
src/plugins/clearData/test/ClearDataActionSpec.js
Normal file
140
src/plugins/clearData/test/ClearDataActionSpec.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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,', () => {
|
||||||
|
const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
|
||||||
|
const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
|
||||||
|
const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
|
||||||
|
const goodMockSelectionPath = [[{
|
||||||
|
context: {
|
||||||
|
item: {
|
||||||
|
identifier: {
|
||||||
|
key: 'apple',
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]];
|
||||||
|
|
||||||
|
const openmct = {
|
||||||
|
objectViews: mockObjectViews,
|
||||||
|
indicators: mockIndicatorProvider,
|
||||||
|
actions: mockActionsProvider,
|
||||||
|
install: function (plugin) {
|
||||||
|
plugin(this);
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
get: function () {
|
||||||
|
return goodMockSelectionPath;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
objects: {
|
||||||
|
areIdsEqual: function (obj1, obj2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockObjectPath = [
|
||||||
|
{
|
||||||
|
name: 'mockObject1',
|
||||||
|
type: 'apple'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mockObject2',
|
||||||
|
type: 'banana'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
it('Global Clear Indicator is installed', () => {
|
||||||
|
openmct.install(ClearDataActionPlugin([]));
|
||||||
|
|
||||||
|
expect(mockIndicatorProvider.add).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Clear Data context menu action is installed', () => {
|
||||||
|
openmct.install(ClearDataActionPlugin([]));
|
||||||
|
|
||||||
|
expect(mockActionsProvider.register).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clear data action emits a clearData event when invoked', () => {
|
||||||
|
const action = new ClearDataAction(openmct);
|
||||||
|
|
||||||
|
action.invoke(mockObjectPath);
|
||||||
|
|
||||||
|
expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clears data on applicable objects', () => {
|
||||||
|
let action = new ClearDataAction(openmct, ['apple']);
|
||||||
|
|
||||||
|
const actionApplies = action.appliesTo(mockObjectPath);
|
||||||
|
|
||||||
|
expect(actionApplies).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not clear data on inapplicable objects', () => {
|
||||||
|
let action = new ClearDataAction(openmct, ['pineapple']);
|
||||||
|
|
||||||
|
const actionApplies = action.appliesTo(mockObjectPath);
|
||||||
|
|
||||||
|
expect(actionApplies).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not clear data if not in the selection path and not a layout', () => {
|
||||||
|
openmct.objects = {
|
||||||
|
areIdsEqual: function (obj1, obj2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openmct.router = {
|
||||||
|
path: [{type: 'not-a-layout'}]
|
||||||
|
};
|
||||||
|
|
||||||
|
let action = new ClearDataAction(openmct, ['apple']);
|
||||||
|
|
||||||
|
const actionApplies = action.appliesTo(mockObjectPath);
|
||||||
|
|
||||||
|
expect(actionApplies).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does clear data if not in the selection path and is a layout', () => {
|
||||||
|
openmct.objects = {
|
||||||
|
areIdsEqual: function (obj1, obj2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openmct.router = {
|
||||||
|
path: [{type: 'layout'}]
|
||||||
|
};
|
||||||
|
|
||||||
|
let action = new ClearDataAction(openmct, ['apple']);
|
||||||
|
|
||||||
|
const actionApplies = action.appliesTo(mockObjectPath);
|
||||||
|
|
||||||
|
expect(actionApplies).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
@ -1,64 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2021, 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 () {
|
|
||||||
const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
|
|
||||||
const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
|
|
||||||
const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
|
|
||||||
|
|
||||||
const openmct = {
|
|
||||||
objectViews: mockObjectViews,
|
|
||||||
indicators: mockIndicatorProvider,
|
|
||||||
actions: mockActionsProvider,
|
|
||||||
install: function (plugin) {
|
|
||||||
plugin(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const 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(mockActionsProvider.register).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]);
|
|
||||||
});
|
|
||||||
});
|
|
59
src/plugins/clock/ClockViewProvider.js
Normal file
59
src/plugins/clock/ClockViewProvider.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 Clock from './components/Clock.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default function ClockViewProvider(openmct) {
|
||||||
|
return {
|
||||||
|
key: 'clock.view',
|
||||||
|
name: 'Clock',
|
||||||
|
cssClass: 'icon-clock',
|
||||||
|
canView(domainObject) {
|
||||||
|
return domainObject.type === 'clock';
|
||||||
|
},
|
||||||
|
|
||||||
|
view: function (domainObject) {
|
||||||
|
let component;
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: function (element) {
|
||||||
|
component = new Vue({
|
||||||
|
el: element,
|
||||||
|
components: {
|
||||||
|
Clock
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct,
|
||||||
|
domainObject
|
||||||
|
},
|
||||||
|
template: '<clock />'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
destroy: function () {
|
||||||
|
component.$destroy();
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
99
src/plugins/clock/components/Clock.vue
Normal file
99
src/plugins/clock/components/Clock.vue
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2021, 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="l-angular-ov-wrapper">
|
||||||
|
<div class="u-contents">
|
||||||
|
<div class="c-clock l-time-display u-style-receiver js-style-receiver">
|
||||||
|
<div class="c-clock__timezone">
|
||||||
|
{{ timeZoneAbbr }}
|
||||||
|
</div>
|
||||||
|
<div class="c-clock__value">
|
||||||
|
{{ timeTextValue }}
|
||||||
|
</div>
|
||||||
|
<div class="c-clock__ampm">
|
||||||
|
{{ timeAmPm }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import momentTimezone from 'moment-timezone';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'domainObject'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
lastTimestamp: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
configuration() {
|
||||||
|
return this.domainObject.configuration;
|
||||||
|
},
|
||||||
|
baseFormat() {
|
||||||
|
return this.configuration.baseFormat;
|
||||||
|
},
|
||||||
|
use24() {
|
||||||
|
return this.configuration.use24 === 'clock24';
|
||||||
|
},
|
||||||
|
timezone() {
|
||||||
|
return this.configuration.timezone;
|
||||||
|
},
|
||||||
|
timeFormat() {
|
||||||
|
return this.use24 ? this.baseFormat.replace('hh', "HH") : this.baseFormat;
|
||||||
|
},
|
||||||
|
zoneName() {
|
||||||
|
return momentTimezone.tz.names().includes(this.timezone) ? this.timezone : "UTC";
|
||||||
|
},
|
||||||
|
momentTime() {
|
||||||
|
return this.zoneName ? moment.utc(this.lastTimestamp).tz(this.zoneName) : moment.utc(this.lastTimestamp);
|
||||||
|
},
|
||||||
|
timeZoneAbbr() {
|
||||||
|
return this.momentTime.zoneAbbr();
|
||||||
|
},
|
||||||
|
timeTextValue() {
|
||||||
|
return this.timeFormat && this.momentTime.format(this.timeFormat);
|
||||||
|
},
|
||||||
|
timeAmPm() {
|
||||||
|
return this.use24 ? '' : this.momentTime.format("A");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const TickerService = this.openmct.$injector.get('tickerService');
|
||||||
|
this.unlisten = TickerService.listen(this.tick);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unlisten) {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
tick(timestamp) {
|
||||||
|
this.lastTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
64
src/plugins/clock/components/ClockIndicator.vue
Normal file
64
src/plugins/clock/components/ClockIndicator.vue
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2021, 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-indicator t-indicator-clock icon-clock no-minify c-indicator--not-clickable">
|
||||||
|
<span class="label c-indicator__label">
|
||||||
|
{{ timeTextValue }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
indicatorFormat: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
timeTextValue: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.on('start', () => {
|
||||||
|
const TickerService = this.openmct.$injector.get('tickerService');
|
||||||
|
this.unlisten = TickerService.listen(this.tick);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unlisten) {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
tick(timestamp) {
|
||||||
|
this.timeTextValue = `${moment.utc(timestamp).format(this.indicatorFormat)} UTC`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
154
src/plugins/clock/plugin.js
Normal file
154
src/plugins/clock/plugin.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 ClockViewProvider from './ClockViewProvider';
|
||||||
|
import ClockIndicator from './components/ClockIndicator.vue';
|
||||||
|
|
||||||
|
import momentTimezone from 'moment-timezone';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default function ClockPlugin(options) {
|
||||||
|
return function install(openmct) {
|
||||||
|
const CLOCK_INDICATOR_FORMAT = 'YYYY/MM/DD HH:mm:ss';
|
||||||
|
openmct.types.addType('clock', {
|
||||||
|
name: 'Clock',
|
||||||
|
description: 'A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.',
|
||||||
|
creatable: true,
|
||||||
|
cssClass: 'icon-clock',
|
||||||
|
initialize: function (domainObject) {
|
||||||
|
domainObject.configuration = {
|
||||||
|
baseFormat: 'YYYY/MM/DD hh:mm:ss',
|
||||||
|
use24: 'clock12',
|
||||||
|
timezone: 'UTC'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
"form": [
|
||||||
|
{
|
||||||
|
"key": "displayFormat",
|
||||||
|
"name": "Display Format",
|
||||||
|
control: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 'YYYY/MM/DD hh:mm:ss',
|
||||||
|
name: 'YYYY/MM/DD hh:mm:ss'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'YYYY/DDD hh:mm:ss',
|
||||||
|
name: 'YYYY/DDD hh:mm:ss'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'hh:mm:ss',
|
||||||
|
name: 'hh:mm:ss'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cssClass: 'l-inline',
|
||||||
|
property: [
|
||||||
|
'configuration',
|
||||||
|
'baseFormat'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
control: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 'clock12',
|
||||||
|
name: '12hr'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'clock24',
|
||||||
|
name: '24hr'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cssClass: 'l-inline',
|
||||||
|
property: [
|
||||||
|
'configuration',
|
||||||
|
'use24'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timezone",
|
||||||
|
"name": "Timezone",
|
||||||
|
"control": "autocomplete",
|
||||||
|
"options": momentTimezone.tz.names(),
|
||||||
|
property: [
|
||||||
|
'configuration',
|
||||||
|
'timezone'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
openmct.objectViews.addProvider(new ClockViewProvider(openmct));
|
||||||
|
|
||||||
|
if (options && options.enableClockIndicator === true) {
|
||||||
|
const clockIndicator = new Vue ({
|
||||||
|
components: {
|
||||||
|
ClockIndicator
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
indicatorFormat: CLOCK_INDICATOR_FORMAT
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: '<ClockIndicator :indicator-format="indicatorFormat" />'
|
||||||
|
});
|
||||||
|
const indicator = {
|
||||||
|
element: clockIndicator.$mount().$el,
|
||||||
|
key: 'clock-indicator'
|
||||||
|
};
|
||||||
|
|
||||||
|
openmct.indicators.add(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
openmct.objects.addGetInterceptor({
|
||||||
|
appliesTo: (identifier, domainObject) => {
|
||||||
|
return domainObject && domainObject.type === 'clock';
|
||||||
|
},
|
||||||
|
invoke: (identifier, domainObject) => {
|
||||||
|
if (domainObject.configuration) {
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domainObject.clockFormat
|
||||||
|
&& domainObject.timezone) {
|
||||||
|
const baseFormat = domainObject.clockFormat[0];
|
||||||
|
const use24 = domainObject.clockFormat[1];
|
||||||
|
const timezone = domainObject.timezone;
|
||||||
|
|
||||||
|
domainObject.configuration = {
|
||||||
|
baseFormat,
|
||||||
|
use24,
|
||||||
|
timezone
|
||||||
|
};
|
||||||
|
|
||||||
|
openmct.objects.mutate(domainObject, 'configuration', domainObject.configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
231
src/plugins/clock/pluginSpec.js
Normal file
231
src/plugins/clock/pluginSpec.js
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||||
|
import clockPlugin from './plugin';
|
||||||
|
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
describe("Clock plugin:", () => {
|
||||||
|
let openmct;
|
||||||
|
let clockDefinition;
|
||||||
|
let element;
|
||||||
|
let child;
|
||||||
|
let appHolder;
|
||||||
|
|
||||||
|
let clockDomainObject;
|
||||||
|
|
||||||
|
function setupClock(enableClockIndicator) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
clockDomainObject = {
|
||||||
|
identifier: {
|
||||||
|
key: 'clock',
|
||||||
|
namespace: 'test-namespace'
|
||||||
|
},
|
||||||
|
type: 'clock'
|
||||||
|
};
|
||||||
|
|
||||||
|
appHolder = document.createElement('div');
|
||||||
|
appHolder.style.width = '640px';
|
||||||
|
appHolder.style.height = '480px';
|
||||||
|
document.body.appendChild(appHolder);
|
||||||
|
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
element = document.createElement('div');
|
||||||
|
child = document.createElement('div');
|
||||||
|
element.appendChild(child);
|
||||||
|
|
||||||
|
openmct.install(clockPlugin({ enableClockIndicator }));
|
||||||
|
|
||||||
|
clockDefinition = openmct.types.get('clock').definition;
|
||||||
|
clockDefinition.initialize(clockDomainObject);
|
||||||
|
|
||||||
|
openmct.on('start', resolve);
|
||||||
|
openmct.start(appHolder);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Clock view:", () => {
|
||||||
|
let clockViewProvider;
|
||||||
|
let clockView;
|
||||||
|
let clockViewObject;
|
||||||
|
let mutableClockObject;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await setupClock(true);
|
||||||
|
|
||||||
|
clockViewObject = {
|
||||||
|
...clockDomainObject,
|
||||||
|
id: "test-object",
|
||||||
|
name: 'Clock',
|
||||||
|
configuration: {
|
||||||
|
baseFormat: 'YYYY/MM/DD hh:mm:ss',
|
||||||
|
use24: 'clock12',
|
||||||
|
timezone: 'UTC'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
|
||||||
|
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
|
||||||
|
|
||||||
|
const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
|
||||||
|
clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');
|
||||||
|
|
||||||
|
mutableClockObject = await openmct.objects.getMutable(clockViewObject.identifier);
|
||||||
|
|
||||||
|
clockView = clockViewProvider.view(mutableClockObject);
|
||||||
|
clockView.show(child);
|
||||||
|
|
||||||
|
await Vue.nextTick();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clockView.destroy();
|
||||||
|
openmct.objects.destroyMutable(mutableClockObject);
|
||||||
|
if (appHolder) {
|
||||||
|
appHolder.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has name as Clock", () => {
|
||||||
|
expect(clockDefinition.name).toEqual('Clock');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is creatable", () => {
|
||||||
|
expect(clockDefinition.creatable).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("provides clock view", () => {
|
||||||
|
expect(clockViewProvider).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders clock element", () => {
|
||||||
|
const clockElement = element.querySelectorAll('.c-clock');
|
||||||
|
expect(clockElement.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders major elements", () => {
|
||||||
|
const clockElement = element.querySelector('.c-clock');
|
||||||
|
const timezone = clockElement.querySelector('.c-clock__timezone');
|
||||||
|
const time = clockElement.querySelector('.c-clock__value');
|
||||||
|
const amPm = clockElement.querySelector('.c-clock__ampm');
|
||||||
|
const hasMajorElements = Boolean(timezone && time && amPm);
|
||||||
|
|
||||||
|
expect(hasMajorElements).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders time in UTC", () => {
|
||||||
|
const clockElement = element.querySelector('.c-clock');
|
||||||
|
const timezone = clockElement.querySelector('.c-clock__timezone').textContent.trim();
|
||||||
|
|
||||||
|
expect(timezone).toBe('UTC');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the 24 hour option in the configuration", (done) => {
|
||||||
|
expect(clockDomainObject.configuration.use24).toBe('clock12');
|
||||||
|
const new24Option = 'clock24';
|
||||||
|
|
||||||
|
openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => {
|
||||||
|
expect(changedDomainObject.use24).toBe(new24Option);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.objects.mutate(clockViewObject, 'configuration.use24', new24Option);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the timezone option in the configuration", (done) => {
|
||||||
|
expect(clockDomainObject.configuration.timezone).toBe('UTC');
|
||||||
|
const newZone = 'CST6CDT';
|
||||||
|
|
||||||
|
openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => {
|
||||||
|
expect(changedDomainObject.timezone).toBe(newZone);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.objects.mutate(clockViewObject, 'configuration.timezone', newZone);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the time format option in the configuration", (done) => {
|
||||||
|
expect(clockDomainObject.configuration.baseFormat).toBe('YYYY/MM/DD hh:mm:ss');
|
||||||
|
const newFormat = 'hh:mm:ss';
|
||||||
|
|
||||||
|
openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => {
|
||||||
|
expect(changedDomainObject.baseFormat).toBe(newFormat);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.objects.mutate(clockViewObject, 'configuration.baseFormat', newFormat);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Clock Indicator view:", () => {
|
||||||
|
let clockIndicator;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (clockIndicator) {
|
||||||
|
clockIndicator.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
clockIndicator = undefined;
|
||||||
|
if (appHolder) {
|
||||||
|
appHolder.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't exist", async () => {
|
||||||
|
await setupClock(false);
|
||||||
|
|
||||||
|
clockIndicator = openmct.indicators.indicatorObjects
|
||||||
|
.find(indicator => indicator.key === 'clock-indicator');
|
||||||
|
|
||||||
|
const clockIndicatorMissing = clockIndicator === null || clockIndicator === undefined;
|
||||||
|
expect(clockIndicatorMissing).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exists", async () => {
|
||||||
|
await setupClock(true);
|
||||||
|
|
||||||
|
clockIndicator = openmct.indicators.indicatorObjects
|
||||||
|
.find(indicator => indicator.key === 'clock-indicator').element;
|
||||||
|
|
||||||
|
const hasClockIndicator = clockIndicator !== null && clockIndicator !== undefined;
|
||||||
|
expect(hasClockIndicator).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains text", async () => {
|
||||||
|
await setupClock(true);
|
||||||
|
|
||||||
|
clockIndicator = openmct.indicators.indicatorObjects
|
||||||
|
.find(indicator => indicator.key === 'clock-indicator').element;
|
||||||
|
|
||||||
|
const clockIndicatorText = clockIndicator.textContent.trim();
|
||||||
|
const textIncludesUTC = clockIndicatorText.includes('UTC');
|
||||||
|
|
||||||
|
expect(textIncludesUTC).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -273,10 +273,7 @@ export default {
|
|||||||
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
||||||
(objectPath) => {
|
(objectPath) => {
|
||||||
this.objectPath = objectPath;
|
this.objectPath = objectPath;
|
||||||
this.navigateToPath = '#/browse/' + this.objectPath
|
this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
|
||||||
.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
|
|
||||||
.reverse()
|
|
||||||
.join('/');
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -297,10 +297,7 @@ export default {
|
|||||||
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
||||||
(objectPath) => {
|
(objectPath) => {
|
||||||
this.objectPath = objectPath;
|
this.objectPath = objectPath;
|
||||||
this.navigateToPath = '#/browse/' + this.objectPath
|
this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
|
||||||
.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
|
|
||||||
.reverse()
|
|
||||||
.join('/');
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
73
src/plugins/imagery/ImageryTimestripViewProvider.js
Normal file
73
src/plugins/imagery/ImageryTimestripViewProvider.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 ImageryTimeView from './components/ImageryTimeView.vue';
|
||||||
|
import Vue from "vue";
|
||||||
|
|
||||||
|
export default function ImageryTimestripViewProvider(openmct) {
|
||||||
|
const type = 'example.imagery.time-strip.view';
|
||||||
|
|
||||||
|
function hasImageTelemetry(domainObject) {
|
||||||
|
const metadata = openmct.telemetry.getMetadata(domainObject);
|
||||||
|
if (!metadata) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.valuesForHints(['image']).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: type,
|
||||||
|
name: 'Imagery Timestrip View',
|
||||||
|
cssClass: 'icon-image',
|
||||||
|
canView: function (domainObject, objectPath) {
|
||||||
|
let isChildOfTimeStrip = objectPath.find(object => object.type === 'time-strip');
|
||||||
|
|
||||||
|
return hasImageTelemetry(domainObject) && isChildOfTimeStrip && !openmct.router.isNavigatedObject(objectPath);
|
||||||
|
},
|
||||||
|
view: function (domainObject, objectPath) {
|
||||||
|
let component;
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: function (element) {
|
||||||
|
component = new Vue({
|
||||||
|
el: element,
|
||||||
|
components: {
|
||||||
|
ImageryTimeView
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct: openmct,
|
||||||
|
domainObject: domainObject,
|
||||||
|
objectPath: objectPath
|
||||||
|
},
|
||||||
|
template: '<imagery-time-view></imagery-time-view>'
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function () {
|
||||||
|
component.$destroy();
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import ImageryViewLayout from './components/ImageryViewLayout.vue';
|
import ImageryViewComponent from './components/ImageryView.vue';
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ export default class ImageryView {
|
|||||||
this.component = new Vue({
|
this.component = new Vue({
|
||||||
el: element,
|
el: element,
|
||||||
components: {
|
components: {
|
||||||
ImageryViewLayout
|
'imagery-view': ImageryViewComponent
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct: this.openmct,
|
openmct: this.openmct,
|
||||||
@ -22,7 +22,8 @@ export default class ImageryView {
|
|||||||
objectPath: this.objectPath,
|
objectPath: this.objectPath,
|
||||||
currentView: this
|
currentView: this
|
||||||
},
|
},
|
||||||
template: '<imagery-view-layout ref="ImageryLayout"></imagery-view-layout>'
|
template: '<imagery-view ref="ImageryContainer"></imagery-view>'
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,8 +37,10 @@ export default function ImageryViewProvider(openmct) {
|
|||||||
key: type,
|
key: type,
|
||||||
name: 'Imagery Layout',
|
name: 'Imagery Layout',
|
||||||
cssClass: 'icon-image',
|
cssClass: 'icon-image',
|
||||||
canView: function (domainObject) {
|
canView: function (domainObject, objectPath) {
|
||||||
return hasImageTelemetry(domainObject);
|
let isChildOfTimeStrip = objectPath.find(object => object.type === 'time-strip');
|
||||||
|
|
||||||
|
return hasImageTelemetry(domainObject) && (!isChildOfTimeStrip || openmct.router.isNavigatedObject(objectPath));
|
||||||
},
|
},
|
||||||
view: function (domainObject, objectPath) {
|
view: function (domainObject, objectPath) {
|
||||||
return new ImageryView(openmct, domainObject, objectPath);
|
return new ImageryView(openmct, domainObject, objectPath);
|
||||||
|
476
src/plugins/imagery/components/ImageryTimeView.vue
Normal file
476
src/plugins/imagery/components/ImageryTimeView.vue
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2021, 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 ref="imagery"
|
||||||
|
class="c-imagery-tsv c-timeline-holder"
|
||||||
|
>
|
||||||
|
<div ref="imageryHolder"
|
||||||
|
class="c-imagery-tsv__contents u-contents"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as d3Scale from 'd3-scale';
|
||||||
|
import SwimLane from "@/ui/components/swim-lane/SwimLane.vue";
|
||||||
|
import Vue from "vue";
|
||||||
|
import imageryData from "../../imagery/mixins/imageryData";
|
||||||
|
import PreviewAction from "@/ui/preview/PreviewAction";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
const PADDING = 1;
|
||||||
|
const RESIZE_POLL_INTERVAL = 200;
|
||||||
|
const ROW_HEIGHT = 100;
|
||||||
|
const IMAGE_WIDTH_THRESHOLD = 40;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [imageryData],
|
||||||
|
inject: ['openmct', 'domainObject', 'objectPath'],
|
||||||
|
data() {
|
||||||
|
let timeSystem = this.openmct.time.timeSystem();
|
||||||
|
this.metadata = {};
|
||||||
|
this.requestCount = 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
viewBounds: undefined,
|
||||||
|
height: 0,
|
||||||
|
durationFormatter: undefined,
|
||||||
|
imageHistory: [],
|
||||||
|
timeSystem: timeSystem,
|
||||||
|
keyString: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
imageHistorySize() {
|
||||||
|
return this.imageHistory.length;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
imageHistorySize(newSize, oldSize) {
|
||||||
|
this.updatePlotImagery();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
|
|
||||||
|
this.canvas = this.$refs.imagery.appendChild(document.createElement('canvas'));
|
||||||
|
this.canvas.height = 0;
|
||||||
|
this.canvasContext = this.canvas.getContext('2d');
|
||||||
|
this.setDimensions();
|
||||||
|
|
||||||
|
this.updateViewBounds();
|
||||||
|
|
||||||
|
this.openmct.time.on("timeSystem", this.setScaleAndPlotImagery);
|
||||||
|
this.openmct.time.on("bounds", this.updateViewBounds);
|
||||||
|
|
||||||
|
this.resize = _.debounce(this.resize, 400);
|
||||||
|
this.imageryStripResizeObserver = new ResizeObserver(this.resize);
|
||||||
|
this.imageryStripResizeObserver.observe(this.$refs.imagery);
|
||||||
|
|
||||||
|
this.unlisten = this.openmct.objects.observe(this.domainObject, '*', this.observeForChanges);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
delete this.unsubscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.imageryStripResizeObserver) {
|
||||||
|
this.imageryStripResizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openmct.time.off("timeSystem", this.setScaleAndPlotImagery);
|
||||||
|
this.openmct.time.off("bounds", this.updateViewBounds);
|
||||||
|
if (this.unlisten) {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
expand(index) {
|
||||||
|
const path = this.objectPath[0];
|
||||||
|
this.previewAction.invoke([path]);
|
||||||
|
},
|
||||||
|
observeForChanges(mutatedObject) {
|
||||||
|
this.updateViewBounds();
|
||||||
|
},
|
||||||
|
resize() {
|
||||||
|
let clientWidth = this.getClientWidth();
|
||||||
|
if (clientWidth !== this.width) {
|
||||||
|
this.setDimensions();
|
||||||
|
this.updateViewBounds();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getClientWidth() {
|
||||||
|
let clientWidth = this.$refs.imagery.clientWidth;
|
||||||
|
|
||||||
|
if (!clientWidth) {
|
||||||
|
//this is a hack - need a better way to find the parent of this component
|
||||||
|
let parent = this.openmct.layout.$refs.browseObject.$el;
|
||||||
|
if (parent) {
|
||||||
|
clientWidth = parent.getBoundingClientRect().width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientWidth;
|
||||||
|
},
|
||||||
|
updateViewBounds(bounds, isTick) {
|
||||||
|
this.viewBounds = this.openmct.time.bounds();
|
||||||
|
//Add a 50% padding to the end bounds to look ahead
|
||||||
|
let timespan = (this.viewBounds.end - this.viewBounds.start);
|
||||||
|
let padding = timespan / 2;
|
||||||
|
this.viewBounds.end = this.viewBounds.end + padding;
|
||||||
|
|
||||||
|
if (this.timeSystem === undefined) {
|
||||||
|
this.timeSystem = this.openmct.time.timeSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setScaleAndPlotImagery(this.timeSystem, !isTick);
|
||||||
|
|
||||||
|
},
|
||||||
|
setScaleAndPlotImagery(timeSystem, clearAllImagery) {
|
||||||
|
if (timeSystem !== undefined) {
|
||||||
|
this.timeSystem = timeSystem;
|
||||||
|
this.timeFormatter = this.getFormatter(this.timeSystem.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setScale(this.timeSystem);
|
||||||
|
this.updatePlotImagery(clearAllImagery);
|
||||||
|
},
|
||||||
|
getFormatter(key) {
|
||||||
|
const metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
|
|
||||||
|
let metadataValue = metadata.value(key) || { format: key };
|
||||||
|
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
||||||
|
|
||||||
|
return valueFormatter;
|
||||||
|
},
|
||||||
|
updatePlotImagery(clearAllImagery) {
|
||||||
|
this.clearPreviousImagery(clearAllImagery);
|
||||||
|
if (this.xScale) {
|
||||||
|
this.drawImagery();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearPreviousImagery(clearAllImagery) {
|
||||||
|
//TODO: Only clear items that are out of bounds
|
||||||
|
let noItemsEl = this.$el.querySelectorAll(".c-imagery-tsv__no-items");
|
||||||
|
noItemsEl.forEach(item => {
|
||||||
|
item.remove();
|
||||||
|
});
|
||||||
|
let imagery = this.$el.querySelectorAll(".c-imagery-tsv__image-wrapper");
|
||||||
|
imagery.forEach(item => {
|
||||||
|
if (clearAllImagery) {
|
||||||
|
item.remove();
|
||||||
|
} else {
|
||||||
|
const id = this.getNSAttributesForElement(item, 'id');
|
||||||
|
if (id) {
|
||||||
|
const timestamp = id.replace('id-', '');
|
||||||
|
if (!this.isImageryInBounds({
|
||||||
|
time: timestamp
|
||||||
|
})) {
|
||||||
|
item.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setDimensions() {
|
||||||
|
const imageryHolder = this.$refs.imagery;
|
||||||
|
this.width = this.getClientWidth();
|
||||||
|
|
||||||
|
this.height = Math.round(imageryHolder.getBoundingClientRect().height);
|
||||||
|
},
|
||||||
|
setScale(timeSystem) {
|
||||||
|
if (!this.width) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeSystem === undefined) {
|
||||||
|
timeSystem = this.openmct.time.timeSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeSystem.isUTCBased) {
|
||||||
|
this.xScale = d3Scale.scaleUtc();
|
||||||
|
this.xScale.domain(
|
||||||
|
[new Date(this.viewBounds.start), new Date(this.viewBounds.end)]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.xScale = d3Scale.scaleLinear();
|
||||||
|
this.xScale.domain(
|
||||||
|
[this.viewBounds.start, this.viewBounds.end]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.xScale.range([PADDING, this.width - PADDING * 2]);
|
||||||
|
},
|
||||||
|
isImageryInBounds(imageObj) {
|
||||||
|
return (imageObj.time < this.viewBounds.end) && (imageObj.time > this.viewBounds.start);
|
||||||
|
},
|
||||||
|
getImageryContainer() {
|
||||||
|
let svgHeight = 100;
|
||||||
|
let svgWidth = this.imageHistory.length ? this.width : 200;
|
||||||
|
let groupSVG;
|
||||||
|
|
||||||
|
let existingSVG = this.$el.querySelector(".c-imagery-tsv__contents svg");
|
||||||
|
if (existingSVG) {
|
||||||
|
groupSVG = existingSVG;
|
||||||
|
this.setNSAttributesForElement(groupSVG, {
|
||||||
|
width: svgWidth
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let component = new Vue({
|
||||||
|
components: {
|
||||||
|
SwimLane
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isNested: true,
|
||||||
|
height: svgHeight,
|
||||||
|
width: svgWidth
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: `<swim-lane :is-nested="isNested" :hide-label="true"><template slot="object"><svg class="c-imagery-tsv-container" :height="height" :width="width"></svg></template></swim-lane>`
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$refs.imageryHolder.appendChild(component.$mount().$el);
|
||||||
|
|
||||||
|
groupSVG = component.$el.querySelector('svg');
|
||||||
|
|
||||||
|
groupSVG.addEventListener('mouseout', (event) => {
|
||||||
|
if (event.target.nodeName === 'svg' || event.target.nodeName === 'use') {
|
||||||
|
this.removeFromForeground();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupSVG;
|
||||||
|
},
|
||||||
|
isImageryWidthAcceptable() {
|
||||||
|
// We're calculating if there is enough space between images to show the thumbnails.
|
||||||
|
// This algorithm could probably be enhanced to check the x co-ordinate distance between 2 consecutive images, but
|
||||||
|
// we will go with this for now assuming imagery is not sorted by asc time so it's difficult to calculate.
|
||||||
|
// TODO: Use telemetry.requestCollection to get sorted telemetry
|
||||||
|
const currentStart = this.viewBounds.start;
|
||||||
|
const currentEnd = this.viewBounds.end;
|
||||||
|
const rectX = this.xScale(currentStart);
|
||||||
|
const rectY = this.xScale(currentEnd);
|
||||||
|
const imageContainerWidth = this.imageHistory.length ? (rectY - rectX) / this.imageHistory.length : 0;
|
||||||
|
|
||||||
|
return imageContainerWidth < IMAGE_WIDTH_THRESHOLD;
|
||||||
|
},
|
||||||
|
drawImagery() {
|
||||||
|
let groupSVG = this.getImageryContainer();
|
||||||
|
const showImagePlaceholders = this.isImageryWidthAcceptable();
|
||||||
|
|
||||||
|
if (this.imageHistory.length) {
|
||||||
|
this.imageHistory.forEach((currentImageObject, index) => {
|
||||||
|
if (this.isImageryInBounds(currentImageObject)) {
|
||||||
|
this.plotImagery(currentImageObject, showImagePlaceholders, groupSVG, index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.plotNoItems(groupSVG);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotNoItems(svgElement) {
|
||||||
|
let textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
this.setNSAttributesForElement(textElement, {
|
||||||
|
x: "10",
|
||||||
|
y: "20",
|
||||||
|
class: "c-imagery-tsv__no-items"
|
||||||
|
});
|
||||||
|
textElement.innerHTML = 'No images within timeframe';
|
||||||
|
|
||||||
|
svgElement.appendChild(textElement);
|
||||||
|
},
|
||||||
|
setNSAttributesForElement(element, attributes) {
|
||||||
|
Object.keys(attributes).forEach((key) => {
|
||||||
|
if (key === 'url') {
|
||||||
|
element.setAttributeNS('http://www.w3.org/1999/xlink', 'href', attributes[key]);
|
||||||
|
} else {
|
||||||
|
element.setAttributeNS(null, key, attributes[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getNSAttributesForElement(element, attribute) {
|
||||||
|
return element.getAttributeNS(null, attribute);
|
||||||
|
},
|
||||||
|
getImageWrapper(item) {
|
||||||
|
const id = `id-${item.time}`;
|
||||||
|
|
||||||
|
return this.$el.querySelector(`.c-imagery-tsv__contents g[id=${id}]`);
|
||||||
|
},
|
||||||
|
plotImagery(item, showImagePlaceholders, svgElement, index) {
|
||||||
|
//TODO: Placeholder image
|
||||||
|
let existingImageWrapper = this.getImageWrapper(item);
|
||||||
|
//imageWrapper wraps the vertical tick rect and the image
|
||||||
|
if (existingImageWrapper) {
|
||||||
|
this.updateExistingImageWrapper(existingImageWrapper, item, showImagePlaceholders);
|
||||||
|
} else {
|
||||||
|
let imageWrapper = this.createImageWrapper(index, item, showImagePlaceholders, svgElement);
|
||||||
|
svgElement.appendChild(imageWrapper);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateExistingImageWrapper(existingImageWrapper, item, showImagePlaceholders) {
|
||||||
|
//Update the x co-ordinates of the handle and image elements and the url of image
|
||||||
|
//this is to avoid tearing down all elements completely and re-drawing them
|
||||||
|
this.setNSAttributesForElement(existingImageWrapper, {
|
||||||
|
showImagePlaceholders
|
||||||
|
});
|
||||||
|
let imageTickElement = existingImageWrapper.querySelector('rect.c-imagery-tsv__image-handle');
|
||||||
|
this.setNSAttributesForElement(imageTickElement, {
|
||||||
|
x: this.xScale(item.time)
|
||||||
|
});
|
||||||
|
|
||||||
|
let imageRect = existingImageWrapper.querySelector('rect.c-imagery-tsv__image-placeholder');
|
||||||
|
this.setNSAttributesForElement(imageRect, {
|
||||||
|
x: this.xScale(item.time) + 2
|
||||||
|
});
|
||||||
|
|
||||||
|
let imageElement = existingImageWrapper.querySelector('image');
|
||||||
|
const selector = `href*=${existingImageWrapper.id}`;
|
||||||
|
let hoverEl = this.$el.querySelector(`.c-imagery-tsv__contents use[${selector}]`);
|
||||||
|
const hideImageUrl = (showImagePlaceholders && !hoverEl);
|
||||||
|
this.setNSAttributesForElement(imageElement, {
|
||||||
|
x: this.xScale(item.time) + 2,
|
||||||
|
url: hideImageUrl ? '' : item.url
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createImageWrapper(index, item, showImagePlaceholders, svgElement) {
|
||||||
|
const id = `id-${item.time}`;
|
||||||
|
const imgSize = String(ROW_HEIGHT - 15);
|
||||||
|
let imageWrapper = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||||
|
this.setNSAttributesForElement(imageWrapper, {
|
||||||
|
id,
|
||||||
|
class: 'c-imagery-tsv__image-wrapper',
|
||||||
|
showImagePlaceholders
|
||||||
|
});
|
||||||
|
//create image tick indicator
|
||||||
|
let imageTickElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
|
this.setNSAttributesForElement(imageTickElement, {
|
||||||
|
class: 'c-imagery-tsv__image-handle',
|
||||||
|
x: this.xScale(item.time),
|
||||||
|
y: 5,
|
||||||
|
rx: 0,
|
||||||
|
width: 2,
|
||||||
|
height: String(ROW_HEIGHT - 10)
|
||||||
|
});
|
||||||
|
imageWrapper.appendChild(imageTickElement);
|
||||||
|
|
||||||
|
let imageRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
|
this.setNSAttributesForElement(imageRect, {
|
||||||
|
class: 'c-imagery-tsv__image-placeholder',
|
||||||
|
x: this.xScale(item.time) + 2,
|
||||||
|
y: 10,
|
||||||
|
rx: 0,
|
||||||
|
width: imgSize,
|
||||||
|
height: imgSize,
|
||||||
|
mask: `#image-${item.time}`
|
||||||
|
});
|
||||||
|
imageWrapper.appendChild(imageRect);
|
||||||
|
|
||||||
|
let imageElement = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
||||||
|
this.setNSAttributesForElement(imageElement, {
|
||||||
|
id: `image-${item.time}`,
|
||||||
|
x: this.xScale(item.time) + 2,
|
||||||
|
y: 10,
|
||||||
|
rx: 0,
|
||||||
|
width: imgSize,
|
||||||
|
height: imgSize,
|
||||||
|
url: showImagePlaceholders ? '' : item.url
|
||||||
|
});
|
||||||
|
imageWrapper.appendChild(imageElement);
|
||||||
|
|
||||||
|
//TODO: Don't add the hover listener if the width is too small
|
||||||
|
imageWrapper.addEventListener('mouseover', this.bringToForeground.bind(this, svgElement, imageWrapper, index, item.url));
|
||||||
|
|
||||||
|
return imageWrapper;
|
||||||
|
},
|
||||||
|
bringToForeground(svgElement, imageWrapper, index, url, event) {
|
||||||
|
const selector = `href*=${imageWrapper.id}`;
|
||||||
|
let hoverEls = this.$el.querySelectorAll(`.c-imagery-tsv__contents use:not([${selector}])`);
|
||||||
|
if (hoverEls.length > 0) {
|
||||||
|
this.removeFromForeground(hoverEls);
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverEls = this.$el.querySelectorAll(`.c-imagery-tsv__contents use[${selector}]`);
|
||||||
|
if (hoverEls.length) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let imageElement = imageWrapper.querySelector('image');
|
||||||
|
this.setNSAttributesForElement(imageElement, {
|
||||||
|
url: url,
|
||||||
|
fill: 'none'
|
||||||
|
});
|
||||||
|
let hoverElement = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
||||||
|
this.setNSAttributesForElement(hoverElement, {
|
||||||
|
class: 'image-highlight',
|
||||||
|
x: 0,
|
||||||
|
href: `#${imageWrapper.id}`
|
||||||
|
});
|
||||||
|
this.setNSAttributesForElement(imageWrapper, {
|
||||||
|
class: 'c-imagery-tsv__image-wrapper is-hovered'
|
||||||
|
});
|
||||||
|
// We're using mousedown here and not 'click' because 'click' doesn't seem to be triggered reliably
|
||||||
|
hoverElement.addEventListener('mousedown', (e) => {
|
||||||
|
if (e.button === 0) {
|
||||||
|
this.expand(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
svgElement.appendChild(hoverElement);
|
||||||
|
|
||||||
|
},
|
||||||
|
removeFromForeground(items) {
|
||||||
|
let hoverEls;
|
||||||
|
if (items) {
|
||||||
|
hoverEls = items;
|
||||||
|
} else {
|
||||||
|
hoverEls = this.$el.querySelectorAll(".c-imagery-tsv__contents use");
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverEls.forEach(item => {
|
||||||
|
let selector = `id*=${this.getNSAttributesForElement(item, 'href').replace('#', '')}`;
|
||||||
|
let imageWrapper = this.$el.querySelector(`.c-imagery-tsv__contents g[${selector}]`);
|
||||||
|
this.setNSAttributesForElement(imageWrapper, {
|
||||||
|
class: 'c-imagery-tsv__image-wrapper'
|
||||||
|
});
|
||||||
|
let showImagePlaceholders = this.getNSAttributesForElement(imageWrapper, 'showImagePlaceholders');
|
||||||
|
if (showImagePlaceholders === 'true') {
|
||||||
|
let imageElement = imageWrapper.querySelector('image');
|
||||||
|
this.setNSAttributesForElement(imageElement, {
|
||||||
|
url: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
item.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -129,12 +129,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="c-imagery__thumbs-wrapper"
|
||||||
class="c-imagery__thumbs-wrapper"
|
:class="[
|
||||||
:class="[
|
{ 'is-paused': isPaused },
|
||||||
{ 'is-paused': isPaused },
|
{ 'is-autoscroll-off': !resizingWindow && !autoScroll && !isPaused }
|
||||||
{ 'is-autoscroll-off': !resizingWindow && !autoScroll && !isPaused }
|
]"
|
||||||
]"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref="thumbsWrapper"
|
ref="thumbsWrapper"
|
||||||
@ -175,7 +174,8 @@ import moment from 'moment';
|
|||||||
import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry';
|
import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry';
|
||||||
import Compass from './Compass/Compass.vue';
|
import Compass from './Compass/Compass.vue';
|
||||||
|
|
||||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
import imageryData from "../../imagery/mixins/imageryData";
|
||||||
|
|
||||||
const REFRESH_CSS_MS = 500;
|
const REFRESH_CSS_MS = 500;
|
||||||
const DURATION_TRACK_MS = 1000;
|
const DURATION_TRACK_MS = 1000;
|
||||||
const ARROW_DOWN_DELAY_CHECK_MS = 400;
|
const ARROW_DOWN_DELAY_CHECK_MS = 400;
|
||||||
@ -197,30 +197,29 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Compass
|
Compass
|
||||||
},
|
},
|
||||||
|
mixins: [imageryData],
|
||||||
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
|
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
|
||||||
data() {
|
data() {
|
||||||
let timeSystem = this.openmct.time.timeSystem();
|
let timeSystem = this.openmct.time.timeSystem();
|
||||||
|
this.metadata = {};
|
||||||
|
this.requestCount = 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
autoScroll: true,
|
|
||||||
durationFormatter: undefined,
|
durationFormatter: undefined,
|
||||||
|
imageHistory: [],
|
||||||
|
timeSystem: timeSystem,
|
||||||
|
keyString: undefined,
|
||||||
|
autoScroll: true,
|
||||||
filters: {
|
filters: {
|
||||||
brightness: 100,
|
brightness: 100,
|
||||||
contrast: 100
|
contrast: 100
|
||||||
},
|
},
|
||||||
imageHistory: [],
|
|
||||||
thumbnailClick: THUMBNAIL_CLICKED,
|
thumbnailClick: THUMBNAIL_CLICKED,
|
||||||
isPaused: false,
|
isPaused: false,
|
||||||
metadata: {},
|
|
||||||
requestCount: 0,
|
|
||||||
timeSystem: timeSystem,
|
|
||||||
timeFormatter: undefined,
|
|
||||||
refreshCSS: false,
|
refreshCSS: false,
|
||||||
keyString: undefined,
|
|
||||||
focusedImageIndex: undefined,
|
focusedImageIndex: undefined,
|
||||||
focusedImageRelatedTelemetry: {},
|
focusedImageRelatedTelemetry: {},
|
||||||
numericDuration: undefined,
|
numericDuration: undefined,
|
||||||
metadataEndpoints: {},
|
|
||||||
relatedTelemetry: {},
|
relatedTelemetry: {},
|
||||||
latestRelatedTelemetry: {},
|
latestRelatedTelemetry: {},
|
||||||
focusedImageNaturalAspectRatio: undefined,
|
focusedImageNaturalAspectRatio: undefined,
|
||||||
@ -231,6 +230,9 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
imageHistorySize() {
|
||||||
|
return this.imageHistory.length;
|
||||||
|
},
|
||||||
compassRoseSizingClasses() {
|
compassRoseSizingClasses() {
|
||||||
let compassRoseSizingClasses = '';
|
let compassRoseSizingClasses = '';
|
||||||
if (this.sizedImageDimensions.width < 300) {
|
if (this.sizedImageDimensions.width < 300) {
|
||||||
@ -258,9 +260,6 @@ export default {
|
|||||||
canTrackDuration() {
|
canTrackDuration() {
|
||||||
return this.openmct.time.clock() && this.timeSystem.isUTCBased;
|
return this.openmct.time.clock() && this.timeSystem.isUTCBased;
|
||||||
},
|
},
|
||||||
focusedImageDownloadName() {
|
|
||||||
return this.getImageDownloadName(this.focusedImage);
|
|
||||||
},
|
|
||||||
isNextDisabled() {
|
isNextDisabled() {
|
||||||
let disabled = false;
|
let disabled = false;
|
||||||
|
|
||||||
@ -383,6 +382,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
imageHistorySize(newSize, oldSize) {
|
||||||
|
this.setFocusedImage(newSize - 1, false);
|
||||||
|
this.scrollToRight();
|
||||||
|
},
|
||||||
focusedImageIndex() {
|
focusedImageIndex() {
|
||||||
this.trackDuration();
|
this.trackDuration();
|
||||||
this.resetAgeCSS();
|
this.resetAgeCSS();
|
||||||
@ -391,18 +394,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
// listen
|
//listen
|
||||||
this.openmct.time.on('bounds', this.boundsChange);
|
this.openmct.time.on('timeSystem', this.trackDuration);
|
||||||
this.openmct.time.on('timeSystem', this.timeSystemChange);
|
this.openmct.time.on('clock', this.trackDuration);
|
||||||
this.openmct.time.on('clock', this.clockChange);
|
|
||||||
|
|
||||||
// set
|
|
||||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
|
||||||
this.imageHints = { ...this.metadata.valuesForHints(['image'])[0] };
|
|
||||||
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
|
||||||
this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.imageHints);
|
|
||||||
this.imageDownloadNameHints = { ...this.metadata.valuesForHints(['imageDownloadName'])[0]};
|
|
||||||
|
|
||||||
// related telemetry keys
|
// related telemetry keys
|
||||||
this.spacecraftPositionKeys = ['positionX', 'positionY', 'positionZ'];
|
this.spacecraftPositionKeys = ['positionX', 'positionY', 'positionZ'];
|
||||||
@ -410,56 +404,49 @@ export default {
|
|||||||
this.cameraKeys = ['cameraPan', 'cameraTilt'];
|
this.cameraKeys = ['cameraPan', 'cameraTilt'];
|
||||||
this.sunKeys = ['sunOrientation'];
|
this.sunKeys = ['sunOrientation'];
|
||||||
|
|
||||||
// initialize
|
|
||||||
this.timeKey = this.timeSystem.key;
|
|
||||||
this.timeFormatter = this.getFormatter(this.timeKey);
|
|
||||||
|
|
||||||
// kickoff
|
|
||||||
this.subscribe();
|
|
||||||
this.requestHistory();
|
|
||||||
|
|
||||||
// related telemetry
|
// related telemetry
|
||||||
await this.initializeRelatedTelemetry();
|
await this.initializeRelatedTelemetry();
|
||||||
this.updateRelatedTelemetryForFocusedImage();
|
await this.updateRelatedTelemetryForFocusedImage();
|
||||||
this.trackLatestRelatedTelemetry();
|
this.trackLatestRelatedTelemetry();
|
||||||
|
|
||||||
// for scrolling through images quickly and resizing the object view
|
// for scrolling through images quickly and resizing the object view
|
||||||
_.debounce(this.updateRelatedTelemetryForFocusedImage, 400);
|
this.updateRelatedTelemetryForFocusedImage = _.debounce(this.updateRelatedTelemetryForFocusedImage, 400);
|
||||||
_.debounce(this.resizeImageContainer, 400);
|
|
||||||
|
|
||||||
this.imageContainerResizeObserver = new ResizeObserver(this.resizeImageContainer);
|
// for resizing the object view
|
||||||
this.imageContainerResizeObserver.observe(this.$refs.imageBG);
|
this.resizeImageContainer = _.debounce(this.resizeImageContainer, 400);
|
||||||
|
|
||||||
|
if (this.$refs.imageBG) {
|
||||||
|
this.imageContainerResizeObserver = new ResizeObserver(this.resizeImageContainer);
|
||||||
|
this.imageContainerResizeObserver.observe(this.$refs.imageBG);
|
||||||
|
}
|
||||||
|
|
||||||
// For adjusting scroll bar size and position when resizing thumbs wrapper
|
// For adjusting scroll bar size and position when resizing thumbs wrapper
|
||||||
this.handleScroll = _.debounce(this.handleScroll, SCROLL_LATENCY);
|
this.handleScroll = _.debounce(this.handleScroll, SCROLL_LATENCY);
|
||||||
this.handleThumbWindowResizeEnded = _.debounce(this.handleThumbWindowResizeEnded, SCROLL_LATENCY);
|
this.handleThumbWindowResizeEnded = _.debounce(this.handleThumbWindowResizeEnded, SCROLL_LATENCY);
|
||||||
|
this.handleThumbWindowResizeStart = _.debounce(this.handleThumbWindowResizeStart, SCROLL_LATENCY);
|
||||||
|
|
||||||
|
if (this.$refs.thumbsWrapper) {
|
||||||
|
this.thumbWrapperResizeObserver = new ResizeObserver(this.handleThumbWindowResizeStart);
|
||||||
|
this.thumbWrapperResizeObserver.observe(this.$refs.thumbsWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
this.thumbWrapperResizeObserver = new ResizeObserver(this.handleThumbWindowResizeStart);
|
|
||||||
this.thumbWrapperResizeObserver.observe(this.$refs.thumbsWrapper);
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (this.unsubscribe) {
|
this.openmct.time.off('timeSystem', this.trackDuration);
|
||||||
this.unsubscribe();
|
this.openmct.time.off('clock', this.trackDuration);
|
||||||
delete this.unsubscribe;
|
|
||||||
|
if (this.thumbWrapperResizeObserver) {
|
||||||
|
this.thumbWrapperResizeObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.imageContainerResizeObserver) {
|
if (this.imageContainerResizeObserver) {
|
||||||
this.imageContainerResizeObserver.disconnect();
|
this.imageContainerResizeObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.thumbWrapperResizeObserver) {
|
|
||||||
this.thumbWrapperResizeObserver.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.relatedTelemetry.hasRelatedTelemetry) {
|
if (this.relatedTelemetry.hasRelatedTelemetry) {
|
||||||
this.relatedTelemetry.destroy();
|
this.relatedTelemetry.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stopDurationTracking();
|
|
||||||
this.openmct.time.off('bounds', this.boundsChange);
|
|
||||||
this.openmct.time.off('timeSystem', this.timeSystemChange);
|
|
||||||
this.openmct.time.off('clock', this.clockChange);
|
|
||||||
|
|
||||||
// unsubscribe from related telemetry
|
// unsubscribe from related telemetry
|
||||||
if (this.relatedTelemetry.hasRelatedTelemetry) {
|
if (this.relatedTelemetry.hasRelatedTelemetry) {
|
||||||
for (let key of this.relatedTelemetry.keys) {
|
for (let key of this.relatedTelemetry.keys) {
|
||||||
@ -576,56 +563,6 @@ export default {
|
|||||||
focusElement() {
|
focusElement() {
|
||||||
this.$el.focus();
|
this.$el.focus();
|
||||||
},
|
},
|
||||||
datumIsNotValid(datum) {
|
|
||||||
if (this.imageHistory.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const datumURL = this.formatImageUrl(datum);
|
|
||||||
const lastHistoryURL = this.formatImageUrl(this.imageHistory.slice(-1)[0]);
|
|
||||||
|
|
||||||
// datum is not valid if it matches the last datum in history,
|
|
||||||
// or it is before the last datum in the history
|
|
||||||
const datumTimeCheck = this.parseTime(datum);
|
|
||||||
const historyTimeCheck = this.parseTime(this.imageHistory.slice(-1)[0]);
|
|
||||||
const matchesLast = (datumTimeCheck === historyTimeCheck) && (datumURL === lastHistoryURL);
|
|
||||||
const isStale = datumTimeCheck < historyTimeCheck;
|
|
||||||
|
|
||||||
return matchesLast || isStale;
|
|
||||||
},
|
|
||||||
formatImageUrl(datum) {
|
|
||||||
if (!datum) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.imageFormatter.format(datum);
|
|
||||||
},
|
|
||||||
formatTime(datum) {
|
|
||||||
if (!datum) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let dateTimeStr = this.timeFormatter.format(datum);
|
|
||||||
|
|
||||||
// Replace ISO "T" with a space to allow wrapping
|
|
||||||
return dateTimeStr.replace("T", " ");
|
|
||||||
},
|
|
||||||
getImageDownloadName(datum) {
|
|
||||||
let imageDownloadName = '';
|
|
||||||
if (datum) {
|
|
||||||
const key = this.imageDownloadNameHints.key;
|
|
||||||
imageDownloadName = datum[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return imageDownloadName;
|
|
||||||
},
|
|
||||||
parseTime(datum) {
|
|
||||||
if (!datum) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.timeFormatter.parse(datum);
|
|
||||||
},
|
|
||||||
handleScroll() {
|
handleScroll() {
|
||||||
const thumbsWrapper = this.$refs.thumbsWrapper;
|
const thumbsWrapper = this.$refs.thumbsWrapper;
|
||||||
if (!thumbsWrapper || this.resizingWindow) {
|
if (!thumbsWrapper || this.resizingWindow) {
|
||||||
@ -683,6 +620,10 @@ export default {
|
|||||||
setFocusedImage(index, thumbnailClick = false) {
|
setFocusedImage(index, thumbnailClick = false) {
|
||||||
if (this.isPaused && !thumbnailClick) {
|
if (this.isPaused && !thumbnailClick) {
|
||||||
this.nextImageIndex = index;
|
this.nextImageIndex = index;
|
||||||
|
//this could happen if bounds changes
|
||||||
|
if (this.focusedImageIndex > this.imageHistory.length - 1) {
|
||||||
|
this.focusedImageIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -693,70 +634,6 @@ export default {
|
|||||||
this.paused(true);
|
this.paused(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
boundsChange(bounds, isTick) {
|
|
||||||
if (!isTick) {
|
|
||||||
this.requestHistory();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async requestHistory() {
|
|
||||||
let bounds = this.openmct.time.bounds();
|
|
||||||
this.requestCount++;
|
|
||||||
const requestId = this.requestCount;
|
|
||||||
this.imageHistory = [];
|
|
||||||
|
|
||||||
let data = await this.openmct.telemetry
|
|
||||||
.request(this.domainObject, bounds) || [];
|
|
||||||
|
|
||||||
if (this.requestCount === requestId) {
|
|
||||||
data.forEach((datum, index) => {
|
|
||||||
this.updateHistory(datum, index === data.length - 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
timeSystemChange(system) {
|
|
||||||
this.timeSystem = this.openmct.time.timeSystem();
|
|
||||||
this.timeKey = this.timeSystem.key;
|
|
||||||
this.timeFormatter = this.getFormatter(this.timeKey);
|
|
||||||
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
|
||||||
this.trackDuration();
|
|
||||||
},
|
|
||||||
clockChange(clock) {
|
|
||||||
this.trackDuration();
|
|
||||||
},
|
|
||||||
subscribe() {
|
|
||||||
this.unsubscribe = this.openmct.telemetry
|
|
||||||
.subscribe(this.domainObject, (datum) => {
|
|
||||||
let parsedTimestamp = this.parseTime(datum);
|
|
||||||
let bounds = this.openmct.time.bounds();
|
|
||||||
|
|
||||||
if (parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
|
|
||||||
this.updateHistory(datum);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateHistory(datum, setFocused = true) {
|
|
||||||
if (this.datumIsNotValid(datum)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = { ...datum };
|
|
||||||
image.formattedTime = this.formatTime(datum);
|
|
||||||
image.url = this.formatImageUrl(datum);
|
|
||||||
image.time = datum[this.timeKey];
|
|
||||||
image.imageDownloadName = this.getImageDownloadName(datum);
|
|
||||||
|
|
||||||
this.imageHistory.push(image);
|
|
||||||
if (setFocused) {
|
|
||||||
this.setFocusedImage(this.imageHistory.length - 1);
|
|
||||||
this.scrollToRight();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getFormatter(key) {
|
|
||||||
let metadataValue = this.metadata.value(key) || { format: key };
|
|
||||||
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
|
||||||
|
|
||||||
return valueFormatter;
|
|
||||||
},
|
|
||||||
trackDuration() {
|
trackDuration() {
|
||||||
if (this.canTrackDuration) {
|
if (this.canTrackDuration) {
|
||||||
this.stopDurationTracking();
|
this.stopDurationTracking();
|
||||||
@ -876,6 +753,10 @@ export default {
|
|||||||
}, { once: true });
|
}, { once: true });
|
||||||
},
|
},
|
||||||
resizeImageContainer() {
|
resizeImageContainer() {
|
||||||
|
if (!this.$refs.imageBG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.$refs.imageBG.clientWidth !== this.imageContainerWidth) {
|
if (this.$refs.imageBG.clientWidth !== this.imageContainerWidth) {
|
||||||
this.imageContainerWidth = this.$refs.imageBG.clientWidth;
|
this.imageContainerWidth = this.$refs.imageBG.clientWidth;
|
||||||
}
|
}
|
@ -312,3 +312,34 @@
|
|||||||
@include cArrowButtonSizing($dimOuter: 32px);
|
@include cArrowButtonSizing($dimOuter: 32px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************** IMAGERY IN TIMESTRIP VIEWS */
|
||||||
|
.c-imagery-tsv {
|
||||||
|
g.c-imagery-tsv__image-wrapper {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.is-hovered {
|
||||||
|
filter: brightness(1) contrast(1) !important;
|
||||||
|
[class*='__image-handle'] {
|
||||||
|
fill: $colorBodyFg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__no-items {
|
||||||
|
fill: $colorBodyFg !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image-handle {
|
||||||
|
fill: rgba($colorBodyFg, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image-placeholder {
|
||||||
|
fill: pushBack($colorBodyBg, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover g.c-imagery-tsv__image-wrapper {
|
||||||
|
// TODO CH: convert to theme constants
|
||||||
|
filter: brightness(0.5) contrast(0.7);
|
||||||
|
}
|
||||||
|
}
|
174
src/plugins/imagery/mixins/imageryData.js
Normal file
174
src/plugins/imagery/mixins/imageryData.js
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'domainObject', 'objectPath'],
|
||||||
|
mounted() {
|
||||||
|
// listen
|
||||||
|
this.openmct.time.on('bounds', this.boundsChange);
|
||||||
|
this.openmct.time.on('timeSystem', this.timeSystemChange);
|
||||||
|
|
||||||
|
// set
|
||||||
|
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
|
this.imageHints = { ...this.metadata.valuesForHints(['image'])[0] };
|
||||||
|
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
||||||
|
this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.imageHints);
|
||||||
|
this.imageDownloadNameHints = { ...this.metadata.valuesForHints(['imageDownloadName'])[0]};
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
this.timeKey = this.timeSystem.key;
|
||||||
|
this.timeFormatter = this.getFormatter(this.timeKey);
|
||||||
|
|
||||||
|
// kickoff
|
||||||
|
this.subscribe();
|
||||||
|
this.requestHistory();
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
delete this.unsubscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openmct.time.off('bounds', this.boundsChange);
|
||||||
|
this.openmct.time.off('timeSystem', this.timeSystemChange);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
datumIsNotValid(datum) {
|
||||||
|
if (this.imageHistory.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const datumURL = this.formatImageUrl(datum);
|
||||||
|
const lastHistoryURL = this.formatImageUrl(this.imageHistory.slice(-1)[0]);
|
||||||
|
|
||||||
|
// datum is not valid if it matches the last datum in history,
|
||||||
|
// or it is before the last datum in the history
|
||||||
|
const datumTimeCheck = this.parseTime(datum);
|
||||||
|
const historyTimeCheck = this.parseTime(this.imageHistory.slice(-1)[0]);
|
||||||
|
const matchesLast = (datumTimeCheck === historyTimeCheck) && (datumURL === lastHistoryURL);
|
||||||
|
const isStale = datumTimeCheck < historyTimeCheck;
|
||||||
|
|
||||||
|
return matchesLast || isStale;
|
||||||
|
},
|
||||||
|
formatImageUrl(datum) {
|
||||||
|
if (!datum) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.imageFormatter.format(datum);
|
||||||
|
},
|
||||||
|
formatTime(datum) {
|
||||||
|
if (!datum) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dateTimeStr = this.timeFormatter.format(datum);
|
||||||
|
|
||||||
|
// Replace ISO "T" with a space to allow wrapping
|
||||||
|
return dateTimeStr.replace("T", " ");
|
||||||
|
},
|
||||||
|
getImageDownloadName(datum) {
|
||||||
|
let imageDownloadName = '';
|
||||||
|
if (datum) {
|
||||||
|
const key = this.imageDownloadNameHints.key;
|
||||||
|
imageDownloadName = datum[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageDownloadName;
|
||||||
|
},
|
||||||
|
parseTime(datum) {
|
||||||
|
if (!datum) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.timeFormatter.parse(datum);
|
||||||
|
},
|
||||||
|
boundsChange(bounds, isTick) {
|
||||||
|
if (!isTick) {
|
||||||
|
this.requestHistory();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async requestHistory() {
|
||||||
|
let bounds = this.openmct.time.bounds();
|
||||||
|
this.requestCount++;
|
||||||
|
const requestId = this.requestCount;
|
||||||
|
this.imageHistory = [];
|
||||||
|
|
||||||
|
let data = await this.openmct.telemetry
|
||||||
|
.request(this.domainObject, bounds) || [];
|
||||||
|
|
||||||
|
if (this.requestCount === requestId) {
|
||||||
|
let imagery = [];
|
||||||
|
data.forEach((datum) => {
|
||||||
|
let image = this.normalizeDatum(datum);
|
||||||
|
if (image) {
|
||||||
|
imagery.push(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//this is to optimize anything that reacts to imageHistory length
|
||||||
|
this.imageHistory = imagery;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timeSystemChange() {
|
||||||
|
this.timeSystem = this.openmct.time.timeSystem();
|
||||||
|
this.timeKey = this.timeSystem.key;
|
||||||
|
this.timeFormatter = this.getFormatter(this.timeKey);
|
||||||
|
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
||||||
|
},
|
||||||
|
subscribe() {
|
||||||
|
this.unsubscribe = this.openmct.telemetry
|
||||||
|
.subscribe(this.domainObject, (datum) => {
|
||||||
|
let parsedTimestamp = this.parseTime(datum);
|
||||||
|
let bounds = this.openmct.time.bounds();
|
||||||
|
|
||||||
|
if (parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
|
||||||
|
let image = this.normalizeDatum(datum);
|
||||||
|
if (image) {
|
||||||
|
this.imageHistory.push(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
normalizeDatum(datum) {
|
||||||
|
if (this.datumIsNotValid(datum)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = { ...datum };
|
||||||
|
image.formattedTime = this.formatTime(datum);
|
||||||
|
image.url = this.formatImageUrl(datum);
|
||||||
|
image.time = datum[this.timeKey];
|
||||||
|
image.imageDownloadName = this.getImageDownloadName(datum);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
},
|
||||||
|
getFormatter(key) {
|
||||||
|
let metadataValue = this.metadata.value(key) || { format: key };
|
||||||
|
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
||||||
|
|
||||||
|
return valueFormatter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -21,10 +21,12 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import ImageryViewProvider from './ImageryViewProvider';
|
import ImageryViewProvider from './ImageryViewProvider';
|
||||||
|
import ImageryTimestripViewProvider from './ImageryTimestripViewProvider';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function install(openmct) {
|
return function install(openmct) {
|
||||||
openmct.objectViews.addProvider(new ImageryViewProvider(openmct));
|
openmct.objectViews.addProvider(new ImageryViewProvider(openmct));
|
||||||
|
openmct.objectViews.addProvider(new ImageryTimestripViewProvider(openmct));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,12 +84,14 @@ function generateTelemetry(start, count) {
|
|||||||
return telemetry;
|
return telemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("The Imagery View Layout", () => {
|
describe("The Imagery View Layouts", () => {
|
||||||
const imageryKey = 'example.imagery';
|
const imageryKey = 'example.imagery';
|
||||||
|
const imageryForTimeStripKey = 'example.imagery.time-strip.view';
|
||||||
const START = Date.now();
|
const START = Date.now();
|
||||||
const COUNT = 10;
|
const COUNT = 10;
|
||||||
|
|
||||||
let resolveFunction;
|
let resolveFunction;
|
||||||
|
let originalRouterPath;
|
||||||
|
|
||||||
let openmct;
|
let openmct;
|
||||||
let appHolder;
|
let appHolder;
|
||||||
@ -116,51 +118,51 @@ describe("The Imagery View Layout", () => {
|
|||||||
"image": 1,
|
"image": 1,
|
||||||
"priority": 3
|
"priority": 3
|
||||||
},
|
},
|
||||||
"source": "url",
|
"source": "url"
|
||||||
"relatedTelemetry": {
|
// "relatedTelemetry": {
|
||||||
"heading": {
|
// "heading": {
|
||||||
"comparisonFunction": comparisonFunction,
|
// "comparisonFunction": comparisonFunction,
|
||||||
"historical": {
|
// "historical": {
|
||||||
"telemetryObjectId": "heading",
|
// "telemetryObjectId": "heading",
|
||||||
"valueKey": "value"
|
// "valueKey": "value"
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
"roll": {
|
// "roll": {
|
||||||
"comparisonFunction": comparisonFunction,
|
// "comparisonFunction": comparisonFunction,
|
||||||
"historical": {
|
// "historical": {
|
||||||
"telemetryObjectId": "roll",
|
// "telemetryObjectId": "roll",
|
||||||
"valueKey": "value"
|
// "valueKey": "value"
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
"pitch": {
|
// "pitch": {
|
||||||
"comparisonFunction": comparisonFunction,
|
// "comparisonFunction": comparisonFunction,
|
||||||
"historical": {
|
// "historical": {
|
||||||
"telemetryObjectId": "pitch",
|
// "telemetryObjectId": "pitch",
|
||||||
"valueKey": "value"
|
// "valueKey": "value"
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
"cameraPan": {
|
// "cameraPan": {
|
||||||
"comparisonFunction": comparisonFunction,
|
// "comparisonFunction": comparisonFunction,
|
||||||
"historical": {
|
// "historical": {
|
||||||
"telemetryObjectId": "cameraPan",
|
// "telemetryObjectId": "cameraPan",
|
||||||
"valueKey": "value"
|
// "valueKey": "value"
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
"cameraTilt": {
|
// "cameraTilt": {
|
||||||
"comparisonFunction": comparisonFunction,
|
// "comparisonFunction": comparisonFunction,
|
||||||
"historical": {
|
// "historical": {
|
||||||
"telemetryObjectId": "cameraTilt",
|
// "telemetryObjectId": "cameraTilt",
|
||||||
"valueKey": "value"
|
// "valueKey": "value"
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
"sunOrientation": {
|
// "sunOrientation": {
|
||||||
"comparisonFunction": comparisonFunction,
|
// "comparisonFunction": comparisonFunction,
|
||||||
"historical": {
|
// "historical": {
|
||||||
"telemetryObjectId": "sunOrientation",
|
// "telemetryObjectId": "sunOrientation",
|
||||||
"valueKey": "value"
|
// "valueKey": "value"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@ -220,6 +222,8 @@ describe("The Imagery View Layout", () => {
|
|||||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
||||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
|
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
|
||||||
|
|
||||||
|
originalRouterPath = openmct.router.path;
|
||||||
|
|
||||||
openmct.on('start', done);
|
openmct.on('start', done);
|
||||||
openmct.start(appHolder);
|
openmct.start(appHolder);
|
||||||
});
|
});
|
||||||
@ -229,10 +233,34 @@ describe("The Imagery View Layout", () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 1
|
end: 1
|
||||||
});
|
});
|
||||||
|
openmct.router.path = originalRouterPath;
|
||||||
|
|
||||||
return resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should provide an imagery time strip view when in a time strip", () => {
|
||||||
|
openmct.router.path = [{
|
||||||
|
identifier: {
|
||||||
|
key: 'test-timestrip',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: 'time-strip'
|
||||||
|
}];
|
||||||
|
|
||||||
|
let applicableViews = openmct.objectViews.get(imageryObject, [imageryObject, {
|
||||||
|
identifier: {
|
||||||
|
key: 'test-timestrip',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: 'time-strip'
|
||||||
|
}]);
|
||||||
|
let imageryView = applicableViews.find(
|
||||||
|
viewProvider => viewProvider.key === imageryForTimeStripKey
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(imageryView).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("should provide an imagery view only for imagery producing objects", () => {
|
it("should provide an imagery view only for imagery producing objects", () => {
|
||||||
let applicableViews = openmct.objectViews.get(imageryObject, []);
|
let applicableViews = openmct.objectViews.get(imageryObject, []);
|
||||||
let imageryView = applicableViews.find(
|
let imageryView = applicableViews.find(
|
||||||
@ -242,6 +270,46 @@ describe("The Imagery View Layout", () => {
|
|||||||
expect(imageryView).toBeDefined();
|
expect(imageryView).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not provide an imagery view when in a time strip", () => {
|
||||||
|
openmct.router.path = [{
|
||||||
|
identifier: {
|
||||||
|
key: 'test-timestrip',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: 'time-strip'
|
||||||
|
}];
|
||||||
|
|
||||||
|
let applicableViews = openmct.objectViews.get(imageryObject, [imageryObject, {
|
||||||
|
identifier: {
|
||||||
|
key: 'test-timestrip',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: 'time-strip'
|
||||||
|
}]);
|
||||||
|
let imageryView = applicableViews.find(
|
||||||
|
viewProvider => viewProvider.key === imageryKey
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(imageryView).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should provide an imagery view when navigated to in the composition of a time strip", () => {
|
||||||
|
openmct.router.path = [imageryObject];
|
||||||
|
|
||||||
|
let applicableViews = openmct.objectViews.get(imageryObject, [imageryObject, {
|
||||||
|
identifier: {
|
||||||
|
key: 'test-timestrip',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: 'time-strip'
|
||||||
|
}]);
|
||||||
|
let imageryView = applicableViews.find(
|
||||||
|
viewProvider => viewProvider.key === imageryKey
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(imageryView).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
describe("imagery view", () => {
|
describe("imagery view", () => {
|
||||||
let applicableViews;
|
let applicableViews;
|
||||||
let imageryViewProvider;
|
let imageryViewProvider;
|
||||||
@ -367,18 +435,18 @@ describe("The Imagery View Layout", () => {
|
|||||||
});
|
});
|
||||||
it ('shows an auto scroll button when scroll to left', async () => {
|
it ('shows an auto scroll button when scroll to left', async () => {
|
||||||
// to mock what a scroll would do
|
// to mock what a scroll would do
|
||||||
imageryView._getInstance().$refs.ImageryLayout.autoScroll = false;
|
imageryView._getInstance().$refs.ImageryContainer.autoScroll = false;
|
||||||
await Vue.nextTick();
|
await Vue.nextTick();
|
||||||
let autoScrollButton = parent.querySelector('.c-imagery__auto-scroll-resume-button');
|
let autoScrollButton = parent.querySelector('.c-imagery__auto-scroll-resume-button');
|
||||||
expect(autoScrollButton).toBeTruthy();
|
expect(autoScrollButton).toBeTruthy();
|
||||||
});
|
});
|
||||||
it ('scrollToRight is called when clicking on auto scroll button', async () => {
|
it ('scrollToRight is called when clicking on auto scroll button', async () => {
|
||||||
// use spyon to spy the scroll function
|
// use spyon to spy the scroll function
|
||||||
spyOn(imageryView._getInstance().$refs.ImageryLayout, 'scrollToRight');
|
spyOn(imageryView._getInstance().$refs.ImageryContainer, 'scrollToRight');
|
||||||
imageryView._getInstance().$refs.ImageryLayout.autoScroll = false;
|
imageryView._getInstance().$refs.ImageryContainer.autoScroll = false;
|
||||||
await Vue.nextTick();
|
await Vue.nextTick();
|
||||||
parent.querySelector('.c-imagery__auto-scroll-resume-button').click();
|
parent.querySelector('.c-imagery__auto-scroll-resume-button').click();
|
||||||
expect(imageryView._getInstance().$refs.ImageryLayout.scrollToRight).toHaveBeenCalledWith('reset');
|
expect(imageryView._getInstance().$refs.ImageryContainer.scrollToRight).toHaveBeenCalledWith('reset');
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -140,6 +140,7 @@ import SearchResults from './SearchResults.vue';
|
|||||||
import Sidebar from './Sidebar.vue';
|
import Sidebar from './Sidebar.vue';
|
||||||
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSectionId, setDefaultNotebookPageId } from '../utils/notebook-storage';
|
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSectionId, setDefaultNotebookPageId } from '../utils/notebook-storage';
|
||||||
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||||
|
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
|
||||||
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
|
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
|
||||||
import objectUtils from 'objectUtils';
|
import objectUtils from 'objectUtils';
|
||||||
|
|
||||||
@ -385,9 +386,13 @@ export default {
|
|||||||
const snapshotId = event.dataTransfer.getData('openmct/snapshot/id');
|
const snapshotId = event.dataTransfer.getData('openmct/snapshot/id');
|
||||||
if (snapshotId.length) {
|
if (snapshotId.length) {
|
||||||
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
||||||
this.newEntry(snapshot);
|
this.newEntry(snapshot.embedObject);
|
||||||
this.snapshotContainer.removeSnapshot(snapshotId);
|
this.snapshotContainer.removeSnapshot(snapshotId);
|
||||||
|
|
||||||
|
const namespace = this.domainObject.identifier.namespace;
|
||||||
|
const notebookImageDomainObject = updateNamespaceOfDomainObject(snapshot.notebookImageDomainObject, namespace);
|
||||||
|
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,12 +456,9 @@ export default {
|
|||||||
: undefined;
|
: undefined;
|
||||||
},
|
},
|
||||||
getDefaultNotebookObject() {
|
getDefaultNotebookObject() {
|
||||||
const oldNotebookStorage = getDefaultNotebook();
|
const defaultNotebook = getDefaultNotebook();
|
||||||
if (!oldNotebookStorage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.openmct.objects.get(oldNotebookStorage.identifier);
|
return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier);
|
||||||
},
|
},
|
||||||
getLinktoNotebook() {
|
getLinktoNotebook() {
|
||||||
const objectPath = this.openmct.router.path;
|
const objectPath = this.openmct.router.path;
|
||||||
|
@ -40,7 +40,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
PopupMenu
|
PopupMenu
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct', 'snapshotContainer'],
|
||||||
props: {
|
props: {
|
||||||
embed: {
|
embed: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -48,6 +48,12 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isSnapshotContainer: {
|
||||||
|
type: Boolean,
|
||||||
|
default() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
removeActionString: {
|
removeActionString: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
@ -135,6 +141,14 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isSnapshotContainer) {
|
||||||
|
const snapshot = this.snapshotContainer.getSnapshot(this.embed.id);
|
||||||
|
const fullSizeImageURL = snapshot.notebookImageDomainObject.configuration.fullSizeImageURL;
|
||||||
|
painterroInstance.show(fullSizeImageURL);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.openmct.objects.get(fullSizeImageObjectIdentifier)
|
this.openmct.objects.get(fullSizeImageObjectIdentifier)
|
||||||
.then(object => {
|
.then(object => {
|
||||||
painterroInstance.show(object.configuration.fullSizeImageURL);
|
painterroInstance.show(object.configuration.fullSizeImageURL);
|
||||||
@ -190,6 +204,14 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isSnapshotContainer) {
|
||||||
|
const snapshot = this.snapshotContainer.getSnapshot(this.embed.id);
|
||||||
|
const fullSizeImageURL = snapshot.notebookImageDomainObject.configuration.fullSizeImageURL;
|
||||||
|
this.openSnapshotOverlay(fullSizeImageURL);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.openmct.objects.get(fullSizeImageObjectIdentifier)
|
this.openmct.objects.get(fullSizeImageObjectIdentifier)
|
||||||
.then(object => {
|
.then(object => {
|
||||||
this.openSnapshotOverlay(object.configuration.fullSizeImageURL);
|
this.openSnapshotOverlay(object.configuration.fullSizeImageURL);
|
||||||
@ -259,8 +281,20 @@ export default {
|
|||||||
updateSnapshot(snapshotObject) {
|
updateSnapshot(snapshotObject) {
|
||||||
this.embed.snapshot.thumbnailImage = snapshotObject.thumbnailImage;
|
this.embed.snapshot.thumbnailImage = snapshotObject.thumbnailImage;
|
||||||
|
|
||||||
updateNotebookImageDomainObject(this.openmct, this.embed.snapshot.fullSizeImageObjectIdentifier, snapshotObject.fullSizeImage);
|
this.updateNotebookImageDomainObjectSnapshot(snapshotObject);
|
||||||
this.updateEmbed(this.embed);
|
this.updateEmbed(this.embed);
|
||||||
|
},
|
||||||
|
updateNotebookImageDomainObjectSnapshot(snapshotObject) {
|
||||||
|
if (this.isSnapshotContainer) {
|
||||||
|
const snapshot = this.snapshotContainer.getSnapshot(this.embed.id);
|
||||||
|
|
||||||
|
snapshot.embedObject.snapshot.thumbnailImage = snapshotObject.thumbnailImage;
|
||||||
|
snapshot.notebookImageDomainObject.configuration.fullSizeImageURL = snapshotObject.fullSizeImage.src;
|
||||||
|
|
||||||
|
this.snapshotContainer.updateSnapshot(snapshot);
|
||||||
|
} else {
|
||||||
|
updateNotebookImageDomainObject(this.openmct, this.embed.snapshot.fullSizeImageObjectIdentifier, snapshotObject.fullSizeImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -102,9 +102,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NotebookEmbed from './NotebookEmbed.vue';
|
import NotebookEmbed from './NotebookEmbed.vue';
|
||||||
import { createNewEmbed } from '../utils/notebook-entries';
|
|
||||||
import Moment from 'moment';
|
|
||||||
import TextHighlight from '../../../utils/textHighlight/TextHighlight.vue';
|
import TextHighlight from '../../../utils/textHighlight/TextHighlight.vue';
|
||||||
|
import { createNewEmbed } from '../utils/notebook-entries';
|
||||||
|
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
|
||||||
|
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -210,8 +212,12 @@ export default {
|
|||||||
const snapshotId = $event.dataTransfer.getData('openmct/snapshot/id');
|
const snapshotId = $event.dataTransfer.getData('openmct/snapshot/id');
|
||||||
if (snapshotId.length) {
|
if (snapshotId.length) {
|
||||||
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
||||||
|
this.entry.embeds.push(snapshot.embedObject);
|
||||||
this.snapshotContainer.removeSnapshot(snapshotId);
|
this.snapshotContainer.removeSnapshot(snapshotId);
|
||||||
this.entry.embeds.push(snapshot);
|
|
||||||
|
const namespace = this.domainObject.identifier.namespace;
|
||||||
|
const notebookImageDomainObject = updateNamespaceOfDomainObject(snapshot.notebookImageDomainObject, namespace);
|
||||||
|
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
||||||
} else {
|
} else {
|
||||||
const data = $event.dataTransfer.getData('openmct/domain-object-path');
|
const data = $event.dataTransfer.getData('openmct/domain-object-path');
|
||||||
const objectPath = JSON.parse(data);
|
const objectPath = JSON.parse(data);
|
||||||
|
@ -17,19 +17,26 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Snapshot from '../snapshot';
|
import Snapshot from '../snapshot';
|
||||||
import { getDefaultNotebook, getNotebookSectionAndPage, validateNotebookStorageObject } from '../utils/notebook-storage';
|
import { getDefaultNotebook, validateNotebookStorageObject } from '../utils/notebook-storage';
|
||||||
import { NOTEBOOK_DEFAULT, NOTEBOOK_SNAPSHOT } from '../notebook-constants';
|
import { NOTEBOOK_DEFAULT, NOTEBOOK_SNAPSHOT } from '../notebook-constants';
|
||||||
|
import { getMenuItems } from '../utils/notebook-snapshot-menu';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
|
currentView: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
domainObject: {
|
domainObject: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default() {
|
default() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ignoreLink: {
|
isPreview: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default() {
|
||||||
return false;
|
return false;
|
||||||
@ -50,51 +57,40 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
validateNotebookStorageObject();
|
validateNotebookStorageObject();
|
||||||
this.getDefaultNotebookObject();
|
|
||||||
|
|
||||||
this.notebookSnapshot = new Snapshot(this.openmct);
|
this.notebookSnapshot = new Snapshot(this.openmct);
|
||||||
this.setDefaultNotebookStatus();
|
this.setDefaultNotebookStatus();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getDefaultNotebookObject() {
|
getPreviewObjectLink() {
|
||||||
const defaultNotebook = getDefaultNotebook();
|
const relativePath = this.openmct.objects.getRelativePath(this.objectPath);
|
||||||
|
const urlParams = this.openmct.router.getParams();
|
||||||
|
urlParams.view = this.currentView.key;
|
||||||
|
|
||||||
return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier);
|
const urlParamsString = Object.entries(urlParams)
|
||||||
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
|
.join('&');
|
||||||
|
|
||||||
|
return `#/browse/${relativePath}?${urlParamsString}`;
|
||||||
},
|
},
|
||||||
async showMenu(event) {
|
async showMenu(event) {
|
||||||
const notebookTypes = [];
|
const menuItemOptions = {
|
||||||
|
default: {
|
||||||
|
cssClass: 'icon-notebook',
|
||||||
|
name: `Save to Notebook`,
|
||||||
|
onItemClicked: () => this.snapshot(NOTEBOOK_DEFAULT, event.target)
|
||||||
|
},
|
||||||
|
snapshot: {
|
||||||
|
cssClass: 'icon-camera',
|
||||||
|
name: 'Save to Notebook Snapshots',
|
||||||
|
onItemClicked: () => this.snapshot(NOTEBOOK_SNAPSHOT, event.target)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const notebookTypes = await getMenuItems(this.openmct, menuItemOptions);
|
||||||
const elementBoundingClientRect = this.$el.getBoundingClientRect();
|
const elementBoundingClientRect = this.$el.getBoundingClientRect();
|
||||||
const x = elementBoundingClientRect.x;
|
const x = elementBoundingClientRect.x;
|
||||||
const y = elementBoundingClientRect.y + elementBoundingClientRect.height;
|
const y = elementBoundingClientRect.y + elementBoundingClientRect.height;
|
||||||
|
|
||||||
const defaultNotebookObject = await this.getDefaultNotebookObject();
|
|
||||||
if (defaultNotebookObject) {
|
|
||||||
const defaultNotebook = getDefaultNotebook();
|
|
||||||
const { section, page } = getNotebookSectionAndPage(defaultNotebookObject, defaultNotebook.defaultSectionId, defaultNotebook.defaultPageId);
|
|
||||||
if (section && page) {
|
|
||||||
const name = defaultNotebookObject.name;
|
|
||||||
const sectionName = section.name;
|
|
||||||
const pageName = page.name;
|
|
||||||
const defaultPath = `${name} - ${sectionName} - ${pageName}`;
|
|
||||||
|
|
||||||
notebookTypes.push({
|
|
||||||
cssClass: 'icon-notebook',
|
|
||||||
name: `Save to Notebook ${defaultPath}`,
|
|
||||||
onItemClicked: () => {
|
|
||||||
return this.snapshot(NOTEBOOK_DEFAULT, event.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notebookTypes.push({
|
|
||||||
cssClass: 'icon-camera',
|
|
||||||
name: 'Save to Notebook Snapshots',
|
|
||||||
onItemClicked: () => {
|
|
||||||
return this.snapshot(NOTEBOOK_SNAPSHOT, event.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.openmct.menus.showMenu(x, y, notebookTypes);
|
this.openmct.menus.showMenu(x, y, notebookTypes);
|
||||||
},
|
},
|
||||||
snapshot(notebookType, target) {
|
snapshot(notebookType, target) {
|
||||||
@ -102,15 +98,12 @@ export default {
|
|||||||
const wrapper = target && target.closest('.js-notebook-snapshot-item-wrapper')
|
const wrapper = target && target.closest('.js-notebook-snapshot-item-wrapper')
|
||||||
|| document;
|
|| document;
|
||||||
const element = wrapper.querySelector('.js-notebook-snapshot-item');
|
const element = wrapper.querySelector('.js-notebook-snapshot-item');
|
||||||
|
|
||||||
const bounds = this.openmct.time.bounds();
|
|
||||||
const link = !this.ignoreLink
|
|
||||||
? window.location.hash
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const objectPath = this.objectPath || this.openmct.router.path;
|
const objectPath = this.objectPath || this.openmct.router.path;
|
||||||
|
const link = this.isPreview
|
||||||
|
? this.getPreviewObjectLink()
|
||||||
|
: window.location.hash;
|
||||||
const snapshotMeta = {
|
const snapshotMeta = {
|
||||||
bounds,
|
bounds: this.openmct.time.bounds(),
|
||||||
link,
|
link,
|
||||||
objectPath,
|
objectPath,
|
||||||
openmct: this.openmct
|
openmct: this.openmct
|
||||||
|
@ -27,15 +27,15 @@
|
|||||||
</div><!-- closes l-browse-bar -->
|
</div><!-- closes l-browse-bar -->
|
||||||
<div class="c-snapshots">
|
<div class="c-snapshots">
|
||||||
<span v-for="snapshot in snapshots"
|
<span v-for="snapshot in snapshots"
|
||||||
:key="snapshot.id"
|
:key="snapshot.embedObject.id"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@dragstart="startEmbedDrag(snapshot, $event)"
|
@dragstart="startEmbedDrag(snapshot, $event)"
|
||||||
>
|
>
|
||||||
<NotebookEmbed ref="notebookEmbed"
|
<NotebookEmbed ref="notebookEmbed"
|
||||||
:key="snapshot.id"
|
:key="snapshot.embedObject.id"
|
||||||
:embed="snapshot"
|
:embed="snapshot.embedObject"
|
||||||
|
:is-snapshot-container="true"
|
||||||
:remove-action-string="'Delete Snapshot'"
|
:remove-action-string="'Delete Snapshot'"
|
||||||
@updateEmbed="updateSnapshot"
|
|
||||||
@removeEmbed="removeSnapshot"
|
@removeEmbed="removeSnapshot"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@ -119,11 +119,8 @@ export default {
|
|||||||
this.snapshots = this.snapshotContainer.getSnapshots();
|
this.snapshots = this.snapshotContainer.getSnapshots();
|
||||||
},
|
},
|
||||||
startEmbedDrag(snapshot, event) {
|
startEmbedDrag(snapshot, event) {
|
||||||
event.dataTransfer.setData('text/plain', snapshot.id);
|
event.dataTransfer.setData('text/plain', snapshot.embedObject.id);
|
||||||
event.dataTransfer.setData('openmct/snapshot/id', snapshot.id);
|
event.dataTransfer.setData('openmct/snapshot/id', snapshot.embedObject.id);
|
||||||
},
|
|
||||||
updateSnapshot(snapshot) {
|
|
||||||
this.snapshotContainer.updateSnapshot(snapshot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -18,13 +18,18 @@ export default class SnapshotContainer extends EventEmitter {
|
|||||||
return SnapshotContainer.instance;
|
return SnapshotContainer.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
addSnapshot(embedObject) {
|
addSnapshot(notebookImageDomainObject, embedObject) {
|
||||||
const snapshots = this.getSnapshots();
|
const snapshots = this.getSnapshots();
|
||||||
if (snapshots.length >= NOTEBOOK_SNAPSHOT_MAX_COUNT) {
|
if (snapshots.length >= NOTEBOOK_SNAPSHOT_MAX_COUNT) {
|
||||||
snapshots.pop();
|
snapshots.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshots.unshift(embedObject);
|
const snapshotObject = {
|
||||||
|
notebookImageDomainObject,
|
||||||
|
embedObject
|
||||||
|
};
|
||||||
|
|
||||||
|
snapshots.unshift(snapshotObject);
|
||||||
|
|
||||||
return this.saveSnapshots(snapshots);
|
return this.saveSnapshots(snapshots);
|
||||||
}
|
}
|
||||||
@ -32,7 +37,7 @@ export default class SnapshotContainer extends EventEmitter {
|
|||||||
getSnapshot(id) {
|
getSnapshot(id) {
|
||||||
const snapshots = this.getSnapshots();
|
const snapshots = this.getSnapshots();
|
||||||
|
|
||||||
return snapshots.find(s => s.id === id);
|
return snapshots.find(s => s.embedObject.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSnapshots() {
|
getSnapshots() {
|
||||||
@ -47,7 +52,7 @@ export default class SnapshotContainer extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const snapshots = this.getSnapshots();
|
const snapshots = this.getSnapshots();
|
||||||
const filteredsnapshots = snapshots.filter(snapshot => snapshot.id !== id);
|
const filteredsnapshots = snapshots.filter(snapshot => snapshot.embedObject.id !== id);
|
||||||
|
|
||||||
return this.saveSnapshots(filteredsnapshots);
|
return this.saveSnapshots(filteredsnapshots);
|
||||||
}
|
}
|
||||||
@ -73,7 +78,7 @@ export default class SnapshotContainer extends EventEmitter {
|
|||||||
updateSnapshot(snapshot) {
|
updateSnapshot(snapshot) {
|
||||||
const snapshots = this.getSnapshots();
|
const snapshots = this.getSnapshots();
|
||||||
const updatedSnapshots = snapshots.map(s => {
|
const updatedSnapshots = snapshots.map(s => {
|
||||||
return s.id === snapshot.id
|
return s.embedObject.id === snapshot.embedObject.id
|
||||||
? snapshot
|
? snapshot
|
||||||
: s;
|
: s;
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries';
|
import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries';
|
||||||
import { getDefaultNotebook, getNotebookSectionAndPage, getDefaultNotebookLink, setDefaultNotebook } from './utils/notebook-storage';
|
import { getDefaultNotebook, getNotebookSectionAndPage, getDefaultNotebookLink, setDefaultNotebook } from './utils/notebook-storage';
|
||||||
import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants';
|
import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants';
|
||||||
import { createNotebookImageDomainObject, DEFAULT_SIZE } from './utils/notebook-image';
|
import { createNotebookImageDomainObject, saveNotebookImageDomainObject, updateNamespaceOfDomainObject, DEFAULT_SIZE } from './utils/notebook-image';
|
||||||
|
|
||||||
import SnapshotContainer from './snapshot-container';
|
import SnapshotContainer from './snapshot-container';
|
||||||
import ImageExporter from '../../exporters/ImageExporter';
|
import ImageExporter from '../../exporters/ImageExporter';
|
||||||
@ -35,29 +35,28 @@ export default class Snapshot {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_saveSnapShot(notebookType, fullSizeImageURL, thumbnailImageURL, snapshotMeta) {
|
_saveSnapShot(notebookType, fullSizeImageURL, thumbnailImageURL, snapshotMeta) {
|
||||||
createNotebookImageDomainObject(this.openmct, fullSizeImageURL)
|
const object = createNotebookImageDomainObject(fullSizeImageURL);
|
||||||
.then(object => {
|
const thumbnailImage = { src: thumbnailImageURL || '' };
|
||||||
const thumbnailImage = { src: thumbnailImageURL || '' };
|
const snapshot = {
|
||||||
const snapshot = {
|
fullSizeImageObjectIdentifier: object.identifier,
|
||||||
fullSizeImageObjectIdentifier: object.identifier,
|
thumbnailImage
|
||||||
thumbnailImage
|
};
|
||||||
};
|
const embed = createNewEmbed(snapshotMeta, snapshot);
|
||||||
const embed = createNewEmbed(snapshotMeta, snapshot);
|
if (notebookType === NOTEBOOK_DEFAULT) {
|
||||||
if (notebookType === NOTEBOOK_DEFAULT) {
|
const notebookStorage = getDefaultNotebook();
|
||||||
this._saveToDefaultNoteBook(embed);
|
|
||||||
|
|
||||||
return;
|
this._saveToDefaultNoteBook(notebookStorage, embed);
|
||||||
}
|
const notebookImageDomainObject = updateNamespaceOfDomainObject(object, notebookStorage.identifier.namespace);
|
||||||
|
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
||||||
this._saveToNotebookSnapshots(embed);
|
} else {
|
||||||
});
|
this._saveToNotebookSnapshots(object, embed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_saveToDefaultNoteBook(embed) {
|
_saveToDefaultNoteBook(notebookStorage, embed) {
|
||||||
const notebookStorage = getDefaultNotebook();
|
|
||||||
this.openmct.objects.get(notebookStorage.identifier)
|
this.openmct.objects.get(notebookStorage.identifier)
|
||||||
.then(async (domainObject) => {
|
.then(async (domainObject) => {
|
||||||
addNotebookEntry(this.openmct, domainObject, notebookStorage, embed);
|
addNotebookEntry(this.openmct, domainObject, notebookStorage, embed);
|
||||||
@ -85,19 +84,22 @@ export default class Snapshot {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_saveToNotebookSnapshots(embed) {
|
_saveToNotebookSnapshots(notebookImageDomainObject, embed) {
|
||||||
this.snapshotContainer.addSnapshot(embed);
|
this.snapshotContainer.addSnapshot(notebookImageDomainObject, embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
_showNotification(msg, url) {
|
_showNotification(msg, url) {
|
||||||
const options = {
|
const options = {
|
||||||
autoDismissTimeout: 30000,
|
autoDismissTimeout: 30000
|
||||||
link: {
|
};
|
||||||
|
|
||||||
|
if (!this.openmct.editor.isEditing()) {
|
||||||
|
options.link = {
|
||||||
cssClass: '',
|
cssClass: '',
|
||||||
text: 'click to view',
|
text: 'click to view',
|
||||||
onClick: this._navigateToNotebook(url)
|
onClick: this._navigateToNotebook(url)
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
this.openmct.notifications.info(msg, options);
|
this.openmct.notifications.info(msg, options);
|
||||||
}
|
}
|
||||||
@ -108,7 +110,8 @@ export default class Snapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.location.href = window.location.origin + url;
|
const path = window.location.href.split('#');
|
||||||
|
window.location.href = path[0] + url;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ export const DEFAULT_SIZE = {
|
|||||||
height: 30
|
height: 30
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createNotebookImageDomainObject(openmct, fullSizeImageURL) {
|
export function createNotebookImageDomainObject(fullSizeImageURL) {
|
||||||
const identifier = {
|
const identifier = {
|
||||||
key: uuid(),
|
key: uuid(),
|
||||||
namespace: ''
|
namespace: ''
|
||||||
};
|
};
|
||||||
const viewType = 'notebookSnapshotImage';
|
const viewType = 'notebookSnapshotImage';
|
||||||
|
|
||||||
const object = {
|
return {
|
||||||
name: 'Notebook Snapshot Image',
|
name: 'Notebook Snapshot Image',
|
||||||
type: viewType,
|
type: viewType,
|
||||||
identifier,
|
identifier,
|
||||||
@ -20,21 +20,6 @@ export function createNotebookImageDomainObject(openmct, fullSizeImageURL) {
|
|||||||
fullSizeImageURL
|
fullSizeImageURL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openmct.objects.save(object)
|
|
||||||
.then(result => {
|
|
||||||
if (result) {
|
|
||||||
resolve(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
reject();
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
console.error(e);
|
|
||||||
reject();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getThumbnailURLFromCanvas(canvas, size = DEFAULT_SIZE) {
|
export function getThumbnailURLFromCanvas(canvas, size = DEFAULT_SIZE) {
|
||||||
@ -67,6 +52,23 @@ export function getThumbnailURLFromimageUrl(imageUrl, size = DEFAULT_SIZE) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function saveNotebookImageDomainObject(openmct, object) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openmct.objects.save(object)
|
||||||
|
.then(result => {
|
||||||
|
if (result) {
|
||||||
|
resolve(object);
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function updateNotebookImageDomainObject(openmct, identifier, fullSizeImage) {
|
export function updateNotebookImageDomainObject(openmct, identifier, fullSizeImage) {
|
||||||
openmct.objects.get(identifier)
|
openmct.objects.get(identifier)
|
||||||
.then(domainObject => {
|
.then(domainObject => {
|
||||||
@ -76,3 +78,9 @@ export function updateNotebookImageDomainObject(openmct, identifier, fullSizeIma
|
|||||||
openmct.objects.mutate(domainObject, 'configuration', configuration);
|
openmct.objects.mutate(domainObject, 'configuration', configuration);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateNamespaceOfDomainObject(object, namespace) {
|
||||||
|
object.identifier.namespace = namespace;
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { createNotebookImageDomainObject, getThumbnailURLFromimageUrl } from './notebook-image';
|
import { createNotebookImageDomainObject, getThumbnailURLFromimageUrl, saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from './notebook-image';
|
||||||
import { mutateObject } from './notebook-entries';
|
import { mutateObject } from './notebook-entries';
|
||||||
|
|
||||||
|
const IMAGE_MIGRATION_VER = "v1";
|
||||||
|
|
||||||
export function notebookImageMigration(openmct, domainObject) {
|
export function notebookImageMigration(openmct, domainObject) {
|
||||||
const configuration = domainObject.configuration;
|
const configuration = domainObject.configuration;
|
||||||
const notebookEntries = configuration.entries;
|
const notebookEntries = configuration.entries;
|
||||||
|
|
||||||
const imageMigrationVer = configuration.imageMigrationVer;
|
const imageMigrationVer = configuration.imageMigrationVer;
|
||||||
if (imageMigrationVer && imageMigrationVer === 'v1') {
|
if (imageMigrationVer && imageMigrationVer === IMAGE_MIGRATION_VER) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration.imageMigrationVer = 'v1';
|
configuration.imageMigrationVer = IMAGE_MIGRATION_VER;
|
||||||
|
|
||||||
// to avoid muliple notebookImageMigration calls updating images.
|
// to avoid muliple notebookImageMigration calls updating images.
|
||||||
mutateObject(openmct, domainObject, 'configuration', configuration);
|
mutateObject(openmct, domainObject, 'configuration', configuration);
|
||||||
@ -27,14 +29,16 @@ export function notebookImageMigration(openmct, domainObject) {
|
|||||||
const fullSizeImageURL = snapshot.src;
|
const fullSizeImageURL = snapshot.src;
|
||||||
if (fullSizeImageURL) {
|
if (fullSizeImageURL) {
|
||||||
const thumbnailImageURL = await getThumbnailURLFromimageUrl(fullSizeImageURL);
|
const thumbnailImageURL = await getThumbnailURLFromimageUrl(fullSizeImageURL);
|
||||||
const notebookImageDomainObject = await createNotebookImageDomainObject(openmct, fullSizeImageURL);
|
const object = createNotebookImageDomainObject(fullSizeImageURL);
|
||||||
|
const notebookImageDomainObject = updateNamespaceOfDomainObject(object, domainObject.identifier.namespace);
|
||||||
embed.snapshot = {
|
embed.snapshot = {
|
||||||
fullSizeImageObjectIdentifier: notebookImageDomainObject.identifier,
|
fullSizeImageObjectIdentifier: notebookImageDomainObject.identifier,
|
||||||
thumbnailImage: { src: thumbnailImageURL || '' }
|
thumbnailImage: { src: thumbnailImageURL || '' }
|
||||||
};
|
};
|
||||||
|
|
||||||
mutateObject(openmct, domainObject, 'configuration.entries', notebookEntries);
|
mutateObject(openmct, domainObject, 'configuration.entries', notebookEntries);
|
||||||
|
|
||||||
|
saveNotebookImageDomainObject(openmct, notebookImageDomainObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
31
src/plugins/notebook/utils/notebook-snapshot-menu.js
Normal file
31
src/plugins/notebook/utils/notebook-snapshot-menu.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { getDefaultNotebook, getNotebookSectionAndPage } from './notebook-storage';
|
||||||
|
|
||||||
|
export async function getMenuItems(openmct, menuItemOptions) {
|
||||||
|
const notebookTypes = [];
|
||||||
|
|
||||||
|
const defaultNotebook = getDefaultNotebook();
|
||||||
|
const defaultNotebookObject = defaultNotebook && await openmct.objects.get(defaultNotebook.identifier);
|
||||||
|
if (defaultNotebookObject) {
|
||||||
|
const { section, page } = getNotebookSectionAndPage(defaultNotebookObject, defaultNotebook.defaultSectionId, defaultNotebook.defaultPageId);
|
||||||
|
if (section && page) {
|
||||||
|
const name = defaultNotebookObject.name;
|
||||||
|
const sectionName = section.name;
|
||||||
|
const pageName = page.name;
|
||||||
|
const defaultPath = `${name} - ${sectionName} - ${pageName}`;
|
||||||
|
|
||||||
|
notebookTypes.push({
|
||||||
|
cssClass: menuItemOptions.default.cssClass,
|
||||||
|
name: `${menuItemOptions.default.name} ${defaultPath}`,
|
||||||
|
onItemClicked: menuItemOptions.default.onItemClicked
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notebookTypes.push({
|
||||||
|
cssClass: menuItemOptions.snapshot.cssClass,
|
||||||
|
name: menuItemOptions.snapshot.name,
|
||||||
|
onItemClicked: menuItemOptions.snapshot.onItemClicked
|
||||||
|
});
|
||||||
|
|
||||||
|
return notebookTypes;
|
||||||
|
}
|
@ -71,11 +71,7 @@ export async function getDefaultNotebookLink(openmct, domainObject = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const path = await openmct.objects.getOriginalPath(domainObject.identifier)
|
const path = await openmct.objects.getOriginalPath(domainObject.identifier)
|
||||||
.then(objectPath => objectPath
|
.then(openmct.objects.getRelativePath);
|
||||||
.map(o => o && openmct.objects.makeKeyString(o.identifier))
|
|
||||||
.reverse()
|
|
||||||
.join('/')
|
|
||||||
);
|
|
||||||
const { defaultPageId, defaultSectionId } = getDefaultNotebook();
|
const { defaultPageId, defaultSectionId } = getDefaultNotebook();
|
||||||
|
|
||||||
return `#/browse/${path}?sectionId=${defaultSectionId}&pageId=${defaultPageId}`;
|
return `#/browse/${path}?sectionId=${defaultSectionId}&pageId=${defaultPageId}`;
|
||||||
|
@ -143,10 +143,12 @@ export default {
|
|||||||
},
|
},
|
||||||
updateViewBounds() {
|
updateViewBounds() {
|
||||||
this.viewBounds = this.openmct.time.bounds();
|
this.viewBounds = this.openmct.time.bounds();
|
||||||
|
if (!this.options.compact) {
|
||||||
//Add a 50% padding to the end bounds to look ahead
|
//Add a 50% padding to the end bounds to look ahead
|
||||||
let timespan = (this.viewBounds.end - this.viewBounds.start);
|
let timespan = (this.viewBounds.end - this.viewBounds.start);
|
||||||
let padding = timespan / 2;
|
let padding = timespan / 2;
|
||||||
this.viewBounds.end = this.viewBounds.end + padding;
|
this.viewBounds.end = this.viewBounds.end + padding;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.timeSystem === undefined) {
|
if (this.timeSystem === undefined) {
|
||||||
this.timeSystem = this.openmct.time.timeSystem();
|
this.timeSystem = this.openmct.time.timeSystem();
|
||||||
|
@ -393,12 +393,31 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clearData() {
|
clearSeries() {
|
||||||
this.config.series.forEach(function (series) {
|
this.config.series.forEach(function (series) {
|
||||||
series.reset();
|
series.reset();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
compositionPathContainsId(domainObjectToClear) {
|
||||||
|
return domainObjectToClear.composition.some((compositionIdentifier) => {
|
||||||
|
return this.openmct.objects.areIdsEqual(compositionIdentifier, this.domainObject.identifier);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearData(domainObjectToClear) {
|
||||||
|
// If we don't have an object to clear (global), or the IDs are equal, just clear the data.
|
||||||
|
// If we have an object to clear, but the IDs don't match, we need to check the composition
|
||||||
|
// of the object we've been asked to clear to see if it contains the id we're looking for.
|
||||||
|
// This happens with stacked plots for example.
|
||||||
|
// If we find the ID, clear the plot.
|
||||||
|
if (!domainObjectToClear
|
||||||
|
|| this.openmct.objects.areIdsEqual(domainObjectToClear.identifier, this.domainObject.identifier)
|
||||||
|
|| this.compositionPathContainsId(domainObjectToClear)) {
|
||||||
|
this.clearSeries();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setDisplayRange(series, xKey) {
|
setDisplayRange(series, xKey) {
|
||||||
if (this.config.series.length !== 1) {
|
if (this.config.series.length !== 1) {
|
||||||
return;
|
return;
|
||||||
@ -1010,7 +1029,8 @@ export default {
|
|||||||
this.$emit('statusUpdated', status);
|
this.$emit('statusUpdated', status);
|
||||||
},
|
},
|
||||||
handleWindowResize() {
|
handleWindowResize() {
|
||||||
if (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth) {
|
if (this.$parent.$refs.plotWrapper
|
||||||
|
&& (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth)) {
|
||||||
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
|
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
|
||||||
this.config.series.models.forEach(this.loadSeriesData, this);
|
this.config.series.models.forEach(this.loadSeriesData, this);
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ define([
|
|||||||
'./CouchDBSearchFolder/plugin',
|
'./CouchDBSearchFolder/plugin',
|
||||||
'./timeline/plugin',
|
'./timeline/plugin',
|
||||||
'./hyperlink/plugin',
|
'./hyperlink/plugin',
|
||||||
|
'./clock/plugin',
|
||||||
'./DeviceClassifier/plugin'
|
'./DeviceClassifier/plugin'
|
||||||
], function (
|
], function (
|
||||||
_,
|
_,
|
||||||
@ -119,6 +120,7 @@ define([
|
|||||||
CouchDBSearchFolder,
|
CouchDBSearchFolder,
|
||||||
Timeline,
|
Timeline,
|
||||||
Hyperlink,
|
Hyperlink,
|
||||||
|
Clock,
|
||||||
DeviceClassifier
|
DeviceClassifier
|
||||||
) {
|
) {
|
||||||
const bundleMap = {
|
const bundleMap = {
|
||||||
@ -223,6 +225,7 @@ define([
|
|||||||
plugins.CouchDBSearchFolder = CouchDBSearchFolder.default;
|
plugins.CouchDBSearchFolder = CouchDBSearchFolder.default;
|
||||||
plugins.Timeline = Timeline.default;
|
plugins.Timeline = Timeline.default;
|
||||||
plugins.Hyperlink = Hyperlink.default;
|
plugins.Hyperlink = Hyperlink.default;
|
||||||
|
plugins.Clock = Clock.default;
|
||||||
plugins.DeviceClassifier = DeviceClassifier.default;
|
plugins.DeviceClassifier = DeviceClassifier.default;
|
||||||
|
|
||||||
return plugins;
|
return plugins;
|
||||||
|
@ -153,6 +153,7 @@ define([
|
|||||||
|
|
||||||
this.telemetryCollections[keyString].on('remove', telemetryRemover);
|
this.telemetryCollections[keyString].on('remove', telemetryRemover);
|
||||||
this.telemetryCollections[keyString].on('add', telemetryProcessor);
|
this.telemetryCollections[keyString].on('add', telemetryProcessor);
|
||||||
|
this.telemetryCollections[keyString].on('clear', this.tableRows.clear);
|
||||||
this.telemetryCollections[keyString].load();
|
this.telemetryCollections[keyString].load();
|
||||||
|
|
||||||
this.decrementOutstandingRequests();
|
this.decrementOutstandingRequests();
|
||||||
|
@ -92,6 +92,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__progress-bar {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************* WRAPPERS */
|
/******************************* WRAPPERS */
|
||||||
&__body-w {
|
&__body-w {
|
||||||
// Wraps __body table provides scrolling
|
// Wraps __body table provides scrolling
|
||||||
|
@ -138,6 +138,13 @@
|
|||||||
class="c-telemetry-table__drop-target"
|
class="c-telemetry-table__drop-target"
|
||||||
:style="dropTargetStyle"
|
:style="dropTargetStyle"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
|
<progress-bar
|
||||||
|
v-if="loading"
|
||||||
|
class="c-telemetry-table__progress-bar"
|
||||||
|
:model="progressLoad"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Headers table -->
|
<!-- Headers table -->
|
||||||
<div
|
<div
|
||||||
v-show="!hideHeaders"
|
v-show="!hideHeaders"
|
||||||
@ -285,6 +292,7 @@ import CSVExporter from '../../../exporters/CSVExporter.js';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||||
import SizingRow from './sizing-row.vue';
|
import SizingRow from './sizing-row.vue';
|
||||||
|
import ProgressBar from "../../../ui/components/ProgressBar.vue";
|
||||||
|
|
||||||
const VISIBLE_ROW_COUNT = 100;
|
const VISIBLE_ROW_COUNT = 100;
|
||||||
const ROW_HEIGHT = 17;
|
const ROW_HEIGHT = 17;
|
||||||
@ -298,7 +306,8 @@ export default {
|
|||||||
search,
|
search,
|
||||||
TableFooterIndicator,
|
TableFooterIndicator,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
SizingRow
|
SizingRow,
|
||||||
|
ProgressBar
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'objectPath', 'table', 'currentView'],
|
inject: ['openmct', 'objectPath', 'table', 'currentView'],
|
||||||
props: {
|
props: {
|
||||||
@ -353,7 +362,7 @@ export default {
|
|||||||
autoScroll: true,
|
autoScroll: true,
|
||||||
sortOptions: {},
|
sortOptions: {},
|
||||||
filters: {},
|
filters: {},
|
||||||
loading: false,
|
loading: true,
|
||||||
scrollable: undefined,
|
scrollable: undefined,
|
||||||
tableEl: undefined,
|
tableEl: undefined,
|
||||||
headersHolderEl: undefined,
|
headersHolderEl: undefined,
|
||||||
@ -374,6 +383,11 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
progressLoad() {
|
||||||
|
return {
|
||||||
|
progressPerc: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
dropTargetStyle() {
|
dropTargetStyle() {
|
||||||
return {
|
return {
|
||||||
top: this.$refs.headersTable.offsetTop + 'px',
|
top: this.$refs.headersTable.offsetTop + 'px',
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
class="u-contents"
|
class="u-contents"
|
||||||
:default-object="item.domainObject"
|
:default-object="item.domainObject"
|
||||||
:object-path="item.objectPath"
|
:object-path="item.objectPath"
|
||||||
|
@change-action-collection="setActionCollection"
|
||||||
/>
|
/>
|
||||||
</swim-lane>
|
</swim-lane>
|
||||||
</template>
|
</template>
|
||||||
@ -106,6 +107,9 @@ export default {
|
|||||||
this.$el, this.context);
|
this.$el, this.context);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
setActionCollection(actionCollection) {
|
||||||
|
this.openmct.menus.actionsToMenuItems(actionCollection.getVisibleActions(), actionCollection.objectPath, actionCollection.view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import PreviewHeader from '@/ui/preview/preview-header.vue';
|
import Preview from '@/ui/preview/Preview.vue';
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
@ -46,70 +46,43 @@ export default class ViewLargeAction {
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._expand(objectPath, childElement, view);
|
this._expand(objectPath, childElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
appliesTo(objectPath, view = {}) {
|
appliesTo(objectPath, view = {}) {
|
||||||
const parentElement = view.parentElement;
|
const parentElement = view.parentElement;
|
||||||
const element = parentElement && parentElement.firstChild;
|
const element = parentElement && parentElement.firstChild;
|
||||||
const viewLargeAction = element && !element.classList.contains('js-main-container')
|
const viewLargeAction = element && !element.classList.contains('js-main-container')
|
||||||
&& !this._isNavigatedObject(objectPath);
|
&& !this.openmct.router.isNavigatedObject(objectPath);
|
||||||
|
|
||||||
return viewLargeAction;
|
return viewLargeAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
_expand(objectPath, childElement, view) {
|
_expand(objectPath, childElement) {
|
||||||
const parentElement = childElement.parentElement;
|
const parentElement = childElement.parentElement;
|
||||||
|
|
||||||
this.overlay = this.openmct.overlays.overlay({
|
this.overlay = this.openmct.overlays.overlay({
|
||||||
element: this._getOverlayElement(objectPath, childElement, view),
|
element: this._getPreview(objectPath),
|
||||||
size: 'large',
|
size: 'large',
|
||||||
|
autoHide: false,
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
parentElement.append(childElement);
|
parentElement.append(childElement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getOverlayElement(objectPath, childElement, view) {
|
_getPreview(objectPath) {
|
||||||
const fragment = new DocumentFragment();
|
|
||||||
const header = this._getPreviewHeader(objectPath, view);
|
|
||||||
fragment.append(header);
|
|
||||||
|
|
||||||
const wrapper = document.createElement('div');
|
|
||||||
wrapper.classList.add('l-preview-window__object-view');
|
|
||||||
wrapper.append(childElement);
|
|
||||||
fragment.append(wrapper);
|
|
||||||
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getPreviewHeader(objectPath, view) {
|
|
||||||
const domainObject = objectPath[0];
|
|
||||||
const actionCollection = this.openmct.actions.getActionsCollection(objectPath, view);
|
|
||||||
const preview = new Vue({
|
const preview = new Vue({
|
||||||
components: {
|
components: {
|
||||||
PreviewHeader
|
Preview
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct: this.openmct,
|
openmct: this.openmct,
|
||||||
objectPath: this.objectPath
|
objectPath
|
||||||
},
|
},
|
||||||
data() {
|
template: '<Preview></Preview>'
|
||||||
return {
|
|
||||||
domainObject,
|
|
||||||
actionCollection
|
|
||||||
};
|
|
||||||
},
|
|
||||||
template: '<PreviewHeader :actionCollection="actionCollection" :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return preview.$mount().$el;
|
return preview.$mount().$el;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isNavigatedObject(objectPath) {
|
|
||||||
let targetObject = objectPath[0];
|
|
||||||
let navigatedObject = this.openmct.router.path[0];
|
|
||||||
|
|
||||||
return this.openmct.objects.areIdsEqual(targetObject.identifier, navigatedObject.identifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,10 @@ $mobileTreeItemH: 35px; // Used
|
|||||||
|
|
||||||
/************************** UI ELEMENTS */
|
/************************** UI ELEMENTS */
|
||||||
/*************** Progress Bar */
|
/*************** Progress Bar */
|
||||||
$colorProgressBarHolder: rgba(black, 0.1);
|
$colorProgressBarHolder: rgba(black, 0.2);
|
||||||
$colorProgressBar: #0085ad;
|
$colorProgressBar: #0085ad;
|
||||||
$progressAnimW: 500px;
|
$progressAnimW: 500px;
|
||||||
$progressBarMinH: 6px;
|
$progressBarMinH: 4px;
|
||||||
/************************** FONT STYLING */
|
/************************** FONT STYLING */
|
||||||
$listFontSizes: 8,9,10,11,12,13,14,16,18,20,24,28,32,36,42,48,72,96,128;
|
$listFontSizes: 8,9,10,11,12,13,14,16,18,20,24,28,32,36,42,48,72,96,128;
|
||||||
|
|
||||||
@ -259,6 +259,8 @@ $glyph-icon-condition-widget: '\eb28';
|
|||||||
$glyph-icon-alphanumeric: '\eb29';
|
$glyph-icon-alphanumeric: '\eb29';
|
||||||
$glyph-icon-image-telemetry: '\eb2a';
|
$glyph-icon-image-telemetry: '\eb2a';
|
||||||
$glyph-icon-telemetry-aggregate: '\eb2b';
|
$glyph-icon-telemetry-aggregate: '\eb2b';
|
||||||
|
$glyph-icon-bar-chart: '\eb2c';
|
||||||
|
$glyph-icon-map: '\eb2d';
|
||||||
|
|
||||||
/************************** GLYPHS AS DATA URI */
|
/************************** GLYPHS AS DATA URI */
|
||||||
// Only objects have been converted, for use in Create menu and folder views
|
// Only objects have been converted, for use in Create menu and folder views
|
||||||
@ -310,3 +312,5 @@ $bg-icon-spectra-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='
|
|||||||
$bg-icon-command: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M185.1 229.7a96.5 96.5 0 0015.8 11.7A68.5 68.5 0 01192 208c0-19.8 8.9-38.8 25.1-53.7 18.5-17 43.7-26.3 70.9-26.3 20.1 0 39.1 5.1 55.1 14.6a81.3 81.3 0 00-16.2-20.3C308.4 105.3 283.2 96 256 96s-52.4 9.3-70.9 26.3C168.9 137.2 160 156.2 160 176s8.9 38.8 25.1 53.7z'/%3e%3cpath d='M442.7 134.8C422.4 57.5 346.5 0 256 0S89.6 57.5 69.3 134.8C26.3 174.8 0 228.7 0 288c0 123.7 114.6 224 256 224s256-100.3 256-224c0-59.3-26.3-113.2-69.3-153.2zM256 64c70.6 0 128 50.2 128 112s-57.4 112-128 112-128-50.2-128-112S185.4 64 256 64zm0 352c-87.7 0-159.2-63.9-160-142.7 34.4 47.4 93.2 78.7 160 78.7s125.6-31.3 160-78.7c-.8 78.8-72.3 142.7-160 142.7z'/%3e%3c/svg%3e");
|
$bg-icon-command: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M185.1 229.7a96.5 96.5 0 0015.8 11.7A68.5 68.5 0 01192 208c0-19.8 8.9-38.8 25.1-53.7 18.5-17 43.7-26.3 70.9-26.3 20.1 0 39.1 5.1 55.1 14.6a81.3 81.3 0 00-16.2-20.3C308.4 105.3 283.2 96 256 96s-52.4 9.3-70.9 26.3C168.9 137.2 160 156.2 160 176s8.9 38.8 25.1 53.7z'/%3e%3cpath d='M442.7 134.8C422.4 57.5 346.5 0 256 0S89.6 57.5 69.3 134.8C26.3 174.8 0 228.7 0 288c0 123.7 114.6 224 256 224s256-100.3 256-224c0-59.3-26.3-113.2-69.3-153.2zM256 64c70.6 0 128 50.2 128 112s-57.4 112-128 112-128-50.2-128-112S185.4 64 256 64zm0 352c-87.7 0-159.2-63.9-160-142.7 34.4 47.4 93.2 78.7 160 78.7s125.6-31.3 160-78.7c-.8 78.8-72.3 142.7-160 142.7z'/%3e%3c/svg%3e");
|
||||||
$bg-icon-conditional: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 0C114.62 0 0 114.62 0 256s114.62 256 256 256 256-114.62 256-256S397.38 0 256 0zm0 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
|
$bg-icon-conditional: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 0C114.62 0 0 114.62 0 256s114.62 256 256 256 256-114.62 256-256S397.38 0 256 0zm0 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
|
||||||
$bg-icon-condition-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM256 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
|
$bg-icon-condition-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM256 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
|
||||||
|
$bg-icon-bar-chart: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM133.82 448H64V224h69.82Zm104.73 0h-69.82V64h69.82Zm104.72 0h-69.82V288h69.82ZM448 448h-69.82V128H448Z'/%3e%3c/svg%3e");
|
||||||
|
$bg-icon-map: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M448 32.7 384 64v448l64-31.3c35.2-17.21 64-60.1 64-95.3v-320c0-35.2-28.8-49.91-64-32.7ZM160 456l193.6 48.4v-448L160 8v448zM129.6.4 128 0 64 31.3C28.8 48.51 0 91.4 0 126.6v320c0 35.2 28.8 49.91 64 32.7l64-31.3 1.6.4Z'/%3e%3c/svg%3e");
|
||||||
|
@ -190,6 +190,8 @@
|
|||||||
.icon-alphanumeric { @include glyphBefore($glyph-icon-alphanumeric); }
|
.icon-alphanumeric { @include glyphBefore($glyph-icon-alphanumeric); }
|
||||||
.icon-image-telemetry { @include glyphBefore($glyph-icon-image-telemetry); }
|
.icon-image-telemetry { @include glyphBefore($glyph-icon-image-telemetry); }
|
||||||
.icon-telemetry-aggregate { @include glyphBefore($glyph-icon-telemetry-aggregate); }
|
.icon-telemetry-aggregate { @include glyphBefore($glyph-icon-telemetry-aggregate); }
|
||||||
|
.icon-bar-chart { @include glyphBefore($glyph-icon-bar-chart); }
|
||||||
|
.icon-map { @include glyphBefore($glyph-icon-map); }
|
||||||
|
|
||||||
/************************** 12 PX CLASSES */
|
/************************** 12 PX CLASSES */
|
||||||
// TODO: sync with 16px redo as of 10/25/18
|
// TODO: sync with 16px redo as of 10/25/18
|
||||||
@ -249,3 +251,5 @@
|
|||||||
.bg-icon-command { @include glyphBg($bg-icon-command); }
|
.bg-icon-command { @include glyphBg($bg-icon-command); }
|
||||||
.bg-icon-conditional { @include glyphBg($bg-icon-conditional); }
|
.bg-icon-conditional { @include glyphBg($bg-icon-conditional); }
|
||||||
.bg-icon-condition-widget { @include glyphBg($bg-icon-condition-widget); }
|
.bg-icon-condition-widget { @include glyphBg($bg-icon-condition-widget); }
|
||||||
|
.bg-icon-bar-chart { @include glyphBg($bg-icon-bar-chart); }
|
||||||
|
.bg-icon-map { @include glyphBg($bg-icon-map); }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "Open MCT Symbols 16px",
|
"name": "Open MCT Symbols 16px",
|
||||||
"lastOpened": 0,
|
"lastOpened": 0,
|
||||||
"created": 1629996145999
|
"created": 1631832601684
|
||||||
},
|
},
|
||||||
"iconSets": [
|
"iconSets": [
|
||||||
{
|
{
|
||||||
@ -1214,6 +1214,22 @@
|
|||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 60203,
|
"code": 60203,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": 199,
|
||||||
|
"id": 172,
|
||||||
|
"name": "icon-bar-graph",
|
||||||
|
"prevSize": 24,
|
||||||
|
"code": 60204,
|
||||||
|
"tempChar": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": 200,
|
||||||
|
"id": 171,
|
||||||
|
"name": "icon-map",
|
||||||
|
"prevSize": 24,
|
||||||
|
"code": 60205,
|
||||||
|
"tempChar": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 0,
|
"id": 0,
|
||||||
@ -3846,6 +3862,52 @@
|
|||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 172,
|
||||||
|
"paths": [
|
||||||
|
"M832 0h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM267.64 896h-139.64v-448h139.64zM477.1 896h-139.64v-768h139.64zM686.54 896h-139.64v-320h139.64zM896 896h-139.64v-640h139.64z"
|
||||||
|
],
|
||||||
|
"attrs": [
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"isMulticolor2": false,
|
||||||
|
"grid": 16,
|
||||||
|
"tags": [
|
||||||
|
"icon-bar-graph"
|
||||||
|
],
|
||||||
|
"colorPermutations": {
|
||||||
|
"12552552551": [
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 171,
|
||||||
|
"paths": [
|
||||||
|
"M896 65.4l-128 62.6v896l128-62.6c70.4-34.42 128-120.2 128-190.6v-640c0-70.4-57.6-99.82-128-65.4z",
|
||||||
|
"M320 912l387.2 96.8v-896l-387.2-96.8v896z",
|
||||||
|
"M259.2 0.8l-3.2-0.8-128 62.6c-70.4 34.42-128 120.2-128 190.6v640c0 70.4 57.6 99.82 128 65.4l128-62.6 3.2 0.8z"
|
||||||
|
],
|
||||||
|
"attrs": [
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"isMulticolor2": false,
|
||||||
|
"grid": 16,
|
||||||
|
"tags": [
|
||||||
|
"icon-map"
|
||||||
|
],
|
||||||
|
"colorPermutations": {
|
||||||
|
"12552552551": [
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"invisible": false,
|
"invisible": false,
|
||||||
|
@ -158,4 +158,6 @@
|
|||||||
<glyph unicode="" glyph-name="icon-alphanumeric" d="M535.6 301.4c-8.4-1.6-17.2-3-26.2-4s-18.2-2.4-27.2-4c-10.196-1.861-18.808-4.010-27.21-6.633l1.61 0.433c-8.609-2.674-16.105-6.348-22.89-10.987l0.29 0.187c-6.693-4.517-12.283-10.107-16.663-16.585l-0.137-0.215c-4.6-6.8-7.4-15.6-8.8-26s-0.4-18.4 2.4-25.2c2.746-6.688 7.224-12.195 12.881-16.122l0.119-0.078c5.967-4.053 13.057-6.94 20.704-8.161l0.296-0.039c7.592-1.527 16.319-2.4 25.25-2.4 0.123 0 0.246 0 0.369 0h-0.019c22.2 0 39.6 3.6 52.6 11s23.2 16.2 30.2 26.4c6.273 8.873 11.271 19.191 14.426 30.285l0.174 0.715c1.853 6.809 3.601 15.41 4.855 24.169l0.145 1.231 5.2 41.6c-5.4-4.217-11.723-7.564-18.583-9.689l-0.417-0.111c-6.489-2.241-14.362-4.255-22.444-5.662l-0.956-0.138zM1024 448v192h-152l24 192h-192l-24-192h-256l24 192h-192l-24-192h-232v-192h208l-32-256h-176v-192h152l-24-192h192l24 192h256l-24-192h192l24 192h232v192h-208l32 256zM702.8 420.2l-26.4-211.8c-2.231-15.809-3.537-34.122-3.6-52.727v-0.073c0-16.8 2.2-29.4 6.4-37.8h-113.4c-1.342 5.556-2.338 12.122-2.781 18.84l-0.019 0.36c-0.261 3.524-0.409 7.634-0.409 11.778 0 2.962 0.076 5.907 0.226 8.832l-0.017-0.41c-18.663-17.401-41.395-30.694-66.597-38.289l-1.203-0.311c-22.627-6.956-48.639-10.974-75.586-11h-0.014c-0.764-0.011-1.666-0.018-2.569-0.018-18.098 0-35.598 2.563-52.156 7.345l1.325-0.328c-15.991 4.512-29.851 12.090-41.545 22.122l0.145-0.122c-11.233 9.982-19.792 22.733-24.624 37.192l-0.176 0.608c-5.2 15.2-6.4 33.4-3.8 54.4s9.4 42.2 19.4 57.2c9.524 14.399 21.535 26.346 35.532 35.512l0.468 0.288c13.387 8.662 28.922 15.533 45.512 19.765l1.088 0.235c13.436 3.792 30.801 7.554 48.47 10.41l2.93 0.39c17 2.6 33.8 4.6 50.4 6.2 16.628 1.527 31.69 4.070 46.349 7.643l-2.149-0.443c13 3 23.6 7.6 31.6 13.6s12.6 15 13.6 26.4 0.8 21.8-2.4 28.8c-2.849 6.902-7.542 12.56-13.468 16.517l-0.132 0.083c-6.217 4.011-13.604 6.78-21.543 7.774l-0.257 0.026c-7.897 1.277-17 2.007-26.274 2.007-0.537 0-1.073-0.002-1.609-0.007l0.082 0.001c-22 0-40-4.6-53.8-14.2s-23-25.2-28-47.2h-111.8c4.8 26.2 14.2 48 27.8 65.4 13.475 16.978 29.89 30.968 48.574 41.377l0.826 0.423c18.192 10.038 39.297 17.806 61.619 22.175l1.381 0.225c20.488 4.162 44.053 6.563 68.171 6.6h0.029c21.8-0.005 43.239-1.532 64.222-4.479l-2.422 0.279c20.641-2.809 39.324-8.783 56.401-17.461l-1.001 0.461c15.909-8.108 28.858-20.031 37.967-34.601l0.233-0.399c9-15 12.2-34.8 9-59.6z" />
|
<glyph unicode="" glyph-name="icon-alphanumeric" d="M535.6 301.4c-8.4-1.6-17.2-3-26.2-4s-18.2-2.4-27.2-4c-10.196-1.861-18.808-4.010-27.21-6.633l1.61 0.433c-8.609-2.674-16.105-6.348-22.89-10.987l0.29 0.187c-6.693-4.517-12.283-10.107-16.663-16.585l-0.137-0.215c-4.6-6.8-7.4-15.6-8.8-26s-0.4-18.4 2.4-25.2c2.746-6.688 7.224-12.195 12.881-16.122l0.119-0.078c5.967-4.053 13.057-6.94 20.704-8.161l0.296-0.039c7.592-1.527 16.319-2.4 25.25-2.4 0.123 0 0.246 0 0.369 0h-0.019c22.2 0 39.6 3.6 52.6 11s23.2 16.2 30.2 26.4c6.273 8.873 11.271 19.191 14.426 30.285l0.174 0.715c1.853 6.809 3.601 15.41 4.855 24.169l0.145 1.231 5.2 41.6c-5.4-4.217-11.723-7.564-18.583-9.689l-0.417-0.111c-6.489-2.241-14.362-4.255-22.444-5.662l-0.956-0.138zM1024 448v192h-152l24 192h-192l-24-192h-256l24 192h-192l-24-192h-232v-192h208l-32-256h-176v-192h152l-24-192h192l24 192h256l-24-192h192l24 192h232v192h-208l32 256zM702.8 420.2l-26.4-211.8c-2.231-15.809-3.537-34.122-3.6-52.727v-0.073c0-16.8 2.2-29.4 6.4-37.8h-113.4c-1.342 5.556-2.338 12.122-2.781 18.84l-0.019 0.36c-0.261 3.524-0.409 7.634-0.409 11.778 0 2.962 0.076 5.907 0.226 8.832l-0.017-0.41c-18.663-17.401-41.395-30.694-66.597-38.289l-1.203-0.311c-22.627-6.956-48.639-10.974-75.586-11h-0.014c-0.764-0.011-1.666-0.018-2.569-0.018-18.098 0-35.598 2.563-52.156 7.345l1.325-0.328c-15.991 4.512-29.851 12.090-41.545 22.122l0.145-0.122c-11.233 9.982-19.792 22.733-24.624 37.192l-0.176 0.608c-5.2 15.2-6.4 33.4-3.8 54.4s9.4 42.2 19.4 57.2c9.524 14.399 21.535 26.346 35.532 35.512l0.468 0.288c13.387 8.662 28.922 15.533 45.512 19.765l1.088 0.235c13.436 3.792 30.801 7.554 48.47 10.41l2.93 0.39c17 2.6 33.8 4.6 50.4 6.2 16.628 1.527 31.69 4.070 46.349 7.643l-2.149-0.443c13 3 23.6 7.6 31.6 13.6s12.6 15 13.6 26.4 0.8 21.8-2.4 28.8c-2.849 6.902-7.542 12.56-13.468 16.517l-0.132 0.083c-6.217 4.011-13.604 6.78-21.543 7.774l-0.257 0.026c-7.897 1.277-17 2.007-26.274 2.007-0.537 0-1.073-0.002-1.609-0.007l0.082 0.001c-22 0-40-4.6-53.8-14.2s-23-25.2-28-47.2h-111.8c4.8 26.2 14.2 48 27.8 65.4 13.475 16.978 29.89 30.968 48.574 41.377l0.826 0.423c18.192 10.038 39.297 17.806 61.619 22.175l1.381 0.225c20.488 4.162 44.053 6.563 68.171 6.6h0.029c21.8-0.005 43.239-1.532 64.222-4.479l-2.422 0.279c20.641-2.809 39.324-8.783 56.401-17.461l-1.001 0.461c15.909-8.108 28.858-20.031 37.967-34.601l0.233-0.399c9-15 12.2-34.8 9-59.6z" />
|
||||||
<glyph unicode="" glyph-name="icon-image-telemetry" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 48.4c-69.581-69.675-165.757-112.776-272-112.776-212.298 0-384.4 172.102-384.4 384.4s172.102 384.4 384.4 384.4c212.298 0 384.4-172.102 384.4-384.4 0-0.008 0-0.017 0-0.025v0.001c0.001-0.264 0.001-0.575 0.001-0.887 0-105.769-42.964-201.503-112.391-270.703l-0.010-0.010zM704 448l-128-128-192 192-192-192c0-176.731 143.269-320 320-320s320 143.269 320 320v0z" />
|
<glyph unicode="" glyph-name="icon-image-telemetry" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 48.4c-69.581-69.675-165.757-112.776-272-112.776-212.298 0-384.4 172.102-384.4 384.4s172.102 384.4 384.4 384.4c212.298 0 384.4-172.102 384.4-384.4 0-0.008 0-0.017 0-0.025v0.001c0.001-0.264 0.001-0.575 0.001-0.887 0-105.769-42.964-201.503-112.391-270.703l-0.010-0.010zM704 448l-128-128-192 192-192-192c0-176.731 143.269-320 320-320s320 143.269 320 320v0z" />
|
||||||
<glyph unicode="" glyph-name="icon-telemetry-aggregate" d="M78 436.56c14 41.44 37.48 100.8 69.2 148.36 38.62 57.78 82.38 87.080 130.14 87.080s91.5-29.3 130-87.080c31.72-47.56 55.14-106.92 69.2-148.36 30.88-90.96 63.12-134.98 78-146.54 14.94 11.56 47.2 55.58 78 146.54 14 41.44 37.48 100.8 69.22 148.36q27.8 41.7 59.12 63.5c-75.7 111.377-201.81 183.58-344.783 183.58-0.034 0-0.068 0-0.103 0h0.006c-229.76 0-416-186.24-416-416 0-0.071 0-0.156 0-0.24 0-39.119 5.396-76.977 15.484-112.871l-0.704 2.931c16.78 21.74 40.4 63.34 63.22 130.74zM754 395.44c-14-41.44-37.48-100.8-69.2-148.36-38.56-57.78-82.32-87.080-130-87.080s-91.5 29.3-130 87.080c-31.72 47.56-55.14 106.92-69.2 148.36-30.88 90.96-63.14 134.98-78 146.54-14.94-11.56-47.2-55.58-78-146.54-14.38-41.44-37.8-100.8-69.6-148.36q-27.8-41.7-59.12-63.5c75.7-111.378 201.81-183.58 344.783-183.58 0.119 0 0.237 0 0.356 0h-0.019c229.76 0 416 186.24 416 416 0 0.071 0 0.156 0 0.24 0 39.119-5.396 76.977-15.484 112.871l0.704-2.931c-16.78-21.74-40.4-63.34-63.22-130.74zM921.56 497.38c4.098-24.449 6.44-52.617 6.44-81.332 0-0.017 0-0.034 0-0.051v0.003c0-0.095 0-0.208 0-0.32 0-282.593-229.087-511.68-511.68-511.68-0.113 0-0.225 0-0.338 0h0.018c-0.014 0-0.031 0-0.048 0-28.716 0-56.884 2.342-84.325 6.845l2.993-0.405c72.483-63.623 168.109-102.44 272.802-102.44 0.203 0 0.406 0 0.61 0h-0.031c229.76 0 416 186.24 416 416 0 0.172 0 0.375 0 0.578 0 104.692-38.817 200.319-102.844 273.271l0.404-0.47z" />
|
<glyph unicode="" glyph-name="icon-telemetry-aggregate" d="M78 436.56c14 41.44 37.48 100.8 69.2 148.36 38.62 57.78 82.38 87.080 130.14 87.080s91.5-29.3 130-87.080c31.72-47.56 55.14-106.92 69.2-148.36 30.88-90.96 63.12-134.98 78-146.54 14.94 11.56 47.2 55.58 78 146.54 14 41.44 37.48 100.8 69.22 148.36q27.8 41.7 59.12 63.5c-75.7 111.377-201.81 183.58-344.783 183.58-0.034 0-0.068 0-0.103 0h0.006c-229.76 0-416-186.24-416-416 0-0.071 0-0.156 0-0.24 0-39.119 5.396-76.977 15.484-112.871l-0.704 2.931c16.78 21.74 40.4 63.34 63.22 130.74zM754 395.44c-14-41.44-37.48-100.8-69.2-148.36-38.56-57.78-82.32-87.080-130-87.080s-91.5 29.3-130 87.080c-31.72 47.56-55.14 106.92-69.2 148.36-30.88 90.96-63.14 134.98-78 146.54-14.94-11.56-47.2-55.58-78-146.54-14.38-41.44-37.8-100.8-69.6-148.36q-27.8-41.7-59.12-63.5c75.7-111.378 201.81-183.58 344.783-183.58 0.119 0 0.237 0 0.356 0h-0.019c229.76 0 416 186.24 416 416 0 0.071 0 0.156 0 0.24 0 39.119-5.396 76.977-15.484 112.871l0.704-2.931c-16.78-21.74-40.4-63.34-63.22-130.74zM921.56 497.38c4.098-24.449 6.44-52.617 6.44-81.332 0-0.017 0-0.034 0-0.051v0.003c0-0.095 0-0.208 0-0.32 0-282.593-229.087-511.68-511.68-511.68-0.113 0-0.225 0-0.338 0h0.018c-0.014 0-0.031 0-0.048 0-28.716 0-56.884 2.342-84.325 6.845l2.993-0.405c72.483-63.623 168.109-102.44 272.802-102.44 0.203 0 0.406 0 0.61 0h-0.031c229.76 0 416 186.24 416 416 0 0.172 0 0.375 0 0.578 0 104.692-38.817 200.319-102.844 273.271l0.404-0.47z" />
|
||||||
|
<glyph unicode="" glyph-name="icon-bar-graph" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM267.64-64h-139.64v448h139.64zM477.1-64h-139.64v768h139.64zM686.54-64h-139.64v320h139.64zM896-64h-139.64v640h139.64z" />
|
||||||
|
<glyph unicode="" glyph-name="icon-map" d="M896 766.6l-128-62.6v-896l128 62.6c70.4 34.42 128 120.2 128 190.6v640c0 70.4-57.6 99.82-128 65.4zM320-80l387.2-96.8v896l-387.2 96.8v-896zM259.2 831.2l-3.2 0.8-128-62.6c-70.4-34.42-128-120.2-128-190.6v-640c0-70.4 57.6-99.82 128-65.4l128 62.6 3.2-0.8z" />
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
Binary file not shown.
Binary file not shown.
@ -16,7 +16,7 @@
|
|||||||
@import "../plugins/folderView/components/grid-view.scss";
|
@import "../plugins/folderView/components/grid-view.scss";
|
||||||
@import "../plugins/folderView/components/list-item.scss";
|
@import "../plugins/folderView/components/list-item.scss";
|
||||||
@import "../plugins/folderView/components/list-view.scss";
|
@import "../plugins/folderView/components/list-view.scss";
|
||||||
@import "../plugins/imagery/components/imagery-view-layout.scss";
|
@import "../plugins/imagery/components/imagery-view.scss";
|
||||||
@import "../plugins/imagery/components/Compass/compass.scss";
|
@import "../plugins/imagery/components/Compass/compass.scss";
|
||||||
@import "../plugins/telemetryTable/components/table-row.scss";
|
@import "../plugins/telemetryTable/components/table-row.scss";
|
||||||
@import "../plugins/telemetryTable/components/table-footer-indicator.scss";
|
@import "../plugins/telemetryTable/components/table-footer-indicator.scss";
|
||||||
|
@ -39,13 +39,7 @@ export function paramsToArray(openmct) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function identifierToString(openmct, objectPath) {
|
export function identifierToString(openmct, objectPath) {
|
||||||
let identifier = '#/browse/' + objectPath.map(function (o) {
|
return '#/browse/' + openmct.objects.getRelativePath(objectPath);
|
||||||
return o && openmct.objects.makeKeyString(o.identifier);
|
|
||||||
})
|
|
||||||
.reverse()
|
|
||||||
.join('/');
|
|
||||||
|
|
||||||
return identifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function objectPathToUrl(openmct, objectPath) {
|
export default function objectPathToUrl(openmct, objectPath) {
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-progress-bar">
|
<div class="c-progress-bar">
|
||||||
<div class="c-progress-bar__holder">
|
<div class="c-progress-bar__bar"
|
||||||
<div
|
:class="{ '--indeterminate': model.progressPerc === undefined }"
|
||||||
class="c-progress-bar__bar"
|
:style="styleBarWidth"
|
||||||
:class="{'--indeterminate': model.progressPerc === 'unknown'}"
|
></div>
|
||||||
:style="styleBarWidth"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="model.progressText !== undefined"
|
v-if="model.progressText !== undefined"
|
||||||
class="c-progress-bar__text"
|
class="c-progress-bar__text"
|
||||||
@ -27,7 +24,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
styleBarWidth() {
|
styleBarWidth() {
|
||||||
return `width: ${this.model.progressPerc}%;`;
|
return (this.model.progressPerc !== undefined) ? `width: ${this.model.progressPerc}%;` : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -122,8 +122,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
drawAxis(bounds, timeSystem) {
|
drawAxis(bounds, timeSystem) {
|
||||||
this.setScale(bounds, timeSystem);
|
let viewBounds = Object.assign({}, bounds);
|
||||||
this.setAxis(bounds);
|
|
||||||
|
this.setScale(viewBounds, timeSystem);
|
||||||
|
this.setAxis(viewBounds);
|
||||||
this.axisElement.call(this.xAxis);
|
this.axisElement.call(this.xAxis);
|
||||||
this.updateNowMarker();
|
this.updateNowMarker();
|
||||||
|
|
||||||
|
@ -1,43 +1,24 @@
|
|||||||
/******************************************************** PROGRESS BAR */
|
/******************************************************** PROGRESS BAR */
|
||||||
@keyframes progress {
|
@keyframes progressIndeterminate {
|
||||||
100% { background-position: $progressAnimW center; }
|
0% { left: 0; width: 0; }
|
||||||
}
|
70% { left: 0; width: 100%; opacity: 1; }
|
||||||
|
100% { left: 100%; opacity: 0; }
|
||||||
@mixin progressAnim($c1, $c2, $size) {
|
|
||||||
$edge: 20%;
|
|
||||||
background-image: linear-gradient(-90deg,
|
|
||||||
$c1 0%, $c2 $edge,
|
|
||||||
$c2 $edge, $c1 100%
|
|
||||||
);
|
|
||||||
background-position: 0 center;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
background-size: $size 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-progress-bar {
|
.c-progress-bar {
|
||||||
display: flex;
|
background: $colorProgressBarHolder;
|
||||||
flex-direction: column;
|
display: block;
|
||||||
|
min-height: $progressBarMinH;
|
||||||
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__holder {
|
|
||||||
background: $colorProgressBarHolder;
|
|
||||||
box-shadow: inset rgba(black, 0.4) 0 0.25px 3px;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
padding: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__bar {
|
&__bar {
|
||||||
@include progressAnim($colorProgressBar, lighten($colorProgressBar, 10%), $progressAnimW);
|
background: $colorProgressBar;
|
||||||
animation: progress 1000ms linear infinite;
|
|
||||||
min-height: $progressBarMinH;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
&.--indeterminate {
|
&.--indeterminate {
|
||||||
width: 100% !important;
|
position: absolute;
|
||||||
|
animation: progressIndeterminate 1.5s ease-in infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
:class="{'c-swimlane': !isNested}"
|
:class="{'c-swimlane': !isNested}"
|
||||||
>
|
>
|
||||||
|
|
||||||
<div class="c-swimlane__lane-label c-object-label"
|
<div v-if="hideLabel === false"
|
||||||
|
class="c-swimlane__lane-label c-object-label"
|
||||||
:class="{'c-swimlane__lane-label--span-cols': (!spanRowsCount && !isNested)}"
|
:class="{'c-swimlane__lane-label--span-cols': (!spanRowsCount && !isNested)}"
|
||||||
:style="gridRowSpan"
|
:style="gridRowSpan"
|
||||||
>
|
>
|
||||||
@ -49,6 +50,12 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
hideLabel: {
|
||||||
|
type: Boolean,
|
||||||
|
default() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
isNested: {
|
isNested: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default() {
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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>
|
<template>
|
||||||
<li
|
<li
|
||||||
draggable="true"
|
draggable="true"
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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>
|
<template>
|
||||||
<div class="c-elements-pool">
|
<div class="c-elements-pool">
|
||||||
<Search
|
<Search
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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>
|
<template>
|
||||||
<div class="c-inspector">
|
<div class="c-inspector">
|
||||||
<object-name />
|
<object-name />
|
||||||
@ -19,7 +41,9 @@
|
|||||||
type="vertical"
|
type="vertical"
|
||||||
>
|
>
|
||||||
<pane class="c-inspector__properties">
|
<pane class="c-inspector__properties">
|
||||||
<properties />
|
<Properties
|
||||||
|
v-if="!activity"
|
||||||
|
/>
|
||||||
<location />
|
<location />
|
||||||
<inspector-views />
|
<inspector-views />
|
||||||
</pane>
|
</pane>
|
||||||
@ -57,7 +81,7 @@ import multipane from '../layout/multipane.vue';
|
|||||||
import pane from '../layout/pane.vue';
|
import pane from '../layout/pane.vue';
|
||||||
import ElementsPool from './ElementsPool.vue';
|
import ElementsPool from './ElementsPool.vue';
|
||||||
import Location from './Location.vue';
|
import Location from './Location.vue';
|
||||||
import Properties from './Properties.vue';
|
import Properties from './details/Properties.vue';
|
||||||
import ObjectName from './ObjectName.vue';
|
import ObjectName from './ObjectName.vue';
|
||||||
import InspectorViews from './InspectorViews.vue';
|
import InspectorViews from './InspectorViews.vue';
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
@ -98,7 +122,8 @@ export default {
|
|||||||
key: '__styles',
|
key: '__styles',
|
||||||
name: 'Styles'
|
name: 'Styles'
|
||||||
}],
|
}],
|
||||||
currentTabbedView: {}
|
currentTabbedView: {},
|
||||||
|
activity: undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -111,9 +136,12 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
updateInspectorViews(selection) {
|
updateInspectorViews(selection) {
|
||||||
this.refreshComposition(selection);
|
this.refreshComposition(selection);
|
||||||
|
|
||||||
if (this.openmct.types.get('conditionSet')) {
|
if (this.openmct.types.get('conditionSet')) {
|
||||||
this.refreshTabs(selection);
|
this.refreshTabs(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setActivity(selection);
|
||||||
},
|
},
|
||||||
refreshComposition(selection) {
|
refreshComposition(selection) {
|
||||||
if (selection.length > 0 && selection[0].length > 0) {
|
if (selection.length > 0 && selection[0].length > 0) {
|
||||||
@ -150,6 +178,12 @@ export default {
|
|||||||
},
|
},
|
||||||
isCurrent(view) {
|
isCurrent(view) {
|
||||||
return _.isEqual(this.currentTabbedView, view);
|
return _.isEqual(this.currentTabbedView, view);
|
||||||
|
},
|
||||||
|
setActivity(selection) {
|
||||||
|
this.activity = selection
|
||||||
|
&& selection.length
|
||||||
|
&& selection[0].length
|
||||||
|
&& selection[0][0].activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
166
src/ui/inspector/InspectorDetailsSpec.js
Normal file
166
src/ui/inspector/InspectorDetailsSpec.js
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 {
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
const INSPECTOR_SELECTOR_PREFIX = '.c-inspect-properties__';
|
||||||
|
|
||||||
|
describe('the inspector', () => {
|
||||||
|
let appHolder;
|
||||||
|
let openmct;
|
||||||
|
let folderItem;
|
||||||
|
let selection;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
folderItem = {
|
||||||
|
name: 'folder',
|
||||||
|
type: 'folder',
|
||||||
|
id: 'mock-folder-key',
|
||||||
|
identifier: {
|
||||||
|
namespace: '',
|
||||||
|
key: 'mock-folder-key'
|
||||||
|
},
|
||||||
|
created: 1592851063871
|
||||||
|
};
|
||||||
|
|
||||||
|
selection = [
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
item: folderItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
appHolder = document.createElement('div');
|
||||||
|
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.start(appHolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays default details for selection', async () => {
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
const details = getDetails();
|
||||||
|
const [
|
||||||
|
title,
|
||||||
|
type,
|
||||||
|
timestamp
|
||||||
|
] = details;
|
||||||
|
|
||||||
|
expect(title.name)
|
||||||
|
.toEqual('Title');
|
||||||
|
expect(title.value.toLowerCase())
|
||||||
|
.toEqual(folderItem.name);
|
||||||
|
|
||||||
|
expect(type.name)
|
||||||
|
.toEqual('Type');
|
||||||
|
expect(type.value.toLowerCase())
|
||||||
|
.toEqual(folderItem.type);
|
||||||
|
|
||||||
|
expect(timestamp.name)
|
||||||
|
.toEqual('Created');
|
||||||
|
expect(new Date(timestamp.value).toString())
|
||||||
|
.toEqual(new Date(folderItem.created).toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('details show modified date', async () => {
|
||||||
|
const modifiedTimestamp = folderItem.created + 1000;
|
||||||
|
folderItem.modified = modifiedTimestamp;
|
||||||
|
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
const details = getDetails();
|
||||||
|
const timestamp = details[details.length - 1];
|
||||||
|
|
||||||
|
expect(timestamp.name)
|
||||||
|
.toEqual('Modified');
|
||||||
|
expect(new Date(timestamp.value).toString())
|
||||||
|
.toEqual(new Date(folderItem.modified).toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays custom details if provided through context', async () => {
|
||||||
|
const NAME_PREFIX = 'Custom Name';
|
||||||
|
const VALUE_PREFIX = 'Custom Value';
|
||||||
|
const indexes = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
const customDetails = indexes.map(index => {
|
||||||
|
return {
|
||||||
|
name: `${NAME_PREFIX} ${index}`,
|
||||||
|
value: `${VALUE_PREFIX} ${index}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
selection[0].context.details = customDetails;
|
||||||
|
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
const details = getDetails();
|
||||||
|
|
||||||
|
expect(details.length)
|
||||||
|
.toEqual(customDetails.length);
|
||||||
|
|
||||||
|
details.forEach((detail, index) => {
|
||||||
|
expect(detail.name)
|
||||||
|
.toEqual(customDetails[index].name);
|
||||||
|
expect(detail.value)
|
||||||
|
.toEqual(customDetails[index].value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getDetailsElements() {
|
||||||
|
const inspectorDetailsSection = appHolder
|
||||||
|
.querySelector(`${INSPECTOR_SELECTOR_PREFIX}section`);
|
||||||
|
const details = inspectorDetailsSection
|
||||||
|
.querySelectorAll(`${INSPECTOR_SELECTOR_PREFIX}row`);
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDetails() {
|
||||||
|
const detailsElements = getDetailsElements();
|
||||||
|
const details = Array.from(detailsElements)
|
||||||
|
.map(element => {
|
||||||
|
return {
|
||||||
|
name: getText(element, 'label'),
|
||||||
|
value: getText(element, 'value')
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getText(element, selectorSuffix) {
|
||||||
|
return element.querySelector(`${INSPECTOR_SELECTOR_PREFIX}${selectorSuffix}`)
|
||||||
|
.textContent.trim();
|
||||||
|
}
|
||||||
|
});
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 const mockTelemetryTableSelection = [
|
export const mockTelemetryTableSelection = [
|
||||||
[{
|
[{
|
||||||
context: {
|
context: {
|
||||||
|
@ -1,10 +1,29 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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>
|
<template>
|
||||||
<div></div>
|
<div></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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>
|
<template>
|
||||||
<div class="c-inspect-properties c-inspect-properties--location">
|
<div class="c-inspect-properties c-inspect-properties--location">
|
||||||
<div
|
<div
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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>
|
<template>
|
||||||
<div class="c-inspector__header">
|
<div class="c-inspector__header">
|
||||||
<div v-if="!multiSelect"
|
<div v-if="!multiSelect"
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-if="!activity"
|
|
||||||
class="c-inspector__properties c-inspect-properties"
|
|
||||||
>
|
|
||||||
<div class="c-inspect-properties__header">
|
|
||||||
Details
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
v-if="!multiSelect && !singleSelectNonObject"
|
|
||||||
class="c-inspect-properties__section"
|
|
||||||
>
|
|
||||||
<li class="c-inspect-properties__row">
|
|
||||||
<div class="c-inspect-properties__label">
|
|
||||||
Title
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-properties__value">
|
|
||||||
{{ item.name }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="c-inspect-properties__row">
|
|
||||||
<div class="c-inspect-properties__label">
|
|
||||||
Type
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-properties__value">
|
|
||||||
{{ typeName }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-if="item.created"
|
|
||||||
class="c-inspect-properties__row"
|
|
||||||
>
|
|
||||||
<div class="c-inspect-properties__label">
|
|
||||||
Created
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-properties__value">
|
|
||||||
{{ formatTime(item.created) }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-if="item.modified"
|
|
||||||
class="c-inspect-properties__row"
|
|
||||||
>
|
|
||||||
<div class="c-inspect-properties__label">
|
|
||||||
Modified
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-properties__value">
|
|
||||||
{{ formatTime(item.modified) }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-for="prop in typeProperties"
|
|
||||||
:key="prop.name"
|
|
||||||
class="c-inspect-properties__row"
|
|
||||||
>
|
|
||||||
<div class="c-inspect-properties__label">
|
|
||||||
{{ prop.name }}
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-properties__value">
|
|
||||||
{{ prop.value }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div
|
|
||||||
v-if="multiSelect"
|
|
||||||
class="c-inspect-properties__row--span-all"
|
|
||||||
>
|
|
||||||
No properties to display for multiple items
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="singleSelectNonObject"
|
|
||||||
class="c-inspect-properties__row--span-all"
|
|
||||||
>
|
|
||||||
No properties to display for this item
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Moment from "moment";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
inject: ['openmct'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
domainObject: {},
|
|
||||||
activity: undefined,
|
|
||||||
multiSelect: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
item() {
|
|
||||||
return this.domainObject || {};
|
|
||||||
},
|
|
||||||
type() {
|
|
||||||
return this.openmct.types.get(this.item.type);
|
|
||||||
},
|
|
||||||
typeName() {
|
|
||||||
if (!this.type) {
|
|
||||||
return `Unknown: ${this.item.type}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.type.definition.name;
|
|
||||||
},
|
|
||||||
typeProperties() {
|
|
||||||
if (!this.type) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let definition = this.type.definition;
|
|
||||||
if (!definition.form || definition.form.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return definition.form
|
|
||||||
.map((field) => {
|
|
||||||
let path = field.property;
|
|
||||||
if (typeof path === 'string') {
|
|
||||||
path = [path];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: field.name,
|
|
||||||
path
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(field => Array.isArray(field.path))
|
|
||||||
.map((field) => {
|
|
||||||
return {
|
|
||||||
name: field.name,
|
|
||||||
value: field.path.reduce((object, key) => {
|
|
||||||
return object[key];
|
|
||||||
}, this.item)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
singleSelectNonObject() {
|
|
||||||
return !this.item.identifier && !this.multiSelect;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.openmct.selection.on('change', this.updateSelection);
|
|
||||||
this.updateSelection(this.openmct.selection.get());
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.openmct.selection.off('change', this.updateSelection);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateSelection(selection) {
|
|
||||||
if (selection.length === 0 || selection[0].length === 0) {
|
|
||||||
this.domainObject = {};
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selection.length > 1) {
|
|
||||||
this.multiSelect = true;
|
|
||||||
this.domainObject = {};
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.multiSelect = false;
|
|
||||||
this.domainObject = selection[0][0].context.item;
|
|
||||||
this.activity = selection[0][0].context.activity;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
formatTime(unixTime) {
|
|
||||||
return Moment.utc(unixTime).format('YYYY-MM-DD[\n]HH:mm:ss') + ' UTC';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -20,22 +20,24 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
export default class ClearDataAction {
|
<template>
|
||||||
constructor(openmct, appliesToObjects) {
|
<li class="c-inspect-properties__row">
|
||||||
this.name = 'Clear Data for Object';
|
<div class="c-inspect-properties__label">
|
||||||
this.key = 'clear-data-action';
|
{{ detail.name }}
|
||||||
this.description = 'Clears current data for object, unsubscribes and resubscribes to data';
|
</div>
|
||||||
this.cssClass = 'icon-clear-data';
|
<div class="c-inspect-properties__value">
|
||||||
|
{{ detail.value }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
this._openmct = openmct;
|
<script>
|
||||||
this._appliesToObjects = appliesToObjects;
|
export default {
|
||||||
|
props: {
|
||||||
|
detail: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
invoke(objectPath) {
|
};
|
||||||
this._openmct.objectViews.emit('clearData', objectPath[0]);
|
</script>
|
||||||
}
|
|
||||||
appliesTo(objectPath) {
|
|
||||||
let contextualDomainObject = objectPath[0];
|
|
||||||
|
|
||||||
return this._appliesToObjects.filter(type => contextualDomainObject.type === type).length;
|
|
||||||
}
|
|
||||||
}
|
|
201
src/ui/inspector/details/Properties.vue
Normal file
201
src/ui/inspector/details/Properties.vue
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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-inspector__properties c-inspect-properties">
|
||||||
|
<div class="c-inspect-properties__header">
|
||||||
|
Details
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
v-if="hasDetails"
|
||||||
|
class="c-inspect-properties__section"
|
||||||
|
>
|
||||||
|
<Component
|
||||||
|
:is="getComponent(detail)"
|
||||||
|
v-for="detail in details"
|
||||||
|
:key="detail.name"
|
||||||
|
:detail="detail"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="c-inspect-properties__row--span-all"
|
||||||
|
>
|
||||||
|
{{ noDetailsMessage }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Moment from 'moment';
|
||||||
|
import DetailText from './DetailText.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
DetailText
|
||||||
|
},
|
||||||
|
inject: ['openmct'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selection: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
details() {
|
||||||
|
return this.customDetails ? this.customDetails : this.domainObjectDetails;
|
||||||
|
},
|
||||||
|
customDetails() {
|
||||||
|
if (this.context === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.context.details;
|
||||||
|
},
|
||||||
|
domainObject() {
|
||||||
|
if (this.context === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.context.item;
|
||||||
|
},
|
||||||
|
type() {
|
||||||
|
if (this.domainObject === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.openmct.types.get(this.domainObject.type);
|
||||||
|
},
|
||||||
|
domainObjectDetails() {
|
||||||
|
if (this.domainObject === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = this.domainObject.name;
|
||||||
|
const typeName = this.type ? this.type.definition.name : `Unknown: ${this.domainObject.type}`;
|
||||||
|
const timestampLabel = this.domainObject.modified ? 'Modified' : 'Created';
|
||||||
|
const timestamp = this.domainObject.modified ? this.domainObject.modified : this.domainObject.created;
|
||||||
|
|
||||||
|
const details = [
|
||||||
|
{
|
||||||
|
name: 'Title',
|
||||||
|
value: title
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Type',
|
||||||
|
value: typeName
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (timestamp !== undefined) {
|
||||||
|
const formattedTimestamp = Moment.utc(timestamp)
|
||||||
|
.format('YYYY-MM-DD[\n]HH:mm:ss')
|
||||||
|
+ ' UTC';
|
||||||
|
|
||||||
|
details.push(
|
||||||
|
{
|
||||||
|
name: timestampLabel,
|
||||||
|
value: formattedTimestamp
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...details, ...this.typeProperties];
|
||||||
|
},
|
||||||
|
context() {
|
||||||
|
if (
|
||||||
|
!this.selection
|
||||||
|
|| !this.selection.length
|
||||||
|
|| !this.selection[0].length
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.selection[0][0].context;
|
||||||
|
},
|
||||||
|
hasDetails() {
|
||||||
|
return Boolean(
|
||||||
|
this.details
|
||||||
|
&& this.details.length
|
||||||
|
&& !this.multiSelection
|
||||||
|
);
|
||||||
|
},
|
||||||
|
multiSelection() {
|
||||||
|
return this.selection && this.selection.length > 1;
|
||||||
|
},
|
||||||
|
noDetailsMessage() {
|
||||||
|
return this.multiSelection
|
||||||
|
? 'No properties to display for multiple items'
|
||||||
|
: 'No properties to display for this item';
|
||||||
|
},
|
||||||
|
typeProperties() {
|
||||||
|
if (!this.type) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let definition = this.type.definition;
|
||||||
|
if (!definition.form || definition.form.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition.form
|
||||||
|
.map((field) => {
|
||||||
|
let path = field.property;
|
||||||
|
if (typeof path === 'string') {
|
||||||
|
path = [path];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: field.name,
|
||||||
|
path
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(field => Array.isArray(field.path))
|
||||||
|
.map((field) => {
|
||||||
|
return {
|
||||||
|
name: field.name,
|
||||||
|
value: field.path.reduce((object, key) => {
|
||||||
|
return object[key];
|
||||||
|
}, this.domainObject)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.selection.on('change', this.updateSelection);
|
||||||
|
this.updateSelection(this.openmct.selection.get());
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.openmct.selection.off('change', this.updateSelection);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getComponent(detail) {
|
||||||
|
const component = detail.component ? detail.component : 'text';
|
||||||
|
|
||||||
|
return `detail-${component}`;
|
||||||
|
},
|
||||||
|
updateSelection(selection) {
|
||||||
|
this.selection = selection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -53,7 +53,7 @@
|
|||||||
class="l-shell__pane-tree"
|
class="l-shell__pane-tree"
|
||||||
handle="after"
|
handle="after"
|
||||||
label="Browse"
|
label="Browse"
|
||||||
collapsable
|
hide-param="hideTree"
|
||||||
@start-resizing="onStartResizing"
|
@start-resizing="onStartResizing"
|
||||||
@end-resizing="onEndResizing"
|
@end-resizing="onEndResizing"
|
||||||
>
|
>
|
||||||
@ -104,7 +104,7 @@
|
|||||||
class="l-shell__pane-inspector l-pane--holds-multipane"
|
class="l-shell__pane-inspector l-pane--holds-multipane"
|
||||||
handle="before"
|
handle="before"
|
||||||
label="Inspect"
|
label="Inspect"
|
||||||
collapsable
|
hide-param="hideInspector"
|
||||||
@start-resizing="onStartResizing"
|
@start-resizing="onStartResizing"
|
||||||
@end-resizing="onEndResizing"
|
@end-resizing="onEndResizing"
|
||||||
>
|
>
|
||||||
|
171
src/ui/layout/LayoutSpec.js
Normal file
171
src/ui/layout/LayoutSpec.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, 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 {
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Layout from './Layout.vue';
|
||||||
|
|
||||||
|
describe('Open MCT Layout:', () => {
|
||||||
|
let openmct;
|
||||||
|
let element;
|
||||||
|
let components;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.on('start', done);
|
||||||
|
|
||||||
|
// to silence error from BrowseBar.vue
|
||||||
|
spyOn(openmct.objectViews, 'get')
|
||||||
|
.and.callFake(() => []);
|
||||||
|
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('the pane:', () => {
|
||||||
|
it('is displayed on layout load', async () => {
|
||||||
|
await createLayout();
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
expect(
|
||||||
|
component.pane
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isCollapsed(component.pane)
|
||||||
|
).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is collapsed on layout load if specified by a hide param', async () => {
|
||||||
|
setHideParams();
|
||||||
|
|
||||||
|
await createLayout();
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
expect(
|
||||||
|
isCollapsed(component.pane)
|
||||||
|
).toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('on toggle collapses if expanded', async () => {
|
||||||
|
await createLayout();
|
||||||
|
toggleCollapseButtons();
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
expect(
|
||||||
|
openmct.router.getSearchParam(component.param)
|
||||||
|
).toEqual('true');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isCollapsed(component.pane)
|
||||||
|
).toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('on toggle expands if collapsed', async () => {
|
||||||
|
setHideParams();
|
||||||
|
|
||||||
|
await createLayout();
|
||||||
|
toggleExpandButtons();
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
expect(
|
||||||
|
openmct.router.getSearchParam(component.param)
|
||||||
|
).not.toEqual('true');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isCollapsed(component.pane)
|
||||||
|
).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function createLayout() {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
const child = document.createElement('div');
|
||||||
|
el.appendChild(child);
|
||||||
|
|
||||||
|
element = await new Vue({
|
||||||
|
el,
|
||||||
|
components: {
|
||||||
|
Layout
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct
|
||||||
|
},
|
||||||
|
template: `<Layout ref="layout"/>`
|
||||||
|
}).$mount().$el;
|
||||||
|
|
||||||
|
setComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setComponents() {
|
||||||
|
components = {
|
||||||
|
tree: {
|
||||||
|
param: 'hideTree',
|
||||||
|
pane: element.querySelector('.l-shell__pane-tree'),
|
||||||
|
collapseButton: element.querySelector('.l-shell__pane-tree .l-pane__collapse-button'),
|
||||||
|
expandButton: element.querySelector('.l-shell__pane-tree .l-pane__expand-button')
|
||||||
|
},
|
||||||
|
inspector: {
|
||||||
|
param: 'hideInspector',
|
||||||
|
pane: element.querySelector('.l-shell__pane-inspector'),
|
||||||
|
collapseButton: element.querySelector('.l-shell__pane-inspector .l-pane__collapse-button'),
|
||||||
|
expandButton: element.querySelector('.l-shell__pane-inspector .l-pane__expand-button')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCollapsed(el) {
|
||||||
|
return el.classList.contains('l-pane--collapsed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHideParams() {
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
openmct.router.setSearchParam(component.param, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCollapseButtons() {
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
component.collapseButton.click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleExpandButtons() {
|
||||||
|
Object.entries(components).forEach(([name, component]) => {
|
||||||
|
component.expandButton.click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -41,10 +41,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const COLLAPSE_THRESHOLD_PX = 40;
|
const COLLAPSE_THRESHOLD_PX = 40;
|
||||||
const HIDE_TREE_PARAM = 'hideTree';
|
|
||||||
const HIDE_INSPECTOR_PARAM = 'hideInspector';
|
|
||||||
const PANE_INSPECTOR = 'Inspect';
|
|
||||||
const PANE_TREE = 'Browse';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
@ -56,13 +52,13 @@ export default {
|
|||||||
return ['', 'before', 'after'].indexOf(value) !== -1;
|
return ['', 'before', 'after'].indexOf(value) !== -1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
collapsable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
hideParam: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -71,6 +67,11 @@ export default {
|
|||||||
resizing: false
|
resizing: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
collapsable() {
|
||||||
|
return this.hideParam && this.hideParam.length;
|
||||||
|
}
|
||||||
|
},
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.type = this.$parent.type;
|
this.type = this.$parent.type;
|
||||||
this.styleProp = (this.type === 'horizontal') ? 'width' : 'height';
|
this.styleProp = (this.type === 'horizontal') ? 'width' : 'height';
|
||||||
@ -78,39 +79,25 @@ export default {
|
|||||||
async mounted() {
|
async mounted() {
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
// Hide tree and/or inspector pane if specified in URL
|
// Hide tree and/or inspector pane if specified in URL
|
||||||
this.handleHideUrl();
|
if (this.collapsable) {
|
||||||
this.openmct.router.on('change:params', this.handleHideUrl);
|
this.handleHideUrl();
|
||||||
},
|
}
|
||||||
beforeDestroy() {
|
|
||||||
this.openmct.router.off('change:params', this.handleHideUrl);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleCollapse: function (e) {
|
toggleCollapse: function (e) {
|
||||||
let target = this.label === PANE_TREE ? HIDE_TREE_PARAM : HIDE_INSPECTOR_PARAM;
|
|
||||||
this.collapsed = !this.collapsed;
|
|
||||||
if (this.collapsed) {
|
if (this.collapsed) {
|
||||||
this.handleCollapse();
|
|
||||||
this.addHideParam(target);
|
|
||||||
} else {
|
|
||||||
this.handleExpand();
|
this.handleExpand();
|
||||||
this.removeHideParam(target);
|
this.removeHideParam(this.hideParam);
|
||||||
|
} else {
|
||||||
|
this.handleCollapse();
|
||||||
|
this.addHideParam(this.hideParam);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleHideUrl: function () {
|
handleHideUrl: function () {
|
||||||
if (!this.collapsable) {
|
const hideParam = this.openmct.router.getSearchParam(this.hideParam);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hideTreeParam = this.openmct.router.getSearchParam(HIDE_TREE_PARAM);
|
if (hideParam === 'true') {
|
||||||
let hideInspectorParam = this.openmct.router.getSearchParam(HIDE_INSPECTOR_PARAM);
|
|
||||||
let hideTree = hideTreeParam === 'true' && this.label === PANE_TREE;
|
|
||||||
let hideInspector = hideInspectorParam === 'true' && this.label === PANE_INSPECTOR;
|
|
||||||
if (hideTree || hideInspector) {
|
|
||||||
this.collapsed = true;
|
|
||||||
this.handleCollapse();
|
this.handleCollapse();
|
||||||
} else {
|
|
||||||
this.collapsed = false;
|
|
||||||
this.handleExpand();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addHideParam: function (target) {
|
addHideParam: function (target) {
|
||||||
@ -122,11 +109,13 @@ export default {
|
|||||||
handleCollapse: function () {
|
handleCollapse: function () {
|
||||||
this.currentSize = (this.dragCollapse === true) ? this.initial : this.$el.style[this.styleProp];
|
this.currentSize = (this.dragCollapse === true) ? this.initial : this.$el.style[this.styleProp];
|
||||||
this.$el.style[this.styleProp] = '';
|
this.$el.style[this.styleProp] = '';
|
||||||
|
this.collapsed = true;
|
||||||
},
|
},
|
||||||
handleExpand: function () {
|
handleExpand: function () {
|
||||||
this.$el.style[this.styleProp] = this.currentSize;
|
this.$el.style[this.styleProp] = this.currentSize;
|
||||||
delete this.currentSize;
|
delete this.currentSize;
|
||||||
delete this.dragCollapse;
|
delete this.dragCollapse;
|
||||||
|
this.collapsed = false;
|
||||||
},
|
},
|
||||||
trackSize: function () {
|
trackSize: function () {
|
||||||
if (!this.dragCollapse === true) {
|
if (!this.dragCollapse === true) {
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2021, 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 {
|
|
||||||
createOpenMct,
|
|
||||||
resetApplicationState
|
|
||||||
} from 'utils/testing';
|
|
||||||
|
|
||||||
describe("the pane", () => {
|
|
||||||
let openmct;
|
|
||||||
let appHolder;
|
|
||||||
let element;
|
|
||||||
let child;
|
|
||||||
let resolveFunction;
|
|
||||||
|
|
||||||
beforeEach((done) => {
|
|
||||||
openmct = createOpenMct();
|
|
||||||
|
|
||||||
appHolder = document.createElement('div');
|
|
||||||
appHolder.style.width = '640px';
|
|
||||||
appHolder.style.height = '480px';
|
|
||||||
|
|
||||||
openmct = createOpenMct();
|
|
||||||
openmct.install(openmct.plugins.MyItems());
|
|
||||||
openmct.install(openmct.plugins.LocalTimeSystem());
|
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
|
||||||
|
|
||||||
element = document.createElement('div');
|
|
||||||
child = document.createElement('div');
|
|
||||||
element.appendChild(child);
|
|
||||||
|
|
||||||
openmct.on('start', done);
|
|
||||||
openmct.start(appHolder);
|
|
||||||
|
|
||||||
document.body.append(appHolder);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
return resetApplicationState(openmct);
|
|
||||||
});
|
|
||||||
it('toggling tree will toggle tree hide params', (done) => {
|
|
||||||
document.querySelector('.l-shell__pane-tree .l-pane__collapse-button').click();
|
|
||||||
expect(openmct.router.getSearchParam('hideTree')).toBe('true');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tree pane collapses when adding hide tree param in URL', () => {
|
|
||||||
openmct.router.setSearchParam('hideTree', 'true');
|
|
||||||
expect(document.querySelector('.l-shell__pane-tree.l-pane--collapsed')).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('inspector pane collapses when adding hide inspector param in URL', () => {
|
|
||||||
openmct.router.setSearchParam('hideInspector', 'true');
|
|
||||||
expect(document.querySelector('.l-shell__pane-inspector.l-pane--collapsed')).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('toggle inspector pane will toggle inspector hide param', (done) => {
|
|
||||||
// There's a short delay on addubg the param.
|
|
||||||
resolveFunction = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
expect(openmct.router.getSearchParam('hideInspector')).toBe('true');
|
|
||||||
done();
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
openmct.router.on('change:params', resolveFunction);
|
|
||||||
document.querySelector('.l-shell__pane-inspector .l-pane__collapse-button').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -57,7 +57,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__progress-bar {
|
&__progress-bar {
|
||||||
flex: 0 0 auto;
|
height: 7px;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
|
|
||||||
// Only show the progress bar
|
// Only show the progress bar
|
||||||
|
@ -59,12 +59,13 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.views = this.openmct.objectViews.get(this.domainObject, this.objectPath).map((view) => {
|
this.views = this.openmct.objectViews.get(this.domainObject, this.objectPath).map((view) => {
|
||||||
view.callBack = () => {
|
view.onItemClicked = () => {
|
||||||
return this.setView(view);
|
return this.setView(view);
|
||||||
};
|
};
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setView(this.views[0]);
|
this.setView(this.views[0]);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
@ -60,6 +60,7 @@ export default class PreviewAction {
|
|||||||
let overlay = this._openmct.overlays.overlay({
|
let overlay = this._openmct.overlays.overlay({
|
||||||
element: preview.$el,
|
element: preview.$el,
|
||||||
size: 'large',
|
size: 'large',
|
||||||
|
autoHide: false,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'Done',
|
label: 'Done',
|
||||||
@ -80,17 +81,10 @@ export default class PreviewAction {
|
|||||||
const isObjectView = parentElement && parentElement.classList.contains('js-object-view');
|
const isObjectView = parentElement && parentElement.classList.contains('js-object-view');
|
||||||
|
|
||||||
return !PreviewAction.isVisible
|
return !PreviewAction.isVisible
|
||||||
&& !this._isNavigatedObject(objectPath)
|
&& !this._openmct.router.isNavigatedObject(objectPath)
|
||||||
&& !isObjectView;
|
&& !isObjectView;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isNavigatedObject(objectPath) {
|
|
||||||
let targetObject = objectPath[0];
|
|
||||||
let navigatedObject = this._openmct.router.path[0];
|
|
||||||
|
|
||||||
return this._openmct.objects.areIdsEqual(targetObject.identifier, navigatedObject.identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
_preventPreview(objectPath) {
|
_preventPreview(objectPath) {
|
||||||
const noPreviewTypes = ['folder'];
|
const noPreviewTypes = ['folder'];
|
||||||
|
|
||||||
|
@ -18,6 +18,12 @@
|
|||||||
:views="views"
|
:views="views"
|
||||||
:current-view="currentView"
|
:current-view="currentView"
|
||||||
/>
|
/>
|
||||||
|
<NotebookMenuSwitcher :domain-object="domainObject"
|
||||||
|
:object-path="objectPath"
|
||||||
|
:is-preview="true"
|
||||||
|
:current-view="currentView"
|
||||||
|
class="c-notebook-snapshot-menubutton"
|
||||||
|
/>
|
||||||
<div class="l-browse-bar__actions">
|
<div class="l-browse-bar__actions">
|
||||||
<button
|
<button
|
||||||
v-for="(item, index) in statusBarItems"
|
v-for="(item, index) in statusBarItems"
|
||||||
@ -38,7 +44,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwitcher.vue';
|
||||||
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
|
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
|
||||||
|
|
||||||
const HIDDEN_ACTIONS = [
|
const HIDDEN_ACTIONS = [
|
||||||
'remove',
|
'remove',
|
||||||
'move',
|
'move',
|
||||||
@ -48,10 +56,12 @@ const HIDDEN_ACTIONS = [
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
NotebookMenuSwitcher,
|
||||||
ViewSwitcher
|
ViewSwitcher
|
||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'openmct'
|
'openmct',
|
||||||
|
'objectPath'
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
currentView: {
|
currentView: {
|
||||||
@ -143,6 +153,7 @@ export default {
|
|||||||
showMenuItems(event) {
|
showMenuItems(event) {
|
||||||
let sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
|
let sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
|
||||||
const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view);
|
const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view);
|
||||||
|
|
||||||
const visibleMenuItems = this.filterHiddenItems(menuItems);
|
const visibleMenuItems = this.filterHiddenItems(menuItems);
|
||||||
this.openmct.menus.showMenu(event.x, event.y, visibleMenuItems);
|
this.openmct.menus.showMenu(event.x, event.y, visibleMenuItems);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,13 @@ class ApplicationRouter extends EventEmitter {
|
|||||||
this.handleLocationChange(hash.substring(1));
|
this.handleLocationChange(hash.substring(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isNavigatedObject(objectPath) {
|
||||||
|
let targetObject = objectPath[0];
|
||||||
|
let navigatedObject = this.path[0];
|
||||||
|
|
||||||
|
return this.openmct.objects.areIdsEqual(targetObject.identifier, navigatedObject.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add routes listeners
|
* Add routes listeners
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user