Compare commits

..

60 Commits

Author SHA1 Message Date
ffca1a8f33 Merge branch 'master' into improve-test-coverage 2020-05-28 11:55:01 -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
f8b5c8e6a1 Merge branch 'master' into improve-test-coverage 2020-05-19 09:33:08 -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
694b8f4666 provide format for name metadata (#3054) 2020-05-18 11:40:05 -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
e43a23f1c5 Merge branch 'master' into improve-test-coverage 2020-05-15 09:36:05 -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
b672b50359 Updated copyright end year 2020-05-12 14:57:20 -07:00
eaf8faa1f1 Quick and easy test to bump up test coverage to 62% 2020-05-12 11:48:15 -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
126 changed files with 1313 additions and 555 deletions

View File

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

View File

@ -10,7 +10,8 @@ module.exports = {
}, },
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:vue/recommended" "plugin:vue/recommended",
"plugin:you-dont-need-lodash-underscore/compatible"
], ],
"parser": "vue-eslint-parser", "parser": "vue-eslint-parser",
"parserOptions": { "parserOptions": {
@ -22,6 +23,9 @@ module.exports = {
} }
}, },
"rules": { "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", "no-bitwise": "error",
"curly": "error", "curly": "error",
"eqeqeq": "error", "eqeqeq": "error",

4
API.md
View File

@ -427,8 +427,8 @@ Each telemetry value description has an object defining hints. Keys in this thi
Known hints: 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. * `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`: 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. * `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available. * `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
##### The Time Conductor and Telemetry ##### 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 ### Merging
When development is complete on an issue, the first step toward merging it 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, should meet code, test, and commit message standards as described below,
and the pull request should include a completed author checklist, also and the pull request should include a completed author checklist, also
as described below. Pull requests may be assigned to specific team 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 pull request containing the reviewer checklist (from below) and complete
the merge back to the master branch. the merge back to the master branch.
Additionally:
* 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 ## Standards
Contributions to Open MCT are expected to meet the following standards. Contributions to Open MCT are expected to meet the following standards.
@ -292,6 +301,7 @@ checklist).
2. Unit tests included and/or updated with changes? 2. Unit tests included and/or updated with changes?
3. Command line build passes? 3. Command line build passes?
4. Changes have been smoke-tested? 4. Changes have been smoke-tested?
5. Testing instructions included?
### Reviewer Checklist ### Reviewer Checklist
@ -299,3 +309,4 @@ checklist).
2. Appropriate unit tests included? 2. Appropriate unit tests included?
3. Code style and in-line documentation are appropriate? 3. Code style and in-line documentation are appropriate?
4. Commit messages meet standards? 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: [ values: [
{ {
key: "name", key: "name",
name: "Name" name: "Name",
format: "string"
}, },
{ {
key: "utc", key: "utc",
@ -99,7 +100,7 @@ define([
}; };
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) { GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return _.extend( return Object.assign(
{}, {},
domainObject.telemetry, domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type] METADATA_BY_TYPE[domainObject.type]

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 as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved. Administration. All rights reserved.
@ -43,9 +43,9 @@
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
); );
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso()); openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems()); openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Generator()); openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery()); openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem()); openmct.install(openmct.plugins.UTCTimeSystem());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
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 * The ImportAsJSONAction is available from context menus and allows a user

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@
* Module defining GenericSearchProvider. Created by shale on 07/16/2015. * Module defining GenericSearchProvider. Created by shale on 07/16/2015.
*/ */
define([ define([
'../../../../src/api/objects/object-utils', 'objectUtils',
'lodash' 'lodash'
], function ( ], function (
objectUtils, objectUtils,
@ -191,7 +191,7 @@ define([
} }
var domainObject = objectUtils.toNewFormat(model, id); 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); return p.appliesTo(domainObject);
}); });

