Compare commits
	
		
			11 Commits
		
	
	
		
			release/2.
			...
			vue-hack
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 62ff404bb3 | ||
|   | 8243cf5d7b | ||
|   | c4c1fea17f | ||
|   | 5e920e90ce | ||
|   | 886db23eb6 | ||
|   | 0ccb546a2e | ||
|   | 271f8ed38f | ||
|   | 650f84e95c | ||
|   | b70af5a1bb | ||
|   | 0af21632db | ||
|   | e2f1ff5442 | 
| @@ -44,9 +44,11 @@ define( | ||||
|                         setText(result.name); | ||||
|                         scope.ngModel[scope.field] = result; | ||||
|                         control.$setValidity("file-input", true); | ||||
|                         scope.$digest(); | ||||
|                     }, function () { | ||||
|                         setText('Select File'); | ||||
|                         control.$setValidity("file-input", false); | ||||
|                         scope.$digest(); | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -288,6 +288,8 @@ define([ | ||||
|         this.install(this.plugins.ObjectInterceptors()); | ||||
|         this.install(this.plugins.NonEditableFolder()); | ||||
|         this.install(this.plugins.DeviceClassifier()); | ||||
|  | ||||
|         this._isVue = true; | ||||
|     } | ||||
|  | ||||
|     MCT.prototype = Object.create(EventEmitter.prototype); | ||||
|   | ||||
| @@ -81,14 +81,8 @@ define([ | ||||
|                     return models; | ||||
|                 } | ||||
|  | ||||
|                 return this.apiFetch(missingIds) | ||||
|                     .then(function (apiResults) { | ||||
|                         Object.keys(apiResults).forEach(function (k) { | ||||
|                             models[k] = apiResults[k]; | ||||
|                         }); | ||||
|  | ||||
|                         return models; | ||||
|                     }); | ||||
|                 //Temporary fix for missing models - don't retry using this.apiFetch | ||||
|                 return models; | ||||
|             }.bind(this)); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -110,7 +110,7 @@ class ActionsAPI extends EventEmitter { | ||||
|         return actionsObject; | ||||
|     } | ||||
|  | ||||
|     _groupAndSortActions(actionsArray) { | ||||
|     _groupAndSortActions(actionsArray = []) { | ||||
|         if (!Array.isArray(actionsArray) && typeof actionsArray === 'object') { | ||||
|             actionsArray = Object.keys(actionsArray).map(key => actionsArray[key]); | ||||
|         } | ||||
|   | ||||
							
								
								
									
										2
									
								
								src/api/objects/ConflictError.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/api/objects/ConflictError.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| export default class ConflictError extends Error { | ||||
| } | ||||
| @@ -26,6 +26,7 @@ import RootRegistry from './RootRegistry'; | ||||
| import RootObjectProvider from './RootObjectProvider'; | ||||
| import EventEmitter from 'EventEmitter'; | ||||
| import InterceptorRegistry from './InterceptorRegistry'; | ||||
| import ConflictError from './ConflictError'; | ||||
|  | ||||
| /** | ||||
|  * Utilities for loading, saving, and manipulating domain objects. | ||||
| @@ -34,6 +35,7 @@ import InterceptorRegistry from './InterceptorRegistry'; | ||||
|  */ | ||||
|  | ||||
| function ObjectAPI(typeRegistry, openmct) { | ||||
|     this.openmct = openmct; | ||||
|     this.typeRegistry = typeRegistry; | ||||
|     this.eventEmitter = new EventEmitter(); | ||||
|     this.providers = {}; | ||||
| @@ -47,6 +49,10 @@ function ObjectAPI(typeRegistry, openmct) { | ||||
|     this.interceptorRegistry = new InterceptorRegistry(); | ||||
|  | ||||
|     this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan']; | ||||
|  | ||||
|     this.errors = { | ||||
|         Conflict: ConflictError | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -181,8 +187,17 @@ ObjectAPI.prototype.get = function (identifier, abortSignal) { | ||||
|  | ||||
|     let objectPromise = provider.get(identifier, abortSignal).then(result => { | ||||
|         delete this.cache[keystring]; | ||||
|  | ||||
|         result = this.applyGetInterceptors(identifier, result); | ||||
|  | ||||
|         return result; | ||||
|     }).catch((result) => { | ||||
|         console.warn(`Failed to retrieve ${keystring}:`, result); | ||||
|  | ||||
|         delete this.cache[keystring]; | ||||
|  | ||||
|         result = this.applyGetInterceptors(identifier); | ||||
|  | ||||
|         return result; | ||||
|     }); | ||||
|  | ||||
| @@ -285,6 +300,7 @@ ObjectAPI.prototype.isPersistable = function (idOrKeyString) { | ||||
| ObjectAPI.prototype.save = function (domainObject) { | ||||
|     let provider = this.getProvider(domainObject.identifier); | ||||
|     let savedResolve; | ||||
|     let savedReject; | ||||
|     let result; | ||||
|  | ||||
|     if (!this.isPersistable(domainObject.identifier)) { | ||||
| @@ -294,14 +310,18 @@ ObjectAPI.prototype.save = function (domainObject) { | ||||
|     } else { | ||||
|         const persistedTime = Date.now(); | ||||
|         if (domainObject.persisted === undefined) { | ||||
|             result = new Promise((resolve) => { | ||||
|             result = new Promise((resolve, reject) => { | ||||
|                 savedResolve = resolve; | ||||
|                 savedReject = reject; | ||||
|             }); | ||||
|             domainObject.persisted = persistedTime; | ||||
|             provider.create(domainObject).then((response) => { | ||||
|                 this.mutate(domainObject, 'persisted', persistedTime); | ||||
|                 savedResolve(response); | ||||
|             }); | ||||
|             provider.create(domainObject) | ||||
|                 .then((response) => { | ||||
|                     this.mutate(domainObject, 'persisted', persistedTime); | ||||
|                     savedResolve(response); | ||||
|                 }).catch((error) => { | ||||
|                     savedReject(error); | ||||
|                 }); | ||||
|         } else { | ||||
|             domainObject.persisted = persistedTime; | ||||
|             this.mutate(domainObject, 'persisted', persistedTime); | ||||
|   | ||||
| @@ -483,6 +483,10 @@ define([ | ||||
|      * @returns {Object<String, {TelemetryValueFormatter}>} | ||||
|      */ | ||||
|     TelemetryAPI.prototype.getFormatMap = function (metadata) { | ||||
|         if (!metadata) { | ||||
|             return {}; | ||||
|         } | ||||
|  | ||||
|         if (!this.formatMapCache.has(metadata)) { | ||||
|             const formatMap = metadata.values().reduce(function (map, valueMetadata) { | ||||
|                 map[valueMetadata.key] = this.getValueFormatter(valueMetadata); | ||||
|   | ||||
| @@ -130,8 +130,13 @@ export class TelemetryCollection extends EventEmitter { | ||||
|         this.options.onPartialResponse = this._processNewTelemetry.bind(this); | ||||
|  | ||||
|         try { | ||||
|             if (this.requestAbort) { | ||||
|                 this.requestAbort.abort(); | ||||
|             } | ||||
|  | ||||
|             this.requestAbort = new AbortController(); | ||||
|             this.options.signal = this.requestAbort.signal; | ||||
|             this.emit('requestStarted'); | ||||
|             historicalData = await this.historicalProvider.request(this.domainObject, this.options); | ||||
|         } catch (error) { | ||||
|             if (error.name !== 'AbortError') { | ||||
| @@ -140,6 +145,7 @@ export class TelemetryCollection extends EventEmitter { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.emit('requestEnded'); | ||||
|         this.requestAbort = undefined; | ||||
|  | ||||
|         this._processNewTelemetry(historicalData); | ||||
|   | ||||
| @@ -41,7 +41,6 @@ const DEFAULTS = [ | ||||
|     'platform/forms', | ||||
|     'platform/identity', | ||||
|     'platform/persistence/aggregator', | ||||
|     'platform/persistence/queue', | ||||
|     'platform/policy', | ||||
|     'platform/entanglement', | ||||
|     'platform/search', | ||||
|   | ||||
| @@ -32,7 +32,7 @@ describe('the plugin', function () { | ||||
|     let openmct; | ||||
|     let composition; | ||||
|  | ||||
|     beforeEach((done) => { | ||||
|     beforeEach(() => { | ||||
|  | ||||
|         openmct = createOpenMct(); | ||||
|  | ||||
| @@ -47,11 +47,6 @@ describe('the plugin', function () { | ||||
|             } | ||||
|         })); | ||||
|  | ||||
|         openmct.on('start', done); | ||||
|         openmct.startHeadless(); | ||||
|  | ||||
|         composition = openmct.composition.get({identifier}); | ||||
|  | ||||
|         spyOn(couchPlugin.couchProvider, 'getObjectsByFilter').and.returnValue(Promise.resolve([ | ||||
|             { | ||||
|                 identifier: { | ||||
| @@ -66,6 +61,19 @@ describe('the plugin', function () { | ||||
|                 } | ||||
|             } | ||||
|         ])); | ||||
|  | ||||
|         spyOn(couchPlugin.couchProvider, "get").and.callFake((id) => { | ||||
|             return Promise.resolve({ | ||||
|                 identifier: id | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         return new Promise((resolve) => { | ||||
|             openmct.once('start', resolve); | ||||
|             openmct.startHeadless(); | ||||
|         }).then(() => { | ||||
|             composition = openmct.composition.get({identifier}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     afterEach(() => { | ||||
|   | ||||
| @@ -96,11 +96,11 @@ export default { | ||||
|  | ||||
|         this.timestampKey = this.openmct.time.timeSystem().key; | ||||
|  | ||||
|         this.valueMetadata = this | ||||
|         this.valueMetadata = this.metadata ? this | ||||
|             .metadata | ||||
|             .valuesForHints(['range'])[0]; | ||||
|             .valuesForHints(['range'])[0] : undefined; | ||||
|  | ||||
|         this.valueKey = this.valueMetadata.key; | ||||
|         this.valueKey = this.valueMetadata ? this.valueMetadata.key : undefined; | ||||
|  | ||||
|         this.unsubscribe = this.openmct | ||||
|             .telemetry | ||||
| @@ -151,7 +151,10 @@ export default { | ||||
|                     size: 1, | ||||
|                     strategy: 'latest' | ||||
|                 }) | ||||
|                 .then((array) => this.updateValues(array[array.length - 1])); | ||||
|                 .then((array) => this.updateValues(array[array.length - 1])) | ||||
|                 .catch((error) => { | ||||
|                     console.warn('Error fetching data', error); | ||||
|                 }); | ||||
|         }, | ||||
|         updateBounds(bounds, isTick) { | ||||
|             this.bounds = bounds; | ||||
|   | ||||
| @@ -73,8 +73,9 @@ export default { | ||||
|         hasUnits() { | ||||
|             let itemsWithUnits = this.items.filter((item) => { | ||||
|                 let metadata = this.openmct.telemetry.getMetadata(item.domainObject); | ||||
|                 const valueMetadatas = metadata ? metadata.valueMetadatas : []; | ||||
|  | ||||
|                 return this.metadataHasUnits(metadata.valueMetadatas); | ||||
|                 return this.metadataHasUnits(valueMetadatas); | ||||
|  | ||||
|             }); | ||||
|  | ||||
|   | ||||
| @@ -98,6 +98,8 @@ describe('the plugin', function () { | ||||
|  | ||||
|         conditionSetDefinition.initialize(mockConditionSetDomainObject); | ||||
|  | ||||
|         spyOn(openmct.objects, "save").and.returnValue(Promise.resolve(true)); | ||||
|  | ||||
|         openmct.on('start', done); | ||||
|         openmct.startHeadless(); | ||||
|     }); | ||||
|   | ||||
| @@ -101,7 +101,7 @@ export default { | ||||
|         addChildren(domainObject) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(domainObject.identifier); | ||||
|             let metadata = this.openmct.telemetry.getMetadata(domainObject); | ||||
|             let metadataWithFilters = metadata.valueMetadatas.filter(value => value.filters); | ||||
|             let metadataWithFilters = metadata ? metadata.valueMetadatas.filter(value => value.filters) : []; | ||||
|             let hasFiltersWithKeyString = this.persistedFilters[keyString] !== undefined; | ||||
|             let mutateFilters = false; | ||||
|             let childObject = { | ||||
|   | ||||
| @@ -159,7 +159,7 @@ export default { | ||||
|             let image = { ...datum }; | ||||
|             image.formattedTime = this.formatTime(datum); | ||||
|             image.url = this.formatImageUrl(datum); | ||||
|             image.time = datum[this.timeKey]; | ||||
|             image.time = this.parseTime(image.formattedTime); | ||||
|             image.imageDownloadName = this.getImageDownloadName(datum); | ||||
|  | ||||
|             return image; | ||||
|   | ||||
| @@ -180,9 +180,13 @@ export default { | ||||
|                 this.openmct.notifications.alert(message); | ||||
|             } | ||||
|  | ||||
|             const relativeHash = hash.slice(hash.indexOf('#')); | ||||
|             const url = new URL(relativeHash, `${location.protocol}//${location.host}${location.pathname}`); | ||||
|             this.openmct.router.navigate(url.hash); | ||||
|             if (this.openmct.editor.isEditing()) { | ||||
|                 this.previewEmbed(); | ||||
|             } else { | ||||
|                 const relativeHash = hash.slice(hash.indexOf('#')); | ||||
|                 const url = new URL(relativeHash, `${location.protocol}//${location.host}${location.pathname}`); | ||||
|                 this.openmct.router.navigate(url.hash); | ||||
|             } | ||||
|         }, | ||||
|         formatTime(unixTime, timeFormat) { | ||||
|             return Moment.utc(unixTime).format(timeFormat); | ||||
|   | ||||
							
								
								
									
										72
									
								
								src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| import {NOTEBOOK_TYPE} from './notebook-constants'; | ||||
|  | ||||
| export default function (openmct) { | ||||
|     const apiSave = openmct.objects.save.bind(openmct.objects); | ||||
|  | ||||
|     openmct.objects.save = async (domainObject) => { | ||||
|         if (domainObject.type !== NOTEBOOK_TYPE) { | ||||
|             return apiSave(domainObject); | ||||
|         } | ||||
|  | ||||
|         const localMutable = openmct.objects._toMutable(domainObject); | ||||
|         let result; | ||||
|  | ||||
|         try { | ||||
|             result = await apiSave(localMutable); | ||||
|         } catch (error) { | ||||
|             if (error instanceof openmct.objects.errors.Conflict) { | ||||
|                 result = resolveConflicts(localMutable, openmct); | ||||
|             } else { | ||||
|                 result = Promise.reject(error); | ||||
|             } | ||||
|         } finally { | ||||
|             openmct.objects.destroyMutable(localMutable); | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function resolveConflicts(localMutable, openmct) { | ||||
|     return openmct.objects.getMutable(localMutable.identifier).then((remoteMutable) => { | ||||
|         const localEntries = localMutable.configuration.entries; | ||||
|         remoteMutable.$refresh(remoteMutable); | ||||
|         applyLocalEntries(remoteMutable, localEntries); | ||||
|  | ||||
|         openmct.objects.destroyMutable(remoteMutable); | ||||
|  | ||||
|         return true; | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function applyLocalEntries(mutable, entries) { | ||||
|     Object.entries(entries).forEach(([sectionKey, pagesInSection]) => { | ||||
|         Object.entries(pagesInSection).forEach(([pageKey, localEntries]) => { | ||||
|             const remoteEntries = mutable.configuration.entries[sectionKey][pageKey]; | ||||
|             const mergedEntries = [].concat(remoteEntries); | ||||
|             let shouldMutate = false; | ||||
|  | ||||
|             const locallyAddedEntries = _.differenceBy(localEntries, remoteEntries, 'id'); | ||||
|             const locallyModifiedEntries = _.differenceWith(localEntries, remoteEntries, (localEntry, remoteEntry) => { | ||||
|                 return localEntry.id === remoteEntry.id && localEntry.text === remoteEntry.text; | ||||
|             }); | ||||
|  | ||||
|             locallyAddedEntries.forEach((localEntry) => { | ||||
|                 mergedEntries.push(localEntry); | ||||
|                 shouldMutate = true; | ||||
|             }); | ||||
|  | ||||
|             locallyModifiedEntries.forEach((locallyModifiedEntry) => { | ||||
|                 let mergedEntry = mergedEntries.find(entry => entry.id === locallyModifiedEntry.id); | ||||
|                 if (mergedEntry !== undefined) { | ||||
|                     mergedEntry.text = locallyModifiedEntry.text; | ||||
|                     shouldMutate = true; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (shouldMutate) { | ||||
|                 mutable.$set(`configuration.entries.${sectionKey}.${pageKey}`, mergedEntries); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| @@ -2,6 +2,7 @@ import CopyToNotebookAction from './actions/CopyToNotebookAction'; | ||||
| import Notebook from './components/Notebook.vue'; | ||||
| import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue'; | ||||
| import SnapshotContainer from './snapshot-container'; | ||||
| import monkeyPatchObjectAPIForNotebooks from './monkeyPatchObjectAPIForNotebooks.js'; | ||||
|  | ||||
| import { notebookImageMigration } from '../notebook/utils/notebook-migration'; | ||||
| import { NOTEBOOK_TYPE } from './notebook-constants'; | ||||
| @@ -165,5 +166,7 @@ export default function NotebookPlugin() { | ||||
|                 return domainObject; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         monkeyPatchObjectAPIForNotebooks(openmct); | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -154,6 +154,8 @@ describe("Notebook plugin:", () => { | ||||
|             testObjectProvider.get.and.returnValue(Promise.resolve(notebookViewObject)); | ||||
|             openmct.objects.addProvider('test-namespace', testObjectProvider); | ||||
|             testObjectProvider.observe.and.returnValue(() => {}); | ||||
|             testObjectProvider.create.and.returnValue(Promise.resolve(true)); | ||||
|             testObjectProvider.update.and.returnValue(Promise.resolve(true)); | ||||
|  | ||||
|             return openmct.objects.getMutable(notebookViewObject.identifier).then((mutableObject) => { | ||||
|                 mutableNotebookObject = mutableObject; | ||||
|   | ||||
| @@ -125,7 +125,7 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed = | ||||
|     const newEntries = addEntryIntoPage(notebookStorage, entries, entry); | ||||
|  | ||||
|     addDefaultClass(domainObject, openmct); | ||||
|     openmct.objects.mutate(domainObject, 'configuration.entries', newEntries); | ||||
|     domainObject.configuration.entries = newEntries; | ||||
|  | ||||
|     return id; | ||||
| } | ||||
|   | ||||
| @@ -15,12 +15,16 @@ | ||||
|  | ||||
|         port.onmessage = async function (event) { | ||||
|             if (event.data.request === 'close') { | ||||
|                 console.log('Closing connection'); | ||||
|                 connections.splice(event.data.connectionId - 1, 1); | ||||
|                 if (connections.length <= 0) { | ||||
|                     // abort any outstanding requests if there's nobody listening to it. | ||||
|                     controller.abort(); | ||||
|                 } | ||||
|  | ||||
|                 console.log('Closed.'); | ||||
|                 connected = false; | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @@ -29,68 +33,9 @@ | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 connected = true; | ||||
|  | ||||
|                 let url = event.data.url; | ||||
|                 let body = event.data.body; | ||||
|                 let error = false; | ||||
|                 // feed=continuous maintains an indefinitely open connection with a keep-alive of HEARTBEAT milliseconds until this client closes the connection | ||||
|                 // style=main_only returns only the current winning revision of the document | ||||
|  | ||||
|                 const response = await fetch(url, { | ||||
|                     method: 'POST', | ||||
|                     headers: { | ||||
|                         "Content-Type": 'application/json' | ||||
|                     }, | ||||
|                     signal, | ||||
|                     body | ||||
|                 }); | ||||
|  | ||||
|                 let reader; | ||||
|  | ||||
|                 if (response.body === undefined) { | ||||
|                     error = true; | ||||
|                 } else { | ||||
|                     reader = response.body.getReader(); | ||||
|                 } | ||||
|  | ||||
|                 while (!error) { | ||||
|                     const {done, value} = await reader.read(); | ||||
|                     //done is true when we lose connection with the provider | ||||
|                     if (done) { | ||||
|                         error = true; | ||||
|                     } | ||||
|  | ||||
|                     if (value) { | ||||
|                         let chunk = new Uint8Array(value.length); | ||||
|                         chunk.set(value, 0); | ||||
|                         const decodedChunk = new TextDecoder("utf-8").decode(chunk).split('\n'); | ||||
|                         if (decodedChunk.length && decodedChunk[decodedChunk.length - 1] === '') { | ||||
|                             decodedChunk.forEach((doc, index) => { | ||||
|                                 try { | ||||
|                                     if (doc) { | ||||
|                                         const objectChanges = JSON.parse(doc); | ||||
|                                         connections.forEach(function (connection) { | ||||
|                                             connection.postMessage({ | ||||
|                                                 objectChanges | ||||
|                                             }); | ||||
|                                         }); | ||||
|                                     } | ||||
|                                 } catch (decodeError) { | ||||
|                                     //do nothing; | ||||
|                                     console.log(decodeError); | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|                 if (error) { | ||||
|                     port.postMessage({ | ||||
|                         error | ||||
|                     }); | ||||
|                 } | ||||
|                 do { | ||||
|                     await self.listenForChanges(event.data.url, event.data.body, port); | ||||
|                 } while (connected); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -103,4 +48,64 @@ | ||||
|         console.log('Error on feed'); | ||||
|     }; | ||||
|  | ||||
|     self.listenForChanges = async function (url, body, port) { | ||||
|         connected = true; | ||||
|         let error = false; | ||||
|         // feed=continuous maintains an indefinitely open connection with a keep-alive of HEARTBEAT milliseconds until this client closes the connection | ||||
|         // style=main_only returns only the current winning revision of the document | ||||
|  | ||||
|         console.log('Opening changes feed connection.'); | ||||
|         const response = await fetch(url, { | ||||
|             method: 'POST', | ||||
|             headers: { | ||||
|                 "Content-Type": 'application/json' | ||||
|             }, | ||||
|             signal, | ||||
|             body | ||||
|         }); | ||||
|  | ||||
|         let reader; | ||||
|  | ||||
|         if (response.body === undefined) { | ||||
|             error = true; | ||||
|         } else { | ||||
|             reader = response.body.getReader(); | ||||
|         } | ||||
|  | ||||
|         while (!error) { | ||||
|             const {done, value} = await reader.read(); | ||||
|             //done is true when we lose connection with the provider | ||||
|             if (done) { | ||||
|                 error = true; | ||||
|             } | ||||
|  | ||||
|             if (value) { | ||||
|                 let chunk = new Uint8Array(value.length); | ||||
|                 chunk.set(value, 0); | ||||
|                 const decodedChunk = new TextDecoder("utf-8").decode(chunk).split('\n'); | ||||
|                 console.log('Received chunk'); | ||||
|                 if (decodedChunk.length && decodedChunk[decodedChunk.length - 1] === '') { | ||||
|                     decodedChunk.forEach((doc, index) => { | ||||
|                         try { | ||||
|                             if (doc) { | ||||
|                                 const objectChanges = JSON.parse(doc); | ||||
|                                 connections.forEach(function (connection) { | ||||
|                                     connection.postMessage({ | ||||
|                                         objectChanges | ||||
|                                     }); | ||||
|                                 }); | ||||
|                             } | ||||
|                         } catch (decodeError) { | ||||
|                             //do nothing; | ||||
|                             console.log(decodeError); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         console.log('Done reading changes feed'); | ||||
|     }; | ||||
|  | ||||
| }()); | ||||
|   | ||||
| @@ -29,7 +29,7 @@ const ID = "_id"; | ||||
| const HEARTBEAT = 50000; | ||||
| const ALL_DOCS = "_all_docs?include_docs=true"; | ||||
|  | ||||
| export default class CouchObjectProvider { | ||||
| class CouchObjectProvider { | ||||
|     constructor(openmct, options, namespace) { | ||||
|         options = this._normalize(options); | ||||
|         this.openmct = openmct; | ||||
| @@ -74,13 +74,6 @@ export default class CouchObjectProvider { | ||||
|         if (event.data.type === 'connection') { | ||||
|             this.changesFeedSharedWorkerConnectionId = event.data.connectionId; | ||||
|         } else { | ||||
|             const error = event.data.error; | ||||
|             if (error && Object.keys(this.observers).length > 0) { | ||||
|                 this.observeObjectChanges(); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let objectChanges = event.data.objectChanges; | ||||
|             objectChanges.identifier = { | ||||
|                 namespace: this.namespace, | ||||
| @@ -126,11 +119,12 @@ export default class CouchObjectProvider { | ||||
|         } | ||||
|  | ||||
|         return fetch(this.url + '/' + subPath, fetchOptions) | ||||
|             .then(response => response.json()) | ||||
|             .then(function (response) { | ||||
|                 return response; | ||||
|             }, function () { | ||||
|                 return undefined; | ||||
|             .then((response) => { | ||||
|                 if (response.status === CouchObjectProvider.HTTP_CONFLICT) { | ||||
|                     throw new this.openmct.objects.errors.Conflict(`Conflict persisting ${fetchOptions.body.name}`); | ||||
|                 } | ||||
|  | ||||
|                 return response.json(); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
| @@ -561,12 +555,18 @@ export default class CouchObjectProvider { | ||||
|         let intermediateResponse = this.getIntermediateResponse(); | ||||
|         const key = model.identifier.key; | ||||
|         this.enqueueObject(key, model, intermediateResponse); | ||||
|         this.objectQueue[key].pending = true; | ||||
|         const queued = this.objectQueue[key].dequeue(); | ||||
|         let document = new CouchDocument(key, queued.model); | ||||
|         this.request(key, "PUT", document).then((response) => { | ||||
|             this.checkResponse(response, queued.intermediateResponse, key); | ||||
|         }); | ||||
|         if (!this.objectQueue[key].pending) { | ||||
|             this.objectQueue[key].pending = true; | ||||
|             const queued = this.objectQueue[key].dequeue(); | ||||
|             let document = new CouchDocument(key, queued.model); | ||||
|             this.request(key, "PUT", document).then((response) => { | ||||
|                 console.log('create check response', key); | ||||
|                 this.checkResponse(response, queued.intermediateResponse, key); | ||||
|             }).catch(error => { | ||||
|                 queued.intermediateResponse.reject(error); | ||||
|                 this.objectQueue[key].pending = false; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         return intermediateResponse.promise; | ||||
|     } | ||||
| @@ -581,6 +581,9 @@ export default class CouchObjectProvider { | ||||
|             let document = new CouchDocument(key, queued.model, this.objectQueue[key].rev); | ||||
|             this.request(key, "PUT", document).then((response) => { | ||||
|                 this.checkResponse(response, queued.intermediateResponse, key); | ||||
|             }).catch((error) => { | ||||
|                 queued.intermediateResponse.reject(error); | ||||
|                 this.objectQueue[key].pending = false; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -594,3 +597,7 @@ export default class CouchObjectProvider { | ||||
|         return intermediateResponse.promise; | ||||
|     } | ||||
| } | ||||
|  | ||||
| CouchObjectProvider.HTTP_CONFLICT = 409; | ||||
|  | ||||
| export default CouchObjectProvider; | ||||
|   | ||||
| @@ -21,67 +21,57 @@ | ||||
| --> | ||||
| <template> | ||||
| <div class="u-contents"> | ||||
|     <ul v-if="canEdit" | ||||
|         class="l-inspector-part" | ||||
|     <div v-if="canEdit" | ||||
|          class="grid-row" | ||||
|     > | ||||
|         <h2 v-if="heading" | ||||
|             :title="heading" | ||||
|         >{{ heading }}</h2> | ||||
|         <li class="grid-row"> | ||||
|             <div class="grid-cell label" | ||||
|                  :title="editTitle" | ||||
|             >{{ shortLabel }}</div> | ||||
|             <div class="grid-cell value"> | ||||
|                 <div class="c-click-swatch c-click-swatch--menu" | ||||
|                      @click="toggleSwatch()" | ||||
|         <div class="grid-cell label" | ||||
|              :title="editTitle" | ||||
|         >{{ shortLabel }}</div> | ||||
|         <div class="grid-cell value"> | ||||
|             <div class="c-click-swatch c-click-swatch--menu" | ||||
|                  @click="toggleSwatch()" | ||||
|             > | ||||
|                 <span class="c-color-swatch" | ||||
|                       :style="{ background: currentColor }" | ||||
|                 > | ||||
|                     <span class="c-color-swatch" | ||||
|                           :style="{ background: currentColor }" | ||||
|                 </span> | ||||
|             </div> | ||||
|             <div class="c-palette c-palette--color"> | ||||
|                 <div v-show="swatchActive" | ||||
|                      class="c-palette__items" | ||||
|                 > | ||||
|                     <div v-for="group in colorPaletteGroups" | ||||
|                          :key="group.id" | ||||
|                          class="u-contents" | ||||
|                     > | ||||
|                     </span> | ||||
|                 </div> | ||||
|                 <div class="c-palette c-palette--color"> | ||||
|                     <div v-show="swatchActive" | ||||
|                          class="c-palette__items" | ||||
|                     > | ||||
|                         <div v-for="group in colorPaletteGroups" | ||||
|                              :key="group.id" | ||||
|                              class="u-contents" | ||||
|                         <div v-for="color in group" | ||||
|                              :key="color.id" | ||||
|                              class="c-palette__item" | ||||
|                              :class="{ 'selected': currentColor === color.hexString }" | ||||
|                              :style="{ background: color.hexString }" | ||||
|                              @click="setColor(color)" | ||||
|                         > | ||||
|                             <div v-for="color in group" | ||||
|                                  :key="color.id" | ||||
|                                  class="c-palette__item" | ||||
|                                  :class="{ 'selected': currentColor === color.hexString }" | ||||
|                                  :style="{ background: color.hexString }" | ||||
|                                  @click="setColor(color)" | ||||
|                             > | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </li> | ||||
|     </ul> | ||||
|     <ul v-else | ||||
|         class="l-inspector-part" | ||||
|         </div> | ||||
|     </div> | ||||
|     <div v-else | ||||
|          class="grid-row" | ||||
|     > | ||||
|         <h2 v-if="heading" | ||||
|             :title="heading" | ||||
|         >{{ heading }}</h2> | ||||
|         <li class="grid-row"> | ||||
|             <div class="grid-cell label" | ||||
|                  :title="viewTitle" | ||||
|             >{{ shortLabel }}</div> | ||||
|             <div class="grid-cell value"> | ||||
|                 <span class="c-color-swatch" | ||||
|                       :style="{ | ||||
|                           'background': currentColor | ||||
|                       }" | ||||
|                 > | ||||
|                 </span> | ||||
|             </div> | ||||
|         </li> | ||||
|     </ul> | ||||
|         <div class="grid-cell label" | ||||
|              :title="viewTitle" | ||||
|         >{{ shortLabel }}</div> | ||||
|         <div class="grid-cell value"> | ||||
|             <span class="c-color-swatch" | ||||
|                   :style="{ | ||||
|                       'background': currentColor | ||||
|                   }" | ||||
|             > | ||||
|             </span> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -114,12 +104,6 @@ export default { | ||||
|             default() { | ||||
|                 return 'Color'; | ||||
|             } | ||||
|         }, | ||||
|         heading: { | ||||
|             type: String, | ||||
|             default() { | ||||
|                 return ''; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|   | ||||
| @@ -107,7 +107,7 @@ export default { | ||||
|                 }; | ||||
|                 this.openmct.objects.mutate( | ||||
|                     this.domainObject, | ||||
|                     `configuration.barStyles[${this.key}]`, | ||||
|                     `configuration.barStyles[${key}]`, | ||||
|                     this.domainObject.configuration.barStyles[key] | ||||
|                 ); | ||||
|             } else { | ||||
| @@ -150,6 +150,10 @@ export default { | ||||
|         }, | ||||
|         getAxisMetadata(telemetryObject) { | ||||
|             const metadata = this.openmct.telemetry.getMetadata(telemetryObject); | ||||
|             if (!metadata) { | ||||
|                 return {}; | ||||
|             } | ||||
|  | ||||
|             const yAxisMetadata = metadata.valuesForHints(['range'])[0]; | ||||
|             //Exclude 'name' and 'time' based metadata specifically, from the x-Axis values by using range hints only | ||||
|             const xAxisMetadata = metadata.valuesForHints(['range']); | ||||
| @@ -255,6 +259,9 @@ export default { | ||||
|                     data.forEach((datum) => { | ||||
|                         this.processData(telemetryObject, datum, axisMetadata); | ||||
|                     }); | ||||
|                 }) | ||||
|                 .catch((error) => { | ||||
|                     console.warn(`Error fetching data`, error); | ||||
|                 }); | ||||
|         }, | ||||
|         subscribeToObject(telemetryObject) { | ||||
|   | ||||
| @@ -33,9 +33,9 @@ | ||||
|     </li> | ||||
|     <ColorSwatch v-if="expanded" | ||||
|                  :current-color="currentColor" | ||||
|                  title="Manually set the color for this bar graph." | ||||
|                  edit-title="Manually set the color for this bar graph" | ||||
|                  view-title="The color for this bar graph." | ||||
|                  title="Manually set the color for this bar graph series." | ||||
|                  edit-title="Manually set the color for this bar graph series" | ||||
|                  view-title="The color for this bar graph series." | ||||
|                  short-label="Color" | ||||
|                  class="grid-properties" | ||||
|                  @colorSet="setColor" | ||||
|   | ||||
| @@ -20,15 +20,13 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <template> | ||||
| <div> | ||||
|     <ul class="c-tree"> | ||||
|         <li v-for="series in domainObject.composition" | ||||
|             :key="series.key" | ||||
|         > | ||||
|             <bar-graph-options :item="series" /> | ||||
|         </li> | ||||
|     </ul> | ||||
| </div> | ||||
| <ul class="c-tree"> | ||||
|     <h2 title="Display properties for this object">Bar Graph Series</h2> | ||||
|     <bar-graph-options v-for="series in domainObject.composition" | ||||
|                        :key="series.key" | ||||
|                        :item="series" | ||||
|     /> | ||||
| </ul> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   | ||||
| @@ -82,12 +82,17 @@ export default class PlotSeries extends Model { | ||||
|             .openmct | ||||
|             .telemetry | ||||
|             .getMetadata(options.domainObject); | ||||
|  | ||||
|         this.formats = options | ||||
|             .openmct | ||||
|             .telemetry | ||||
|             .getFormatMap(this.metadata); | ||||
|  | ||||
|         const range = this.metadata.valuesForHints(['range'])[0]; | ||||
|         //if the object is missing or doesn't have metadata for some reason | ||||
|         let range = {}; | ||||
|         if (this.metadata) { | ||||
|             range = this.metadata.valuesForHints(['range'])[0]; | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             name: options.domainObject.name, | ||||
| @@ -191,7 +196,10 @@ export default class PlotSeries extends Model { | ||||
|                     .uniq(true, point => [this.getXVal(point), this.getYVal(point)].join()) | ||||
|                     .value(); | ||||
|                 this.reset(newPoints); | ||||
|             }.bind(this)); | ||||
|             }.bind(this)) | ||||
|             .catch((error) => { | ||||
|                 console.warn('Error fetching data', error); | ||||
|             }); | ||||
|         /* eslint-enable you-dont-need-lodash-underscore/concat */ | ||||
|     } | ||||
|     /** | ||||
| @@ -199,7 +207,9 @@ export default class PlotSeries extends Model { | ||||
|      */ | ||||
|     onXKeyChange(xKey) { | ||||
|         const format = this.formats[xKey]; | ||||
|         this.getXVal = format.parse.bind(format); | ||||
|         if (format) { | ||||
|             this.getXVal = format.parse.bind(format); | ||||
|         } | ||||
|     } | ||||
|     /** | ||||
|      * Update y formatter on change, default to stepAfter interpolation if | ||||
|   | ||||
| @@ -184,7 +184,7 @@ export default class YAxisModel extends Model { | ||||
|         this.set('values', yMetadata.values); | ||||
|         if (!label) { | ||||
|             const labelName = series.map(function (s) { | ||||
|                 return s.metadata.value(s.get('yKey')).name; | ||||
|                 return s.metadata ? s.metadata.value(s.get('yKey')).name : ''; | ||||
|             }).reduce(function (a, b) { | ||||
|                 if (a === undefined) { | ||||
|                     return b; | ||||
| @@ -204,7 +204,7 @@ export default class YAxisModel extends Model { | ||||
|             } | ||||
|  | ||||
|             const labelUnits = series.map(function (s) { | ||||
|                 return s.metadata.value(s.get('yKey')).units; | ||||
|                 return s.metadata ? s.metadata.value(s.get('yKey')).units : ''; | ||||
|             }).reduce(function (a, b) { | ||||
|                 if (a === undefined) { | ||||
|                     return b; | ||||
|   | ||||
| @@ -60,18 +60,17 @@ define([ | ||||
|             this.addTelemetryObject = this.addTelemetryObject.bind(this); | ||||
|             this.removeTelemetryObject = this.removeTelemetryObject.bind(this); | ||||
|             this.removeTelemetryCollection = this.removeTelemetryCollection.bind(this); | ||||
|             this.incrementOutstandingRequests = this.incrementOutstandingRequests.bind(this); | ||||
|             this.decrementOutstandingRequests = this.decrementOutstandingRequests.bind(this); | ||||
|             this.resetRowsFromAllData = this.resetRowsFromAllData.bind(this); | ||||
|             this.isTelemetryObject = this.isTelemetryObject.bind(this); | ||||
|             this.refreshData = this.refreshData.bind(this); | ||||
|             this.updateFilters = this.updateFilters.bind(this); | ||||
|             this.clearData = this.clearData.bind(this); | ||||
|             this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this); | ||||
|  | ||||
|             this.filterObserver = undefined; | ||||
|  | ||||
|             this.createTableRowCollections(); | ||||
|  | ||||
|             openmct.time.on('bounds', this.refreshData); | ||||
|             openmct.time.on('timeSystem', this.refreshData); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
| @@ -141,8 +140,6 @@ define([ | ||||
|             let columnMap = this.getColumnMapForObject(keyString); | ||||
|             let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); | ||||
|  | ||||
|             this.incrementOutstandingRequests(); | ||||
|  | ||||
|             const telemetryProcessor = this.getTelemetryProcessor(keyString, columnMap, limitEvaluator); | ||||
|             const telemetryRemover = this.getTelemetryRemover(); | ||||
|  | ||||
| @@ -151,13 +148,13 @@ define([ | ||||
|             this.telemetryCollections[keyString] = this.openmct.telemetry | ||||
|                 .requestCollection(telemetryObject, requestOptions); | ||||
|  | ||||
|             this.telemetryCollections[keyString].on('requestStarted', this.incrementOutstandingRequests); | ||||
|             this.telemetryCollections[keyString].on('requestEnded', this.decrementOutstandingRequests); | ||||
|             this.telemetryCollections[keyString].on('remove', telemetryRemover); | ||||
|             this.telemetryCollections[keyString].on('add', telemetryProcessor); | ||||
|             this.telemetryCollections[keyString].on('clear', this.tableRows.clear); | ||||
|             this.telemetryCollections[keyString].on('clear', this.clearData); | ||||
|             this.telemetryCollections[keyString].load(); | ||||
|  | ||||
|             this.decrementOutstandingRequests(); | ||||
|  | ||||
|             this.telemetryObjects[keyString] = { | ||||
|                 telemetryObject, | ||||
|                 keyString, | ||||
| @@ -268,17 +265,6 @@ define([ | ||||
|             this.emit('object-removed', objectIdentifier); | ||||
|         } | ||||
|  | ||||
|         refreshData(bounds, isTick) { | ||||
|             if (!isTick && this.tableRows.outstandingRequests === 0) { | ||||
|                 this.tableRows.clear(); | ||||
|                 this.tableRows.sortBy({ | ||||
|                     key: this.openmct.time.timeSystem().key, | ||||
|                     direction: 'asc' | ||||
|                 }); | ||||
|                 this.tableRows.resubscribe(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         clearData() { | ||||
|             this.tableRows.clear(); | ||||
|             this.emit('refresh'); | ||||
| @@ -378,9 +364,6 @@ define([ | ||||
|             let keystrings = Object.keys(this.telemetryCollections); | ||||
|             keystrings.forEach(this.removeTelemetryCollection); | ||||
|  | ||||
|             this.openmct.time.off('bounds', this.refreshData); | ||||
|             this.openmct.time.off('timeSystem', this.refreshData); | ||||
|  | ||||
|             if (this.filterObserver) { | ||||
|                 this.filterObserver(); | ||||
|             } | ||||
|   | ||||
| @@ -131,7 +131,8 @@ export default { | ||||
|             objects.forEach(object => this.addColumnsForObject(object, false)); | ||||
|         }, | ||||
|         addColumnsForObject(telemetryObject) { | ||||
|             let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values(); | ||||
|             const metadata = this.openmct.telemetry.getMetadata(telemetryObject); | ||||
|             let metadataValues = metadata ? metadata.values() : []; | ||||
|             metadataValues.forEach(metadatum => { | ||||
|                 let column = new TelemetryTableColumn(this.openmct, metadatum); | ||||
|                 this.tableConfiguration.addSingleColumnForObject(telemetryObject, column); | ||||
|   | ||||
| @@ -105,7 +105,8 @@ export default { | ||||
|                 composition.load().then((domainObjects) => { | ||||
|                     domainObjects.forEach(telemetryObject => { | ||||
|                         let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|                         let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values(); | ||||
|                         const metadata = this.openmct.telemetry.getMetadata(telemetryObject); | ||||
|                         let metadataValues = metadata ? metadata.values() : []; | ||||
|                         let filters = this.filteredTelemetry[keyString]; | ||||
|  | ||||
|                         if (filters !== undefined) { | ||||
|   | ||||
| @@ -125,7 +125,6 @@ | ||||
|     <div | ||||
|         class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar u-style-receiver js-style-receiver" | ||||
|         :class="{ | ||||
|             'loading': loading, | ||||
|             'is-paused' : paused | ||||
|         }" | ||||
|     > | ||||
| @@ -362,7 +361,7 @@ export default { | ||||
|             autoScroll: true, | ||||
|             sortOptions: {}, | ||||
|             filters: {}, | ||||
|             loading: true, | ||||
|             loading: false, | ||||
|             scrollable: undefined, | ||||
|             tableEl: undefined, | ||||
|             headersHolderEl: undefined, | ||||
| @@ -422,6 +421,14 @@ export default { | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|         loading: { | ||||
|             handler(isLoading) { | ||||
|                 if (this.viewActionsCollection) { | ||||
|                     let action = isLoading ? 'disable' : 'enable'; | ||||
|                     this.viewActionsCollection[action](['export-csv-all']); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         markedRows: { | ||||
|             handler(newVal, oldVal) { | ||||
|                 this.$emit('marked-rows-updated', newVal, oldVal); | ||||
| @@ -1020,6 +1027,12 @@ export default { | ||||
|                 this.viewActionsCollection.disable(['export-csv-marked', 'unmark-all-rows']); | ||||
|             } | ||||
|  | ||||
|             if (this.loading) { | ||||
|                 this.viewActionsCollection.disable(['export-csv-all']); | ||||
|             } else { | ||||
|                 this.viewActionsCollection.enable(['export-csv-all']); | ||||
|             } | ||||
|  | ||||
|             if (this.paused) { | ||||
|                 this.viewActionsCollection.hide(['pause-data']); | ||||
|                 this.viewActionsCollection.show(['play-data']); | ||||
|   | ||||
| @@ -151,29 +151,22 @@ export default { | ||||
|             this.stopFollowingTimeContext(); | ||||
|             this.timeContext = this.openmct.time.getContextForView([this.domainObject]); | ||||
|             this.timeContext.on('timeContext', this.setTimeContext); | ||||
|             this.timeContext.on('clock', this.setViewFromClock); | ||||
|             this.timeContext.on('clock', this.setTimeOptions); | ||||
|         }, | ||||
|         stopFollowingTimeContext() { | ||||
|             if (this.timeContext) { | ||||
|                 this.timeContext.off('timeContext', this.setTimeContext); | ||||
|                 this.timeContext.off('clock', this.setViewFromClock); | ||||
|                 this.timeContext.off('clock', this.setTimeOptions); | ||||
|             } | ||||
|         }, | ||||
|         setViewFromClock(clock) { | ||||
|             if (!this.timeOptions.mode) { | ||||
|                 this.setTimeOptions(clock); | ||||
|             } | ||||
|         }, | ||||
|         setTimeOptions() { | ||||
|             if (!this.timeOptions || !this.timeOptions.mode) { | ||||
|                 this.mode = this.timeContext.clock() === undefined ? { key: 'fixed' } : { key: Object.create(this.timeContext.clock()).key}; | ||||
|                 this.timeOptions = { | ||||
|                     clockOffsets: this.timeContext.clockOffsets(), | ||||
|                     fixedOffsets: this.timeContext.bounds() | ||||
|                 }; | ||||
|             } | ||||
|         setTimeOptions(clock) { | ||||
|             this.timeOptions.clockOffsets = this.timeOptions.clockOffsets || this.timeContext.clockOffsets(); | ||||
|             this.timeOptions.fixedOffsets = this.timeOptions.fixedOffsets || this.timeContext.bounds(); | ||||
|  | ||||
|             this.registerIndependentTimeOffsets(); | ||||
|             if (!this.timeOptions.mode) { | ||||
|                 this.mode = this.timeContext.clock() === undefined ? {key: 'fixed'} : {key: Object.create(this.timeContext.clock()).key}; | ||||
|                 this.registerIndependentTimeOffsets(); | ||||
|             } | ||||
|         }, | ||||
|         saveFixedOffsets(offsets) { | ||||
|             const newOptions = Object.assign({}, this.timeOptions, { | ||||
|   | ||||
| @@ -20,7 +20,8 @@ | ||||
| * at runtime from the About dialog for additional information. | ||||
| *****************************************************************************/ | ||||
| <template> | ||||
| <div ref="modeMenuButton" | ||||
| <div v-if="modes.length > 1" | ||||
|      ref="modeMenuButton" | ||||
|      class="c-ctrl-wrapper c-ctrl-wrapper--menus-up" | ||||
| > | ||||
|     <div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"> | ||||
|   | ||||
| @@ -88,7 +88,7 @@ export default { | ||||
|             this.mutablePromise.then(() => { | ||||
|                 this.openmct.objects.destroyMutable(this.domainObject); | ||||
|             }); | ||||
|         } else { | ||||
|         } else if (this.domainObject.isMutable) { | ||||
|             this.openmct.objects.destroyMutable(this.domainObject); | ||||
|         } | ||||
|     }, | ||||
|   | ||||
| @@ -160,7 +160,9 @@ export default { | ||||
|         this.status = this.openmct.status.get(this.domainObject.identifier); | ||||
|         this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.setStatus); | ||||
|         const provider = this.openmct.objectViews.get(this.domainObject, this.objectPath)[0]; | ||||
|         this.$refs.objectView.show(this.domainObject, provider.key, false, this.objectPath); | ||||
|         if (provider) { | ||||
|             this.$refs.objectView.show(this.domainObject, provider.key, false, this.objectPath); | ||||
|         } | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.removeStatusListener(); | ||||
| @@ -193,8 +195,10 @@ export default { | ||||
|         }, | ||||
|         showMenuItems(event) { | ||||
|             const sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems); | ||||
|             const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view); | ||||
|             this.openmct.menus.showMenu(event.x, event.y, menuItems); | ||||
|             if (sortedActions.length) { | ||||
|                 const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view); | ||||
|                 this.openmct.menus.showMenu(event.x, event.y, menuItems); | ||||
|             } | ||||
|         }, | ||||
|         setStatus(status) { | ||||
|             this.status = status; | ||||
|   | ||||
| @@ -49,6 +49,7 @@ describe("the inspector", () => { | ||||
|  | ||||
|     beforeEach((done) => { | ||||
|         openmct = createOpenMct(); | ||||
|         spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true)); | ||||
|         openmct.on('start', done); | ||||
|         openmct.startHeadless(); | ||||
|     }); | ||||
| @@ -77,12 +78,12 @@ describe("the inspector", () => { | ||||
|         expect(savedStylesViewComponent.$children[0].$children.length).toBe(0); | ||||
|         stylesViewComponent.$children[0].saveStyle(mockStyle); | ||||
|  | ||||
|         stylesViewComponent.$nextTick().then(() => { | ||||
|         return stylesViewComponent.$nextTick().then(() => { | ||||
|             expect(savedStylesViewComponent.$children[0].$children.length).toBe(1); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it("should allow a saved style to be applied", () => { | ||||
|     xit("should allow a saved style to be applied", () => { | ||||
|         spyOn(openmct.editor, 'isEditing').and.returnValue(true); | ||||
|  | ||||
|         selection = mockTelemetryTableSelection; | ||||
| @@ -91,12 +92,12 @@ describe("the inspector", () => { | ||||
|  | ||||
|         stylesViewComponent.$children[0].saveStyle(mockStyle); | ||||
|  | ||||
|         stylesViewComponent.$nextTick().then(() => { | ||||
|         return stylesViewComponent.$nextTick().then(() => { | ||||
|             const styleSelectorComponent = savedStylesViewComponent.$children[0].$children[0]; | ||||
|  | ||||
|             styleSelectorComponent.selectStyle(); | ||||
|  | ||||
|             savedStylesViewComponent.$nextTick().then(() => { | ||||
|             return savedStylesViewComponent.$nextTick().then(() => { | ||||
|                 const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; | ||||
|                 const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; | ||||
|                 const styles = styleEditorComponent.$children.filter(component => component.options.value === mockStyle.color); | ||||
| @@ -147,7 +148,7 @@ describe("the inspector", () => { | ||||
|         stylesViewComponent = createViewComponent(StylesView, selection, openmct); | ||||
|         savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct); | ||||
|  | ||||
|         stylesViewComponent.$nextTick().then(() => { | ||||
|         return stylesViewComponent.$nextTick().then(() => { | ||||
|             const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; | ||||
|             const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; | ||||
|             const saveStyleButtonIndex = styleEditorComponent.$children.length - 1; | ||||
| @@ -168,7 +169,7 @@ describe("the inspector", () => { | ||||
|         stylesViewComponent = createViewComponent(StylesView, selection, openmct); | ||||
|         savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct); | ||||
|  | ||||
|         stylesViewComponent.$nextTick().then(() => { | ||||
|         return stylesViewComponent.$nextTick().then(() => { | ||||
|             const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; | ||||
|             const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; | ||||
|             const saveStyleButtonIndex = styleEditorComponent.$children.length - 1; | ||||
| @@ -185,7 +186,7 @@ describe("the inspector", () => { | ||||
|         stylesViewComponent = createViewComponent(StylesView, selection, openmct); | ||||
|         savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct); | ||||
|  | ||||
|         stylesViewComponent.$nextTick().then(() => { | ||||
|         return stylesViewComponent.$nextTick().then(() => { | ||||
|             const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; | ||||
|             const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; | ||||
|             const saveStyleButtonIndex = styleEditorComponent.$children.length - 1; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user