Compare commits

...

8 Commits

Author SHA1 Message Date
akhenry
aea7753ecb Allow 'editable' property on view providers to optionally be a function 2018-09-01 12:40:30 +01:00
akhenry
ecefed2b6a Organized source directory 2018-09-01 11:43:09 +01:00
akhenry
4d9a186d6e reorganizing source directory 2018-08-30 16:19:36 -07:00
Andrew Henry
15c6d46674 Updated vue tables to use ES6 style function notation 2018-08-30 15:17:29 -07:00
Andrew Henry
4d316c3806 Fixed table configuration persistence 2018-08-30 08:18:34 -07:00
Andrew Henry
61d62eeaf6 Updated table configuration to remove table namespace, and support column width in future. 2018-08-29 18:11:01 -07:00
Andrew Henry
f0753de949 Reimplemented tables in Vue 2018-08-29 17:39:44 -07:00
Pete Richards
0d53898af9 Build refactor to webpack (#2139)
* Move to webpack build
* Use webpack for building openmct.  Move SCSS to one folder and load
all core css up front.  Remove bower, begin removing gulp in favor
of npm run.
* Uses eslint instead of jshint and jscs.  Merge style checking rules
into .eshintrc.js, carrying over core parts of crockford style and
our adaptations.  Current code base fails to pass the linter, want
to separate linter changes from fixes to linting rules.
* Support for Vue SFC with example
* Remove outdated examples
* Use HTML loader for html (supports relative imports of resources e.g.
images) and raw-loader for when javascript must be loaded as text.
2018-08-07 14:47:50 -07:00
378 changed files with 3633 additions and 7426 deletions

View File

@@ -11,24 +11,20 @@ jobs:
name: Update npm
command: 'sudo npm install -g npm@latest'
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}-{{ checksum "bower.json" }}
key: dependency-cache-{{ checksum "package.json" }}
- run:
name: Installing dependencies (npm install)
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}-{{ checksum "bower.json" }}
key: dependency-cache-{{ checksum "package.json" }}
paths:
- node_modules
- bower_components
- run:
name: npm run test
command: npm run test
- run:
name: npm run lint
command: npm run lint
- run:
name: npm run checkstyle
command: npm run checkstyle
- store_artifacts:
path: dist
prefix: dist

79
.eslintrc.js Normal file
View File

@@ -0,0 +1,79 @@
module.exports = {
"env": {
"browser": true,
"es6": true,
"jasmine": true,
"amd": true
},
"extends": "eslint:recommended",
"parser": "babel-eslint",
"parserOptions": {
"allowImportExportEverywhere": true,
"ecmaVersion": 2015,
"ecmaFeatures": {
"impliedStrict": true
}
},
"rules": {
"no-bitwise": "error",
"curly": "error",
"eqeqeq": "error",
"guard-for-in": "error",
"no-extend-native": "error",
"no-inner-declarations": "off",
"no-use-before-define": ["error", "nofunc"],
"no-caller": "error",
"no-sequences": "error",
"no-irregular-whitespace": "error",
"no-new": "error",
"no-shadow": "error",
"no-undef": "error",
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "none"
}
],
"no-console": "off",
"no-trailing-spaces": "error",
"space-before-function-paren": [
"error",
{
"anonymous": "always",
"asyncArrow": "always",
"named": "never",
}
],
"array-bracket-spacing": "error",
"space-in-parens": "error",
"space-before-blocks": "error",
"comma-dangle": "error",
"eol-last": "error",
"new-cap": [
"error",
{
"capIsNew": false,
"properties": false
}
],
"dot-notation": "error",
"indent": ["error", 4]
},
"overrides": [
{
"files": ["*Spec.js"],
"rules": {
"no-unused-vars": [
"warn",
{
"vars": "all",
"args": "none",
"varsIgnorePattern": "controller",
}
]
}
}
]
};

View File

@@ -1,5 +0,0 @@
{
"preset": "crockford",
"requireMultipleVarDecl": false,
"requireVarDeclFirst": false
}

View File

@@ -1,26 +0,0 @@
{
"bitwise": true,
"browser": true,
"curly": true,
"eqeqeq": true,
"forin": true,
"freeze": true,
"funcscope": false,
"futurehostile": true,
"latedef": true,
"noarg": true,
"nocomma": true,
"nonbsp": true,
"nonew": true,
"predef": [
"define",
"Promise",
"WeakMap",
"Map"
],
"shadow": "outer",
"strict": "implied",
"undef": true,
"unused": "vars",
"latedef": "nofunc"
}

View File

@@ -84,7 +84,6 @@ Documentation will be generated in `target/docs`.
## Deploying Open MCT
Open MCT is built using [`npm`](http://npmjs.com/)
and [`gulp`](http://gulpjs.com/).
To build Open MCT for deployment:
@@ -94,32 +93,13 @@ This will compile and minify JavaScript sources, as well as copy over assets.
The contents of the `dist` folder will contain a runnable Open MCT
instance (e.g. by starting an HTTP server in that directory), including:
* A `main.js` file containing Open MCT source code.
* Various assets in the `example` and `platform` directories.
* An `index.html` that runs Open MCT in its default configuration.
Additional `gulp` tasks are defined in [the gulpfile](gulpfile.js).
## Bundles
A bundle is a group of software components (including source code, declared
as AMD modules, as well as resources such as images and HTML templates)
that is intended to be added or removed as a single unit. A plug-in for
Open MCT will be expressed as a bundle; platform components are also
expressed as bundles.
A bundle is also just a directory which contains a file `bundle.json`,
which declares its contents.
The file `bundles.json` (note the plural), at the top level of the
repository, is a JSON file containing an array of all bundles (expressed as
directory names) to include in a running instance of Open MCT. Adding or
removing paths from this list will add or remove bundles from the running
application.
* `openmct.js` - Open MCT source code.
* `openmct.css` - Basic styles to load to prevent a FOUC.
* `index.html`, an example to run Open MCT in the basic configuration.
## Tests
Tests are written for [Jasmine 1.3](http://jasmine.github.io/1.3/introduction.html)
Tests are written for [Jasmine 3](http://jasmine.github.io/)
and run by [Karma](http://karma-runner.github.io). To run:
`npm test`

64
app.js
View File

@@ -7,28 +7,17 @@
* node app.js [options]
*/
(function () {
"use strict";
var BUNDLE_FILE = 'bundles.json',
options = require('minimist')(process.argv.slice(2)),
express = require('express'),
app = express(),
fs = require('fs'),
request = require('request');
const options = require('minimist')(process.argv.slice(2));
const express = require('express');
const app = express();
const fs = require('fs');
const request = require('request');
// Defaults
options.port = options.port || options.p || 8080;
options.host = options.host || options.h || 'localhost'
options.directory = options.directory || options.D || '.';
['include', 'exclude', 'i', 'x'].forEach(function (opt) {
options[opt] = options[opt] || [];
// Make sure includes/excludes always end up as arrays
options[opt] = Array.isArray(options[opt]) ?
options[opt] : [options[opt]];
});
options.include = options.include.concat(options.i);
options.exclude = options.exclude.concat(options.x);
// Show command line options
if (options.help || options.h) {
@@ -36,8 +25,6 @@
console.log("Options:");
console.log(" --help, -h Show this message.");
console.log(" --port, -p <number> Specify port.");
console.log(" --include, -i <bundle> Include the specified bundle.");
console.log(" --exclude, -x <bundle> Exclude the specified bundle.");
console.log(" --directory, -D <bundle> Serve files from specified directory.");
console.log("");
process.exit(0);
@@ -45,28 +32,6 @@
app.disable('x-powered-by');
// Override bundles.json for HTTP requests
app.use('/' + BUNDLE_FILE, function (req, res) {
var bundles;
try {
bundles = JSON.parse(fs.readFileSync(BUNDLE_FILE, 'utf8'));
} catch (e) {
bundles = [];
}
// Handle command line inclusions/exclusions
bundles = bundles.concat(options.include);
bundles = bundles.filter(function (bundle) {
return options.exclude.indexOf(bundle) === -1;
});
bundles = bundles.filter(function (bundle, index) { // Uniquify
return bundles.indexOf(bundle) === index;
});
res.send(JSON.stringify(bundles));
});
app.use('/proxyUrl', function proxyRequest(req, res, next) {
console.log('Proxying request to: ', req.query.url);
req.pipe(request({
@@ -75,11 +40,24 @@
}).on('error', next)).pipe(res);
});
// Expose everything else as static files
app.use(express['static'](options.directory));
const webpack = require('webpack');
const webpackConfig = require('./webpack.config.js');
const compiler = webpack(webpackConfig);
const webpackDevRoute = require('webpack-dev-middleware')(
compiler, {
publicPath: '/dist',
logLevel: 'warn'
}
);
app.use(webpackDevRoute);
// Expose index.html for development users.
app.get('/', function (req, res) {
fs.createReadStream('index.html').pipe(res);
});
// Finally, open the HTTP server and log the instance to the console
app.listen(options.port, options.host, function() {
console.log('Open MCT application running at %s:%s', options.host, options.port)
});
}());

View File

@@ -1,27 +0,0 @@
{
"name": "openmct",
"description": "The Open MCT core platform",
"main": "",
"license": "Apache-2.0",
"moduleType": [],
"homepage": "http://nasa.github.io/openmct/",
"private": true,
"dependencies": {
"angular": "1.4.4",
"angular-route": "1.4.4",
"moment": "^2.11.1",
"moment-duration-format": "^1.3.0",
"requirejs": "~2.1.22",
"text": "requirejs-text#^2.0.14",
"es6-promise": "^3.3.0",
"screenfull": "^3.0.0",
"node-uuid": "^1.4.7",
"comma-separated-values": "^3.6.4",
"file-saver": "1.3.3",
"zepto": "^1.1.6",
"eventemitter3": "^1.2.0",
"lodash": "3.10.1",
"almond": "~0.3.2",
"moment-timezone": "^0.5.13"
}
}

View File

@@ -1,8 +0,0 @@
This bundle is intended to serve as an example of registering
extensions which are mapped directly to built-in Angular features.
These are:
* Controllers
* Directives
* Routes
* Services

View File

@@ -1,74 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
"./src/ExampleController",
"./src/ExampleDirective",
"./src/ExampleService",
'legacyRegistry'
], function (
ExampleController,
ExampleDirective,
ExampleService,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/builtins", {
"name": "Angular Built-ins Example",
"description": "Example showing how to declare extensions with built-in support from Angular.",
"sources": "src",
"extensions": {
"controllers": [
{
"key": "ExampleController",
"implementation": ExampleController,
"depends": [
"$scope",
"exampleService"
]
}
],
"directives": [
{
"key": "exampleDirective",
"implementation": ExampleDirective,
"depends": [
"examples[]"
]
}
],
"routes": [
{
"templateUrl": "templates/example.html"
}
],
"services": [
{
"key": "exampleService",
"implementation": ExampleService
}
]
}
});
});

View File

@@ -1,24 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<p>Hello, world! I am the default route.</p>
<p ng-controller="ExampleController">My controller has told me: "{{phrase}}"</p>
<span example-directive></span>

View File

@@ -1,42 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining ExampleController. Created by vwoeltje on 11/4/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function ExampleController($scope, exampleService) {
$scope.phrase = exampleService.getMessage();
}
return ExampleController;
}
);

View File

@@ -1,66 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining ExampleDirective. Created by vwoeltje on 11/4/14.
*/
define(
[],
function () {
"use strict";
var HAS_EXTENSIONS = "A directive loaded these message from " +
"example extensions.",
NO_EXTENSIONS = "A directive tried to load example extensions," +
" but found none.",
MESSAGE = "I heard this from my partial constructor.";
/**
*
* @constructor
*/
function ExampleDirective(examples) {
// Build up a template from example extensions
var template = examples.length > 0 ?
HAS_EXTENSIONS : NO_EXTENSIONS;
template += "<ul>";
examples.forEach(function (E) {
template += "<li>";
if (typeof E === 'function') {
template += (new E(MESSAGE)).getText();
} else {
template += E.text;
}
template += "</li>";
});
template += "</ul>";
return {
template: template
};
}
return ExampleDirective;
}
);

View File

@@ -1,46 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining ExampleService. Created by vwoeltje on 11/4/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function ExampleService() {
return {
getMessage: function () {
return "I heard this from a service";
}
};
}
return ExampleService;
}
);

View File

@@ -1,82 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
"./src/SomeProvider",
"./src/SomeOtherProvider",
"./src/SomeDecorator",
"./src/SomeOtherDecorator",
"./src/SomeAggregator",
"./src/SomeOtherExample",
'legacyRegistry'
], function (
SomeProvider,
SomeOtherProvider,
SomeDecorator,
SomeOtherDecorator,
SomeAggregator,
SomeOtherExample,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/composite", {
"extensions": {
"components": [
{
"implementation": SomeProvider,
"provides": "someService",
"type": "provider"
},
{
"implementation": SomeOtherProvider,
"provides": "someService",
"type": "provider"
},
{
"implementation": SomeDecorator,
"provides": "someService",
"type": "decorator"
},
{
"implementation": SomeOtherDecorator,
"provides": "someService",
"type": "decorator"
},
{
"implementation": SomeAggregator,
"provides": "someService",
"type": "aggregator"
}
],
"examples": [
{
"implementation": SomeOtherExample,
"depends": [
"someService"
]
}
]
}
});
});

View File

@@ -1,50 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining SomeAggregator. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function SomeAggregator(someProviders) {
return {
getMessages: function () {
return someProviders.map(function (provider) {
return provider.getMessages();
}).reduce(function (a, b) {
return a.concat(b);
}, []);
}
};
}
return SomeAggregator;
}
);

View File

@@ -1,48 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining SomeDecorator. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function SomeDecorator(someProvider) {
return {
getMessages: function () {
return someProvider.getMessages().map(function (msg) {
return msg.toLocaleUpperCase();
});
}
};
}
return SomeDecorator;
}
);

View File

@@ -1,48 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining SomeOtherDecorator. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function SomeOtherDecorator(someProvider) {
return {
getMessages: function () {
return someProvider.getMessages().map(function (msg) {
return msg + "...";
});
}
};
}
return SomeOtherDecorator;
}
);

View File

@@ -1,48 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining SomeOtherProvider. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function SomeOtherProvider() {
return {
getMessages: function () {
return [
"I am a message from some other provider."
];
}
};
}
return SomeOtherProvider;
}
);

View File

@@ -1,48 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining SomeProvider. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function SomeProvider() {
return {
getMessages: function () {
return [
"I am a message from some provider."
];
}
};
}
return SomeProvider;
}
);

View File

@@ -27,12 +27,11 @@
* Modified by shale on 06/23/2015.
*/
define(
['text!../data/transcript.json'],
function (transcript) {
['../data/transcript.json'],
function (messages) {
"use strict";
var firstObservedTime = Date.now(),
messages = JSON.parse(transcript);
var firstObservedTime = Date.now();
function EventTelemetry(request, interval) {

View File

@@ -1,51 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
"./src/SomeExample",
'legacyRegistry'
], function (
SomeExample,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/extensions", {
"name": "Custom Extensions Examples",
"description": "Example showing how to declare custom extensions.",
"sources": "src",
"extensions": {
"examples": [
{
"text": "I came from example/extensions"
},
{
"implementation": SomeExample,
"depends": [
"exampleService"
]
}
]
}
});
});

View File

@@ -1,52 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining SomeExample. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
*
* @constructor
*/
function SomeExample(exampleService, message) {
return {
getText: function () {
return [
'"',
exampleService.getMessage(),
'" and "',
message,
'"'
].join("");
}
};
}
return SomeExample;
}
);

View File

