Compare commits

...

40 Commits

Author SHA1 Message Date
29977f8eb2 slicing the added array by a size option if one is passed in 2022-06-30 12:01:53 -04:00
f404c4022b fix typo 2022-04-14 11:19:13 -07:00
bf3cad3f6e fix missing y-axis label
fix units missing from plot legend
2022-04-14 11:05:43 -07:00
8780cc33d0 fix lint errors 2022-04-12 14:20:25 -07:00
dc62e56279 use merge sort instead of insertion sort 2022-04-12 13:57:51 -07:00
1c794bdece De-reactify tables 2022-04-11 14:26:01 -07:00
052ec19892 remove form element if not dialog 2022-03-27 20:08:34 -07:00
5f70ab7dd3 allow hiding cancel button 2022-03-25 19:14:54 -07:00
7f73e1e765 safety check for overlay 2022-03-25 19:14:40 -07:00
ea7cf080c3 only show required fields message if form has required fields 2022-03-25 14:01:10 -07:00
9b0762f201 all custom labels 2022-03-25 12:59:14 -07:00
31aa291672 add checkbox
(merged to master after this branch was cut)
2022-03-25 11:08:14 -07:00
65e8d8b23c warn should be alert 2022-03-22 22:43:52 -07:00
fdb35094f4 fix enum issue 2022-03-22 22:05:48 -07:00
3b99a12d7f use domain.source for sorting
do not add telemetry for incorrect timesystems
2022-03-22 10:54:45 -07:00
25ee2d8099 hack for vista timesystems 2022-03-16 10:29:37 -07:00
c14cc25977 clean up observers 2022-03-16 10:27:01 -07:00
50997270e9 separate name and composition listeners 2022-03-16 10:11:20 -07:00
67e3094c6c warn instead of error on missing timesystem so vista still functional 2022-03-14 16:10:18 -07:00
526e31d10c more identifier fixes to search 2022-03-14 16:01:23 -07:00
3b316ed491 fix identifier issues 2022-03-14 13:53:33 -07:00
282ead581a object get in onWorkerMessage call should use identifier 2022-03-14 11:21:04 -07:00
c4f18a4797 object get in indexing process should use identifier not key 2022-03-10 14:27:22 -08:00
c273e83093 debug for search 2022-03-10 14:26:40 -08:00
e4b9242864 hack to parse identifier if namespace and key was mashed 2022-03-01 16:10:53 -08:00
ceddadcac6 fix canvas fallback in plots
https://github.com/nasa/openmct/pull/4784
2022-01-26 14:27:33 -08:00
6e2437b09e fix navigate after create path 2022-01-18 11:19:35 -08:00
5a44931537 default to string not undefined 2022-01-13 17:15:24 -08:00
f165d9c064 Revert "bring back identifierService for getProvider"
This reverts commit 830f321f90.
2022-01-13 17:08:08 -08:00
613973d936 set namespace for malformed identifiers 2022-01-13 17:06:24 -08:00
830f321f90 bring back identifierService for getProvider 2022-01-13 14:16:28 -08:00
a14cd62878 Merge branch 'release/1.8.3' into vista-r4.9-release 2022-01-10 13:49:05 -08:00
8314d03af5 Fix Link Action to accept the right input for validation (#4696) 2022-01-07 14:46:31 -08:00
187da3c462 Snapshot notice link not navigating as expected #4194 (#4686) 2022-01-07 11:51:11 -08:00
e4f134ca59 Mct 4555 rebased v3 (#4689)
* Preserve the previousFocusedImage for subscription updates, bound change for local and fixed time

* Only preserve previous focused image if paused

* Forcibly reset imageContainer size to prevent aspect ratio distortion

* Remove unneccesary mixin invocation

* Use image history instead of imagehistory size for watcher. Revert other changes

* Added check if last image index is selected

* isPaused instead of paused

Co-authored-by: Michael Rogers <contact@mhrogers.com>
2022-01-07 11:09:09 -06:00
76829ad252 Check for ref when trying to get the style receiver. (#4687)
Also ensure that the property being displayed in Properties is actually a part of the domainObject.
2022-01-07 10:57:10 -06:00
a8da0d5917 Notebook Snapshotting to the default Notebook isn't working (#4475)
* Notebook Snapshotting to the default Notebook isn't working #4469
2022-01-06 16:44:21 -08:00
488beb5b3f new colors show better on espresso and snow themes 2022-01-06 11:25:33 -08:00
2f63718385 use relative path (#4683) 2022-01-06 11:14:21 -08:00
433f1bf28e Fix object creation (#4675)
* Save the object before adding it to the parent so that transaction committing works properly
* Fix object creation - composition policy changes
2022-01-05 11:05:02 -08:00
30 changed files with 452 additions and 220 deletions

View File

@ -1,5 +1,6 @@
import AutoCompleteField from './components/controls/AutoCompleteField.vue';
import ClockDisplayFormatField from './components/controls/ClockDisplayFormatField.vue';
import CheckBoxField from './components/controls/CheckBoxField.vue';
import Datetime from './components/controls/Datetime.vue';
import FileInput from './components/controls/FileInput.vue';
import Locator from './components/controls/Locator.vue';
@ -12,6 +13,7 @@ import Vue from 'vue';
export const DEFAULT_CONTROLS_MAP = {
'autocomplete': AutoCompleteField,
'checkbox': CheckBoxField,
'composite': ClockDisplayFormatField,
'datetime': Datetime,
'file-input': FileInput,

View File

@ -172,7 +172,11 @@ export default class FormsAPI {
function onFormSave(save) {
return () => {
overlay.dismiss();
if (element) {
formElement.remove();
} else {
overlay.dismiss();
}
if (save) {
save(changes);

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -24,49 +24,60 @@
<div class="c-form">
<div class="c-overlay__top-bar c-form__top-bar">
<div class="c-overlay__dialog-title">{{ model.title }}</div>
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
<div
v-if="hasRequiredFields"
class="c-overlay__dialog-hint hint"
>All fields marked <span class="req icon-asterisk"></span> are required.</div>
</div>
<form name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
<form
name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
>
<div v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
<div
v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
>
<h2 v-if="section.name"
<h2
v-if="section.name"
class="c-form__section-header"
>
{{ section.name }}
</h2>
<div v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
<div
v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
>
<FormRow :css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
<FormRow
:css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
/>
</div>
</div>
</form>
<div class="mct-form__controls c-overlay__button-bar c-form__bottom-bar">
<button tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
<button
tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
>
OK
{{ submitLabel }}
</button>
<button tabindex="0"
class="c-button"
@click="onDismiss"
<button
v-if="!hideCancel"
tabindex="0"
class="c-button"
@click="onDismiss"
>
Cancel
{{ cancelLabel }}
</button>
</div>
</div>
@ -100,11 +111,42 @@ export default {
};
},
computed: {
hasRequiredFields() {
return this.model.sections.some(section =>
section.rows.some(row => row.required));
},
isInvalid() {
return Object.entries(this.invalidProperties)
.some(([key, value]) => {
return value;
});
},
submitLabel() {
if (
this.model.buttons
&& this.model.buttons.submit
&& this.model.buttons.submit.label
) {
return this.model.buttons.submit.label;
}
return 'OK';
},
cancelLabel() {
if (
this.model.buttons
&& this.model.buttons.cancel
&& this.model.buttons.cancel.label
) {
return this.model.buttons.submit.label;
}
return 'Cancel';
},
hideCancel() {
return this.model.buttons
&& this.model.buttons.cancel
&& this.model.buttons.cancel.hide === true;
}
},
mounted() {

View File

@ -75,10 +75,12 @@ export default {
rowClass() {
let cssClass = this.cssClass;
if (this.row.required) {
cssClass = `${cssClass} req`;
if (!this.row.required) {
return;
}
cssClass = `${cssClass} req`;
if (this.visited && this.valid !== undefined) {
if (this.valid === true) {
cssClass = `${cssClass} valid`;

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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>
<span class="form-control shell">
<span
class="field control"
:class="model.cssClass"
>
<input
type="checkbox"
:checked="isChecked"
@input="toggleCheckBox"
>
</span>
</span>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
isChecked: this.model.value
};
},
methods: {
toggleCheckBox() {
this.isChecked = !this.isChecked;
const data = {
model: this.model,
value: this.isChecked
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -36,13 +36,14 @@ class InMemorySearchProvider {
*/
this.MAX_CONCURRENT_REQUESTS = 100;
/**
* If max results is not specified in query, use this as default.
*/
* If max results is not specified in query, use this as default.
*/
this.DEFAULT_MAX_RESULTS = 100;
this.openmct = openmct;
this.indexedIds = {};
this.indexedCompositions = {};
this.idsToIndex = [];
this.pendingIndex = {};
this.pendingRequests = 0;
@ -58,7 +59,6 @@ class InMemorySearchProvider {
this.onWorkerMessageError = this.onWorkerMessageError.bind(this);
this.onerror = this.onWorkerError.bind(this);
this.startIndexing = this.startIndexing.bind(this);
this.onMutationOfIndexedObject = this.onMutationOfIndexedObject.bind(this);
this.openmct.on('start', this.startIndexing);
this.openmct.on('destroy', () => {
@ -68,6 +68,9 @@ class InMemorySearchProvider {
this.worker.port.onmessageerror = null;
this.worker.port.close();
}
this.destroyObservers(this.indexedIds);
this.destroyObservers(this.indexedCompositions);
});
}
@ -137,7 +140,7 @@ class InMemorySearchProvider {
};
modelResults.hits = await Promise.all(event.data.results.map(async (hit) => {
const identifier = this.openmct.objects.parseKeyString(hit.keyString);
const domainObject = await this.openmct.objects.get(identifier.key);
const domainObject = await this.openmct.objects.get(identifier);
return domainObject;
}));
@ -213,29 +216,52 @@ class InMemorySearchProvider {
}
}
onMutationOfIndexedObject(domainObject) {
onNameMutation(domainObject, name) {
const provider = this;
provider.index(domainObject.identifier, domainObject);
domainObject.name = name;
provider.index(domainObject);
}
onCompositionMutation(domainObject, composition) {
const provider = this;
const indexedComposition = domainObject.composition;
const identifiersToIndex = composition
.filter(identifier => !indexedComposition
.some(indexedIdentifier => this.openmct.objects
.areIdsEqual([identifier, indexedIdentifier])));
identifiersToIndex.forEach(identifier => {
this.openmct.objects.get(identifier).then(objectToIndex => provider.index(objectToIndex));
});
}
/**
* Pass an id and model to the worker to be indexed. If the model has
* composition, schedule those ids for later indexing.
* Pass a domainObject to the worker to be indexed.
* If the object has composition, schedule those ids for later indexing.
* Watch for object changes and re-index object and children if so
*
* @private
* @param id a model id
* @param model a model
* @param domainObject a domainObject
*/
async index(id, domainObject) {
async index(domainObject) {
const provider = this;
const keyString = this.openmct.objects.makeKeyString(id);
const keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.indexedIds[keyString]) {
this.openmct.objects.observe(domainObject, `*`, this.onMutationOfIndexedObject);
this.indexedIds[keyString] = this.openmct.objects.observe(
domainObject,
'name',
this.onNameMutation.bind(this, domainObject)
);
this.indexedCompositions[keyString] = this.openmct.objects.observe(
domainObject,
'composition',
this.onCompositionMutation.bind(this, domainObject)
);
}
this.indexedIds[keyString] = true;
if ((id.key !== 'ROOT')) {
if ((keyString !== 'ROOT')) {
if (this.worker) {
this.worker.port.postMessage({
request: 'index',
@ -247,15 +273,12 @@ class InMemorySearchProvider {
}
}
const composition = this.openmct.composition.registry.find(foundComposition => {
return foundComposition.appliesTo(domainObject);
});
const composition = this.openmct.composition.get(domainObject);
if (composition) {
const childIdentifiers = await composition.load(domainObject);
childIdentifiers.forEach(function (childIdentifier) {
provider.scheduleForIndexing(childIdentifier);
});
if (composition !== undefined) {
const children = await composition.load();
children.forEach(child => provider.scheduleForIndexing(child.identifier));
}
}
@ -271,12 +294,12 @@ class InMemorySearchProvider {
const provider = this;
this.pendingRequests += 1;
const identifier = await this.openmct.objects.parseKeyString(keyString);
const domainObject = await this.openmct.objects.get(identifier.key);
const domainObject = await this.openmct.objects.get(keyString);
delete provider.pendingIndex[keyString];
try {
if (domainObject) {
await provider.index(identifier, domainObject);
await provider.index(domainObject);
}
} catch (error) {
console.warn('Failed to index domain object ' + keyString, error);
@ -305,9 +328,9 @@ class InMemorySearchProvider {
}
/**
* A local version of the same SharedWorker function
* if we don't have SharedWorkers available (e.g., iOS)
*/
* A local version of the same SharedWorker function
* if we don't have SharedWorkers available (e.g., iOS)
*/
localIndexItem(keyString, model) {
this.localIndexedItems[keyString] = {
type: model.type,
@ -347,6 +370,16 @@ class InMemorySearchProvider {
};
this.onWorkerMessage(eventToReturn);
}
destroyObservers(observers) {
Object.entries(observers).forEach(([keyString, unobserve]) => {
if (typeof unobserve === 'function') {
unobserve();
}
delete observers[keyString];
});
}
}
export default InMemorySearchProvider;

View File

@ -33,8 +33,10 @@
port.onmessage = function (event) {
if (event.data.request === 'index') {
console.log('onmessage index: ', event.data);
indexItem(event.data.keyString, event.data.model);
} else if (event.data.request === 'search') {
console.log('onmessage search: ', event.data);
port.postMessage(search(event.data));
}
};
@ -77,6 +79,8 @@
queryId: data.queryId
};
console.log('indexed on search: ', indexedItems);
results = Object.values(indexedItems).filter((indexedItem) => {
return indexedItem.name.toLowerCase().includes(input);
});

View File

@ -55,6 +55,11 @@ define([
*/
function parseKeyString(keyString) {
if (isIdentifier(keyString)) {
// hack to workaround a bug mashing keyString into identifier.key
if (!keyString.namespace && keyString.key.includes(':')) {
return parseKeyString(keyString.key);
}
return keyString;
}

View File

@ -25,6 +25,7 @@ import EventEmitter from 'EventEmitter';
const ERRORS = {
TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
TIMESYSTEM_KEY_NOTIFICATION: 'Telemetry metadata does not match the active time system.',
LOADED: 'Telemetry Collection has already been loaded.'
};
@ -221,6 +222,11 @@ export class TelemetryCollection extends EventEmitter {
}
if (added.length) {
// check if there is a size option, if so force it's adherence
if (this.options.size !== undefined && this.options.size > 0) {
added = added.slice(0, this.options.size);
}
this.emit('add', added);
}
}
@ -266,6 +272,10 @@ export class TelemetryCollection extends EventEmitter {
this.lastBounds = bounds;
if (isTick) {
if (this.timeKey === undefined) {
return;
}
// need to check futureBuffer and need to check
// if anything has fallen out of bounds
let startIndex = 0;
@ -305,7 +315,6 @@ export class TelemetryCollection extends EventEmitter {
if (added.length > 0) {
this.emit('add', added);
}
} else {
// user bounds change, reset
this._reset();
@ -325,12 +334,14 @@ export class TelemetryCollection extends EventEmitter {
let domains = this.metadata.valuesForHints(['domain']);
let domain = domains.find((d) => d.key === timeSystem.key);
if (domain === undefined) {
this._error(ERRORS.TIMESYSTEM_KEY);
if (domain !== undefined) {
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source;
} else {
this._warn(ERRORS.TIMESYSTEM_KEY);
this.openmct.notifications.alert(ERRORS.TIMESYSTEM_KEY_NOTIFICATION);
}
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source; // this defaults to key if no source is set
let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
@ -400,4 +411,8 @@ export class TelemetryCollection extends EventEmitter {
_error(message) {
throw new Error(message);
}
_warn(message) {
console.warn(message);
}
}

View File

@ -39,8 +39,10 @@ export default class ConditionSetViewProvider {
return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
}
canEdit(domainObject) {
return domainObject.type === 'conditionSet';
canEdit(domainObject, objectPath) {
const isConditionSet = domainObject.type === 'conditionSet';
return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
}
view(domainObject, objectPath) {

View File

@ -66,10 +66,11 @@ export default class CreateAction extends PropertiesAction {
});
const parentDomainObject = parentDomainObjectPath[0];
const namespace = parentDomainObject.identifier.namespace || parentDomainObject.key || '';
this.domainObject.modified = Date.now();
this.domainObject.location = this.openmct.objects.makeKeyString(parentDomainObject.identifier);
this.domainObject.identifier.namespace = parentDomainObject.identifier.namespace;
this.domainObject.identifier.namespace = namespace;
// Show saving progress dialog
let dialog = this.openmct.overlays.progressDialog({
@ -99,6 +100,7 @@ export default class CreateAction extends PropertiesAction {
*/
async _navigateAndEdit(domainObject, parentDomainObjectpath) {
let objectPath;
let self = this;
if (parentDomainObjectpath) {
objectPath = parentDomainObjectpath && [domainObject].concat(parentDomainObjectpath);
} else {
@ -106,17 +108,22 @@ export default class CreateAction extends PropertiesAction {
}
const url = '#/browse/' + objectPath
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
.map(object => object && this.openmct.objects.makeKeyString(object.identifier))
.reverse()
.join('/');
this.openmct.router.navigate(url);
function editObject() {
const objectView = self.openmct.objectViews.get(domainObject, objectPath)[0];
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
const objectView = this.openmct.objectViews.get(domainObject, objectPath)[0];
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
if (canEdit) {
this.openmct.editor.edit();
if (canEdit) {
self.openmct.editor.edit();
}
}
this.openmct.router.once('afterNavigation', editObject);
this.openmct.router.navigate(url);
}
/**

View File

@ -65,13 +65,8 @@ export default {
keyString: undefined
};
},
computed: {
imageHistorySize() {
return this.imageHistory.length;
}
},
watch: {
imageHistorySize(newSize, oldSize) {
imageHistory(newHistory, oldHistory) {
this.updatePlotImagery();
}
},

View File

@ -240,9 +240,6 @@ export default {
};
},
computed: {
imageHistorySize() {
return this.imageHistory.length;
},
compassRoseSizingClasses() {
let compassRoseSizingClasses = '';
if (this.sizedImageDimensions.width < 300) {
@ -409,19 +406,23 @@ export default {
}
},
watch: {
imageHistorySize(newSize, oldSize) {
let imageIndex;
if (this.focusedImageTimestamp !== undefined) {
const foundImageIndex = this.imageHistory.findIndex(image => {
return image.time === this.focusedImageTimestamp;
});
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
} else {
imageIndex = newSize > 0 ? newSize - 1 : undefined;
}
imageHistory: {
handler(newHistory, oldHistory) {
const newSize = newHistory.length;
let imageIndex;
if (this.focusedImageTimestamp !== undefined) {
const foundImageIndex = this.imageHistory.findIndex(image => {
return image.time === this.focusedImageTimestamp;
});
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
} else {
imageIndex = newSize > 0 ? newSize - 1 : undefined;
}
this.setFocusedImage(imageIndex, false);
this.scrollToRight();
this.setFocusedImage(imageIndex, false);
this.scrollToRight();
},
deep: true
},
focusedImageIndex() {
this.trackDuration();
@ -510,12 +511,6 @@ export default {
this.timeContext.off("clock", this.trackDuration);
}
},
boundsChange(bounds, isTick) {
if (!isTick) {
this.previousFocusedImage = this.focusedImage ? JSON.parse(JSON.stringify(this.focusedImage)) : undefined;
this.requestHistory();
}
},
expand() {
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
const visibleActions = actionCollection.getVisibleActions();
@ -690,22 +685,32 @@ export default {
return;
}
if (this.previousFocusedImage) {
// determine if the previous image exists in the new bounds of imageHistory
const matchIndex = this.matchIndexOfPreviousImage(
this.previousFocusedImage,
this.imageHistory
);
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
delete this.previousFocusedImage;
}
if (thumbnailClick) {
//We use the props till the user changes what they want to see
this.focusedImageTimestamp = undefined;
//set the previousFocusedImage when a user chooses an image
this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
}
if (this.previousFocusedImage) {
// determine if the previous image exists in the new bounds of imageHistory
if (!thumbnailClick) {
const matchIndex = this.matchIndexOfPreviousImage(
this.previousFocusedImage,
this.imageHistory
);
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
}
if (!(this.isPaused || thumbnailClick)
|| focusedIndex === this.imageHistory.length - 1) {
delete this.previousFocusedImage;
}
}
this.focusedImageIndex = focusedIndex;
//TODO: do we even need this anymore?
if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
this.nextImageIndex = focusedIndex;
//this could happen if bounds changes
@ -716,8 +721,6 @@ export default {
return;
}
this.focusedImageIndex = focusedIndex;
if (thumbnailClick && !this.isPaused) {
this.paused(true);
}

View File

@ -120,9 +120,15 @@ export default {
return this.timeFormatter.parse(datum);
},
boundsChange(bounds, isTick) {
if (!isTick) {
this.requestHistory();
if (isTick) {
return;
}
// forcibly reset the imageContainer size to prevent an aspect ratio distortion
delete this.imageContainerWidth;
delete this.imageContainerHeight;
return this.requestHistory();
},
async requestHistory() {
let bounds = this.timeContext.bounds();

View File

@ -91,11 +91,11 @@ export default class LinkAction {
}
validate(currentParent) {
return (object, data) => {
const parentCandidate = data.value;
return (data) => {
const parentCandidate = data.value[0];
const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
const objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
if (!parentCandidateKeystring || !currentParentKeystring) {
return false;
@ -114,7 +114,7 @@ export default class LinkAction {
return false;
}
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
};
}
}

View File

@ -142,7 +142,6 @@ import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaul
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 objectUtils from 'objectUtils';
import { debounce } from 'lodash';
import objectLink from '../../../ui/mixins/object-link';
@ -455,11 +454,6 @@ export default {
? getDefaultNotebook().defaultSectionId
: undefined;
},
getDefaultNotebookObject() {
const defaultNotebook = getDefaultNotebook();
return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier);
},
getLinktoNotebook() {
const objectPath = this.openmct.router.path;
const link = objectLink.computed.objectLink.call({
@ -619,12 +613,12 @@ export default {
this.sectionsChanged({ sections });
},
removeDefaultClass(domainObject) {
if (!domainObject) {
removeDefaultClass(defaultNotebookIdentifier) {
if (!defaultNotebookIdentifier) {
return;
}
this.openmct.status.delete(domainObject.identifier);
this.openmct.status.delete(defaultNotebookIdentifier);
},
resetSearch() {
this.search = '';
@ -633,15 +627,16 @@ export default {
toggleNav() {
this.showNav = !this.showNav;
},
async updateDefaultNotebook(notebookStorage) {
const defaultNotebookObject = await this.getDefaultNotebookObject();
const isSameNotebook = defaultNotebookObject
&& objectUtils.makeKeyString(defaultNotebookObject.identifier) === objectUtils.makeKeyString(notebookStorage.identifier);
updateDefaultNotebook(notebookStorage) {
const defaultNotebook = getDefaultNotebook();
const defaultNotebookIdentifier = defaultNotebook && defaultNotebook.identifier;
const isSameNotebook = defaultNotebookIdentifier
&& this.openmct.objects.areIdsEqual(defaultNotebookIdentifier, notebookStorage.identifier);
if (!isSameNotebook) {
this.removeDefaultClass(defaultNotebookObject);
this.removeDefaultClass(defaultNotebookIdentifier);
}
if (!defaultNotebookObject || !isSameNotebook) {
if (!defaultNotebookIdentifier || !isSameNotebook) {
setDefaultNotebook(this.openmct, notebookStorage, this.domainObject);
}

View File

@ -110,7 +110,8 @@ export default class Snapshot {
}
return () => {
window.location.href = window.location.origin + url;
const path = window.location.href.split('#');
window.location.href = path[0] + url;
};
}
}

View File

@ -105,11 +105,6 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
const date = Date.now();
const configuration = domainObject.configuration;
const entries = configuration.entries || {};
if (!entries) {
return;
}
const embeds = embed
? [embed]
: [];
@ -125,7 +120,8 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
addDefaultClass(domainObject, openmct);
domainObject.configuration.entries = newEntries;
mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
return id;
}

View File

@ -223,7 +223,8 @@ export default {
loaded: false,
isTimeOutOfSync: false,
showLimitLineLabels: undefined,
isFrozenOnMouseDown: false
isFrozenOnMouseDown: false,
hasSameRangeValue: true
};
},
computed: {
@ -321,6 +322,7 @@ export default {
this.setDisplayRange(series, xKey);
}, this);
this.listenTo(series, 'change:yKey', () => {
this.checkSameRangeValue();
this.loadSeriesData(series);
}, this);
@ -328,10 +330,18 @@ export default {
this.loadSeriesData(series);
}, this);
this.checkSameRangeValue();
this.loadSeriesData(series);
},
checkSameRangeValue() {
this.hasSameRangeValue = this.seriesModels.every((model) => {
return model.get('yKey') === this.seriesModels[0].get('yKey');
});
},
removeSeries(plotSeries) {
this.checkSameRangeValue();
this.stopListening(plotSeries);
},
@ -447,7 +457,7 @@ export default {
},
setDisplayRange(series, xKey) {
if (this.config.series.length !== 1) {
if (this.config.series.model.length !== 1) {
return;
}

View File

@ -27,9 +27,10 @@
}"
>
<div v-if="singleSeries"
class="gl-plot-label gl-plot-y-label"
:class="{'icon-gear': (yKeyOptions.length > 1)}"
<div
v-if="canShowYAxisLabel"
class="gl-plot-label gl-plot-y-label"
:class="{'icon-gear': (yKeyOptions.length > 1 && singleSeries)}"
>{{ yAxisLabel }}
</div>
@ -71,6 +72,12 @@ export default {
return true;
}
},
hasSameRangeValue: {
type: Boolean,
default() {
return true;
}
},
seriesModel: {
type: Object,
default() {
@ -90,6 +97,11 @@ export default {
loaded: false
};
},
computed: {
canShowYAxisLabel() {
return this.singleSeries === true || this.hasSameRangeValue === true;
}
},
mounted() {
this.yAxis = this.getYAxisFromConfig();
this.loaded = true;

View File

@ -278,7 +278,7 @@ export default {
// Have to throw away the old canvas elements and replace with new
// canvas elements in order to get new drawing contexts.
const div = document.createElement('div');
div.innerHTML = this.TEMPLATE;
div.innerHTML = this.canvasTemplate + this.canvasTemplate;
const mainCanvas = div.querySelectorAll("canvas")[1];
const overlayCanvas = div.querySelectorAll("canvas")[0];
this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);

View File

@ -232,6 +232,7 @@ export default class PlotSeries extends Model {
this.evaluate = function (datum) {
return this.limitEvaluator.evaluate(datum, valueMetadata);
}.bind(this);
this.set('unit', valueMetadata.unit);
const format = this.formats[newKey];
this.getYVal = format.parse.bind(format);
}

View File

@ -29,10 +29,9 @@ define(
}
SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
const parentType = parent.getCapability('type');
const newStyleChild = child.useCapability('adapter');
const parentType = parent.type;
if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) {
if (parentType === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) {
return false;
}

View File

@ -9,7 +9,11 @@ export default class TelemetryTableView {
this.objectPath = objectPath;
this.component = undefined;
this.table = new TelemetryTable(domainObject, openmct);
Object.defineProperty(this, 'table', {
value: new TelemetryTable(domainObject, openmct),
enumerable: false,
configurable: false
});
}
getViewContext() {

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -29,10 +29,6 @@ define(
_,
EventEmitter
) {
const LESS_THAN = -1;
const EQUAL = 0;
const GREATER_THAN = 1;
/**
* @constructor
*/
@ -80,10 +76,7 @@ define(
this.rows = [];
}
for (let row of rowsToAdd) {
let index = this.sortedIndex(this.rows, row);
this.rows.splice(index, 0, row);
}
this.sortAndMergeRows(rowsToAdd);
// we emit filter no matter what to trigger
// an update of visible rows
@ -92,58 +85,85 @@ define(
}
}
sortedLastIndex(rows, testRow) {
return this.sortedIndex(rows, testRow, _.sortedLastIndex);
}
sortAndMergeRows(rows) {
const sortedRowsToAdd = this.sortCollection(rows);
/**
* Finds the correct insertion point for the given row.
* Leverages lodash's `sortedIndex` function which implements a binary search.
* @private
*/
sortedIndex(rows, testRow, lodashFunction = _.sortedIndexBy) {
if (this.rows.length === 0) {
return 0;
this.rows = sortedRowsToAdd;
return;
}
const testRowValue = this.getValueForSortColumn(testRow);
const firstValue = this.getValueForSortColumn(this.rows[0]);
const lastValue = this.getValueForSortColumn(this.rows[this.rows.length - 1]);
const firstIncomingRow = sortedRowsToAdd[0];
const lastIncomingRow = sortedRowsToAdd[sortedRowsToAdd.length - 1];
const firstExistingRow = this.rows[0];
const lastExistingRow = this.rows[this.rows.length - 1];
if (this.firstRowInSortOrder(lastIncomingRow, firstExistingRow)
=== lastIncomingRow
) {
this.rows = [...sortedRowsToAdd, ...this.rows];
} else if (this.firstRowInSortOrder(lastExistingRow, firstIncomingRow)
=== lastExistingRow
) {
this.rows = [...this.rows, ...sortedRowsToAdd];
} else {
this.mergeSortedRows(sortedRowsToAdd);
}
}
sortCollection(rows) {
const sortedRows = _.orderBy(
rows,
row => row.getParsedValue(this.sortOptions.key), this.sortOptions.direction
);
return sortedRows;
}
mergeSortedRows(rows) {
const mergedRows = [];
let i = 0;
let j = 0;
while (i < this.rows.length && j < rows.length) {
const iRow = this.rows[i];
const jRow = rows[j];
if (this.firstRowInSortOrder(iRow, jRow) === iRow) {
mergedRows.push(iRow);
i++;
} else {
mergedRows.push(jRow);
j++;
}
}
// tail of existing rows is all that is left to merge
if (i < this.rows.length) {
for (i; i < this.rows.length; i++) {
mergedRows.push(this.rows[i]);
}
}
// tail of incoming rows is all that is left to merge
if (j < rows.length) {
for (j; j < rows.length; j++) {
mergedRows.push(rows[j]);
}
}
this.rows = mergedRows;
}
firstRowInSortOrder(row1, row2) {
const val1 = this.getValueForSortColumn(row1);
const val2 = this.getValueForSortColumn(row2);
if (this.sortOptions.direction === 'asc') {
if (testRowValue > lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
// Maintain stable sort
return this.rows.length;
} else if (testRowValue <= firstValue) {
return 0;
} else {
return lodashFunction(rows, testRow, (thisRow) => {
return this.getValueForSortColumn(thisRow);
});
}
return val1 <= val2 ? row1 : row2;
} else {
if (testRowValue >= firstValue) {
return 0;
} else if (testRowValue < lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
// Maintain stable sort
return this.rows.length;
} else {
// Use a custom comparison function to support descending sort.
return lodashFunction(rows, testRow, (thisRow) => {
const thisRowValue = this.getValueForSortColumn(thisRow);
if (testRowValue === thisRowValue) {
return EQUAL;
} else if (testRowValue < thisRowValue) {
return LESS_THAN;
} else {
return GREATER_THAN;
}
});
}
return val1 >= val2 ? row1 : row2;
}
}

View File

@ -21,7 +21,7 @@
*****************************************************************************/
export const COLOR_PALETTE = [
[0x00, 0x37, 0xFF],
[0x43, 0xB0, 0xFF],
[0xF0, 0x60, 0x00],
[0x00, 0x70, 0x40],
[0xFB, 0x49, 0x49],
@ -30,25 +30,25 @@ export const COLOR_PALETTE = [
[0xFF, 0xA6, 0x3D],
[0x05, 0xA3, 0x00],
[0xF0, 0x00, 0x6C],
[0x77, 0x17, 0x7A],
[0xAC, 0x54, 0xAE],
[0x23, 0xA9, 0xDB],
[0xFA, 0xF0, 0x6F],
[0x4E, 0xF0, 0x48],
[0xC7, 0xBE, 0x52],
[0x5A, 0xBD, 0x56],
[0xAD, 0x50, 0x72],
[0x94, 0x25, 0xEA],
[0x21, 0x87, 0x82],
[0x8F, 0x6E, 0x47],
[0xf0, 0x59, 0xcb],
[0x34, 0xB6, 0x7D],
[0x6A, 0x36, 0xFF],
[0x56, 0xF0, 0xE8],
[0x7F, 0x52, 0xFF],
[0x46, 0xC7, 0xC0],
[0xA1, 0x8C, 0x1C],
[0xCB, 0xE1, 0x44],
[0x95, 0xB1, 0x26],
[0xFF, 0x84, 0x9E],
[0xB7, 0x79, 0xE7],
[0x8C, 0xC9, 0xFD],
[0xDB, 0xAA, 0x6E],
[0xB8, 0xDF, 0x97],
[0x93, 0xB5, 0x77],
[0xFF, 0xBC, 0xDA],
[0xD3, 0xB6, 0xDE]
];

View File

@ -141,11 +141,15 @@ export default {
this.openmct.objectViews.off('clearData', this.clearData);
},
getStyleReceiver() {
let styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
|| this.$refs.objectViewWrapper.querySelector(':first-child');
let styleReceiver;
if (styleReceiver === null) {
styleReceiver = undefined;
if (this.$refs.objectViewWrapper !== undefined) {
styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
|| this.$refs.objectViewWrapper.querySelector(':first-child');
if (styleReceiver === null) {
styleReceiver = undefined;
}
}
return styleReceiver;

View File

@ -186,6 +186,10 @@ export default {
return {
name: field.name,
value: field.path.reduce((object, key) => {
if (object === undefined) {
return object;
}
return object[key];
}, this.domainObject)
};

View File

@ -445,6 +445,10 @@ export default {
}
// sorting composition items
if (!a.name || !b.name) {
return 0;
}
if (a.name.toLowerCase()
> b.name.toLowerCase()) {
return 1;

View File

@ -1,4 +1,4 @@
import objectPathToUrl from '/src/tools/url';
import objectPathToUrl from '../../tools/url';
export default {
inject: ['openmct'],