Example imagery vue (#2525)
* WIP: imagery vue refactor * cleaup * show orange border when paused. * resize image and thumbs wrappers. * scrollToBottom fixed. * fixed lint errors * use multipane vue component for resize + cleanup + style adjustments. * added min-height to image pane and thumbs-layout pane. * remove old plugin and using es6 const. * using ES6 imports. * clean up + formatting changes. * updated as per review comments. * extracted styles from vue component. * fixed lint errors. * updated as per review comments + cleanup.
This commit is contained in:
@@ -1,86 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"./src/policies/ImageryViewPolicy",
|
||||
"./src/controllers/ImageryController",
|
||||
"./src/directives/MCTBackgroundImage",
|
||||
"./res/templates/imagery.html"
|
||||
], function (
|
||||
ImageryViewPolicy,
|
||||
ImageryController,
|
||||
MCTBackgroundImage,
|
||||
imageryTemplate
|
||||
) {
|
||||
|
||||
return {
|
||||
name:"platform/features/imagery",
|
||||
definition: {
|
||||
"name": "Plot view for telemetry",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"name": "Imagery",
|
||||
"key": "imagery",
|
||||
"cssClass": "icon-image",
|
||||
"template": imageryTemplate,
|
||||
"priority": "preferred",
|
||||
"needs": [
|
||||
"telemetry"
|
||||
],
|
||||
"editable": false
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
{
|
||||
"category": "view",
|
||||
"implementation": ImageryViewPolicy,
|
||||
"depends": [
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "ImageryController",
|
||||
"implementation": ImageryController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$window",
|
||||
"$element",
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctBackgroundImage",
|
||||
"implementation": MCTBackgroundImage,
|
||||
"depends": [
|
||||
"$document"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
<div class="t-imagery c-imagery" ng-controller="ImageryController as imagery">
|
||||
<mct-split-pane class='abs' anchor="bottom" alias="imagery">
|
||||
<div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col">
|
||||
<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">
|
||||
<input class="icon-brightness" type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
ng-model="filters.brightness" />
|
||||
<input class="icon-contrast" type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
ng-model="filters.contrast" />
|
||||
</span>
|
||||
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
|
||||
<a class="s-icon-button icon-reset t-btn-reset"
|
||||
ng-click="filters = { brightness: 100, contrast: 100 }"></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="l-image-main s-image-main flex-elem grows"
|
||||
ng-class="{ paused: imagery.paused(), stale:false }">
|
||||
<div class="image-main"
|
||||
|
||||
mct-background-image="imagery.getImageUrl()"
|
||||
filters="filters">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="l-image-main-controlbar flex-elem l-flex-row">
|
||||
<div class="l-datetime-w flex-elem grows">
|
||||
<a class="c-button show-thumbs sm hidden icon-thumbs-strip"
|
||||
ng-click="showThumbsBubble = (showThumbsBubble) ? false:true"></a>
|
||||
<span class="l-time">{{imagery.getTime()}}</span>
|
||||
</div>
|
||||
<div class="h-local-controls flex-elem">
|
||||
<a class="c-button icon-pause pause-play"
|
||||
ng-click="imagery.paused(!imagery.paused())"
|
||||
ng-class="{ 'is-paused': imagery.paused() }"></a>
|
||||
<a href=""
|
||||
class="s-button l-mag s-mag vsm icon-reset"
|
||||
ng-click="clipped = false"
|
||||
ng-show="clipped === true"
|
||||
title="Not all of image is visible; click to reset."></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mct-splitter></mct-splitter>
|
||||
<div class="split-pane-component l-image-thumbs-wrapper">
|
||||
<div class="l-image-thumb-item" ng-class="{selected: image.selected}" ng-repeat="image in imageHistory track by $index"
|
||||
ng-click="imagery.setSelectedImage(image)" ng-init="imagery.scrollToBottom()">
|
||||
<img class="l-thumb"
|
||||
ng-src={{imagery.getImageUrl(image)}}>
|
||||
<div class="l-time">{{imagery.getTime(image)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</mct-split-pane>
|
||||
</div>
|
||||
@@ -1,284 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements views of image telemetry.
|
||||
* @namespace platform/features/imagery
|
||||
*/
|
||||
|
||||
define(
|
||||
[
|
||||
'zepto',
|
||||
'lodash'
|
||||
],
|
||||
function ($, _) {
|
||||
|
||||
/**
|
||||
* Controller for the "Imagery" view of a domain object which
|
||||
* provides image telemetry.
|
||||
* @constructor
|
||||
* @memberof platform/features/imagery
|
||||
*/
|
||||
|
||||
function ImageryController($scope, $window, element, openmct) {
|
||||
this.$scope = $scope;
|
||||
this.$window = $window;
|
||||
this.openmct = openmct;
|
||||
this.date = "";
|
||||
this.time = "";
|
||||
this.zone = "";
|
||||
this.imageUrl = "";
|
||||
this.requestCount = 0;
|
||||
this.scrollable = $(".l-image-thumbs-wrapper");
|
||||
this.autoScroll = openmct.time.clock() ? true : false;
|
||||
this.$scope.imageHistory = [];
|
||||
this.$scope.filters = {
|
||||
brightness: 100,
|
||||
contrast: 100
|
||||
};
|
||||
|
||||
this.subscribe = this.subscribe.bind(this);
|
||||
this.stopListening = this.stopListening.bind(this);
|
||||
this.updateValues = this.updateValues.bind(this);
|
||||
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);
|
||||
|
||||
this.$scope.$on('$destroy', this.stopListening);
|
||||
this.openmct.time.on('bounds', this.onBoundsChange);
|
||||
this.scrollable.on('scroll', this.onScroll);
|
||||
}
|
||||
|
||||
ImageryController.prototype.subscribe = function (domainObject) {
|
||||
this.date = "";
|
||||
this.imageUrl = "";
|
||||
this.openmct.objects.get(domainObject.getId())
|
||||
.then(function (object) {
|
||||
this.domainObject = object;
|
||||
var metadata = this.openmct
|
||||
.telemetry
|
||||
.getMetadata(this.domainObject);
|
||||
this.timeKey = this.openmct.time.timeSystem().key;
|
||||
this.timeFormat = this.openmct
|
||||
.telemetry
|
||||
.getValueFormatter(metadata.value(this.timeKey));
|
||||
this.imageFormat = this.openmct
|
||||
.telemetry
|
||||
.getValueFormatter(metadata.valuesForHints(['image'])[0]);
|
||||
this.unsubscribe = this.openmct.telemetry
|
||||
.subscribe(this.domainObject, function (datum) {
|
||||
this.updateHistory(datum);
|
||||
this.updateValues(datum);
|
||||
}.bind(this));
|
||||
|
||||
this.requestHistory(this.openmct.time.bounds());
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ImageryController.prototype.requestHistory = function (bounds) {
|
||||
this.requestCount++;
|
||||
this.$scope.imageHistory = [];
|
||||
var requestId = this.requestCount;
|
||||
this.openmct.telemetry
|
||||
.request(this.domainObject, bounds)
|
||||
.then(function (values) {
|
||||
if (this.requestCount > requestId) {
|
||||
return Promise.resolve('Stale request');
|
||||
}
|
||||
|
||||
values.forEach(function (datum) {
|
||||
this.updateHistory(datum);
|
||||
}, this);
|
||||
|
||||
this.updateValues(values[values.length - 1]);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ImageryController.prototype.stopListening = function () {
|
||||
this.openmct.time.off('bounds', this.onBoundsChange);
|
||||
this.scrollable.off('scroll', this.onScroll);
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
delete this.unsubscribe;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Responds to bound change event be requesting new
|
||||
* historical data if the bound change was manual.
|
||||
* @private
|
||||
* @param {object} [newBounds] new bounds object
|
||||
* @param {boolean} [tick] true when change is automatic
|
||||
*/
|
||||
ImageryController.prototype.onBoundsChange = function (newBounds, tick) {
|
||||
if (this.domainObject && !tick) {
|
||||
this.requestHistory(newBounds);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates displayable values to match those of the most
|
||||
* recently received datum.
|
||||
* @param {object} [datum] the datum
|
||||
* @private
|
||||
*/
|
||||
ImageryController.prototype.updateValues = function (datum) {
|
||||
if (this.isPaused) {
|
||||
this.nextDatum = datum;
|
||||
return;
|
||||
}
|
||||
this.time = this.timeFormat.format(datum);
|
||||
this.imageUrl = this.imageFormat.format(datum);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Appends given imagery datum to running history.
|
||||
* @private
|
||||
* @param {object} [datum] target telemetry datum
|
||||
* @returns {boolean} falsy when a duplicate datum is given
|
||||
*/
|
||||
ImageryController.prototype.updateHistory = function (datum) {
|
||||
if (!this.datumMatchesMostRecent(datum)) {
|
||||
var index = _.sortedIndex(this.$scope.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
|
||||
this.$scope.imageHistory.splice(index, 0, datum);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks to see if the given datum is the same as the most recent in history.
|
||||
* @private
|
||||
* @param {object} [datum] target telemetry datum
|
||||
* @returns {boolean} true if datum is most recent in history, false otherwise
|
||||
*/
|
||||
ImageryController.prototype.datumMatchesMostRecent = function (datum) {
|
||||
if (this.$scope.imageHistory.length !== 0) {
|
||||
var datumTime = this.timeFormat.format(datum);
|
||||
var datumURL = this.imageFormat.format(datum);
|
||||
var lastHistoryTime = this.timeFormat.format(this.$scope.imageHistory.slice(-1)[0]);
|
||||
var lastHistoryURL = this.imageFormat.format(this.$scope.imageHistory.slice(-1)[0]);
|
||||
|
||||
return datumTime === lastHistoryTime && datumURL === lastHistoryURL;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
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) - (thumbnailWrapperWidth) ||
|
||||
this.scrollable[0].scrollTop <
|
||||
(this.scrollable[0].scrollHeight - this.scrollable[0].clientHeight) - (thumbnailWrapperHeight)) {
|
||||
this.autoScroll = false;
|
||||
} else {
|
||||
this.autoScroll = true;
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Force history imagery div to scroll to bottom.
|
||||
*/
|
||||
ImageryController.prototype.scrollToBottom = function () {
|
||||
if (this.autoScroll) {
|
||||
this.scrollable[0].scrollTop = this.scrollable[0].scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the time portion (hours, minutes, seconds) of the
|
||||
* timestamp associated with the incoming image telemetry
|
||||
* if no parameter is given, or of a provided datum.
|
||||
* @param {object} [datum] target telemetry datum
|
||||
* @returns {string} the time
|
||||
*/
|
||||
ImageryController.prototype.getTime = function (datum) {
|
||||
return datum ?
|
||||
this.timeFormat.format(datum) :
|
||||
this.time;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the URL of the most recent image telemetry if no
|
||||
* parameter is given, or of a provided datum.
|
||||
* @param {object} [datum] target telemetry datum
|
||||
* @returns {string} URL for telemetry image
|
||||
*/
|
||||
ImageryController.prototype.getImageUrl = function (datum) {
|
||||
return datum ?
|
||||
this.imageFormat.format(datum) :
|
||||
this.imageUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Getter-setter for paused state of the view (true means
|
||||
* paused, false means not.)
|
||||
* @param {boolean} [state] the state to set
|
||||
* @returns {boolean} the current state
|
||||
*/
|
||||
ImageryController.prototype.paused = function (state) {
|
||||
if (arguments.length > 0 && state !== this.isPaused) {
|
||||
this.unselectAllImages();
|
||||
this.isPaused = state;
|
||||
if (this.nextDatum) {
|
||||
this.updateValues(this.nextDatum);
|
||||
delete this.nextDatum;
|
||||
} else {
|
||||
this.updateValues(this.$scope.imageHistory[this.$scope.imageHistory.length - 1]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
);
|
||||
@@ -1,110 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Defines the `mct-background-image` directive.
|
||||
*
|
||||
* Used as an attribute, this will set the `background-image`
|
||||
* property to the URL given in its value, but only after that
|
||||
* image has loaded; this avoids "flashing" as images change.
|
||||
*
|
||||
* If the value of `mct-background-image`is falsy, no image
|
||||
* will be displayed (immediately.)
|
||||
*
|
||||
* Optionally, a `filters` attribute may be specified as an
|
||||
* object with `brightness` and/or `contrast` properties,
|
||||
* whose values are percentages. A value of 100 will make
|
||||
* no changes to the image's brightness or contrast.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/imagery
|
||||
*/
|
||||
function MCTBackgroundImage($document) {
|
||||
function link(scope, element) {
|
||||
// General strategy here:
|
||||
// - Keep count of how many images have been requested; this
|
||||
// counter will be used as an internal identifier or sorts
|
||||
// for each image that loads.
|
||||
// - As the src attribute changes, begin loading those images.
|
||||
// - When images do load, update the background-image property
|
||||
// of the element, but only if a more recently
|
||||
// requested image has not already been loaded.
|
||||
// The order in which URLs are passed in and the order
|
||||
// in which images are actually loaded may be different, so
|
||||
// some strategy like this is necessary to ensure that images
|
||||
// do not display out-of-order.
|
||||
var requested = 0, loaded = 0;
|
||||
|
||||
function updateFilters(filters) {
|
||||
var styleValue = filters ?
|
||||
Object.keys(filters).map(function (k) {
|
||||
return k + "(" + filters[k] + "%)";
|
||||
}).join(' ') :
|
||||
"";
|
||||
element.css('filter', styleValue);
|
||||
element.css('webkitFilter', styleValue);
|
||||
}
|
||||
|
||||
function nextImage(url) {
|
||||
var myCounter = requested,
|
||||
image;
|
||||
|
||||
function useImage() {
|
||||
if (loaded <= myCounter) {
|
||||
loaded = myCounter;
|
||||
element.css('background-image', "url('" + url + "')");
|
||||
}
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
loaded = myCounter;
|
||||
element.css('background-image', 'none');
|
||||
} else {
|
||||
image = $document[0].createElement('img');
|
||||
image.src = url;
|
||||
image.onload = useImage;
|
||||
}
|
||||
|
||||
requested += 1;
|
||||
}
|
||||
|
||||
scope.$watch('mctBackgroundImage', nextImage);
|
||||
scope.$watchCollection('filters', updateFilters);
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: "A",
|
||||
scope: {
|
||||
mctBackgroundImage: "=",
|
||||
filters: "="
|
||||
},
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
return MCTBackgroundImage;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'../../../../../src/api/objects/object-utils'
|
||||
], function (
|
||||
objectUtils
|
||||
) {
|
||||
/**
|
||||
* Policy preventing the Imagery view from being made available for
|
||||
* domain objects which do not have associated image telemetry.
|
||||
* @implements {Policy.<View, DomainObject>}
|
||||
* @constructor
|
||||
*/
|
||||
function ImageryViewPolicy(openmct) {
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
ImageryViewPolicy.prototype.hasImageTelemetry = function (domainObject) {
|
||||
var newDO = objectUtils.toNewFormat(
|
||||
domainObject.getModel(),
|
||||
domainObject.getId()
|
||||
);
|
||||
|
||||
var metadata = this.openmct.telemetry.getMetadata(newDO);
|
||||
var values = metadata.valuesForHints(['image']);
|
||||
return values.length >= 1;
|
||||
};
|
||||
|
||||
ImageryViewPolicy.prototype.allow = function (view, domainObject) {
|
||||
if (view.key === 'imagery' || view.key === 'historical-imagery') {
|
||||
return this.hasImageTelemetry(domainObject);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return ImageryViewPolicy;
|
||||
});
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[
|
||||
"zepto",
|
||||
"../../src/controllers/ImageryController"
|
||||
],
|
||||
function ($, ImageryController) {
|
||||
|
||||
var MOCK_ELEMENT_TEMPLATE =
|
||||
'<div class="l-image-thumbs-wrapper"></div>';
|
||||
|
||||
xdescribe("The Imagery controller", function () {
|
||||
var $scope,
|
||||
openmct,
|
||||
oldDomainObject,
|
||||
newDomainObject,
|
||||
unsubscribe,
|
||||
metadata,
|
||||
prefix,
|
||||
controller,
|
||||
requestPromise,
|
||||
mockWindow,
|
||||
mockElement;
|
||||
|
||||
beforeEach(function () {
|
||||
$scope = jasmine.createSpyObj('$scope', ['$on', '$watch']);
|
||||
oldDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getId']
|
||||
);
|
||||
newDomainObject = { name: 'foo' };
|
||||
oldDomainObject.getId.and.returnValue('testID');
|
||||
openmct = {
|
||||
objects: jasmine.createSpyObj('objectAPI', [
|
||||
'get'
|
||||
]),
|
||||
time: jasmine.createSpyObj('timeAPI', [
|
||||
'timeSystem',
|
||||
'clock',
|
||||
'on',
|
||||
'off',
|
||||
'bounds'
|
||||
]),
|
||||
telemetry: jasmine.createSpyObj('telemetryAPI', [
|
||||
'subscribe',
|
||||
'request',
|
||||
'getValueFormatter',
|
||||
'getMetadata'
|
||||
])
|
||||
};
|
||||
metadata = jasmine.createSpyObj('metadata', [
|
||||
'value',
|
||||
'valuesForHints'
|
||||
]);
|
||||
metadata.value.and.returnValue("timestamp");
|
||||
metadata.valuesForHints.and.returnValue(["value"]);
|
||||
|
||||
prefix = "formatted ";
|
||||
unsubscribe = jasmine.createSpy('unsubscribe');
|
||||
openmct.telemetry.subscribe.and.returnValue(unsubscribe);
|
||||
openmct.time.timeSystem.and.returnValue({
|
||||
key: 'testKey'
|
||||
});
|
||||
$scope.domainObject = oldDomainObject;
|
||||
openmct.objects.get.and.returnValue(Promise.resolve(newDomainObject));
|
||||
openmct.telemetry.getMetadata.and.returnValue(metadata);
|
||||
openmct.telemetry.getValueFormatter.and.callFake(function (property) {
|
||||
var formatter =
|
||||
jasmine.createSpyObj("formatter-" + property, ['format']);
|
||||
var isTime = (property === "timestamp");
|
||||
formatter.format.and.callFake(function (datum) {
|
||||
return (isTime ? prefix : "") + datum[property];
|
||||
});
|
||||
return formatter;
|
||||
});
|
||||
|
||||
requestPromise = new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve([{
|
||||
timestamp: 1434600258123,
|
||||
value: 'some/url'
|
||||
}]);
|
||||
}, 10);
|
||||
});
|
||||
|
||||
openmct.telemetry.request.and.returnValue(requestPromise);
|
||||
mockElement = $(MOCK_ELEMENT_TEMPLATE);
|
||||
mockWindow = jasmine.createSpyObj('$window', ['requestAnimationFrame']);
|
||||
mockWindow.requestAnimationFrame.and.callFake(function (f) {
|
||||
return f();
|
||||
});
|
||||
|
||||
controller = new ImageryController(
|
||||
$scope,
|
||||
mockWindow,
|
||||
mockElement,
|
||||
openmct
|
||||
);
|
||||
});
|
||||
|
||||
describe("when loaded", function () {
|
||||
var callback,
|
||||
boundsListener,
|
||||
bounds;
|
||||
|
||||
beforeEach(function () {
|
||||
return requestPromise.then(function () {
|
||||
openmct.time.on.calls.all().forEach(function (call) {
|
||||
if (call.args[0] === "bounds") {
|
||||
boundsListener = call.args[1];
|
||||
}
|
||||
});
|
||||
callback =
|
||||
openmct.telemetry.subscribe.calls.mostRecent().args[1];
|
||||
});
|
||||
});
|
||||
|
||||
it("requests history", function () {
|
||||
expect(openmct.telemetry.request).toHaveBeenCalledWith(
|
||||
newDomainObject, bounds
|
||||
);
|
||||
expect(controller.getTime()).toEqual(prefix + 1434600258123);
|
||||
expect(controller.getImageUrl()).toEqual('some/url');
|
||||
});
|
||||
|
||||
|
||||
it("exposes the latest telemetry values", function () {
|
||||
callback({
|
||||
timestamp: 1434600259456,
|
||||
value: "some/other/url"
|
||||
});
|
||||
|
||||
expect(controller.getTime()).toEqual(prefix + 1434600259456);
|
||||
expect(controller.getImageUrl()).toEqual("some/other/url");
|
||||
});
|
||||
|
||||
it("allows updates to be paused and unpaused", function () {
|
||||
var newTimestamp = 1434600259456,
|
||||
newUrl = "some/other/url",
|
||||
initialTimestamp = controller.getTime(),
|
||||
initialUrl = controller.getImageUrl();
|
||||
|
||||
expect(initialTimestamp).not.toBe(prefix + newTimestamp);
|
||||
expect(initialUrl).not.toBe(newUrl);
|
||||
expect(controller.paused()).toBeFalsy();
|
||||
|
||||
controller.paused(true);
|
||||
expect(controller.paused()).toBeTruthy();
|
||||
callback({ timestamp: newTimestamp, value: newUrl });
|
||||
|
||||
expect(controller.getTime()).toEqual(initialTimestamp);
|
||||
expect(controller.getImageUrl()).toEqual(initialUrl);
|
||||
|
||||
controller.paused(false);
|
||||
expect(controller.paused()).toBeFalsy();
|
||||
expect(controller.getTime()).toEqual(prefix + newTimestamp);
|
||||
expect(controller.getImageUrl()).toEqual(newUrl);
|
||||
});
|
||||
|
||||
it("forwards large image view to latest image in history on un-pause", function () {
|
||||
$scope.imageHistory = [
|
||||
{ utc: 1434600258122, url: 'some/url1', selected: false},
|
||||
{ utc: 1434600258123, url: 'some/url2', selected: false}
|
||||
];
|
||||
controller.paused(true);
|
||||
controller.paused(false);
|
||||
|
||||
expect(controller.getImageUrl()).toEqual(controller.getImageUrl($scope.imageHistory[1]));
|
||||
});
|
||||
|
||||
it("subscribes to telemetry", function () {
|
||||
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
|
||||
newDomainObject,
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("requests telemetry", function () {
|
||||
expect(openmct.telemetry.request).toHaveBeenCalledWith(
|
||||
newDomainObject,
|
||||
bounds
|
||||
);
|
||||
});
|
||||
|
||||
it("unsubscribes and unlistens when scope is destroyed", function () {
|
||||
expect(unsubscribe).not.toHaveBeenCalled();
|
||||
|
||||
$scope.$on.calls.all().forEach(function (call) {
|
||||
if (call.args[0] === '$destroy') {
|
||||
call.args[1]();
|
||||
}
|
||||
});
|
||||
expect(unsubscribe).toHaveBeenCalled();
|
||||
expect(openmct.time.off)
|
||||
.toHaveBeenCalledWith('bounds', jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("listens for bounds event and responds to tick and manual change", function () {
|
||||
var mockBounds = {start: 1434600000000, end: 1434600500000};
|
||||
expect(openmct.time.on).toHaveBeenCalled();
|
||||
openmct.telemetry.request.calls.reset();
|
||||
boundsListener(mockBounds, true);
|
||||
expect(openmct.telemetry.request).not.toHaveBeenCalled();
|
||||
boundsListener(mockBounds, false);
|
||||
expect(openmct.telemetry.request).toHaveBeenCalledWith(newDomainObject, mockBounds);
|
||||
});
|
||||
|
||||
it ("doesnt append duplicate datum", function () {
|
||||
var mockDatum = {value: 'image/url', timestamp: 1434700000000};
|
||||
var mockDatum2 = {value: 'image/url', timestamp: 1434700000000};
|
||||
var mockDatum3 = {value: 'image/url', url: 'someval', timestamp: 1434700000000};
|
||||
expect(controller.updateHistory(mockDatum)).toBe(true);
|
||||
expect(controller.updateHistory(mockDatum)).toBe(false);
|
||||
expect(controller.updateHistory(mockDatum)).toBe(false);
|
||||
expect(controller.updateHistory(mockDatum2)).toBe(false);
|
||||
expect(controller.updateHistory(mockDatum3)).toBe(false);
|
||||
});
|
||||
|
||||
describe("when 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 () {
|
||||
expect(controller.getTime()).toEqual("");
|
||||
expect(controller.getImageUrl()).toEqual("");
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/directives/MCTBackgroundImage"],
|
||||
function (MCTBackgroundImage) {
|
||||
|
||||
describe("The mct-background-image directive", function () {
|
||||
var mockDocument,
|
||||
mockScope,
|
||||
mockElement,
|
||||
testImage,
|
||||
directive;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDocument = [
|
||||
jasmine.createSpyObj('document', ['createElement'])
|
||||
];
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
'$watch',
|
||||
'$watchCollection'
|
||||
]);
|
||||
mockElement = jasmine.createSpyObj('element', ['css']);
|
||||
testImage = {};
|
||||
|
||||
mockDocument[0].createElement.and.returnValue(testImage);
|
||||
|
||||
directive = new MCTBackgroundImage(mockDocument);
|
||||
});
|
||||
|
||||
it("is applicable as an attribute", function () {
|
||||
expect(directive.restrict).toEqual("A");
|
||||
});
|
||||
|
||||
it("two-way-binds its own value", function () {
|
||||
expect(directive.scope.mctBackgroundImage).toEqual("=");
|
||||
});
|
||||
|
||||
describe("once linked", function () {
|
||||
beforeEach(function () {
|
||||
directive.link(mockScope, mockElement, {});
|
||||
});
|
||||
|
||||
it("watches for changes to the URL", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
'mctBackgroundImage',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("updates images in-order, even when they load out-of-order", function () {
|
||||
var firstOnload;
|
||||
|
||||
mockScope.$watch.calls.mostRecent().args[1]("some/url/0");
|
||||
firstOnload = testImage.onload;
|
||||
|
||||
mockScope.$watch.calls.mostRecent().args[1]("some/url/1");
|
||||
|
||||
// Resolve in a different order
|
||||
testImage.onload();
|
||||
firstOnload();
|
||||
|
||||
// Should still have taken the more recent value
|
||||
expect(mockElement.css.calls.mostRecent().args).toEqual([
|
||||
"background-image",
|
||||
"url('some/url/1')"
|
||||
]);
|
||||
});
|
||||
|
||||
it("clears the background image when undefined is passed in", function () {
|
||||
mockScope.$watch.calls.mostRecent().args[1]("some/url/0");
|
||||
testImage.onload();
|
||||
mockScope.$watch.calls.mostRecent().args[1](undefined);
|
||||
|
||||
expect(mockElement.css.calls.mostRecent().args).toEqual([
|
||||
"background-image",
|
||||
"none"
|
||||
]);
|
||||
});
|
||||
|
||||
it("updates filters on change", function () {
|
||||
var filters = { brightness: 123, contrast: 21 };
|
||||
mockScope.$watchCollection.calls.all().forEach(function (call) {
|
||||
if (call.args[0] === 'filters') {
|
||||
call.args[1](filters);
|
||||
}
|
||||
});
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'filter',
|
||||
'brightness(123%) contrast(21%)'
|
||||
);
|
||||
});
|
||||
|
||||
it("clears filters when none are present", function () {
|
||||
mockScope.$watchCollection.calls.all().forEach(function (call) {
|
||||
if (call.args[0] === 'filters') {
|
||||
call.args[1](undefined);
|
||||
}
|
||||
});
|
||||
expect(mockElement.css)
|
||||
.toHaveBeenCalledWith('filter', '');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/policies/ImageryViewPolicy"],
|
||||
function (ImageryViewPolicy) {
|
||||
|
||||
describe("Imagery view policy", function () {
|
||||
var testView,
|
||||
openmct,
|
||||
mockDomainObject,
|
||||
mockTelemetry,
|
||||
mockMetadata,
|
||||
policy;
|
||||
|
||||
beforeEach(function () {
|
||||
testView = { key: "imagery" };
|
||||
mockMetadata = jasmine.createSpyObj('metadata', [
|
||||
"valuesForHints"
|
||||
]);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getId', 'getModel', 'getCapability']
|
||||
);
|
||||
mockTelemetry = jasmine.createSpyObj(
|
||||
'telemetry',
|
||||
['getMetadata']
|
||||
);
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
return c === 'telemetry' ? mockTelemetry : undefined;
|
||||
});
|
||||
mockDomainObject.getId.and.returnValue("some-id");
|
||||
mockDomainObject.getModel.and.returnValue({ name: "foo" });
|
||||
mockTelemetry.getMetadata.and.returnValue(mockMetadata);
|
||||
mockMetadata.valuesForHints.and.returnValue(["bar"]);
|
||||
|
||||
openmct = { telemetry: mockTelemetry };
|
||||
|
||||
policy = new ImageryViewPolicy(openmct);
|
||||
});
|
||||
|
||||
it("checks for hints indicating image telemetry", function () {
|
||||
policy.allow(testView, mockDomainObject);
|
||||
expect(mockMetadata.valuesForHints)
|
||||
.toHaveBeenCalledWith(["image"]);
|
||||
});
|
||||
|
||||
it("allows the imagery view for domain objects with image telemetry", function () {
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("disallows the imagery view for domain objects without image telemetry", function () {
|
||||
mockMetadata.valuesForHints.and.returnValue([]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("allows other views", function () {
|
||||
testView.key = "somethingElse";
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user