Compare commits

...

70 Commits

Author SHA1 Message Date
7645240612 Test karma upgrade test coverage 2020-06-03 13:49:49 -07:00
02b537580c Merge pull request #3057 from nasa/code-standards-update
Updated code standard
2020-06-02 13:31:21 -07:00
7073b0717f Merge branch 'master' into code-standards-update 2020-06-02 13:05:32 -07:00
d6bb1b2a12 Added note about requiring a single return statement. 2020-06-02 13:05:15 -07:00
c256696790 Image view should react to time conductor changes - 2712 (#2934)
Image view reacts to time conductor changes
Fixes #2712
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-06-02 12:27:55 -07:00
d5480e7524 Multiple item conditional styles (#3017)
Conditional Styles for multiple items #3076

* Preview condition styles on selecting that condition or one of it's styles
Do not evaluate conditional styles in edit mode

* Conditions styling tweaked

- Condition match `is-current` styling for browse and edit modes;
- Disallowed pointer events on Conditions to prevent selection in
Inspector when not editing;

* Highlight current condition in conditionSet view

* Addresses review comments.

* Condition matching highlighting tweaked

- Enable match highlighting during Editing;
- Tweaks to `is-current` styling;

* Don't reset the callback on destroy

* Combine multiple and single selection styling of objects

* Fix issue with applying styles in edit mode

* Fix item styles bug

* Remove comment

* Adds back visibility toggle

* Set isEditing on initialization.

* Addresses review comments - removes use of lodash.

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-06-01 12:40:40 -07:00
ab463e93fe Refactor Notifications to use Vue, and add clear all option (#3068)
[Notifications] Need a clear all notifications option #3006

* create new notifications indicator in vue, and sunset old one

* add notifications list overlay

* working notifications

* add support for progress notifications

* update percentage on mounted

* Markup cleanups in new Vue Notifications files

- Removed unneeded markup and class wrappers;
- Removed unneeded inline styling;

* remove example notifications

* fix lint errors

* make reviewer requested changes, remove old not needed files, add test

* update testTools to testUtils

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-06-01 11:45:33 -07:00
8363c65312 [Notebook] display bounds change notification only if bounds changed (#3062) Resolves (#3051)
* display bounds change notification only if bounds changed

* avoid empty notification messages.
2020-05-28 12:29:32 -07:00
04598b6cf1 Bug fixes for plots (#3019) (#3069)
* prevent plots from breaking when more than one NaN is received.

* fix y axis label issue

* dont emit a viewport change event when marquee doesnt happen
2020-05-28 09:58:22 -07:00
43628ad9d6 Lodash upgrade and cleanup (#2990)
* Upgrades lodash
* Replaces some usage of lodash with native functions.
* Adds linting to catch cases where native functions could be used instead of lodash functions
* Renamed testTools to testUtils

Co-authored-by: Joshi <simplyrender@gmail.com>
Co-authored-by: David Tsay <david.e.tsay@nasa.gov>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-27 10:59:02 -07:00
67bea86bc8 Added headless mode start option (#3065)
* Added headless mode start option. Fixes #3064
2020-05-26 11:39:55 -07:00
4eb4cbfffc Merge pull request #3020 from nasa/updated-checklists
Add details about pull requests to contributing guide
2020-05-19 09:30:05 -07:00
eda01abcbc Merge branch 'master' into updated-checklists 2020-05-18 12:12:26 -07:00
2fa29124bf Merge branch 'master' into code-standards-update 2020-05-18 11:45:27 -07:00
694b8f4666 provide format for name metadata (#3054) 2020-05-18 11:40:05 -07:00
33faeafa98 New proposed ESLint rules that require no code changes 2020-05-16 16:19:36 -07:00
a40ff07353 Updated guidance on anonymous functions 2020-05-16 16:11:42 -07:00
41dc9c794d Fixed capitalization on CONST 2020-05-16 16:08:43 -07:00
f1faf3965d Changed reference to constructors to classes 2020-05-16 16:07:49 -07:00
da2ecbbcad Fixed formatting issues, removed outdated example code. 2020-05-16 16:06:59 -07:00
32c892fe98 Updated code standards 2020-05-16 15:56:30 -07:00
bbb271a678 clarify value hints (#2673)
remove confusing comments regarding domain - input and range - output
2020-05-15 14:48:25 -07:00
fec1438806 dont emit a viewport change event when marquee doesnt happen (#3036)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-15 14:35:13 -07:00
28f19ec310 [Notebook] Clicking on an embed in a notebook entry does not result in bounds change #3034 (#3042) 2020-05-14 14:56:41 -07:00
f934454c25 Fixes computation of result when telemetry is not used by an object (#3040)
* Resolves issue #30307
Don't compute the result of a condition if telemetry datum is not being used by that condition

* Adds tests for condition results
2020-05-13 14:30:45 -07:00
eb49ffae02 Update CONTRIBUTING.md 2020-05-13 09:21:00 -07:00
5751012872 Update CONTRIBUTING.md 2020-05-13 09:20:03 -07:00
aa041e04cf Merge remote-tracking branch 'origin/update-contributing-guidelines' into updated-checklists 2020-05-11 17:13:53 -07:00
24e7ea143a Added more details on the process around pull requests 2020-05-11 17:12:25 -07:00
79d5d9c4d0 Update CONTRIBUTING.md 2020-05-11 16:15:17 -07:00
b5bfdc4418 Update CONTRIBUTING.md 2020-05-11 15:56:49 -07:00
59730c60ec Update CONTRIBUTING.md 2020-05-11 15:54:25 -07:00
4a87a5d847 Show object styles in preview modal (#3018)
* Adds conditional styles to Preview window
2020-05-11 14:25:39 -07:00
421c09ec2c Allow users to lazy load Tabs (#2958)
* lazy load tabs

* remove listener on destroy

* fix lint error

* Store current tab position on domainObject

* remove lodash dependency and use keystring
2020-05-08 10:36:13 -07:00
0679b246b8 [Notebook]: Remove default section/page from localstorage on notebook delete (#2900)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-07 16:13:32 -07:00
83f9c6c528 improve plot gestures - and clean up (#3013) 2020-05-06 10:33:59 -07:00
a5f3ba6259 Use evalAsync instead of digest() (#3001)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-05-05 12:23:41 -07:00
a70facf0c8 Merge pull request #3000 from nasa/upgrade-moment
Upgrade moment to 2.25.3
2020-05-05 11:01:39 -07:00
447fe94325 Merge branch 'upgrade-moment' of https://github.com/nasa/openmct into upgrade-moment 2020-05-05 10:36:43 -07:00
8e2b666766 Upgraded moment version to 2.25.3 2020-05-05 10:36:33 -07:00
dcbfbdbb89 Merge branch 'master' into upgrade-moment 2020-05-05 10:12:50 -07:00
4c76bf34ab Highlight currently winning Condition in Condition Set Edit and Read-only views (#2936)
* Preview condition styles on selecting that condition or one of it's styles

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-05-05 09:55:42 -07:00
81b7a9d3e0 Merge branch 'master' into upgrade-moment 2020-05-01 16:47:26 -07:00
dc573c479c Upgrade moment to 2.24.0 2020-05-01 16:25:49 -07:00
23303c910e Don't allow recursive Preview actions #2775 (#2869)
* Don't allow recursive Preview actions #2775

* actionsToBeIncluded and actionsToBeSkipped passed in as options object.

* Revert "actionsToBeIncluded and actionsToBeSkipped passed in as options object."

This reverts commit f501d0b4ba.

* Revert "Don't allow recursive Preview actions #2775"

This reverts commit 5563cbea3a.

* Don't allow recursive Preview actions

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-01 09:33:10 -07:00
3282934cf6 fix test coverage. (#2951)
* fix test coverage.

* changes per comments + added test-coverage script to increase max-old-space-size of V8
ref: https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_mbytes

* renamed test:coverage and test:debug.

* circle-ci to use test:coverage.

* reduced test coverage thresholds.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 16:57:53 -07:00
c157fab081 [Notebook] Save snapshot dropdown should be available from "view large" overlay #2922 (#2926)
* [Notebook] Save snapshot dropdown should be available from "view large" overlay #2922\
* Significant improvements to Snapshot styling
* [Notebook] Embed links aren't navigating #2979

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 16:39:20 -07:00
7c07b66cc9 [Notebook] : Error in event handler for "updateSection": "TypeError: Cannot read property 'id' of undefined" #2921 (#2924)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 16:30:09 -07:00
7a906ccf5c Preview condition styles on selecting that condition or one of it's styles (#2925)
* Preview condition styles on selecting that condition or one of it's styles
* Do not evaluate conditional styles in edit mode

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 13:00:43 -07:00
ff7debfb81 [Notebook] Entries and Embeds need to use the same timesystem #2920 (#2923)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 11:56:08 -07:00
92ba103f45 modified eslint script and fixed errors found (#2905)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 11:53:07 -07:00
2c2d8d6b56 [Notebook]: Unnecessary notification "Time bounds changed to fixed timespan mode" #2843 (#2866)
* [Notebook]: Unnecessary notification "Time bounds changed to fixed timespan mode" #2843

* removed stopclock call.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-30 10:47:49 -07:00
cfadb9f4fd Fixes #2901 (#2902)
- Mod PreviewAction to prevent folders from being previewed;

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 09:48:16 -07:00
396817b2d1 handle non-valid requests (#2984)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 15:24:12 -07:00
96eb6d6b74 [Conditionals] evaluation fixes (#2981)
* change single output to state and value

* do not send telemetryObjects to telemetry api request cal

* normalize data on requests

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 14:56:07 -07:00
cb5d47f66f Use only the values required for description (#2919)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-28 16:58:58 -07:00
ea90d02d66 Show the Styles tab for non creatable layout objects including condition sets (#2975) 2020-04-28 13:10:29 -07:00
95f73d8eb8 [Conditionals] fix #2961 in master (#2969)
* use correct id for telemetry requests

* request and subscription data cache should be mutually exclusive

use latest timestamp for any/all requests

* do not add prop to datum

remove unnecessary if check

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-28 13:02:23 -07:00
a37c686993 Merge pull request #2968 from nasa/revert-2885-lodash-upgrade-test
Revert "Lodash upgrade"
2020-04-24 11:55:44 -07:00
f12166097c Revert "Lodash upgrade (#2885)"
This reverts commit d103a22fa0.
2020-04-24 11:53:31 -07:00
d103a22fa0 Lodash upgrade (#2885)
* upgraded lodash, changed method names
* native implementations as requested
2020-04-23 10:38:44 -07:00
04a60cfcbb fixes #2713 (#2928)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-22 15:22:11 -07:00
8d723960f4 removed acorn from package.json (#2906)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:50:45 -07:00
6d3cd2c699 Upgrade angular from 1.4.14 to 1.7.9 (#2955)
* successfully upgraded to v1.6 with $compileProvider.preAssignBindingsEnabled(true)
* removed $compileProvider.preAssignBindingsEnabled(true), wrapped constructors for plot and chart inside onInit function
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:34:12 -07:00
87bf94fe0a Updated year in index.html (#2930) 2020-04-21 15:28:10 -07:00
af93823b6f Elasticsearch support for change to typeless API (#2941)
* added elasticsearch to bundlemap
2020-04-21 15:23:43 -07:00
4a39ddf425 Check for any and all criteria (#2948) 2020-04-16 15:01:14 -07:00
83c273b976 Remove telemetry from criteria when not editing a condition set (#2933) 2020-04-16 12:32:32 -07:00
7dd81beb03 Remove telemetry data cache if a telemetry endpoint is removed (#2916) 2020-04-10 16:49:29 -07:00
1842d3923c [Conditionals] Only provide telemetry for incoming telemetry that is used (#2914)
* Ensures that results for a specific datapoint are evaluated atomically.

* Removes timestamp based evalutation from conditionManager

* Remove unused code

* remove generating timestamp for telemetry data

* get results directly instead of using events

* remove unused listeners, events, and helpers

* linting

* remove commented code

* telemetry criterion stores its own result

* refactor all/any telemetry criterion to use new evaluator

* tie in requests and eliminate unused code

* use current timesystem to compare latest

* scope function names

* AllTelemetryCriterion extends TelemetryCriterion

* fix telemetrycriterion and unit testing

* fix unit tests

* check if telemetry is used at condition manager level

* move check to condition manager

* remove whitespace

Co-authored-by: Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 16:45:09 -07:00
141 changed files with 3051 additions and 866 deletions

View File

@ -20,8 +20,8 @@ jobs:
paths:
- node_modules
- run:
name: npm run test
command: npm run test
name: npm run test:coverage
command: npm run test:coverage
- run:
name: npm run lint
command: npm run lint

View File

@ -1,3 +1,4 @@
const LEGACY_FILES = ["platform/**", "example/**"];
module.exports = {
"env": {
"browser": true,
@ -10,7 +11,8 @@ module.exports = {
},
"extends": [
"eslint:recommended",
"plugin:vue/recommended"
"plugin:vue/recommended",
"plugin:you-dont-need-lodash-underscore/compatible"
],
"parser": "vue-eslint-parser",
"parserOptions": {
@ -22,6 +24,9 @@ module.exports = {
}
},
"rules": {
"you-dont-need-lodash-underscore/omit": "off",
"you-dont-need-lodash-underscore/throttle": "off",
"you-dont-need-lodash-underscore/flatten": "off",
"no-bitwise": "error",
"curly": "error",
"eqeqeq": "error",
@ -66,6 +71,56 @@ module.exports = {
],
"dot-notation": "error",
"indent": ["error", 4],
// https://eslint.org/docs/rules/no-case-declarations
"no-case-declarations": "error",
// https://eslint.org/docs/rules/max-classes-per-file
"max-classes-per-file": ["error", 1],
// https://eslint.org/docs/rules/no-eq-null
"no-eq-null": "error",
// https://eslint.org/docs/rules/no-eval
"no-eval": "error",
// https://eslint.org/docs/rules/no-floating-decimal
"no-floating-decimal": "error",
// https://eslint.org/docs/rules/no-implicit-globals
"no-implicit-globals": "error",
// https://eslint.org/docs/rules/no-implied-eval
"no-implied-eval": "error",
// https://eslint.org/docs/rules/no-lone-blocks
"no-lone-blocks": "error",
// https://eslint.org/docs/rules/no-loop-func
"no-loop-func": "error",
// https://eslint.org/docs/rules/no-new-func
"no-new-func": "error",
// https://eslint.org/docs/rules/no-new-wrappers
"no-new-wrappers": "error",
// https://eslint.org/docs/rules/no-octal-escape
"no-octal-escape": "error",
// https://eslint.org/docs/rules/no-proto
"no-proto": "error",
// https://eslint.org/docs/rules/no-return-await
"no-return-await": "error",
// https://eslint.org/docs/rules/no-script-url
"no-script-url": "error",
// https://eslint.org/docs/rules/no-self-compare
"no-self-compare": "error",
// https://eslint.org/docs/rules/no-sequences
"no-sequences": "error",
// https://eslint.org/docs/rules/no-unmodified-loop-condition
"no-unmodified-loop-condition": "error",
// https://eslint.org/docs/rules/no-useless-call
"no-useless-call": "error",
// https://eslint.org/docs/rules/wrap-iife
"wrap-iife": "error",
// https://eslint.org/docs/rules/no-nested-ternary
"no-nested-ternary": "error",
// https://eslint.org/docs/rules/switch-colon-spacing
"switch-colon-spacing": "error",
// https://eslint.org/docs/rules/no-useless-computed-key
"no-useless-computed-key": "error",
// https://eslint.org/docs/rules/rest-spread-spacing
"rest-spread-spacing": ["error"],
"vue/html-indent": [
"error",
4,
@ -112,6 +167,13 @@ module.exports = {
}
]
}
}, {
"files": LEGACY_FILES,
"rules": {
// https://eslint.org/docs/rules/no-nested-ternary
"no-nested-ternary": "off",
"no-var": "off"
}
}
]
};

4
API.md
View File

@ -427,8 +427,8 @@ Each telemetry value description has an object defining hints. Keys in this thi
Known hints:
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
* `range`: Indicates that the value is the "output" of a datum. Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
* `domain`: Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
* `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
##### The Time Conductor and Telemetry

View File

@ -103,7 +103,7 @@ the name chosen could not be mistaken for a topic or master branch.
### Merging
When development is complete on an issue, the first step toward merging it
back into the master branch is to file a Pull Request. The contributions
back into the master branch is to file a Pull Request (PR). The contributions
should meet code, test, and commit message standards as described below,
and the pull request should include a completed author checklist, also
as described below. Pull requests may be assigned to specific team
@ -114,6 +114,15 @@ request. When the reviewer is satisfied, they should add a comment to
the pull request containing the reviewer checklist (from below) and complete
the merge back to the master branch.
Additionally:
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull requests __author__. If no issue exists, create one.
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull requests __author__.
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
* Every PR must have two reviewers assigned, though only one approval is necessary for merge.
* Changes to API require approval by a senior developer.
* When creating a PR, it is the author's responsibility to apply any priority label from the issue to the PR as well. This helps with prioritization.
## Standards
Contributions to Open MCT are expected to meet the following standards.
@ -122,89 +131,96 @@ changes.
### Code Standards
JavaScript sources in Open MCT must satisfy JSLint under its default
settings. This is verified by the command line build.
JavaScript sources in Open MCT must satisfy the ESLint rules defined in
this repository. This is verified by the command line build.
#### Code Guidelines
JavaScript sources in Open MCT should:
* Use four spaces for indentation. Tabs should not be used.
* Include JSDoc for any exposed API (e.g. public methods, constructors).
* Include non-JSDoc comments as-needed for explaining private variables,
methods, or algorithms when they are non-obvious.
* Define one public class per script, expressed as a constructor function
returned from an AMD-style module.
* Follow “Java-like” naming conventions. These includes:
* Classes should use camel case, first letter capitalized
(e.g. SomeClassName).
* Methods, variables, fields, and function names should use camel case,
first letter lower-case (e.g. someVariableName).
* Constants (variables or fields which are meant to be declared and
initialized statically, and never changed) should use only capital
letters, with underscores between words (e.g. SOME_CONSTANT).
* File names should be the name of the exported class, plus a .js extension
(e.g. SomeClassName.js).
* Avoid anonymous functions, except when functions are short (a few lines)
and/or their inclusion makes sense within the flow of the code
(e.g. as arguments to a forEach call).
* Avoid deep nesting (especially of functions), except where necessary
(e.g. due to closure scope).
* End with a single new-line character.
* Expose public methods by declaring them on the class's prototype.
* Within a given function's scope, do not mix declarations and imperative
code, and present these in the following order:
* First, variable declarations and initialization.
* Second, function declarations.
* Third, imperative statements.
* Finally, the returned value.
1. Write clean code. Heres a good summary - https://github.com/ryanmcdermott/clean-code-javascript.
1. Include JSDoc for any exposed API (e.g. public methods, classes).
1. Include non-JSDoc comments as-needed for explaining private variables,
methods, or algorithms when they are non-obvious. Otherwise code
should be self-documenting.
1. Classes and Vue components should use camel case, first letter capitalized
(e.g. SomeClassName).
1. Methods, variables, fields, events, and function names should use camelCase,
first letter lower-case (e.g. someVariableName).
1. Source files that export functions should use camelCase, first letter lower-case (eg. testTools.js)
1. Constants (variables or fields which are meant to be declared and
initialized statically, and never changed) should use only capital
letters, with underscores between words (e.g. SOME_CONSTANT). They should always be declared as `const`s
1. File names should be the name of the exported class, plus a .js extension
(e.g. SomeClassName.js).
1. Avoid anonymous functions, except when functions are short (one or two lines)
and their inclusion makes sense within the flow of the code
(e.g. as arguments to a forEach call). Anonymous functions should always be arrow functions.
1. Named functions are preferred over functions assigned to variables.
eg.
```JavaScript
function renameObject(object, newName) {
Object.name = newName;
}
```
is preferable to
```JavaScript
const rename = (object, newName) => {
Object.name = newName;
}
```
1. Avoid deep nesting (especially of functions), except where necessary
(e.g. due to closure scope).
1. End with a single new-line character.
1. Always use ES6 `Class`es and inheritence rather than the pre-ES6 prototypal
pattern.
1. Within a given function's scope, do not mix declarations and imperative
code, and present these in the following order:
* First, variable declarations and initialization.
* Secondly, imperative statements.
* Finally, the returned value. Functions should only have a single return statement.
1. Avoid the use of "magic" values.
eg.
```JavaScript
Const UNAUTHORIZED = 401
if (responseCode === UNAUTHORIZED)
```
is preferable to
```JavaScript
if (responseCode === 401)
```
1. Dont use the ternary operator. Yes it's terse, but there's probably a clearer way of writing it.
1. Test specs should reside alongside the source code they test, not in a separate directory.
1. Organize code by feature, not by type.
eg.
```
- telemetryTable
- row
TableRow.js
TableRowCollection.js
TableRow.vue
- column
TableColumn.js
TableColumn.vue
plugin.js
pluginSpec.js
```
is preferable to
```
- telemetryTable
- components
TableRow.vue
TableColumn.vue
- collections
TableRowCollection.js
TableColumn.js
TableRow.js
plugin.js
pluginSpec.js
```
Deviations from Open MCT code style guidelines require two-party agreement,
typically from the author of the change and its reviewer.
#### Code Example
```js
/*global define*/
/**
* Bundles should declare themselves as namespaces in whichever source
* file is most like the "main point of entry" to the bundle.
* @namespace some/bundle
*/
define(
['./OtherClass'],
function (OtherClass) {
"use strict";
/**
* A summary of how to use this class goes here.
*
* @constructor
* @memberof some/bundle
*/
function ExampleClass() {
}
// Methods which are not intended for external use should
// not have JSDoc (or should be marked @private)
ExampleClass.prototype.privateMethod = function () {
};
/**
* A summary of this method goes here.
* @param {number} n a parameter
* @returns {number} a return value
*/
ExampleClass.prototype.publicMethod = function (n) {
return n * 2;
}
return ExampleClass;
}
);
```
### Test Standards
Automated testing shall occur whenever changes are merged into the main
@ -292,6 +308,7 @@ checklist).
2. Unit tests included and/or updated with changes?
3. Command line build passes?
4. Changes have been smoke-tested?
5. Testing instructions included?
### Reviewer Checklist
@ -299,3 +316,4 @@ checklist).
2. Appropriate unit tests included?
3. Code style and in-line documentation are appropriate?
4. Commit messages meet standards?
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)

View File

@ -50,7 +50,8 @@ define([
values: [
{
key: "name",
name: "Name"
name: "Name",
format: "string"
},
{
key: "utc",
@ -99,7 +100,7 @@ define([
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return _.extend(
return Object.assign(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]

View File

@ -52,6 +52,7 @@ define([
return {
name: name,
utc: Math.floor(timestamp / 5000) * 5000,
local: Math.floor(timestamp / 5000) * 5000,
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
};
}
@ -78,7 +79,7 @@ define([
},
request: function (domainObject, options) {
var start = options.start;
var end = options.end;
var end = Math.min(options.end, Date.now());
var data = [];
while (start <= end && data.length < 5000) {
data.push(pointForTimestamp(start, domainObject.name));
@ -118,6 +119,14 @@ define([
name: 'Time',
key: 'utc',
format: 'utc',
hints: {
domain: 2
}
},
{
name: 'Local Time',
key: 'local',
format: 'local-format',
hints: {
domain: 1
}

View File

@ -1,14 +1,14 @@
<template>
<div class="example">{{ msg }}</div>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
data() {
return {
msg: 'Hello world!'
}
}
}
}
</script>

View File

@ -1,5 +1,5 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
Open MCT, Copyright (c) 2014-2020, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@ -43,9 +43,9 @@
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());

View File

@ -24,16 +24,27 @@
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'];
if (coverageEnabled) {
reporters.push('coverage-istanbul');
}
module.exports = (config) => {
const webpackConfig = require('./webpack.config.js');
delete webpackConfig.output;
if (!devMode) {
if (!devMode || coverageEnabled) {
webpackConfig.module.rules.push({
test: /\.js$/,
exclude: /node_modules|example/,
use: 'istanbul-instrumenter-loader'
exclude: /node_modules|example|lib|dist/,
use: {
loader: 'istanbul-instrumenter-loader',
options: {
esModules: true
}
}
});
}
@ -45,11 +56,7 @@ module.exports = (config) => {
'src/**/*Spec.js'
],
port: 9876,
reporters: [
'progress',
'coverage',
'html'
],
reporters: reporters,
browsers: browsers,
customLaunchers: {
ChromeDebugging: {
@ -61,27 +68,27 @@ module.exports = (config) => {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
coverageReporter: {
dir: process.env.CIRCLE_ARTIFACTS ?
process.env.CIRCLE_ARTIFACTS + '/coverage' :
"dist/reports/coverage",
check: {
global: {
lines: 80,
excludes: ['src/plugins/plot/**/*.js']
}
}
},
// HTML test reporting.
htmlReporter: {
outputDir: "dist/reports/tests",
preserveDescribeNesting: true,
foldAll: false
},
coverageIstanbulReporter: {
fixWebpackSourcePaths: true,
dir: process.env.CIRCLE_ARTIFACTS ?
process.env.CIRCLE_ARTIFACTS + '/coverage' :
"dist/reports/coverage",
reports: ['html', 'lcovonly', 'text-summary'],
thresholds: {
global: {
lines: 62
}
}
},
preprocessors: {
// add webpack as preprocessor
'platform/**/*Spec.js': [ 'webpack', 'sourcemap' ],
'src/**/*Spec.js': [ 'webpack', 'sourcemap' ]
'platform/**/*Spec.js': ['webpack', 'sourcemap'],
'src/**/*Spec.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {

View File

@ -2,10 +2,8 @@
"name": "openmct",
"version": "1.0.0-snapshot",
"description": "The Open MCT core platform",
"dependencies": {},
"devDependencies": {
"acorn": "6.2.0",
"angular": "1.4.14",
"angular": "1.7.9",
"angular-route": "1.4.14",
"babel-eslint": "8.2.6",
"comma-separated-values": "^3.6.4",
@ -25,6 +23,7 @@
"d3-time-format": "2.1.x",
"eslint": "5.2.0",
"eslint-plugin-vue": "^6.0.0",
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
"eventemitter3": "^1.2.0",
"exports-loader": "^0.7.0",
"express": "^4.13.1",
@ -43,22 +42,24 @@
"karma-chrome-launcher": "^2.2.0",
"karma-cli": "^1.0.1",
"karma-coverage": "^1.1.2",
"karma-coverage-istanbul-reporter": "^2.1.1",
"karma-html-reporter": "^0.2.7",
"karma-jasmine": "^1.1.2",
"karma-jasmine": "^2.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.0",
"location-bar": "^3.0.1",
"lodash": "^3.10.1",
"lodash": "^4.17.12",
"markdown-toc": "^0.11.7",
"marked": "^0.3.5",
"mini-css-extract-plugin": "^0.4.1",
"minimist": "^1.1.1",
"moment": "^2.11.1",
"moment": "2.25.3",
"moment-duration-format": "^2.2.2",
"moment-timezone": "^0.5.21",
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"node-sass": "^4.9.2",
"painterro": "^0.2.65",
"plotly.js-dist": "^1.54.1",
"printj": "^1.2.1",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
@ -76,14 +77,16 @@
"zepto": "^1.2.0"
},
"scripts": {
"clean": "rm -rf ./dist",
"start": "node app.js",
"lint": "eslint platform example src/**/*.{js,vue} openmct.js",
"lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix",
"lint": "eslint platform example src --ext .js,.vue openmct.js",
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
"build:prod": "cross-env NODE_ENV=production webpack",
"build:dev": "webpack",
"build:watch": "webpack --watch",
"test": "karma start --single-run",
"test-debug": "cross-env NODE_ENV=debug karma start --no-single-run",
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
"test:coverage": "./scripts/test-coverage.sh",
"test:watch": "karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",

View File

@ -6,6 +6,12 @@
ng-show="ngModel.dialog.messages.length > 1 ||
ngModel.dialog.messages.length == 0">s</span>
</div>
<button
ng-if="ngModel.dialog.topBarButton"
class="c-button c-button--major"
ng-click="ngModel.topBarButton.onClick">
{{ ngModel.topBarButton.label }}
</button>
</div>
<div class="w-messages c-overlay__messages">
<mct-include
@ -16,7 +22,7 @@
<button ng-repeat="dialogAction in ngModel.dialog.actions"
class="c-button c-button--major"
ng-click="dialogAction.action()">
{{dialogAction.label}}
{{ dialogAction.label }}
</button>
</div>
</div>

View File

@ -21,7 +21,7 @@
*****************************************************************************/
define(
['../../../../../src/api/objects/object-utils'],
['objectUtils'],
function (objectUtils) {
/**

View File

@ -21,44 +21,15 @@
*****************************************************************************/
define([
"./src/NotificationIndicatorController",
"./src/NotificationIndicator",
"./src/NotificationService",
"./res/notification-indicator.html"
"./src/NotificationService"
], function (
NotificationIndicatorController,
NotificationIndicator,
NotificationService,
notificationIndicatorTemplate
NotificationService
) {
return {
name:"platform/commonUI/notification",
definition: {
"extensions": {
"templates": [
{
"key": "notificationIndicatorTemplate",
"template": notificationIndicatorTemplate
}
],
"controllers": [
{
"key": "NotificationIndicatorController",
"implementation": NotificationIndicatorController,
"depends": [
"$scope",
"openmct",
"dialogService"
]
}
],
"indicators": [
{
"implementation": NotificationIndicator,
"priority": "fallback"
}
],
"services": [
{
"key": "notificationService",

View File

@ -1,8 +0,0 @@
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<div ng-show="notifications.length > 0" class="c-indicator c-indicator--clickable s-status-{{highest.severity}} icon-bell"
ng-controller="NotificationIndicatorController">
<span class="label c-indicator__label">
<button ng-click="showNotificationsList()">
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span></button>
</span><span class="c-indicator__count">{{notifications.length}}</span>
</div>

View File

@ -1,73 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Provides an indicator that is visible when there are
* banner notifications that have been minimized. Will also indicate
* the number of notifications. Notifications can be viewed by
* clicking on the indicator to launch a dialog showing a list of
* notifications.
* @param $scope
* @param notificationService
* @param dialogService
* @constructor
*/
function NotificationIndicatorController($scope, openmct, dialogService) {
$scope.notifications = openmct.notifications.notifications;
$scope.highest = openmct.notifications.highest;
/**
* Launch a dialog showing a list of current notifications.
*/
$scope.showNotificationsList = function () {
let notificationsList = openmct.notifications.notifications.map(notification => {
if (notification.model.severity === 'alert' || notification.model.severity === 'info') {
notification.model.primaryOption = {
label: 'Dismiss',
callback: () => {
let currentIndex = notificationsList.indexOf(notification);
notification.dismiss();
notificationsList.splice(currentIndex, 1);
}
}
}
return notification;
})
dialogService.getDialogResponse('overlay-message-list', {
dialog: {
title: "Messages",
//Launch the message list dialog with the models
// from the notifications
messages: notificationsList
}
});
};
}
return NotificationIndicatorController;
}
);

View File

@ -1,60 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['../src/NotificationIndicatorController'],
function (NotificationIndicatorController) {
xdescribe("The notification indicator controller ", function () {
var mockNotificationService,
mockScope,
mockDialogService,
controller;
beforeEach(function () {
mockNotificationService = jasmine.createSpy("notificationService");
mockScope = jasmine.createSpy("$scope");
mockDialogService = jasmine.createSpyObj(
"dialogService",
["getDialogResponse","dismiss"]
);
mockNotificationService.highest = {
severity: "error"
};
controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
});
it("exposes the highest notification severity to the template", function () {
expect(mockScope.highest).toBeTruthy();
expect(mockScope.highest.severity).toBe("error");
});
it("invokes the dialog service to show list of messages", function () {
expect(mockScope.showNotificationsList).toBeDefined();
mockScope.showNotificationsList();
expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
expect(mockDialogService.getDialogResponse.calls.mostRecent().args[0]).toBe('overlay-message-list');
expect(mockDialogService.getDialogResponse.calls.mostRecent().args[1].dialog).toBeDefined();
});
});
}
);

View File

@ -26,7 +26,7 @@
* @namespace platform/containment
*/
define(
['../../../src/api/objects/object-utils'],
['objectUtils'],
function (objectUtils) {
function PersistableCompositionPolicy(openmct) {

View File

@ -81,7 +81,7 @@ define(
baseContext = context || {};
}
var actionContext = _.extend({}, baseContext);
var actionContext = Object.assign({}, baseContext);
actionContext.domainObject = this.domainObject;
return this.actionService.getActions(actionContext);

View File

@ -87,6 +87,11 @@ define([
bootstrapper
);
// Override of angular1.6 ! hashPrefix
app.config(['$locationProvider', function ($locationProvider) {
$locationProvider.hashPrefix('');
}]);
// Apply logging levels; this must be done now, before the
// first log statement.
new LogLevel(logLevel).configure(app, $log);

View File

@ -121,7 +121,7 @@ define(['lodash'], function (_) {
*/
ExportAsJSONAction.prototype.rewriteLink = function (child, parent) {
this.externalIdentifiers.push(this.getId(child));
var index = _.findIndex(parent.composition, function (id) {
var index = parent.composition.findIndex(id => {
return _.isEqual(child.identifier, id);
});
var copyOfChild = this.copyObject(child);

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, objectUtils) {
define(['zepto', 'objectUtils'], function ($, objectUtils) {
/**
* The ImportAsJSONAction is available from context menus and allows a user

View File

@ -71,7 +71,7 @@ define([
},
{
"key": "ELASTIC_PATH",
"value": "mct/domain_object",
"value": "mct/_doc",
"priority": "fallback"
},
{

View File

@ -32,9 +32,9 @@ define(
// JSLint doesn't like underscore-prefixed properties,
// so hide them here.
var SRC = "_source",
REV = "_version",
ID = "_id",
CONFLICT = 409;
CONFLICT = 409,
SEQ_NO = "_seq_no",
PRIMARY_TERM = "_primary_term";
/**
* The ElasticPersistenceProvider reads and writes JSON documents
@ -104,7 +104,8 @@ define(
// Get a domain object model out of ElasticSearch's response
ElasticPersistenceProvider.prototype.getModel = function (response) {
if (response && response[SRC]) {
this.revs[response[ID]] = response[REV];
this.revs[response[SEQ_NO]] = response[SEQ_NO];
this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
return response[SRC];
} else {
return undefined;
@ -116,7 +117,8 @@ define(
// indicate that the request failed.
ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
if (response && !response.error) {
this.revs[key] = response[REV];
this.revs[SEQ_NO] = response[SEQ_NO];
this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
return response;
} else {
return this.handleError(response, key);
@ -147,7 +149,7 @@ define(
function checkUpdate(response) {
return self.checkResponse(response, key);
}
return this.put(key, value, { version: this.revs[key] })
return this.put(key, value)
.then(checkUpdate);
};

View File

@ -85,7 +85,7 @@ define(
it("allows object creation", function () {
var model = { someKey: "some value" };
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1 }
data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1 }
}));
provider.createObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
@ -100,7 +100,7 @@ define(
it("allows object models to be read back", function () {
var model = { someKey: "some value" };
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1, "_source": model }
data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1, "_source": model }
}));
provider.readObject("testSpace", "abc").then(capture);
expect(mockHttp).toHaveBeenCalledWith({
@ -117,19 +117,19 @@ define(
// First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
data: { "_id": "abc", "_source": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 43, "_source": {} }
data: { "_id": "abc", "_seq_no": 1, "_source": {} }
}));
provider.updateObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "PUT",
params: { version: 42 },
params: undefined,
data: model
});
expect(capture.calls.mostRecent().args[0]).toBeTruthy();
@ -138,13 +138,13 @@ define(
it("allows object deletion", function () {
// First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
data: { "_id": "abc", "_source": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
data: { "_id": "abc", "_source": {} }
}));
provider.deleteObject("testSpace", "abc", {}).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
@ -167,13 +167,13 @@ define(
expect(capture).toHaveBeenCalledWith(undefined);
});
it("handles rejection due to version", function () {
it("handles rejection due to _seq_no", function () {
var model = { someKey: "some value" },
mockErrorCallback = jasmine.createSpy('error');
// First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
data: { "_id": "abc", "_seq_no": 1, "_source": {} }
}));
provider.readObject("testSpace", "abc");
@ -196,7 +196,7 @@ define(
// First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
data: { "_id": "abc", "_seq_no": 1, "_source": {} }
}));
provider.readObject("testSpace", "abc");

View File

@ -25,7 +25,7 @@
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
*/
define([
'../../../../src/api/objects/object-utils',
'objectUtils',
'lodash'
], function (
objectUtils,
@ -191,7 +191,7 @@ define([
}
var domainObject = objectUtils.toNewFormat(model, id);
var composition = _.find(this.openmct.composition.registry, function (p) {
var composition = this.openmct.composition.registry.find(p => {
return p.appliesTo(domainObject);
});

View File

@ -25,7 +25,7 @@
*/
define(
[
'../../../src/api/objects/object-utils',
'objectUtils',
'lodash'
],
function (
@ -235,7 +235,7 @@ define(
var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.indexBy(metadata.values(), 'key');
var sourceMap = _.keyBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
telemetryAPI.legacyProvider;
@ -300,7 +300,7 @@ define(
var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.indexBy(metadata.values(), 'key');
var sourceMap = _.keyBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
telemetryAPI.legacyProvider;

View File

@ -0,0 +1,621 @@
{
"header": {
"event": "Allocation failed - JavaScript heap out of memory",
"location": "OnFatalError",
"filename": "report.20200527.134750.93992.001.json",
"dumpEventTime": "2020-05-27T13:47:50Z",
"dumpEventTimeStamp": "1590612470877",
"processId": 93992,
"commandLine": [
"node",
"/Users/dtailor/Desktop/openmct/node_modules/.bin/karma",
"start",
"--single-run"
],
"nodejsVersion": "v11.9.0",
"wordSize": 64,
"componentVersions": {
"node": "11.9.0",
"v8": "7.0.276.38-node.16",
"uv": "1.25.0",
"zlib": "1.2.11",
"brotli": "1.0.7",
"ares": "1.15.0",
"modules": "67",
"nghttp2": "1.34.0",
"napi": "4",
"llhttp": "1.0.1",
"http_parser": "2.8.0",
"openssl": "1.1.1a",
"cldr": "34.0",
"icu": "63.1",
"tz": "2018e",
"unicode": "11.0",
"arch": "x64",
"platform": "darwin",
"release": "node"
},
"osVersion": "Darwin 18.7.0 Darwin Kernel Version 18.7.0: Thu Jan 23 06:52:12 PST 2020; root:xnu-4903.278.25~1/RELEASE_X86_64",
"machine": "Darwin 18.7.0 Darwin Kernel Version 18.7.0: Thu Jan 23 06:52:12 PST 2020; root:xnu-4903.278.25~1/RELEASE_X86_64tailor x86_64"
},
"javascriptStack": {
"message": "No stack.",
"stack": [
"Unavailable."
]
},
"nativeStack": [
" [pc=0x10013090e] report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, v8::Local<v8::String>) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x100063744] node::OnFatalError(char const*, char const*) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1001a8c47] v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1001a8be4] v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1005add42] v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1005b0273] v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1005ac7a8] v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1005aa965] v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1005b720c] v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1005b728f] v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x100586484] v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x1008389a4] v8::internal::Runtime_AllocateInNewSpace(int, v8::internal::Object**, v8::internal::Isolate*) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
" [pc=0x14acfddcfc7d] "
],
"javascriptHeap": {
"totalMemory": 1479229440,
"totalCommittedMemory": 1477309024,
"usedMemory": 1445511032,
"availableMemory": 50296592,
"memoryLimit": 1526909922,
"heapSpaces": {
"read_only_space": {
"memorySize": 524288,
"committedMemory": 42224,
"capacity": 515584,
"used": 33520,
"available": 482064
},
"new_space": {
"memorySize": 4194304,
"committedMemory": 4194288,
"capacity": 2062336,
"used": 59016,
"available": 2003320
},
"old_space": {
"memorySize": 305860608,
"committedMemory": 305138544,
"capacity": 283264904,
"used": 282942208,
"available": 322696
},
"code_space": {
"memorySize": 6291456,
"committedMemory": 5687328,
"capacity": 5237152,
"used": 5237152,
"available": 0
},
"map_space": {
"memorySize": 5255168,
"committedMemory": 5143024,
"capacity": 2523280,
"used": 2523280,
"available": 0
},
"large_object_space": {
"memorySize": 1157103616,
"committedMemory": 1157103616,
"capacity": 1202204368,
"used": 1154715856,
"available": 47488512
},
"new_large_object_space": {
"memorySize": 0,
"committedMemory": 0,
"capacity": 0,
"used": 0,
"available": 0
}
}
},
"resourceUsage": {
"userCpuSeconds": 43.1616,
"kernelCpuSeconds": 43.1616,
"cpuConsumptionPercent": 5.42705e-06,
"maxRss": 1966080000000,
"pageFaults": {
"IORequired": 245,
"IONotRequired": 832598
},
"fsActivity": {
"reads": 0,
"writes": 0
}
},
"libuv": [
],
"environmentVariables": {
"npm_config_save_dev": "",
"npm_config_legacy_bundling": "",
"npm_config_dry_run": "",
"npm_package_devDependencies_markdown_toc": "^0.11.7",
"npm_config_only": "",
"npm_config_browser": "",
"npm_config_viewer": "man",
"npm_config_commit_hooks": "true",
"npm_package_gitHead": "7126abe7ec1d66d3252f3598fbd6bd27217018bc",
"npm_config_also": "",
"npm_package_scripts_otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
"npm_package_devDependencies_minimist": "^1.1.1",
"npm_config_sign_git_commit": "",
"npm_config_rollback": "true",
"npm_package_devDependencies_fast_sass_loader": "1.4.6",
"TERM_PROGRAM": "Apple_Terminal",
"npm_config_usage": "",
"npm_config_audit": "true",
"npm_package_devDependencies_git_rev_sync": "^1.4.0",
"npm_package_devDependencies_file_loader": "^1.1.11",
"npm_package_devDependencies_d3_selection": "1.3.x",
"NODE": "/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node",
"npm_package_homepage": "https://github.com/nasa/openmct#readme",
"INIT_CWD": "/Users/dtailor/Desktop/openmct",
"NVM_CD_FLAGS": "",
"npm_config_globalignorefile": "/Users/dtailor/.nvm/versions/node/v11.9.0/etc/npmignore",
"npm_package_devDependencies_comma_separated_values": "^3.6.4",
"SHELL": "/bin/bash",
"TERM": "xterm-256color",
"npm_config_init_author_url": "",
"npm_config_shell": "/bin/bash",
"npm_config_maxsockets": "50",
"npm_package_devDependencies_vue_template_compiler": "2.5.6",
"npm_package_devDependencies_style_loader": "^1.0.1",
"npm_package_devDependencies_moment_duration_format": "^2.2.2",
"npm_config_parseable": "",
"npm_config_shrinkwrap": "true",
"npm_config_metrics_registry": "https://registry.npmjs.org/",
"TMPDIR": "/var/folders/ks/ytghmh9x4lj3cchr5km5lhkcb7v9y2/T/",
"npm_config_timing": "",
"npm_config_init_license": "ISC",
"npm_package_scripts_lint": "eslint platform example src --ext .js,.vue openmct.js",
"npm_package_devDependencies_d3_array": "1.2.x",
"Apple_PubSub_Socket_Render": "/private/tmp/com.apple.launchd.PsV6Dfq4Tm/Render",
"npm_config_if_present": "",
"npm_package_devDependencies_concurrently": "^3.6.1",
"TERM_PROGRAM_VERSION": "421.2",
"npm_package_scripts_jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
"npm_config_sign_git_tag": "",
"npm_config_init_author_email": "",
"npm_config_cache_max": "Infinity",
"npm_config_preid": "",
"npm_config_long": "",
"npm_config_local_address": "",
"npm_config_cert": "",
"npm_config_git_tag_version": "true",
"npm_package_devDependencies_exports_loader": "^0.7.0",
"TERM_SESSION_ID": "0630D2FA-BAC2-48D3-A21D-9AB58A79FB14",
"npm_config_noproxy": "",
"npm_config_registry": "https://registry.npmjs.org/",
"npm_config_fetch_retries": "2",
"npm_package_private": "true",
"npm_package_devDependencies_karma_jasmine": "^1.1.2",
"npm_package_repository_url": "git+https://github.com/nasa/openmct.git",
"npm_config_versions": "",
"npm_config_key": "",
"npm_config_message": "%s",
"npm_package_readmeFilename": "README.md",
"npm_package_devDependencies_painterro": "^0.2.65",
"npm_package_scripts_verify": "concurrently 'npm:test' 'npm:lint'",
"npm_package_devDependencies_webpack": "^4.16.2",
"npm_package_devDependencies_eventemitter3": "^1.2.0",
"npm_package_description": "The Open MCT core platform",
"USER": "dtailor",
"NVM_DIR": "/Users/dtailor/.nvm",
"npm_package_license": "Apache-2.0",
"npm_package_scripts_build_dev": "webpack",
"npm_package_devDependencies_webpack_cli": "^3.1.0",
"npm_package_devDependencies_location_bar": "^3.0.1",
"npm_package_devDependencies_jasmine_core": "^3.1.0",
"npm_config_globalconfig": "/Users/dtailor/.nvm/versions/node/v11.9.0/etc/npmrc",
"npm_package_devDependencies_karma": "^2.0.3",
"npm_config_prefer_online": "",
"npm_config_always_auth": "",
"npm_config_logs_max": "10",
"npm_package_devDependencies_angular": "1.7.9",
"SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.JH8E4KgH06/Listeners",
"npm_package_devDependencies_request": "^2.69.0",
"npm_package_devDependencies_eslint": "5.2.0",
"__CF_USER_TEXT_ENCODING": "0x167DA7C2:0x0:0x0",
"npm_execpath": "/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/bin/npm-cli.js",
"npm_config_global_style": "",
"npm_config_cache_lock_retries": "10",
"npm_config_cafile": "",
"npm_config_update_notifier": "true",
"npm_package_scripts_test_debug": "cross-env NODE_ENV=debug karma start --no-single-run",
"npm_package_devDependencies_glob": ">= 3.0.0",
"npm_config_heading": "npm",
"npm_config_audit_level": "low",
"npm_package_devDependencies_mini_css_extract_plugin": "^0.4.1",
"npm_package_devDependencies_copy_webpack_plugin": "^4.5.2",
"npm_config_read_only": "",
"npm_config_offline": "",
"npm_config_searchlimit": "20",
"npm_config_fetch_retry_mintimeout": "10000",
"npm_package_devDependencies_webpack_dev_middleware": "^3.1.3",
"npm_config_json": "",
"npm_config_access": "",
"npm_config_argv": "{\"remain\":[],\"cooked\":[\"run\",\"test\"],\"original\":[\"run\",\"test\"]}",
"npm_package_scripts_lint_fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
"npm_package_devDependencies_uuid": "^3.3.3",
"npm_package_devDependencies_karma_coverage": "^1.1.2",
"PATH": "/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/dtailor/Desktop/openmct/node_modules/.bin:/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/dtailor/Desktop/openmct/node_modules/.bin:/Users/dtailor/.nvm/versions/node/v11.9.0/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/local/bin:/Users/dtailor/.homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/dtailor/Applications/Visual Studio Code.app/Contents/Resources/app/bin:/opt/local/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/local/bin:/Users/dtailor/.homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin",
"npm_config_allow_same_version": "",
"npm_config_https_proxy": "",
"npm_config_engine_strict": "",
"npm_config_description": "true",
"npm_package_devDependencies_html2canvas": "^1.0.0-alpha.12",
"_": "/Users/dtailor/Desktop/openmct/node_modules/.bin/karma",
"npm_config_userconfig": "/Users/dtailor/.npmrc",
"npm_config_init_module": "/Users/dtailor/.npm-init.js",
"npm_package_author": "",
"npm_package_devDependencies_karma_chrome_launcher": "^2.2.0",
"npm_package_devDependencies_d3_scale": "1.0.x",
"npm_config_cidr": "",
"npm_package_devDependencies_printj": "^1.2.1",
"PWD": "/Users/dtailor/Desktop/openmct",
"npm_config_user": "377333698",
"npm_config_node_version": "11.9.0",
"npm_package_bugs_url": "https://github.com/nasa/openmct/issues",
"npm_package_scripts_test_watch": "karma start --no-single-run",
"npm_lifecycle_event": "test",
"npm_package_devDependencies_v8_compile_cache": "^1.1.0",
"npm_config_ignore_prepublish": "",
"npm_config_save": "true",
"npm_config_editor": "vi",
"npm_config_auth_type": "legacy",
"npm_package_repository_type": "git",
"npm_package_devDependencies_vue": "2.5.6",
"npm_package_devDependencies_marked": "^0.3.5",
"npm_package_devDependencies_angular_route": "1.4.14",
"npm_package_name": "openmct",
"LANG": "en_US.UTF-8",
"npm_config_script_shell": "",
"npm_config_tag": "latest",
"npm_config_global": "",
"npm_config_progress": "true",
"npm_package_scripts_start": "node app.js",
"npm_package_devDependencies_karma_coverage_istanbul_reporter": "^2.1.1",
"npm_config_ham_it_up": "",
"npm_config_searchstaleness": "900",
"npm_config_optional": "true",
"npm_package_scripts_docs": "npm run jsdoc ; npm run otherdoc",
"npm_package_devDependencies_istanbul_instrumenter_loader": "^3.0.1",
"XPC_FLAGS": "0x0",
"npm_config_save_prod": "",
"npm_config_force": "",
"npm_config_bin_links": "true",
"npm_package_devDependencies_moment": "2.25.3",
"npm_package_devDependencies_karma_webpack": "^3.0.0",
"npm_package_devDependencies_express": "^4.13.1",
"npm_config_searchopts": "",
"npm_package_devDependencies_d3_time": "1.0.x",
"FORCE_COLOR": "2",
"npm_config_node_gyp": "/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",
"npm_config_depth": "Infinity",
"npm_package_scripts_build_prod": "cross-env NODE_ENV=production webpack",
"npm_config_sso_poll_frequency": "500",
"npm_config_rebuild_bundle": "true",
"npm_package_version": "1.0.0-snapshot",
"XPC_SERVICE_NAME": "0",
"npm_config_unicode": "true",
"npm_package_devDependencies_jsdoc": "^3.3.2",
"SHLVL": "4",
"HOME": "/Users/dtailor",
"npm_config_fetch_retry_maxtimeout": "60000",
"npm_package_scripts_test": "karma start --single-run",
"npm_package_devDependencies_zepto": "^1.2.0",
"npm_package_devDependencies_eslint_plugin_vue": "^6.0.0",
"npm_config_ca": "",
"npm_config_tag_version_prefix": "v",
"npm_config_strict_ssl": "true",
"npm_config_sso_type": "oauth",
"npm_config_scripts_prepend_node_path": "warn-only",
"npm_config_save_prefix": "^",
"npm_config_loglevel": "notice",
"npm_package_devDependencies_lodash": "^3.10.1",
"npm_package_devDependencies_karma_cli": "^1.0.1",
"npm_package_devDependencies_d3_color": "1.0.x",
"npm_config_save_exact": "",
"npm_config_dev": "",
"npm_config_group": "1286109195",
"npm_config_fetch_retry_factor": "10",
"npm_package_devDependencies_webpack_hot_middleware": "^2.22.3",
"npm_package_devDependencies_cross_env": "^6.0.3",
"npm_package_devDependencies_babel_eslint": "8.2.6",
"HOMEBREW_PREFIX": "/Users/dtailor/.homebrew",
"npm_config_version": "",
"npm_config_prefer_offline": "",
"npm_config_cache_lock_stale": "60000",
"npm_config_otp": "",
"npm_config_cache_min": "10",
"npm_package_devDependencies_vue_loader": "^15.2.6",
"npm_config_searchexclude": "",
"npm_config_cache": "/Users/dtailor/.npm",
"npm_package_scripts_test_coverage": "./scripts/test-coverage.sh",
"npm_package_devDependencies_d3_interpolate": "1.1.x",
"npm_package_devDependencies_d3_format": "1.2.x",
"LOGNAME": "dtailor",
"npm_lifecycle_script": "karma start --single-run",
"npm_config_color": "true",
"npm_package_devDependencies_node_bourbon": "^4.2.3",
"npm_package_devDependencies_karma_sourcemap_loader": "^0.3.7",
"npm_package_devDependencies_karma_html_reporter": "^0.2.7",
"npm_config_proxy": "",
"npm_config_package_lock": "true",
"npm_package_devDependencies_d3_time_format": "2.1.x",
"npm_package_devDependencies_d3_axis": "1.0.x",
"npm_config_package_lock_only": "",
"npm_package_devDependencies_moment_timezone": "0.5.28",
"npm_config_save_optional": "",
"NVM_BIN": "/Users/dtailor/.nvm/versions/node/v11.9.0/bin",
"npm_config_ignore_scripts": "",
"npm_config_user_agent": "npm/6.5.0 node/v11.9.0 darwin x64",
"npm_package_devDependencies_imports_loader": "^0.8.0",
"npm_package_devDependencies_file_saver": "^1.3.8",
"npm_config_cache_lock_wait": "10000",
"npm_config_production": "",
"npm_package_scripts_build_watch": "webpack --watch",
"DISPLAY": "/private/tmp/com.apple.launchd.E3N8oC6RMf/org.macosforge.xquartz:0",
"npm_config_send_metrics": "",
"npm_config_save_bundle": "",
"npm_package_scripts_prepare": "npm run build:prod",
"npm_config_node_options": "",
"npm_config_umask": "0022",
"npm_config_init_version": "1.0.0",
"npm_package_devDependencies_split": "^1.0.0",
"npm_package_devDependencies_raw_loader": "^0.5.1",
"npm_config_init_author_name": "",
"npm_config_git": "git",
"npm_config_scope": "",
"npm_package_scripts_clean": "rm -rf ./dist",
"npm_package_devDependencies_node_sass": "^4.9.2",
"npm_package_devDependencies_css_loader": "^1.0.0",
"DISABLE_UPDATE_CHECK": "1",
"npm_config_onload_script": "",
"npm_config_unsafe_perm": "true",
"npm_config_tmp": "/var/folders/ks/ytghmh9x4lj3cchr5km5lhkcb7v9y2/T",
"npm_package_devDependencies_d3_collection": "1.0.x",
"npm_node_execpath": "/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node",
"npm_config_link": "",
"npm_config_prefix": "/Users/dtailor/.nvm/versions/node/v11.9.0",
"npm_package_devDependencies_html_loader": "^0.5.5"
},
"userLimits": {
"core_file_size_blocks": {
"soft": 0,
"hard": "unlimited"
},
"data_seg_size_kbytes": {
"soft": "unlimited",
"hard": "unlimited"
},
"file_size_blocks": {
"soft": "unlimited",
"hard": "unlimited"
},
"max_locked_memory_bytes": {
"soft": "unlimited",
"hard": "unlimited"
},
"max_memory_size_kbytes": {
"soft": "unlimited",
"hard": "unlimited"
},
"open_files": {
"soft": 24576,
"hard": "unlimited"
},
"stack_size_bytes": {
"soft": 8388608,
"hard": 67104768
},
"cpu_time_seconds": {
"soft": "unlimited",
"hard": "unlimited"
},
"max_user_processes": {
"soft": 1418,
"hard": 2128
},
"virtual_memory_kbytes": {
"soft": "unlimited",
"hard": "unlimited"
}
},
"sharedObjects": [
"/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node",
"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
"/usr/lib/libSystem.B.dylib",
"/usr/lib/libc++.1.dylib",
"/usr/lib/libobjc.A.dylib",
"/usr/lib/libDiagnosticMessagesClient.dylib",
"/usr/lib/libicucore.A.dylib",
"/usr/lib/libz.1.dylib",
"/usr/lib/libc++abi.dylib",
"/usr/lib/system/libcache.dylib",
"/usr/lib/system/libcommonCrypto.dylib",
"/usr/lib/system/libcompiler_rt.dylib",
"/usr/lib/system/libcopyfile.dylib",
"/usr/lib/system/libcorecrypto.dylib",
"/usr/lib/system/libdispatch.dylib",
"/usr/lib/system/libdyld.dylib",
"/usr/lib/system/libkeymgr.dylib",
"/usr/lib/system/liblaunch.dylib",
"/usr/lib/system/libmacho.dylib",
"/usr/lib/system/libquarantine.dylib",
"/usr/lib/system/libremovefile.dylib",
"/usr/lib/system/libsystem_asl.dylib",
"/usr/lib/system/libsystem_blocks.dylib",
"/usr/lib/system/libsystem_c.dylib",
"/usr/lib/system/libsystem_configuration.dylib",
"/usr/lib/system/libsystem_coreservices.dylib",
"/usr/lib/system/libsystem_darwin.dylib",
"/usr/lib/system/libsystem_dnssd.dylib",
"/usr/lib/system/libsystem_info.dylib",
"/usr/lib/system/libsystem_m.dylib",
"/usr/lib/system/libsystem_malloc.dylib",
"/usr/lib/system/libsystem_networkextension.dylib",
"/usr/lib/system/libsystem_notify.dylib",
"/usr/lib/system/libsystem_sandbox.dylib",
"/usr/lib/system/libsystem_secinit.dylib",
"/usr/lib/system/libsystem_kernel.dylib",
"/usr/lib/system/libsystem_platform.dylib",
"/usr/lib/system/libsystem_pthread.dylib",
"/usr/lib/system/libsystem_symptoms.dylib",
"/usr/lib/system/libsystem_trace.dylib",
"/usr/lib/system/libunwind.dylib",
"/usr/lib/system/libxpc.dylib",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices",
"/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics",
"/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO",
"/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LangAnalysis.framework/Versions/A/LangAnalysis",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis",
"/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight",
"/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface",
"/usr/lib/libxml2.2.dylib",
"/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate",
"/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
"/usr/lib/libcompression.dylib",
"/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
"/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay",
"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit",
"/System/Library/Frameworks/Metal.framework/Versions/A/Metal",
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders",
"/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport",
"/System/Library/Frameworks/Security.framework/Versions/A/Security",
"/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
"/usr/lib/libbsm.0.dylib",
"/usr/lib/liblzma.5.dylib",
"/usr/lib/libauto.dylib",
"/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration",
"/usr/lib/libarchive.2.dylib",
"/usr/lib/liblangid.dylib",
"/usr/lib/libCRFSuite.dylib",
"/usr/lib/libenergytrace.dylib",
"/usr/lib/system/libkxld.dylib",
"/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression",
"/usr/lib/libOpenScriptingUtil.dylib",
"/usr/lib/libcoretls.dylib",
"/usr/lib/libcoretls_cfhelpers.dylib",
"/usr/lib/libpam.2.dylib",
"/usr/lib/libsqlite3.dylib",
"/usr/lib/libxar.1.dylib",
"/usr/lib/libbz2.1.0.dylib",
"/usr/lib/libnetwork.dylib",
"/usr/lib/libapple_nghttp2.dylib",
"/usr/lib/libpcap.A.dylib",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices",
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList",
"/System/Library/Frameworks/NetFS.framework/Versions/A/NetFS",
"/System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth",
"/System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport",
"/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC",
"/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP",
"/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities",
"/usr/lib/libmecabra.dylib",
"/usr/lib/libmecab.1.0.0.dylib",
"/usr/lib/libgermantok.dylib",
"/usr/lib/libThaiTokenizer.dylib",
"/usr/lib/libChineseTokenizer.dylib",
"/usr/lib/libiconv.2.dylib",
"/usr/lib/libcharset.1.dylib",
"/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling",
"/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji",
"/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon",
"/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData",
"/usr/lib/libcmph.dylib",
"/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData",
"/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory",
"/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS",
"/usr/lib/libutil.dylib",
"/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement",
"/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement",
"/usr/lib/libxslt.1.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib",
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib",
"/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler",
"/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator",
"/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment",
"/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib",
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSCore.framework/Versions/A/MPSCore",
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSImage.framework/Versions/A/MPSImage",
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork",
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix",
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector",
"/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools",
"/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary",
"/usr/lib/libMobileGestalt.dylib",
"/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage",
"/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL",
"/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer",
"/System/Library/PrivateFrameworks/FaceCore.framework/Versions/A/FaceCore",
"/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL",
"/usr/lib/libFosl_dynamic.dylib",
"/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontParser.dylib",
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib",
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib",
"/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib",
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib",
"/usr/lib/libcups.2.dylib",
"/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos",
"/System/Library/Frameworks/GSS.framework/Versions/A/GSS",
"/usr/lib/libresolv.9.dylib",
"/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal",
"/usr/lib/libheimdal-asn1.dylib",
"/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory",
"/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth",
"/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation",
"/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio",
"/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox",
"/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce",
"/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices",
"/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard"
]
}

2
scripts/test-coverage.sh Executable file
View File

@ -0,0 +1,2 @@
export NODE_OPTIONS=--max_old_space_size=4096
cross-env COVERAGE=true karma start --single-run

View File

@ -28,7 +28,7 @@ define([
'./api/api',
'./api/overlays/OverlayAPI',
'./selection/Selection',
'./api/objects/object-utils',
'objectUtils',
'./plugins/plugins',
'./adapter/indicators/legacy-indicators-plugin',
'./plugins/buildInfo/plugin',
@ -249,7 +249,7 @@ define([
this.legacyRegistry = new BundleRegistry();
installDefaultBundles(this.legacyRegistry);
// Plugin's that are installed by default
// Plugins that are installed by default
this.install(this.plugins.Plot());
this.install(this.plugins.TelemetryTable());
@ -266,6 +266,7 @@ define([
this.install(this.plugins.WebPage());
this.install(this.plugins.Condition());
this.install(this.plugins.ConditionWidget());
this.install(this.plugins.NotificationIndicator());
}
MCT.prototype = Object.create(EventEmitter.prototype);
@ -350,17 +351,13 @@ define([
* @param {HTMLElement} [domElement] the DOM element in which to run
* MCT; if undefined, MCT will be run in the body of the document
*/
MCT.prototype.start = function (domElement) {
MCT.prototype.start = function (domElement = document.body, isHeadlessMode = false) {
if (!this.plugins.DisplayLayout._installed) {
this.install(this.plugins.DisplayLayout({
showAsView: ['summary-widget']
}));
}
if (!domElement) {
domElement = document.body;
}
this.element = domElement;
this.legacyExtension('runs', {
@ -400,24 +397,31 @@ define([
// something has depended upon objectService. Cool, right?
this.$injector.get('objectService');
var appLayout = new Vue({
components: {
'Layout': Layout.default
},
provide: {
openmct: this
},
template: '<Layout ref="layout"></Layout>'
});
domElement.appendChild(appLayout.$mount().$el);
if (!isHeadlessMode) {
var appLayout = new Vue({
components: {
'Layout': Layout.default
},
provide: {
openmct: this
},
template: '<Layout ref="layout"></Layout>'
});
domElement.appendChild(appLayout.$mount().$el);
this.layout = appLayout.$refs.layout;
Browse(this);
this.layout = appLayout.$refs.layout;
Browse(this);
}
this.router.start();
this.emit('start');
}.bind(this));
};
MCT.prototype.startHeadless = function () {
let unreachableNode = document.createElement('div');
return this.start(unreachableNode, true);
}
/**
* Install a plugin in MCT.
*

View File

@ -21,11 +21,11 @@
*****************************************************************************/
define([
'./MCT',
'./plugins/plugins',
'legacyRegistry'
], function (MCT, plugins, legacyRegistry) {
xdescribe("MCT", function () {
'legacyRegistry',
'testUtils'
], function (plugins, legacyRegistry, testUtils) {
describe("MCT", function () {
var openmct;
var mockPlugin;
var mockPlugin2;
@ -38,7 +38,7 @@ define([
mockListener = jasmine.createSpy('listener');
oldBundles = legacyRegistry.list();
openmct = new MCT();
openmct = testUtils.createOpenMct();
openmct.install(mockPlugin);
openmct.install(mockPlugin2);
@ -63,8 +63,11 @@ define([
});
describe("start", function () {
beforeEach(function () {
openmct.start();
let appHolder;
beforeEach(function (done) {
appHolder = document.createElement("div");
openmct.on('start', done);
openmct.start(appHolder);
});
it("calls plugins for configuration", function () {
@ -75,25 +78,51 @@ define([
it("emits a start event", function () {
expect(mockListener).toHaveBeenCalled();
});
it("Renders the application into the provided container element", function () {
let openMctShellElements = appHolder.querySelectorAll('div.l-shell');
expect(openMctShellElements.length).toBe(1);
});
});
describe("startHeadless", function () {
beforeEach(function (done) {
openmct.on('start', done);
openmct.startHeadless();
});
it("calls plugins for configuration", function () {
expect(mockPlugin).toHaveBeenCalledWith(openmct);
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
});
it("emits a start event", function () {
expect(mockListener).toHaveBeenCalled();
});
it("Does not render Open MCT", function () {
let openMctShellElements = document.body.querySelectorAll('div.l-shell');
expect(openMctShellElements.length).toBe(0);
});
});
describe("setAssetPath", function () {
var testAssetPath;
beforeEach(function () {
testAssetPath = "some/path";
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
openmct.setAssetPath(testAssetPath);
});
it("internally configures the path for assets", function () {
expect(openmct.legacyExtension).toHaveBeenCalledWith(
'constants',
{
key: "ASSETS_PATH",
value: testAssetPath
}
);
it("configures the path for assets", function () {
testAssetPath = "some/path/";
openmct.setAssetPath(testAssetPath);
expect(openmct.getAssetPath()).toBe(testAssetPath);
});
it("adds a trailing /", function () {
testAssetPath = "some/path";
openmct.setAssetPath(testAssetPath);
expect(openmct.getAssetPath()).toBe(testAssetPath + "/");
});
});
});

View File

@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (objectUtils) {
function ActionDialogDecorator(mct, actionService) {
this.mct = mct;

View File

@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['../../api/objects/object-utils'], function (objectUtils) {
define(['objectUtils'], function (objectUtils) {
function AdapterCapability(domainObject) {
this.domainObject = domainObject;
}

View File

@ -24,7 +24,7 @@
* Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14.
*/
define([
'../../api/objects/object-utils',
'objectUtils',
'../../../platform/core/src/capabilities/ContextualDomainObject'
], function (objectUtils, ContextualDomainObject) {
function AlternateCompositionCapability($injector, domainObject) {

View File

@ -31,6 +31,7 @@ define([
var capability = viewConstructor(domainObject);
var oldInvoke = capability.invoke.bind(capability);
/* eslint-disable you-dont-need-lodash-underscore/map */
capability.invoke = function () {
var availableViews = oldInvoke();
var newDomainObject = capability
@ -52,6 +53,8 @@ define([
.map('view')
.value();
};
/* eslint-enable you-dont-need-lodash-underscore/map */
return capability;
};
}

View File

@ -22,7 +22,7 @@
define([
'../capabilities/AlternateCompositionCapability',
'../../api/objects/object-utils'
'objectUtils'
], function (
AlternateCompositionCapability,
objectUtils

View File

@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (
utils
) {

View File

@ -78,7 +78,7 @@ define([
};
TimeSettingsURLHandler.prototype.parseQueryParams = function () {
var searchParams = _.pick(this.$location.search(), _.values(SEARCH));
var searchParams = _.pick(this.$location.search(), Object.values(SEARCH));
var parsedParams = {
clock: searchParams[SEARCH.MODE],
timeSystem: searchParams[SEARCH.TIME_SYSTEM]

View File

@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (
utils
) {

View File

@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (
objectUtils
) {

View File

@ -1,7 +1,7 @@
define([
'./LegacyViewProvider',
'./TypeInspectorViewProvider',
'../../api/objects/object-utils'
'objectUtils'
], function (
LegacyViewProvider,
TypeInspectorViewProvider,

View File

@ -70,7 +70,7 @@ define([
* @memberof module:openmct.CompositionAPI#
*/
CompositionAPI.prototype.get = function (domainObject) {
var provider = _.find(this.registry, function (p) {
var provider = this.registry.find(p => {
return p.appliesTo(domainObject);
});

View File

@ -122,7 +122,7 @@ define([
throw new Error('Event not supported by composition: ' + event);
}
var index = _.findIndex(this.listeners[event], function (l) {
var index = this.listeners[event].findIndex(l => {
return l.callback === callback && l.context === context;
});

View File

@ -22,7 +22,7 @@
define([
'lodash',
'../objects/object-utils'
'objectUtils'
], function (
_,
objectUtils
@ -143,7 +143,7 @@ define([
var keyString = objectUtils.makeKeyString(domainObject.identifier);
var objectListeners = this.listeningTo[keyString];
var index = _.findIndex(objectListeners[event], function (l) {
var index = objectListeners[event].findIndex(l => {
return l.callback === callback && l.context === context;
});
@ -196,8 +196,8 @@ define([
* @private
*/
DefaultCompositionProvider.prototype.includes = function (parent, childId) {
return parent.composition.findIndex(composee =>
this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
return parent.composition.some(composee =>
this.publicAPI.objects.areIdsEqual(composee, childId));
};
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {

View File

@ -128,6 +128,11 @@ export default class NotificationAPI extends EventEmitter {
return this._notify(notificationModel);
}
dismissAllNotifications() {
this.notifications = [];
this.emit('dismiss-all');
}
/**
* Minimize a notification. The notification will still be available
* from the notification list. Typically notifications with a

View File

@ -21,7 +21,7 @@
*****************************************************************************/
define([
'./object-utils.js',
'objectUtils',
'lodash'
], function (
utils,

View File

@ -22,7 +22,7 @@
define([
'lodash',
'./object-utils',
'objectUtils',
'./MutableObject',
'./RootRegistry',
'./RootObjectProvider',

View File

@ -43,7 +43,7 @@ define([
}
RootRegistry.prototype.addRoot = function (key) {
if (isKey(key) || (_.isArray(key) && _.every(key, isKey))) {
if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
this.providers.push(function () {
return key;
});

View File

@ -1,5 +1,5 @@
define([
'../object-utils'
'objectUtils'
], function (
objectUtils
) {

View File

@ -85,9 +85,9 @@ define([
value: +e.value
};
}), 'e.value');
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values);
valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
valueMetadata.max = Math.max(valueMetadata.values);
valueMetadata.min = Math.min(valueMetadata.values);
}
valueMetadatas.push(valueMetadata);
@ -103,7 +103,7 @@ define([
var metadata = domainObject.telemetry || {};
if (this.typeHasTelemetry(domainObject)) {
var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
_.extend(metadata, typeMetadata);
Object.assign(metadata, typeMetadata);
if (!metadata.values) {
metadata.values = valueMetadatasFromOldFormat(metadata);
}

View File

@ -24,7 +24,7 @@ define([
'./TelemetryMetadataManager',
'./TelemetryValueFormatter',
'./DefaultMetadataProvider',
'../objects/object-utils',
'objectUtils',
'lodash'
], function (
TelemetryMetadataManager,
@ -370,7 +370,7 @@ define([
TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) {
var options = metadatas.map(function (metadata) {
var values = metadata.valuesForHints(hints);
return _.indexBy(values, 'key');
return _.keyBy(values, 'key');
}).reduce(function (a, b) {
var results = {};
Object.keys(a).forEach(function (key) {
@ -383,7 +383,7 @@ define([
var sortKeys = hints.map(function (h) {
return 'hints.' + h;
});
return _.sortByAll(options, sortKeys);
return _.sortBy(options, sortKeys);
};
/**

View File

@ -57,13 +57,13 @@ define([
if (valueMetadata.format === 'enum') {
if (!valueMetadata.values) {
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
}
if (!valueMetadata.hasOwnProperty('max')) {
valueMetadata.max = _.max(valueMetadata.values) + 1;
valueMetadata.max = Math.max(valueMetadata.values) + 1;
}
if (!valueMetadata.hasOwnProperty('min')) {
valueMetadata.min = _.min(valueMetadata.values) - 1;
valueMetadata.min = Math.min(valueMetadata.values) - 1;
}
}
@ -121,7 +121,7 @@ define([
return metadata.hints[hint];
}
});
return _.sortByAll(matchingMetadata, ...iteratees);
return _.sortBy(matchingMetadata, ...iteratees);
};
TelemetryMetadataManager.prototype.getFilterableValues = function () {

View File

@ -75,7 +75,7 @@ export default {
this.items.push(item);
},
removeItem(identifier) {
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
let index = this.items.findIndex(item => this.openmct.objects.makeKeyString(identifier) === item.key);
this.items.splice(index, 1);
},

View File

@ -102,7 +102,7 @@ export default {
this.compositions.push({composition, addCallback, removeCallback});
},
removePrimary(identifier) {
let index = _.findIndex(this.primaryTelemetryObjects, (primary) => this.openmct.objects.makeKeyString(identifier) === primary.key),
let index = this.primaryTelemetryObjects.findIndex(primary => this.openmct.objects.makeKeyString(identifier) === primary.key),
primary = this.primaryTelemetryObjects[index];
this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
@ -130,7 +130,7 @@ export default {
removeSecondary(primary) {
return (identifier) => {
let array = this.secondaryTelemetryObjects[primary.key],
index = _.findIndex(array, (secondary) => this.openmct.objects.makeKeyString(identifier) === secondary.key);
index = array.findIndex(secondary => this.openmct.objects.makeKeyString(identifier) === secondary.key);
array.splice(index, 1);

View File

@ -69,18 +69,19 @@ export default class ConditionClass extends EventEmitter {
console.log('no data received');
return;
}
if (!this.isTelemetryUsed(datum.id)) {
return;
}
this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects);
} else {
criterion.getResult(datum);
}
});
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
if (this.isTelemetryUsed(datum.id)) {
this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects);
} else {
criterion.getResult(datum);
}
});
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
}
}
isAnyOrAllTelemetry(criterion) {
@ -206,7 +207,7 @@ export default class ConditionClass extends EventEmitter {
let latestTimestamp;
let criteriaResults = {};
const criteriaRequests = this.criteria
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects}));
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
return Promise.all(criteriaRequests)
.then(results => {

View File

@ -55,9 +55,8 @@ export default class ConditionManager extends EventEmitter {
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
this.subscriptions[id] = this.openmct.telemetry.subscribe(
endpoint,
this.telemetryReceived.bind(this, id)
this.telemetryReceived.bind(this, endpoint)
);
// TODO check if this is needed
this.updateConditionTelemetry();
}
@ -71,6 +70,7 @@ export default class ConditionManager extends EventEmitter {
this.subscriptions[id]();
delete this.subscriptions[id];
delete this.telemetryObjects[id];
this.removeConditionTelemetry();
}
initialize() {
@ -86,6 +86,30 @@ export default class ConditionManager extends EventEmitter {
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
}
removeConditionTelemetry() {
let conditionsChanged = false;
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
if (!isAnyAllTelemetry) {
const found = Object.values(this.telemetryObjects).find((telemetryObject) => {
return this.openmct.objects.areIdsEqual(telemetryObject.identifier, criterion.telemetry);
});
if (!found) {
criterion.telemetry = '';
criterion.metadata = '';
criterion.input = [];
criterion.operation = '';
conditionsChanged = true;
}
}
});
});
if (conditionsChanged) {
this.persistConditions();
}
}
updateCondition(conditionConfiguration, index) {
let condition = this.conditionClassCollection[index];
condition.update(conditionConfiguration);
@ -234,9 +258,13 @@ export default class ConditionManager extends EventEmitter {
this.openmct.time.timeSystem()
);
});
const currentCondition = this.getCurrentConditionLAD(conditionResults);
return Object.assign(
if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
return [];
}
const currentCondition = this.getCurrentConditionLAD(conditionResults);
const currentOutput = Object.assign(
{
output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier,
@ -244,12 +272,30 @@ export default class ConditionManager extends EventEmitter {
},
latestTimestamp
);
return [currentOutput];
});
});
}
telemetryReceived(id, datum) {
const normalizedDatum = this.createNormalizedDatum(datum, id);
isTelemetryUsed(endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
for(const condition of this.conditionClassCollection) {
if (condition.isTelemetryUsed(id)) {
return true;
}
}
return false;
}
telemetryReceived(endpoint, datum) {
if (!this.isTelemetryUsed(endpoint)) {
return;
}
const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
const timeSystemKey = this.openmct.time.timeSystem().key;
let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
@ -283,8 +329,11 @@ export default class ConditionManager extends EventEmitter {
return data;
}
createNormalizedDatum(telemetryDatum, id) {
const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => {
createNormalizedDatum(telemetryDatum, endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const testValue = this.getTestData(metadatum);
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);

View File

@ -54,13 +54,22 @@ export default class ConditionSetMetadataProvider {
return {
values: this.getDomains().concat([
{
name: 'Output',
key: 'output',
format: 'enum',
key: "state",
source: "output",
name: "State",
format: "enum",
enumerations: enumerations,
hints: {
range: 1
}
},
{
key: "output",
name: "Value",
format: "string",
hints: {
range: 2
}
}
])
};

View File

@ -45,7 +45,7 @@ export default class ConditionSetTelemetryProvider {
return conditionManager.requestLADConditionSetOutput()
.then(latestOutput => {
return latestOutput ? [latestOutput] : [];
return latestOutput;
});
}

View File

@ -47,17 +47,24 @@ describe("The condition", function () {
name: "Test Object",
telemetry: {
values: [{
key: "some-key",
name: "Some attribute",
key: "value",
name: "Value",
hints: {
range: 2
}
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
}, {
key: "some-other-key",
name: "Another attribute",
hints: {
range: 1
}
key: "testSource",
source: "value",
name: "Test",
format: "string"
}]
}
};
@ -136,4 +143,38 @@ describe("The condition", function () {
expect(result).toBeTrue();
expect(conditionObj.criteria.length).toEqual(0);
});
it("gets the result of a condition when new telemetry data is received", function () {
conditionObj.getResult({
value: '0',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(conditionObj.result).toBeTrue();
});
it("gets the result of a condition when new telemetry data is received", function () {
conditionObj.getResult({
value: '1',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(conditionObj.result).toBeFalse();
});
it("keeps the old result new telemetry data is not used by it", function () {
conditionObj.getResult({
value: '0',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(conditionObj.result).toBeTrue();
conditionObj.getResult({
value: '1',
utc: 'Hi',
id: '1234'
});
expect(conditionObj.result).toBeTrue();
});
});

View File

@ -23,10 +23,14 @@
import EventEmitter from 'EventEmitter';
export default class StyleRuleManager extends EventEmitter {
constructor(styleConfiguration, openmct, callback) {
constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
super();
this.openmct = openmct;
this.callback = callback;
if (suppressSubscriptionOnEdit) {
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
this.isEditing = this.openmct.editor.editing;
}
if (styleConfiguration) {
this.initialize(styleConfiguration);
if (styleConfiguration.conditionSetIdentifier) {
@ -37,9 +41,25 @@ export default class StyleRuleManager extends EventEmitter {
}
}
toggleSubscription(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetIdentifier) {
this.applySelectedConditionStyle();
}
} else if (this.conditionSetIdentifier) {
this.subscribeToConditionSet();
}
}
initialize(styleConfiguration) {
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
this.staticStyle = styleConfiguration.staticStyle;
this.selectedConditionId = styleConfiguration.selectedConditionId;
this.defaultConditionId = styleConfiguration.defaultConditionId;
this.updateConditionStylesMap(styleConfiguration.styles || []);
}
@ -54,7 +74,7 @@ export default class StyleRuleManager extends EventEmitter {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
});
}
@ -66,9 +86,13 @@ export default class StyleRuleManager extends EventEmitter {
let isNewConditionSet = !this.conditionSetIdentifier ||
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
this.initialize(styleConfiguration);
//Only resubscribe if the conditionSet has changed.
if (isNewConditionSet) {
this.subscribeToConditionSet();
if (this.isEditing) {
this.applySelectedConditionStyle();
} else {
//Only resubscribe if the conditionSet has changed.
if (isNewConditionSet) {
this.subscribeToConditionSet();
}
}
}
}
@ -103,6 +127,16 @@ export default class StyleRuleManager extends EventEmitter {
}
}
applySelectedConditionStyle() {
const conditionId = this.selectedConditionId || this.defaultConditionId;
if (!conditionId) {
this.applyStaticStyle();
} else if (this.conditionalStyleMap[conditionId]) {
this.currentStyle = this.conditionalStyleMap[conditionId];
this.updateDomainObjectStyle();
}
}
applyStaticStyle() {
if (this.staticStyle) {
this.currentStyle = this.staticStyle.style;

View File

@ -30,6 +30,7 @@
>
<div class="c-condition-h__drop-target"></div>
<div v-if="isEditing"
:class="{'is-current': condition.id === currentConditionId}"
class="c-condition c-condition--edit"
>
<!-- Edit view -->
@ -167,6 +168,7 @@
</div>
<div v-else
class="c-condition c-condition--browse"
:class="{'is-current': condition.id === currentConditionId}"
>
<!-- Browse view -->
<div class="c-condition__header">
@ -199,6 +201,10 @@ export default {
ConditionDescription
},
props: {
currentConditionId: {
type: String,
default: ''
},
condition: {
type: Object,
required: true

View File

@ -58,6 +58,7 @@
<Condition v-for="(condition, index) in conditionCollection"
:key="condition.id"
:condition="condition"
:current-condition-id="currentConditionId"
:condition-index="index"
:telemetry="telemetryObjs"
:is-editing="isEditing"
@ -107,7 +108,8 @@ export default {
moveIndex: undefined,
isDragging: false,
defaultOutput: undefined,
dragCounter: 0
dragCounter: 0,
currentConditionId: ''
};
},
watch: {
@ -145,6 +147,7 @@ export default {
},
methods: {
handleConditionSetResultUpdated(data) {
this.currentConditionId = data.conditionId;
this.$emit('conditionSetResultUpdated', data)
},
observeForChanges() {
@ -197,7 +200,7 @@ export default {
this.$emit('telemetryUpdated', this.telemetryObjs);
},
removeTelemetryObject(identifier) {
let index = _.findIndex(this.telemetryObjs, (obj) => {
let index = this.telemetryObjs.findIndex(obj => {
let objId = this.openmct.objects.makeKeyString(obj.identifier);
let id = this.openmct.objects.makeKeyString(identifier);
return objId === id;

View File

@ -190,6 +190,7 @@
}
.c-condition {
border: 1px solid transparent;
flex-direction: column;
min-width: 400px;
@ -234,6 +235,12 @@
&__summary {
flex: 1 1 auto;
}
&.is-current {
$c: $colorBodyFg;
border-color: rgba($c, 0.2);
background: rgba($c, 0.2);
}
}
/***************************** CONDITION DEFINITION, EDITING */

View File

@ -79,6 +79,8 @@
<div v-for="(conditionStyle, index) in conditionalStyles"
:key="index"
class="c-inspect-styles__condition"
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
>
<condition-error :show-label="true"
:condition="getCondition(conditionStyle.conditionId)"
@ -106,6 +108,7 @@ import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import Vue from 'vue';
import PreviewAction from "@/ui/preview/PreviewAction.js";
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty';
export default {
name: 'ConditionalStylesView',
@ -126,7 +129,8 @@ export default {
isEditing: this.openmct.editor.isEditing(),
conditions: undefined,
conditionsLoaded: false,
navigateToPath: ''
navigateToPath: '',
selectedConditionId: ''
}
},
destroyed() {
@ -191,6 +195,9 @@ export default {
if (this.stopObservingItems) {
this.stopObservingItems();
}
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
},
initialize(conditionSetDomainObject) {
//If there are new conditions in the conditionSet we need to set those styles to default
@ -200,6 +207,13 @@ export default {
},
setEditState(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
} else {
this.subscribeToConditionSet();
}
},
addConditionSet() {
let conditionSetDomainObject;
@ -270,33 +284,44 @@ export default {
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.itemId) {
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
domainObjectStyles[this.itemId].selectedConditionId = undefined;
domainObjectStyles[this.itemId].defaultConditionId = undefined;
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
domainObjectStyles[this.itemId].styles = undefined;
delete domainObjectStyles[this.itemId].styles;
if (_.isEmpty(domainObjectStyles[this.itemId])) {
if (isEmpty(domainObjectStyles[this.itemId])) {
delete domainObjectStyles[this.itemId];
}
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
domainObjectStyles.selectedConditionId = undefined;
domainObjectStyles.defaultConditionId = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
if (_.isEmpty(domainObjectStyles)) {
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(domainObjectStyles);
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
},
updateDomainObjectItemStyles(newItems) {
//check that all items that have been styles still exist. Otherwise delete those styles
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
let itemsToRemove = [];
let keys = Object.keys(domainObjectStyles);
//TODO: Need an easier way to find which properties are itemIds
keys.forEach((key) => {
if ((key !== 'styles') &&
const keyIsItemId = (key !== 'styles') &&
(key !== 'staticStyle') &&
(key !== 'conditionSetIdentifier')) {
(key !== 'defaultConditionId') &&
(key !== 'selectedConditionId') &&
(key !== 'conditionSetIdentifier');
if (keyIsItemId) {
if (!(newItems.find(item => item.id === key))) {
itemsToRemove.push(key);
}
@ -313,7 +338,7 @@ export default {
delete domainObjectStyles[this.itemId];
}
});
if (_.isEmpty(domainObjectStyles)) {
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(domainObjectStyles);
@ -324,6 +349,9 @@ export default {
}
let conditionalStyles = [];
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
if (conditionConfiguration.isDefault) {
this.selectedConditionId = conditionConfiguration.id;
}
this.conditions[conditionConfiguration.id] = conditionConfiguration;
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
if (foundStyle) {
@ -339,7 +367,27 @@ export default {
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
this.conditionalStyles = conditionalStyles;
this.conditionsLoaded = true;
this.persist(this.getDomainObjectConditionalStyle());
this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
if (!this.isEditing) {
this.subscribeToConditionSet();
}
},
subscribeToConditionSet() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetDomainObject) {
this.openmct.telemetry.request(this.conditionSetDomainObject)
.then(output => {
if (output && output.length) {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
}
},
handleConditionSetResultUpdated(resultData) {
this.selectedConditionId = resultData ? resultData.conditionId : '';
},
initializeStaticStyle(objectStyles) {
let staticStyle = objectStyles && objectStyles.staticStyle;
@ -364,14 +412,19 @@ export default {
let found = this.findStyleByConditionId(conditionStyle.conditionId);
if (found) {
found.style = conditionStyle.style;
this.selectedConditionId = found.conditionId;
this.persist(this.getDomainObjectConditionalStyle());
}
},
getDomainObjectConditionalStyle() {
getDomainObjectConditionalStyle(defaultConditionId) {
let objectStyle = {
styles: this.conditionalStyles,
staticStyle: this.staticStyle
staticStyle: this.staticStyle,
selectedConditionId: this.selectedConditionId
};
if (defaultConditionId) {
objectStyle.defaultConditionId = defaultConditionId;
}
if (this.conditionSetDomainObject) {
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
}
@ -393,6 +446,10 @@ export default {
getCondition(id) {
return this.conditions ? this.conditions[id] : {};
},
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.persist(this.getDomainObjectConditionalStyle());
},
persist(style) {
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
}

View File

@ -50,6 +50,7 @@
import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty';
export default {
name: 'MultiSelectStylesView',
@ -178,7 +179,7 @@ export default {
domainObjectStyles[itemId] = undefined;
delete domainObjectStyles[this.itemId];
if (_.isEmpty(domainObjectStyles)) {
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(this.domainObject, domainObjectStyles);
@ -239,7 +240,7 @@ export default {
if (this.isStaticAndConditionalStyles) {
this.removeConditionalStyles(domainObjectStyles, item.id);
}
if (_.isEmpty(itemStaticStyle)) {
if (isEmpty(itemStaticStyle)) {
itemStaticStyle = undefined;
domainObjectStyles[item.id] = undefined;
} else {

View File

@ -0,0 +1,601 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
<div class="c-inspector__styles c-inspect-styles">
<div v-if="isStaticAndConditionalStyles"
class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
>
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
</div>
<template v-if="!conditionSetDomainObject">
<div class="c-inspect-styles__header">
Object Style
</div>
<div class="c-inspect-styles__content">
<div v-if="staticStyle"
class="c-inspect-styles__style"
>
<style-editor class="c-inspect-styles__editor"
:style-item="staticStyle"
:is-editing="isEditing"
:mixed-styles="mixedStyles"
@persist="updateStaticStyle"
/>
</div>
<button
id="addConditionSet"
class="c-button c-button--major c-toggle-styling-button labeled"
@click="addConditionSet"
>
<span class="c-cs-button__label">Use Conditional Styling...</span>
</button>
</div>
</template>
<template v-else>
<div class="c-inspect-styles__header">
Conditional Object Styles
</div>
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
<a v-if="conditionSetDomainObject"
class="c-object-label icon-conditional"
:href="navigateToPath"
@click="navigateOrPreview"
>
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
</a>
<template v-if="isEditing">
<button
id="changeConditionSet"
class="c-button labeled"
@click="addConditionSet"
>
<span class="c-button__label">Change...</span>
</button>
<button class="c-click-icon icon-x"
title="Remove conditional styles"
@click="removeConditionSet"
></button>
</template>
</div>
<div v-if="conditionsLoaded"
class="c-inspect-styles__conditions"
>
<div v-for="(conditionStyle, index) in conditionalStyles"
:key="index"
class="c-inspect-styles__condition"
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
>
<condition-error :show-label="true"
:condition="getCondition(conditionStyle.conditionId)"
/>
<condition-description :show-label="true"
:condition="getCondition(conditionStyle.conditionId)"
/>
<style-editor class="c-inspect-styles__editor"
:style-item="conditionStyle"
:is-editing="isEditing"
@persist="updateConditionalStyle"
/>
</div>
</div>
</template>
</div>
</template>
<script>
import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
import ConditionSetSelectorDialog from "@/plugins/condition/components/inspector/ConditionSetSelectorDialog.vue";
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
import Vue from 'vue';
export default {
name: 'StylesView',
components: {
StyleEditor,
ConditionError,
ConditionDescription
},
inject: [
'openmct',
'selection'
],
data() {
return {
staticStyle: undefined,
isEditing: this.openmct.editor.isEditing(),
mixedStyles: [],
isStaticAndConditionalStyles: false,
conditionalStyles: [],
conditionSetDomainObject: undefined,
conditions: undefined,
conditionsLoaded: false,
navigateToPath: '',
selectedConditionId: ''
}
},
destroyed() {
this.removeListeners();
},
mounted() {
this.items = [];
this.previewAction = new PreviewAction(this.openmct);
this.isMultipleSelection = this.selection.length > 1;
this.getObjectsAndItemsFromSelection();
if (!this.isMultipleSelection) {
let objectStyles = this.getObjectStyles();
this.initializeStaticStyle(objectStyles);
if (objectStyles && objectStyles.conditionSetIdentifier) {
this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
this.conditionalStyles = objectStyles.styles;
}
} else {
this.initializeStaticStyle();
}
this.openmct.editor.on('isEditing', this.setEditState);
},
methods: {
getObjectStyles() {
let objectStyles;
if (this.domainObjectsById) {
const domainObject = Object.values(this.domainObjectsById)[0];
if (domainObject.configuration && domainObject.configuration.objectStyles) {
objectStyles = domainObject.configuration.objectStyles;
}
} else if (this.items.length) {
const itemId = this.items[0].id;
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles && this.domainObject.configuration.objectStyles[itemId]) {
objectStyles = this.domainObject.configuration.objectStyles[itemId];
}
} else if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
objectStyles = this.domainObject.configuration.objectStyles;
}
return objectStyles;
},
setEditState(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
} else {
this.subscribeToConditionSet();
}
},
enableConditionSetNav() {
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
(objectPath) => {
this.objectPath = objectPath;
this.navigateToPath = '#/browse/' + this.objectPath
.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
.reverse()
.join('/');
}
);
},
navigateOrPreview(event) {
// If editing, display condition set in Preview overlay; otherwise nav to it while browsing
if (this.openmct.editor.isEditing()) {
event.preventDefault();
this.previewAction.invoke(this.objectPath);
}
},
isItemType(type, item) {
return item && (item.type === type);
},
hasConditionalStyle(domainObject, layoutItem) {
const id = layoutItem ? layoutItem.id : undefined;
return getConditionSetIdentifierForItem(domainObject, id) !== undefined;
},
getObjectsAndItemsFromSelection() {
let domainObject;
let subObjects = [];
let itemsWithConditionalStyles = 0;
//multiple selection
let itemInitialStyles = [];
let itemStyle;
this.selection.forEach((selectionItem) => {
const item = selectionItem[0].context.item;
const layoutItem = selectionItem[0].context.layoutItem;
const isChildItem = selectionItem.length > 1;
if (!isChildItem) {
domainObject = item;
itemStyle = getApplicableStylesForItem(item);
if (this.hasConditionalStyle(item)) {
itemsWithConditionalStyles += 1;
}
} else {
this.canHide = true;
domainObject = selectionItem[1].context.item;
if (item && !layoutItem || this.isItemType('subobject-view', layoutItem)) {
subObjects.push(item);
itemStyle = getApplicableStylesForItem(item);
if (this.hasConditionalStyle(item)) {
itemsWithConditionalStyles += 1;
}
} else {
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
this.items.push({
id: layoutItem.id,
applicableStyles: itemStyle
});
if (this.hasConditionalStyle(item, layoutItem)) {
itemsWithConditionalStyles += 1;
}
}
}
itemInitialStyles.push(itemStyle);
});
this.isStaticAndConditionalStyles = this.isMultipleSelection && itemsWithConditionalStyles;
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
this.initialStyles = styles;
this.mixedStyles = mixedStyles;
this.domainObject = domainObject;
this.removeListeners();
if (this.domainObject) {
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
}
subObjects.forEach(this.registerListener);
},
updateDomainObjectItemStyles(newItems) {
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
keys.forEach((key) => {
if (this.isKeyItemId(key)) {
if (!(newItems.find(item => item.id === key))) {
this.removeItemStyles(key);
}
}
});
},
isKeyItemId(key) {
return (key !== 'styles') &&
(key !== 'staticStyle') &&
(key !== 'defaultConditionId') &&
(key !== 'selectedConditionId') &&
(key !== 'conditionSetIdentifier');
},
registerListener(domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.domainObjectsById) {
this.domainObjectsById = {};
}
if (!this.domainObjectsById[id]) {
this.domainObjectsById[id] = domainObject;
this.observeObject(domainObject, id);
}
},
observeObject(domainObject, id) {
let unobserveObject = this.openmct.objects.observe(domainObject, '*', (newObject) => {
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
});
this.unObserveObjects.push(unobserveObject);
},
removeListeners() {
if (this.stopObserving) {
this.stopObserving();
}
if (this.stopObservingItems) {
this.stopObservingItems();
}
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.unObserveObjects) {
this.unObserveObjects.forEach((unObserveObject) => {
unObserveObject();
});
}
this.unObserveObjects = [];
},
subscribeToConditionSet() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetDomainObject) {
this.openmct.telemetry.request(this.conditionSetDomainObject)
.then(output => {
if (output && output.length) {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
}
},
handleConditionSetResultUpdated(resultData) {
this.selectedConditionId = resultData ? resultData.conditionId : '';
},
initialize(conditionSetDomainObject) {
//If there are new conditions in the conditionSet we need to set those styles to default
this.conditionSetDomainObject = conditionSetDomainObject;
this.enableConditionSetNav();
this.initializeConditionalStyles();
},
initializeConditionalStyles() {
if (!this.conditions) {
this.conditions = {};
}
let conditionalStyles = [];
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
if (conditionConfiguration.isDefault) {
this.selectedConditionId = conditionConfiguration.id;
}
this.conditions[conditionConfiguration.id] = conditionConfiguration;
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
if (foundStyle) {
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
conditionalStyles.push(foundStyle);
} else {
conditionalStyles.splice(index, 0, {
conditionId: conditionConfiguration.id,
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
});
}
});
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
this.conditionalStyles = conditionalStyles;
this.conditionsLoaded = true;
this.getAndPersistStyles(null, this.selectedConditionId);
if (!this.isEditing) {
this.subscribeToConditionSet();
}
},
//TODO: Double check how this works for single styles
initializeStaticStyle(objectStyles) {
let staticStyle = objectStyles && objectStyles.staticStyle;
if (staticStyle) {
this.staticStyle = {
style: Object.assign({}, this.initialStyles, staticStyle.style)
};
} else {
this.staticStyle = {
style: Object.assign({}, this.initialStyles)
};
}
},
removeItemStyles(itemId) {
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (itemId && domainObjectStyles[itemId]) {
delete domainObjectStyles[itemId];
if (Object.keys(domainObjectStyles).length <= 0) {
domainObjectStyles = undefined;
}
this.persist(this.domainObject, domainObjectStyles);
}
},
findStyleByConditionId(id) {
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
},
getCondition(id) {
return this.conditions ? this.conditions[id] : {};
},
addConditionSet() {
let conditionSetDomainObject;
const handleItemSelection = (item) => {
if (item) {
conditionSetDomainObject = item;
}
};
const dismissDialog = (overlay, initialize) => {
overlay.dismiss();
if (initialize && conditionSetDomainObject) {
this.conditionSetDomainObject = conditionSetDomainObject;
this.conditionalStyles = [];
this.initializeConditionalStyles();
}
};
let vm = new Vue({
provide: {
openmct: this.openmct
},
components: {ConditionSetSelectorDialog},
data() {
return {
handleItemSelection
}
},
template: '<condition-set-selector-dialog @conditionSetSelected="handleItemSelection"></condition-set-selector-dialog>'
}).$mount();
let overlay = this.openmct.overlays.overlay({
element: vm.$el,
size: 'small',
buttons: [
{
label: 'OK',
emphasis: 'true',
callback: () => dismissDialog(overlay, true)
},
{
label: 'Cancel',
callback: () => dismissDialog(overlay, false)
}
],
onDestroy: () => vm.$destroy()
});
},
removeConditionSet() {
this.conditionSetDomainObject = undefined;
this.conditionalStyles = [];
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.domainObjectsById) {
const domainObjects = Object.values(this.domainObjectsById);
domainObjects.forEach(domainObject => {
let objectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
this.removeConditionalStyles(objectStyles);
if (Object.keys(objectStyles).length <= 0) {
objectStyles = undefined;
}
this.persist(domainObject, objectStyles);
});
}
if (this.items.length) {
this.items.forEach((item) => {
const itemId = item.id;
this.removeConditionalStyles(domainObjectStyles, itemId);
if (Object.keys(domainObjectStyles[itemId]).length <= 0) {
delete domainObjectStyles[itemId];
}
});
} else {
this.removeConditionalStyles(domainObjectStyles);
}
if (Object.keys(domainObjectStyles).length <= 0) {
domainObjectStyles = undefined;
}
this.persist(this.domainObject, domainObjectStyles);
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
},
removeConditionalStyles(domainObjectStyles, itemId) {
if (itemId && domainObjectStyles[itemId]) {
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
delete domainObjectStyles[itemId].conditionSetIdentifier;
domainObjectStyles[itemId].selectedConditionId = undefined;
domainObjectStyles[itemId].defaultConditionId = undefined;
domainObjectStyles[itemId].styles = undefined;
delete domainObjectStyles[itemId].styles;
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.selectedConditionId = undefined;
domainObjectStyles.defaultConditionId = undefined;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
},
updateStaticStyle(staticStyle, property) {
//update the static style for each of the layoutItems as well as each sub object item
this.staticStyle = staticStyle;
this.removeConditionSet();
this.getAndPersistStyles(property);
},
updateConditionalStyle(conditionStyle, property) {
let foundStyle = this.findStyleByConditionId(conditionStyle.conditionId);
if (foundStyle) {
foundStyle.style = conditionStyle.style;
this.selectedConditionId = foundStyle.conditionId;
this.getAndPersistStyles(property);
}
},
getAndPersistStyles(property, defaultConditionId) {
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items, defaultConditionId));
if (this.domainObjectsById) {
const domainObjects = Object.values(this.domainObjectsById);
domainObjects.forEach(domainObject => {
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property, null, defaultConditionId));
});
}
if (!this.items.length && !this.domainObjectsById) {
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, null, defaultConditionId));
}
this.isStaticAndConditionalStyles = false;
if (property) {
let foundIndex = this.mixedStyles.indexOf(property);
if (foundIndex > -1) {
this.mixedStyles.splice(foundIndex, 1);
}
}
},
getDomainObjectStyle(domainObject, property, items, defaultConditionId) {
let objectStyle = {
styles: this.conditionalStyles,
staticStyle: this.staticStyle,
selectedConditionId: this.selectedConditionId
};
if (defaultConditionId) {
objectStyle.defaultConditionId = defaultConditionId;
}
if (this.conditionSetDomainObject) {
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
}
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
if (items) {
items.forEach(item => {
let itemStaticStyle = {};
let itemConditionalStyle = { styles: []};
if (!this.conditionSetDomainObject) {
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
}
if (item.applicableStyles[property]) {
itemStaticStyle[property] = this.staticStyle.style[property];
}
if (Object.keys(itemStaticStyle).length <= 0) {
itemStaticStyle = undefined;
}
domainObjectStyles[item.id] = { staticStyle: { style: itemStaticStyle } };
} else {
objectStyle.styles.forEach((conditionalStyle, index) => {
let style = {};
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
style[key] = conditionalStyle.style[key];
});
itemConditionalStyle.styles.push({
...conditionalStyle,
style
});
});
domainObjectStyles[item.id] = {
...domainObjectStyles[item.id],
...objectStyle,
...itemConditionalStyle
};
}
});
} else {
domainObjectStyles = {
...domainObjectStyles,
...objectStyle
};
}
return domainObjectStyles;
},
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.getAndPersistStyles();
},
persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
}
}
}
</script>

View File

@ -60,6 +60,31 @@
&__condition {
@include discreteItem();
border: 1px solid transparent;
pointer-events: none; // Prevent selecting when the object isn't being edited
&.is-current {
$c: $colorBodyFg;
border-color: rgba($c, 0.2);
background: rgba($c, 0.2);
}
.is-editing & {
cursor: pointer;
pointer-events: initial;
transition: $transOut;
&:hover {
background: rgba($colorBodyFg, 0.1);
transition: $transIn;
}
&.is-current {
$c: $editUIColorBg;
border-color: $c;
background: rgba($c, 0.1);
}
}
}
.c-style {

View File

@ -22,6 +22,7 @@
import TelemetryCriterion from './TelemetryCriterion';
import { evaluateResults } from "../utils/evaluator";
import { getLatestTimestamp } from '../utils/time';
export default class AllTelemetryCriterion extends TelemetryCriterion {
@ -47,6 +48,21 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
updateTelemetry(telemetryObjects) {
this.telemetryObjects = { ...telemetryObjects };
this.removeTelemetryDataCache();
}
removeTelemetryDataCache() {
const telemetryCacheIds = Object.keys(this.telemetryDataCache);
Object.values(this.telemetryObjects).forEach(telemetryObject => {
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
const foundIndex = telemetryCacheIds.indexOf(id);
if (foundIndex > -1) {
telemetryCacheIds.splice(foundIndex, 1);
}
});
telemetryCacheIds.forEach(id => {
delete (this.telemetryDataCache[id]);
});
}
formatData(data, telemetryObjects) {
@ -92,40 +108,53 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
}
requestLAD(options) {
options = Object.assign({},
options,
{
strategy: 'latest',
size: 1
}
);
requestLAD(telemetryObjects) {
const options = {
strategy: 'latest',
size: 1
};
if (!this.isValid()) {
return this.formatData({}, options.telemetryObjects);
return this.formatData({}, telemetryObjects);
}
let keys = Object.keys(Object.assign({}, options.telemetryObjects));
let keys = Object.keys(Object.assign({}, telemetryObjects));
const telemetryRequests = keys
.map(key => this.openmct.telemetry.request(
options.telemetryObjects[key],
telemetryObjects[key],
options
));
let telemetryDataCache = {};
return Promise.all(telemetryRequests)
.then(telemetryRequestsResults => {
let latestDatum;
let latestTimestamp;
const timeSystems = this.openmct.time.getAllTimeSystems();
const timeSystem = this.openmct.time.timeSystem();
telemetryRequestsResults.forEach((results, index) => {
latestDatum = results.length ? results[results.length - 1] : {};
if (index < telemetryRequestsResults.length-1) {
if (latestDatum) {
this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
}
}
const latestDatum = results.length ? results[results.length - 1] : {};
const datumId = keys[index];
const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
latestTimestamp = getLatestTimestamp(
latestTimestamp,
normalizedDatum,
timeSystems,
timeSystem
);
});
const datum = {
result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
...latestTimestamp
};
return {
id: this.id,
data: this.formatData(latestDatum, options.telemetryObjects)
data: datum
};
});
}

View File

@ -61,6 +61,21 @@ export default class TelemetryCriterion extends EventEmitter {
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
}
createNormalizedDatum(telemetryDatum, endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
return datum;
}, {});
normalizedDatum.id = id;
return normalizedDatum;
}
formatData(data) {
const datum = {
result: this.computeResult(data)
@ -79,14 +94,11 @@ export default class TelemetryCriterion extends EventEmitter {
this.result = this.computeResult(validatedData);
}
requestLAD(options) {
options = Object.assign({},
options,
{
strategy: 'latest',
size: 1
}
);
requestLAD() {
const options = {
strategy: 'latest',
size: 1
};
if (!this.isValid()) {
return {
@ -100,9 +112,11 @@ export default class TelemetryCriterion extends EventEmitter {
options
).then(results => {
const latestDatum = results.length ? results[results.length - 1] : {};
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
return {
id: this.id,
data: this.formatData(latestDatum)
data: this.formatData(normalizedDatum)
};
});
}

View File

@ -20,25 +20,21 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import { createOpenMct } from "testTools";
import { createOpenMct } from "testUtils";
import ConditionPlugin from "./plugin";
let openmct = createOpenMct();
openmct.install(new ConditionPlugin());
let conditionSetDefinition;
let mockConditionSetDomainObject;
let element;
let child;
describe('the plugin', function () {
let conditionSetDefinition;
let mockConditionSetDomainObject;
let element;
let child;
let openmct;
beforeAll((done) => {
openmct = createOpenMct();
openmct.install(new ConditionPlugin());
conditionSetDefinition = openmct.types.get('conditionSet').definition;
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
element = document.createElement('div');
child = document.createElement('div');
@ -55,7 +51,7 @@ describe('the plugin', function () {
conditionSetDefinition.initialize(mockConditionSetDomainObject);
openmct.on('start', done);
openmct.start(appHolder);
openmct.startHeadless();
});
let mockConditionSetObject = {

View File

@ -20,8 +20,6 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import _ from 'lodash';
const convertToNumbers = (input) => {
let numberInputs = [];
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
@ -34,6 +32,10 @@ const convertToStrings = (input) => {
return stringInputs;
};
const joinValues = (values, length) => {
return values.slice(0, length).join(', ');
};
export const OPERATIONS = [
{
name: 'equalTo',
@ -44,7 +46,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' is ' + values.join(', ');
return ' is ' + joinValues(values, 1);
}
},
{
@ -56,7 +58,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' is not ' + values.join(', ');
return ' is not ' + joinValues(values, 1);
}
},
{
@ -68,7 +70,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' > ' + values.join(', ');
return ' > ' + joinValues(values, 1);
}
},
{
@ -80,7 +82,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' < ' + values.join(', ');
return ' < ' + joinValues(values, 1);
}
},
{
@ -92,7 +94,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' >= ' + values.join(', ');
return ' >= ' + joinValues(values, 1);
}
},
{
@ -104,7 +106,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' <= ' + values.join(', ');
return ' <= ' + joinValues(values, 1);
}
},
{
@ -146,7 +148,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' contains ' + values.join(', ');
return ' contains ' + joinValues(values, 1);
}
},
{
@ -158,7 +160,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' does not contain ' + values.join(', ');
return ' does not contain ' + joinValues(values, 1);
}
},
{
@ -170,7 +172,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' starts with ' + values.join(', ');
return ' starts with ' + joinValues(values, 1);
}
},
{
@ -182,7 +184,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' ends with ' + values.join(', ');
return ' ends with ' + joinValues(values, 1);
}
},
{
@ -194,7 +196,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' is exactly ' + values.join(', ');
return ' is exactly ' + joinValues(values, 1);
}
},
{
@ -231,7 +233,7 @@ export const OPERATIONS = [
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' is ' + values.join(', ');
return ' is ' + joinValues(values, 1);
}
},
{
@ -244,7 +246,7 @@ export const OPERATIONS = [
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' is not ' + values.join(', ');
return ' is not ' + joinValues(values, 1);
}
},
{
@ -253,7 +255,7 @@ export const OPERATIONS = [
const lhsValue = input[0] !== undefined ? input[0].toString() : '';
if (input[1]) {
const values = input[1].split(',');
return values.find((value) => lhsValue === _.trim(value.toString()));
return values.find((value) => lhsValue === value.toString().trim());
}
return false;
},
@ -270,7 +272,7 @@ export const OPERATIONS = [
const lhsValue = input[0] !== undefined ? input[0].toString() : '';
if (input[1]) {
const values = input[1].split(',');
const found = values.find((value) => lhsValue === _.trim(value.toString()));
const found = values.find((value) => lhsValue === value.toString().trim());
return !found;
}
return false;

View File

@ -19,6 +19,8 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import isEmpty from 'lodash/isEmpty';
const NONE_VALUE = '__no_value';
const styleProps = {
@ -122,12 +124,25 @@ export const getConditionalStyleForItem = (domainObject, id) => {
if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
return domainObjectStyles[id].styles;
}
} else if (domainObjectStyles.staticStyle) {
} else if (domainObjectStyles.conditionSetIdentifier) {
return domainObjectStyles.styles;
}
}
};
export const getConditionSetIdentifierForItem = (domainObject, id) => {
let domainObjectStyles = domainObject && domainObject.configuration && domainObject.configuration.objectStyles;
if (domainObjectStyles) {
if (id) {
if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
return domainObjectStyles[id].conditionSetIdentifier;
}
} else if (domainObjectStyles.conditionSetIdentifier) {
return domainObjectStyles.conditionSetIdentifier;
}
}
};
//Returns either existing static styles or uses SVG defaults if available
export const getApplicableStylesForItem = (domainObject, item) => {
const type = item && item.type;
@ -154,7 +169,7 @@ export const getApplicableStylesForItem = (domainObject, item) => {
};
export const getStylesWithoutNoneValue = (style) => {
if (_.isEmpty(style) || !style) {
if (isEmpty(style) || !style) {
return;
}
let styleObj = {};

View File

@ -68,7 +68,6 @@
<script>
import uuid from 'uuid';
import SubobjectView from './SubobjectView.vue'
import TelemetryView from './TelemetryView.vue'
import BoxView from './BoxView.vue'
@ -76,6 +75,7 @@ import TextView from './TextView.vue'
import LineView from './LineView.vue'
import ImageView from './ImageView.vue'
import EditMarquee from './EditMarquee.vue'
import _ from 'lodash'
const ITEM_TYPE_VIEW_MAP = {
'subobject-view': SubobjectView,
@ -512,7 +512,7 @@ export default {
}
},
updateTelemetryFormat(item, format) {
let index = _.findIndex(this.layoutItems, item);
let index = this.layoutItems.findIndex(item);
item.format = format;
this.mutate(`configuration.items[${index}]`, item);
}

View File

@ -40,6 +40,7 @@
<script>
import LayoutDrag from './../LayoutDrag'
import _ from 'lodash'
export default {
inject: ['openmct'],

View File

@ -62,6 +62,7 @@
<script>
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
import _ from 'lodash';
const START_HANDLE_QUADRANTS = {
1: 'c-frame-edit__handle--sw',

View File

@ -52,7 +52,7 @@ export default {
},
initObjectStyles() {
if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this));
this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this), true);
} else {
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
}

View File

@ -22,7 +22,7 @@
import Layout from './components/DisplayLayout.vue'
import Vue from 'vue'
import objectUtils from '../../api/objects/object-utils.js'
import objectUtils from 'objectUtils'
import DisplayLayoutType from './DisplayLayoutType.js'
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'
import AlphaNumericFormatViewProvider from './AlphanumericFormatViewProvider.js'

View File

@ -62,6 +62,7 @@
<script>
import FilterField from './FilterField.vue';
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
import isEmpty from 'lodash/isEmpty';
export default {
inject: ['openmct'],
@ -102,7 +103,7 @@ export default {
hasActiveFilters() {
// Should be true when the user has entered any filter values.
return Object.values(this.persistedFilters).some(comparator => {
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
return (typeof(comparator) === 'object' && !isEmpty(comparator));
});
}
},

View File

@ -27,7 +27,8 @@
<script>
import FilterObject from './FilterObject.vue';
import GlobalFilters from './GlobalFilters.vue'
import GlobalFilters from './GlobalFilters.vue';
import _ from 'lodash';
const FILTER_VIEW_TITLE = 'Filters applied';
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';

View File

@ -64,6 +64,7 @@
<script>
import compositionLoader from './composition-loader';
import ListItem from './ListItem.vue';
import _ from 'lodash';
export default {
components: {ListItem},

View File

@ -66,7 +66,6 @@ export default {
data() {
return {
autoScroll: true,
date: '',
filters : {
brightness: 100,
contrast: 100
@ -78,22 +77,39 @@ export default {
imageHistory: [],
imageUrl: '',
isPaused: false,
metadata: {},
requestCount: 0,
timeFormat: ''
}
},
mounted() {
// set
this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.subscribe(this.domainObject);
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.imageFormat = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]);
// initialize
this.timeKey = this.openmct.time.timeSystem().key;
this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey));
// listen
this.openmct.time.on('bounds', this.boundsChange);
this.openmct.time.on('timeSystem', this.timeSystemChange);
// kickoff
this.subscribe();
this.requestHistory();
},
updated() {
this.scrollToRight();
},
beforeDestroy() {
this.stopListening();
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
}
this.openmct.time.off('bounds', this.boundsChange);
this.openmct.time.off('timeSystem', this.timeSystemChange);
},
methods: {
datumMatchesMostRecent(datum) {
datumIsNotValid(datum) {
if (this.imageHistory.length === 0) {
return false;
}
@ -103,7 +119,14 @@ export default {
const lastHistoryTime = this.timeFormat.format(this.imageHistory.slice(-1)[0]);
const lastHistoryURL = this.imageFormat.format(this.imageHistory.slice(-1)[0]);
return (datumTime === lastHistoryTime) && (datumURL === lastHistoryURL);
// datum is not valid if it matches the last datum in history,
// or it is before the last datum in the history
const datumTimeCheck = this.timeFormat.parse(datum);
const historyTimeCheck = this.timeFormat.parse(this.imageHistory.slice(-1)[0]);
const matchesLast = (datumTime === lastHistoryTime) && (datumURL === lastHistoryURL);
const isStale = datumTimeCheck < historyTimeCheck;
return matchesLast || isStale;
},
getImageUrl(datum) {
return datum ?
@ -147,21 +170,6 @@ export default {
return this.isPaused;
},
requestHistory(bounds) {
this.requestCount++;
this.imageHistory = [];
const requestId = this.requestCount;
this.openmct.telemetry
.request(this.domainObject, bounds)
.then((values = []) => {
if (this.requestCount > requestId) {
return Promise.resolve('Stale request');
}
values.forEach(this.updateHistory);
this.updateValues(values[values.length - 1]);
});
},
scrollToRight() {
if (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll) {
return;
@ -188,40 +196,56 @@ export default {
image.selected = true;
}
},
stopListening() {
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
boundsChange(bounds, isTick) {
if(!isTick) {
this.requestHistory();
}
},
subscribe(domainObject) {
this.date = ''
this.imageUrl = '';
this.openmct.objects.get(this.keystring)
.then((object) => {
const metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.timeKey = this.openmct.time.timeSystem().key;
this.timeFormat = this.openmct.telemetry.getValueFormatter(metadata.value(this.timeKey));
this.imageFormat = this.openmct.telemetry.getValueFormatter(metadata.valuesForHints(['image'])[0]);
this.unsubscribe = this.openmct.telemetry
.subscribe(this.domainObject, (datum) => {
this.updateHistory(datum);
this.updateValues(datum);
});
requestHistory() {
let bounds = this.openmct.time.bounds();
this.requestCount++;
const requestId = this.requestCount;
this.imageHistory = [];
this.openmct.telemetry
.request(this.domainObject, bounds)
.then((values = []) => {
if (this.requestCount === requestId) {
values.forEach(this.updateHistory, false);
this.updateValues(values[values.length - 1]);
}
});
},
timeSystemChange(system) {
// reset timesystem dependent variables
this.timeKey = system.key;
this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey));
},
subscribe() {
this.unsubscribe = this.openmct.telemetry
.subscribe(this.domainObject, (datum) => {
let parsedTimestamp = this.timeFormat.parse(datum[this.timeKey]),
bounds = this.openmct.time.bounds();
this.requestHistory(this.openmct.time.bounds());
if(parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
this.updateHistory(datum);
this.updateValues(datum);
}
});
},
unselectAllImages() {
this.imageHistory.forEach(image => image.selected = false);
},
updateHistory(datum) {
if (this.datumMatchesMostRecent(datum)) {
updateHistory(datum, updateValues = true) {
if (this.datumIsNotValid(datum)) {
return;
}
const index = _.sortedIndex(this.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
const index = _.sortedIndexBy(this.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
this.imageHistory.splice(index, 0, datum);
if(updateValues) {
this.updateValues(datum);
}
},
updateValues(datum) {
if (this.isPaused) {

View File

@ -162,20 +162,35 @@ export default {
}).show(this.embed.snapshot.src);
},
changeLocation() {
this.openmct.time.stopClock();
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
const link = this.embed.historicLink;
if (!link) {
return;
}
const bounds = this.openmct.time.bounds();
const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
|| this.embed.bounds.end !== bounds.end;
const isFixedTimespanMode = !this.openmct.time.clock();
this.openmct.time.stopClock();
let message = '';
if (isTimeBoundChanged) {
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
message = 'Time bound values changed';
}
if (!isFixedTimespanMode) {
message = 'Time bound values changed to fixed timespan mode';
}
if (message.length) {
this.openmct.notifications.alert(message);
}
window.location.href = link;
const message = 'Time bounds changed to fixed timespan mode';
this.openmct.notifications.alert(message);
},
formatTime(unixTime, timeFormat) {
return Moment.utc(unixTime).format(timeFormat);

View File

@ -221,7 +221,7 @@ export default {
return position;
},
formatTime(unixTime, timeFormat) {
return Moment(unixTime).format(timeFormat);
return Moment.utc(unixTime).format(timeFormat);
},
moveSnapshot(snapshotId) {
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);

View File

@ -29,7 +29,7 @@
<script>
import Snapshot from '../snapshot';
import { clearDefaultNotebook, getDefaultNotebook } from '../utils/notebook-storage';
import { getDefaultNotebook } from '../utils/notebook-storage';
import { NOTEBOOK_DEFAULT, NOTEBOOK_SNAPSHOT } from '../notebook-constants';
export default {
@ -40,6 +40,18 @@ export default {
default() {
return {};
}
},
ignoreLink: {
type: Boolean,
default() {
return false;
}
},
objectPath: {
type: Array,
default() {
return null;
}
}
},
data() {
@ -60,28 +72,22 @@ export default {
methods: {
async setNotebookTypes() {
const notebookTypes = [];
let defaultPath = '';
const defaultNotebook = getDefaultNotebook();
if (defaultNotebook) {
const domainObject = await this.openmct.objects.get(defaultNotebook.notebookMeta.identifier)
.then(d => d);
const domainObject = defaultNotebook.domainObject;
if (!domainObject.location) {
clearDefaultNotebook();
} else {
defaultPath = `${domainObject.name} - ${defaultNotebook.section.name} - ${defaultNotebook.page.name}`;
if (domainObject.location) {
const defaultPath = `${domainObject.name} - ${defaultNotebook.section.name} - ${defaultNotebook.page.name}`;
notebookTypes.push({
cssClass: 'icon-notebook',
name: `Save to Notebook ${defaultPath}`,
type: NOTEBOOK_DEFAULT
});
}
}
if (defaultPath.length !== 0) {
notebookTypes.push({
cssClass: 'icon-notebook',
name: `Save to Notebook ${defaultPath}`,
type: NOTEBOOK_DEFAULT
});
}
notebookTypes.push({
cssClass: 'icon-notebook',
name: 'Save to Notebook Snapshots',
@ -97,17 +103,27 @@ export default {
this.showMenu = false;
},
snapshot(notebook) {
let element = document.getElementsByClassName("l-shell__main-container")[0];
const bounds = this.openmct.time.bounds();
const objectPath = this.openmct.router.path;
const snapshotMeta = {
bounds,
link: window.location.href,
objectPath,
openmct: this.openmct
};
this.hideMenu();
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
this.$nextTick(() => {
const element = document.querySelector('.c-overlay__contents')
|| document.getElementsByClassName('l-shell__main-container')[0];
const bounds = this.openmct.time.bounds();
const link = !this.ignoreLink
? window.location.href
: null;
const objectPath = this.objectPath || this.openmct.router.path;
const snapshotMeta = {
bounds,
link,
objectPath,
openmct: this.openmct
};
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
});
}
}
}

View File

@ -239,6 +239,7 @@ export default {
const section = this.getSelectedSection();
return {
domainObject: this.internalDomainObject,
notebookMeta,
section,
page
@ -440,7 +441,7 @@ export default {
async updateDefaultNotebook(notebookStorage) {
const defaultNotebookObject = await this.getDefaultNotebookObject();
this.removeDefaultClass(defaultNotebookObject);
setDefaultNotebook(notebookStorage);
setDefaultNotebook(this.openmct, notebookStorage);
this.addDefaultClass();
this.defaultSectionId = notebookStorage.section.id;
this.defaultPageId = notebookStorage.page.id;
@ -495,7 +496,7 @@ export default {
return;
}
if (section.id !== defaultNotebookSection.id) {
if (id !== defaultNotebookSection.id) {
return;
}

View File

@ -1,6 +1,46 @@
const NOTEBOOK_LOCAL_STORAGE = 'notebook-storage';
let currentNotebookObject = null;
let unlisten = null;
function defaultNotebookObjectChanged(newDomainObject) {
if (newDomainObject.location !== null) {
currentNotebookObject = newDomainObject;
const notebookStorage = getDefaultNotebook();
notebookStorage.domainObject = newDomainObject;
saveDefaultNotebook(notebookStorage);
return;
}
if (unlisten) {
unlisten();
unlisten = null;
}
clearDefaultNotebook();
}
function observeDefaultNotebookObject(openmct, notebookStorage) {
const domainObject = notebookStorage.domainObject;
if (currentNotebookObject
&& currentNotebookObject.identifier.key === domainObject.identifier.key) {
return;
}
if (unlisten) {
unlisten();
unlisten = null;
}
unlisten = openmct.objects.observe(notebookStorage.domainObject, '*', defaultNotebookObjectChanged);
}
function saveDefaultNotebook(notebookStorage) {
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
}
export function clearDefaultNotebook() {
currentNotebookObject = null;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, null);
}
@ -10,20 +50,21 @@ export function getDefaultNotebook() {
return JSON.parse(notebookStorage);
}
export function setDefaultNotebook(notebookStorage) {
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
export function setDefaultNotebook(openmct, notebookStorage) {
observeDefaultNotebookObject(openmct, notebookStorage);
saveDefaultNotebook(notebookStorage);
}
export function setDefaultNotebookSection(section) {
const notebookStorage = getDefaultNotebook();
notebookStorage.section = section;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
saveDefaultNotebook(notebookStorage);
}
export function setDefaultNotebookPage(page) {
const notebookStorage = getDefaultNotebook();
notebookStorage.page = page;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
saveDefaultNotebook(notebookStorage);
}

View File

@ -0,0 +1,70 @@
<template>
<div
v-if="notifications.length > 0"
class="c-indicator c-indicator--clickable icon-bell"
:class="[severityClass]"
>
<span class="c-indicator__label">
<button @click="toggleNotificationsList(true)">
{{ notificationsCountMessage(notifications.length) }}
</button>
<button @click="dismissAllNotifications()">
Clear All
</button>
</span>
<span class="c-indicator__count">{{ notifications.length }}</span>
<notifications-list
v-if="showNotificationsOverlay"
:notifications="notifications"
@close="toggleNotificationsList"
@clear-all="dismissAllNotifications"
/>
</div>
</template>
<script>
import NotificationsList from './NotificationsList.vue';
export default {
inject: ['openmct'],
components: {
NotificationsList
},
data() {
return {
notifications: this.openmct.notifications.notifications,
highest: this.openmct.notifications.highest,
showNotificationsOverlay: false
}
},
computed: {
severityClass() {
return `s-status-${this.highest.severity}`;
}
},
mounted() {
this.openmct.notifications.on('notification', this.updateNotifications);
this.openmct.notifications.on('dismiss-all', this.updateNotifications);
},
methods: {
dismissAllNotifications() {
this.openmct.notifications.dismissAllNotifications();
},
toggleNotificationsList(flag) {
this.showNotificationsOverlay = flag;
},
updateNotifications() {
this.notifications = this.openmct.notifications.notifications;
this.highest = this.openmct.notifications.highest;
},
notificationsCountMessage(count) {
if (count > 1) {
return `${count} Notifications`;
} else {
return `${count} Notification`;
}
}
}
}
</script>

View File

@ -0,0 +1,85 @@
<template>
<div
class="c-message"
:class="'message-severity-' + notification.model.severity"
>
<div class="c-ne__time-and-content">
<div class="c-ne__time">
<span>{{ notification.model.timestamp }}</span>
</div>
<div class="c-ne__content">
<div class="w-message-contents">
<div class="c-message__top-bar">
<div class="c-message__title">{{ notification.model.message }}</div>
</div>
<div class="message-body">
<progress-bar
v-if="isProgressNotification"
:model="progressObject"
/>
</div>
</div>
</div>
<div class="c-overlay__button-bar">
<button
v-for="(dialogOption, index) in notification.model.options"
:key="index"
class="c-button"
@click="dialogOption.callback()"
>
{{ dialogOption.label }}
</button>
<button
v-if="notification.model.primaryOption"
class="c-button c-button--major"
@click="notification.model.primaryOption.callback()"
>
{{ notification.model.primaryOption.label }}
</button>
</div>
</div>
</div>
</template>
<script>
import ProgressBar from '../../../ui/components/ProgressBar.vue';
export default {
components: {
ProgressBar
},
props:{
notification: {
type: Object,
required: true
}
},
data() {
return {
isProgressNotification: false,
progressPerc: this.notification.model.progressPerc,
progressText: this.notification.model.progressText
}
},
computed: {
progressObject() {
return {
progressPerc: this.progressPerc,
progressText: this.progressText
}
}
},
mounted() {
if (this.notification.model.progressPerc) {
this.isProgressNotification = true;
this.notification.on('progress', this.updateProgressBar)
}
},
methods: {
updateProgressBar(progressPerc, progressText) {
this.progressPerc = progressPerc;
this.progressText = progressText;
}
}
}
</script>

View File

@ -0,0 +1,69 @@
<template>
<div class="t-message-list c-overlay__contents">
<div class="c-overlay__top-bar">
<div class="c-overlay__dialog-title">Notifications</div>
<div class="c-overlay__dialog-hint">
{{ notificationsCountDisplayMessage(notifications.length) }}
</div>
</div>
<div class="w-messages c-overlay__messages">
<notification-message
v-for="notification in notifications"
:key="notification.model.timestamp"
:notification="notification"
/>
</div>
</div>
</template>
<script>
import NotificationMessage from './NotificationMessage.vue';
export default {
components: {
NotificationMessage
},
inject: ['openmct'],
props: {
notifications: {
type: Array,
required: true
}
},
data() {
return {}
},
mounted() {
this.openOverlay();
},
methods: {
openOverlay() {
this.overlay = this.openmct.overlays.overlay({
element: this.$el,
size: 'large',
dismissable: true,
buttons: [
{
label: 'Clear All Notifications',
emphasis: true,
callback:() => {
this.$emit('clear-all');
this.overlay.dismiss();
}
}
],
onDestroy: () => {
this.$emit('close', false);
}
});
},
notificationsCountDisplayMessage(count) {
if (count > 1 || count === 0) {
return `Displaying ${count} notifications`;
} else {
return `Displaying ${count} notification`;
}
}
}
}
</script>

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,15 +19,25 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Vue from 'vue';
import NotificationIndicator from './components/NotificationIndicator.vue';
define(
[],
function () {
export default function plugin() {
return function install(openmct) {
let component = new Vue ({
provide: {
openmct
},
components: {
NotificationIndicator: NotificationIndicator
},
template: '<NotificationIndicator></NotificationIndicator>'
}),
indicator = {
key: 'notifications-indicator',
element: component.$mount().$el
};
function NotificationIndicator() {}
NotificationIndicator.template = 'notificationIndicatorTemplate';
return NotificationIndicator;
}
);
openmct.indicators.add(indicator);
};
}

View File

@ -0,0 +1,71 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 NotificationIndicatorPlugin from './plugin.js';
import Vue from 'vue';
import {
createOpenMct
} from 'testUtils';
describe('the plugin', () => {
let notificationIndicatorPlugin,
openmct,
indicatorObject,
indicatorElement,
parentElement,
mockMessages = ['error', 'test', 'notifications'];
beforeEach((done) => {
openmct = createOpenMct();
notificationIndicatorPlugin = new NotificationIndicatorPlugin();
openmct.install(notificationIndicatorPlugin);
parentElement = document.createElement('div');
indicatorObject = openmct.indicators.indicatorObjects.find(indicator => indicator.key === 'notifications-indicator');
indicatorElement = indicatorObject.element;
openmct.on('start', () => {
mockMessages.forEach(message => {
openmct.notifications.error(message);
});
done();
});
openmct.startHeadless();
});
describe('the indicator plugin element', () => {
beforeEach(() => {
parentElement.append(indicatorElement);
return Vue.nextTick();
});
it('notifies the user of the number of notifications', () => {
let notificationCountElement = parentElement.querySelector('.c-indicator__count');
expect(notificationCountElement.innerText).toEqual(mockMessages.length.toString());
});
});
});

View File

@ -21,7 +21,7 @@
-->
<div ng-controller="StackedPlotController as stackedPlot"
class="c-plot c-plot--stacked holder holder-plot has-control-bar">
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
<div class="c-control-bar" ng-show="!stackedPlot.hideExportButtons">
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="stackedPlot.exportPNG()"

View File

@ -54,27 +54,29 @@ function (
* @constructor
*/
function MCTChartController($scope) {
this.$scope = $scope;
this.isDestroyed = false;
this.lines = [];
this.pointSets = [];
this.alarmSets = [];
this.offset = {};
this.config = $scope.config;
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.draw = this.draw.bind(this);
this.scheduleDraw = this.scheduleDraw.bind(this);
this.seriesElements = new WeakMap();
this.$onInit = () => {
this.$scope = $scope;
this.isDestroyed = false;
this.lines = [];
this.pointSets = [];
this.alarmSets = [];
this.offset = {};
this.config = $scope.config;
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.draw = this.draw.bind(this);
this.scheduleDraw = this.scheduleDraw.bind(this);
this.seriesElements = new WeakMap();
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
}
}
eventHelpers.extend(MCTChartController.prototype);
@ -150,7 +152,7 @@ function (
MCTChartController.prototype.destroy = function () {
this.isDestroyed = true;
this.stopListening();
_.invoke(this.lines, 'destroy');
this.lines.forEach(line => line.destroy());
DrawLoader.releaseDrawAPI(this.drawAPI);
};

View File

@ -44,7 +44,7 @@ define([
this.initialize(options);
}
_.extend(Collection.prototype, EventEmitter.prototype);
Object.assign(Collection.prototype, EventEmitter.prototype);
eventHelpers.extend(Collection.prototype);
Collection.extend = extend;
@ -105,12 +105,7 @@ define([
};
Collection.prototype.indexOf = function (model) {
return _.findIndex(
this.models,
function (m) {
return m === model;
}
);
return this.models.findIndex(m => m === model);
};
Collection.prototype.remove = function (model) {

View File

@ -49,7 +49,7 @@ define([
this.initialize(options);
}
_.extend(Model.prototype, EventEmitter.prototype);
Object.assign(Model.prototype, EventEmitter.prototype);
eventHelpers.extend(Model.prototype);
Model.extend = extend;

View File

@ -146,7 +146,7 @@ define([
strategy = 'minmax';
}
options = _.extend({}, { size: 1000, strategy, filters: this.filters }, options || {});
options = Object.assign({}, { size: 1000, strategy, filters: this.filters }, options || {});
if (!this.unsubscribe) {
this.unsubscribe = this.openmct
@ -160,6 +160,7 @@ define([
);
}
/* eslint-disable you-dont-need-lodash-underscore/concat */
return this.openmct
.telemetry
.request(this.domainObject, options)
@ -171,6 +172,7 @@ define([
.value();
this.reset(newPoints);
}.bind(this));
/* eslint-enable you-dont-need-lodash-underscore/concat */
},
/**
* Update x formatter on x change.
@ -270,7 +272,7 @@ define([
* @private
*/
sortedIndex: function (point) {
return _.sortedIndex(this.data, point, this.getXVal);
return _.sortedIndexBy(this.data, point, this.getXVal);
},
/**
* Update min/max stats for the series.
@ -322,7 +324,15 @@ define([
* a point to the end without dupe checking.
*/
add: function (point, appendOnly) {
var insertIndex = this.data.length;
var insertIndex = this.data.length,
currentYVal = this.getYVal(point),
lastYVal = this.getYVal(this.data[insertIndex - 1]);
if (this.isValueInvalid(currentYVal) && this.isValueInvalid(lastYVal)) {
console.warn('[Plot] Invalid Y Values detected');
return;
}
if (!appendOnly) {
insertIndex = this.sortedIndex(point);
if (this.getXVal(this.data[insertIndex]) === this.getXVal(point)) {
@ -332,11 +342,21 @@ define([
return;
}
}
this.updateStats(point);
point.mctLimitState = this.evaluate(point);
this.data.splice(insertIndex, 0, point);
this.emit('add', point, insertIndex, this);
},
/**
*
* @private
*/
isValueInvalid: function (val) {
return Number.isNaN(val) || val === undefined;
},
/**
* Remove a point from the data array and notify listeners.
* @private

View File

@ -101,11 +101,11 @@ define([
var plotObject = this.plot.get('domainObject');
if (plotObject.type === 'telemetry.plot.overlay') {
var persistedIndex = _.findIndex(plotObject.configuration.series, function (s) {
var persistedIndex = plotObject.configuration.series.findIndex(s => {
return _.isEqual(identifier, s.identifier);
});
var configIndex = _.findIndex(this.models, function (m) {
var configIndex = this.models.findIndex(m => {
return _.isEqual(m.domainObject.identifier, identifier);
});

View File

@ -182,21 +182,6 @@ define([
this.set('format', yFormat.format.bind(yFormat));
this.set('values', yMetadata.values);
if (!label) {
var labelUnits = series.map(function (s) {
return s.metadata.value(s.get('yKey')).units;
}).reduce(function (a, b) {
if (a === undefined) {
return b;
}
if (a === b) {
return a;
}
return '';
}, undefined);
if (labelUnits) {
this.set('label', labelUnits);
return;
}
var labelName = series.map(function (s) {
return s.metadata.value(s.get('yKey')).name;
}).reduce(function (a, b) {
@ -208,7 +193,28 @@ define([
}
return '';
}, undefined);
this.set('label', labelName);
if (labelName) {
this.set('label', labelName);
return;
}
var labelUnits = series.map(function (s) {
return s.metadata.value(s.get('yKey')).units;
}).reduce(function (a, b) {
if (a === undefined) {
return b;
}
if (a === b) {
return a;
}
return '';
}, undefined);
if (labelUnits) {
this.set('label', labelUnits);
return;
}
}
},
defaults: function (options) {

View File

@ -51,7 +51,7 @@ define([
}
}
_.extend(Draw2D.prototype, EventEmitter.prototype);
Object.assign(Draw2D.prototype, EventEmitter.prototype);
eventHelpers.extend(Draw2D.prototype);
// Convert from logical to physical x coordinates

View File

@ -78,7 +78,7 @@ define([
this.listenTo(this.canvas, "webglcontextlost", this.onContextLost, this);
}
_.extend(DrawWebGL.prototype, EventEmitter.prototype);
Object.assign(DrawWebGL.prototype, EventEmitter.prototype);
eventHelpers.extend(DrawWebGL.prototype);
DrawWebGL.prototype.onContextLost = function (event) {

View File

@ -23,7 +23,7 @@
define([
'../configuration/configStore',
'../lib/eventHelpers',
'../../../../api/objects/object-utils',
'objectUtils',
'lodash'
], function (
configStore,

Some files were not shown because too many files have changed in this diff Show More