Compare commits
	
		
			39 Commits
		
	
	
		
			temp/inspe
			...
			imagery-ag
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6016c9e31d | ||
|   | 65c7b5082d | ||
|   | 7f4647559a | ||
|   | 0fdd053fb4 | ||
|   | f220953bba | ||
|   | 6320ceff82 | ||
|   | 6784f6f3b3 | ||
|   | 3ac15870c6 | ||
|   | 02b8b599c4 | ||
|   | 5f30a0758d | ||
|   | c564bf518a | ||
|   | 2ff9ea80c4 | ||
|   | 868dd4eea1 | ||
|   | e72096cd7f | ||
|   | baebca7dec | ||
|   | 9f100c331a | ||
|   | d106687303 | ||
|   | 4d64524a94 | ||
|   | 9afceda7ed | ||
|   | 095fcb4e0c | ||
|   | 91f492bcd8 | ||
|   | f54f27c1bf | ||
|   | 7397d14778 | ||
|   | 17fb6e57ec | ||
|   | a42c70bd0c | ||
|   | 6557f02b0a | ||
|   | 2fce3138e9 | ||
|   | 6fa3e26c27 | ||
|   | 927f848b9a | ||
|   | 0d31301a30 | ||
|   | 59d15b9e31 | ||
|   | de1d57d22d | ||
|   | 2f59350493 | ||
|   | 5f10f05509 | ||
|   | d4ccbdabf2 | ||
|   | 4d7e25d9bc | ||
|   | 16c15ccf3e | ||
|   | a010be5718 | ||
|   | 2b89c61911 | 
| @@ -27,7 +27,7 @@ define([ | ||||
| ) { | ||||
|     function ImageryPlugin() { | ||||
|  | ||||
|         var IMAGE_SAMPLES = [ | ||||
|         const IMAGE_SAMPLES = [ | ||||
|             "https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg", | ||||
|             "https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg", | ||||
|             "https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg", | ||||
| @@ -47,13 +47,14 @@ define([ | ||||
|             "https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg", | ||||
|             "https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg" | ||||
|         ]; | ||||
|         const IMAGE_DELAY = 20000; | ||||
|  | ||||
|         function pointForTimestamp(timestamp, name) { | ||||
|             return { | ||||
|                 name: name, | ||||
|                 utc: Math.floor(timestamp / 5000) * 5000, | ||||
|                 local: Math.floor(timestamp / 5000) * 5000, | ||||
|                 url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length] | ||||
|                 utc: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY, | ||||
|                 local: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY, | ||||
|                 url: IMAGE_SAMPLES[Math.floor(timestamp / IMAGE_DELAY) % IMAGE_SAMPLES.length] | ||||
|             }; | ||||
|         } | ||||
|  | ||||
| @@ -64,7 +65,7 @@ define([ | ||||
|             subscribe: function (domainObject, callback) { | ||||
|                 var interval = setInterval(function () { | ||||
|                     callback(pointForTimestamp(Date.now(), domainObject.name)); | ||||
|                 }, 5000); | ||||
|                 }, IMAGE_DELAY); | ||||
|  | ||||
|                 return function () { | ||||
|                     clearInterval(interval); | ||||
| @@ -81,9 +82,9 @@ define([ | ||||
|                 var start = options.start; | ||||
|                 var end = Math.min(options.end, Date.now()); | ||||
|                 var data = []; | ||||
|                 while (start <= end && data.length < 5000) { | ||||
|                 while (start <= end && data.length < IMAGE_DELAY) { | ||||
|                     data.push(pointForTimestamp(start, domainObject.name)); | ||||
|                     start += 5000; | ||||
|                     start += IMAGE_DELAY; | ||||
|                 } | ||||
|  | ||||
|                 return Promise.resolve(data); | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| <template> | ||||
| <div class="c-imagery"> | ||||
| <div | ||||
|     tabindex="0" | ||||
|     class="c-imagery" | ||||
|     @keyup="arrowUpHandler" | ||||
|     @keydown="arrowDownHandler" | ||||
|     @mouseover="focusElement" | ||||
| > | ||||
|     <div class="c-imagery__main-image-wrapper has-local-controls"> | ||||
|         <div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc"> | ||||
|             <span class="holder flex-elem grows c-imagery__lc__sliders"> | ||||
| @@ -22,98 +28,188 @@ | ||||
|                 ></a> | ||||
|             </span> | ||||
|         </div> | ||||
|         <div class="main-image s-image-main c-imagery__main-image has-local-controls js-imageryView-image" | ||||
|              :class="{'paused unnsynced': paused(),'stale':false }" | ||||
|              :style="{'background-image': getImageUrl() ? `url(${getImageUrl()})` : 'none', | ||||
|         <div class="main-image s-image-main c-imagery__main-image has-local-controls" | ||||
|              :class="{'paused unnsynced': isPaused,'stale':false }" | ||||
|              :style="{'background-image': imageUrl ? `url(${imageUrl})` : 'none', | ||||
|                       'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`}" | ||||
|              :data-openmct-image-timestamp="getTime()" | ||||
|              :data-openmct-object-keystring="keystring" | ||||
|              :data-openmct-image-timestamp="time" | ||||
|              :data-openmct-object-keystring="keyString" | ||||
|         > | ||||
|             <div class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-buttons"> | ||||
|                 <button class="c-nav c-nav--prev" | ||||
|                         title="Previous image" | ||||
|                         :disabled="isPrevDisabled()" | ||||
|                         :disabled="isPrevDisabled" | ||||
|                         @click="prevImage()" | ||||
|                 ></button> | ||||
|                 <button class="c-nav c-nav--next" | ||||
|                         title="Next image" | ||||
|                         :disabled="isNextDisabled()" | ||||
|                         :disabled="isNextDisabled" | ||||
|                         @click="nextImage()" | ||||
|                 ></button> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="c-imagery__control-bar"> | ||||
|             <div class="c-imagery__timestamp">{{ getTime() }}</div> | ||||
|             <div class="c-imagery__time"> | ||||
|                 <div class="c-imagery__timestamp">{{ time }}</div> | ||||
|                 <div | ||||
|                     v-if="canTrackDuration" | ||||
|                     :class="{'c-imagery--new': isImageNew && !refreshCSS}" | ||||
|                     class="c-imagery__age icon-timer" | ||||
|                 >{{ formattedDuration }}</div> | ||||
|             </div> | ||||
|             <div class="h-local-controls flex-elem"> | ||||
|                 <button | ||||
|                     class="c-button icon-pause pause-play" | ||||
|                     :class="{'is-paused': paused()}" | ||||
|                     @click="paused(!paused(), true)" | ||||
|                     :class="{'is-paused': isPaused}" | ||||
|                     @click="paused(!isPaused, 'button')" | ||||
|                 ></button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div ref="thumbsWrapper" | ||||
|          class="c-imagery__thumbs-wrapper" | ||||
|          :class="{'is-paused': paused()}" | ||||
|          :class="{'is-paused': isPaused}" | ||||
|          @scroll="handleScroll" | ||||
|     > | ||||
|         <div v-for="(imageData, index) in imageHistory" | ||||
|              :key="index" | ||||
|         <div v-for="(datum, index) in imageHistory" | ||||
|              :key="datum.url" | ||||
|              class="c-imagery__thumb c-thumb" | ||||
|              :class="{selected: imageData.selected}" | ||||
|              @click="setSelectedImage(imageData)" | ||||
|              :class="{ selected: focusedImageIndex === index && isPaused }" | ||||
|              @click="setFocusedImage(index, thumbnailClick)" | ||||
|         > | ||||
|             <img class="c-thumb__image" | ||||
|                  :src="getImageUrl(imageData)" | ||||
|                  :src="formatImageUrl(datum)" | ||||
|             > | ||||
|             <div class="c-thumb__timestamp">{{ getTime(imageData) }}</div> | ||||
|             <div class="c-thumb__timestamp">{{ formatTime(datum) }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import moment from 'moment'; | ||||
|  | ||||
| const DEFAULT_DURATION_FORMATTER = 'duration'; | ||||
| const REFRESH_CSS_MS = 500; | ||||
| const DURATION_TRACK_MS = 1000; | ||||
| const ARROW_DOWN_DELAY_CHECK_MS = 400; | ||||
| const ARROW_SCROLL_RATE_MS = 100; | ||||
| const THUMBNAIL_CLICKED = true; | ||||
|  | ||||
| const ONE_MINUTE = 60 * 1000; | ||||
| const FIVE_MINUTES = 5 * ONE_MINUTE; | ||||
| const ONE_HOUR = ONE_MINUTE * 60; | ||||
| const EIGHT_HOURS = 8 * ONE_HOUR; | ||||
| const TWENTYFOUR_HOURS = EIGHT_HOURS * 3; | ||||
|  | ||||
| const ARROW_RIGHT = 39; | ||||
| const ARROW_LEFT = 37; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct', 'domainObject'], | ||||
|     data() { | ||||
|         let timeSystem = this.openmct.time.timeSystem(); | ||||
|  | ||||
|         return { | ||||
|             autoScroll: true, | ||||
|             durationFormatter: undefined, | ||||
|             filters: { | ||||
|                 brightness: 100, | ||||
|                 contrast: 100 | ||||
|             }, | ||||
|             image: { | ||||
|                 selected: '' | ||||
|             }, | ||||
|             imageFormat: '', | ||||
|             imageHistory: [], | ||||
|             imageUrl: '', | ||||
|             thumbnailClick: THUMBNAIL_CLICKED, | ||||
|             isPaused: false, | ||||
|             metadata: {}, | ||||
|             requestCount: 0, | ||||
|             timeFormat: '', | ||||
|             keystring: '' | ||||
|             timeSystem: timeSystem, | ||||
|             timeFormatter: undefined, | ||||
|             refreshCSS: false, | ||||
|             keyString: undefined, | ||||
|             focusedImageIndex: undefined, | ||||
|             numericDuration: undefined | ||||
|         }; | ||||
|     }, | ||||
|     computed: { | ||||
|         bounds() { | ||||
|             return this.openmct.time.bounds(); | ||||
|         time() { | ||||
|             return this.formatTime(this.focusedImage); | ||||
|         }, | ||||
|         imageUrl() { | ||||
|             return this.formatImageUrl(this.focusedImage); | ||||
|         }, | ||||
|         isImageNew() { | ||||
|             let cutoff = FIVE_MINUTES; | ||||
|             let age = this.numericDuration; | ||||
|  | ||||
|             return age < cutoff && !this.refreshCSS; | ||||
|         }, | ||||
|         canTrackDuration() { | ||||
|             return this.openmct.time.clock() && this.timeSystem.isUTCBased; | ||||
|         }, | ||||
|         isNextDisabled() { | ||||
|             let disabled = false; | ||||
|  | ||||
|             if (this.focusedImageIndex === -1 || this.focusedImageIndex === this.imageHistory.length - 1) { | ||||
|                 disabled = true; | ||||
|             } | ||||
|  | ||||
|             return disabled; | ||||
|         }, | ||||
|         isPrevDisabled() { | ||||
|             let disabled = false; | ||||
|  | ||||
|             if (this.focusedImageIndex === 0 || this.imageHistory.length < 2) { | ||||
|                 disabled = true; | ||||
|             } | ||||
|  | ||||
|             return disabled; | ||||
|         }, | ||||
|         focusedImage() { | ||||
|             return this.imageHistory[this.focusedImageIndex]; | ||||
|         }, | ||||
|         parsedSelectedTime() { | ||||
|             return this.parseTime(this.focusedImage); | ||||
|         }, | ||||
|         formattedDuration() { | ||||
|             let result = 'N/A'; | ||||
|             let negativeAge = -1; | ||||
|  | ||||
|             if (this.numericDuration > TWENTYFOUR_HOURS) { | ||||
|                 negativeAge *= (this.numericDuration / TWENTYFOUR_HOURS); | ||||
|                 result = moment.duration(negativeAge, 'days').humanize(true); | ||||
|             } else if (this.numericDuration > EIGHT_HOURS) { | ||||
|                 negativeAge *= (this.numericDuration / ONE_HOUR); | ||||
|                 result = moment.duration(negativeAge, 'hours').humanize(true); | ||||
|             } else if (this.durationFormatter) { | ||||
|                 result = this.durationFormatter.format(this.numericDuration); | ||||
|             } | ||||
|  | ||||
|             return result; | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|         focusedImageIndex() { | ||||
|             this.trackDuration(); | ||||
|             this.resetAgeCSS(); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         // set | ||||
|         this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier); | ||||
|         this.metadata = this.openmct.telemetry.getMetadata(this.domainObject); | ||||
|         this.imageFormat = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]); | ||||
|         // initialize | ||||
|         this.timeKey = this.openmct.time.timeSystem().key; | ||||
|         this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey)); | ||||
|         // listen | ||||
|         this.openmct.time.on('bounds', this.boundsChange); | ||||
|         this.openmct.time.on('timeSystem', this.timeSystemChange); | ||||
|         this.openmct.time.on('clock', this.clockChange); | ||||
|  | ||||
|         // set | ||||
|         this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); | ||||
|         this.metadata = this.openmct.telemetry.getMetadata(this.domainObject); | ||||
|         this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER); | ||||
|         this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]); | ||||
|  | ||||
|         // initialize | ||||
|         this.timeKey = this.timeSystem.key; | ||||
|         this.timeFormatter = this.getFormatter(this.timeKey); | ||||
|  | ||||
|         // kickoff | ||||
|         this.subscribe(); | ||||
|         this.requestHistory(); | ||||
| @@ -127,41 +223,55 @@ export default { | ||||
|             delete this.unsubscribe; | ||||
|         } | ||||
|  | ||||
|         this.stopDurationTracking(); | ||||
|         this.openmct.time.off('bounds', this.boundsChange); | ||||
|         this.openmct.time.off('timeSystem', this.timeSystemChange); | ||||
|         this.openmct.time.off('clock', this.clockChange); | ||||
|     }, | ||||
|     methods: { | ||||
|         focusElement() { | ||||
|             this.$el.focus(); | ||||
|         }, | ||||
|         datumIsNotValid(datum) { | ||||
|             if (this.imageHistory.length === 0) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             const datumTime = this.timeFormat.format(datum); | ||||
|             const datumURL = this.imageFormat.format(datum); | ||||
|             const lastHistoryTime = this.timeFormat.format(this.imageHistory.slice(-1)[0]); | ||||
|             const lastHistoryURL = this.imageFormat.format(this.imageHistory.slice(-1)[0]); | ||||
|             const datumURL = this.formatImageUrl(datum); | ||||
|             const lastHistoryURL = this.formatImageUrl(this.imageHistory.slice(-1)[0]); | ||||
|  | ||||
|             // datum is not valid if it matches the last datum in history, | ||||
|             // or it is before the last datum in the history | ||||
|             const datumTimeCheck = this.timeFormat.parse(datum); | ||||
|             const historyTimeCheck = this.timeFormat.parse(this.imageHistory.slice(-1)[0]); | ||||
|             const matchesLast = (datumTime === lastHistoryTime) && (datumURL === lastHistoryURL); | ||||
|             const datumTimeCheck = this.parseTime(datum); | ||||
|             const historyTimeCheck = this.parseTime(this.imageHistory.slice(-1)[0]); | ||||
|             const matchesLast = (datumTimeCheck === historyTimeCheck) && (datumURL === lastHistoryURL); | ||||
|             const isStale = datumTimeCheck < historyTimeCheck; | ||||
|  | ||||
|             return matchesLast || isStale; | ||||
|         }, | ||||
|         getImageUrl(datum) { | ||||
|             return datum | ||||
|                 ? this.imageFormat.format(datum) | ||||
|                 : this.imageUrl; | ||||
|         formatImageUrl(datum) { | ||||
|             if (!datum) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             return this.imageFormatter.format(datum); | ||||
|         }, | ||||
|         getTime(datum) { | ||||
|             let dateTimeStr = datum | ||||
|                 ? this.timeFormat.format(datum) | ||||
|                 : this.time; | ||||
|         formatTime(datum) { | ||||
|             if (!datum) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let dateTimeStr = this.timeFormatter.format(datum); | ||||
|  | ||||
|             // Replace ISO "T" with a space to allow wrapping | ||||
|             return dateTimeStr ? dateTimeStr.replace("T", " ") : ""; | ||||
|             return dateTimeStr.replace("T", " "); | ||||
|         }, | ||||
|         parseTime(datum) { | ||||
|             if (!datum) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             return this.timeFormatter.parse(datum); | ||||
|         }, | ||||
|         handleScroll() { | ||||
|             const thumbsWrapper = this.$refs.thumbsWrapper; | ||||
| @@ -174,26 +284,35 @@ export default { | ||||
|                     || (scrollHeight - scrollTop) > 2 * clientHeight; | ||||
|             this.autoScroll = !disableScroll; | ||||
|         }, | ||||
|         paused(state, button = false) { | ||||
|             if (arguments.length > 0 && state !== this.isPaused) { | ||||
|                 this.unselectAllImages(); | ||||
|                 this.isPaused = state; | ||||
|                 if (state === true && button) { | ||||
|                     // If we are pausing, select the latest image in imageHistory | ||||
|                     this.setSelectedImage(this.imageHistory[this.imageHistory.length - 1]); | ||||
|                 } | ||||
|         paused(state, type) { | ||||
|  | ||||
|                 if (this.nextDatum) { | ||||
|                     this.updateValues(this.nextDatum); | ||||
|                     delete this.nextDatum; | ||||
|                 } else { | ||||
|                     this.updateValues(this.imageHistory[this.imageHistory.length - 1]); | ||||
|                 } | ||||
|             this.isPaused = state; | ||||
|  | ||||
|                 this.autoScroll = true; | ||||
|             if (type === 'button') { | ||||
|                 this.setFocusedImage(this.imageHistory.length - 1); | ||||
|             } | ||||
|  | ||||
|             return this.isPaused; | ||||
|             if (this.nextImageIndex) { | ||||
|                 this.setFocusedImage(this.nextImageIndex); | ||||
|                 delete this.nextImageIndex; | ||||
|             } | ||||
|  | ||||
|             this.autoScroll = true; | ||||
|         }, | ||||
|         scrollToFocused() { | ||||
|             const thumbsWrapper = this.$refs.thumbsWrapper; | ||||
|             if (!thumbsWrapper) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let domThumb = thumbsWrapper.children[this.focusedImageIndex]; | ||||
|  | ||||
|             if (domThumb) { | ||||
|                 domThumb.scrollIntoView({ | ||||
|                     behavior: 'smooth', | ||||
|                     block: 'center' | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         scrollToRight() { | ||||
|             if (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll) { | ||||
| @@ -207,22 +326,17 @@ export default { | ||||
|  | ||||
|             setTimeout(() => this.$refs.thumbsWrapper.scrollLeft = scrollWidth, 0); | ||||
|         }, | ||||
|         setSelectedImage(image) { | ||||
|             // If we are paused and the current image IS selected, unpause | ||||
|             // Otherwise, set current image and pause | ||||
|             if (!image) { | ||||
|         setFocusedImage(index, thumbnailClick = false) { | ||||
|             if (this.isPaused && !thumbnailClick) { | ||||
|                 this.nextImageIndex = index; | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (this.isPaused && image.selected) { | ||||
|                 this.paused(false); | ||||
|                 this.unselectAllImages(); | ||||
|             } else { | ||||
|                 this.imageUrl = this.getImageUrl(image); | ||||
|                 this.time = this.getTime(image); | ||||
|             this.focusedImageIndex = index; | ||||
|  | ||||
|             if (thumbnailClick && !this.isPaused) { | ||||
|                 this.paused(true); | ||||
|                 this.unselectAllImages(); | ||||
|                 image.selected = true; | ||||
|             } | ||||
|         }, | ||||
|         boundsChange(bounds, isTick) { | ||||
| @@ -230,98 +344,158 @@ export default { | ||||
|                 this.requestHistory(); | ||||
|             } | ||||
|         }, | ||||
|         requestHistory() { | ||||
|             const requestId = ++this.requestCount; | ||||
|         async requestHistory() { | ||||
|             let bounds = this.openmct.time.bounds(); | ||||
|             this.requestCount++; | ||||
|             const requestId = this.requestCount; | ||||
|             this.imageHistory = []; | ||||
|             this.openmct.telemetry | ||||
|                 .request(this.domainObject, this.bounds) | ||||
|                 .then((values = []) => { | ||||
|                     if (this.requestCount === requestId) { | ||||
|                         // add each image to the history | ||||
|                         // update values for the very last image (set current image time and url) | ||||
|                         values.forEach((datum, index) => this.updateHistory(datum, index === values.length - 1)); | ||||
|                     } | ||||
|             let data = await this.openmct.telemetry | ||||
|                 .request(this.domainObject, bounds) || []; | ||||
|  | ||||
|             if (this.requestCount === requestId) { | ||||
|                 data.forEach((datum, index) => { | ||||
|                     this.updateHistory(datum, index === data.length - 1); | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         timeSystemChange(system) { | ||||
|             // reset timesystem dependent variables | ||||
|             this.timeKey = system.key; | ||||
|             this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey)); | ||||
|             this.timeSystem = this.openmct.time.timeSystem(); | ||||
|             this.timeKey = this.timeSystem.key; | ||||
|             this.timeFormatter = this.getFormatter(this.timeKey); | ||||
|             this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER); | ||||
|             this.trackDuration(); | ||||
|         }, | ||||
|         clockChange(clock) { | ||||
|             this.trackDuration(); | ||||
|         }, | ||||
|         subscribe() { | ||||
|             this.unsubscribe = this.openmct.telemetry | ||||
|                 .subscribe(this.domainObject, (datum) => { | ||||
|                     let parsedTimestamp = this.timeFormat.parse(datum); | ||||
|                     let parsedTimestamp = this.parseTime(datum); | ||||
|                     let bounds = this.openmct.time.bounds(); | ||||
|  | ||||
|                     if (parsedTimestamp >= this.bounds.start && parsedTimestamp <= this.bounds.end) { | ||||
|                     if (parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) { | ||||
|                         this.updateHistory(datum); | ||||
|                     } | ||||
|                 }); | ||||
|         }, | ||||
|         unselectAllImages() { | ||||
|             this.imageHistory.forEach(image => image.selected = false); | ||||
|         }, | ||||
|         updateHistory(datum, updateValues = true) { | ||||
|         updateHistory(datum, setFocused = true) { | ||||
|             if (this.datumIsNotValid(datum)) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.imageHistory.push(datum); | ||||
|  | ||||
|             if (updateValues) { | ||||
|                 this.updateValues(datum); | ||||
|             if (setFocused) { | ||||
|                 this.setFocusedImage(this.imageHistory.length - 1); | ||||
|             } | ||||
|         }, | ||||
|         updateValues(datum) { | ||||
|             if (this.isPaused) { | ||||
|                 this.nextDatum = datum; | ||||
|         getFormatter(key) { | ||||
|             let metadataValue = this.metadata.value(key) || { format: key }; | ||||
|             let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue); | ||||
|  | ||||
|             return valueFormatter; | ||||
|         }, | ||||
|         trackDuration() { | ||||
|             if (this.canTrackDuration) { | ||||
|                 this.stopDurationTracking(); | ||||
|                 this.updateDuration(); | ||||
|                 this.durationTracker = window.setInterval( | ||||
|                     this.updateDuration, DURATION_TRACK_MS | ||||
|                 ); | ||||
|             } else { | ||||
|                 this.stopDurationTracking(); | ||||
|             } | ||||
|         }, | ||||
|         stopDurationTracking() { | ||||
|             window.clearInterval(this.durationTracker); | ||||
|         }, | ||||
|         updateDuration() { | ||||
|             let currentTime = this.openmct.time.clock().currentValue(); | ||||
|             this.numericDuration = currentTime - this.parsedSelectedTime; | ||||
|         }, | ||||
|         resetAgeCSS() { | ||||
|             this.refreshCSS = true; | ||||
|             // unable to make this work with nextTick | ||||
|             setTimeout(() => { | ||||
|                 this.refreshCSS = false; | ||||
|             }, REFRESH_CSS_MS); | ||||
|         }, | ||||
|         nextImage() { | ||||
|             if (this.isNextDisabled) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.time = this.timeFormat.format(datum); | ||||
|             this.imageUrl = this.imageFormat.format(datum); | ||||
|         }, | ||||
|         selectedImageIndex() { | ||||
|             return this.imageHistory.findIndex(image => image.selected); | ||||
|         }, | ||||
|         setSelectedByIndex(index) { | ||||
|             this.setSelectedImage(this.imageHistory[index]); | ||||
|         }, | ||||
|         nextImage() { | ||||
|             let index = this.selectedImageIndex(); | ||||
|             this.setSelectedByIndex(++index); | ||||
|             let index = this.focusedImageIndex; | ||||
|  | ||||
|             this.setFocusedImage(++index, THUMBNAIL_CLICKED); | ||||
|             if (index === this.imageHistory.length - 1) { | ||||
|                 this.paused(false); | ||||
|             } | ||||
|         }, | ||||
|         prevImage() { | ||||
|             let index = this.selectedImageIndex(); | ||||
|             if (index === -1) { | ||||
|                 this.setSelectedByIndex(this.imageHistory.length - 2); | ||||
|             if (this.isPrevDisabled) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let index = this.focusedImageIndex; | ||||
|  | ||||
|             if (index === this.imageHistory.length - 1) { | ||||
|                 this.setFocusedImage(this.imageHistory.length - 2, THUMBNAIL_CLICKED); | ||||
|             } else { | ||||
|                 this.setSelectedByIndex(--index); | ||||
|                 this.setFocusedImage(--index, THUMBNAIL_CLICKED); | ||||
|             } | ||||
|         }, | ||||
|         isNextDisabled() { | ||||
|             let disabled = false; | ||||
|             let index = this.selectedImageIndex(); | ||||
|         arrowDownHandler(event) { | ||||
|             let key = event.keyCode; | ||||
|  | ||||
|             if (index === -1 || index === this.imageHistory.length - 1) { | ||||
|                 disabled = true; | ||||
|             if (this.isLeftOrRightArrowKey(key)) { | ||||
|                 this.arrowDown = true; | ||||
|                 window.clearTimeout(this.arrowDownDelayTimeout); | ||||
|                 this.arrowDownDelayTimeout = window.setTimeout(() => { | ||||
|                     this.arrowKeyScroll(this.directionByKey(key)); | ||||
|                 }, ARROW_DOWN_DELAY_CHECK_MS); | ||||
|             } | ||||
|  | ||||
|             return disabled; | ||||
|         }, | ||||
|         isPrevDisabled() { | ||||
|             let disabled = false; | ||||
|             let index = this.selectedImageIndex(); | ||||
|         arrowUpHandler(event) { | ||||
|             let key = event.keyCode; | ||||
|  | ||||
|             if (index === 0 || this.imageHistory.length < 2) { | ||||
|                 disabled = true; | ||||
|             window.clearTimeout(this.arrowDownDelayTimeout); | ||||
|  | ||||
|             if (this.isLeftOrRightArrowKey(key)) { | ||||
|                 this.arrowDown = false; | ||||
|                 let direction = this.directionByKey(key); | ||||
|                 this[direction + 'Image'](); | ||||
|             } | ||||
|         }, | ||||
|         arrowKeyScroll(direction) { | ||||
|             if (this.arrowDown) { | ||||
|                 this.arrowKeyScrolling = true; | ||||
|                 this[direction + 'Image'](); | ||||
|                 setTimeout(() => { | ||||
|                     this.arrowKeyScroll(direction); | ||||
|                 }, ARROW_SCROLL_RATE_MS); | ||||
|             } else { | ||||
|                 window.clearTimeout(this.arrowDownDelayTimeout); | ||||
|                 this.arrowKeyScrolling = false; | ||||
|                 this.scrollToFocused(); | ||||
|             } | ||||
|         }, | ||||
|         directionByKey(keyCode) { | ||||
|             let direction; | ||||
|  | ||||
|             if (keyCode === ARROW_LEFT) { | ||||
|                 direction = 'prev'; | ||||
|             } | ||||
|  | ||||
|             return disabled; | ||||
|             if (keyCode === ARROW_RIGHT) { | ||||
|                 direction = 'next'; | ||||
|             } | ||||
|  | ||||
|             return direction; | ||||
|         }, | ||||
|         isLeftOrRightArrowKey(keyCode) { | ||||
|             return [ARROW_RIGHT, ARROW_LEFT].includes(keyCode); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -4,6 +4,10 @@ | ||||
|     overflow: hidden; | ||||
|     height: 100%; | ||||
|  | ||||
|     &:focus { | ||||
|         outline: none; | ||||
|     } | ||||
|  | ||||
|     > * + * { | ||||
|         margin-top: $interiorMargin; | ||||
|     } | ||||
| @@ -25,14 +29,57 @@ | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &__control-bar { | ||||
|         padding: 5px 0 0 0; | ||||
|     &__control-bar, | ||||
|     &__time { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         align-items: baseline; | ||||
|  | ||||
|         > * + * { | ||||
|             margin-left: $interiorMarginSm; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     &__control-bar { | ||||
|         margin-top: 2px; | ||||
|         padding: $interiorMarginSm 0; | ||||
|         justify-content: space-between; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     &__time { | ||||
|         flex: 0 1 auto; | ||||
|         overflow: hidden; | ||||
|     } | ||||
|  | ||||
|     &__timestamp, | ||||
|     &__age { | ||||
|         @include ellipsize(); | ||||
|         flex: 0 1 auto; | ||||
|     } | ||||
|  | ||||
|     &__timestamp { | ||||
|         flex: 1 1 auto; | ||||
|         flex-shrink: 10; | ||||
|     } | ||||
|  | ||||
|     &__age { | ||||
|         border-radius: $controlCr; | ||||
|         display: flex; | ||||
|         flex-shrink: 0; | ||||
|         align-items: baseline; | ||||
|         padding: 1px $interiorMarginSm; | ||||
|  | ||||
|         &:before { | ||||
|             opacity: 0.5; | ||||
|             margin-right: $interiorMarginSm; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &--new { | ||||
|         // New imagery | ||||
|         $bgColor: $colorOk; | ||||
|         background: rgba($bgColor, 0.5); | ||||
|         @include flash($animName: flashImageAge, $dur: 250ms, $valStart: rgba($colorOk, 0.7), $valEnd: rgba($colorOk, 0)); | ||||
|     } | ||||
|  | ||||
|     &__thumbs-wrapper { | ||||
| @@ -151,6 +198,8 @@ | ||||
| /*************************************** BUTTONS */ | ||||
| .c-button.pause-play { | ||||
|     // Pause icon set by default in markup | ||||
|     justify-self: end; | ||||
|  | ||||
|     &.is-paused { | ||||
|         background: $colorPausedBg !important; | ||||
|         color: $colorPausedFg; | ||||
|   | ||||
| @@ -50,6 +50,18 @@ | ||||
| } | ||||
|  | ||||
| /************************** EFFECTS */ | ||||
| @mixin flash($animName: flash, $dur: 500ms, $dir: alternate, $iter: 20, $prop: background, $valStart: rgba($colorOk, 1), $valEnd: rgba($colorOk, 0)) { | ||||
|     @keyframes #{$animName} { | ||||
|         0%   { #{$prop}: $valStart; } | ||||
|         100% {  #{$prop}: $valEnd; } | ||||
|     } | ||||
|     animation-name: $animName; | ||||
|     animation-duration: $dur; | ||||
|     animation-direction: $dir; | ||||
|     animation-iteration-count: $iter; | ||||
|     animation-timing-function: ease-out; | ||||
| } | ||||
|  | ||||
| @mixin mixedBg() { | ||||
|     $c1: nth($mixedSettingBg, 1); | ||||
|     $c2: nth($mixedSettingBg, 2); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user