Compare commits
3 Commits
v1.7.3
...
sprint-1.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab93ca9024 | ||
|
|
80451935a9 | ||
|
|
783e0fd4f8 |
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,43 +0,0 @@
|
||||
<!--- This is for filing bugs. If you have a general question, please -->
|
||||
<!--- visit https://github.com/nasa/openmct/discussions -->
|
||||
|
||||
---
|
||||
name: Bug Report
|
||||
about: File a Bug !
|
||||
|
||||
---
|
||||
<!--- Focus on user impact in the title. Use the Summary Field to -->
|
||||
<!--- describe the problem technically. -->
|
||||
|
||||
#### Summary
|
||||
<!--- A description of the issue encountered. When possible, a description -->
|
||||
<!--- of the impact of the issue. What use case does it impede?-->
|
||||
|
||||
#### Expected vs Current Behavior
|
||||
<!--- Tell us what should have happened -->
|
||||
|
||||
#### Impact Check List
|
||||
<!--- Please select from the following options -->
|
||||
|
||||
- [ ] Data loss or misrepresented data?
|
||||
- [ ] Regression? Did this used to work or has it always been broken?
|
||||
- [ ] Is there a workaround available?
|
||||
- [ ] Does this impact a critical component?
|
||||
- [ ] Is this just a visual bug?
|
||||
|
||||
#### Steps to Reproduce
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
#### Environment
|
||||
* Open MCT Version: <!--- date of build, version, or SHA -->
|
||||
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yams? -->
|
||||
* OS:
|
||||
* Browser:
|
||||
|
||||
#### Additional Information
|
||||
<!--- Include any screenshots, gifs, or logs which will expedite triage -->
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,23 +0,0 @@
|
||||
<!--- This is for filing enhancements or features. If you have a general -->
|
||||
<!--- question, please visit https://github.com/nasa/openmct/discussions -->
|
||||
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for suggesting an idea to make Open MCT better.
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- Please describe the problem you are trying to solve. -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!--- Please describe the desired behavior. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!--- Please describe alternative solutions or features you have considered. -->
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,12 +0,0 @@
|
||||
### All Submissions:
|
||||
|
||||
* [ ] Have you followed the guidelines in our [Contributing document](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md)?
|
||||
* [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/nasa/openmct/pulls) for the same update/change?
|
||||
|
||||
### Author Checklist
|
||||
|
||||
* [ ] Changes address original issue?
|
||||
* [ ] Unit tests included and/or updated with changes?
|
||||
* [ ] Command line build passes?
|
||||
* [ ] Has this been smoke tested?
|
||||
* [ ] Testing instructions included in associated issue?
|
||||
@@ -10,7 +10,7 @@ accept changes from external contributors.
|
||||
|
||||
The short version:
|
||||
|
||||
1. Write your contribution or describe your idea in the form of an [GitHub issue](https://github.com/nasa/openmct/issues/new/choose) or [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions)
|
||||
1. Write your contribution.
|
||||
2. Make sure your contribution meets code, test, and commit message
|
||||
standards as described below.
|
||||
3. Submit a pull request from a topic branch back to `master`. Include a check
|
||||
@@ -18,7 +18,6 @@ The short version:
|
||||
for review.)
|
||||
4. Respond to any discussion. When the reviewer decides it's ready, they
|
||||
will merge back `master` and fill out their own check list.
|
||||
5. If you are a first-time contributor, please see [this discussion](https://github.com/nasa/openmct/discussions/3821) for further information.
|
||||
|
||||
## Contribution Process
|
||||
|
||||
@@ -116,7 +115,7 @@ the pull request containing the reviewer checklist (from below) and complete
|
||||
the merge back to the master branch.
|
||||
|
||||
Additionally:
|
||||
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, [create one](https://github.com/nasa/openmct/issues/new/choose).
|
||||
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, create one.
|
||||
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
|
||||
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull request’s __author__.
|
||||
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
|
||||
@@ -297,12 +296,23 @@ these standards.
|
||||
|
||||
Issues are tracked at https://github.com/nasa/openmct/issues.
|
||||
|
||||
Issues should include:
|
||||
|
||||
* A short description of the issue encountered.
|
||||
* A longer-form description of the issue encountered. When possible, steps to
|
||||
reproduce the issue.
|
||||
* When possible, a description of the impact of the issue. What use case does
|
||||
it impede?
|
||||
* An assessment of the severity of the issue.
|
||||
|
||||
Issue severity is categorized as follows (in ascending order):
|
||||
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
|
||||
* _Medium_: Some impairment of use, but simple workarounds exist
|
||||
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
|
||||
* _Blocker_: Major functionality is impaired or lost, threatening mission success. Display of telemetry data is impaired or blocked by the bug, which could lead to loss of situational awareness.
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the
|
||||
software; a "nice-to-have."
|
||||
* _(Unspecified)_: Major loss of functionality or impairment of use.
|
||||
* _Critical_: Large-scale loss of functionality or impairment of use,
|
||||
such that remaining utility becomes marginal.
|
||||
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
|
||||
|
||||
## Check Lists
|
||||
|
||||
@@ -312,19 +322,16 @@ checklist).
|
||||
|
||||
### Author Checklist
|
||||
|
||||
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
|
||||
1. Changes address original issue?
|
||||
2. Unit tests included and/or updated with changes?
|
||||
3. Command line build passes?
|
||||
4. Changes have been smoke-tested?
|
||||
5. Testing instructions included?
|
||||
|
||||
### Reviewer Checklist
|
||||
|
||||
* [ ] Changes appear to address issue?
|
||||
* [ ] Appropriate unit tests included?
|
||||
* [ ] Code style and in-line documentation are appropriate?
|
||||
* [ ] Commit messages meet standards?
|
||||
* [ ] Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||
* [ ] Has associated issue been labelled `bug`? (only applicable if this PR is for a bug fix)
|
||||
* [ ] List of Acceptance Tests Performed.
|
||||
|
||||
Write out a small list of tests performed with just enough detail for another developer on the team
|
||||
to execute.
|
||||
|
||||
i.e. ```When Clicking on Add button, new `object` appears in dropdown.```
|
||||
1. Changes appear to address issue?
|
||||
2. Appropriate unit tests included?
|
||||
3. Code style and in-line documentation are appropriate?
|
||||
4. Commit messages meet standards?
|
||||
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||
|
||||
@@ -44,7 +44,7 @@ The clearest examples for developing Open MCT plugins are in the
|
||||
our documentation.
|
||||
|
||||
We want Open MCT to be as easy to use, install, run, and develop for as
|
||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues/new/choose), [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
|
||||
## Building Applications With Open MCT
|
||||
|
||||
|
||||
@@ -73,11 +73,11 @@ acceptance testing (e.g. by resolving any blockers found); any
|
||||
resources not needed for this effort should be used to begin work
|
||||
for the subsequent sprint.
|
||||
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-------------------------------------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze and sprint branch |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship and merge sprint branch to master|
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship |
|
||||
|
||||
* If necessary.
|
||||
|
||||
@@ -105,20 +105,14 @@ emphasis on testing.
|
||||
that team may begin work for that sprint during the
|
||||
third week, since testing and blocker resolution is unlikely
|
||||
to require all available resources.
|
||||
* Testing success criteria identified per issue (where necessary). This could be in the form of acceptance tests on the issue or detailing performance tests, for example.
|
||||
* __Tag-up.__ Check in and status update among development team.
|
||||
May amend plan for sprint as-needed.
|
||||
* __Code freeze.__ Any new work from this sprint
|
||||
(features, bug fixes, enhancements) must be integrated by the
|
||||
end of the second week of the sprint. After code freeze, a sprint
|
||||
branch will be created (and until the end of the sprint) the only
|
||||
changes that should be merged into the sprint branch should
|
||||
directly address issues needed to pass acceptance testing.
|
||||
During this time, any other feature development will continue to
|
||||
be merged into the master branch for the next sprint.
|
||||
* __Sprint branch merge to master.__ After acceptance testing, the sprint branch
|
||||
will be merged back to the master branch. Any code conflicts that
|
||||
arise will be resolved by the team.
|
||||
end of the second week of the sprint. After code freeze
|
||||
(and until the end of the sprint) the only changes that should be
|
||||
merged into the master branch should directly address issues
|
||||
needed to pass acceptance testing.
|
||||
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
|
||||
Structured testing with predefined
|
||||
success criteria. No release should ship without passing
|
||||
@@ -132,8 +126,8 @@ emphasis on testing.
|
||||
* [__Testathon.__](testing/plan.md#user-testing)
|
||||
Multi-user testing, involving as many users as
|
||||
is feasible, plus development team. Open-ended; should verify
|
||||
completed work from this sprint using the sprint branch, test
|
||||
exploratorily for regressions, et cetera.
|
||||
completed work from this sprint, test exploratorily for
|
||||
regressions, et cetera.
|
||||
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
|
||||
test to verify that the software remains
|
||||
stable after running for longer durations. May include some
|
||||
@@ -149,7 +143,7 @@ emphasis on testing.
|
||||
Subset of Pre-release Testing
|
||||
which should be performed before shipping at the end of any
|
||||
sprint. Time is allocated for a second round of
|
||||
Pre-release Testing if the first round is not passed. Smoke tests collected from issues/PRs
|
||||
Pre-release Testing if the first round is not passed.
|
||||
* __Triage.__ Team reviews issues from acceptance testing and uses
|
||||
success criteria to determine whether or not they should block
|
||||
release, then formulates a plan to address these issues before
|
||||
|
||||
@@ -19,7 +19,7 @@ Testing for Open MCT includes:
|
||||
|
||||
Manual, non-rigorous testing of the software and/or specific features
|
||||
of interest. Verifies that the software runs and that basic functionality
|
||||
is present. The outcome of Smoke Testing should be a simplified list of Acceptance Tests which could be executed by another team member with sufficient context.
|
||||
is present.
|
||||
|
||||
### Unit Testing
|
||||
|
||||
@@ -49,7 +49,7 @@ User testing will focus on the following activities:
|
||||
* General "trying to break things."
|
||||
|
||||
During user testing, users will
|
||||
[report issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
[report issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
as they are encountered.
|
||||
|
||||
Desired outcomes of user testing are:
|
||||
@@ -71,7 +71,7 @@ usage. After twenty-four hours, the software is evaluated for:
|
||||
at the start of the test? Is it as responsive?
|
||||
|
||||
Any defects or unexpected behavior identified during testing should be
|
||||
[reported as issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
[reported as issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
and reviewed for severity.
|
||||
|
||||
## Test Performance
|
||||
|
||||
@@ -92,8 +92,8 @@ should update (or delegate the task of updating) Open MCT version
|
||||
numbers by the following process:
|
||||
|
||||
1. Update version number in `package.json`
|
||||
1. Checkout branch created for the last sprint that has been successfully tested.
|
||||
2. Remove a `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
1. Create a new branch off the `master` branch.
|
||||
2. Remove `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
3. Verify that resulting version number meets semantic versioning
|
||||
requirements relative to previous stable version. Increment the
|
||||
version number if necessary.
|
||||
|
||||
@@ -93,36 +93,5 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
||||
return {
|
||||
limits: function () {
|
||||
return {
|
||||
WARNING: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--yellow",
|
||||
sin: -YELLOW.sin,
|
||||
cos: -YELLOW.cos
|
||||
},
|
||||
high: {
|
||||
cssClass: "is-limit--upr is-limit--yellow",
|
||||
...YELLOW
|
||||
}
|
||||
},
|
||||
DISTRESS: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--red",
|
||||
sin: -RED.sin,
|
||||
cos: -RED.cos
|
||||
},
|
||||
high: {
|
||||
cssClass: "is-limit--upr is-limit--red",
|
||||
...RED
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return SinewaveLimitProvider;
|
||||
});
|
||||
|
||||
@@ -78,7 +78,6 @@ module.exports = (config) => {
|
||||
preserveDescribeNesting: true,
|
||||
foldAll: false
|
||||
},
|
||||
browserConsoleLogOptions: { level: "error", format: "%b %T: %m", terminal: true },
|
||||
coverageIstanbulReporter: {
|
||||
fixWebpackSourcePaths: true,
|
||||
dir: process.env.CIRCLE_ARTIFACTS ?
|
||||
|
||||
@@ -78,8 +78,7 @@
|
||||
"zepto": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf ./dist /node_modules; rm package-lock.json",
|
||||
"clean-test-lint": "npm run clean; npm install ; npm run test; npm run lint",
|
||||
"clean": "rm -rf ./dist",
|
||||
"start": "node app.js",
|
||||
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||
|
||||
@@ -86,7 +86,7 @@ define(
|
||||
})
|
||||
.join('/');
|
||||
|
||||
openmct.router.navigate(url);
|
||||
window.location.href = url;
|
||||
|
||||
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
|
||||
openmct.editor.edit();
|
||||
|
||||
@@ -23,11 +23,13 @@
|
||||
define([
|
||||
"moment-timezone",
|
||||
"./src/indicators/ClockIndicator",
|
||||
"./src/indicators/FollowIndicator",
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/ClockController",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/FollowTimerAction",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
@@ -37,11 +39,13 @@ define([
|
||||
], function (
|
||||
MomentTimezone,
|
||||
ClockIndicator,
|
||||
FollowIndicator,
|
||||
TickerService,
|
||||
TimerService,
|
||||
ClockController,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
FollowTimerAction,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
@@ -140,6 +144,15 @@ define([
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.follow",
|
||||
"implementation": FollowTimerAction,
|
||||
"depends": ["timerService"],
|
||||
"category": "contextual",
|
||||
"name": "Follow Timer",
|
||||
"cssClass": "icon-clock",
|
||||
"priority": "optional"
|
||||
},
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
@@ -286,7 +299,10 @@ define([
|
||||
}
|
||||
}
|
||||
],
|
||||
"runs": [],
|
||||
"runs": [{
|
||||
"implementation": FollowIndicator,
|
||||
"depends": ["openmct", "timerService"]
|
||||
}],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "moment-duration-format",
|
||||
|
||||
56
platform/features/clock/src/actions/FollowTimerAction.js
Normal file
56
platform/features/clock/src/actions/FollowTimerAction.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, 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 () {
|
||||
|
||||
/**
|
||||
* Designates a specific timer for following. Timelines, for example,
|
||||
* use the actively followed timer to display a time-of-interest line
|
||||
* and interpret time conductor bounds in the Timeline's relative
|
||||
* time frame.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function FollowTimerAction(timerService, context) {
|
||||
var domainObject =
|
||||
context.domainObject
|
||||
&& context.domainObject.useCapability('adapter');
|
||||
this.perform =
|
||||
timerService.setTimer.bind(timerService, domainObject);
|
||||
}
|
||||
|
||||
FollowTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
return model.type === 'timer';
|
||||
};
|
||||
|
||||
return FollowTimerAction;
|
||||
}
|
||||
);
|
||||
51
platform/features/clock/src/indicators/FollowIndicator.js
Normal file
51
platform/features/clock/src/indicators/FollowIndicator.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-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 () {
|
||||
|
||||
/**
|
||||
* Indicator that displays the active timer, as well as its
|
||||
* current state.
|
||||
* @memberof platform/features/clock
|
||||
*/
|
||||
return function installFollowIndicator(openmct, timerService) {
|
||||
var indicator = openmct.indicators.simpleIndicator();
|
||||
var timer = timerService.getTimer();
|
||||
setIndicatorStatus(timer);
|
||||
|
||||
function setIndicatorStatus(newTimer) {
|
||||
if (newTimer !== undefined) {
|
||||
indicator.iconClass('icon-timer');
|
||||
indicator.statusClass('s-status-on');
|
||||
indicator.text('Following timer ' + newTimer.name);
|
||||
} else {
|
||||
indicator.iconClass('icon-timer');
|
||||
indicator.statusClass('s-status-disabled');
|
||||
indicator.text('No timer being followed');
|
||||
}
|
||||
}
|
||||
|
||||
timerService.on('change', setIndicatorStatus);
|
||||
|
||||
openmct.indicators.add(indicator);
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, 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/actions/FollowTimerAction"
|
||||
], function (FollowTimerAction) {
|
||||
var TIMER_SERVICE_METHODS =
|
||||
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
|
||||
|
||||
describe("The Follow Timer action", function () {
|
||||
var testContext;
|
||||
var testModel;
|
||||
var testAdaptedObject;
|
||||
|
||||
beforeEach(function () {
|
||||
testModel = {};
|
||||
testContext = {
|
||||
domainObject: jasmine.createSpyObj('domainObject', [
|
||||
'getModel',
|
||||
'useCapability'
|
||||
])
|
||||
};
|
||||
testAdaptedObject = { foo: 'bar' };
|
||||
testContext.domainObject.getModel.and.returnValue(testModel);
|
||||
testContext.domainObject.useCapability.and.callFake(function (c) {
|
||||
return c === 'adapter' && testAdaptedObject;
|
||||
});
|
||||
});
|
||||
|
||||
it("is applicable to timers", function () {
|
||||
testModel.type = "timer";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(true);
|
||||
});
|
||||
|
||||
it("is inapplicable to non-timers", function () {
|
||||
testModel.type = "folder";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(false);
|
||||
});
|
||||
|
||||
describe("when instantiated", function () {
|
||||
var mockTimerService;
|
||||
var action;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimerService = jasmine.createSpyObj(
|
||||
'timerService',
|
||||
TIMER_SERVICE_METHODS
|
||||
);
|
||||
action = new FollowTimerAction(mockTimerService, testContext);
|
||||
});
|
||||
|
||||
it("does not interact with the timer service", function () {
|
||||
TIMER_SERVICE_METHODS.forEach(function (method) {
|
||||
expect(mockTimerService[method]).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and performed", function () {
|
||||
beforeEach(function () {
|
||||
action.perform();
|
||||
});
|
||||
|
||||
it("sets the active timer", function () {
|
||||
expect(mockTimerService.setTimer)
|
||||
.toHaveBeenCalledWith(testAdaptedObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-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/indicators/FollowIndicator",
|
||||
"../../src/services/TimerService",
|
||||
"../../../../../src/MCT",
|
||||
'zepto'
|
||||
], function (
|
||||
FollowIndicator,
|
||||
TimerService,
|
||||
MCT,
|
||||
$
|
||||
) {
|
||||
xdescribe("The timer-following indicator", function () {
|
||||
var timerService;
|
||||
var openmct;
|
||||
|
||||
beforeEach(function () {
|
||||
openmct = new MCT();
|
||||
timerService = new TimerService(openmct);
|
||||
spyOn(openmct.indicators, "add");
|
||||
});
|
||||
|
||||
it("adds an indicator when installed", function () {
|
||||
FollowIndicator(openmct, timerService);
|
||||
expect(openmct.indicators.add).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("indicates that no timer is being followed", function () {
|
||||
FollowIndicator(openmct, timerService);
|
||||
var simpleIndicator = openmct.indicators.add.calls.mostRecent().args[0];
|
||||
var element = simpleIndicator.element;
|
||||
var text = $('.indicator-text', element).text().trim();
|
||||
expect(text).toEqual('No timer being followed');
|
||||
});
|
||||
|
||||
describe("when a timer is set", function () {
|
||||
var testObject;
|
||||
var simpleIndicator;
|
||||
|
||||
beforeEach(function () {
|
||||
testObject = {
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'key'
|
||||
},
|
||||
name: "some timer!"
|
||||
};
|
||||
timerService.setTimer(testObject);
|
||||
FollowIndicator(openmct, timerService);
|
||||
simpleIndicator = openmct.indicators.add.calls.mostRecent().args[0];
|
||||
});
|
||||
|
||||
it("displays the timer's name", function () {
|
||||
var element = simpleIndicator.element;
|
||||
var text = $('.indicator-text', element).text().trim();
|
||||
expect(text).toEqual('Following timer ' + testObject.name);
|
||||
});
|
||||
|
||||
it("displays the timer's name when it changes", function () {
|
||||
var secondTimer = {
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'key2'
|
||||
},
|
||||
name: "Some other timer"
|
||||
};
|
||||
var element = simpleIndicator.element;
|
||||
timerService.setTimer(secondTimer);
|
||||
var text = $('.indicator-text', element).text().trim();
|
||||
expect(text).toEqual('Following timer ' + secondTimer.name);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -252,7 +252,7 @@ define([
|
||||
|
||||
this.status = new api.StatusAPI(this);
|
||||
|
||||
this.router = new ApplicationRouter(this);
|
||||
this.router = new ApplicationRouter();
|
||||
|
||||
this.branding = BrandingAPI.default;
|
||||
|
||||
|
||||
@@ -161,22 +161,6 @@ define([
|
||||
evaluate: function (datum, property) {
|
||||
return limitEvaluator.evaluate(datum, property && property.key);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
LegacyTelemetryProvider.prototype.getLimits = function (domainObject) {
|
||||
const oldObject = this.instantiate(
|
||||
utils.toOldFormat(domainObject),
|
||||
utils.makeKeyString(domainObject.identifier)
|
||||
);
|
||||
const limitEvaluator = oldObject.getCapability("limit");
|
||||
|
||||
return {
|
||||
limits: function () {
|
||||
return limitEvaluator.limits();
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -119,8 +119,7 @@ describe('The ActionCollection', () => {
|
||||
|
||||
afterEach(() => {
|
||||
actionCollection.destroy();
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("disable method invoked with action keys", () => {
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('The Actions API', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("register method", () => {
|
||||
|
||||
@@ -215,12 +215,12 @@ define([
|
||||
* @memberof {module:openmct.CompositionCollection#}
|
||||
* @name load
|
||||
*/
|
||||
CompositionCollection.prototype.load = function (abortSignal) {
|
||||
CompositionCollection.prototype.load = function () {
|
||||
this.cleanUpMutables();
|
||||
|
||||
return this.provider.load(this.domainObject)
|
||||
.then(function (children) {
|
||||
return Promise.all(children.map((c) => this.publicAPI.objects.get(c, abortSignal)));
|
||||
return Promise.all(children.map((c) => this.publicAPI.objects.get(c)));
|
||||
}.bind(this))
|
||||
.then(function (childObjects) {
|
||||
childObjects.forEach(c => this.add(c, true));
|
||||
|
||||
@@ -76,7 +76,7 @@ describe ('The Menu API', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("showMenu method", () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("The Status API", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("set function", () => {
|
||||
|
||||
@@ -504,26 +504,6 @@ define([
|
||||
return this.getLimitEvaluator(domainObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a limits for this domain object.
|
||||
* Limits help you display limits and alarms of
|
||||
* telemetry for display purposes without having to interact directly
|
||||
* with the Limit API.
|
||||
*
|
||||
* This method is optional.
|
||||
* If a provider does not implement this method, it is presumed
|
||||
* that no limits are defined for this domain object's telemetry.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} domainObject the domain
|
||||
* object for which to get limits
|
||||
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
|
||||
* @method limits
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.limitDefinition = function (domainObject) {
|
||||
return this.getLimits(domainObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a limit evaluator for this domain object.
|
||||
* Limit Evaluators help you evaluate limit and alarm status of individual
|
||||
@@ -551,42 +531,5 @@ define([
|
||||
return provider.getLimitEvaluator(domainObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a limit definitions for this domain object.
|
||||
* Limit Definitions help you indicate limits and alarms of
|
||||
* telemetry for display purposes without having to interact directly
|
||||
* with the Limit API.
|
||||
*
|
||||
* This method is optional.
|
||||
* If a provider does not implement this method, it is presumed
|
||||
* that no limits are defined for this domain object's telemetry.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} domainObject the domain
|
||||
* object for which to display limits
|
||||
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
|
||||
* @method limits returns a limits object of
|
||||
* type {
|
||||
* level1: {
|
||||
* low: { key1: value1, key2: value2 },
|
||||
* high: { key1: value1, key2: value2 }
|
||||
* },
|
||||
* level2: {
|
||||
* low: { key1: value1, key2: value2 },
|
||||
* high: { key1: value1, key2: value2 }
|
||||
* }
|
||||
* }
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.getLimits = function (domainObject) {
|
||||
const provider = this.findLimitEvaluator(domainObject);
|
||||
if (!provider || !provider.getLimits) {
|
||||
return {
|
||||
limits: function () {}
|
||||
};
|
||||
}
|
||||
|
||||
return provider.getLimits(domainObject);
|
||||
};
|
||||
|
||||
return TelemetryAPI;
|
||||
});
|
||||
|
||||
@@ -43,15 +43,15 @@ export default function LADTableSetViewProvider(openmct) {
|
||||
components: {
|
||||
LadTableSet: LadTableSet
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
objectPath
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject
|
||||
};
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
objectPath
|
||||
},
|
||||
template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
|
||||
});
|
||||
},
|
||||
|
||||
@@ -292,11 +292,6 @@ describe("The LAD Table Set", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import {
|
||||
getAllSearchParams,
|
||||
setAllSearchParams
|
||||
} from 'utils/openmctLocation';
|
||||
|
||||
const TIME_EVENTS = ['timeSystem', 'clock', 'clockOffsets'];
|
||||
const SEARCH_MODE = 'tc.mode';
|
||||
@@ -45,8 +49,9 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.openmct.router.on('change:params', this.updateTimeSettings);
|
||||
this.updateTimeSettings();
|
||||
|
||||
window.addEventListener('hashchange', this.updateTimeSettings);
|
||||
TIME_EVENTS.forEach(event => {
|
||||
this.openmct.time.on(event, this.setUrlFromTimeApi);
|
||||
});
|
||||
@@ -54,8 +59,7 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.openmct.router.off('change:params', this.updateTimeSettings);
|
||||
|
||||
window.removeEventListener('hashchange', this.updateTimeSettings);
|
||||
this.openmct.off('start', this.initialize);
|
||||
this.openmct.off('destroy', this.destroy);
|
||||
|
||||
@@ -66,18 +70,22 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
updateTimeSettings() {
|
||||
let timeParameters = this.parseParametersFromUrl();
|
||||
// Prevent from triggering self
|
||||
if (!this.isUrlUpdateInProgress) {
|
||||
let timeParameters = this.parseParametersFromUrl();
|
||||
|
||||
if (this.areTimeParametersValid(timeParameters)) {
|
||||
this.setTimeApiFromUrl(timeParameters);
|
||||
this.openmct.router.setLocationFromUrl();
|
||||
if (this.areTimeParametersValid(timeParameters)) {
|
||||
this.setTimeApiFromUrl(timeParameters);
|
||||
} else {
|
||||
this.setUrlFromTimeApi();
|
||||
}
|
||||
} else {
|
||||
this.setUrlFromTimeApi();
|
||||
this.isUrlUpdateInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
parseParametersFromUrl() {
|
||||
let searchParams = this.openmct.router.getAllSearchParams();
|
||||
let searchParams = getAllSearchParams();
|
||||
|
||||
let mode = searchParams.get(SEARCH_MODE);
|
||||
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
|
||||
@@ -140,7 +148,7 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
setUrlFromTimeApi() {
|
||||
let searchParams = this.openmct.router.getAllSearchParams();
|
||||
let searchParams = getAllSearchParams();
|
||||
let clock = this.openmct.time.clock();
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let clockOffsets = this.openmct.time.clockOffsets();
|
||||
@@ -168,7 +176,8 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
|
||||
this.openmct.router.setAllSearchParams(searchParams);
|
||||
this.isUrlUpdateInProgress = true;
|
||||
setAllSearchParams(searchParams);
|
||||
}
|
||||
|
||||
areTimeParametersValid(timeParameters) {
|
||||
|
||||
@@ -25,118 +25,306 @@ import {
|
||||
} from 'utils/testing';
|
||||
|
||||
describe("The URLTimeSettingsSynchronizer", () => {
|
||||
let appHolder;
|
||||
let openmct;
|
||||
let resolveFunction;
|
||||
let oldHash;
|
||||
let testClock;
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
openmct.install(openmct.plugins.LocalTimeSystem());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
testClock = jasmine.createSpyObj("testClock", ["start", "stop", "tick", "currentValue", "on", "off"]);
|
||||
testClock.key = "test-clock";
|
||||
testClock.currentValue.and.returnValue(0);
|
||||
|
||||
openmct.time.addClock(testClock);
|
||||
|
||||
openmct.on('start', done);
|
||||
|
||||
appHolder = document.createElement("div");
|
||||
openmct.start(appHolder);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.stopClock();
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
afterEach(() => resetApplicationState(openmct));
|
||||
|
||||
appHolder = undefined;
|
||||
openmct = undefined;
|
||||
resolveFunction = undefined;
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("initial clock is set to fixed is reflected in URL", (done) => {
|
||||
resolveFunction = () => {
|
||||
oldHash = window.location.hash;
|
||||
describe("realtime mode", () => {
|
||||
it("when the clock is set via the time API, it is immediately reflected in the URL", () => {
|
||||
//Test expected initial conditions
|
||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it("when the clock is set via the time API, it is reflected in the URL", (done) => {
|
||||
let success;
|
||||
|
||||
resolveFunction = () => {
|
||||
openmct.time.clock('local', {
|
||||
start: -1000,
|
||||
end: 100
|
||||
});
|
||||
|
||||
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
|
||||
|
||||
//Test that expected initial conditions are no longer true
|
||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
||||
});
|
||||
it("when offsets are set via the time API, they are immediately reflected in the URL", () => {
|
||||
//Test expected initial conditions
|
||||
expect(window.location.hash.includes('tc.startDelta')).toBe(false);
|
||||
expect(window.location.hash.includes('tc.endDelta')).toBe(false);
|
||||
|
||||
openmct.time.clock('local', {
|
||||
start: -1000,
|
||||
end: 100
|
||||
});
|
||||
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
|
||||
|
||||
openmct.time.clockOffsets({
|
||||
start: -2000,
|
||||
end: 200
|
||||
});
|
||||
expect(window.location.hash.includes('tc.startDelta=2000')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.endDelta=200')).toBe(true);
|
||||
|
||||
const hasStartDelta = window.location.hash.includes('tc.startDelta=2000');
|
||||
const hasEndDelta = window.location.hash.includes('tc.endDelta=200');
|
||||
const hasLocalClock = window.location.hash.includes('tc.mode=local');
|
||||
success = hasStartDelta && hasEndDelta && hasLocalClock;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
//Test that expected initial conditions are no longer true
|
||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
||||
});
|
||||
describe("when set in the url", () => {
|
||||
it("will change from fixed to realtime mode when the mode changes", () => {
|
||||
expectLocationToBeInFixedMode();
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
return switchToRealtimeMode().then(() => {
|
||||
let clock = openmct.time.clock();
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
expect(clock).toBeDefined();
|
||||
expect(clock.key).toBe('local');
|
||||
});
|
||||
});
|
||||
it("the clock is correctly set in the API from the URL parameters", () => {
|
||||
return switchToRealtimeMode().then(() => {
|
||||
let resolveFunction;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
|
||||
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||
//detected in the API.
|
||||
openmct.time.on('clock', resolveFunction);
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.mode=local', 'tc.mode=test-clock');
|
||||
window.location.hash = hash;
|
||||
}).then(() => {
|
||||
let clock = openmct.time.clock();
|
||||
expect(clock).toBeDefined();
|
||||
expect(clock.key).toBe('test-clock');
|
||||
openmct.time.off('clock', resolveFunction);
|
||||
});
|
||||
});
|
||||
});
|
||||
it("the clock offsets are correctly set in the API from the URL parameters", () => {
|
||||
return switchToRealtimeMode().then(() => {
|
||||
let resolveFunction;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||
//detected in the API.
|
||||
openmct.time.on('clockOffsets', resolveFunction);
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.startDelta=1000', 'tc.startDelta=2000');
|
||||
hash = hash.replace('tc.endDelta=100', 'tc.endDelta=200');
|
||||
window.location.hash = hash;
|
||||
}).then(() => {
|
||||
let clockOffsets = openmct.time.clockOffsets();
|
||||
expect(clockOffsets).toBeDefined();
|
||||
expect(clockOffsets.start).toBe(-2000);
|
||||
expect(clockOffsets.end).toBe(200);
|
||||
openmct.time.off('clockOffsets', resolveFunction);
|
||||
});
|
||||
});
|
||||
});
|
||||
it("the time system is correctly set in the API from the URL parameters", () => {
|
||||
return switchToRealtimeMode().then(() => {
|
||||
let resolveFunction;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
|
||||
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||
//detected in the API.
|
||||
openmct.time.on('timeSystem', resolveFunction);
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
|
||||
window.location.hash = hash;
|
||||
}).then(() => {
|
||||
let timeSystem = openmct.time.timeSystem();
|
||||
expect(timeSystem).toBeDefined();
|
||||
expect(timeSystem.key).toBe('local');
|
||||
openmct.time.off('timeSystem', resolveFunction);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("fixed timespan mode", () => {
|
||||
beforeEach(() => {
|
||||
openmct.time.stopClock();
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
});
|
||||
|
||||
it("when bounds are set via the time API, they are immediately reflected in the URL", () => {
|
||||
//Test expected initial conditions
|
||||
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
|
||||
|
||||
openmct.time.bounds({
|
||||
start: 10,
|
||||
end: 20
|
||||
});
|
||||
|
||||
expect(window.location.hash.includes('tc.startBound=10')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.endBound=20')).toBe(true);
|
||||
|
||||
//Test that expected initial conditions are no longer true
|
||||
expect(window.location.hash.includes('tc.startBound=0')).toBe(false);
|
||||
expect(window.location.hash.includes('tc.endBound=1')).toBe(false);
|
||||
});
|
||||
|
||||
it("when time system is set via the time API, it is immediately reflected in the URL", () => {
|
||||
//Test expected initial conditions
|
||||
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(true);
|
||||
|
||||
openmct.time.timeSystem('local', {
|
||||
start: 20,
|
||||
end: 30
|
||||
});
|
||||
|
||||
expect(window.location.hash.includes('tc.timeSystem=local')).toBe(true);
|
||||
|
||||
//Test that expected initial conditions are no longer true
|
||||
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(false);
|
||||
});
|
||||
describe("when set in the url", () => {
|
||||
it("time system changes are reflected in the API", () => {
|
||||
let resolveFunction;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let timeSystem = openmct.time.timeSystem();
|
||||
resolveFunction = resolve;
|
||||
|
||||
expect(timeSystem.key).toBe('utc');
|
||||
window.location.hash = window.location.hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
|
||||
|
||||
openmct.time.on('timeSystem', resolveFunction);
|
||||
}).then(() => {
|
||||
let timeSystem = openmct.time.timeSystem();
|
||||
expect(timeSystem.key).toBe('local');
|
||||
|
||||
openmct.time.off('timeSystem', resolveFunction);
|
||||
});
|
||||
});
|
||||
it("mode can be changed from realtime to fixed", () => {
|
||||
return switchToRealtimeMode().then(() => {
|
||||
expectLocationToBeInRealtimeMode();
|
||||
|
||||
expect(openmct.time.clock()).toBeDefined();
|
||||
}).then(switchToFixedMode).then(() => {
|
||||
let clock = openmct.time.clock();
|
||||
expect(clock).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
it("bounds are correctly set in the API from the URL parameters", () => {
|
||||
let resolveFunction;
|
||||
|
||||
expectLocationToBeInFixedMode();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
openmct.time.on('bounds', resolveFunction);
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.startBound=0', 'tc.startBound=222')
|
||||
.replace('tc.endBound=1', 'tc.endBound=333');
|
||||
window.location.hash = hash;
|
||||
}).then(() => {
|
||||
let bounds = openmct.time.bounds();
|
||||
|
||||
expect(bounds).toBeDefined();
|
||||
expect(bounds.start).toBe(222);
|
||||
expect(bounds.end).toBe(333);
|
||||
});
|
||||
});
|
||||
it("bounds are correctly set in the API from the URL parameters where only the end bound changes", () => {
|
||||
let resolveFunction;
|
||||
|
||||
expectLocationToBeInFixedMode();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
openmct.time.on('bounds', resolveFunction);
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.endBound=1', 'tc.endBound=333');
|
||||
window.location.hash = hash;
|
||||
}).then(() => {
|
||||
let bounds = openmct.time.bounds();
|
||||
|
||||
expect(bounds).toBeDefined();
|
||||
expect(bounds.start).toBe(0);
|
||||
expect(bounds.end).toBe(333);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
|
||||
let success;
|
||||
function setRealtimeLocationParameters() {
|
||||
let hash = window.location.hash.toString()
|
||||
.replace('tc.mode=fixed', 'tc.mode=local')
|
||||
.replace('tc.startBound=0', 'tc.startDelta=1000')
|
||||
.replace('tc.endBound=1', 'tc.endDelta=100');
|
||||
|
||||
resolveFunction = () => {
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
|
||||
window.location.hash = hash;
|
||||
window.location.hash = hash;
|
||||
}
|
||||
|
||||
success = window.location.hash.includes('tc.mode=local');
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
};
|
||||
function setFixedLocationParameters() {
|
||||
let hash = window.location.hash.toString()
|
||||
.replace('tc.mode=local', 'tc.mode=fixed')
|
||||
.replace('tc.timeSystem=utc', 'tc.timeSystem=local')
|
||||
.replace('tc.startDelta=1000', 'tc.startBound=50')
|
||||
.replace('tc.endDelta=100', 'tc.endBound=60');
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
window.location.hash = hash;
|
||||
}
|
||||
|
||||
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
|
||||
let success;
|
||||
function switchToRealtimeMode() {
|
||||
let resolveFunction;
|
||||
|
||||
resolveFunction = () => {
|
||||
let hash = window.location.hash;
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
openmct.time.on('clock', resolveFunction);
|
||||
setRealtimeLocationParameters();
|
||||
}).then(() => {
|
||||
openmct.time.off('clock', resolveFunction);
|
||||
});
|
||||
}
|
||||
|
||||
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
|
||||
window.location.hash = hash;
|
||||
success = window.location.hash.includes('tc.mode=local');
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
};
|
||||
function switchToFixedMode() {
|
||||
let resolveFunction;
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||
//detected in the API.
|
||||
openmct.time.on('clock', resolveFunction);
|
||||
setFixedLocationParameters();
|
||||
}).then(() => {
|
||||
openmct.time.off('clock', resolveFunction);
|
||||
});
|
||||
}
|
||||
|
||||
it("reset hash", (done) => {
|
||||
let success;
|
||||
function expectLocationToBeInRealtimeMode() {
|
||||
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
||||
}
|
||||
|
||||
window.location.hash = oldHash;
|
||||
resolveFunction = () => {
|
||||
success = window.location.hash === oldHash;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
function expectLocationToBeInFixedMode() {
|
||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
|
||||
expect(window.location.hash.includes('tc.mode=local')).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||
<a v-if="conditionSetDomainObject"
|
||||
class="c-object-label icon-conditional"
|
||||
:href="navigateToPath"
|
||||
@click="navigateOrPreview"
|
||||
>
|
||||
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
||||
@@ -285,8 +286,6 @@ export default {
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
this.previewAction.invoke(this.objectPath);
|
||||
} else {
|
||||
this.openmct.router.navigate(this.navigateToPath);
|
||||
}
|
||||
},
|
||||
removeConditionSet() {
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||
<a v-if="conditionSetDomainObject"
|
||||
class="c-object-label"
|
||||
:href="navigateToPath"
|
||||
@click="navigateOrPreview"
|
||||
>
|
||||
<span class="c-object-label__type-icon icon-conditional"></span>
|
||||
@@ -308,8 +309,6 @@ export default {
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
this.previewAction.invoke(this.objectPath);
|
||||
} else {
|
||||
this.openmct.router.navigate(this.navigateToPath);
|
||||
}
|
||||
},
|
||||
isItemType(type, item) {
|
||||
|
||||
@@ -46,7 +46,7 @@ xdescribe("the plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('installs the new folder action', () => {
|
||||
|
||||
@@ -112,7 +112,7 @@ describe("The Duplicate Action plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'is-alias': item.isAlias === true,
|
||||
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
|
||||
}, statusClass]"
|
||||
@click="navigate"
|
||||
:href="objectLink"
|
||||
>
|
||||
<div
|
||||
class="c-grid-item__type-icon"
|
||||
@@ -49,17 +49,11 @@ import statusListener from './status-listener';
|
||||
|
||||
export default {
|
||||
mixins: [contextMenuGesture, objectLink, statusListener],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigate() {
|
||||
this.openmct.router.navigate(this.objectLink);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
ref="objectLink"
|
||||
class="c-object-label"
|
||||
:class="[statusClass]"
|
||||
@click="navigate"
|
||||
:href="objectLink"
|
||||
>
|
||||
<div
|
||||
class="c-object-label__type-icon c-list-item__name__type-icon"
|
||||
@@ -45,7 +45,6 @@ import statusListener from './status-listener';
|
||||
|
||||
export default {
|
||||
mixins: [contextMenuGesture, objectLink, statusListener],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
@@ -57,7 +56,7 @@ export default {
|
||||
return moment(timestamp).format(format);
|
||||
},
|
||||
navigate() {
|
||||
this.openmct.router.navigate(this.objectLink);
|
||||
this.$refs.objectLink.click();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
margin: 0 $interiorMargin $interiorMargin 0;
|
||||
}
|
||||
}
|
||||
|
||||
body.mobile & {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************* GRID ITEMS */
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class GoToOriginalAction {
|
||||
.slice(1)
|
||||
.join('/');
|
||||
|
||||
this._openmct.router.navigate(url);
|
||||
window.location.href = url;
|
||||
});
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
|
||||
@@ -47,6 +47,7 @@ describe("the plugin", () => {
|
||||
});
|
||||
|
||||
describe('when invoked', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockObjectPath = [{
|
||||
name: 'mock folder',
|
||||
@@ -62,15 +63,11 @@ describe("the plugin", () => {
|
||||
key: 'test'
|
||||
}
|
||||
}));
|
||||
|
||||
goToFolderAction.invoke(mockObjectPath);
|
||||
});
|
||||
|
||||
it('goes to the original location', (done) => {
|
||||
setTimeout(() => {
|
||||
expect(window.location.href).toContain('context.html#/browse/?tc.mode=fixed&tc.startBound=0&tc.endBound=1&tc.timeSystem=utc');
|
||||
done();
|
||||
}, 1500);
|
||||
it('goes to the original location', () => {
|
||||
expect(window.location.href).toContain('context.html#/browse/?tc.mode=fixed&tc.startBound=0&tc.endBound=1&tc.timeSystem=utc');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
:camera-pan="cameraPan"
|
||||
/>
|
||||
<CompassRose
|
||||
v-if="hasCameraFieldOfView"
|
||||
v-if="true"
|
||||
:heading="heading"
|
||||
:sized-image-width="sizedImageDimensions.width"
|
||||
:sun-heading="sunHeading"
|
||||
|
||||
@@ -390,9 +390,7 @@ export default {
|
||||
delete this.unsubscribe;
|
||||
}
|
||||
|
||||
if (this.imageContainerResizeObserver) {
|
||||
this.imageContainerResizeObserver.disconnect();
|
||||
}
|
||||
this.imageContainerResizeObserver.disconnect();
|
||||
|
||||
if (this.relatedTelemetry.hasRelatedTelemetry) {
|
||||
this.relatedTelemetry.destroy();
|
||||
@@ -704,7 +702,7 @@ export default {
|
||||
window.clearInterval(this.durationTracker);
|
||||
},
|
||||
updateDuration() {
|
||||
let currentTime = this.openmct.time.clock() && this.openmct.time.clock().currentValue();
|
||||
let currentTime = this.openmct.time.clock().currentValue();
|
||||
this.numericDuration = currentTime - this.parsedSelectedTime;
|
||||
},
|
||||
resetAgeCSS() {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import ImageryPlugin from './plugin.js';
|
||||
import Vue from 'vue';
|
||||
import {
|
||||
createOpenMct,
|
||||
@@ -89,11 +89,15 @@ describe("The Imagery View Layout", () => {
|
||||
const START = Date.now();
|
||||
const COUNT = 10;
|
||||
|
||||
let resolveFunction;
|
||||
|
||||
let openmct;
|
||||
let imageryPlugin;
|
||||
let parent;
|
||||
let child;
|
||||
let timeFormat = 'utc';
|
||||
let bounds = {
|
||||
start: START - TEN_MINUTES,
|
||||
end: START
|
||||
};
|
||||
let imageTelemetry = generateTelemetry(START - TEN_MINUTES, COUNT);
|
||||
let imageryObject = {
|
||||
identifier: {
|
||||
@@ -201,10 +205,6 @@ describe("The Imagery View Layout", () => {
|
||||
|
||||
openmct = createOpenMct();
|
||||
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
openmct.install(openmct.plugins.LocalTimeSystem());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
|
||||
parent = document.createElement('div');
|
||||
child = document.createElement('div');
|
||||
parent.appendChild(child);
|
||||
@@ -215,18 +215,22 @@ describe("The Imagery View Layout", () => {
|
||||
});
|
||||
|
||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
||||
|
||||
imageryPlugin = new ImageryPlugin();
|
||||
openmct.install(imageryPlugin);
|
||||
|
||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
|
||||
|
||||
openmct.time.timeSystem(timeFormat, {
|
||||
start: 0,
|
||||
end: 4
|
||||
});
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.start(appHolder);
|
||||
openmct.startHeadless(appHolder);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
@@ -244,7 +248,7 @@ describe("The Imagery View Layout", () => {
|
||||
let imageryViewProvider;
|
||||
let imageryView;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(async (done) => {
|
||||
let telemetryRequestResolve;
|
||||
let telemetryRequestPromise = new Promise((resolve) => {
|
||||
telemetryRequestResolve = resolve;
|
||||
@@ -256,18 +260,23 @@ describe("The Imagery View Layout", () => {
|
||||
return telemetryRequestPromise;
|
||||
});
|
||||
|
||||
openmct.time.clock('local', {
|
||||
start: bounds.start,
|
||||
end: bounds.end + 100
|
||||
});
|
||||
|
||||
applicableViews = openmct.objectViews.get(imageryObject, []);
|
||||
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
|
||||
imageryView = imageryViewProvider.view(imageryObject);
|
||||
imageryView.show(child);
|
||||
|
||||
await telemetryRequestPromise;
|
||||
await Vue.nextTick();
|
||||
|
||||
return done();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.stopClock();
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
|
||||
imageryView.destroy();
|
||||
});
|
||||
|
||||
@@ -277,44 +286,43 @@ describe("The Imagery View Layout", () => {
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("should show the clicked thumbnail as the main image", (done) => {
|
||||
it("should show the clicked thumbnail as the main image", async () => {
|
||||
const target = imageTelemetry[5].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
await Vue.nextTick();
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("should show that an image is new", async (done) => {
|
||||
await Vue.nextTick();
|
||||
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
|
||||
expect(imageIsNew).toBeTrue();
|
||||
done();
|
||||
});
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
|
||||
xit("should show that an image is new", (done) => {
|
||||
Vue.nextTick(() => {
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
expect(imageIsNew).toBeTrue();
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
});
|
||||
|
||||
xit("should show that an image is not new", (done) => {
|
||||
it("should show that an image is not new", async (done) => {
|
||||
const target = imageTelemetry[2].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
await Vue.nextTick();
|
||||
|
||||
expect(imageIsNew).toBeFalse();
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
|
||||
expect(imageIsNew).toBeFalse();
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
|
||||
it("should navigate via arrow keys", (done) => {
|
||||
it("should navigate via arrow keys", async () => {
|
||||
let keyOpts = {
|
||||
element: parent.querySelector('.c-imagery'),
|
||||
key: 'ArrowLeft',
|
||||
@@ -324,15 +332,14 @@ describe("The Imagery View Layout", () => {
|
||||
|
||||
simulateKeyEvent(keyOpts);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
await Vue.nextTick();
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("should navigate via numerous arrow keys", (done) => {
|
||||
it("should navigate via numerous arrow keys", async () => {
|
||||
let element = parent.querySelector('.c-imagery');
|
||||
let type = 'keyup';
|
||||
let leftKeyOpts = {
|
||||
@@ -355,12 +362,12 @@ describe("The Imagery View Layout", () => {
|
||||
// right once
|
||||
simulateKeyEvent(rightKeyOpts);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
await Vue.nextTick();
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe("The local time", () => {
|
||||
beforeEach(() => {
|
||||
localTimeSystem = openmct.time.timeSystem(LOCAL_SYSTEM_KEY, {
|
||||
start: 0,
|
||||
end: 1
|
||||
end: 4
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("The Move Action plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
|
||||
@@ -135,7 +135,6 @@ import SearchResults from './SearchResults.vue';
|
||||
import Sidebar from './Sidebar.vue';
|
||||
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
|
||||
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
|
||||
import objectUtils from 'objectUtils';
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
@@ -190,14 +189,14 @@ export default {
|
||||
selectedPage() {
|
||||
const pages = this.getPages();
|
||||
if (!pages) {
|
||||
return {};
|
||||
return null;
|
||||
}
|
||||
|
||||
return pages.find(page => page.isSelected);
|
||||
},
|
||||
selectedSection() {
|
||||
if (!this.sections.length) {
|
||||
return {};
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.sections.find(section => section.isSelected);
|
||||
@@ -217,7 +216,6 @@ export default {
|
||||
|
||||
window.addEventListener('orientationchange', this.formatSidebar);
|
||||
window.addEventListener("hashchange", this.navigateToSectionPage, false);
|
||||
this.openmct.router.on('change:params', this.changeSectionPage);
|
||||
|
||||
this.navigateToSectionPage();
|
||||
},
|
||||
@@ -228,7 +226,6 @@ export default {
|
||||
|
||||
window.removeEventListener('orientationchange', this.formatSidebar);
|
||||
window.removeEventListener("hashchange", this.navigateToSectionPage);
|
||||
this.openmct.router.off('change:params', this.changeSectionPage);
|
||||
},
|
||||
updated: function () {
|
||||
this.$nextTick(() => {
|
||||
@@ -236,28 +233,6 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
changeSectionPage(newParams, oldParams, changedParams) {
|
||||
if (newParams.view !== NOTEBOOK_VIEW_TYPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
let pageId = newParams.pageId;
|
||||
let sectionId = newParams.sectionId;
|
||||
|
||||
if (!pageId && !sectionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sections.forEach(section => {
|
||||
section.isSelected = Boolean(section.id === sectionId);
|
||||
|
||||
if (section.isSelected) {
|
||||
section.pages.forEach(page => {
|
||||
page.isSelected = Boolean(page.id === pageId);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
changeSelectedSection({ sectionId, pageId }) {
|
||||
const sections = this.sections.map(s => {
|
||||
s.isSelected = false;
|
||||
@@ -543,11 +518,9 @@ export default {
|
||||
return this.sections.find(section => section.isSelected);
|
||||
},
|
||||
navigateToSectionPage() {
|
||||
let { pageId, sectionId } = this.openmct.router.getParams();
|
||||
|
||||
const { pageId, sectionId } = this.openmct.router.getParams();
|
||||
if (!pageId || !sectionId) {
|
||||
sectionId = this.selectedSection.id;
|
||||
pageId = this.selectedPage.id;
|
||||
return;
|
||||
}
|
||||
|
||||
const sections = this.sections.map(s => {
|
||||
|
||||
@@ -145,7 +145,7 @@ export default {
|
||||
|
||||
const relativeHash = hash.slice(hash.indexOf('#'));
|
||||
const url = new URL(relativeHash, `${location.protocol}//${location.host}${location.pathname}`);
|
||||
this.openmct.router.navigate(url.hash);
|
||||
window.location.hash = url.hash;
|
||||
},
|
||||
formatTime(unixTime, timeFormat) {
|
||||
return Moment.utc(unixTime).format(timeFormat);
|
||||
|
||||
@@ -111,6 +111,10 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
pages() {
|
||||
const selectedSection = this.sections.find(section => section.isSelected);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export const EVENT_SNAPSHOTS_UPDATED = 'SNAPSHOTS_UPDATED';
|
||||
export const NOTEBOOK_DEFAULT = 'DEFAULT';
|
||||
export const NOTEBOOK_SNAPSHOT = 'SNAPSHOT';
|
||||
export const NOTEBOOK_VIEW_TYPE = 'notebook-vue';
|
||||
|
||||
@@ -65,8 +65,7 @@ describe("Notebook plugin:", () => {
|
||||
|
||||
afterAll(() => {
|
||||
appHolder.remove();
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("has type as Notebook", () => {
|
||||
|
||||
@@ -140,8 +140,7 @@ describe('Notebook Entries:', () => {
|
||||
|
||||
afterEach(() => {
|
||||
notebookDomainObject.configuration.entries[selectedSection.id][selectedPage.id] = [];
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('getNotebookEntries has no entries', () => {
|
||||
|
||||
@@ -83,7 +83,7 @@ describe('Notebook Storage:', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('has empty local Storage', () => {
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('the plugin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('updates an object', (done) => {
|
||||
it('updates an object', () => {
|
||||
return openmct.objects.save(mockDomainObject).then((result) => {
|
||||
expect(result).toBeTrue();
|
||||
expect(provider.create).toHaveBeenCalled();
|
||||
@@ -128,7 +128,6 @@ describe('the plugin', () => {
|
||||
return openmct.objects.save(mockDomainObject).then((updatedResult) => {
|
||||
expect(updatedResult).toBeTrue();
|
||||
expect(provider.update).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
:series="config.series.models"
|
||||
:highlights="highlights"
|
||||
:legend="config.legend"
|
||||
@legendHoverChanged="legendHoverChanged"
|
||||
/>
|
||||
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
|
||||
<y-axis v-if="config.series.models.length > 0"
|
||||
@@ -68,7 +67,6 @@
|
||||
>
|
||||
<mct-chart :rectangles="rectangles"
|
||||
:highlights="highlights"
|
||||
:show-limit-line-labels="showLimitLineLabels"
|
||||
@plotReinitializeCanvas="initCanvas"
|
||||
/>
|
||||
</div>
|
||||
@@ -161,7 +159,6 @@ import MctTicks from "./MctTicks.vue";
|
||||
import MctChart from "./chart/MctChart.vue";
|
||||
import XAxis from "./axis/XAxis.vue";
|
||||
import YAxis from "./axis/YAxis.vue";
|
||||
import _ from "lodash";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -215,8 +212,7 @@ export default {
|
||||
pending: 0,
|
||||
isRealTime: this.openmct.time.clock() !== undefined,
|
||||
loaded: false,
|
||||
isTimeOutOfSync: false,
|
||||
showLimitLineLabels: undefined
|
||||
isTimeOutOfSync: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -500,10 +496,6 @@ export default {
|
||||
},
|
||||
|
||||
initialize() {
|
||||
_.debounce(this.handleWindowResize, 400);
|
||||
this.plotContainerResizeObserver = new ResizeObserver(this.handleWindowResize);
|
||||
this.plotContainerResizeObserver.observe(this.$parent.$refs.plotWrapper);
|
||||
|
||||
// Setup canvas etc.
|
||||
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
|
||||
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
|
||||
@@ -1007,23 +999,12 @@ export default {
|
||||
this.removeStatusListener();
|
||||
}
|
||||
|
||||
this.plotContainerResizeObserver.disconnect();
|
||||
|
||||
this.openmct.time.off('clock', this.updateRealTime);
|
||||
this.openmct.time.off('bounds', this.updateDisplayBounds);
|
||||
this.openmct.objectViews.off('clearData', this.clearData);
|
||||
},
|
||||
updateStatus(status) {
|
||||
this.$emit('statusUpdated', status);
|
||||
},
|
||||
handleWindowResize() {
|
||||
if (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth) {
|
||||
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
|
||||
this.config.series.models.forEach(this.loadSeriesData, this);
|
||||
}
|
||||
},
|
||||
legendHoverChanged(data) {
|
||||
this.showLimitLineLabels = data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -228,16 +228,15 @@ export default {
|
||||
|
||||
doTickUpdate() {
|
||||
if (this.shouldCheckWidth) {
|
||||
const tickElements = this.$refs.tickContainer && this.$refs.tickContainer.querySelectorAll('.gl-plot-tick > span');
|
||||
if (tickElements) {
|
||||
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
|
||||
return Math.max(memo, first.offsetWidth);
|
||||
}, 0));
|
||||
const tickElements = this.$refs.tickContainer.querySelectorAll('.gl-plot-tick > span');
|
||||
|
||||
this.tickWidth = tickWidth;
|
||||
this.$emit('plotTickWidth', tickWidth);
|
||||
this.shouldCheckWidth = false;
|
||||
}
|
||||
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
|
||||
return Math.max(memo, first.offsetWidth);
|
||||
}, 0));
|
||||
|
||||
this.tickWidth = tickWidth;
|
||||
this.$emit('plotTickWidth', tickWidth);
|
||||
this.shouldCheckWidth = false;
|
||||
}
|
||||
|
||||
this.tickUpdate = false;
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<div class="plot-series-limit-label"
|
||||
:style="styleObj"
|
||||
:class="limit.cssClass"
|
||||
>
|
||||
<span class="plot-series-limit-value">{{ limit.value }}</span>
|
||||
<span class="plot-series-color-swatch"
|
||||
:style="{ 'background-color': limit.color }"
|
||||
></span>
|
||||
<span class="plot-series-name">{{ limit.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
limit: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
point: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleObj() {
|
||||
const top = `${this.point.top - 10}px`;
|
||||
const left = `${this.point.left + 5}px`;
|
||||
|
||||
return {
|
||||
'position': 'absolute',
|
||||
'top': top,
|
||||
'left': left,
|
||||
'color': '#fff'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,44 +0,0 @@
|
||||
<template>
|
||||
<hr :style="styleObj"
|
||||
:class="cssWithoutUprLwr"
|
||||
>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
point: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
cssClass: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleObj() {
|
||||
const top = `${this.point.top}px`;
|
||||
const left = `${this.point.left}px`;
|
||||
|
||||
return {
|
||||
'position': 'absolute',
|
||||
'width': '100%',
|
||||
'top': top,
|
||||
'left': left
|
||||
};
|
||||
},
|
||||
cssWithoutUprLwr() {
|
||||
let cssClass = this.cssClass.replace(/is-limit--upr/gi, 'is-limit--line');
|
||||
cssClass = cssClass.replace(/is-limit--lwr/gi, 'is-limit--line');
|
||||
|
||||
return cssClass;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,105 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import eventHelpers from '../lib/eventHelpers';
|
||||
|
||||
export default class MCTChartAlarmLineSet {
|
||||
constructor(series, chart, offset, bounds) {
|
||||
this.series = series;
|
||||
this.chart = chart;
|
||||
this.offset = offset;
|
||||
this.bounds = bounds;
|
||||
this.limits = [];
|
||||
|
||||
eventHelpers.extend(this);
|
||||
this.listenTo(series, 'limitBounds', this.updateBounds, this);
|
||||
this.listenTo(series, 'change:xKey', this.getLimitPoints, this);
|
||||
|
||||
if (series.limits) {
|
||||
this.getLimitPoints(series);
|
||||
}
|
||||
}
|
||||
|
||||
updateBounds(bounds) {
|
||||
this.bounds = bounds;
|
||||
this.getLimitPoints(this.series);
|
||||
}
|
||||
|
||||
color() {
|
||||
return this.series.get('color');
|
||||
}
|
||||
|
||||
name() {
|
||||
return this.series.get('name');
|
||||
}
|
||||
|
||||
makePoint(point, series) {
|
||||
if (!this.offset.xVal) {
|
||||
this.chart.setOffset(point, undefined, series);
|
||||
}
|
||||
|
||||
return {
|
||||
x: this.offset.xVal(point, series),
|
||||
y: this.offset.yVal(point, series)
|
||||
};
|
||||
}
|
||||
|
||||
getLimitPoints(series) {
|
||||
this.limits = [];
|
||||
let xKey = series.get('xKey');
|
||||
Object.keys(series.limits).forEach((key) => {
|
||||
const limitForLevel = series.limits[key];
|
||||
if (limitForLevel.high) {
|
||||
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.high), series);
|
||||
this.limits.push({
|
||||
seriesKey: series.keyString,
|
||||
value: series.getYVal(limitForLevel.high),
|
||||
color: this.color().asHexString(),
|
||||
name: this.name(),
|
||||
point,
|
||||
cssClass: limitForLevel.high.cssClass
|
||||
});
|
||||
}
|
||||
|
||||
if (limitForLevel.low) {
|
||||
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.low), series);
|
||||
this.limits.push({
|
||||
seriesKey: series.keyString,
|
||||
value: series.getYVal(limitForLevel.low),
|
||||
color: this.color().asHexString(),
|
||||
name: this.name(),
|
||||
point,
|
||||
cssClass: limitForLevel.low.cssClass
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.limits = [];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.stopListening();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,9 +23,6 @@
|
||||
<div class="gl-plot-chart-area">
|
||||
<span v-html="canvasTemplate"></span>
|
||||
<span v-html="canvasTemplate"></span>
|
||||
<div ref="limitArea"
|
||||
class="js-limit-area"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -37,12 +34,8 @@ import MCTChartLineLinear from './MCTChartLineLinear';
|
||||
import MCTChartLineStepAfter from './MCTChartLineStepAfter';
|
||||
import MCTChartPointSet from './MCTChartPointSet';
|
||||
import MCTChartAlarmPointSet from './MCTChartAlarmPointSet';
|
||||
import MCTChartAlarmLineSet from "./MCTChartAlarmLineSet";
|
||||
import configStore from "../configuration/configStore";
|
||||
import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
|
||||
import LimitLine from "./LimitLine.vue";
|
||||
import LimitLabel from "./LimitLabel.vue";
|
||||
import Vue from 'vue';
|
||||
|
||||
const MARKER_SIZE = 6.0;
|
||||
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
|
||||
@@ -61,12 +54,6 @@ export default {
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
showLimitLineLabels: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -80,22 +67,18 @@ export default {
|
||||
},
|
||||
rectangles() {
|
||||
this.scheduleDraw();
|
||||
},
|
||||
showLimitLineLabels() {
|
||||
this.drawLimitLines();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
eventHelpers.extend(this);
|
||||
|
||||
this.config = this.getConfig();
|
||||
this.isDestroyed = false;
|
||||
this.lines = [];
|
||||
this.limitLines = [];
|
||||
this.pointSets = [];
|
||||
this.alarmSets = [];
|
||||
this.offset = {};
|
||||
this.seriesElements = new WeakMap();
|
||||
this.seriesLimits = new WeakMap();
|
||||
|
||||
let canvasEls = this.$parent.$refs.chartContainer.querySelectorAll("canvas");
|
||||
const mainCanvas = canvasEls[1];
|
||||
@@ -107,8 +90,8 @@ export default {
|
||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
||||
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
|
||||
this.listenTo(this.config.yAxis, 'change', this.updateLimitsAndDraw);
|
||||
this.listenTo(this.config.xAxis, 'change', this.updateLimitsAndDraw);
|
||||
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
|
||||
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
|
||||
this.config.series.forEach(this.onSeriesAdd, this);
|
||||
},
|
||||
beforeDestroy() {
|
||||
@@ -133,18 +116,15 @@ export default {
|
||||
this.changeInterpolate(mode, o, series);
|
||||
this.changeMarkers(mode, o, series);
|
||||
this.changeAlarmMarkers(mode, o, series);
|
||||
this.changeLimitLines(mode, o, series);
|
||||
},
|
||||
onSeriesAdd(series) {
|
||||
this.listenTo(series, 'change:xKey', this.reDraw, this);
|
||||
this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
|
||||
this.listenTo(series, 'change:markers', this.changeMarkers, this);
|
||||
this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
|
||||
this.listenTo(series, 'change:limitLines', this.changeLimitLines, this);
|
||||
this.listenTo(series, 'change', this.scheduleDraw);
|
||||
this.listenTo(series, 'add', this.scheduleDraw);
|
||||
this.makeChartElement(series);
|
||||
this.makeLimitLines(series);
|
||||
},
|
||||
changeInterpolate(mode, o, series) {
|
||||
if (mode === o) {
|
||||
@@ -198,14 +178,6 @@ export default {
|
||||
this.pointSets.push(pointSet);
|
||||
}
|
||||
},
|
||||
changeLimitLines(mode, o, series) {
|
||||
if (mode === o) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeLimitLines(series);
|
||||
this.updateLimitsAndDraw();
|
||||
},
|
||||
onSeriesRemove(series) {
|
||||
this.stopListening(series);
|
||||
this.removeChartElement(series);
|
||||
@@ -215,7 +187,6 @@ export default {
|
||||
this.isDestroyed = true;
|
||||
this.stopListening();
|
||||
this.lines.forEach(line => line.destroy());
|
||||
this.limitLines.forEach(line => line.destroy());
|
||||
DrawLoader.releaseDrawAPI(this.drawAPI);
|
||||
},
|
||||
clearOffset() {
|
||||
@@ -228,9 +199,6 @@ export default {
|
||||
this.lines.forEach(function (line) {
|
||||
line.reset();
|
||||
});
|
||||
this.limitLines.forEach(function (line) {
|
||||
line.reset();
|
||||
});
|
||||
this.pointSets.forEach(function (pointSet) {
|
||||
pointSet.reset();
|
||||
});
|
||||
@@ -301,8 +269,6 @@ export default {
|
||||
}
|
||||
|
||||
this.seriesElements.delete(series);
|
||||
|
||||
this.clearLimitLines(series);
|
||||
},
|
||||
lineForSeries(series) {
|
||||
if (series.get('interpolate') === 'linear') {
|
||||
@@ -321,14 +287,6 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
limitLineForSeries(series) {
|
||||
return new MCTChartAlarmLineSet(
|
||||
series,
|
||||
this,
|
||||
this.offset,
|
||||
this.openmct.time.bounds()
|
||||
);
|
||||
},
|
||||
pointSetForSeries(series) {
|
||||
if (series.get('markers')) {
|
||||
return new MCTChartPointSet(
|
||||
@@ -350,8 +308,7 @@ export default {
|
||||
makeChartElement(series) {
|
||||
const elements = {
|
||||
lines: [],
|
||||
pointSets: [],
|
||||
limitLines: []
|
||||
pointSets: []
|
||||
};
|
||||
|
||||
const line = this.lineForSeries(series);
|
||||
@@ -373,37 +330,6 @@ export default {
|
||||
|
||||
this.seriesElements.set(series, elements);
|
||||
},
|
||||
makeLimitLines(series) {
|
||||
this.clearLimitLines(series);
|
||||
|
||||
if (!series.get('limitLines')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limitElements = {
|
||||
limitLines: []
|
||||
};
|
||||
|
||||
const limitLine = this.limitLineForSeries(series);
|
||||
if (limitLine) {
|
||||
limitElements.limitLines.push(limitLine);
|
||||
this.limitLines.push(limitLine);
|
||||
}
|
||||
|
||||
this.seriesLimits.set(series, limitElements);
|
||||
},
|
||||
clearLimitLines(series) {
|
||||
const seriesLimits = this.seriesLimits.get(series);
|
||||
|
||||
if (seriesLimits) {
|
||||
seriesLimits.limitLines.forEach(function (line) {
|
||||
this.limitLines.splice(this.limitLines.indexOf(line), 1);
|
||||
line.destroy();
|
||||
}, this);
|
||||
|
||||
this.seriesLimits.delete(series);
|
||||
}
|
||||
},
|
||||
canDraw() {
|
||||
if (!this.offset.x || !this.offset.y) {
|
||||
return false;
|
||||
@@ -411,10 +337,6 @@ export default {
|
||||
|
||||
return true;
|
||||
},
|
||||
updateLimitsAndDraw() {
|
||||
this.drawLimitLines();
|
||||
this.scheduleDraw();
|
||||
},
|
||||
scheduleDraw() {
|
||||
if (!this.drawScheduled) {
|
||||
requestAnimationFrame(this.draw);
|
||||
@@ -463,68 +385,6 @@ export default {
|
||||
this.pointSets.forEach(this.drawPoints, this);
|
||||
this.alarmSets.forEach(this.drawAlarmPoints, this);
|
||||
},
|
||||
drawLimitLines() {
|
||||
if (this.canDraw()) {
|
||||
this.updateViewport();
|
||||
|
||||
if (!this.drawAPI.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
|
||||
|
||||
this.limitLines.forEach((limitLine) => {
|
||||
let limitContainerEl = this.$refs.limitArea;
|
||||
limitLine.limits.forEach((limit) => {
|
||||
const showLabels = this.showLabels(limit.seriesKey);
|
||||
if (showLabels) {
|
||||
let limitLabelEl = this.getLimitLabel(limit);
|
||||
limitContainerEl.appendChild(limitLabelEl);
|
||||
}
|
||||
|
||||
let limitEl = this.getLimitElement(limit);
|
||||
limitContainerEl.appendChild(limitEl);
|
||||
|
||||
}, this);
|
||||
});
|
||||
}
|
||||
},
|
||||
showLabels(seriesKey) {
|
||||
return this.showLimitLineLabels.seriesKey
|
||||
&& (this.showLimitLineLabels.seriesKey === seriesKey);
|
||||
},
|
||||
getLimitElement(limit) {
|
||||
let point = {
|
||||
left: 0,
|
||||
top: this.drawAPI.y(limit.point.y)
|
||||
};
|
||||
let LimitLineClass = Vue.extend(LimitLine);
|
||||
const component = new LimitLineClass({
|
||||
propsData: {
|
||||
point,
|
||||
cssClass: limit.cssClass
|
||||
}
|
||||
});
|
||||
component.$mount();
|
||||
|
||||
return component.$el;
|
||||
},
|
||||
getLimitLabel(limit) {
|
||||
let point = {
|
||||
left: 0,
|
||||
top: this.drawAPI.y(limit.point.y)
|
||||
};
|
||||
let LimitLabelClass = Vue.extend(LimitLabel);
|
||||
const component = new LimitLabelClass({
|
||||
propsData: {
|
||||
limit,
|
||||
point
|
||||
}
|
||||
});
|
||||
component.$mount();
|
||||
|
||||
return component.$el;
|
||||
},
|
||||
drawAlarmPoints(alarmSet) {
|
||||
this.drawAPI.drawLimitPoints(
|
||||
alarmSet.points,
|
||||
@@ -541,12 +401,11 @@ export default {
|
||||
chartElement.series.get('markerShape')
|
||||
);
|
||||
},
|
||||
drawLine(chartElement, disconnected) {
|
||||
drawLine(chartElement) {
|
||||
this.drawAPI.drawLine(
|
||||
chartElement.getBuffer(),
|
||||
chartElement.color().asRGBAArray(),
|
||||
chartElement.count,
|
||||
disconnected
|
||||
chartElement.count
|
||||
);
|
||||
},
|
||||
drawHighlights() {
|
||||
|
||||
@@ -97,8 +97,7 @@ export default class PlotSeries extends Model {
|
||||
markers: true,
|
||||
markerShape: 'point',
|
||||
markerSize: 2.0,
|
||||
alarmMarkers: true,
|
||||
limitLines: false
|
||||
alarmMarkers: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,16 +115,9 @@ export default class PlotSeries extends Model {
|
||||
this.domainObject = options.domainObject;
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
|
||||
this.limitDefinition = this.openmct.telemetry.limitDefinition(options.domainObject);
|
||||
this.limits = this.limitDefinition.limits();
|
||||
this.openmct.time.on('bounds', this.updateLimits);
|
||||
this.on('destroy', this.onDestroy, this);
|
||||
}
|
||||
|
||||
updateLimits(bounds) {
|
||||
this.emit('limitBounds', bounds);
|
||||
}
|
||||
|
||||
locateOldObject(oldStyleParent) {
|
||||
return oldStyleParent.useCapability('composition')
|
||||
.then(function (children) {
|
||||
|
||||
@@ -60,14 +60,6 @@
|
||||
{{ alarmMarkers ? "Enabled" : "Disabled" }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<div class="grid-cell label"
|
||||
title="Display lines visually denoting alarm limits."
|
||||
>Limit lines</div>
|
||||
<div class="grid-cell value">
|
||||
{{ limitLines ? "Enabled" : "Disabled" }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<div class="grid-cell label"
|
||||
title="The plot line and marker color for this series."
|
||||
@@ -143,9 +135,6 @@ export default {
|
||||
alarmMarkers() {
|
||||
return this.series.get('alarmMarkers');
|
||||
},
|
||||
limitLines() {
|
||||
return this.series.get('limitLines');
|
||||
},
|
||||
seriesHexColor() {
|
||||
return this.series.get('color').asHexString();
|
||||
}
|
||||
|
||||
@@ -91,17 +91,6 @@
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<div class="grid-cell label"
|
||||
title="Display limit lines"
|
||||
>Limit lines</div>
|
||||
<div class="grid-cell value">
|
||||
<input v-model="limitLines"
|
||||
type="checkbox"
|
||||
@change="updateForm('limitLines')"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
<li v-show="markers || alarmMarkers"
|
||||
class="grid-row"
|
||||
>
|
||||
@@ -179,7 +168,6 @@ export default {
|
||||
markers: this.series.get('markers'),
|
||||
markerShape: this.series.get('markerShape'),
|
||||
alarmMarkers: this.series.get('alarmMarkers'),
|
||||
limitLines: this.series.get('limitLines'),
|
||||
markerSize: this.series.get('markerSize'),
|
||||
validation: {},
|
||||
swatchActive: false
|
||||
@@ -252,11 +240,6 @@ export default {
|
||||
modelProp: 'alarmMarkers',
|
||||
coerce: Boolean,
|
||||
objectPath: this.dynamicPathForKey('alarmMarkers')
|
||||
},
|
||||
{
|
||||
modelProp: 'limitLines',
|
||||
coerce: Boolean,
|
||||
objectPath: this.dynamicPathForKey('limitLines')
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
:highlights="highlights"
|
||||
:value-to-show-when-collapsed="legend.get('valueToShowWhenCollapsed')"
|
||||
:series-object="seriesObject"
|
||||
@legendHoverChanged="legendHoverChanged"
|
||||
:closest="seriesObject.closest"
|
||||
/>
|
||||
</div>
|
||||
<!-- EXPANDED PLOT LEGEND -->
|
||||
@@ -89,7 +89,6 @@
|
||||
:series-object="seriesObject"
|
||||
:highlights="highlights"
|
||||
:legend="legend"
|
||||
@legendHoverChanged="legendHoverChanged"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -134,36 +133,19 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLegendExpanded: this.legend.get('expanded') === true
|
||||
isLegendHidden: this.legend.get('hideLegendWhenSmall') !== true,
|
||||
isLegendExpanded: this.legend.get('expanded') === true,
|
||||
showTimestampWhenExpanded: this.legend.get('showTimestampWhenExpanded') === true,
|
||||
showValueWhenExpanded: this.legend.get('showValueWhenExpanded') === true,
|
||||
showUnitsWhenExpanded: this.legend.get('showUnitsWhenExpanded') === true,
|
||||
showMinimumWhenExpanded: this.legend.get('showMinimumWhenExpanded') === true,
|
||||
showMaximumWhenExpanded: this.legend.get('showMaximumWhenExpanded') === true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showUnitsWhenExpanded() {
|
||||
return this.legend.get('showUnitsWhenExpanded') === true;
|
||||
},
|
||||
showMinimumWhenExpanded() {
|
||||
return this.legend.get('showMinimumWhenExpanded') === true;
|
||||
},
|
||||
showMaximumWhenExpanded() {
|
||||
return this.legend.get('showMaximumWhenExpanded') === true;
|
||||
},
|
||||
showValueWhenExpanded() {
|
||||
return this.legend.get('showValueWhenExpanded') === true;
|
||||
},
|
||||
showTimestampWhenExpanded() {
|
||||
return this.legend.get('showTimestampWhenExpanded') === true;
|
||||
},
|
||||
isLegendHidden() {
|
||||
return this.legend.get('hideLegendWhenSmall') === true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
expandLegend() {
|
||||
this.isLegendExpanded = !this.isLegendExpanded;
|
||||
this.legend.set('expanded', this.isLegendExpanded);
|
||||
},
|
||||
legendHoverChanged(data) {
|
||||
this.$emit('legendHoverChanged', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
:class="{
|
||||
'is-status--missing': isMissing
|
||||
}"
|
||||
@mouseover="toggleHover(true)"
|
||||
@mouseleave="toggleHover(false)"
|
||||
>
|
||||
<div class="plot-series-swatch-and-name">
|
||||
<span class="plot-series-color-swatch"
|
||||
@@ -123,12 +121,6 @@ export default {
|
||||
} else {
|
||||
this.formattedYValueFromStats = '';
|
||||
}
|
||||
},
|
||||
toggleHover(hover) {
|
||||
this.hover = hover;
|
||||
this.$emit('legendHoverChanged', {
|
||||
seriesKey: this.hover ? this.seriesObject.keyString : ''
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
:class="{
|
||||
'is-status--missing': isMissing
|
||||
}"
|
||||
@mouseover="toggleHover(true)"
|
||||
@mouseleave="toggleHover(false)"
|
||||
>
|
||||
<td class="plot-series-swatch-and-name">
|
||||
<span class="plot-series-color-swatch"
|
||||
@@ -96,6 +94,11 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showTimestampWhenExpanded: this.legend.get('showTimestampWhenExpanded'),
|
||||
showValueWhenExpanded: this.legend.get('showValueWhenExpanded'),
|
||||
showUnitsWhenExpanded: this.legend.get('showUnitsWhenExpanded'),
|
||||
showMinimumWhenExpanded: this.legend.get('showMinimumWhenExpanded'),
|
||||
showMaximumWhenExpanded: this.legend.get('showMaximumWhenExpanded'),
|
||||
isMissing: false,
|
||||
colorAsHexString: '',
|
||||
name: '',
|
||||
@@ -107,23 +110,6 @@ export default {
|
||||
mctLimitStateClass: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showUnitsWhenExpanded() {
|
||||
return this.legend.get('showUnitsWhenExpanded') === true;
|
||||
},
|
||||
showMinimumWhenExpanded() {
|
||||
return this.legend.get('showMinimumWhenExpanded') === true;
|
||||
},
|
||||
showMaximumWhenExpanded() {
|
||||
return this.legend.get('showMaximumWhenExpanded') === true;
|
||||
},
|
||||
showValueWhenExpanded() {
|
||||
return this.legend.get('showValueWhenExpanded') === true;
|
||||
},
|
||||
showTimestampWhenExpanded() {
|
||||
return this.legend.get('showTimestampWhenExpanded') === true;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
highlights(newHighlights) {
|
||||
const highlightedObject = newHighlights.find(highlight => highlight.series.keyString === this.seriesObject.keyString);
|
||||
@@ -162,12 +148,6 @@ export default {
|
||||
this.formattedMinY = '';
|
||||
this.formattedMaxY = '';
|
||||
}
|
||||
},
|
||||
toggleHover(hover) {
|
||||
this.hover = hover;
|
||||
this.$emit('legendHoverChanged', {
|
||||
seriesKey: this.hover ? this.seriesObject.keyString : ''
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,7 +36,6 @@ describe("the plugin", function () {
|
||||
let telemetryPromise;
|
||||
let cleanupFirst;
|
||||
let mockObjectPath;
|
||||
let telemetrylimitProvider;
|
||||
|
||||
beforeEach((done) => {
|
||||
mockObjectPath = [
|
||||
@@ -89,45 +88,6 @@ describe("the plugin", function () {
|
||||
return telemetryPromise;
|
||||
});
|
||||
|
||||
telemetrylimitProvider = jasmine.createSpyObj('telemetrylimitProvider', [
|
||||
'supportsLimits',
|
||||
'getLimits',
|
||||
'getLimitEvaluator'
|
||||
]);
|
||||
telemetrylimitProvider.supportsLimits.and.returnValue(true);
|
||||
telemetrylimitProvider.getLimits.and.returnValue({
|
||||
limits: function () {
|
||||
return {
|
||||
WARNING: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--yellow",
|
||||
'some-key': -0.5
|
||||
},
|
||||
high: {
|
||||
cssClass: "is-limit--upr is-limit--yellow",
|
||||
'some-key': 0.5
|
||||
}
|
||||
},
|
||||
DISTRESS: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--red",
|
||||
'some-key': -0.9
|
||||
},
|
||||
high: {
|
||||
cssClass: "is-limit--upr is-limit--red",
|
||||
'some-key': 0.9
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
telemetrylimitProvider.getLimitEvaluator.and.returnValue({
|
||||
evaluate: function () {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
openmct.telemetry.addProvider(telemetrylimitProvider);
|
||||
|
||||
openmct.install(new PlotVuePlugin());
|
||||
|
||||
element = document.createElement("div");
|
||||
@@ -139,11 +99,6 @@ describe("the plugin", function () {
|
||||
element.appendChild(child);
|
||||
document.body.appendChild(element);
|
||||
|
||||
spyOn(window, 'ResizeObserver').and.returnValue({
|
||||
observe() {},
|
||||
disconnect() {}
|
||||
});
|
||||
|
||||
openmct.time.timeSystem("utc", {
|
||||
start: 0,
|
||||
end: 4
|
||||
@@ -163,11 +118,6 @@ describe("the plugin", function () {
|
||||
});
|
||||
|
||||
afterEach((done) => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
// Needs to be in a timeout because plots use a bunch of setTimeouts, some of which can resolve during or after
|
||||
// teardown, which causes problems
|
||||
// This is hacky, we should find a better approach here.
|
||||
@@ -179,7 +129,7 @@ describe("the plugin", function () {
|
||||
|
||||
configStore.deleteAll();
|
||||
|
||||
resetApplicationState(openmct).then(done).catch(done);
|
||||
resetApplicationState(openmct).then(done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -697,25 +647,6 @@ describe("the plugin", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('limits', () => {
|
||||
|
||||
it('lines are not displayed by default', () => {
|
||||
let limitEl = element.querySelectorAll(".js-limit-area hr");
|
||||
expect(limitEl.length).toBe(0);
|
||||
});
|
||||
|
||||
it('lines are displayed when configuration is set to true', (done) => {
|
||||
config.series.models[0].set('limitLines', true);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
let limitEl = element.querySelectorAll(".js-limit-area hr");
|
||||
expect(limitEl.length).toBe(4);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('the inspector view', () => {
|
||||
@@ -862,7 +793,7 @@ describe("the plugin", function () {
|
||||
expandControl.dispatchEvent(clickEvent);
|
||||
|
||||
const plotOptionsProperties = browseOptionsEl.querySelectorAll('.js-plot-options-browse-properties .grid-row');
|
||||
expect(plotOptionsProperties.length).toEqual(6);
|
||||
expect(plotOptionsProperties.length).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -904,7 +835,7 @@ describe("the plugin", function () {
|
||||
expandControl.dispatchEvent(clickEvent);
|
||||
|
||||
const plotOptionsProperties = editOptionsEl.querySelectorAll(".js-plot-options-edit-properties .grid-row");
|
||||
expect(plotOptionsProperties.length).toEqual(7);
|
||||
expect(plotOptionsProperties.length).toEqual(6);
|
||||
});
|
||||
|
||||
it('shows yKeyOptions', () => {
|
||||
|
||||
@@ -78,7 +78,7 @@ export default class RemoveAction {
|
||||
.map(object => this.openmct.objects.makeKeyString(object.identifier))
|
||||
.join("/");
|
||||
|
||||
this.openmct.router.navigate('#/browse/' + urlPath);
|
||||
window.location.href = '#/browse/' + urlPath;
|
||||
}
|
||||
|
||||
removeFromComposition(parent, child) {
|
||||
|
||||
@@ -72,7 +72,7 @@ describe("The Remove Action plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
|
||||
@@ -65,6 +65,11 @@
|
||||
<script>
|
||||
import ObjectView from '../../../ui/components/ObjectView.vue';
|
||||
import RemoveAction from '../../remove/RemoveAction.js';
|
||||
import {
|
||||
getSearchParam,
|
||||
setSearchParam,
|
||||
deleteSearchParam
|
||||
} from 'utils/openmctLocation';
|
||||
|
||||
const unknownObjectType = {
|
||||
definition: {
|
||||
@@ -110,7 +115,7 @@ export default {
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.onReorder);
|
||||
this.composition.load().then(() => {
|
||||
let currentTabIndexFromURL = this.openmct.router.getSearchParam(this.searchTabKey);
|
||||
let currentTabIndexFromURL = getSearchParam(this.searchTabKey);
|
||||
let currentTabIndexFromDomainObject = this.internalDomainObject.currentTabIndex;
|
||||
|
||||
if (currentTabIndexFromURL !== null) {
|
||||
@@ -124,8 +129,6 @@ export default {
|
||||
|
||||
this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
||||
|
||||
this.openmct.router.on('change:params', this.updateCurrentTab.bind(this));
|
||||
|
||||
this.RemoveAction = new RemoveAction(this.openmct);
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
@@ -145,8 +148,6 @@ export default {
|
||||
this.unsubscribe();
|
||||
this.clearCurrentTabIndexFromURL();
|
||||
|
||||
this.openmct.router.off('change:params', this.updateCurrentTab.bind(this));
|
||||
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
},
|
||||
@@ -284,15 +285,15 @@ export default {
|
||||
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
|
||||
},
|
||||
storeCurrentTabIndexInURL(index) {
|
||||
let currentTabIndexInURL = this.openmct.router.getSearchParam(this.searchTabKey);
|
||||
let currentTabIndexInURL = getSearchParam(this.searchTabKey);
|
||||
|
||||
if (index !== currentTabIndexInURL) {
|
||||
this.openmct.router.setSearchParam(this.searchTabKey, index);
|
||||
setSearchParam(this.searchTabKey, index);
|
||||
this.currentTabIndex = index;
|
||||
}
|
||||
},
|
||||
clearCurrentTabIndexFromURL() {
|
||||
this.openmct.router.deleteSearchParam(this.searchTabKey);
|
||||
deleteSearchParam(this.searchTabKey);
|
||||
},
|
||||
updateStatus(keyString, status) {
|
||||
let tabPos = this.tabsList.findIndex((tab) => {
|
||||
@@ -308,19 +309,6 @@ export default {
|
||||
} else {
|
||||
return this.loadedTabs[tab.keyString];
|
||||
}
|
||||
},
|
||||
updateCurrentTab(newParams, oldParams, changedParams) {
|
||||
const tabIndex = changedParams[this.searchTabKey];
|
||||
if (!tabIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentTabIndex === parseInt(tabIndex, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentTabIndex = tabIndex;
|
||||
this.currentTab = this.tabsList[tabIndex];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,11 +82,6 @@ describe("the plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
|
||||
@@ -83,9 +83,9 @@
|
||||
@update="timePopUpdate"
|
||||
/>
|
||||
<button
|
||||
ref="startOffset"
|
||||
class="c-button c-conductor__delta-button"
|
||||
@click="showTimePopupStart"
|
||||
class="c-button c-conductor__delta-button"
|
||||
ref="startOffset"
|
||||
>
|
||||
{{ offsets.start }}
|
||||
</button>
|
||||
@@ -135,7 +135,7 @@
|
||||
class="c-button c-conductor__delta-button"
|
||||
@click="showTimePopupEnd"
|
||||
>
|
||||
{{ offsets.end }}
|
||||
{{offsets.end}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ describe("The UTC Time System", () => {
|
||||
it("can be set to be the main time system", () => {
|
||||
openmct.time.timeSystem(UTC_SYSTEM_AND_FORMAT_KEY, {
|
||||
start: 0,
|
||||
end: 1
|
||||
end: 4
|
||||
});
|
||||
|
||||
expect(openmct.time.timeSystem().key).toBe(UTC_SYSTEM_AND_FORMAT_KEY);
|
||||
|
||||
@@ -362,7 +362,7 @@ $legendTableHeadBg: $colorTabHeaderBg;
|
||||
|
||||
// Tree
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: rgba(#fff, 0.1);
|
||||
$colorItemTreeHoverBg: rgba(#fff, 0.03);
|
||||
$colorItemTreeHoverFg: #fff;
|
||||
$colorItemTreeIcon: $colorKey; // Used
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
|
||||
|
||||
@@ -212,7 +212,6 @@ $glyph-icon-3-dots: '\ea37';
|
||||
$glyph-icon-grid-on: '\ea38';
|
||||
$glyph-icon-grid-off: '\ea39';
|
||||
$glyph-icon-camera: '\ea3a';
|
||||
$glyph-icon-folders-collapse: '\ea3b';
|
||||
$glyph-icon-activity: '\eb00';
|
||||
$glyph-icon-activity-mode: '\eb01';
|
||||
$glyph-icon-autoflow-tabular: '\eb02';
|
||||
|
||||
@@ -144,7 +144,6 @@
|
||||
.icon-grid-on { @include glyphBefore($glyph-icon-grid-on); }
|
||||
.icon-grid-off { @include glyphBefore($glyph-icon-grid-off); }
|
||||
.icon-camera { @include glyphBefore($glyph-icon-camera); }
|
||||
.icon-folders-collapse { @include glyphBefore($glyph-icon-folders-collapse); }
|
||||
.icon-activity { @include glyphBefore($glyph-icon-activity); }
|
||||
.icon-activity-mode { @include glyphBefore($glyph-icon-activity-mode); }
|
||||
.icon-autoflow-tabular { @include glyphBefore($glyph-icon-autoflow-tabular); }
|
||||
|
||||
@@ -531,7 +531,7 @@ mct-plot {
|
||||
}
|
||||
|
||||
/***************** GENERAL STYLES, ALL STATES */
|
||||
.plot-legend-item, .plot-series-limit-label {
|
||||
.plot-legend-item {
|
||||
// General styles for legend items, both expanded and collapsed legend states
|
||||
> * + * {
|
||||
margin-left: $interiorMarginSm;
|
||||
|
||||
@@ -70,10 +70,6 @@
|
||||
&.is-limit--lwr:before { content: $glyph-icon-arrow-down !important; }
|
||||
}
|
||||
|
||||
@mixin andLine {
|
||||
&.is-limit--line:before { content: '' !important; }
|
||||
}
|
||||
|
||||
@mixin uIndicator($bg, $fg, $glyph) {
|
||||
background: $bg;
|
||||
color: $fg;
|
||||
@@ -104,14 +100,12 @@
|
||||
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg, true);
|
||||
@include statusIcon($colorLimitYellowIc, $glyph-icon-alert-rect);
|
||||
@include andUprLwr();
|
||||
@include andLine();
|
||||
}
|
||||
|
||||
&.is-limit--red {
|
||||
@include statusStyle($colorLimitRedBg, $colorLimitRedFg, true);
|
||||
@include statusIcon($colorLimitRedIc, $glyph-icon-alert-triangle);
|
||||
@include andUprLwr();
|
||||
@include andLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"metadata": {
|
||||
"name": "Open MCT Symbols 16px",
|
||||
"lastOpened": 0,
|
||||
"created": 1621648023886
|
||||
"created": 1602779919972
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@@ -847,21 +847,13 @@
|
||||
"code": 59962,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 196,
|
||||
"id": 168,
|
||||
"name": "icon-folders-collapse",
|
||||
"prevSize": 24,
|
||||
"code": 59963,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 144,
|
||||
"id": 97,
|
||||
"name": "icon-activity",
|
||||
"prevSize": 24,
|
||||
"code": 60160,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 104,
|
||||
@@ -869,7 +861,7 @@
|
||||
"name": "icon-activity-mode",
|
||||
"prevSize": 24,
|
||||
"code": 60161,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 137,
|
||||
@@ -877,7 +869,7 @@
|
||||
"name": "icon-autoflow-tabular",
|
||||
"prevSize": 24,
|
||||
"code": 60162,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 115,
|
||||
@@ -885,7 +877,7 @@
|
||||
"name": "icon-clock",
|
||||
"prevSize": 24,
|
||||
"code": 60163,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 2,
|
||||
@@ -893,7 +885,7 @@
|
||||
"name": "icon-database",
|
||||
"prevSize": 24,
|
||||
"code": 60164,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 3,
|
||||
@@ -901,7 +893,7 @@
|
||||
"name": "icon-database-query",
|
||||
"prevSize": 24,
|
||||
"code": 60165,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 67,
|
||||
@@ -909,7 +901,7 @@
|
||||
"name": "icon-dataset",
|
||||
"prevSize": 24,
|
||||
"code": 60166,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 59,
|
||||
@@ -917,7 +909,7 @@
|
||||
"name": "icon-datatable",
|
||||
"prevSize": 24,
|
||||
"code": 60167,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 136,
|
||||
@@ -925,7 +917,7 @@
|
||||
"name": "icon-dictionary",
|
||||
"prevSize": 24,
|
||||
"code": 60168,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 51,
|
||||
@@ -933,7 +925,7 @@
|
||||
"name": "icon-folder",
|
||||
"prevSize": 24,
|
||||
"code": 60169,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 147,
|
||||
@@ -941,7 +933,7 @@
|
||||
"name": "icon-image",
|
||||
"prevSize": 24,
|
||||
"code": 60170,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 4,
|
||||
@@ -949,7 +941,7 @@
|
||||
"name": "icon-layout",
|
||||
"prevSize": 24,
|
||||
"code": 60171,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 24,
|
||||
@@ -957,7 +949,7 @@
|
||||
"name": "icon-object",
|
||||
"prevSize": 24,
|
||||
"code": 60172,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 52,
|
||||
@@ -965,7 +957,7 @@
|
||||
"name": "icon-object-unknown",
|
||||
"prevSize": 24,
|
||||
"code": 60173,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 105,
|
||||
@@ -973,7 +965,7 @@
|
||||
"name": "icon-packet",
|
||||
"prevSize": 24,
|
||||
"code": 60174,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 126,
|
||||
@@ -981,7 +973,7 @@
|
||||
"name": "icon-page",
|
||||
"prevSize": 24,
|
||||
"code": 60175,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 130,
|
||||
@@ -989,7 +981,7 @@
|
||||
"name": "icon-plot-overlay",
|
||||
"prevSize": 24,
|
||||
"code": 60176,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 80,
|
||||
@@ -997,7 +989,7 @@
|
||||
"name": "icon-plot-stacked",
|
||||
"prevSize": 24,
|
||||
"code": 60177,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 134,
|
||||
@@ -1005,7 +997,7 @@
|
||||
"name": "icon-session",
|
||||
"prevSize": 24,
|
||||
"code": 60178,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 109,
|
||||
@@ -1013,7 +1005,7 @@
|
||||
"name": "icon-tabular",
|
||||
"prevSize": 24,
|
||||
"code": 60179,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 107,
|
||||
@@ -1021,7 +1013,7 @@
|
||||
"name": "icon-tabular-lad",
|
||||
"prevSize": 24,
|
||||
"code": 60180,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 106,
|
||||
@@ -1029,7 +1021,7 @@
|
||||
"name": "icon-tabular-lad-set",
|
||||
"prevSize": 24,
|
||||
"code": 60181,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 70,
|
||||
@@ -1037,7 +1029,7 @@
|
||||
"name": "icon-tabular-realtime",
|
||||
"prevSize": 24,
|
||||
"code": 60182,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 60,
|
||||
@@ -1045,7 +1037,7 @@
|
||||
"name": "icon-tabular-scrolling",
|
||||
"prevSize": 24,
|
||||
"code": 60183,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 131,
|
||||
@@ -1053,7 +1045,7 @@
|
||||
"name": "icon-telemetry",
|
||||
"prevSize": 24,
|
||||
"code": 60184,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 108,
|
||||
@@ -1061,7 +1053,7 @@
|
||||
"name": "icon-timeline",
|
||||
"prevSize": 24,
|
||||
"code": 60185,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 81,
|
||||
@@ -1069,7 +1061,7 @@
|
||||
"name": "icon-timer",
|
||||
"prevSize": 24,
|
||||
"code": 60186,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 69,
|
||||
@@ -1077,7 +1069,7 @@
|
||||
"name": "icon-topic",
|
||||
"prevSize": 24,
|
||||
"code": 60187,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 79,
|
||||
@@ -1085,7 +1077,7 @@
|
||||
"name": "icon-box-with-dashed-lines-v2",
|
||||
"prevSize": 24,
|
||||
"code": 60188,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 90,
|
||||
@@ -1093,7 +1085,7 @@
|
||||
"name": "icon-summary-widget",
|
||||
"prevSize": 24,
|
||||
"code": 60189,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 92,
|
||||
@@ -1101,7 +1093,7 @@
|
||||
"name": "icon-notebook",
|
||||
"prevSize": 24,
|
||||
"code": 60190,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 168,
|
||||
@@ -1109,7 +1101,7 @@
|
||||
"name": "icon-tabs-view",
|
||||
"prevSize": 24,
|
||||
"code": 60191,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 117,
|
||||
@@ -1117,7 +1109,7 @@
|
||||
"name": "icon-flexible-layout",
|
||||
"prevSize": 24,
|
||||
"code": 60192,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 166,
|
||||
@@ -1125,7 +1117,7 @@
|
||||
"name": "icon-generator-sine",
|
||||
"prevSize": 24,
|
||||
"code": 60193,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 167,
|
||||
@@ -1133,7 +1125,7 @@
|
||||
"name": "icon-generator-event",
|
||||
"prevSize": 24,
|
||||
"code": 60194,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 165,
|
||||
@@ -1141,7 +1133,7 @@
|
||||
"name": "icon-gauge-v2",
|
||||
"prevSize": 24,
|
||||
"code": 60195,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 170,
|
||||
@@ -1149,7 +1141,7 @@
|
||||
"name": "icon-spectra",
|
||||
"prevSize": 24,
|
||||
"code": 60196,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 171,
|
||||
@@ -1157,7 +1149,7 @@
|
||||
"name": "icon-telemetry-spectra",
|
||||
"prevSize": 24,
|
||||
"code": 60197,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 172,
|
||||
@@ -1165,7 +1157,7 @@
|
||||
"name": "icon-pushbutton",
|
||||
"prevSize": 24,
|
||||
"code": 60198,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 174,
|
||||
@@ -1173,7 +1165,7 @@
|
||||
"name": "icon-conditional",
|
||||
"prevSize": 24,
|
||||
"code": 60199,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 178,
|
||||
@@ -1181,7 +1173,7 @@
|
||||
"name": "icon-condition-widget",
|
||||
"prevSize": 24,
|
||||
"code": 60200,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 180,
|
||||
@@ -1189,7 +1181,7 @@
|
||||
"name": "icon-alphanumeric",
|
||||
"prevSize": 24,
|
||||
"code": 60201,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 183,
|
||||
@@ -1197,7 +1189,7 @@
|
||||
"name": "icon-image-telemetry",
|
||||
"prevSize": 24,
|
||||
"code": 60202,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"id": 0,
|
||||
@@ -3001,29 +2993,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 168,
|
||||
"paths": [
|
||||
"M896 320v448c-0.215 70.606-57.394 127.785-127.979 128l-0.021 0h-576c0.215 70.606 57.394 127.785 127.979 128l0.021 0h576c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-448c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0z",
|
||||
"M832 704v-448c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0h-192l-101.5-82.74c-24.88-24.9-74.040-45.26-109.24-45.26h-237.26c-35.305 0.102-63.898 28.695-64 63.99l-0 0.010v640c0.215 70.606 57.394 127.785 127.979 128l0.021 0h576c70.606-0.215 127.785-57.394 128-127.979l0-0.021zM128 644v-516l256 260z"
|
||||
],
|
||||
"attrs": [
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-folders-collapse"
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"colorPermutations": {
|
||||
"12552552551": [
|
||||
{},
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 97,
|
||||
"paths": [
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
<glyph unicode="" glyph-name="icon-grid-on" d="M1024 448v128h-256v256h-128v-256h-256v256h-128v-256h-256v-128h256v-256h-256v-128h256v-256h128v256h256v-256h128v256h256v128h-256v256zM640 192h-256v256h256z" />
|
||||
<glyph unicode="" glyph-name="icon-grid-off" d="M256 280.6l128 157.6v9.8h8l104 128h-112v256h-128v-256h-256v-128h256v-167.4zM184 192h-184v-128h80l104 128zM768 359.4l-128-157.6v-9.8h-8l-104-128h112v-256h128v256h256v128h-256v167.4zM840 448h184v128h-80l-104-128zM832 832l-832-1024h192l832 1024h-192z" />
|
||||
<glyph unicode="" glyph-name="icon-camera" d="M896 576h-128l-128 256h-256l-128-256h-128c-70.601-0.227-127.773-57.399-128-127.978v-512.022c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v512.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022zM512-32c-141.385 0-256 114.615-256 256s114.615 256 256 256c141.385 0 256-114.615 256-256v0c0-141.385-114.615-256-256-256v0z" />
|
||||
<glyph unicode="" glyph-name="icon-folders-collapse" d="M896 512v-448c-0.215-70.606-57.394-127.785-127.979-128h-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v448.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM832 128v448c-0.215 70.606-57.394 127.785-127.979 128h-192.021l-101.5 82.74c-24.88 24.9-74.040 45.26-109.24 45.26h-237.26c-35.305-0.102-63.898-28.695-64-63.99v-640.010c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v0.021zM128 188v516l256-260z" />
|
||||
<glyph unicode="" glyph-name="icon-activity" d="M576 768h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
|
||||
<glyph unicode="" glyph-name="icon-activity-mode" d="M512 832c-214.8 0-398.8-132.4-474.8-320h90.8c56.8 0 108-24.8 143-64h241l-192 192h256l320-320-320-320h-256l192 192h-241c-35-39.2-86.2-64-143-64h-90.8c76-187.6 259.8-320 474.8-320 282.8 0 512 229.2 512 512s-229.2 512-512 512z" />
|
||||
<glyph unicode="" glyph-name="icon-autoflow-tabular" d="M192 832c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 832h256v-1024h-256v1024zM832 832h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 58 KiB |
Binary file not shown.
Binary file not shown.
@@ -75,7 +75,7 @@ export default {
|
||||
event.preventDefault();
|
||||
this.preview();
|
||||
} else {
|
||||
this.openmct.router.navigate(this.objectLink);
|
||||
window.location.assign(this.objectLink);
|
||||
}
|
||||
},
|
||||
preview() {
|
||||
|
||||
@@ -180,16 +180,16 @@ export default {
|
||||
},
|
||||
hasParent() {
|
||||
return this.domainObject !== PLACEHOLDER_OBJECT
|
||||
&& this.parentUrl !== '/browse';
|
||||
&& this.parentUrl !== '#/browse';
|
||||
},
|
||||
parentUrl() {
|
||||
const objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
const hash = this.openmct.router.getCurrentLocation().path;
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
let hash = window.location.hash;
|
||||
|
||||
return hash.slice(0, hash.lastIndexOf('/' + objectKeyString));
|
||||
},
|
||||
type() {
|
||||
const objectType = this.openmct.types.get(this.domainObject.type);
|
||||
let objectType = this.openmct.types.get(this.domainObject.type);
|
||||
if (!objectType) {
|
||||
return {};
|
||||
}
|
||||
@@ -336,7 +336,7 @@ export default {
|
||||
});
|
||||
},
|
||||
goToParent() {
|
||||
this.openmct.router.navigate(this.parentUrl);
|
||||
window.location.hash = this.parentUrl;
|
||||
},
|
||||
updateActionItems(actionItems) {
|
||||
this.statusBarItems = this.actionCollection.getStatusBarActions();
|
||||
|
||||
@@ -54,13 +54,6 @@
|
||||
label="Browse"
|
||||
collapsable
|
||||
>
|
||||
<button
|
||||
slot="controls"
|
||||
class="c-icon-button l-shell__reset-tree-button icon-folders-collapse"
|
||||
title="Collapse all tree items"
|
||||
@click="handleTreeReset"
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
slot="controls"
|
||||
class="c-icon-button l-shell__sync-tree-button icon-target"
|
||||
@@ -70,7 +63,6 @@
|
||||
</button>
|
||||
<mct-tree
|
||||
:sync-tree-navigation="triggerSync"
|
||||
:reset-tree-navigation="triggerReset"
|
||||
class="l-shell__tree"
|
||||
/>
|
||||
</pane>
|
||||
@@ -156,7 +148,6 @@ export default {
|
||||
hasToolbar: false,
|
||||
actionCollection: undefined,
|
||||
triggerSync: false,
|
||||
triggerReset: false,
|
||||
headExpanded
|
||||
};
|
||||
},
|
||||
@@ -237,9 +228,6 @@ export default {
|
||||
},
|
||||
handleSyncTreeNavigation() {
|
||||
this.triggerSync = !this.triggerSync;
|
||||
},
|
||||
handleTreeReset() {
|
||||
this.triggerReset = !this.triggerReset;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,14 +100,8 @@
|
||||
&__pane-tree {
|
||||
background: linear-gradient(90deg, transparent 70%, rgba(black, 0.2) 99%, rgba(black, 0.3));
|
||||
|
||||
.l-pane__header {
|
||||
// Hide all buttons except the collapse button
|
||||
> :not(.l-pane__collapse-button) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
[class*="expand-button"] {
|
||||
[class*="expand-button"],
|
||||
[class*="sync-tree-button"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__scrollable {
|
||||
&__scrollable-children {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
@include userSelectNone();
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: $interiorMarginSm;
|
||||
|
||||
.icon-arrow-nav-to-parent {
|
||||
visibility: hidden;
|
||||
@@ -91,7 +90,7 @@
|
||||
color: $colorItemTreeIcon;
|
||||
}
|
||||
|
||||
@include hover {
|
||||
&:hover {
|
||||
background: $colorItemTreeHoverBg;
|
||||
filter: $filterHov;
|
||||
}
|
||||
@@ -125,16 +124,14 @@
|
||||
}
|
||||
|
||||
&__item {
|
||||
[class*="view-control"] {
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-left: ceil($interiorMarginSm / 2);
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
||||
@include hover {
|
||||
background: $colorItemTreeHoverBg;
|
||||
@include desktop {
|
||||
&:hover {
|
||||
background: $colorItemTreeHoverBg;
|
||||
}
|
||||
}
|
||||
|
||||
// Object labels in trees
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div
|
||||
:style="treeItemStyles"
|
||||
:style="{
|
||||
'top': virtualScroll ? itemTop : 'auto',
|
||||
'position': virtualScroll ? 'absolute' : 'relative'
|
||||
}"
|
||||
class="c-tree__item-h"
|
||||
>
|
||||
<div
|
||||
@@ -14,23 +17,28 @@
|
||||
@contextmenu.capture="handleContextMenu"
|
||||
>
|
||||
<view-control
|
||||
ref="navigate"
|
||||
ref="navUp"
|
||||
v-model="expanded"
|
||||
class="c-tree__item__view-control"
|
||||
:value="isOpen || isLoading"
|
||||
:enabled="!activeSearch && hasComposition"
|
||||
@input="navigationClick()"
|
||||
:control-class="'icon-arrow-nav-to-parent'"
|
||||
:enabled="showUp"
|
||||
@input="resetTreeHere"
|
||||
/>
|
||||
<object-label
|
||||
ref="objectLabel"
|
||||
:domain-object="node.object"
|
||||
:object-path="node.objectPath"
|
||||
:navigate-to-path="navigationPath"
|
||||
:style="{ paddingLeft: leftOffset }"
|
||||
@context-click-active="setContextClickActive"
|
||||
/>
|
||||
<span
|
||||
v-if="isLoading"
|
||||
class="loading"
|
||||
></span>
|
||||
<view-control
|
||||
ref="navDown"
|
||||
v-model="expanded"
|
||||
class="c-tree__item__view-control"
|
||||
:control-class="'c-nav__down'"
|
||||
:enabled="hasComposition && showDown"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -51,14 +59,18 @@ export default {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
activeSearch: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
leftOffset: {
|
||||
type: String,
|
||||
default: '0px'
|
||||
},
|
||||
showUp: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showDown: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
itemIndex: {
|
||||
type: Number,
|
||||
required: false,
|
||||
@@ -74,13 +86,9 @@ export default {
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
openItems: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
loadingItems: {
|
||||
type: Object,
|
||||
required: true
|
||||
virtualScroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -89,6 +97,7 @@ export default {
|
||||
return {
|
||||
hasComposition: false,
|
||||
navigated: this.isNavigated(),
|
||||
expanded: false,
|
||||
contextClickActive: false
|
||||
};
|
||||
},
|
||||
@@ -104,44 +113,37 @@ export default {
|
||||
|
||||
return parentKeyString !== this.node.object.location;
|
||||
},
|
||||
isLoading() {
|
||||
return Boolean(this.loadingItems[this.navigationPath]);
|
||||
},
|
||||
isOpen() {
|
||||
return this.openItems.includes(this.navigationPath);
|
||||
},
|
||||
treeItemStyles() {
|
||||
let itemTop = (this.itemOffset + this.itemIndex) * this.itemHeight + 'px';
|
||||
|
||||
return {
|
||||
'top': itemTop,
|
||||
'position': 'absolute',
|
||||
'padding-left': this.leftOffset
|
||||
};
|
||||
itemTop() {
|
||||
return (this.itemOffset + this.itemIndex) * this.itemHeight + 'px';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
expanded() {
|
||||
this.$emit('expanded', this.domainObject);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.domainObject = this.node.object;
|
||||
let objectComposition = this.openmct.composition.get(this.node.object);
|
||||
|
||||
if (this.openmct.composition.get(this.domainObject)) {
|
||||
this.domainObject = this.node.object;
|
||||
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
|
||||
this.domainObject = newObject;
|
||||
});
|
||||
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
if (objectComposition) {
|
||||
this.hasComposition = true;
|
||||
}
|
||||
|
||||
this.openmct.router.on('change:path', this.highlightIfNavigated);
|
||||
|
||||
this.$emit('tree-item-mounted', this.navigationPath);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.router.off('change:path', this.highlightIfNavigated);
|
||||
this.$emit('tree-item-destoyed', this.navigationPath);
|
||||
},
|
||||
methods: {
|
||||
navigationClick() {
|
||||
this.$emit('navigation-click', this.isOpen || this.isLoading ? 'close' : 'open');
|
||||
},
|
||||
handleClick(event) {
|
||||
// skip for navigation, let viewControl handle click
|
||||
if (this.$refs.navigate.$el === event.target) {
|
||||
if ([this.$refs.navUp.$el, this.$refs.navDown.$el].includes(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,6 +160,9 @@ export default {
|
||||
highlightIfNavigated() {
|
||||
this.navigated = this.isNavigated();
|
||||
},
|
||||
resetTreeHere() {
|
||||
this.$emit('resetTree', this.node);
|
||||
},
|
||||
setContextClickActive(active) {
|
||||
this.contextClickActive = active;
|
||||
}
|
||||
|
||||
@@ -23,382 +23,7 @@
|
||||
|
||||
const LocationBar = require('location-bar');
|
||||
const EventEmitter = require('EventEmitter');
|
||||
const _ = require('lodash');
|
||||
|
||||
class ApplicationRouter extends EventEmitter {
|
||||
/**
|
||||
* events
|
||||
* change:params -> notify listeners w/ new, old, and changed.
|
||||
* change:path -> notify listeners w/ new, old paths.
|
||||
*
|
||||
* methods:
|
||||
* update(path, params) -> updates path and params at the same time. Only
|
||||
* updates specified params, other params are not modified.
|
||||
* updateParams(newParams) -> update only specified params, leaving rest
|
||||
* intact. Does not modify path.
|
||||
* updatePath(path);
|
||||
|
||||
* route(path, handler);
|
||||
* start(); Start routing.
|
||||
*/
|
||||
constructor(openmct) {
|
||||
super();
|
||||
|
||||
this.locationBar = new LocationBar();
|
||||
this.openmct = openmct;
|
||||
this.routes = [];
|
||||
this.started = false;
|
||||
|
||||
this.setHash = _.debounce(this.setHash.bind(this), 300);
|
||||
}
|
||||
|
||||
// Public Methods
|
||||
|
||||
destroy() {
|
||||
this.locationBar.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a given query parameter from current url
|
||||
*
|
||||
* @param {string} paramName name of searchParam to delete from current url searchParams
|
||||
*/
|
||||
deleteSearchParam(paramName) {
|
||||
let url = this.getHashRelativeURL();
|
||||
|
||||
url.searchParams.delete(paramName);
|
||||
this.setLocationFromUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* object for accessing all current search parameters
|
||||
*
|
||||
* @returns {URLSearchParams} A {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/entries|URLSearchParams}
|
||||
*/
|
||||
getAllSearchParams() {
|
||||
return this.getHashRelativeURL().searchParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniquely identifies a domain object.
|
||||
*
|
||||
* @typedef CurrentLocation
|
||||
* @property {URL} url current url location
|
||||
* @property {string} path current url location pathname
|
||||
* @property {string} getQueryString a function which returns url search query
|
||||
* @property {object} params object representing url searchParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* object for accessing current url location and search params
|
||||
*
|
||||
* @returns {CurrentLocation} A {@link CurrentLocation}
|
||||
*/
|
||||
getCurrentLocation() {
|
||||
return this.currentLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current location URL Object
|
||||
*
|
||||
* @returns {URL} current url location
|
||||
*/
|
||||
getHashRelativeURL() {
|
||||
return this.getCurrentLocation().url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current location URL Object searchParams
|
||||
*
|
||||
* @returns {object} object representing current url searchParams
|
||||
*/
|
||||
getParams() {
|
||||
return this.currentLocation.params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value of given param from current url searchParams
|
||||
*
|
||||
* @returns {string} value of paramName from current url searchParams
|
||||
*/
|
||||
getSearchParam(paramName) {
|
||||
return this.getAllSearchParams().get(paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navgate to given hash and update current location object and notify listeners about location change
|
||||
*
|
||||
* @param {string} paramName name of searchParam to get from current url searchParams
|
||||
*
|
||||
* @returns {string} value of paramName from current url searchParams
|
||||
*/
|
||||
navigate(hash) {
|
||||
this.handleLocationChange(hash.substring(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add routes listeners
|
||||
*
|
||||
* @param {string} matcher Regex to match value in url
|
||||
* @param {@function} callback function called when found match in url
|
||||
*/
|
||||
route(matcher, callback) {
|
||||
this.routes.push({
|
||||
matcher,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url hash using path and queryString
|
||||
*
|
||||
* @param {string} path path for url
|
||||
* @param {string} queryString queryString for url
|
||||
*/
|
||||
set(path, queryString) {
|
||||
this.setHash(`${path}?${queryString}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace all current search parameters with the ones defined in urlSearchParams
|
||||
*/
|
||||
setAllSearchParams() {
|
||||
this.setLocationFromUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* To force update url based on value in currentLocation object
|
||||
*/
|
||||
setLocationFromUrl() {
|
||||
this.updateTimeSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url hash using path
|
||||
*
|
||||
* @param {string} path path for url
|
||||
*/
|
||||
setPath(path) {
|
||||
this.handleLocationChange(path.substring(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update param value from current url searchParams
|
||||
*
|
||||
* @param {string} paramName param name from current url searchParams
|
||||
* @param {string} paramValue param value from current url searchParams
|
||||
*/
|
||||
setSearchParam(paramName, paramValue) {
|
||||
let url = this.getHashRelativeURL();
|
||||
|
||||
url.searchParams.set(paramName, paramValue);
|
||||
this.setLocationFromUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* start application routing, should be done after handlers are registered.
|
||||
*/
|
||||
start() {
|
||||
if (this.started) {
|
||||
throw new Error('Router already started!');
|
||||
}
|
||||
|
||||
this.started = true;
|
||||
|
||||
this.locationBar.onChange(p => this.hashChaged(p));
|
||||
this.locationBar.start({
|
||||
root: location.pathname
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url hash using path and searchParams object
|
||||
*
|
||||
* @param {string} path path for url
|
||||
* @param {string} params oject representing searchParams key/value
|
||||
*/
|
||||
update(path, params) {
|
||||
let searchParams = this.currentLocation.url.searchParams;
|
||||
for (let [key, value] of Object.entries(params)) {
|
||||
if (typeof value === 'undefined') {
|
||||
searchParams.delete(key);
|
||||
} else {
|
||||
searchParams.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
this.set(path, searchParams.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update route params. Takes an object of updates. New parameters
|
||||
*/
|
||||
updateParams(updateParams) {
|
||||
let searchParams = this.currentLocation.url.searchParams;
|
||||
Object.entries(updateParams).forEach(([key, value]) => {
|
||||
if (typeof value === 'undefined') {
|
||||
searchParams.delete(key);
|
||||
} else {
|
||||
searchParams.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
this.setQueryString(searchParams.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* To force update url based on value in currentLocation object
|
||||
*/
|
||||
updateTimeSettings() {
|
||||
const hash = `${this.currentLocation.path}?${this.currentLocation.getQueryString()}`;
|
||||
|
||||
this.setHash(hash);
|
||||
}
|
||||
|
||||
// Private Methods
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Create currentLocation object
|
||||
*
|
||||
* @param {string} pathString USVString representing relative URL.
|
||||
*
|
||||
* @returns {CurrentLocation} A {@link CurrentLocation}
|
||||
*/
|
||||
createLocation(pathString) {
|
||||
if (pathString[0] !== '/') {
|
||||
pathString = '/' + pathString;
|
||||
}
|
||||
|
||||
let url = new URL(
|
||||
pathString,
|
||||
`${location.protocol}//${location.host}${location.pathname}`
|
||||
);
|
||||
|
||||
return {
|
||||
url: url,
|
||||
path: url.pathname,
|
||||
getQueryString: () => url.search.replace(/^\?/, ''),
|
||||
params: paramsToObject(url.searchParams)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Compare new and old path and on change emit event 'change:path'
|
||||
*
|
||||
* @param {string} newPath new path of url
|
||||
* @param {string} oldPath old path of url
|
||||
*/
|
||||
doPathChange(newPath, oldPath) {
|
||||
if (newPath === oldPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
let route = this.routes.filter(r => r.matcher.test(newPath))[0];
|
||||
if (route) {
|
||||
route.callback(newPath, route.matcher.exec(newPath), this.currentLocation.params);
|
||||
}
|
||||
|
||||
this.emit('change:path', newPath, oldPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Compare new and old params and on change emit event 'change:params'
|
||||
*
|
||||
* @param {object} newParams new params of url
|
||||
* @param {object} oldParams old params of url
|
||||
*/
|
||||
doParamsChange(newParams, oldParams) {
|
||||
if (_.isEqual(newParams, oldParams)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let changedParams = {};
|
||||
Object.entries(newParams).forEach(([key, value]) => {
|
||||
if (value !== oldParams[key]) {
|
||||
changedParams[key] = value;
|
||||
}
|
||||
});
|
||||
Object.keys(oldParams).forEach(key => {
|
||||
if (!Object.prototype.hasOwnProperty.call(newParams, key)) {
|
||||
changedParams[key] = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
this.emit('change:params', newParams, oldParams, changedParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* On location change, update currentLocation object and emit appropriate events
|
||||
*
|
||||
* @param {string} pathString USVString representing relative URL.
|
||||
*/
|
||||
handleLocationChange(pathString) {
|
||||
let oldLocation = this.currentLocation;
|
||||
let newLocation = this.createLocation(pathString);
|
||||
|
||||
this.currentLocation = newLocation;
|
||||
|
||||
if (!oldLocation) {
|
||||
this.doPathChange(newLocation.path, null);
|
||||
this.doParamsChange(newLocation.params, {});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.doPathChange(
|
||||
newLocation.path,
|
||||
oldLocation.path
|
||||
);
|
||||
|
||||
this.doParamsChange(
|
||||
newLocation.params,
|
||||
oldLocation.params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* On hash changed, update currentLocation object and emit appropriate events
|
||||
*
|
||||
* @param {string} hash new hash for url
|
||||
*/
|
||||
hashChaged(hash) {
|
||||
this.emit('change:hash', hash);
|
||||
this.handleLocationChange(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Set new hash for url
|
||||
*
|
||||
* @param {string} hash new hash for url
|
||||
*/
|
||||
setHash(hash) {
|
||||
location.hash = '#' + hash.replace(/#/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Set queryString part of current url
|
||||
*
|
||||
* @param {string} queryString queryString part of url
|
||||
*/
|
||||
setQueryString(queryString) {
|
||||
this.handleLocationChange(`${this.currentLocation.path}?${queryString}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert searchParams into Object
|
||||
*
|
||||
* @param {URLSearchParams} searchParams queryString part of url
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function paramsToObject(searchParams) {
|
||||
let params = {};
|
||||
for (let [key, value] of searchParams.entries()) {
|
||||
@@ -416,4 +41,169 @@ function paramsToObject(searchParams) {
|
||||
return params;
|
||||
}
|
||||
|
||||
class ApplicationRouter extends EventEmitter {
|
||||
/**
|
||||
* events
|
||||
* change:params -> notify listeners w/ new, old, and changed.
|
||||
* change:path -> notify listeners w/ new, old paths.
|
||||
*
|
||||
* methods:
|
||||
* update(path, params) -> updates path and params at the same time. Only
|
||||
* updates specified params, other params are not modified.
|
||||
* updateParams(newParams) -> update only specified params, leaving rest
|
||||
* intact. Does not modify path.
|
||||
* updatePath(path);
|
||||
|
||||
* route(path, handler);
|
||||
* start(); Start routing.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.routes = [];
|
||||
this.started = false;
|
||||
this.locationBar = new LocationBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* start application routing, should be done after handlers are registered.
|
||||
*/
|
||||
start() {
|
||||
if (this.started) {
|
||||
throw new Error('Router already started!');
|
||||
}
|
||||
|
||||
this.started = true;
|
||||
|
||||
this.locationBar.onChange(p => this.handleLocationChange(p));
|
||||
this.locationBar.start({
|
||||
root: location.pathname
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.locationBar.stop();
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
handleLocationChange(pathString) {
|
||||
if (pathString[0] !== '/') {
|
||||
pathString = '/' + pathString;
|
||||
}
|
||||
|
||||
let url = new URL(
|
||||
pathString,
|
||||
`${location.protocol}//${location.host}${location.pathname}`
|
||||
);
|
||||
|
||||
let oldLocation = this.currentLocation;
|
||||
|
||||
let newLocation = {
|
||||
url: url,
|
||||
path: url.pathname,
|
||||
queryString: url.search.replace(/^\?/, ''),
|
||||
params: paramsToObject(url.searchParams)
|
||||
};
|
||||
|
||||
this.currentLocation = newLocation;
|
||||
|
||||
if (!oldLocation) {
|
||||
this.doPathChange(newLocation.path, null, newLocation);
|
||||
this.doParamsChange(newLocation.params, {}, newLocation);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldLocation.path !== newLocation.path) {
|
||||
this.doPathChange(
|
||||
newLocation.path,
|
||||
oldLocation.path,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
if (!_.isEqual(oldLocation.params, newLocation.params)) {
|
||||
this.doParamsChange(
|
||||
newLocation.params,
|
||||
oldLocation.params,
|
||||
newLocation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
doPathChange(newPath, oldPath, newLocation) {
|
||||
let route = this.routes.filter(r => r.matcher.test(newPath))[0];
|
||||
if (route) {
|
||||
route.callback(newPath, route.matcher.exec(newPath), this.currentLocation.params);
|
||||
}
|
||||
|
||||
this.emit('change:path', newPath, oldPath);
|
||||
}
|
||||
|
||||
doParamsChange(newParams, oldParams, newLocation) {
|
||||
let changedParams = {};
|
||||
Object.entries(newParams).forEach(([key, value]) => {
|
||||
if (value !== oldParams[key]) {
|
||||
changedParams[key] = value;
|
||||
}
|
||||
});
|
||||
Object.keys(oldParams).forEach(key => {
|
||||
if (!Object.prototype.hasOwnProperty.call(newParams, key)) {
|
||||
changedParams[key] = undefined;
|
||||
}
|
||||
});
|
||||
this.emit('change:params', newParams, oldParams, changedParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update route params. Takes an object of updates. New parameters
|
||||
*/
|
||||
updateParams(updateParams) {
|
||||
let searchParams = this.currentLocation.url.searchParams;
|
||||
Object.entries(updateParams).forEach(([key, value]) => {
|
||||
if (typeof value === 'undefined') {
|
||||
searchParams.delete(key);
|
||||
} else {
|
||||
searchParams.set(key, value);
|
||||
}
|
||||
});
|
||||
this.setQueryString(searchParams.toString());
|
||||
}
|
||||
|
||||
getParams() {
|
||||
return this.currentLocation.params;
|
||||
}
|
||||
|
||||
update(path, params) {
|
||||
let searchParams = this.currentLocation.url.searchParams;
|
||||
for (let [key, value] of Object.entries(params)) {
|
||||
if (typeof value === 'undefined') {
|
||||
searchParams.delete(key);
|
||||
} else {
|
||||
searchParams.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
this.set(path, searchParams.toString());
|
||||
}
|
||||
|
||||
set(path, queryString) {
|
||||
location.hash = `${path}?${queryString}`;
|
||||
}
|
||||
|
||||
setQueryString(queryString) {
|
||||
this.set(this.currentLocation.path, queryString);
|
||||
}
|
||||
|
||||
setPath(path) {
|
||||
this.set(path, this.currentLocation.queryString);
|
||||
}
|
||||
|
||||
route(matcher, callback) {
|
||||
this.routes.push({
|
||||
matcher,
|
||||
callback
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ApplicationRouter;
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
|
||||
let openmct;
|
||||
let element;
|
||||
let child;
|
||||
let appHolder;
|
||||
let resolveFunction;
|
||||
|
||||
let initialHash = '';
|
||||
|
||||
describe('Application router utility functions', () => {
|
||||
beforeAll(done => {
|
||||
appHolder = document.createElement('div');
|
||||
appHolder.style.width = '640px';
|
||||
appHolder.style.height = '480px';
|
||||
|
||||
openmct = createOpenMct();
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
openmct.install(openmct.plugins.LocalTimeSystem());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
|
||||
element = document.createElement('div');
|
||||
child = document.createElement('div');
|
||||
element.appendChild(child);
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.start(appHolder);
|
||||
|
||||
document.body.append(appHolder);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
openmct.router.setHash(initialHash);
|
||||
appHolder.remove();
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('has initial hash when loaded', (done) => {
|
||||
let success;
|
||||
resolveFunction = () => {
|
||||
openmct.router.setLocationFromUrl();
|
||||
success = window.location.hash !== null;
|
||||
if (success) {
|
||||
initialHash = window.location.hash;
|
||||
expect(success).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it('The setSearchParam function sets an individual search parameter in the window location hash', (done) => {
|
||||
let success;
|
||||
openmct.router.setSearchParam('testParam', 'testValue');
|
||||
resolveFunction = () => {
|
||||
success = window.location.hash.includes('testParam=testValue');
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it('The getSearchParam function returns the value of an individual search paramater in the window location hash', () => {
|
||||
expect(openmct.router.getSearchParam('testParam')).toBe('testValue');
|
||||
});
|
||||
|
||||
it('The deleteSearchParam function deletes an individual search paramater in the window location hash', (done) => {
|
||||
let success;
|
||||
openmct.router.deleteSearchParam('testParam');
|
||||
resolveFunction = () => {
|
||||
success = window.location.hash.includes('testParam=testValue') === false;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it('The setSearchParam function sets an individual search parameters in the window location hash', (done) => {
|
||||
let success;
|
||||
openmct.router.setSearchParam('testParam1', 'testValue1');
|
||||
openmct.router.setSearchParam('testParam2', 'testValue2');
|
||||
|
||||
resolveFunction = () => {
|
||||
const hasTestParam1 = window.location.hash.includes('testParam1=testValue1');
|
||||
const hasTestParam2 = window.location.hash.includes('testParam2=testValue2');
|
||||
success = hasTestParam1 && hasTestParam2;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it('The setAllSearchParams function replaces all search paramaters in the window location hash', (done) => {
|
||||
let success;
|
||||
|
||||
openmct.router.setSearchParam('testParam2', 'updatedtestValue2');
|
||||
openmct.router.setSearchParam('newTestParam3', 'newTestValue3');
|
||||
|
||||
resolveFunction = () => {
|
||||
const hasupdatedValueForTestParam2 = window.location.hash.includes('testParam2=updatedtestValue2');
|
||||
const hasNewTestParam3 = window.location.hash.includes('newTestParam3=newTestValue3');
|
||||
success = hasupdatedValueForTestParam2 && hasNewTestParam3;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it('The getAllSearchParams function returns the values of all search paramaters in the window location hash', () => {
|
||||
let searchParams = openmct.router.getAllSearchParams();
|
||||
expect(searchParams.get('testParam1')).toBe('testValue1');
|
||||
expect(searchParams.get('testParam2')).toBe('updatedtestValue2');
|
||||
expect(searchParams.get('newTestParam3')).toBe('newTestValue3');
|
||||
});
|
||||
});
|
||||
@@ -13,12 +13,13 @@ define([
|
||||
let mutable;
|
||||
|
||||
openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot);
|
||||
|
||||
openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => {
|
||||
isRoutingInProgress = true;
|
||||
let navigatePath = results[1];
|
||||
clearMutationListeners();
|
||||
|
||||
navigateToPath(navigatePath, params.view);
|
||||
onParamsChanged(null, null, params);
|
||||
});
|
||||
|
||||
openmct.router.on('change:params', onParamsChanged);
|
||||
@@ -132,21 +133,18 @@ define([
|
||||
}
|
||||
|
||||
function navigateToFirstChildOfRoot() {
|
||||
openmct.objects.get('ROOT')
|
||||
.then(rootObject => {
|
||||
openmct.composition.get(rootObject).load()
|
||||
.then(children => {
|
||||
let lastChild = children[children.length - 1];
|
||||
if (!lastChild) {
|
||||
console.error('Unable to navigate to anything. No root objects found.');
|
||||
} else {
|
||||
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
|
||||
openmct.router.setPath(`#/browse/${lastChildId}`);
|
||||
}
|
||||
})
|
||||
.catch(e => console.error(e));
|
||||
})
|
||||
.catch(e => console.error(e));
|
||||
openmct.objects.get('ROOT').then(rootObject => {
|
||||
openmct.composition.get(rootObject).load()
|
||||
.then(children => {
|
||||
let lastChild = children[children.length - 1];
|
||||
if (!lastChild) {
|
||||
console.error('Unable to navigate to anything. No root objects found.');
|
||||
} else {
|
||||
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
|
||||
openmct.router.setPath(`#/browse/${lastChildId}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clearMutationListeners() {
|
||||
|
||||
108
src/utils/openmctLocation.js
Normal file
108
src/utils/openmctLocation.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import objectUtils from '../api/objects/object-utils.js';
|
||||
|
||||
/**
|
||||
* Utility functions for getting and setting Open MCT search parameters and navigated object path.
|
||||
* Open MCT encodes application state into the "hash" of the url, making it awkward to use standard browser API such
|
||||
* as URL for modifying state in the URL. This wraps native API with some utility functions that operate only on the
|
||||
* hash section of the URL.
|
||||
*/
|
||||
|
||||
export function setSearchParam(paramName, paramValue) {
|
||||
let url = getHashRelativeURL();
|
||||
|
||||
url.searchParams.set(paramName, paramValue);
|
||||
setLocationFromUrl(url);
|
||||
}
|
||||
|
||||
export function deleteSearchParam(paramName) {
|
||||
let url = getHashRelativeURL();
|
||||
|
||||
url.searchParams.delete(paramName);
|
||||
setLocationFromUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace all current search parameters with the ones defined in urlSearchParams
|
||||
* @param {URLSearchParams} paramMap
|
||||
*/
|
||||
export function setAllSearchParams(newSearchParams) {
|
||||
let url = getHashRelativeURL();
|
||||
|
||||
Array.from(url.searchParams.keys()).forEach((key) => url.searchParams.delete(key));
|
||||
|
||||
Array.from(newSearchParams.keys()).forEach(key => {
|
||||
url.searchParams.set(key, newSearchParams.get(key));
|
||||
});
|
||||
|
||||
setLocationFromUrl(url);
|
||||
}
|
||||
|
||||
export function getSearchParam(paramName) {
|
||||
return getAllSearchParams().get(paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {URLSearchParams} A {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/entries|URLSearchParams}
|
||||
* object for accessing all current search parameters
|
||||
*/
|
||||
export function getAllSearchParams() {
|
||||
return getHashRelativeURL().searchParams;
|
||||
}
|
||||
|
||||
export function getObjectPath() {
|
||||
return getHashRelativeURL().pathname;
|
||||
}
|
||||
|
||||
export function setObjectPath(objectPath) {
|
||||
let objectPathString;
|
||||
let url = getHashRelativeURL();
|
||||
|
||||
if (objectPath instanceof Array) {
|
||||
if (objectPath.length > 0 && isDomainObject(objectPath[0])) {
|
||||
throw 'setObjectPath must be called with either a string, or an array of Domain Objects';
|
||||
}
|
||||
|
||||
objectPathString = objectPath.reduce((pathString, object) => {
|
||||
return `${pathString}/${objectUtils.makeKeyString(object.identifier)}`;
|
||||
}, '');
|
||||
} else {
|
||||
objectPathString = objectPath;
|
||||
}
|
||||
|
||||
url.pathname = objectPathString;
|
||||
setLocationFromUrl(url);
|
||||
}
|
||||
|
||||
function isDomainObject(potentialObject) {
|
||||
return potentialObject.identifier === undefined;
|
||||
}
|
||||
|
||||
function setLocationFromUrl(url) {
|
||||
window.location.hash = `${url.pathname}${url.search}`;
|
||||
}
|
||||
|
||||
function getHashRelativeURL() {
|
||||
return new URL(window.location.hash.substring(1), window.location.origin);
|
||||
}
|
||||
113
src/utils/openmctLocationSpec.js
Normal file
113
src/utils/openmctLocationSpec.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
import {
|
||||
setSearchParam,
|
||||
deleteSearchParam,
|
||||
getAllSearchParams,
|
||||
getSearchParam,
|
||||
setAllSearchParams,
|
||||
getObjectPath,
|
||||
setObjectPath
|
||||
} from './openmctLocation';
|
||||
|
||||
import {resetApplicationState} from 'utils/testing';
|
||||
|
||||
describe('the openmct location utility functions', () => {
|
||||
afterEach(() => resetApplicationState());
|
||||
|
||||
it('The setSearchParam function sets an individual search parameters in the window location hash', () => {
|
||||
setSearchParam('testParam', 'testValue');
|
||||
expect(window.location.hash.includes('testParam=testValue')).toBe(true);
|
||||
});
|
||||
|
||||
it('The deleteSearchParam function deletes an individual search paramater in the window location hash', () => {
|
||||
window.location.hash = '#/?testParam=testValue';
|
||||
deleteSearchParam('testParam');
|
||||
expect(window.location.hash.includes('testParam=testValue')).toBe(false);
|
||||
});
|
||||
|
||||
it('The getSearchParam function returns the value of an individual search paramater in the window location hash', () => {
|
||||
window.location.hash = '#/?testParam=testValue';
|
||||
expect(getSearchParam('testParam')).toBe('testValue');
|
||||
});
|
||||
|
||||
it('The getAllSearchParams function returns the values of all search paramaters in the window location hash', () => {
|
||||
window.location.hash = '#/?testParam1=testValue1&testParam2=testValue2&testParam3=testValue3';
|
||||
let searchParams = getAllSearchParams();
|
||||
expect(searchParams.get('testParam1')).toBe('testValue1');
|
||||
expect(searchParams.get('testParam2')).toBe('testValue2');
|
||||
expect(searchParams.get('testParam3')).toBe('testValue3');
|
||||
});
|
||||
|
||||
it('The setAllSearchParams function replaces all search paramaters in the window location hash', () => {
|
||||
window.location.hash = '#/?testParam1=testValue1&testParam2=testValue2&testParam3=testValue3';
|
||||
let searchParams = getAllSearchParams();
|
||||
searchParams.delete('testParam3');
|
||||
searchParams.set('testParam1', 'updatedTestValue1');
|
||||
searchParams.set('newTestParam4', 'newTestValue4');
|
||||
setAllSearchParams(searchParams);
|
||||
expect(window.location.hash).toBe('#/?testParam1=updatedTestValue1&testParam2=testValue2&newTestParam4=newTestValue4');
|
||||
});
|
||||
|
||||
it('The getObjectPath function returns the current object path', () => {
|
||||
window.location.hash = '#/some/object/path?someParameter=someValue';
|
||||
expect(getObjectPath()).toBe('/some/object/path');
|
||||
});
|
||||
|
||||
it('The setObjectPath function allows the object path to be set to a given string', () => {
|
||||
window.location.hash = '#/some/object/path?someParameter=someValue';
|
||||
setObjectPath('/some/other/object/path');
|
||||
expect(window.location.hash).toBe('#/some/other/object/path?someParameter=someValue');
|
||||
});
|
||||
|
||||
it('The setObjectPath function allows the object path to be set from an array of domain objects', () => {
|
||||
const OBJECT_PATH = [
|
||||
{
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'objectKey1'
|
||||
}
|
||||
},
|
||||
{
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'objectKey2'
|
||||
}
|
||||
},
|
||||
{
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'objectKey3'
|
||||
}
|
||||
}
|
||||
];
|
||||
window.location.hash = '#/some/object/path?someParameter=someValue';
|
||||
setObjectPath(OBJECT_PATH);
|
||||
expect(window.location.hash).toBe('#/namespace:objectKey1/namespace:objectKey2/namespace:objectKey3?someParameter=someValue');
|
||||
});
|
||||
|
||||
it('The setObjectPath function throws an error if called with anything other than a string or an array of domain objects', () => {
|
||||
expect(() => setObjectPath(["array", "of", "strings"])).toThrow();
|
||||
expect(() => setObjectPath([{}, {someKey: 'someValue'}])).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user