From 8e8c66280f6a7593c5d1d60032232817251a7148 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 11 Aug 2017 14:35:21 -0700 Subject: [PATCH] Fix for Issue #1676 Add history imagery under large imagery view. Allow users to click on history imagery thumbs to set main image and pause the imagery view. Allow users to unpause and continue imagery stream. Users can adjust the height of the imagery panes, and the user selected height is persisted. --- .../commonUI/general/res/sass/_constants.scss | 4 +- .../general/res/sass/features/_imagery.scss | 35 +++++----- .../general/src/directives/MCTSplitPane.js | 13 ++-- platform/features/imagery/bundle.js | 13 ---- .../imagery/res/templates/imagery.html | 13 +++- .../res/templates/imageryTimeline.html | 8 --- .../src/controllers/ImageryController.js | 66 +++++++++++++------ .../test/controllers/ImageryControllerSpec.js | 22 +++++++ 8 files changed, 111 insertions(+), 63 deletions(-) delete mode 100644 platform/features/imagery/res/templates/imageryTimeline.html diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 040752e26d..283e1b8695 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -21,7 +21,7 @@ *****************************************************************************/ /************************** FEATURES */ -$enableImageryThumbs: false; // Set to true if historical imagery thumbnails are supported +$enableImageryThumbs: true; // Set to true if historical imagery thumbnails are supported /************************** VERY INFLUENTIAL GLOBAL DIMENSIONS */ $bodyMargin: 10px; @@ -82,7 +82,7 @@ $tabularTdPadTB: 2px; /*************** Imagery */ $imageMainControlBarH: 25px; $imageThumbsD: 120px; -$imageThumbsWrapperH: $imageThumbsD * 1.4; +$imageThumbsWrapperH: 155px; $imageThumbPad: 1px; /*************** Ticks */ $ticksH: 25px; diff --git a/platform/commonUI/general/res/sass/features/_imagery.scss b/platform/commonUI/general/res/sass/features/_imagery.scss index e3d1b58e49..e9c71262fe 100644 --- a/platform/commonUI/general/res/sass/features/_imagery.scss +++ b/platform/commonUI/general/res/sass/features/_imagery.scss @@ -9,7 +9,6 @@ @if $enableImageryThumbs == true { bottom: $interiorMargin*2 + $imageThumbsWrapperH; } - min-height: 100px; min-width: 150px; .l-image-main { background-color: $colorPlotBg; @@ -22,7 +21,8 @@ .l-image-thumbs-wrapper { top: auto; - height: $imageThumbsWrapperH; + min-height: $imageThumbsWrapperH; + max-height: 60%; } .l-date, @@ -82,9 +82,8 @@ /*************************************** THUMBS */ .l-image-thumbs-wrapper { - //@include test(green); - overflow-x: auto; - overflow-y: hidden; + overflow-x: hidden; + overflow-y: auto; padding-bottom: $interiorMargin; white-space: nowrap; } @@ -92,8 +91,17 @@ .l-image-thumb-item { @include transition(background-color, 0.25s); box-sizing: border-box; + cursor: pointer; + direction: ltr; + display: inline-block; + float: left; + font-size: 0.8em; padding: 1px; - position: relative; + margin-left: $interiorMarginSm; + position: relative; + text-align: left; + width: $imageThumbsD + $imageThumbPad*2; + white-space: normal; .l-thumb, .l-date, .l-time { @@ -103,14 +111,7 @@ .l-time { padding: 2px 3px; } - cursor: pointer; - direction: ltr; - display: inline-block; - font-size: 0.8em; - margin-left: $interiorMarginSm; - text-align: left; - width: $imageThumbsD + $imageThumbPad*2; - white-space: normal; + &:hover { background: $colorThumbHoverBg; .l-date, @@ -184,7 +185,8 @@ /*************************************** WHEN IN FRAME */ .frame .t-imagery { .l-image-main-wrapper { - bottom: 0; + bottom: 0 !important; + height: 100% !important; .l-image-main-controlbar { font-size: 0.7em; } @@ -194,7 +196,8 @@ } } } - .l-image-thumbs-wrapper { + .l-image-thumbs-wrapper, + mct-splitter { display: none; } } diff --git a/platform/commonUI/general/src/directives/MCTSplitPane.js b/platform/commonUI/general/src/directives/MCTSplitPane.js index f84c67a0a3..6a7aa0a53f 100644 --- a/platform/commonUI/general/src/directives/MCTSplitPane.js +++ b/platform/commonUI/general/src/directives/MCTSplitPane.js @@ -117,9 +117,7 @@ define( // Apply styles to child elements function updateChildren(children) { - if (alias) { - position = userWidthPreference || position; - } + position = userWidthPreference || position; // Pick out correct elements to update, flowing from // selected anchor edge. @@ -134,6 +132,11 @@ define( // Get actual size (to obey min-width etc.) firstSize = getSize(first[0]); + + if (alias === 'mctSplitPane-imagery') { + firstSize = firstSize - 5; + } + first.css(anchor.dimension, firstSize + 'px'); splitter.css(anchor.edge, firstSize + 'px'); splitter.css(anchor.opposite, "auto"); @@ -182,7 +185,9 @@ define( } function setUserWidthPreference(value) { - userWidthPreference = value; + if (alias) { + userWidthPreference = value; + } } function persistToLocalStorage(value) { diff --git a/platform/features/imagery/bundle.js b/platform/features/imagery/bundle.js index ddca767501..25c0b1820f 100644 --- a/platform/features/imagery/bundle.js +++ b/platform/features/imagery/bundle.js @@ -25,14 +25,12 @@ define([ "./src/controllers/ImageryController", "./src/directives/MCTBackgroundImage", "text!./res/templates/imagery.html", - "text!./res/templates/imageryTimeline.html", 'legacyRegistry' ], function ( ImageryViewPolicy, ImageryController, MCTBackgroundImage, imageryTemplate, - imageryTimelineTemplate, legacyRegistry ) { @@ -50,17 +48,6 @@ define([ "telemetry" ], "editable": false - }, - { - "name": "Historical Imagery", - "key": "historical-imagery", - "cssClass": "icon-image", - "template": imageryTimelineTemplate, - "priority": "preferred", - "needs": [ - "telemetry" - ], - "editable": false } ], "policies": [ diff --git a/platform/features/imagery/res/templates/imagery.html b/platform/features/imagery/res/templates/imagery.html index 1b2b1c3e53..e67cf5ae16 100644 --- a/platform/features/imagery/res/templates/imagery.html +++ b/platform/features/imagery/res/templates/imagery.html @@ -1,5 +1,6 @@
-
+
+ +
+
+ +
{{imagery.getTime(image)}}
+
+
+
diff --git a/platform/features/imagery/res/templates/imageryTimeline.html b/platform/features/imagery/res/templates/imageryTimeline.html deleted file mode 100644 index a0a2723d86..0000000000 --- a/platform/features/imagery/res/templates/imageryTimeline.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
- -
{{imagery.getTime(image)}}
-
-
diff --git a/platform/features/imagery/src/controllers/ImageryController.js b/platform/features/imagery/src/controllers/ImageryController.js index d8be8d46dc..73713dc448 100644 --- a/platform/features/imagery/src/controllers/ImageryController.js +++ b/platform/features/imagery/src/controllers/ImageryController.js @@ -48,9 +48,8 @@ define( this.zone = ""; this.imageUrl = ""; this.requestCount = 0; - this.scrollable = $(element[0]); + this.scrollable = $(".l-image-thumbs-wrapper"); this.autoScroll = openmct.time.clock() ? true : false; - this.$scope.imageHistory = []; this.$scope.filters = { brightness: 100, @@ -63,6 +62,7 @@ define( this.updateHistory = this.updateHistory.bind(this); this.onBoundsChange = this.onBoundsChange.bind(this); this.onScroll = this.onScroll.bind(this); + this.setSelectedImage = this.setSelectedImage.bind(this); this.subscribe(this.$scope.domainObject); @@ -80,10 +80,10 @@ define( var metadata = this.openmct .telemetry .getMetadata(this.domainObject); - var timeKey = this.openmct.time.timeSystem().key; + this.timeKey = this.openmct.time.timeSystem().key; this.timeFormat = this.openmct .telemetry - .getValueFormatter(metadata.value(timeKey)); + .getValueFormatter(metadata.value(this.timeKey)); this.imageFormat = this.openmct .telemetry .getValueFormatter(metadata.valuesForHints(['image'])[0]); @@ -161,7 +161,7 @@ define( /** * Updates displayable values to match those of the most - * recently recieved datum. + * recently received datum. * @param {object} [datum] the datum * @private */ @@ -170,7 +170,6 @@ define( this.nextDatum = datum; return; } - this.time = this.timeFormat.format(datum); this.imageUrl = this.imageFormat.format(datum); @@ -185,8 +184,7 @@ define( ImageryController.prototype.updateHistory = function (datum) { if (this.$scope.imageHistory.length === 0 || !_.isEqual(this.$scope.imageHistory.slice(-1)[0], datum)) { - - var index = _.sortedIndex(this.$scope.imageHistory, datum, 'utc'); + var index = _.sortedIndex(this.$scope.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat)); this.$scope.imageHistory.splice(index, 0, datum); return true; } @@ -196,8 +194,12 @@ define( ImageryController.prototype.onScroll = function (event) { this.$window.requestAnimationFrame(function () { + var thumbnailWrapperHeight = this.scrollable[0].offsetHeight; + var thumbnailWrapperWidth = this.scrollable[0].offsetWidth; if (this.scrollable[0].scrollLeft < - (this.scrollable[0].scrollWidth - this.scrollable[0].clientWidth) - 20) { + (this.scrollable[0].scrollWidth - this.scrollable[0].clientWidth) - (thumbnailWrapperWidth) || + this.scrollable[0].scrollTop < + (this.scrollable[0].scrollHeight - this.scrollable[0].clientHeight) - (thumbnailWrapperHeight)) { this.autoScroll = false; } else { this.autoScroll = true; @@ -205,12 +207,16 @@ define( }.bind(this)); }; - ImageryController.prototype.scrollToRight = function () { + /** + * Force history imagery div to scroll to bottom. + */ + ImageryController.prototype.scrollToBottom = function () { if (this.autoScroll) { - this.scrollable[0].scrollLeft = this.scrollable[0].scrollWidth; + this.scrollable[0].scrollTop = this.scrollable[0].scrollHeight; } }; + /** * Get the time portion (hours, minutes, seconds) of the * timestamp associated with the incoming image telemetry @@ -243,16 +249,38 @@ define( * @returns {boolean} the current state */ ImageryController.prototype.paused = function (state) { - if (arguments.length > 0 && state !== this.isPaused) { - this.isPaused = state; - if (this.nextDatum) { - this.updateValues(this.nextDatum); - delete this.nextDatum; - } + if (arguments.length > 0 && state !== this.isPaused) { + this.unselectAllImages(); + this.isPaused = state; + if (this.nextDatum) { + this.updateValues(this.nextDatum); + delete this.nextDatum; } - return this.isPaused; - }; + this.autoScroll = true; + } + return this.isPaused; + }; + /** + * Set the selected image on the state for the large imagery div to use. + * @param {object} [image] the image object to get url from. + */ + ImageryController.prototype.setSelectedImage = function (image) { + this.imageUrl = this.getImageUrl(image); + this.time = this.getTime(image); + this.paused(true); + this.unselectAllImages(); + image.selected = true; + }; + + /** + * Loop through the history imagery data to set all images to unselected. + */ + ImageryController.prototype.unselectAllImages = function () { + for (var i = 0; i < this.$scope.imageHistory.length; i++) { + this.$scope.imageHistory[i].selected = false; + } + }; return ImageryController; } ); diff --git a/platform/features/imagery/test/controllers/ImageryControllerSpec.js b/platform/features/imagery/test/controllers/ImageryControllerSpec.js index ce81e6ef67..64393b35c4 100644 --- a/platform/features/imagery/test/controllers/ImageryControllerSpec.js +++ b/platform/features/imagery/test/controllers/ImageryControllerSpec.js @@ -226,6 +226,28 @@ define( expect(controller.updateHistory(mockDatum)).toBe(false); expect(controller.updateHistory(mockDatum)).toBe(false); }); + + describe("user clicks on imagery thumbnail", function () { + var mockDatum = { utc: 1434600258123, url: 'some/url', selected: false}; + + it("pauses and adds selected class to imagery thumbnail", function () { + controller.setSelectedImage(mockDatum); + expect(controller.paused()).toBeTruthy(); + expect(mockDatum.selected).toBeTruthy(); + }); + + it("unselects previously selected image", function () { + $scope.imageHistory = [{ utc: 1434600258123, url: 'some/url', selected: true}]; + controller.unselectAllImages(); + expect($scope.imageHistory[0].selected).toBeFalsy(); + }); + + it("updates larger image url and time", function () { + controller.setSelectedImage(mockDatum); + expect(controller.getImageUrl()).toEqual(controller.getImageUrl(mockDatum)); + expect(controller.getTime()).toEqual(controller.timeFormat.format(mockDatum.utc)); + }); + }); }); it("initially shows an empty string for date/time", function () {