View File

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

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/api',
'./api/overlays/OverlayAPI', './api/overlays/OverlayAPI',
'./selection/Selection', './selection/Selection',
'./api/objects/object-utils', 'objectUtils',
'./plugins/plugins', './plugins/plugins',
'./adapter/indicators/legacy-indicators-plugin', './adapter/indicators/legacy-indicators-plugin',
'./plugins/buildInfo/plugin', './plugins/buildInfo/plugin',
@ -249,7 +249,7 @@ define([
this.legacyRegistry = new BundleRegistry(); this.legacyRegistry = new BundleRegistry();
installDefaultBundles(this.legacyRegistry); 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.Plot());
this.install(this.plugins.TelemetryTable()); this.install(this.plugins.TelemetryTable());
@ -350,17 +350,13 @@ define([
* @param {HTMLElement} [domElement] the DOM element in which to run * @param {HTMLElement} [domElement] the DOM element in which to run
* MCT; if undefined, MCT will be run in the body of the document * 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) { if (!this.plugins.DisplayLayout._installed) {
this.install(this.plugins.DisplayLayout({ this.install(this.plugins.DisplayLayout({
showAsView: ['summary-widget'] showAsView: ['summary-widget']
})); }));
} }
if (!domElement) {
domElement = document.body;
}
this.element = domElement; this.element = domElement;
this.legacyExtension('runs', { this.legacyExtension('runs', {
@ -400,6 +396,7 @@ define([
// something has depended upon objectService. Cool, right? // something has depended upon objectService. Cool, right?
this.$injector.get('objectService'); this.$injector.get('objectService');
if (!isHeadlessMode) {
var appLayout = new Vue({ var appLayout = new Vue({
components: { components: {
'Layout': Layout.default 'Layout': Layout.default
@ -413,11 +410,17 @@ define([
this.layout = appLayout.$refs.layout; this.layout = appLayout.$refs.layout;
Browse(this); Browse(this);
}
this.router.start(); this.router.start();
this.emit('start'); this.emit('start');
}.bind(this)); }.bind(this));
}; };
MCT.prototype.startHeadless = function () {
let unreachableNode = document.createElement('div');
return this.start(unreachableNode, true);
}
/** /**
* Install a plugin in MCT. * Install a plugin in MCT.
* *

View File

@ -21,11 +21,11 @@
*****************************************************************************/ *****************************************************************************/
define([ define([
'./MCT',
'./plugins/plugins', './plugins/plugins',
'legacyRegistry' 'legacyRegistry',
], function (MCT, plugins, legacyRegistry) { 'testUtils'
xdescribe("MCT", function () { ], function (plugins, legacyRegistry, testUtils) {
describe("MCT", function () {
var openmct; var openmct;
var mockPlugin; var mockPlugin;
var mockPlugin2; var mockPlugin2;
@ -38,7 +38,7 @@ define([
mockListener = jasmine.createSpy('listener'); mockListener = jasmine.createSpy('listener');
oldBundles = legacyRegistry.list(); oldBundles = legacyRegistry.list();
openmct = new MCT(); openmct = testUtils.createOpenMct();
openmct.install(mockPlugin); openmct.install(mockPlugin);
openmct.install(mockPlugin2); openmct.install(mockPlugin2);
@ -63,8 +63,11 @@ define([
}); });
describe("start", function () { describe("start", function () {
beforeEach(function () { let appHolder;
openmct.start(); beforeEach(function (done) {
appHolder = document.createElement("div");
openmct.on('start', done);
openmct.start(appHolder);
}); });
it("calls plugins for configuration", function () { it("calls plugins for configuration", function () {
@ -75,25 +78,51 @@ define([
it("emits a start event", function () { it("emits a start event", function () {
expect(mockListener).toHaveBeenCalled(); 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 () { describe("setAssetPath", function () {
var testAssetPath; var testAssetPath;
beforeEach(function () { beforeEach(function () {
testAssetPath = "some/path";
openmct.legacyExtension = jasmine.createSpy('legacyExtension'); openmct.legacyExtension = jasmine.createSpy('legacyExtension');
openmct.setAssetPath(testAssetPath);
}); });
it("internally configures the path for assets", function () { it("configures the path for assets", function () {
expect(openmct.legacyExtension).toHaveBeenCalledWith( testAssetPath = "some/path/";
'constants', openmct.setAssetPath(testAssetPath);
{ expect(openmct.getAssetPath()).toBe(testAssetPath);
key: "ASSETS_PATH", });
value: testAssetPath
} it("adds a trailing /", function () {
); testAssetPath = "some/path";
openmct.setAssetPath(testAssetPath);
expect(openmct.getAssetPath()).toBe(testAssetPath + "/");
}); });
}); });
}); });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -122,7 +122,7 @@ define([
throw new Error('Event not supported by composition: ' + event); 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; return l.callback === callback && l.context === context;
}); });

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ define([
} }
RootRegistry.prototype.addRoot = function (key) { 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 () { this.providers.push(function () {
return key; return key;
}); });

View File

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

View File

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

View File

@ -24,7 +24,7 @@ define([
'./TelemetryMetadataManager', './TelemetryMetadataManager',
'./TelemetryValueFormatter', './TelemetryValueFormatter',
'./DefaultMetadataProvider', './DefaultMetadataProvider',
'../objects/object-utils', 'objectUtils',
'lodash' 'lodash'
], function ( ], function (
TelemetryMetadataManager, TelemetryMetadataManager,
@ -370,7 +370,7 @@ define([
TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) { TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) {
var options = metadatas.map(function (metadata) { var options = metadatas.map(function (metadata) {
var values = metadata.valuesForHints(hints); var values = metadata.valuesForHints(hints);
return _.indexBy(values, 'key'); return _.keyBy(values, 'key');
}).reduce(function (a, b) { }).reduce(function (a, b) {
var results = {}; var results = {};
Object.keys(a).forEach(function (key) { Object.keys(a).forEach(function (key) {
@ -383,7 +383,7 @@ define([
var sortKeys = hints.map(function (h) { var sortKeys = hints.map(function (h) {
return 'hints.' + 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.format === 'enum') {
if (!valueMetadata.values) { if (!valueMetadata.values) {
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value'); valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
} }
if (!valueMetadata.hasOwnProperty('max')) { if (!valueMetadata.hasOwnProperty('max')) {
valueMetadata.max = _.max(valueMetadata.values) + 1; valueMetadata.max = Math.max(valueMetadata.values) + 1;
} }
if (!valueMetadata.hasOwnProperty('min')) { 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 metadata.hints[hint];
} }
}); });
return _.sortByAll(matchingMetadata, ...iteratees); return _.sortBy(matchingMetadata, ...iteratees);
}; };
TelemetryMetadataManager.prototype.getFilterableValues = function () { TelemetryMetadataManager.prototype.getFilterableValues = function () {

View File

@ -75,7 +75,7 @@ export default {
this.items.push(item); this.items.push(item);
}, },
removeItem(identifier) { 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); this.items.splice(index, 1);
}, },

View File

@ -102,7 +102,7 @@ export default {
this.compositions.push({composition, addCallback, removeCallback}); this.compositions.push({composition, addCallback, removeCallback});
}, },
removePrimary(identifier) { 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]; primary = this.primaryTelemetryObjects[index];
this.$set(this.secondaryTelemetryObjects, primary.key, undefined); this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
@ -130,7 +130,7 @@ export default {
removeSecondary(primary) { removeSecondary(primary) {
return (identifier) => { return (identifier) => {
let array = this.secondaryTelemetryObjects[primary.key], 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); array.splice(index, 1);

View File

@ -69,9 +69,9 @@ export default class ConditionClass extends EventEmitter {
console.log('no data received'); console.log('no data received');
return; return;
} }
if (!this.isTelemetryUsed(datum.id)) {
return; if (this.isTelemetryUsed(datum.id)) {
}
this.criteria.forEach(criterion => { this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) { if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects); criterion.getResult(datum, this.conditionManager.telemetryObjects);
@ -82,6 +82,7 @@ export default class ConditionClass extends EventEmitter {
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger); this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
} }
}
isAnyOrAllTelemetry(criterion) { isAnyOrAllTelemetry(criterion) {
return (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any')); return (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any'));
@ -206,7 +207,7 @@ export default class ConditionClass extends EventEmitter {
let latestTimestamp; let latestTimestamp;
let criteriaResults = {}; let criteriaResults = {};
const criteriaRequests = this.criteria const criteriaRequests = this.criteria
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects})); .map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
return Promise.all(criteriaRequests) return Promise.all(criteriaRequests)
.then(results => { .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.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
this.subscriptions[id] = this.openmct.telemetry.subscribe( this.subscriptions[id] = this.openmct.telemetry.subscribe(
endpoint, endpoint,
this.telemetryReceived.bind(this, id) this.telemetryReceived.bind(this, endpoint)
); );
// TODO check if this is needed
this.updateConditionTelemetry(); this.updateConditionTelemetry();
} }
@ -71,6 +70,7 @@ export default class ConditionManager extends EventEmitter {
this.subscriptions[id](); this.subscriptions[id]();
delete this.subscriptions[id]; delete this.subscriptions[id];
delete this.telemetryObjects[id]; delete this.telemetryObjects[id];
this.removeConditionTelemetry();
} }
initialize() { initialize() {
@ -86,6 +86,30 @@ export default class ConditionManager extends EventEmitter {
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry()); 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) { updateCondition(conditionConfiguration, index) {
let condition = this.conditionClassCollection[index]; let condition = this.conditionClassCollection[index];
condition.update(conditionConfiguration); condition.update(conditionConfiguration);
@ -234,9 +258,13 @@ export default class ConditionManager extends EventEmitter {
this.openmct.time.timeSystem() 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, output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier, id: this.conditionSetDomainObject.identifier,
@ -244,12 +272,30 @@ export default class ConditionManager extends EventEmitter {
}, },
latestTimestamp latestTimestamp
); );
return [currentOutput];
}); });
}); });
} }
telemetryReceived(id, datum) { isTelemetryUsed(endpoint) {
const normalizedDatum = this.createNormalizedDatum(datum, id); 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; const timeSystemKey = this.openmct.time.timeSystem().key;
let timestamp = {}; let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey]; timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
@ -283,8 +329,11 @@ export default class ConditionManager extends EventEmitter {
return data; return data;
} }
createNormalizedDatum(telemetryDatum, id) { createNormalizedDatum(telemetryDatum, endpoint) {
const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => { 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 testValue = this.getTestData(metadatum);
const formatter = this.openmct.telemetry.getValueFormatter(metadatum); const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]); datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);

View File

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

View File

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

View File

@ -47,17 +47,24 @@ describe("The condition", function () {
name: "Test Object", name: "Test Object",
telemetry: { telemetry: {
values: [{ values: [{
key: "some-key", key: "value",
name: "Some attribute", name: "Value",
hints: {
range: 2
}
},
{
key: "utc",
name: "Time",
format: "utc",
hints: { hints: {
domain: 1 domain: 1
} }
}, { }, {
key: "some-other-key", key: "testSource",
name: "Another attribute", source: "value",
hints: { name: "Test",
range: 1 format: "string"
}
}] }]
} }
}; };
@ -136,4 +143,38 @@ describe("The condition", function () {
expect(result).toBeTrue(); expect(result).toBeTrue();
expect(conditionObj.criteria.length).toEqual(0); 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,13 @@
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
export default class StyleRuleManager extends EventEmitter { export default class StyleRuleManager extends EventEmitter {
constructor(styleConfiguration, openmct, callback) { constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
super(); super();
this.openmct = openmct; this.openmct = openmct;
this.callback = callback; this.callback = callback;
if (suppressSubscriptionOnEdit) {
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
}
if (styleConfiguration) { if (styleConfiguration) {
this.initialize(styleConfiguration); this.initialize(styleConfiguration);
if (styleConfiguration.conditionSetIdentifier) { if (styleConfiguration.conditionSetIdentifier) {
@ -37,9 +40,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) { initialize(styleConfiguration) {
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier; this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
this.staticStyle = styleConfiguration.staticStyle; this.staticStyle = styleConfiguration.staticStyle;
this.selectedConditionId = styleConfiguration.selectedConditionId;
this.defaultConditionId = styleConfiguration.defaultConditionId;
this.updateConditionStylesMap(styleConfiguration.styles || []); this.updateConditionStylesMap(styleConfiguration.styles || []);
} }
@ -54,7 +73,7 @@ export default class StyleRuleManager extends EventEmitter {
this.handleConditionSetResultUpdated(output[0]); 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,12 +85,16 @@ export default class StyleRuleManager extends EventEmitter {
let isNewConditionSet = !this.conditionSetIdentifier || let isNewConditionSet = !this.conditionSetIdentifier ||
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier); !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
this.initialize(styleConfiguration); this.initialize(styleConfiguration);
if (this.isEditing) {
this.applySelectedConditionStyle();
} else {
//Only resubscribe if the conditionSet has changed. //Only resubscribe if the conditionSet has changed.
if (isNewConditionSet) { if (isNewConditionSet) {
this.subscribeToConditionSet(); this.subscribeToConditionSet();
} }
} }
} }
}
updateConditionStylesMap(conditionStyles) { updateConditionStylesMap(conditionStyles) {
let conditionStyleMap = {}; let conditionStyleMap = {};
@ -103,6 +126,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() { applyStaticStyle() {
if (this.staticStyle) { if (this.staticStyle) {
this.currentStyle = this.staticStyle.style; this.currentStyle = this.staticStyle.style;
@ -123,6 +156,7 @@ export default class StyleRuleManager extends EventEmitter {
} }
delete this.stopProvidingTelemetry; delete this.stopProvidingTelemetry;
this.conditionSetIdentifier = undefined; this.conditionSetIdentifier = undefined;
this.isEditing = undefined;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,31 @@
&__condition { &__condition {
@include discreteItem(); @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 { .c-style {

View File

@ -22,6 +22,7 @@
import TelemetryCriterion from './TelemetryCriterion'; import TelemetryCriterion from './TelemetryCriterion';
import { evaluateResults } from "../utils/evaluator"; import { evaluateResults } from "../utils/evaluator";
import { getLatestTimestamp } from '../utils/time';
export default class AllTelemetryCriterion extends TelemetryCriterion { export default class AllTelemetryCriterion extends TelemetryCriterion {
@ -47,6 +48,21 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
updateTelemetry(telemetryObjects) { updateTelemetry(telemetryObjects) {
this.telemetryObjects = { ...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) { formatData(data, telemetryObjects) {
@ -92,40 +108,53 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry); this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
} }
requestLAD(options) { requestLAD(telemetryObjects) {
options = Object.assign({}, const options = {
options,
{
strategy: 'latest', strategy: 'latest',
size: 1 size: 1
} };
);
if (!this.isValid()) { 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 const telemetryRequests = keys
.map(key => this.openmct.telemetry.request( .map(key => this.openmct.telemetry.request(
options.telemetryObjects[key], telemetryObjects[key],
options options
)); ));
let telemetryDataCache = {};
return Promise.all(telemetryRequests) return Promise.all(telemetryRequests)
.then(telemetryRequestsResults => { .then(telemetryRequestsResults => {
let latestDatum; let latestTimestamp;
const timeSystems = this.openmct.time.getAllTimeSystems();
const timeSystem = this.openmct.time.timeSystem();
telemetryRequestsResults.forEach((results, index) => { telemetryRequestsResults.forEach((results, index) => {
latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
if (index < telemetryRequestsResults.length-1) { const datumId = keys[index];
if (latestDatum) { const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
} telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
}
latestTimestamp = getLatestTimestamp(
latestTimestamp,
normalizedDatum,
timeSystems,
timeSystem
);
}); });
const datum = {
result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
...latestTimestamp
};
return { return {
id: this.id, 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]; 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) { formatData(data) {
const datum = { const datum = {
result: this.computeResult(data) result: this.computeResult(data)
@ -79,14 +94,11 @@ export default class TelemetryCriterion extends EventEmitter {
this.result = this.computeResult(validatedData); this.result = this.computeResult(validatedData);
} }
requestLAD(options) { requestLAD() {
options = Object.assign({}, const options = {
options,
{
strategy: 'latest', strategy: 'latest',
size: 1 size: 1
} };
);
if (!this.isValid()) { if (!this.isValid()) {
return { return {
@ -100,9 +112,11 @@ export default class TelemetryCriterion extends EventEmitter {
options options
).then(results => { ).then(results => {
const latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
return { return {
id: this.id, 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. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import { createOpenMct } from "testTools"; import { createOpenMct } from "testUtils";
import ConditionPlugin from "./plugin"; import ConditionPlugin from "./plugin";
let openmct = createOpenMct(); describe('the plugin', function () {
openmct.install(new ConditionPlugin());
let conditionSetDefinition; let conditionSetDefinition;
let mockConditionSetDomainObject; let mockConditionSetDomainObject;
let element; let element;
let child; let child;
let openmct;
describe('the plugin', function () {
beforeAll((done) => { beforeAll((done) => {
openmct = createOpenMct();
openmct.install(new ConditionPlugin());
conditionSetDefinition = openmct.types.get('conditionSet').definition; conditionSetDefinition = openmct.types.get('conditionSet').definition;
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
element = document.createElement('div'); element = document.createElement('div');
child = document.createElement('div'); child = document.createElement('div');
@ -55,7 +51,7 @@ describe('the plugin', function () {
conditionSetDefinition.initialize(mockConditionSetDomainObject); conditionSetDefinition.initialize(mockConditionSetDomainObject);
openmct.on('start', done); openmct.on('start', done);
openmct.start(appHolder); openmct.startHeadless();
}); });
let mockConditionSetObject = { let mockConditionSetObject = {

View File

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

View File

@ -19,6 +19,8 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import isEmpty from 'lodash/isEmpty';
const NONE_VALUE = '__no_value'; const NONE_VALUE = '__no_value';
const styleProps = { const styleProps = {
@ -154,7 +156,7 @@ export const getApplicableStylesForItem = (domainObject, item) => {
}; };
export const getStylesWithoutNoneValue = (style) => { export const getStylesWithoutNoneValue = (style) => {
if (_.isEmpty(style) || !style) { if (isEmpty(style) || !style) {
return; return;
} }
let styleObj = {}; let styleObj = {};

View File

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

View File

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

View File

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

View File

@ -52,7 +52,7 @@ export default {
}, },
initObjectStyles() { initObjectStyles() {
if (!this.styleRuleManager) { 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 { } else {
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle); this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
} }

View File

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

View File

@ -62,6 +62,7 @@
<script> <script>
import FilterField from './FilterField.vue'; import FilterField from './FilterField.vue';
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue'; import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
import isEmpty from 'lodash/isEmpty';
export default { export default {
inject: ['openmct'], inject: ['openmct'],
@ -102,7 +103,7 @@ export default {
hasActiveFilters() { hasActiveFilters() {
// Should be true when the user has entered any filter values. // Should be true when the user has entered any filter values.
return Object.values(this.persistedFilters).some(comparator => { 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> <script>
import FilterObject from './FilterObject.vue'; 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 = 'Filters applied';
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied'; const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';

View File

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

View File

@ -220,7 +220,7 @@ export default {
return; 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); this.imageHistory.splice(index, 0, datum);
}, },
updateValues(datum) { updateValues(datum) {

View File

@ -162,19 +162,32 @@ export default {
}).show(this.embed.snapshot.src); }).show(this.embed.snapshot.src);
}, },
changeLocation() { changeLocation() {
this.openmct.time.stopClock();
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
const link = this.embed.historicLink; const link = this.embed.historicLink;
if (!link) { if (!link) {
return; 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();
window.location.href = link; window.location.href = link;
const message = 'Time bounds changed to fixed timespan mode';
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';
}
this.openmct.notifications.alert(message); this.openmct.notifications.alert(message);
}, },
formatTime(unixTime, timeFormat) { formatTime(unixTime, timeFormat) {

View File

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

View File

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

View File

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

View File

@ -0,0 +1,58 @@
/*****************************************************************************
* 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 NotebookPlugin from './plugin.js';
import {createOpenMct} from "@/testTools";
describe('When the Notebook Snapshot Plugin is installed,', function () {
let openmct = createOpenMct();
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
let element = document.createElement('div');
let child = document.createElement('div');
element.appendChild(child);
openmct.install(NotebookPlugin());
let notebookDefinition = openmct.types.get('notebook').definition;
let mockNotebookObject = {
identifier: {
key: 'testNotebookKey',
namespace: ''
},
type: 'notebook'
};
notebookDefinition.initialize(mockNotebookObject);
it('defines a notebook object type with the correct key', () => {
expect(notebookDefinition.key).toEqual(mockNotebookObject.key);
});
it('Global Notebook Indicator is installed', function () {
expect(openmct.indicators.indicatorObjects.length).toEqual(1);
});
});

View File

@ -1,6 +1,46 @@
const NOTEBOOK_LOCAL_STORAGE = 'notebook-storage'; 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() { export function clearDefaultNotebook() {
currentNotebookObject = null;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, null); window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, null);
} }
@ -10,20 +50,21 @@ export function getDefaultNotebook() {
return JSON.parse(notebookStorage); return JSON.parse(notebookStorage);
} }
export function setDefaultNotebook(notebookStorage) { export function setDefaultNotebook(openmct, notebookStorage) {
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage)); observeDefaultNotebookObject(openmct, notebookStorage);
saveDefaultNotebook(notebookStorage);
} }
export function setDefaultNotebookSection(section) { export function setDefaultNotebookSection(section) {
const notebookStorage = getDefaultNotebook(); const notebookStorage = getDefaultNotebook();
notebookStorage.section = section; notebookStorage.section = section;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage)); saveDefaultNotebook(notebookStorage);
} }
export function setDefaultNotebookPage(page) { export function setDefaultNotebookPage(page) {
const notebookStorage = getDefaultNotebook(); const notebookStorage = getDefaultNotebook();
notebookStorage.page = page; notebookStorage.page = page;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage)); saveDefaultNotebook(notebookStorage);
} }

View File

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

View File

@ -54,6 +54,7 @@ function (
* @constructor * @constructor
*/ */
function MCTChartController($scope) { function MCTChartController($scope) {
this.$onInit = () => {
this.$scope = $scope; this.$scope = $scope;
this.isDestroyed = false; this.isDestroyed = false;
this.lines = []; this.lines = [];
@ -76,6 +77,7 @@ function (
this.$scope.$watch('rectangles', this.scheduleDraw); this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this); this.config.series.forEach(this.onSeriesAdd, this);
} }
}
eventHelpers.extend(MCTChartController.prototype); eventHelpers.extend(MCTChartController.prototype);
@ -150,7 +152,7 @@ function (
MCTChartController.prototype.destroy = function () { MCTChartController.prototype.destroy = function () {
this.isDestroyed = true; this.isDestroyed = true;
this.stopListening(); this.stopListening();
_.invoke(this.lines, 'destroy'); this.lines.forEach(line => line.destroy());
DrawLoader.releaseDrawAPI(this.drawAPI); DrawLoader.releaseDrawAPI(this.drawAPI);
}; };

View File

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

View File

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

View File

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

View File

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

View File

@ -182,21 +182,6 @@ define([
this.set('format', yFormat.format.bind(yFormat)); this.set('format', yFormat.format.bind(yFormat));
this.set('values', yMetadata.values); this.set('values', yMetadata.values);
if (!label) { 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) { var labelName = series.map(function (s) {
return s.metadata.value(s.get('yKey')).name; return s.metadata.value(s.get('yKey')).name;
}).reduce(function (a, b) { }).reduce(function (a, b) {
@ -208,7 +193,28 @@ define([
} }
return ''; return '';
}, undefined); }, undefined);
if (labelName) {
this.set('label', 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) { 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); eventHelpers.extend(Draw2D.prototype);
// Convert from logical to physical x coordinates // Convert from logical to physical x coordinates

View File

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

View File

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

View File

@ -31,7 +31,7 @@ define([
function dynamicPathForKey(key) { function dynamicPathForKey(key) {
return function (object, model) { return function (object, model) {
var modelIdentifier = model.get('identifier'); var modelIdentifier = model.get('identifier');
var index = _.findIndex(object.configuration.series, function (s) { var index = object.configuration.series.findIndex(s => {
return _.isEqual(s.identifier, modelIdentifier); return _.isEqual(s.identifier, modelIdentifier);
}); });
return 'configuration.series[' + index + '].' + key; return 'configuration.series[' + index + '].' + key;

View File

@ -73,10 +73,10 @@ define([
if (range.max === '' || range.max === null || typeof range.max === 'undefined') { if (range.max === '' || range.max === null || typeof range.max === 'undefined') {
return 'Must specify Maximum'; return 'Must specify Maximum';
} }
if (_.isNaN(Number(range.min))) { if (Number.isNaN(Number(range.min))) {
return 'Minimum must be a number.'; return 'Minimum must be a number.';
} }
if (_.isNaN(Number(range.max))) { if (Number.isNaN(Number(range.max))) {
return 'Maximum must be a number.'; return 'Maximum must be a number.';
} }
if (Number(range.min) > Number(range.max)) { if (Number(range.min) > Number(range.max)) {

View File

@ -34,6 +34,7 @@ define([
* values near the cursor. * values near the cursor.
*/ */
function MCTPlotController($scope, $element, $window) { function MCTPlotController($scope, $element, $window) {
this.$onInit = () => {
this.$scope = $scope; this.$scope = $scope;
this.$scope.config = this.config; this.$scope.config = this.config;
this.$scope.plot = this; this.$scope.plot = this;
@ -54,6 +55,7 @@ define([
this.initialize(); this.initialize();
} }
}
MCTPlotController.$inject = ['$scope', '$element', '$window']; MCTPlotController.$inject = ['$scope', '$element', '$window'];
@ -65,10 +67,10 @@ define([
} }
this.$canvas = this.$element.find('canvas'); this.$canvas = this.$element.find('canvas');
this.listenTo(this.$canvas, 'click', this.onMouseClick, this);
this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this); this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this); this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this); this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee(); this.watchForMarquee();
}; };
@ -76,7 +78,6 @@ define([
MCTPlotController.prototype.initialize = function () { MCTPlotController.prototype.initialize = function () {
this.$canvas = this.$element.find('canvas'); this.$canvas = this.$element.find('canvas');
this.listenTo(this.$canvas, 'click', this.onMouseClick, this);
this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this); this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this); this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this); this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
@ -208,23 +209,6 @@ define([
this.highlightValues(point); this.highlightValues(point);
}; };
MCTPlotController.prototype.onMouseClick = function ($event) {
const isClick = this.isMouseClick();
if (this.pan) {
this.endPan($event);
}
if (this.marquee) {
this.endMarquee($event);
}
this.$scope.$apply();
if (!this.$scope.highlights.length || !isClick) {
return;
}
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
};
MCTPlotController.prototype.highlightValues = function (point) { MCTPlotController.prototype.highlightValues = function (point) {
this.highlightPoint = point; this.highlightPoint = point;
this.$scope.$emit('plot:highlight:update', point); this.$scope.$emit('plot:highlight:update', point);
@ -272,11 +256,23 @@ define([
MCTPlotController.prototype.onMouseUp = function ($event) { MCTPlotController.prototype.onMouseUp = function ($event) {
this.stopListening(this.$window, 'mouseup', this.onMouseUp, this); this.stopListening(this.$window, 'mouseup', this.onMouseUp, this);
this.stopListening(this.$window, 'mousemove', this.trackMousePosition, this); this.stopListening(this.$window, 'mousemove', this.trackMousePosition, this);
if (this.isMouseClick()) {
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
}
if (this.allowPan) {
return this.endPan($event);
}
if (this.allowMarquee) {
return this.endMarquee($event);
}
}; };
MCTPlotController.prototype.isMouseClick = function () { MCTPlotController.prototype.isMouseClick = function () {
if (!this.marquee) { if (!this.marquee) {
return; return false;
} }
const { start, end } = this.marquee; const { start, end } = this.marquee;
@ -329,7 +325,7 @@ define([
} else { } else {
// A history entry is created by startMarquee, need to remove // A history entry is created by startMarquee, need to remove
// if marquee zoom doesn't occur. // if marquee zoom doesn't occur.
this.back(); this.plotHistory.pop();
} }
this.$scope.rectangles = []; this.$scope.rectangles = [];
this.marquee = undefined; this.marquee = undefined;

View File

@ -114,6 +114,7 @@ define([
} }
function MCTTicksController($scope, $element) { function MCTTicksController($scope, $element) {
this.$onInit = () => {
this.$scope = $scope; this.$scope = $scope;
this.$element = $element; this.$element = $element;
@ -124,6 +125,7 @@ define([
this.listenTo(this.$scope, '$destroy', this.stopListening, this); this.listenTo(this.$scope, '$destroy', this.stopListening, this);
this.updateTicks(); this.updateTicks();
} }
}
MCTTicksController.$inject = ['$scope', '$element']; MCTTicksController.$inject = ['$scope', '$element'];

View File

@ -81,7 +81,8 @@ define(
clonedElement.classList.add(className); clonedElement.classList.add(className);
} }
element.id = oldId; element.id = oldId;
} },
removeContainer: true // Set to false to debug what html2canvas renders
}).then(function (canvas) { }).then(function (canvas) {
dialog.dismiss(); dialog.dismiss();
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {

View File

@ -227,8 +227,9 @@ define([
}; };
PlotController.prototype.stopLoading = function () { PlotController.prototype.stopLoading = function () {
this.$scope.$evalAsync(() => {
this.$scope.pending -= 1; this.$scope.pending -= 1;
this.$scope.$digest(); });
}; };
/** /**

View File

@ -76,7 +76,7 @@ define([
if (childObj) { if (childObj) {
var index = telemetryObjects.indexOf(childObj); var index = telemetryObjects.indexOf(childObj);
telemetryObjects.splice(index, 1); telemetryObjects.splice(index, 1);
$scope.$broadcast('plot:tickWidth', _.max(tickWidthMap)); $scope.$broadcast('plot:tickWidth', Math.max(...Object.values(tickWidthMap)));
} }
} }

View File

@ -88,7 +88,8 @@ define([
var bundleMap = { var bundleMap = {
LocalStorage: 'platform/persistence/local', LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items', MyItems: 'platform/features/my-items',
CouchDB: 'platform/persistence/couch' CouchDB: 'platform/persistence/couch',
Elasticsearch: 'platform/persistence/elastic'
}; };
var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) { var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {

View File

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

View File

@ -1,6 +1,6 @@
define ([ define ([
'./ConditionEvaluator', './ConditionEvaluator',
'../../../api/objects/object-utils', 'objectUtils',
'EventEmitter', 'EventEmitter',
'zepto', 'zepto',
'lodash' 'lodash'
@ -9,7 +9,8 @@ define ([
objectUtils, objectUtils,
EventEmitter, EventEmitter,
$, $,
_ _,
) { ) {
/** /**

View File

@ -5,7 +5,7 @@ define([
'./TestDataManager', './TestDataManager',
'./WidgetDnD', './WidgetDnD',
'./eventHelpers', './eventHelpers',
'../../../api/objects/object-utils', 'objectUtils',
'lodash', 'lodash',
'zepto' 'zepto'
], function ( ], function (

View File

@ -1,6 +1,6 @@
define([ define([
'./Select', './Select',
'../../../../api/objects/object-utils' 'objectUtils'
], function ( ], function (
Select, Select,
objectUtils objectUtils

View File

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

View File

@ -23,7 +23,7 @@
define([ define([
'./SummaryWidgetRule', './SummaryWidgetRule',
'../eventHelpers', '../eventHelpers',
'../../../../api/objects/object-utils', 'objectUtils',
'lodash' 'lodash'
], function ( ], function (
SummaryWidgetRule, SummaryWidgetRule,
@ -80,10 +80,12 @@ define([
} }
}.bind(this); }.bind(this);
/* eslint-disable you-dont-need-lodash-underscore/map */
unsubscribes = _.map( unsubscribes = _.map(
realtimeStates, realtimeStates,
this.subscribeToObjectState.bind(this, updateCallback) this.subscribeToObjectState.bind(this, updateCallback)
); );
/* eslint-enable you-dont-need-lodash-underscore/map */
}.bind(this)); }.bind(this));
return function () { return function () {
@ -151,11 +153,13 @@ define([
SummaryWidgetEvaluator.prototype.getBaseStateClone = function () { SummaryWidgetEvaluator.prototype.getBaseStateClone = function () {
return this.load() return this.load()
.then(function () { .then(function () {
/* eslint-disable you-dont-need-lodash-underscore/values */
return _(this.baseState) return _(this.baseState)
.values() .values()
.map(_.clone) .map(_.clone)
.indexBy('id') .keyBy('id')
.value(); .value();
/* eslint-enable you-dont-need-lodash-underscore/values */
}.bind(this)); }.bind(this));
}; };
@ -182,7 +186,7 @@ define([
* @private. * @private.
*/ */
SummaryWidgetEvaluator.prototype.updateObjectStateFromLAD = function (options, objectState) { SummaryWidgetEvaluator.prototype.updateObjectStateFromLAD = function (options, objectState) {
options = _.extend({}, options, { options = Object.assign({}, options, {
strategy: 'latest', strategy: 'latest',
size: 1 size: 1
}); });
@ -255,10 +259,12 @@ define([
} }
} }
/* eslint-disable you-dont-need-lodash-underscore/map */
var latestTimestamp = _(state) var latestTimestamp = _(state)
.map('timestamps') .map('timestamps')
.sortBy(timestampKey) .sortBy(timestampKey)
.last(); .last();
/* eslint-enable you-dont-need-lodash-underscore/map */
if (!latestTimestamp) { if (!latestTimestamp) {
latestTimestamp = {}; latestTimestamp = {};

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