@@ -27,8 +27,14 @@ define([
) {
var RED = 0.9,
YELLOW = 0.5,
var RED = {
sin: 0.9,
cos: 0.9
},
YELLOW = {
sin: 0.5,
cos: 0.5
},
LIMITS = {
rh: {
cssClass: "s-limit-upr s-limit-red",
@@ -67,17 +73,18 @@ define([
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
return {
evaluate: function (datum, valueMetadata) {
var range = valueMetadata ? valueMetadata.key : 'sin'
if (datum[range] > RED) {
var range = valueMetadata && valueMetadata.key;
if (datum[range] > RED[range]) {
return LIMITS.rh;
}
if (datum[range] < -RED) {
if (datum[range] < -RED[range]) {
return LIMITS.rl;
}
if (datum[range] > YELLOW) {
if (datum[range] > YELLOW[range]) {
return LIMITS.yh;
}
if (datum[range] < -YELLOW) {
if (datum[range] < -YELLOW[range]) {
return LIMITS.yl;
}
}

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
'text!./generatorWorker.js',
'raw-loader!./generatorWorker.js',
'uuid'
], function (
workerText,

View File

@@ -0,0 +1,19 @@
<template>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
}
}
}
</script>
<style>
.example {
color: red;
}
</style>

View File

@@ -0,0 +1,37 @@
import Vue from 'Vue';
import HelloWorld from './HelloWorld.vue';
function SimpleVuePlugin () {
return function install(openmct) {
var views = (openmct.mainViews || openmct.objectViews);
openmct.types.addType('hello-world', {
name: 'Hello World',
description: 'An introduction object',
creatable: true
});
openmct.objectViews.addProvider({
name: "demo-provider",
key: "hello-world",
cssClass: "icon-packet",
canView: function (d) {
return d.type === 'hello-world';
},
view: function (domainObject) {
var vm;
return {
show: function (container) {
vm = new Vue(HelloWorld);
container.appendChild(vm.$mount().$el);
},
destroy: function (container) {
vm.$destroy();
}
};
}
});
}
}
export default SimpleVuePlugin

View File

@@ -1,10 +1,26 @@
define([
"./src/ExampleStyleGuideModelProvider",
"./src/MCTExample",
"./res/templates/intro.html",
"./res/templates/standards.html",
"./res/templates/colors.html",
"./res/templates/status.html",
"./res/templates/glyphs.html",
"./res/templates/controls.html",
"./res/templates/input.html",
"./res/templates/menus.html",
'legacyRegistry'
], function (
ExampleStyleGuideModelProvider,
MCTExample,
introTemplate,
standardsTemplate,
colorsTemplate,
statusTemplate,
glyphsTemplate,
controlsTemplate,
inputTemplate,
menusTemplate,
legacyRegistry
) {
legacyRegistry.register("example/styleguide", {
@@ -23,14 +39,14 @@ define([
{ "key": "styleguide.menus", "name": "Menus", "cssClass": "icon-page", "description": "Context menus, dropdowns" }
],
"views": [
{ "key": "styleguide.intro", "type": "styleguide.intro", "templateUrl": "templates/intro.html", "editable": false },
{ "key": "styleguide.standards", "type": "styleguide.standards", "templateUrl": "templates/standards.html", "editable": false },
{ "key": "styleguide.colors", "type": "styleguide.colors", "templateUrl": "templates/colors.html", "editable": false },
{ "key": "styleguide.status", "type": "styleguide.status", "templateUrl": "templates/status.html", "editable": false },
{ "key": "styleguide.glyphs", "type": "styleguide.glyphs", "templateUrl": "templates/glyphs.html", "editable": false },
{ "key": "styleguide.controls", "type": "styleguide.controls", "templateUrl": "templates/controls.html", "editable": false },
{ "key": "styleguide.input", "type": "styleguide.input", "templateUrl": "templates/input.html", "editable": false },
{ "key": "styleguide.menus", "type": "styleguide.menus", "templateUrl": "templates/menus.html", "editable": false }
{ "key": "styleguide.intro", "type": "styleguide.intro", "template": introTemplate, "editable": false },
{ "key": "styleguide.standards", "type": "styleguide.standards", "template": standardsTemplate, "editable": false },
{ "key": "styleguide.colors", "type": "styleguide.colors", "template": colorsTemplate, "editable": false },
{ "key": "styleguide.status", "type": "styleguide.status", "template": statusTemplate, "editable": false },
{ "key": "styleguide.glyphs", "type": "styleguide.glyphs", "template": glyphsTemplate, "editable": false },
{ "key": "styleguide.controls", "type": "styleguide.controls", "template": controlsTemplate, "editable": false },
{ "key": "styleguide.input", "type": "styleguide.input", "template": inputTemplate, "editable": false },
{ "key": "styleguide.menus", "type": "styleguide.menus", "template": menusTemplate, "editable": false }
],
"roots": [
{
@@ -85,16 +101,6 @@ define([
"$q"
]
}
],
"stylesheets": [
{
"stylesheetUrl": "css/style-guide-espresso.css",
"theme": "espresso"
},
{
"stylesheetUrl": "css/style-guide-snow.css",
"theme": "snow"
}
]
}
});

View File

@@ -34,7 +34,7 @@
<p>As you develop plugins for Open MCT, consider how a generalized component might be combined with others when designing to create a rich and powerful larger object, rather than adding a single monolithic, non-modular plugin. To solve a particular problem or allow a new feature in Open MCT, you may need to introduce more than just one new object type.</p>
</div>
<div class="col">
<img src="/example/styleguide/res/images/diagram-objects.svg" />
<img src="../images/diagram-objects.svg" />
</div>
</div>
</div>
@@ -48,7 +48,7 @@
<p>The types of objects that a container can hold should be based on the purpose of the container and the views that it affords. For example, a Folders purpose is to allow a user to conceptually organize objects of all other types; a Folder must therefore be able to contain an object of any type.</p>
</div>
<div class="col">
<img src="/example/styleguide/res/images/diagram-containment.svg" />
<img src="../images/diagram-containment.svg" />
</div>
</div>
</div>
@@ -60,7 +60,7 @@
<p>Views are simply different ways to view the content of a given object. For example, telemetry data could be viewed as a plot or a table. A clock can display its time in analog fashion or with digital numbers. In each view, all of the content is present; its just represented differently. When providing views for an object, all the content of the object should be present in each view.</p>
</div>
<div class="col">
<img src="/example/styleguide/res/images/diagram-views.svg" />
<img src="../images/diagram-views.svg" />
</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
define([
'text!../res/templates/mct-example.html'
'../res/templates/mct-example.html'
], function (
MCTExampleTemplate
) {

View File

@@ -1,68 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
"./src/ExampleTaxonomyModelProvider",
'legacyRegistry'
], function (
ExampleTaxonomyModelProvider,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/taxonomy", {
"name": "Example taxonomy",
"description": "Example illustrating the addition of a static top-level hierarchy",
"extensions": {
"roots": [
{
"id": "exampleTaxonomy"
}
],
"models": [
{
"id": "exampleTaxonomy",
"model": {
"type": "folder",
"name": "Stub Subsystems",
"composition": [
"examplePacket0",
"examplePacket1",
"examplePacket2"
]
}
}
],
"components": [
{
"provides": "modelService",
"type": "provider",
"implementation": ExampleTaxonomyModelProvider,
"depends": [
"$q"
]
}
]
}
});
});

View File

@@ -1,69 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
[],
function () {
"use strict";
function ExampleTaxonomyModelProvider($q) {
var models = {},
packetId,
telemetryId,
i,
j;
// Add some "subsystems"
for (i = 0; i < 3; i += 1) {
packetId = "examplePacket" + i;
models[packetId] = {
name: "Stub Subsystem " + (i + 1),
type: "telemetry.panel",
composition: []
};
// Add some "telemetry points"
for (j = 0; j < 100 * (i + 1); j += 1) {
telemetryId = "exampleTelemetry" + j;
models[telemetryId] = {
name: "SWG" + i + "." + j,
type: "generator",
telemetry: {
period: 10 + i + j
}
};
models[packetId].composition.push(telemetryId);
}
}
return {
getModels: function () {
return $q.when(models);
}
};
}
return ExampleTaxonomyModelProvider;
}
);

View File

@@ -1 +0,0 @@
Example of running a Web Worker using the `workerService`.

View File

@@ -1,67 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
[],
function () {
"use strict";
/**
* Displays Fibonacci numbers in the status area.
* @constructor
*/
function FibonacciIndicator(workerService, $rootScope) {
var latest,
counter = 0,
worker = workerService.run('example.fibonacci');
function requestNext() {
worker.postMessage([counter]);
counter += 1;
}
function handleResponse(event) {
latest = event.data;
$rootScope.$apply();
requestNext();
}
worker.onmessage = handleResponse;
requestNext();
return {
getCssClass: function () {
return "icon-object-unknown";
},
getText: function () {
return "" + latest;
},
getDescription: function () {
return "";
}
};
}
return FibonacciIndicator;
}
);

View File

@@ -1,15 +0,0 @@
/*global self*/
(function () {
"use strict";
// Calculate fibonacci numbers inefficiently.
// We can do this because we're on a background thread, and
// won't halt the UI.
function fib(n) {
return n < 2 ? n : (fib(n - 1) + fib(n - 2));
}
self.onmessage = function (event) {
self.postMessage(fib(event.data));
};
}());

View File

@@ -1,182 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global require,__dirname*/
require("v8-compile-cache");
var gulp = require('gulp'),
sourcemaps = require('gulp-sourcemaps'),
path = require('path'),
fs = require('fs'),
git = require('git-rev-sync'),
moment = require('moment'),
project = require('./package.json'),
_ = require('lodash'),
paths = {
main: 'openmct.js',
dist: 'dist',
reports: 'dist/reports',
scss: ['./platform/**/*.scss', './example/**/*.scss'],
assets: [
'./{example,platform}/**/*.{css,css.map,png,svg,ico,woff,eot,ttf}'
],
scripts: [ 'openmct.js', 'platform/**/*.js', 'src/**/*.js' ],
specs: [ 'platform/**/*Spec.js', 'src/**/*Spec.js' ],
},
options = {
requirejsOptimize: {
name: 'bower_components/almond/almond.js',
include: paths.main.replace('.js', ''),
wrap: {
start: (function () {
var buildVariables = {
version: project.version,
timestamp: moment.utc(Date.now()).format(),
revision: fs.existsSync('.git') ? git.long() : 'Unknown',
branch: fs.existsSync('.git') ? git.branch() : 'Unknown'
};
return fs.readFileSync("src/start.frag", 'utf-8')
.replace(/@@(\w+)/g, function (match, key) {
return buildVariables[key];
});;
}()),
endFile: "src/end.frag"
},
optimize: 'uglify2',
uglify2: { output: { comments: /@preserve/ } },
mainConfigFile: paths.main,
wrapShim: true
},
karma: {
configFile: path.resolve(__dirname, 'karma.conf.js'),
singleRun: true
},
sass: {
sourceComments: true
}
};
if (process.env.NODE_ENV === 'development') {
options.requirejsOptimize.optimize = 'none';
}
gulp.task('scripts', function () {
var requirejsOptimize = require('gulp-requirejs-optimize');
return gulp.src(paths.main)
.pipe(sourcemaps.init())
.pipe(requirejsOptimize(options.requirejsOptimize))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.dist));
});
gulp.task('test', function (done) {
var karma = require('karma');
new karma.Server(options.karma, done).start();
});
gulp.task('stylesheets', function () {
var sass = require('gulp-sass');
var rename = require('gulp-rename');
var bourbon = require('node-bourbon');
options.sass.includePaths = bourbon.includePaths;
return gulp.src(paths.scss, {base: '.'})
.pipe(sourcemaps.init())
.pipe(sass(options.sass).on('error', sass.logError))
.pipe(rename(function (file) {
file.dirname =
file.dirname.replace(path.sep + 'sass', path.sep + 'css');
return file;
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(__dirname));
});
gulp.task('lint', function () {
var jshint = require('gulp-jshint');
var merge = require('merge-stream');
var nonspecs = paths.specs.map(function (glob) {
return "!" + glob;
}),
scriptLint = gulp.src(paths.scripts.concat(nonspecs))
.pipe(jshint()),
specLint = gulp.src(paths.specs)
.pipe(jshint({ jasmine: true }));
return merge(scriptLint, specLint)
.pipe(jshint.reporter('gulp-jshint-html-reporter', {
filename: paths.reports + '/lint/jshint-report.html',
createMissingFolders : true
}))
.pipe(jshint.reporter('default'))
.pipe(jshint.reporter('fail'));
});
gulp.task('checkstyle', function () {
var jscs = require('gulp-jscs');
var mkdirp = require('mkdirp');
var reportName = 'jscs-html-report.html';
var reportPath = path.resolve(paths.reports, 'checkstyle', reportName);
var moveReport = fs.rename.bind(fs, reportName, reportPath, _.noop);
mkdirp.sync(path.resolve(paths.reports, 'checkstyle'));
return gulp.src(paths.scripts)
.pipe(jscs())
.pipe(jscs.reporter())
.pipe(jscs.reporter('jscs-html-reporter')).on('finish', moveReport)
.pipe(jscs.reporter('fail'));
});
gulp.task('fixstyle', function () {
var jscs = require('gulp-jscs');
return gulp.src(paths.scripts, { base: '.' })
.pipe(jscs({ fix: true }))
.pipe(gulp.dest('.'));
});
gulp.task('assets', ['stylesheets'], function () {
return gulp.src(paths.assets)
.pipe(gulp.dest(paths.dist));
});
gulp.task('watch', function () {
return gulp.watch(paths.scss, ['stylesheets', 'assets']);
});
gulp.task('serve', function () {
console.log('Running development server with all defaults');
var app = require('./app.js');
});
gulp.task('develop', ['serve', 'stylesheets', 'watch']);
gulp.task('install', [ 'assets', 'scripts' ]);
gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]);
gulp.task('build', [ 'verify', 'install' ]);

View File

@@ -26,11 +26,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<title></title>
<script src="bower_components/requirejs/require.js"> </script>
<script src="dist/openmct.js"></script>
<link rel="stylesheet" href="dist/openmct.css">
<link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16">
<link rel="shortcut icon" href="dist/favicons/favicon.ico">
</head>
<body>
<div class="l-splash-holder s-splash-holder">
<div class="l-splash s-splash"></div>
</div>
</body>
<script>
var THIRTY_MINUTES = 30 * 60 * 1000;
require(['openmct'], function (openmct) {
[
'example/eventGenerator',
'example/styleguide'
@@ -73,19 +82,5 @@
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();
window.openmct = openmct;
});
</script>
<link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css">
<link rel="stylesheet" href="platform/commonUI/general/res/css/openmct.css">
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
<link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
</head>
<body>
<div class="l-splash-holder s-splash-holder">
<div class="l-splash s-splash"></div>
</div>
</body>
</html>

View File

@@ -21,70 +21,40 @@
*****************************************************************************/
/*global module,process*/
module.exports = function(config) {
const devMode = process.env.NODE_ENV !== 'production';
module.exports = (config) => {
const webpackConfig = require('./webpack.config.js');
delete webpackConfig.output;
if (!devMode) {
webpackConfig.module.rules.push({
test: /\.js$/,
exclude: /node_modules|example/,
use: 'istanbul-instrumenter-loader'
});
}
config.set({
// Base path that will be used to resolve all file patterns.
basePath: '',
// Frameworks to use
// Available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine', 'requirejs'],
// List of files / patterns to load in the browser.
// By default, files are also included in a script tag.
frameworks: ['jasmine'],
files: [
{pattern: 'bower_components/**/*.js', included: false},
{pattern: 'node_modules/d3-*/**/*.js', included: false},
{pattern: 'node_modules/vue/**/*.js', included: false},
{pattern: 'node_modules/printj/dist/*.js', included: false},
{pattern: 'src/**/*', included: false},
{pattern: 'node_modules/painterro/build/*.js', included: false},
{pattern: 'node_modules/html2canvas/dist/*', included: false},
{pattern: 'example/**/*.html', included: false},
{pattern: 'example/**/*.js', included: false},
{pattern: 'example/**/*.json', included: false},
{pattern: 'platform/**/*.js', included: false},
{pattern: 'warp/**/*.js', included: false},
{pattern: 'platform/**/*.html', included: false},
'test-main.js'
'platform/**/*Spec.js',
'src/**/*Spec.js'
],
// List of files to exclude.
exclude: [
'platform/framework/src/Main.js'
],
// Preprocess matching files before serving them to the browser.
// https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/**/!(*Spec).js': [ 'coverage' ],
'platform/**/src/**/!(*Spec).js': [ 'coverage' ]
},
// Test results reporter to use
// Possible values: 'dots', 'progress'
// Available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'coverage', 'html'],
// Web server port.
port: 9876,
// Wnable / disable colors in the output (reporters and logs).
reporters: [
'progress',
'coverage',
'html'
],
browsers: ['ChromeHeadless'],
colors: true,
logLevel: config.LOG_INFO,
// Rerun tests when any file changes.
autoWatch: true,
// Specify browsers to run tests in.
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: [
'ChromeHeadless'
],
// Code coverage reporting.
coverageReporter: {
dir: process.env.CIRCLE_ARTIFACTS ?
process.env.CIRCLE_ARTIFACTS + '/coverage' :
@@ -104,8 +74,19 @@ module.exports = function(config) {
foldAll: false
},
// Continuous Integration mode.
// If true, Karma captures browsers, runs the tests and exits.
preprocessors: {
// add webpack as preprocessor
'platform/**/*Spec.js': [ 'webpack' ],
'src/**/*Spec.js': [ 'webpack' ]
},
webpack: webpackConfig,
webpackMiddleware: {
stats: 'errors-only',
logLevel: 'warn'
},
singleRun: true
});
};
}

View File

@@ -19,94 +19,26 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global requirejs,BUILD_CONSTANTS*/
/*global module,BUILD_CONSTANTS*/
requirejs.config({
"paths": {
"legacyRegistry": "src/legacyRegistry",
"angular": "bower_components/angular/angular.min",
"angular-route": "bower_components/angular-route/angular-route.min",
"csv": "bower_components/comma-separated-values/csv.min",
"EventEmitter": "bower_components/eventemitter3/index",
"es6-promise": "bower_components/es6-promise/es6-promise.min",
"moment": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
"saveAs": "bower_components/file-saver/FileSaver.min",
"screenfull": "bower_components/screenfull/dist/screenfull.min",
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",
"vue": "node_modules/vue/dist/vue.min",
"zepto": "bower_components/zepto/zepto.min",
"lodash": "bower_components/lodash/lodash",
"d3-selection": "node_modules/d3-selection/dist/d3-selection.min",
"d3-scale": "node_modules/d3-scale/build/d3-scale.min",
"d3-axis": "node_modules/d3-axis/build/d3-axis.min",
"d3-array": "node_modules/d3-array/build/d3-array.min",
"d3-collection": "node_modules/d3-collection/build/d3-collection.min",
"d3-color": "node_modules/d3-color/build/d3-color.min",
"d3-format": "node_modules/d3-format/build/d3-format.min",
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
"d3-time": "node_modules/d3-time/build/d3-time.min",
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
"html2canvas": "node_modules/html2canvas/dist/html2canvas.min",
"painterro": "node_modules/painterro/build/painterro.min",
"printj": "node_modules/printj/dist/printj.min"
},
"shim": {
"angular": {
"exports": "angular"
},
"angular-route": {
"deps": ["angular"]
},
"EventEmitter": {
"exports": "EventEmitter"
},
"moment-duration-format": {
"deps": ["moment"]
},
"painterro": {
"exports": "Painterro"
},
"saveAs": {
"exports": "saveAs"
},
"screenfull": {
"exports": "screenfull"
},
"zepto": {
"exports": "Zepto"
},
"lodash": {
"exports": "lodash"
},
"d3-selection": {
"exports": "d3-selection"
},
"d3-scale": {
"deps": ["d3-array", "d3-collection", "d3-color", "d3-format", "d3-interpolate", "d3-time", "d3-time-format"],
"exports": "d3-scale"
},
"d3-axis": {
"exports": "d3-axis"
},
"dom-to-image": {
"exports": "domtoimage"
const matcher = /\/openmct.js$/;
if (document.currentScript) {
let src = document.currentScript.src;
if (src && matcher.test(src)) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = src.replace(matcher, '') + '/';
}
}
});
const Main = require('./platform/framework/src/Main');
const defaultRegistry = require('./src/defaultRegistry');
const MCT = require('./src/MCT');
const buildInfo = require('./src/plugins/buildInfo/plugin');
define([
'./platform/framework/src/Main',
'./src/defaultRegistry',
'./src/MCT',
'./src/plugins/buildInfo/plugin'
], function (Main, defaultRegistry, MCT, buildInfo) {
var openmct = new MCT();
openmct.legacyRegistry = defaultRegistry;
openmct.install(openmct.plugins.Plot());
openmct.install(openmct.plugins.TelemetryTable());
if (typeof BUILD_CONSTANTS !== 'undefined') {
openmct.install(buildInfo(BUILD_CONSTANTS));
@@ -116,5 +48,4 @@ define([
return new Main().run(defaultRegistry);
});
return openmct;
});
module.exports = openmct;

View File

@@ -2,7 +2,15 @@
"name": "openmct",
"version": "0.14.0-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"dependencies": {},
"devDependencies": {
"angular": "1.4.14",
"angular-route": "1.4.14",
"babel-eslint": "8.2.6",
"comma-separated-values": "^3.6.4",
"concurrently": "^3.6.1",
"copy-webpack-plugin": "^4.5.2",
"css-loader": "^1.0.0",
"d3-array": "1.2.x",
"d3-axis": "1.0.x",
"d3-collection": "1.0.x",
@@ -13,59 +21,68 @@
"d3-selection": "1.3.x",
"d3-time": "1.0.x",
"d3-time-format": "2.1.x",
"eslint": "5.2.0",
"eventemitter3": "^1.2.0",
"exports-loader": "^0.7.0",
"express": "^4.13.1",
"minimist": "^1.1.1",
"painterro": "^0.2.65",
"request": "^2.69.0",
"vue": "^2.5.6"
},
"devDependencies": {
"bower": "^1.7.7",
"fast-sass-loader": "^1.4.5",
"file-loader": "^1.1.11",
"file-saver": "^1.3.8",
"git-rev-sync": "^1.4.0",
"glob": ">= 3.0.0",
"gulp": "^3.9.1",
"gulp-jscs": "^3.0.2",
"gulp-jshint": "^2.0.0",
"gulp-jshint-html-reporter": "^0.1.3",
"gulp-rename": "^1.2.2",
"gulp-requirejs-optimize": "^0.3.1",
"gulp-sass": "^3.1.0",
"gulp-sourcemaps": "^1.6.0",
"html-loader": "^0.5.5",
"html2canvas": "^1.0.0-alpha.12",
"imports-loader": "^0.8.0",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine-core": "^3.1.0",
"jscs-html-reporter": "^0.1.0",
"jsdoc": "^3.3.2",
"jshint": "^2.7.0",
"karma": "^2.0.3",
"karma-chrome-launcher": "^2.2.0",
"karma-cli": "^1.0.1",
"karma-coverage": "^1.1.2",
"karma-html-reporter": "^0.2.7",
"karma-jasmine": "^1.1.2",
"karma-requirejs": "^1.1.0",
"karma-webpack": "^3.0.0",
"lodash": "^3.10.1",
"markdown-toc": "^0.11.7",
"marked": "^0.3.5",
"merge-stream": "^1.0.0",
"mkdirp": "^0.5.1",
"mini-css-extract-plugin": "^0.4.1",
"minimist": "^1.1.1",
"moment": "^2.11.1",
"moment-duration-format": "^2.2.2",
"moment-timezone": "^0.5.21",
"node-bourbon": "^4.2.3",
"node-sass": "^4.9.2",
"painterro": "^0.2.65",
"printj": "^1.1.0",
"requirejs": "2.1.x",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
"screenfull": "^3.3.2",
"split": "^1.0.0",
"v8-compile-cache": "^1.1.0"
"style-loader": "^0.21.0",
"v8-compile-cache": "^1.1.0",
"vue": "2.5.6",
"vue-loader": "^15.2.6",
"vue-template-compiler": "2.5.6",
"webpack": "^4.16.2",
"webpack-cli": "^3.1.0",
"webpack-dev-middleware": "^3.1.3",
"zepto": "^1.2.0"
},
"scripts": {
"start": "node app.js",
"lint": "eslint platform src openmct.js",
"lint:fix": "eslint platform src openmct.js --fix",
"build:prod": "NODE_ENV=production webpack",
"build:dev": "webpack",
"build:watch": "webpack --watch",
"test": "karma start --single-run",
"jshint": "jshint platform example",
"lint": "./node_modules/gulp/bin/gulp.js lint",
"checkstyle": "./node_modules/gulp/bin/gulp.js checkstyle",
"watch": "karma start",
"test:watch": "karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
"docs": "npm run jsdoc ; npm run otherdoc",
"prepare": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install"
"prepare": "npm run build:prod"
},
"repository": {
"type": "git",

View File

@@ -21,17 +21,17 @@
*****************************************************************************/
define([
"text!./res/templates/about-dialog.html",
"./res/templates/about-dialog.html",
"./src/LogoController",
"./src/AboutController",
"./src/LicenseController",
"text!./res/templates/app-logo.html",
"text!./res/templates/about-logo.html",
"text!./res/templates/overlay-about.html",
"text!./res/templates/license-apache.html",
"text!./res/templates/license-mit.html",
"text!./res/templates/licenses.html",
"text!./res/templates/licenses-export-md.html",
"./res/templates/app-logo.html",
"./res/templates/about-logo.html",
"./res/templates/overlay-about.html",
"./res/templates/license-apache.html",
"./res/templates/license-mit.html",
"./res/templates/licenses.html",
"./res/templates/licenses-export-md.html",
'legacyRegistry'
], function (
aboutDialogTemplate,

View File

@@ -33,16 +33,16 @@ define([
"./src/windowing/NewTabAction",
"./src/windowing/FullscreenAction",
"./src/windowing/WindowTitler",
"text!./res/templates/browse.html",
"text!./res/templates/browse-object.html",
"text!./res/templates/items/grid-item.html",
"text!./res/templates/browse/object-header.html",
"text!./res/templates/browse/object-header-frame.html",
"text!./res/templates/menu-arrow.html",
"text!./res/templates/back-arrow.html",
"text!./res/templates/items/items.html",
"text!./res/templates/browse/object-properties.html",
"text!./res/templates/browse/inspector-region.html",
"./res/templates/browse.html",
"./res/templates/browse-object.html",
"./res/templates/items/grid-item.html",
"./res/templates/browse/object-header.html",
"./res/templates/browse/object-header-frame.html",
"./res/templates/menu-arrow.html",
"./res/templates/back-arrow.html",
"./res/templates/items/items.html",
"./res/templates/browse/object-properties.html",
"./res/templates/browse/inspector-region.html",
'legacyRegistry'
], function (
BrowseController,

View File

@@ -31,7 +31,6 @@ define(
* @constructor
*/
function BrowseObjectController($scope, $location, $route) {
var navigatedObject;
function setViewForDomainObject(domainObject) {
var locationViewKey = $location.search().view;
@@ -47,7 +46,6 @@ define(
((domainObject && domainObject.useCapability('view')) || [])
.forEach(selectViewIfMatching);
}
navigatedObject = domainObject;
}
function updateQueryParam(viewKey) {

View File

@@ -27,7 +27,6 @@ define(
describe("The PaneController", function () {
var mockScope,
mockAgentService,
mockDomainObjects,
mockWindow,
controller,
mockLocation,
@@ -47,17 +46,6 @@ define(
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$on"]);
mockDomainObjects = ['a', 'b'].map(function (id) {
var mockDomainObject = jasmine.createSpyObj(
'domainObject-' + id,
['getId', 'getModel', 'getCapability']
);
mockDomainObject.getId.and.returnValue(id);
mockDomainObject.getModel.and.returnValue({});
return mockDomainObject;
});
mockAgentService = jasmine.createSpyObj(
"agentService",
["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"]

View File

@@ -47,7 +47,7 @@ define(
it("toggles fullscreen mode when performed", function () {
action.perform();
expect(window.screenfull.toggle).toHaveBeenCalled();
expect(screenfull.toggle).toHaveBeenCalled();
});
it("provides displayable metadata", function () {

View File

@@ -32,7 +32,7 @@ define(
mockRootScope,
mockDocument,
mockDomainObject,
titler;
titler; // eslint-disable-line
beforeEach(function () {
mockNavigationService = jasmine.createSpyObj(

View File

@@ -23,13 +23,13 @@
define([
"./src/DialogService",
"./src/OverlayService",
"text!./res/templates/overlay-dialog.html",
"text!./res/templates/overlay-options.html",
"text!./res/templates/dialog.html",
"text!./res/templates/overlay-blocking-message.html",
"text!./res/templates/message.html",
"text!./res/templates/overlay-message-list.html",
"text!./res/templates/overlay.html",
"./res/templates/overlay-dialog.html",
"./res/templates/overlay-options.html",
"./res/templates/dialog.html",
"./res/templates/overlay-blocking-message.html",
"./res/templates/message.html",
"./res/templates/overlay-message-list.html",
"./res/templates/overlay.html",
'legacyRegistry'
], function (
DialogService,

View File

@@ -49,14 +49,14 @@ define([
"./src/creation/CreateActionProvider",
"./src/creation/AddActionProvider",
"./src/creation/CreationService",
"text!./res/templates/create/locator.html",
"text!./res/templates/create/create-button.html",
"text!./res/templates/create/create-menu.html",
"text!./res/templates/library.html",
"text!./res/templates/edit-object.html",
"text!./res/templates/edit-action-buttons.html",
"text!./res/templates/elements.html",
"text!./res/templates/topbar-edit.html",
"./res/templates/create/locator.html",
"./res/templates/create/create-button.html",
"./res/templates/create/create-menu.html",
"./res/templates/library.html",
"./res/templates/edit-object.html",
"./res/templates/edit-action-buttons.html",
"./res/templates/elements.html",
"./res/templates/topbar-edit.html",
'legacyRegistry'
], function (
EditActionController,

View File

@@ -55,7 +55,7 @@ define(
// A view is editable unless explicitly flagged as not
(views || []).forEach(function (view) {
if (view.editable === true ||
if (isEditable(view) ||
(view.key === 'plot' && type.getKey() === 'telemetry.panel') ||
(view.key === 'table' && type.getKey() === 'table') ||
(view.key === 'rt-table' && type.getKey() === 'rttable')
@@ -64,6 +64,14 @@ define(
}
});
function isEditable(view) {
if (typeof view.editable === Function) {
return view.editable(domainObject.useCapability('adapter'));
} else {
return view.editable === true;
}
}
return count;
};

View File

@@ -25,8 +25,7 @@ define(
function (EditAndComposeAction) {
describe("The Link action", function () {
var mockQ,
mockDomainObject,
var mockDomainObject,
mockParent,
mockContext,
mockComposition,
@@ -47,13 +46,10 @@ define(
}
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability"]
);
mockQ = { when: mockPromise };
mockParent = {
getModel: function () {
return model;

View File

@@ -34,7 +34,6 @@ define(
mockDomainObject,
capabilities = {},
mockEditAction,
mockSaveAction,
action;
function mockPromise(value) {
@@ -83,12 +82,6 @@ define(
mockDomainObject.getCapability.and.callFake(function (name) {
return capabilities[name];
});
mockSaveAction = jasmine.createSpyObj(
"saveAction",
[
"perform"
]
);
capabilities.action = jasmine.createSpyObj(
"actionCapability",

View File

@@ -27,7 +27,6 @@ define(
describe("The Edit action policy", function () {
var editableView,
nonEditableView,
undefinedView,
testViews,
testContext,
mockDomainObject,
@@ -67,7 +66,6 @@ define(
editableView = { editable: true };
nonEditableView = { editable: false };
undefinedView = { someKey: "some value" };
plotView = { key: "plot", editable: false };
testViews = [];

View File

@@ -30,12 +30,6 @@ define(
mockObjects,
mockDomainObject,
testStructure,
testAB,
testABC,
testABC2,
testABCXYZ,
testABCYZ,
testM,
toolbar;
beforeEach(function () {
@@ -62,13 +56,6 @@ define(
{ name: "M", method: "m", domainObject: mockDomainObject }
];
testAB = { a: 0, b: 1 };
testABC = { a: 0, b: 1, c: 2 };
testABC2 = { a: 4, b: 1, c: 2 }; // For inconsistent-state checking
testABCXYZ = { a: 0, b: 1, c: 2, x: 'X!', y: 'Y!', z: 'Z!' };
testABCYZ = { a: 0, b: 1, c: 2, y: 'Y!', z: 'Z!' };
testM = { m: jasmine.createSpy("method") };
toolbar = new EditToolbar(mockScope, mockOpenMCT, testStructure);
});

View File

@@ -43,13 +43,12 @@ define(["../../src/services/NestedTransaction"], function (NestedTransaction) {
describe("when callbacks are added", function () {
var mockCommit,
mockCancel,
remove;
mockCancel;
beforeEach(function () {
mockCommit = jasmine.createSpy('commit');
mockCancel = jasmine.createSpy('cancel');
remove = nestedTransaction.add(mockCommit, mockCancel);
nestedTransaction.add(mockCommit, mockCancel);
});
it("does not interact with its parent transaction", function () {

View File

@@ -52,26 +52,26 @@ define([
"./src/directives/MCTPreview",
"./src/actions/MCTPreviewAction",
"./src/filters/ReverseFilter",
"text!./res/templates/bottombar.html",
"text!./res/templates/controls/action-button.html",
"text!./res/templates/controls/input-filter.html",
"text!./res/templates/angular-indicator.html",
"text!./res/templates/message-banner.html",
"text!./res/templates/progress-bar.html",
"text!./res/templates/controls/time-controller.html",
"text!./res/templates/containers/accordion.html",
"text!./res/templates/subtree.html",
"text!./res/templates/tree.html",
"text!./res/templates/tree-node.html",
"text!./res/templates/label.html",
"text!./res/templates/controls/action-group.html",
"text!./res/templates/menu/context-menu.html",
"text!./res/templates/controls/switcher.html",
"text!./res/templates/object-inspector.html",
"text!./res/templates/controls/selector.html",
"text!./res/templates/controls/datetime-picker.html",
"text!./res/templates/controls/datetime-field.html",
"text!./res/templates/preview.html",
"./res/templates/bottombar.html",
"./res/templates/controls/action-button.html",
"./res/templates/controls/input-filter.html",
"./res/templates/angular-indicator.html",
"./res/templates/message-banner.html",
"./res/templates/progress-bar.html",
"./res/templates/controls/time-controller.html",
"./res/templates/containers/accordion.html",
"./res/templates/subtree.html",
"./res/templates/tree.html",
"./res/templates/tree-node.html",
"./res/templates/label.html",
"./res/templates/controls/action-group.html",
"./res/templates/menu/context-menu.html",
"./res/templates/controls/switcher.html",
"./res/templates/object-inspector.html",
"./res/templates/controls/selector.html",
"./res/templates/controls/datetime-picker.html",
"./res/templates/controls/datetime-field.html",
"./res/templates/preview.html",
'legacyRegistry'
], function (
UrlService,
@@ -173,12 +173,6 @@ define([
"key": "reverse"
}
],
"stylesheets": [
{
"stylesheetUrl": "css/normalize.min.css",
"priority": "mandatory"
}
],
"templates": [
{
"key": "bottombar",

View File

@@ -1,48 +0,0 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@@ -22,7 +22,7 @@
define([
'zepto',
'text!../../res/templates/tree/toggle.html'
'../../res/templates/tree/toggle.html'
], function ($, toggleTemplate) {
function ToggleView(state) {
this.expanded = !!state;

View File

@@ -22,7 +22,7 @@
define([
'zepto',
'text!../../res/templates/tree/tree-label.html'
'../../res/templates/tree/tree-label.html'
], function ($, labelTemplate) {
function TreeLabelView(gestureService) {

View File

@@ -22,7 +22,7 @@
define([
'zepto',
'text!../../res/templates/tree/node.html',
'../../res/templates/tree/node.html',
'./ToggleView',
'./TreeLabelView'
], function ($, nodeTemplate, ToggleView, TreeLabelView) {

View File

@@ -23,7 +23,7 @@
define([
'zepto',
'./TreeNodeView',
'text!../../res/templates/tree/wait-node.html'
'../../res/templates/tree/wait-node.html'
], function ($, TreeNodeView, spinnerTemplate) {
function TreeView(gestureService, openmct, selectFn) {

View File

@@ -31,7 +31,7 @@ define(
mockHead,
mockElement,
testBundle,
loader;
loader; // eslint-disable-line
beforeEach(function () {
testBundle = {

View File

@@ -41,7 +41,6 @@ define(
mockScope,
mockElement,
testAttrs,
mockBody,
mockTransclude,
mockParentEl,
mockNewElement,
@@ -59,8 +58,6 @@ define(
jasmine.createSpyObj("$scope", ["$eval", "$apply", "$on"]);
mockElement =
jasmine.createSpyObj("element", JQLITE_METHODS);
mockBody =
jasmine.createSpyObj("body", JQLITE_METHODS);
mockTransclude =
jasmine.createSpy("transclude");
mockParentEl =

View File

@@ -24,10 +24,10 @@ define([
"./src/gestures/InfoGesture",
"./src/gestures/InfoButtonGesture",
"./src/services/InfoService",
"text!./res/info-table.html",
"text!./res/info-bubble.html",
"text!./res/bubble.html",
"text!./res/templates/info-button.html",
"./res/info-table.html",
"./res/info-bubble.html",
"./res/bubble.html",
"./res/templates/info-button.html",
'legacyRegistry'
], function (
InfoGesture,

View File

@@ -25,8 +25,7 @@ define(
function (InfoButtonGesture) {
describe("The info button gesture", function () {
var mockTimeout,
mockDocument,
var mockDocument,
mockBody,
mockAgentService,
mockInfoService,
@@ -42,7 +41,6 @@ define(
fireDismissGesture;
beforeEach(function () {
mockTimeout = jasmine.createSpy('$timeout');
mockDocument = jasmine.createSpyObj('$document', ['find']);
mockBody = jasmine.createSpyObj('body', ['on', 'off', 'scope', 'css', 'unbind']);
mockDocument.find.and.returnValue(mockBody);

View File

@@ -74,7 +74,7 @@ define(
"device " + (trueMethods.join(", "));
describe("when " + summary, function () {
var classifier;
var classifier; // eslint-disable-line
beforeEach(function () {
trueMethods.forEach(function (m) {

View File

@@ -24,7 +24,7 @@ define([
"./src/NotificationIndicatorController",
"./src/NotificationIndicator",
"./src/NotificationService",
"text!./res/notification-indicator.html",
"./res/notification-indicator.html",
'legacyRegistry'
], function (
NotificationIndicatorController,

View File

@@ -48,10 +48,11 @@ define(
});
it("throws exceptions on unrecognized conversions", function () {
var caught = false, tmp;
var caught = false;
try {
tmp = new TypePropertyConversion("some-unknown-conversion");
// eslint-disable-next-line
new TypePropertyConversion("some-unknown-conversion");
} catch (e) {
caught = true;
}

View File

@@ -124,7 +124,6 @@ define(
var mockQ,
mockDeferred,
createObjectPromise,
copyService,
object,
newParent,
@@ -138,7 +137,6 @@ define(
resolvedValue;
beforeEach(function () {
createObjectPromise = synchronousPromise(undefined);
policyService.allow.and.returnValue(true);
persistObjectPromise = synchronousPromise(undefined);
@@ -275,8 +273,7 @@ define(
describe("on domainObject with composition", function () {
var childObject,
objectClone,
childObjectClone,
compositionPromise;
childObjectClone;
beforeEach(function () {
var invocationCount = 0,
@@ -325,11 +322,6 @@ define(
}
});
compositionPromise = jasmine.createSpyObj(
'compositionPromise',
['then']
);
compositionCapability
.invoke
.and.returnValue(synchronousPromise([childObject]));

View File

@@ -178,7 +178,6 @@ define(
type: { type: 'object' }
}
});
moveResult = moveService.perform(object, newParent);
});
@@ -189,6 +188,10 @@ define(
);
});
it("returns a promise", function () {
expect(moveResult.then).toEqual(jasmine.any(Function));
});
it("waits for result of link", function () {
expect(linkService.perform.calls.mostRecent().promise.then)
.toHaveBeenCalledWith(jasmine.any(Function));

View File

@@ -34,8 +34,8 @@ define([
"./src/actions/RestartTimerAction",
"./src/actions/StopTimerAction",
"./src/actions/PauseTimerAction",
"text!./res/templates/clock.html",
"text!./res/templates/timer.html",
"./res/templates/clock.html",
"./res/templates/timer.html",
'legacyRegistry'
], function (
MomentTimezone,

View File

@@ -29,10 +29,10 @@ define([
"./src/ui/ConductorAxisDirective",
"./src/ui/NumberFormat",
"./src/ui/StringFormat",
"text!./res/templates/time-conductor.html",
"text!./res/templates/mode-selector/mode-selector.html",
"text!./res/templates/mode-selector/mode-menu.html",
"text!./res/templates/time-of-interest.html",
"./res/templates/time-conductor.html",
"./res/templates/mode-selector/mode-selector.html",
"./res/templates/mode-selector/mode-menu.html",
"./res/templates/time-of-interest.html",
"legacyRegistry"
], function (
TimeConductorController,
@@ -97,16 +97,6 @@ define([
"implementation": ConductorTOIDirective
}
],
"stylesheets": [
{
"stylesheetUrl": "css/time-conductor-espresso.css",
"theme": "espresso"
},
{
"stylesheetUrl": "css/time-conductor-snow.css",
"theme": "snow"
}
],
"templates": [
{
"key": "conductor",

View File

@@ -37,8 +37,6 @@ define([
mockConductorViewService,
mockFormatService,
mockScope,
mockElement,
mockTarget,
mockBounds,
element,
mockTimeSystem,
@@ -56,13 +54,6 @@ define([
]);
//Add some HTML elements
mockTarget = {
offsetWidth: 0,
offsetHeight: 0
};
mockElement = {
firstChild: mockTarget
};
mockBounds = {
start: 100,
end: 200

View File

@@ -376,7 +376,6 @@ define(['./TimeConductorController'], function (TimeConductorController) {
describe("when the URL defines conductor state", function () {
var urlBounds;
var urlTimeSystem;
var urlMode;
var urlDeltas;
var mockDeltaFormat;
@@ -439,7 +438,6 @@ define(['./TimeConductorController'], function (TimeConductorController) {
end: 200
};
urlTimeSystem = "otherTimeSystem";
urlMode = "realtime";
urlDeltas = {
start: 300,
end: 400

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
"text!../layout/res/templates/fixed.html",
"../layout/res/templates/fixed.html",
'legacyRegistry'
], function (
fixedTemplate,

View File

@@ -23,7 +23,7 @@
define([
'./src/HyperlinkController',
'legacyRegistry',
'text!./res/templates/hyperlink.html'
'./res/templates/hyperlink.html'
], function (
HyperlinkController,
legacyRegistry,

View File

@@ -24,7 +24,7 @@ define([
"./src/policies/ImageryViewPolicy",
"./src/controllers/ImageryController",
"./src/directives/MCTBackgroundImage",
"text!./res/templates/imagery.html",
"./res/templates/imagery.html",
'legacyRegistry'
], function (
ImageryViewPolicy,

View File

@@ -25,14 +25,14 @@ define([
"./src/FixedController",
"./src/LayoutCompositionPolicy",
'./src/MCTTriggerModal',
"text!./res/templates/layout.html",
"text!./res/templates/fixed.html",
"text!./res/templates/frame.html",
"text!./res/templates/elements/telemetry.html",
"text!./res/templates/elements/box.html",
"text!./res/templates/elements/line.html",
"text!./res/templates/elements/text.html",
"text!./res/templates/elements/image.html",
"./res/templates/layout.html",
"./res/templates/fixed.html",
"./res/templates/frame.html",
"./res/templates/elements/telemetry.html",
"./res/templates/elements/box.html",
"./res/templates/elements/line.html",
"./res/templates/elements/text.html",
"./res/templates/elements/image.html",
'legacyRegistry'
], function (
LayoutController,

View File

@@ -23,7 +23,7 @@
define([
'./src/controllers/ListViewController',
'./src/directives/MCTGesture',
'text!./res/templates/listview.html',
'./res/templates/listview.html',
'legacyRegistry'
], function (
ListViewController,

View File

@@ -13,13 +13,13 @@ define([
"./src/actions/NewEntryContextual",
"./src/capabilities/NotebookCapability",
"./src/policies/CompositionPolicy",
"text!./res/templates/notebook.html",
"text!./res/templates/entry.html",
"text!./res/templates/annotation.html",
"text!./res/templates/notifications.html",
"text!../layout/res/templates/frame.html",
"text!./res/templates/controls/embedControl.html",
"text!./res/templates/controls/snapSelect.html"
"./res/templates/notebook.html",
"./res/templates/entry.html",
"./res/templates/annotation.html",
"./res/templates/notifications.html",
"../layout/res/templates/frame.html",
"./res/templates/controls/embedControl.html",
"./res/templates/controls/snapSelect.html"
], function (
legacyRegistry,
NotebookController,
@@ -288,19 +288,6 @@ define([
"key": "snapshot-select",
"template": snapSelectTemplate
}
],
"stylesheets": [
{
"stylesheetUrl": "css/notebook.css"
},
{
"stylesheetUrl": "css/notebook-espresso.css",
"theme": "espresso"
},
{
"stylesheetUrl": "css/notebook-snow.css",
"theme": "snow"
}
]
}
});

View File

@@ -38,7 +38,7 @@ var OVERLAY_TEMPLATE = '' +
define([
'zepto',
"text!../../res/templates/snapshotHeader.html"
"../../res/templates/snapshotHeader.html"
],
function ($, headerTemplate) {

View File

@@ -310,8 +310,7 @@ define(
initiatingEvent = agentService.isMobile() ?
'touchstart' : 'mousedown',
dismissExistingMenu,
menu,
popup;
menu;
var container = $($event.currentTarget).parent().parent();
@@ -333,7 +332,7 @@ define(
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
popup = popupService.display(menu, [$event.pageX,$event.pageY], {
popupService.display(menu, [$event.pageX,$event.pageY], {
marginX: 0,
marginY: -50
});

View File

@@ -31,11 +31,9 @@ define(['zepto'], function ($) {
var selectedModel = selectedObject.getModel();
var cssClass = selectedObject.getCapability('type').typeDef.cssClass;
var entryId = -1;
var embedId = -1;
$scope.clearSearch();
if ($element[0].id === 'newEntry') {
entryId = $scope.domainObject.model.entries.length;
embedId = 0;
var lastEntry = $scope.domainObject.model.entries[entryId - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
@@ -84,8 +82,6 @@ define(['zepto'], function ($) {
});
});
embedId = $scope.domainObject.model.entries[entryId].embeds.length - 1;
if (selectedObject) {
e.preventDefault();

View File

@@ -22,7 +22,7 @@
define([
"./src/EmbeddedPageController",
"text!./res/iframe.html",
"./res/iframe.html",
'legacyRegistry'
], function (
EmbeddedPageController,

View File

@@ -22,7 +22,7 @@
define([
"text!./res/markup.html",
"./res/markup.html",
'legacyRegistry'
], function (

View File

@@ -1,134 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"./src/directives/MCTTable",
"./src/controllers/TelemetryTableController",
"./src/controllers/TableOptionsController",
'../../commonUI/regions/src/Region',
'../../commonUI/browse/src/InspectorRegion',
"text!./res/templates/table-options-edit.html",
"text!./res/templates/telemetry-table.html",
"legacyRegistry"
], function (
MCTTable,
TelemetryTableController,
TableOptionsController,
Region,
InspectorRegion,
tableOptionsEditTemplate,
telemetryTableTemplate,
legacyRegistry
) {
/**
* Two region parts are defined here. One that appears only in browse
* mode, and one that appears only in edit mode. For not they both point
* to the same representation, but a different key could be used here to
* include a customized representation for edit mode.
*/
var tableInspector = new InspectorRegion(),
tableOptionsEditRegion = new Region({
name: "table-options",
title: "Table Options",
modes: ['edit'],
content: {
key: "table-options-edit"
}
});
tableInspector.addRegion(tableOptionsEditRegion);
legacyRegistry.register("platform/features/table", {
"extensions": {
"types": [
{
"key": "table",
"name": "Telemetry Table",
"cssClass": "icon-tabular-realtime",
"description": "A table of values over a given time period. The table will be automatically updated with new values as they become available",
"priority": 861,
"features": "creation",
"delegates": [
"telemetry"
],
"inspector": "table-options-edit",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"views": [
"table"
]
}
],
"controllers": [
{
"key": "TelemetryTableController",
"implementation": TelemetryTableController,
"depends": ["$scope", "$timeout", "openmct"]
},
{
"key": "TableOptionsController",
"implementation": TableOptionsController,
"depends": ["$scope"]
}
],
"views": [
{
"name": "Telemetry Table",
"key": "table",
"cssClass": "icon-tabular-realtime",
"template": telemetryTableTemplate,
"needs": [
"telemetry"
],
"delegation": true,
"editable": false
}
],
"directives": [
{
"key": "mctTable",
"implementation": MCTTable,
"depends": ["$timeout"]
}
],
"representations": [
{
"key": "table-options-edit",
"template": tableOptionsEditTemplate
}
],
"stylesheets": [
{
"stylesheetUrl": "css/table.css",
"priority": "mandatory"
}
]
}
});
});

View File

@@ -1,95 +0,0 @@
<div class="l-control-bar">
<a class="s-button t-export icon-download labeled"
ng-click="exportAsCSV()"
title="Export This View's Data">
Export
</a>
</div>
<div class="mct-table-headers-w" mct-scroll-x="scroll.x">
<table class="mct-table l-tabular-headers filterable"
ng-style="{
'max-width': totalWidth
}">
<thead>
<tr>
<th ng-repeat="header in displayHeaders"
ng-style="{
width: columnWidths[$index] + 'px',
'max-width': columnWidths[$index] + 'px',
}"
ng-class="[
enableSort ? 'sortable' : '',
sortColumn === header ? 'sort' : '',
sortDirection || ''
].join(' ')"
ng-click="toggleSort(header)">
{{ header }}
</th>
</tr>
<tr ng-if="enableFilter" class="s-filters">
<th ng-repeat="header in displayHeaders"
ng-style="{
width: columnWidths[$index] + 'px',
'max-width': columnWidths[$index] + 'px',
}">
<div class="holder l-filter flex-elem grows"
ng-class="{active: filters[header]}">
<input type="text"
ng-model="filters[header]"/>
<a class="clear-icon clear-input icon-x-in-circle"
ng-class="{show: filters[header]}"
ng-click="filters[header] = undefined"></a>
</div>
</th>
</tr>
</thead>
</table>
</div>
<table class="mct-sizing-table t-sizing-table"
ng-style="{
width: calcTableWidthPx
}">
<tbody>
<tr>
<td ng-repeat="header in displayHeaders">{{header}}</td>
</tr>
<tr><td ng-repeat="header in displayHeaders" >
{{sizingRow[header].text}}
</td></tr>
</tbody>
</table>
<div class="l-tabular-body t-scrolling vscroll--persist" mct-resize="resize()" mct-scroll-x="scroll.x">
<div class="mct-table-scroll-forcer"
ng-style="{
width: totalWidth
}"></div>
<table class="mct-table"
ng-style="{
height: totalHeight + 'px',
'max-width': totalWidth
}">
<tbody>
<tr ng-repeat-start="visibleRow in visibleRows track by $index"
ng-if="visibleRow.rowIndex === toiRowIndex"
ng-style="{ top: visibleRow.offsetY + 'px' }"
class="l-toi-tablerow">
<td colspan="999">
<mct-include key="'time-of-interest'"
class="l-toi-holder pinned"></mct-include>
</td>
</tr>
<tr ng-repeat-end
ng-style="{ top: visibleRow.offsetY + 'px' }"
ng-click="table.onRowClick($event, visibleRow.rowIndex)">
<td ng-repeat="header in displayHeaders"
ng-style="{
width: columnWidths[$index] + 'px',
'max-width': columnWidths[$index] + 'px',
}"
class="{{visibleRow.contents[header].cssClass}}">
{{ visibleRow.contents[header].text }}
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -1,32 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2018, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div ng-if="domainObject.getCapability('editor').inEditContext()"
ng-controller="TableOptionsController"
class="flex-elem grows l-inspector-part">
<mct-form
ng-model="configuration.table.columns"
structure="columnsForm"
name="columnsFormState"
class="flex-elem no-margin">
</mct-form>
</div>

View File

@@ -1,15 +0,0 @@
<div ng-controller="TelemetryTableController as tableController"
ng-class="{'loading': loading}">
<mct-table
headers="headers"
rows="rows"
time-columns="[tableController.table.timeSystemColumnTitle]"
format-cell="formatCell"
enableFilter="true"
enableSort="true"
auto-scroll="autoScroll"
default-sort="defaultSort"
export-as="{{ exportAs }}"
class="tabular-holder l-sticky-headers has-control-bar">
</mct-table>
</div>

View File

@@ -1,67 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(function () {
function TableColumn(openmct, telemetryObject, metadatum) {
this.openmct = openmct;
this.telemetryObject = telemetryObject;
this.metadatum = metadatum;
this.formatter = openmct.telemetry.getValueFormatter(metadatum);
this.titleValue = this.metadatum.name;
}
TableColumn.prototype.title = function (title) {
if (arguments.length > 0) {
this.titleValue = title;
}
return this.titleValue;
};
TableColumn.prototype.isCurrentTimeSystem = function () {
var isCurrentTimeSystem = this.metadatum.hints.hasOwnProperty('domain') &&
this.metadatum.key === this.openmct.time.timeSystem().key;
return isCurrentTimeSystem;
};
TableColumn.prototype.hasValue = function (telemetryObject, telemetryDatum) {
var keyStringForDatum = this.openmct.objects.makeKeyString(telemetryObject.identifier);
var keyStringForColumn = this.openmct.objects.makeKeyString(this.telemetryObject.identifier);
return keyStringForDatum === keyStringForColumn && telemetryDatum.hasOwnProperty(this.metadatum.source);
};
TableColumn.prototype.getValue = function (telemetryDatum, limitEvaluator) {
var alarm = limitEvaluator &&
limitEvaluator.evaluate(telemetryDatum, this.metadatum);
var value = {
text: this.formatter.format(telemetryDatum),
value: this.formatter.parse(telemetryDatum)
};
if (alarm) {
value.cssClass = alarm.cssClass;
}
return value;
};
return TableColumn;
});

View File

@@ -1,164 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* global Set */
define(
['./TableColumn'],
function (TableColumn) {
/**
* Class that manages table metadata, state, and contents.
* @memberof platform/features/table
* @param domainObject
* @constructor
*/
function TableConfiguration(domainObject, openmct) {
this.domainObject = domainObject;
this.openmct = openmct;
this.timeSystemColumn = undefined;
this.columns = [];
this.headers = new Set();
this.timeSystemColumnTitle = undefined;
}
/**
* Build column definition based on supplied telemetry metadata
* @param telemetryObject the telemetry producing object associated with this column
* @param metadata Metadata describing the domains and ranges available
* @returns {TableConfiguration} This object
*/
TableConfiguration.prototype.addColumn = function (telemetryObject, metadatum) {
var column = new TableColumn(this.openmct, telemetryObject, metadatum);
if (column.isCurrentTimeSystem()) {
if (!this.timeSystemColumnTitle) {
this.timeSystemColumnTitle = column.title();
}
column.title(this.timeSystemColumnTitle);
}
this.columns.push(column);
this.headers.add(column.title());
};
/**
* Retrieve and format values for a given telemetry datum.
* @param telemetryObject The object that the telemetry data is
* associated with
* @param datum The telemetry datum to retrieve values from
* @returns {Object} Key value pairs where the key is the column
* title, and the value is the formatted value from the provided datum.
*/
TableConfiguration.prototype.getRowValues = function (telemetryObject, limitEvaluator, datum) {
return this.columns.reduce(function (rowObject, column) {
var columnTitle = column.title();
var columnValue = {
text: '',
value: undefined
};
if (rowObject[columnTitle] === undefined) {
rowObject[columnTitle] = columnValue;
}
if (column.hasValue(telemetryObject, datum)) {
columnValue = column.getValue(datum, limitEvaluator);
if (columnValue.text === undefined) {
columnValue.text = '';
}
// Don't replace something with nothing.
// This occurs when there are multiple columns with the same
// column title
if (rowObject[columnTitle].text === undefined ||
rowObject[columnTitle].text.length === 0) {
rowObject[columnTitle] = columnValue;
}
}
return rowObject;
}, {});
};
/**
* @private
*/
TableConfiguration.prototype.defaultColumnConfiguration = function () {
return ((this.domainObject.getModel().configuration || {}).table || {}).columns || {};
};
/**
* Set the established configuration on the domain object
* @private
*/
TableConfiguration.prototype.saveColumnConfiguration = function (columnConfig) {
this.domainObject.useCapability('mutation', function (model) {
model.configuration = model.configuration || {};
model.configuration.table = model.configuration.table || {};
model.configuration.table.columns = columnConfig;
});
};
function configChanged(config1, config2) {
var config1Keys = Object.keys(config1),
config2Keys = Object.keys(config2);
return (config1Keys.length !== config2Keys.length) ||
config1Keys.some(function (key) {
return config1[key] !== config2[key];
});
}
/**
* As part of the process of building the table definition, extract
* configuration from column definitions.
* @returns {Object} A configuration object consisting of key-value
* pairs where the key is the column title, and the value is a
* boolean indicating whether the column should be shown.
*/
TableConfiguration.prototype.buildColumnConfiguration = function () {
var configuration = {},
//Use existing persisted config, or default it
defaultConfig = this.defaultColumnConfiguration();
/**
* For each column header, define a configuration value
* specifying whether the column is visible or not. Default to
* existing (persisted) configuration if available
*/
this.headers.forEach(function (columnTitle) {
configuration[columnTitle] =
typeof defaultConfig[columnTitle] === 'undefined' ? true :
defaultConfig[columnTitle];
});
//Synchronize column configuration with model
if (this.domainObject.hasCapability('editor') &&
this.domainObject.getCapability('editor').isEditContextRoot() &&
configChanged(configuration, defaultConfig)) {
this.saveColumnConfiguration(configuration);
}
return configuration;
};
return TableConfiguration;
}
);

View File

@@ -1,249 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'lodash',
'EventEmitter'
],
function (_, EventEmitter) {
/**
* @constructor
*/
function TelemetryCollection() {
EventEmitter.call(this, arguments);
this.dupeCheck = false;
this.telemetry = [];
this.highBuffer = [];
this.sortField = undefined;
this.lastBounds = {};
_.bindAll(this, [
'addOne',
'iteratee'
]);
}
TelemetryCollection.prototype = Object.create(EventEmitter.prototype);
TelemetryCollection.prototype.iteratee = function (item) {
return _.get(item, this.sortField);
};
/**
* This function is optimized for ticking - it assumes that start and end
* bounds will only increase and as such this cannot be used for decreasing
* bounds changes.
*
* An implication of this is that data will not be discarded that exceeds
* the given end bounds. For arbitrary bounds changes, it's assumed that
* a telemetry requery is performed anyway, and the collection is cleared
* and repopulated.
*
* @fires TelemetryCollection#added
* @fires TelemetryCollection#discarded
* @param bounds
*/
TelemetryCollection.prototype.bounds = function (bounds) {
var startChanged = this.lastBounds.start !== bounds.start;
var endChanged = this.lastBounds.end !== bounds.end;
var startIndex = 0;
var endIndex = 0;
var discarded;
var added;
var testValue;
this.lastBounds = bounds;
// If collection is not sorted by a time field, we cannot respond to
// bounds events
if (this.sortField === undefined) {
this.lastBounds = bounds;
return;
}
if (startChanged) {
testValue = _.set({}, this.sortField, bounds.start);
// Calculate the new index of the first item within the bounds
startIndex = _.sortedIndex(this.telemetry, testValue, this.sortField);
discarded = this.telemetry.splice(0, startIndex);
}
if (endChanged) {
testValue = _.set({}, this.sortField, bounds.end);
// Calculate the new index of the last item in bounds
endIndex = _.sortedLastIndex(this.highBuffer, testValue, this.sortField);
added = this.highBuffer.splice(0, endIndex);
added.forEach(function (datum) {
this.telemetry.push(datum);
}.bind(this));
}
if (discarded && discarded.length > 0) {
/**
* A `discarded` event is emitted when telemetry data fall out of
* bounds due to a bounds change event
* @type {object[]} discarded the telemetry data
* discarded as a result of the bounds change
*/
this.emit('discarded', discarded);
}
if (added && added.length > 0) {
/**
* An `added` event is emitted when a bounds change results in
* received telemetry falling within the new bounds.
* @type {object[]} added the telemetry data that is now within bounds
*/
this.emit('added', added);
}
};
/**
* Adds an individual item to the collection. Used internally only
* @private
* @param item
*/
TelemetryCollection.prototype.addOne = function (item) {
var isDuplicate = false;
var boundsDefined = this.lastBounds &&
(this.lastBounds.start !== undefined && this.lastBounds.end !== undefined);
var array;
var boundsLow;
var boundsHigh;
// If collection is not sorted by a time field, we cannot respond to
// bounds events, so no bounds checking necessary
if (this.sortField === undefined) {
this.telemetry.push(item);
return true;
}
// Insert into either in-bounds array, or the out of bounds high buffer.
// Data in the high buffer will be re-evaluated for possible insertion on next tick
if (boundsDefined) {
boundsHigh = _.get(item, this.sortField) > this.lastBounds.end;
boundsLow = _.get(item, this.sortField) < this.lastBounds.start;
if (!boundsHigh && !boundsLow) {
array = this.telemetry;
} else if (boundsHigh) {
array = this.highBuffer;
}
} else {
array = this.telemetry;
}
// If out of bounds low, disregard data
if (!boundsLow) {
// Going to check for duplicates. Bound the search problem to
// items around the given time. Use sortedIndex because it
// employs a binary search which is O(log n). Can use binary search
// based on time stamp because the array is guaranteed ordered due
// to sorted insertion.
var startIx = _.sortedIndex(array, item, this.sortField);
var endIx;
if (this.dupeCheck && startIx !== array.length) {
endIx = _.sortedLastIndex(array, item, this.sortField);
// Create an array of potential dupes, based on having the
// same time stamp
var potentialDupes = array.slice(startIx, endIx + 1);
// Search potential dupes for exact dupe
isDuplicate = _.findIndex(potentialDupes, _.isEqual.bind(undefined, item)) > -1;
}
if (!isDuplicate) {
array.splice(endIx || startIx, 0, item);
//Return true if it was added and in bounds
return array === this.telemetry;
}
}
return false;
};
/**
* Add an array of objects to this telemetry collection
* @fires TelemetryCollection#added
* @param {object[]} items
*/
TelemetryCollection.prototype.add = function (items) {
var added = items.filter(this.addOne);
this.emit('added', added);
this.dupeCheck = true;
};
/**
* Clears the contents of the telemetry collection
*/
TelemetryCollection.prototype.clear = function () {
this.telemetry = [];
this.highBuffer = [];
};
/**
* Sorts the telemetry collection based on the provided sort field
* specifier. Subsequent inserts are sorted to maintain specified sport
* order.
*
* @example
* // First build some mock telemetry for the purpose of an example
* let now = Date.now();
* let telemetry = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(function (value) {
* return {
* // define an object property to demonstrate nested paths
* timestamp: {
* ms: now - value * 1000,
* text:
* },
* value: value
* }
* });
* let collection = new TelemetryCollection();
*
* collection.add(telemetry);
*
* // Sort by telemetry value
* collection.sort("value");
*
* // Sort by ms since epoch
* collection.sort("timestamp.ms");
*
* // Sort by formatted date text
* collection.sort("timestamp.text");
*
*
* @param {string} sortField An object property path.
*/
TelemetryCollection.prototype.sort = function (sortField) {
this.sortField = sortField;
if (sortField !== undefined) {
this.telemetry = _.sortBy(this.telemetry, this.iteratee);
}
};
return TelemetryCollection;
}
);

View File

@@ -1,828 +0,0 @@
define(
[
'zepto',
'lodash'
],
function ($, _) {
/**
* A controller for the MCTTable directive. Populates scope with
* data used for populating, sorting, and filtering
* tables.
* @param $scope
* @param $timeout
* @param element
* @constructor
*/
function MCTTableController($scope, $window, element, exportService, formatService, openmct) {
var self = this;
this.$scope = $scope;
this.element = $(element[0]);
this.$window = $window;
this.maxDisplayRows = 100;
this.scrollable = this.element.find('.t-scrolling').first();
this.resultsHeader = this.element.find('.mct-table>thead').first();
this.sizingTableBody = this.element.find('.t-sizing-table>tbody').first();
this.$scope.sizingRow = {};
this.$scope.calcTableWidthPx = '100%';
this.timeApi = openmct.time;
this.toiFormatter = undefined;
this.formatService = formatService;
this.callbacks = {};
//Bind all class functions to 'this'
_.bindAll(this, [
'addRows',
'binarySearch',
'buildLargestRow',
'changeBounds',
'changeTimeOfInterest',
'changeTimeSystem',
'destroyConductorListeners',
'digest',
'filterAndSort',
'filterRows',
'firstVisible',
'insertSorted',
'lastVisible',
'onRowClick',
'onScroll',
'removeRows',
'resize',
'scrollToBottom',
'scrollToRow',
'setElementSizes',
'setHeaders',
'setRows',
'setTimeOfInterestRow',
'setVisibleRows',
'sortComparator',
'sortRows'
]);
this.scrollable.on('scroll', this.onScroll);
$scope.visibleRows = [];
$scope.displayRows = [];
/**
* Set default values for optional parameters on a given scope
*/
function setDefaults(scope) {
if (typeof scope.enableFilter === 'undefined') {
scope.enableFilter = true;
scope.filters = {};
}
if (typeof scope.enableSort === 'undefined') {
scope.enableSort = true;
scope.sortColumn = undefined;
scope.sortDirection = undefined;
}
if (scope.sortColumn !== undefined) {
scope.sortDirection = "asc";
}
}
setDefaults($scope);
$scope.exportAsCSV = function () {
var headers = $scope.displayHeaders,
filename = $(element[0]).attr('export-as');
exportService.exportCSV($scope.displayRows.map(function (row) {
return headers.reduce(function (r, header) {
r[header] = row[header].text;
return r;
}, {});
}), {
headers: headers,
filename: filename
});
};
$scope.toggleSort = function (key) {
if (!$scope.enableSort) {
return;
}
if ($scope.sortColumn !== key) {
$scope.sortColumn = key;
$scope.sortDirection = 'asc';
} else if ($scope.sortDirection === 'asc') {
$scope.sortDirection = 'desc';
} else if ($scope.sortDirection === 'desc') {
$scope.sortColumn = undefined;
$scope.sortDirection = undefined;
} else if ($scope.sortColumn !== undefined &&
$scope.sortDirection === undefined) {
$scope.sortDirection = 'asc';
}
self.setRows($scope.rows);
self.setTimeOfInterestRow(self.timeApi.timeOfInterest());
};
/*
* Define watches to listen for changes to headers and rows.
*/
$scope.$watchCollection('filters', function () {
self.setRows($scope.rows);
});
$scope.$watch('headers', function (newHeaders, oldHeaders) {
if (newHeaders !== oldHeaders) {
this.setHeaders(newHeaders);
}
}.bind(this));
$scope.$watch('rows', this.setRows);
/*
* Listen for rows added individually (eg. for real-time tables)
*/
$scope.$on('add:rows', this.addRows);
$scope.$on('remove:rows', this.removeRows);
/**
* Populated from the default-sort attribute on MctTable
* directive tag.
*/
$scope.$watch('defaultSort', function (newColumn, oldColumn) {
if (newColumn !== oldColumn) {
$scope.toggleSort(newColumn);
}
});
/*
* Listen for resize events to trigger recalculation of table width
*/
$scope.resize = this.setElementSizes;
/**
* Scope variable that is populated from the 'time-columns'
* attribute on the MctTable tag. Indicates which columns, while
* sorted, can be used for indicated time of interest.
*/
$scope.$watch("timeColumns", function (timeColumns) {
if (timeColumns) {
this.destroyConductorListeners();
this.timeApi.on('timeSystem', this.changeTimeSystem);
this.timeApi.on('timeOfInterest', this.changeTimeOfInterest);
this.timeApi.on('bounds', this.changeBounds);
// If time system defined, set initially
if (this.timeApi.timeSystem() !== undefined) {
this.changeTimeSystem(this.timeApi.timeSystem());
}
}
}.bind(this));
$scope.$on('$destroy', function () {
this.scrollable.off('scroll', this.onScroll);
this.destroyConductorListeners();
}.bind(this));
}
MCTTableController.prototype.destroyConductorListeners = function () {
this.timeApi.off('timeSystem', this.changeTimeSystem);
this.timeApi.off('timeOfInterest', this.changeTimeOfInterest);
this.timeApi.off('bounds', this.changeBounds);
};
MCTTableController.prototype.changeTimeSystem = function (timeSystem) {
var format = timeSystem.timeFormat;
this.toiFormatter = this.formatService.getFormat(format);
};
/**
* If auto-scroll is enabled, this function will scroll to the
* bottom of the page
* @private
*/
MCTTableController.prototype.scrollToBottom = function () {
this.scrollable[0].scrollTop = this.scrollable[0].scrollHeight;
};
/**
* Handles a row add event. Rows can be added as needed using the
* `add:row` broadcast event.
* @private
*/
MCTTableController.prototype.addRows = function (event, rows) {
//Does the row pass the current filter?
if (this.filterRows(rows).length > 0) {
rows.forEach(this.insertSorted.bind(this, this.$scope.displayRows));
//Resize the columns , then update the rows visible in the table
this.resize([this.$scope.sizingRow].concat(rows))
.then(this.setVisibleRows)
.then(function () {
if (this.$scope.autoScroll) {
this.scrollToBottom();
}
}.bind(this));
var toi = this.timeApi.timeOfInterest();
if (toi !== -1) {
this.setTimeOfInterestRow(toi);
}
}
};
/**
* Handles a row remove event. Rows can be removed as needed using the
* `remove:row` broadcast event.
* @private
*/
MCTTableController.prototype.removeRows = function (event, rows) {
var indexInDisplayRows;
rows.forEach(function (row) {
// Do a sequential search here. Only way of finding row is by
// object equality, so array is in effect unsorted.
indexInDisplayRows = this.$scope.displayRows.indexOf(row);
if (indexInDisplayRows !== -1) {
this.$scope.displayRows.splice(indexInDisplayRows, 1);
}
}, this);
this.$scope.sizingRow = this.buildLargestRow([this.$scope.sizingRow].concat(rows));
this.setElementSizes();
this.setVisibleRows()
.then(function () {
if (this.$scope.autoScroll) {
this.scrollToBottom();
}
}.bind(this));
};
/**
* @private
*/
MCTTableController.prototype.onScroll = function (event) {
this.scrollWindow = {
top: this.scrollable[0].scrollTop,
bottom: this.scrollable[0].scrollTop + this.scrollable[0].offsetHeight,
offsetHeight: this.scrollable[0].offsetHeight,
height: this.scrollable[0].scrollHeight
};
this.$window.requestAnimationFrame(function () {
this.setVisibleRows();
this.digest();
// If user scrolls away from bottom, disable auto-scroll.
// Auto-scroll will be re-enabled if user scrolls to bottom again.
if (this.scrollWindow.top <
(this.scrollWindow.height - this.scrollWindow.offsetHeight) - 20) {
this.$scope.autoScroll = false;
} else {
this.$scope.autoScroll = true;
}
this.scrolling = false;
delete this.scrollWindow;
}.bind(this));
};
/**
* Return first visible row, based on current scroll state.
* @private
*/
MCTTableController.prototype.firstVisible = function () {
var topScroll = this.scrollWindow ?
this.scrollWindow.top :
this.scrollable[0].scrollTop;
return Math.floor(
(topScroll) / this.$scope.rowHeight
);
};
/**
* Return last visible row, based on current scroll state.
* @private
*/
MCTTableController.prototype.lastVisible = function () {
var bottomScroll = this.scrollWindow ?
this.scrollWindow.bottom :
this.scrollable[0].scrollTop + this.scrollable[0].offsetHeight;
return Math.ceil(
(bottomScroll) /
this.$scope.rowHeight
);
};
/**
* Sets visible rows based on array
* content and current scroll state.
*/
MCTTableController.prototype.setVisibleRows = function () {
var self = this,
totalVisible,
numberOffscreen,
firstVisible,
lastVisible,
start,
end;
//No need to scroll
if (this.$scope.displayRows.length < this.maxDisplayRows) {
start = 0;
end = this.$scope.displayRows.length;
} else {
firstVisible = this.firstVisible();
lastVisible = this.lastVisible();
totalVisible = lastVisible - firstVisible;
numberOffscreen = this.maxDisplayRows - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
end = Math.min(this.maxDisplayRows,
this.$scope.displayRows.length);
} else if (end >= this.$scope.displayRows.length) {
end = this.$scope.displayRows.length;
start = end - this.maxDisplayRows + 1;
}
if (this.$scope.visibleRows[0] &&
this.$scope.visibleRows[0].rowIndex === start &&
this.$scope.visibleRows[this.$scope.visibleRows.length - 1]
.rowIndex === end) {
return this.digest();
}
}
//Set visible rows from display rows, based on calculated offset.
this.$scope.visibleRows = this.$scope.displayRows.slice(start, end)
.map(function (row, i) {
return {
rowIndex: start + i,
offsetY: ((start + i) * self.$scope.rowHeight),
contents: row
};
});
return this.digest();
};
/**
* Update table headers with new headers. If filtering is
* enabled, reset filters. If sorting is enabled, reset
* sorting.
*/
MCTTableController.prototype.setHeaders = function (newHeaders) {
if (!newHeaders) {
return;
}
this.$scope.displayHeaders = newHeaders;
if (this.$scope.enableFilter) {
this.$scope.filters = {};
}
// Reset column sort information unless the new headers
// contain the column currently sorted on.
if (this.$scope.enableSort &&
newHeaders.indexOf(this.$scope.sortColumn) === -1) {
this.$scope.sortColumn = undefined;
this.$scope.sortDirection = undefined;
}
this.setRows(this.$scope.rows);
};
/**
* Read styles from the DOM and use them to calculate offsets
* for individual rows.
*/
MCTTableController.prototype.setElementSizes = function () {
var tbody = this.sizingTableBody,
firstRow = tbody.find('tr'),
column = firstRow.find('td'),
rowHeight = firstRow.prop('offsetHeight'),
columnWidth,
tableWidth = 0,
overallHeight = (rowHeight *
(this.$scope.displayRows ? this.$scope.displayRows.length - 1 : 0));
this.$scope.columnWidths = [];
while (column.length) {
columnWidth = column.prop('offsetWidth');
this.$scope.columnWidths.push(column.prop('offsetWidth'));
tableWidth += columnWidth;
column = column.next();
}
this.$scope.rowHeight = rowHeight;
this.$scope.totalHeight = overallHeight;
var scrollW = this.scrollable[0].offsetWidth - this.scrollable[0].clientWidth;
if (scrollW && scrollW > 0) {
this.$scope.calcTableWidthPx = 'calc(100% - ' + scrollW + 'px)';
}
if (tableWidth > 0) {
this.$scope.totalWidth = tableWidth + 'px';
} else {
this.$scope.totalWidth = 'none';
}
};
/**
* Finds the correct insertion point for a new row, which takes into
* account duplicates to make sure new rows are inserted in a way that
* maintains arrival order.
*
* @private
* @param {Array} searchArray
* @param {Object} searchElement Object to find the insertion point for
*/
MCTTableController.prototype.findInsertionPoint = function (searchArray, searchElement) {
var index;
var testIndex;
var first = searchArray[0];
var last = searchArray[searchArray.length - 1];
if (first) {
first = first[this.$scope.sortColumn].text;
}
if (last) {
last = last[this.$scope.sortColumn].text;
}
// Shortcut check for append/prepend
if (first && this.sortComparator(first, searchElement) >= 0) {
index = testIndex = 0;
} else if (last && this.sortComparator(last, searchElement) <= 0) {
index = testIndex = searchArray.length;
} else {
// use a binary search to find the correct insertion point
index = testIndex = this.binarySearch(
searchArray,
searchElement,
0,
searchArray.length - 1
);
}
//It's possible that the insertion point is a duplicate of the element to be inserted
var isDupe = function () {
return this.sortComparator(searchElement,
searchArray[testIndex][this.$scope.sortColumn].text) === 0;
}.bind(this);
// In the event of a duplicate, scan left or right (depending on
// sort order) to find an insertion point that maintains order received
while (testIndex >= 0 && testIndex < searchArray.length && isDupe()) {
if (this.$scope.sortDirection === 'asc') {
index = ++testIndex;
} else {
index = testIndex--;
}
}
return index;
};
/**
* @private
*/
MCTTableController.prototype.binarySearch = function (searchArray, searchElement, min, max) {
var sampleAt = Math.floor((max - min) / 2) + min;
if (max < min) {
return min; // Element is not in array, min gives direction
}
switch (this.sortComparator(searchElement,
searchArray[sampleAt][this.$scope.sortColumn].text)) {
case -1:
return this.binarySearch(searchArray, searchElement, min,
sampleAt - 1);
case 0:
return sampleAt;
case 1:
return this.binarySearch(searchArray, searchElement,
sampleAt + 1, max);
}
};
/**
* @private
*/
MCTTableController.prototype.insertSorted = function (array, element) {
var index = -1;
if (!this.$scope.sortColumn || !this.$scope.sortDirection) {
//No sorting applied, push it on the end.
index = array.length;
} else {
//Sort is enabled, perform binary search to find insertion point
index = this.findInsertionPoint(array, element[this.$scope.sortColumn].text);
}
if (index === -1) {
array.unshift(element);
} else if (index === array.length) {
array.push(element);
} else {
array.splice(index, 0, element);
}
};
/**
* Compare two variables, returning a number that represents
* which is larger. Similar to the default array sort
* comparator, but does not coerce all values to string before
* conversion. Strings are lowercased before comparison.
*
* @private
*/
MCTTableController.prototype.sortComparator = function (a, b) {
var result = 0,
sortDirectionMultiplier,
numberA,
numberB;
/**
* Given a value, if it is a number, or a string representation of a
* number, then return a number representation. Otherwise, return
* the original value. It's a little more robust than using just
* Number() or parseFloat, or isNaN in isolation, all of which are
* fairly inconsistent in their results.
* @param value The value to return as a number.
* @returns {*} The value cast to a Number, or the original value if
* a Number representation is not possible.
*/
function toNumber(value) {
var val = !isNaN(Number(value)) && !isNaN(parseFloat(value)) ? Number(value) : value;
return val;
}
numberA = toNumber(a);
numberB = toNumber(b);
//If they're both numbers, then compare them as numbers
if (typeof numberA === "number" && typeof numberB === "number") {
a = numberA;
b = numberB;
}
//If they're both strings, then ignore case
if (typeof a === "string" && typeof b === "string") {
a = a.toLowerCase();
b = b.toLowerCase();
}
if (a < b) {
result = -1;
}
if (a > b) {
result = 1;
}
if (this.$scope.sortDirection === 'asc') {
sortDirectionMultiplier = 1;
} else if (this.$scope.sortDirection === 'desc') {
sortDirectionMultiplier = -1;
}
return result * sortDirectionMultiplier;
};
/**
* Returns a new array which is a result of applying the sort
* criteria defined in $scope.
*
* Does not modify the array that was passed in.
*/
MCTTableController.prototype.sortRows = function (rowsToSort) {
var self = this,
sortKey = this.$scope.sortColumn;
if (!this.$scope.sortColumn || !this.$scope.sortDirection) {
return rowsToSort;
}
return rowsToSort.sort(function (a, b) {
return self.sortComparator(a[sortKey].text, b[sortKey].text);
});
};
/**
* Returns an object which contains the largest values
* for each key in the given set of rows. This is used to
* pre-calculate optimal column sizes without having to render
* every row.
*/
MCTTableController.prototype.buildLargestRow = function (rows) {
var largestRow = rows.reduce(function (prevLargest, row) {
Object.keys(row).forEach(function (key) {
var currentColumn,
currentColumnLength,
largestColumn,
largestColumnLength;
if (row[key]) {
currentColumn = (row[key]).text;
currentColumnLength =
(currentColumn && currentColumn.length) ?
currentColumn.length :
currentColumn;
largestColumn = prevLargest[key] ? prevLargest[key].text : "";
largestColumnLength = largestColumn.length;
if (currentColumnLength > largestColumnLength) {
prevLargest[key] = JSON.parse(JSON.stringify(row[key]));
}
}
});
return prevLargest;
}, JSON.parse(JSON.stringify(rows[0] || {})));
return largestRow;
};
// Will effectively cap digests at 60Hz
// Also turns digest into a promise allowing code to force digest, then
// schedule something to happen afterwards
MCTTableController.prototype.digest = function () {
var scope = this.$scope;
var self = this;
var raf = this.$window.requestAnimationFrame;
var promise = this.digestPromise;
if (!promise) {
self.digestPromise = promise = new Promise(function (resolve) {
raf(function () {
scope.$digest();
self.digestPromise = undefined;
resolve();
});
});
}
return promise;
};
/**
* Calculates the widest row in the table, and if necessary, resizes
* the table accordingly
*
* @param rows the rows on which to resize
* @returns {Promise} a promise that will resolve when resizing has
* occurred.
* @private
*/
MCTTableController.prototype.resize = function (rows) {
this.$scope.sizingRow = this.buildLargestRow(rows);
return this.digest().then(this.setElementSizes);
};
/**
* @private
*/
MCTTableController.prototype.filterAndSort = function (rows) {
var displayRows = rows;
if (this.$scope.enableFilter) {
displayRows = this.filterRows(displayRows);
}
if (this.$scope.enableSort) {
displayRows = this.sortRows(displayRows.slice(0));
}
return displayRows;
};
/**
* Update rows with new data. If filtering is enabled, rows
* will be sorted before display.
*/
MCTTableController.prototype.setRows = function (newRows) {
//Nothing to show because no columns visible
if (!this.$scope.displayHeaders || !newRows) {
return;
}
this.$scope.displayRows = this.filterAndSort(newRows || []);
return this.resize(newRows)
.then(function (rows) {
return this.setVisibleRows(rows);
}.bind(this))
//Timeout following setVisibleRows to allow digest to
// perform DOM changes, otherwise scrollTo won't work.
.then(function () {
//If TOI specified, scroll to it
var timeOfInterest = this.timeApi.timeOfInterest();
if (timeOfInterest) {
this.setTimeOfInterestRow(timeOfInterest);
this.scrollToRow(this.$scope.toiRowIndex);
}
}.bind(this));
};
/**
* Applies user defined filters to rows. These filters are based on
* the text entered in the search areas in each column.
* @param rowsToFilter {Object[]} The rows to apply filters to
* @returns {Object[]} A filtered copy of the supplied rows
*/
MCTTableController.prototype.filterRows = function (rowsToFilter) {
var filters = {},
self = this;
/**
* Returns true if row matches all filters.
*/
function matchRow(filterMap, row) {
return Object.keys(filterMap).every(function (key) {
if (!row[key]) {
return false;
}
var testVal = String(row[key].text).toLowerCase();
return testVal.indexOf(filterMap[key]) !== -1;
});
}
if (!Object.keys(this.$scope.filters).length) {
return rowsToFilter;
}
Object.keys(this.$scope.filters).forEach(function (key) {
if (!self.$scope.filters[key]) {
return;
}
filters[key] = self.$scope.filters[key].toLowerCase();
});
return rowsToFilter.filter(matchRow.bind(null, filters));
};
/**
* Scroll the view to a given row index
* @param displayRowIndex {number} The index in the displayed rows
* to scroll to.
*/
MCTTableController.prototype.scrollToRow = function (displayRowIndex) {
var visible = displayRowIndex > this.firstVisible() && displayRowIndex < this.lastVisible();
if (!visible) {
var scrollTop = displayRowIndex * this.$scope.rowHeight +
(this.scrollable[0].offsetHeight / 2);
this.scrollable[0].scrollTop = scrollTop;
this.setVisibleRows();
}
};
/**
* Update rows with new data. If filtering is enabled, rows
* will be sorted before display.
*/
MCTTableController.prototype.setTimeOfInterestRow = function (newTOI) {
var isSortedByTime =
this.$scope.timeColumns &&
this.$scope.timeColumns.indexOf(this.$scope.sortColumn) !== -1;
this.$scope.toiRowIndex = -1;
if (newTOI && isSortedByTime) {
var formattedTOI = this.toiFormatter.format(newTOI);
var rowIndex = this.binarySearch(
this.$scope.displayRows,
formattedTOI,
0,
this.$scope.displayRows.length - 1);
if (rowIndex > 0 && rowIndex < this.$scope.displayRows.length) {
this.$scope.toiRowIndex = rowIndex;
}
}
};
MCTTableController.prototype.changeTimeOfInterest = function (newTOI) {
this.setTimeOfInterestRow(newTOI);
this.scrollToRow(this.$scope.toiRowIndex);
};
/**
* On zoom, pan, etc. reset TOI
* @param bounds
*/
MCTTableController.prototype.changeBounds = function (bounds) {
this.setTimeOfInterestRow(this.timeApi.timeOfInterest());
if (this.$scope.toiRowIndex !== -1) {
this.scrollToRow(this.$scope.toiRowIndex);
}
};
/**
* @private
*/
MCTTableController.prototype.onRowClick = function (event, rowIndex) {
if (this.$scope.timeColumns.indexOf(this.$scope.sortColumn) !== -1) {
var selectedTime = this.$scope.displayRows[rowIndex][this.$scope.sortColumn].text;
if (selectedTime &&
this.toiFormatter.validate(selectedTime) &&
event.altKey) {
this.timeApi.timeOfInterest(this.toiFormatter.parse(selectedTime));
}
}
};
return MCTTableController;
}
);

View File

@@ -1,113 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Notes on implementation of plot options
*
* Multiple y-axes will have to be handled with multiple forms as
* they will need to be stored on distinct model object
*
* Likewise plot series options per-child will need to be separate
* forms.
*/
/**
* The LayoutController is responsible for supporting the
* Layout view. It arranges frames according to saved configuration
* and provides methods for updating these based on mouse
* movement.
* @memberof platform/features/plot
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function TableOptionsController($scope) {
var self = this;
this.$scope = $scope;
this.domainObject = $scope.domainObject;
this.listeners = [];
$scope.columnsForm = {};
function unlisten() {
self.listeners.forEach(function (listener) {
listener();
});
}
$scope.$watch('domainObject', function (domainObject) {
unlisten();
self.populateForm(domainObject.getModel());
self.listeners.push(self.domainObject.getCapability('mutation').listen(function (model) {
self.populateForm(model);
}));
});
/**
* Maintain a configuration object on scope that stores column
* configuration. On change, synchronize with object model.
*/
$scope.$watchCollection('configuration.table.columns', function (newColumns, oldColumns) {
if (newColumns !== oldColumns) {
self.domainObject.useCapability('mutation', function (model) {
model.configuration.table.columns = newColumns;
});
self.domainObject.getCapability('persistence').persist();
}
});
/**
* Destroy all mutation listeners
*/
$scope.$on('$destroy', unlisten);
}
TableOptionsController.prototype.populateForm = function (model) {
var columnsDefinition = (((model.configuration || {}).table || {}).columns || {}),
rows = [];
this.$scope.columnsForm = {
'name': 'Columns',
'sections': [{
'name': 'Columns',
'rows': rows
}]};
Object.keys(columnsDefinition).forEach(function (key) {
rows.push({
'name': key,
'control': 'checkbox',
'key': key
});
});
this.$scope.configuration = JSON.parse(JSON.stringify(model.configuration || {}));
};
return TableOptionsController;
}
);

View File

@@ -1,450 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* global console*/
/**
* This bundle adds a table view for displaying telemetry data.
* @namespace platform/features/table
*/
define(
[
'../TableConfiguration',
'../../../../../src/api/objects/object-utils',
'../TelemetryCollection',
'lodash'
],
function (TableConfiguration, objectUtils, TelemetryCollection, _) {
/**
* The TableController is responsible for getting data onto the page
* in the table widget. This includes handling composition,
* configuration, and telemetry subscriptions.
* @memberof platform/features/table
* @param $scope
* @constructor
*/
function TelemetryTableController(
$scope,
$timeout,
openmct
) {
this.$scope = $scope;
this.$timeout = $timeout;
this.openmct = openmct;
this.batchSize = 1000;
/*
* Initialization block
*/
this.columns = {}; //Range and Domain columns
this.unobserveObject = undefined;
this.subscriptions = [];
this.timeColumns = [];
$scope.rows = [];
this.table = new TableConfiguration($scope.domainObject,
openmct);
this.lastBounds = this.openmct.time.bounds();
this.lastRequestTime = 0;
this.telemetry = new TelemetryCollection();
if (this.lastBounds) {
this.telemetry.bounds(this.lastBounds);
}
/*
* Create a new format object from legacy object, and replace it
* when it changes
*/
this.domainObject = objectUtils.toNewFormat($scope.domainObject.getModel(),
$scope.domainObject.getId());
this.$scope.exportAs = this.$scope.domainObject.getModel().name;
_.bindAll(this, [
'destroy',
'sortByTimeSystem',
'loadColumns',
'getHistoricalData',
'subscribeToNewData',
'changeBounds',
'setClock',
'addRowsToTable',
'removeRowsFromTable'
]);
// Retrieve data when domain object is available.
// Also deferring telemetry request makes testing easier as controller
// construction has no unintended consequences.
$scope.$watch("domainObject", function () {
this.getData();
this.registerChangeListeners();
}.bind(this));
this.setClock(this.openmct.time.clock());
this.$scope.$on("$destroy", this.destroy);
}
/**
* @private
* @param {boolean} scroll
*/
TelemetryTableController.prototype.setClock = function (clock) {
this.$scope.autoScroll = clock !== undefined;
};
/**
* Based on the selected time system, find a matching domain column
* to sort by. By default will just match on key.
*
* @private
*/
TelemetryTableController.prototype.sortByTimeSystem = function () {
var scope = this.$scope;
var sortColumn;
scope.defaultSort = undefined;
sortColumn = this.table.columns.filter(function (column) {
return column.isCurrentTimeSystem();
})[0];
if (sortColumn) {
scope.defaultSort = sortColumn.title();
this.telemetry.sort(sortColumn.title() + '.value');
}
};
/**
* Attaches listeners that respond to state change in domain object,
* conductor, and receipt of telemetry
*
* @private
*/
TelemetryTableController.prototype.registerChangeListeners = function () {
if (this.unobserveObject) {
this.unobserveObject();
}
this.unobserveObject = this.openmct.objects.observe(this.domainObject, "*",
function (domainObject) {
this.domainObject = domainObject;
this.getData();
}.bind(this)
);
this.openmct.time.on('timeSystem', this.sortByTimeSystem);
this.openmct.time.on('bounds', this.changeBounds);
this.openmct.time.on('clock', this.setClock);
this.telemetry.on('added', this.addRowsToTable);
this.telemetry.on('discarded', this.removeRowsFromTable);
};
/**
* On receipt of new telemetry, informs mct-table directive that new rows
* are available and passes populated rows to it
*
* @private
* @param rows
*/
TelemetryTableController.prototype.addRowsToTable = function (rows) {
this.$scope.$broadcast('add:rows', rows);
};
/**
* When rows are to be removed, informs mct-table directive. Row removal
* happens when rows call outside the bounds of the time conductor
*
* @private
* @param rows
*/
TelemetryTableController.prototype.removeRowsFromTable = function (rows) {
this.$scope.$broadcast('remove:rows', rows);
};
/**
* On Time Conductor bounds change, update displayed telemetry. In the
* case of a tick, previously visible telemetry that is now out of band
* will be removed from the table.
* @param {openmct.TimeConductorBounds~TimeConductorBounds} bounds
*/
TelemetryTableController.prototype.changeBounds = function (bounds, isTick) {
if (isTick) {
this.telemetry.bounds(bounds);
} else {
// Is fixed bounds change
this.getData();
}
this.lastBounds = bounds;
};
/**
* Clean controller, deregistering listeners etc.
*/
TelemetryTableController.prototype.destroy = function () {
this.openmct.time.off('timeSystem', this.sortByTimeSystem);
this.openmct.time.off('bounds', this.changeBounds);
this.openmct.time.off('clock', this.setClock);
this.subscriptions.forEach(function (subscription) {
subscription();
});
if (this.unobserveObject) {
this.unobserveObject();
}
this.subscriptions = [];
if (this.timeoutHandle) {
this.$timeout.cancel(this.timeoutHandle);
}
};
/**
* For given objects, populate column metadata and table headers.
* @private
* @param {module:openmct.DomainObject[]} objects the domain objects for
* which columns should be populated
*/
TelemetryTableController.prototype.loadColumns = function (objects) {
var telemetryApi = this.openmct.telemetry;
this.table = new TableConfiguration(this.$scope.domainObject,
this.openmct);
this.$scope.headers = [];
if (objects.length > 0) {
objects.forEach(function (object) {
var metadataValues = telemetryApi.getMetadata(object).values();
metadataValues.forEach(function (metadatum) {
this.table.addColumn(object, metadatum);
}.bind(this));
}.bind(this));
this.filterColumns();
this.sortByTimeSystem();
}
return objects;
};
/**
* Request telemetry data from an historical store for given objects.
* @private
* @param {object[]} The domain objects to request telemetry for
* @returns {Promise} resolved when historical data is available
*/
TelemetryTableController.prototype.getHistoricalData = function (objects) {
var self = this;
var openmct = this.openmct;
var bounds = openmct.time.bounds();
var scope = this.$scope;
var rowData = [];
var processedObjects = 0;
var requestTime = this.lastRequestTime = Date.now();
var telemetryCollection = this.telemetry;
var promise = new Promise(function (resolve, reject) {
/*
* On completion of batched processing, set the rows on scope
*/
function finishProcessing() {
telemetryCollection.add(rowData);
scope.rows = telemetryCollection.telemetry;
self.loading(false);
resolve(scope.rows);
}
/*
* Process a batch of historical data
*/
function processData(object, historicalData, index, limitEvaluator) {
if (index >= historicalData.length) {
processedObjects++;
if (processedObjects === objects.length) {
finishProcessing();
}
} else {
rowData = rowData.concat(historicalData.slice(index, index + self.batchSize)
.map(self.table.getRowValues.bind(self.table, object, limitEvaluator)));
/*
Use timeout to yield process to other UI activities. On
return, process next batch
*/
self.timeoutHandle = self.$timeout(function () {
processData(object, historicalData, index + self.batchSize, limitEvaluator);
});
}
}
function makeTableRows(object, historicalData) {
// Only process the most recent request
if (requestTime === self.lastRequestTime) {
var limitEvaluator = openmct.telemetry.limitEvaluator(object);
processData(object, historicalData, 0, limitEvaluator);
} else {
resolve(rowData);
}
}
/*
Use the telemetry API to request telemetry for a given object
*/
function requestData(object) {
return openmct.telemetry.request(object, {
start: bounds.start,
end: bounds.end
}).then(makeTableRows.bind(undefined, object))
.catch(reject);
}
this.$timeout.cancel(this.timeoutHandle);
if (objects.length > 0) {
objects.forEach(requestData);
} else {
self.loading(false);
resolve([]);
}
}.bind(this));
return promise;
};
/**
* Subscribe to real-time data for the given objects.
* @private
* @param {object[]} objects The objects to subscribe to.
*/
TelemetryTableController.prototype.subscribeToNewData = function (objects) {
var telemetryApi = this.openmct.telemetry;
var telemetryCollection = this.telemetry;
//Set table max length to avoid unbounded growth.
var limitEvaluator;
var table = this.table;
this.subscriptions.forEach(function (subscription) {
subscription();
});
this.subscriptions = [];
function newData(domainObject, datum) {
limitEvaluator = telemetryApi.limitEvaluator(domainObject);
telemetryCollection.add([table.getRowValues(domainObject, limitEvaluator, datum)]);
}
objects.forEach(function (object) {
this.subscriptions.push(
telemetryApi.subscribe(object, newData.bind(this, object), {}));
}.bind(this));
return objects;
};
/**
* Return an array of telemetry objects in this view that should be
* subscribed to.
* @private
* @returns {Promise<Array>} a promise that resolves with an array of
* telemetry objects in this view.
*/
TelemetryTableController.prototype.getTelemetryObjects = function () {
var telemetryApi = this.openmct.telemetry;
var compositionApi = this.openmct.composition;
function filterForTelemetry(objects) {
return objects.filter(telemetryApi.isTelemetryObject.bind(telemetryApi));
}
/*
* If parent object is a telemetry object, subscribe to it. Do not
* test composees.
*/
if (telemetryApi.isTelemetryObject(this.domainObject)) {
return Promise.resolve([this.domainObject]);
} else {
/*
* If parent object is not a telemetry object, subscribe to all
* composees that are telemetry producing objects.
*/
var composition = compositionApi.get(this.domainObject);
if (composition) {
return composition
.load()
.then(filterForTelemetry);
}
}
};
/**
* Request historical data, and subscribe to for real-time data.
* @private
* @returns {Promise} A promise that is resolved once subscription is
* established, and historical telemetry is received and processed.
*/
TelemetryTableController.prototype.getData = function () {
var scope = this.$scope;
this.telemetry.clear();
this.telemetry.bounds(this.openmct.time.bounds());
this.loading(true);
scope.rows = [];
return this.getTelemetryObjects()
.then(this.loadColumns)
.then(this.subscribeToNewData)
.then(this.getHistoricalData)
.catch(function error(e) {
this.loading(false);
console.error(e.stack || e);
}.bind(this));
};
TelemetryTableController.prototype.loading = function (loading) {
this.$timeout(function () {
this.$scope.loading = loading;
}.bind(this));
};
/**
* When column configuration changes, update the visible headers
* accordingly.
* @private
*/
TelemetryTableController.prototype.filterColumns = function () {
var columnConfig = this.table.buildColumnConfiguration();
//Populate headers with visible columns (determined by configuration)
this.$scope.headers = Object.keys(columnConfig).filter(function (column) {
return columnConfig[column];
});
};
return TelemetryTableController;
}
);

View File

@@ -1,115 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
"../controllers/MCTTableController",
"text!../../res/templates/mct-table.html"
],
function (MCTTableController, TableTemplate) {
/**
* Defines a generic 'Table' component. The table can be populated
* en-masse by setting the rows attribute, or rows can be added as
* needed via a broadcast 'addRow' event.
*
* This directive accepts parameters specifying header and row
* content, as well as some additional options.
*
* Two broadcast events for notifying the table that the rows have
* changed. For performance reasons, the table does not monitor the
* content of `rows` constantly.
* - 'add:row': A $broadcast event that will notify the table that
* a new row has been added to the table.
* eg.
* <pre><code>
* $scope.rows.push(newRow);
* $scope.$broadcast('add:row', $scope.rows.length-1);
* </code></pre>
* The code above adds a new row, and alerts the table using the
* add:row event. Sorting and filtering will be applied
* automatically by the table component.
*
* - 'remove:row': A $broadcast event that will notify the table that a
* row should be removed from the table.
* eg.
* <pre><code>
* $scope.rows.slice(5, 1);
* $scope.$broadcast('remove:row', 5);
* </code></pre>
* The code above removes a row from the rows array, and then alerts
* the table to its removal.
*
* @memberof platform/features/table
* @param {string[]} headers The column titles to appear at the top
* of the table. Corresponding values are specified in the rows
* using the header title provided here.
* @param {Object[]} rows The row content. Each row is an object
* with key-value pairs where the key corresponds to a header
* specified in the headers parameter.
* @param {boolean} enableFilter If true, values will be searchable
* and results filtered
* @param {boolean} enableSort If true, sorting will be enabled
* allowing sorting by clicking on column headers
* @param {boolean} autoScroll If true, table will automatically
* scroll to the bottom as new data arrives. Auto-scroll can be
* disengaged manually by scrolling away from the bottom of the
* table, and can also be enabled manually by scrolling to the bottom of
* the table rows.
*
* @constructor
*/
function MCTTable() {
return {
restrict: "E",
template: TableTemplate,
controller: [
'$scope',
'$window',
'$element',
'exportService',
'formatService',
'openmct',
MCTTableController
],
controllerAs: "table",
scope: {
headers: "=",
rows: "=",
formatCell: "=?",
enableFilter: "=?",
enableSort: "=?",
autoScroll: "=?",
// Used to indicate which columns contain time data. This
// will be used for determining when the table is sorted
// by the column that can be used for time conductor
// time of interest.
timeColumns: "=?",
// Indicate a column to sort on. Allows control of sort
// via configuration (eg. for default sort column).
defaultSort: "=?"
}
};
}
return MCTTable;
}
);

View File

@@ -1,214 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
"../src/TableConfiguration"
],
function (Table) {
describe("A table", function () {
var mockTableObject,
mockTelemetryObject,
mockAPI,
mockTelemetryAPI,
table,
mockTimeAPI,
mockObjectsAPI,
mockModel;
beforeEach(function () {
mockTableObject = jasmine.createSpyObj('domainObject',
['getModel', 'useCapability', 'getCapability', 'hasCapability']
);
mockModel = {};
mockTableObject.getModel.and.returnValue(mockModel);
mockTableObject.getCapability.and.callFake(function (name) {
return name === 'editor' && {
isEditContextRoot: function () {
return true;
}
};
});
mockTelemetryObject = {
identifier: {
namespace: 'mock',
key: 'domainObject'
}
};
mockTelemetryAPI = jasmine.createSpyObj('telemetryAPI', [
'getValueFormatter'
]);
mockTimeAPI = jasmine.createSpyObj('timeAPI', [
'timeSystem'
]);
mockObjectsAPI = jasmine.createSpyObj('objectsAPI', [
'makeKeyString'
]);
mockObjectsAPI.makeKeyString.and.callFake(function (identifier) {
return [identifier.namespace, identifier.key].join(':');
});
mockAPI = {
telemetry: mockTelemetryAPI,
time: mockTimeAPI,
objects: mockObjectsAPI
};
mockTelemetryAPI.getValueFormatter.and.callFake(function (metadata) {
var formatter = jasmine.createSpyObj(
'telemetryFormatter:' + metadata.key,
[
'format',
'parse'
]
);
var getter = function (datum) {
return datum[metadata.key];
};
formatter.format.and.callFake(getter);
formatter.parse.and.callFake(getter);
return formatter;
});
table = new Table(mockTableObject, mockAPI);
});
describe("Building columns from telemetry metadata", function () {
var metadata = [
{
name: 'Range 1',
key: 'range1',
source: 'range1',
hints: {
range: 1
}
},
{
name: 'Range 2',
key: 'range2',
source: 'range2',
hints: {
range: 2
}
},
{
name: 'Domain 1',
key: 'domain1',
source: 'domain1',
format: 'utc',
hints: {
domain: 1
}
},
{
name: 'Domain 2',
key: 'domain2',
source: 'domain2',
format: 'utc',
hints: {
domain: 2
}
}
];
beforeEach(function () {
mockTimeAPI.timeSystem.and.returnValue({
key: 'domain1'
});
metadata.forEach(function (metadatum) {
table.addColumn(mockTelemetryObject, metadatum);
});
});
it("populates columns", function () {
expect(table.columns.length).toBe(4);
});
it("Produces headers for each column based on metadata name", function () {
expect(table.headers.size).toBe(4);
Array.from(table.headers.values).forEach(function (header, i) {
expect(header).toEqual(metadata[i].name);
});
});
it("Provides a default configuration with all columns" +
" visible", function () {
var configuration = table.buildColumnConfiguration();
expect(configuration).toBeDefined();
expect(Object.keys(configuration).every(function (key) {
return configuration[key];
}));
});
it("Column configuration exposes persisted configuration", function () {
var tableConfig,
modelConfig = {
table: {
columns : {
'Range 1': false
}
}
};
mockModel.configuration = modelConfig;
tableConfig = table.buildColumnConfiguration();
expect(tableConfig).toBeDefined();
expect(tableConfig['Range 1']).toBe(false);
});
describe('retrieving row values', function () {
var datum,
rowValues;
beforeEach(function () {
datum = {
'range1': 10,
'range2': 20,
'domain1': 0,
'domain2': 1
};
var limitEvaluator = {
evaluate: function () {
return {
"cssClass": "alarm-class"
};
}
};
rowValues = table.getRowValues(mockTelemetryObject, limitEvaluator, datum);
});
it("Returns a value for every column", function () {
expect(rowValues['Range 1'].text).toEqual(10);
});
it("Applies appropriate css class if limit violated.", function () {
expect(rowValues['Range 1'].cssClass).toEqual("alarm-class");
});
});
});
});
}
);

View File

@@ -1,212 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
"../src/TelemetryCollection"
],
function (TelemetryCollection) {
describe("A telemetry collection", function () {
var collection;
var telemetryObjects;
var ms;
var integerTextMap = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE",
"SIX", "SEVEN", "EIGHT", "NINE", "TEN", "ELEVEN"];
beforeEach(function () {
telemetryObjects = [0,9,2,4,7,8,5,1,3,6].map(function (number) {
ms = number * 1000;
return {
timestamp: ms,
value: {
integer: number,
text: integerTextMap[number]
}
};
});
collection = new TelemetryCollection();
});
it("Sorts inserted telemetry by specified field",
function () {
collection.sort('value.integer');
collection.add(telemetryObjects);
expect(collection.telemetry[0].value.integer).toBe(0);
expect(collection.telemetry[1].value.integer).toBe(1);
expect(collection.telemetry[2].value.integer).toBe(2);
expect(collection.telemetry[3].value.integer).toBe(3);
collection.sort('value.text');
expect(collection.telemetry[0].value.text).toBe("EIGHT");
expect(collection.telemetry[1].value.text).toBe("FIVE");
expect(collection.telemetry[2].value.text).toBe("FOUR");
expect(collection.telemetry[3].value.text).toBe("NINE");
}
);
describe("on bounds change", function () {
var discardedCallback;
beforeEach(function () {
discardedCallback = jasmine.createSpy("discarded");
collection.on("discarded", discardedCallback);
collection.sort("timestamp");
collection.add(telemetryObjects);
collection.bounds({start: 5000, end: 8000});
});
it("emits an event indicating that telemetry has " +
"been discarded", function () {
expect(discardedCallback).toHaveBeenCalled();
});
it("discards telemetry data with a time stamp " +
"before specified start bound", function () {
var discarded = discardedCallback.calls.mostRecent().args[0];
// Expect 5 because as an optimization, the TelemetryCollection
// will not consider telemetry values that exceed the upper
// bounds. Arbitrary bounds changes in which the end bound is
// decreased is assumed to require a new historical query, and
// hence re-population of the collection anyway
expect(discarded.length).toBe(5);
expect(discarded[0].value.integer).toBe(0);
expect(discarded[1].value.integer).toBe(1);
expect(discarded[4].value.integer).toBe(4);
});
});
describe("when adding telemetry to a collection", function () {
var addedCallback;
beforeEach(function () {
collection.sort("timestamp");
collection.add(telemetryObjects);
addedCallback = jasmine.createSpy("added");
collection.on("added", addedCallback);
});
it("emits an event",
function () {
var addedObject = {
timestamp: 10000,
value: {
integer: 10,
text: integerTextMap[10]
}
};
collection.add([addedObject]);
expect(addedCallback).toHaveBeenCalledWith([addedObject]);
}
);
it("inserts in the correct order",
function () {
var addedObjectA = {
timestamp: 10000,
value: {
integer: 10,
text: integerTextMap[10]
}
};
var addedObjectB = {
timestamp: 11000,
value: {
integer: 11,
text: integerTextMap[11]
}
};
collection.add([addedObjectB, addedObjectA]);
expect(collection.telemetry[11]).toBe(addedObjectB);
}
);
it("maintains insertion order in the case of duplicate time stamps",
function () {
var addedObjectA = {
timestamp: 10000,
value: {
integer: 10,
text: integerTextMap[10]
}
};
var addedObjectB = {
timestamp: 10000,
value: {
integer: 11,
text: integerTextMap[11]
}
};
collection.add([addedObjectA, addedObjectB]);
expect(collection.telemetry[11]).toBe(addedObjectB);
}
);
});
describe("buffers telemetry", function () {
var addedObjectA;
var addedObjectB;
beforeEach(function () {
collection.sort("timestamp");
collection.add(telemetryObjects);
addedObjectA = {
timestamp: 10000,
value: {
integer: 10,
text: integerTextMap[10]
}
};
addedObjectB = {
timestamp: 11000,
value: {
integer: 11,
text: integerTextMap[11]
}
};
collection.bounds({start: 0, end: 10000});
collection.add([addedObjectA, addedObjectB]);
});
it("when it falls outside of bounds", function () {
expect(collection.highBuffer).toBeDefined();
expect(collection.highBuffer.length).toBe(1);
expect(collection.highBuffer[0]).toBe(addedObjectB);
});
it("and adds it to collection when it falls within bounds", function () {
expect(collection.telemetry.length).toBe(11);
collection.bounds({start: 0, end: 11000});
expect(collection.telemetry.length).toBe(12);
expect(collection.telemetry[11]).toBe(addedObjectB);
});
it("and removes it from the buffer when it falls within bounds", function () {
expect(collection.highBuffer.length).toBe(1);
collection.bounds({start: 0, end: 11000});
expect(collection.highBuffer.length).toBe(0);
});
});
});
}
);

View File

@@ -1,598 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
"zepto",
"moment",
"../../src/controllers/MCTTableController"
],
function ($, moment, MCTTableController) {
var MOCK_ELEMENT_TEMPLATE =
'<div><div class="l-view-section t-scrolling">' +
'<table class="sizing-table"><tbody></tbody></table>' +
'<table class="mct-table"><thead></thead></table>' +
'</div></div>';
describe('The MCTTable Controller', function () {
var controller,
mockScope,
watches,
mockWindow,
mockElement,
mockExportService,
mockConductor,
mockFormatService,
mockFormat;
function getCallback(target, event) {
return target.calls.all().filter(function (call) {
return call.args[0] === event;
})[0].args[1];
}
beforeEach(function () {
watches = {};
mockScope = jasmine.createSpyObj('scope', [
'$watch',
'$on',
'$watchCollection',
'$digest'
]);
mockScope.$watchCollection.and.callFake(function (event, callback) {
watches[event] = callback;
});
mockElement = $(MOCK_ELEMENT_TEMPLATE);
mockExportService = jasmine.createSpyObj('exportService', [
'exportCSV'
]);
mockConductor = jasmine.createSpyObj('conductor', [
'bounds',
'timeOfInterest',
'timeSystem',
'on',
'off'
]);
mockScope.displayHeaders = true;
mockWindow = jasmine.createSpyObj('$window', ['requestAnimationFrame']);
mockWindow.requestAnimationFrame.and.callFake(function (f) {
return f();
});
mockFormat = jasmine.createSpyObj('formatter', [
'parse',
'format'
]);
mockFormatService = jasmine.createSpyObj('formatService', [
'getFormat'
]);
mockFormatService.getFormat.and.returnValue(mockFormat);
controller = new MCTTableController(
mockScope,
mockWindow,
mockElement,
mockExportService,
mockFormatService,
{time: mockConductor}
);
spyOn(controller, 'setVisibleRows').and.callThrough();
});
it('Reacts to changes to filters, headers, and rows', function () {
expect(mockScope.$watchCollection).toHaveBeenCalledWith('filters', jasmine.any(Function));
expect(mockScope.$watch).toHaveBeenCalledWith('headers', jasmine.any(Function));
expect(mockScope.$watch).toHaveBeenCalledWith('rows', jasmine.any(Function));
});
it('unregisters listeners on destruction', function () {
expect(mockScope.$on).toHaveBeenCalledWith('$destroy', jasmine.any(Function));
getCallback(mockScope.$on, '$destroy')();
expect(mockConductor.off).toHaveBeenCalledWith('timeSystem', controller.changeTimeSystem);
expect(mockConductor.off).toHaveBeenCalledWith('timeOfInterest', controller.changeTimeOfInterest);
expect(mockConductor.off).toHaveBeenCalledWith('bounds', controller.changeBounds);
});
describe('The time of interest', function () {
var rowsAsc = [];
var rowsDesc = [];
beforeEach(function () {
rowsAsc = [
{
'col1': {'text': 'row1 col1 match'},
'col2': {'text': '2012-10-31 00:00:00.000Z'},
'col3': {'text': 'row1 col3'}
},
{
'col1': {'text': 'row2 col1 match'},
'col2': {'text': '2012-11-01 00:00:00.000Z'},
'col3': {'text': 'row2 col3'}
},
{
'col1': {'text': 'row3 col1'},
'col2': {'text': '2012-11-03 00:00:00.000Z'},
'col3': {'text': 'row3 col3'}
},
{
'col1': {'text': 'row3 col1'},
'col2': {'text': '2012-11-04 00:00:00.000Z'},
'col3': {'text': 'row3 col3'}
}
];
rowsDesc = [
{
'col1': {'text': 'row1 col1 match'},
'col2': {'text': '2012-11-02 00:00:00.000Z'},
'col3': {'text': 'row1 col3'}
},
{
'col1': {'text': 'row2 col1 match'},
'col2': {'text': '2012-11-01 00:00:00.000Z'},
'col3': {'text': 'row2 col3'}
},
{
'col1': {'text': 'row3 col1'},
'col2': {'text': '2012-10-30 00:00:00.000Z'},
'col3': {'text': 'row3 col3'}
},
{
'col1': {'text': 'row3 col1'},
'col2': {'text': '2012-10-29 00:00:00.000Z'},
'col3': {'text': 'row3 col3'}
}
];
mockScope.timeColumns = ['col2'];
mockScope.sortColumn = 'col2';
controller.toiFormatter = mockFormat;
});
it("is observed for changes", function () {
//Mock setting time columns
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
expect(mockConductor.on).toHaveBeenCalledWith('timeOfInterest',
jasmine.any(Function));
});
describe("causes corresponding row to be highlighted", function () {
it("when changed and rows sorted ascending", function () {
var testDate = "2012-11-02 00:00:00.000Z";
mockScope.rows = rowsAsc;
mockScope.displayRows = rowsAsc;
mockScope.sortDirection = 'asc';
var toi = moment.utc(testDate).valueOf();
mockFormat.parse.and.returnValue(toi);
mockFormat.format.and.returnValue(testDate);
//mock setting the timeColumns parameter
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
var toiCallback = getCallback(mockConductor.on, 'timeOfInterest');
toiCallback(toi);
expect(mockScope.toiRowIndex).toBe(2);
});
it("when changed and rows sorted descending", function () {
var testDate = "2012-10-31 00:00:00.000Z";
mockScope.rows = rowsDesc;
mockScope.displayRows = rowsDesc;
mockScope.sortDirection = 'desc';
var toi = moment.utc(testDate).valueOf();
mockFormat.parse.and.returnValue(toi);
mockFormat.format.and.returnValue(testDate);
//mock setting the timeColumns parameter
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
var toiCallback = getCallback(mockConductor.on, 'timeOfInterest');
toiCallback(toi);
expect(mockScope.toiRowIndex).toBe(2);
});
it("when rows are set and sorted ascending", function () {
var testDate = "2012-11-02 00:00:00.000Z";
mockScope.sortDirection = 'asc';
var toi = moment.utc(testDate).valueOf();
mockFormat.parse.and.returnValue(toi);
mockFormat.format.and.returnValue(testDate);
mockConductor.timeOfInterest.and.returnValue(toi);
//mock setting the timeColumns parameter
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
//Mock setting the rows on scope
var rowsCallback = getCallback(mockScope.$watch, 'rows');
var setRowsPromise = rowsCallback(rowsAsc);
return setRowsPromise.then(function () {
expect(mockScope.toiRowIndex).toBe(2);
});
});
});
});
describe('rows', function () {
var testRows = [];
beforeEach(function () {
testRows = [
{
'col1': {'text': 'row1 col1 match'},
'col2': {'text': 'def'},
'col3': {'text': 'row1 col3'}
},
{
'col1': {'text': 'row2 col1 match'},
'col2': {'text': 'abc'},
'col3': {'text': 'row2 col3'}
},
{
'col1': {'text': 'row3 col1'},
'col2': {'text': 'ghi'},
'col3': {'text': 'row3 col3'}
}
];
mockScope.rows = testRows;
});
it('Filters results based on filter input', function () {
var filters = {},
filteredRows;
mockScope.filters = filters;
filteredRows = controller.filterRows(testRows);
expect(filteredRows.length).toBe(3);
filters.col1 = 'row1';
filteredRows = controller.filterRows(testRows);
expect(filteredRows.length).toBe(1);
filters.col1 = 'match';
filteredRows = controller.filterRows(testRows);
expect(filteredRows.length).toBe(2);
});
it('Sets rows on scope when rows change', function () {
controller.setRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
expect(mockScope.displayRows).toEqual(testRows);
});
it('Supports adding rows individually', function () {
var addRowFunc = getCallback(mockScope.$on, 'add:rows'),
row4 = {
'col1': {'text': 'row3 col1'},
'col2': {'text': 'ghi'},
'col3': {'text': 'row3 col3'}
};
controller.setRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
testRows.push(row4);
addRowFunc(undefined, [row4]);
expect(mockScope.displayRows.length).toBe(4);
});
it('Supports removing rows individually', function () {
var removeRowFunc = getCallback(mockScope.$on, 'remove:rows');
controller.setRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
removeRowFunc(undefined, [testRows[2]]);
expect(mockScope.displayRows.length).toBe(2);
expect(controller.setVisibleRows).toHaveBeenCalled();
});
it("can be exported as CSV", function () {
controller.setRows(testRows);
controller.setHeaders(Object.keys(testRows[0]));
mockScope.exportAsCSV();
expect(mockExportService.exportCSV)
.toHaveBeenCalled();
mockExportService.exportCSV.calls.mostRecent().args[0]
.forEach(function (row, i) {
Object.keys(row).forEach(function (k) {
expect(row[k]).toEqual(
mockScope.displayRows[i][k].text
);
});
});
});
describe('sorting', function () {
var sortedRows;
beforeEach(function () {
sortedRows = [];
});
it('Sorts rows ascending', function () {
mockScope.sortColumn = 'col1';
mockScope.sortDirection = 'asc';
sortedRows = controller.sortRows(testRows);
expect(sortedRows[0].col1.text).toEqual('row1 col1 match');
expect(sortedRows[1].col1.text).toEqual('row2 col1' +
' match');
expect(sortedRows[2].col1.text).toEqual('row3 col1');
});
it('Sorts rows descending', function () {
mockScope.sortColumn = 'col1';
mockScope.sortDirection = 'desc';
sortedRows = controller.sortRows(testRows);
expect(sortedRows[0].col1.text).toEqual('row3 col1');
expect(sortedRows[1].col1.text).toEqual('row2 col1 match');
expect(sortedRows[2].col1.text).toEqual('row1 col1 match');
});
it('Sorts rows descending based on selected sort column', function () {
mockScope.sortColumn = 'col2';
mockScope.sortDirection = 'desc';
sortedRows = controller.sortRows(testRows);
expect(sortedRows[0].col2.text).toEqual('ghi');
expect(sortedRows[1].col2.text).toEqual('def');
expect(sortedRows[2].col2.text).toEqual('abc');
});
it('Allows sort column to be changed externally by ' +
'setting or changing sortBy attribute', function () {
mockScope.displayRows = testRows;
var sortByCB = getCallback(mockScope.$watch, 'defaultSort');
sortByCB('col2');
expect(mockScope.sortDirection).toEqual('asc');
expect(mockScope.displayRows[0].col2.text).toEqual('abc');
expect(mockScope.displayRows[1].col2.text).toEqual('def');
expect(mockScope.displayRows[2].col2.text).toEqual('ghi');
});
// https://github.com/nasa/openmct/issues/910
it('updates visible rows in scope', function () {
var oldRows;
mockScope.rows = testRows;
var setRowsPromise = controller.setRows(testRows);
oldRows = mockScope.visibleRows;
mockScope.toggleSort('col2');
return setRowsPromise.then(function () {
expect(mockScope.visibleRows).not.toEqual(oldRows);
});
});
it('correctly sorts rows of differing types', function () {
mockScope.sortColumn = 'col2';
mockScope.sortDirection = 'desc';
testRows.push({
'col1': {'text': 'row4 col1'},
'col2': {'text': '123'},
'col3': {'text': 'row4 col3'}
});
testRows.push({
'col1': {'text': 'row5 col1'},
'col2': {'text': '456'},
'col3': {'text': 'row5 col3'}
});
testRows.push({
'col1': {'text': 'row5 col1'},
'col2': {'text': ''},
'col3': {'text': 'row5 col3'}
});
sortedRows = controller.sortRows(testRows);
expect(sortedRows[0].col2.text).toEqual('ghi');
expect(sortedRows[1].col2.text).toEqual('def');
expect(sortedRows[2].col2.text).toEqual('abc');
expect(sortedRows[sortedRows.length - 3].col2.text).toEqual('456');
expect(sortedRows[sortedRows.length - 2].col2.text).toEqual('123');
expect(sortedRows[sortedRows.length - 1].col2.text).toEqual('');
});
describe('The sort comparator', function () {
it('Correctly sorts different data types', function () {
var val1 = "",
val2 = "1",
val3 = "2016-04-05 18:41:30.713Z",
val4 = "1.1",
val5 = "8.945520958175627e-13";
mockScope.sortDirection = "asc";
expect(controller.sortComparator(val1, val2)).toEqual(-1);
expect(controller.sortComparator(val3, val1)).toEqual(1);
expect(controller.sortComparator(val3, val2)).toEqual(1);
expect(controller.sortComparator(val4, val2)).toEqual(1);
expect(controller.sortComparator(val2, val5)).toEqual(1);
});
});
describe('Adding new rows', function () {
var row4,
row5,
row6;
beforeEach(function () {
row4 = {
'col1': {'text': 'row4 col1'},
'col2': {'text': 'xyz'},
'col3': {'text': 'row4 col3'}
};
row5 = {
'col1': {'text': 'row5 col1'},
'col2': {'text': 'aaa'},
'col3': {'text': 'row5 col3'}
};
row6 = {
'col1': {'text': 'row6 col1'},
'col2': {'text': 'ggg'},
'col3': {'text': 'row6 col3'}
};
});
it('Adds new rows at the correct sort position when' +
' sorted ', function () {
mockScope.sortColumn = 'col2';
mockScope.sortDirection = 'desc';
mockScope.displayRows = controller.sortRows(testRows.slice(0));
controller.addRows(undefined, [row4, row5, row6, row6]);
expect(mockScope.displayRows[0].col2.text).toEqual('xyz');
expect(mockScope.displayRows[6].col2.text).toEqual('aaa');
//Added a duplicate row
expect(mockScope.displayRows[2].col2.text).toEqual('ggg');
expect(mockScope.displayRows[3].col2.text).toEqual('ggg');
});
it('Inserts duplicate values for sort column in order received when sorted descending', function () {
mockScope.sortColumn = 'col2';
mockScope.sortDirection = 'desc';
mockScope.displayRows = controller.sortRows(testRows.slice(0));
var row6b = {
'col1': {'text': 'row6b col1'},
'col2': {'text': 'ggg'},
'col3': {'text': 'row6b col3'}
};
var row6c = {
'col1': {'text': 'row6c col1'},
'col2': {'text': 'ggg'},
'col3': {'text': 'row6c col3'}
};
controller.addRows(undefined, [row4, row5]);
controller.addRows(undefined, [row6, row6b, row6c]);
expect(mockScope.displayRows[0].col2.text).toEqual('xyz');
expect(mockScope.displayRows[7].col2.text).toEqual('aaa');
// Added duplicate rows
expect(mockScope.displayRows[2].col2.text).toEqual('ggg');
expect(mockScope.displayRows[3].col2.text).toEqual('ggg');
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
// Check that original order is maintained with dupes
expect(mockScope.displayRows[2].col3.text).toEqual('row6c col3');
expect(mockScope.displayRows[3].col3.text).toEqual('row6b col3');
expect(mockScope.displayRows[4].col3.text).toEqual('row6 col3');
});
it('Inserts duplicate values for sort column in order received when sorted ascending', function () {
mockScope.sortColumn = 'col2';
mockScope.sortDirection = 'asc';
mockScope.displayRows = controller.sortRows(testRows.slice(0));
var row6b = {
'col1': {'text': 'row6b col1'},
'col2': {'text': 'ggg'},
'col3': {'text': 'row6b col3'}
};
var row6c = {
'col1': {'text': 'row6c col1'},
'col2': {'text': 'ggg'},
'col3': {'text': 'row6c col3'}
};
controller.addRows(undefined, [row4, row5, row6]);
controller.addRows(undefined, [row6b, row6c]);
expect(mockScope.displayRows[0].col2.text).toEqual('aaa');
expect(mockScope.displayRows[7].col2.text).toEqual('xyz');
// Added duplicate rows
expect(mockScope.displayRows[3].col2.text).toEqual('ggg');
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
expect(mockScope.displayRows[5].col2.text).toEqual('ggg');
// Check that original order is maintained with dupes
expect(mockScope.displayRows[3].col3.text).toEqual('row6 col3');
expect(mockScope.displayRows[4].col3.text).toEqual('row6b col3');
expect(mockScope.displayRows[5].col3.text).toEqual('row6c col3');
});
it('Adds new rows at the correct sort position when' +
' sorted and filtered', function () {
mockScope.sortColumn = 'col2';
mockScope.sortDirection = 'desc';
mockScope.filters = {'col2': 'a'};//Include only
// rows with 'a'
mockScope.displayRows = controller.sortRows(testRows.slice(0));
mockScope.displayRows = controller.filterRows(testRows);
controller.addRows(undefined, [row5]);
expect(mockScope.displayRows.length).toBe(2);
expect(mockScope.displayRows[1].col2.text).toEqual('aaa');
controller.addRows(undefined, [row6]);
expect(mockScope.displayRows.length).toBe(2);
//Row was not added because does not match filter
});
it('Adds new rows at the correct sort position when' +
' not sorted ', function () {
mockScope.sortColumn = undefined;
mockScope.sortDirection = undefined;
mockScope.filters = {};
mockScope.displayRows = testRows.slice(0);
controller.addRows(undefined, [row5]);
expect(mockScope.displayRows[3].col2.text).toEqual('aaa');
controller.addRows(undefined, [row6]);
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
});
it('Resizes columns if length of any columns in new' +
' row exceeds corresponding existing column', function () {
var row7 = {
'col1': {'text': 'row6 col1'},
'col2': {'text': 'some longer string'},
'col3': {'text': 'row6 col3'}
};
mockScope.sortColumn = undefined;
mockScope.sortDirection = undefined;
mockScope.filters = {};
mockScope.displayRows = testRows.slice(0);
controller.addRows(undefined, [row7]);
expect(controller.$scope.sizingRow.col2).toEqual({text: 'some longer string'});
});
});
});
});
});
});

View File

@@ -1,113 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
"../../src/controllers/TableOptionsController"
],
function (TableOptionsController) {
describe('The Table Options Controller', function () {
var mockDomainObject,
mockCapability,
controller,
mockScope;
beforeEach(function () {
mockCapability = jasmine.createSpyObj('mutationCapability', [
'listen'
]);
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getCapability',
'getModel'
]);
mockDomainObject.getCapability.and.returnValue(mockCapability);
mockDomainObject.getModel.and.returnValue({});
mockScope = jasmine.createSpyObj('scope', [
'$watchCollection',
'$watch',
'$on'
]);
mockScope.domainObject = mockDomainObject;
controller = new TableOptionsController(mockScope);
});
it('Listens for changing domain object', function () {
expect(mockScope.$watch).toHaveBeenCalledWith('domainObject', jasmine.any(Function));
});
it('On destruction of controller, destroys listeners', function () {
var unlistenFunc = jasmine.createSpy("unlisten");
controller.listeners.push(unlistenFunc);
expect(mockScope.$on).toHaveBeenCalledWith('$destroy', jasmine.any(Function));
mockScope.$on.calls.mostRecent().args[1]();
expect(unlistenFunc).toHaveBeenCalled();
});
it('Registers a listener for mutation events on the object', function () {
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
expect(mockCapability.listen).toHaveBeenCalled();
});
it('Listens for changes to object composition and updates' +
' options accordingly', function () {
expect(mockScope.$watchCollection).toHaveBeenCalledWith('configuration.table.columns', jasmine.any(Function));
});
describe('Populates scope with a form definition based on provided' +
' column configuration', function () {
var mockModel;
beforeEach(function () {
mockModel = {
configuration: {
table: {
columns: {
'column1': true,
'column2': true,
'column3': false,
'column4': true
}
}
}
};
controller.populateForm(mockModel);
});
it('creates form on scope', function () {
expect(mockScope.columnsForm).toBeDefined();
expect(mockScope.columnsForm.sections[0]).toBeDefined();
expect(mockScope.columnsForm.sections[0].rows).toBeDefined();
expect(mockScope.columnsForm.sections[0].rows.length).toBe(4);
});
it('presents columns as checkboxes', function () {
expect(mockScope.columnsForm.sections[0].rows.every(function (row) {
return row.control === 'checkbox';
})).toBe(true);
});
});
});
});

View File

@@ -1,417 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'../../src/controllers/TelemetryTableController',
'../../../../../src/api/objects/object-utils',
'lodash'
],
function (TelemetryTableController, objectUtils, _) {
describe('The TelemetryTableController', function () {
var controller,
mockScope,
mockTimeout,
mockConductor,
mockAPI,
mockDomainObject,
mockTelemetryAPI,
mockObjectAPI,
mockCompositionAPI,
unobserve,
mockBounds;
function getCallback(target, event) {
return target.calls.all().filter(function (call) {
return call.args[0] === event;
})[0].args[1];
}
beforeEach(function () {
mockBounds = {
start: 0,
end: 10
};
mockConductor = jasmine.createSpyObj("conductor", [
"bounds",
"clock",
"on",
"off",
"timeSystem"
]);
mockConductor.bounds.and.returnValue(mockBounds);
mockConductor.clock.and.returnValue(undefined);
mockDomainObject = jasmine.createSpyObj("domainObject", [
"getModel",
"getId",
"useCapability",
"hasCapability"
]);
mockDomainObject.getModel.and.returnValue({});
mockDomainObject.getId.and.returnValue("mockId");
mockDomainObject.useCapability.and.returnValue(true);
mockCompositionAPI = jasmine.createSpyObj("compositionAPI", [
"get"
]);
mockObjectAPI = jasmine.createSpyObj("objectAPI", [
"observe",
"makeKeyString"
]);
unobserve = jasmine.createSpy("unobserve");
mockObjectAPI.observe.and.returnValue(unobserve);
mockScope = jasmine.createSpyObj("scope", [
"$on",
"$watch",
"$broadcast"
]);
mockScope.domainObject = mockDomainObject;
mockTelemetryAPI = jasmine.createSpyObj("telemetryAPI", [
"isTelemetryObject",
"subscribe",
"getMetadata",
"commonValuesForHints",
"request",
"limitEvaluator",
"getValueFormatter"
]);
mockTelemetryAPI.commonValuesForHints.and.returnValue([]);
mockTelemetryAPI.request.and.returnValue(Promise.resolve([]));
mockTelemetryAPI.getMetadata.and.returnValue({
values: function () {
return [];
}
});
mockTelemetryAPI.getValueFormatter.and.callFake(function (metadata) {
var formatter = jasmine.createSpyObj(
'telemetryFormatter:' + metadata.key,
[
'format',
'parse'
]
);
var getter = function (datum) {
return datum[metadata.key];
};
formatter.format.and.callFake(getter);
formatter.parse.and.callFake(getter);
return formatter;
});
mockTelemetryAPI.isTelemetryObject.and.returnValue(false);
mockTimeout = jasmine.createSpy("timeout");
mockTimeout.and.returnValue(1); // Return something
mockTimeout.cancel = jasmine.createSpy("cancel");
mockAPI = {
time: mockConductor,
objects: mockObjectAPI,
telemetry: mockTelemetryAPI,
composition: mockCompositionAPI
};
controller = new TelemetryTableController(mockScope, mockTimeout, mockAPI);
});
describe('listens for', function () {
beforeEach(function () {
controller.registerChangeListeners();
});
it('object mutation', function () {
var calledObject = mockObjectAPI.observe.calls.mostRecent().args[0];
expect(mockObjectAPI.observe).toHaveBeenCalled();
expect(calledObject.identifier.key).toEqual(mockDomainObject.getId());
});
it('conductor changes', function () {
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", jasmine.any(Function));
expect(mockConductor.on).toHaveBeenCalledWith("bounds", jasmine.any(Function));
expect(mockConductor.on).toHaveBeenCalledWith("clock", jasmine.any(Function));
});
});
describe('deregisters all listeners on scope destruction', function () {
var timeSystemListener,
boundsListener,
clockListener;
beforeEach(function () {
controller.registerChangeListeners();
timeSystemListener = getCallback(mockConductor.on, "timeSystem");
boundsListener = getCallback(mockConductor.on, "bounds");
clockListener = getCallback(mockConductor.on, "clock");
var destroy = getCallback(mockScope.$on, "$destroy");
destroy();
});
it('object mutation', function () {
expect(unobserve).toHaveBeenCalled();
});
it('conductor changes', function () {
expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", timeSystemListener);
expect(mockConductor.off).toHaveBeenCalledWith("bounds", boundsListener);
expect(mockConductor.off).toHaveBeenCalledWith("clock", clockListener);
});
});
describe ('when getting telemetry', function () {
var mockComposition,
mockTelemetryObject,
mockChildren,
unsubscribe;
beforeEach(function () {
mockComposition = jasmine.createSpyObj("composition", [
"load"
]);
mockTelemetryObject = {};
mockTelemetryObject.identifier = {
key: "mockTelemetryObject"
};
unsubscribe = jasmine.createSpy("unsubscribe");
mockTelemetryAPI.subscribe.and.returnValue(unsubscribe);
mockChildren = [mockTelemetryObject];
mockComposition.load.and.returnValue(Promise.resolve(mockChildren));
mockCompositionAPI.get.and.returnValue(mockComposition);
mockTelemetryAPI.isTelemetryObject.and.callFake(function (obj) {
return obj.identifier.key === mockTelemetryObject.identifier.key;
});
});
it('fetches historical data for the time period specified by the conductor bounds', function () {
return controller.getData().then(function () {
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(mockTelemetryObject, mockBounds);
});
});
it('unsubscribes on view destruction', function () {
return controller.getData().then(function () {
var destroy = getCallback(mockScope.$on, "$destroy");
destroy();
expect(unsubscribe).toHaveBeenCalled();
});
});
it('fetches historical data for the time period specified by the conductor bounds', function () {
return controller.getData().then(function () {
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(mockTelemetryObject, mockBounds);
});
});
it('fetches data for, and subscribes to parent object if it is a telemetry object', function () {
return controller.getData().then(function () {
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(mockTelemetryObject, jasmine.any(Function), {});
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(mockTelemetryObject, jasmine.any(Object));
});
});
it('fetches data for, and subscribes to parent object if it is a telemetry object', function () {
return controller.getData().then(function () {
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(mockTelemetryObject, jasmine.any(Function), {});
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(mockTelemetryObject, jasmine.any(Object));
});
});
it('fetches data for, and subscribes to any composees that are telemetry objects if parent is not', function () {
mockChildren = [
{name: "child 1"}
];
var mockTelemetryChildren = [
{name: "child 2"},
{name: "child 3"},
{name: "child 4"}
];
mockChildren = mockChildren.concat(mockTelemetryChildren);
mockComposition.load.and.returnValue(Promise.resolve(mockChildren));
mockTelemetryAPI.isTelemetryObject.and.callFake(function (object) {
if (object === mockTelemetryObject) {
return false;
} else {
return mockTelemetryChildren.indexOf(object) !== -1;
}
});
return controller.getData().then(function () {
mockTelemetryChildren.forEach(function (child) {
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(child, jasmine.any(Function), {});
});
mockTelemetryChildren.forEach(function (child) {
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(child, jasmine.any(Object));
});
expect(mockTelemetryAPI.subscribe).not.toHaveBeenCalledWith(mockChildren[0], jasmine.any(Function), {});
expect(mockTelemetryAPI.subscribe).not.toHaveBeenCalledWith(mockTelemetryObject[0], jasmine.any(Function), {});
});
});
});
it('When in real-time mode, enables auto-scroll', function () {
controller.registerChangeListeners();
var clockCallback = getCallback(mockConductor.on, "clock");
//Confirm pre-condition
expect(mockScope.autoScroll).toBeFalsy();
//Mock setting the a clock in the Time API
clockCallback({});
expect(mockScope.autoScroll).toBe(true);
});
describe('populates table columns', function () {
var allMetadata;
var mockTimeSystem1;
var mockTimeSystem2;
beforeEach(function () {
allMetadata = [{
key: "column1",
name: "Column 1",
hints: {
domain: 1
}
}, {
key: "column2",
name: "Column 2",
hints: {
domain: 2
}
}, {
key: "column3",
name: "Column 3",
hints: {}
}];
mockTimeSystem1 = {
key: "column1"
};
mockTimeSystem2 = {
key: "column2"
};
mockConductor.timeSystem.and.returnValue(mockTimeSystem1);
mockTelemetryAPI.getMetadata.and.returnValue({
values: function () {
return allMetadata;
}
});
controller.loadColumns([mockDomainObject]);
});
it('based on metadata for given objects', function () {
expect(mockScope.headers).toBeDefined();
expect(mockScope.headers.length).toBeGreaterThan(0);
expect(mockScope.headers.indexOf(allMetadata[0].name)).not.toBe(-1);
expect(mockScope.headers.indexOf(allMetadata[1].name)).not.toBe(-1);
expect(mockScope.headers.indexOf(allMetadata[2].name)).not.toBe(-1);
});
it('and sorts by column matching time system', function () {
expect(mockScope.defaultSort).toEqual("Column 1");
mockConductor.timeSystem.and.returnValue(mockTimeSystem2);
controller.sortByTimeSystem();
expect(mockScope.defaultSort).toEqual("Column 2");
});
it('batches processing of rows for performance when receiving historical telemetry', function () {
var mockHistoricalData = [
{
"column1": 1,
"column2": 2,
"column3": 3
},{
"column1": 4,
"column2": 5,
"column3": 6
}, {
"column1": 7,
"column2": 8,
"column3": 9
}
];
controller.batchSize = 2;
mockTelemetryAPI.request.and.returnValue(Promise.resolve(mockHistoricalData));
controller.getHistoricalData([mockDomainObject]);
return new Promise(function (resolve) {
mockTimeout.and.callFake(function () {
resolve();
});
}).then(function () {
mockTimeout.calls.mostRecent().args[0]();
expect(mockTimeout.calls.count()).toBe(2);
mockTimeout.calls.mostRecent().args[0]();
expect(mockScope.rows.length).toBe(3);
});
});
});
it('Removes telemetry rows from table when they fall out of bounds', function () {
var discardedRows = [
{"column1": "value 1"},
{"column2": "value 2"},
{"column3": "value 3"}
];
spyOn(controller.telemetry, "on").and.callThrough();
controller.registerChangeListeners();
expect(controller.telemetry.on).toHaveBeenCalledWith("discarded", jasmine.any(Function));
var onDiscard = getCallback(controller.telemetry.on, "discarded");
onDiscard(discardedRows);
expect(mockScope.$broadcast).toHaveBeenCalledWith("remove:rows", discardedRows);
});
describe('when telemetry is added', function () {
var testRows;
beforeEach(function () {
testRows = [{ a: 0 }, { a: 1 }, { a: 2 }];
controller.registerChangeListeners();
controller.telemetry.add(testRows);
});
it("Adds the rows to the MCTTable directive", function () {
expect(mockScope.$broadcast).toHaveBeenCalledWith("add:rows", testRows);
});
});
});
});

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
"text!./res/templates/deprecated-timeline-message.html",
"./res/templates/deprecated-timeline-message.html",
'legacyRegistry'
], function (
deprecatedTimelineMessage,

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