mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
chore: upgrade eventemitter to v5.0.2 (#7709)
* chore: upgrade eventemitter to v5.0.2 * fix: pass context to eventHelpers * fix: no need to destroy router as it is destroyed during openmct teardown * fix: register `CreateAction` and retrieve it from the registry * test: fix tests * refactor: import action key consts * fix: update usage. don't use getters Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com> --------- Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
This commit is contained in:
parent
810d580b18
commit
017380bb6a
@ -6,7 +6,7 @@ information to pull requests.
|
||||
|
||||
import config from './webpack.dev.mjs';
|
||||
|
||||
config.devtool = 'source-map';
|
||||
config.devtool = 'inline-source-map';
|
||||
config.devServer.hot = false;
|
||||
|
||||
config.module.rules.push({
|
||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -43,7 +43,7 @@
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
"eslint-plugin-vue": "9.22.0",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "6.13.0",
|
||||
"eventemitter3": "1.2.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"file-saver": "2.0.5",
|
||||
"flatbush": "4.2.0",
|
||||
"git-rev-sync": "3.0.2",
|
||||
@ -5391,9 +5391,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
|
||||
"integrity": "sha512-DOFqA1MF46fmZl2xtzXR3MPCRsXqgoFqdXcrCVYM3JNnfUeHTm/fh/v/iU7gBFpwkuBmoJPAm5GuhdDfSEJMJA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/events": {
|
||||
|
@ -46,7 +46,7 @@
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
"eslint-plugin-vue": "9.22.0",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "6.13.0",
|
||||
"eventemitter3": "1.2.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"file-saver": "2.0.5",
|
||||
"flatbush": "4.2.0",
|
||||
"git-rev-sync": "3.0.2",
|
||||
|
@ -79,7 +79,6 @@ import Browse from './ui/router/Browse.js';
|
||||
export class MCT extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.buildInfo = {
|
||||
version: __OPENMCT_VERSION__,
|
||||
@ -371,6 +370,5 @@ export class MCT extends EventEmitter {
|
||||
destroy() {
|
||||
window.removeEventListener('beforeunload', this.destroy);
|
||||
this.emit('destroy');
|
||||
this.router.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -57,14 +57,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const CONTEXT_MENU_ACTIONS = ['viewDatumAction', 'viewHistoricalData', 'remove'];
|
||||
const BLANK_VALUE = '---';
|
||||
|
||||
import { objectPathToUrl } from '/src/tools/url.js';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction.js';
|
||||
import { REMOVE_ACTION_KEY } from '@/plugins/remove/RemoveAction.js';
|
||||
import { VIEW_DATUM_ACTION_KEY } from '@/plugins/viewDatumAction/ViewDatumAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
import { VIEW_HISTORICAL_DATA_ACTION_KEY } from '@/ui/preview/ViewHistoricalDataAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
|
||||
const BLANK_VALUE = '---';
|
||||
const CONTEXT_MENU_ACTIONS = [
|
||||
VIEW_DATUM_ACTION_KEY,
|
||||
VIEW_HISTORICAL_DATA_ACTION_KEY,
|
||||
REMOVE_ACTION_KEY
|
||||
];
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'currentView', 'renderWhenVisible'],
|
||||
@ -236,14 +243,12 @@ export default {
|
||||
this.setUnit();
|
||||
}
|
||||
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
},
|
||||
unmounted() {
|
||||
this.openmct.time.off('timeSystem', this.updateTimeSystem);
|
||||
this.telemetryCollection.off('add', this.setLatestValues);
|
||||
this.telemetryCollection.off('clear', this.resetValues);
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
|
||||
this.telemetryCollection.destroy();
|
||||
},
|
||||
|
@ -32,16 +32,18 @@ function inSelectionPath(openmct, domainObject) {
|
||||
});
|
||||
}
|
||||
|
||||
export default class ClearDataAction {
|
||||
const CLEAR_DATA_ACTION_KEY = 'clear-data-action';
|
||||
class ClearDataAction {
|
||||
constructor(openmct, appliesToObjects) {
|
||||
this.name = 'Clear Data for Object';
|
||||
this.key = 'clear-data-action';
|
||||
this.key = CLEAR_DATA_ACTION_KEY;
|
||||
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) {
|
||||
@ -76,3 +78,7 @@ export default class ClearDataAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { CLEAR_DATA_ACTION_KEY };
|
||||
|
||||
export default ClearDataAction;
|
||||
|
@ -142,7 +142,7 @@ import {
|
||||
getConditionSetIdentifierForItem,
|
||||
getConsolidatedStyleValues
|
||||
} from '@/plugins/condition/utils/styleUtils';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import FontStyleEditor from '../../../inspectorViews/styles/FontStyleEditor.vue';
|
||||
import StyleEditor from './StyleEditor.vue';
|
||||
@ -237,7 +237,7 @@ export default {
|
||||
this.stylesManager.off('styleSelected', this.applyStyleToSelection);
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.isMultipleSelection = this.selection.length > 1;
|
||||
this.getObjectsAndItemsFromSelection();
|
||||
this.useConditionSetOutputAsLabel = this.getConfigurationForLabel();
|
||||
|
@ -1,13 +1,15 @@
|
||||
import clipboard from '@/utils/clipboard';
|
||||
|
||||
export default class CopyToClipboardAction {
|
||||
const COPY_TO_CLIPBOARD_ACTION_KEY = 'copyToClipboard';
|
||||
|
||||
class CopyToClipboardAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.description = 'Copy value to clipboard';
|
||||
this.group = 'action';
|
||||
this.key = 'copyToClipboard';
|
||||
this.key = COPY_TO_CLIPBOARD_ACTION_KEY;
|
||||
this.name = 'Copy to Clipboard';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -36,3 +38,7 @@ export default class CopyToClipboardAction {
|
||||
return row.formattedValueForCopy && typeof row.formattedValueForCopy === 'function';
|
||||
}
|
||||
}
|
||||
|
||||
export { COPY_TO_CLIPBOARD_ACTION_KEY };
|
||||
|
||||
export default CopyToClipboardAction;
|
||||
|
@ -72,11 +72,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { COPY_TO_CLIPBOARD_ACTION_KEY } from '@/plugins/displayLayout/actions/CopyToClipboardAction.js';
|
||||
import { COPY_TO_NOTEBOOK_ACTION_KEY } from '@/plugins/notebook/actions/CopyToNotebookAction.js';
|
||||
import {
|
||||
getDefaultNotebook,
|
||||
getNotebookSectionAndPage
|
||||
} from '@/plugins/notebook/utils/notebook-storage.js';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import { VIEW_HISTORICAL_DATA_ACTION_KEY } from '@/ui/preview/ViewHistoricalDataAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import conditionalStylesMixin from '../mixins/objectStyles-mixin.js';
|
||||
@ -84,7 +87,11 @@ import LayoutFrame from './LayoutFrame.vue';
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
|
||||
const DEFAULT_POSITION = [1, 1];
|
||||
const CONTEXT_MENU_ACTIONS = ['copyToClipboard', 'copyToNotebook', 'viewHistoricalData'];
|
||||
const CONTEXT_MENU_ACTIONS = [
|
||||
COPY_TO_CLIPBOARD_ACTION_KEY,
|
||||
COPY_TO_NOTEBOOK_ACTION_KEY,
|
||||
VIEW_HISTORICAL_DATA_ACTION_KEY
|
||||
];
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
@ -381,7 +388,7 @@ export default {
|
||||
|
||||
return CONTEXT_MENU_ACTIONS.map((actionKey) => {
|
||||
const action = this.openmct.actions.getAction(actionKey);
|
||||
if (action.key === 'copyToNotebook') {
|
||||
if (action.key === COPY_TO_NOTEBOOK_ACTION_KEY) {
|
||||
action.name = defaultNotebookName;
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,12 @@
|
||||
*****************************************************************************/
|
||||
import DuplicateTask from './DuplicateTask.js';
|
||||
|
||||
export default class DuplicateAction {
|
||||
const DUPLICATE_ACTION_KEY = 'duplicate';
|
||||
|
||||
class DuplicateAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Duplicate';
|
||||
this.key = 'duplicate';
|
||||
this.key = DUPLICATE_ACTION_KEY;
|
||||
this.description = 'Duplicate this object.';
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.group = 'action';
|
||||
@ -169,3 +171,7 @@ export default class DuplicateAction {
|
||||
this.transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { DUPLICATE_ACTION_KEY };
|
||||
|
||||
export default DuplicateAction;
|
||||
|
@ -22,8 +22,9 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import JSONExporter from '/src/exporters/JSONExporter.js';
|
||||
const EXPORT_AS_JSON_ACTION_KEY = 'export.JSON';
|
||||
|
||||
export default class ExportAsJSONAction {
|
||||
class ExportAsJSONAction {
|
||||
#openmct;
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ export default class ExportAsJSONAction {
|
||||
this.saveAs = this.saveAs.bind(this);
|
||||
|
||||
this.name = 'Export as JSON';
|
||||
this.key = 'export.JSON';
|
||||
this.key = EXPORT_AS_JSON_ACTION_KEY;
|
||||
this.description = '';
|
||||
this.cssClass = 'icon-export';
|
||||
this.group = 'export';
|
||||
@ -410,3 +411,7 @@ export default class ExportAsJSONAction {
|
||||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
}
|
||||
|
||||
export { EXPORT_AS_JSON_ACTION_KEY };
|
||||
|
||||
export default ExportAsJSONAction;
|
||||
|
@ -26,19 +26,22 @@ import { v4 as uuid } from 'uuid';
|
||||
import CreateWizard from './CreateWizard.js';
|
||||
import PropertiesAction from './PropertiesAction.js';
|
||||
|
||||
export default class CreateAction extends PropertiesAction {
|
||||
const CREATE_ACTION_KEY = 'create';
|
||||
|
||||
class CreateAction extends PropertiesAction {
|
||||
#transaction;
|
||||
|
||||
constructor(openmct, type, parentDomainObject) {
|
||||
constructor(openmct) {
|
||||
super(openmct);
|
||||
|
||||
this.type = type;
|
||||
this.parentDomainObject = parentDomainObject;
|
||||
this.#transaction = null;
|
||||
this.key = CREATE_ACTION_KEY;
|
||||
// Hide the create action from context menus by default
|
||||
this.isHidden = true;
|
||||
}
|
||||
|
||||
invoke() {
|
||||
this._showCreateForm(this.type);
|
||||
get invoke() {
|
||||
return (type, parentDomainObject) => this._showCreateForm(type, parentDomainObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +145,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_showCreateForm(type) {
|
||||
_showCreateForm(type, parentDomainObject) {
|
||||
const typeDefinition = this.openmct.types.get(type);
|
||||
const definition = typeDefinition.definition;
|
||||
const domainObject = {
|
||||
@ -150,7 +153,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
type,
|
||||
identifier: {
|
||||
key: uuid(),
|
||||
namespace: this.parentDomainObject.identifier.namespace
|
||||
namespace: parentDomainObject.identifier.namespace
|
||||
}
|
||||
};
|
||||
|
||||
@ -160,7 +163,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
definition.initialize(this.domainObject);
|
||||
}
|
||||
|
||||
const createWizard = new CreateWizard(this.openmct, this.domainObject, this.parentDomainObject);
|
||||
const createWizard = new CreateWizard(this.openmct, this.domainObject, parentDomainObject);
|
||||
const formStructure = createWizard.getFormStructure(true);
|
||||
formStructure.title = 'Create a New ' + definition.name;
|
||||
|
||||
@ -191,3 +194,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
this.#transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { CREATE_ACTION_KEY };
|
||||
|
||||
export default CreateAction;
|
||||
|
@ -22,7 +22,7 @@
|
||||
import { debounce } from 'lodash';
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
|
||||
import CreateAction from './CreateAction.js';
|
||||
import { CREATE_ACTION_KEY } from './CreateAction.js';
|
||||
|
||||
let parentObject;
|
||||
let parentObjectPath;
|
||||
@ -115,8 +115,8 @@ describe('The create action plugin', () => {
|
||||
const deBouncedCallback = debounce(callback, 300);
|
||||
unObserve = openmct.objects.observe(parentObject, '*', deBouncedCallback);
|
||||
|
||||
const createAction = new CreateAction(openmct, type, parentObject);
|
||||
createAction.invoke();
|
||||
const createAction = openmct.actions.getAction(CREATE_ACTION_KEY);
|
||||
createAction.invoke(type, parentObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -24,13 +24,14 @@ import _ from 'lodash';
|
||||
|
||||
import CreateWizard from './CreateWizard.js';
|
||||
import PropertiesAction from './PropertiesAction.js';
|
||||
const EDIT_PROPERTIES_ACTION_KEY = 'properties';
|
||||
|
||||
export default class EditPropertiesAction extends PropertiesAction {
|
||||
class EditPropertiesAction extends PropertiesAction {
|
||||
constructor(openmct) {
|
||||
super(openmct);
|
||||
|
||||
this.name = 'Edit Properties...';
|
||||
this.key = 'properties';
|
||||
this.key = EDIT_PROPERTIES_ACTION_KEY;
|
||||
this.description = 'Edit properties of this object.';
|
||||
this.cssClass = 'major icon-pencil';
|
||||
this.hideInDefaultMenu = true;
|
||||
@ -100,3 +101,7 @@ export default class EditPropertiesAction extends PropertiesAction {
|
||||
.catch(this._onCancel.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
export { EDIT_PROPERTIES_ACTION_KEY };
|
||||
|
||||
export default EditPropertiesAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import CreateAction from './CreateAction.js';
|
||||
import EditPropertiesAction from './EditPropertiesAction.js';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.actions.register(new EditPropertiesAction(openmct));
|
||||
openmct.actions.register(new CreateAction(openmct));
|
||||
};
|
||||
}
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class GoToOriginalAction {
|
||||
const GO_TO_ORIGINAL_ACTION_KEY = 'goToOriginal';
|
||||
|
||||
class GoToOriginalAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Go To Original';
|
||||
this.key = 'goToOriginal';
|
||||
this.key = GO_TO_ORIGINAL_ACTION_KEY;
|
||||
this.description = 'Go to the original unlinked instance of this object';
|
||||
this.group = 'action';
|
||||
this.priority = 4;
|
||||
@ -62,3 +64,7 @@ export default class GoToOriginalAction {
|
||||
return parentKeystring !== objectPath[0].location;
|
||||
}
|
||||
}
|
||||
|
||||
export { GO_TO_ORIGINAL_ACTION_KEY };
|
||||
|
||||
export default GoToOriginalAction;
|
||||
|
@ -20,14 +20,15 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class OpenImageInNewTabAction {
|
||||
const OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY = 'openImageInNewTab';
|
||||
class OpenImageInNewTabAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-new-window';
|
||||
this.description = 'Open the image in a new tab';
|
||||
this.group = 'action';
|
||||
this.key = 'openImageInNewTab';
|
||||
this.key = OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY;
|
||||
this.name = 'Open Image in New Tab';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -44,3 +45,7 @@ export default class OpenImageInNewTabAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY };
|
||||
|
||||
export default OpenImageInNewTabAction;
|
||||
|
@ -20,14 +20,15 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class SaveImageAction {
|
||||
const SAVE_IMAGE_ACTION_KEY = 'saveImageAs';
|
||||
class SaveImageAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-save-as';
|
||||
this.description = 'Save image to file';
|
||||
this.group = 'action';
|
||||
this.key = 'saveImageAs';
|
||||
this.key = SAVE_IMAGE_ACTION_KEY;
|
||||
this.name = 'Save Image As';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -65,3 +66,7 @@ export default class SaveImageAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { SAVE_IMAGE_ACTION_KEY };
|
||||
|
||||
export default SaveImageAction;
|
||||
|
@ -38,13 +38,15 @@ import isEqual from 'lodash/isEqual';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import TagEditorClassNames from '../../inspectorViews/annotations/tags/TagEditorClassNames.js';
|
||||
import { OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY } from '../actions/OpenImageInNewTabAction.js';
|
||||
import { SAVE_IMAGE_ACTION_KEY } from '../actions/SaveImageAsAction.js';
|
||||
|
||||
const EXISTING_ANNOTATION_STROKE_STYLE = '#D79078';
|
||||
const EXISTING_ANNOTATION_FILL_STYLE = 'rgba(202, 202, 142, 0.2)';
|
||||
const SELECTED_ANNOTATION_STROKE_COLOR = '#BD8ECC';
|
||||
const SELECTED_ANNOTATION_FILL_STYLE = 'rgba(199, 87, 231, 0.2)';
|
||||
|
||||
const CONTEXT_MENU_ACTIONS = ['openImageInNewTab', 'saveImageAs'];
|
||||
const CONTEXT_MENU_ACTIONS = [OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY, SAVE_IMAGE_ACTION_KEY];
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
|
||||
|
@ -32,7 +32,7 @@ import _ from 'lodash';
|
||||
import mount from 'utils/mount';
|
||||
|
||||
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import imageryData from '../../imagery/mixins/imageryData.js';
|
||||
|
||||
@ -71,7 +71,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
|
||||
this.canvas = this.$refs.imagery.appendChild(document.createElement('canvas'));
|
||||
this.canvas.height = 0;
|
||||
|
@ -213,8 +213,10 @@ import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { TIME_CONTEXT_EVENTS } from '../../../api/time/constants.js';
|
||||
import imageryData from '../../imagery/mixins/imageryData.js';
|
||||
import { TIME_CONTEXT_EVENTS } from '@/api/time/constants.js';
|
||||
import imageryData from '@/plugins/imagery/mixins/imageryData.js';
|
||||
import { VIEW_LARGE_ACTION_KEY } from '@/plugins/viewLargeAction/viewLargeAction.js';
|
||||
|
||||
import eventHelpers from '../lib/eventHelpers.js';
|
||||
import AnnotationsCanvas from './AnnotationsCanvas.vue';
|
||||
import Compass from './Compass/CompassComponent.vue';
|
||||
@ -827,7 +829,9 @@ export default {
|
||||
this.currentView
|
||||
);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
const viewLargeAction = visibleActions?.find((action) => action.key === 'large.view');
|
||||
const viewLargeAction = visibleActions?.find(
|
||||
(action) => action.key === VIEW_LARGE_ACTION_KEY
|
||||
);
|
||||
|
||||
if (viewLargeAction?.appliesTo(this.objectPath, this.currentView)) {
|
||||
viewLargeAction.invoke(this.objectPath, this.currentView);
|
||||
|
@ -41,7 +41,7 @@ const helperFunctions = {
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
object.on(event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
@ -78,7 +78,7 @@ const helperFunctions = {
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
listener.object.off(listener.event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
return listener;
|
||||
|
@ -28,6 +28,8 @@ import {
|
||||
} from 'utils/testing';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { PREVIEW_ACTION_KEY } from '../../ui/preview/PreviewAction.js';
|
||||
|
||||
const ONE_MINUTE = 1000 * 60;
|
||||
const TEN_MINUTES = ONE_MINUTE * 10;
|
||||
const MAIN_IMAGE_CLASS = '.js-imageryView-image';
|
||||
@ -81,11 +83,10 @@ describe('The Imagery View Layouts', () => {
|
||||
const START = Date.now();
|
||||
const COUNT = 10;
|
||||
|
||||
// let resolveFunction;
|
||||
let originalRouterPath;
|
||||
let telemetryPromise;
|
||||
let telemetryPromiseResolve;
|
||||
let cleanupFirst;
|
||||
let previewAction;
|
||||
|
||||
let openmct;
|
||||
let parent;
|
||||
@ -172,8 +173,6 @@ describe('The Imagery View Layouts', () => {
|
||||
|
||||
// this setups up the app
|
||||
beforeEach((done) => {
|
||||
cleanupFirst = [];
|
||||
|
||||
openmct = createOpenMct();
|
||||
|
||||
telemetryPromise = new Promise((resolve) => {
|
||||
@ -193,6 +192,8 @@ describe('The Imagery View Layouts', () => {
|
||||
return telemetryPromise;
|
||||
});
|
||||
|
||||
previewAction = openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
|
||||
parent = document.createElement('div');
|
||||
parent.style.width = '640px';
|
||||
parent.style.height = '480px';
|
||||
@ -222,20 +223,11 @@ describe('The Imagery View Layouts', () => {
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach((done) => {
|
||||
afterEach(async () => {
|
||||
openmct.router.path = originalRouterPath;
|
||||
document.body.removeChild(parent);
|
||||
|
||||
// Needs to be in a timeout because plots use a bunch of setTimeouts, some of which can resolve during or after
|
||||
// teardown, which causes problems
|
||||
// This is hacky, we should find a better approach here.
|
||||
setTimeout(() => {
|
||||
//Cleanup code that needs to happen before dom elements start being destroyed
|
||||
cleanupFirst.forEach((cleanup) => cleanup());
|
||||
cleanupFirst = [];
|
||||
document.body.removeChild(parent);
|
||||
|
||||
resetApplicationState(openmct).then(done).catch(done);
|
||||
});
|
||||
await resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('should provide an imagery time strip view when in a time strip', () => {
|
||||
@ -609,7 +601,7 @@ describe('The Imagery View Layouts', () => {
|
||||
imageryTimeView.show(child);
|
||||
|
||||
componentView = imageryTimeView.getComponent().$refs.root;
|
||||
spyOn(componentView.previewAction, 'invoke').and.callThrough();
|
||||
spyOn(previewAction, 'invoke').and.callThrough();
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -633,13 +625,12 @@ describe('The Imagery View Layouts', () => {
|
||||
imageWrapper[2].dispatchEvent(mouseDownEvent);
|
||||
await nextTick();
|
||||
const timestamp = imageWrapper[2].id.replace('wrapper-', '');
|
||||
const mockInvoke = componentView.previewAction.invoke;
|
||||
// Make sure the function was called
|
||||
expect(mockInvoke).toHaveBeenCalled();
|
||||
expect(previewAction.invoke).toHaveBeenCalled();
|
||||
|
||||
// Get the arguments of the first call
|
||||
const firstArg = mockInvoke.calls.mostRecent().args[0];
|
||||
const secondArg = mockInvoke.calls.mostRecent().args[1];
|
||||
const firstArg = previewAction.invoke.calls.mostRecent().args[0];
|
||||
const secondArg = previewAction.invoke.calls.mostRecent().args[1];
|
||||
|
||||
// Compare the first argument
|
||||
expect(firstArg).toEqual([componentView.objectPath[0]]);
|
||||
|
@ -24,10 +24,12 @@ import { parseKeyString } from 'objectUtils';
|
||||
import { filter__proto__ } from 'utils/sanitization';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default class ImportAsJSONAction {
|
||||
const IMPORT_FROM_JSON_ACTION_KEY = 'import.JSON';
|
||||
|
||||
class ImportFromJSONAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Import from JSON';
|
||||
this.key = 'import.JSON';
|
||||
this.key = IMPORT_FROM_JSON_ACTION_KEY;
|
||||
this.description = '';
|
||||
this.cssClass = 'icon-import';
|
||||
this.group = 'import';
|
||||
@ -405,3 +407,7 @@ export default class ImportAsJSONAction {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
export { IMPORT_FROM_JSON_ACTION_KEY };
|
||||
|
||||
export default ImportFromJSONAction;
|
||||
|
@ -66,6 +66,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NEW_TAB_ACTION_KEY } from '@/plugins/openInNewTabAction/openInNewTabAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
provide() {
|
||||
@ -119,7 +122,7 @@ export default {
|
||||
'tc.endBound': timeBounds?.end,
|
||||
'tc.mode': 'fixed'
|
||||
};
|
||||
const newTabAction = this.openmct.actions.getAction('newTab');
|
||||
const newTabAction = this.openmct.actions.getAction(NEW_TAB_ACTION_KEY);
|
||||
// No view context needed, so pass undefined.
|
||||
// The urlParams arg will override the global time bounds with the data visualization
|
||||
// plot bounds.
|
||||
@ -127,7 +130,7 @@ export default {
|
||||
this.showMenu = false;
|
||||
},
|
||||
previewTelemetry() {
|
||||
const previewAction = this.openmct.actions.getAction('preview');
|
||||
const previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
previewAction.invoke([this.telemetryObject]);
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class LinkAction {
|
||||
const LINK_ACTION_KEY = 'link';
|
||||
|
||||
class LinkAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Create Link';
|
||||
this.key = 'link';
|
||||
this.key = LINK_ACTION_KEY;
|
||||
this.description = 'Create Link to object in another location.';
|
||||
this.cssClass = 'icon-link';
|
||||
this.group = 'action';
|
||||
@ -154,3 +156,7 @@ export default class LinkAction {
|
||||
this.transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { LINK_ACTION_KEY };
|
||||
|
||||
export default LinkAction;
|
||||
|
@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
import { createOpenMct, getMockObjects, resetApplicationState } from 'utils/testing';
|
||||
|
||||
import LinkAction from './LinkAction.js';
|
||||
import { LINK_ACTION_KEY } from './LinkAction.js';
|
||||
import LinkActionPlugin from './plugin.js';
|
||||
|
||||
describe('The Link Action plugin', () => {
|
||||
@ -31,8 +31,7 @@ describe('The Link Action plugin', () => {
|
||||
let parentObject;
|
||||
let anotherParentObject;
|
||||
const ORIGINAL_PARENT_ID = 'original-parent-object';
|
||||
const LINK_ACITON_KEY = 'link';
|
||||
const LINK_ACITON_NAME = 'Create Link';
|
||||
const LINK_ACTION_NAME = 'Create Link';
|
||||
|
||||
beforeEach((done) => {
|
||||
const appHolder = document.createElement('div');
|
||||
@ -97,14 +96,14 @@ describe('The Link Action plugin', () => {
|
||||
it('should make the link action available for an appropriate domainObject', () => {
|
||||
const actionCollection = openmct.actions.getActionsCollection([childObject]);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
linkAction = visibleActions.find((a) => a.key === LINK_ACITON_KEY);
|
||||
linkAction = visibleActions.find((a) => a.key === LINK_ACTION_KEY);
|
||||
|
||||
expect(linkAction.name).toEqual(LINK_ACITON_NAME);
|
||||
expect(linkAction.name).toEqual(LINK_ACTION_NAME);
|
||||
});
|
||||
|
||||
describe('when linking an object in a new parent', () => {
|
||||
beforeEach(() => {
|
||||
linkAction = new LinkAction(openmct);
|
||||
linkAction = openmct.actions.getAction(LINK_ACTION_KEY);
|
||||
linkAction.linkInNewParent(childObject, anotherParentObject);
|
||||
});
|
||||
|
||||
|
@ -19,10 +19,13 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
export default class MoveAction {
|
||||
|
||||
const MOVE_ACTION_KEY = 'move';
|
||||
|
||||
class MoveAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Move';
|
||||
this.key = 'move';
|
||||
this.key = MOVE_ACTION_KEY;
|
||||
this.description = 'Move this object from its containing object to another object.';
|
||||
this.cssClass = 'icon-move';
|
||||
this.group = 'action';
|
||||
@ -216,3 +219,7 @@ export default class MoveAction {
|
||||
this.transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { MOVE_ACTION_KEY };
|
||||
|
||||
export default MoveAction;
|
||||
|
@ -19,13 +19,14 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import CreateAction from '@/plugins/formActions/CreateAction';
|
||||
|
||||
export default class NewFolderAction {
|
||||
const NEW_FOLDER_ACTION_KEY = 'newFolder';
|
||||
|
||||
class NewFolderAction {
|
||||
constructor(openmct) {
|
||||
this.type = 'folder';
|
||||
this.name = 'Add New Folder';
|
||||
this.key = 'newFolder';
|
||||
this.key = NEW_FOLDER_ACTION_KEY;
|
||||
this.description = 'Create a new folder';
|
||||
this.cssClass = 'icon-folder-new';
|
||||
this.group = 'action';
|
||||
@ -36,8 +37,8 @@ export default class NewFolderAction {
|
||||
|
||||
invoke(objectPath) {
|
||||
const parentDomainObject = objectPath[0];
|
||||
const createAction = new CreateAction(this._openmct, this.type, parentDomainObject);
|
||||
createAction.invoke();
|
||||
const createAction = this._openmct.actions.getAction('create');
|
||||
createAction.invoke(this.type, parentDomainObject);
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
@ -47,3 +48,7 @@ export default class NewFolderAction {
|
||||
return domainObject.type === this.type && isPersistable;
|
||||
}
|
||||
}
|
||||
|
||||
export { NEW_FOLDER_ACTION_KEY };
|
||||
|
||||
export default NewFolderAction;
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { addNotebookEntry } from '../utils/notebook-entries.js';
|
||||
import { getDefaultNotebook, getNotebookSectionAndPage } from '../utils/notebook-storage.js';
|
||||
|
||||
export default class CopyToNotebookAction {
|
||||
const COPY_TO_NOTEBOOK_ACTION_KEY = 'copyToNotebook';
|
||||
class CopyToNotebookAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.description = 'Copy value to notebook as an entry';
|
||||
this.group = 'action';
|
||||
this.key = 'copyToNotebook';
|
||||
this.key = COPY_TO_NOTEBOOK_ACTION_KEY;
|
||||
this.name = 'Copy to Notebook';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -49,3 +50,7 @@ export default class CopyToNotebookAction {
|
||||
return row.formattedValueForCopy && typeof row.formattedValueForCopy === 'function';
|
||||
}
|
||||
}
|
||||
|
||||
export { COPY_TO_NOTEBOOK_ACTION_KEY };
|
||||
|
||||
export default CopyToNotebookAction;
|
||||
|
@ -5,15 +5,16 @@ import { NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE } from '../notebook-constants.j
|
||||
const UNKNOWN_USER = 'Unknown';
|
||||
const UNKNOWN_TIME = 'Unknown';
|
||||
const ALLOWED_TYPES = [NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE];
|
||||
const EXPORT_NOTEBOOK_AS_TEXT_ACTION_KEY = 'exportNotebookAsText';
|
||||
|
||||
export default class ExportNotebookAsTextAction {
|
||||
class ExportNotebookAsTextAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-export';
|
||||
this.description = 'Exports notebook contents as a text file';
|
||||
this.group = 'export';
|
||||
this.key = 'exportNotebookAsText';
|
||||
this.key = EXPORT_NOTEBOOK_AS_TEXT_ACTION_KEY;
|
||||
this.name = 'Export Notebook as Text';
|
||||
}
|
||||
|
||||
@ -179,3 +180,7 @@ export default class ExportNotebookAsTextAction {
|
||||
return this.onSave(changes, objectPath);
|
||||
}
|
||||
}
|
||||
|
||||
export { EXPORT_NOTEBOOK_AS_TEXT_ACTION_KEY };
|
||||
|
||||
export default ExportNotebookAsTextAction;
|
||||
|
@ -54,10 +54,10 @@ import Moment from 'moment';
|
||||
import mount from 'utils/mount';
|
||||
|
||||
import { objectPathToUrl } from '@/tools/url';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import ImageExporter from '../../../exporters/ImageExporter.js';
|
||||
import PreviewAction from '../../../ui/preview/PreviewAction.js';
|
||||
import { updateNotebookImageDomainObject } from '../utils/notebook-image.js';
|
||||
import PainterroInstance from '../utils/painterroInstance.js';
|
||||
import RemoveDialog from '../utils/removeDialog.js';
|
||||
@ -393,10 +393,9 @@ export default {
|
||||
}
|
||||
},
|
||||
previewEmbed() {
|
||||
const self = this;
|
||||
const previewAction = new PreviewAction(self.openmct);
|
||||
const previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.openmct.objects
|
||||
.get(self.embed.domainObject.identifier)
|
||||
.get(this.embed.domainObject.identifier)
|
||||
.then((domainObject) => previewAction.invoke([domainObject]));
|
||||
},
|
||||
removeEmbed(success) {
|
||||
|
@ -20,10 +20,13 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import { objectPathToUrl } from '/src/tools/url.js';
|
||||
export default class OpenInNewTab {
|
||||
|
||||
const NEW_TAB_ACTION_KEY = 'newTab';
|
||||
|
||||
class OpenInNewTab {
|
||||
constructor(openmct) {
|
||||
this.name = 'Open In New Tab';
|
||||
this.key = 'newTab';
|
||||
this.key = NEW_TAB_ACTION_KEY;
|
||||
this.description = 'Open in a new browser tab';
|
||||
this.group = 'windowing';
|
||||
this.priority = 10;
|
||||
@ -54,3 +57,7 @@ export default class OpenInNewTab {
|
||||
window.open(url, undefined, 'noopener');
|
||||
}
|
||||
}
|
||||
|
||||
export { NEW_TAB_ACTION_KEY };
|
||||
|
||||
export default OpenInNewTab;
|
||||
|
@ -550,7 +550,7 @@ export default {
|
||||
this.canvas = mainCanvas;
|
||||
this.overlay = overlayCanvas;
|
||||
this.drawAPI = DrawLoader.getDrawAPI(mainCanvas, overlayCanvas);
|
||||
if (this.drawAPI) {
|
||||
if (this.drawAPI?.on) {
|
||||
this.listenTo(this.drawAPI, 'error', this.fallbackToCanvas, this);
|
||||
}
|
||||
|
||||
|
@ -39,113 +39,104 @@ import { MARKER_SHAPES } from './MarkerShapes.js';
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable
|
||||
*/
|
||||
function Draw2D(canvas) {
|
||||
this.canvas = canvas;
|
||||
this.c2d = canvas.getContext('2d');
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.dimensions = [this.width, this.height];
|
||||
this.origin = [0, 0];
|
||||
class Draw2D extends EventEmitter {
|
||||
constructor(canvas) {
|
||||
super();
|
||||
eventHelpers.extend(this);
|
||||
this.canvas = canvas;
|
||||
this.c2d = canvas.getContext('2d');
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.dimensions = [this.width, this.height];
|
||||
this.origin = [0, 0];
|
||||
|
||||
if (!this.c2d) {
|
||||
throw new Error('Canvas 2d API unavailable.');
|
||||
if (!this.c2d) {
|
||||
throw new Error('Canvas 2d API unavailable.');
|
||||
}
|
||||
}
|
||||
// Convert from logical to physical x coordinates
|
||||
x(v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
}
|
||||
// Convert from logical to physical y coordinates
|
||||
y(v) {
|
||||
return this.height - ((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
}
|
||||
// Set the color to be used for drawing operations
|
||||
setColor(color) {
|
||||
const mappedColor = color
|
||||
.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : c;
|
||||
})
|
||||
.join(',');
|
||||
this.c2d.strokeStyle = 'rgba(' + mappedColor + ')';
|
||||
this.c2d.fillStyle = 'rgba(' + mappedColor + ')';
|
||||
}
|
||||
clear() {
|
||||
this.width = this.canvas.width = this.canvas.offsetWidth;
|
||||
this.height = this.canvas.height = this.canvas.offsetHeight;
|
||||
this.c2d.clearRect(0, 0, this.width, this.height);
|
||||
}
|
||||
setDimensions(newDimensions, newOrigin) {
|
||||
this.dimensions = newDimensions;
|
||||
this.origin = newOrigin;
|
||||
}
|
||||
drawLine(buf, color, points) {
|
||||
let i;
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
// Configure context to draw two-pixel-thick lines
|
||||
this.c2d.lineWidth = 1;
|
||||
|
||||
// Start a new path...
|
||||
if (buf.length > 1) {
|
||||
this.c2d.beginPath();
|
||||
this.c2d.moveTo(this.x(buf[0]), this.y(buf[1]));
|
||||
}
|
||||
|
||||
// ...and add points to it...
|
||||
for (i = 2; i < points * 2; i = i + 2) {
|
||||
this.c2d.lineTo(this.x(buf[i]), this.y(buf[i + 1]));
|
||||
}
|
||||
|
||||
// ...before finally drawing it.
|
||||
this.c2d.stroke();
|
||||
}
|
||||
drawSquare(min, max, color) {
|
||||
const x1 = this.x(min[0]);
|
||||
const y1 = this.y(min[1]);
|
||||
const w = this.x(max[0]) - x1;
|
||||
const h = this.y(max[1]) - y1;
|
||||
|
||||
this.setColor(color);
|
||||
this.c2d.fillRect(x1, y1, w, h);
|
||||
}
|
||||
drawPoints(buf, color, points, pointSize, shape) {
|
||||
const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
for (let i = 0; i < points; i++) {
|
||||
drawC2DShape(this.x(buf[i * 2]), this.y(buf[i * 2 + 1]), pointSize);
|
||||
}
|
||||
}
|
||||
drawLimitPoint(x, y, size) {
|
||||
this.c2d.fillRect(x + size, y, size, size);
|
||||
this.c2d.fillRect(x, y + size, size, size);
|
||||
this.c2d.fillRect(x - size, y, size, size);
|
||||
this.c2d.fillRect(x, y - size, size, size);
|
||||
}
|
||||
drawLimitPoints(points, color, pointSize) {
|
||||
const limitSize = pointSize * 2;
|
||||
const offset = limitSize / 2;
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
this.drawLimitPoint(this.x(points[i].x) - offset, this.y(points[i].y) - offset, limitSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(Draw2D.prototype, EventEmitter.prototype);
|
||||
eventHelpers.extend(Draw2D.prototype);
|
||||
|
||||
// Convert from logical to physical x coordinates
|
||||
Draw2D.prototype.x = function (v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
};
|
||||
|
||||
// Convert from logical to physical y coordinates
|
||||
Draw2D.prototype.y = function (v) {
|
||||
return this.height - ((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
};
|
||||
|
||||
// Set the color to be used for drawing operations
|
||||
Draw2D.prototype.setColor = function (color) {
|
||||
const mappedColor = color
|
||||
.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : c;
|
||||
})
|
||||
.join(',');
|
||||
this.c2d.strokeStyle = 'rgba(' + mappedColor + ')';
|
||||
this.c2d.fillStyle = 'rgba(' + mappedColor + ')';
|
||||
};
|
||||
|
||||
Draw2D.prototype.clear = function () {
|
||||
this.width = this.canvas.width = this.canvas.offsetWidth;
|
||||
this.height = this.canvas.height = this.canvas.offsetHeight;
|
||||
this.c2d.clearRect(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
Draw2D.prototype.setDimensions = function (newDimensions, newOrigin) {
|
||||
this.dimensions = newDimensions;
|
||||
this.origin = newOrigin;
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawLine = function (buf, color, points) {
|
||||
let i;
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
// Configure context to draw two-pixel-thick lines
|
||||
this.c2d.lineWidth = 1;
|
||||
|
||||
// Start a new path...
|
||||
if (buf.length > 1) {
|
||||
this.c2d.beginPath();
|
||||
this.c2d.moveTo(this.x(buf[0]), this.y(buf[1]));
|
||||
}
|
||||
|
||||
// ...and add points to it...
|
||||
for (i = 2; i < points * 2; i = i + 2) {
|
||||
this.c2d.lineTo(this.x(buf[i]), this.y(buf[i + 1]));
|
||||
}
|
||||
|
||||
// ...before finally drawing it.
|
||||
this.c2d.stroke();
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawSquare = function (min, max, color) {
|
||||
const x1 = this.x(min[0]);
|
||||
const y1 = this.y(min[1]);
|
||||
const w = this.x(max[0]) - x1;
|
||||
const h = this.y(max[1]) - y1;
|
||||
|
||||
this.setColor(color);
|
||||
this.c2d.fillRect(x1, y1, w, h);
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
|
||||
const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
for (let i = 0; i < points; i++) {
|
||||
drawC2DShape(this.x(buf[i * 2]), this.y(buf[i * 2 + 1]), pointSize);
|
||||
}
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawLimitPoint = function (x, y, size) {
|
||||
this.c2d.fillRect(x + size, y, size, size);
|
||||
this.c2d.fillRect(x, y + size, size, size);
|
||||
this.c2d.fillRect(x - size, y, size, size);
|
||||
this.c2d.fillRect(x, y - size, size, size);
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawLimitPoints = function (points, color, pointSize) {
|
||||
const limitSize = pointSize * 2;
|
||||
const offset = limitSize / 2;
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
this.drawLimitPoint(this.x(points[i].x) - offset, this.y(points[i].y) - offset, limitSize);
|
||||
}
|
||||
};
|
||||
|
||||
export default Draw2D;
|
||||
|
@ -83,231 +83,219 @@ const VERTEX_SHADER = `
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if WebGL is unavailable.
|
||||
*/
|
||||
function DrawWebGL(canvas, overlay) {
|
||||
this.canvas = canvas;
|
||||
this.gl =
|
||||
this.canvas.getContext('webgl', { preserveDrawingBuffer: true }) ||
|
||||
this.canvas.getContext('experimental-webgl', { preserveDrawingBuffer: true });
|
||||
class DrawWebGL extends EventEmitter {
|
||||
constructor(canvas, overlay) {
|
||||
super();
|
||||
eventHelpers.extend(this);
|
||||
this.canvas = canvas;
|
||||
this.gl =
|
||||
this.canvas.getContext('webgl', { preserveDrawingBuffer: true }) ||
|
||||
this.canvas.getContext('experimental-webgl', { preserveDrawingBuffer: true });
|
||||
|
||||
this.overlay = overlay;
|
||||
this.c2d = overlay.getContext('2d');
|
||||
if (!this.c2d) {
|
||||
throw new Error('No canvas 2d!');
|
||||
this.overlay = overlay;
|
||||
this.c2d = overlay.getContext('2d');
|
||||
if (!this.c2d) {
|
||||
throw new Error('No canvas 2d!');
|
||||
}
|
||||
|
||||
// Ensure a context was actually available before proceeding
|
||||
if (!this.gl) {
|
||||
throw new Error('WebGL unavailable.');
|
||||
}
|
||||
|
||||
this.initContext();
|
||||
|
||||
this.listenTo(this.canvas, 'webglcontextlost', this.onContextLost, this);
|
||||
}
|
||||
|
||||
// Ensure a context was actually available before proceeding
|
||||
if (!this.gl) {
|
||||
throw new Error('WebGL unavailable.');
|
||||
onContextLost(event) {
|
||||
this.emit('error');
|
||||
this.isContextLost = true;
|
||||
this.destroy();
|
||||
// TODO re-initialize and re-draw on context restored
|
||||
}
|
||||
initContext() {
|
||||
// Initialize shaders
|
||||
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
||||
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
||||
this.gl.compileShader(this.vertexShader);
|
||||
|
||||
this.initContext();
|
||||
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
||||
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
|
||||
this.gl.compileShader(this.fragmentShader);
|
||||
|
||||
this.listenTo(this.canvas, 'webglcontextlost', this.onContextLost, this);
|
||||
// Assemble vertex/fragment shaders into programs
|
||||
this.program = this.gl.createProgram();
|
||||
this.gl.attachShader(this.program, this.vertexShader);
|
||||
this.gl.attachShader(this.program, this.fragmentShader);
|
||||
this.gl.linkProgram(this.program);
|
||||
this.gl.useProgram(this.program);
|
||||
|
||||
// Get locations for attribs/uniforms from the
|
||||
// shader programs (to pass values into shaders at draw-time)
|
||||
this.aVertexPosition = this.gl.getAttribLocation(this.program, 'aVertexPosition');
|
||||
this.uColor = this.gl.getUniformLocation(this.program, 'uColor');
|
||||
this.uMarkerShape = this.gl.getUniformLocation(this.program, 'uMarkerShape');
|
||||
this.uDimensions = this.gl.getUniformLocation(this.program, 'uDimensions');
|
||||
this.uOrigin = this.gl.getUniformLocation(this.program, 'uOrigin');
|
||||
this.uPointSize = this.gl.getUniformLocation(this.program, 'uPointSize');
|
||||
|
||||
this.gl.enableVertexAttribArray(this.aVertexPosition);
|
||||
|
||||
// Create a buffer to holds points which will be drawn
|
||||
this.buffer = this.gl.createBuffer();
|
||||
|
||||
// Enable blending, for smoothness
|
||||
this.gl.enable(this.gl.BLEND);
|
||||
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
destroy() {
|
||||
// Lose the context and delete all associated resources
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#lose_contexts_eagerly
|
||||
this.gl?.getExtension('WEBGL_lose_context')?.loseContext();
|
||||
this.gl?.deleteBuffer(this.buffer);
|
||||
this.buffer = undefined;
|
||||
this.gl?.deleteProgram(this.program);
|
||||
this.program = undefined;
|
||||
this.gl?.deleteShader(this.vertexShader);
|
||||
this.vertexShader = undefined;
|
||||
this.gl?.deleteShader(this.fragmentShader);
|
||||
this.fragmentShader = undefined;
|
||||
this.gl = undefined;
|
||||
|
||||
this.stopListening();
|
||||
this.canvas = undefined;
|
||||
this.overlay = undefined;
|
||||
}
|
||||
// Convert from logical to physical x coordinates
|
||||
x(v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
}
|
||||
// Convert from logical to physical y coordinates
|
||||
y(v) {
|
||||
return this.height - ((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
}
|
||||
doDraw(drawType, buf, color, points, shape) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
|
||||
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
|
||||
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
|
||||
this.gl.uniform4fv(this.uColor, color);
|
||||
this.gl.uniform1i(this.uMarkerShape, shapeCode);
|
||||
if (points !== 0) {
|
||||
this.gl.drawArrays(drawType, 0, points);
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.height = this.canvas.height = this.canvas.offsetHeight;
|
||||
this.width = this.canvas.width = this.canvas.offsetWidth;
|
||||
this.overlay.height = this.overlay.offsetHeight;
|
||||
this.overlay.width = this.overlay.offsetWidth;
|
||||
// Set the viewport size; note that we use the width/height
|
||||
// that our WebGL context reports, which may be lower
|
||||
// resolution than the canvas we requested.
|
||||
this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT + this.gl.DEPTH_BUFFER_BIT);
|
||||
}
|
||||
/**
|
||||
* Set the logical boundaries of the chart.
|
||||
* @param {number[]} dimensions the horizontal and
|
||||
* vertical dimensions of the chart
|
||||
* @param {number[]} origin the horizontal/vertical
|
||||
* origin of the chart
|
||||
*/
|
||||
setDimensions(dimensions, origin) {
|
||||
this.dimensions = dimensions;
|
||||
this.origin = origin;
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dimensions && dimensions.length > 0 && origin && origin.length > 0) {
|
||||
this.gl?.uniform2fv(this.uDimensions, dimensions);
|
||||
this.gl?.uniform2fv(this.uOrigin, origin);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Draw the supplied buffer as a line strip (a sequence
|
||||
* of line segments), in the chosen color.
|
||||
* @param {Float32Array} buf the line strip to draw,
|
||||
* in alternating x/y positions
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the line, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @param {number} points the number of points to draw
|
||||
*/
|
||||
drawLine(buf, color, points) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
||||
}
|
||||
/**
|
||||
* Draw the buffer as points.
|
||||
*
|
||||
*/
|
||||
drawPoints(buf, color, points, pointSize, shape) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gl.uniform1f(this.uPointSize, pointSize);
|
||||
this.doDraw(this.gl.POINTS, buf, color, points, shape);
|
||||
}
|
||||
/**
|
||||
* Draw a rectangle extending from one corner to another,
|
||||
* in the chosen color.
|
||||
* @param {number[]} min the first corner of the rectangle
|
||||
* @param {number[]} max the opposite corner
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the rectangle, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
*/
|
||||
drawSquare(min, max, color) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doDraw(
|
||||
this.gl.TRIANGLE_FAN,
|
||||
new Float32Array(min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])),
|
||||
color,
|
||||
4
|
||||
);
|
||||
}
|
||||
drawLimitPoint(x, y, size) {
|
||||
this.c2d.fillRect(x + size, y, size, size);
|
||||
this.c2d.fillRect(x, y + size, size, size);
|
||||
this.c2d.fillRect(x - size, y, size, size);
|
||||
this.c2d.fillRect(x, y - size, size, size);
|
||||
}
|
||||
drawLimitPoints(points, color, pointSize) {
|
||||
const limitSize = pointSize * 2;
|
||||
const offset = limitSize / 2;
|
||||
|
||||
const mappedColor = color
|
||||
.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : c;
|
||||
})
|
||||
.join(',');
|
||||
this.c2d.strokeStyle = 'rgba(' + mappedColor + ')';
|
||||
this.c2d.fillStyle = 'rgba(' + mappedColor + ')';
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
this.drawLimitPoint(this.x(points[i].x) - offset, this.y(points[i].y) - offset, limitSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(DrawWebGL.prototype, EventEmitter.prototype);
|
||||
eventHelpers.extend(DrawWebGL.prototype);
|
||||
|
||||
DrawWebGL.prototype.onContextLost = function (event) {
|
||||
this.emit('error');
|
||||
this.isContextLost = true;
|
||||
this.destroy();
|
||||
// TODO re-initialize and re-draw on context restored
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.initContext = function () {
|
||||
// Initialize shaders
|
||||
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
||||
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
||||
this.gl.compileShader(this.vertexShader);
|
||||
|
||||
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
||||
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
|
||||
this.gl.compileShader(this.fragmentShader);
|
||||
|
||||
// Assemble vertex/fragment shaders into programs
|
||||
this.program = this.gl.createProgram();
|
||||
this.gl.attachShader(this.program, this.vertexShader);
|
||||
this.gl.attachShader(this.program, this.fragmentShader);
|
||||
this.gl.linkProgram(this.program);
|
||||
this.gl.useProgram(this.program);
|
||||
|
||||
// Get locations for attribs/uniforms from the
|
||||
// shader programs (to pass values into shaders at draw-time)
|
||||
this.aVertexPosition = this.gl.getAttribLocation(this.program, 'aVertexPosition');
|
||||
this.uColor = this.gl.getUniformLocation(this.program, 'uColor');
|
||||
this.uMarkerShape = this.gl.getUniformLocation(this.program, 'uMarkerShape');
|
||||
this.uDimensions = this.gl.getUniformLocation(this.program, 'uDimensions');
|
||||
this.uOrigin = this.gl.getUniformLocation(this.program, 'uOrigin');
|
||||
this.uPointSize = this.gl.getUniformLocation(this.program, 'uPointSize');
|
||||
|
||||
this.gl.enableVertexAttribArray(this.aVertexPosition);
|
||||
|
||||
// Create a buffer to holds points which will be drawn
|
||||
this.buffer = this.gl.createBuffer();
|
||||
|
||||
// Enable blending, for smoothness
|
||||
this.gl.enable(this.gl.BLEND);
|
||||
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.destroy = function () {
|
||||
// Lose the context and delete all associated resources
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#lose_contexts_eagerly
|
||||
this.gl?.getExtension('WEBGL_lose_context')?.loseContext();
|
||||
this.gl?.deleteBuffer(this.buffer);
|
||||
this.buffer = undefined;
|
||||
this.gl?.deleteProgram(this.program);
|
||||
this.program = undefined;
|
||||
this.gl?.deleteShader(this.vertexShader);
|
||||
this.vertexShader = undefined;
|
||||
this.gl?.deleteShader(this.fragmentShader);
|
||||
this.fragmentShader = undefined;
|
||||
this.gl = undefined;
|
||||
|
||||
this.stopListening();
|
||||
this.canvas = undefined;
|
||||
this.overlay = undefined;
|
||||
};
|
||||
|
||||
// Convert from logical to physical x coordinates
|
||||
DrawWebGL.prototype.x = function (v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
};
|
||||
|
||||
// Convert from logical to physical y coordinates
|
||||
DrawWebGL.prototype.y = function (v) {
|
||||
return this.height - ((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
|
||||
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
|
||||
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
|
||||
this.gl.uniform4fv(this.uColor, color);
|
||||
this.gl.uniform1i(this.uMarkerShape, shapeCode);
|
||||
if (points !== 0) {
|
||||
this.gl.drawArrays(drawType, 0, points);
|
||||
}
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.clear = function () {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.height = this.canvas.height = this.canvas.offsetHeight;
|
||||
this.width = this.canvas.width = this.canvas.offsetWidth;
|
||||
this.overlay.height = this.overlay.offsetHeight;
|
||||
this.overlay.width = this.overlay.offsetWidth;
|
||||
// Set the viewport size; note that we use the width/height
|
||||
// that our WebGL context reports, which may be lower
|
||||
// resolution than the canvas we requested.
|
||||
this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT + this.gl.DEPTH_BUFFER_BIT);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the logical boundaries of the chart.
|
||||
* @param {number[]} dimensions the horizontal and
|
||||
* vertical dimensions of the chart
|
||||
* @param {number[]} origin the horizontal/vertical
|
||||
* origin of the chart
|
||||
*/
|
||||
DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
|
||||
this.dimensions = dimensions;
|
||||
this.origin = origin;
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dimensions && dimensions.length > 0 && origin && origin.length > 0) {
|
||||
this.gl?.uniform2fv(this.uDimensions, dimensions);
|
||||
this.gl?.uniform2fv(this.uOrigin, origin);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the supplied buffer as a line strip (a sequence
|
||||
* of line segments), in the chosen color.
|
||||
* @param {Float32Array} buf the line strip to draw,
|
||||
* in alternating x/y positions
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the line, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @param {number} points the number of points to draw
|
||||
*/
|
||||
DrawWebGL.prototype.drawLine = function (buf, color, points) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the buffer as points.
|
||||
*
|
||||
*/
|
||||
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gl.uniform1f(this.uPointSize, pointSize);
|
||||
this.doDraw(this.gl.POINTS, buf, color, points, shape);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a rectangle extending from one corner to another,
|
||||
* in the chosen color.
|
||||
* @param {number[]} min the first corner of the rectangle
|
||||
* @param {number[]} max the opposite corner
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the rectangle, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
*/
|
||||
DrawWebGL.prototype.drawSquare = function (min, max, color) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doDraw(
|
||||
this.gl.TRIANGLE_FAN,
|
||||
new Float32Array(min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])),
|
||||
color,
|
||||
4
|
||||
);
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.drawLimitPoint = function (x, y, size) {
|
||||
this.c2d.fillRect(x + size, y, size, size);
|
||||
this.c2d.fillRect(x, y + size, size, size);
|
||||
this.c2d.fillRect(x - size, y, size, size);
|
||||
this.c2d.fillRect(x, y - size, size, size);
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.drawLimitPoints = function (points, color, pointSize) {
|
||||
const limitSize = pointSize * 2;
|
||||
const offset = limitSize / 2;
|
||||
|
||||
const mappedColor = color
|
||||
.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : c;
|
||||
})
|
||||
.join(',');
|
||||
this.c2d.strokeStyle = 'rgba(' + mappedColor + ')';
|
||||
this.c2d.fillStyle = 'rgba(' + mappedColor + ')';
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
this.drawLimitPoint(this.x(points[i].x) - offset, this.y(points[i].y) - offset, limitSize);
|
||||
}
|
||||
};
|
||||
|
||||
export default DrawWebGL;
|
||||
|
@ -37,7 +37,7 @@ const helperFunctions = {
|
||||
if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
object.on(event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
@ -74,7 +74,7 @@ const helperFunctions = {
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
listener.object.off(listener.event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
return listener;
|
||||
|
@ -19,10 +19,13 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
export default class ReloadAction {
|
||||
|
||||
const RELOAD_ACTION_KEY = 'reload';
|
||||
|
||||
class ReloadAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Reload';
|
||||
this.key = 'reload';
|
||||
this.key = RELOAD_ACTION_KEY;
|
||||
this.description = 'Reload this object and its children';
|
||||
this.group = 'action';
|
||||
this.priority = 10;
|
||||
@ -35,3 +38,7 @@ export default class ReloadAction {
|
||||
this.openmct.objectViews.emit('reload', domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
export { RELOAD_ACTION_KEY };
|
||||
|
||||
export default ReloadAction;
|
||||
|
@ -21,13 +21,14 @@
|
||||
*****************************************************************************/
|
||||
|
||||
const SPECIAL_MESSAGE_TYPES = ['layout', 'flexible-layout'];
|
||||
const REMOVE_ACTION_KEY = 'remove';
|
||||
|
||||
export default class RemoveAction {
|
||||
class RemoveAction {
|
||||
#transaction;
|
||||
|
||||
constructor(openmct) {
|
||||
this.name = 'Remove';
|
||||
this.key = 'remove';
|
||||
this.key = REMOVE_ACTION_KEY;
|
||||
this.description = 'Remove this object from its containing object.';
|
||||
this.cssClass = 'icon-trash';
|
||||
this.group = 'action';
|
||||
@ -162,3 +163,7 @@ export default class RemoveAction {
|
||||
this.#transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { REMOVE_ACTION_KEY };
|
||||
|
||||
export default RemoveAction;
|
||||
|
@ -41,7 +41,7 @@ const helperFunctions = {
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
object.on(event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
@ -78,7 +78,7 @@ const helperFunctions = {
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
listener.object.off(listener.event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
return listener;
|
||||
|
@ -91,7 +91,6 @@ import _ from 'lodash';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import ObjectView from '../../../ui/components/ObjectView.vue';
|
||||
import RemoveAction from '../../remove/RemoveAction.js';
|
||||
|
||||
const unknownObjectType = {
|
||||
definition: {
|
||||
@ -171,7 +170,6 @@ export default {
|
||||
this.updateCurrentTab = this.updateCurrentTab.bind(this);
|
||||
this.openmct.router.on('change:params', this.updateCurrentTab);
|
||||
|
||||
this.RemoveAction = new RemoveAction(this.openmct);
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
},
|
||||
|
@ -19,6 +19,8 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import { VIEW_DATUM_ACTION_KEY } from '@/plugins/viewDatumAction/ViewDatumAction.js';
|
||||
import { VIEW_HISTORICAL_DATA_ACTION_KEY } from '@/ui/preview/ViewHistoricalDataAction.js';
|
||||
|
||||
export default class TelemetryTableRow {
|
||||
constructor(datum, columns, objectKeyString, limitEvaluator, inPlaceUpdateKey) {
|
||||
@ -86,7 +88,7 @@ export default class TelemetryTableRow {
|
||||
}
|
||||
|
||||
getContextMenuActions() {
|
||||
return ['viewDatumAction', 'viewHistoricalData'];
|
||||
return [VIEW_DATUM_ACTION_KEY, VIEW_HISTORICAL_DATA_ACTION_KEY];
|
||||
}
|
||||
|
||||
updateWithDatum(updatesToDatum) {
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class PauseTimerAction {
|
||||
const PAUSE_TIMER_ACTION_KEY = 'timer.pause';
|
||||
|
||||
class PauseTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Pause';
|
||||
this.key = 'timer.pause';
|
||||
this.key = PAUSE_TIMER_ACTION_KEY;
|
||||
this.description = 'Pause the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-pause';
|
||||
@ -59,3 +61,7 @@ export default class PauseTimerAction {
|
||||
: domainObject.type === 'timer' && timerState === 'started';
|
||||
}
|
||||
}
|
||||
|
||||
export { PAUSE_TIMER_ACTION_KEY };
|
||||
|
||||
export default PauseTimerAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class RestartTimerAction {
|
||||
const RESTART_TIMER_ACTION_KEY = 'timer.restart';
|
||||
|
||||
class RestartTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Restart at 0';
|
||||
this.key = 'timer.restart';
|
||||
this.key = RESTART_TIMER_ACTION_KEY;
|
||||
this.description = 'Restart the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-refresh';
|
||||
@ -60,3 +62,7 @@ export default class RestartTimerAction {
|
||||
: domainObject.type === 'timer' && timerState !== 'stopped';
|
||||
}
|
||||
}
|
||||
|
||||
export { RESTART_TIMER_ACTION_KEY };
|
||||
|
||||
export default RestartTimerAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class StartTimerAction {
|
||||
const START_TIMER_ACTION_KEY = 'timer.start';
|
||||
|
||||
class StartTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Start';
|
||||
this.key = 'timer.start';
|
||||
this.key = START_TIMER_ACTION_KEY;
|
||||
this.description = 'Start the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-play';
|
||||
@ -72,3 +74,7 @@ export default class StartTimerAction {
|
||||
: domainObject.type === 'timer' && timerState !== 'started';
|
||||
}
|
||||
}
|
||||
|
||||
export { START_TIMER_ACTION_KEY };
|
||||
|
||||
export default StartTimerAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class StopTimerAction {
|
||||
const STOP_TIMER_ACTION_KEY = 'timer.stop';
|
||||
|
||||
class StopTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Stop';
|
||||
this.key = 'timer.stop';
|
||||
this.key = STOP_TIMER_ACTION_KEY;
|
||||
this.description = 'Stop the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-box-round-corners';
|
||||
@ -60,3 +62,7 @@ export default class StopTimerAction {
|
||||
: domainObject.type === 'timer' && timerState !== 'stopped';
|
||||
}
|
||||
}
|
||||
|
||||
export { STOP_TIMER_ACTION_KEY };
|
||||
|
||||
export default StopTimerAction;
|
||||
|
@ -24,10 +24,12 @@ import mount from 'utils/mount';
|
||||
|
||||
import MetadataListView from './components/MetadataList.vue';
|
||||
|
||||
export default class ViewDatumAction {
|
||||
const VIEW_DATUM_ACTION_KEY = 'viewDatumAction';
|
||||
|
||||
class ViewDatumAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'View Full Datum';
|
||||
this.key = 'viewDatumAction';
|
||||
this.key = VIEW_DATUM_ACTION_KEY;
|
||||
this.description = 'View full value of datum received';
|
||||
this.cssClass = 'icon-object';
|
||||
|
||||
@ -76,3 +78,7 @@ export default class ViewDatumAction {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { VIEW_DATUM_ACTION_KEY };
|
||||
|
||||
export default ViewDatumAction;
|
||||
|
@ -24,14 +24,16 @@ import mount from 'utils/mount';
|
||||
|
||||
import PreviewContainer from '@/ui/preview/PreviewContainer.vue';
|
||||
|
||||
export default class ViewLargeAction {
|
||||
const VIEW_LARGE_ACTION_KEY = 'large.view';
|
||||
|
||||
class ViewLargeAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-items-expand';
|
||||
this.description = 'View Large';
|
||||
this.group = 'windowing';
|
||||
this.key = 'large.view';
|
||||
this.key = VIEW_LARGE_ACTION_KEY;
|
||||
this.name = 'Large View';
|
||||
this.priority = 1;
|
||||
this.showInStatusBar = true;
|
||||
@ -107,3 +109,7 @@ export default class ViewLargeAction {
|
||||
return this.preview.$el;
|
||||
}
|
||||
}
|
||||
|
||||
export { VIEW_LARGE_ACTION_KEY };
|
||||
|
||||
export default ViewLargeAction;
|
||||
|
@ -50,11 +50,12 @@
|
||||
<script>
|
||||
import { inject } from 'vue';
|
||||
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins.js';
|
||||
import { useIsEditing } from '../../ui/composables/edit.js';
|
||||
import ContextMenuGesture from '../mixins/context-menu-gesture.js';
|
||||
import ObjectLink from '../mixins/object-link.js';
|
||||
import PreviewAction from '../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
mixins: [ObjectLink, ContextMenuGesture, tooltipHelpers],
|
||||
@ -116,7 +117,7 @@ export default {
|
||||
this.setStatus
|
||||
);
|
||||
this.status = this.openmct.status.get(this.domainObject.identifier);
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
},
|
||||
unmounted() {
|
||||
this.removeStatusListener();
|
||||
|
@ -31,9 +31,8 @@
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CreateAction from '@/plugins/formActions/CreateAction';
|
||||
import { CREATE_ACTION_KEY } from '@/plugins/formActions/CreateAction';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
@ -102,9 +101,8 @@ export default {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
create(key) {
|
||||
const createAction = new CreateAction(this.openmct, key, this.openmct.router.path[0]);
|
||||
|
||||
createAction.invoke();
|
||||
const createAction = this.openmct.actions.getAction(CREATE_ACTION_KEY);
|
||||
createAction.invoke(key, this.openmct.router.path[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -62,9 +62,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins.js';
|
||||
import ObjectPath from '../components/ObjectPath.vue';
|
||||
import PreviewAction from '../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
name: 'RecentObjectsListItem',
|
||||
@ -99,7 +100,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
},
|
||||
unmounted() {
|
||||
|
@ -56,9 +56,10 @@
|
||||
import { Marked } from 'marked';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import { identifierToString } from '../../../../src/tools/url.js';
|
||||
import ObjectPath from '../../components/ObjectPath.vue';
|
||||
import PreviewAction from '../../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
name: 'AnnotationSearchResult',
|
||||
@ -109,12 +110,10 @@ export default {
|
||||
this.marked = new Marked();
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.fireAnnotationSelection = this.fireAnnotationSelection.bind(this);
|
||||
},
|
||||
unmounted() {
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
this.openmct.selection.off('change', this.fireAnnotationSelection);
|
||||
},
|
||||
methods: {
|
||||
|
@ -54,10 +54,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import { objectPathToUrl } from '../../../tools/url.js';
|
||||
import ObjectPath from '../../components/ObjectPath.vue';
|
||||
import PreviewAction from '../../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
name: 'ObjectSearchResult',
|
||||
@ -88,7 +89,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
},
|
||||
unmounted() {
|
||||
|
@ -24,14 +24,16 @@ import mount from 'utils/mount';
|
||||
|
||||
import PreviewContainer from './PreviewContainer.vue';
|
||||
|
||||
export default class PreviewAction extends EventEmitter {
|
||||
const PREVIEW_ACTION_KEY = 'preview';
|
||||
|
||||
class PreviewAction extends EventEmitter {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
/**
|
||||
* Metadata
|
||||
*/
|
||||
this.name = 'View';
|
||||
this.key = 'preview';
|
||||
this.key = PREVIEW_ACTION_KEY;
|
||||
this.description = 'View in large dialog';
|
||||
this.cssClass = 'icon-items-expand';
|
||||
this.group = 'windowing';
|
||||
@ -110,3 +112,7 @@ export default class PreviewAction extends EventEmitter {
|
||||
return noPreviewTypes.includes(objectPath[0].type);
|
||||
}
|
||||
}
|
||||
|
||||
export { PREVIEW_ACTION_KEY };
|
||||
|
||||
export default PreviewAction;
|
||||
|
@ -58,14 +58,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nextTick, toRaw } from 'vue';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import { CREATE_ACTION_KEY } from '@/plugins/formActions/CreateAction.js';
|
||||
import { MOVE_ACTION_KEY } from '@/plugins/move/MoveAction.js';
|
||||
import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwitcher.vue';
|
||||
import { RELOAD_ACTION_KEY } from '@/plugins/reloadAction/ReloadAction.js';
|
||||
import { REMOVE_ACTION_KEY } from '@/plugins/remove/RemoveAction.js';
|
||||
import { VIEW_LARGE_ACTION_KEY } from '@/plugins/viewLargeAction/viewLargeAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import ViewSwitcher from '../layout/ViewSwitcher.vue';
|
||||
|
||||
const HIDDEN_ACTIONS = ['remove', 'move', 'preview', 'large.view', 'reload'];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotebookMenuSwitcher,
|
||||
@ -107,23 +111,33 @@ export default {
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
async currentView() {
|
||||
// wait for view to render with next tick
|
||||
await nextTick();
|
||||
if (this.actionCollection) {
|
||||
this.unlistenToActionCollection();
|
||||
}
|
||||
currentView: {
|
||||
handler: function (newView) {
|
||||
if (this.actionCollection) {
|
||||
this.unlistenToActionCollection();
|
||||
}
|
||||
this.actionCollection = this.openmct.actions.getActionsCollection(
|
||||
toRaw(this.objectPath),
|
||||
toRaw(newView)
|
||||
);
|
||||
|
||||
this.actionCollection = this.openmct.actions.getActionsCollection(
|
||||
toRaw(this.objectPath),
|
||||
toRaw(this.currentView)
|
||||
);
|
||||
|
||||
this.actionCollection.on('update', this.updateActionItems);
|
||||
this.updateActionItems(this.actionCollection.getActionsObject());
|
||||
this.actionCollection.on('update', this.updateActionItems);
|
||||
this.updateActionItems(this.actionCollection.getActionsObject());
|
||||
},
|
||||
flush: 'post' // Access the DOM after Vue has updated it
|
||||
}
|
||||
},
|
||||
unmounted() {
|
||||
created() {
|
||||
this.HIDDEN_ACTIONS = [
|
||||
CREATE_ACTION_KEY,
|
||||
REMOVE_ACTION_KEY,
|
||||
MOVE_ACTION_KEY,
|
||||
PREVIEW_ACTION_KEY,
|
||||
VIEW_LARGE_ACTION_KEY,
|
||||
RELOAD_ACTION_KEY
|
||||
];
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.actionCollection) {
|
||||
this.actionCollection.off('update', this.updateActionItems);
|
||||
}
|
||||
@ -135,7 +149,7 @@ export default {
|
||||
const isGroup = Array.isArray(menuItem);
|
||||
if (isGroup) {
|
||||
items.push(this.filterHiddenItems(menuItem));
|
||||
} else if (!HIDDEN_ACTIONS.includes(menuItem.key)) {
|
||||
} else if (this.HIDDEN_ACTIONS.includes(menuItem.key) === false) {
|
||||
items.push(menuItem);
|
||||
}
|
||||
});
|
||||
|
@ -22,12 +22,14 @@
|
||||
|
||||
import PreviewAction from './PreviewAction.js';
|
||||
|
||||
export default class ViewHistoricalDataAction extends PreviewAction {
|
||||
const VIEW_HISTORICAL_DATA_ACTION_KEY = 'viewHistoricalData';
|
||||
|
||||
class ViewHistoricalDataAction extends PreviewAction {
|
||||
constructor(openmct) {
|
||||
super(openmct);
|
||||
|
||||
this.name = 'View Historical Data';
|
||||
this.key = 'viewHistoricalData';
|
||||
this.key = VIEW_HISTORICAL_DATA_ACTION_KEY;
|
||||
this.description = 'View Historical Data in a Table or Plot';
|
||||
this.cssClass = 'icon-eye-open';
|
||||
this.hideInDefaultMenu = true;
|
||||
@ -41,3 +43,7 @@ export default class ViewHistoricalDataAction extends PreviewAction {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { VIEW_HISTORICAL_DATA_ACTION_KEY };
|
||||
|
||||
export default ViewHistoricalDataAction;
|
||||
|
Loading…
Reference in New Issue
Block a user