Compare commits
68 Commits
remove-ang
...
new-link-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7da48d493 | ||
|
|
4ba9bcb8f3 | ||
|
|
9fbacfd023 | ||
|
|
affa934766 | ||
|
|
f203806406 | ||
|
|
6540518daa | ||
|
|
730efb75ed | ||
|
|
a2aa0b8ab0 | ||
|
|
8a3864edf6 | ||
|
|
f1b748b46e | ||
|
|
a2a6d3020c | ||
|
|
6813f2083a | ||
|
|
a641996a3f | ||
|
|
3cae138b6a | ||
|
|
a44d8ab451 | ||
|
|
af4db1f799 | ||
|
|
38416b9434 | ||
|
|
d27d4aba16 | ||
|
|
354b590852 | ||
|
|
99427b4bed | ||
|
|
3b3b9368b5 | ||
|
|
a829a14463 | ||
|
|
f42e37930c | ||
|
|
aeb9dbeb33 | ||
|
|
f9393f80e0 | ||
|
|
28fe4688db | ||
|
|
50192b1aca | ||
|
|
c5e3838353 | ||
|
|
a66e77821f | ||
|
|
068d804a79 | ||
|
|
05558c02aa | ||
|
|
cc6c57398d | ||
|
|
a40867d544 | ||
|
|
dbed9262c0 | ||
|
|
43ac66233e | ||
|
|
04e85c176a | ||
|
|
8274c23129 | ||
|
|
5fafde5f23 | ||
|
|
80a6e7f719 | ||
|
|
2c13aeecce | ||
|
|
ac015c3e45 | ||
|
|
ae1a4bcc6a | ||
|
|
e1e0eeac56 | ||
|
|
c90dfb2a1f | ||
|
|
1dfa5e5b8c | ||
|
|
99896b72ea | ||
|
|
979ba77c8e | ||
|
|
aebb5df611 | ||
|
|
605eeff9d7 | ||
|
|
a83ee1f90f | ||
|
|
fe899cbcc8 | ||
|
|
633bac2ed5 | ||
|
|
dacec48aec | ||
|
|
3ca133c782 | ||
|
|
12416b8079 | ||
|
|
9920e67c83 | ||
|
|
0e80a5b8a0 | ||
|
|
05f9202fe4 | ||
|
|
0da35a44b0 | ||
|
|
2305cd2e49 | ||
|
|
b30b6bc94e | ||
|
|
564f254652 | ||
|
|
9fa71244ea | ||
|
|
8157cdc7e9 | ||
|
|
721bdd737a | ||
|
|
1b57999059 | ||
|
|
b7460cef41 | ||
|
|
46f7f6dd04 |
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<!--- 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
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!--- 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
Normal file
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
### 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?
|
||||||
3
API.md
3
API.md
@@ -423,13 +423,14 @@ attribute | type | flags | notes
|
|||||||
|
|
||||||
###### Value Hints
|
###### Value Hints
|
||||||
|
|
||||||
Each telemetry value description has an object defining hints. Keys in this this object represent the hint itself, and the value represents the weight of that hint. A lower weight means the hint has a higher priority. For example, multiple values could be hinted for use as the y-axis of a plot (raw, engineering), but the highest priority would be the default choice. Likewise, a table will use hints to determine the default order of columns.
|
Each telemetry value description has an object defining hints. Keys in this object represent the hint itself, and the value represents the weight of that hint. A lower weight means the hint has a higher priority. For example, multiple values could be hinted for use as the y-axis of a plot (raw, engineering), but the highest priority would be the default choice. Likewise, a table will use hints to determine the default order of columns.
|
||||||
|
|
||||||
Known hints:
|
Known hints:
|
||||||
|
|
||||||
* `domain`: Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
|
* `domain`: Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
|
||||||
* `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
|
* `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
|
||||||
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
|
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
|
||||||
|
* `imageDownloadName`: Indicates that the value may be interpreted as the name of the image file.
|
||||||
|
|
||||||
##### The Time Conductor and Telemetry
|
##### The Time Conductor and Telemetry
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ accept changes from external contributors.
|
|||||||
|
|
||||||
The short version:
|
The short version:
|
||||||
|
|
||||||
1. Write your contribution.
|
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)
|
||||||
2. Make sure your contribution meets code, test, and commit message
|
2. Make sure your contribution meets code, test, and commit message
|
||||||
standards as described below.
|
standards as described below.
|
||||||
3. Submit a pull request from a topic branch back to `master`. Include a check
|
3. Submit a pull request from a topic branch back to `master`. Include a check
|
||||||
@@ -18,6 +18,7 @@ The short version:
|
|||||||
for review.)
|
for review.)
|
||||||
4. Respond to any discussion. When the reviewer decides it's ready, they
|
4. Respond to any discussion. When the reviewer decides it's ready, they
|
||||||
will merge back `master` and fill out their own check list.
|
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
|
## Contribution Process
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ the pull request containing the reviewer checklist (from below) and complete
|
|||||||
the merge back to the master branch.
|
the merge back to the master branch.
|
||||||
|
|
||||||
Additionally:
|
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.
|
* 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 __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.
|
* 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__.
|
* 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.
|
* 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.
|
||||||
@@ -296,23 +297,12 @@ these standards.
|
|||||||
|
|
||||||
Issues are tracked at https://github.com/nasa/openmct/issues.
|
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):
|
Issue severity is categorized as follows (in ascending order):
|
||||||
|
|
||||||
* _Trivial_: Minimal impact on the usefulness and functionality of the
|
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
|
||||||
software; a "nice-to-have."
|
* _Medium_: Some impairment of use, but simple workarounds exist
|
||||||
* _(Unspecified)_: Major loss of functionality or impairment of use.
|
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
|
||||||
* _Critical_: Large-scale loss of functionality or impairment of use,
|
* _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.
|
||||||
such that remaining utility becomes marginal.
|
|
||||||
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
|
|
||||||
|
|
||||||
## Check Lists
|
## Check Lists
|
||||||
|
|
||||||
@@ -322,16 +312,19 @@ checklist).
|
|||||||
|
|
||||||
### Author Checklist
|
### Author Checklist
|
||||||
|
|
||||||
1. Changes address original issue?
|
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
|
||||||
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
|
### Reviewer Checklist
|
||||||
|
|
||||||
1. Changes appear to address issue?
|
* [ ] Changes appear to address issue?
|
||||||
2. Appropriate unit tests included?
|
* [ ] Appropriate unit tests included?
|
||||||
3. Code style and in-line documentation are appropriate?
|
* [ ] Code style and in-line documentation are appropriate?
|
||||||
4. Commit messages meet standards?
|
* [ ] Commit messages meet standards?
|
||||||
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
* [ ] 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.```
|
||||||
@@ -44,7 +44,7 @@ The clearest examples for developing Open MCT plugins are in the
|
|||||||
our documentation.
|
our documentation.
|
||||||
|
|
||||||
We want Open MCT to be as easy to use, install, run, and develop for as
|
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), 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/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).
|
||||||
|
|
||||||
## Building Applications With Open MCT
|
## 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
|
resources not needed for this effort should be used to begin work
|
||||||
for the subsequent sprint.
|
for the subsequent sprint.
|
||||||
|
|
||||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-------------------------------------:|
|
||||||
| __1__ | Sprint plan | Tag-up | | | |
|
| __1__ | Sprint plan | Tag-up | | | |
|
||||||
| __2__ | | Tag-up | | | Code freeze |
|
| __2__ | | Tag-up | | | Code freeze and sprint branch |
|
||||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship |
|
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship and merge sprint branch to master|
|
||||||
|
|
||||||
* If necessary.
|
* If necessary.
|
||||||
|
|
||||||
@@ -105,14 +105,20 @@ emphasis on testing.
|
|||||||
that team may begin work for that sprint during the
|
that team may begin work for that sprint during the
|
||||||
third week, since testing and blocker resolution is unlikely
|
third week, since testing and blocker resolution is unlikely
|
||||||
to require all available resources.
|
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.
|
* __Tag-up.__ Check in and status update among development team.
|
||||||
May amend plan for sprint as-needed.
|
May amend plan for sprint as-needed.
|
||||||
* __Code freeze.__ Any new work from this sprint
|
* __Code freeze.__ Any new work from this sprint
|
||||||
(features, bug fixes, enhancements) must be integrated by the
|
(features, bug fixes, enhancements) must be integrated by the
|
||||||
end of the second week of the sprint. After code freeze
|
end of the second week of the sprint. After code freeze, a sprint
|
||||||
(and until the end of the sprint) the only changes that should be
|
branch will be created (and until the end of the sprint) the only
|
||||||
merged into the master branch should directly address issues
|
changes that should be merged into the sprint branch should
|
||||||
needed to pass acceptance testing.
|
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.
|
||||||
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
|
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
|
||||||
Structured testing with predefined
|
Structured testing with predefined
|
||||||
success criteria. No release should ship without passing
|
success criteria. No release should ship without passing
|
||||||
@@ -126,8 +132,8 @@ emphasis on testing.
|
|||||||
* [__Testathon.__](testing/plan.md#user-testing)
|
* [__Testathon.__](testing/plan.md#user-testing)
|
||||||
Multi-user testing, involving as many users as
|
Multi-user testing, involving as many users as
|
||||||
is feasible, plus development team. Open-ended; should verify
|
is feasible, plus development team. Open-ended; should verify
|
||||||
completed work from this sprint, test exploratorily for
|
completed work from this sprint using the sprint branch, test
|
||||||
regressions, et cetera.
|
exploratorily for regressions, et cetera.
|
||||||
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
|
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
|
||||||
test to verify that the software remains
|
test to verify that the software remains
|
||||||
stable after running for longer durations. May include some
|
stable after running for longer durations. May include some
|
||||||
@@ -143,7 +149,7 @@ emphasis on testing.
|
|||||||
Subset of Pre-release Testing
|
Subset of Pre-release Testing
|
||||||
which should be performed before shipping at the end of any
|
which should be performed before shipping at the end of any
|
||||||
sprint. Time is allocated for a second round of
|
sprint. Time is allocated for a second round of
|
||||||
Pre-release Testing if the first round is not passed.
|
Pre-release Testing if the first round is not passed. Smoke tests collected from issues/PRs
|
||||||
* __Triage.__ Team reviews issues from acceptance testing and uses
|
* __Triage.__ Team reviews issues from acceptance testing and uses
|
||||||
success criteria to determine whether or not they should block
|
success criteria to determine whether or not they should block
|
||||||
release, then formulates a plan to address these issues before
|
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
|
Manual, non-rigorous testing of the software and/or specific features
|
||||||
of interest. Verifies that the software runs and that basic functionality
|
of interest. Verifies that the software runs and that basic functionality
|
||||||
is present.
|
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.
|
||||||
|
|
||||||
### Unit Testing
|
### Unit Testing
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ User testing will focus on the following activities:
|
|||||||
* General "trying to break things."
|
* General "trying to break things."
|
||||||
|
|
||||||
During user testing, users will
|
During user testing, users will
|
||||||
[report issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
[report issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||||
as they are encountered.
|
as they are encountered.
|
||||||
|
|
||||||
Desired outcomes of user testing are:
|
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?
|
at the start of the test? Is it as responsive?
|
||||||
|
|
||||||
Any defects or unexpected behavior identified during testing should be
|
Any defects or unexpected behavior identified during testing should be
|
||||||
[reported as issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
[reported as issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||||
and reviewed for severity.
|
and reviewed for severity.
|
||||||
|
|
||||||
## Test Performance
|
## Test Performance
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ should update (or delegate the task of updating) Open MCT version
|
|||||||
numbers by the following process:
|
numbers by the following process:
|
||||||
|
|
||||||
1. Update version number in `package.json`
|
1. Update version number in `package.json`
|
||||||
1. Create a new branch off the `master` branch.
|
1. Checkout branch created for the last sprint that has been successfully tested.
|
||||||
2. Remove `-SNAPSHOT` suffix from the version in `package.json`.
|
2. Remove a `-SNAPSHOT` suffix from the version in `package.json`.
|
||||||
3. Verify that resulting version number meets semantic versioning
|
3. Verify that resulting version number meets semantic versioning
|
||||||
requirements relative to previous stable version. Increment the
|
requirements relative to previous stable version. Increment the
|
||||||
version number if necessary.
|
version number if necessary.
|
||||||
|
|||||||
@@ -93,5 +93,36 @@ 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;
|
return SinewaveLimitProvider;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,11 +50,16 @@ define([
|
|||||||
const IMAGE_DELAY = 20000;
|
const IMAGE_DELAY = 20000;
|
||||||
|
|
||||||
function pointForTimestamp(timestamp, name) {
|
function pointForTimestamp(timestamp, name) {
|
||||||
|
const url = IMAGE_SAMPLES[Math.floor(timestamp / IMAGE_DELAY) % IMAGE_SAMPLES.length];
|
||||||
|
const urlItems = url.split('/');
|
||||||
|
const imageDownloadName = `example.imagery.${urlItems[urlItems.length - 1]}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: name,
|
name,
|
||||||
utc: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
|
utc: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
|
||||||
local: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
|
local: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
|
||||||
url: IMAGE_SAMPLES[Math.floor(timestamp / IMAGE_DELAY) % IMAGE_SAMPLES.length]
|
url,
|
||||||
|
imageDownloadName
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +144,14 @@ define([
|
|||||||
hints: {
|
hints: {
|
||||||
image: 1
|
image: 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Image Download Name',
|
||||||
|
key: 'imageDownloadName',
|
||||||
|
format: 'imageDownloadName',
|
||||||
|
hints: {
|
||||||
|
imageDownloadName: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,7 +88,6 @@
|
|||||||
openmct.install(openmct.plugins.ExampleImagery());
|
openmct.install(openmct.plugins.ExampleImagery());
|
||||||
openmct.install(openmct.plugins.PlanLayout());
|
openmct.install(openmct.plugins.PlanLayout());
|
||||||
openmct.install(openmct.plugins.Timeline());
|
openmct.install(openmct.plugins.Timeline());
|
||||||
openmct.install(openmct.plugins.PlotVue());
|
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
openmct.install(openmct.plugins.AutoflowView({
|
openmct.install(openmct.plugins.AutoflowView({
|
||||||
type: "telemetry.panel"
|
type: "telemetry.panel"
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ module.exports = (config) => {
|
|||||||
preserveDescribeNesting: true,
|
preserveDescribeNesting: true,
|
||||||
foldAll: false
|
foldAll: false
|
||||||
},
|
},
|
||||||
|
browserConsoleLogOptions: { level: "error", format: "%b %T: %m", terminal: true },
|
||||||
coverageIstanbulReporter: {
|
coverageIstanbulReporter: {
|
||||||
fixWebpackSourcePaths: true,
|
fixWebpackSourcePaths: true,
|
||||||
dir: process.env.CIRCLE_ARTIFACTS ?
|
dir: process.env.CIRCLE_ARTIFACTS ?
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "1.6.3-SNAPSHOT",
|
"version": "1.7.3-SNAPSHOT",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -78,7 +78,8 @@
|
|||||||
"zepto": "^1.2.0"
|
"zepto": "^1.2.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -rf ./dist",
|
"clean": "rm -rf ./dist /node_modules; rm package-lock.json",
|
||||||
|
"clean-test-lint": "npm run clean; npm install ; npm run test; npm run lint",
|
||||||
"start": "node app.js",
|
"start": "node app.js",
|
||||||
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||||
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<mct-container key="c-overlay__contents">
|
<mct-container key="c-overlay__contents">
|
||||||
<div class=c-overlay__top-bar">
|
<div class="c-overlay__top-bar">
|
||||||
<div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
|
<div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
|
||||||
<div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div>
|
<div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ define([
|
|||||||
"./src/controllers/EditObjectController",
|
"./src/controllers/EditObjectController",
|
||||||
"./src/actions/EditAndComposeAction",
|
"./src/actions/EditAndComposeAction",
|
||||||
"./src/actions/EditAction",
|
"./src/actions/EditAction",
|
||||||
"./src/actions/PropertiesAction",
|
|
||||||
"./src/actions/SaveAction",
|
"./src/actions/SaveAction",
|
||||||
"./src/actions/SaveAndStopEditingAction",
|
"./src/actions/SaveAndStopEditingAction",
|
||||||
"./src/actions/SaveAsAction",
|
"./src/actions/SaveAsAction",
|
||||||
@@ -55,7 +54,6 @@ define([
|
|||||||
EditObjectController,
|
EditObjectController,
|
||||||
EditAndComposeAction,
|
EditAndComposeAction,
|
||||||
EditAction,
|
EditAction,
|
||||||
PropertiesAction,
|
|
||||||
SaveAction,
|
SaveAction,
|
||||||
SaveAndStopEditingAction,
|
SaveAndStopEditingAction,
|
||||||
SaveAsAction,
|
SaveAsAction,
|
||||||
@@ -143,22 +141,6 @@ define([
|
|||||||
"group": "action",
|
"group": "action",
|
||||||
"priority": 10
|
"priority": 10
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"key": "properties",
|
|
||||||
"category": [
|
|
||||||
"contextual",
|
|
||||||
"view-control"
|
|
||||||
],
|
|
||||||
"implementation": PropertiesAction,
|
|
||||||
"cssClass": "major icon-pencil",
|
|
||||||
"name": "Edit Properties...",
|
|
||||||
"group": "action",
|
|
||||||
"priority": 10,
|
|
||||||
"description": "Edit properties of this object.",
|
|
||||||
"depends": [
|
|
||||||
"dialogService"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "save-and-stop-editing",
|
"key": "save-and-stop-editing",
|
||||||
"category": "save",
|
"category": "save",
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../creation/CreateWizard',
|
// '../creation/CreateWizard',
|
||||||
'./SaveInProgressDialog'
|
'./SaveInProgressDialog'
|
||||||
],
|
],
|
||||||
function (
|
function (
|
||||||
CreateWizard,
|
// CreateWizard,
|
||||||
SaveInProgressDialog
|
SaveInProgressDialog
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -100,7 +100,8 @@ function (
|
|||||||
toUndirty = [];
|
toUndirty = [];
|
||||||
|
|
||||||
function doWizardSave(parent) {
|
function doWizardSave(parent) {
|
||||||
var wizard = self.createWizard(parent);
|
console.log('SaveAsAction');
|
||||||
|
// var wizard = self.createWizard(parent);
|
||||||
|
|
||||||
return self.dialogService
|
return self.dialogService
|
||||||
.getUserInput(wizard.getFormStructure(true),
|
.getUserInput(wizard.getFormStructure(true),
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ define(
|
|||||||
})
|
})
|
||||||
.join('/');
|
.join('/');
|
||||||
|
|
||||||
window.location.href = url;
|
openmct.router.navigate(url);
|
||||||
|
|
||||||
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
|
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
|
||||||
openmct.editor.edit();
|
openmct.editor.edit();
|
||||||
|
|||||||
@@ -141,11 +141,17 @@ define(
|
|||||||
if (mutationResult !== false) {
|
if (mutationResult !== false) {
|
||||||
// Copy values if result was a different object
|
// Copy values if result was a different object
|
||||||
// (either our clone or some other new thing)
|
// (either our clone or some other new thing)
|
||||||
if (model !== result) {
|
let modelHasChanged = _.isEqual(model, result) === false;
|
||||||
|
if (modelHasChanged) {
|
||||||
copyValues(model, result);
|
copyValues(model, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
model.modified = useTimestamp ? timestamp : now();
|
if (modelHasChanged
|
||||||
|
|| (useTimestamp !== undefined)
|
||||||
|
|| (model.modified === undefined)) {
|
||||||
|
model.modified = useTimestamp ? timestamp : now();
|
||||||
|
}
|
||||||
|
|
||||||
notifyListeners(model);
|
notifyListeners(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,25 +21,21 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
"./src/actions/LinkAction",
|
|
||||||
"./src/actions/SetPrimaryLocationAction",
|
"./src/actions/SetPrimaryLocationAction",
|
||||||
"./src/services/LocatingCreationDecorator",
|
"./src/services/LocatingCreationDecorator",
|
||||||
"./src/services/LocatingObjectDecorator",
|
"./src/services/LocatingObjectDecorator",
|
||||||
"./src/policies/CopyPolicy",
|
"./src/policies/CopyPolicy",
|
||||||
"./src/policies/CrossSpacePolicy",
|
"./src/policies/CrossSpacePolicy",
|
||||||
"./src/capabilities/LocationCapability",
|
"./src/capabilities/LocationCapability",
|
||||||
"./src/services/LinkService",
|
|
||||||
"./src/services/CopyService",
|
"./src/services/CopyService",
|
||||||
"./src/services/LocationService"
|
"./src/services/LocationService"
|
||||||
], function (
|
], function (
|
||||||
LinkAction,
|
|
||||||
SetPrimaryLocationAction,
|
SetPrimaryLocationAction,
|
||||||
LocatingCreationDecorator,
|
LocatingCreationDecorator,
|
||||||
LocatingObjectDecorator,
|
LocatingObjectDecorator,
|
||||||
CopyPolicy,
|
CopyPolicy,
|
||||||
CrossSpacePolicy,
|
CrossSpacePolicy,
|
||||||
LocationCapability,
|
LocationCapability,
|
||||||
LinkService,
|
|
||||||
CopyService,
|
CopyService,
|
||||||
LocationService
|
LocationService
|
||||||
) {
|
) {
|
||||||
@@ -52,21 +48,6 @@ define([
|
|||||||
"configuration": {},
|
"configuration": {},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
|
||||||
"key": "link",
|
|
||||||
"name": "Create Link",
|
|
||||||
"description": "Create Link to object in another location.",
|
|
||||||
"cssClass": "icon-link",
|
|
||||||
"category": "contextual",
|
|
||||||
"group": "action",
|
|
||||||
"priority": 7,
|
|
||||||
"implementation": LinkAction,
|
|
||||||
"depends": [
|
|
||||||
"policyService",
|
|
||||||
"locationService",
|
|
||||||
"linkService"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "locate",
|
"key": "locate",
|
||||||
"name": "Set Primary Location",
|
"name": "Set Primary Location",
|
||||||
@@ -115,15 +96,6 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"services": [
|
"services": [
|
||||||
{
|
|
||||||
"key": "linkService",
|
|
||||||
"name": "Link Service",
|
|
||||||
"description": "Provides a service for linking objects",
|
|
||||||
"implementation": LinkService,
|
|
||||||
"depends": [
|
|
||||||
"openmct"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "copyService",
|
"key": "copyService",
|
||||||
"name": "Copy Service",
|
"name": "Copy Service",
|
||||||
|
|||||||
@@ -1,48 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
['./AbstractComposeAction'],
|
|
||||||
function (AbstractComposeAction) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The LinkAction is available from context menus and allows a user to
|
|
||||||
* link an object to another location of their choosing.
|
|
||||||
*
|
|
||||||
* @implements {Action}
|
|
||||||
* @constructor
|
|
||||||
* @memberof platform/entanglement
|
|
||||||
*/
|
|
||||||
function LinkAction(policyService, locationService, linkService, context) {
|
|
||||||
AbstractComposeAction.apply(
|
|
||||||
this,
|
|
||||||
[policyService, locationService, linkService, context, "Link"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkAction.prototype = Object.create(AbstractComposeAction.prototype);
|
|
||||||
LinkAction.appliesTo = AbstractComposeAction.appliesTo;
|
|
||||||
|
|
||||||
return LinkAction;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
@@ -1,71 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
function () {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LinkService provides an interface for linking objects to additional
|
|
||||||
* locations. It also provides a method for determining if an object
|
|
||||||
* can be copied to a specific location.
|
|
||||||
* @constructor
|
|
||||||
* @memberof platform/entanglement
|
|
||||||
* @implements {platform/entanglement.AbstractComposeService}
|
|
||||||
*/
|
|
||||||
function LinkService(openmct) {
|
|
||||||
this.openmct = openmct;
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkService.prototype.validate = function (object, parentCandidate) {
|
|
||||||
if (!parentCandidate || !parentCandidate.getId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentCandidate.getId() === object.getId()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parentCandidate.hasCapability('composition')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.openmct.composition.checkPolicy(parentCandidate.useCapability('adapter'), object.useCapability('adapter'));
|
|
||||||
};
|
|
||||||
|
|
||||||
LinkService.prototype.perform = function (object, parentObject) {
|
|
||||||
if (!this.validate(object, parentObject)) {
|
|
||||||
throw new Error(
|
|
||||||
"Tried to link objects without validating first."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parentObject.getCapability('composition').add(object);
|
|
||||||
};
|
|
||||||
|
|
||||||
return LinkService;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
@@ -1,178 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[
|
|
||||||
'../../src/actions/LinkAction',
|
|
||||||
'../services/MockLinkService',
|
|
||||||
'../DomainObjectFactory'
|
|
||||||
],
|
|
||||||
function (LinkAction, MockLinkService, domainObjectFactory) {
|
|
||||||
|
|
||||||
describe("Link Action", function () {
|
|
||||||
|
|
||||||
var linkAction,
|
|
||||||
policyService,
|
|
||||||
locationService,
|
|
||||||
locationServicePromise,
|
|
||||||
linkService,
|
|
||||||
context,
|
|
||||||
selectedObject,
|
|
||||||
selectedObjectContextCapability,
|
|
||||||
currentParent,
|
|
||||||
newParent;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
policyService = jasmine.createSpyObj(
|
|
||||||
'policyService',
|
|
||||||
['allow']
|
|
||||||
);
|
|
||||||
policyService.allow.and.returnValue(true);
|
|
||||||
|
|
||||||
selectedObjectContextCapability = jasmine.createSpyObj(
|
|
||||||
'selectedObjectContextCapability',
|
|
||||||
[
|
|
||||||
'getParent'
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
selectedObject = domainObjectFactory({
|
|
||||||
name: 'selectedObject',
|
|
||||||
model: {
|
|
||||||
name: 'selectedObject'
|
|
||||||
},
|
|
||||||
capabilities: {
|
|
||||||
context: selectedObjectContextCapability
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
currentParent = domainObjectFactory({
|
|
||||||
name: 'currentParent'
|
|
||||||
});
|
|
||||||
|
|
||||||
selectedObjectContextCapability
|
|
||||||
.getParent
|
|
||||||
.and.returnValue(currentParent);
|
|
||||||
|
|
||||||
newParent = domainObjectFactory({
|
|
||||||
name: 'newParent'
|
|
||||||
});
|
|
||||||
|
|
||||||
locationService = jasmine.createSpyObj(
|
|
||||||
'locationService',
|
|
||||||
[
|
|
||||||
'getLocationFromUser'
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
locationServicePromise = jasmine.createSpyObj(
|
|
||||||
'locationServicePromise',
|
|
||||||
[
|
|
||||||
'then'
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
locationService
|
|
||||||
.getLocationFromUser
|
|
||||||
.and.returnValue(locationServicePromise);
|
|
||||||
|
|
||||||
linkService = new MockLinkService();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with context from context-action", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
context = {
|
|
||||||
domainObject: selectedObject
|
|
||||||
};
|
|
||||||
|
|
||||||
linkAction = new LinkAction(
|
|
||||||
policyService,
|
|
||||||
locationService,
|
|
||||||
linkService,
|
|
||||||
context
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("initializes happily", function () {
|
|
||||||
expect(linkAction).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when performed it", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
linkAction.perform();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("prompts for location", function () {
|
|
||||||
expect(locationService.getLocationFromUser)
|
|
||||||
.toHaveBeenCalledWith(
|
|
||||||
"Link selectedObject To a New Location",
|
|
||||||
"Link To",
|
|
||||||
jasmine.any(Function),
|
|
||||||
currentParent
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("waits for location and handles cancellation by user", function () {
|
|
||||||
expect(locationServicePromise.then)
|
|
||||||
.toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("links object to selected location", function () {
|
|
||||||
locationServicePromise
|
|
||||||
.then
|
|
||||||
.calls.mostRecent()
|
|
||||||
.args[0](newParent);
|
|
||||||
|
|
||||||
expect(linkService.perform)
|
|
||||||
.toHaveBeenCalledWith(selectedObject, newParent);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with context from drag-drop", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
context = {
|
|
||||||
selectedObject: selectedObject,
|
|
||||||
domainObject: newParent
|
|
||||||
};
|
|
||||||
|
|
||||||
linkAction = new LinkAction(
|
|
||||||
policyService,
|
|
||||||
locationService,
|
|
||||||
linkService,
|
|
||||||
context
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("initializes happily", function () {
|
|
||||||
expect(linkAction).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("performs link immediately", function () {
|
|
||||||
linkAction.perform();
|
|
||||||
expect(linkService.perform)
|
|
||||||
.toHaveBeenCalledWith(selectedObject, newParent);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,215 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[
|
|
||||||
'../../src/services/LinkService',
|
|
||||||
'../DomainObjectFactory',
|
|
||||||
'../ControlledPromise'
|
|
||||||
],
|
|
||||||
function (LinkService, domainObjectFactory, ControlledPromise) {
|
|
||||||
|
|
||||||
xdescribe("LinkService", function () {
|
|
||||||
|
|
||||||
var linkService,
|
|
||||||
mockPolicyService;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockPolicyService = jasmine.createSpyObj(
|
|
||||||
'policyService',
|
|
||||||
['allow']
|
|
||||||
);
|
|
||||||
mockPolicyService.allow.and.returnValue(true);
|
|
||||||
linkService = new LinkService(mockPolicyService);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("validate", function () {
|
|
||||||
|
|
||||||
var object,
|
|
||||||
parentCandidate,
|
|
||||||
validate;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
object = domainObjectFactory({
|
|
||||||
name: 'object'
|
|
||||||
});
|
|
||||||
parentCandidate = domainObjectFactory({
|
|
||||||
name: 'parentCandidate',
|
|
||||||
capabilities: {
|
|
||||||
composition: jasmine.createSpyObj(
|
|
||||||
'composition',
|
|
||||||
['invoke', 'add']
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
validate = function () {
|
|
||||||
return linkService.validate(object, parentCandidate);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not allow invalid parentCandidate", function () {
|
|
||||||
parentCandidate = undefined;
|
|
||||||
expect(validate()).toBe(false);
|
|
||||||
parentCandidate = {};
|
|
||||||
expect(validate()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not allow parent to be object", function () {
|
|
||||||
parentCandidate.id = object.id = 'abc';
|
|
||||||
expect(validate()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not allow parent that contains object", function () {
|
|
||||||
object.id = 'abc';
|
|
||||||
parentCandidate.id = 'xyz';
|
|
||||||
parentCandidate.model.composition = ['abc'];
|
|
||||||
expect(validate()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not allow parents without composition", function () {
|
|
||||||
parentCandidate = domainObjectFactory({
|
|
||||||
name: 'parentCandidate'
|
|
||||||
});
|
|
||||||
object.id = 'abc';
|
|
||||||
parentCandidate.id = 'xyz';
|
|
||||||
parentCandidate.hasCapability.and.callFake(function (c) {
|
|
||||||
return c !== 'composition';
|
|
||||||
});
|
|
||||||
expect(validate()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("defers to policyService", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
object.id = 'abc';
|
|
||||||
object.capabilities.type = { type: 'object' };
|
|
||||||
parentCandidate.id = 'xyz';
|
|
||||||
parentCandidate.capabilities.type = {
|
|
||||||
type: 'parentCandidate'
|
|
||||||
};
|
|
||||||
parentCandidate.model.composition = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
it("calls policy service with correct args", function () {
|
|
||||||
validate();
|
|
||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
|
||||||
"composition",
|
|
||||||
parentCandidate,
|
|
||||||
object
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("and returns false", function () {
|
|
||||||
mockPolicyService.allow.and.returnValue(true);
|
|
||||||
expect(validate()).toBe(true);
|
|
||||||
expect(mockPolicyService.allow).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("and returns true", function () {
|
|
||||||
mockPolicyService.allow.and.returnValue(false);
|
|
||||||
expect(validate()).toBe(false);
|
|
||||||
expect(mockPolicyService.allow).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("perform", function () {
|
|
||||||
|
|
||||||
var object,
|
|
||||||
linkedObject,
|
|
||||||
parentModel,
|
|
||||||
parentObject,
|
|
||||||
compositionPromise,
|
|
||||||
addPromise,
|
|
||||||
compositionCapability;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
compositionPromise = new ControlledPromise();
|
|
||||||
addPromise = new ControlledPromise();
|
|
||||||
compositionCapability = jasmine.createSpyObj(
|
|
||||||
'compositionCapability',
|
|
||||||
['invoke', 'add']
|
|
||||||
);
|
|
||||||
compositionCapability.invoke.and.returnValue(compositionPromise);
|
|
||||||
compositionCapability.add.and.returnValue(addPromise);
|
|
||||||
parentModel = {
|
|
||||||
composition: []
|
|
||||||
};
|
|
||||||
parentObject = domainObjectFactory({
|
|
||||||
name: 'parentObject',
|
|
||||||
model: parentModel,
|
|
||||||
capabilities: {
|
|
||||||
mutation: {
|
|
||||||
invoke: function (mutator) {
|
|
||||||
mutator(parentModel);
|
|
||||||
|
|
||||||
return new ControlledPromise();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
composition: compositionCapability
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
object = domainObjectFactory({
|
|
||||||
name: 'object',
|
|
||||||
id: 'xyz'
|
|
||||||
});
|
|
||||||
|
|
||||||
linkedObject = domainObjectFactory({
|
|
||||||
name: 'object-link',
|
|
||||||
id: 'xyz'
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds to the parent's composition", function () {
|
|
||||||
expect(compositionCapability.add).not.toHaveBeenCalled();
|
|
||||||
linkService.perform(object, parentObject);
|
|
||||||
expect(compositionCapability.add)
|
|
||||||
.toHaveBeenCalledWith(object);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns object representing new link", function () {
|
|
||||||
var returnPromise, whenComplete;
|
|
||||||
returnPromise = linkService.perform(object, parentObject);
|
|
||||||
whenComplete = jasmine.createSpy('whenComplete');
|
|
||||||
returnPromise.then(whenComplete);
|
|
||||||
|
|
||||||
addPromise.resolve(linkedObject);
|
|
||||||
compositionPromise.resolve([linkedObject]);
|
|
||||||
expect(whenComplete).toHaveBeenCalledWith(linkedObject);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an error when performed on invalid inputs", function () {
|
|
||||||
function perform() {
|
|
||||||
linkService.perform(object, parentObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
spyOn(linkService, 'validate');
|
|
||||||
linkService.validate.and.returnValue(true);
|
|
||||||
expect(perform).not.toThrow();
|
|
||||||
linkService.validate.and.returnValue(false);
|
|
||||||
expect(perform).toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -23,13 +23,11 @@
|
|||||||
define([
|
define([
|
||||||
"moment-timezone",
|
"moment-timezone",
|
||||||
"./src/indicators/ClockIndicator",
|
"./src/indicators/ClockIndicator",
|
||||||
"./src/indicators/FollowIndicator",
|
|
||||||
"./src/services/TickerService",
|
"./src/services/TickerService",
|
||||||
"./src/services/TimerService",
|
"./src/services/TimerService",
|
||||||
"./src/controllers/ClockController",
|
"./src/controllers/ClockController",
|
||||||
"./src/controllers/TimerController",
|
"./src/controllers/TimerController",
|
||||||
"./src/controllers/RefreshingController",
|
"./src/controllers/RefreshingController",
|
||||||
"./src/actions/FollowTimerAction",
|
|
||||||
"./src/actions/StartTimerAction",
|
"./src/actions/StartTimerAction",
|
||||||
"./src/actions/RestartTimerAction",
|
"./src/actions/RestartTimerAction",
|
||||||
"./src/actions/StopTimerAction",
|
"./src/actions/StopTimerAction",
|
||||||
@@ -39,13 +37,11 @@ define([
|
|||||||
], function (
|
], function (
|
||||||
MomentTimezone,
|
MomentTimezone,
|
||||||
ClockIndicator,
|
ClockIndicator,
|
||||||
FollowIndicator,
|
|
||||||
TickerService,
|
TickerService,
|
||||||
TimerService,
|
TimerService,
|
||||||
ClockController,
|
ClockController,
|
||||||
TimerController,
|
TimerController,
|
||||||
RefreshingController,
|
RefreshingController,
|
||||||
FollowTimerAction,
|
|
||||||
StartTimerAction,
|
StartTimerAction,
|
||||||
RestartTimerAction,
|
RestartTimerAction,
|
||||||
StopTimerAction,
|
StopTimerAction,
|
||||||
@@ -144,15 +140,6 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
|
||||||
"key": "timer.follow",
|
|
||||||
"implementation": FollowTimerAction,
|
|
||||||
"depends": ["timerService"],
|
|
||||||
"category": "contextual",
|
|
||||||
"name": "Follow Timer",
|
|
||||||
"cssClass": "icon-clock",
|
|
||||||
"priority": "optional"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "timer.start",
|
"key": "timer.start",
|
||||||
"implementation": StartTimerAction,
|
"implementation": StartTimerAction,
|
||||||
@@ -295,14 +282,11 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"model": {
|
"model": {
|
||||||
"timerFormat": "DDD hh:mm:ss"
|
"timerFormat": "long"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"runs": [{
|
"runs": [],
|
||||||
"implementation": FollowIndicator,
|
|
||||||
"depends": ["openmct", "timerService"]
|
|
||||||
}],
|
|
||||||
"licenses": [
|
"licenses": [
|
||||||
{
|
{
|
||||||
"name": "moment-duration-format",
|
"name": "moment-duration-format",
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* 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);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* 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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -154,7 +154,9 @@ define(['zepto', 'objectUtils'], function ($, objectUtils) {
|
|||||||
tree = JSON.stringify(tree).replace(new RegExp(oldIdKeyString, 'g'), newIdKeyString);
|
tree = JSON.stringify(tree).replace(new RegExp(oldIdKeyString, 'g'), newIdKeyString);
|
||||||
|
|
||||||
return JSON.parse(tree, (key, value) => {
|
return JSON.parse(tree, (key, value) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(value, 'key')
|
if (value !== undefined
|
||||||
|
&& value !== null
|
||||||
|
&& Object.prototype.hasOwnProperty.call(value, 'key')
|
||||||
&& Object.prototype.hasOwnProperty.call(value, 'namespace')
|
&& Object.prototype.hasOwnProperty.call(value, 'namespace')
|
||||||
&& value.key === oldId.key
|
&& value.key === oldId.key
|
||||||
&& value.namespace === oldId.namespace) {
|
&& value.namespace === oldId.namespace) {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# Couch DB Persistence Plugin
|
|
||||||
An adapter for using CouchDB for persistence of user-created objects. The plugin installation function takes the URL
|
|
||||||
for the CouchDB database as a parameter.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
```js
|
|
||||||
openmct.install(openmct.plugins.CouchDB('http://localhost:5984/openmct'))
|
|
||||||
```
|
|
||||||
@@ -1,78 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define([
|
|
||||||
"./src/CouchPersistenceProvider",
|
|
||||||
"./src/CouchIndicator"
|
|
||||||
], function (
|
|
||||||
CouchPersistenceProvider,
|
|
||||||
CouchIndicator
|
|
||||||
) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: "platform/persistence/couch",
|
|
||||||
definition: {
|
|
||||||
"name": "Couch Persistence",
|
|
||||||
"description": "Adapter to read and write objects using a CouchDB instance.",
|
|
||||||
"extensions": {
|
|
||||||
"components": [
|
|
||||||
{
|
|
||||||
"provides": "persistenceService",
|
|
||||||
"type": "provider",
|
|
||||||
"implementation": CouchPersistenceProvider,
|
|
||||||
"depends": [
|
|
||||||
"$http",
|
|
||||||
"$q",
|
|
||||||
"PERSISTENCE_SPACE",
|
|
||||||
"COUCHDB_PATH"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"constants": [
|
|
||||||
{
|
|
||||||
"key": "PERSISTENCE_SPACE",
|
|
||||||
"value": "mct"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "COUCHDB_PATH",
|
|
||||||
"value": "/couch/openmct"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "COUCHDB_INDICATOR_INTERVAL",
|
|
||||||
"value": 15000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indicators": [
|
|
||||||
{
|
|
||||||
"implementation": CouchIndicator,
|
|
||||||
"depends": [
|
|
||||||
"$http",
|
|
||||||
"$interval",
|
|
||||||
"COUCHDB_PATH",
|
|
||||||
"COUCHDB_INDICATOR_INTERVAL"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,61 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A CouchDocument describes domain object model in a format
|
|
||||||
* which is easily read-written to CouchDB. This includes
|
|
||||||
* Couch's _id and _rev fields, as well as a separate
|
|
||||||
* metadata field which contains a subset of information found
|
|
||||||
* in the model itself (to support search optimization with
|
|
||||||
* CouchDB views.)
|
|
||||||
* @memberof platform/persistence/couch
|
|
||||||
* @constructor
|
|
||||||
* @param {string} id the id under which to store this mode
|
|
||||||
* @param {object} model the model to store
|
|
||||||
* @param {string} rev the revision to include (or undefined,
|
|
||||||
* if no revision should be noted for couch)
|
|
||||||
* @param {boolean} whether or not to mark this document as
|
|
||||||
* deleted (see CouchDB docs for _deleted)
|
|
||||||
*/
|
|
||||||
function CouchDocument(id, model, rev, markDeleted) {
|
|
||||||
return {
|
|
||||||
"_id": id,
|
|
||||||
"_rev": rev,
|
|
||||||
"_deleted": markDeleted,
|
|
||||||
"metadata": {
|
|
||||||
"category": "domain object",
|
|
||||||
"type": model.type,
|
|
||||||
"owner": "admin",
|
|
||||||
"name": model.name,
|
|
||||||
"created": Date.now()
|
|
||||||
},
|
|
||||||
"model": model
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return CouchDocument;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,119 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
|
|
||||||
// Set of connection states; changing among these states will be
|
|
||||||
// reflected in the indicator's appearance.
|
|
||||||
// CONNECTED: Everything nominal, expect to be able to read/write.
|
|
||||||
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
|
||||||
// SEMICONNECTED: Connected to the database, but it reported an error.
|
|
||||||
// PENDING: Still trying to connect, and haven't failed yet.
|
|
||||||
var CONNECTED = {
|
|
||||||
text: "Connected",
|
|
||||||
glyphClass: "ok",
|
|
||||||
statusClass: "s-status-on",
|
|
||||||
description: "Connected to the domain object database."
|
|
||||||
},
|
|
||||||
DISCONNECTED = {
|
|
||||||
text: "Disconnected",
|
|
||||||
glyphClass: "err",
|
|
||||||
statusClass: "s-status-caution",
|
|
||||||
description: "Unable to connect to the domain object database."
|
|
||||||
},
|
|
||||||
SEMICONNECTED = {
|
|
||||||
text: "Unavailable",
|
|
||||||
glyphClass: "caution",
|
|
||||||
statusClass: "s-status-caution",
|
|
||||||
description: "Database does not exist or is unavailable."
|
|
||||||
},
|
|
||||||
PENDING = {
|
|
||||||
text: "Checking connection...",
|
|
||||||
statusClass: "s-status-caution"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicator for the current CouchDB connection. Polls CouchDB
|
|
||||||
* at a regular interval (defined by bundle constants) to ensure
|
|
||||||
* that the database is available.
|
|
||||||
* @constructor
|
|
||||||
* @memberof platform/persistence/couch
|
|
||||||
* @implements {Indicator}
|
|
||||||
* @param $http Angular's $http service
|
|
||||||
* @param $interval Angular's $interval service
|
|
||||||
* @param {string} path the URL to poll to check for couch availability
|
|
||||||
* @param {number} interval the interval, in milliseconds, to poll at
|
|
||||||
*/
|
|
||||||
function CouchIndicator($http, $interval, path, interval) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Track the current connection state
|
|
||||||
this.state = PENDING;
|
|
||||||
|
|
||||||
this.$http = $http;
|
|
||||||
this.$interval = $interval;
|
|
||||||
this.path = path;
|
|
||||||
this.interval = interval;
|
|
||||||
|
|
||||||
// Callback if the HTTP request to Couch fails
|
|
||||||
function handleError() {
|
|
||||||
self.state = DISCONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback if the HTTP request succeeds. CouchDB may
|
|
||||||
// report an error, so check for that.
|
|
||||||
function handleResponse(response) {
|
|
||||||
var data = response.data;
|
|
||||||
self.state = data.error ? SEMICONNECTED : CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to connect to CouchDB, and update the indicator.
|
|
||||||
function updateIndicator() {
|
|
||||||
$http.get(path).then(handleResponse, handleError);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the indicator initially, and start polling.
|
|
||||||
updateIndicator();
|
|
||||||
$interval(updateIndicator, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
CouchIndicator.prototype.getCssClass = function () {
|
|
||||||
return "c-indicator--clickable icon-suitcase " + this.state.statusClass;
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchIndicator.prototype.getGlyphClass = function () {
|
|
||||||
return this.state.glyphClass;
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchIndicator.prototype.getText = function () {
|
|
||||||
return this.state.text;
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchIndicator.prototype.getDescription = function () {
|
|
||||||
return this.state.description;
|
|
||||||
};
|
|
||||||
|
|
||||||
return CouchIndicator;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,145 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This bundle implements a persistence service which uses CouchDB to
|
|
||||||
* store documents.
|
|
||||||
* @namespace platform/persistence/cache
|
|
||||||
*/
|
|
||||||
define(
|
|
||||||
["./CouchDocument"],
|
|
||||||
function (CouchDocument) {
|
|
||||||
|
|
||||||
// JSLint doesn't like dangling _'s, but CouchDB uses these, so
|
|
||||||
// hide this behind variables.
|
|
||||||
var REV = "_rev",
|
|
||||||
ID = "_id";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The CouchPersistenceProvider reads and writes JSON documents
|
|
||||||
* (more specifically, domain object models) to/from a CouchDB
|
|
||||||
* instance.
|
|
||||||
* @memberof platform/persistence/couch
|
|
||||||
* @constructor
|
|
||||||
* @implements {PersistenceService}
|
|
||||||
* @param $http Angular's $http service
|
|
||||||
* @param $interval Angular's $interval service
|
|
||||||
* @param {string} space the name of the persistence space being served
|
|
||||||
* @param {string} path the path to the CouchDB instance
|
|
||||||
*/
|
|
||||||
function CouchPersistenceProvider($http, $q, space, path) {
|
|
||||||
this.spaces = [space];
|
|
||||||
this.revs = {};
|
|
||||||
this.$q = $q;
|
|
||||||
this.$http = $http;
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull out a list of document IDs from CouchDB's
|
|
||||||
// _all_docs response
|
|
||||||
function getIdsFromAllDocs(allDocs) {
|
|
||||||
return allDocs.rows.map(function (r) {
|
|
||||||
return r.id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the response to a create/update/delete request;
|
|
||||||
// track the rev if it's valid, otherwise return false to
|
|
||||||
// indicate that the request failed.
|
|
||||||
CouchPersistenceProvider.prototype.checkResponse = function (response) {
|
|
||||||
if (response && response.ok) {
|
|
||||||
this.revs[response.id] = response.rev;
|
|
||||||
|
|
||||||
return response.ok;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get a domain object model out of CouchDB's response
|
|
||||||
CouchPersistenceProvider.prototype.getModel = function (response) {
|
|
||||||
if (response && response.model) {
|
|
||||||
this.revs[response[ID]] = response[REV];
|
|
||||||
|
|
||||||
return response.model;
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Issue a request using $http; get back the plain JS object
|
|
||||||
// from the expected JSON response
|
|
||||||
CouchPersistenceProvider.prototype.request = function (subpath, method, value) {
|
|
||||||
return this.$http({
|
|
||||||
method: method,
|
|
||||||
url: this.path + '/' + subpath,
|
|
||||||
data: value
|
|
||||||
}).then(function (response) {
|
|
||||||
return response.data;
|
|
||||||
}, function () {
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Shorthand methods for GET/PUT methods
|
|
||||||
CouchPersistenceProvider.prototype.get = function (subpath) {
|
|
||||||
return this.request(subpath, "GET");
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.put = function (subpath, value) {
|
|
||||||
return this.request(subpath, "PUT", value);
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.listSpaces = function () {
|
|
||||||
return this.$q.when(this.spaces);
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.listObjects = function () {
|
|
||||||
return this.get("_all_docs").then(getIdsFromAllDocs.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.createObject = function (space, key, value) {
|
|
||||||
return this.put(key, new CouchDocument(key, value))
|
|
||||||
.then(this.checkResponse.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.readObject = function (space, key) {
|
|
||||||
return this.get(key).then(this.getModel.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.updateObject = function (space, key, value) {
|
|
||||||
var rev = this.revs[key];
|
|
||||||
|
|
||||||
return this.put(key, new CouchDocument(key, value, rev))
|
|
||||||
.then(this.checkResponse.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
CouchPersistenceProvider.prototype.deleteObject = function (space, key, value) {
|
|
||||||
var rev = this.revs[key];
|
|
||||||
|
|
||||||
return this.put(key, new CouchDocument(key, value, rev, true))
|
|
||||||
.then(this.checkResponse.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
return CouchPersistenceProvider;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,63 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
|
|
||||||
*/
|
|
||||||
define(
|
|
||||||
["../src/CouchDocument"],
|
|
||||||
function (CouchDocument) {
|
|
||||||
|
|
||||||
// JSLint doesn't like dangling _'s, but CouchDB uses these, so
|
|
||||||
// hide this behind variables.
|
|
||||||
var REV = "_rev",
|
|
||||||
ID = "_id",
|
|
||||||
DELETED = "_deleted";
|
|
||||||
|
|
||||||
describe("A couch document", function () {
|
|
||||||
it("includes an id", function () {
|
|
||||||
expect(new CouchDocument("testId", {})[ID])
|
|
||||||
.toEqual("testId");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("includes a rev only when one is provided", function () {
|
|
||||||
expect(new CouchDocument("testId", {})[REV])
|
|
||||||
.not.toBeDefined();
|
|
||||||
expect(new CouchDocument("testId", {}, "testRev")[REV])
|
|
||||||
.toEqual("testRev");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("includes the provided model", function () {
|
|
||||||
var model = { someKey: "some value" };
|
|
||||||
expect(new CouchDocument("testId", model).model)
|
|
||||||
.toEqual(model);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("marks documents as deleted only on request", function () {
|
|
||||||
expect(new CouchDocument("testId", {}, "testRev")[DELETED])
|
|
||||||
.not.toBeDefined();
|
|
||||||
expect(new CouchDocument("testId", {}, "testRev", true)[DELETED])
|
|
||||||
.toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,129 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
["../src/CouchIndicator"],
|
|
||||||
function (CouchIndicator) {
|
|
||||||
|
|
||||||
xdescribe("The CouchDB status indicator", function () {
|
|
||||||
var mockHttp,
|
|
||||||
mockInterval,
|
|
||||||
testPath,
|
|
||||||
testInterval,
|
|
||||||
mockPromise,
|
|
||||||
indicator;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockHttp = jasmine.createSpyObj("$http", ["get"]);
|
|
||||||
mockInterval = jasmine.createSpy("$interval");
|
|
||||||
mockPromise = jasmine.createSpyObj("promise", ["then"]);
|
|
||||||
testPath = "/test/path";
|
|
||||||
testInterval = 12321; // Some number
|
|
||||||
|
|
||||||
mockHttp.get.and.returnValue(mockPromise);
|
|
||||||
|
|
||||||
indicator = new CouchIndicator(
|
|
||||||
mockHttp,
|
|
||||||
mockInterval,
|
|
||||||
testPath,
|
|
||||||
testInterval
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("polls for changes", function () {
|
|
||||||
expect(mockInterval).toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Function),
|
|
||||||
testInterval
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("has a database icon", function () {
|
|
||||||
expect(indicator.getCssClass()).toEqual("icon-database s-status-caution");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("consults the database at the configured path", function () {
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledWith(testPath);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes when the database connection is nominal", function () {
|
|
||||||
var initialText = indicator.getText(),
|
|
||||||
initialDescrption = indicator.getDescription(),
|
|
||||||
initialGlyphClass = indicator.getGlyphClass();
|
|
||||||
|
|
||||||
// Nominal just means getting back an object, without
|
|
||||||
// an error field.
|
|
||||||
mockPromise.then.calls.mostRecent().args[0]({ data: {} });
|
|
||||||
|
|
||||||
// Verify that these values changed;
|
|
||||||
// don't test for specific text.
|
|
||||||
expect(indicator.getText()).not.toEqual(initialText);
|
|
||||||
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
|
|
||||||
expect(indicator.getDescription()).not.toEqual(initialDescrption);
|
|
||||||
|
|
||||||
// Do check for specific class
|
|
||||||
expect(indicator.getGlyphClass()).toEqual("ok");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes when the server reports an error", function () {
|
|
||||||
var initialText = indicator.getText(),
|
|
||||||
initialDescrption = indicator.getDescription(),
|
|
||||||
initialGlyphClass = indicator.getGlyphClass();
|
|
||||||
|
|
||||||
// Nominal just means getting back an object, with
|
|
||||||
// an error field.
|
|
||||||
mockPromise.then.calls.mostRecent().args[0](
|
|
||||||
{ data: { error: "Uh oh." } }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify that these values changed;
|
|
||||||
// don't test for specific text.
|
|
||||||
expect(indicator.getText()).not.toEqual(initialText);
|
|
||||||
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
|
|
||||||
expect(indicator.getDescription()).not.toEqual(initialDescrption);
|
|
||||||
|
|
||||||
// Do check for specific class
|
|
||||||
expect(indicator.getGlyphClass()).toEqual("caution");
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes when the server cannot be reached", function () {
|
|
||||||
var initialText = indicator.getText(),
|
|
||||||
initialDescrption = indicator.getDescription(),
|
|
||||||
initialGlyphClass = indicator.getGlyphClass();
|
|
||||||
|
|
||||||
// Nominal just means getting back an object, without
|
|
||||||
// an error field.
|
|
||||||
mockPromise.then.calls.mostRecent().args[1]({ data: {} });
|
|
||||||
|
|
||||||
// Verify that these values changed;
|
|
||||||
// don't test for specific text.
|
|
||||||
expect(indicator.getText()).not.toEqual(initialText);
|
|
||||||
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
|
|
||||||
expect(indicator.getDescription()).not.toEqual(initialDescrption);
|
|
||||||
|
|
||||||
// Do check for specific class
|
|
||||||
expect(indicator.getGlyphClass()).toEqual("err");
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,223 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
|
|
||||||
*/
|
|
||||||
define(
|
|
||||||
["../src/CouchPersistenceProvider"],
|
|
||||||
function (CouchPersistenceProvider) {
|
|
||||||
|
|
||||||
describe("The couch persistence provider", function () {
|
|
||||||
var mockHttp,
|
|
||||||
mockQ,
|
|
||||||
testSpace = "testSpace",
|
|
||||||
testPath = "/test/db",
|
|
||||||
capture,
|
|
||||||
provider;
|
|
||||||
|
|
||||||
function mockPromise(value) {
|
|
||||||
return {
|
|
||||||
then: function (callback) {
|
|
||||||
return mockPromise(callback(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockHttp = jasmine.createSpy("$http");
|
|
||||||
mockQ = jasmine.createSpyObj("$q", ["when"]);
|
|
||||||
|
|
||||||
mockQ.when.and.callFake(mockPromise);
|
|
||||||
|
|
||||||
// Capture promise results
|
|
||||||
capture = jasmine.createSpy("capture");
|
|
||||||
|
|
||||||
provider = new CouchPersistenceProvider(
|
|
||||||
mockHttp,
|
|
||||||
mockQ,
|
|
||||||
testSpace,
|
|
||||||
testPath
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("reports available spaces", function () {
|
|
||||||
provider.listSpaces().then(capture);
|
|
||||||
expect(capture).toHaveBeenCalledWith([testSpace]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// General pattern of tests below is to simulate CouchDB's
|
|
||||||
// response, verify that request looks like what CouchDB
|
|
||||||
// would expect, and finally verify that CouchPersistenceProvider's
|
|
||||||
// return values match what is expected.
|
|
||||||
it("lists all available documents", function () {
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: { rows: [{ id: "a" }, { id: "b" }, { id: "c" }] }
|
|
||||||
}));
|
|
||||||
provider.listObjects().then(capture);
|
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
|
||||||
url: "/test/db/_all_docs", // couch document listing
|
|
||||||
method: "GET",
|
|
||||||
data: undefined
|
|
||||||
});
|
|
||||||
expect(capture).toHaveBeenCalledWith(["a", "b", "c"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows object creation", function () {
|
|
||||||
var model = { someKey: "some value" };
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"ok": true
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.createObject("testSpace", "abc", model).then(capture);
|
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
|
||||||
url: "/test/db/abc",
|
|
||||||
method: "PUT",
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": undefined,
|
|
||||||
"_deleted": undefined,
|
|
||||||
metadata: jasmine.any(Object),
|
|
||||||
model: model
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(capture).toHaveBeenCalledWith(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows object models to be read back", function () {
|
|
||||||
var model = { someKey: "some value" };
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"model": model
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.readObject("testSpace", "abc").then(capture);
|
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
|
||||||
url: "/test/db/abc",
|
|
||||||
method: "GET",
|
|
||||||
data: undefined
|
|
||||||
});
|
|
||||||
expect(capture).toHaveBeenCalledWith(model);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows object update", function () {
|
|
||||||
var model = { someKey: "some value" };
|
|
||||||
|
|
||||||
// First do a read to populate rev tags...
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"model": {}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.readObject("testSpace", "abc");
|
|
||||||
|
|
||||||
// Now perform an update
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "uvw",
|
|
||||||
"ok": true
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.updateObject("testSpace", "abc", model).then(capture);
|
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
|
||||||
url: "/test/db/abc",
|
|
||||||
method: "PUT",
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"_deleted": undefined,
|
|
||||||
metadata: jasmine.any(Object),
|
|
||||||
model: model
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(capture).toHaveBeenCalledWith(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows object deletion", function () {
|
|
||||||
// First do a read to populate rev tags...
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"model": {}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.readObject("testSpace", "abc");
|
|
||||||
|
|
||||||
// Now perform an update
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "uvw",
|
|
||||||
"ok": true
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.deleteObject("testSpace", "abc", {}).then(capture);
|
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
|
||||||
url: "/test/db/abc",
|
|
||||||
method: "PUT",
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"_deleted": true,
|
|
||||||
metadata: jasmine.any(Object),
|
|
||||||
model: {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(capture).toHaveBeenCalledWith(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("reports failure to create objects", function () {
|
|
||||||
var model = { someKey: "some value" };
|
|
||||||
mockHttp.and.returnValue(mockPromise({
|
|
||||||
data: {
|
|
||||||
"_id": "abc",
|
|
||||||
"_rev": "xyz",
|
|
||||||
"ok": false
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
provider.createObject("testSpace", "abc", model).then(capture);
|
|
||||||
expect(capture).toHaveBeenCalledWith(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns undefined when objects are not found", function () {
|
|
||||||
// Act like a 404
|
|
||||||
mockHttp.and.returnValue({
|
|
||||||
then: function (success, fail) {
|
|
||||||
return mockPromise(fail());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
provider.readObject("testSpace", "abc").then(capture);
|
|
||||||
expect(capture).toHaveBeenCalledWith(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -47,6 +47,7 @@ define([
|
|||||||
'./plugins/licenses/plugin',
|
'./plugins/licenses/plugin',
|
||||||
'./plugins/remove/plugin',
|
'./plugins/remove/plugin',
|
||||||
'./plugins/move/plugin',
|
'./plugins/move/plugin',
|
||||||
|
'./plugins/linkAction/plugin',
|
||||||
'./plugins/duplicate/plugin',
|
'./plugins/duplicate/plugin',
|
||||||
'vue'
|
'vue'
|
||||||
], function (
|
], function (
|
||||||
@@ -76,6 +77,7 @@ define([
|
|||||||
LicensesPlugin,
|
LicensesPlugin,
|
||||||
RemoveActionPlugin,
|
RemoveActionPlugin,
|
||||||
MoveActionPlugin,
|
MoveActionPlugin,
|
||||||
|
LinkActionPlugin,
|
||||||
DuplicateActionPlugin,
|
DuplicateActionPlugin,
|
||||||
Vue
|
Vue
|
||||||
) {
|
) {
|
||||||
@@ -252,7 +254,8 @@ define([
|
|||||||
|
|
||||||
this.status = new api.StatusAPI(this);
|
this.status = new api.StatusAPI(this);
|
||||||
|
|
||||||
this.router = new ApplicationRouter();
|
this.router = new ApplicationRouter(this);
|
||||||
|
this.forms = new api.FormsAPI.default(this);
|
||||||
|
|
||||||
this.branding = BrandingAPI.default;
|
this.branding = BrandingAPI.default;
|
||||||
|
|
||||||
@@ -268,6 +271,7 @@ define([
|
|||||||
this.install(LicensesPlugin.default());
|
this.install(LicensesPlugin.default());
|
||||||
this.install(RemoveActionPlugin.default());
|
this.install(RemoveActionPlugin.default());
|
||||||
this.install(MoveActionPlugin.default());
|
this.install(MoveActionPlugin.default());
|
||||||
|
this.install(LinkActionPlugin.default());
|
||||||
this.install(DuplicateActionPlugin.default());
|
this.install(DuplicateActionPlugin.default());
|
||||||
this.install(this.plugins.FolderView());
|
this.install(this.plugins.FolderView());
|
||||||
this.install(this.plugins.Tabs());
|
this.install(this.plugins.Tabs());
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"];
|
const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"];
|
||||||
const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
|
const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "move", "link", "remove", "locate"];
|
||||||
|
|
||||||
export default class LegacyContextMenuAction {
|
export default class LegacyContextMenuAction {
|
||||||
constructor(openmct, LegacyAction) {
|
constructor(openmct, LegacyAction) {
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ define([
|
|||||||
'./views/installLegacyViews',
|
'./views/installLegacyViews',
|
||||||
'./policies/LegacyCompositionPolicyAdapter',
|
'./policies/LegacyCompositionPolicyAdapter',
|
||||||
'./actions/LegacyActionAdapter',
|
'./actions/LegacyActionAdapter',
|
||||||
'./services/LegacyPersistenceAdapter'
|
'./services/LegacyPersistenceAdapter',
|
||||||
|
'./services/ExportImageService'
|
||||||
], function (
|
], function (
|
||||||
ActionDialogDecorator,
|
ActionDialogDecorator,
|
||||||
AdapterCapability,
|
AdapterCapability,
|
||||||
@@ -53,7 +54,8 @@ define([
|
|||||||
installLegacyViews,
|
installLegacyViews,
|
||||||
legacyCompositionPolicyAdapter,
|
legacyCompositionPolicyAdapter,
|
||||||
LegacyActionAdapter,
|
LegacyActionAdapter,
|
||||||
LegacyPersistenceAdapter
|
LegacyPersistenceAdapter,
|
||||||
|
ExportImageService
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
name: 'src/adapter',
|
name: 'src/adapter',
|
||||||
@@ -82,6 +84,13 @@ define([
|
|||||||
"identifierService",
|
"identifierService",
|
||||||
"cacheService"
|
"cacheService"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "exportImageService",
|
||||||
|
"implementation": ExportImageService,
|
||||||
|
"depends": [
|
||||||
|
"dialogService"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
components: [
|
components: [
|
||||||
|
|||||||
@@ -161,6 +161,22 @@ define([
|
|||||||
evaluate: function (datum, property) {
|
evaluate: function (datum, property) {
|
||||||
return limitEvaluator.evaluate(datum, property && property.key);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ define([
|
|||||||
|
|
||||||
oldStyleObject.getCapability('mutation').mutate(function () {
|
oldStyleObject.getCapability('mutation').mutate(function () {
|
||||||
return utils.toOldFormat(newStyleObject);
|
return utils.toOldFormat(newStyleObject);
|
||||||
});
|
}, newStyleObject.modified);
|
||||||
|
|
||||||
removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
|
removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ describe('The ActionCollection', () => {
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
actionCollection.destroy();
|
actionCollection.destroy();
|
||||||
resetApplicationState(openmct);
|
|
||||||
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("disable method invoked with action keys", () => {
|
describe("disable method invoked with action keys", () => {
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ describe('The Actions API', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("register method", () => {
|
describe("register method", () => {
|
||||||
|
|||||||
@@ -21,41 +21,44 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'./time/TimeAPI',
|
|
||||||
'./objects/ObjectAPI',
|
|
||||||
'./composition/CompositionAPI',
|
|
||||||
'./types/TypeRegistry',
|
|
||||||
'./telemetry/TelemetryAPI',
|
|
||||||
'./indicators/IndicatorAPI',
|
|
||||||
'./notifications/NotificationAPI',
|
|
||||||
'./Editor',
|
|
||||||
'./menu/MenuAPI',
|
|
||||||
'./actions/ActionsAPI',
|
'./actions/ActionsAPI',
|
||||||
'./status/StatusAPI'
|
'./composition/CompositionAPI',
|
||||||
|
'./Editor',
|
||||||
|
'./forms/FormsAPI',
|
||||||
|
'./indicators/IndicatorAPI',
|
||||||
|
'./menu/MenuAPI',
|
||||||
|
'./notifications/NotificationAPI',
|
||||||
|
'./objects/ObjectAPI',
|
||||||
|
'./status/StatusAPI',
|
||||||
|
'./telemetry/TelemetryAPI',
|
||||||
|
'./time/TimeAPI',
|
||||||
|
'./types/TypeRegistry'
|
||||||
], function (
|
], function (
|
||||||
TimeAPI,
|
|
||||||
ObjectAPI,
|
|
||||||
CompositionAPI,
|
|
||||||
TypeRegistry,
|
|
||||||
TelemetryAPI,
|
|
||||||
IndicatorAPI,
|
|
||||||
NotificationAPI,
|
|
||||||
EditorAPI,
|
|
||||||
MenuAPI,
|
|
||||||
ActionsAPI,
|
ActionsAPI,
|
||||||
StatusAPI
|
CompositionAPI,
|
||||||
|
EditorAPI,
|
||||||
|
FormsAPI,
|
||||||
|
IndicatorAPI,
|
||||||
|
MenuAPI,
|
||||||
|
NotificationAPI,
|
||||||
|
ObjectAPI,
|
||||||
|
StatusAPI,
|
||||||
|
TelemetryAPI,
|
||||||
|
TimeAPI,
|
||||||
|
TypeRegistry
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
TimeAPI: TimeAPI,
|
|
||||||
ObjectAPI: ObjectAPI,
|
|
||||||
CompositionAPI: CompositionAPI,
|
|
||||||
TypeRegistry: TypeRegistry,
|
|
||||||
TelemetryAPI: TelemetryAPI,
|
|
||||||
IndicatorAPI: IndicatorAPI,
|
|
||||||
NotificationAPI: NotificationAPI.default,
|
|
||||||
EditorAPI: EditorAPI,
|
|
||||||
MenuAPI: MenuAPI.default,
|
|
||||||
ActionsAPI: ActionsAPI.default,
|
ActionsAPI: ActionsAPI.default,
|
||||||
StatusAPI: StatusAPI.default
|
CompositionAPI: CompositionAPI,
|
||||||
|
EditorAPI: EditorAPI,
|
||||||
|
FormsAPI: FormsAPI,
|
||||||
|
IndicatorAPI: IndicatorAPI,
|
||||||
|
MenuAPI: MenuAPI.default,
|
||||||
|
NotificationAPI: NotificationAPI.default,
|
||||||
|
ObjectAPI: ObjectAPI,
|
||||||
|
StatusAPI: StatusAPI.default,
|
||||||
|
TelemetryAPI: TelemetryAPI,
|
||||||
|
TimeAPI: TimeAPI,
|
||||||
|
TypeRegistry: TypeRegistry,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -215,12 +215,12 @@ define([
|
|||||||
* @memberof {module:openmct.CompositionCollection#}
|
* @memberof {module:openmct.CompositionCollection#}
|
||||||
* @name load
|
* @name load
|
||||||
*/
|
*/
|
||||||
CompositionCollection.prototype.load = function () {
|
CompositionCollection.prototype.load = function (abortSignal) {
|
||||||
this.cleanUpMutables();
|
this.cleanUpMutables();
|
||||||
|
|
||||||
return this.provider.load(this.domainObject)
|
return this.provider.load(this.domainObject)
|
||||||
.then(function (children) {
|
.then(function (children) {
|
||||||
return Promise.all(children.map((c) => this.publicAPI.objects.get(c)));
|
return Promise.all(children.map((c) => this.publicAPI.objects.get(c, abortSignal)));
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
.then(function (childObjects) {
|
.then(function (childObjects) {
|
||||||
childObjects.forEach(c => this.add(c, true));
|
childObjects.forEach(c => this.add(c, true));
|
||||||
|
|||||||
135
src/api/forms/CreateWizard.js
Normal file
135
src/api/forms/CreateWizard.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export default class CreateWizard {
|
||||||
|
constructor(openmct, domainObject, parent) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.type = openmct.types.get(domainObject.type);
|
||||||
|
|
||||||
|
this.model = domainObject;
|
||||||
|
this.parent = parent;
|
||||||
|
this.properties = this.type.definition.form || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addNotes(sections) {
|
||||||
|
const row = {
|
||||||
|
control: 'textarea',
|
||||||
|
cssClass: 'l-textarea-sm',
|
||||||
|
key: 'notes',
|
||||||
|
name: 'Notes',
|
||||||
|
required: false,
|
||||||
|
value: this.domainObject.notes
|
||||||
|
};
|
||||||
|
|
||||||
|
sections.forEach(section => {
|
||||||
|
if (section.name !== 'Properties') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.rows.unshift(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addTitle(sections) {
|
||||||
|
const row = {
|
||||||
|
control: 'textfield',
|
||||||
|
cssClass: 'l-input-lg',
|
||||||
|
key: 'name',
|
||||||
|
name: 'Title',
|
||||||
|
pattern: `\\S+`,
|
||||||
|
required: true,
|
||||||
|
value: this.domainObject.name
|
||||||
|
};
|
||||||
|
|
||||||
|
sections.forEach(section => {
|
||||||
|
if (section.name !== 'Properties') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.rows.unshift(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the form model for this wizard; this is a description
|
||||||
|
* that will be rendered to an HTML form. See the
|
||||||
|
* platform/forms bundle
|
||||||
|
* @param {boolean} includeLocation if true, a 'location' section
|
||||||
|
* will be included that will allow the user to select the location
|
||||||
|
* of the newly created object, otherwise the .location property of
|
||||||
|
* the model will be used.
|
||||||
|
*/
|
||||||
|
getFormStructure(includeLocation) {
|
||||||
|
let sections = [];
|
||||||
|
let domainObject = this.domainObject;
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
sections.push({
|
||||||
|
name: 'Properties',
|
||||||
|
rows: this.properties.map(property => {
|
||||||
|
const row = JSON.parse(JSON.stringify(property));
|
||||||
|
row.value = this.getValue(row);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}).filter(row => row && row.control)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addNotes(sections);
|
||||||
|
this.addTitle(sections);
|
||||||
|
|
||||||
|
// Ensure there is always a 'save in' section
|
||||||
|
if (includeLocation) {
|
||||||
|
function validateLocation(object, data) {
|
||||||
|
return self.openmct.composition.checkPolicy(data.value, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.push({
|
||||||
|
name: 'Location',
|
||||||
|
cssClass: 'grows',
|
||||||
|
rows: [{
|
||||||
|
name: 'Save In',
|
||||||
|
cssClass: 'grows',
|
||||||
|
control: 'locator',
|
||||||
|
domainObject,
|
||||||
|
required: true,
|
||||||
|
parent: this.parent,
|
||||||
|
validate: validateLocation.bind(this),
|
||||||
|
key: 'location'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sections
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(row) {
|
||||||
|
if (row.property) {
|
||||||
|
return row.property.reduce((acc, property) => acc && acc[property], this.domainObject);
|
||||||
|
} else {
|
||||||
|
return this.domainObject[row.key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/api/forms/FormsAPI.js
Normal file
159
src/api/forms/FormsAPI.js
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 EditPropertiesAction from './actions/EditPropertiesAction';
|
||||||
|
import FormProperties from './components/FormProperties.vue';
|
||||||
|
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export const CONTROLS = [
|
||||||
|
"autocomplete",
|
||||||
|
"button",
|
||||||
|
"checkbox",
|
||||||
|
"color",
|
||||||
|
"composite",
|
||||||
|
"datetime",
|
||||||
|
"dialog-button",
|
||||||
|
"file-input",
|
||||||
|
"menu-button",
|
||||||
|
"numberfield",
|
||||||
|
"radio",
|
||||||
|
"select",
|
||||||
|
"textarea",
|
||||||
|
"textfield"
|
||||||
|
];
|
||||||
|
|
||||||
|
export default class FormsAPI {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.controls = {};
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
addControl(name, actions) {
|
||||||
|
const control = this.controls[name];
|
||||||
|
if (control) {
|
||||||
|
this.openmct.notifications.error(`Error: provided form control '${name}', already exists`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.controls[name] = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllControls() {
|
||||||
|
return this.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
getControl(name) {
|
||||||
|
const control = this.controls[name];
|
||||||
|
if (control) {
|
||||||
|
console.error(`Error: form control '${name}', does not exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
showForm(formStructure, options) {
|
||||||
|
const changes = {};
|
||||||
|
let overlay;
|
||||||
|
|
||||||
|
let parentDomainObject = options.parentDomainObject || {};
|
||||||
|
const domainObject = options.domainObject;
|
||||||
|
const onSave = () => {
|
||||||
|
overlay.dismiss();
|
||||||
|
|
||||||
|
if(options.onSave) {
|
||||||
|
options.onSave(domainObject, changes, parentDomainObject);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDismiss = () => {
|
||||||
|
overlay.dismiss();
|
||||||
|
|
||||||
|
if (options.onDismiss) {
|
||||||
|
options.onDismiss();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const vm = new Vue({
|
||||||
|
components: { FormProperties },
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct,
|
||||||
|
domainObject
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formStructure,
|
||||||
|
onChange,
|
||||||
|
onDismiss,
|
||||||
|
onSave
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: '<FormProperties :model="formStructure" @onChange="onChange" @onDismiss="onDismiss" @onSave="onSave"></FormProperties>'
|
||||||
|
}).$mount();
|
||||||
|
|
||||||
|
overlay = this.openmct.overlays.overlay({
|
||||||
|
element: vm.$el,
|
||||||
|
size: 'small',
|
||||||
|
onDestroy: () => vm.$destroy()
|
||||||
|
});
|
||||||
|
|
||||||
|
function onChange(data) {
|
||||||
|
if (options.onChange) {
|
||||||
|
options.onChange(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.model) {
|
||||||
|
const property = data.model.property;
|
||||||
|
let key = data.model.key;
|
||||||
|
if (key === 'location') {
|
||||||
|
parentDomainObject = data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property && property.length) {
|
||||||
|
key = property.join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
changes[key] = data.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_addDefaultFormControls() {
|
||||||
|
CONTROLS.forEach(control => {
|
||||||
|
this.addControl(control);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
init() {
|
||||||
|
this.openmct.actions.register(new EditPropertiesAction(this.openmct));
|
||||||
|
|
||||||
|
this._addDefaultFormControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/api/forms/actions/CreateAction.js
Normal file
131
src/api/forms/actions/CreateAction.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 PropertiesAction from './PropertiesAction';
|
||||||
|
import CreateWizard from '../CreateWizard';
|
||||||
|
|
||||||
|
import uuid from 'uuid';
|
||||||
|
|
||||||
|
export default class CreateAction extends PropertiesAction {
|
||||||
|
constructor(openmct, type, parentDomainObject) {
|
||||||
|
super(openmct);
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
this.parentDomainObject = parentDomainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke() {
|
||||||
|
this._showCreateForm(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
|
||||||
|
async _onSave(domainObject, changes, parentDomainObject) {
|
||||||
|
Object.entries(changes).forEach(([key, value]) => {
|
||||||
|
const properties = key.split('.');
|
||||||
|
let object = this.domainObject;
|
||||||
|
const propertiesLength = properties.length;
|
||||||
|
properties.forEach((property, index) => {
|
||||||
|
const isComplexProperty = propertiesLength > 1 && index != propertiesLength - 1;
|
||||||
|
if (isComplexProperty && object[property] !== null) {
|
||||||
|
object = object[property];
|
||||||
|
} else {
|
||||||
|
object[property] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
object = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.domainObject.modified = Date.now();
|
||||||
|
this.domainObject.location = this.openmct.objects.makeKeyString(parentDomainObject.identifier);
|
||||||
|
this.domainObject.identifier.namespace = parentDomainObject.identifier.namespace;
|
||||||
|
|
||||||
|
// Show saving progress dialog
|
||||||
|
let dialog = this.openmct.overlays.progressDialog({
|
||||||
|
progressPerc: 'unknown',
|
||||||
|
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
|
||||||
|
iconClass: 'info',
|
||||||
|
title: 'Saving'
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = await this.openmct.objects.save(this.domainObject);
|
||||||
|
if (success) {
|
||||||
|
const compositionCollection = await openmct.composition.get(parentDomainObject);
|
||||||
|
compositionCollection.add(this.domainObject);
|
||||||
|
|
||||||
|
this._navigateAndEdit(this.domainObject);
|
||||||
|
|
||||||
|
this.openmct.notifications.info('Save successful');
|
||||||
|
} else {
|
||||||
|
this.openmct.notifications.error('Error saving objects');
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _navigateAndEdit(domainObject) {
|
||||||
|
const objectPath = await this.openmct.objects.getOriginalPath(domainObject.identifier);
|
||||||
|
|
||||||
|
const url = '#/browse/' + objectPath
|
||||||
|
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
|
||||||
|
.reverse()
|
||||||
|
.join('/');
|
||||||
|
|
||||||
|
window.location.href = url;
|
||||||
|
|
||||||
|
const objectView = openmct.objectViews.get(domainObject, objectPath)[0];
|
||||||
|
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
|
||||||
|
if (canEdit) {
|
||||||
|
openmct.editor.edit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_showCreateForm(type) {
|
||||||
|
const typeDefinition = this.openmct.types.get(type);
|
||||||
|
const definition = typeDefinition.definition;
|
||||||
|
const domainObject = {
|
||||||
|
name: `Unnamed ${definition.name}`,
|
||||||
|
type,
|
||||||
|
identifier: {
|
||||||
|
key: uuid(),
|
||||||
|
namespace: this.parentDomainObject.identifier.namespace
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
|
||||||
|
if (definition.initialize) {
|
||||||
|
definition.initialize(domainObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
const createWizard = new CreateWizard(this.openmct, domainObject, this.parentDomainObject);
|
||||||
|
const formStructure = createWizard.getFormStructure(true);
|
||||||
|
formStructure.title = 'Create a New ' + definition.name;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
domainObject,
|
||||||
|
onSave: this._onSave.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openmct.forms.showForm(formStructure, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/api/forms/actions/EditPropertiesAction.js
Normal file
102
src/api/forms/actions/EditPropertiesAction.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 PropertiesAction from './PropertiesAction';
|
||||||
|
import CreateWizard from '../CreateWizard';
|
||||||
|
export default class EditPropertiesAction extends PropertiesAction {
|
||||||
|
constructor(openmct) {
|
||||||
|
super(openmct);
|
||||||
|
|
||||||
|
this.name = 'Edit Properties...';
|
||||||
|
this.key = 'properties';
|
||||||
|
this.description = 'Edit properties of this object.';
|
||||||
|
this.cssClass = 'major icon-pencil';
|
||||||
|
this.hideInDefaultMenu = true;
|
||||||
|
this.group = 'action';
|
||||||
|
this.priority = 10;
|
||||||
|
this.formProperties = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
appliesTo(objectPath) {
|
||||||
|
const definition = this._getTypeDefinition(objectPath[0].type);
|
||||||
|
|
||||||
|
return definition && definition.creatable;
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke(objectPath) {
|
||||||
|
this._showEditForm(objectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
|
||||||
|
async _onSave(domainObject, changes, parentDomainObject) {
|
||||||
|
Object.entries(changes).forEach(([key, value]) => {
|
||||||
|
const properties = key.split('.');
|
||||||
|
let object = this.domainObject;
|
||||||
|
const propertiesLength = properties.length;
|
||||||
|
properties.forEach((property, index) => {
|
||||||
|
const isComplexProperty = propertiesLength > 1 && index != propertiesLength - 1;
|
||||||
|
if (isComplexProperty && object[property] !== null) {
|
||||||
|
object = object[property];
|
||||||
|
} else {
|
||||||
|
object[property] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
object = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.domainObject.modified = Date.now();
|
||||||
|
|
||||||
|
// Show saving progress dialog
|
||||||
|
let dialog = this.openmct.overlays.progressDialog({
|
||||||
|
progressPerc: 'unknown',
|
||||||
|
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
|
||||||
|
iconClass: 'info',
|
||||||
|
title: 'Saving'
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = await this.openmct.objects.save(this.domainObject);
|
||||||
|
if (success) {
|
||||||
|
this.openmct.notifications.info('Save successful');
|
||||||
|
} else {
|
||||||
|
this.openmct.notifications.error('Error saving objects');
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
_showEditForm(objectPath) {
|
||||||
|
this.domainObject = objectPath[0];
|
||||||
|
|
||||||
|
const createWizard = new CreateWizard(this.openmct, this.domainObject, objectPath[1]);
|
||||||
|
const formStructure = createWizard.getFormStructure(false);
|
||||||
|
formStructure.title = 'Edit ' + this.domainObject.name;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
domainObject: this.domainObject,
|
||||||
|
onSave: this._onSave.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openmct.forms.showForm(formStructure, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
* Administration. All rights reserved.
|
* Administration. All rights reserved.
|
||||||
*
|
*
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
* 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.
|
* 'License'); you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
* License for the specific language governing permissions and limitations
|
* License for the specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
@@ -20,22 +19,17 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
export default class PropertiesAction {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
}
|
||||||
|
|
||||||
define([
|
/**
|
||||||
'./Region'
|
* @private
|
||||||
], function (
|
*/
|
||||||
Region
|
_getTypeDefinition(type) {
|
||||||
) {
|
const TypeDefinition = this.openmct.types.get(type);
|
||||||
|
|
||||||
const PlotBrowseRegion = new Region({
|
return TypeDefinition.definition;
|
||||||
name: "plot-options",
|
}
|
||||||
title: "Plot Options",
|
}
|
||||||
modes: ['browse'],
|
|
||||||
content: {
|
|
||||||
key: "plot-options-browse"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return PlotBrowseRegion;
|
|
||||||
|
|
||||||
});
|
|
||||||
99
src/api/forms/components/FormProperties.vue
Normal file
99
src/api/forms/components/FormProperties.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form name="mctForm"
|
||||||
|
class="form c-form mct-form"
|
||||||
|
autocomplete="off"
|
||||||
|
@submit.prevent
|
||||||
|
>
|
||||||
|
<div class="mct-form__title c-overlay__top-bar">
|
||||||
|
<div class="c-overlay__dialog-title">{{ model.title }}</div>
|
||||||
|
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
||||||
|
</div>
|
||||||
|
<span v-for="section in model.sections"
|
||||||
|
:key="section.name"
|
||||||
|
class="mct-form__sections l-form-section c-form__section"
|
||||||
|
:class="section.cssClass"
|
||||||
|
>
|
||||||
|
<h2 class="c-form__header"
|
||||||
|
v-if="section.name"
|
||||||
|
>
|
||||||
|
{{ section.name }}
|
||||||
|
</h2>
|
||||||
|
<div v-for="(row, index) in section.rows"
|
||||||
|
:key="row.name"
|
||||||
|
>
|
||||||
|
<FormRow :css-class="section.cssClass"
|
||||||
|
:first="index < 1"
|
||||||
|
:row="row"
|
||||||
|
@onChange="onChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="mct-form__controls c-overlay__button-bar">
|
||||||
|
<button tabindex="0"
|
||||||
|
:disabled="isInvalid"
|
||||||
|
class="c-button c-button--major"
|
||||||
|
@click="onSave"
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</button>
|
||||||
|
<button tabindex="0"
|
||||||
|
class="c-button"
|
||||||
|
@click="onDismiss"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FormRow from "@/api/forms/components/FormRow.vue";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FormRow
|
||||||
|
},
|
||||||
|
inject: ['openmct', 'domainObject'],
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inValidProperties: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isInvalid() {
|
||||||
|
return Object.entries(this.inValidProperties)
|
||||||
|
.some(([key, value]) => {
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange(data) {
|
||||||
|
this.$set(this.inValidProperties, data.model.key, data.invalid);
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
},
|
||||||
|
onDismiss() {
|
||||||
|
this.$emit('onDismiss');
|
||||||
|
},
|
||||||
|
onSave() {
|
||||||
|
this.$emit('onSave');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
156
src/api/forms/components/FormRow.vue
Normal file
156
src/api/forms/components/FormRow.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="form-row c-form__row"
|
||||||
|
:class="rowClass"
|
||||||
|
@onChange="onChange"
|
||||||
|
>
|
||||||
|
<div v-if="row.name"
|
||||||
|
class="c-form__row__label label flex-elem"
|
||||||
|
:title="row.description"
|
||||||
|
>
|
||||||
|
{{ row.name }}
|
||||||
|
</div>
|
||||||
|
<div class="c-form__row__controls controls flex-elem">
|
||||||
|
<div v-if="row.control"
|
||||||
|
class="c-form__controls-wrapper wrapper"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="getComponent"
|
||||||
|
:key="row.key"
|
||||||
|
:ref="`form-control-${row.key}`"
|
||||||
|
:model="row"
|
||||||
|
:value="row.value"
|
||||||
|
:required="isRequired"
|
||||||
|
@onChange="onChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AutoCompleteField from "@/api/forms/components/controls/AutoCompleteField.vue";
|
||||||
|
import Composite from "@/api/forms/components/controls/Composite.vue";
|
||||||
|
import FileInput from "@/api/forms/components/controls/FileInput.vue"
|
||||||
|
import Locator from "@/api/forms/components/controls/Locator.vue";
|
||||||
|
import NumberField from "@/api/forms/components/controls/NumberField.vue";
|
||||||
|
import SelectField from '@/api/forms/components/controls/SelectField.vue';
|
||||||
|
import TextArea from "@/api/forms/components/controls/TextArea.vue";
|
||||||
|
import TextField from "@/api/forms/components/controls/TextField.vue";
|
||||||
|
|
||||||
|
const CONTROL_TYPE_VIEW_MAP = {
|
||||||
|
'autocomplete': AutoCompleteField,
|
||||||
|
'composite': Composite,
|
||||||
|
'file-input': FileInput,
|
||||||
|
'locator': Locator,
|
||||||
|
'numberfield': NumberField,
|
||||||
|
'select': SelectField,
|
||||||
|
'textarea': TextArea,
|
||||||
|
'textfield': TextField,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FormRow',
|
||||||
|
components: CONTROL_TYPE_VIEW_MAP,
|
||||||
|
inject: ['openmct', 'domainObject'],
|
||||||
|
props: {
|
||||||
|
cssClass: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
first: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
valid: undefined,
|
||||||
|
visited: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
getComponent() {
|
||||||
|
return CONTROL_TYPE_VIEW_MAP[this.row.control];
|
||||||
|
},
|
||||||
|
isRequired() {
|
||||||
|
//TODO: Check if field is required
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
rowClass() {
|
||||||
|
let cssClass = this.cssClass;
|
||||||
|
|
||||||
|
if (this.first === true) {
|
||||||
|
cssClass = `${cssClass} first`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.row.required) {
|
||||||
|
cssClass = `${cssClass} req`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.row.layout === 'controls-first') {
|
||||||
|
cssClass = `${cssClass} l-controls-first`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.row.layout === 'controls-under') {
|
||||||
|
cssClass = `${cssClass} l-controls-under`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.visited && this.valid !== undefined) {
|
||||||
|
if (this.valid === true) {
|
||||||
|
cssClass = `${cssClass} valid`;
|
||||||
|
} else {
|
||||||
|
cssClass = `${cssClass} invalid`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cssClass;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.row.required) {
|
||||||
|
const data = {
|
||||||
|
model: this.row,
|
||||||
|
value: this.row.value
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onChange(data, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange(data, visited = true) {
|
||||||
|
this.visited = visited;
|
||||||
|
|
||||||
|
const valid = this.validateRow(data);
|
||||||
|
this.valid = valid;
|
||||||
|
data.invalid = !valid;
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
},
|
||||||
|
validateRow(data) {
|
||||||
|
let valid = true;
|
||||||
|
if (this.row.required) {
|
||||||
|
valid = data.value !== undefined && data.value !== null && data.value !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern = data.model.pattern;
|
||||||
|
if (valid && pattern) {
|
||||||
|
const regex = new RegExp(pattern);
|
||||||
|
valid = regex.test(data.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = data.model.validate;
|
||||||
|
if (valid && validate) {
|
||||||
|
valid = validate(this.domainObject, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
175
src/api/forms/components/controls/AutoCompleteField.vue
Normal file
175
src/api/forms/components/controls/AutoCompleteField.vue
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="form-control autocomplete">
|
||||||
|
<input v-model="field"
|
||||||
|
class="autocompleteInput"
|
||||||
|
type="text"
|
||||||
|
@click="inputClicked()"
|
||||||
|
@keydown="keyDown($event)"
|
||||||
|
>
|
||||||
|
<span class="icon-arrow-down"
|
||||||
|
@click="arrowClicked()"
|
||||||
|
></span>
|
||||||
|
<div
|
||||||
|
class="autocompleteOptions"
|
||||||
|
@blur="hideOptions = true"
|
||||||
|
>
|
||||||
|
<ul v-if="!hideOptions">
|
||||||
|
<li v-for="opt in filteredOptions"
|
||||||
|
:key="opt.optionId"
|
||||||
|
:class="{'optionPreSelected': optionIndex === opt.optionId}"
|
||||||
|
@click="fillInputWithString(opt.name)"
|
||||||
|
@mouseover="optionMouseover(opt.optionId)"
|
||||||
|
>
|
||||||
|
<span class="optionText">{{ opt.name }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const key = {
|
||||||
|
down: 40,
|
||||||
|
up: 38,
|
||||||
|
enter: 13
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hideOptions: true,
|
||||||
|
optionIndex: 0,
|
||||||
|
field: this.model.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed : {
|
||||||
|
filteredOptions() {
|
||||||
|
const options = this.optionNames || [];
|
||||||
|
return options
|
||||||
|
.filter(option => {
|
||||||
|
return option.toLowerCase().indexOf(this.field.toLowerCase()) >= 0;
|
||||||
|
}).map((option, index) => {
|
||||||
|
return {
|
||||||
|
optionId: index,
|
||||||
|
name: option
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.options = this.model.options;
|
||||||
|
this.autocompleteInputElement = this.$el.getElementsByClassName('autocompleteInput')[0];
|
||||||
|
if (this.options[0].name) {
|
||||||
|
// If "options" include name, value pair
|
||||||
|
this.optionNames = this.options.map((opt) => {
|
||||||
|
return opt.name;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If options is only an array of string.
|
||||||
|
this.optionNames = this.options;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
decrementOptionIndex() {
|
||||||
|
if (this.optionIndex === 0) {
|
||||||
|
this.optionIndex = this.filteredOptions.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.optionIndex--;
|
||||||
|
this.scrollIntoView();
|
||||||
|
},
|
||||||
|
incrementOptionIndex() {
|
||||||
|
if (this.optionIndex === this.filteredOptions.length - 1) {
|
||||||
|
this.optionIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.optionIndex++;
|
||||||
|
this.scrollIntoView();
|
||||||
|
},
|
||||||
|
fillInputWithString(string) {
|
||||||
|
this.hideOptions = true;
|
||||||
|
this.field = string;
|
||||||
|
},
|
||||||
|
showOptions(string) {
|
||||||
|
this.hideOptions = false;
|
||||||
|
this.optionIndex = 0;
|
||||||
|
},
|
||||||
|
keyDown($event) {
|
||||||
|
if (this.filteredOptions) {
|
||||||
|
let keyCode = $event.keyCode;
|
||||||
|
switch (keyCode) {
|
||||||
|
case key.down:
|
||||||
|
this.incrementOptionIndex();
|
||||||
|
break;
|
||||||
|
case key.up:
|
||||||
|
$event.preventDefault(); // Prevents cursor jumping back and forth
|
||||||
|
this.decrementOptionIndex();
|
||||||
|
break;
|
||||||
|
case key.enter:
|
||||||
|
if (this.filteredOptions[this.optionIndex]) {
|
||||||
|
this.fillInputWithString(this.filteredOptions[this.optionIndex].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputClicked() {
|
||||||
|
this.autocompleteInputElement.select();
|
||||||
|
this.showOptions(this.autocompleteInputElement.value);
|
||||||
|
},
|
||||||
|
arrowClicked() {
|
||||||
|
this.autocompleteInputElement.select();
|
||||||
|
this.showOptions('');
|
||||||
|
},
|
||||||
|
optionMouseover(optionId) {
|
||||||
|
this.optionIndex = optionId;
|
||||||
|
},
|
||||||
|
scrollIntoView() {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.$el.querySelector('.optionPreSelected');
|
||||||
|
|
||||||
|
element && element.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'nearest'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
field(newValue, oldValue) {
|
||||||
|
if (newValue !== oldValue) {
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
model: this.model,
|
||||||
|
value: newValue
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
56
src/api/forms/components/controls/Composite.vue
Normal file
56
src/api/forms/components/controls/Composite.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<span>
|
||||||
|
<CompositeItem v-for="(item, index) in model.items"
|
||||||
|
:key="item.name"
|
||||||
|
:first="index < 1"
|
||||||
|
:value="JSON.stringify(model.value[index])"
|
||||||
|
:item="item"
|
||||||
|
@onChange="onChange"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CompositeItem from "@/api/forms/components/controls/CompositeItem.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CompositeItem
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange(data) {
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.model.items.forEach((item, index) => item.key = `${this.model.key}.${index}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
71
src/api/forms/components/controls/CompositeItem.vue
Normal file
71
src/api/forms/components/controls/CompositeItem.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div :class="compositeCssClass">
|
||||||
|
<FormRow :css-class="item.cssClass"
|
||||||
|
:first="first"
|
||||||
|
:row="row"
|
||||||
|
@onChange="onChange"
|
||||||
|
/>
|
||||||
|
<span class="composite-control-label">
|
||||||
|
{{ item.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FormRow: () => import('@/api/forms/components/FormRow.vue')
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
first: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
compositeCssClass() {
|
||||||
|
return `l-composite-control l-${this.item.control}`;
|
||||||
|
},
|
||||||
|
row() {
|
||||||
|
const row = this.item;
|
||||||
|
row.value = JSON.parse(this.value);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange(data) {
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
71
src/api/forms/components/controls/FileInput.vue
Normal file
71
src/api/forms/components/controls/FileInput.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<span class="form-control shell">
|
||||||
|
<span class="field control"
|
||||||
|
:class="model.cssClass"
|
||||||
|
>
|
||||||
|
<input ref="fileInput" id="fileElem" type="file" accept=".json" style="display:none">
|
||||||
|
<button id="fileSelect"
|
||||||
|
class="c-button"
|
||||||
|
@click="selectFile"
|
||||||
|
>{{ name }}</button>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fileInfo: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
name() {
|
||||||
|
const fileInfo = this.fileInfo || this.model.value;
|
||||||
|
|
||||||
|
return fileInfo && fileInfo.name || this.model.text;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$refs.fileInput.addEventListener("change", this.handleFiles, false);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleFiles() {
|
||||||
|
const fileList = this.$refs.fileInput.files;
|
||||||
|
this.readFile(fileList[0]);
|
||||||
|
},
|
||||||
|
readFile(file) {
|
||||||
|
const self = this;
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
const fileInfo = {};
|
||||||
|
fileInfo.name = file.name;
|
||||||
|
fileReader.onload = function (event) {
|
||||||
|
fileInfo.body = event.target.result;
|
||||||
|
self.fileInfo = fileInfo;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
model: self.model,
|
||||||
|
value: fileInfo
|
||||||
|
};
|
||||||
|
self.$emit('onChange', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.onerror = function (error) {
|
||||||
|
console.error('fileReader error', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
},
|
||||||
|
selectFile() {
|
||||||
|
this.$refs.fileInput.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
50
src/api/forms/components/controls/Locator.vue
Normal file
50
src/api/forms/components/controls/Locator.vue
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<ConditionSetSelectorDialog
|
||||||
|
:hideTitle="true"
|
||||||
|
:ignoreTypeCheck="true"
|
||||||
|
:cssClass="`form-locator`"
|
||||||
|
:parent="model.parent"
|
||||||
|
@conditionSetSelected="handleItemSelection"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConditionSetSelectorDialog from '@/plugins/condition/components/inspector/ConditionSetSelectorDialog.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ConditionSetSelectorDialog
|
||||||
|
},
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// remove following after css fix
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelector('.c-overlay__contents-main').style.height = '200px';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleItemSelection(parentDomainObject) {
|
||||||
|
const data = { model: this.model, value: parentDomainObject };
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
62
src/api/forms/components/controls/NumberField.vue
Normal file
62
src/api/forms/components/controls/NumberField.vue
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<span class="form-control shell">
|
||||||
|
<span class="field control"
|
||||||
|
:class="model.cssClass"
|
||||||
|
>
|
||||||
|
<input v-model="field"
|
||||||
|
type="number"
|
||||||
|
:min="model.min"
|
||||||
|
:max="model.max"
|
||||||
|
:step="model.step"
|
||||||
|
@blur="blur()"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
field: this.model.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
blur() {
|
||||||
|
const data = {
|
||||||
|
model :this.model,
|
||||||
|
value: this.field
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
42
src/api/forms/components/controls/SelectField.vue
Normal file
42
src/api/forms/components/controls/SelectField.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class='form-control SelectField'>
|
||||||
|
<select required="model.required"
|
||||||
|
name="mctControl"
|
||||||
|
@change="onChange($event)"
|
||||||
|
v-model="selected"
|
||||||
|
>
|
||||||
|
<option v-for="option in model.options"
|
||||||
|
:key="option.name"
|
||||||
|
:value="option.value"
|
||||||
|
>
|
||||||
|
{{ option.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selected: this.model.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange() {
|
||||||
|
const data = {
|
||||||
|
model: this.model,
|
||||||
|
value: this.selected
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
60
src/api/forms/components/controls/TextArea.vue
Normal file
60
src/api/forms/components/controls/TextArea.vue
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<span class="form-control shell">
|
||||||
|
<span class="field control"
|
||||||
|
:class="model.cssClass"
|
||||||
|
>
|
||||||
|
<textarea v-model="field"
|
||||||
|
type="text"
|
||||||
|
:size="model.size"
|
||||||
|
@blur="blur()"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
field: this.model.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
blur() {
|
||||||
|
const data = {
|
||||||
|
model :this.model,
|
||||||
|
value: this.field
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -19,13 +19,42 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div ng-if="!domainObject.model.locked && domainObject.getCapability('editor').inEditContext()">
|
<template>
|
||||||
<mct-representation key="'plot-options-edit'"
|
<span class="form-control shell">
|
||||||
mct-object="domainObject">
|
<span class="field control"
|
||||||
</mct-representation>
|
:class="model.cssClass"
|
||||||
</div>
|
>
|
||||||
<div ng-if="domainObject.model.locked || !domainObject.getCapability('editor').inEditContext()">
|
<input v-model="field"
|
||||||
<mct-representation key="'plot-options-browse'"
|
type="text"
|
||||||
mct-object="domainObject">
|
:size="model.size"
|
||||||
</mct-representation>
|
@blur="blur()"
|
||||||
</div>
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
field: this.model.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
blur() {
|
||||||
|
const data = {
|
||||||
|
model :this.model,
|
||||||
|
value: this.field
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$emit('onChange', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -76,7 +76,7 @@ describe ('The Menu API', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("showMenu method", () => {
|
describe("showMenu method", () => {
|
||||||
|
|||||||
@@ -76,7 +76,10 @@ class MutableDomainObject {
|
|||||||
}
|
}
|
||||||
$set(path, value) {
|
$set(path, value) {
|
||||||
_.set(this, path, value);
|
_.set(this, path, value);
|
||||||
_.set(this, 'modified', Date.now());
|
|
||||||
|
if (path !== 'persisted' && path !== 'modified') {
|
||||||
|
_.set(this, 'modified', Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
//Emit secret synchronization event first, so that all objects are in sync before subsequent events fired.
|
//Emit secret synchronization event first, so that all objects are in sync before subsequent events fired.
|
||||||
this._globalEventEmitter.emit(qualifiedEventName(this, '$_synchronize_model'), this);
|
this._globalEventEmitter.emit(qualifiedEventName(this, '$_synchronize_model'), this);
|
||||||
@@ -112,9 +115,11 @@ class MutableDomainObject {
|
|||||||
return () => this._instanceEventEmitter.off(event, callback);
|
return () => this._instanceEventEmitter.off(event, callback);
|
||||||
}
|
}
|
||||||
$destroy() {
|
$destroy() {
|
||||||
this._observers.forEach(observer => observer());
|
while (this._observers.length > 0) {
|
||||||
delete this._globalEventEmitter;
|
const observer = this._observers.pop();
|
||||||
delete this._observers;
|
observer();
|
||||||
|
}
|
||||||
|
|
||||||
this._instanceEventEmitter.emit('$_destroy');
|
this._instanceEventEmitter.emit('$_destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ ObjectAPI.prototype.addProvider = function (namespace, provider) {
|
|||||||
|
|
||||||
ObjectAPI.prototype.get = function (identifier, abortSignal) {
|
ObjectAPI.prototype.get = function (identifier, abortSignal) {
|
||||||
let keystring = this.makeKeyString(identifier);
|
let keystring = this.makeKeyString(identifier);
|
||||||
|
|
||||||
if (this.cache[keystring] !== undefined) {
|
if (this.cache[keystring] !== undefined) {
|
||||||
return this.cache[keystring];
|
return this.cache[keystring];
|
||||||
}
|
}
|
||||||
@@ -176,15 +177,16 @@ ObjectAPI.prototype.get = function (identifier, abortSignal) {
|
|||||||
throw new Error('Provider does not support get!');
|
throw new Error('Provider does not support get!');
|
||||||
}
|
}
|
||||||
|
|
||||||
let objectPromise = provider.get(identifier, abortSignal);
|
let objectPromise = provider.get(identifier, abortSignal).then(result => {
|
||||||
this.cache[keystring] = objectPromise;
|
|
||||||
|
|
||||||
return objectPromise.then(result => {
|
|
||||||
delete this.cache[keystring];
|
delete this.cache[keystring];
|
||||||
result = this.applyGetInterceptors(identifier, result);
|
result = this.applyGetInterceptors(identifier, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.cache[keystring] = objectPromise;
|
||||||
|
|
||||||
|
return objectPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -484,6 +486,12 @@ ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ObjectAPI.prototype.isObjectPathToALink = function (domainObject, objectPath) {
|
||||||
|
return objectPath !== undefined
|
||||||
|
&& objectPath.length > 1
|
||||||
|
&& domainObject.location !== this.makeKeyString(objectPath[1].identifier);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uniquely identifies a domain object.
|
* Uniquely identifies a domain object.
|
||||||
*
|
*
|
||||||
@@ -520,8 +528,10 @@ ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function hasAlreadyBeenPersisted(domainObject) {
|
function hasAlreadyBeenPersisted(domainObject) {
|
||||||
return domainObject.persisted !== undefined
|
const result = domainObject.persisted !== undefined
|
||||||
&& domainObject.persisted === domainObject.modified;
|
&& domainObject.persisted >= domainObject.modified;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectAPI;
|
export default ObjectAPI;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe("The Status API", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("set function", () => {
|
describe("set function", () => {
|
||||||
|
|||||||
@@ -504,6 +504,26 @@ define([
|
|||||||
return this.getLimitEvaluator(domainObject);
|
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.
|
* Get a limit evaluator for this domain object.
|
||||||
* Limit Evaluators help you evaluate limit and alarm status of individual
|
* Limit Evaluators help you evaluate limit and alarm status of individual
|
||||||
@@ -531,5 +551,42 @@ define([
|
|||||||
return provider.getLimitEvaluator(domainObject);
|
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) {
|
||||||
|
return {
|
||||||
|
limits: function () {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider.getLimits(domainObject);
|
||||||
|
};
|
||||||
|
|
||||||
return TelemetryAPI;
|
return TelemetryAPI;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ const DEFAULTS = [
|
|||||||
'platform/features/clock',
|
'platform/features/clock',
|
||||||
'platform/features/hyperlink',
|
'platform/features/hyperlink',
|
||||||
'platform/features/timeline',
|
'platform/features/timeline',
|
||||||
'platform/forms',
|
|
||||||
'platform/identity',
|
'platform/identity',
|
||||||
'platform/persistence/aggregator',
|
'platform/persistence/aggregator',
|
||||||
'platform/persistence/queue',
|
'platform/persistence/queue',
|
||||||
@@ -85,12 +84,10 @@ define([
|
|||||||
'../platform/features/hyperlink/bundle',
|
'../platform/features/hyperlink/bundle',
|
||||||
'../platform/features/static-markup/bundle',
|
'../platform/features/static-markup/bundle',
|
||||||
'../platform/features/timeline/bundle',
|
'../platform/features/timeline/bundle',
|
||||||
'../platform/forms/bundle',
|
|
||||||
'../platform/framework/bundle',
|
'../platform/framework/bundle',
|
||||||
'../platform/framework/src/load/Bundle',
|
'../platform/framework/src/load/Bundle',
|
||||||
'../platform/identity/bundle',
|
'../platform/identity/bundle',
|
||||||
'../platform/persistence/aggregator/bundle',
|
'../platform/persistence/aggregator/bundle',
|
||||||
'../platform/persistence/couch/bundle',
|
|
||||||
'../platform/persistence/elastic/bundle',
|
'../platform/persistence/elastic/bundle',
|
||||||
'../platform/persistence/local/bundle',
|
'../platform/persistence/local/bundle',
|
||||||
'../platform/persistence/queue/bundle',
|
'../platform/persistence/queue/bundle',
|
||||||
|
|||||||
@@ -45,10 +45,14 @@ export default function LADTableSetViewProvider(openmct) {
|
|||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
domainObject,
|
|
||||||
objectPath
|
objectPath
|
||||||
},
|
},
|
||||||
template: '<lad-table-set></lad-table-set>'
|
data() {
|
||||||
|
return {
|
||||||
|
domainObject
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroy: function (element) {
|
destroy: function (element) {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
objectPath: {
|
pathToTable: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
@@ -66,20 +66,19 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
let currentObjectPath = this.objectPath.slice();
|
|
||||||
currentObjectPath.unshift(this.domainObject);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timestamp: undefined,
|
timestamp: undefined,
|
||||||
value: '---',
|
value: '---',
|
||||||
valueClass: '',
|
valueClass: '',
|
||||||
currentObjectPath,
|
|
||||||
unit: ''
|
unit: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
formattedTimestamp() {
|
formattedTimestamp() {
|
||||||
return this.timestamp !== undefined ? this.getFormattedTimestamp(this.timestamp) : '---';
|
return this.timestamp !== undefined ? this.getFormattedTimestamp(this.timestamp) : '---';
|
||||||
|
},
|
||||||
|
objectPath() {
|
||||||
|
return [this.domainObject, ...this.pathToTable];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -182,7 +181,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
showContextMenu(event) {
|
showContextMenu(event) {
|
||||||
let actionCollection = this.openmct.actions.get(this.currentObjectPath, this.getView());
|
let actionCollection = this.openmct.actions.get(this.objectPath, this.getView());
|
||||||
let allActions = actionCollection.getActionsObject();
|
let allActions = actionCollection.getActionsObject();
|
||||||
let applicableActions = CONTEXT_MENU_ACTIONS.map(key => allActions[key]);
|
let applicableActions = CONTEXT_MENU_ACTIONS.map(key => allActions[key]);
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,10 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<lad-row
|
<lad-row
|
||||||
v-for="item in items"
|
v-for="ladRow in items"
|
||||||
:key="item.key"
|
:key="ladRow.key"
|
||||||
:domain-object="item.domainObject"
|
:domain-object="ladRow.domainObject"
|
||||||
:object-path="objectPath"
|
:path-to-table="objectPath"
|
||||||
:has-units="hasUnits"
|
:has-units="hasUnits"
|
||||||
/>
|
/>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -43,9 +43,10 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<lad-row
|
<lad-row
|
||||||
v-for="telemetryObject in ladTelemetryObjects[ladTable.key]"
|
v-for="ladRow in ladTelemetryObjects[ladTable.key]"
|
||||||
:key="telemetryObject.key"
|
:key="ladRow.key"
|
||||||
:domain-object="telemetryObject.domainObject"
|
:domain-object="ladRow.domainObject"
|
||||||
|
:path-to-table="ladTable.objectPath"
|
||||||
:has-units="hasUnits"
|
:has-units="hasUnits"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -60,7 +61,13 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
LadRow
|
LadRow
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'domainObject'],
|
inject: ['openmct', 'objectPath'],
|
||||||
|
props: {
|
||||||
|
domainObject: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ladTableObjects: [],
|
ladTableObjects: [],
|
||||||
@@ -106,6 +113,7 @@ export default {
|
|||||||
let ladTable = {};
|
let ladTable = {};
|
||||||
ladTable.domainObject = domainObject;
|
ladTable.domainObject = domainObject;
|
||||||
ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
ladTable.objectPath = [domainObject, ...this.objectPath];
|
||||||
|
|
||||||
this.$set(this.ladTelemetryObjects, ladTable.key, []);
|
this.$set(this.ladTelemetryObjects, ladTable.key, []);
|
||||||
this.ladTableObjects.push(ladTable);
|
this.ladTableObjects.push(ladTable);
|
||||||
|
|||||||
@@ -292,6 +292,11 @@ describe("The LAD Table Set", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
openmct.time.timeSystem('utc', {
|
||||||
|
start: 0,
|
||||||
|
end: 1
|
||||||
|
});
|
||||||
|
|
||||||
return resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,6 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import {
|
|
||||||
getAllSearchParams,
|
|
||||||
setAllSearchParams
|
|
||||||
} from 'utils/openmctLocation';
|
|
||||||
|
|
||||||
const TIME_EVENTS = ['timeSystem', 'clock', 'clockOffsets'];
|
const TIME_EVENTS = ['timeSystem', 'clock', 'clockOffsets'];
|
||||||
const SEARCH_MODE = 'tc.mode';
|
const SEARCH_MODE = 'tc.mode';
|
||||||
@@ -49,9 +45,8 @@ export default class URLTimeSettingsSynchronizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.updateTimeSettings();
|
this.openmct.router.on('change:params', this.updateTimeSettings);
|
||||||
|
|
||||||
window.addEventListener('hashchange', this.updateTimeSettings);
|
|
||||||
TIME_EVENTS.forEach(event => {
|
TIME_EVENTS.forEach(event => {
|
||||||
this.openmct.time.on(event, this.setUrlFromTimeApi);
|
this.openmct.time.on(event, this.setUrlFromTimeApi);
|
||||||
});
|
});
|
||||||
@@ -59,7 +54,8 @@ export default class URLTimeSettingsSynchronizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
window.removeEventListener('hashchange', this.updateTimeSettings);
|
this.openmct.router.off('change:params', this.updateTimeSettings);
|
||||||
|
|
||||||
this.openmct.off('start', this.initialize);
|
this.openmct.off('start', this.initialize);
|
||||||
this.openmct.off('destroy', this.destroy);
|
this.openmct.off('destroy', this.destroy);
|
||||||
|
|
||||||
@@ -70,22 +66,18 @@ export default class URLTimeSettingsSynchronizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTimeSettings() {
|
updateTimeSettings() {
|
||||||
// Prevent from triggering self
|
let timeParameters = this.parseParametersFromUrl();
|
||||||
if (!this.isUrlUpdateInProgress) {
|
|
||||||
let timeParameters = this.parseParametersFromUrl();
|
|
||||||
|
|
||||||
if (this.areTimeParametersValid(timeParameters)) {
|
if (this.areTimeParametersValid(timeParameters)) {
|
||||||
this.setTimeApiFromUrl(timeParameters);
|
this.setTimeApiFromUrl(timeParameters);
|
||||||
} else {
|
this.openmct.router.setLocationFromUrl();
|
||||||
this.setUrlFromTimeApi();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.isUrlUpdateInProgress = false;
|
this.setUrlFromTimeApi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseParametersFromUrl() {
|
parseParametersFromUrl() {
|
||||||
let searchParams = getAllSearchParams();
|
let searchParams = this.openmct.router.getAllSearchParams();
|
||||||
|
|
||||||
let mode = searchParams.get(SEARCH_MODE);
|
let mode = searchParams.get(SEARCH_MODE);
|
||||||
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
|
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
|
||||||
@@ -148,7 +140,7 @@ export default class URLTimeSettingsSynchronizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setUrlFromTimeApi() {
|
setUrlFromTimeApi() {
|
||||||
let searchParams = getAllSearchParams();
|
let searchParams = this.openmct.router.getAllSearchParams();
|
||||||
let clock = this.openmct.time.clock();
|
let clock = this.openmct.time.clock();
|
||||||
let bounds = this.openmct.time.bounds();
|
let bounds = this.openmct.time.bounds();
|
||||||
let clockOffsets = this.openmct.time.clockOffsets();
|
let clockOffsets = this.openmct.time.clockOffsets();
|
||||||
@@ -176,8 +168,7 @@ export default class URLTimeSettingsSynchronizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
|
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
|
||||||
this.isUrlUpdateInProgress = true;
|
this.openmct.router.setAllSearchParams(searchParams);
|
||||||
setAllSearchParams(searchParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
areTimeParametersValid(timeParameters) {
|
areTimeParametersValid(timeParameters) {
|
||||||
|
|||||||
@@ -25,306 +25,118 @@ import {
|
|||||||
} from 'utils/testing';
|
} from 'utils/testing';
|
||||||
|
|
||||||
describe("The URLTimeSettingsSynchronizer", () => {
|
describe("The URLTimeSettingsSynchronizer", () => {
|
||||||
|
let appHolder;
|
||||||
let openmct;
|
let openmct;
|
||||||
let testClock;
|
let resolveFunction;
|
||||||
|
let oldHash;
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
openmct = createOpenMct();
|
openmct = createOpenMct();
|
||||||
|
openmct.install(openmct.plugins.MyItems());
|
||||||
openmct.install(openmct.plugins.LocalTimeSystem());
|
openmct.install(openmct.plugins.LocalTimeSystem());
|
||||||
testClock = jasmine.createSpyObj("testClock", ["start", "stop", "tick", "currentValue", "on", "off"]);
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
testClock.key = "test-clock";
|
|
||||||
testClock.currentValue.and.returnValue(0);
|
|
||||||
|
|
||||||
openmct.time.addClock(testClock);
|
|
||||||
|
|
||||||
openmct.on('start', done);
|
openmct.on('start', done);
|
||||||
openmct.startHeadless();
|
|
||||||
|
appHolder = document.createElement("div");
|
||||||
|
openmct.start(appHolder);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => resetApplicationState(openmct));
|
afterEach(() => {
|
||||||
|
openmct.time.stopClock();
|
||||||
|
openmct.router.removeListener('change:hash', resolveFunction);
|
||||||
|
|
||||||
describe("realtime mode", () => {
|
appHolder = undefined;
|
||||||
it("when the clock is set via the time API, it is immediately reflected in the URL", () => {
|
openmct = undefined;
|
||||||
//Test expected initial conditions
|
resolveFunction = undefined;
|
||||||
|
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initial clock is set to fixed is reflected in URL", (done) => {
|
||||||
|
resolveFunction = () => {
|
||||||
|
oldHash = window.location.hash;
|
||||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
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', {
|
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,
|
start: -2000,
|
||||||
end: 200
|
end: 200
|
||||||
});
|
});
|
||||||
expect(window.location.hash.includes('tc.startDelta=2000')).toBe(true);
|
|
||||||
expect(window.location.hash.includes('tc.endDelta=200')).toBe(true);
|
|
||||||
|
|
||||||
//Test that expected initial conditions are no longer true
|
const hasStartDelta = window.location.hash.includes('tc.startDelta=2000');
|
||||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
const hasEndDelta = window.location.hash.includes('tc.endDelta=200');
|
||||||
});
|
const hasLocalClock = window.location.hash.includes('tc.mode=local');
|
||||||
describe("when set in the url", () => {
|
success = hasStartDelta && hasEndDelta && hasLocalClock;
|
||||||
it("will change from fixed to realtime mode when the mode changes", () => {
|
if (success) {
|
||||||
expectLocationToBeInFixedMode();
|
expect(success).toBe(true);
|
||||||
|
|
||||||
return switchToRealtimeMode().then(() => {
|
openmct.router.removeListener('change:hash', resolveFunction);
|
||||||
let clock = openmct.time.clock();
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
expect(clock).toBeDefined();
|
openmct.router.on('change:hash', resolveFunction);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function setRealtimeLocationParameters() {
|
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
|
||||||
let hash = window.location.hash.toString()
|
let success;
|
||||||
.replace('tc.mode=fixed', 'tc.mode=local')
|
|
||||||
.replace('tc.startBound=0', 'tc.startDelta=1000')
|
|
||||||
.replace('tc.endBound=1', 'tc.endDelta=100');
|
|
||||||
|
|
||||||
window.location.hash = hash;
|
resolveFunction = () => {
|
||||||
}
|
let hash = window.location.hash;
|
||||||
|
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
|
||||||
|
window.location.hash = hash;
|
||||||
|
|
||||||
function setFixedLocationParameters() {
|
success = window.location.hash.includes('tc.mode=local');
|
||||||
let hash = window.location.hash.toString()
|
if (success) {
|
||||||
.replace('tc.mode=local', 'tc.mode=fixed')
|
expect(success).toBe(true);
|
||||||
.replace('tc.timeSystem=utc', 'tc.timeSystem=local')
|
done();
|
||||||
.replace('tc.startDelta=1000', 'tc.startBound=50')
|
}
|
||||||
.replace('tc.endDelta=100', 'tc.endBound=60');
|
};
|
||||||
|
|
||||||
window.location.hash = hash;
|
openmct.router.on('change:hash', resolveFunction);
|
||||||
}
|
});
|
||||||
|
|
||||||
function switchToRealtimeMode() {
|
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
|
||||||
let resolveFunction;
|
let success;
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
resolveFunction = () => {
|
||||||
resolveFunction = resolve;
|
let hash = window.location.hash;
|
||||||
openmct.time.on('clock', resolveFunction);
|
|
||||||
setRealtimeLocationParameters();
|
|
||||||
}).then(() => {
|
|
||||||
openmct.time.off('clock', resolveFunction);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchToFixedMode() {
|
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
|
||||||
let resolveFunction;
|
window.location.hash = hash;
|
||||||
|
success = window.location.hash.includes('tc.mode=local');
|
||||||
|
if (success) {
|
||||||
|
expect(success).toBe(true);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
openmct.router.on('change:hash', resolveFunction);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectLocationToBeInRealtimeMode() {
|
it("reset hash", (done) => {
|
||||||
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
|
let success;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectLocationToBeInFixedMode() {
|
window.location.hash = oldHash;
|
||||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
resolveFunction = () => {
|
||||||
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
|
success = window.location.hash === oldHash;
|
||||||
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
|
if (success) {
|
||||||
expect(window.location.hash.includes('tc.mode=local')).toBe(false);
|
expect(success).toBe(true);
|
||||||
}
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
openmct.router.on('change:hash', resolveFunction);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ export default class Condition extends EventEmitter {
|
|||||||
if (this.isAnyOrAllTelemetry(criterion)) {
|
if (this.isAnyOrAllTelemetry(criterion)) {
|
||||||
criterion.updateResult(datum, this.conditionManager.telemetryObjects);
|
criterion.updateResult(datum, this.conditionManager.telemetryObjects);
|
||||||
} else {
|
} else {
|
||||||
criterion.updateResult(datum);
|
if (criterion.usesTelemetry(datum.id)) {
|
||||||
|
criterion.updateResult(datum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ export default class Condition extends EventEmitter {
|
|||||||
|
|
||||||
isTelemetryUsed(id) {
|
isTelemetryUsed(id) {
|
||||||
return this.criteria.some(criterion => {
|
return this.criteria.some(criterion => {
|
||||||
return this.isAnyOrAllTelemetry(criterion) || criterion.telemetryObjectIdAsString === id;
|
return this.isAnyOrAllTelemetry(criterion) || criterion.usesTelemetry(id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,11 +272,11 @@ export default class Condition extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLADConditionResult() {
|
requestLADConditionResult(options) {
|
||||||
let latestTimestamp;
|
let latestTimestamp;
|
||||||
let criteriaResults = {};
|
let criteriaResults = {};
|
||||||
const criteriaRequests = this.criteria
|
const criteriaRequests = this.criteria
|
||||||
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
|
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects, options));
|
||||||
|
|
||||||
return Promise.all(criteriaRequests)
|
return Promise.all(criteriaRequests)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
return currentCondition;
|
return currentCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLADConditionSetOutput() {
|
requestLADConditionSetOutput(options) {
|
||||||
if (!this.conditions.length) {
|
if (!this.conditions.length) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
@@ -291,7 +291,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
let latestTimestamp;
|
let latestTimestamp;
|
||||||
let conditionResults = {};
|
let conditionResults = {};
|
||||||
const conditionRequests = this.conditions
|
const conditionRequests = this.conditions
|
||||||
.map(condition => condition.requestLADConditionResult());
|
.map(condition => condition.requestLADConditionResult(options));
|
||||||
|
|
||||||
return Promise.all(conditionRequests)
|
return Promise.all(conditionRequests)
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ export default class ConditionSetTelemetryProvider {
|
|||||||
return domainObject.type === 'conditionSet';
|
return domainObject.type === 'conditionSet';
|
||||||
}
|
}
|
||||||
|
|
||||||
request(domainObject) {
|
request(domainObject, options) {
|
||||||
let conditionManager = this.getConditionManager(domainObject);
|
let conditionManager = this.getConditionManager(domainObject);
|
||||||
|
|
||||||
return conditionManager.requestLADConditionSetOutput()
|
return conditionManager.requestLADConditionSetOutput(options)
|
||||||
.then(latestOutput => {
|
.then(latestOutput => {
|
||||||
return latestOutput;
|
return latestOutput;
|
||||||
});
|
});
|
||||||
@@ -52,7 +52,9 @@ export default class ConditionSetTelemetryProvider {
|
|||||||
subscribe(domainObject, callback) {
|
subscribe(domainObject, callback) {
|
||||||
let conditionManager = this.getConditionManager(domainObject);
|
let conditionManager = this.getConditionManager(domainObject);
|
||||||
|
|
||||||
conditionManager.on('conditionSetResultUpdated', callback);
|
conditionManager.on('conditionSetResultUpdated', (data) => {
|
||||||
|
callback(data);
|
||||||
|
});
|
||||||
|
|
||||||
return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
|
return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
if (styleConfiguration) {
|
if (styleConfiguration) {
|
||||||
this.initialize(styleConfiguration);
|
this.initialize(styleConfiguration);
|
||||||
if (styleConfiguration.conditionSetIdentifier) {
|
if (styleConfiguration.conditionSetIdentifier) {
|
||||||
|
this.openmct.time.on("bounds", this.refreshData.bind(this));
|
||||||
this.subscribeToConditionSet();
|
this.subscribeToConditionSet();
|
||||||
} else {
|
} else {
|
||||||
this.applyStaticStyle();
|
this.applyStaticStyle();
|
||||||
@@ -83,6 +84,25 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshData(bounds, isTick) {
|
||||||
|
if (!isTick) {
|
||||||
|
let options = {
|
||||||
|
start: bounds.start,
|
||||||
|
end: bounds.end,
|
||||||
|
size: 1,
|
||||||
|
strategy: 'latest'
|
||||||
|
};
|
||||||
|
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||||
|
this.openmct.telemetry.request(conditionSetDomainObject, options)
|
||||||
|
.then(output => {
|
||||||
|
if (output && output.length) {
|
||||||
|
this.handleConditionSetResultUpdated(output[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateObjectStyleConfig(styleConfiguration) {
|
updateObjectStyleConfig(styleConfiguration) {
|
||||||
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
|
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
|
||||||
this.initialize(styleConfiguration || {});
|
this.initialize(styleConfiguration || {});
|
||||||
@@ -160,10 +180,14 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
|
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
delete this.stopProvidingTelemetry;
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.openmct.time.off("bounds", this.refreshData);
|
||||||
|
this.openmct.editor.off('isEditing', this.toggleSubscription);
|
||||||
|
|
||||||
this.conditionSetIdentifier = undefined;
|
this.conditionSetIdentifier = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
:node="child"
|
:node="child"
|
||||||
:selected-item="selectedItem"
|
:selected-item="selectedItem"
|
||||||
:handle-item-selected="handleItemSelected"
|
:handle-item-selected="handleItemSelected"
|
||||||
|
:navigateToParent="navigateToParent"
|
||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -88,7 +89,13 @@ export default {
|
|||||||
default() {
|
default() {
|
||||||
return (item) => {};
|
return (item) => {};
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
navigateToParent: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -147,6 +154,16 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.domainObject = this.node.object;
|
this.domainObject = this.node.object;
|
||||||
|
|
||||||
|
if (this.navigateToParent && this.navigateToParent.includes(this.openmct.objects.makeKeyString(this.domainObject.identifier))) {
|
||||||
|
this.expanded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.navigateToParent && this.navigateToParent.endsWith(this.openmct.objects.makeKeyString(this.domainObject.identifier))) {
|
||||||
|
this.handleItemSelected(this.node.object, this.node)
|
||||||
|
this.$el.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
|
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
|
||||||
this.domainObject = newObject;
|
this.domainObject = newObject;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,10 +22,14 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="u-contents">
|
<div class="u-contents">
|
||||||
<div class="c-overlay__top-bar">
|
<div v-if="!hideTitle"
|
||||||
|
class="c-overlay__top-bar"
|
||||||
|
>
|
||||||
<div class="c-overlay__dialog-title">Select Condition Set</div>
|
<div class="c-overlay__dialog-title">Select Condition Set</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-overlay__contents-main c-selector c-tree-and-search">
|
<div class="c-overlay__contents-main c-selector c-tree-and-search"
|
||||||
|
:class="cssClass"
|
||||||
|
>
|
||||||
<div class="c-tree-and-search__search">
|
<div class="c-tree-and-search__search">
|
||||||
<search ref="shell-search"
|
<search ref="shell-search"
|
||||||
class="c-search"
|
class="c-search"
|
||||||
@@ -58,6 +62,7 @@
|
|||||||
:node="treeItem"
|
:node="treeItem"
|
||||||
:selected-item="selectedItem"
|
:selected-item="selectedItem"
|
||||||
:handle-item-selected="handleItemSelection"
|
:handle-item-selected="handleItemSelection"
|
||||||
|
:navigateToParent="navigateToParent"
|
||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- end main tree -->
|
<!-- end main tree -->
|
||||||
@@ -91,6 +96,36 @@ export default {
|
|||||||
ConditionSetDialogTreeItem
|
ConditionSetDialogTreeItem
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
cssClass: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideTitle: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ignoreTypeCheck: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
@@ -98,7 +133,8 @@ export default {
|
|||||||
allTreeItems: [],
|
allTreeItems: [],
|
||||||
filteredTreeItems: [],
|
filteredTreeItems: [],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
selectedItem: undefined
|
selectedItem: undefined,
|
||||||
|
navigateToParent: undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -115,10 +151,24 @@ export default {
|
|||||||
this.getDebouncedFilteredChildren = debounce(this.getFilteredChildren, 400);
|
this.getDebouncedFilteredChildren = debounce(this.getFilteredChildren, 400);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getAllChildren();
|
|
||||||
|
if (this.parent) {
|
||||||
|
(async () => {
|
||||||
|
const objectPath = await this.openmct.objects.getOriginalPath(this.parent.identifier);
|
||||||
|
this.navigateToParent = '/browse/'
|
||||||
|
+ objectPath
|
||||||
|
.map(parent => this.openmct.objects.makeKeyString(parent.identifier))
|
||||||
|
.reverse()
|
||||||
|
.join('/');
|
||||||
|
|
||||||
|
this.getAllChildren(this.navigateToParent);
|
||||||
|
})();
|
||||||
|
} else {
|
||||||
|
this.getAllChildren();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getAllChildren() {
|
getAllChildren(navigateToParent) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.openmct.objects.get('ROOT')
|
this.openmct.objects.get('ROOT')
|
||||||
.then(root => {
|
.then(root => {
|
||||||
@@ -131,7 +181,7 @@ export default {
|
|||||||
id: this.openmct.objects.makeKeyString(c.identifier),
|
id: this.openmct.objects.makeKeyString(c.identifier),
|
||||||
object: c,
|
object: c,
|
||||||
objectPath: [c],
|
objectPath: [c],
|
||||||
navigateToParent: '/browse'
|
navigateToParent: navigateToParent || '/browse'
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -178,7 +228,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleItemSelection(item, node) {
|
handleItemSelection(item, node) {
|
||||||
if (item && item.type === 'conditionSet') {
|
if (item && (this.ignoreTypeCheck || item.type === 'conditionSet')) {
|
||||||
const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
|
const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
|
||||||
this.selectedItem = {
|
this.selectedItem = {
|
||||||
itemId: item.identifier,
|
itemId: item.identifier,
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||||
<a v-if="conditionSetDomainObject"
|
<a v-if="conditionSetDomainObject"
|
||||||
class="c-object-label icon-conditional"
|
class="c-object-label icon-conditional"
|
||||||
:href="navigateToPath"
|
|
||||||
@click="navigateOrPreview"
|
@click="navigateOrPreview"
|
||||||
>
|
>
|
||||||
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
||||||
@@ -286,6 +285,8 @@ export default {
|
|||||||
if (this.openmct.editor.isEditing()) {
|
if (this.openmct.editor.isEditing()) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.previewAction.invoke(this.objectPath);
|
this.previewAction.invoke(this.objectPath);
|
||||||
|
} else {
|
||||||
|
this.openmct.router.navigate(this.navigateToPath);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeConditionSet() {
|
removeConditionSet() {
|
||||||
|
|||||||
@@ -66,7 +66,6 @@
|
|||||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||||
<a v-if="conditionSetDomainObject"
|
<a v-if="conditionSetDomainObject"
|
||||||
class="c-object-label"
|
class="c-object-label"
|
||||||
:href="navigateToPath"
|
|
||||||
@click="navigateOrPreview"
|
@click="navigateOrPreview"
|
||||||
>
|
>
|
||||||
<span class="c-object-label__type-icon icon-conditional"></span>
|
<span class="c-object-label__type-icon icon-conditional"></span>
|
||||||
@@ -309,6 +308,8 @@ export default {
|
|||||||
if (this.openmct.editor.isEditing()) {
|
if (this.openmct.editor.isEditing()) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.previewAction.invoke(this.objectPath);
|
this.previewAction.invoke(this.objectPath);
|
||||||
|
} else {
|
||||||
|
this.openmct.router.navigate(this.navigateToPath);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isItemType(type, item) {
|
isItemType(type, item) {
|
||||||
@@ -344,6 +345,11 @@ export default {
|
|||||||
const layoutItem = selectionItem[0].context.layoutItem;
|
const layoutItem = selectionItem[0].context.layoutItem;
|
||||||
const isChildItem = selectionItem.length > 1;
|
const isChildItem = selectionItem.length > 1;
|
||||||
|
|
||||||
|
if (!item && !layoutItem) {
|
||||||
|
// cases where selection is used for table cells
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isChildItem) {
|
if (!isChildItem) {
|
||||||
domainObject = item;
|
domainObject = item;
|
||||||
itemStyle = getApplicableStylesForItem(item);
|
itemStyle = getApplicableStylesForItem(item);
|
||||||
|
|||||||
@@ -147,12 +147,16 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
|||||||
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLAD(telemetryObjects) {
|
requestLAD(telemetryObjects, requestOptions) {
|
||||||
const options = {
|
let options = {
|
||||||
strategy: 'latest',
|
strategy: 'latest',
|
||||||
size: 1
|
size: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (requestOptions !== undefined) {
|
||||||
|
options = Object.assign(options, requestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isValid()) {
|
if (!this.isValid()) {
|
||||||
return this.formatData({}, telemetryObjects);
|
return this.formatData({}, telemetryObjects);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usesTelemetry(id) {
|
||||||
|
return this.telemetryObjectIdAsString && (this.telemetryObjectIdAsString === id);
|
||||||
|
}
|
||||||
|
|
||||||
subscribeForStaleData() {
|
subscribeForStaleData() {
|
||||||
if (this.stalenessSubscription) {
|
if (this.stalenessSubscription) {
|
||||||
this.stalenessSubscription.clear();
|
this.stalenessSubscription.clear();
|
||||||
@@ -133,12 +137,16 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLAD() {
|
requestLAD(telemetryObjects, requestOptions) {
|
||||||
const options = {
|
let options = {
|
||||||
strategy: 'latest',
|
strategy: 'latest',
|
||||||
size: 1
|
size: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (requestOptions !== undefined) {
|
||||||
|
options = Object.assign(options, requestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isValid()) {
|
if (!this.isValid()) {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export function getConsolidatedStyleValues(multipleItemStyles) {
|
|||||||
const properties = Object.keys(styleProps);
|
const properties = Object.keys(styleProps);
|
||||||
properties.forEach((property) => {
|
properties.forEach((property) => {
|
||||||
const values = aggregatedStyleValues[property];
|
const values = aggregatedStyleValues[property];
|
||||||
if (values.length) {
|
if (values && values.length) {
|
||||||
if (values.every(value => value === values[0])) {
|
if (values.every(value => value === values[0])) {
|
||||||
styleValues[property] = values[0];
|
styleValues[property] = values[0];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ xdescribe("the plugin", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('installs the new folder action', () => {
|
it('installs the new folder action', () => {
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ define(['lodash'], function (_) {
|
|||||||
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'Ok',
|
label: 'OK',
|
||||||
emphasis: 'true',
|
emphasis: 'true',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
removeItem(getAllTypes(selection));
|
removeItem(getAllTypes(selection));
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export default {
|
|||||||
this.mutablePromise.then(() => {
|
this.mutablePromise.then(() => {
|
||||||
this.openmct.objects.destroyMutable(this.domainObject);
|
this.openmct.objects.destroyMutable(this.domainObject);
|
||||||
});
|
});
|
||||||
} else {
|
} else if (this.domainObject.isMutable) {
|
||||||
this.openmct.objects.destroyMutable(this.domainObject);
|
this.openmct.objects.destroyMutable(this.domainObject);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export default {
|
|||||||
this.mutablePromise.then(() => {
|
this.mutablePromise.then(() => {
|
||||||
this.openmct.objects.destroyMutable(this.domainObject);
|
this.openmct.objects.destroyMutable(this.domainObject);
|
||||||
});
|
});
|
||||||
} else {
|
} else if (this.domainObject.isMutable) {
|
||||||
this.openmct.objects.destroyMutable(this.domainObject);
|
this.openmct.objects.destroyMutable(this.domainObject);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -269,7 +269,12 @@ export default {
|
|||||||
},
|
},
|
||||||
subscribeToObject() {
|
subscribeToObject() {
|
||||||
this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
|
this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
|
||||||
if (this.openmct.time.clock() !== undefined) {
|
const key = this.openmct.time.timeSystem().key;
|
||||||
|
const datumTimeStamp = datum[key];
|
||||||
|
if (this.openmct.time.clock() !== undefined
|
||||||
|
|| (datumTimeStamp
|
||||||
|
&& (this.openmct.time.bounds().end >= datumTimeStamp))
|
||||||
|
) {
|
||||||
this.updateView(datum);
|
this.updateView(datum);
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|||||||
@@ -34,45 +34,10 @@ export default class DuplicateAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async invoke(objectPath) {
|
async invoke(objectPath) {
|
||||||
let duplicationTask = new DuplicateTask(this.openmct);
|
let object = objectPath[0];
|
||||||
let originalObject = objectPath[0];
|
this.parent = objectPath[1];
|
||||||
let parent = objectPath[1];
|
|
||||||
let userInput = await this.getUserInput(originalObject, parent);
|
|
||||||
let newParent = userInput.location;
|
|
||||||
let inNavigationPath = this.inNavigationPath(originalObject);
|
|
||||||
|
|
||||||
// legacy check
|
this.showForm(object, this.parent);
|
||||||
if (this.isLegacyDomainObject(newParent)) {
|
|
||||||
newParent = await this.convertFromLegacy(newParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if editing, save
|
|
||||||
if (inNavigationPath && this.openmct.editor.isEditing()) {
|
|
||||||
this.openmct.editor.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// duplicate
|
|
||||||
let newObject = await duplicationTask.duplicate(originalObject, newParent);
|
|
||||||
this.updateNameCheck(newObject, userInput.name);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUserInput(originalObject, parent) {
|
|
||||||
let dialogService = this.openmct.$injector.get('dialogService');
|
|
||||||
let dialogForm = this.getDialogForm(originalObject, parent);
|
|
||||||
let formState = {
|
|
||||||
name: originalObject.name
|
|
||||||
};
|
|
||||||
let userInput = await dialogService.getUserInput(dialogForm, formState);
|
|
||||||
|
|
||||||
return userInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNameCheck(object, name) {
|
|
||||||
if (object.name !== name) {
|
|
||||||
this.openmct.objects.mutate(object, 'name', name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inNavigationPath(object) {
|
inNavigationPath(object) {
|
||||||
@@ -80,40 +45,66 @@ export default class DuplicateAction {
|
|||||||
.some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, object.identifier));
|
.some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, object.identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogForm(object, parent) {
|
async onSave(object, changes, parent) {
|
||||||
return {
|
console.log('onSave');
|
||||||
name: "Duplicate Item",
|
let inNavigationPath = this.inNavigationPath(object);
|
||||||
|
if (inNavigationPath && this.openmct.editor.isEditing()) {
|
||||||
|
this.openmct.editor.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.name && (changes.name !== object.name)) {
|
||||||
|
object.name = changes.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// duplicate
|
||||||
|
let duplicationTask = new DuplicateTask(this.openmct);
|
||||||
|
duplicationTask.duplicate(object, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
showForm(domainObject, parentDomainObject) {
|
||||||
|
const formStructure = {
|
||||||
|
title: "Duplicate Item",
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
rows: [
|
rows: [
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
control: "textfield",
|
control: "textfield",
|
||||||
name: "Name",
|
name: "Title",
|
||||||
pattern: "\\S+",
|
pattern: "\\S+",
|
||||||
required: true,
|
required: true,
|
||||||
cssClass: "l-input-lg"
|
cssClass: "l-input-lg",
|
||||||
|
value: domainObject.name
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "location",
|
name: "location",
|
||||||
cssClass: "grows",
|
|
||||||
control: "locator",
|
control: "locator",
|
||||||
validate: this.validate(object, parent),
|
required: true,
|
||||||
|
parent: parentDomainObject,
|
||||||
|
validate: this.validate(parentDomainObject),
|
||||||
key: 'location'
|
key: 'location'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.openmct.forms.showForm(formStructure, {
|
||||||
|
domainObject,
|
||||||
|
parentDomainObject,
|
||||||
|
onSave: this.onSave.bind(this)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(object, currentParent) {
|
validate(currentParent) {
|
||||||
return (parentCandidate) => {
|
return (object, data) => {
|
||||||
|
const parentCandidate = data.value;
|
||||||
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
||||||
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.getId());
|
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
||||||
let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
|
let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
|
||||||
|
|
||||||
if (!parentCandidate || !currentParentKeystring) {
|
if (!parentCandidateKeystring || !currentParentKeystring) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,24 +112,15 @@ export default class DuplicateAction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.openmct.composition.checkPolicy(
|
const parentCandidateComposition = parentCandidate.composition;
|
||||||
parentCandidate.useCapability('adapter'),
|
if (parentCandidateComposition && parentCandidateComposition.indexOf(objectKeystring) !== -1) {
|
||||||
object
|
return false;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isLegacyDomainObject(domainObject) {
|
|
||||||
return domainObject.getCapability !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
async convertFromLegacy(legacyDomainObject) {
|
|
||||||
let objectContext = legacyDomainObject.getCapability('context');
|
|
||||||
let domainObject = await this.openmct.objects.get(objectContext.domainObject.id);
|
|
||||||
|
|
||||||
return domainObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
appliesTo(objectPath) {
|
appliesTo(objectPath) {
|
||||||
let parent = objectPath[1];
|
let parent = objectPath[1];
|
||||||
let parentType = parent && this.openmct.types.get(parent.type);
|
let parentType = parent && this.openmct.types.get(parent.type);
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import uuid from 'uuid';
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default class DuplicateTask {
|
export default class DuplicateTask {
|
||||||
|
|
||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
this.domainObject = undefined;
|
this.domainObject = undefined;
|
||||||
this.parent = undefined;
|
this.parent = undefined;
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ describe("The Duplicate Action plugin", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
|
|||||||
@@ -90,14 +90,12 @@ export default {
|
|||||||
this.composition.load();
|
this.composition.load();
|
||||||
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
||||||
this.unobserveGlobalFilters = this.openmct.objects.observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters);
|
this.unobserveGlobalFilters = this.openmct.objects.observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters);
|
||||||
this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject);
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.composition.off('add', this.addChildren);
|
this.composition.off('add', this.addChildren);
|
||||||
this.composition.off('remove', this.removeChildren);
|
this.composition.off('remove', this.removeChildren);
|
||||||
this.unobserve();
|
this.unobserve();
|
||||||
this.unobserveGlobalFilters();
|
this.unobserveGlobalFilters();
|
||||||
this.unobserveAllMutation();
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addChildren(domainObject) {
|
addChildren(domainObject) {
|
||||||
@@ -158,25 +156,28 @@ export default {
|
|||||||
},
|
},
|
||||||
getGlobalFiltersToRemove(keyString) {
|
getGlobalFiltersToRemove(keyString) {
|
||||||
let filtersToRemove = new Set();
|
let filtersToRemove = new Set();
|
||||||
|
const child = this.children[keyString];
|
||||||
|
if (child && child.metadataWithFilters) {
|
||||||
|
const metadataWithFilters = child.metadataWithFilters;
|
||||||
|
metadataWithFilters.forEach(metadatum => {
|
||||||
|
let keepFilter = false;
|
||||||
|
Object.keys(this.children).forEach(childKeyString => {
|
||||||
|
if (childKeyString !== keyString) {
|
||||||
|
let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key);
|
||||||
|
|
||||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
if (filterMatched) {
|
||||||
let keepFilter = false;
|
keepFilter = true;
|
||||||
Object.keys(this.children).forEach(childKeyString => {
|
|
||||||
if (childKeyString !== keyString) {
|
|
||||||
let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key);
|
|
||||||
|
|
||||||
if (filterMatched) {
|
return;
|
||||||
keepFilter = true;
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!keepFilter) {
|
||||||
|
filtersToRemove.add(metadatum.key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (!keepFilter) {
|
|
||||||
filtersToRemove.add(metadatum.key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(filtersToRemove);
|
return Array.from(filtersToRemove);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ function ToolbarProvider(openmct) {
|
|||||||
message: `This action will remove this frame from this Flexible Layout. Do you want to continue?`,
|
message: `This action will remove this frame from this Flexible Layout. Do you want to continue?`,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'Ok',
|
label: 'OK',
|
||||||
emphasis: 'true',
|
emphasis: 'true',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
deleteFrameAction(primary.context.frameId);
|
deleteFrameAction(primary.context.frameId);
|
||||||
@@ -162,7 +162,7 @@ function ToolbarProvider(openmct) {
|
|||||||
message: 'This action will permanently delete this container from this Flexible Layout',
|
message: 'This action will permanently delete this container from this Flexible Layout',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'Ok',
|
label: 'OK',
|
||||||
emphasis: 'true',
|
emphasis: 'true',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
removeContainer(containerId);
|
removeContainer(containerId);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
'is-alias': item.isAlias === true,
|
'is-alias': item.isAlias === true,
|
||||||
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
|
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
|
||||||
}, statusClass]"
|
}, statusClass]"
|
||||||
:href="objectLink"
|
@click="navigate"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-grid-item__type-icon"
|
class="c-grid-item__type-icon"
|
||||||
@@ -49,11 +49,17 @@ import statusListener from './status-listener';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [contextMenuGesture, objectLink, statusListener],
|
mixins: [contextMenuGesture, objectLink, statusListener],
|
||||||
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
navigate() {
|
||||||
|
this.openmct.router.navigate(this.objectLink);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
ref="objectLink"
|
ref="objectLink"
|
||||||
class="c-object-label"
|
class="c-object-label"
|
||||||
:class="[statusClass]"
|
:class="[statusClass]"
|
||||||
:href="objectLink"
|
@click="navigate"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-object-label__type-icon c-list-item__name__type-icon"
|
class="c-object-label__type-icon c-list-item__name__type-icon"
|
||||||
@@ -45,6 +45,7 @@ import statusListener from './status-listener';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [contextMenuGesture, objectLink, statusListener],
|
mixins: [contextMenuGesture, objectLink, statusListener],
|
||||||
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -56,7 +57,7 @@ export default {
|
|||||||
return moment(timestamp).format(format);
|
return moment(timestamp).format(format);
|
||||||
},
|
},
|
||||||
navigate() {
|
navigate() {
|
||||||
this.$refs.objectLink.click();
|
this.openmct.router.navigate(this.objectLink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user