Compare commits

...

90 Commits

Author SHA1 Message Date
Joshi
bd6c1a73ce Zoom pan controls moved to component 2022-03-04 16:05:00 -08:00
Joshi
dae6784c82 Merge branch 'mct34' of https://github.com/nasa/openmct into mct34 2022-03-04 13:22:43 -08:00
Michael Rogers
f8f7a625f9 Clip overflow to allow pause border to be seen 2022-02-25 11:56:48 -06:00
Michael Rogers
cbac592ef1 Add default value for param 2022-02-24 16:15:49 -06:00
Michael Rogers
55dce252fd Moved imagery controls to a separate component 2022-02-24 16:07:04 -06:00
John Hill
64d3da22ce Merge branch 'master' into mct34 2022-02-22 14:15:48 -08:00
Michael Rogers
5b1ae06ab6 Merge branch 'master' into mct34 2022-02-18 08:52:00 -06:00
Joshi
ba94cc8981 Merge branch 'master' of https://github.com/nasa/openmct into mct34 2022-02-18 06:35:32 -08:00
Michael Rogers
1a7bf5040d Update selector for e2e 2022-02-15 15:12:49 -06:00
Michael Rogers
d6a45234a2 Externalized static css properties 2022-02-15 15:09:42 -06:00
Michael Rogers
4262b7df9d Cleanup 2022-02-15 10:37:10 -06:00
Michael Rogers
527245cbe5 Zoom factor moved to computed 2022-02-15 10:35:07 -06:00
Michael Rogers
58c35c7f9e Change cursor zoom in/out and pannable to computer properties 2022-02-14 15:34:37 -06:00
Michael Rogers
92c3a78e46 Changed htimeouts to hover to wait for stable element 2022-02-14 14:15:32 -06:00
Michael Rogers
a2b4eb36fc Mct34 refactor (#4854)
* refactored

* use this.imageIndex to store new image index.

* clean up pause function and remove nextImageIndex.

* fixed issue with persisting selection of old image.

Co-authored-by: Nikhil Mandlik <nikhil.k.mandlik@nasa.gov>
2022-02-14 13:35:32 -06:00
Michael Rogers
d794509417 Merge branch 'master' into mct34 2022-02-11 13:50:51 -06:00
Michael Rogers
ee206382cf Merge branch 'mct34' of github.com:nasa/openmct into mct34 2022-02-10 11:44:06 -06:00
Michael Rogers
52557f38cf Test for UI zoom / reset buttons 2022-02-09 16:55:51 -06:00
Michael Rogers
d08cbe469f Merge branch 'master' into mct34 2022-02-08 11:53:57 -06:00
Michael Rogers
37f6c855e3 e2e test for wheel zoom and panning 2022-02-04 18:23:17 -06:00
unlikelyzero
e3563ebbbc comparison 2022-02-04 07:17:30 -08:00
unlikelyzero
4a68fdb798 Changes 2022-02-04 07:13:35 -08:00
unlikelyzero
5578c041fb more clarification 2022-02-02 15:49:46 -08:00
unlikelyzero
47605cac74 More scenarios 2022-02-02 15:48:48 -08:00
unlikelyzero
5b29969dca test stubs 2022-02-02 15:43:52 -08:00
Michael Rogers
a0e0f6b589 Added debug logs 2022-02-02 15:46:30 -06:00
Michael Rogers
c76f56308c Changed last focused image logic to preserve when last index 2022-02-02 14:48:19 -06:00
Michael Rogers
d35e09616e Ignore meta and alt pressed clicks on the layout 2022-02-02 13:44:44 -06:00
Michael Rogers
37a06c27f5 Fixed linting errors 2022-02-02 11:32:26 -06:00
Michael Rogers
2605ab7956 Add cursor styling 2022-02-02 09:27:41 -06:00
Jamie Vigliotta
67190c0099 using a prop I created 2022-02-01 16:57:30 -08:00
Jamie Vigliotta
398b003a46 Merge branch 'mct34' of http://github.com/nasa/openmct into mct34
Merge'n with Michael
2022-02-01 16:45:15 -08:00
Jamie Vigliotta
0ed669af99 chasing down a race condition 2022-02-01 16:44:55 -08:00
unlikelyzero
a8a92cf702 first attempt of a e2e test 2022-02-01 15:42:52 -08:00
Michael Rogers
acba9b73bc Only left clicks for zoom 2022-02-01 17:18:23 -06:00
Michael Rogers
d5b4c0ba7a Key tracking for meta and shift keys 2022-02-01 16:53:59 -06:00
Michael Rogers
3d55e6eebe Merge branch 'mct34' of github.com:nasa/openmct into mct34 2022-02-01 14:20:06 -06:00
Charles Hacskaylo
e05986704b Merge branch 'mct34' of github.com:nasa/openmct into mct34 2022-02-01 11:21:54 -08:00
Charles Hacskaylo
e61595ba65 Tweaks for mct34
- Added in-image pan hint element;
2022-02-01 11:21:45 -08:00
Michael Rogers
e03dc38ed0 Merge branch 'release/1.8.4' into mct34 2022-02-01 13:09:37 -06:00
Charles Hacskaylo
c0334ed8d1 Tweaks for mct34
- Added title attributes to zoom and pan buttons;
2022-02-01 11:06:43 -08:00
Michael Rogers
78bd44007f Added visibile clip and corrected background-size property 2022-02-01 13:00:57 -06:00
Michael Rogers
a575e35cfd Revert image tag comment 2022-02-01 13:00:56 -06:00
Michael Rogers
f532e8702e Cleanup canvas remnants 2022-02-01 13:00:56 -06:00
Michael Rogers
f94a12ff82 Removed draw library 2022-02-01 13:00:56 -06:00
Michael Rogers
402b7be7d5 Moved constants to a global 2022-02-01 13:00:56 -06:00
Charles Hacskaylo
6e985c08bd Styling for mct34
- Moved controls into local controls holder;
- `<a>` tags converted to `button`;
- WIP! Have not linted or made sure I didn't break tests yet;
2022-02-01 13:00:56 -06:00
Michael Rogers
63d35a9f77 Code cleanup 2022-02-01 13:00:56 -06:00
Michael Rogers
8c12359968 Add debounced method to track wheel zoom sequence 2022-02-01 13:00:56 -06:00
Michael Rogers
a3799d0b9e Capture initial wheelzoom coordinates by using debounced flag 2022-02-01 13:00:56 -06:00
Michael Rogers
36495c33e7 Pan capability 2022-02-01 13:00:56 -06:00
Michael Rogers
4260f49604 Change click listener to mousedown 2022-02-01 13:00:55 -06:00
Michael Rogers
23f85a2a38 Add panning handlers 2022-02-01 13:00:55 -06:00
Andrew Henry
a213b781df Alternative implementation of zoomImage 2022-02-01 13:00:55 -06:00
Michael Rogers
bc274244f8 Separated the translation calculations externally 2022-02-01 13:00:55 -06:00
Michael Rogers
98d5702949 Remove unused draw library files 2022-02-01 13:00:55 -06:00
Michael Rogers
dffbc68652 WIP: scale translation 2022-02-01 13:00:55 -06:00
Michael Rogers
03dd1744e5 ARemove canvas related element and scale using background-image 2022-02-01 13:00:50 -06:00
Michael Rogers
50c7ee58e6 Exclude tests 2022-02-01 12:55:52 -06:00
Michael Rogers
fb02c1b789 Added zoom control button test 2022-02-01 12:55:52 -06:00
Michael Rogers
1a54d440ad Add event helpers and draw library from plots 2022-02-01 12:55:52 -06:00
Michael Rogers
b16d50e456 Add a selectable class to main-image to enable crosshair and disable drag 2022-02-01 12:55:52 -06:00
Michael Rogers
8485e79b27 Added visibile clip and corrected background-size property 2022-01-31 16:46:33 -06:00
Michael Rogers
9653def074 Revert image tag comment 2022-01-31 15:00:25 -06:00
Michael Rogers
fff822fbf7 Cleanup canvas remnants 2022-01-31 14:34:11 -06:00
Michael Rogers
c23ab66321 Removed draw library 2022-01-31 10:20:19 -06:00
Michael Rogers
28da3ed887 Moved constants to a global 2022-01-31 09:17:11 -06:00
Charles Hacskaylo
daaf7f0bda Styling for mct34
- Moved controls into local controls holder;
- `<a>` tags converted to `button`;
- WIP! Have not linted or made sure I didn't break tests yet;
2022-01-28 17:12:04 -08:00
Shefali Joshi
2732e9f2a6 Revert "Request data when switching between real time and fixed. (#4807)" (#4809)
This reverts commit 232907618c.
2022-01-28 15:11:07 -08:00
Shefali Joshi
232907618c Request data when switching between real time and fixed. (#4807)
* Request data when switching between real time and fixed.
Also add some null checks
* Address review comments - add more conditional logic to optimize time conductor.
2022-01-28 10:10:33 -08:00
Michael Rogers
a5c734b793 Code cleanup 2022-01-27 13:43:42 -06:00
Shefali Joshi
44f5372c31 fix typo when using fallback template (#4784) 2022-01-26 13:34:22 -08:00
Shefali Joshi
2f292fbd07 Follow domain object changes for Independent time conductor (#4783)
* Track if domain object changes when independent time conductor is in use.
2022-01-26 13:04:47 -08:00
Andrew Henry
205dc67809 Observe changes to sub-objects in flexible layouts. (#4780)
* Get child of flex layout as mutable if possible

* Fix bug when no default notebook defined
2022-01-25 23:22:42 -08:00
Michael Rogers
fb84c0510b Add debounced method to track wheel zoom sequence 2022-01-25 15:24:31 -06:00
Scott Bell
169c23dbcc update copyright (#4775) 2022-01-25 12:27:27 -08:00
Michael Rogers
a20da34a60 Capture initial wheelzoom coordinates by using debounced flag 2022-01-24 16:10:26 -06:00
Michael Rogers
bfc7f2d405 Pan capability 2022-01-24 14:56:18 -06:00
Michael Rogers
30a7846988 Change click listener to mousedown 2022-01-24 10:42:33 -06:00
Michael Rogers
d1a899cb78 Add panning handlers 2022-01-21 17:36:44 -06:00
Andrew Henry
d53ce2d88c Alternative implementation of zoomImage 2022-01-21 12:30:14 -08:00
Michael Rogers
0d06308814 Separated the translation calculations externally 2022-01-21 11:17:51 -06:00
Shefali Joshi
457cd42987 Update version number (#4759) 2022-01-20 18:57:05 -08:00
Michael Rogers
118866363f Remove unused draw library files 2022-01-20 11:09:28 -06:00
Michael Rogers
8b3f6b914c WIP: scale translation 2022-01-19 17:13:36 -06:00
Michael Rogers
6e5574ddfd ARemove canvas related element and scale using background-image 2022-01-19 15:42:22 -06:00
Michael Rogers
c69374b538 Exclude tests 2022-01-19 15:18:04 -06:00
Michael Rogers
c4cc2f43e3 Added zoom control button test 2022-01-19 11:09:12 -06:00
Michael Rogers
5ec9b915fd Add event helpers and draw library from plots 2022-01-05 10:40:38 -06:00
Michael Rogers
4bb452804e Add a selectable class to main-image to enable crosshair and disable drag 2021-12-03 16:06:02 -06:00
7 changed files with 953 additions and 149 deletions

View File

@@ -0,0 +1,217 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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 test suite is dedicated to tests which verify the basic operations surrounding imagery,
but only assume that example imagery is present.
*/
const { test, expect } = require('@playwright/test');
test.describe('Example Imagery', () => {
test.beforeEach(async ({ page }) => {
page.on('console', msg => console.log(msg.text()))
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Example Imagery
await page.click('text=Example Imagery');
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/dab945d4-5a84-480e-8180-222b4aa730fa?tc.mode=fixed&tc.startBound=1639696164435&tc.endBound=1639697964435&tc.timeSystem=utc&view=conditionSet.view' }*/),
page.click('text=OK')
]);
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
});
const backgroundImageSelector = '.c-imagery__main-image__background-image';
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
const bgImageLocator = await page.locator(backgroundImageSelector);
const deltaYStep = 100; //equivalent to 1x zoom
await bgImageLocator.hover();
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
// zoom in
await bgImageLocator.hover();
await page.mouse.wheel(0, deltaYStep * 2);
// wait for zoom animation to finish
await bgImageLocator.hover();
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
// zoom out
await bgImageLocator.hover();
await page.mouse.wheel(0, -deltaYStep);
// wait for zoom animation to finish
await bgImageLocator.hover();
const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox();
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
expect(imageMouseZoomedOut.height).toBeLessThan(imageMouseZoomedIn.height);
expect(imageMouseZoomedOut.width).toBeLessThan(imageMouseZoomedIn.width);
});
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
const deltaYStep = 100; //equivalent to 1x zoom
const bgImageLocator = await page.locator(backgroundImageSelector);
await bgImageLocator.hover();
// zoom in
await page.mouse.wheel(0, deltaYStep * 2);
await bgImageLocator.hover();
const zoomedBoundingBox = await bgImageLocator.boundingBox();
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
// move to the right
// center the mouse pointer
await page.mouse.move(imageCenterX, imageCenterY);
// pan right
await page.keyboard.down('Alt');
await page.mouse.down();
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
await page.mouse.up();
await page.keyboard.up('Alt');
const afterRightPanBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
// pan left
await page.keyboard.down('Alt');
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY, 10);
await page.mouse.up();
await page.keyboard.up('Alt');
const afterLeftPanBoundingBox = await bgImageLocator.boundingBox();
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
// pan up
await page.mouse.move(imageCenterX, imageCenterY);
await page.keyboard.down('Alt');
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
await page.mouse.up();
await page.keyboard.up('Alt');
const afterUpPanBoundingBox = await bgImageLocator.boundingBox();
expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
// pan down
await page.keyboard.down('Alt');
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
await page.mouse.up();
await page.keyboard.up('Alt');
const afterDownPanBoundingBox = await bgImageLocator.boundingBox();
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
});
test('Can use + - buttons to zoom on the image', async ({ page }) => {
const bgImageLocator = await page.locator(backgroundImageSelector);
await bgImageLocator.hover();
const zoomInBtn = await page.locator('.t-btn-zoom-in');
const zoomOutBtn = await page.locator('.t-btn-zoom-out');
const initialBoundingBox = await bgImageLocator.boundingBox();
await zoomInBtn.click();
await zoomInBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover();
const zoomedInBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
await zoomOutBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover();
const zoomedOutBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
});
test('Can use the reset button to reset the image', async ({ page }) => {
const bgImageLocator = await page.locator(backgroundImageSelector);
await bgImageLocator.hover();
const zoomInBtn = await page.locator('.t-btn-zoom-in');
const zoomResetBtn = await page.locator('.t-btn-zoom-reset');
const initialBoundingBox = await bgImageLocator.boundingBox();
await zoomInBtn.click();
await zoomInBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover();
const zoomedInBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
await zoomResetBtn.click();
await bgImageLocator.hover();
const resetBoundingBox = await bgImageLocator.boundingBox();
expect(resetBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
expect(resetBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
expect(resetBoundingBox.height).toEqual(initialBoundingBox.height);
expect(resetBoundingBox.width).toEqual(initialBoundingBox.width);
});
//test('Can use Mouse Wheel to zoom in and out of previous image');
//test('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
//test.skip('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
//test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
//test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
//test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
});
test.describe('Example Imagery in Display layout', () => {
test.skip('Can use Mouse Wheel to zoom in and out of previous image');
test.skip('Can use alt+drag to move around image once zoomed in');
test.skip('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
});
test.describe('Example Imagery in Flexible layout', () => {
test.skip('Can use Mouse Wheel to zoom in and out of previous image');
test.skip('Can use alt+drag to move around image once zoomed in');
test.skip('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
});
test.describe('Example Imagery in Tabs view', () => {
test.skip('Can use Mouse Wheel to zoom in and out of previous image');
test.skip('Can use alt+drag to move around image once zoomed in');
test.skip('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
test.skip('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
});

View File

@@ -89,7 +89,7 @@
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
"test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke default condition.e2e",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js default",
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",

View File

@@ -0,0 +1,172 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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.
*****************************************************************************/
<template>
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
<div class="c-image-controls__control c-image-controls__zoom icon-magnify">
<div class="c-button-set c-button-set--strip-h">
<button class="c-button t-btn-zoom-out icon-minus"
title="Zoom out"
@click="incrementZoomFactor(-1)"
></button>
<button class="c-button t-btn-zoom-in icon-plus"
title="Zoom in"
@click="incrementZoomFactor(1)"
></button>
</div>
<button class="c-button t-btn-zoom-lock"
title="Lock current zoom and pan across all images"
:class="{'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked}"
@click="toggleLock"
></button>
<button class="c-button icon-reset t-btn-zoom-reset"
title="Remove zoom and pan"
@click="handleResetImage"
></button>
<span class="c-image-controls__zoom-factor">x{{ zoomFactor }}</span>
</div>
<div class="c-image-controls__control c-image-controls__brightness-contrast">
<span
class="c-image-controls__sliders"
draggable="true"
@dragstart="startDrag"
>
<div class="c-image-controls__input icon-brightness">
<input
v-model="filters.contrast"
type="range"
min="0"
max="500"
@change="notifyFiltersChanged"
>
</div>
<div class="c-image-controls__input icon-contrast">
<input
v-model="filters.brightness"
type="range"
min="0"
max="500"
@change="notifyFiltersChanged"
>
</div>
</span>
<span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
<button class="c-icon-link icon-reset t-btn-reset"
@click="handleResetFilters"
></button>
</span>
</div>
</div>
</template>
<script>
const DEFAULT_FILTER_VALUES = {
brightness: '100',
contrast: '100'
};
const ZOOM_LIMITS_MAX_DEFAULT = 20;
const ZOOM_LIMITS_MIN_DEFAULT = 1;
export default {
inject: ['openmct', 'domainObject'],
props: {
formattedZoomFactor: {
type: Number,
default() {
return 1;
}
}
},
data() {
return {
altPressed: false,
shiftPressed: false,
metaPressed: false,
panZoomLocked: false,
zoomFactor: 1,
filters: {
brightness: 100,
contrast: 100
}
};
},
watch: {
formattedZoomFactor(newZoomFactor) {
this.zoomFactor = newZoomFactor;
}
},
methods: {
handleResetImage() {
this.$emit('resetImage', true);
},
handleUpdatePanZoom(options) {
this.$emit('panZoomUpdated', options);
},
toggleLock() {
this.panZoomLocked = !this.panZoomLocked;
},
notifyFiltersChanged() {
this.$emit('filtersUpdated', this.filters);
},
handleResetFilters() {
this.filters = DEFAULT_FILTER_VALUES;
this.notifyFiltersChanged();
},
startDrag(e) {
e.preventDefault();
e.stopPropagation();
},
// used to increment the zoom without knowledge of current level
incrementZoomFactor(increment, userCoordX, userCoordY) {
const newFactor = this.zoomFactor + increment;
this.zoomImage(newFactor, userCoordX, userCoordY);
},
zoomImage(newScaleFactor, screenClientX, screenClientY) {
if (newScaleFactor > ZOOM_LIMITS_MAX_DEFAULT) {
newScaleFactor = ZOOM_LIMITS_MAX_DEFAULT;
return;
}
if (newScaleFactor <= 0 || newScaleFactor < ZOOM_LIMITS_MIN_DEFAULT) {
this.zoomFactor = 1;
this.panZoomLocked = false;
return this.handleResetImage(true);
}
this.handleUpdatePanZoom({
newScaleFactor,
screenClientX,
screenClientY
});
}
}
};
</script>

View File

@@ -29,52 +29,70 @@
@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 c-image-controls__controls">
<span class="c-image-controls__sliders"
draggable="true"
@dragstart="startDrag"
>
<div class="c-image-controls__slider-wrapper icon-brightness">
<input v-model="filters.brightness"
type="range"
min="0"
max="500"
>
</div>
<div class="c-image-controls__slider-wrapper icon-contrast">
<input v-model="filters.contrast"
type="range"
min="0"
max="500"
>
</div>
</span>
<span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
<a class="s-icon-button icon-reset t-btn-reset"
@click="filters={brightness: 100, contrast: 100}"
></a>
</span>
</div>
<ImageControls
:formatted-zoom-factor="formattedZoomFactor"
@resetImage="resetImage"
@panZoomUpdated="handlePanZoomUpdate"
@filtersUpdated="setFilters"
/>
<div ref="imageBG"
class="c-imagery__main-image__bg"
:class="{'paused unnsynced': isPaused && !isFixed,'stale':false }"
:class="{
'paused unnsynced': isPaused && !isFixed,
'stale': false,
'pannable': isPannable,
'cursor-zoom-in': showCursorZoomIn,
'cursor-zoom-out': showCursorZoomOut
}"
@click="expand"
>
<div class="image-wrapper"
<div v-if="zoomFactor > 1"
class="c-imagery__hints"
>Alt-drag to pan</div>
<div ref="focusedImageWrapper"
class="image-wrapper"
:style="{
'width': `${sizedImageDimensions.width}px`,
'height': `${sizedImageDimensions.height}px`
'width': `${sizedImageWidth}px`,
'height': `${sizedImageHeight}px`
}"
>
<img ref="focusedImage"
class="c-imagery__main-image__image js-imageryView-image"
class="c-imagery__main-image__image js-imageryView-image "
:src="imageUrl"
:draggable="!isSelectable"
:style="{
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
}"
:data-openmct-image-timestamp="time"
:data-openmct-object-keystring="keyString"
>
<div
v-if="imageUrl"
ref="focusedImageElement"
class="c-imagery__main-image__background-image"
:draggable="!isSelectable"
:style="{
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`,
'background-image':
`${imageUrl ? (
`url(${imageUrl}),
repeating-linear-gradient(
45deg,
transparent,
transparent 4px,
rgba(125,125,125,.2) 4px,
rgba(125,125,125,.2) 8px
)`
) : ''}`,
'transform': `scale(${zoomFactor}) translate(${imageTranslateX}px, ${imageTranslateY}px)`,
'transition': `${!pan && animateZoom ? 'transform 250ms ease-in' : 'initial'}`,
'width': `${sizedImageWidth}px`,
'height': `${sizedImageHeight}px`,
}"
@mousedown="handlePanZoomClick"
></div>
<Compass
v-if="shouldDisplayCompass"
:compass-rose-sizing-classes="compassRoseSizingClasses"
@@ -125,7 +143,7 @@
v-if="!isFixed"
class="c-button icon-pause pause-play"
:class="{'is-paused': isPaused}"
@click="paused(!isPaused, 'button')"
@click="paused(!isPaused)"
></button>
</div>
</div>
@@ -145,7 +163,7 @@
:key="image.url + image.time"
class="c-imagery__thumb c-thumb"
:class="{ selected: focusedImageIndex === index && isPaused }"
@click="setFocusedImage(index, thumbnailClick)"
@click="thumbnailClicked(index)"
>
<a href=""
:download="image.imageDownloadName"
@@ -169,12 +187,13 @@
</template>
<script>
import eventHelpers from '../lib/eventHelpers';
import _ from 'lodash';
import moment from 'moment';
import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry';
import Compass from './Compass/Compass.vue';
import ImageControls from './ImageControls.vue';
import imageryData from "../../imagery/mixins/imageryData";
const REFRESH_CSS_MS = 500;
@@ -196,7 +215,8 @@ const SCROLL_LATENCY = 250;
export default {
components: {
Compass
Compass,
ImageControls
},
mixins: [imageryData],
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
@@ -219,10 +239,6 @@ export default {
timeSystem: timeSystem,
keyString: undefined,
autoScroll: true,
filters: {
brightness: 100,
contrast: 100
},
thumbnailClick: THUMBNAIL_CLICKED,
isPaused: false,
refreshCSS: false,
@@ -234,19 +250,32 @@ export default {
focusedImageNaturalAspectRatio: undefined,
imageContainerWidth: undefined,
imageContainerHeight: undefined,
sizedImageWidth: 0,
sizedImageHeight: 0,
lockCompass: true,
resizingWindow: false,
timeContext: undefined
timeContext: undefined,
zoomFactor: 1,
filters: {
brightness: 100,
contrast: 100
},
imageTranslateX: 0,
imageTranslateY: 0,
pan: undefined,
animateZoom: true,
imagePanned: false,
wheelZooming: false
};
},
computed: {
compassRoseSizingClasses() {
let compassRoseSizingClasses = '';
if (this.sizedImageDimensions.width < 300) {
if (this.sizedImageWidth < 300) {
compassRoseSizingClasses = '--rose-small --rose-min';
} else if (this.sizedImageDimensions.width < 500) {
} else if (this.sizedImageWidth < 500) {
compassRoseSizingClasses = '--rose-small';
} else if (this.sizedImageDimensions.width > 1000) {
} else if (this.sizedImageWidth > 1000) {
compassRoseSizingClasses = '--rose-max';
}
@@ -315,10 +344,18 @@ export default {
return result;
},
shouldDisplayCompass() {
return this.focusedImage !== undefined
const imageHeightAndWidth = this.sizedImageHeight !== 0
&& this.sizedImageWidth !== 0;
const display = this.focusedImage !== undefined
&& this.focusedImageNaturalAspectRatio !== undefined
&& this.imageContainerWidth !== undefined
&& this.imageContainerHeight !== undefined;
&& this.imageContainerHeight !== undefined
&& imageHeightAndWidth
&& this.zoomFactor === 1
&& this.imagePanned !== true;
return display;
},
isSpacecraftPositionFresh() {
let isFresh = undefined;
@@ -380,20 +417,6 @@ export default {
return isFresh;
},
sizedImageDimensions() {
let sizedImageDimensions = {};
if ((this.imageContainerWidth / this.imageContainerHeight) > this.focusedImageNaturalAspectRatio) {
// container is wider than image
sizedImageDimensions.width = this.imageContainerHeight * this.focusedImageNaturalAspectRatio;
sizedImageDimensions.height = this.imageContainerHeight;
} else {
// container is taller than image
sizedImageDimensions.width = this.imageContainerWidth;
sizedImageDimensions.height = this.imageContainerWidth / this.focusedImageNaturalAspectRatio;
}
return sizedImageDimensions;
},
isFixed() {
let clock;
if (this.timeContext) {
@@ -403,24 +426,70 @@ export default {
}
return clock === undefined;
},
isSelectable() {
return true;
},
sizedImageDimensions() {
return {
width: this.sizedImageWidth,
height: this.sizedImageHeight
};
},
isPannable() {
return this.altPressed && this.zoomFactor > 1;
},
showCursorZoomIn() {
return this.metaPressed && !this.shiftPressed;
},
showCursorZoomOut() {
return this.metaPressed && this.shiftPressed;
},
formattedZoomFactor() {
return Number.parseFloat(this.zoomFactor).toPrecision(2);
}
},
watch: {
imageUrl(newUrl, oldUrl) {
if (newUrl) {
this.resetImage();
}
},
imageHistory: {
handler(newHistory, oldHistory) {
const newSize = newHistory.length;
let imageIndex;
if (this.focusedImageTimestamp !== undefined) {
const foundImageIndex = this.imageHistory.findIndex(image => {
return image.time === this.focusedImageTimestamp;
});
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
const foundImageIndex = newHistory.findIndex(img => img.time === this.focusedImageTimestamp);
imageIndex = foundImageIndex > -1
? foundImageIndex
: newSize - 1;
} else {
imageIndex = newSize > 0 ? newSize - 1 : undefined;
imageIndex = newSize > 0
? newSize - 1
: undefined;
}
this.setFocusedImage(imageIndex, false);
this.scrollToRight();
this.nextImageIndex = imageIndex;
if (this.previousFocusedImage && newHistory.length) {
const matchIndex = this.matchIndexOfPreviousImage(
this.previousFocusedImage,
newHistory
);
if (matchIndex > -1) {
this.setFocusedImage(matchIndex);
} else {
this.paused();
}
}
if (!this.isPaused) {
this.setFocusedImage(imageIndex);
this.scrollToRight();
}
},
deep: true
},
@@ -432,6 +501,12 @@ export default {
}
},
async mounted() {
eventHelpers.extend(this);
this.focusedImageWrapper = this.$refs.focusedImageWrapper;
this.focusedImageElement = this.$refs.focusedImageElement;
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp);
//We only need to use this till the user focuses an image manually
if (this.focusedImageTimestamp !== undefined) {
this.isPaused = true;
@@ -472,6 +547,9 @@ export default {
this.thumbWrapperResizeObserver.observe(this.$refs.thumbsWrapper);
}
this.clearWheelZoom = _.debounce(this.clearWheelZoom, 600);
this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
},
beforeDestroy() {
this.stopFollowingTimeContext();
@@ -496,6 +574,10 @@ export default {
}
}
}
this.stopListening(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('keyup', this.handleKeyUp);
},
methods: {
setTimeContext() {
@@ -512,6 +594,11 @@ export default {
}
},
expand() {
// check for modifier keys so it doesnt interfere with the layout
if (this.altPressed || this.metaPressed) {
return;
}
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
const visibleActions = actionCollection.getVisibleActions();
const viewLargeAction = visibleActions
@@ -617,6 +704,29 @@ export default {
focusElement() {
this.$el.focus();
},
handleKeyDown(event) {
if (event.key === 'Alt') {
this.altPressed = true;
}
if (event.metaKey) {
this.metaPressed = true;
}
if (event.shiftKey) {
this.shiftPressed = true;
}
},
handleKeyUp(event) {
if (event.key === 'Alt') {
this.altPressed = false;
}
this.shiftPressed = false;
if (!event.metaKey) {
this.metaPressed = false;
}
},
handleScroll() {
const thumbsWrapper = this.$refs.thumbsWrapper;
if (!thumbsWrapper || this.resizingWindow) {
@@ -627,20 +737,15 @@ export default {
const disableScroll = scrollWidth > Math.ceil(scrollLeft + clientWidth);
this.autoScroll = !disableScroll;
},
paused(state, type) {
this.isPaused = state;
paused(state) {
this.isPaused = Boolean(state);
if (type === 'button') {
this.setFocusedImage(this.imageHistory.length - 1);
}
if (this.nextImageIndex) {
if (!state) {
this.previousFocusedImage = null;
this.setFocusedImage(this.nextImageIndex);
delete this.nextImageIndex;
this.autoScroll = true;
this.scrollToRight();
}
this.autoScroll = true;
this.scrollToRight();
},
scrollToFocused() {
const thumbsWrapper = this.$refs.thumbsWrapper;
@@ -679,51 +784,24 @@ export default {
&& x.time === previous.time
));
},
setFocusedImage(index, thumbnailClick = false) {
let focusedIndex = index;
thumbnailClicked(index) {
this.setFocusedImage(index);
this.paused(true);
this.setPreviousFocusedImage(index);
},
setPreviousFocusedImage(index) {
this.focusedImageTimestamp = undefined;
this.previousFocusedImage = this.imageHistory[index]
? JSON.parse(JSON.stringify(this.imageHistory[index]))
: undefined;
},
setFocusedImage(index) {
if (!(Number.isInteger(index) && index > -1)) {
return;
}
if (thumbnailClick) {
//We use the props till the user changes what they want to see
this.focusedImageTimestamp = undefined;
//set the previousFocusedImage when a user chooses an image
this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
}
if (this.previousFocusedImage) {
// determine if the previous image exists in the new bounds of imageHistory
if (!thumbnailClick) {
const matchIndex = this.matchIndexOfPreviousImage(
this.previousFocusedImage,
this.imageHistory
);
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
}
if (!(this.isPaused || thumbnailClick)
|| focusedIndex === this.imageHistory.length - 1) {
delete this.previousFocusedImage;
}
}
this.focusedImageIndex = focusedIndex;
//TODO: do we even need this anymore?
if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
this.nextImageIndex = focusedIndex;
//this could happen if bounds changes
if (this.focusedImageIndex > this.imageHistory.length - 1) {
this.focusedImageIndex = focusedIndex;
}
return;
}
if (thumbnailClick && !this.isPaused) {
this.paused(true);
}
this.focusedImageIndex = index;
},
trackDuration() {
if (this.canTrackDuration) {
@@ -761,7 +839,7 @@ export default {
let index = this.focusedImageIndex;
this.setFocusedImage(++index, THUMBNAIL_CLICKED);
this.thumbnailClicked(++index);
if (index === this.imageHistory.length - 1) {
this.paused(false);
}
@@ -774,14 +852,69 @@ export default {
let index = this.focusedImageIndex;
if (index === this.imageHistory.length - 1) {
this.setFocusedImage(this.imageHistory.length - 2, THUMBNAIL_CLICKED);
this.thumbnailClicked(this.imageHistory.length - 2);
} else {
this.setFocusedImage(--index, THUMBNAIL_CLICKED);
this.thumbnailClicked(--index);
}
},
startDrag(e) {
e.preventDefault();
e.stopPropagation();
resetImage(overrideLock = false) {
if (!overrideLock) {
return false;
}
this.imagePanned = false;
this.imageTranslateX = 0;
this.imageTranslateY = 0;
},
handlePanZoomUpdate(options) {
const { newScaleFactor, screenClientX, screenClientY } = options;
if (!this.isPaused) {
this.paused(true);
}
if (!(screenClientX || screenClientY)) {
return this.updatePanZoom(newScaleFactor, 0, 0);
}
// handle mouse events
const imageRect = this.focusedImageWrapper.getBoundingClientRect();
const imageContainerX = screenClientX - imageRect.left;
const imageContainerY = screenClientY - imageRect.top;
const offsetFromCenterX = (imageRect.width / 2) - imageContainerX;
const offsetFromCenterY = (imageRect.height / 2) - imageContainerY;
this.updatePanZoom(newScaleFactor, offsetFromCenterX, offsetFromCenterY);
},
updatePanZoom(newScaleFactor, offsetFromCenterX, offsetFromCenterY) {
const currentScale = this.zoomFactor;
const previousTranslateX = this.imageTranslateX;
const previousTranslateY = this.imageTranslateY;
const offsetXInOriginalScale = offsetFromCenterX / currentScale;
const offsetYInOriginalScale = offsetFromCenterY / currentScale;
const translateX = offsetXInOriginalScale + previousTranslateX;
const translateY = offsetYInOriginalScale + previousTranslateY;
this.imageTranslateX = translateX;
this.imageTranslateY = translateY;
this.zoomFactor = newScaleFactor;
},
handlePanZoomClick(e) {
const step = 1;
if (this.altPressed) {
return this.startPan(e);
}
if (!(this.metaPressed && e.button === 0)) {
return;
}
const newZoomFactor = this.zoomFactor + (this.shiftPressed ? -step : step);
this.handlePanZoomUpdate({
newScaleFactor: newZoomFactor,
screenClientX: e.clientX,
screenClientY: e.clientY
});
},
arrowDownHandler(event) {
let key = event.keyCode;
@@ -844,7 +977,7 @@ export default {
// TODO - should probably cache this
img.addEventListener('load', () => {
this.focusedImageNaturalAspectRatio = img.naturalWidth / img.naturalHeight;
this.setSizedImageDimensions();
}, { once: true });
},
resizeImageContainer() {
@@ -859,6 +992,21 @@ export default {
if (this.$refs.imageBG.clientHeight !== this.imageContainerHeight) {
this.imageContainerHeight = this.$refs.imageBG.clientHeight;
}
this.setSizedImageDimensions();
},
setSizedImageDimensions() {
this.focusedImageNaturalAspectRatio = this.$refs.focusedImage.naturalWidth / this.$refs.focusedImage.naturalHeight;
if ((this.imageContainerWidth / this.imageContainerHeight) > this.focusedImageNaturalAspectRatio) {
// container is wider than image
this.sizedImageWidth = this.imageContainerHeight * this.focusedImageNaturalAspectRatio;
this.sizedImageHeight = this.imageContainerHeight;
} else {
// container is taller than image
this.sizedImageWidth = this.imageContainerWidth;
this.sizedImageHeight = this.imageContainerWidth / this.focusedImageNaturalAspectRatio;
}
},
handleThumbWindowResizeStart() {
if (!this.autoScroll) {
@@ -877,6 +1025,88 @@ export default {
this.$nextTick(() => {
this.resizingWindow = false;
});
},
// used to increment the zoom without knowledge of current level
incrementZoomFactor(increment, userCoordX, userCoordY) {
const newFactor = this.zoomFactor + increment;
this.zoomImage(newFactor, userCoordX, userCoordY);
},
// debounced method
clearWheelZoom() {
this.wheelZooming = false;
},
wheelZoom(e) {
e.preventDefault();
if (!this.isPaused) {
this.paused(true);
}
// only use x,y coordinates on scrolling in
if (this.wheelZooming === false && e.deltaY > 0) {
this.wheelZooming = true;
// grab first x,y coordinates
this.incrementZoomFactor(e.deltaY * 0.01, e.clientX, e.clientY);
} else {
// ignore subsequent event x,y so scroll drift doesn't occur
this.incrementZoomFactor(e.deltaY * 0.01);
}
// debounced method that will only fire after the scroll series is complete
this.clearWheelZoom();
},
startPan(e) {
e.preventDefault();
if (!this.pan && this.zoomFactor > 1) {
this.animateZoom = false;
this.imagePanned = true;
this.pan = {
x: e.clientX,
y: e.clientY
};
this.listenTo(window, 'mouseup', this.onMouseUp, this);
this.listenTo(window, 'mousemove', this.trackMousePosition, this);
}
return false;
},
trackMousePosition(e) {
if (!e.altKey) {
return this.onMouseUp(e);
}
this.updatePan(e);
e.preventDefault();
},
updatePan(e) {
if (!this.pan) {
return;
}
const dX = e.clientX - this.pan.x;
const dY = e.clientY - this.pan.y;
this.pan = {
x: e.clientX,
y: e.clientY
};
this.updatePanZoom(this.zoomFactor, dX, dY);
},
endPan() {
this.pan = undefined;
this.animateZoom = true;
},
onMouseUp(event) {
this.stopListening(window, 'mouseup', this.onMouseUp, this);
this.stopListening(window, 'mousemove', this.trackMousePosition, this);
if (this.pan) {
return this.endPan(event);
}
},
setFilters(filtersObj) {
this.filters = filtersObj;
}
}
};

View File

@@ -18,6 +18,11 @@
flex: 1 1 auto;
}
.image-wrapper {
overflow: visible clip;
background-image: repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(125, 125, 125, 0.2) 4px, rgba(125, 125, 125, 0.2) 8px);
}
&__main-image {
&__bg {
background-color: $colorPlotBg;
@@ -27,18 +32,46 @@
justify-content: center;
flex: 1 1 auto;
height: 0;
overflow: hidden;
&.unnsynced{
@include sUnsynced();
}
}
&.cursor-zoom-in {
cursor: zoom-in;
}
&.cursor-zoom-out {
cursor: zoom-out;
}
&.pannable {
@include cursorGrab();
}
}
&__background-image {
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
&__image {
height: 100%;
width: 100%;
visibility: hidden;
display: contents;
}
}
&__hints {
$m: $interiorMargin;
background: rgba(black, 0.2);
border-radius: $smallCr;
padding: 2px $interiorMargin;
position: absolute;
right: $m;
top: $m;
opacity: 0.9;
z-index: 2;
}
&__control-bar,
&__time {
display: flex;
@@ -177,8 +210,8 @@
z-index: 2;
background: $colorLocalControlOvrBg;
border-radius: $basicCr;
max-width: 200px;
min-width: 70px;
max-width: 250px;
min-width: 170px;
width: 35%;
align-items: center;
padding: $interiorMargin $interiorMarginLg;
@@ -202,6 +235,7 @@
&__lc {
&__reset-btn {
// Span that holds bracket graphics and button
$bc: $scrollbarTrackColorBg;
&:before,
@@ -222,18 +256,50 @@
border-bottom: 1px solid $bc;
margin-top: 2px;
}
.c-icon-link {
color: $colorBtnFg;
}
}
}
}
.c-image-controls {
// Brightness/contrast
&__controls {
// Sliders and reset element
display: flex;
align-items: stretch;
flex-direction: column;
> * + * {
margin-top: $interiorMargin;
}
[class*='c-button'] { flex: 0 0 auto; }
}
&__control,
&__input {
display: flex;
align-items: center;
margin-right: $interiorMargin; // Need some extra space due to proximity to close button
width: 100%;
&:before {
color: rgba($colorMenuFg, 0.5);
margin-right: $interiorMarginSm;
}
}
&__input {
// A wrapper is needed to add the type icon to left of each control
input[type='range'] {
//width: 100%; // Do we need this?
}
}
&__zoom {
> * + * { margin-left: $interiorMargin; }
}
&__sliders {
@@ -246,21 +312,6 @@
}
}
&__slider-wrapper {
// A wrapper is needed to add the type icon to left of each range input
display: flex;
align-items: center;
&:before {
color: rgba($colorMenuFg, 0.5);
margin-right: $interiorMarginSm;
}
input[type='range'] {
width: 100px;
}
}
&__btn-reset {
flex: 0 0 auto;
}

View File

@@ -0,0 +1,98 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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 () {
const helperFunctions = {
listenTo: function (object, event, callback, context) {
if (!this._listeningTo) {
this._listeningTo = [];
}
const listener = {
object: object,
event: event,
callback: callback,
context: context,
_cb: context ? callback.bind(context) : callback
};
if (object.$watch && event.indexOf('change:') === 0) {
const scopePath = event.replace('change:', '');
listener.unlisten = object.$watch(scopePath, listener._cb, true);
} else if (object.$on) {
listener.unlisten = object.$on(event, listener._cb);
} else if (object.addEventListener) {
object.addEventListener(event, listener._cb);
} else {
object.on(event, listener._cb);
}
this._listeningTo.push(listener);
},
stopListening: function (object, event, callback, context) {
if (!this._listeningTo) {
this._listeningTo = [];
}
this._listeningTo.filter(function (listener) {
if (object && object !== listener.object) {
return false;
}
if (event && event !== listener.event) {
return false;
}
if (callback && callback !== listener.callback) {
return false;
}
if (context && context !== listener.context) {
return false;
}
return true;
})
.map(function (listener) {
if (listener.unlisten) {
listener.unlisten();
} else if (listener.object.removeEventListener) {
listener.object.removeEventListener(listener.event, listener._cb);
} else {
listener.object.off(listener.event, listener._cb);
}
return listener;
})
.forEach(function (listener) {
this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
}, this);
},
extend: function (object) {
object.listenTo = helperFunctions.listenTo;
object.stopListening = helperFunctions.stopListening;
}
};
return helperFunctions;
});

View File

@@ -483,6 +483,42 @@ describe("The Imagery View Layouts", () => {
});
});
});
xit('should change the image zoom factor when using the zoom buttons', async (done) => {
await Vue.nextTick();
let imageSizeBefore;
let imageSizeAfter;
// test clicking the zoom in button
imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
parent.querySelector('.t-btn-zoom-in').click();
await Vue.nextTick();
imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
expect(imageSizeAfter.height).toBeGreaterThan(imageSizeBefore.height);
expect(imageSizeAfter.width).toBeGreaterThan(imageSizeBefore.width);
// test clicking the zoom out button
imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
parent.querySelector('.t-btn-zoom-out').click();
await Vue.nextTick();
imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height);
expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
done();
});
xit('should reset the zoom factor on the image when clicking the zoom button', async (done) => {
await Vue.nextTick();
// test clicking the zoom reset button
// zoom in to scale up the image dimensions
parent.querySelector('.t-btn-zoom-in').click();
await Vue.nextTick();
let imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
await Vue.nextTick();
parent.querySelector('.t-btn-zoom-reset').click();
let imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height);
expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
done();
});
it('clear data action is installed', () => {
expect(clearDataAction).toBeDefined();
});