Compare commits
107 Commits
api-mmgis-
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2301e67d1 | ||
|
|
44cbd3fc50 | ||
|
|
858de567ae | ||
|
|
ad22661029 | ||
|
|
26aa49d9a8 | ||
|
|
cb55500cba | ||
|
|
340246ab26 | ||
|
|
9cb743fb48 | ||
|
|
9c52ec7233 | ||
|
|
933ce7aa3c | ||
|
|
8ef3869325 | ||
|
|
53f5fdabe5 | ||
|
|
d10561fc7f | ||
|
|
566469e691 | ||
|
|
6d4a324fca | ||
|
|
b8fabb7e73 | ||
|
|
1680c3cc1b | ||
|
|
61dd85c704 | ||
|
|
980777691a | ||
|
|
a50c1704e5 | ||
|
|
98080784a3 | ||
|
|
bf7b672957 | ||
|
|
5dadf6f6fc | ||
|
|
555db60bf7 | ||
|
|
1972ca9ea7 | ||
|
|
1e11bbc2ad | ||
|
|
c4a87ae5d7 | ||
|
|
4e325fb165 | ||
|
|
e59e4efdf0 | ||
|
|
f17fda53a0 | ||
|
|
f2dbe6d816 | ||
|
|
510d3bd333 | ||
|
|
a908eb1d65 | ||
|
|
c0bda64927 | ||
|
|
d0c5731287 | ||
|
|
5eaf222f88 | ||
|
|
0249ab4df5 | ||
|
|
4f8cba160d | ||
|
|
c269e089da | ||
|
|
4873f40614 | ||
|
|
10bb9173ec | ||
|
|
ea8c9c7cc8 | ||
|
|
4c9c084eec | ||
|
|
b64ee10812 | ||
|
|
ee1ecf43db | ||
|
|
4d8db8eb7c | ||
|
|
1b13965200 | ||
|
|
38db8f7fe5 | ||
|
|
4ba8f893a6 | ||
|
|
c4b9be18f1 | ||
|
|
eabdf6cd04 | ||
|
|
e56c673005 | ||
|
|
dad9f12a5c | ||
|
|
aa5edb0b83 | ||
|
|
b315803180 | ||
|
|
b27317631b | ||
|
|
953a9daafb | ||
|
|
63f9cd449f | ||
|
|
54220f547b | ||
|
|
93d967c2b3 | ||
|
|
1226459c6f | ||
|
|
d7c9c9cb98 | ||
|
|
2131ef2397 | ||
|
|
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 |
@@ -7,30 +7,42 @@ orbs:
|
||||
node: circleci/node@4.5.1
|
||||
browser-tools: circleci/browser-tools@1.1.3
|
||||
jobs:
|
||||
npm-audit:
|
||||
executor: linux
|
||||
steps:
|
||||
- checkout
|
||||
- node/install:
|
||||
install-npm: true
|
||||
node-version: lts/fermium
|
||||
- run: npm install
|
||||
- run: npm audit --audit-level=low
|
||||
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:
|
||||
install-npm: false
|
||||
node-version: << parameters.node-version >>
|
||||
- node/install-packages:
|
||||
override-ci-command: npm install
|
||||
- when: # Just to save time until caching saves the browser bin
|
||||
- run: npm install
|
||||
- when:
|
||||
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
|
||||
version: "91.2.0esr" #https://archive.mozilla.org/pub/firefox/releases/
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "FirefoxHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-firefox
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "ChromeHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
@@ -42,28 +54,60 @@ jobs:
|
||||
- ~/.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/
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "", <<parameters.browser>> ] #Only run linting when browsers are not running to save time
|
||||
steps:
|
||||
- run: npm run lint
|
||||
- when:
|
||||
condition: << parameters.browser >> #Truthy evaluation to only run when browser is specified
|
||||
steps:
|
||||
- run: npm run test:coverage -- --browsers=<<parameters.browser>>
|
||||
- store_test_results:
|
||||
path: dist/reports/tests/
|
||||
- store_artifacts:
|
||||
path: dist/reports/
|
||||
workflows:
|
||||
matrix-tests:
|
||||
jobs:
|
||||
- test:
|
||||
post-steps:
|
||||
- run:
|
||||
command:
|
||||
curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov
|
||||
name: node10-chrome
|
||||
node-version: lts/dubnium
|
||||
browser: ChromeHeadless
|
||||
always-pass: false
|
||||
- test:
|
||||
name: node12-firefoxESR
|
||||
name: node12-build-lint
|
||||
node-version: lts/erbium
|
||||
browser: "" #Skip unit tests
|
||||
- test:
|
||||
name: node14-build-lint
|
||||
node-version: lts/fermium
|
||||
browser: "" #Skip unit tests
|
||||
|
||||
nightly:
|
||||
jobs:
|
||||
- test:
|
||||
name: node10-chrome-nightly
|
||||
node-version: lts/dubnium
|
||||
browser: ChromeHeadless
|
||||
- test:
|
||||
name: node12-firefoxESR-nightly
|
||||
node-version: lts/erbium
|
||||
browser: FirefoxESR
|
||||
always-pass: true
|
||||
- test:
|
||||
name: node14-chrome
|
||||
name: node14-firefox-nightly
|
||||
node-version: lts/fermium
|
||||
browser: ChromeHeadless
|
||||
always-pass: true
|
||||
browser: FirefoxHeadless
|
||||
- npm-audit
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,11 +1,12 @@
|
||||
<!--- This is for filing bugs. If you have a general question, please -->
|
||||
<!--- visit https://github.com/nasa/openmct/discussions -->
|
||||
|
||||
---
|
||||
name: Bug Report
|
||||
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. -->
|
||||
|
||||
@@ -35,7 +36,7 @@ about: File a Bug !
|
||||
|
||||
#### Environment
|
||||
* Open MCT Version: <!--- date of build, version, or SHA -->
|
||||
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yams? -->
|
||||
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yamcs? -->
|
||||
* OS:
|
||||
* Browser:
|
||||
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Discussions
|
||||
url: https://github.com/nasa/openmct/discussions
|
||||
about: Have a question about the project?
|
||||
|
||||
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
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,23 +0,0 @@
|
||||
<!--- This is for filing enhancements or features. If you have a general -->
|
||||
<!--- question, please visit https://github.com/nasa/openmct/discussions -->
|
||||
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for suggesting an idea to make Open MCT better.
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- Please describe the problem you are trying to solve. -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!--- Please describe the desired behavior. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!--- Please describe alternative solutions or features you have considered. -->
|
||||
11
.github/ISSUE_TEMPLATE/maintenance-type.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/maintenance-type.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Maintenance
|
||||
about: Add, update or remove documentation, tests, or dependencies.
|
||||
title: ''
|
||||
labels: type:maintenance
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Summary
|
||||
<!--- Generally describe the purpose of the change. -->
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
* [ ] 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? For example, developers won't need to change how they are calling the API or how they've extended core plugins such as Tables or Plots.
|
||||
|
||||
### Author Checklist
|
||||
|
||||
|
||||
23
.github/dependabot.yml
vendored
Normal file
23
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 2
|
||||
labels:
|
||||
- "type:maintenance"
|
||||
- "dependencies"
|
||||
allow:
|
||||
- dependency-name: "eslint*"
|
||||
- dependency-name: "karma*"
|
||||
- dependency-name: "jasmine*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "type:maintenance"
|
||||
- "dependencies"
|
||||
41
.github/workflows/codeql-analysis.yml
vendored
Normal file
41
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '**/*Spec.js'
|
||||
- '**/*.md'
|
||||
- '**/*.txt'
|
||||
- '**/*.yml'
|
||||
- '**/*.yaml'
|
||||
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
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -44,3 +44,6 @@ report.*.json
|
||||
.lighthouseci
|
||||
|
||||
package-lock.json
|
||||
|
||||
#codecov artifacts
|
||||
codecov
|
||||
|
||||
2
API.md
2
API.md
@@ -996,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.
|
||||
|
||||
|
||||
@@ -317,6 +317,7 @@ checklist).
|
||||
### Reviewer Checklist
|
||||
|
||||
* [ ] 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?
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript)
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [](https://codecov.io/gh/nasa/openmct)
|
||||
|
||||
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.
|
||||
|
||||
@@ -85,6 +85,8 @@ naming convention is otherwise the same.)
|
||||
When `npm test` is run, test results will be written as HTML to
|
||||
`dist/reports/tests/`. Code coverage information is written to `dist/reports/coverage`.
|
||||
|
||||
Code Coverage Reports are available from [codecov.io](https://app.codecov.io/gh/nasa/openmct/)
|
||||
|
||||
# Glossary
|
||||
|
||||
Certain terms are used throughout Open MCT with consistent meanings
|
||||
|
||||
31
SECURITY.md
Normal file
31
SECURITY.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Security Policy
|
||||
|
||||
The Open MCT team secures our code base using a combination of code review, dependency review, and periodic security reviews. Static analysis performed during automated verification additionally safeguards against common coding errors which may result in vulnerabilities.
|
||||
|
||||
### Reporting a Vulnerability
|
||||
|
||||
For general defects, please for a [Bug Report](https://github.com/nasa/openmct/issues/new/choose)
|
||||
|
||||
To report a vulnerability for Open MCT please send a detailed report to [arc-dl-openmct](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
|
||||
See our [top-level security policy](https://github.com/nasa/openmct/security/policy) for additional information.
|
||||
|
||||
### CodeQL and LGTM
|
||||
|
||||
The [CodeQL GitHub Actions workflow](https://github.com/nasa/openmct/blob/master/.github/workflows/codeql-analysis.yml) is available to the public. To review the results, fork the repository and run the CodeQL workflow.
|
||||
|
||||
CodeQL is run for every pull-request in GitHub Actions.
|
||||
|
||||
The project is also monitored by [LGTM](https://lgtm.com/projects/g/nasa/openmct/) and is available to public.
|
||||
|
||||
### ESLint
|
||||
|
||||
Static analysis is run for every push on the master branch and every pull request on all branches in Github Actions.
|
||||
|
||||
For more information about ESLint, visit https://eslint.org/.
|
||||
|
||||
### General Support
|
||||
|
||||
For additional support, please open a [Github Discussion](https://github.com/nasa/openmct/discussions).
|
||||
|
||||
If you wish to report a cybersecurity incident or concern, please contact the NASA Security Operations Center either by phone at 1-877-627-2732 or via email address soc@nasa.gov.
|
||||
27
codecov.yml
Normal file
27
codecov.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
codecov:
|
||||
require_ci_to_pass: false #This setting will update the bot regardless of whether or not tests pass
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
precision: 2
|
||||
round: down
|
||||
range: "66...100"
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: true
|
||||
loop: true
|
||||
method: false
|
||||
macro: false
|
||||
|
||||
comment:
|
||||
layout: "reach,diff,flags,files,footer"
|
||||
behavior: default
|
||||
require_changes: false
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -20,156 +20,219 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
const DEFAULT_IMAGE_SAMPLES = [
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
|
||||
];
|
||||
const DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS = 20000;
|
||||
const MIN_IMAGE_LOAD_DELAY_IN_MILISECONDS = 5000;
|
||||
|
||||
], function (
|
||||
let openmctInstance;
|
||||
|
||||
) {
|
||||
function ImageryPlugin() {
|
||||
|
||||
const IMAGE_SAMPLES = [
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg",
|
||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
|
||||
];
|
||||
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('/');
|
||||
const imageDownloadName = `example.imagery.${urlItems[urlItems.length - 1]}`;
|
||||
|
||||
return {
|
||||
name,
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
var realtimeProvider = {
|
||||
supportsSubscribe: function (domainObject) {
|
||||
return domainObject.type === 'example.imagery';
|
||||
},
|
||||
subscribe: function (domainObject, callback) {
|
||||
var interval = setInterval(function () {
|
||||
callback(pointForTimestamp(Date.now(), domainObject.name));
|
||||
}, IMAGE_DELAY);
|
||||
|
||||
return function () {
|
||||
clearInterval(interval);
|
||||
export default function () {
|
||||
return function install(openmct) {
|
||||
openmctInstance = openmct;
|
||||
openmct.types.addType('example.imagery', {
|
||||
key: 'example.imagery',
|
||||
name: 'Example Imagery',
|
||||
cssClass: 'icon-image',
|
||||
description: 'For development use. Creates example imagery '
|
||||
+ 'data that mimics a live imagery stream.',
|
||||
creatable: true,
|
||||
initialize: (object) => {
|
||||
object.configuration = {
|
||||
imageLocation: '',
|
||||
imageLoadDelayInMilliSeconds: DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS,
|
||||
imageSamples: []
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var historicalProvider = {
|
||||
supportsRequest: function (domainObject, options) {
|
||||
return domainObject.type === 'example.imagery'
|
||||
&& options.strategy !== 'latest';
|
||||
},
|
||||
request: function (domainObject, options) {
|
||||
var start = options.start;
|
||||
var end = Math.min(options.end, Date.now());
|
||||
var data = [];
|
||||
while (start <= end && data.length < IMAGE_DELAY) {
|
||||
data.push(pointForTimestamp(start, domainObject.name));
|
||||
start += IMAGE_DELAY;
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
};
|
||||
|
||||
var ladProvider = {
|
||||
supportsRequest: function (domainObject, options) {
|
||||
return domainObject.type === 'example.imagery'
|
||||
&& options.strategy === 'latest';
|
||||
},
|
||||
request: function (domainObject, options) {
|
||||
return Promise.resolve([pointForTimestamp(Date.now(), domainObject.name)]);
|
||||
}
|
||||
};
|
||||
|
||||
return function install(openmct) {
|
||||
openmct.types.addType('example.imagery', {
|
||||
key: 'example.imagery',
|
||||
name: 'Example Imagery',
|
||||
cssClass: 'icon-image',
|
||||
description: 'For development use. Creates example imagery '
|
||||
+ 'data that mimics a live imagery stream.',
|
||||
creatable: true,
|
||||
initialize: function (object) {
|
||||
object.telemetry = {
|
||||
values: [
|
||||
{
|
||||
name: 'Name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
name: 'Time',
|
||||
key: 'utc',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Local Time',
|
||||
key: 'local',
|
||||
format: 'local-format',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Image',
|
||||
key: 'url',
|
||||
format: 'image',
|
||||
hints: {
|
||||
image: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Image Download Name',
|
||||
key: 'imageDownloadName',
|
||||
format: 'imageDownloadName',
|
||||
hints: {
|
||||
imageDownloadName: 1
|
||||
}
|
||||
object.telemetry = {
|
||||
values: [
|
||||
{
|
||||
name: 'Name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
name: 'Time',
|
||||
key: 'utc',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 2
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
{
|
||||
name: 'Local Time',
|
||||
key: 'local',
|
||||
format: 'local-format',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Image',
|
||||
key: 'url',
|
||||
format: 'image',
|
||||
hints: {
|
||||
image: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Image Download Name',
|
||||
key: 'imageDownloadName',
|
||||
format: 'imageDownloadName',
|
||||
hints: {
|
||||
imageDownloadName: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
form: [
|
||||
{
|
||||
key: 'imageLocation',
|
||||
name: 'Images url list (comma separated)',
|
||||
control: 'textarea',
|
||||
cssClass: 'l-inline',
|
||||
property: [
|
||||
"configuration",
|
||||
"imageLocation"
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'imageLoadDelayInMilliSeconds',
|
||||
name: 'Image load delay (milliseconds)',
|
||||
control: 'numberfield',
|
||||
required: true,
|
||||
cssClass: 'l-inline',
|
||||
property: [
|
||||
"configuration",
|
||||
"imageLoadDelayInMilliSeconds"
|
||||
]
|
||||
}
|
||||
});
|
||||
]
|
||||
});
|
||||
|
||||
openmct.telemetry.addProvider(realtimeProvider);
|
||||
openmct.telemetry.addProvider(historicalProvider);
|
||||
openmct.telemetry.addProvider(ladProvider);
|
||||
};
|
||||
openmct.telemetry.addProvider(getRealtimeProvider());
|
||||
openmct.telemetry.addProvider(getHistoricalProvider());
|
||||
openmct.telemetry.addProvider(getLadProvider());
|
||||
};
|
||||
}
|
||||
|
||||
function getCompassValues(min, max) {
|
||||
return min + Math.random() * (max - min);
|
||||
}
|
||||
|
||||
function getImageSamples(configuration) {
|
||||
let imageSamples = DEFAULT_IMAGE_SAMPLES;
|
||||
|
||||
if (configuration.imageLocation && configuration.imageLocation.length) {
|
||||
imageSamples = getImageUrlListFromConfig(configuration);
|
||||
}
|
||||
|
||||
return ImageryPlugin;
|
||||
});
|
||||
return imageSamples;
|
||||
}
|
||||
|
||||
function getImageUrlListFromConfig(configuration) {
|
||||
return configuration.imageLocation.split(',');
|
||||
}
|
||||
|
||||
function getImageLoadDelay(domainObject) {
|
||||
const imageLoadDelay = domainObject.configuration.imageLoadDelayInMilliSeconds;
|
||||
if (!imageLoadDelay) {
|
||||
openmctInstance.objects.mutate(domainObject, 'configuration.imageLoadDelayInMilliSeconds', DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS);
|
||||
|
||||
return DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS;
|
||||
}
|
||||
|
||||
if (imageLoadDelay < MIN_IMAGE_LOAD_DELAY_IN_MILISECONDS) {
|
||||
openmctInstance.objects.mutate(domainObject, 'configuration.imageLoadDelayInMilliSeconds', MIN_IMAGE_LOAD_DELAY_IN_MILISECONDS);
|
||||
|
||||
return MIN_IMAGE_LOAD_DELAY_IN_MILISECONDS;
|
||||
}
|
||||
|
||||
return imageLoadDelay;
|
||||
}
|
||||
|
||||
function getRealtimeProvider() {
|
||||
return {
|
||||
supportsSubscribe: domainObject => domainObject.type === 'example.imagery',
|
||||
subscribe: (domainObject, callback) => {
|
||||
const delay = getImageLoadDelay(domainObject);
|
||||
const interval = setInterval(() => {
|
||||
callback(pointForTimestamp(Date.now(), domainObject.name, getImageSamples(domainObject.configuration), delay));
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getHistoricalProvider() {
|
||||
return {
|
||||
supportsRequest: (domainObject, options) => {
|
||||
return domainObject.type === 'example.imagery'
|
||||
&& options.strategy !== 'latest';
|
||||
},
|
||||
request: (domainObject, options) => {
|
||||
const delay = getImageLoadDelay(domainObject);
|
||||
let start = options.start;
|
||||
const end = Math.min(options.end, Date.now());
|
||||
const data = [];
|
||||
while (start <= end && data.length < delay) {
|
||||
data.push(pointForTimestamp(start, domainObject.name, getImageSamples(domainObject.configuration), delay));
|
||||
start += delay;
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getLadProvider() {
|
||||
return {
|
||||
supportsRequest: (domainObject, options) => {
|
||||
return domainObject.type === 'example.imagery'
|
||||
&& options.strategy === 'latest';
|
||||
},
|
||||
request: (domainObject, options) => {
|
||||
const delay = getImageLoadDelay(domainObject);
|
||||
|
||||
return Promise.resolve([pointForTimestamp(Date.now(), domainObject.name, delay)]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function pointForTimestamp(timestamp, name, imageSamples, delay) {
|
||||
const url = imageSamples[Math.floor(timestamp / delay) % imageSamples.length];
|
||||
const urlItems = url.split('/');
|
||||
const imageDownloadName = `example.imagery.${urlItems[urlItems.length - 1]}`;
|
||||
|
||||
return {
|
||||
name,
|
||||
utc: Math.floor(timestamp / delay) * delay,
|
||||
local: Math.floor(timestamp / delay) * 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>
|
||||
|
||||
@@ -82,12 +82,14 @@
|
||||
);
|
||||
|
||||
openmct.install(openmct.plugins.LocalStorage());
|
||||
|
||||
openmct.install(openmct.plugins.Espresso());
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
openmct.install(openmct.plugins.Generator());
|
||||
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 +196,8 @@
|
||||
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
||||
{indicator: true}
|
||||
));
|
||||
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
|
||||
openmct.install(openmct.plugins.Timer());
|
||||
openmct.start();
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
const devMode = process.env.NODE_ENV !== 'production';
|
||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
|
||||
const coverageEnabled = process.env.COVERAGE === 'true';
|
||||
const reporters = ['progress', 'html', 'junit'];
|
||||
const reporters = ['spec', 'junit'];
|
||||
|
||||
if (coverageEnabled) {
|
||||
reporters.push('coverage-istanbul');
|
||||
@@ -60,7 +60,7 @@ module.exports = (config) => {
|
||||
client: {
|
||||
jasmine: {
|
||||
random: false,
|
||||
timeoutInterval: 30000
|
||||
timeoutInterval: 5000
|
||||
}
|
||||
},
|
||||
customLaunchers: {
|
||||
@@ -78,33 +78,37 @@ module.exports = (config) => {
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
// HTML test reporting.
|
||||
htmlReporter: {
|
||||
outputDir: "dist/reports/tests",
|
||||
preserveDescribeNesting: true,
|
||||
foldAll: false
|
||||
},
|
||||
// htmlReporter: {
|
||||
// outputDir: "dist/reports/tests",
|
||||
// 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",
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
reports: ['lcovonly', 'text-summary'],
|
||||
thresholds: {
|
||||
global: {
|
||||
lines: 66
|
||||
}
|
||||
}
|
||||
},
|
||||
specReporter: {
|
||||
maxLogLines: 5,
|
||||
suppressErrorSummary: false,
|
||||
suppressFailed: false,
|
||||
suppressPassed: false,
|
||||
suppressSkipped: true,
|
||||
showSpecTiming: true,
|
||||
failFast: false
|
||||
},
|
||||
preprocessors: {
|
||||
'indexTest.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
|
||||
20
package.json
20
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "1.7.4",
|
||||
"version": "1.8.1-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@braintree/sanitize-url": "^5.0.2",
|
||||
"angular": ">=1.8.0",
|
||||
"angular-route": "1.4.14",
|
||||
"babel-eslint": "10.0.3",
|
||||
@@ -12,16 +12,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",
|
||||
@@ -41,14 +34,14 @@
|
||||
"jsdoc": "^3.3.2",
|
||||
"karma": "6.3.4",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-firefox-launcher": "2.1.0",
|
||||
"karma-cli": "2.0.0",
|
||||
"karma-coverage": "2.0.3",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-junit-reporter": "2.0.1",
|
||||
"karma-html-reporter": "0.2.7",
|
||||
"karma-firefox-launcher": "2.1.1",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-junit-reporter": "2.0.1",
|
||||
"karma-sourcemap-loader": "0.3.8",
|
||||
"karma-spec-reporter": "0.0.32",
|
||||
"karma-webpack": "4.0.2",
|
||||
"location-bar": "^3.0.1",
|
||||
"lodash": "^4.17.12",
|
||||
@@ -62,6 +55,8 @@
|
||||
"node-bourbon": "^4.2.3",
|
||||
"node-sass": "^4.14.1",
|
||||
"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",
|
||||
@@ -70,6 +65,7 @@
|
||||
"uuid": "^3.3.3",
|
||||
"v8-compile-cache": "^1.1.0",
|
||||
"vue": "2.5.6",
|
||||
"vue-eslint-parser": "7.11.0",
|
||||
"vue-loader": "^15.2.6",
|
||||
"vue-template-compiler": "2.5.6",
|
||||
"webpack": "^4.16.2",
|
||||
|
||||
@@ -64,7 +64,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) {
|
||||
|
||||
@@ -34,9 +34,6 @@ define([
|
||||
"./src/policies/EditPersistableObjectsPolicy",
|
||||
"./src/representers/EditRepresenter",
|
||||
"./src/capabilities/EditorCapability",
|
||||
"./src/capabilities/TransactionCapabilityDecorator",
|
||||
"./src/services/TransactionManager",
|
||||
"./src/services/TransactionService",
|
||||
"./src/creation/CreateMenuController",
|
||||
"./src/creation/LocatorController",
|
||||
"./src/creation/CreationPolicy",
|
||||
@@ -63,9 +60,6 @@ define([
|
||||
EditPersistableObjectsPolicy,
|
||||
EditRepresenter,
|
||||
EditorCapability,
|
||||
TransactionCapabilityDecorator,
|
||||
TransactionManager,
|
||||
TransactionService,
|
||||
CreateMenuController,
|
||||
LocatorController,
|
||||
CreationPolicy,
|
||||
@@ -263,26 +257,6 @@ define([
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"type": "decorator",
|
||||
"provides": "capabilityService",
|
||||
"implementation": TransactionCapabilityDecorator,
|
||||
"depends": [
|
||||
"$q",
|
||||
"transactionManager"
|
||||
],
|
||||
"priority": "fallback"
|
||||
},
|
||||
{
|
||||
"type": "provider",
|
||||
"provides": "transactionService",
|
||||
"implementation": TransactionService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$log",
|
||||
"cacheService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreateActionProvider",
|
||||
"provides": "actionService",
|
||||
@@ -320,7 +294,6 @@ define([
|
||||
"description": "Provides transactional editing capabilities",
|
||||
"implementation": EditorCapability,
|
||||
"depends": [
|
||||
"transactionService",
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
@@ -331,15 +304,6 @@ define([
|
||||
"template": locatorTemplate
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "transactionManager",
|
||||
"implementation": TransactionManager,
|
||||
"depends": [
|
||||
"transactionService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
depends: [
|
||||
|
||||
@@ -96,8 +96,7 @@ function (
|
||||
SaveAsAction.prototype.save = function () {
|
||||
var self = this,
|
||||
domainObject = this.domainObject,
|
||||
dialog = new SaveInProgressDialog(this.dialogService),
|
||||
toUndirty = [];
|
||||
dialog = new SaveInProgressDialog(this.dialogService);
|
||||
|
||||
function doWizardSave(parent) {
|
||||
var wizard = self.createWizard(parent);
|
||||
@@ -132,11 +131,14 @@ function (
|
||||
return fetchObject(object.getModel().location);
|
||||
}
|
||||
|
||||
function saveObject(parent) {
|
||||
return self.openmct.editor.save().then(() => {
|
||||
// Force mutation for search indexing
|
||||
return parent;
|
||||
});
|
||||
function saveObject(object) {
|
||||
//persist the object, which adds it to the transaction and then call editor.save
|
||||
return object.getCapability("persistence").persist()
|
||||
.then(() => {
|
||||
return self.openmct.editor.save().then(() => {
|
||||
return object;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addSavedObjectToParent(parent) {
|
||||
@@ -150,17 +152,6 @@ function (
|
||||
});
|
||||
}
|
||||
|
||||
function undirty(object) {
|
||||
return object.getCapability('persistence').refresh();
|
||||
}
|
||||
|
||||
function undirtyOriginals(object) {
|
||||
return Promise.all(toUndirty.map(undirty))
|
||||
.then(() => {
|
||||
return object;
|
||||
});
|
||||
}
|
||||
|
||||
function indexForSearch(addedObject) {
|
||||
addedObject.useCapability('mutation', (model) => {
|
||||
return model;
|
||||
@@ -187,10 +178,9 @@ function (
|
||||
return getParent(domainObject)
|
||||
.then(doWizardSave)
|
||||
.then(showBlockingDialog)
|
||||
.then(getParent)
|
||||
.then(saveObject)
|
||||
.then(getParent)
|
||||
.then(addSavedObjectToParent)
|
||||
.then(undirtyOriginals)
|
||||
.then((addedObject) => {
|
||||
return fetchObject(addedObject.getId());
|
||||
})
|
||||
|
||||
@@ -30,34 +30,17 @@ define(
|
||||
* Once initiated, any persist operations will be queued pending a
|
||||
* subsequent call to [.save()](@link #save) or [.finish()](@link
|
||||
* #finish).
|
||||
* @param transactionService
|
||||
* @param domainObject
|
||||
* @constructor
|
||||
*/
|
||||
function EditorCapability(
|
||||
transactionService,
|
||||
openmct,
|
||||
domainObject
|
||||
) {
|
||||
this.transactionService = transactionService;
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an editing session. This will start a transaction during
|
||||
* which any persist operations will be deferred until either save()
|
||||
* or finish() are called.
|
||||
*/
|
||||
EditorCapability.prototype.edit = function () {
|
||||
console.warn('DEPRECATED: cannot edit via edit capability, use openmct.editor instead.');
|
||||
|
||||
if (!this.openmct.editor.isEditing()) {
|
||||
this.openmct.editor.edit();
|
||||
this.domainObject.getCapability('status').set('editing', true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether this object, or any of its ancestors are
|
||||
* currently being edited.
|
||||
@@ -76,38 +59,6 @@ define(
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
/**
|
||||
* Save any unsaved changes from this editing session. This will
|
||||
* end the current transaction and continue with a new one.
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.save = function () {
|
||||
console.warn('DEPRECATED: cannot save via edit capability, use openmct.editor instead.');
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
||||
|
||||
/**
|
||||
* Finish the current editing session. This will discard any pending
|
||||
* persist operations
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.finish = function () {
|
||||
console.warn('DEPRECATED: cannot finish via edit capability, use openmct.editor instead.');
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if there have been any domain model
|
||||
* modifications since the last persist, false otherwise.
|
||||
*/
|
||||
EditorCapability.prototype.dirty = function () {
|
||||
return this.transactionService.size() > 0;
|
||||
};
|
||||
|
||||
return EditorCapability;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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(
|
||||
['./TransactionalPersistenceCapability'],
|
||||
function (TransactionalPersistenceCapability) {
|
||||
|
||||
/**
|
||||
* Wraps the [PersistenceCapability]{@link PersistenceCapability} with
|
||||
* transactional capabilities.
|
||||
* @param $q
|
||||
* @param transactionService
|
||||
* @param capabilityService
|
||||
* @see TransactionalPersistenceCapability
|
||||
* @constructor
|
||||
*/
|
||||
function TransactionCapabilityDecorator(
|
||||
$q,
|
||||
transactionService,
|
||||
capabilityService
|
||||
) {
|
||||
this.capabilityService = capabilityService;
|
||||
this.transactionService = transactionService;
|
||||
this.$q = $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate PersistenceCapability to queue persistence calls when a
|
||||
* transaction is in progress.
|
||||
*/
|
||||
TransactionCapabilityDecorator.prototype.getCapabilities = function () {
|
||||
var self = this,
|
||||
capabilities = this.capabilityService.getCapabilities
|
||||
.apply(this.capabilityService, arguments),
|
||||
persistenceCapability = capabilities.persistence;
|
||||
|
||||
capabilities.persistence = function (domainObject) {
|
||||
var original =
|
||||
(typeof persistenceCapability === 'function')
|
||||
? persistenceCapability(domainObject)
|
||||
: persistenceCapability;
|
||||
|
||||
return new TransactionalPersistenceCapability(
|
||||
self.$q,
|
||||
self.transactionService,
|
||||
original,
|
||||
domainObject
|
||||
);
|
||||
};
|
||||
|
||||
return capabilities;
|
||||
};
|
||||
|
||||
return TransactionCapabilityDecorator;
|
||||
}
|
||||
);
|
||||
@@ -1,91 +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 () {
|
||||
|
||||
/**
|
||||
* Wraps persistence capability to enable transactions. Transactions
|
||||
* will cause persist calls not to be invoked immediately, but
|
||||
* rather queued until [EditorCapability.save()]{@link EditorCapability#save}
|
||||
* or [EditorCapability.cancel()]{@link EditorCapability#cancel} are
|
||||
* called.
|
||||
* @memberof platform/commonUI/edit/capabilities
|
||||
* @param $q
|
||||
* @param transactionManager
|
||||
* @param persistenceCapability
|
||||
* @param domainObject
|
||||
* @constructor
|
||||
*/
|
||||
function TransactionalPersistenceCapability(
|
||||
$q,
|
||||
transactionManager,
|
||||
persistenceCapability,
|
||||
domainObject
|
||||
) {
|
||||
this.transactionManager = transactionManager;
|
||||
this.persistenceCapability = persistenceCapability;
|
||||
this.domainObject = domainObject;
|
||||
this.$q = $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* The wrapped persist function. If a transaction is active, persist
|
||||
* will be queued until the transaction is committed or cancelled.
|
||||
* @returns {*}
|
||||
*/
|
||||
TransactionalPersistenceCapability.prototype.persist = function () {
|
||||
var wrappedPersistence = this.persistenceCapability;
|
||||
|
||||
if (this.transactionManager.isActive()) {
|
||||
this.transactionManager.addToTransaction(
|
||||
this.domainObject.getId(),
|
||||
wrappedPersistence.persist.bind(wrappedPersistence),
|
||||
wrappedPersistence.refresh.bind(wrappedPersistence)
|
||||
);
|
||||
|
||||
//Need to return a promise from this function
|
||||
return this.$q.when(true);
|
||||
} else {
|
||||
return this.persistenceCapability.persist();
|
||||
}
|
||||
};
|
||||
|
||||
TransactionalPersistenceCapability.prototype.refresh = function () {
|
||||
this.transactionManager
|
||||
.clearTransactionsFor(this.domainObject.getId());
|
||||
|
||||
return this.persistenceCapability.refresh();
|
||||
};
|
||||
|
||||
TransactionalPersistenceCapability.prototype.getSpace = function () {
|
||||
return this.persistenceCapability.getSpace();
|
||||
};
|
||||
|
||||
TransactionalPersistenceCapability.prototype.persisted = function () {
|
||||
return this.persistenceCapability.persisted();
|
||||
};
|
||||
|
||||
return TransactionalPersistenceCapability;
|
||||
}
|
||||
);
|
||||
@@ -86,11 +86,20 @@ define(
|
||||
})
|
||||
.join('/');
|
||||
|
||||
openmct.router.navigate(url);
|
||||
function editObject() {
|
||||
const path = objectPath.slice(-1).map(obj => {
|
||||
const objNew = obj.getCapability('adapter').invoke();
|
||||
|
||||
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
|
||||
openmct.editor.edit();
|
||||
return objNew;
|
||||
});
|
||||
if (isFirstViewEditable(object.useCapability('adapter'), path)) {
|
||||
openmct.editor.edit();
|
||||
}
|
||||
}
|
||||
|
||||
openmct.router.once('afterNavigation', editObject);
|
||||
|
||||
openmct.router.navigate(url);
|
||||
}
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
|
||||
@@ -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([], function () {
|
||||
/**
|
||||
* A Transaction represents a set of changes that are intended to
|
||||
* be kept or discarded as a unit.
|
||||
* @param $log Angular's `$log` service, for logging messages
|
||||
* @constructor
|
||||
* @memberof platform/commonUI/edit/services
|
||||
*/
|
||||
function Transaction($log) {
|
||||
this.$log = $log;
|
||||
this.callbacks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a change to the current transaction, as expressed by functions
|
||||
* to either keep or discard the change.
|
||||
* @param {Function} commit called when the transaction is committed
|
||||
* @param {Function} cancel called when the transaction is cancelled
|
||||
* @returns {Function) a function which may be called to remove this
|
||||
* pair of callbacks from the transaction
|
||||
*/
|
||||
Transaction.prototype.add = function (commit, cancel) {
|
||||
var callback = {
|
||||
commit: commit,
|
||||
cancel: cancel
|
||||
};
|
||||
this.callbacks.push(callback);
|
||||
|
||||
return function () {
|
||||
this.callbacks = this.callbacks.filter(function (c) {
|
||||
return c !== callback;
|
||||
});
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of changes in the current transaction.
|
||||
* @returns {number} the size of the current transaction
|
||||
*/
|
||||
Transaction.prototype.size = function () {
|
||||
return this.callbacks.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Keep all changes associated with this transaction.
|
||||
* @method {platform/commonUI/edit/services.Transaction#commit}
|
||||
* @returns {Promise} a promise which will resolve when all callbacks
|
||||
* have been handled.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Discard all changes associated with this transaction.
|
||||
* @method {platform/commonUI/edit/services.Transaction#cancel}
|
||||
* @returns {Promise} a promise which will resolve when all callbacks
|
||||
* have been handled.
|
||||
*/
|
||||
|
||||
['commit', 'cancel'].forEach(function (method) {
|
||||
Transaction.prototype[method] = function () {
|
||||
var promises = [];
|
||||
var callback;
|
||||
|
||||
while (this.callbacks.length > 0) {
|
||||
callback = this.callbacks.shift();
|
||||
try {
|
||||
promises.push(callback[method]());
|
||||
} catch (e) {
|
||||
this.$log
|
||||
.error("Error trying to " + method + " transaction.");
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
};
|
||||
});
|
||||
|
||||
return Transaction;
|
||||
});
|
||||
@@ -1,119 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* Manages transactions to support the TransactionalPersistenceCapability.
|
||||
* This assumes that all commit/cancel callbacks for a given domain
|
||||
* object are equivalent, and only need to be added once to any active
|
||||
* transaction. Violating this assumption may cause unexpected behavior.
|
||||
* @constructor
|
||||
* @memberof platform/commonUI/edit
|
||||
*/
|
||||
function TransactionManager(transactionService) {
|
||||
this.transactionService = transactionService;
|
||||
this.clearTransactionFns = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a transaction is currently active.
|
||||
* @returns {boolean} true if there is a transaction active
|
||||
*/
|
||||
TransactionManager.prototype.isActive = function () {
|
||||
return this.transactionService.isActive();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if callbacks associated with this domain object have already
|
||||
* been added to the active transaction.
|
||||
* @private
|
||||
* @param {string} id the identifier of the domain object to check
|
||||
* @returns {boolean} true if callbacks have been added
|
||||
*/
|
||||
TransactionManager.prototype.isScheduled = function (id) {
|
||||
return Boolean(this.clearTransactionFns[id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add callbacks associated with this domain object to the active
|
||||
* transaction. Both callbacks are expected to return promises that
|
||||
* resolve when their associated behavior is complete.
|
||||
*
|
||||
* If callbacks associated with this domain object have already been
|
||||
* added to the active transaction, this call will be ignored.
|
||||
*
|
||||
* @param {string} id the identifier of the associated domain object
|
||||
* @param {Function} onCommit behavior to invoke when committing transaction
|
||||
* @param {Function} onCancel behavior to invoke when cancelling transaction
|
||||
*/
|
||||
TransactionManager.prototype.addToTransaction = function (
|
||||
id,
|
||||
onCommit,
|
||||
onCancel
|
||||
) {
|
||||
var release = this.releaseClearFn.bind(this, id);
|
||||
|
||||
function chain(promiseFn, nextFn) {
|
||||
return function () {
|
||||
return promiseFn().then(nextFn);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any existing persistence calls for object with given ID. This ensures only the most recent persistence
|
||||
* call is executed. This should prevent stale objects being persisted and overwriting fresh ones.
|
||||
*/
|
||||
if (this.isScheduled(id)) {
|
||||
this.clearTransactionsFor(id);
|
||||
}
|
||||
|
||||
this.clearTransactionFns[id] =
|
||||
this.transactionService.addToTransaction(
|
||||
chain(onCommit, release),
|
||||
chain(onCancel, release)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove any callbacks associated with this domain object from the
|
||||
* active transaction.
|
||||
* @param {string} id the identifier for the domain object
|
||||
*/
|
||||
TransactionManager.prototype.clearTransactionsFor = function (id) {
|
||||
if (this.isScheduled(id)) {
|
||||
this.clearTransactionFns[id]();
|
||||
this.releaseClearFn(id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Release the cached "remove from transaction" function that has been
|
||||
* stored in association with this domain object.
|
||||
* @param {string} id the identifier for the domain object
|
||||
* @private
|
||||
*/
|
||||
TransactionManager.prototype.releaseClearFn = function (id) {
|
||||
delete this.clearTransactionFns[id];
|
||||
};
|
||||
|
||||
return TransactionManager;
|
||||
});
|
||||
@@ -1,138 +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(
|
||||
['./Transaction', './NestedTransaction'],
|
||||
function (Transaction, NestedTransaction) {
|
||||
/**
|
||||
* Implements an application-wide transaction state. Once a
|
||||
* transaction is started, calls to
|
||||
* [PersistenceCapability.persist()]{@link PersistenceCapability#persist}
|
||||
* will be deferred until a subsequent call to
|
||||
* [TransactionService.commit]{@link TransactionService#commit} is made.
|
||||
*
|
||||
* @memberof platform/commonUI/edit/services
|
||||
* @param $q
|
||||
* @constructor
|
||||
*/
|
||||
function TransactionService($q, $log, cacheService) {
|
||||
this.$q = $q;
|
||||
this.$log = $log;
|
||||
this.cacheService = cacheService;
|
||||
this.transactions = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transaction. While a transaction is active all calls to
|
||||
* [PersistenceCapability.persist](@link PersistenceCapability#persist)
|
||||
* will be queued until [commit]{@link #commit} or [cancel]{@link
|
||||
* #cancel} are called
|
||||
*/
|
||||
TransactionService.prototype.startTransaction = function () {
|
||||
var transaction = this.isActive()
|
||||
? new NestedTransaction(this.transactions[0])
|
||||
: new Transaction(this.$log);
|
||||
|
||||
this.transactions.push(transaction);
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} If true, indicates that a transaction is in progress
|
||||
*/
|
||||
TransactionService.prototype.isActive = function () {
|
||||
return this.transactions.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds provided functions to a queue to be called on
|
||||
* [.commit()]{@link #commit} or
|
||||
* [.cancel()]{@link #commit}
|
||||
* @param onCommit A function to call on commit
|
||||
* @param onCancel A function to call on cancel
|
||||
*/
|
||||
TransactionService.prototype.addToTransaction = function (onCommit, onCancel) {
|
||||
if (this.isActive()) {
|
||||
return this.activeTransaction().add(onCommit, onCancel);
|
||||
} else {
|
||||
//Log error because this is a programming error if it occurs.
|
||||
this.$log.error("No transaction in progress");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the transaction at the top of the stack.
|
||||
* @private
|
||||
*/
|
||||
TransactionService.prototype.activeTransaction = function () {
|
||||
return this.transactions[this.transactions.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* All persist calls deferred since the beginning of the transaction
|
||||
* will be committed. If this is the last transaction, clears the
|
||||
* cache.
|
||||
*
|
||||
* @returns {Promise} resolved when all persist operations have
|
||||
* completed. Will reject if any commit operations fail
|
||||
*/
|
||||
TransactionService.prototype.commit = function () {
|
||||
var transaction = this.transactions.pop();
|
||||
if (!transaction) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
if (!this.isActive()) {
|
||||
return transaction.commit()
|
||||
.then(function (r) {
|
||||
this.cacheService.flush();
|
||||
|
||||
return r;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
return transaction.commit();
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel the current transaction, replacing any dirty objects from
|
||||
* persistence. Not a true rollback, as it cannot be used to undo any
|
||||
* persist calls that were successful in the event one of a batch of
|
||||
* persists failing.
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
TransactionService.prototype.cancel = function () {
|
||||
var transaction = this.transactions.pop();
|
||||
|
||||
return transaction ? transaction.cancel() : Promise.reject();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size (the number of commit/cancel callbacks) of
|
||||
* the active transaction.
|
||||
* @returns {number} size of the active transaction
|
||||
*/
|
||||
TransactionService.prototype.size = function () {
|
||||
return this.isActive() ? this.activeTransaction().size() : 0;
|
||||
};
|
||||
|
||||
return TransactionService;
|
||||
});
|
||||
@@ -1,192 +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/capabilities/EditorCapability"],
|
||||
function (EditorCapability) {
|
||||
|
||||
xdescribe("The editor capability", function () {
|
||||
var mockDomainObject,
|
||||
capabilities,
|
||||
mockParentObject,
|
||||
mockTransactionService,
|
||||
mockStatusCapability,
|
||||
mockParentStatus,
|
||||
mockContextCapability,
|
||||
capability;
|
||||
|
||||
function fastPromise(val) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return callback(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
|
||||
);
|
||||
mockParentObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
|
||||
);
|
||||
mockTransactionService = jasmine.createSpyObj(
|
||||
"transactionService",
|
||||
[
|
||||
"startTransaction",
|
||||
"size",
|
||||
"commit",
|
||||
"cancel"
|
||||
]
|
||||
);
|
||||
mockTransactionService.commit.and.returnValue(fastPromise());
|
||||
mockTransactionService.cancel.and.returnValue(fastPromise());
|
||||
mockTransactionService.isActive = jasmine.createSpy('isActive');
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"statusCapability",
|
||||
["get", "set"]
|
||||
);
|
||||
mockParentStatus = jasmine.createSpyObj(
|
||||
"statusCapability",
|
||||
["get", "set"]
|
||||
);
|
||||
mockContextCapability = jasmine.createSpyObj(
|
||||
"contextCapability",
|
||||
["getParent"]
|
||||
);
|
||||
mockContextCapability.getParent.and.returnValue(mockParentObject);
|
||||
|
||||
capabilities = {
|
||||
context: mockContextCapability,
|
||||
status: mockStatusCapability
|
||||
};
|
||||
|
||||
mockDomainObject.hasCapability.and.callFake(function (name) {
|
||||
return capabilities[name] !== undefined;
|
||||
});
|
||||
|
||||
mockDomainObject.getCapability.and.callFake(function (name) {
|
||||
return capabilities[name];
|
||||
});
|
||||
|
||||
mockParentObject.getCapability.and.returnValue(mockParentStatus);
|
||||
mockParentObject.hasCapability.and.returnValue(false);
|
||||
|
||||
capability = new EditorCapability(
|
||||
mockTransactionService,
|
||||
mockDomainObject
|
||||
);
|
||||
});
|
||||
|
||||
it("starts a transaction when edit is invoked", function () {
|
||||
capability.edit();
|
||||
expect(mockTransactionService.startTransaction).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets editing status on object", function () {
|
||||
capability.edit();
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith("editing", true);
|
||||
});
|
||||
|
||||
it("uses editing status to determine editing context root", function () {
|
||||
capability.edit();
|
||||
mockStatusCapability.get.and.returnValue(false);
|
||||
expect(capability.isEditContextRoot()).toBe(false);
|
||||
mockStatusCapability.get.and.returnValue(true);
|
||||
expect(capability.isEditContextRoot()).toBe(true);
|
||||
});
|
||||
|
||||
it("inEditingContext returns true if parent object is being"
|
||||
+ " edited", function () {
|
||||
mockStatusCapability.get.and.returnValue(false);
|
||||
mockParentStatus.get.and.returnValue(false);
|
||||
expect(capability.inEditContext()).toBe(false);
|
||||
mockParentStatus.get.and.returnValue(true);
|
||||
expect(capability.inEditContext()).toBe(true);
|
||||
});
|
||||
|
||||
describe("save", function () {
|
||||
beforeEach(function () {
|
||||
capability.edit();
|
||||
capability.save();
|
||||
});
|
||||
it("commits the transaction", function () {
|
||||
expect(mockTransactionService.commit).toHaveBeenCalled();
|
||||
});
|
||||
it("begins a new transaction", function () {
|
||||
expect(mockTransactionService.startTransaction).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("finish", function () {
|
||||
beforeEach(function () {
|
||||
mockTransactionService.isActive.and.returnValue(true);
|
||||
capability.edit();
|
||||
capability.finish();
|
||||
});
|
||||
it("cancels the transaction", function () {
|
||||
expect(mockTransactionService.cancel).toHaveBeenCalled();
|
||||
});
|
||||
it("resets the edit state", function () {
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("finish", function () {
|
||||
beforeEach(function () {
|
||||
mockTransactionService.isActive.and.returnValue(false);
|
||||
capability.edit();
|
||||
});
|
||||
|
||||
it("does not cancel transaction when transaction is not active", function () {
|
||||
capability.finish();
|
||||
expect(mockTransactionService.cancel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns a promise", function () {
|
||||
expect(capability.finish() instanceof Promise).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("dirty", function () {
|
||||
var model = {};
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject.getModel.and.returnValue(model);
|
||||
capability.edit();
|
||||
capability.finish();
|
||||
});
|
||||
it("returns true if the object has been modified since it"
|
||||
+ " was last persisted", function () {
|
||||
mockTransactionService.size.and.returnValue(0);
|
||||
expect(capability.dirty()).toBe(false);
|
||||
mockTransactionService.size.and.returnValue(1);
|
||||
expect(capability.dirty()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,111 +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/capabilities/TransactionalPersistenceCapability"
|
||||
],
|
||||
function (TransactionalPersistenceCapability) {
|
||||
|
||||
function fastPromise(val) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return callback(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe("The transactional persistence decorator", function () {
|
||||
var mockQ,
|
||||
mockTransactionManager,
|
||||
mockPersistence,
|
||||
mockDomainObject,
|
||||
testId,
|
||||
capability;
|
||||
|
||||
beforeEach(function () {
|
||||
testId = "test-id";
|
||||
|
||||
mockQ = jasmine.createSpyObj("$q", ["when"]);
|
||||
mockQ.when.and.callFake(function (val) {
|
||||
return fastPromise(val);
|
||||
});
|
||||
mockTransactionManager = jasmine.createSpyObj(
|
||||
"transactionService",
|
||||
["isActive", "addToTransaction", "clearTransactionsFor"]
|
||||
);
|
||||
mockPersistence = jasmine.createSpyObj(
|
||||
"persistenceCapability",
|
||||
["persist", "refresh", "getSpace"]
|
||||
);
|
||||
mockPersistence.persist.and.returnValue(fastPromise());
|
||||
mockPersistence.refresh.and.returnValue(fastPromise());
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getModel", "getId"]
|
||||
);
|
||||
mockDomainObject.getModel.and.returnValue({persisted: 1});
|
||||
mockDomainObject.getId.and.returnValue(testId);
|
||||
|
||||
capability = new TransactionalPersistenceCapability(
|
||||
mockQ,
|
||||
mockTransactionManager,
|
||||
mockPersistence,
|
||||
mockDomainObject
|
||||
);
|
||||
});
|
||||
|
||||
it("if no transaction is active, passes through to persistence"
|
||||
+ " provider", function () {
|
||||
mockTransactionManager.isActive.and.returnValue(false);
|
||||
capability.persist();
|
||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("if transaction is active, persist and cancel calls are"
|
||||
+ " queued", function () {
|
||||
mockTransactionManager.isActive.and.returnValue(true);
|
||||
capability.persist();
|
||||
expect(mockTransactionManager.addToTransaction).toHaveBeenCalled();
|
||||
mockTransactionManager.addToTransaction.calls.mostRecent().args[1]();
|
||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||
mockTransactionManager.addToTransaction.calls.mostRecent().args[2]();
|
||||
expect(mockPersistence.refresh).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("wraps getSpace", function () {
|
||||
mockPersistence.getSpace.and.returnValue('foo');
|
||||
expect(capability.getSpace()).toEqual('foo');
|
||||
});
|
||||
|
||||
it("clears transactions and delegates refresh calls", function () {
|
||||
capability.refresh();
|
||||
expect(mockTransactionManager.clearTransactionsFor)
|
||||
.toHaveBeenCalledWith(testId);
|
||||
expect(mockPersistence.refresh)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -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/services/NestedTransaction"], function (NestedTransaction) {
|
||||
var TRANSACTION_METHODS = ['add', 'commit', 'cancel', 'size'];
|
||||
|
||||
describe("A NestedTransaction", function () {
|
||||
var mockTransaction,
|
||||
nestedTransaction;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTransaction =
|
||||
jasmine.createSpyObj('transaction', TRANSACTION_METHODS);
|
||||
nestedTransaction = new NestedTransaction(mockTransaction);
|
||||
});
|
||||
|
||||
it("exposes a Transaction's interface", function () {
|
||||
TRANSACTION_METHODS.forEach(function (method) {
|
||||
expect(nestedTransaction[method])
|
||||
.toEqual(jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe("when callbacks are added", function () {
|
||||
var mockCommit,
|
||||
mockCancel;
|
||||
|
||||
beforeEach(function () {
|
||||
mockCommit = jasmine.createSpy('commit');
|
||||
mockCancel = jasmine.createSpy('cancel');
|
||||
nestedTransaction.add(mockCommit, mockCancel);
|
||||
});
|
||||
|
||||
it("does not interact with its parent transaction", function () {
|
||||
TRANSACTION_METHODS.forEach(function (method) {
|
||||
expect(mockTransaction[method])
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and the transaction is committed", function () {
|
||||
beforeEach(function () {
|
||||
nestedTransaction.commit();
|
||||
});
|
||||
|
||||
it("adds to its parent transaction", function () {
|
||||
expect(mockTransaction.add).toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/services/TransactionManager"],
|
||||
function (TransactionManager) {
|
||||
describe("TransactionManager", function () {
|
||||
var mockTransactionService,
|
||||
testId,
|
||||
mockOnCommit,
|
||||
mockOnCancel,
|
||||
mockRemoves,
|
||||
mockPromise,
|
||||
manager;
|
||||
|
||||
beforeEach(function () {
|
||||
mockRemoves = [];
|
||||
mockTransactionService = jasmine.createSpyObj(
|
||||
"transactionService",
|
||||
["addToTransaction", "isActive"]
|
||||
);
|
||||
mockOnCommit = jasmine.createSpy('commit');
|
||||
mockOnCancel = jasmine.createSpy('cancel');
|
||||
testId = 'test-id';
|
||||
mockPromise = jasmine.createSpyObj('promise', ['then']);
|
||||
|
||||
mockOnCommit.and.returnValue(mockPromise);
|
||||
mockOnCancel.and.returnValue(mockPromise);
|
||||
|
||||
mockTransactionService.addToTransaction.and.callFake(function () {
|
||||
var mockRemove =
|
||||
jasmine.createSpy('remove-' + mockRemoves.length);
|
||||
mockRemoves.push(mockRemove);
|
||||
|
||||
return mockRemove;
|
||||
});
|
||||
|
||||
manager = new TransactionManager(mockTransactionService);
|
||||
});
|
||||
|
||||
it("delegates isActive calls", function () {
|
||||
[false, true].forEach(function (state) {
|
||||
mockTransactionService.isActive.and.returnValue(state);
|
||||
expect(manager.isActive()).toBe(state);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when addToTransaction is called", function () {
|
||||
beforeEach(function () {
|
||||
manager.addToTransaction(
|
||||
testId,
|
||||
mockOnCommit,
|
||||
mockOnCancel
|
||||
);
|
||||
});
|
||||
|
||||
it("adds callbacks to the active transaction", function () {
|
||||
expect(mockTransactionService.addToTransaction)
|
||||
.toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("invokes passed-in callbacks from its own callbacks", function () {
|
||||
expect(mockOnCommit).not.toHaveBeenCalled();
|
||||
mockTransactionService.addToTransaction
|
||||
.calls.mostRecent().args[0]();
|
||||
expect(mockOnCommit).toHaveBeenCalled();
|
||||
|
||||
expect(mockOnCancel).not.toHaveBeenCalled();
|
||||
mockTransactionService.addToTransaction
|
||||
.calls.mostRecent().args[1]();
|
||||
expect(mockOnCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("Adds callbacks to transaction", function () {
|
||||
beforeEach(function () {
|
||||
spyOn(manager, 'clearTransactionsFor');
|
||||
manager.clearTransactionsFor.and.callThrough();
|
||||
});
|
||||
|
||||
it("and clears pending calls if same object", function () {
|
||||
manager.addToTransaction(
|
||||
testId,
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(manager.clearTransactionsFor).toHaveBeenCalledWith(testId);
|
||||
});
|
||||
|
||||
it("and does not clear pending calls if different object", function () {
|
||||
manager.addToTransaction(
|
||||
'other-id',
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(manager.clearTransactionsFor).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect(mockTransactionService.addToTransaction.calls.count()).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not remove callbacks from the transaction", function () {
|
||||
expect(mockRemoves[0]).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("and clearTransactionsFor is subsequently called", function () {
|
||||
beforeEach(function () {
|
||||
manager.clearTransactionsFor(testId);
|
||||
});
|
||||
|
||||
it("removes callbacks from the transaction", function () {
|
||||
expect(mockRemoves[0]).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,139 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/services/TransactionService"],
|
||||
function (TransactionService) {
|
||||
|
||||
describe("The Transaction Service", function () {
|
||||
var mockQ,
|
||||
mockLog,
|
||||
mockCacheService,
|
||||
transactionService;
|
||||
|
||||
function fastPromise(val) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return fastPromise(callback(val));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockQ = jasmine.createSpyObj("$q", ["all"]);
|
||||
mockCacheService = jasmine.createSpyObj("cacheService", ["flush"]);
|
||||
mockQ.all.and.returnValue(fastPromise());
|
||||
mockLog = jasmine.createSpyObj("$log", ["error"]);
|
||||
transactionService = new TransactionService(mockQ, mockLog, mockCacheService);
|
||||
});
|
||||
|
||||
it("isActive returns true if a transaction is in progress", function () {
|
||||
expect(transactionService.isActive()).toBe(false);
|
||||
transactionService.startTransaction();
|
||||
expect(transactionService.isActive()).toBe(true);
|
||||
});
|
||||
|
||||
it("addToTransaction queues onCommit and onCancel functions", function () {
|
||||
var onCommit = jasmine.createSpy('onCommit'),
|
||||
onCancel = jasmine.createSpy('onCancel');
|
||||
|
||||
transactionService.startTransaction();
|
||||
transactionService.addToTransaction(onCommit, onCancel);
|
||||
expect(transactionService.size()).toBe(1);
|
||||
});
|
||||
|
||||
it("size function returns size of commit and cancel queues", function () {
|
||||
var onCommit = jasmine.createSpy('onCommit'),
|
||||
onCancel = jasmine.createSpy('onCancel');
|
||||
|
||||
transactionService.startTransaction();
|
||||
transactionService.addToTransaction(onCommit, onCancel);
|
||||
transactionService.addToTransaction(onCommit, onCancel);
|
||||
transactionService.addToTransaction(onCommit, onCancel);
|
||||
expect(transactionService.size()).toBe(3);
|
||||
});
|
||||
|
||||
describe("commit", function () {
|
||||
var onCommits;
|
||||
|
||||
beforeEach(function () {
|
||||
onCommits = [0, 1, 2].map(function (val) {
|
||||
return jasmine.createSpy("onCommit" + val);
|
||||
});
|
||||
|
||||
transactionService.startTransaction();
|
||||
onCommits.forEach(transactionService.addToTransaction.bind(transactionService));
|
||||
});
|
||||
|
||||
it("commit calls all queued commit functions", function () {
|
||||
expect(transactionService.size()).toBe(3);
|
||||
|
||||
return transactionService.commit().then(() => {
|
||||
onCommits.forEach(function (spy) {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("commit resets active state and clears queues", function () {
|
||||
return transactionService.commit().then(() => {
|
||||
expect(transactionService.isActive()).toBe(false);
|
||||
expect(transactionService.size()).toBe(0);
|
||||
expect(transactionService.size()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("cancel", function () {
|
||||
var onCancels;
|
||||
|
||||
beforeEach(function () {
|
||||
onCancels = [0, 1, 2].map(function (val) {
|
||||
return jasmine.createSpy("onCancel" + val);
|
||||
});
|
||||
|
||||
transactionService.startTransaction();
|
||||
onCancels.forEach(function (onCancel) {
|
||||
transactionService.addToTransaction(undefined, onCancel);
|
||||
});
|
||||
});
|
||||
|
||||
it("cancel calls all queued cancel functions", function () {
|
||||
expect(transactionService.size()).toBe(3);
|
||||
transactionService.cancel();
|
||||
onCancels.forEach(function (spy) {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("cancel resets active state and clears queues", function () {
|
||||
transactionService.cancel();
|
||||
expect(transactionService.isActive()).toBe(false);
|
||||
expect(transactionService.size()).toBe(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -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/services/Transaction"],
|
||||
function (Transaction) {
|
||||
|
||||
describe("A Transaction", function () {
|
||||
var mockLog,
|
||||
transaction;
|
||||
|
||||
beforeEach(function () {
|
||||
mockLog = jasmine.createSpyObj(
|
||||
'$log',
|
||||
['warn', 'info', 'error', 'debug']
|
||||
);
|
||||
transaction = new Transaction(mockLog);
|
||||
});
|
||||
|
||||
it("initially has a size of zero", function () {
|
||||
expect(transaction.size()).toEqual(0);
|
||||
});
|
||||
|
||||
describe("when callbacks are added", function () {
|
||||
var mockCommit,
|
||||
mockCancel,
|
||||
remove;
|
||||
|
||||
beforeEach(function () {
|
||||
mockCommit = jasmine.createSpy('commit');
|
||||
mockCancel = jasmine.createSpy('cancel');
|
||||
remove = transaction.add(mockCommit, mockCancel);
|
||||
});
|
||||
|
||||
it("reports a new size", function () {
|
||||
expect(transaction.size()).toEqual(1);
|
||||
});
|
||||
|
||||
it("returns a function to remove those callbacks", function () {
|
||||
expect(remove).toEqual(jasmine.any(Function));
|
||||
remove();
|
||||
expect(transaction.size()).toEqual(0);
|
||||
});
|
||||
|
||||
describe("and the transaction is committed", function () {
|
||||
beforeEach(function () {
|
||||
transaction.commit();
|
||||
});
|
||||
|
||||
it("triggers the commit callback", function () {
|
||||
expect(mockCommit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not trigger the cancel callback", function () {
|
||||
expect(mockCancel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and the transaction is cancelled", function () {
|
||||
beforeEach(function () {
|
||||
transaction.cancel();
|
||||
});
|
||||
|
||||
it("triggers the cancel callback", function () {
|
||||
expect(mockCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not trigger the commit callback", function () {
|
||||
expect(mockCommit).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and an exception is encountered during commit", function () {
|
||||
beforeEach(function () {
|
||||
mockCommit.and.callFake(function () {
|
||||
throw new Error("test error");
|
||||
});
|
||||
transaction.commit();
|
||||
});
|
||||
|
||||
it("logs an error", function () {
|
||||
expect(mockLog.error).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -25,15 +25,14 @@ define([
|
||||
], function (
|
||||
moment
|
||||
) {
|
||||
|
||||
var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS",
|
||||
DATE_FORMATS = [
|
||||
DATE_FORMAT,
|
||||
DATE_FORMAT + "Z",
|
||||
"YYYY-MM-DD HH:mm:ss",
|
||||
"YYYY-MM-DD HH:mm",
|
||||
"YYYY-MM-DD"
|
||||
];
|
||||
const DATE_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS";
|
||||
const DATE_FORMATS = [
|
||||
DATE_FORMAT,
|
||||
DATE_FORMAT + "Z",
|
||||
"YYYY-MM-DD HH:mm:ss",
|
||||
"YYYY-MM-DD HH:mm",
|
||||
"YYYY-MM-DD"
|
||||
];
|
||||
|
||||
/**
|
||||
* @typedef Scale
|
||||
@@ -53,15 +52,27 @@ define([
|
||||
this.key = "utc";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} formatString
|
||||
* @returns the value of formatString if the value is a string type and exists in the DATE_FORMATS array; otherwise the DATE_FORMAT value.
|
||||
*/
|
||||
function validateFormatString(formatString) {
|
||||
return typeof formatString === 'string' && DATE_FORMATS.includes(formatString) ? formatString : DATE_FORMAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} value The value to format.
|
||||
* @returns {string} the formatted date(s). If multiple values were requested, then an array of
|
||||
* @param {string} formatString The string format to format. Default "YYYY-MM-DD HH:mm:ss.SSS" + "Z"
|
||||
* @returns {string} the formatted date(s) according to the proper parameter of formatString or the default value of "YYYY-MM-DD HH:mm:ss.SSS" + "Z".
|
||||
* If multiple values were requested, then an array of
|
||||
* formatted values will be returned. Where a value could not be formatted, `undefined` will be returned at its position
|
||||
* in the array.
|
||||
*/
|
||||
UTCTimeFormat.prototype.format = function (value) {
|
||||
UTCTimeFormat.prototype.format = function (value, formatString) {
|
||||
if (value !== undefined) {
|
||||
return moment.utc(value).format(DATE_FORMAT) + "Z";
|
||||
const format = validateFormatString(formatString);
|
||||
|
||||
return moment.utc(value).format(format) + (formatString ? '' : 'Z');
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -28,7 +28,6 @@ define([
|
||||
"./src/models/ModelCacheService",
|
||||
"./src/models/PersistedModelProvider",
|
||||
"./src/models/CachingModelDecorator",
|
||||
"./src/models/MissingModelDecorator",
|
||||
"./src/types/TypeProvider",
|
||||
"./src/actions/ActionProvider",
|
||||
"./src/actions/ActionAggregator",
|
||||
@@ -45,7 +44,6 @@ define([
|
||||
"./src/capabilities/MutationCapability",
|
||||
"./src/capabilities/DelegationCapability",
|
||||
"./src/capabilities/InstantiationCapability",
|
||||
"./src/runs/TransactingMutationListener",
|
||||
"./src/services/Now",
|
||||
"./src/services/Throttle",
|
||||
"./src/services/Topic",
|
||||
@@ -58,7 +56,6 @@ define([
|
||||
ModelCacheService,
|
||||
PersistedModelProvider,
|
||||
CachingModelDecorator,
|
||||
MissingModelDecorator,
|
||||
TypeProvider,
|
||||
ActionProvider,
|
||||
ActionAggregator,
|
||||
@@ -75,7 +72,6 @@ define([
|
||||
MutationCapability,
|
||||
DelegationCapability,
|
||||
InstantiationCapability,
|
||||
TransactingMutationListener,
|
||||
Now,
|
||||
Throttle,
|
||||
Topic,
|
||||
@@ -150,12 +146,6 @@ define([
|
||||
"cacheService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"provides": "modelService",
|
||||
"type": "decorator",
|
||||
"priority": "fallback",
|
||||
"implementation": MissingModelDecorator
|
||||
},
|
||||
{
|
||||
"provides": "typeService",
|
||||
"type": "provider",
|
||||
@@ -363,12 +353,6 @@ define([
|
||||
]
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"implementation": TransactingMutationListener,
|
||||
"depends": ["topic", "transactionService", "cacheService"]
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "PERSISTENCE_SPACE",
|
||||
@@ -379,7 +363,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",
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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 () {
|
||||
|
||||
/**
|
||||
* Listens for mutation on domain objects and triggers persistence when
|
||||
* it occurs.
|
||||
* @param {Topic} topic the `topic` service; used to listen for mutation
|
||||
* @memberof platform/core
|
||||
*/
|
||||
function TransactingMutationListener(
|
||||
topic,
|
||||
transactionService,
|
||||
cacheService
|
||||
) {
|
||||
|
||||
function hasChanged(domainObject) {
|
||||
var model = domainObject.getModel();
|
||||
|
||||
return model.persisted === undefined || model.modified > model.persisted;
|
||||
}
|
||||
|
||||
var mutationTopic = topic('mutation');
|
||||
mutationTopic.listen(function (domainObject) {
|
||||
var persistence = domainObject.getCapability('persistence');
|
||||
cacheService.put(domainObject.getId(), domainObject.getModel());
|
||||
|
||||
if (hasChanged(domainObject)) {
|
||||
persistence.persist();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return TransactingMutationListener;
|
||||
});
|
||||
@@ -1,86 +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/models/MissingModelDecorator"],
|
||||
function (MissingModelDecorator) {
|
||||
|
||||
describe("The missing model decorator", function () {
|
||||
var mockModelService,
|
||||
testModels,
|
||||
decorator;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockModelService = jasmine.createSpyObj(
|
||||
"modelService",
|
||||
["getModels"]
|
||||
);
|
||||
|
||||
testModels = {
|
||||
testId: { someKey: "some value" }
|
||||
};
|
||||
|
||||
mockModelService.getModels.and.returnValue(asPromise(testModels));
|
||||
|
||||
decorator = new MissingModelDecorator(mockModelService);
|
||||
});
|
||||
|
||||
it("delegates to the wrapped model service", function () {
|
||||
decorator.getModels(['a', 'b', 'c']);
|
||||
expect(mockModelService.getModels)
|
||||
.toHaveBeenCalledWith(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it("provides models for any IDs which are missing", function () {
|
||||
var models;
|
||||
decorator.getModels(['testId', 'otherId'])
|
||||
.then(function (m) {
|
||||
models = m;
|
||||
});
|
||||
expect(models.otherId).toBeDefined();
|
||||
});
|
||||
|
||||
it("does not overwrite existing models", function () {
|
||||
var models;
|
||||
decorator.getModels(['testId', 'otherId'])
|
||||
.then(function (m) {
|
||||
models = m;
|
||||
});
|
||||
expect(models.testId).toEqual({ someKey: "some value" });
|
||||
});
|
||||
|
||||
it("does not modify the wrapped service's response", function () {
|
||||
decorator.getModels(['testId', 'otherId']);
|
||||
expect(testModels.otherId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
@@ -1,112 +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/runs/TransactingMutationListener"],
|
||||
function (TransactingMutationListener) {
|
||||
|
||||
describe("TransactingMutationListener", function () {
|
||||
var mockTopic,
|
||||
mockMutationTopic,
|
||||
mockCacheService,
|
||||
mockTransactionService,
|
||||
mockDomainObject,
|
||||
mockModel,
|
||||
mockPersistence;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTopic = jasmine.createSpy('topic');
|
||||
mockMutationTopic =
|
||||
jasmine.createSpyObj('mutation', ['listen']);
|
||||
mockCacheService =
|
||||
jasmine.createSpyObj('cacheService', [
|
||||
'put'
|
||||
]);
|
||||
mockTransactionService =
|
||||
jasmine.createSpyObj('transactionService', [
|
||||
'isActive',
|
||||
'startTransaction',
|
||||
'commit'
|
||||
]);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getId', 'getCapability', 'getModel']
|
||||
);
|
||||
mockPersistence = jasmine.createSpyObj(
|
||||
'persistence',
|
||||
['persist', 'refresh', 'persisted']
|
||||
);
|
||||
|
||||
mockTopic.and.callFake(function (t) {
|
||||
expect(t).toBe('mutation');
|
||||
|
||||
return mockMutationTopic;
|
||||
});
|
||||
|
||||
mockDomainObject.getId.and.returnValue('mockId');
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
expect(c).toBe('persistence');
|
||||
|
||||
return mockPersistence;
|
||||
});
|
||||
mockModel = {};
|
||||
mockDomainObject.getModel.and.returnValue(mockModel);
|
||||
|
||||
mockPersistence.persisted.and.returnValue(true);
|
||||
|
||||
return new TransactingMutationListener(
|
||||
mockTopic,
|
||||
mockTransactionService,
|
||||
mockCacheService
|
||||
);
|
||||
});
|
||||
|
||||
it("listens for mutation", function () {
|
||||
expect(mockMutationTopic.listen)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("calls persist if the model has changed", function () {
|
||||
mockModel.persisted = Date.now();
|
||||
|
||||
//Mark the model dirty by setting the mutated date later than the last persisted date.
|
||||
mockModel.modified = mockModel.persisted + 1;
|
||||
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
|
||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not call persist if the model has not changed", function () {
|
||||
mockModel.persisted = Date.now();
|
||||
|
||||
mockModel.modified = mockModel.persisted;
|
||||
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
|
||||
expect(mockPersistence.persist).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,305 +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([
|
||||
"moment-timezone",
|
||||
"./src/indicators/ClockIndicator",
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/ClockController",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
"./src/actions/PauseTimerAction",
|
||||
"./res/templates/clock.html",
|
||||
"./res/templates/timer.html"
|
||||
], function (
|
||||
MomentTimezone,
|
||||
ClockIndicator,
|
||||
TickerService,
|
||||
TimerService,
|
||||
ClockController,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
PauseTimerAction,
|
||||
clockTemplate,
|
||||
timerTemplate
|
||||
) {
|
||||
return {
|
||||
name: "platform/features/clock",
|
||||
definition: {
|
||||
"name": "Clocks/Timers",
|
||||
"descriptions": "Domain objects for displaying current & relative times.",
|
||||
"configuration": {
|
||||
"paths": {
|
||||
"moment-duration-format": "moment-duration-format"
|
||||
},
|
||||
"shim": {
|
||||
"moment-duration-format": {
|
||||
"deps": [
|
||||
"moment"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"constants": [
|
||||
{
|
||||
"key": "CLOCK_INDICATOR_FORMAT",
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "timerService",
|
||||
"implementation": TimerService,
|
||||
"depends": ["openmct"]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "ClockController",
|
||||
"implementation": ClockController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"tickerService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "TimerController",
|
||||
"implementation": TimerController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$window",
|
||||
"now"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "RefreshingController",
|
||||
"implementation": RefreshingController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"tickerService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [
|
||||
{
|
||||
"key": "clock",
|
||||
"type": "clock",
|
||||
"editable": false,
|
||||
"template": clockTemplate
|
||||
},
|
||||
{
|
||||
"key": "timer",
|
||||
"type": "timer",
|
||||
"editable": false,
|
||||
"template": timerTemplate
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Start",
|
||||
"cssClass": "icon-play",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"key": "timer.pause",
|
||||
"implementation": PauseTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Pause",
|
||||
"cssClass": "icon-pause",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"key": "timer.restart",
|
||||
"implementation": RestartTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Restart at 0",
|
||||
"cssClass": "icon-refresh",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"key": "timer.stop",
|
||||
"implementation": StopTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Stop",
|
||||
"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",
|
||||
"cssClass": "icon-timer",
|
||||
"description": "A timer that counts up or down to a datetime. Timers can be started, stopped and reset whenever needed, and support a variety of display formats. Each Timer displays the same value to all users. Timers can be added to Display Layouts.",
|
||||
"priority": 100,
|
||||
"features": [
|
||||
"creation"
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"key": "timestamp",
|
||||
"control": "datetime",
|
||||
"name": "Target"
|
||||
},
|
||||
{
|
||||
"key": "timerFormat",
|
||||
"control": "select",
|
||||
"name": "Display Format",
|
||||
"options": [
|
||||
{
|
||||
"value": "long",
|
||||
"name": "DDD hh:mm:ss"
|
||||
},
|
||||
{
|
||||
"value": "short",
|
||||
"name": "hh:mm:ss"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model": {
|
||||
"timerFormat": "DDD hh:mm:ss"
|
||||
}
|
||||
}
|
||||
],
|
||||
"runs": [],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "moment-duration-format",
|
||||
"version": "1.3.0",
|
||||
"author": "John Madhavan-Reese",
|
||||
"description": "Duration parsing/formatting",
|
||||
"website": "https://github.com/jsmreese/moment-duration-format",
|
||||
"copyright": "Copyright 2014 John Madhavan-Reese",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/jsmreese/moment-duration-format/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,32 +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.
|
||||
-->
|
||||
<div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
|
||||
<div class="c-clock__timezone">
|
||||
{{clock.zone()}}
|
||||
</div>
|
||||
<div class="c-clock__value">
|
||||
{{clock.text()}}
|
||||
</div>
|
||||
<div class="c-clock__ampm">
|
||||
{{clock.ampm()}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,37 +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.
|
||||
-->
|
||||
<div class="c-timer u-style-receiver js-style-receiver is-{{timer.timerState}}" ng-controller="TimerController as timer">
|
||||
<div class="c-timer__controls">
|
||||
<button ng-click="timer.clickStopButton()"
|
||||
ng-hide="timer.timerState == 'stopped'"
|
||||
title="Reset"
|
||||
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button>
|
||||
<button ng-click="timer.clickButton()"
|
||||
title="{{timer.buttonText()}}"
|
||||
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button>
|
||||
</div>
|
||||
<div class="c-timer__direction {{timer.signClass()}}"
|
||||
ng-hide="!timer.signClass()"></div>
|
||||
<div class="c-timer__value">{{timer.text() || "--:--:--"}}
|
||||
</div>
|
||||
<span class="c-timer__ng-controller u-contents" ng-controller="RefreshingController"></span>
|
||||
</div>
|
||||
@@ -1,70 +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 () {
|
||||
|
||||
/**
|
||||
* Implements the "Pause" action for timers.
|
||||
*
|
||||
* Sets the reference pausedTime in a timer to the current
|
||||
* time, such that it stops counting up.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function PauseTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
PauseTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which have
|
||||
// a target time, or is in a playing state.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState === 'started';
|
||||
};
|
||||
|
||||
PauseTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now;
|
||||
|
||||
function updateModel(model) {
|
||||
model.timerState = 'paused';
|
||||
model.pausedTime = now();
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return PauseTimerAction;
|
||||
}
|
||||
);
|
||||
@@ -1,70 +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 () {
|
||||
|
||||
/**
|
||||
* Implements the "Restart at 0" action.
|
||||
*
|
||||
* Behaves the same as (and delegates functionality to)
|
||||
* the "Start" action.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function RestartTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
RestartTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which already have a target time.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState !== 'stopped';
|
||||
};
|
||||
|
||||
RestartTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now;
|
||||
|
||||
function updateModel(model) {
|
||||
model.timestamp = now();
|
||||
model.timerState = 'started';
|
||||
model.pausedTime = undefined;
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return RestartTimerAction;
|
||||
}
|
||||
);
|
||||
@@ -1,78 +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 () {
|
||||
|
||||
/**
|
||||
* Implements the "Start" action for timers.
|
||||
*
|
||||
* Sets the reference timestamp in a timer to the current
|
||||
* time, such that it begins counting up.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function StartTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
StartTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which do not yet have
|
||||
// a target time.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState !== 'started';
|
||||
};
|
||||
|
||||
StartTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now;
|
||||
|
||||
function updateModel(model) {
|
||||
//if we are resuming
|
||||
if (model.pausedTime) {
|
||||
var timeShift = now() - model.pausedTime;
|
||||
model.timestamp = model.timestamp + timeShift;
|
||||
} else {
|
||||
model.timestamp = now();
|
||||
}
|
||||
|
||||
model.timerState = 'started';
|
||||
model.pausedTime = undefined;
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return StartTimerAction;
|
||||
}
|
||||
);
|
||||
@@ -1,70 +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 () {
|
||||
|
||||
/**
|
||||
* Implements the "Stop" action for timers.
|
||||
*
|
||||
* Sets the reference timestamp in a timer undefined,
|
||||
* such that it is reset and makes no movements.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function StopTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
StopTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which do not yet have
|
||||
// a target time.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState !== 'stopped';
|
||||
};
|
||||
|
||||
StopTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject;
|
||||
|
||||
function updateModel(model) {
|
||||
model.timestamp = undefined;
|
||||
model.timerState = 'stopped';
|
||||
model.pausedTime = undefined;
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return StopTimerAction;
|
||||
}
|
||||
);
|
||||
@@ -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,55 +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 () {
|
||||
|
||||
/**
|
||||
* Continually refreshes the represented domain object.
|
||||
*
|
||||
* This is a short-term workaround to assure Timer views stay
|
||||
* up-to-date; should be replaced by a global auto-refresh.
|
||||
*
|
||||
* @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 RefreshingController($scope, tickerService) {
|
||||
var unlisten;
|
||||
|
||||
function triggerRefresh() {
|
||||
var persistence = $scope.domainObject
|
||||
&& $scope.domainObject.getCapability('persistence');
|
||||
|
||||
return persistence && persistence.refresh();
|
||||
}
|
||||
|
||||
unlisten = tickerService.listen(triggerRefresh);
|
||||
$scope.$on('$destroy', unlisten);
|
||||
}
|
||||
|
||||
return RefreshingController;
|
||||
}
|
||||
);
|
||||
@@ -1,239 +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(
|
||||
['./TimerFormatter'],
|
||||
function (TimerFormatter) {
|
||||
|
||||
var FORMATTER = new TimerFormatter();
|
||||
|
||||
/**
|
||||
* Controller for views of a Timer domain object.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
* @param {angular.Scope} $scope the Angular scope
|
||||
* @param $window Angular-provided window object
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
*/
|
||||
function TimerController($scope, $window, now) {
|
||||
var formatter,
|
||||
active = true,
|
||||
relativeTimestamp,
|
||||
lastTimestamp,
|
||||
relativeTimerState,
|
||||
self = this;
|
||||
|
||||
function update() {
|
||||
var timeDelta = lastTimestamp - relativeTimestamp;
|
||||
|
||||
if (formatter && !isNaN(timeDelta)) {
|
||||
self.textValue = formatter(timeDelta);
|
||||
self.signValue = timeDelta < 0 ? "-"
|
||||
: timeDelta >= 1000 ? "+" : "";
|
||||
self.signCssClass = timeDelta < 0 ? "icon-minus"
|
||||
: timeDelta >= 1000 ? "icon-plus" : "";
|
||||
} else {
|
||||
self.textValue = "";
|
||||
self.signValue = "";
|
||||
self.signCssClass = "";
|
||||
}
|
||||
}
|
||||
|
||||
function updateFormat(key) {
|
||||
formatter = FORMATTER[key] || FORMATTER.long;
|
||||
}
|
||||
|
||||
function updateTimestamp(timestamp) {
|
||||
relativeTimestamp = timestamp;
|
||||
}
|
||||
|
||||
function updateTimerState(timerState) {
|
||||
self.timerState = relativeTimerState = timerState;
|
||||
}
|
||||
|
||||
function updateActions(actionCapability, actionKey) {
|
||||
self.relevantAction = actionCapability
|
||||
&& actionCapability.getActions(actionKey)[0];
|
||||
|
||||
self.stopAction = relativeTimerState !== 'stopped'
|
||||
? actionCapability && actionCapability.getActions('timer.stop')[0] : undefined;
|
||||
|
||||
}
|
||||
|
||||
function isPaused() {
|
||||
return relativeTimerState === 'paused';
|
||||
}
|
||||
|
||||
function handleLegacyTimer(model) {
|
||||
if (model.timerState === undefined) {
|
||||
model.timerState = model.timestamp === undefined
|
||||
? 'stopped' : 'started';
|
||||
}
|
||||
}
|
||||
|
||||
function updateObject(domainObject) {
|
||||
var model = domainObject.getModel();
|
||||
handleLegacyTimer(model);
|
||||
|
||||
var timestamp = model.timestamp,
|
||||
formatKey = model.timerFormat,
|
||||
timerState = model.timerState,
|
||||
actionCapability = domainObject.getCapability('action'),
|
||||
actionKey = (timerState !== 'started')
|
||||
? 'timer.start' : 'timer.pause';
|
||||
|
||||
updateFormat(formatKey);
|
||||
updateTimestamp(timestamp);
|
||||
updateTimerState(timerState);
|
||||
updateActions(actionCapability, actionKey);
|
||||
|
||||
//if paused on startup show last known position
|
||||
if (isPaused() && !lastTimestamp) {
|
||||
lastTimestamp = model.pausedTime;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
function handleObjectChange(domainObject) {
|
||||
if (domainObject) {
|
||||
updateObject(domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
function handleModification() {
|
||||
handleObjectChange($scope.domainObject);
|
||||
}
|
||||
|
||||
function tick() {
|
||||
var lastSign = self.signValue,
|
||||
lastText = self.textValue;
|
||||
|
||||
if (!isPaused()) {
|
||||
lastTimestamp = now();
|
||||
update();
|
||||
}
|
||||
|
||||
if (relativeTimerState === undefined) {
|
||||
handleModification();
|
||||
}
|
||||
|
||||
// We're running in an animation frame, not in a digest cycle.
|
||||
// We need to trigger a digest cycle if our displayable data
|
||||
// changes.
|
||||
if (lastSign !== self.signValue || lastText !== self.textValue) {
|
||||
$scope.$apply();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
$window.requestAnimationFrame(tick);
|
||||
}
|
||||
}
|
||||
|
||||
$window.requestAnimationFrame(tick);
|
||||
|
||||
// Pull in the timer format from the domain object model
|
||||
$scope.$watch('domainObject', handleObjectChange);
|
||||
$scope.$watch('model.modified', handleModification);
|
||||
|
||||
// When the scope is destroyed, stop requesting anim. frames
|
||||
$scope.$on('$destroy', function () {
|
||||
active = false;
|
||||
});
|
||||
|
||||
this.$scope = $scope;
|
||||
this.signValue = '';
|
||||
this.textValue = '';
|
||||
this.updateObject = updateObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS class to display the right icon
|
||||
* for the start/pause button.
|
||||
* @returns {string} cssclass to display
|
||||
*/
|
||||
TimerController.prototype.buttonCssClass = function () {
|
||||
return this.relevantAction
|
||||
? this.relevantAction.getMetadata().cssClass : "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to show for the start/pause button
|
||||
* (e.g. in a tooltip)
|
||||
* @returns {string} name of the action
|
||||
*/
|
||||
TimerController.prototype.buttonText = function () {
|
||||
return this.relevantAction
|
||||
? this.relevantAction.getMetadata().name : "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform the action associated with the start/pause button.
|
||||
*/
|
||||
TimerController.prototype.clickButton = function () {
|
||||
if (this.relevantAction) {
|
||||
this.relevantAction.perform();
|
||||
this.updateObject(this.$scope.domainObject);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform the action associated with the stop button.
|
||||
*/
|
||||
TimerController.prototype.clickStopButton = function () {
|
||||
if (this.stopAction) {
|
||||
this.stopAction.perform();
|
||||
this.updateObject(this.$scope.domainObject);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the sign (+ or -) of the current timer value, as
|
||||
* displayable text.
|
||||
* @returns {string} sign of the current timer value
|
||||
*/
|
||||
TimerController.prototype.sign = function () {
|
||||
return this.signValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the sign (+ or -) of the current timer value, as
|
||||
* a CSS class.
|
||||
* @returns {string} sign of the current timer value
|
||||
*/
|
||||
TimerController.prototype.signClass = function () {
|
||||
return this.signCssClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to display for the current timer value.
|
||||
* @returns {string} current timer value
|
||||
*/
|
||||
TimerController.prototype.text = function () {
|
||||
return this.textValue;
|
||||
};
|
||||
|
||||
return TimerController;
|
||||
}
|
||||
);
|
||||
@@ -1,73 +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-duration-format'],
|
||||
function (moment) {
|
||||
|
||||
var SHORT_FORMAT = "HH:mm:ss",
|
||||
LONG_FORMAT = "d[D] HH:mm:ss";
|
||||
|
||||
/**
|
||||
* Provides formatting functions for Timers.
|
||||
*
|
||||
* Display formats for timers are a little different from what
|
||||
* moment.js provides, so we have custom logic here. This specifically
|
||||
* supports `TimerController`.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
*/
|
||||
function TimerFormatter() {
|
||||
}
|
||||
|
||||
// Round this timestamp down to the second boundary
|
||||
// (e.g. 1124ms goes down to 1000ms, -2400ms goes down to -3000ms)
|
||||
function toWholeSeconds(duration) {
|
||||
return Math.abs(Math.floor(duration / 1000) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a duration for display, using the short form.
|
||||
* (e.g. 03:33:11)
|
||||
* @param {number} duration the duration, in milliseconds
|
||||
* @param {boolean} sign true if positive
|
||||
*/
|
||||
TimerFormatter.prototype.short = function (duration) {
|
||||
return moment.duration(toWholeSeconds(duration), 'ms')
|
||||
.format(SHORT_FORMAT, { trim: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a duration for display, using the long form.
|
||||
* (e.g. 0d 03:33:11)
|
||||
* @param {number} duration the duration, in milliseconds
|
||||
* @param {boolean} sign true if positive
|
||||
*/
|
||||
TimerFormatter.prototype.long = function (duration) {
|
||||
return moment.duration(toWholeSeconds(duration), 'ms')
|
||||
.format(LONG_FORMAT, { trim: false });
|
||||
};
|
||||
|
||||
return TimerFormatter;
|
||||
}
|
||||
);
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
@@ -1,87 +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 () {
|
||||
|
||||
/**
|
||||
* Calls functions every second, as close to the actual second
|
||||
* tick as is feasible.
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
* @param $timeout Angular's $timeout
|
||||
* @param {Function} now function to provide the current time in ms
|
||||
*/
|
||||
function TickerService($timeout, now) {
|
||||
var self = this;
|
||||
|
||||
function tick() {
|
||||
var timestamp = now(),
|
||||
millis = timestamp % 1000;
|
||||
|
||||
// Only update callbacks if a second has actually passed.
|
||||
if (timestamp >= self.last + 1000) {
|
||||
self.callbacks.forEach(function (callback) {
|
||||
callback(timestamp);
|
||||
});
|
||||
self.last = timestamp - millis;
|
||||
}
|
||||
|
||||
// Try to update at exactly the next second
|
||||
$timeout(tick, 1000 - millis, true);
|
||||
}
|
||||
|
||||
tick();
|
||||
|
||||
this.callbacks = [];
|
||||
this.last = now() - 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for clock ticks. The provided callback will
|
||||
* be invoked with the current timestamp (in milliseconds
|
||||
* since Jan 1 1970) at regular intervals, as near to the
|
||||
* second boundary as possible.
|
||||
*
|
||||
* @param {Function} callback callback to invoke
|
||||
* @returns {Function} a function to unregister this listener
|
||||
*/
|
||||
TickerService.prototype.listen = function (callback) {
|
||||
var self = this;
|
||||
|
||||
self.callbacks.push(callback);
|
||||
|
||||
// Provide immediate feedback
|
||||
callback(this.last);
|
||||
|
||||
// Provide a deregistration function
|
||||
return function () {
|
||||
self.callbacks = self.callbacks.filter(function (cb) {
|
||||
return cb !== callback;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return TickerService;
|
||||
}
|
||||
);
|
||||
@@ -1,113 +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(['EventEmitter'], function (EventEmitter) {
|
||||
|
||||
/**
|
||||
* Tracks the currently-followed Timer object. Used by
|
||||
* timelines et al to synchronize to a particular timer.
|
||||
*
|
||||
* The TimerService emits `change` events when the active timer
|
||||
* is changed.
|
||||
*/
|
||||
function TimerService(openmct) {
|
||||
EventEmitter.apply(this);
|
||||
this.time = openmct.time;
|
||||
this.objects = openmct.objects;
|
||||
}
|
||||
|
||||
TimerService.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Set (or clear, if `timer` is undefined) the currently active timer.
|
||||
* @param {DomainObject} timer the new active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.setTimer = function (timer) {
|
||||
this.timer = timer;
|
||||
this.emit('change', timer);
|
||||
|
||||
if (this.stopObserving) {
|
||||
this.stopObserving();
|
||||
delete this.stopObserving;
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
this.stopObserving =
|
||||
this.objects.observe(timer, '*', this.setTimer.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently active timer.
|
||||
* @return {DomainObject} the active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.getTimer = function () {
|
||||
return this.timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is a currently active timer.
|
||||
* @return {boolean} true if there is a timer
|
||||
*/
|
||||
TimerService.prototype.hasTimer = function () {
|
||||
return Boolean(this.timer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the provided timestamp to milliseconds relative to
|
||||
* the active timer.
|
||||
* @return {number} milliseconds since timer start
|
||||
*/
|
||||
TimerService.prototype.convert = function (timestamp) {
|
||||
var clock = this.time.clock();
|
||||
var canConvert = this.hasTimer()
|
||||
&& Boolean(clock)
|
||||
&& this.timer.timerState !== 'stopped';
|
||||
|
||||
if (!canConvert) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var now = clock.currentValue();
|
||||
var delta = this.timer.timerState === 'paused'
|
||||
? now - this.timer.pausedTime : 0;
|
||||
var epoch = this.timer.timestamp;
|
||||
|
||||
return timestamp - epoch - delta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the active clock, adjusted to be relative to the active
|
||||
* timer. If there is no clock or no active timer, this will return
|
||||
* `undefined`.
|
||||
* @return {number} milliseconds since the start of the active timer
|
||||
*/
|
||||
TimerService.prototype.now = function () {
|
||||
var clock = this.time.clock();
|
||||
|
||||
return clock && this.convert(clock.currentValue());
|
||||
};
|
||||
|
||||
return TimerService;
|
||||
});
|
||||
@@ -1,106 +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/PauseTimerAction"],
|
||||
function (PauseTimerAction) {
|
||||
|
||||
describe("A timer's Pause action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(PauseTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = {domainObject: mockDomainObject};
|
||||
|
||||
action = new PauseTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = 'started';
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('paused');
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = undefined;
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(12000);
|
||||
});
|
||||
|
||||
it("applies only to timers in a playing state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, false);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, false);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, true);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'started', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,112 +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/RestartTimerAction"],
|
||||
function (RestartTimerAction) {
|
||||
|
||||
describe("A timer's restart action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = { domainObject: mockDomainObject };
|
||||
|
||||
action = new RestartTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timestamp", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.timestamp).toEqual(12000);
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = 'stopped';
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('started');
|
||||
});
|
||||
|
||||
it("applies only to timers in a non-stopped state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, false);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, true);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, true);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'paused', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,111 +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/StartTimerAction"],
|
||||
function (StartTimerAction) {
|
||||
|
||||
describe("A timer's start action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(StartTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = {domainObject: mockDomainObject};
|
||||
|
||||
action = new StartTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timestamp", function () {
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.timestamp).toEqual(12000);
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = undefined;
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('started');
|
||||
});
|
||||
|
||||
it("applies only to timers not in a playing state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, true);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, true);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, false);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'paused', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,111 +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/StopTimerAction"],
|
||||
function (StopTimerAction) {
|
||||
|
||||
describe("A timer's stop action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(StopTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(StopTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = {domainObject: mockDomainObject};
|
||||
|
||||
action = new StopTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timestamp", function () {
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.timestamp).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = 'started';
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('stopped');
|
||||
});
|
||||
|
||||
it("applies only to timers in a non-stopped state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, false);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, true);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, true);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'paused', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,80 +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/controllers/RefreshingController"],
|
||||
function (RefreshingController) {
|
||||
|
||||
describe("The refreshing controller", function () {
|
||||
var mockScope,
|
||||
mockTicker,
|
||||
mockUnticker,
|
||||
controller;
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj('$scope', ['$on']);
|
||||
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
|
||||
mockUnticker = jasmine.createSpy('unticker');
|
||||
|
||||
mockTicker.listen.and.returnValue(mockUnticker);
|
||||
|
||||
controller = new RefreshingController(mockScope, mockTicker);
|
||||
});
|
||||
|
||||
it("refreshes the represented object on every tick", function () {
|
||||
var mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability']
|
||||
),
|
||||
mockPersistence = jasmine.createSpyObj(
|
||||
'persistence',
|
||||
['persist', 'refresh']
|
||||
);
|
||||
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
return (c === 'persistence') && mockPersistence;
|
||||
});
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockTicker.listen.calls.mostRecent().args[0](12321);
|
||||
expect(mockPersistence.refresh).toHaveBeenCalled();
|
||||
expect(mockPersistence.persist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,227 +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/controllers/TimerController"],
|
||||
function (TimerController) {
|
||||
|
||||
// Wed, 03 Jun 2015 17:56:14 GMT
|
||||
var TEST_TIMESTAMP = 1433354174000;
|
||||
|
||||
describe("A timer view's controller", function () {
|
||||
var mockScope,
|
||||
mockWindow,
|
||||
mockNow,
|
||||
mockDomainObject,
|
||||
mockActionCapability,
|
||||
mockStart,
|
||||
mockPause,
|
||||
mockStop,
|
||||
testModel,
|
||||
controller;
|
||||
|
||||
function invokeWatch(expr, value) {
|
||||
mockScope.$watch.calls.all().forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
'$scope',
|
||||
['$watch', '$on', '$apply']
|
||||
);
|
||||
mockWindow = jasmine.createSpyObj(
|
||||
'$window',
|
||||
['requestAnimationFrame']
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
mockActionCapability = jasmine.createSpyObj(
|
||||
'action',
|
||||
['getActions']
|
||||
);
|
||||
mockStart = jasmine.createSpyObj(
|
||||
'start',
|
||||
['getMetadata', 'perform']
|
||||
);
|
||||
mockPause = jasmine.createSpyObj(
|
||||
'paused',
|
||||
['getMetadata', 'perform']
|
||||
);
|
||||
mockStop = jasmine.createSpyObj(
|
||||
'stopped',
|
||||
['getMetadata', 'perform']
|
||||
);
|
||||
mockNow = jasmine.createSpy('now');
|
||||
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
return (c === 'action') && mockActionCapability;
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
mockActionCapability.getActions.and.callFake(function (k) {
|
||||
return [{
|
||||
'timer.start': mockStart,
|
||||
'timer.pause': mockPause,
|
||||
'timer.stop': mockStop
|
||||
}[k]];
|
||||
});
|
||||
|
||||
mockStart.getMetadata.and.returnValue({
|
||||
cssClass: "icon-play",
|
||||
name: "Start"
|
||||
});
|
||||
mockPause.getMetadata.and.returnValue({
|
||||
cssClass: "icon-pause",
|
||||
name: "Pause"
|
||||
});
|
||||
mockStop.getMetadata.and.returnValue({
|
||||
cssClass: "icon-box-round-corners",
|
||||
name: "Stop"
|
||||
});
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
testModel = {};
|
||||
|
||||
controller = new TimerController(mockScope, mockWindow, mockNow);
|
||||
});
|
||||
|
||||
it("watches for the domain object in view", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"domainObject",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("watches for domain object modifications", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"model.modified",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("updates on a timer", function () {
|
||||
expect(mockWindow.requestAnimationFrame)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("displays nothing when there is no target", function () {
|
||||
// Notify that domain object is available via scope
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("");
|
||||
expect(controller.signClass()).toEqual("");
|
||||
expect(controller.text()).toEqual("");
|
||||
});
|
||||
|
||||
it("formats time to display relative to target", function () {
|
||||
testModel.timestamp = TEST_TIMESTAMP;
|
||||
testModel.timerFormat = 'long';
|
||||
// Notify that domain object is available via scope
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP + 121000);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("+");
|
||||
expect(controller.signClass()).toEqual("icon-plus");
|
||||
expect(controller.text()).toEqual("0D 00:02:01");
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP - 121000);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("-");
|
||||
expect(controller.signClass()).toEqual("icon-minus");
|
||||
expect(controller.text()).toEqual("0D 00:02:01");
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("");
|
||||
expect(controller.signClass()).toEqual("");
|
||||
expect(controller.text()).toEqual("0D 00:00:00");
|
||||
});
|
||||
|
||||
it("shows cssClass & name for the applicable start/pause action", function () {
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
expect(controller.buttonCssClass()).toEqual("icon-play");
|
||||
expect(controller.buttonText()).toEqual("Start");
|
||||
|
||||
testModel.timestamp = 12321;
|
||||
testModel.timerState = 'started';
|
||||
invokeWatch('model.modified', 1);
|
||||
expect(controller.buttonCssClass()).toEqual("icon-pause");
|
||||
expect(controller.buttonText()).toEqual("Pause");
|
||||
});
|
||||
|
||||
it("performs correct start/pause/stop action on click", function () {
|
||||
//test start
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
expect(mockStart.perform).not.toHaveBeenCalled();
|
||||
controller.clickButton();
|
||||
expect(mockStart.perform).toHaveBeenCalled();
|
||||
|
||||
//test pause
|
||||
testModel.timestamp = 12321;
|
||||
testModel.timerState = 'started';
|
||||
invokeWatch('model.modified', 1);
|
||||
expect(mockPause.perform).not.toHaveBeenCalled();
|
||||
controller.clickButton();
|
||||
expect(mockPause.perform).toHaveBeenCalled();
|
||||
|
||||
//test stop
|
||||
expect(mockStop.perform).not.toHaveBeenCalled();
|
||||
controller.clickStopButton();
|
||||
expect(mockStop.perform).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("stops requesting animation frames when destroyed", function () {
|
||||
var initialCount = mockWindow.requestAnimationFrame.calls.count();
|
||||
|
||||
// First, check that normally new frames keep getting requested
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 1);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 2);
|
||||
|
||||
// Now, verify that it stops after $destroy
|
||||
expect(mockScope.$on.calls.mostRecent().args[0])
|
||||
.toEqual('$destroy');
|
||||
mockScope.$on.calls.mostRecent().args[1]();
|
||||
|
||||
// Frames should no longer get requested
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 2);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,111 +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/controllers/TimerFormatter"],
|
||||
function (TimerFormatter) {
|
||||
|
||||
var MS_IN_SEC = 1000,
|
||||
MS_IN_MIN = MS_IN_SEC * 60,
|
||||
MS_IN_HR = MS_IN_MIN * 60,
|
||||
MS_IN_DAY = MS_IN_HR * 24;
|
||||
|
||||
describe("The timer value formatter", function () {
|
||||
var formatter = new TimerFormatter();
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function toDuration(days, hours, mins, secs) {
|
||||
return [
|
||||
days * MS_IN_DAY,
|
||||
hours * MS_IN_HR,
|
||||
mins * MS_IN_MIN,
|
||||
secs * MS_IN_SEC
|
||||
].reduce(sum, 0);
|
||||
}
|
||||
|
||||
it("formats short-form values (no days)", function () {
|
||||
expect(formatter.short(toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("123:02:03");
|
||||
});
|
||||
|
||||
it("formats negative short-form values (no days)", function () {
|
||||
expect(formatter.short(-toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("123:02:03");
|
||||
});
|
||||
|
||||
it("formats long-form values (with days)", function () {
|
||||
expect(formatter.long(toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("5D 03:02:03");
|
||||
});
|
||||
|
||||
it("formats negative long-form values (no days)", function () {
|
||||
expect(formatter.long(-toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("5D 03:02:03");
|
||||
});
|
||||
|
||||
it("rounds seconds down for positive durations", function () {
|
||||
expect(formatter.short(MS_IN_SEC + 600))
|
||||
.toEqual("00:00:01");
|
||||
});
|
||||
|
||||
it("rounds seconds up for negative durations", function () {
|
||||
expect(formatter.short(-MS_IN_SEC - 600))
|
||||
.toEqual("00:00:02");
|
||||
});
|
||||
|
||||
it("short-formats correctly around negative time borders", function () {
|
||||
expect(formatter.short(-1)).toEqual("00:00:01");
|
||||
expect(formatter.short(-1000)).toEqual("00:00:01");
|
||||
expect(formatter.short(-1001)).toEqual("00:00:02");
|
||||
expect(formatter.short(-2000)).toEqual("00:00:02");
|
||||
expect(formatter.short(-59001)).toEqual("00:01:00");
|
||||
expect(formatter.short(-60000)).toEqual("00:01:00");
|
||||
|
||||
expect(formatter.short(-MS_IN_HR + 999)).toEqual("01:00:00");
|
||||
expect(formatter.short(-MS_IN_HR)).toEqual("01:00:00");
|
||||
});
|
||||
|
||||
it("differentiates between values around zero", function () {
|
||||
// These are more than 1000 ms apart so should not appear
|
||||
// as the same second
|
||||
expect(formatter.short(-999))
|
||||
.not.toEqual(formatter.short(999));
|
||||
});
|
||||
|
||||
it("handles negative days", function () {
|
||||
expect(formatter.long(-10 * MS_IN_DAY))
|
||||
.toEqual("10D 00:00:00");
|
||||
expect(formatter.long(-10 * MS_IN_DAY + 100))
|
||||
.toEqual("10D 00:00:00");
|
||||
expect(formatter.long(-10 * MS_IN_DAY + 999))
|
||||
.toEqual("10D 00:00:00");
|
||||
|
||||
expect(formatter.short(-10 * MS_IN_DAY + 100))
|
||||
.toEqual("240:00:00");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -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,62 +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/services/TickerService"],
|
||||
function (TickerService) {
|
||||
|
||||
var TEST_TIMESTAMP = 1433354174000;
|
||||
|
||||
describe("The ticker service", function () {
|
||||
var mockTimeout,
|
||||
mockNow,
|
||||
mockCallback,
|
||||
tickerService;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy('$timeout');
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockCallback = jasmine.createSpy('callback');
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
|
||||
tickerService = new TickerService(mockTimeout, mockNow);
|
||||
});
|
||||
|
||||
it("notifies listeners of clock ticks", function () {
|
||||
tickerService.listen(mockCallback);
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP + 12321);
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
expect(mockCallback)
|
||||
.toHaveBeenCalledWith(TEST_TIMESTAMP + 12321);
|
||||
});
|
||||
|
||||
it("allows listeners to unregister", function () {
|
||||
tickerService.listen(mockCallback)(); // Unregister immediately
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP + 12321);
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
expect(mockCallback).not
|
||||
.toHaveBeenCalledWith(TEST_TIMESTAMP + 12321);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,77 +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/services/TimerService'
|
||||
], function (TimerService) {
|
||||
describe("TimerService", function () {
|
||||
var callback;
|
||||
var mockmct;
|
||||
var timerService;
|
||||
|
||||
beforeEach(function () {
|
||||
callback = jasmine.createSpy('callback');
|
||||
mockmct = {
|
||||
time: { clock: jasmine.createSpy('clock') },
|
||||
objects: { observe: jasmine.createSpy('observe') }
|
||||
};
|
||||
timerService = new TimerService(mockmct);
|
||||
timerService.on('change', callback);
|
||||
});
|
||||
|
||||
it("initially emits no change events", function () {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports no current timer", function () {
|
||||
expect(timerService.getTimer()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("setTimer", function () {
|
||||
var testTimer;
|
||||
|
||||
beforeEach(function () {
|
||||
testTimer = { name: "I am some timer; you are nobody." };
|
||||
timerService.setTimer(testTimer);
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports the current timer", function () {
|
||||
expect(timerService.getTimer()).toBe(testTimer);
|
||||
});
|
||||
|
||||
it("observes changes to an object", function () {
|
||||
var newTimer = { name: "I am another timer." };
|
||||
expect(mockmct.objects.observe).toHaveBeenCalledWith(
|
||||
testTimer,
|
||||
'*',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
mockmct.objects.observe.calls.mostRecent().args[2](newTimer);
|
||||
expect(timerService.getTimer()).toBe(newTimer);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
@@ -44,9 +44,11 @@ define(
|
||||
setText(result.name);
|
||||
scope.ngModel[scope.field] = result;
|
||||
control.$setValidity("file-input", true);
|
||||
scope.$digest();
|
||||
}, function () {
|
||||
setText('Select File');
|
||||
control.$setValidity("file-input", false);
|
||||
scope.$digest();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
};
|
||||
|
||||
@@ -71,13 +71,15 @@ define(['zepto', 'objectUtils'], function ($, objectUtils) {
|
||||
var rootObj = this.instantiate(rootModel, rootId);
|
||||
var newStyleParent = parent.useCapability('adapter');
|
||||
var newStyleRootObj = rootObj.useCapability('adapter');
|
||||
newStyleRootObj.location = parent.getId();
|
||||
|
||||
if (this.openmct.composition.checkPolicy(newStyleParent, newStyleRootObj)) {
|
||||
// Instantiate all objects in tree with their newly generated ids,
|
||||
// adding each to its rightful parent's composition
|
||||
rootObj.getCapability("location").setPrimaryLocation(parent.getId());
|
||||
this.deepInstantiate(rootObj, tree.openmct, []);
|
||||
parent.getCapability("composition").add(rootObj);
|
||||
this.openmct.objects.save(newStyleRootObj);
|
||||
const compositionCollection = this.openmct.composition.get(newStyleParent);
|
||||
compositionCollection.add(newStyleRootObj);
|
||||
} else {
|
||||
var dialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
@@ -103,7 +105,6 @@ define(['zepto', 'objectUtils'], function ($, objectUtils) {
|
||||
var newObj;
|
||||
|
||||
seen.push(parent.getId());
|
||||
|
||||
parentModel.composition.forEach(function (childId) {
|
||||
let keystring = this.openmct.objects.makeKeyString(childId);
|
||||
|
||||
@@ -115,8 +116,7 @@ define(['zepto', 'objectUtils'], function ($, objectUtils) {
|
||||
delete newModel.persisted;
|
||||
|
||||
newObj = this.instantiate(newModel, keystring);
|
||||
newObj.getCapability("location")
|
||||
.setPrimaryLocation(tree[keystring].location);
|
||||
this.openmct.objects.save(newModel);
|
||||
this.deepInstantiate(newObj, tree, seen);
|
||||
}, this);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -42,15 +42,21 @@ define(
|
||||
newObjects;
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
uniqueId = 0;
|
||||
newObjects = [];
|
||||
openmct = {
|
||||
$injector: jasmine.createSpyObj('$injector', ['get']),
|
||||
objects: {
|
||||
makeKeyString: function (identifier) {
|
||||
return identifier.key;
|
||||
}
|
||||
makeKeyString: identifier => identifier.key,
|
||||
save: o => true
|
||||
},
|
||||
composition: {
|
||||
get: (o) => {
|
||||
return {
|
||||
add: v => {}
|
||||
};
|
||||
},
|
||||
checkPolicy: (a, b) => true
|
||||
}
|
||||
};
|
||||
mockInstantiate = jasmine.createSpy('instantiate').and.callFake(
|
||||
@@ -60,14 +66,6 @@ define(
|
||||
"id": id,
|
||||
"capabilities": {}
|
||||
};
|
||||
var locationCapability = {
|
||||
setPrimaryLocation: jasmine.createSpy('setPrimaryLocation')
|
||||
.and
|
||||
.callFake(function (newLocation) {
|
||||
config.model.location = newLocation;
|
||||
})
|
||||
};
|
||||
config.capabilities.location = locationCapability;
|
||||
if (model.composition) {
|
||||
var compCapability =
|
||||
jasmine.createSpy('compCapability')
|
||||
@@ -79,6 +77,10 @@ define(
|
||||
config.capabilities.composition = compCapability;
|
||||
}
|
||||
|
||||
config.capabilities.adapter = {
|
||||
invoke: () => model
|
||||
};
|
||||
|
||||
newObjects.push(domainObjectFactory(config));
|
||||
|
||||
return domainObjectFactory(config);
|
||||
@@ -150,10 +152,29 @@ define(
|
||||
var compDomainObject = domainObjectFactory({
|
||||
name: 'compObject',
|
||||
model: { name: 'compObject'},
|
||||
capabilities: {"composition": compositionCapability}
|
||||
capabilities: {
|
||||
"composition": compositionCapability,
|
||||
'adapter': {
|
||||
invoke: () => {
|
||||
return {
|
||||
name: 'parent',
|
||||
composition: [],
|
||||
id: "mine",
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'mine'
|
||||
},
|
||||
location: "ROOT",
|
||||
modified: 1637287323760,
|
||||
persisted: 1637287323760,
|
||||
type: "folder"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
context.domainObject = compDomainObject;
|
||||
|
||||
context.domainObject = compDomainObject;
|
||||
dialogService.getUserInput.and.returnValue(Promise.resolve(
|
||||
{
|
||||
selectFile: {
|
||||
@@ -161,10 +182,14 @@ define(
|
||||
"openmct": {
|
||||
"infiniteParent": {
|
||||
"composition": [{
|
||||
key: "infinteChild",
|
||||
namespace: ""
|
||||
"key": "infinteChild",
|
||||
"namespace": ""
|
||||
}],
|
||||
"name": "1",
|
||||
"identifier": {
|
||||
"key": "infiniteParent",
|
||||
"namespace": ""
|
||||
},
|
||||
"name": "parent",
|
||||
"type": "folder",
|
||||
"modified": 1503598129176,
|
||||
"location": "mine",
|
||||
@@ -172,10 +197,14 @@ define(
|
||||
},
|
||||
"infinteChild": {
|
||||
"composition": [{
|
||||
key: "infinteParent",
|
||||
namespace: ""
|
||||
"key": "infiniteParent",
|
||||
"namespace": ""
|
||||
}],
|
||||
"name": "2",
|
||||
"identifier": {
|
||||
"key": "infinteChild",
|
||||
"namespace": ""
|
||||
},
|
||||
"name": "child",
|
||||
"type": "folder",
|
||||
"modified": 1503598132428,
|
||||
"location": "infiniteParent",
|
||||
@@ -229,7 +258,6 @@ define(
|
||||
expect(newObjects[0].getId()).toBe('1');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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];
|
||||
|
||||
13
src/MCT.js
13
src/MCT.js
@@ -122,6 +122,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
/**
|
||||
* Tracks current selection state of the application.
|
||||
* @private
|
||||
@@ -135,7 +136,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,6 +253,8 @@ define([
|
||||
|
||||
this.status = new api.StatusAPI(this);
|
||||
|
||||
this.priority = api.PriorityAPI;
|
||||
|
||||
this.router = new ApplicationRouter(this);
|
||||
|
||||
this.branding = BrandingAPI.default;
|
||||
@@ -262,7 +265,8 @@ define([
|
||||
// Plugins that are installed by default
|
||||
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(this.plugins.Chart());
|
||||
this.install(this.plugins.TelemetryTable.default());
|
||||
this.install(PreviewPlugin.default());
|
||||
this.install(LegacyIndicatorsPlugin());
|
||||
this.install(LicensesPlugin.default());
|
||||
@@ -283,8 +287,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);
|
||||
@@ -434,6 +440,8 @@ define([
|
||||
Browse(this);
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', this.destroy);
|
||||
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
}.bind(this));
|
||||
@@ -457,6 +465,7 @@ define([
|
||||
};
|
||||
|
||||
MCT.prototype.destroy = function () {
|
||||
window.removeEventListener('beforeunload', this.destroy);
|
||||
this.emit('destroy');
|
||||
this.router.destroy();
|
||||
};
|
||||
|
||||
@@ -28,8 +28,6 @@ export default function LegacyActionAdapter(openmct, legacyActions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
console.warn(`DEPRECATION WARNING: Action ${action.definition.key} in bundle ${action.bundle.path} is non-contextual and should be migrated.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,37 +25,31 @@ define([
|
||||
'./capabilities/AdapterCapability',
|
||||
'./directives/MCTView',
|
||||
'./services/Instantiate',
|
||||
'./services/MissingModelCompatibilityDecorator',
|
||||
'./capabilities/APICapabilityDecorator',
|
||||
'./policies/AdaptedViewPolicy',
|
||||
'./runs/AlternateCompositionInitializer',
|
||||
'./runs/TypeDeprecationChecker',
|
||||
'./runs/LegacyTelemetryProvider',
|
||||
'./runs/RegisterLegacyTypes',
|
||||
'./services/LegacyObjectAPIInterceptor',
|
||||
'./views/installLegacyViews',
|
||||
'./policies/LegacyCompositionPolicyAdapter',
|
||||
'./actions/LegacyActionAdapter',
|
||||
'./services/LegacyPersistenceAdapter',
|
||||
'./services/ExportImageService'
|
||||
'./services/LegacyPersistenceAdapter'
|
||||
], function (
|
||||
ActionDialogDecorator,
|
||||
AdapterCapability,
|
||||
MCTView,
|
||||
Instantiate,
|
||||
MissingModelCompatibilityDecorator,
|
||||
APICapabilityDecorator,
|
||||
AdaptedViewPolicy,
|
||||
AlternateCompositionInitializer,
|
||||
TypeDeprecationChecker,
|
||||
LegacyTelemetryProvider,
|
||||
RegisterLegacyTypes,
|
||||
LegacyObjectAPIInterceptor,
|
||||
installLegacyViews,
|
||||
legacyCompositionPolicyAdapter,
|
||||
LegacyActionAdapter,
|
||||
LegacyPersistenceAdapter,
|
||||
ExportImageService
|
||||
LegacyPersistenceAdapter
|
||||
) {
|
||||
return {
|
||||
name: 'src/adapter',
|
||||
@@ -84,13 +78,6 @@ define([
|
||||
"identifierService",
|
||||
"cacheService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "exportImageService",
|
||||
"implementation": ExportImageService,
|
||||
"depends": [
|
||||
"dialogService"
|
||||
]
|
||||
}
|
||||
],
|
||||
components: [
|
||||
@@ -108,12 +95,6 @@ define([
|
||||
implementation: ActionDialogDecorator,
|
||||
depends: ["openmct"]
|
||||
},
|
||||
{
|
||||
type: "decorator",
|
||||
provides: "modelService",
|
||||
implementation: MissingModelCompatibilityDecorator,
|
||||
depends: ["openmct"]
|
||||
},
|
||||
{
|
||||
provides: "objectService",
|
||||
type: "decorator",
|
||||
@@ -144,10 +125,6 @@ define([
|
||||
}
|
||||
],
|
||||
runs: [
|
||||
{
|
||||
implementation: TypeDeprecationChecker,
|
||||
depends: ["types[]"]
|
||||
},
|
||||
{
|
||||
implementation: AlternateCompositionInitializer,
|
||||
depends: ["openmct"]
|
||||
|
||||
@@ -173,10 +173,11 @@ define([
|
||||
const limitEvaluator = oldObject.getCapability("limit");
|
||||
|
||||
return {
|
||||
limits: function () {
|
||||
return limitEvaluator.limits();
|
||||
limits: () => {
|
||||
return limitEvaluator.limits.then !== undefined
|
||||
? limitEvaluator.limits()
|
||||
: Promise.resolve(limitEvaluator.limits());
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,12 +4,6 @@ define([
|
||||
|
||||
) {
|
||||
function RegisterLegacyTypes(types, openmct) {
|
||||
types.forEach(function (legacyDefinition) {
|
||||
if (!openmct.types.get(legacyDefinition.key)) {
|
||||
console.warn(`DEPRECATION WARNING: Migrate type ${legacyDefinition.key} from ${legacyDefinition.bundle.path} to use the new Types API. Legacy type support will be removed soon.`);
|
||||
}
|
||||
});
|
||||
|
||||
openmct.types.importLegacyTypes(types);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,218 +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 {object} options Image options.
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.renderElement = function (element, {imageType, className, thumbnailSize}) {
|
||||
const self = this;
|
||||
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) {
|
||||
if (thumbnailSize) {
|
||||
const thumbnail = self.getThumbnail(canvas, mimeType, thumbnailSize);
|
||||
|
||||
return canvas.toBlob(blob => resolve({
|
||||
blob,
|
||||
thumbnail
|
||||
}), mimeType);
|
||||
}
|
||||
|
||||
return canvas.toBlob(blob => resolve({ blob }), 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();
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ExportImageService.prototype.getThumbnail = function (canvas, mimeType, size) {
|
||||
const thumbnailCanvas = document.createElement('canvas');
|
||||
thumbnailCanvas.setAttribute('width', size.width);
|
||||
thumbnailCanvas.setAttribute('height', size.height);
|
||||
const ctx = thumbnailCanvas.getContext('2d');
|
||||
ctx.globalCompositeOperation = "copy";
|
||||
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, size.width, size.height);
|
||||
|
||||
return thumbnailCanvas.toDataURL(mimeType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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, {
|
||||
imageType: 'jpg',
|
||||
className
|
||||
})
|
||||
.then(function (img) {
|
||||
saveAs(img.blob, 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, {
|
||||
imageType: 'png',
|
||||
className
|
||||
})
|
||||
.then(function (img) {
|
||||
saveAs(img.blob, 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, options) {
|
||||
|
||||
return this.renderElement(element, {
|
||||
imageType: 'png',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user