mirror of
https://github.com/nasa/openmct.git
synced 2025-06-26 03:00:13 +00:00
Compare commits
117 Commits
expose-vue
...
unload-ope
Author | SHA1 | Date | |
---|---|---|---|
b062615c25 | |||
4ba8f893a6 | |||
c4b9be18f1 | |||
eabdf6cd04 | |||
e56c673005 | |||
dad9f12a5c | |||
aa5edb0b83 | |||
b315803180 | |||
b27317631b | |||
953a9daafb | |||
63f9cd449f | |||
54220f547b | |||
93d967c2b3 | |||
1226459c6f | |||
d7c9c9cb98 | |||
e784242379 | |||
da8dbf25bf | |||
a5571eec05 | |||
19802ce2cc | |||
e83735a927 | |||
c9a47a411a | |||
a6e55fd493 | |||
76f35a0bcd | |||
b6930ef7cd | |||
57b9cbd42f | |||
17901147ef | |||
58b8f90682 | |||
d06cbc8975 | |||
2131ef2397 | |||
553b18587c | |||
590dc74e45 | |||
3557ed4b4c | |||
41cc52d50a | |||
48c22369a1 | |||
6506077f4d | |||
b1b4266ff3 | |||
42b0148f93 | |||
9461ad8edd | |||
40055ba955 | |||
9cb85ad176 | |||
f2b2953a5d | |||
62de310686 | |||
4b9ff67e49 | |||
d5e32ec494 | |||
38880ba3d1 | |||
a99ce7733c | |||
9f48764210 | |||
a1aaa0dd41 | |||
bee15e98c8 | |||
092bbe547d | |||
6cbe05317c | |||
3b92fcdf6c | |||
6dde54bd25 | |||
359e7377ac | |||
9f4190f781 | |||
f3fc991a74 | |||
2564e75fc9 | |||
f42fe78acf | |||
fe928a1386 | |||
b329ed6ed5 | |||
9b7a0d7e4c | |||
5c15e53abb | |||
f58b3881f2 | |||
071a13b219 | |||
ca66898e51 | |||
94c7b2343a | |||
c397c336ab | |||
eea23f2caf | |||
6665641c02 | |||
c3ebf52dd2 | |||
f8f2e7da9b | |||
240f58b2d0 | |||
7d3baee7b5 | |||
1f5cb7ca42 | |||
4a7ebe326c | |||
10da314a4a | |||
b3ceccd7fb | |||
1bde4c9a0c | |||
4b85360446 | |||
41b860a547 | |||
254b3db966 | |||
cbb3f32d1e | |||
e3bf72e77f | |||
0b63b782cf | |||
da39fd0c70 | |||
96dd581a67 | |||
2a1e322230 | |||
300b98bd54 | |||
c946609d13 | |||
7ca559fbe4 | |||
71392915c1 | |||
2889e88a97 | |||
d56d176aac | |||
925518c83f | |||
fa5aceb7b3 | |||
6755ef4641 | |||
333e8b5583 | |||
9d8a8b36d2 | |||
b484a4a959 | |||
64e7c62d98 | |||
6483fe2402 | |||
a123889d6a | |||
a40867d544 | |||
dbed9262c0 | |||
43ac66233e | |||
04e85c176a | |||
8274c23129 | |||
5fafde5f23 | |||
80a6e7f719 | |||
2c13aeecce | |||
ac015c3e45 | |||
ae1a4bcc6a | |||
e1e0eeac56 | |||
c90dfb2a1f | |||
1dfa5e5b8c | |||
99896b72ea | |||
979ba77c8e |
@ -1,36 +1,93 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
version: 2.1
|
||||
executors:
|
||||
linux:
|
||||
docker:
|
||||
- image: circleci/node:13-browsers
|
||||
environment:
|
||||
CHROME_BIN: "/usr/bin/google-chrome"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update npm
|
||||
command: 'sudo npm install -g npm@latest'
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Installing dependencies (npm install)
|
||||
command: npm install
|
||||
- save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- run:
|
||||
name: npm run test:coverage
|
||||
command: npm run test:coverage
|
||||
- run:
|
||||
name: npm run lint
|
||||
command: npm run lint
|
||||
- store_artifacts:
|
||||
path: dist
|
||||
prefix: dist
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
- image: cimg/base:stable
|
||||
orbs:
|
||||
node: circleci/node@4.5.1
|
||||
browser-tools: circleci/browser-tools@1.1.3
|
||||
jobs:
|
||||
test:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
browser:
|
||||
type: string
|
||||
always-pass:
|
||||
type: boolean
|
||||
executor: linux
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}
|
||||
- node/install:
|
||||
node-version: << parameters.node-version >>
|
||||
- node/install-packages:
|
||||
override-ci-command: npm install
|
||||
- when: # Just to save time until caching saves the browser bin
|
||||
condition:
|
||||
equal: [ "FirefoxESR", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-firefox:
|
||||
version: "78.11.0esr" #https://archive.mozilla.org/pub/firefox/releases/
|
||||
- when: # Just to save time until caching saves the browser bin
|
||||
condition:
|
||||
equal: [ "ChromeHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-chrome:
|
||||
replace-existing: false
|
||||
- save_cache:
|
||||
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ~/.npm
|
||||
- ~/.cache
|
||||
- node_modules
|
||||
- run: npm run test:coverage -- --browsers=<<parameters.browser>> || <<parameters.always-pass>>
|
||||
- store_test_results:
|
||||
path: dist/reports/tests/
|
||||
- store_artifacts:
|
||||
path: dist/reports/
|
||||
workflows:
|
||||
matrix-tests:
|
||||
jobs:
|
||||
- build
|
||||
- test:
|
||||
name: node10-chrome
|
||||
node-version: lts/dubnium
|
||||
browser: ChromeHeadless
|
||||
always-pass: false
|
||||
- test:
|
||||
name: node12-firefoxESR-build-only
|
||||
node-version: lts/erbium
|
||||
browser: FirefoxESR
|
||||
always-pass: true
|
||||
- test:
|
||||
name: node14-chrome-build-only
|
||||
node-version: lts/fermium
|
||||
browser: ChromeHeadless
|
||||
always-pass: true
|
||||
nightly:
|
||||
jobs:
|
||||
- test:
|
||||
name: node10-chrome-nightly
|
||||
node-version: lts/dubnium
|
||||
browser: ChromeHeadless
|
||||
always-pass: false
|
||||
- test:
|
||||
name: node12-firefoxESR-nightly
|
||||
node-version: lts/erbium
|
||||
browser: FirefoxESR
|
||||
always-pass: false
|
||||
- test:
|
||||
name: node14-chrome-nightly
|
||||
node-version: lts/fermium
|
||||
browser: ChromeHeadless
|
||||
always-pass: false
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
|
||||
|
44
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
44
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: File a Bug !
|
||||
title: ''
|
||||
labels: type:bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- 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-yamcs? -->
|
||||
* OS:
|
||||
* Browser:
|
||||
|
||||
#### Additional Information
|
||||
<!--- Include any screenshots, gifs, or logs which will expedite triage -->
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Discussions
|
||||
url: https://github.com/nasa/openmct/discussions
|
||||
about: Got a question?
|
20
.github/ISSUE_TEMPLATE/enhancement-request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/enhancement-request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Enhancement request
|
||||
about: Suggest an enhancement or new improvement for this project
|
||||
title: ''
|
||||
labels: type:enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
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. -->
|
13
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
13
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
### 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?
|
||||
* [ ] Is this change backwards compatible? Will users need to change how they are calling the API, or how they've extended core plugins such as Tables or Plots?
|
||||
|
||||
### 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?
|
33
.github/workflows/codeql-analysis.yml
vendored
Normal file
33
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '28 21 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: javascript
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
20
.github/workflows/lighthouse.yml
vendored
Normal file
20
.github/workflows/lighthouse.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: lighthouse
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Which branch do you want to test?' # Limited to branch for now
|
||||
required: false
|
||||
default: 'master'
|
||||
jobs:
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.version }}
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- run: npm install && npm install -g @lhci/cli #Don't want to include this in our deps
|
||||
- run: lhci autorun
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -40,4 +40,7 @@ npm-debug.log
|
||||
# karma reports
|
||||
report.*.json
|
||||
|
||||
# Lighthouse reports
|
||||
.lighthouseci
|
||||
|
||||
package-lock.json
|
||||
|
18
API.md
18
API.md
@ -423,7 +423,7 @@ attribute | type | flags | notes
|
||||
|
||||
###### 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:
|
||||
|
||||
@ -595,9 +595,17 @@ section.
|
||||
|
||||
#### Limit Evaluators **draft**
|
||||
|
||||
Limit evaluators allow a telemetry integrator to define how limits should be
|
||||
applied to telemetry from a given domain object. For an example of a limit
|
||||
evaluator, take a look at `examples/generator/SinewaveLimitProvider.js`.
|
||||
Limit evaluators allow a telemetry integrator to define which limits exist for a
|
||||
telemetry endpoint and how limits should be applied to telemetry from a given domain object.
|
||||
|
||||
A limit evaluator can implement the `evalute` method which is used to define how limits
|
||||
should be applied to telemetry and the `getLimits` method which is used to specify
|
||||
what the limit values are for different limit levels.
|
||||
|
||||
Limit levels can be mapped to one of 5 colors for visualization:
|
||||
`purple`, `red`, `orange`, `yellow` and `cyan`.
|
||||
|
||||
For an example of a limit evaluator, take a look at `examples/generator/SinewaveLimitProvider.js`.
|
||||
|
||||
### Telemetry Consumer APIs **draft**
|
||||
|
||||
@ -988,7 +996,7 @@ reveal additional information when the mouse cursor is hovered over it.
|
||||
A common use case for indicators is to convey the state of some external system such as a
|
||||
persistence backend or HTTP server. So long as this system is accessible via HTTP request,
|
||||
Open MCT provides a general purpose indicator to show whether the server is available and
|
||||
returing a 2xx status code. The URL Status Indicator is made available as a default plugin. See
|
||||
returning a 2xx status code. The URL Status Indicator is made available as a default plugin. See
|
||||
the [documentation](./src/plugins/URLIndicatorPlugin) for details on how to install and configure the
|
||||
URL Status Indicator.
|
||||
|
||||
|
@ -10,7 +10,7 @@ accept changes from external contributors.
|
||||
|
||||
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
|
||||
standards as described below.
|
||||
3. Submit a pull request from a topic branch back to `master`. Include a check
|
||||
@ -18,6 +18,7 @@ The short version:
|
||||
for review.)
|
||||
4. Respond to any discussion. When the reviewer decides it's ready, they
|
||||
will merge back `master` and fill out their own check list.
|
||||
5. If you are a first-time contributor, please see [this discussion](https://github.com/nasa/openmct/discussions/3821) for further information.
|
||||
|
||||
## Contribution Process
|
||||
|
||||
@ -115,7 +116,7 @@ the pull request containing the reviewer checklist (from below) and complete
|
||||
the merge back to the master branch.
|
||||
|
||||
Additionally:
|
||||
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, create one.
|
||||
* 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.
|
||||
* 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.
|
||||
@ -296,23 +297,12 @@ these standards.
|
||||
|
||||
Issues are tracked at https://github.com/nasa/openmct/issues.
|
||||
|
||||
Issues should include:
|
||||
|
||||
* A short description of the issue encountered.
|
||||
* A longer-form description of the issue encountered. When possible, steps to
|
||||
reproduce the issue.
|
||||
* When possible, a description of the impact of the issue. What use case does
|
||||
it impede?
|
||||
* An assessment of the severity of the issue.
|
||||
|
||||
Issue severity is categorized as follows (in ascending order):
|
||||
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the
|
||||
software; a "nice-to-have."
|
||||
* _(Unspecified)_: Major loss of functionality or impairment of use.
|
||||
* _Critical_: Large-scale loss of functionality or impairment of use,
|
||||
such that remaining utility becomes marginal.
|
||||
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
|
||||
* _Medium_: Some impairment of use, but simple workarounds exist
|
||||
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
|
||||
* _Blocker_: Major functionality is impaired or lost, threatening mission success. Display of telemetry data is impaired or blocked by the bug, which could lead to loss of situational awareness.
|
||||
|
||||
## Check Lists
|
||||
|
||||
@ -322,16 +312,20 @@ checklist).
|
||||
|
||||
### Author Checklist
|
||||
|
||||
1. Changes address original issue?
|
||||
2. Unit tests included and/or updated with changes?
|
||||
3. Command line build passes?
|
||||
4. Changes have been smoke-tested?
|
||||
5. Testing instructions included?
|
||||
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
|
||||
|
||||
### Reviewer Checklist
|
||||
|
||||
1. Changes appear to address issue?
|
||||
2. Appropriate unit tests included?
|
||||
3. Code style and in-line documentation are appropriate?
|
||||
4. Commit messages meet standards?
|
||||
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||
* [ ] Changes appear to address issue?
|
||||
* [ ] Changes appear not to be breaking changes?
|
||||
* [ ] Appropriate unit tests included?
|
||||
* [ ] Code style and in-line documentation are appropriate?
|
||||
* [ ] Commit messages meet standards?
|
||||
* [ ] Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||
* [ ] Has associated issue been labelled `bug`? (only applicable if this PR is for a bug fix)
|
||||
* [ ] List of Acceptance Tests Performed.
|
||||
|
||||
Write out a small list of tests performed with just enough detail for another developer on the team
|
||||
to execute.
|
||||
|
||||
i.e. ```When Clicking on Add button, new `object` appears in dropdown.```
|
@ -1,9 +1,11 @@
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript)
|
||||
|
||||
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
||||
|
||||
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)
|
||||
|
||||
Once you've created something amazing with Open MCT, showcase your work in our GitHub Discussions [Show and Tell](https://github.com/nasa/openmct/discussions/categories/show-and-tell) section. We love seeing unique and wonderful implementations of Open MCT!
|
||||
|
||||
## See Open MCT in Action
|
||||
|
||||
Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/).
|
||||
@ -44,7 +46,7 @@ The clearest examples for developing Open MCT plugins are in the
|
||||
our documentation.
|
||||
|
||||
We want Open MCT to be as easy to use, install, run, and develop for as
|
||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues), 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
|
||||
|
||||
|
@ -423,7 +423,7 @@ which can help with this, however.
|
||||
instead of separate approaches for static and substitutable
|
||||
dependencies.
|
||||
* Removes need to understand Angular's DI mechanism.
|
||||
* Improves useability of documentation (`typeService` is an
|
||||
* Improves usability of documentation (`typeService` is an
|
||||
instance of `CompositeService` and implements `TypeService`
|
||||
so you can easily traverse links in the JSDoc.)
|
||||
* Can be used more easily from Web Workers, allowing services
|
||||
|
@ -25,7 +25,7 @@
|
||||
## Legacy Documentation
|
||||
|
||||
As we transition to a new API, the following documentation for the old API
|
||||
(which is supported during the transtion) may be useful as well:
|
||||
(which is supported during the transition) may be useful as well:
|
||||
|
||||
* The [Architecture Overview](architecture/) describes the concepts used
|
||||
throughout Open MCT, and gives a high level overview of the platform's design.
|
||||
|
@ -73,11 +73,11 @@ acceptance testing (e.g. by resolving any blockers found); any
|
||||
resources not needed for this effort should be used to begin work
|
||||
for the subsequent sprint.
|
||||
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship |
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-------------------------------------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze and sprint branch |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship and merge sprint branch to master|
|
||||
|
||||
* If necessary.
|
||||
|
||||
@ -105,14 +105,20 @@ emphasis on testing.
|
||||
that team may begin work for that sprint during the
|
||||
third week, since testing and blocker resolution is unlikely
|
||||
to require all available resources.
|
||||
* Testing success criteria identified per issue (where necessary). This could be in the form of acceptance tests on the issue or detailing performance tests, for example.
|
||||
* __Tag-up.__ Check in and status update among development team.
|
||||
May amend plan for sprint as-needed.
|
||||
* __Code freeze.__ Any new work from this sprint
|
||||
(features, bug fixes, enhancements) must be integrated by the
|
||||
end of the second week of the sprint. After code freeze
|
||||
(and until the end of the sprint) the only changes that should be
|
||||
merged into the master branch should directly address issues
|
||||
needed to pass acceptance testing.
|
||||
end of the second week of the sprint. After code freeze, a sprint
|
||||
branch will be created (and until the end of the sprint) the only
|
||||
changes that should be merged into the sprint branch should
|
||||
directly address issues needed to pass acceptance testing.
|
||||
During this time, any other feature development will continue to
|
||||
be merged into the master branch for the next sprint.
|
||||
* __Sprint branch merge to master.__ After acceptance testing, the sprint branch
|
||||
will be merged back to the master branch. Any code conflicts that
|
||||
arise will be resolved by the team.
|
||||
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
|
||||
Structured testing with predefined
|
||||
success criteria. No release should ship without passing
|
||||
@ -126,8 +132,8 @@ emphasis on testing.
|
||||
* [__Testathon.__](testing/plan.md#user-testing)
|
||||
Multi-user testing, involving as many users as
|
||||
is feasible, plus development team. Open-ended; should verify
|
||||
completed work from this sprint, test exploratorily for
|
||||
regressions, et cetera.
|
||||
completed work from this sprint using the sprint branch, test
|
||||
exploratorily for regressions, et cetera.
|
||||
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
|
||||
test to verify that the software remains
|
||||
stable after running for longer durations. May include some
|
||||
@ -143,7 +149,7 @@ emphasis on testing.
|
||||
Subset of Pre-release Testing
|
||||
which should be performed before shipping at the end of any
|
||||
sprint. Time is allocated for a second round of
|
||||
Pre-release Testing if the first round is not passed.
|
||||
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
|
||||
success criteria to determine whether or not they should block
|
||||
release, then formulates a plan to address these issues before
|
||||
|
@ -19,7 +19,7 @@ Testing for Open MCT includes:
|
||||
|
||||
Manual, non-rigorous testing of the software and/or specific features
|
||||
of interest. Verifies that the software runs and that basic functionality
|
||||
is present.
|
||||
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
|
||||
|
||||
@ -49,7 +49,7 @@ User testing will focus on the following activities:
|
||||
* General "trying to break things."
|
||||
|
||||
During user testing, users will
|
||||
[report issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
[report issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
as they are encountered.
|
||||
|
||||
Desired outcomes of user testing are:
|
||||
@ -71,7 +71,7 @@ usage. After twenty-four hours, the software is evaluated for:
|
||||
at the start of the test? Is it as responsive?
|
||||
|
||||
Any defects or unexpected behavior identified during testing should be
|
||||
[reported as issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
[reported as issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
and reviewed for severity.
|
||||
|
||||
## Test Performance
|
||||
|
@ -92,8 +92,8 @@ should update (or delegate the task of updating) Open MCT version
|
||||
numbers by the following process:
|
||||
|
||||
1. Update version number in `package.json`
|
||||
1. Create a new branch off the `master` branch.
|
||||
2. Remove `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
1. Checkout branch created for the last sprint that has been successfully tested.
|
||||
2. Remove a `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
3. Verify that resulting version number meets semantic versioning
|
||||
requirements relative to previous stable version. Increment the
|
||||
version number if necessary.
|
||||
@ -131,7 +131,8 @@ numbers by the following process:
|
||||
3. In `package.json` change package to be public (private: false)
|
||||
4. Test the package before publishing by doing `npm publish --dry-run`
|
||||
if necessary.
|
||||
5. Publish the package to the npmjs registry (e.g. `npm publish --access public`)
|
||||
5. Publish the package to the npmjs registry (e.g. `npm publish --access public`)
|
||||
NOTE: Use the `--tag unstable` flag to the npm publishj if this is a prerelease.
|
||||
6. Confirm the package has been published (e.g. `https://www.npmjs.com/package/openmct`)
|
||||
5. Update snapshot status in `package.json`
|
||||
1. Create a new branch off the `master` branch.
|
||||
|
@ -41,11 +41,6 @@ define([
|
||||
"$scope"
|
||||
]
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"templateUrl": "templates/exampleForm.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,15 @@ define([
|
||||
domain: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "cos",
|
||||
name: "Cosine",
|
||||
unit: "deg",
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
domain: 3
|
||||
}
|
||||
},
|
||||
// Need to enable "LocalTimeSystem" plugin to make use of this
|
||||
// {
|
||||
// key: "local",
|
||||
@ -109,6 +118,100 @@ define([
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
'example.spectral-generator': {
|
||||
values: [
|
||||
{
|
||||
key: "name",
|
||||
name: "Name",
|
||||
format: "string"
|
||||
},
|
||||
{
|
||||
key: "utc",
|
||||
name: "Time",
|
||||
format: "utc",
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "wavelength",
|
||||
name: "Wavelength",
|
||||
unit: "Hz",
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
domain: 2,
|
||||
spectralAttribute: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "cos",
|
||||
name: "Cosine",
|
||||
unit: "deg",
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
range: 2,
|
||||
spectralAttribute: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
'example.spectral-aggregate-generator': {
|
||||
values: [
|
||||
{
|
||||
key: "name",
|
||||
name: "Name",
|
||||
format: "string"
|
||||
},
|
||||
{
|
||||
key: "utc",
|
||||
name: "Time",
|
||||
format: "utc",
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ch1",
|
||||
name: "Channel 1",
|
||||
format: "string",
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ch2",
|
||||
name: "Channel 2",
|
||||
format: "string",
|
||||
hints: {
|
||||
range: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ch3",
|
||||
name: "Channel 3",
|
||||
format: "string",
|
||||
hints: {
|
||||
range: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ch4",
|
||||
name: "Channel 4",
|
||||
format: "string",
|
||||
hints: {
|
||||
range: 4
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ch5",
|
||||
name: "Channel 5",
|
||||
format: "string",
|
||||
hints: {
|
||||
range: 5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -96,5 +96,9 @@ define([
|
||||
return this.workerInterface.subscribe(workerRequest, callback);
|
||||
};
|
||||
|
||||
GeneratorProvider.prototype.destroy = function () {
|
||||
this.workerInterface.destroy();
|
||||
};
|
||||
|
||||
return GeneratorProvider;
|
||||
});
|
||||
|
@ -26,14 +26,26 @@ define([
|
||||
|
||||
) {
|
||||
|
||||
var RED = {
|
||||
var PURPLE = {
|
||||
sin: 2.2,
|
||||
cos: 2.2
|
||||
},
|
||||
RED = {
|
||||
sin: 0.9,
|
||||
cos: 0.9
|
||||
},
|
||||
ORANGE = {
|
||||
sin: 0.7,
|
||||
cos: 0.7
|
||||
},
|
||||
YELLOW = {
|
||||
sin: 0.5,
|
||||
cos: 0.5
|
||||
},
|
||||
CYAN = {
|
||||
sin: 0.45,
|
||||
cos: 0.45
|
||||
},
|
||||
LIMITS = {
|
||||
rh: {
|
||||
cssClass: "is-limit--upr is-limit--red",
|
||||
@ -93,5 +105,70 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
||||
|
||||
return {
|
||||
limits: function () {
|
||||
return Promise.resolve({
|
||||
WATCH: {
|
||||
low: {
|
||||
color: "cyan",
|
||||
sin: -CYAN.sin,
|
||||
cos: -CYAN.cos
|
||||
},
|
||||
high: {
|
||||
color: "cyan",
|
||||
...CYAN
|
||||
}
|
||||
},
|
||||
WARNING: {
|
||||
low: {
|
||||
color: "yellow",
|
||||
sin: -YELLOW.sin,
|
||||
cos: -YELLOW.cos
|
||||
},
|
||||
high: {
|
||||
color: "yellow",
|
||||
...YELLOW
|
||||
}
|
||||
},
|
||||
DISTRESS: {
|
||||
low: {
|
||||
color: "orange",
|
||||
sin: -ORANGE.sin,
|
||||
cos: -ORANGE.cos
|
||||
},
|
||||
high: {
|
||||
color: "orange",
|
||||
...ORANGE
|
||||
}
|
||||
},
|
||||
CRITICAL: {
|
||||
low: {
|
||||
color: "red",
|
||||
sin: -RED.sin,
|
||||
cos: -RED.cos
|
||||
},
|
||||
high: {
|
||||
color: "red",
|
||||
...RED
|
||||
}
|
||||
},
|
||||
SEVERE: {
|
||||
low: {
|
||||
color: "purple",
|
||||
sin: -PURPLE.sin,
|
||||
cos: -PURPLE.cos
|
||||
},
|
||||
high: {
|
||||
color: "purple",
|
||||
...PURPLE
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return SinewaveLimitProvider;
|
||||
});
|
||||
|
86
example/generator/SpectralAggregateGeneratorProvider.js
Normal file
86
example/generator/SpectralAggregateGeneratorProvider.js
Normal file
@ -0,0 +1,86 @@
|
||||
/*****************************************************************************
|
||||
* 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 (
|
||||
|
||||
) {
|
||||
|
||||
function SpectralAggregateGeneratorProvider() {
|
||||
|
||||
}
|
||||
|
||||
function pointForTimestamp(timestamp, count, name) {
|
||||
return {
|
||||
name: name,
|
||||
utc: String(Math.floor(timestamp / count) * count),
|
||||
ch1: String(Math.floor(timestamp / count) % 1),
|
||||
ch2: String(Math.floor(timestamp / count) % 2),
|
||||
ch3: String(Math.floor(timestamp / count) % 3),
|
||||
ch4: String(Math.floor(timestamp / count) % 4),
|
||||
ch5: String(Math.floor(timestamp / count) % 5)
|
||||
};
|
||||
}
|
||||
|
||||
SpectralAggregateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
|
||||
return domainObject.type === 'example.spectral-aggregate-generator';
|
||||
};
|
||||
|
||||
SpectralAggregateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var count = 5000;
|
||||
|
||||
var interval = setInterval(function () {
|
||||
var now = Date.now();
|
||||
var datum = pointForTimestamp(now, count, domainObject.name);
|
||||
callback(datum);
|
||||
}, count);
|
||||
|
||||
return function () {
|
||||
clearInterval(interval);
|
||||
};
|
||||
};
|
||||
|
||||
SpectralAggregateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
|
||||
return domainObject.type === 'example.spectral-aggregate-generator';
|
||||
};
|
||||
|
||||
SpectralAggregateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||
var start = options.start;
|
||||
var end = Math.min(Date.now(), options.end); // no future values
|
||||
var count = 5000;
|
||||
if (options.strategy === 'latest' || options.size === 1) {
|
||||
start = end;
|
||||
}
|
||||
|
||||
var data = [];
|
||||
while (start <= end && data.length < 5000) {
|
||||
data.push(pointForTimestamp(start, count, domainObject.name));
|
||||
start += count;
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
};
|
||||
|
||||
return SpectralAggregateGeneratorProvider;
|
||||
|
||||
});
|
102
example/generator/SpectralGeneratorProvider.js
Normal file
102
example/generator/SpectralGeneratorProvider.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.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./WorkerInterface'
|
||||
], function (
|
||||
WorkerInterface
|
||||
) {
|
||||
|
||||
var REQUEST_DEFAULTS = {
|
||||
amplitude: 1,
|
||||
wavelength: 1,
|
||||
period: 10,
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
randomness: 0,
|
||||
phase: 0
|
||||
};
|
||||
|
||||
function SpectralGeneratorProvider() {
|
||||
this.workerInterface = new WorkerInterface();
|
||||
}
|
||||
|
||||
SpectralGeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||
return domainObject.type === 'example.spectral-generator';
|
||||
};
|
||||
|
||||
SpectralGeneratorProvider.prototype.supportsRequest =
|
||||
SpectralGeneratorProvider.prototype.supportsSubscribe =
|
||||
SpectralGeneratorProvider.prototype.canProvideTelemetry;
|
||||
|
||||
SpectralGeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request = {}) {
|
||||
var props = [
|
||||
'amplitude',
|
||||
'wavelength',
|
||||
'period',
|
||||
'offset',
|
||||
'dataRateInHz',
|
||||
'phase',
|
||||
'randomness'
|
||||
];
|
||||
|
||||
var workerRequest = {};
|
||||
|
||||
props.forEach(function (prop) {
|
||||
if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
|
||||
workerRequest[prop] = domainObject.telemetry[prop];
|
||||
}
|
||||
|
||||
if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
|
||||
workerRequest[prop] = request[prop];
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
|
||||
workerRequest[prop] = REQUEST_DEFAULTS[prop];
|
||||
}
|
||||
|
||||
workerRequest[prop] = Number(workerRequest[prop]);
|
||||
});
|
||||
|
||||
workerRequest.name = domainObject.name;
|
||||
|
||||
return workerRequest;
|
||||
};
|
||||
|
||||
SpectralGeneratorProvider.prototype.request = function (domainObject, request) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||
workerRequest.start = request.start;
|
||||
workerRequest.end = request.end;
|
||||
workerRequest.spectra = true;
|
||||
|
||||
return this.workerInterface.request(workerRequest);
|
||||
};
|
||||
|
||||
SpectralGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, {});
|
||||
workerRequest.spectra = true;
|
||||
|
||||
return this.workerInterface.subscribe(workerRequest, callback);
|
||||
};
|
||||
|
||||
return SpectralGeneratorProvider;
|
||||
});
|
@ -63,7 +63,7 @@ define([
|
||||
|
||||
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||
var start = options.start;
|
||||
var end = options.end;
|
||||
var end = Math.min(Date.now(), options.end); // no future values
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
if (options.strategy === 'latest' || options.size === 1) {
|
||||
start = end;
|
||||
|
@ -40,6 +40,11 @@ define([
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
WorkerInterface.prototype.destroy = function () {
|
||||
delete this.worker.onmessage;
|
||||
this.worker.terminate();
|
||||
};
|
||||
|
||||
WorkerInterface.prototype.onMessage = function (message) {
|
||||
message = message.data;
|
||||
var callback = this.callbacks[message.id];
|
||||
|
@ -54,23 +54,38 @@
|
||||
var start = Date.now();
|
||||
var step = 1000 / data.dataRateInHz;
|
||||
var nextStep = start - (start % step) + step;
|
||||
let work;
|
||||
if (data.spectra) {
|
||||
work = function (now) {
|
||||
while (nextStep < now) {
|
||||
const messageCopy = Object.create(message);
|
||||
message.data.start = nextStep - (60 * 1000);
|
||||
message.data.end = nextStep;
|
||||
onRequest(messageCopy);
|
||||
nextStep += step;
|
||||
}
|
||||
|
||||
function work(now) {
|
||||
while (nextStep < now) {
|
||||
self.postMessage({
|
||||
id: message.id,
|
||||
data: {
|
||||
name: data.name,
|
||||
utc: nextStep,
|
||||
yesterday: nextStep - 60 * 60 * 24 * 1000,
|
||||
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
|
||||
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
|
||||
}
|
||||
});
|
||||
nextStep += step;
|
||||
}
|
||||
return nextStep;
|
||||
};
|
||||
} else {
|
||||
work = function (now) {
|
||||
while (nextStep < now) {
|
||||
self.postMessage({
|
||||
id: message.id,
|
||||
data: {
|
||||
name: data.name,
|
||||
utc: nextStep,
|
||||
yesterday: nextStep - 60 * 60 * 24 * 1000,
|
||||
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
|
||||
wavelength: wavelength(start, nextStep),
|
||||
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
|
||||
}
|
||||
});
|
||||
nextStep += step;
|
||||
}
|
||||
|
||||
return nextStep;
|
||||
return nextStep;
|
||||
};
|
||||
}
|
||||
|
||||
subscriptions[message.id] = work;
|
||||
@ -111,13 +126,21 @@
|
||||
utc: nextStep,
|
||||
yesterday: nextStep - 60 * 60 * 24 * 1000,
|
||||
sin: sin(nextStep, period, amplitude, offset, phase, randomness),
|
||||
wavelength: wavelength(start, nextStep),
|
||||
cos: cos(nextStep, period, amplitude, offset, phase, randomness)
|
||||
});
|
||||
}
|
||||
|
||||
self.postMessage({
|
||||
id: message.id,
|
||||
data: data
|
||||
data: request.spectra ? {
|
||||
wavelength: data.map((item) => {
|
||||
return item.wavelength;
|
||||
}),
|
||||
cos: data.map((item) => {
|
||||
return item.cos;
|
||||
})
|
||||
} : data
|
||||
});
|
||||
}
|
||||
|
||||
@ -131,6 +154,10 @@
|
||||
* Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
|
||||
}
|
||||
|
||||
function wavelength(start, nextStep) {
|
||||
return (nextStep - start) / 10;
|
||||
}
|
||||
|
||||
function sendError(error, message) {
|
||||
self.postMessage({
|
||||
error: error.name + ': ' + error.message,
|
||||
|
@ -24,11 +24,15 @@ define([
|
||||
"./GeneratorProvider",
|
||||
"./SinewaveLimitProvider",
|
||||
"./StateGeneratorProvider",
|
||||
"./SpectralGeneratorProvider",
|
||||
"./SpectralAggregateGeneratorProvider",
|
||||
"./GeneratorMetadataProvider"
|
||||
], function (
|
||||
GeneratorProvider,
|
||||
SinewaveLimitProvider,
|
||||
StateGeneratorProvider,
|
||||
SpectralGeneratorProvider,
|
||||
SpectralAggregateGeneratorProvider,
|
||||
GeneratorMetadataProvider
|
||||
) {
|
||||
|
||||
@ -61,6 +65,37 @@ define([
|
||||
|
||||
openmct.telemetry.addProvider(new StateGeneratorProvider());
|
||||
|
||||
openmct.types.addType("example.spectral-generator", {
|
||||
name: "Spectral Generator",
|
||||
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||
cssClass: "icon-generator-telemetry",
|
||||
creatable: true,
|
||||
initialize: function (object) {
|
||||
object.telemetry = {
|
||||
period: 10,
|
||||
amplitude: 1,
|
||||
wavelength: 1,
|
||||
frequency: 1,
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
phase: 0,
|
||||
randomness: 0
|
||||
};
|
||||
}
|
||||
});
|
||||
openmct.telemetry.addProvider(new SpectralGeneratorProvider());
|
||||
|
||||
openmct.types.addType("example.spectral-aggregate-generator", {
|
||||
name: "Spectral Aggregate Generator",
|
||||
description: "For development use. Generates example streaming telemetry data using a simple state algorithm.",
|
||||
cssClass: "icon-generator-telemetry",
|
||||
creatable: true,
|
||||
initialize: function (object) {
|
||||
object.telemetry = {};
|
||||
}
|
||||
});
|
||||
openmct.telemetry.addProvider(new SpectralAggregateGeneratorProvider());
|
||||
|
||||
openmct.types.addType("generator", {
|
||||
name: "Sine Wave Generator",
|
||||
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||
@ -146,7 +181,11 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
openmct.telemetry.addProvider(new GeneratorProvider());
|
||||
const generatorProvider = new GeneratorProvider();
|
||||
openmct.once('destroy', () => {
|
||||
generatorProvider.destroy();
|
||||
});
|
||||
openmct.telemetry.addProvider(generatorProvider);
|
||||
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
|
||||
openmct.telemetry.addProvider(new SinewaveLimitProvider());
|
||||
};
|
||||
|
@ -49,6 +49,10 @@ define([
|
||||
];
|
||||
const IMAGE_DELAY = 20000;
|
||||
|
||||
function getCompassValues(min, max) {
|
||||
return min + Math.random() * (max - min);
|
||||
}
|
||||
|
||||
function pointForTimestamp(timestamp, name) {
|
||||
const url = IMAGE_SAMPLES[Math.floor(timestamp / IMAGE_DELAY) % IMAGE_SAMPLES.length];
|
||||
const urlItems = url.split('/');
|
||||
@ -59,6 +63,9 @@ define([
|
||||
utc: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
|
||||
local: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
|
||||
url,
|
||||
sunOrientation: getCompassValues(0, 360),
|
||||
cameraPan: getCompassValues(0, 360),
|
||||
heading: getCompassValues(0, 360),
|
||||
imageDownloadName
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Vue from 'Vue';
|
||||
import Vue from 'vue';
|
||||
import HelloWorld from './HelloWorld.vue';
|
||||
|
||||
function SimpleVuePlugin() {
|
||||
|
@ -152,7 +152,7 @@
|
||||
<h2>How to Use Glyphs</h2>
|
||||
<div class="cols cols1-1">
|
||||
<div class="col">
|
||||
<p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a psuedo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
|
||||
<p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a pseudo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
|
||||
<p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach.</p>
|
||||
</div>
|
||||
<mct-example><a class="s-button icon-gear" title="Settings"></a>
|
||||
|
@ -88,6 +88,7 @@
|
||||
openmct.install(openmct.plugins.ExampleImagery());
|
||||
openmct.install(openmct.plugins.PlanLayout());
|
||||
openmct.install(openmct.plugins.Timeline());
|
||||
openmct.install(openmct.plugins.Hyperlink());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.install(openmct.plugins.AutoflowView({
|
||||
type: "telemetry.panel"
|
||||
@ -194,6 +195,7 @@
|
||||
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
||||
{indicator: true}
|
||||
));
|
||||
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
|
||||
openmct.start();
|
||||
</script>
|
||||
</html>
|
||||
|
@ -23,9 +23,9 @@
|
||||
/*global module,process*/
|
||||
|
||||
const devMode = process.env.NODE_ENV !== 'production';
|
||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'FirefoxHeadless'];
|
||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
|
||||
const coverageEnabled = process.env.COVERAGE === 'true';
|
||||
const reporters = ['progress', 'html'];
|
||||
const reporters = ['progress', 'html', 'junit'];
|
||||
|
||||
if (coverageEnabled) {
|
||||
reporters.push('coverage-istanbul');
|
||||
@ -59,7 +59,8 @@ module.exports = (config) => {
|
||||
browsers: browsers,
|
||||
client: {
|
||||
jasmine: {
|
||||
random: false
|
||||
random: false,
|
||||
timeoutInterval: 30000
|
||||
}
|
||||
},
|
||||
customLaunchers: {
|
||||
@ -67,6 +68,10 @@ module.exports = (config) => {
|
||||
base: 'Chrome',
|
||||
flags: ['--remote-debugging-port=9222'],
|
||||
debug: true
|
||||
},
|
||||
FirefoxESR: {
|
||||
base: 'FirefoxHeadless',
|
||||
name: 'FirefoxESR'
|
||||
}
|
||||
},
|
||||
colors: true,
|
||||
@ -78,11 +83,21 @@ module.exports = (config) => {
|
||||
preserveDescribeNesting: true,
|
||||
foldAll: false
|
||||
},
|
||||
junitReporter: {
|
||||
outputDir: "dist/reports/tests",
|
||||
outputFile: "test-results.xml",
|
||||
useBrowserName: false
|
||||
},
|
||||
browserConsoleLogOptions: {
|
||||
level: "error",
|
||||
format: "%b %T: %m",
|
||||
terminal: true
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
fixWebpackSourcePaths: true,
|
||||
dir: process.env.CIRCLE_ARTIFACTS ?
|
||||
process.env.CIRCLE_ARTIFACTS + '/coverage' :
|
||||
"dist/reports/coverage",
|
||||
dir: process.env.CIRCLE_ARTIFACTS
|
||||
? process.env.CIRCLE_ARTIFACTS + '/coverage'
|
||||
: "dist/reports/coverage",
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
thresholds: {
|
||||
global: {
|
||||
|
96
lighthouserc.yml
Normal file
96
lighthouserc.yml
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
ci:
|
||||
collect:
|
||||
urls:
|
||||
- http://localhost/
|
||||
numberOfRuns: 5
|
||||
settings:
|
||||
onlyCategories:
|
||||
- performance
|
||||
- best-practices
|
||||
upload:
|
||||
target: temporary-public-storage
|
||||
assert:
|
||||
preset: lighthouse:recommended
|
||||
assertions:
|
||||
### Applicable assertions
|
||||
bootup-time:
|
||||
- warn
|
||||
- minScore: 0.88 #Original value was calculated at 0.88
|
||||
dom-size:
|
||||
- error
|
||||
- maxNumericValue: 200 #Original value was calculated at 188
|
||||
first-contentful-paint:
|
||||
- error
|
||||
- minScore: 0.07 #Original value was calculated at 0.08
|
||||
mainthread-work-breakdown:
|
||||
- warn
|
||||
- minScore: 0.8 #Original value was calculated at 0.8
|
||||
unused-javascript:
|
||||
- warn
|
||||
- maxLength: 1
|
||||
- error
|
||||
- maxNumericValue: 2000 #Original value was calculated at 1855
|
||||
unused-css-rules: warn
|
||||
installable-manifest: warn
|
||||
service-worker: warn
|
||||
### Disabled seo, accessibility, and pwa assertions, below
|
||||
categories:seo: 'off'
|
||||
categories:accessibility: 'off'
|
||||
categories:pwa: 'off'
|
||||
accesskeys: 'off'
|
||||
apple-touch-icon: 'off'
|
||||
aria-allowed-attr: 'off'
|
||||
aria-command-name: 'off'
|
||||
aria-hidden-body: 'off'
|
||||
aria-hidden-focus: 'off'
|
||||
aria-input-field-name: 'off'
|
||||
aria-meter-name: 'off'
|
||||
aria-progressbar-name: 'off'
|
||||
aria-required-attr: 'off'
|
||||
aria-required-children: 'off'
|
||||
aria-required-parent: 'off'
|
||||
aria-roles: 'off'
|
||||
aria-toggle-field-name: 'off'
|
||||
aria-tooltip-name: 'off'
|
||||
aria-treeitem-name: 'off'
|
||||
aria-valid-attr: 'off'
|
||||
aria-valid-attr-value: 'off'
|
||||
button-name: 'off'
|
||||
bypass: 'off'
|
||||
canonical: 'off'
|
||||
color-contrast: 'off'
|
||||
content-width: 'off'
|
||||
crawlable-anchors: 'off'
|
||||
csp-xss: 'off'
|
||||
font-display: 'off'
|
||||
font-size: 'off'
|
||||
maskable-icon: 'off'
|
||||
heading-order: 'off'
|
||||
hreflang: 'off'
|
||||
html-has-lang: 'off'
|
||||
html-lang-valid: 'off'
|
||||
http-status-code: 'off'
|
||||
image-alt: 'off'
|
||||
input-image-alt: 'off'
|
||||
is-crawlable: 'off'
|
||||
label: 'off'
|
||||
link-name: 'off'
|
||||
link-text: 'off'
|
||||
list: 'off'
|
||||
listitem: 'off'
|
||||
meta-description: 'off'
|
||||
meta-refresh: 'off'
|
||||
meta-viewport: 'off'
|
||||
object-alt: 'off'
|
||||
plugins: 'off'
|
||||
robots-txt: 'off'
|
||||
splash-screen: 'off'
|
||||
tabindex: 'off'
|
||||
tap-targets: 'off'
|
||||
td-headers-attr: 'off'
|
||||
th-has-data-cells: 'off'
|
||||
themed-omnibox: 'off'
|
||||
valid-lang: 'off'
|
||||
video-caption: 'off'
|
||||
viewport: 'off'
|
34
package.json
34
package.json
@ -1,8 +1,7 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "1.7.1-SNAPSHOT",
|
||||
"version": "1.7.8-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"angular": ">=1.8.0",
|
||||
"angular-route": "1.4.14",
|
||||
@ -12,16 +11,9 @@
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"cross-env": "^6.0.3",
|
||||
"css-loader": "^1.0.0",
|
||||
"d3-array": "1.2.x",
|
||||
"d3-axis": "1.0.x",
|
||||
"d3-collection": "1.0.x",
|
||||
"d3-color": "1.0.x",
|
||||
"d3-format": "1.2.x",
|
||||
"d3-interpolate": "1.1.x",
|
||||
"d3-scale": "1.0.x",
|
||||
"d3-selection": "1.3.x",
|
||||
"d3-time": "1.0.x",
|
||||
"d3-time-format": "2.1.x",
|
||||
"eslint": "7.0.0",
|
||||
"eslint-plugin-vue": "^7.5.0",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
|
||||
@ -34,20 +26,21 @@
|
||||
"git-rev-sync": "^1.4.0",
|
||||
"glob": ">= 3.0.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html2canvas": "^1.0.0-alpha.12",
|
||||
"html2canvas": "^1.0.0-rc.7",
|
||||
"imports-loader": "^0.8.0",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jasmine-core": "^3.1.0",
|
||||
"jasmine-core": "^3.7.1",
|
||||
"jsdoc": "^3.3.2",
|
||||
"karma": "5.1.1",
|
||||
"karma": "6.3.4",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-firefox-launcher": "1.3.0",
|
||||
"karma-cli": "2.0.0",
|
||||
"karma-coverage": "2.0.3",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-firefox-launcher": "2.1.1",
|
||||
"karma-html-reporter": "0.2.7",
|
||||
"karma-jasmine": "3.3.1",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-junit-reporter": "2.0.1",
|
||||
"karma-sourcemap-loader": "0.3.8",
|
||||
"karma-webpack": "4.0.2",
|
||||
"location-bar": "^3.0.1",
|
||||
"lodash": "^4.17.12",
|
||||
@ -60,7 +53,9 @@
|
||||
"moment-timezone": "0.5.28",
|
||||
"node-bourbon": "^4.2.3",
|
||||
"node-sass": "^4.14.1",
|
||||
"painterro": "^1.0.35",
|
||||
"painterro": "^1.2.56",
|
||||
"plotly.js-basic-dist": "^2.5.0",
|
||||
"plotly.js-gl2d-dist": "^2.5.0",
|
||||
"printj": "^1.2.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"request": "^2.69.0",
|
||||
@ -78,7 +73,8 @@
|
||||
"zepto": "^1.2.0"
|
||||
},
|
||||
"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",
|
||||
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||
@ -88,6 +84,7 @@
|
||||
"test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
|
||||
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
|
||||
"test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
|
||||
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
|
||||
"verify": "concurrently 'npm:test' 'npm:lint'",
|
||||
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
|
||||
@ -99,6 +96,9 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/nasa/openmct.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.10.2 <16.0.0"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"private": true
|
||||
|
@ -164,16 +164,6 @@ define([
|
||||
"license": "license-apache",
|
||||
"link": "http://logging.apache.org/log4net/license.html"
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"when": "/licenses",
|
||||
"template": licensesTemplate
|
||||
},
|
||||
{
|
||||
"when": "/licenses-md",
|
||||
"template": licensesExportMdTemplate
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ define([
|
||||
"./src/navigation/NavigationService",
|
||||
"./src/navigation/NavigateAction",
|
||||
"./src/navigation/OrphanNavigationHandler",
|
||||
"./src/windowing/NewTabAction",
|
||||
"./res/templates/browse.html",
|
||||
"./res/templates/browse-object.html",
|
||||
"./res/templates/browse/object-header.html",
|
||||
@ -37,7 +36,6 @@ define([
|
||||
NavigationService,
|
||||
NavigateAction,
|
||||
OrphanNavigationHandler,
|
||||
NewTabAction,
|
||||
browseTemplate,
|
||||
browseObjectTemplate,
|
||||
objectHeaderTemplate,
|
||||
@ -52,8 +50,6 @@ define([
|
||||
name: "platform/commonUI/browse",
|
||||
definition: {
|
||||
"extensions": {
|
||||
"routes": [
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "DEFAULT_PATH",
|
||||
@ -128,23 +124,6 @@ define([
|
||||
"depends": [
|
||||
"navigationService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "window",
|
||||
"name": "Open In New Tab",
|
||||
"implementation": NewTabAction,
|
||||
"description": "Open in a new browser tab",
|
||||
"category": [
|
||||
"view-control",
|
||||
"contextual"
|
||||
],
|
||||
"depends": [
|
||||
"urlService",
|
||||
"$window"
|
||||
],
|
||||
"group": "windowing",
|
||||
"priority": 10,
|
||||
"cssClass": "icon-new-window"
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
|
@ -39,9 +39,6 @@ define(
|
||||
this.callbacks = [];
|
||||
this.checks = [];
|
||||
this.$window = $window;
|
||||
|
||||
this.oldUnload = $window.onbeforeunload;
|
||||
$window.onbeforeunload = this.onBeforeUnload.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,7 +61,7 @@ define(
|
||||
*
|
||||
* @param {DomainObject} domainObject the domain object to navigate to
|
||||
* @param {Boolean} force if true, force navigation to occur.
|
||||
* @returns {Boolean} true if navigation occured, otherwise false.
|
||||
* @returns {Boolean} true if navigation occurred, otherwise false.
|
||||
*/
|
||||
NavigationService.prototype.setNavigation = function (domainObject, force) {
|
||||
if (force) {
|
||||
|
@ -1,75 +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/windowing/NewTabAction"],
|
||||
function (NewTabAction) {
|
||||
|
||||
describe("The new tab action", function () {
|
||||
var actionSelected,
|
||||
actionCurrent,
|
||||
mockWindow,
|
||||
mockContextCurrent,
|
||||
mockContextSelected,
|
||||
mockUrlService;
|
||||
|
||||
beforeEach(function () {
|
||||
mockWindow = jasmine.createSpyObj("$window", ["open", "location"]);
|
||||
|
||||
// Context if the current object is selected
|
||||
// For example, when the top right new tab
|
||||
// button is clicked, the user is using the
|
||||
// current domainObject
|
||||
mockContextCurrent = jasmine.createSpyObj("context", ["domainObject"]);
|
||||
|
||||
// Context if the selected object is selected
|
||||
// For example, when an object in the left
|
||||
// tree is opened in a new tab using the
|
||||
// context menu
|
||||
mockContextSelected = jasmine.createSpyObj("context", ["selectedObject",
|
||||
"domainObject"]);
|
||||
|
||||
// Mocks the urlService used to make the new tab's url from a
|
||||
// domainObject and mode
|
||||
mockUrlService = jasmine.createSpyObj("urlService", ["urlForNewTab"]);
|
||||
|
||||
// Action done using the current context or mockContextCurrent
|
||||
actionCurrent = new NewTabAction(mockUrlService, mockWindow,
|
||||
mockContextCurrent);
|
||||
|
||||
// Action done using the selected context or mockContextSelected
|
||||
actionSelected = new NewTabAction(mockUrlService, mockWindow,
|
||||
mockContextSelected);
|
||||
|
||||
});
|
||||
|
||||
it("new tab with current url is opened", function () {
|
||||
actionCurrent.perform();
|
||||
});
|
||||
|
||||
it("new tab with a selected url is opened", function () {
|
||||
actionSelected.perform();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -86,7 +86,7 @@ define(
|
||||
})
|
||||
.join('/');
|
||||
|
||||
window.location.href = url;
|
||||
openmct.router.navigate(url);
|
||||
|
||||
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
|
||||
openmct.editor.edit();
|
||||
|
@ -21,28 +21,14 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"./src/MCTDevice",
|
||||
"./src/AgentService",
|
||||
"./src/DeviceClassifier"
|
||||
"./src/AgentService"
|
||||
], function (
|
||||
MCTDevice,
|
||||
AgentService,
|
||||
DeviceClassifier
|
||||
AgentService
|
||||
) {
|
||||
|
||||
return {
|
||||
name: "platform/commonUI/mobile",
|
||||
definition: {
|
||||
"extensions": {
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctDevice",
|
||||
"implementation": MCTDevice,
|
||||
"depends": [
|
||||
"agentService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "agentService",
|
||||
@ -51,15 +37,6 @@ define([
|
||||
"$window"
|
||||
]
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"implementation": DeviceClassifier,
|
||||
"depends": [
|
||||
"agentService",
|
||||
"$document"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -20,122 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Provides features which support variant behavior on mobile devices.
|
||||
*
|
||||
* @namespace platform/commonUI/mobile
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
define(["../../../../src/utils/agent/Agent.js"], function (Agent) {
|
||||
function AngularAgentServiceWrapper(window) {
|
||||
const AS = Agent.default;
|
||||
|
||||
/**
|
||||
* The query service handles calls for browser and userAgent
|
||||
* info using a comparison between the userAgent and key
|
||||
* device names
|
||||
* @constructor
|
||||
* @param $window Angular-injected instance of the window
|
||||
* @memberof platform/commonUI/mobile
|
||||
*/
|
||||
function AgentService($window) {
|
||||
var userAgent = $window.navigator.userAgent,
|
||||
matches = userAgent.match(/iPad|iPhone|Android/i) || [];
|
||||
|
||||
this.userAgent = userAgent;
|
||||
this.mobileName = matches[0];
|
||||
this.$window = $window;
|
||||
this.touchEnabled = ($window.ontouchstart !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is on a mobile device.
|
||||
* @returns {boolean} true on mobile
|
||||
*/
|
||||
AgentService.prototype.isMobile = function () {
|
||||
return Boolean(this.mobileName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user is on a phone-sized mobile device.
|
||||
* @returns {boolean} true on a phone
|
||||
*/
|
||||
AgentService.prototype.isPhone = function () {
|
||||
if (this.isMobile()) {
|
||||
if (this.isAndroidTablet()) {
|
||||
return false;
|
||||
} else if (this.mobileName === 'iPad') {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user is on a tablet sized android device
|
||||
* @returns {boolean} true on an android tablet
|
||||
*/
|
||||
AgentService.prototype.isAndroidTablet = function () {
|
||||
if (this.mobileName === 'Android') {
|
||||
if (this.isPortrait() && window.innerWidth >= 768) {
|
||||
return true;
|
||||
} else if (this.isLandscape() && window.innerHeight >= 768) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user is on a tablet-sized mobile device.
|
||||
* @returns {boolean} true on a tablet
|
||||
*/
|
||||
AgentService.prototype.isTablet = function () {
|
||||
return (this.isMobile() && !this.isPhone() && this.mobileName !== 'Android') || (this.isMobile() && this.isAndroidTablet());
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user's device is in a portrait-style
|
||||
* orientation (display width is narrower than display height.)
|
||||
* @returns {boolean} true in portrait mode
|
||||
*/
|
||||
AgentService.prototype.isPortrait = function () {
|
||||
return this.$window.innerWidth < this.$window.innerHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user's device is in a landscape-style
|
||||
* orientation (display width is greater than display height.)
|
||||
* @returns {boolean} true in landscape mode
|
||||
*/
|
||||
AgentService.prototype.isLandscape = function () {
|
||||
return !this.isPortrait();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user's device supports a touch interface.
|
||||
* @returns {boolean} true if touch is supported
|
||||
*/
|
||||
AgentService.prototype.isTouch = function () {
|
||||
return this.touchEnabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user agent matches a certain named device,
|
||||
* as indicated by checking for a case-insensitive substring
|
||||
* match.
|
||||
* @param {string} name the name to check for
|
||||
* @returns {boolean} true if the user agent includes that name
|
||||
*/
|
||||
AgentService.prototype.isBrowser = function (name) {
|
||||
name = name.toLowerCase();
|
||||
|
||||
return this.userAgent.toLowerCase().indexOf(name) !== -1;
|
||||
};
|
||||
|
||||
return AgentService;
|
||||
return new AS(window);
|
||||
}
|
||||
);
|
||||
|
||||
return AngularAgentServiceWrapper;
|
||||
});
|
||||
|
96
platform/commonUI/mobile/src/AgentServiceSpec.js
Normal file
96
platform/commonUI/mobile/src/AgentServiceSpec.js
Normal file
@ -0,0 +1,96 @@
|
||||
/*****************************************************************************
|
||||
* 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 AgentService from "./AgentService";
|
||||
|
||||
const TEST_USER_AGENTS = {
|
||||
DESKTOP:
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
|
||||
IPAD:
|
||||
"Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
|
||||
IPHONE:
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53"
|
||||
};
|
||||
|
||||
describe("The AgentService", function () {
|
||||
let testWindow;
|
||||
let agentService;
|
||||
|
||||
beforeEach(function () {
|
||||
testWindow = {
|
||||
innerWidth: 640,
|
||||
innerHeight: 480,
|
||||
navigator: {
|
||||
userAgent: TEST_USER_AGENTS.DESKTOP
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it("recognizes desktop devices as non-mobile", function () {
|
||||
testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP;
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isMobile()).toBeFalsy();
|
||||
expect(agentService.isPhone()).toBeFalsy();
|
||||
expect(agentService.isTablet()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("detects iPhones", function () {
|
||||
testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE;
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isMobile()).toBeTruthy();
|
||||
expect(agentService.isPhone()).toBeTruthy();
|
||||
expect(agentService.isTablet()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("detects iPads", function () {
|
||||
testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD;
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isMobile()).toBeTruthy();
|
||||
expect(agentService.isPhone()).toBeFalsy();
|
||||
expect(agentService.isTablet()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("detects display orientation", function () {
|
||||
agentService = new AgentService(testWindow);
|
||||
testWindow.innerWidth = 1024;
|
||||
testWindow.innerHeight = 400;
|
||||
expect(agentService.isPortrait()).toBeFalsy();
|
||||
expect(agentService.isLandscape()).toBeTruthy();
|
||||
testWindow.innerWidth = 400;
|
||||
testWindow.innerHeight = 1024;
|
||||
expect(agentService.isPortrait()).toBeTruthy();
|
||||
expect(agentService.isLandscape()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("detects touch support", function () {
|
||||
testWindow.ontouchstart = null;
|
||||
expect(new AgentService(testWindow).isTouch()).toBe(true);
|
||||
delete testWindow.ontouchstart;
|
||||
expect(new AgentService(testWindow).isTouch()).toBe(false);
|
||||
});
|
||||
|
||||
it("allows for checking browser type", function () {
|
||||
testWindow.navigator.userAgent = "Chromezilla Safarifox";
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isBrowser("Chrome")).toBe(true);
|
||||
expect(agentService.isBrowser("Firefox")).toBe(false);
|
||||
});
|
||||
});
|
@ -1,72 +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(
|
||||
['./DeviceMatchers'],
|
||||
function (DeviceMatchers) {
|
||||
|
||||
/**
|
||||
* Runs at application startup and adds a subset of the following
|
||||
* CSS classes to the body of the document, depending on device
|
||||
* attributes:
|
||||
*
|
||||
* * `mobile`: Phones or tablets.
|
||||
* * `phone`: Phones specifically.
|
||||
* * `tablet`: Tablets specifically.
|
||||
* * `desktop`: Non-mobile devices.
|
||||
* * `portrait`: Devices in a portrait-style orientation.
|
||||
* * `landscape`: Devices in a landscape-style orientation.
|
||||
* * `touch`: Device supports touch events.
|
||||
*
|
||||
* @param {platform/commonUI/mobile.AgentService} agentService
|
||||
* the service used to examine the user agent
|
||||
* @param $document Angular's jqLite-wrapped document element
|
||||
* @constructor
|
||||
*/
|
||||
function MobileClassifier(agentService, $document) {
|
||||
var body = $document.find('body');
|
||||
|
||||
Object.keys(DeviceMatchers).forEach(function (key, index, array) {
|
||||
if (DeviceMatchers[key](agentService)) {
|
||||
body.addClass(key);
|
||||
}
|
||||
});
|
||||
|
||||
if (agentService.isMobile()) {
|
||||
var mediaQuery = window.matchMedia('(orientation: landscape)');
|
||||
|
||||
mediaQuery.addListener(function (event) {
|
||||
if (event.matches) {
|
||||
body.removeClass('portrait');
|
||||
body.addClass('landscape');
|
||||
} else {
|
||||
body.removeClass('landscape');
|
||||
body.addClass('portrait');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return MobileClassifier;
|
||||
|
||||
}
|
||||
);
|
@ -1,58 +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 () {
|
||||
|
||||
/**
|
||||
* An object containing key-value pairs, where keys are symbolic of
|
||||
* device attributes, and values are functions that take the
|
||||
* `agentService` as inputs and return boolean values indicating
|
||||
* whether or not the current device has these attributes.
|
||||
*
|
||||
* For internal use by the mobile support bundle.
|
||||
*
|
||||
* @memberof platform/commonUI/mobile
|
||||
* @private
|
||||
*/
|
||||
return {
|
||||
mobile: function (agentService) {
|
||||
return agentService.isMobile();
|
||||
},
|
||||
phone: function (agentService) {
|
||||
return agentService.isPhone();
|
||||
},
|
||||
tablet: function (agentService) {
|
||||
return agentService.isTablet();
|
||||
},
|
||||
desktop: function (agentService) {
|
||||
return !agentService.isMobile();
|
||||
},
|
||||
portrait: function (agentService) {
|
||||
return agentService.isPortrait();
|
||||
},
|
||||
landscape: function (agentService) {
|
||||
return agentService.isLandscape();
|
||||
},
|
||||
touch: function (agentService) {
|
||||
return agentService.isTouch();
|
||||
}
|
||||
};
|
||||
});
|
@ -1,88 +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(
|
||||
['./DeviceMatchers'],
|
||||
function (DeviceMatchers) {
|
||||
|
||||
/**
|
||||
* The `mct-device` directive, when applied as an attribute,
|
||||
* only includes the element when the device being used matches
|
||||
* a set of characteristics required.
|
||||
*
|
||||
* Required characteristics are given as space-separated strings
|
||||
* as the value to this attribute, e.g.:
|
||||
*
|
||||
* <span mct-device="mobile portrait">Hello world!</span>
|
||||
*
|
||||
* ...will only show Hello world! when viewed on a mobile device
|
||||
* in the portrait orientation.
|
||||
*
|
||||
* Valid device characteristics to detect are:
|
||||
*
|
||||
* * `mobile`: Phones or tablets.
|
||||
* * `phone`: Phones specifically.
|
||||
* * `tablet`: Tablets specifically.
|
||||
* * `desktop`: Non-mobile devices.
|
||||
* * `portrait`: Devices in a portrait-style orientation.
|
||||
* * `landscape`: Devices in a landscape-style orientation.
|
||||
* * `touch`: Device supports touch events.
|
||||
*
|
||||
* @param {AgentService} agentService used to detect device type
|
||||
* based on information about the user agent
|
||||
*/
|
||||
function MCTDevice(agentService) {
|
||||
|
||||
function deviceMatches(tokens) {
|
||||
tokens = tokens || "";
|
||||
|
||||
return tokens.split(" ").every(function (token) {
|
||||
var fn = DeviceMatchers[token];
|
||||
|
||||
return fn && fn(agentService);
|
||||
});
|
||||
}
|
||||
|
||||
function link(scope, element, attrs, ctrl, transclude) {
|
||||
if (deviceMatches(attrs.mctDevice)) {
|
||||
transclude(function (clone) {
|
||||
element.replaceWith(clone);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
link: link,
|
||||
// We are transcluding the whole element (like ng-if)
|
||||
transclude: 'element',
|
||||
// 1 more than ng-if
|
||||
priority: 601,
|
||||
// Also terminal, since element will be transcluded
|
||||
terminal: true,
|
||||
// Only apply as an attribute
|
||||
restrict: "A"
|
||||
};
|
||||
}
|
||||
|
||||
return MCTDevice;
|
||||
}
|
||||
);
|
@ -1,99 +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/AgentService"],
|
||||
function (AgentService) {
|
||||
|
||||
var TEST_USER_AGENTS = {
|
||||
DESKTOP: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
|
||||
IPAD: "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
|
||||
IPHONE: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53"
|
||||
};
|
||||
|
||||
describe("The AgentService", function () {
|
||||
var testWindow, agentService;
|
||||
|
||||
beforeEach(function () {
|
||||
testWindow = {
|
||||
innerWidth: 640,
|
||||
innerHeight: 480,
|
||||
navigator: {
|
||||
userAgent: TEST_USER_AGENTS.DESKTOP
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it("recognizes desktop devices as non-mobile", function () {
|
||||
testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP;
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isMobile()).toBeFalsy();
|
||||
expect(agentService.isPhone()).toBeFalsy();
|
||||
expect(agentService.isTablet()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("detects iPhones", function () {
|
||||
testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE;
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isMobile()).toBeTruthy();
|
||||
expect(agentService.isPhone()).toBeTruthy();
|
||||
expect(agentService.isTablet()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("detects iPads", function () {
|
||||
testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD;
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isMobile()).toBeTruthy();
|
||||
expect(agentService.isPhone()).toBeFalsy();
|
||||
expect(agentService.isTablet()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("detects display orientation", function () {
|
||||
agentService = new AgentService(testWindow);
|
||||
testWindow.innerWidth = 1024;
|
||||
testWindow.innerHeight = 400;
|
||||
expect(agentService.isPortrait()).toBeFalsy();
|
||||
expect(agentService.isLandscape()).toBeTruthy();
|
||||
testWindow.innerWidth = 400;
|
||||
testWindow.innerHeight = 1024;
|
||||
expect(agentService.isPortrait()).toBeTruthy();
|
||||
expect(agentService.isLandscape()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("detects touch support", function () {
|
||||
testWindow.ontouchstart = null;
|
||||
expect(new AgentService(testWindow).isTouch())
|
||||
.toBe(true);
|
||||
delete testWindow.ontouchstart;
|
||||
expect(new AgentService(testWindow).isTouch())
|
||||
.toBe(false);
|
||||
});
|
||||
|
||||
it("allows for checking browser type", function () {
|
||||
testWindow.navigator.userAgent = "Chromezilla Safarifox";
|
||||
agentService = new AgentService(testWindow);
|
||||
expect(agentService.isBrowser("Chrome")).toBe(true);
|
||||
expect(agentService.isBrowser("Firefox")).toBe(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,109 +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/DeviceClassifier", "../src/DeviceMatchers"],
|
||||
function (DeviceClassifier, DeviceMatchers) {
|
||||
|
||||
var AGENT_SERVICE_METHODS = [
|
||||
'isMobile',
|
||||
'isPhone',
|
||||
'isTablet',
|
||||
'isPortrait',
|
||||
'isLandscape',
|
||||
'isTouch'
|
||||
],
|
||||
TEST_PERMUTATIONS = [
|
||||
['isMobile', 'isPhone', 'isTouch', 'isPortrait'],
|
||||
['isMobile', 'isPhone', 'isTouch', 'isLandscape'],
|
||||
['isMobile', 'isTablet', 'isTouch', 'isPortrait'],
|
||||
['isMobile', 'isTablet', 'isTouch', 'isLandscape'],
|
||||
['isTouch'],
|
||||
[]
|
||||
];
|
||||
|
||||
describe("DeviceClassifier", function () {
|
||||
var mockAgentService,
|
||||
mockDocument,
|
||||
mockBody;
|
||||
|
||||
beforeEach(function () {
|
||||
mockAgentService = jasmine.createSpyObj(
|
||||
'agentService',
|
||||
AGENT_SERVICE_METHODS
|
||||
);
|
||||
mockDocument = jasmine.createSpyObj(
|
||||
'$document',
|
||||
['find']
|
||||
);
|
||||
mockBody = jasmine.createSpyObj(
|
||||
'body',
|
||||
['addClass']
|
||||
);
|
||||
mockDocument.find.and.callFake(function (sel) {
|
||||
return sel === 'body' && mockBody;
|
||||
});
|
||||
AGENT_SERVICE_METHODS.forEach(function (m) {
|
||||
mockAgentService[m].and.returnValue(false);
|
||||
});
|
||||
});
|
||||
|
||||
TEST_PERMUTATIONS.forEach(function (trueMethods) {
|
||||
var summary = trueMethods.length === 0
|
||||
? "device has no detected characteristics"
|
||||
: "device " + (trueMethods.join(", "));
|
||||
|
||||
describe("when " + summary, function () {
|
||||
var classifier; // eslint-disable-line
|
||||
|
||||
beforeEach(function () {
|
||||
trueMethods.forEach(function (m) {
|
||||
mockAgentService[m].and.returnValue(true);
|
||||
});
|
||||
classifier = new DeviceClassifier(
|
||||
mockAgentService,
|
||||
mockDocument
|
||||
);
|
||||
});
|
||||
|
||||
it("adds classes for matching, detected characteristics", function () {
|
||||
Object.keys(DeviceMatchers).filter(function (m) {
|
||||
return DeviceMatchers[m](mockAgentService);
|
||||
}).forEach(function (key) {
|
||||
expect(mockBody.addClass)
|
||||
.toHaveBeenCalledWith(key);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not add classes for non-matching characteristics", function () {
|
||||
Object.keys(DeviceMatchers).filter(function (m) {
|
||||
return !DeviceMatchers[m](mockAgentService);
|
||||
}).forEach(function (key) {
|
||||
expect(mockBody.addClass)
|
||||
.not.toHaveBeenCalledWith(key);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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/DeviceMatchers"],
|
||||
function (DeviceMatchers) {
|
||||
|
||||
describe("DeviceMatchers", function () {
|
||||
var mockAgentService;
|
||||
|
||||
beforeEach(function () {
|
||||
mockAgentService = jasmine.createSpyObj(
|
||||
'agentService',
|
||||
[
|
||||
'isMobile',
|
||||
'isPhone',
|
||||
'isTablet',
|
||||
'isPortrait',
|
||||
'isLandscape',
|
||||
'isTouch'
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it("detects when a device is a desktop device", function () {
|
||||
mockAgentService.isMobile.and.returnValue(false);
|
||||
expect(DeviceMatchers.desktop(mockAgentService))
|
||||
.toBe(true);
|
||||
mockAgentService.isMobile.and.returnValue(true);
|
||||
expect(DeviceMatchers.desktop(mockAgentService))
|
||||
.toBe(false);
|
||||
});
|
||||
|
||||
function method(deviceType) {
|
||||
return "is" + deviceType[0].toUpperCase() + deviceType.slice(1);
|
||||
}
|
||||
|
||||
[
|
||||
"mobile",
|
||||
"phone",
|
||||
"tablet",
|
||||
"landscape",
|
||||
"portrait",
|
||||
"landscape",
|
||||
"touch"
|
||||
].forEach(function (deviceType) {
|
||||
it("detects when a device is a " + deviceType + " device", function () {
|
||||
mockAgentService[method(deviceType)].and.returnValue(true);
|
||||
expect(DeviceMatchers[deviceType](mockAgentService))
|
||||
.toBe(true);
|
||||
mockAgentService[method(deviceType)].and.returnValue(false);
|
||||
expect(DeviceMatchers[deviceType](mockAgentService))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,168 +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/MCTDevice'],
|
||||
function (MCTDevice) {
|
||||
|
||||
var JQLITE_METHODS = ['replaceWith'];
|
||||
|
||||
describe("The mct-device directive", function () {
|
||||
var mockAgentService,
|
||||
mockTransclude,
|
||||
mockElement,
|
||||
mockClone,
|
||||
testAttrs,
|
||||
directive;
|
||||
|
||||
function link() {
|
||||
directive.link(null, mockElement, testAttrs, null, mockTransclude);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockAgentService = jasmine.createSpyObj(
|
||||
"agentService",
|
||||
["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"]
|
||||
);
|
||||
mockTransclude = jasmine.createSpy("$transclude");
|
||||
mockElement = jasmine.createSpyObj(name, JQLITE_METHODS);
|
||||
mockClone = jasmine.createSpyObj(name, JQLITE_METHODS);
|
||||
|
||||
mockTransclude.and.callFake(function (fn) {
|
||||
fn(mockClone);
|
||||
});
|
||||
|
||||
// Look desktop-like by default
|
||||
mockAgentService.isLandscape.and.returnValue(true);
|
||||
|
||||
testAttrs = {};
|
||||
|
||||
directive = new MCTDevice(mockAgentService);
|
||||
});
|
||||
|
||||
function expectInclusion() {
|
||||
expect(mockElement.replaceWith)
|
||||
.toHaveBeenCalledWith(mockClone);
|
||||
}
|
||||
|
||||
function expectExclusion() {
|
||||
expect(mockElement.replaceWith).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
it("is applicable at the attribute level", function () {
|
||||
expect(directive.restrict).toEqual("A");
|
||||
});
|
||||
|
||||
it("transcludes at the element level", function () {
|
||||
expect(directive.transclude).toEqual('element');
|
||||
});
|
||||
|
||||
it("has a greater priority number than ng-if", function () {
|
||||
expect(directive.priority > 600).toBeTruthy();
|
||||
});
|
||||
|
||||
it("restricts element inclusion for mobile devices", function () {
|
||||
testAttrs.mctDevice = "mobile";
|
||||
link();
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isMobile.and.returnValue(true);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
|
||||
it("restricts element inclusion for tablet devices", function () {
|
||||
testAttrs.mctDevice = "tablet";
|
||||
mockAgentService.isMobile.and.returnValue(true);
|
||||
link();
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isTablet.and.returnValue(true);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
|
||||
it("restricts element inclusion for phone devices", function () {
|
||||
testAttrs.mctDevice = "phone";
|
||||
mockAgentService.isMobile.and.returnValue(true);
|
||||
link();
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isPhone.and.returnValue(true);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
|
||||
it("restricts element inclusion for desktop devices", function () {
|
||||
testAttrs.mctDevice = "desktop";
|
||||
mockAgentService.isMobile.and.returnValue(true);
|
||||
link();
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isMobile.and.returnValue(false);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
|
||||
it("restricts element inclusion for portrait orientation", function () {
|
||||
testAttrs.mctDevice = "portrait";
|
||||
link();
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isPortrait.and.returnValue(true);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
|
||||
it("restricts element inclusion for landscape orientation", function () {
|
||||
testAttrs.mctDevice = "landscape";
|
||||
mockAgentService.isLandscape.and.returnValue(false);
|
||||
mockAgentService.isPortrait.and.returnValue(true);
|
||||
link();
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isLandscape.and.returnValue(true);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
|
||||
it("allows multiple device characteristics to be requested", function () {
|
||||
// Won't try to test every permutation here, just
|
||||
// make sure the multi-characteristic feature has support.
|
||||
testAttrs.mctDevice = "portrait mobile";
|
||||
link();
|
||||
// Neither portrait nor mobile, not called
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isPortrait.and.returnValue(true);
|
||||
link();
|
||||
|
||||
// Was portrait, but not mobile, so no
|
||||
expectExclusion();
|
||||
|
||||
mockAgentService.isMobile.and.returnValue(true);
|
||||
link();
|
||||
expectInclusion();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -379,7 +379,7 @@ define([
|
||||
{
|
||||
"name": "Math.uuid.js",
|
||||
"version": "1.4.7",
|
||||
"description": "Unique identifer generation (code adapted.)",
|
||||
"description": "Unique identifier generation (code adapted.)",
|
||||
"author": "Robert Kieffer",
|
||||
"website": "https://github.com/broofa/node-uuid",
|
||||
"copyright": "Copyright (c) 2010-2012 Robert Kieffer",
|
||||
|
@ -21,36 +21,24 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"moment-timezone",
|
||||
"./src/indicators/ClockIndicator",
|
||||
"./src/indicators/FollowIndicator",
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/ClockController",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/FollowTimerAction",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
"./src/actions/PauseTimerAction",
|
||||
"./res/templates/clock.html",
|
||||
"./res/templates/timer.html"
|
||||
], function (
|
||||
MomentTimezone,
|
||||
ClockIndicator,
|
||||
FollowIndicator,
|
||||
TickerService,
|
||||
TimerService,
|
||||
ClockController,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
FollowTimerAction,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
PauseTimerAction,
|
||||
clockTemplate,
|
||||
timerTemplate
|
||||
) {
|
||||
return {
|
||||
@ -77,23 +65,14 @@ define([
|
||||
"value": "YYYY/MM/DD HH:mm:ss"
|
||||
}
|
||||
],
|
||||
"indicators": [
|
||||
{
|
||||
"implementation": ClockIndicator,
|
||||
"depends": [
|
||||
"tickerService",
|
||||
"CLOCK_INDICATOR_FORMAT"
|
||||
],
|
||||
"priority": "preferred"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "tickerService",
|
||||
"implementation": TickerService,
|
||||
"depends": [
|
||||
"$timeout",
|
||||
"now"
|
||||
"now",
|
||||
"$rootScope"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -103,14 +82,6 @@ define([
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "ClockController",
|
||||
"implementation": ClockController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"tickerService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "TimerController",
|
||||
"implementation": TimerController,
|
||||
@ -130,12 +101,6 @@ define([
|
||||
}
|
||||
],
|
||||
"views": [
|
||||
{
|
||||
"key": "clock",
|
||||
"type": "clock",
|
||||
"editable": false,
|
||||
"template": clockTemplate
|
||||
},
|
||||
{
|
||||
"key": "timer",
|
||||
"type": "timer",
|
||||
@ -144,15 +109,6 @@ define([
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.follow",
|
||||
"implementation": FollowTimerAction,
|
||||
"depends": ["timerService"],
|
||||
"category": "contextual",
|
||||
"name": "Follow Timer",
|
||||
"cssClass": "icon-clock",
|
||||
"priority": "optional"
|
||||
},
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
@ -194,75 +150,11 @@ define([
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Stop",
|
||||
"cssClass": "icon-box",
|
||||
"cssClass": "icon-box-round-corners",
|
||||
"priority": "preferred"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"key": "clock",
|
||||
"name": "Clock",
|
||||
"cssClass": "icon-clock",
|
||||
"description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.",
|
||||
"priority": 101,
|
||||
"features": [
|
||||
"creation"
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"key": "clockFormat",
|
||||
"name": "Display Format",
|
||||
"control": "composite",
|
||||
"items": [
|
||||
{
|
||||
"control": "select",
|
||||
"options": [
|
||||
{
|
||||
"value": "YYYY/MM/DD hh:mm:ss",
|
||||
"name": "YYYY/MM/DD hh:mm:ss"
|
||||
},
|
||||
{
|
||||
"value": "YYYY/DDD hh:mm:ss",
|
||||
"name": "YYYY/DDD hh:mm:ss"
|
||||
},
|
||||
{
|
||||
"value": "hh:mm:ss",
|
||||
"name": "hh:mm:ss"
|
||||
}
|
||||
],
|
||||
"cssClass": "l-inline"
|
||||
},
|
||||
{
|
||||
"control": "select",
|
||||
"options": [
|
||||
{
|
||||
"value": "clock12",
|
||||
"name": "12hr"
|
||||
},
|
||||
{
|
||||
"value": "clock24",
|
||||
"name": "24hr"
|
||||
}
|
||||
],
|
||||
"cssClass": "l-inline"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "timezone",
|
||||
"name": "Timezone",
|
||||
"control": "autocomplete",
|
||||
"options": MomentTimezone.tz.names()
|
||||
}
|
||||
],
|
||||
"model": {
|
||||
"clockFormat": [
|
||||
"YYYY/MM/DD hh:mm:ss",
|
||||
"clock12"
|
||||
],
|
||||
"timezone": "UTC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "timer",
|
||||
"name": "Timer",
|
||||
@ -299,10 +191,7 @@ define([
|
||||
}
|
||||
}
|
||||
],
|
||||
"runs": [{
|
||||
"implementation": FollowIndicator,
|
||||
"depends": ["openmct", "timerService"]
|
||||
}],
|
||||
"runs": [],
|
||||
"licenses": [
|
||||
{
|
||||
"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,110 +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([
|
||||
'moment',
|
||||
'moment-timezone'
|
||||
],
|
||||
function (
|
||||
moment,
|
||||
momentTimezone
|
||||
) {
|
||||
|
||||
/**
|
||||
* Controller for views of a Clock domain object.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
* @param {angular.Scope} $scope the Angular scope
|
||||
* @param {platform/features/clock.TickerService} tickerService
|
||||
* a service used to align behavior with clock ticks
|
||||
*/
|
||||
function ClockController($scope, tickerService) {
|
||||
var lastTimestamp,
|
||||
unlisten,
|
||||
timeFormat,
|
||||
zoneName,
|
||||
self = this;
|
||||
|
||||
function update() {
|
||||
var m = zoneName
|
||||
? moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp);
|
||||
self.zoneAbbr = m.zoneAbbr();
|
||||
self.textValue = timeFormat && m.format(timeFormat);
|
||||
self.ampmValue = m.format("A"); // Just the AM or PM part
|
||||
}
|
||||
|
||||
function tick(timestamp) {
|
||||
lastTimestamp = timestamp;
|
||||
update();
|
||||
}
|
||||
|
||||
function updateModel(model) {
|
||||
var baseFormat;
|
||||
if (model !== undefined) {
|
||||
baseFormat = model.clockFormat[0];
|
||||
|
||||
self.use24 = model.clockFormat[1] === 'clock24';
|
||||
timeFormat = self.use24
|
||||
? baseFormat.replace('hh', "HH") : baseFormat;
|
||||
// If wrong timezone is provided, the UTC will be used
|
||||
zoneName = momentTimezone.tz.names().includes(model.timezone)
|
||||
? model.timezone : "UTC";
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
// Pull in the model (clockFormat and timezone) from the domain object model
|
||||
$scope.$watch('model', updateModel);
|
||||
|
||||
// Listen for clock ticks ... and stop listening on destroy
|
||||
unlisten = tickerService.listen(tick);
|
||||
$scope.$on('$destroy', unlisten);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clock's time zone, as displayable text.
|
||||
* @returns {string}
|
||||
*/
|
||||
ClockController.prototype.zone = function () {
|
||||
return this.zoneAbbr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current time, as displayable text.
|
||||
* @returns {string}
|
||||
*/
|
||||
ClockController.prototype.text = function () {
|
||||
return this.textValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to display to qualify a time as AM or PM.
|
||||
* @returns {string}
|
||||
*/
|
||||
ClockController.prototype.ampm = function () {
|
||||
return this.use24 ? '' : this.ampmValue;
|
||||
};
|
||||
|
||||
return ClockController;
|
||||
}
|
||||
);
|
@ -1,65 +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(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
|
||||
/**
|
||||
* Indicator that displays the current UTC time in the status area.
|
||||
* @implements {Indicator}
|
||||
* @memberof platform/features/clock
|
||||
* @param {platform/features/clock.TickerService} tickerService
|
||||
* a service used to align behavior with clock ticks
|
||||
* @param {string} indicatorFormat format string for timestamps
|
||||
* shown in this indicator
|
||||
*/
|
||||
function ClockIndicator(tickerService, indicatorFormat) {
|
||||
var self = this;
|
||||
|
||||
this.text = "";
|
||||
|
||||
tickerService.listen(function (timestamp) {
|
||||
self.text = moment.utc(timestamp)
|
||||
.format(indicatorFormat) + " UTC";
|
||||
});
|
||||
}
|
||||
|
||||
ClockIndicator.prototype.getGlyphClass = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getCssClass = function () {
|
||||
return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable";
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getText = function () {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getDescription = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
return ClockIndicator;
|
||||
}
|
||||
);
|
@ -32,8 +32,13 @@ define(
|
||||
* @param $timeout Angular's $timeout
|
||||
* @param {Function} now function to provide the current time in ms
|
||||
*/
|
||||
function TickerService($timeout, now) {
|
||||
function TickerService($timeout, now, $rootScope) {
|
||||
var self = this;
|
||||
var timeoutId;
|
||||
|
||||
$rootScope.$on('$destroy', function () {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
|
||||
function tick() {
|
||||
var timestamp = now(),
|
||||
@ -48,7 +53,7 @@ define(
|
||||
}
|
||||
|
||||
// Try to update at exactly the next second
|
||||
$timeout(tick, 1000 - millis, true);
|
||||
timeoutId = $timeout(tick, 1000 - millis, true);
|
||||
}
|
||||
|
||||
tick();
|
||||
|
@ -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,107 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2017, 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/controllers/ClockController"],
|
||||
function (ClockController) {
|
||||
|
||||
// Wed, 03 Jun 2015 17:56:14 GMT
|
||||
var TEST_TIMESTAMP = 1433354174000;
|
||||
|
||||
describe("A clock view's controller", function () {
|
||||
var mockScope,
|
||||
mockTicker,
|
||||
mockUnticker,
|
||||
controller;
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
|
||||
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
|
||||
mockUnticker = jasmine.createSpy('unticker');
|
||||
|
||||
mockTicker.listen.and.returnValue(mockUnticker);
|
||||
|
||||
controller = new ClockController(mockScope, mockTicker);
|
||||
});
|
||||
|
||||
it("watches for model (clockFormat and timezone) from the domain object model", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"model",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("subscribes to clock ticks", function () {
|
||||
expect(mockTicker.listen)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("unsubscribes to ticks when destroyed", function () {
|
||||
// Make sure $destroy is being listened for...
|
||||
expect(mockScope.$on.calls.mostRecent().args[0]).toEqual('$destroy');
|
||||
expect(mockUnticker).not.toHaveBeenCalled();
|
||||
|
||||
// ...and makes sure that its listener unsubscribes from ticker
|
||||
mockScope.$on.calls.mostRecent().args[1]();
|
||||
expect(mockUnticker).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("formats using the format string from the model", function () {
|
||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
||||
mockScope.$watch.calls.mostRecent().args[1]({
|
||||
"clockFormat": [
|
||||
"YYYY-DDD hh:mm:ss",
|
||||
"clock24"
|
||||
],
|
||||
"timezone": "Canada/Eastern"
|
||||
});
|
||||
|
||||
expect(controller.zone()).toEqual("EDT");
|
||||
expect(controller.text()).toEqual("2015-154 13:56:14");
|
||||
expect(controller.ampm()).toEqual("");
|
||||
});
|
||||
|
||||
it("formats 12-hour time", function () {
|
||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
||||
mockScope.$watch.calls.mostRecent().args[1]({
|
||||
"clockFormat": [
|
||||
"YYYY-DDD hh:mm:ss",
|
||||
"clock12"
|
||||
],
|
||||
"timezone": ""
|
||||
});
|
||||
|
||||
expect(controller.zone()).toEqual("UTC");
|
||||
expect(controller.text()).toEqual("2015-154 05:56:14");
|
||||
expect(controller.ampm()).toEqual("PM");
|
||||
});
|
||||
|
||||
it("does not throw exceptions when model is undefined", function () {
|
||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
||||
expect(function () {
|
||||
mockScope.$watch.calls.mostRecent().args[1](undefined);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -101,7 +101,7 @@ define(
|
||||
name: "Pause"
|
||||
});
|
||||
mockStop.getMetadata.and.returnValue({
|
||||
cssClass: "icon-box",
|
||||
cssClass: "icon-box-round-corners",
|
||||
name: "Stop"
|
||||
});
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
@ -1,58 +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/indicators/ClockIndicator"],
|
||||
function (ClockIndicator) {
|
||||
|
||||
// Wed, 03 Jun 2015 17:56:14 GMT
|
||||
var TEST_TIMESTAMP = 1433354174000,
|
||||
TEST_FORMAT = "YYYY-DDD HH:mm:ss";
|
||||
|
||||
describe("The clock indicator", function () {
|
||||
var mockTicker,
|
||||
mockUnticker,
|
||||
indicator;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
|
||||
mockUnticker = jasmine.createSpy('unticker');
|
||||
|
||||
mockTicker.listen.and.returnValue(mockUnticker);
|
||||
|
||||
indicator = new ClockIndicator(mockTicker, TEST_FORMAT);
|
||||
});
|
||||
|
||||
it("displays the current time", function () {
|
||||
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
|
||||
expect(indicator.getText()).toEqual("2015-154 17:56:14 UTC");
|
||||
});
|
||||
|
||||
it("implements the Indicator interface", function () {
|
||||
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getText()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getDescription()).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
@ -30,16 +30,18 @@ define(
|
||||
var mockTimeout,
|
||||
mockNow,
|
||||
mockCallback,
|
||||
tickerService;
|
||||
tickerService,
|
||||
mockRootScope;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy('$timeout');
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockCallback = jasmine.createSpy('callback');
|
||||
mockRootScope = jasmine.createSpyObj('rootScope', ['$on']);
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
|
||||
tickerService = new TickerService(mockTimeout, mockNow);
|
||||
tickerService = new TickerService(mockTimeout, mockNow, mockRootScope);
|
||||
});
|
||||
|
||||
it("notifies listeners of clock ticks", function () {
|
||||
|
@ -1,120 +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/HyperlinkController',
|
||||
'./res/templates/hyperlink.html'
|
||||
], function (
|
||||
HyperlinkController,
|
||||
hyperlinkTemplate
|
||||
) {
|
||||
return {
|
||||
name: "platform/features/hyperlink",
|
||||
definition: {
|
||||
"name": "Hyperlink",
|
||||
"description": "Insert a hyperlink to reference a link",
|
||||
"extensions": {
|
||||
"types": [
|
||||
{
|
||||
"key": "hyperlink",
|
||||
"name": "Hyperlink",
|
||||
"cssClass": "icon-chain-links",
|
||||
"description": "A hyperlink to redirect to a different link",
|
||||
"features": ["creation"],
|
||||
"properties": [
|
||||
{
|
||||
"key": "url",
|
||||
"name": "URL",
|
||||
"control": "textfield",
|
||||
"required": true,
|
||||
"cssClass": "l-input-lg"
|
||||
},
|
||||
|
||||
{
|
||||
"key": "displayText",
|
||||
"name": "Text to Display",
|
||||
"control": "textfield",
|
||||
"required": true,
|
||||
"cssClass": "l-input-lg"
|
||||
},
|
||||
{
|
||||
"key": "displayFormat",
|
||||
"name": "Display Format",
|
||||
"control": "select",
|
||||
"options": [
|
||||
{
|
||||
"name": "Link",
|
||||
"value": "link"
|
||||
},
|
||||
{
|
||||
"value": "button",
|
||||
"name": "Button"
|
||||
}
|
||||
],
|
||||
"cssClass": "l-inline"
|
||||
},
|
||||
{
|
||||
"key": "openNewTab",
|
||||
"name": "Tab to Open Hyperlink",
|
||||
"control": "select",
|
||||
"options": [
|
||||
{
|
||||
"name": "Open in this tab",
|
||||
"value": "thisTab"
|
||||
},
|
||||
{
|
||||
"value": "newTab",
|
||||
"name": "Open in a new tab"
|
||||
}
|
||||
],
|
||||
"cssClass": "l-inline"
|
||||
|
||||
}
|
||||
],
|
||||
"model": {
|
||||
"displayFormat": "link",
|
||||
"openNewTab": "thisTab",
|
||||
"removeTitle": true
|
||||
}
|
||||
|
||||
}
|
||||
],
|
||||
"views": [
|
||||
{
|
||||
"key": "hyperlink",
|
||||
"type": "hyperlink",
|
||||
"name": "Hyperlink Display",
|
||||
"template": hyperlinkTemplate,
|
||||
"editable": false
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "HyperlinkController",
|
||||
"implementation": HyperlinkController,
|
||||
"depends": ["$scope"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
@ -1,61 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle adds the Hyperlink object type, which can be used to add hyperlinks as a domain Object type
|
||||
and into display Layouts as either a button or link that can be chosen to open in either the same tab or
|
||||
create a new tab to open the link in
|
||||
* @namespace platform/features/hyperlink
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
function HyperlinkController($scope) {
|
||||
this.$scope = $scope;
|
||||
}
|
||||
|
||||
/**Function to analyze the location in which to open the hyperlink
|
||||
@returns true if the hyperlink is chosen to open in a different tab, false if the same tab
|
||||
**/
|
||||
HyperlinkController.prototype.openNewTab = function () {
|
||||
if (this.$scope.domainObject.getModel().openNewTab === "thisTab") {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**Function to specify the format in which the hyperlink should be created
|
||||
@returns true if the hyperlink is chosen to be created as a button, false if a link
|
||||
**/
|
||||
HyperlinkController.prototype.isButton = function () {
|
||||
if (this.$scope.domainObject.getModel().displayFormat === "link") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return HyperlinkController;
|
||||
}
|
||||
|
||||
);
|
@ -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/HyperlinkController"],
|
||||
function (HyperlinkController) {
|
||||
|
||||
describe("The controller for hyperlinks", function () {
|
||||
var domainObject,
|
||||
controller,
|
||||
scope;
|
||||
beforeEach(function () {
|
||||
scope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["domainObject"]
|
||||
);
|
||||
domainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getModel"]
|
||||
);
|
||||
scope.domainObject = domainObject;
|
||||
controller = new HyperlinkController(scope);
|
||||
});
|
||||
it("knows when it should open a new tab", function () {
|
||||
scope.domainObject.getModel.and.returnValue({
|
||||
"displayFormat": "link",
|
||||
"openNewTab": "newTab",
|
||||
"showTitle": false
|
||||
}
|
||||
);
|
||||
controller = new HyperlinkController(scope);
|
||||
expect(controller.openNewTab())
|
||||
.toBe(true);
|
||||
});
|
||||
it("knows when it is a button", function () {
|
||||
scope.domainObject.getModel.and.returnValue({
|
||||
"displayFormat": "button",
|
||||
"openNewTab": "thisTab",
|
||||
"showTitle": false
|
||||
}
|
||||
);
|
||||
controller = new HyperlinkController(scope);
|
||||
expect(controller.isButton())
|
||||
.toEqual(true);
|
||||
});
|
||||
it("knows when it should open in the same tab", function () {
|
||||
scope.domainObject.getModel.and.returnValue({
|
||||
"displayFormat": "link",
|
||||
"openNewTab": "thisTab",
|
||||
"showTitle": false
|
||||
}
|
||||
);
|
||||
controller = new HyperlinkController(scope);
|
||||
expect(controller.openNewTab())
|
||||
.toBe(false);
|
||||
});
|
||||
it("knows when it is a link", function () {
|
||||
scope.domainObject.getModel.and.returnValue({
|
||||
"displayFormat": "link",
|
||||
"openNewTab": "thisTab",
|
||||
"showTitle": false
|
||||
}
|
||||
);
|
||||
controller = new HyperlinkController(scope);
|
||||
expect(controller.openNewTab())
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,70 +0,0 @@
|
||||
This bundle provides the Timeline domain object type, as well
|
||||
as other associated domain object types and relevant views.
|
||||
|
||||
# Implementation notes
|
||||
|
||||
## Model Properties
|
||||
|
||||
The properties below record properties relevant to using and
|
||||
understanding timelines based on their JSON representation.
|
||||
Additional common properties, such as `modified`
|
||||
or `persisted` timestamps, may also be present.
|
||||
|
||||
### Timeline Model
|
||||
|
||||
A timeline's model looks like:
|
||||
|
||||
```
|
||||
{
|
||||
"type": "timeline",
|
||||
"start": {
|
||||
"timestamp": <number> (milliseconds since epoch),
|
||||
"epoch": <string> (currently, always "SET")
|
||||
},
|
||||
"capacity": <number> (optional; battery capacity in watt-hours)
|
||||
"composition": <string[]> (array of identifiers for contained objects)
|
||||
}
|
||||
```
|
||||
|
||||
The identifiers in a timeline's `composition` field should refer to
|
||||
other Timeline objects, or to Activity objects.
|
||||
|
||||
### Activity Model
|
||||
|
||||
An activity's model looks like:
|
||||
|
||||
```
|
||||
{
|
||||
"type": "activity",
|
||||
"start": {
|
||||
"timestamp": <number> (milliseconds since epoch),
|
||||
"epoch": <string> (currently, always "SET")
|
||||
},
|
||||
"duration": {
|
||||
"timestamp": <number> (duration of this activity, in milliseconds)
|
||||
"epoch": "SET" (this is ignored)
|
||||
},
|
||||
"relationships": {
|
||||
"modes": <string[]> (array of applicable Activity Mode ids)
|
||||
},
|
||||
"link": <string> (optional; URL linking to associated external resource)
|
||||
"composition": <string[]> (array of identifiers for contained objects)
|
||||
}
|
||||
```
|
||||
|
||||
The identifiers in a timeline's `composition` field should only refer to
|
||||
other Activity objects.
|
||||
|
||||
### Activity Mode Model
|
||||
|
||||
An activity mode's model looks like:
|
||||
|
||||
```
|
||||
{
|
||||
"type": "mode",
|
||||
"resources": {
|
||||
"comms": <number> (communications utilization, in Kbps)
|
||||
"power": <number> (power utilization, in watts)
|
||||
}
|
||||
}
|
||||
```
|
@ -1,10 +0,0 @@
|
||||
<div>
|
||||
Timeline, Activity and Activity Mode objects have been deprecated and will no longer be supported.
|
||||
</div>
|
||||
<div>
|
||||
Please open an issue in the
|
||||
<a href="https://github.com/nasa/openmct/issues" target="_blank">
|
||||
Open MCT Issue tracker
|
||||
</a>
|
||||
if you have any questions about the timeline plugin.
|
||||
</div>
|
@ -58,7 +58,7 @@ define([
|
||||
) {
|
||||
var $http = this.$http,
|
||||
$log = this.$log,
|
||||
app = angular.module(Constants.MODULE_NAME, ["ngRoute"]),
|
||||
app = angular.module(Constants.MODULE_NAME, []),
|
||||
loader = new BundleLoader($http, $log, openmct.legacyRegistry),
|
||||
resolver = new BundleResolver(
|
||||
new ExtensionResolver(
|
||||
|
@ -28,8 +28,7 @@
|
||||
define(
|
||||
[
|
||||
'./FrameworkLayer',
|
||||
'angular',
|
||||
'angular-route'
|
||||
'angular'
|
||||
],
|
||||
function (
|
||||
FrameworkLayer,
|
||||
|
@ -138,34 +138,6 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
// Custom registration function for extensions of category "route"
|
||||
function registerRoute(extension) {
|
||||
var app = this.app,
|
||||
$log = this.$log,
|
||||
route = Object.create(extension);
|
||||
|
||||
// Adjust path for bundle
|
||||
if (route.templateUrl) {
|
||||
route.templateUrl = [
|
||||
route.bundle.path,
|
||||
route.bundle.resources,
|
||||
route.templateUrl
|
||||
].join(Constants.SEPARATOR);
|
||||
}
|
||||
|
||||
// Log the registration
|
||||
$log.info("Registering route: " + (route.key || route.when));
|
||||
|
||||
// Register the route with Angular
|
||||
app.config(['$routeProvider', function ($routeProvider) {
|
||||
if (route.when) {
|
||||
$routeProvider.when(route.when, route);
|
||||
} else {
|
||||
$routeProvider.otherwise(route);
|
||||
}
|
||||
}]);
|
||||
}
|
||||
|
||||
// Handle service compositing
|
||||
function registerComponents(components) {
|
||||
var app = this.app,
|
||||
@ -194,13 +166,6 @@ define(
|
||||
CustomRegistrars.prototype.constants =
|
||||
mapUpon(registerConstant);
|
||||
|
||||
/**
|
||||
* Register Angular routes.
|
||||
* @param {Array} extensions the resolved extensions
|
||||
*/
|
||||
CustomRegistrars.prototype.routes =
|
||||
mapUpon(registerRoute);
|
||||
|
||||
/**
|
||||
* Register Angular directives.
|
||||
* @param {Array} extensions the resolved extensions
|
||||
|
@ -57,7 +57,6 @@ define(
|
||||
expect(customRegistrars.directives).toBeTruthy();
|
||||
expect(customRegistrars.controllers).toBeTruthy();
|
||||
expect(customRegistrars.services).toBeTruthy();
|
||||
expect(customRegistrars.routes).toBeTruthy();
|
||||
expect(customRegistrars.constants).toBeTruthy();
|
||||
expect(customRegistrars.runs).toBeTruthy();
|
||||
});
|
||||
@ -139,47 +138,6 @@ define(
|
||||
expect(mockLog.warn.calls.count()).toEqual(0);
|
||||
});
|
||||
|
||||
it("allows routes to be registered", function () {
|
||||
var mockRouteProvider = jasmine.createSpyObj(
|
||||
"$routeProvider",
|
||||
["when", "otherwise"]
|
||||
),
|
||||
bundle = {
|
||||
path: "test/bundle",
|
||||
resources: "res"
|
||||
},
|
||||
routes = [
|
||||
{
|
||||
when: "foo",
|
||||
templateUrl: "templates/test.html",
|
||||
bundle: bundle
|
||||
},
|
||||
{
|
||||
templateUrl: "templates/default.html",
|
||||
bundle: bundle
|
||||
}
|
||||
];
|
||||
|
||||
customRegistrars.routes(routes);
|
||||
|
||||
// Give it the route provider based on its config call
|
||||
mockApp.config.calls.all().forEach(function (call) {
|
||||
// Invoke the provided callback
|
||||
call.args[0][1](mockRouteProvider);
|
||||
});
|
||||
|
||||
// The "when" clause should have been mapped to the when method...
|
||||
expect(mockRouteProvider.when).toHaveBeenCalled();
|
||||
expect(mockRouteProvider.when.calls.mostRecent().args[0]).toEqual("foo");
|
||||
expect(mockRouteProvider.when.calls.mostRecent().args[1].templateUrl)
|
||||
.toEqual("test/bundle/res/templates/test.html");
|
||||
|
||||
// ...while the other should have been treated as a default route
|
||||
expect(mockRouteProvider.otherwise).toHaveBeenCalled();
|
||||
expect(mockRouteProvider.otherwise.calls.mostRecent().args[0].templateUrl)
|
||||
.toEqual("test/bundle/res/templates/default.html");
|
||||
});
|
||||
|
||||
it("accepts components for service compositing", function () {
|
||||
// Most relevant code will be exercised in service compositor spec
|
||||
expect(customRegistrars.components).toBeTruthy();
|
||||
|
@ -30,8 +30,8 @@ define([
|
||||
|
||||
return function ImportExportPlugin() {
|
||||
return function (openmct) {
|
||||
ExportAsJSONAction.appliesTo = function (context) {
|
||||
return openmct.$injector.get('policyService')
|
||||
ExportAsJSONAction.prototype.appliesTo = function (context) {
|
||||
return this.openmct.$injector.get('policyService')
|
||||
.allow("creation", context.domainObject.getCapability("type")
|
||||
);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ define(
|
||||
],
|
||||
function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
|
||||
|
||||
xdescribe("The export JSON action", function () {
|
||||
describe("The export JSON action", function () {
|
||||
|
||||
var context,
|
||||
action,
|
||||
@ -102,7 +102,7 @@ define(
|
||||
expect(action).toBeDefined();
|
||||
});
|
||||
|
||||
it("doesn't export non-creatable objects in tree", function () {
|
||||
xit("doesn't export non-creatable objects in tree", function () {
|
||||
var nonCreatableType = {
|
||||
hasFeature:
|
||||
function (feature) {
|
||||
@ -149,7 +149,7 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("can export self-containing objects", function () {
|
||||
xit("can export self-containing objects", function () {
|
||||
var parent = domainObjectFactory({
|
||||
name: 'parent',
|
||||
model: {
|
||||
@ -191,7 +191,7 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("exports links to external objects as new objects", function () {
|
||||
xit("exports links to external objects as new objects", function () {
|
||||
var parent = domainObjectFactory({
|
||||
name: 'parent',
|
||||
model: {
|
||||
|
@ -27,7 +27,7 @@ define(
|
||||
],
|
||||
function (ImportAsJSONAction, domainObjectFactory) {
|
||||
|
||||
xdescribe("The import JSON action", function () {
|
||||
describe("The import JSON action", function () {
|
||||
|
||||
var context = {};
|
||||
var action,
|
||||
@ -146,7 +146,7 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("can import self-containing objects", function () {
|
||||
xit("can import self-containing objects", function () {
|
||||
var compDomainObject = domainObjectFactory({
|
||||
name: 'compObject',
|
||||
model: { name: 'compObject'},
|
||||
@ -198,7 +198,7 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("assigns new ids to each imported object", function () {
|
||||
xit("assigns new ids to each imported object", function () {
|
||||
dialogService.getUserInput.and.returnValue(Promise.resolve(
|
||||
{
|
||||
selectFile: {
|
||||
|
@ -47,7 +47,7 @@ define(
|
||||
* @param $interval Angular's $interval service
|
||||
* @param {string} space the name of the persistence space being served
|
||||
* @param {string} root the root of the path to ElasticSearch
|
||||
* @param {stirng} path the path to domain objects within ElasticSearch
|
||||
* @param {string} path the path to domain objects within ElasticSearch
|
||||
*/
|
||||
function ElasticPersistenceProvider($http, $q, space, root, path) {
|
||||
this.spaces = [space];
|
||||
|
@ -110,8 +110,15 @@ define([
|
||||
worker = workerService.run('bareBonesSearchWorker');
|
||||
}
|
||||
|
||||
worker.addEventListener('message', function (messageEvent) {
|
||||
function handleWorkerMessage(messageEvent) {
|
||||
provider.onWorkerMessage(messageEvent);
|
||||
}
|
||||
|
||||
worker.addEventListener('message', handleWorkerMessage);
|
||||
|
||||
this.openmct.once('destroy', () => {
|
||||
worker.removeEventListener('message', handleWorkerMessage);
|
||||
worker.terminate();
|
||||
});
|
||||
|
||||
return worker;
|
||||
|
64
src/MCT.js
64
src/MCT.js
@ -31,7 +31,6 @@ define([
|
||||
'objectUtils',
|
||||
'./plugins/plugins',
|
||||
'./adapter/indicators/legacy-indicators-plugin',
|
||||
'./plugins/buildInfo/plugin',
|
||||
'./ui/registries/ViewRegistry',
|
||||
'./plugins/imagery/plugin',
|
||||
'./ui/registries/InspectorViewRegistry',
|
||||
@ -40,6 +39,7 @@ define([
|
||||
'./ui/router/Browse',
|
||||
'../platform/framework/src/Main',
|
||||
'./ui/layout/Layout.vue',
|
||||
'./ui/inspector/styles/StylesManager',
|
||||
'../platform/core/src/objects/DomainObjectImpl',
|
||||
'../platform/core/src/capabilities/ContextualDomainObject',
|
||||
'./ui/preview/plugin',
|
||||
@ -60,7 +60,6 @@ define([
|
||||
objectUtils,
|
||||
plugins,
|
||||
LegacyIndicatorsPlugin,
|
||||
buildInfoPlugin,
|
||||
ViewRegistry,
|
||||
ImageryPlugin,
|
||||
InspectorViewRegistry,
|
||||
@ -69,6 +68,7 @@ define([
|
||||
Browse,
|
||||
Main,
|
||||
Layout,
|
||||
stylesManager,
|
||||
DomainObjectImpl,
|
||||
ContextualDomainObject,
|
||||
PreviewPlugin,
|
||||
@ -122,6 +122,8 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
|
||||
/**
|
||||
* Tracks current selection state of the application.
|
||||
* @private
|
||||
@ -135,7 +137,7 @@ define([
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name conductor
|
||||
*/
|
||||
this.time = new api.TimeAPI();
|
||||
this.time = new api.TimeAPI(this);
|
||||
|
||||
/**
|
||||
* An interface for interacting with the composition of domain objects.
|
||||
@ -252,7 +254,7 @@ define([
|
||||
|
||||
this.status = new api.StatusAPI(this);
|
||||
|
||||
this.router = new ApplicationRouter();
|
||||
this.router = new ApplicationRouter(this);
|
||||
|
||||
this.branding = BrandingAPI.default;
|
||||
|
||||
@ -262,7 +264,7 @@ define([
|
||||
// Plugins that are installed by default
|
||||
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(this.plugins.TelemetryTable.default());
|
||||
this.install(PreviewPlugin.default());
|
||||
this.install(LegacyIndicatorsPlugin());
|
||||
this.install(LicensesPlugin.default());
|
||||
@ -274,6 +276,7 @@ define([
|
||||
this.install(ImageryPlugin.default());
|
||||
this.install(this.plugins.FlexibleLayout());
|
||||
this.install(this.plugins.GoToOriginalAction());
|
||||
this.install(this.plugins.OpenInNewTabAction());
|
||||
this.install(this.plugins.ImportExport());
|
||||
this.install(this.plugins.WebPage());
|
||||
this.install(this.plugins.Condition());
|
||||
@ -282,8 +285,10 @@ define([
|
||||
this.install(this.plugins.NotificationIndicator());
|
||||
this.install(this.plugins.NewFolderAction());
|
||||
this.install(this.plugins.ViewDatumAction());
|
||||
this.install(this.plugins.ViewLargeAction());
|
||||
this.install(this.plugins.ObjectInterceptors());
|
||||
this.install(this.plugins.NonEditableFolder());
|
||||
this.install(this.plugins.DeviceClassifier());
|
||||
}
|
||||
|
||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||
@ -372,6 +377,7 @@ define([
|
||||
* MCT; if undefined, MCT will be run in the body of the document
|
||||
*/
|
||||
MCT.prototype.start = function (domElement = document.body, isHeadlessMode = false) {
|
||||
|
||||
if (this.types.get('layout') === undefined) {
|
||||
this.install(this.plugins.DisplayLayout({
|
||||
showAsView: ['summary-widget']
|
||||
@ -430,9 +436,14 @@ define([
|
||||
domElement.appendChild(appLayout.$mount().$el);
|
||||
|
||||
this.layout = appLayout.$refs.layout;
|
||||
this.once('destroy', () => {
|
||||
appLayout.$destroy();
|
||||
});
|
||||
Browse(this);
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', this.destroy);
|
||||
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
}.bind(this));
|
||||
@ -456,16 +467,41 @@ define([
|
||||
};
|
||||
|
||||
MCT.prototype.destroy = function () {
|
||||
this.emit('destroy');
|
||||
this.router.destroy();
|
||||
};
|
||||
if (this._destroyed === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose this project's Vue so plugins can make use of it.
|
||||
*
|
||||
* @memberof module:openmct.MCT#
|
||||
*/
|
||||
MCT.prototype.Vue = Vue;
|
||||
window.removeEventListener('beforeunload', this.destroy);
|
||||
|
||||
this.emit('destroy');
|
||||
this.removeAllListeners();
|
||||
|
||||
if (this.$injector) {
|
||||
this.$injector.get('$rootScope').$destroy();
|
||||
this.$injector = null;
|
||||
}
|
||||
|
||||
if (this.$angular) {
|
||||
this.$angular.element(this.element).off().removeData();
|
||||
this.$angular.element(this.element).empty();
|
||||
this.$angular = null;
|
||||
}
|
||||
|
||||
this.overlays.destroy();
|
||||
|
||||
if (this.element) {
|
||||
this.element.remove();
|
||||
}
|
||||
|
||||
stylesManager.default.removeAllListeners();
|
||||
|
||||
window.angular = null;
|
||||
window.openmct = null;
|
||||
|
||||
Object.keys(require.cache).forEach(key => delete require.cache[key]);
|
||||
|
||||
this._destroyed = true;
|
||||
};
|
||||
|
||||
MCT.prototype.plugins = plugins;
|
||||
|
||||
|
@ -36,8 +36,7 @@ define([
|
||||
'./views/installLegacyViews',
|
||||
'./policies/LegacyCompositionPolicyAdapter',
|
||||
'./actions/LegacyActionAdapter',
|
||||
'./services/LegacyPersistenceAdapter',
|
||||
'./services/ExportImageService'
|
||||
'./services/LegacyPersistenceAdapter'
|
||||
], function (
|
||||
ActionDialogDecorator,
|
||||
AdapterCapability,
|
||||
@ -54,8 +53,7 @@ define([
|
||||
installLegacyViews,
|
||||
legacyCompositionPolicyAdapter,
|
||||
LegacyActionAdapter,
|
||||
LegacyPersistenceAdapter,
|
||||
ExportImageService
|
||||
LegacyPersistenceAdapter
|
||||
) {
|
||||
return {
|
||||
name: 'src/adapter',
|
||||
@ -84,13 +82,6 @@ define([
|
||||
"identifierService",
|
||||
"cacheService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "exportImageService",
|
||||
"implementation": ExportImageService,
|
||||
"depends": [
|
||||
"dialogService"
|
||||
]
|
||||
}
|
||||
],
|
||||
components: [
|
||||
|
@ -32,6 +32,10 @@ define([
|
||||
// cannot be injected.
|
||||
function AlternateCompositionInitializer(openmct) {
|
||||
AlternateCompositionCapability.appliesTo = function (model, id) {
|
||||
openmct.once('destroy', () => {
|
||||
delete AlternateCompositionCapability.appliesTo;
|
||||
});
|
||||
|
||||
model = objectUtils.toNewFormat(model, id || '');
|
||||
|
||||
return Boolean(openmct.composition.get(model));
|
||||
|
@ -161,6 +161,23 @@ define([
|
||||
evaluate: function (datum, property) {
|
||||
return limitEvaluator.evaluate(datum, property && property.key);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
LegacyTelemetryProvider.prototype.getLimits = function (domainObject) {
|
||||
const oldObject = this.instantiate(
|
||||
utils.toOldFormat(domainObject),
|
||||
utils.makeKeyString(domainObject.identifier)
|
||||
);
|
||||
const limitEvaluator = oldObject.getCapability("limit");
|
||||
|
||||
return {
|
||||
limits: () => {
|
||||
return limitEvaluator.limits.then !== undefined
|
||||
? limitEvaluator.limits()
|
||||
: Promise.resolve(limitEvaluator.limits());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,186 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Module defining ExportImageService. Created by hudsonfoo on 09/02/16
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"html2canvas",
|
||||
"saveAs"
|
||||
],
|
||||
function (
|
||||
html2canvas,
|
||||
{ saveAs }
|
||||
) {
|
||||
|
||||
/**
|
||||
* The export image service will export any HTML node to
|
||||
* JPG, or PNG.
|
||||
* @param {object} dialogService
|
||||
* @constructor
|
||||
*/
|
||||
function ExportImageService(dialogService) {
|
||||
this.dialogService = dialogService;
|
||||
this.exportCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HTML element into a PNG or JPG Blob.
|
||||
* @private
|
||||
* @param {node} element that will be converted to an image
|
||||
* @param {string} type of image to convert the element to.
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.renderElement = function (element, imageType, className) {
|
||||
const dialogService = this.dialogService;
|
||||
|
||||
const dialog = dialogService.showBlockingMessage({
|
||||
title: "Capturing...",
|
||||
hint: "Capturing an image",
|
||||
unknownProgress: true,
|
||||
severity: "info",
|
||||
delay: true
|
||||
});
|
||||
|
||||
let mimeType = "image/png";
|
||||
if (imageType === "jpg") {
|
||||
mimeType = "image/jpeg";
|
||||
}
|
||||
|
||||
let exportId = undefined;
|
||||
let oldId = undefined;
|
||||
if (className) {
|
||||
exportId = 'export-element-' + this.exportCount;
|
||||
this.exportCount++;
|
||||
oldId = element.id;
|
||||
element.id = exportId;
|
||||
}
|
||||
|
||||
return html2canvas(element, {
|
||||
onclone: function (document) {
|
||||
if (className) {
|
||||
const clonedElement = document.getElementById(exportId);
|
||||
clonedElement.classList.add(className);
|
||||
}
|
||||
|
||||
element.id = oldId;
|
||||
},
|
||||
removeContainer: true // Set to false to debug what html2canvas renders
|
||||
}).then(function (canvas) {
|
||||
dialog.dismiss();
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
return canvas.toBlob(resolve, mimeType);
|
||||
});
|
||||
}, function (error) {
|
||||
console.log('error capturing image', error);
|
||||
dialog.dismiss();
|
||||
const errorDialog = dialogService.showBlockingMessage({
|
||||
title: "Error capturing image",
|
||||
severity: "error",
|
||||
hint: "Image was not captured successfully!",
|
||||
options: [{
|
||||
label: "OK",
|
||||
callback: function () {
|
||||
errorDialog.dismiss();
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a screenshot of a DOM node and exports to JPG.
|
||||
* @param {node} element to be exported
|
||||
* @param {string} filename the exported image
|
||||
* @param {string} className to be added to element before capturing (optional)
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportJPG = function (element, filename, className) {
|
||||
const processedFilename = replaceDotsWithUnderscores(filename);
|
||||
|
||||
return this.renderElement(element, "jpg", className).then(function (img) {
|
||||
saveAs(img, processedFilename);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a screenshot of a DOM node and exports to PNG.
|
||||
* @param {node} element to be exported
|
||||
* @param {string} filename the exported image
|
||||
* @param {string} className to be added to element before capturing (optional)
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportPNG = function (element, filename, className) {
|
||||
const processedFilename = replaceDotsWithUnderscores(filename);
|
||||
|
||||
return this.renderElement(element, "png", className).then(function (img) {
|
||||
saveAs(img, processedFilename);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a screenshot of a DOM node in PNG format.
|
||||
* @param {node} element to be exported
|
||||
* @param {string} filename the exported image
|
||||
* @returns {promise}
|
||||
*/
|
||||
|
||||
ExportImageService.prototype.exportPNGtoSRC = function (element, className) {
|
||||
return this.renderElement(element, "png", className);
|
||||
};
|
||||
|
||||
function replaceDotsWithUnderscores(filename) {
|
||||
const regex = /\./gi;
|
||||
|
||||
return filename.replace(regex, '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
|
||||
* implements the method in browsers that would not otherwise support it.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
|
||||
*/
|
||||
function polyfillToBlob() {
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
|
||||
value: function (callback, mimeType, quality) {
|
||||
const binStr = atob(this.toDataURL(mimeType, quality).split(',')[1]);
|
||||
const len = binStr.length;
|
||||
const arr = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
arr[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
|
||||
callback(new Blob([arr], {type: mimeType || "image/png"}));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
polyfillToBlob();
|
||||
|
||||
return ExportImageService;
|
||||
}
|
||||
);
|
@ -28,6 +28,10 @@ export default class Editor extends EventEmitter {
|
||||
super();
|
||||
this.editing = false;
|
||||
this.openmct = openmct;
|
||||
|
||||
openmct.once('destroy', () => {
|
||||
this.removeAllListeners();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,8 +46,6 @@ class ActionCollection extends EventEmitter {
|
||||
this._observeObjectPath();
|
||||
this.openmct.editor.on('isEditing', this._updateActions);
|
||||
}
|
||||
|
||||
this._initializeActions();
|
||||
}
|
||||
|
||||
disable(actionKeys) {
|
||||
@ -156,19 +154,10 @@ class ActionCollection extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
_initializeActions() {
|
||||
Object.keys(this.applicableActions).forEach(key => {
|
||||
this.applicableActions[key].callBack = () => {
|
||||
return this.applicableActions[key].invoke(this.objectPath, this.view);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
_updateActions() {
|
||||
let newApplicableActions = this.openmct.actions._applicableActions(this.objectPath, this.view);
|
||||
|
||||
this.applicableActions = this._mergeOldAndNewActions(this.applicableActions, newApplicableActions);
|
||||
this._initializeActions();
|
||||
this._update();
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,15 @@ describe('The ActionCollection', () => {
|
||||
}
|
||||
});
|
||||
|
||||
openmct.$injector.get.and.returnValue(mockIdentifierService);
|
||||
openmct.$injector.get.and.callFake((key) => {
|
||||
return {
|
||||
'identifierService': mockIdentifierService,
|
||||
'$rootScope': {
|
||||
'$destroy': () => {}
|
||||
}
|
||||
}[key];
|
||||
});
|
||||
|
||||
mockObjectPath = [
|
||||
{
|
||||
name: 'mock folder',
|
||||
@ -119,7 +127,8 @@ describe('The ActionCollection', () => {
|
||||
|
||||
afterEach(() => {
|
||||
actionCollection.destroy();
|
||||
resetApplicationState(openmct);
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("disable method invoked with action keys", () => {
|
||||
|
@ -34,7 +34,7 @@ class ActionsAPI extends EventEmitter {
|
||||
this._groupOrder = ['windowing', 'undefined', 'view', 'action', 'json'];
|
||||
|
||||
this.register = this.register.bind(this);
|
||||
this.get = this.get.bind(this);
|
||||
this.getActionsCollection = this.getActionsCollection.bind(this);
|
||||
this._applicableActions = this._applicableActions.bind(this);
|
||||
this._updateCachedActionCollections = this._updateCachedActionCollections.bind(this);
|
||||
}
|
||||
@ -43,12 +43,14 @@ class ActionsAPI extends EventEmitter {
|
||||
this._allActions[actionDefinition.key] = actionDefinition;
|
||||
}
|
||||
|
||||
get(objectPath, view) {
|
||||
if (view) {
|
||||
getAction(key) {
|
||||
return this._allActions[key];
|
||||
}
|
||||
|
||||
getActionsCollection(objectPath, view) {
|
||||
if (view) {
|
||||
return this._getCachedActionCollection(objectPath, view) || this._newActionCollection(objectPath, view, true);
|
||||
} else {
|
||||
|
||||
return this._newActionCollection(objectPath, view, true);
|
||||
}
|
||||
}
|
||||
@ -57,25 +59,24 @@ class ActionsAPI extends EventEmitter {
|
||||
this._groupOrder = groupArray;
|
||||
}
|
||||
|
||||
_get(objectPath, view) {
|
||||
let actionCollection = this._newActionCollection(objectPath, view);
|
||||
|
||||
this._actionCollections.set(view, actionCollection);
|
||||
actionCollection.on('destroy', this._updateCachedActionCollections);
|
||||
|
||||
return actionCollection;
|
||||
}
|
||||
|
||||
_getCachedActionCollection(objectPath, view) {
|
||||
let cachedActionCollection = this._actionCollections.get(view);
|
||||
|
||||
return cachedActionCollection;
|
||||
return this._actionCollections.get(view);
|
||||
}
|
||||
|
||||
_newActionCollection(objectPath, view, skipEnvironmentObservers) {
|
||||
let applicableActions = this._applicableActions(objectPath, view);
|
||||
|
||||
return new ActionCollection(applicableActions, objectPath, view, this._openmct, skipEnvironmentObservers);
|
||||
const actionCollection = new ActionCollection(applicableActions, objectPath, view, this._openmct, skipEnvironmentObservers);
|
||||
if (view) {
|
||||
this._cacheActionCollection(view, actionCollection);
|
||||
}
|
||||
|
||||
return actionCollection;
|
||||
}
|
||||
|
||||
_cacheActionCollection(view, actionCollection) {
|
||||
this._actionCollections.set(view, actionCollection);
|
||||
actionCollection.on('destroy', this._updateCachedActionCollections);
|
||||
}
|
||||
|
||||
_updateCachedActionCollections(key) {
|
||||
|
@ -99,14 +99,14 @@ describe('The Actions API', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("register method", () => {
|
||||
it("adds action to ActionsAPI", () => {
|
||||
actionsAPI.register(mockAction);
|
||||
|
||||
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
|
||||
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
|
||||
let action = actionCollection.getActionsObject()[mockAction.key];
|
||||
|
||||
expect(action.key).toEqual(mockAction.key);
|
||||
@ -121,21 +121,21 @@ describe('The Actions API', () => {
|
||||
});
|
||||
|
||||
it("returns an ActionCollection when invoked with an objectPath only", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath);
|
||||
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath);
|
||||
let instanceOfActionCollection = actionCollection instanceof ActionCollection;
|
||||
|
||||
expect(instanceOfActionCollection).toBeTrue();
|
||||
});
|
||||
|
||||
it("returns an ActionCollection when invoked with an objectPath and view", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
|
||||
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
|
||||
let instanceOfActionCollection = actionCollection instanceof ActionCollection;
|
||||
|
||||
expect(instanceOfActionCollection).toBeTrue();
|
||||
});
|
||||
|
||||
it("returns relevant actions when invoked with objectPath only", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath);
|
||||
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath);
|
||||
let action = actionCollection.getActionsObject()[mockObjectPathAction.key];
|
||||
|
||||
expect(action.key).toEqual(mockObjectPathAction.key);
|
||||
@ -143,7 +143,7 @@ describe('The Actions API', () => {
|
||||
});
|
||||
|
||||
it("returns relevant actions when invoked with objectPath and view", () => {
|
||||
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
|
||||
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
|
||||
let action = actionCollection.getActionsObject()[mockAction.key];
|
||||
|
||||
expect(action.key).toEqual(mockAction.key);
|
||||
|
@ -46,7 +46,7 @@ define([
|
||||
StatusAPI
|
||||
) {
|
||||
return {
|
||||
TimeAPI: TimeAPI,
|
||||
TimeAPI: TimeAPI.default,
|
||||
ObjectAPI: ObjectAPI,
|
||||
CompositionAPI: CompositionAPI,
|
||||
TypeRegistry: TypeRegistry,
|
||||
|
@ -215,12 +215,12 @@ define([
|
||||
* @memberof {module:openmct.CompositionCollection#}
|
||||
* @name load
|
||||
*/
|
||||
CompositionCollection.prototype.load = function () {
|
||||
CompositionCollection.prototype.load = function (abortSignal) {
|
||||
this.cleanUpMutables();
|
||||
|
||||
return this.provider.load(this.domainObject)
|
||||
.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))
|
||||
.then(function (childObjects) {
|
||||
childObjects.forEach(c => this.add(c, true));
|
||||
|
@ -37,7 +37,7 @@ import Menu, { MENU_PLACEMENT } from './menu.js';
|
||||
* @property {Boolean} isDisabled adds disable class if true
|
||||
* @property {String} name Menu item text
|
||||
* @property {String} description Menu item description
|
||||
* @property {Function} callBack callback function: invoked when item is clicked
|
||||
* @property {Function} onItemClicked callback function: invoked when item is clicked
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -66,12 +66,27 @@ class MenuAPI {
|
||||
* @param {Array.<Action>|Array.<Array.<Action>>} actions collection of actions{@link Action} or collection of groups of actions {@link Action}
|
||||
* @param {MenuOptions} [menuOptions] [Optional] The {@link MenuOptions} options for Menu
|
||||
*/
|
||||
showMenu(x, y, actions, menuOptions) {
|
||||
this._createMenuComponent(x, y, actions, menuOptions);
|
||||
showMenu(x, y, items, menuOptions) {
|
||||
this._createMenuComponent(x, y, items, menuOptions);
|
||||
|
||||
this.menuComponent.showMenu();
|
||||
}
|
||||
|
||||
actionsToMenuItems(actions, objectPath, view) {
|
||||
return actions.map(action => {
|
||||
const isActionGroup = Array.isArray(action);
|
||||
if (isActionGroup) {
|
||||
action = this.actionsToMenuItems(action, objectPath, view);
|
||||
} else {
|
||||
action.onItemClicked = () => {
|
||||
action.invoke(objectPath, view);
|
||||
};
|
||||
}
|
||||
|
||||
return action;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show popup menu with description of item on hover
|
||||
* @param {number} x x-coordinates for popup
|
||||
|
@ -57,7 +57,7 @@ describe ('The Menu API', () => {
|
||||
name: 'Test Action 1',
|
||||
cssClass: 'icon-clock',
|
||||
description: 'This is a test action',
|
||||
callBack: () => {
|
||||
onItemClicked: () => {
|
||||
result = 'Test Action 1 Invoked';
|
||||
}
|
||||
},
|
||||
@ -66,7 +66,7 @@ describe ('The Menu API', () => {
|
||||
name: 'Test Action 2',
|
||||
cssClass: 'icon-clock',
|
||||
description: 'This is a test action',
|
||||
callBack: () => {
|
||||
onItemClicked: () => {
|
||||
result = 'Test Action 2 Invoked';
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ describe ('The Menu API', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("showMenu method", () => {
|
||||
|
@ -11,7 +11,7 @@
|
||||
:key="action.name"
|
||||
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
|
||||
:title="action.description"
|
||||
@click="action.callBack"
|
||||
@click="action.onItemClicked"
|
||||
>
|
||||
{{ action.name }}
|
||||
</li>
|
||||
@ -36,7 +36,7 @@
|
||||
:key="action.name"
|
||||
:class="action.cssClass"
|
||||
:title="action.description"
|
||||
@click="action.callBack"
|
||||
@click="action.onItemClicked"
|
||||
>
|
||||
{{ action.name }}
|
||||
</li>
|
||||
|
@ -13,7 +13,7 @@
|
||||
:key="action.name"
|
||||
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
|
||||
:title="action.description"
|
||||
@click="action.callBack"
|
||||
@click="action.onItemClicked"
|
||||
@mouseover="toggleItemDescription(action)"
|
||||
@mouseleave="toggleItemDescription()"
|
||||
>
|
||||
@ -42,7 +42,7 @@
|
||||
:key="action.name"
|
||||
:class="action.cssClass"
|
||||
:title="action.description"
|
||||
@click="action.callBack"
|
||||
@click="action.onItemClicked"
|
||||
@mouseover="toggleItemDescription(action)"
|
||||
@mouseleave="toggleItemDescription()"
|
||||
>
|
||||
|
@ -71,12 +71,12 @@ class Menu extends EventEmitter {
|
||||
|
||||
showMenu() {
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
options: this.options
|
||||
},
|
||||
components: {
|
||||
MenuComponent
|
||||
},
|
||||
provide: {
|
||||
options: this.options
|
||||
},
|
||||
template: '<menu-component />'
|
||||
});
|
||||
|
||||
@ -85,12 +85,12 @@ class Menu extends EventEmitter {
|
||||
|
||||
showSuperMenu() {
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
options: this.options
|
||||
},
|
||||
components: {
|
||||
SuperMenuComponent
|
||||
},
|
||||
provide: {
|
||||
options: this.options
|
||||
},
|
||||
template: '<super-menu-component />'
|
||||
});
|
||||
|
||||
|
@ -42,7 +42,7 @@ import EventEmitter from 'EventEmitter';
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} message The message to be displayed by the notification
|
||||
* @property {number | 'unknown'} [progress] The progres of some ongoing task. Should be a number between 0 and 100, or
|
||||
* @property {number | 'unknown'} [progress] The progress of some ongoing task. Should be a number between 0 and 100, or
|
||||
* with the string literal 'unknown'.
|
||||
* @property {string} [progressText] A message conveying progress of some ongoing task.
|
||||
|
||||
@ -98,7 +98,7 @@ export default class NotificationAPI extends EventEmitter {
|
||||
* Present an alert to the user.
|
||||
* @param {string} message The message to display to the user.
|
||||
* @param {Object} [options] object with following properties
|
||||
* autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
|
||||
* autoDismissTimeout: {number} in milliseconds to automatically dismisses notification
|
||||
* link: {Object} Add a link to notifications for navigation
|
||||
* onClick: callback function
|
||||
* cssClass: css class name to add style on link
|
||||
@ -119,7 +119,7 @@ export default class NotificationAPI extends EventEmitter {
|
||||
* Present an error message to the user
|
||||
* @param {string} message
|
||||
* @param {Object} [options] object with following properties
|
||||
* autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
|
||||
* autoDismissTimeout: {number} in milliseconds to automatically dismisses notification
|
||||
* link: {Object} Add a link to notifications for navigation
|
||||
* onClick: callback function
|
||||
* cssClass: css class name to add style on link
|
||||
|
@ -45,6 +45,8 @@ function ObjectAPI(typeRegistry, openmct) {
|
||||
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
||||
this.cache = {};
|
||||
this.interceptorRegistry = new InterceptorRegistry();
|
||||
|
||||
this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,6 +358,20 @@ ObjectAPI.prototype.applyGetInterceptors = function (identifier, domainObject) {
|
||||
return domainObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return relative url path from a given object path
|
||||
* eg: #/browse/mine/cb56f6bf-c900-43b7-b923-2e3b64b412db/6e89e858-77ce-46e4-a1ad-749240286497/....
|
||||
* @param {Array} objectPath
|
||||
* @returns {string} relative url for object
|
||||
*/
|
||||
ObjectAPI.prototype.getRelativePath = function (objectPath) {
|
||||
return objectPath
|
||||
.map(p => this.makeKeyString(p.identifier))
|
||||
.reverse()
|
||||
.join('/')
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify a domain object.
|
||||
* @param {module:openmct.DomainObject} object the object to mutate
|
||||
@ -397,20 +413,25 @@ ObjectAPI.prototype._toMutable = function (object) {
|
||||
mutableObject = object;
|
||||
} else {
|
||||
mutableObject = MutableDomainObject.createMutable(object, this.eventEmitter);
|
||||
}
|
||||
|
||||
// Check if provider supports realtime updates
|
||||
let identifier = utils.parseKeyString(mutableObject.identifier);
|
||||
let provider = this.getProvider(identifier);
|
||||
// Check if provider supports realtime updates
|
||||
let identifier = utils.parseKeyString(mutableObject.identifier);
|
||||
let provider = this.getProvider(identifier);
|
||||
|
||||
if (provider !== undefined
|
||||
&& provider.observe !== undefined) {
|
||||
let unobserve = provider.observe(identifier, (updatedModel) => {
|
||||
mutableObject.$refresh(updatedModel);
|
||||
});
|
||||
mutableObject.$on('$destroy', () => {
|
||||
unobserve();
|
||||
});
|
||||
if (provider !== undefined
|
||||
&& provider.observe !== undefined
|
||||
&& this.SYNCHRONIZED_OBJECT_TYPES.includes(object.type)) {
|
||||
let unobserve = provider.observe(identifier, (updatedModel) => {
|
||||
if (updatedModel.persisted > mutableObject.modified) {
|
||||
//Don't replace with a stale model. This can happen on slow connections when multiple mutations happen
|
||||
//in rapid succession and intermediate persistence states are returned by the observe function.
|
||||
mutableObject.$refresh(updatedModel);
|
||||
}
|
||||
});
|
||||
mutableObject.$on('$_destroy', () => {
|
||||
unobserve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return mutableObject;
|
||||
|
@ -163,14 +163,22 @@ describe("The Object API", () => {
|
||||
key: 'test-key'
|
||||
},
|
||||
name: 'test object',
|
||||
type: 'notebook',
|
||||
otherAttribute: 'other-attribute-value',
|
||||
modified: 0,
|
||||
persisted: 0,
|
||||
objectAttribute: {
|
||||
embeddedObject: {
|
||||
embeddedKey: 'embedded-value'
|
||||
}
|
||||
}
|
||||
};
|
||||
updatedTestObject = Object.assign({otherAttribute: 'changed-attribute-value'}, testObject);
|
||||
updatedTestObject = Object.assign({
|
||||
otherAttribute: 'changed-attribute-value'
|
||||
}, testObject);
|
||||
updatedTestObject.modified = 1;
|
||||
updatedTestObject.persisted = 1;
|
||||
|
||||
mockProvider = jasmine.createSpyObj("mock provider", [
|
||||
"get",
|
||||
"create",
|
||||
@ -182,6 +190,8 @@ describe("The Object API", () => {
|
||||
mockProvider.observeObjectChanges.and.callFake(() => {
|
||||
callbacks[0](updatedTestObject);
|
||||
callbacks.splice(0, 1);
|
||||
|
||||
return () => {};
|
||||
});
|
||||
mockProvider.observe.and.callFake((id, callback) => {
|
||||
if (callbacks.length === 0) {
|
||||
@ -189,6 +199,8 @@ describe("The Object API", () => {
|
||||
} else {
|
||||
callbacks[0] = callback;
|
||||
}
|
||||
|
||||
return () => {};
|
||||
});
|
||||
|
||||
objectAPI.addProvider(TEST_NAMESPACE, mockProvider);
|
||||
|
@ -10,28 +10,37 @@ const cssClasses = {
|
||||
};
|
||||
|
||||
class Overlay extends EventEmitter {
|
||||
constructor(options) {
|
||||
constructor({
|
||||
buttons,
|
||||
autoHide = true,
|
||||
dismissable = true,
|
||||
element,
|
||||
onDestroy,
|
||||
size
|
||||
} = {}) {
|
||||
super();
|
||||
|
||||
this.dismissable = options.dismissable !== false;
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('l-overlay-wrapper', cssClasses[options.size]);
|
||||
this.container.classList.add('l-overlay-wrapper', cssClasses[size]);
|
||||
|
||||
this.autoHide = autoHide;
|
||||
this.dismissable = dismissable !== false;
|
||||
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
dismiss: this.dismiss.bind(this),
|
||||
element: options.element,
|
||||
buttons: options.buttons,
|
||||
dismissable: this.dismissable
|
||||
},
|
||||
components: {
|
||||
OverlayComponent: OverlayComponent
|
||||
},
|
||||
provide: {
|
||||
dismiss: this.dismiss.bind(this),
|
||||
element,
|
||||
buttons,
|
||||
dismissable: this.dismissable
|
||||
},
|
||||
template: '<overlay-component></overlay-component>'
|
||||
});
|
||||
|
||||
if (options.onDestroy) {
|
||||
this.once('destroy', options.onDestroy);
|
||||
if (onDestroy) {
|
||||
this.once('destroy', onDestroy);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,7 @@ class OverlayAPI {
|
||||
|
||||
this.dismissLastOverlay = this.dismissLastOverlay.bind(this);
|
||||
|
||||
document.addEventListener('keyup', (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
this.dismissLastOverlay();
|
||||
}
|
||||
});
|
||||
document.addEventListener('keyup', this.dismissLastOverlay);
|
||||
|
||||
}
|
||||
|
||||
@ -30,7 +26,10 @@ class OverlayAPI {
|
||||
*/
|
||||
showOverlay(overlay) {
|
||||
if (this.activeOverlays.length) {
|
||||
this.activeOverlays[this.activeOverlays.length - 1].container.classList.add('invisible');
|
||||
const previousOverlay = this.activeOverlays[this.activeOverlays.length - 1];
|
||||
if (previousOverlay.autoHide) {
|
||||
previousOverlay.container.classList.add('invisible');
|
||||
}
|
||||
}
|
||||
|
||||
this.activeOverlays.push(overlay);
|
||||
@ -49,10 +48,12 @@ class OverlayAPI {
|
||||
/**
|
||||
* private
|
||||
*/
|
||||
dismissLastOverlay() {
|
||||
let lastOverlay = this.activeOverlays[this.activeOverlays.length - 1];
|
||||
if (lastOverlay && lastOverlay.dismissable) {
|
||||
lastOverlay.dismiss();
|
||||
dismissLastOverlay(event) {
|
||||
if (event.key === 'Escape') {
|
||||
let lastOverlay = this.activeOverlays[this.activeOverlays.length - 1];
|
||||
if (lastOverlay && lastOverlay.dismissable) {
|
||||
lastOverlay.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,6 +130,10 @@ class OverlayAPI {
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
document.removeEventListener('keyup', this.dismissLastOverlay);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default OverlayAPI;
|
||||
|
@ -12,7 +12,7 @@
|
||||
></button>
|
||||
<div
|
||||
ref="element"
|
||||
class="c-overlay__contents"
|
||||
class="c-overlay__contents js-notebook-snapshot-item-wrapper"
|
||||
tabindex="0"
|
||||
></div>
|
||||
<div
|
||||
|
@ -21,8 +21,7 @@
|
||||
|
||||
&__outer {
|
||||
@include abs();
|
||||
background: $overlayColorBg;
|
||||
color: $overlayColorFg;
|
||||
background: $colorBodyBg;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $overlayInnerMargin;
|
||||
@ -30,7 +29,6 @@
|
||||
|
||||
&__close-button {
|
||||
$p: $interiorMargin + 2px;
|
||||
color: $overlayColorFg;
|
||||
font-size: 1.5em;
|
||||
position: absolute;
|
||||
top: $p; right: $p;
|
||||
@ -82,11 +80,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.c-button,
|
||||
.c-click-icon {
|
||||
filter: $overlayBrightnessAdjust;
|
||||
}
|
||||
|
||||
.c-object-label__name {
|
||||
filter: $objectLabelNameFilter;
|
||||
}
|
||||
@ -103,6 +96,7 @@ body.desktop {
|
||||
}
|
||||
|
||||
// Overlay types, styling for desktop. Appended to .l-overlay-wrapper element.
|
||||
.l-overlay-large,
|
||||
.l-overlay-small,
|
||||
.l-overlay-fit {
|
||||
.c-overlay__outer {
|
||||
@ -124,12 +118,8 @@ body.desktop {
|
||||
$tbPad: floor($pad * 0.8);
|
||||
$lrPad: $pad;
|
||||
.c-overlay {
|
||||
&__blocker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__outer {
|
||||
@include overlaySizing($overlayOuterMarginFullscreen);
|
||||
@include overlaySizing($overlayOuterMarginLarge);
|
||||
padding: $tbPad $lrPad;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user