Compare commits
65 Commits
summary-wi
...
telemetry-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b939eb2fbe | ||
|
|
1afecdc82c | ||
|
|
b9cda6985e | ||
|
|
401b7e3f19 | ||
|
|
c9e9845172 | ||
|
|
abbba38eac | ||
|
|
db856251da | ||
|
|
e1566e448d | ||
|
|
d9aae0700c | ||
|
|
8703f363b8 | ||
|
|
26210eaa50 | ||
|
|
a4a1cb5e05 | ||
|
|
9570f2f7a1 | ||
|
|
eb4ded39b3 | ||
|
|
5c29726cc0 | ||
|
|
5c207c3fe0 | ||
|
|
eba1dd1a4e | ||
|
|
570edc0dec | ||
|
|
ad6bcd4ef8 | ||
|
|
aedbbbbf75 | ||
|
|
3caaf00483 | ||
|
|
971eda4d88 | ||
|
|
090d216517 | ||
|
|
7deb3cd025 | ||
|
|
d42d7ae68d | ||
|
|
06779e6cd9 | ||
|
|
7f43c0bf1a | ||
|
|
68e6e3c121 | ||
|
|
7e7f39db2d | ||
|
|
b6e0fca828 | ||
|
|
ffc5896e5a | ||
|
|
fd6ebd152f | ||
|
|
7a5c1c0e1f | ||
|
|
2f7e1e3f1a | ||
|
|
d73746b51b | ||
|
|
2df54af019 | ||
|
|
586269f761 | ||
|
|
e536ab34d7 | ||
|
|
e15002dd72 | ||
|
|
453cf3ad6a | ||
|
|
5c46e48bde | ||
|
|
bfa3bbcdc7 | ||
|
|
2baf3f8bb0 | ||
|
|
868ea9362f | ||
|
|
10ac13ac5c | ||
|
|
138cb199bb | ||
|
|
374c363a78 | ||
|
|
d69106ff2c | ||
|
|
e9cb5cd639 | ||
|
|
bc7d92ee0d | ||
|
|
1658b17c56 | ||
|
|
78f49784a0 | ||
|
|
39cf0528ca | ||
|
|
3d12f7312b | ||
|
|
14592d1c3e | ||
|
|
8754c438cc | ||
|
|
1419c75503 | ||
|
|
ca8cad0a74 | ||
|
|
7f9fd5c705 | ||
|
|
a3a55d3b48 | ||
|
|
e66f818996 | ||
|
|
ae5ef33487 | ||
|
|
59c5430579 | ||
|
|
1e1a2443d5 | ||
|
|
d65e1e604e |
@@ -78,8 +78,8 @@ define([
|
||||
return this.workerInterface.request(workerRequest);
|
||||
};
|
||||
|
||||
GeneratorProvider.prototype.subscribe = function (domainObject, callback, request) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||
GeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, {});
|
||||
return this.workerInterface.subscribe(workerRequest, callback);
|
||||
};
|
||||
|
||||
|
||||
@@ -127,7 +127,8 @@
|
||||
{ 'meaning': 'Timer object', 'cssClass': 'icon-timer', 'cssContent': 'e1127', 'htmlEntity': '&#xe1127' },
|
||||
{ 'meaning': 'Data Topic', 'cssClass': 'icon-topic', 'cssContent': 'e1128', 'htmlEntity': '&#xe1128' },
|
||||
{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&#xe1129' },
|
||||
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' }
|
||||
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' },
|
||||
{ 'meaning': 'Notebook object', 'cssClass': 'icon-notebook', 'cssContent': 'e1131', 'htmlEntity': '&#xe1131' }
|
||||
];
|
||||
"></div>
|
||||
|
||||
|
||||
28
gulpfile.js
28
gulpfile.js
@@ -46,9 +46,22 @@ var gulp = require('gulp'),
|
||||
name: 'bower_components/almond/almond.js',
|
||||
include: paths.main.replace('.js', ''),
|
||||
wrap: {
|
||||
startFile: "src/start.frag",
|
||||
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
|
||||
},
|
||||
@@ -58,14 +71,6 @@ var gulp = require('gulp'),
|
||||
},
|
||||
sass: {
|
||||
sourceComments: true
|
||||
},
|
||||
replace: {
|
||||
variables: {
|
||||
version: project.version,
|
||||
timestamp: moment.utc(Date.now()).format(),
|
||||
revision: fs.existsSync('.git') ? git.long() : 'Unknown',
|
||||
branch: fs.existsSync('.git') ? git.branch() : 'Unknown'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,16 +81,11 @@ if (process.env.NODE_ENV === 'development') {
|
||||
|
||||
gulp.task('scripts', function () {
|
||||
var requirejsOptimize = require('gulp-requirejs-optimize');
|
||||
var replace = require('gulp-replace-task');
|
||||
var header = require('gulp-header');
|
||||
var comment = fs.readFileSync('src/about.frag');
|
||||
|
||||
return gulp.src(paths.main)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(requirejsOptimize(options.requirejsOptimize))
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(replace(options.replace))
|
||||
.pipe(header(comment, options.replace.variables))
|
||||
.pipe(gulp.dest(paths.dist));
|
||||
});
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
openmct.install(openmct.plugins.ExampleImagery());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.install(openmct.plugins.ImportExport());
|
||||
openmct.install(openmct.plugins.TelemetryMean());
|
||||
openmct.install(openmct.plugins.Conductor({
|
||||
menuOptions: [
|
||||
{
|
||||
|
||||
11
openmct.js
11
openmct.js
@@ -19,7 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global requirejs*/
|
||||
/*global requirejs,BUILD_CONSTANTS*/
|
||||
|
||||
requirejs.config({
|
||||
"paths": {
|
||||
@@ -91,12 +91,17 @@ requirejs.config({
|
||||
define([
|
||||
'./platform/framework/src/Main',
|
||||
'./src/defaultRegistry',
|
||||
'./src/MCT'
|
||||
], function (Main, defaultRegistry, MCT) {
|
||||
'./src/MCT',
|
||||
'./src/plugins/buildInfo/plugin'
|
||||
], function (Main, defaultRegistry, MCT, buildInfo) {
|
||||
var openmct = new MCT();
|
||||
|
||||
openmct.legacyRegistry = defaultRegistry;
|
||||
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
openmct.install(buildInfo(BUILD_CONSTANTS));
|
||||
}
|
||||
|
||||
openmct.on('start', function () {
|
||||
return new Main().run(defaultRegistry);
|
||||
});
|
||||
|
||||
@@ -22,12 +22,10 @@
|
||||
"git-rev-sync": "^1.4.0",
|
||||
"glob": ">= 3.0.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-header": "^1.8.8",
|
||||
"gulp-jscs": "^3.0.2",
|
||||
"gulp-jshint": "^2.0.0",
|
||||
"gulp-jshint-html-reporter": "^0.1.3",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace-task": "^0.11.0",
|
||||
"gulp-requirejs-optimize": "^0.3.1",
|
||||
"gulp-sass": "^2.2.0",
|
||||
"gulp-sourcemaps": "^1.6.0",
|
||||
|
||||
@@ -26,6 +26,7 @@ define([
|
||||
"./src/InspectorPaneController",
|
||||
"./src/BrowseObjectController",
|
||||
"./src/MenuArrowController",
|
||||
"./src/ObjectHeaderController",
|
||||
"./src/navigation/NavigationService",
|
||||
"./src/navigation/NavigateAction",
|
||||
"./src/navigation/OrphanNavigationHandler",
|
||||
@@ -36,6 +37,7 @@ define([
|
||||
"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",
|
||||
@@ -48,6 +50,7 @@ define([
|
||||
InspectorPaneController,
|
||||
BrowseObjectController,
|
||||
MenuArrowController,
|
||||
ObjectHeaderController,
|
||||
NavigationService,
|
||||
NavigateAction,
|
||||
OrphanNavigationHandler,
|
||||
@@ -58,6 +61,7 @@ define([
|
||||
browseObjectTemplate,
|
||||
gridItemTemplate,
|
||||
objectHeaderTemplate,
|
||||
objectHeaderFrameTemplate,
|
||||
menuArrowTemplate,
|
||||
backArrowTemplate,
|
||||
itemsTemplate,
|
||||
@@ -140,6 +144,13 @@ define([
|
||||
"$location",
|
||||
"$attrs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "ObjectHeaderController",
|
||||
"implementation": ObjectHeaderController,
|
||||
"depends": [
|
||||
"$scope"
|
||||
]
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
@@ -173,6 +184,13 @@ define([
|
||||
"type"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "object-header-frame",
|
||||
"template": objectHeaderFrameTemplate,
|
||||
"uses": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "menu-arrow",
|
||||
"template": menuArrowTemplate,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
||||
<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
|
||||
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
||||
<span class='title-label flex-elem holder flex-can-shrink s-input-inline'>{{model.name}}</span>
|
||||
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
|
||||
<mct-representation
|
||||
key="'menu-arrow'"
|
||||
mct-object='domainObject'
|
||||
class="flex-elem context-available-w"></mct-representation>
|
||||
</span>
|
||||
@@ -20,9 +20,13 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
||||
<span class="l-elem-wrapper l-flex-row flex-elem grows">
|
||||
<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
|
||||
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
||||
<span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span>
|
||||
<span ng-attr-contenteditable="{{ controller.editable ? true : undefined }}"
|
||||
class='title-label flex-elem holder flex-can-shrink s-input-inline'
|
||||
ng-click="controller.edit()"
|
||||
ng-blur="controller.updateName($event)"
|
||||
ng-keypress="controller.updateName($event)">{{model.name}}</span>
|
||||
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
|
||||
<mct-representation
|
||||
key="'menu-arrow'"
|
||||
|
||||
78
platform/commonUI/browse/src/ObjectHeaderController.js
Normal file
78
platform/commonUI/browse/src/ObjectHeaderController.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Controller to provide the ability to inline edit an object name.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/commonUI/browse
|
||||
*/
|
||||
function ObjectHeaderController($scope) {
|
||||
this.$scope = $scope;
|
||||
this.domainObject = $scope.domainObject;
|
||||
this.editable = this.allowEdit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the object name on blur and enter keypress events.
|
||||
*
|
||||
* @param event the mouse event
|
||||
*/
|
||||
ObjectHeaderController.prototype.updateName = function (event) {
|
||||
if (event && (event.type === 'blur' || event.which === 13)) {
|
||||
var name = event.currentTarget.innerHTML;
|
||||
|
||||
if (name.length === 0) {
|
||||
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
|
||||
event.currentTarget.innerHTML = name;
|
||||
}
|
||||
|
||||
if (name !== this.$scope.domainObject.model.name) {
|
||||
this.domainObject.getCapability('mutation').mutate(function (model) {
|
||||
model.name = name;
|
||||
});
|
||||
}
|
||||
|
||||
if (event.which === 13) {
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the domain object is editable.
|
||||
*
|
||||
* @private
|
||||
* @return true if object is editable
|
||||
*/
|
||||
ObjectHeaderController.prototype.allowEdit = function () {
|
||||
var type = this.domainObject && this.domainObject.getCapability('type');
|
||||
return !!(type && type.hasFeature('creation'));
|
||||
};
|
||||
|
||||
return ObjectHeaderController;
|
||||
}
|
||||
);
|
||||
132
platform/commonUI/browse/test/ObjectHeaderControllerSpec.js
Normal file
132
platform/commonUI/browse/test/ObjectHeaderControllerSpec.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../src/ObjectHeaderController"],
|
||||
function (ObjectHeaderController) {
|
||||
|
||||
describe("The object header controller", function () {
|
||||
var mockScope,
|
||||
mockDomainObject,
|
||||
mockCapabilities,
|
||||
mockMutationCapability,
|
||||
mockTypeCapability,
|
||||
mockEvent,
|
||||
mockCurrentTarget,
|
||||
controller;
|
||||
|
||||
beforeEach(function () {
|
||||
mockMutationCapability = jasmine.createSpyObj("mutation", ["mutate"]);
|
||||
mockTypeCapability = jasmine.createSpyObj("type", ["typeDef", "hasFeature"]);
|
||||
mockTypeCapability.typeDef = { name: ""};
|
||||
mockTypeCapability.hasFeature.andCallFake(function (feature) {
|
||||
return feature === 'creation';
|
||||
});
|
||||
|
||||
mockCapabilities = {
|
||||
mutation: mockMutationCapability,
|
||||
type: mockTypeCapability
|
||||
};
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "model"]);
|
||||
mockDomainObject.model = {name: "Test name"};
|
||||
mockDomainObject.getCapability.andCallFake(function (key) {
|
||||
return mockCapabilities[key];
|
||||
});
|
||||
|
||||
mockScope = {
|
||||
domainObject: mockDomainObject
|
||||
};
|
||||
|
||||
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "innerHTML"]);
|
||||
mockCurrentTarget.blur.andReturn(mockCurrentTarget);
|
||||
|
||||
mockEvent = {
|
||||
which: {},
|
||||
type: {},
|
||||
currentTarget: mockCurrentTarget
|
||||
};
|
||||
|
||||
controller = new ObjectHeaderController(mockScope);
|
||||
});
|
||||
|
||||
it("updates the model with new name on blur", function () {
|
||||
mockEvent.type = "blur";
|
||||
mockCurrentTarget.innerHTML = "New name";
|
||||
controller.updateName(mockEvent);
|
||||
|
||||
expect(mockMutationCapability.mutate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("updates the model with a default for blank names", function () {
|
||||
mockEvent.type = "blur";
|
||||
mockCurrentTarget.innerHTML = "";
|
||||
controller.updateName(mockEvent);
|
||||
|
||||
expect(mockCurrentTarget.innerHTML.length).not.toEqual(0);
|
||||
expect(mockMutationCapability.mutate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not update the model if the same name", function () {
|
||||
mockEvent.type = "blur";
|
||||
mockCurrentTarget.innerHTML = mockDomainObject.model.name;
|
||||
controller.updateName(mockEvent);
|
||||
|
||||
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("updates the model on enter keypress event only", function () {
|
||||
mockCurrentTarget.innerHTML = "New name";
|
||||
controller.updateName(mockEvent);
|
||||
|
||||
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
|
||||
|
||||
mockEvent.which = 13;
|
||||
controller.updateName(mockEvent);
|
||||
|
||||
expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function));
|
||||
|
||||
mockMutationCapability.mutate.mostRecentCall.args[0](mockDomainObject.model);
|
||||
|
||||
expect(mockDomainObject.model.name).toBe("New name");
|
||||
});
|
||||
|
||||
it("blurs the field on enter key press", function () {
|
||||
mockEvent.which = 13;
|
||||
controller.updateName(mockEvent);
|
||||
|
||||
expect(mockEvent.currentTarget.blur).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows editting name when object is creatable", function () {
|
||||
expect(controller.allowEdit()).toBe(true);
|
||||
});
|
||||
|
||||
it("disallows editting name when object is non-creatable", function () {
|
||||
mockTypeCapability.hasFeature.andReturn(false);
|
||||
|
||||
expect(controller.allowEdit()).toBe(false);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="s-menu-button major create-button" ng-click="createController.toggle()">
|
||||
<span class="title-label">Create</span>
|
||||
</div>
|
||||
<div class="menu super-menu" ng-show="createController.isActive()">
|
||||
<div class="menu super-menu l-create-menu" ng-show="createController.isActive()">
|
||||
<mct-representation mct-object="domainObject" key="'create-menu'">
|
||||
</mct-representation>
|
||||
</div>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="contents" ng-controller="CreateMenuController">
|
||||
<div class="pane left menu-items">
|
||||
<div class="w-menu" ng-controller="CreateMenuController">
|
||||
<div class="col menu-items">
|
||||
<ul>
|
||||
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
|
||||
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
|
||||
@@ -31,13 +31,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pane right menu-item-description">
|
||||
<div class="col menu-item-description">
|
||||
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
|
||||
<div class="desc-area title">
|
||||
{{representation.activeMetadata.name}}
|
||||
</div>
|
||||
<div class="desc-area description">
|
||||
{{representation.activeMetadata.description}}
|
||||
<div class="w-title-desc">
|
||||
<div class="desc-area title">
|
||||
{{representation.activeMetadata.name}}
|
||||
</div>
|
||||
<div class="desc-area description">
|
||||
{{representation.activeMetadata.description}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,6 +80,12 @@ define(
|
||||
return closeEditor();
|
||||
}
|
||||
|
||||
function resolveWith (object) {
|
||||
return function () {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
newModel.location = this.parent.getId();
|
||||
newObject = this.parent.useCapability('instantiation', newModel);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"metadata": {
|
||||
"name": "openmct-symbols-16px",
|
||||
"lastOpened": 0,
|
||||
"created": 1505151140023
|
||||
"created": 1506973656040
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@@ -899,6 +899,14 @@
|
||||
"prevSize": 24,
|
||||
"code": 921904,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 139,
|
||||
"id": 117,
|
||||
"name": "icon-notebook",
|
||||
"prevSize": 24,
|
||||
"code": 921905,
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -3524,6 +3532,29 @@
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 117,
|
||||
"paths": [
|
||||
"M896 110.8c0-79.8-55.4-127.4-123-105.4l-773 250.6h896v-145.2z",
|
||||
"M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM832 832h-384v-320h384v320z"
|
||||
],
|
||||
"attrs": [
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-notebook"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{},
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"colorThemes": [
|
||||
|
||||
Binary file not shown.
@@ -118,4 +118,5 @@
|
||||
<glyph unicode="󡄨" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
|
||||
<glyph unicode="󡄩" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
|
||||
<glyph unicode="󡄰" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
|
||||
<glyph unicode="󡄱" glyph-name="icon-notebook" d="M896 849.2c0 79.8-55.4 127.4-123 105.4l-773-250.6h896v145.2zM896 640h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM832 128h-384v320h384v-320z" />
|
||||
</font></defs></svg>
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Binary file not shown.
@@ -137,6 +137,11 @@
|
||||
min-height: 0;
|
||||
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
|
||||
}
|
||||
&.l-flex-accordion .flex-accordion-holder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//overflow: hidden !important;
|
||||
}
|
||||
.flex-container { @include flex-direction(column); }
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,9 @@ $bubbleMaxW: 300px;
|
||||
$reqSymbolW: 15px;
|
||||
$reqSymbolM: $interiorMargin * 2;
|
||||
$reqSymbolFontSize: 0.75em;
|
||||
$inputTextP: 3px 5px;
|
||||
$inputTextPTopBtm: 3px;
|
||||
$inputTextPLeftRight: 5px;
|
||||
$inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
|
||||
/*************** Wait Spinner Defaults */
|
||||
$waitSpinnerD: 32px;
|
||||
$waitSpinnerTreeD: 20px;
|
||||
|
||||
@@ -146,6 +146,7 @@ $glyph-icon-timer: '\e1127';
|
||||
$glyph-icon-topic: '\e1128';
|
||||
$glyph-icon-box-with-dashed-lines: '\e1129';
|
||||
$glyph-icon-summary-widget: '\e1130';
|
||||
$glyph-icon-notebook: '\e1131';
|
||||
|
||||
/************************** 16 PX CLASSES */
|
||||
|
||||
@@ -260,6 +261,7 @@ $glyph-icon-summary-widget: '\e1130';
|
||||
.icon-topic { @include glyphBefore($glyph-icon-topic); }
|
||||
.icon-box-with-dashed-lines { @include glyphBefore($glyph-icon-box-with-dashed-lines); }
|
||||
.icon-summary-widget { @include glyphBefore($glyph-icon-summary-widget); }
|
||||
.icon-notebook { @include glyphBefore($glyph-icon-notebook); }
|
||||
|
||||
/************************** 12 PX CLASSES */
|
||||
.icon-crosshair-12px { @include glyphBefore($glyph-icon-crosshair,'symbolsfont-12px'); }
|
||||
|
||||
@@ -26,5 +26,6 @@
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,23 +316,28 @@
|
||||
text-shadow: $shdwItemText;
|
||||
}
|
||||
|
||||
@mixin input-base($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
|
||||
@mixin input-base() {
|
||||
@include appearance(none);
|
||||
border-radius: $controlCr;
|
||||
box-sizing: border-box;
|
||||
box-shadow: inset $shdw;
|
||||
background: $bg;
|
||||
border: none;
|
||||
color: $fg;
|
||||
outline: none;
|
||||
&:focus { outline: 0; }
|
||||
&.error {
|
||||
background-color: $colorFormFieldErrorBg;
|
||||
color: $colorFormFieldErrorFg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg) {
|
||||
@include input-base($bg, $fg);
|
||||
@mixin s-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
|
||||
@include input-base();
|
||||
background: $bg;
|
||||
box-shadow: inset $shdw;
|
||||
color: $fg;
|
||||
}
|
||||
|
||||
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
|
||||
@include s-input($bg, $fg, $shdw);
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@mixin contextArrow() {
|
||||
@@ -344,7 +349,7 @@
|
||||
}
|
||||
|
||||
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
||||
@include input-base($bg, $fg);
|
||||
@include nice-input($bg, $fg);
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
content:'';
|
||||
font-family: symbolsfont;
|
||||
font-size: 0.8em;
|
||||
display: inline;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 0.8rem;
|
||||
padding: $interiorMarginLg $interiorMarginLg * 2;
|
||||
cursor: pointer;
|
||||
pointer-events: none;
|
||||
&[href] {
|
||||
pointer-events: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.widget-rules-wrapper,
|
||||
.widget-rule-content,
|
||||
.w-widget-test-data-content {
|
||||
@include trans-prop-nice($props: (height, min-height, opacity), $dur: 250ms);
|
||||
@@ -72,16 +73,18 @@
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
&.expanded {
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-rules-wrapper {
|
||||
flex: 1 1 auto !important;
|
||||
}
|
||||
|
||||
.widget-rule-content.expanded {
|
||||
overflow: visible !important;
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
|
||||
.w-widget-test-data-content {
|
||||
@@ -119,11 +122,43 @@
|
||||
|
||||
> .l-summary-widget {
|
||||
// Main view of the summary widget
|
||||
// Give some airspace and center the widget in the area
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.widget-edit-holder {
|
||||
display: flex; // Overrides display: none during Browse mode
|
||||
display: flex; // Overrides `display: none` during Browse mode
|
||||
.flex-accordion-holder {
|
||||
// Needed because otherwise accordion elements "creep" when contents expand and contract
|
||||
display: block !important;
|
||||
}
|
||||
&.expanded-widget-test-data {
|
||||
.w-widget-test-data-content {
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
&:not(.expanded-widget-rules) {
|
||||
// Test data is expanded and rules are collapsed
|
||||
// Make text data take up all the vertical space
|
||||
.flex-accordion-holder { display: flex; }
|
||||
.widget-test-data {
|
||||
flex-grow: 999999;
|
||||
}
|
||||
.w-widget-test-data-items {
|
||||
max-height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.expanded-widget-rules {
|
||||
.widget-rules-wrapper {
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.s-status-no-data {
|
||||
|
||||
@@ -234,12 +234,16 @@ textarea {
|
||||
}
|
||||
|
||||
/******************************************************** INPUTS */
|
||||
%input-base {
|
||||
@include input-base();
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="search"],
|
||||
input[type="number"] {
|
||||
@include nice-input();
|
||||
vertical-align: baseline;
|
||||
padding: $inputTextP;
|
||||
padding: $inputTextPTopBtm $inputTextPLeftRight;
|
||||
&.numeric {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -283,20 +287,44 @@ textarea.lg { position: relative; height: 300px; }
|
||||
}
|
||||
}
|
||||
|
||||
*[contenteditable].s-input-inline,
|
||||
input[type="text"].s-input-inline,
|
||||
.s-input-inline input[type="text"] {
|
||||
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
|
||||
@extend %input-base;
|
||||
@include trans-prop-nice((padding, box-shadow), 250ms);
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
min-width: 20px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
&:hover,
|
||||
&:focus {
|
||||
padding-left: $inputTextPLeftRight;
|
||||
padding-right: $inputTextPLeftRight;
|
||||
}
|
||||
&:hover {
|
||||
border-color: rgba($colorBodyFg, 0.2);
|
||||
}
|
||||
&:focus {
|
||||
@include s-input();
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** SELECTS */
|
||||
.select {
|
||||
@include btnSubtle($bg: $colorSelectBg);
|
||||
@extend .icon-arrow-down; // Context arrow
|
||||
//@if $shdwBtns != none { margin: 0 0 2px 0; } // Needed to avoid dropshadow from being clipped by parent containers
|
||||
display: inline-block;
|
||||
padding: 0 $interiorMargin;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
//height: $btnStdH;
|
||||
//line-height: $btnStdH;
|
||||
select {
|
||||
@include appearance(none);
|
||||
box-sizing: border-box;
|
||||
&:focus { outline: 0; }
|
||||
background: none;
|
||||
color: $colorSelectFg;
|
||||
cursor: pointer;
|
||||
@@ -366,8 +394,7 @@ textarea.lg { position: relative; height: 300px; }
|
||||
.l-elem-wrapper {
|
||||
mct-representation {
|
||||
// Holds the context-available item
|
||||
// Must have min-width to make flex work properly
|
||||
// in Safari
|
||||
// Must have min-width to make flex work properly in Safari
|
||||
min-width: 0.7em;
|
||||
}
|
||||
}
|
||||
@@ -533,7 +560,6 @@ textarea.lg { position: relative; height: 300px; }
|
||||
height: $h;
|
||||
margin-top: 1 + floor($h/2) * -1;
|
||||
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
||||
//border-radius: 50% !important;
|
||||
}
|
||||
|
||||
@mixin sliderKnobRound() {
|
||||
@@ -548,7 +574,6 @@ textarea.lg { position: relative; height: 300px; }
|
||||
|
||||
input[type="range"] {
|
||||
// HTML5 range inputs
|
||||
|
||||
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||
background: transparent; /* Otherwise white in Chrome */
|
||||
&:focus {
|
||||
@@ -747,11 +772,15 @@ body.desktop {
|
||||
}
|
||||
|
||||
.overlay ::-webkit-scrollbar-thumb {
|
||||
$lr: 15%;
|
||||
background: $scrollbarThumbColorOverlay;
|
||||
&:hover { background: $scrollbarThumbColorOverlayHov; }
|
||||
}
|
||||
|
||||
.menu ::-webkit-scrollbar-thumb {
|
||||
background: $scrollbarThumbColorMenu;
|
||||
&:hover { background: $scrollbarThumbColorMenuHov; }
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: $scrollbarTrackColorBg;
|
||||
}
|
||||
|
||||
@@ -21,20 +21,20 @@
|
||||
*****************************************************************************/
|
||||
/******************************************************** MENU BUTTONS */
|
||||
.s-menu-button {
|
||||
// Formerly .btn-menu
|
||||
@extend .s-button;
|
||||
span.l-click-area {
|
||||
// In markup, this element should not enclose anything.
|
||||
@extend .abs;
|
||||
}
|
||||
// Formerly .btn-menu
|
||||
@extend .s-button;
|
||||
span.l-click-area {
|
||||
// In markup, this element should not enclose anything.
|
||||
@extend .abs;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 16px; //120%;
|
||||
}
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.title-label {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
.title-label {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.icon-swatch,
|
||||
.color-swatch {
|
||||
@@ -52,66 +52,60 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
// Adds the downward facing 'context available / invoke menu' arrow element
|
||||
@include contextArrow();
|
||||
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
||||
}
|
||||
&:after {
|
||||
// Adds the downward facing 'context available / invoke menu' arrow element
|
||||
@include contextArrow();
|
||||
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
||||
}
|
||||
|
||||
&.create-button {
|
||||
&.create-button {
|
||||
@extend .icon-plus;
|
||||
.title-label {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
.title-label {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
left: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.menu {
|
||||
left: 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** MENUS THEMSELVES */
|
||||
.menu-element {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.s-menu {
|
||||
border-radius: $basicCr;
|
||||
@include containerSubtle($colorMenuBg, $colorMenuFg);
|
||||
@include boxShdw($shdwMenu);
|
||||
@include txtShdw($shdwMenuText);
|
||||
padding: $interiorMarginSm 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu {
|
||||
// TODO: reduce size of icons
|
||||
@extend .s-menu;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
ul {
|
||||
@include menuUlReset();
|
||||
li {
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid pullForward($colorMenuBg, 10%);
|
||||
border-radius: $basicCr;
|
||||
@include containerSubtle($colorMenuBg, $colorMenuFg);
|
||||
@include boxShdw($shdwMenu);
|
||||
@include txtShdw($shdwMenuText);
|
||||
padding: $interiorMarginSm 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
ul {
|
||||
@include menuUlReset();
|
||||
li {
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid pullForward($colorMenuBg, 10%);
|
||||
color: $colorMenuFg;
|
||||
//color: pullForward($colorMenuBg, 60%);
|
||||
line-height: $menuLineH;
|
||||
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
&:first-child {
|
||||
border: none;
|
||||
}
|
||||
&:hover {
|
||||
background: $colorMenuHovBg;
|
||||
color: $colorMenuHovFg;
|
||||
line-height: $menuLineH;
|
||||
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
&:first-child {
|
||||
border: none;
|
||||
}
|
||||
&:hover {
|
||||
background: $colorMenuHovBg;
|
||||
color: $colorMenuHovFg;
|
||||
&:before {
|
||||
color: $colorMenuHovIc;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
@extend .ui-symbol;
|
||||
@extend .type-icon;
|
||||
@@ -119,8 +113,8 @@
|
||||
display: inline-block;
|
||||
left: $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu,
|
||||
@@ -128,94 +122,97 @@
|
||||
.context-menu,
|
||||
.super-menu,
|
||||
.s-menu-button .menu {
|
||||
pointer-events: auto;
|
||||
ul li {
|
||||
a.menu-item-a {
|
||||
pointer-events: auto;
|
||||
ul li {
|
||||
a.menu-item-a {
|
||||
color: $colorMenuFg;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
&:before,
|
||||
a.menu-item-a:before {
|
||||
color: $colorMenuIc;
|
||||
left: $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-menu {
|
||||
// Used in search dropdown in tree
|
||||
@extend .context-menu;
|
||||
ul li {
|
||||
padding-left: 50px;
|
||||
.checkbox {
|
||||
$d: 0.7rem;
|
||||
position: absolute;
|
||||
left: $interiorMargin;
|
||||
top: ($menuLineH - $d) / 1.5;
|
||||
em {
|
||||
height: $d;
|
||||
width: $d;
|
||||
&:before {
|
||||
font-size: 7px !important;
|
||||
height: $d;
|
||||
width: $d;
|
||||
line-height: $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
// Used in search dropdown in tree
|
||||
@extend .context-menu;
|
||||
ul li {
|
||||
padding-left: 50px;
|
||||
.checkbox {
|
||||
$d: 0.7rem;
|
||||
position: absolute;
|
||||
left: $interiorMargin;
|
||||
top: ($menuLineH - $d) / 1.5;
|
||||
em {
|
||||
height: $d;
|
||||
width: $d;
|
||||
&:before {
|
||||
font-size: 7px !important;
|
||||
height: $d;
|
||||
width: $d;
|
||||
line-height: $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
// Type icon
|
||||
left: 25px;
|
||||
}
|
||||
}
|
||||
left: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.super-menu,
|
||||
.super-menu > mct-representation,
|
||||
.super-menu > .contents {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.super-menu {
|
||||
$w: 500px;
|
||||
$h: $w - 20;
|
||||
$plw: 50%;
|
||||
$prw: 50%;
|
||||
display: block;
|
||||
width: $w;
|
||||
height: $h;
|
||||
.contents {
|
||||
@include absPosDefault($interiorMargin);
|
||||
}
|
||||
.pane {
|
||||
box-sizing: border-box;
|
||||
&.menu-items {
|
||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||
left: 0;
|
||||
padding-right: $interiorMargin;
|
||||
right: auto;
|
||||
width: $plw;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
ul {
|
||||
li {
|
||||
border-radius: $controlCr;
|
||||
padding-left: 30px;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.menu-item-description {
|
||||
left: auto;
|
||||
right: 0;
|
||||
padding: $interiorMargin * 5;
|
||||
width: $prw;
|
||||
$plw: 50%;
|
||||
$prw: 100% - $plw;
|
||||
position: absolute;
|
||||
.w-menu {
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: $interiorMarginLg;
|
||||
}
|
||||
.col {
|
||||
box-sizing: border-box;
|
||||
flex: 1 1 auto;
|
||||
overflow-x: hidden;
|
||||
&.menu-items {
|
||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||
overflow-y: auto;
|
||||
padding-right: $interiorMargin;
|
||||
width: $plw;
|
||||
ul {
|
||||
li {
|
||||
border-radius: $controlCr;
|
||||
padding-left: 30px;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.menu-item-description {
|
||||
$p: $interiorMargin * 3;
|
||||
overflow-y: hidden;
|
||||
padding: $p $p 0 $p;
|
||||
width: $prw;
|
||||
|
||||
.desc-area {
|
||||
&.icon {
|
||||
color: $colorCreateMenuLgIcon;
|
||||
font-size: 8em;
|
||||
margin-bottom: $interiorMargin * 3;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
&.title {
|
||||
color: $colorCreateMenuText;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: $interiorMargin * 2;
|
||||
}
|
||||
&.description {
|
||||
color: pushBack($colorCreateMenuText, 20%);
|
||||
@@ -223,67 +220,104 @@
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.w-title-desc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden; // Height set in specific menu instances
|
||||
}
|
||||
|
||||
// Specific menu instances
|
||||
&.l-create-menu {
|
||||
width: 500px;
|
||||
.col {
|
||||
max-height: 70vh;
|
||||
}
|
||||
.w-title-desc {
|
||||
height: 190px;
|
||||
}
|
||||
.desc-area {
|
||||
&.icon {
|
||||
font-size: 8em;
|
||||
height: 135px;
|
||||
margin-bottom: $interiorMargin * 3;
|
||||
}
|
||||
&.title {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mini {
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
.pane {
|
||||
.col {
|
||||
max-height: 50vh;
|
||||
&.menu-items {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
&.menu-item-description {
|
||||
padding: $interiorMargin * 3;
|
||||
.desc-area {
|
||||
&.icon {
|
||||
font-size: 4em;
|
||||
}
|
||||
&.title {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
$p: $interiorMargin * 2;
|
||||
padding: $p $p 0 $p;
|
||||
}
|
||||
}
|
||||
.w-title-desc {
|
||||
height: 180px;
|
||||
}
|
||||
.desc-area {
|
||||
&.icon {
|
||||
font-size: 4em;
|
||||
height: 70px;
|
||||
margin-bottom: $interiorMargin * 3;
|
||||
}
|
||||
&.title {
|
||||
font-size: 1em;
|
||||
margin-bottom: $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
font-size: 0.80rem;
|
||||
font-size: 0.80rem;
|
||||
}
|
||||
|
||||
.context-menu-holder,
|
||||
.menu-holder {
|
||||
position: absolute;
|
||||
z-index: 120;
|
||||
.context-menu-wrapper {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
&.go-left .context-menu,
|
||||
&.go-left .menu {
|
||||
right: 0;
|
||||
}
|
||||
&.go-up .context-menu,
|
||||
&.go-up .menu {
|
||||
bottom: 0;
|
||||
}
|
||||
position: absolute;
|
||||
z-index: 120;
|
||||
.context-menu-wrapper {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
&.go-left .context-menu,
|
||||
&.go-left .menu {
|
||||
right: 0;
|
||||
}
|
||||
&.go-up .context-menu,
|
||||
&.go-up .menu {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-holder {
|
||||
pointer-events: none;
|
||||
height: 200px;
|
||||
width: 170px;
|
||||
pointer-events: none;
|
||||
height: 200px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
.btn-bar.right .menu,
|
||||
.menus-to-left .menu {
|
||||
z-index: 79;
|
||||
left: auto;
|
||||
right: 0;
|
||||
width: auto;
|
||||
left: auto;
|
||||
right: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.menus-up .menu {
|
||||
bottom: $btnStdH; top: auto;
|
||||
bottom: $btnStdH;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,11 @@
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
background: none !important;
|
||||
margin-right: $interiorMarginSm;
|
||||
&[class*='s-status']:before {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
@@ -192,7 +196,6 @@
|
||||
padding: 0 $interiorMargin;
|
||||
}
|
||||
.close {
|
||||
//@include test(red, 0.7);
|
||||
cursor: pointer;
|
||||
font-size: 7px;
|
||||
width: 8px;
|
||||
@@ -247,7 +250,6 @@
|
||||
// Archetypal message
|
||||
.l-message {
|
||||
$iconW: 32px;
|
||||
//@include test();
|
||||
@include display(flex);
|
||||
@include flex-direction(row);
|
||||
@include align-items(stretch);
|
||||
@@ -292,7 +294,6 @@
|
||||
}
|
||||
|
||||
.message-body {
|
||||
//@include test(blue);
|
||||
@include flex(1 1 100%);
|
||||
}
|
||||
}
|
||||
@@ -335,7 +336,6 @@
|
||||
|
||||
> div,
|
||||
> span {
|
||||
//@include test(red);
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,9 +129,6 @@
|
||||
}
|
||||
|
||||
.s-filter {
|
||||
input[type="search"] {
|
||||
@include input-base();
|
||||
}
|
||||
.clear-icon,
|
||||
.menu-icon,
|
||||
&:before {
|
||||
|
||||
@@ -157,6 +157,8 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: auto;
|
||||
padding-right: $interiorMargin;
|
||||
padding-bottom: $interiorMargin;
|
||||
.field.l-input-med {
|
||||
input[type='text'] {
|
||||
width: 100%;
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
&.t-object-type-timer,
|
||||
&.t-object-type-clock,
|
||||
&.t-object-type-hyperlink,
|
||||
&.t-object-type-summary-widget {
|
||||
&.t-object-type-summary-widget,
|
||||
&.no-frame .t-object-type-fixed-display,
|
||||
&.no-frame .t-object-type-layout {
|
||||
// Hide the right side buttons for objects where they don't make sense
|
||||
// Note that this will hide the view Switcher button if applied
|
||||
// to an object that has it.
|
||||
@@ -104,7 +106,7 @@
|
||||
}
|
||||
&.t-frame-outer > .t-rep-frame {
|
||||
&.contents {
|
||||
$m: 2px;
|
||||
$m: 0px;
|
||||
top: $m;
|
||||
right: $m;
|
||||
bottom: $m;
|
||||
@@ -121,6 +123,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.t-frame-outer .s-input-inline {
|
||||
// Prevent inline inputs from being edited when nested in a Layout
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/********************************************************** OBJECT TYPES */
|
||||
.t-object-type-hyperlink,
|
||||
.t-object-type-summary-widget {
|
||||
@@ -130,13 +137,12 @@
|
||||
.w-summary-widget,
|
||||
.l-summary-widget,
|
||||
.l-hyperlink.s-button {
|
||||
// Some object types expand to the full size of the object-holder.
|
||||
@extend .abs;
|
||||
}
|
||||
|
||||
.l-summary-widget,
|
||||
.l-hyperlink.s-button {
|
||||
// When a hyperlink is a button in a frame, make it expand to fill out to the object-holder
|
||||
//@extend .abs;
|
||||
.label {
|
||||
@include ellipsize();
|
||||
@include transform(translateY(-50%));
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.s-hover-border {
|
||||
border: 1px dotted transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.s-status-editing {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<!-- look at action-button for example -->
|
||||
<span class="t-filter l-filter s-filter"
|
||||
<span class="t-filter l-filter"
|
||||
ng-controller="GetterSetterController">
|
||||
<input type="search"
|
||||
class="t-filter-input"
|
||||
|
||||
@@ -108,8 +108,11 @@ define(
|
||||
|
||||
getMetadata();
|
||||
});
|
||||
}
|
||||
|
||||
var mutation = $scope.ngModel.selectedObject.getCapability('mutation');
|
||||
var unlisten = mutation.listen(getMetadata);
|
||||
$scope.$on('$destroy', unlisten);
|
||||
}
|
||||
return ObjectInspectorController;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -39,10 +39,18 @@ define(
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$watch"]
|
||||
["$watch", "$on"]
|
||||
);
|
||||
mockScope.ngModel = {};
|
||||
mockScope.ngModel.selectedObject = 'mock selected object';
|
||||
mockScope.ngModel.selectedObject = {
|
||||
getCapability: function () {
|
||||
return {
|
||||
listen: function () {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
mockObjectService = jasmine.createSpyObj(
|
||||
"objectService",
|
||||
|
||||
@@ -201,13 +201,15 @@ $shdwItemTreeIcon: 0.6;
|
||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
||||
|
||||
// Scrollbar
|
||||
$scrollbarTrackSize: 10px;
|
||||
$scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px;
|
||||
$scrollbarTrackColorBg: rgba(#000, 0.4);
|
||||
$scrollbarTrackSize: 7px;
|
||||
$scrollbarTrackShdw: rgba(#000, 0.5) 0 1px 5px;
|
||||
$scrollbarTrackColorBg: transparent; //rgba(#000, 0.4);
|
||||
$scrollbarThumbColor: pullForward($colorBodyBg, 10%);
|
||||
$scrollbarThumbColorHov: pullForward($scrollbarThumbColor, 2%);
|
||||
$scrollbarThumbColorOverlay: pullForward($colorOvrBg, 10%);
|
||||
$scrollbarThumbColorOverlayHov: pullForward($scrollbarThumbColorOverlay, 2%);
|
||||
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 20%);
|
||||
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
|
||||
|
||||
// Splitter
|
||||
// All splitterD* values MUST both be either odd or even numbers
|
||||
|
||||
@@ -201,13 +201,15 @@ $shdwItemTreeIcon: none;
|
||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
||||
|
||||
// Scrollbar
|
||||
$scrollbarTrackSize: 10px;
|
||||
$scrollbarTrackSize: 7px;
|
||||
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
|
||||
$scrollbarTrackColorBg: rgba(#000, 0.2);
|
||||
$scrollbarThumbColor: darken($colorBodyBg, 50%);
|
||||
$scrollbarThumbColorHov: $colorKey;
|
||||
$scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
|
||||
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
|
||||
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%);
|
||||
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
|
||||
|
||||
// Splitter
|
||||
// All splitterD* values MUST both be either odd or even numbers
|
||||
|
||||
@@ -94,31 +94,6 @@ define([
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"versions": [
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "@@version",
|
||||
"priority": 999
|
||||
},
|
||||
{
|
||||
"name": "Built",
|
||||
"value": "@@timestamp",
|
||||
"description": "The date on which this version of the client was built.",
|
||||
"priority": 990
|
||||
},
|
||||
{
|
||||
"name": "Revision",
|
||||
"value": "@@revision",
|
||||
"description": "A unique revision identifier for the client sources.",
|
||||
"priority": 995
|
||||
},
|
||||
{
|
||||
"name": "Branch",
|
||||
"value": "@@branch",
|
||||
"description": "The name of the branch that was used during the build.",
|
||||
"priority": 994
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"provides": "objectService",
|
||||
|
||||
@@ -23,10 +23,13 @@
|
||||
define([
|
||||
"moment-timezone",
|
||||
"./src/indicators/ClockIndicator",
|
||||
"./src/indicators/FollowIndicator",
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/ClockController",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/FollowTimerAction",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
@@ -37,10 +40,13 @@ define([
|
||||
], function (
|
||||
MomentTimezone,
|
||||
ClockIndicator,
|
||||
FollowIndicator,
|
||||
TickerService,
|
||||
TimerService,
|
||||
ClockController,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
FollowTimerAction,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
@@ -80,6 +86,11 @@ define([
|
||||
"CLOCK_INDICATOR_FORMAT"
|
||||
],
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"implementation": FollowIndicator,
|
||||
"depends": ["timerService"],
|
||||
"priority": "fallback"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
@@ -90,6 +101,11 @@ define([
|
||||
"$timeout",
|
||||
"now"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "timerService",
|
||||
"implementation": TimerService,
|
||||
"depends": ["openmct"]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
@@ -134,6 +150,15 @@ define([
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.follow",
|
||||
"implementation": FollowTimerAction,
|
||||
"depends": ["timerService"],
|
||||
"category": "contextual",
|
||||
"name": "Follow Timer",
|
||||
"cssClass": "icon-clock",
|
||||
"priority": "optional"
|
||||
},
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
|
||||
54
platform/features/clock/src/actions/FollowTimerAction.js
Normal file
54
platform/features/clock/src/actions/FollowTimerAction.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Designates a specific timer for following. Timelines, for example,
|
||||
* use the actively followed timer to display a time-of-interest line
|
||||
* and interpret time conductor bounds in the Timeline's relative
|
||||
* time frame.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function FollowTimerAction(timerService, context) {
|
||||
var domainObject = context.domainObject;
|
||||
this.perform =
|
||||
timerService.setTimer.bind(timerService, domainObject);
|
||||
}
|
||||
|
||||
FollowTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel()) ||
|
||||
{};
|
||||
|
||||
return model.type === 'timer';
|
||||
};
|
||||
|
||||
return FollowTimerAction;
|
||||
}
|
||||
);
|
||||
57
platform/features/clock/src/indicators/FollowIndicator.js
Normal file
57
platform/features/clock/src/indicators/FollowIndicator.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
var NO_TIMER = "No timer being followed";
|
||||
|
||||
/**
|
||||
* Indicator that displays the active timer, as well as its
|
||||
* current state.
|
||||
* @implements {Indicator}
|
||||
* @memberof platform/features/clock
|
||||
*/
|
||||
function FollowIndicator(timerService) {
|
||||
this.timerService = timerService;
|
||||
}
|
||||
|
||||
FollowIndicator.prototype.getGlyphClass = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
FollowIndicator.prototype.getCssClass = function () {
|
||||
return (this.timerService.getTimer()) ? "icon-timer s-status-ok" : "icon-timer";
|
||||
};
|
||||
|
||||
FollowIndicator.prototype.getText = function () {
|
||||
var timer = this.timerService.getTimer();
|
||||
return (timer) ? 'Following timer ' + timer.getModel().name : NO_TIMER;
|
||||
};
|
||||
|
||||
FollowIndicator.prototype.getDescription = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
return FollowIndicator;
|
||||
}
|
||||
);
|
||||
102
platform/features/clock/src/services/TimerService.js
Normal file
102
platform/features/clock/src/services/TimerService.js
Normal file
@@ -0,0 +1,102 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['EventEmitter'], function (EventEmitter) {
|
||||
|
||||
/**
|
||||
* Tracks the currently-followed Timer object. Used by
|
||||
* timelines et al to synchronize to a particular timer.
|
||||
*
|
||||
* The TimerService emits `change` events when the active timer
|
||||
* is changed.
|
||||
*/
|
||||
function TimerService(openmct) {
|
||||
EventEmitter.apply(this);
|
||||
this.time = openmct.time;
|
||||
}
|
||||
|
||||
TimerService.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Set (or clear, if `timer` is undefined) the currently active timer.
|
||||
* @param {DomainObject} timer the new active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.setTimer = function (timer) {
|
||||
this.timer = timer;
|
||||
this.emit('change');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently active timer.
|
||||
* @return {DomainObject} the active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.getTimer = function () {
|
||||
return this.timer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if there is a currently active timer.
|
||||
* @return {boolean} true if there is a timer
|
||||
*/
|
||||
TimerService.prototype.hasTimer = function () {
|
||||
return !!this.timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the provided timestamp to milliseconds relative to
|
||||
* the active timer.
|
||||
* @return {number} milliseconds since timer start
|
||||
*/
|
||||
TimerService.prototype.convert = function (timestamp) {
|
||||
var clock = this.time.clock();
|
||||
var canConvert = this.hasTimer() &&
|
||||
!!clock &&
|
||||
this.timer.getModel().timerState !== 'stopped';
|
||||
|
||||
if (!canConvert) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var now = clock.currentValue();
|
||||
var model = this.timer.getModel();
|
||||
var delta = model.timerState === 'paused' ? now - model.pausedTime : 0;
|
||||
var epoch = model.timestamp;
|
||||
|
||||
return timestamp - epoch - delta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the active clock, adjusted to be relative to the active
|
||||
* timer. If there is no clock or no active timer, this will return
|
||||
* `undefined`.
|
||||
* @return {number} milliseconds since the start of the active timer
|
||||
*/
|
||||
TimerService.prototype.now = function () {
|
||||
var clock = this.time.clock();
|
||||
return clock && this.convert(clock.currentValue());
|
||||
};
|
||||
|
||||
return TimerService;
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"../../src/actions/FollowTimerAction"
|
||||
], function (FollowTimerAction) {
|
||||
var TIMER_SERVICE_METHODS =
|
||||
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
|
||||
|
||||
describe("The Follow Timer action", function () {
|
||||
var testContext;
|
||||
var testModel;
|
||||
|
||||
beforeEach(function () {
|
||||
testModel = {};
|
||||
testContext = { domainObject: { getModel: function () {
|
||||
return testModel;
|
||||
} } };
|
||||
});
|
||||
|
||||
it("is applicable to timers", function () {
|
||||
testModel.type = "timer";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(true);
|
||||
});
|
||||
|
||||
it("is inapplicable to non-timers", function () {
|
||||
testModel.type = "folder";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(false);
|
||||
});
|
||||
|
||||
describe("when instantiated", function () {
|
||||
var mockTimerService;
|
||||
var action;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimerService = jasmine.createSpyObj(
|
||||
'timerService',
|
||||
TIMER_SERVICE_METHODS
|
||||
);
|
||||
action = new FollowTimerAction(mockTimerService, testContext);
|
||||
});
|
||||
|
||||
it("does not interact with the timer service", function () {
|
||||
TIMER_SERVICE_METHODS.forEach(function (method) {
|
||||
expect(mockTimerService[method]).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and performed", function () {
|
||||
beforeEach(function () {
|
||||
action.perform();
|
||||
});
|
||||
|
||||
it("sets the active timer", function () {
|
||||
expect(mockTimerService.setTimer)
|
||||
.toHaveBeenCalledWith(testContext.domainObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(["../../src/indicators/FollowIndicator"], function (FollowIndicator) {
|
||||
var TIMER_SERVICE_METHODS =
|
||||
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
|
||||
|
||||
describe("The timer-following indicator", function () {
|
||||
var mockTimerService;
|
||||
var indicator;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimerService =
|
||||
jasmine.createSpyObj('timerService', TIMER_SERVICE_METHODS);
|
||||
indicator = new FollowIndicator(mockTimerService);
|
||||
});
|
||||
|
||||
it("implements the Indicator interface", function () {
|
||||
expect(indicator.getGlyphClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getText()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getDescription()).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
describe("when a timer is set", function () {
|
||||
var testModel;
|
||||
var mockDomainObject;
|
||||
|
||||
beforeEach(function () {
|
||||
testModel = { name: "some timer!" };
|
||||
mockDomainObject = jasmine.createSpyObj('timer', ['getModel']);
|
||||
mockDomainObject.getModel.andReturn(testModel);
|
||||
mockTimerService.getTimer.andReturn(mockDomainObject);
|
||||
});
|
||||
|
||||
it("displays the timer's name", function () {
|
||||
expect(indicator.getText().indexOf(testModel.name))
|
||||
.not.toEqual(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
63
platform/features/clock/test/services/TimerServiceSpec.js
Normal file
63
platform/features/clock/test/services/TimerServiceSpec.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'../../src/services/TimerService'
|
||||
], function (TimerService) {
|
||||
describe("TimerService", function () {
|
||||
var callback;
|
||||
var mockmct;
|
||||
var timerService;
|
||||
|
||||
beforeEach(function () {
|
||||
callback = jasmine.createSpy('callback');
|
||||
mockmct = { time: { clock: jasmine.createSpy('clock') } };
|
||||
timerService = new TimerService(mockmct);
|
||||
timerService.on('change', callback);
|
||||
});
|
||||
|
||||
it("initially emits no change events", function () {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports no current timer", function () {
|
||||
expect(timerService.getTimer()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("setTimer", function () {
|
||||
var testTimer;
|
||||
|
||||
beforeEach(function () {
|
||||
testTimer = { name: "I am some timer; you are nobody." };
|
||||
timerService.setTimer(testTimer);
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports the current timer", function () {
|
||||
expect(timerService.getTimer()).toBe(testTimer);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
$ueTimeConductorH: (25px, 16px, 20px); // Heights for Ticks, Data Visualization, Controls elements
|
||||
$ueTimeConductorRtH: (25px, 3px, 20px); // Heights for elements in Real-time mode
|
||||
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
|
||||
$timeCondInputDeltaDefW: 60px; // Default width for delta value inputs, typically 00:00:00
|
||||
$timeCondInputDeltaDefW: 65px; // Default width for delta value inputs, typically 00:00:00
|
||||
$timeCondTOIIconD: 12px; // height and width of icon used for TOI indicator
|
||||
$timeCondTOIValOffset: 0px;
|
||||
$ticksBlockerFadeW: 50px;
|
||||
|
||||
@@ -162,9 +162,6 @@
|
||||
.l-time-conductor-inputs {
|
||||
pointer-events: auto;
|
||||
}
|
||||
input[type="text"] {
|
||||
@include trans-prop-nice(padding, 250ms);
|
||||
}
|
||||
.time-range-input input[type="text"] {
|
||||
width: $timeCondInputTimeSysDefW;
|
||||
}
|
||||
@@ -290,18 +287,6 @@
|
||||
|
||||
.l-time-conductor-inputs-holder {
|
||||
.l-time-range-input-w {
|
||||
input[type="text"]:not(.error) {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
&:hover,
|
||||
&:focus {
|
||||
@include nice-input();
|
||||
padding: $inputTextP;
|
||||
}
|
||||
}
|
||||
.icon-calendar {
|
||||
display: none;
|
||||
}
|
||||
@@ -309,8 +294,11 @@
|
||||
display: none;
|
||||
}
|
||||
&.end-date {
|
||||
// Displays the current time
|
||||
pointer-events: none;
|
||||
input[type="text"] {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
color: pullForward($colorTimeCondKeyBg, 5%);
|
||||
margin-right: $interiorMargin;
|
||||
tab-index: -1;
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="contents">
|
||||
<div class="pane left menu-items">
|
||||
<div class="w-menu">
|
||||
<div class="col menu-items">
|
||||
<ul>
|
||||
<li ng-repeat="metadata in ngModel.options"
|
||||
ng-click="ngModel.select(metadata)">
|
||||
@@ -32,13 +32,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pane right menu-item-description">
|
||||
<div class="col menu-item-description">
|
||||
<div class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssClass}}"></div>
|
||||
<div class="desc-area title">
|
||||
{{ngModel.activeMetadata.name}}
|
||||
</div>
|
||||
<div class="desc-area description">
|
||||
{{ngModel.activeMetadata.description}}
|
||||
<div class="w-title-desc">
|
||||
<div class="desc-area title">
|
||||
{{ngModel.activeMetadata.name}}
|
||||
</div>
|
||||
<div class="desc-area description">
|
||||
{{ngModel.activeMetadata.description}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
ng-click="modeController.toggle()">
|
||||
<span class="title-label">{{ngModel.selected.name}}</span>
|
||||
</div>
|
||||
<div class="menu super-menu mini mode-selector-menu"
|
||||
<div class="menu super-menu mini l-mode-selector-menu"
|
||||
ng-show="modeController.isActive()">
|
||||
<mct-include key="'mode-menu'"
|
||||
ng-model="ngModel">
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.setOffsetsFromView(boundsModel)"
|
||||
field="'startOffset'"
|
||||
class="hrs-min-input">
|
||||
class="s-input-inline hrs-min-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
</span>
|
||||
@@ -71,7 +71,7 @@
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.setOffsetsFromView(boundsModel)"
|
||||
field="'endOffset'"
|
||||
class="hrs-min-input">
|
||||
class="s-input-inline hrs-min-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -201,7 +201,7 @@ define(
|
||||
var options = [{
|
||||
key: 'fixed',
|
||||
name: 'Fixed Timespan Mode',
|
||||
description: 'Query and explore data that falls between two fixed datetimes',
|
||||
description: 'Query and explore data that falls between two fixed datetimes.',
|
||||
cssClass: 'icon-calendar'
|
||||
}];
|
||||
var clocks = {};
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="abs object-browse-bar l-flex-row">
|
||||
<div class="left flex-elem l-flex-row grows">
|
||||
<mct-representation
|
||||
key="'object-header'"
|
||||
key="'object-header-frame'"
|
||||
mct-object="domainObject"
|
||||
class="l-flex-row flex-elem object-header grows">
|
||||
</mct-representation>
|
||||
|
||||
@@ -118,7 +118,10 @@ define([
|
||||
"policies": [
|
||||
{
|
||||
"category": "view",
|
||||
"implementation": PlotViewPolicy
|
||||
"implementation": PlotViewPolicy,
|
||||
"depends": [
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
|
||||
@@ -24,7 +24,7 @@ define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
var DIGITS = 3;
|
||||
var DIGITS = 2;
|
||||
|
||||
/**
|
||||
* Wraps a `TelemetryFormatter` to provide formats for domain and
|
||||
|
||||
@@ -30,30 +30,32 @@ define(
|
||||
* @constructor
|
||||
* @memberof platform/features/plot
|
||||
*/
|
||||
function PlotViewPolicy() {
|
||||
function PlotViewPolicy(openmct) {
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
function hasNumericTelemetry(domainObject) {
|
||||
var telemetry = domainObject &&
|
||||
domainObject.getCapability('telemetry'),
|
||||
metadata = telemetry ? telemetry.getMetadata() : {},
|
||||
ranges = metadata.ranges || [];
|
||||
PlotViewPolicy.prototype.hasNumericTelemetry = function (domainObject) {
|
||||
var adaptedObject = domainObject.useCapability('adapter');
|
||||
|
||||
// Generally, we want to allow Plot for telemetry-providing
|
||||
// objects (most telemetry is plottable.) We only want to
|
||||
// suppress this for telemetry which only has explicitly
|
||||
// non-numeric values.
|
||||
return ranges.length === 0 || ranges.some(function (range) {
|
||||
// Assume format is numeric if it is undefined
|
||||
// (numeric telemetry is the common case)
|
||||
return range.format === undefined ||
|
||||
range.format === 'number';
|
||||
});
|
||||
}
|
||||
if (!adaptedObject.telemetry) {
|
||||
return domainObject.hasCapability('delegation') &&
|
||||
domainObject.getCapability('delegation')
|
||||
.doesDelegateCapability('telemetry');
|
||||
}
|
||||
|
||||
var metadata = this.openmct.telemetry.getMetadata(adaptedObject);
|
||||
var rangeValues = metadata.valuesForHints(['range']);
|
||||
if (rangeValues.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return !rangeValues.every(function (value) {
|
||||
return value.format === 'string';
|
||||
});
|
||||
};
|
||||
|
||||
PlotViewPolicy.prototype.allow = function (view, domainObject) {
|
||||
if (view.key === 'plot') {
|
||||
return hasNumericTelemetry(domainObject);
|
||||
return this.hasNumericTelemetry(domainObject);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -27,51 +27,97 @@ define(
|
||||
describe("Plot view policy", function () {
|
||||
var testView,
|
||||
mockDomainObject,
|
||||
mockTelemetry,
|
||||
testMetadata,
|
||||
testAdaptedObject,
|
||||
openmct,
|
||||
telemetryMetadata,
|
||||
policy;
|
||||
|
||||
beforeEach(function () {
|
||||
testView = { key: "plot" };
|
||||
testMetadata = {};
|
||||
testAdaptedObject = { telemetry: {} };
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getId', 'getModel', 'getCapability']
|
||||
['useCapability', 'hasCapability', 'getCapability']
|
||||
);
|
||||
mockTelemetry = jasmine.createSpyObj(
|
||||
'telemetry',
|
||||
['getMetadata']
|
||||
);
|
||||
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||
return c === 'telemetry' ? mockTelemetry : undefined;
|
||||
mockDomainObject.useCapability.andReturn(testAdaptedObject);
|
||||
openmct = {
|
||||
telemetry: jasmine.createSpyObj('telemetryAPI', [
|
||||
'getMetadata'
|
||||
])
|
||||
};
|
||||
telemetryMetadata = jasmine.createSpyObj('telemetryMetadata', [
|
||||
'valuesForHints'
|
||||
]);
|
||||
telemetryMetadata.valuesForHints.andReturn([]);
|
||||
openmct.telemetry.getMetadata.andReturn(telemetryMetadata);
|
||||
policy = new PlotViewPolicy(openmct);
|
||||
});
|
||||
|
||||
it('fetches metadata from telem api', function () {
|
||||
policy.allow(testView, mockDomainObject);
|
||||
expect(mockDomainObject.useCapability)
|
||||
.toHaveBeenCalledWith('adapter');
|
||||
expect(openmct.telemetry.getMetadata)
|
||||
.toHaveBeenCalledWith(testAdaptedObject);
|
||||
expect(telemetryMetadata.valuesForHints)
|
||||
.toHaveBeenCalledWith(['range']);
|
||||
});
|
||||
|
||||
it('returns false if no ranges exist', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if any ranges exist', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([{}]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if all ranges are strings', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([{
|
||||
format: 'string'
|
||||
}, {
|
||||
format: 'string'
|
||||
}]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if only some ranges are strings', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([{
|
||||
format: 'string'
|
||||
}, {}]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for telemetry delegators', function () {
|
||||
delete testAdaptedObject.telemetry;
|
||||
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||
return c === 'delegation';
|
||||
});
|
||||
mockTelemetry.getMetadata.andReturn(testMetadata);
|
||||
|
||||
policy = new PlotViewPolicy();
|
||||
mockDomainObject.getCapability.andReturn(
|
||||
jasmine.createSpyObj('delegation', [
|
||||
'doesDelegateCapability'
|
||||
])
|
||||
);
|
||||
mockDomainObject.getCapability('delegation')
|
||||
.doesDelegateCapability.andCallFake(function (c) {
|
||||
return c === 'telemetry';
|
||||
});
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
expect(openmct.telemetry.getMetadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows the imagery view for domain objects with numeric telemetry", function () {
|
||||
testMetadata.ranges = [{ key: "foo", format: "number" }];
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("allows the imagery view for domain objects with unspecified telemetry", function () {
|
||||
testMetadata.ranges = [{ key: "foo" }];
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("disallows the imagery view for domain objects without image telemetry", function () {
|
||||
testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
|
||||
it('returns true for non-telemetry non-delegators', function () {
|
||||
delete testAdaptedObject.telemetry;
|
||||
mockDomainObject.hasCapability.andReturn(false);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows other views", function () {
|
||||
testView.key = "somethingElse";
|
||||
testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
|
||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ define([
|
||||
"./src/controllers/TimelineTickController",
|
||||
"./src/controllers/TimelineTableController",
|
||||
"./src/controllers/TimelineGanttController",
|
||||
"./src/controllers/TimelineTOIController",
|
||||
"./src/controllers/ActivityModeValuesController",
|
||||
"./src/capabilities/ActivityTimespanCapability",
|
||||
"./src/capabilities/TimelineTimespanCapability",
|
||||
@@ -59,6 +60,7 @@ define([
|
||||
TimelineTickController,
|
||||
TimelineTableController,
|
||||
TimelineGanttController,
|
||||
TimelineTOIController,
|
||||
ActivityModeValuesController,
|
||||
ActivityTimespanCapability,
|
||||
TimelineTimespanCapability,
|
||||
@@ -502,6 +504,15 @@ define([
|
||||
"TIMELINE_MAXIMUM_OFFSCREEN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "TimelineTOIController",
|
||||
"implementation": TimelineTOIController,
|
||||
"depends": [
|
||||
"openmct",
|
||||
"timerService",
|
||||
"$scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "ActivityModeValuesController",
|
||||
"implementation": ActivityModeValuesController,
|
||||
|
||||
@@ -29,6 +29,44 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Follow Line
|
||||
.l-follow-line {
|
||||
// TODO: move before and after into l-timeline-gantt so those only render in that pane
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
width: 1px;
|
||||
z-index: 9; // Just below .l-hover-btns-holder
|
||||
}
|
||||
}
|
||||
|
||||
.l-timeline-gantt {
|
||||
.l-follow-line {
|
||||
$d: 0.8rem;
|
||||
top: $interiorMargin;
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: $d;
|
||||
width: $d;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@include transform(translateX(-50%));
|
||||
}
|
||||
&:before {
|
||||
// Icon blocker
|
||||
width: 2 * $d;
|
||||
}
|
||||
&:after {
|
||||
// Icon
|
||||
font-size: $d;
|
||||
line-height: $d;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.s-timeline-gantt {
|
||||
@@ -108,10 +146,9 @@
|
||||
}
|
||||
.s-hover-btns-holder {
|
||||
$bg: $timelineHeaderColorBg;
|
||||
$bga: 1;
|
||||
$l: 5%;
|
||||
@include user-select(none);
|
||||
@include background-image(linear-gradient(-90deg, rgba($bg, $bga), rgba($bg, $bga) 70%, rgba($bg, 0) 100%));
|
||||
@include background-image(linear-gradient(-90deg, rgba($bg, 1), rgba($bg, 1) 70%, rgba($bg, 0) 100%));
|
||||
.s-button {
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
@@ -129,4 +166,27 @@
|
||||
color: $timelineResourceGraphFg;
|
||||
}
|
||||
}
|
||||
|
||||
.s-follow-line {
|
||||
background: rgba($timeControllerToiLineColor, 0.5);
|
||||
}
|
||||
|
||||
.s-timeline-gantt {
|
||||
.s-follow-line {
|
||||
&:after {
|
||||
// Icon
|
||||
color: $timeControllerToiLineColor;
|
||||
content: $glyph-icon-timer;
|
||||
font-family: symbolsfont;
|
||||
text-shadow: $shdwItemText;
|
||||
}
|
||||
&:before {
|
||||
// Blocker
|
||||
$bg: $timelineHeaderColorBg;
|
||||
$l: 30%;
|
||||
@include background-image(linear-gradient(90deg, rgba($bg, 0), rgba($bg, 1) $l, rgba($bg, 1) 100% - $l, rgba($bg, 0)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@
|
||||
}
|
||||
}
|
||||
&.l-timeline-gantt {
|
||||
.abs.l-timeline-gantt-header-w {
|
||||
overflow: hidden;
|
||||
height: $timelineTopPaneHeaderH;
|
||||
}
|
||||
.l-swimlanes-holder {
|
||||
@include scrollV(scroll);
|
||||
bottom: $scrollbarTrackSize;
|
||||
|
||||
@@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
|
||||
|
||||
@import "../../../../commonUI/general/res/sass/constants";
|
||||
@import "../../../../commonUI/general/res/sass/mixins";
|
||||
@import "../../../../commonUI/general/res/sass/glyphs";
|
||||
@import "../../../../commonUI/themes/espresso/res/sass/constants";
|
||||
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
|
||||
@import "constants";
|
||||
|
||||
@@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
|
||||
|
||||
@import "../../../../commonUI/general/res/sass/constants";
|
||||
@import "../../../../commonUI/general/res/sass/mixins";
|
||||
@import "../../../../commonUI/general/res/sass/glyphs";
|
||||
@import "../../../../commonUI/themes/snow/res/sass/constants";
|
||||
@import "../../../../commonUI/themes/snow/res/sass/mixins";
|
||||
@import "constants";
|
||||
|
||||
@@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
|
||||
|
||||
@import "../../../../commonUI/general/res/sass/constants";
|
||||
@import "../../../../commonUI/general/res/sass/mixins";
|
||||
@import "../../../../commonUI/general/res/sass/glyphs";
|
||||
@import "../../../../commonUI/themes/espresso/res/sass/constants";
|
||||
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
|
||||
@import "constants";
|
||||
|
||||
@@ -96,109 +96,124 @@
|
||||
|
||||
<!-- RIGHT PANE: GANTT AND RESOURCE PLOTS -->
|
||||
<span ng-controller="TimelineZoomController as zoomController" class="abs">
|
||||
<mct-split-pane anchor="bottom"
|
||||
|
||||
<span class="toi-control-holder temp" ng-controller="TimelineTOIController as toiController">
|
||||
<mct-split-pane anchor="bottom"
|
||||
position="pane.y"
|
||||
class="abs split-pane-component l-timeline-pane l-pane-r t-pane-v">
|
||||
|
||||
<!-- TOP PANE GANTT BARS -->
|
||||
<div class="split-pane-component l-timeline-pane t-pane-h l-pane-top t-timeline-gantt l-timeline-gantt s-timeline-gantt">
|
||||
<div class="l-hover-btns-holder s-hover-btns-holder">
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="zoomController.fit()"
|
||||
ng-show="true"
|
||||
title="Zoom to fit">
|
||||
</a>
|
||||
<!-- TOP PANE GANTT BARS -->
|
||||
<div class="split-pane-component l-timeline-pane t-pane-h l-pane-top t-timeline-gantt l-timeline-gantt s-timeline-gantt">
|
||||
<div class="l-hover-btns-holder s-hover-btns-holder">
|
||||
<a class="s-button icon-timer"
|
||||
ng-click="scroll.follow = true"
|
||||
ng-show="!toiController.isFollowing() && toiController.isActive()"
|
||||
title="Follow time bounds">
|
||||
</a>
|
||||
|
||||
<a class="s-button icon-magnify-in"
|
||||
ng-click="zoomController.zoom(-1)"
|
||||
ng-show="true"
|
||||
title="Zoom in">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="scroll.follow = false; zoomController.fit()"
|
||||
ng-show="true"
|
||||
title="Zoom to fit">
|
||||
</a>
|
||||
|
||||
<a class="s-button icon-magnify-out"
|
||||
ng-click="zoomController.zoom(1)"
|
||||
ng-show="true"
|
||||
title="Zoom out">
|
||||
</a>
|
||||
</div>
|
||||
<a class="s-button icon-magnify-in"
|
||||
ng-click="scroll.follow = false; zoomController.zoom(-1)"
|
||||
ng-show="true"
|
||||
title="Zoom in">
|
||||
</a>
|
||||
|
||||
<div style="overflow: hidden; position: absolute; left: 0; top: 0; right: 0; height: 30px;" mct-scroll-x="scroll.x">
|
||||
<mct-include key="'timeline-ticks'"
|
||||
parameters="{
|
||||
fullWidth: zoomController.width(timelineController.end()),
|
||||
start: scroll.x,
|
||||
width: scroll.width,
|
||||
step: zoomController.toPixels(zoomController.zoom()),
|
||||
toMillis: zoomController.toMillis
|
||||
}">
|
||||
</mct-include>
|
||||
</div>
|
||||
<a class="s-button icon-magnify-out"
|
||||
ng-click="scroll.follow = false; zoomController.zoom(1)"
|
||||
ng-show="true"
|
||||
title="Zoom out">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="t-swimlanes-holder l-swimlanes-holder"
|
||||
mct-scroll-x="scroll.x"
|
||||
mct-scroll-y="scroll.y">
|
||||
<div class="l-width-control"
|
||||
ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }">
|
||||
<div class="t-swimlane s-swimlane l-swimlane"
|
||||
ng-repeat="swimlane in timelineController.swimlanes()"
|
||||
ng-class="{
|
||||
exceeded: swimlane.exceeded(),
|
||||
selected: selection.selected(swimlane),
|
||||
'drop-into': swimlane.highlight(),
|
||||
'drop-after': swimlane.highlightBottom()
|
||||
}"
|
||||
ng-click="selection.select(swimlane)"
|
||||
mct-swimlane-drop="swimlane">
|
||||
<div style="overflow: hidden; position: absolute; left: 0; top: 0; right: 0; height: 30px;" mct-scroll-x="scroll.x">
|
||||
<mct-include key="'timeline-ticks'"
|
||||
parameters="{
|
||||
fullWidth: zoomController.width(timelineController.end()),
|
||||
start: scroll.x,
|
||||
width: scroll.width,
|
||||
step: zoomController.toPixels(zoomController.zoom()),
|
||||
toMillis: zoomController.toMillis
|
||||
}">
|
||||
</mct-include>
|
||||
</div>
|
||||
<div ng-if="toiController.isActive()" class="l-follow-line s-follow-line"
|
||||
ng-style="{ left: toiController.x() - scroll.x + 'px' }"></div>
|
||||
|
||||
<mct-representation key="'gantt'"
|
||||
mct-object="swimlane.domainObject"
|
||||
parameters="{
|
||||
scroll: scroll,
|
||||
toPixels: zoomController.toPixels
|
||||
}">
|
||||
</mct-representation>
|
||||
<div class="t-swimlanes-holder l-swimlanes-holder"
|
||||
mct-scroll-x="scroll.x"
|
||||
mct-scroll-y="scroll.y">
|
||||
<div class="l-width-control"
|
||||
ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }">
|
||||
<div class="t-swimlane s-swimlane l-swimlane"
|
||||
ng-repeat="swimlane in timelineController.swimlanes()"
|
||||
ng-class="{
|
||||
exceeded: swimlane.exceeded(),
|
||||
selected: selection.selected(swimlane),
|
||||
'drop-into': swimlane.highlight(),
|
||||
'drop-after': swimlane.highlightBottom()
|
||||
}"
|
||||
ng-click="selection.select(swimlane)"
|
||||
mct-swimlane-drop="swimlane">
|
||||
|
||||
<span ng-if="selection.selected(swimlane)">
|
||||
<span ng-repeat="handle in timelineController.handles()"
|
||||
ng-style="handle.style(zoomController)"
|
||||
style="position: absolute; top: 0px; bottom: 0px;"
|
||||
class="handle"
|
||||
ng-class="{ start: $index === 0, mid: $index === 1, end: $index > 1 }"
|
||||
mct-drag-down="handle.begin()"
|
||||
mct-drag="handle.drag(delta[0], zoomController); timelineController.refresh()"
|
||||
mct-drag-up="handle.finish()">
|
||||
</span>
|
||||
</span>
|
||||
<mct-representation key="'gantt'"
|
||||
mct-object="swimlane.domainObject"
|
||||
parameters="{
|
||||
scroll: scroll,
|
||||
toPixels: zoomController.toPixels
|
||||
}">
|
||||
</mct-representation>
|
||||
|
||||
<span ng-if="selection.selected(swimlane)">
|
||||
<span ng-repeat="handle in timelineController.handles()"
|
||||
ng-style="handle.style(zoomController)"
|
||||
style="position: absolute; top: 0px; bottom: 0px;"
|
||||
class="handle"
|
||||
ng-class="{ start: $index === 0, mid: $index === 1, end: $index > 1 }"
|
||||
mct-drag-down="handle.begin()"
|
||||
mct-drag="handle.drag(delta[0], zoomController); timelineController.refresh()"
|
||||
mct-drag-up="handle.finish()">
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HORZ SPLITTER -->
|
||||
<mct-splitter></mct-splitter>
|
||||
<!-- HORZ SPLITTER -->
|
||||
<mct-splitter></mct-splitter>
|
||||
|
||||
<!-- BOTTOM PANE RESOURCE GRAPHS AND RIGHT PANE HORIZONTAL SCROLL CONTROL -->
|
||||
<div class="split-pane-component l-timeline-resource-graph l-timeline-pane t-pane-h l-pane-btm">
|
||||
<div class="l-graphs-holder"
|
||||
mct-resize="scroll.width = bounds.width">
|
||||
<div class="t-graphs l-graphs">
|
||||
<mct-include key="'timeline-resource-graphs'"
|
||||
parameters="{
|
||||
origin: zoomController.toMillis(scroll.x),
|
||||
duration: zoomController.toMillis(scroll.width),
|
||||
graphs: timelineController.graphs()
|
||||
}">
|
||||
</mct-include>
|
||||
<!-- BOTTOM PANE RESOURCE GRAPHS AND RIGHT PANE HORIZONTAL SCROLL CONTROL -->
|
||||
<div class="split-pane-component l-timeline-resource-graph l-timeline-pane t-pane-h l-pane-btm">
|
||||
<div class="l-graphs-holder"
|
||||
mct-resize="scroll.width = bounds.width">
|
||||
<div class="t-graphs l-graphs">
|
||||
<mct-include key="'timeline-resource-graphs'"
|
||||
parameters="{
|
||||
origin: zoomController.toMillis(scroll.x),
|
||||
duration: zoomController.toMillis(scroll.width),
|
||||
graphs: timelineController.graphs()
|
||||
}">
|
||||
</mct-include>
|
||||
</div>
|
||||
<div ng-if="toiController.isActive()" class="l-follow-line s-follow-line"
|
||||
ng-style="{ left: toiController.x() - scroll.x + 'px' }"></div>
|
||||
</div>
|
||||
<div mct-scroll-x="scroll.x"
|
||||
class="t-pane-r-scroll-h-control l-scroll-control s-scroll-control">
|
||||
<div class="l-width-control"
|
||||
ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div mct-scroll-x="scroll.x"
|
||||
class="t-pane-r-scroll-h-control l-scroll-control s-scroll-control">
|
||||
<div class="l-width-control"
|
||||
ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mct-split-pane>
|
||||
</mct-split-pane>
|
||||
</span>
|
||||
|
||||
|
||||
</span>
|
||||
</mct-split-pane>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
|
||||
/**
|
||||
* Tracks time-of-interest in timelines, updating both scroll state
|
||||
* (when appropriate) and positioning of the displayed line.
|
||||
*/
|
||||
function TimelineTOIController(openmct, timerService, $scope) {
|
||||
this.openmct = openmct;
|
||||
this.timerService = timerService;
|
||||
this.$scope = $scope;
|
||||
|
||||
this.change = this.change.bind(this);
|
||||
this.bounds = this.bounds.bind(this);
|
||||
this.destroy = this.destroy.bind(this);
|
||||
|
||||
this.timerService.on('change', this.change);
|
||||
this.openmct.time.on('bounds', this.bounds);
|
||||
|
||||
this.$scope.$on('$destroy', this.destroy);
|
||||
|
||||
this.$scope.scroll.follow = this.timerService.hasTimer();
|
||||
if (this.$scope.zoomController) {
|
||||
this.bounds(this.openmct.time.bounds());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a `change` event from the timer service; track the
|
||||
* new timer.
|
||||
*/
|
||||
TimelineTOIController.prototype.change = function () {
|
||||
this.$scope.scroll.follow =
|
||||
this.$scope.scroll.follow || this.timerService.hasTimer();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a `bounds` event from the time API; scroll the timeline
|
||||
* to match the current bounds, if currently in follow mode.
|
||||
*/
|
||||
TimelineTOIController.prototype.bounds = function (bounds) {
|
||||
if (this.isFollowing()) {
|
||||
var start = this.timerService.convert(bounds.start);
|
||||
var end = this.timerService.convert(bounds.end);
|
||||
this.duration = bounds.end - bounds.start;
|
||||
this.$scope.zoomController.bounds(start, end);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a `$destroy` event from scope; detach all observers.
|
||||
*/
|
||||
TimelineTOIController.prototype.destroy = function () {
|
||||
this.timerService.off('change', this.change);
|
||||
this.openmct.time.off('bounds', this.bounds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the x position of the time-of-interest line,
|
||||
* in pixels from the left edge of the timeline area.
|
||||
*/
|
||||
TimelineTOIController.prototype.x = function () {
|
||||
var now = this.timerService.now();
|
||||
|
||||
if (now === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.$scope.zoomController.toPixels(this.timerService.now());
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an active time-of-interest to be shown.
|
||||
* @return {boolean} true when active
|
||||
*/
|
||||
TimelineTOIController.prototype.isActive = function () {
|
||||
return this.x() !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the timeline should be following time conductor bounds.
|
||||
* @return {boolean} true when following
|
||||
*/
|
||||
TimelineTOIController.prototype.isFollowing = function () {
|
||||
return !!this.$scope.scroll.follow && this.timerService.now() !== undefined;
|
||||
};
|
||||
|
||||
return TimelineTOIController;
|
||||
});
|
||||
@@ -32,7 +32,8 @@ define(
|
||||
// Prefer to start with the middle index
|
||||
var zoomLevels = ZOOM_CONFIGURATION.levels || [1000],
|
||||
zoomIndex = Math.floor(zoomLevels.length / 2),
|
||||
tickWidth = ZOOM_CONFIGURATION.width || 200;
|
||||
tickWidth = ZOOM_CONFIGURATION.width || 200,
|
||||
lastWidth = Number.MAX_VALUE; // Don't constrain prematurely
|
||||
|
||||
function toMillis(pixels) {
|
||||
return (pixels / tickWidth) * zoomLevels[zoomIndex];
|
||||
@@ -55,19 +56,29 @@ define(
|
||||
|
||||
function setScroll(x) {
|
||||
$window.requestAnimationFrame(function () {
|
||||
$scope.scroll.x = x;
|
||||
$scope.scroll.x = Math.min(
|
||||
Math.max(x, 0),
|
||||
lastWidth - $scope.scroll.width
|
||||
);
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
function initializeZoomFromTimespan(timespan) {
|
||||
var timelineDuration = timespan.getDuration();
|
||||
function initializeZoomFromStartEnd(start, end) {
|
||||
var duration = end - start;
|
||||
zoomIndex = 0;
|
||||
while (toMillis($scope.scroll.width) < timelineDuration &&
|
||||
while (toMillis($scope.scroll.width) < duration &&
|
||||
zoomIndex < zoomLevels.length - 1) {
|
||||
zoomIndex += 1;
|
||||
}
|
||||
setScroll(toPixels(timespan.getStart()));
|
||||
setScroll(toPixels(start));
|
||||
}
|
||||
|
||||
function initializeZoomFromTimespan(timespan) {
|
||||
return initializeZoomFromStartEnd(
|
||||
timespan.getStart(),
|
||||
timespan.getEnd()
|
||||
);
|
||||
}
|
||||
|
||||
function initializeZoom() {
|
||||
@@ -101,6 +112,13 @@ define(
|
||||
}
|
||||
return zoomLevels[zoomIndex];
|
||||
},
|
||||
/**
|
||||
* Adjust the current zoom bounds to fit both the
|
||||
* start and the end time provided.
|
||||
* @param {number} start the starting timestamp
|
||||
* @param {number} end the ending timestamp
|
||||
*/
|
||||
bounds: initializeZoomFromStartEnd,
|
||||
/**
|
||||
* Set the zoom level to fit the bounds of the timeline
|
||||
* being viewed.
|
||||
@@ -119,14 +137,14 @@ define(
|
||||
*/
|
||||
toMillis: toMillis,
|
||||
/**
|
||||
* Get the pixel width necessary to fit the specified
|
||||
* timestamp, expressed as an offset in milliseconds from
|
||||
* the start of the timeline.
|
||||
* Set the maximum timestamp value to be displayed, and get
|
||||
* the pixel width necessary to display this value.
|
||||
* @param {number} timestamp the time to display
|
||||
*/
|
||||
width: function (timestamp) {
|
||||
var pixels = Math.ceil(toPixels(timestamp * (1 + PADDING)));
|
||||
return Math.max($scope.scroll.width, pixels);
|
||||
lastWidth = Math.max($scope.scroll.width, pixels);
|
||||
return lastWidth;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"../../src/controllers/TimelineTOIController",
|
||||
"EventEmitter"
|
||||
], function (TimelineTOIController, EventEmitter) {
|
||||
describe("The timeline TOI controller", function () {
|
||||
var mockmct;
|
||||
var mockTimerService;
|
||||
var mockScope;
|
||||
var controller;
|
||||
|
||||
beforeEach(function () {
|
||||
mockmct = { time: new EventEmitter() };
|
||||
mockmct.time.bounds = jasmine.createSpy('bounds');
|
||||
mockTimerService = new EventEmitter();
|
||||
mockTimerService.getTimer = jasmine.createSpy('getTimer');
|
||||
mockTimerService.hasTimer = jasmine.createSpy('hasTimer');
|
||||
mockTimerService.now = jasmine.createSpy('now');
|
||||
mockTimerService.convert = jasmine.createSpy('convert');
|
||||
mockScope = new EventEmitter();
|
||||
mockScope.$on = mockScope.on.bind(mockScope);
|
||||
mockScope.zoomController = jasmine.createSpyObj('zoom', [
|
||||
'bounds',
|
||||
'toPixels'
|
||||
]);
|
||||
mockScope.scroll = { x: 10, width: 1000 };
|
||||
|
||||
spyOn(mockmct.time, "on").andCallThrough();
|
||||
spyOn(mockmct.time, "off").andCallThrough();
|
||||
spyOn(mockTimerService, "on").andCallThrough();
|
||||
spyOn(mockTimerService, "off").andCallThrough();
|
||||
|
||||
controller = new TimelineTOIController(
|
||||
mockmct,
|
||||
mockTimerService,
|
||||
mockScope
|
||||
);
|
||||
});
|
||||
|
||||
it("reports an undefined x position initially", function () {
|
||||
expect(controller.x()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("listens for bounds changes", function () {
|
||||
expect(mockmct.time.on)
|
||||
.toHaveBeenCalledWith('bounds', controller.bounds);
|
||||
});
|
||||
|
||||
it("listens for timer changes", function () {
|
||||
expect(mockTimerService.on)
|
||||
.toHaveBeenCalledWith('change', controller.change);
|
||||
});
|
||||
|
||||
it("is not active", function () {
|
||||
expect(controller.isActive()).toBe(false);
|
||||
});
|
||||
|
||||
describe("on $destroy from scope", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.emit("$destroy");
|
||||
});
|
||||
|
||||
it("unregisters listeners", function () {
|
||||
expect(mockmct.time.off)
|
||||
.toHaveBeenCalledWith('bounds', controller.bounds);
|
||||
expect(mockTimerService.off)
|
||||
.toHaveBeenCalledWith('change', controller.change);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a timer and timestamp present", function () {
|
||||
var mockTimer;
|
||||
var testNow;
|
||||
|
||||
beforeEach(function () {
|
||||
testNow = 333221;
|
||||
mockScope.zoomController.toPixels
|
||||
.andCallFake(function (millis) {
|
||||
return millis * 2;
|
||||
});
|
||||
mockTimerService.emit('change', mockTimer);
|
||||
mockTimerService.now.andReturn(testNow);
|
||||
});
|
||||
|
||||
it("reports an x value from the zoomController", function () {
|
||||
var now = mockTimerService.now();
|
||||
var expected = mockScope.zoomController.toPixels(now);
|
||||
expect(controller.x()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when follow mode is disabled", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.scroll.follow = false;
|
||||
});
|
||||
|
||||
it("ignores bounds events", function () {
|
||||
mockmct.time.emit('bounds', { start: 0, end: 1000 });
|
||||
expect(mockScope.zoomController.bounds)
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when follow mode is enabled", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.scroll.follow = true;
|
||||
mockTimerService.now.andReturn(500);
|
||||
});
|
||||
|
||||
it("zooms on bounds events", function () {
|
||||
mockmct.time.emit('bounds', { start: 0, end: 1000 });
|
||||
expect(mockScope.zoomController.bounds)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -47,7 +47,7 @@ define([
|
||||
"name": "Export as JSON",
|
||||
"implementation": ExportAsJSONAction,
|
||||
"category": "contextual",
|
||||
"cssClass": "icon-save",
|
||||
"cssClass": "icon-export",
|
||||
"depends": [
|
||||
"exportService",
|
||||
"policyService",
|
||||
@@ -59,7 +59,7 @@ define([
|
||||
"name": "Import from JSON",
|
||||
"implementation": ImportAsJSONAction,
|
||||
"category": "contextual",
|
||||
"cssClass": "icon-download",
|
||||
"cssClass": "icon-import",
|
||||
"depends": [
|
||||
"exportService",
|
||||
"identifierService",
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
*/
|
||||
define(
|
||||
[
|
||||
'../../../src/api/objects/object-utils'
|
||||
'../../../src/api/objects/object-utils',
|
||||
'lodash'
|
||||
],
|
||||
function (
|
||||
objectUtils
|
||||
objectUtils,
|
||||
_
|
||||
) {
|
||||
|
||||
var ZERO = function () {
|
||||
@@ -189,14 +191,17 @@ define(
|
||||
return fullRequest;
|
||||
};
|
||||
|
||||
function asSeries(telemetry, defaultDomain, defaultRange, sourceMap) {
|
||||
function getValue(index, key) {
|
||||
return telemetry[index][sourceMap[key].source];
|
||||
}
|
||||
|
||||
function asSeries(telemetry, defaultDomain, defaultRange) {
|
||||
return {
|
||||
getRangeValue: function (index, range) {
|
||||
return telemetry[index][range || defaultRange];
|
||||
return getValue(index, range || defaultRange);
|
||||
},
|
||||
getDomainValue: function (index, domain) {
|
||||
return telemetry[index][domain || defaultDomain];
|
||||
return getValue(index, domain || defaultDomain);
|
||||
},
|
||||
getPointCount: function () {
|
||||
return telemetry.length;
|
||||
@@ -223,9 +228,11 @@ define(
|
||||
var telemetryAPI = this.openmct.telemetry;
|
||||
|
||||
var metadata = telemetryAPI.getMetadata(domainObject);
|
||||
var defaultDomain = metadata.valuesForHints(['domain'])[0].source;
|
||||
var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
|
||||
var defaultRange = metadata.valuesForHints(['range'])[0];
|
||||
defaultRange = defaultRange ? defaultRange.source : undefined;
|
||||
defaultRange = defaultRange ? defaultRange.key : undefined;
|
||||
|
||||
var sourceMap = _.indexBy(metadata.values(), 'key');
|
||||
|
||||
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
|
||||
telemetryAPI.legacyProvider;
|
||||
@@ -250,7 +257,7 @@ define(
|
||||
requestTelemetryFromService().then(getRelevantResponse);
|
||||
} else {
|
||||
return telemetryAPI.request(domainObject, fullRequest).then(function (telemetry) {
|
||||
return asSeries(telemetry, defaultDomain, defaultRange);
|
||||
return asSeries(telemetry, defaultDomain, defaultRange, sourceMap);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -286,15 +293,17 @@ define(
|
||||
var telemetryAPI = this.openmct.telemetry;
|
||||
|
||||
var metadata = telemetryAPI.getMetadata(domainObject);
|
||||
var defaultDomain = metadata.valuesForHints(['domain'])[0].source;
|
||||
var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
|
||||
var defaultRange = metadata.valuesForHints(['range'])[0];
|
||||
defaultRange = defaultRange ? defaultRange.source : undefined;
|
||||
defaultRange = defaultRange ? defaultRange.key : undefined;
|
||||
|
||||
var sourceMap = _.indexBy(metadata.values(), 'key');
|
||||
|
||||
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
|
||||
telemetryAPI.legacyProvider;
|
||||
|
||||
function update(telemetry) {
|
||||
callback(asSeries([telemetry], defaultDomain, defaultRange));
|
||||
callback(asSeries([telemetry], defaultDomain, defaultRange, sourceMap));
|
||||
}
|
||||
|
||||
// Unpack the relevant telemetry series
|
||||
|
||||
@@ -34,6 +34,7 @@ define(
|
||||
mockUnsubscribe,
|
||||
telemetry,
|
||||
mockTelemetryAPI,
|
||||
mockMetadata,
|
||||
mockAPI;
|
||||
|
||||
function mockPromise(value) {
|
||||
@@ -90,14 +91,23 @@ define(
|
||||
"findRequestProvider",
|
||||
"findSubscriptionProvider"
|
||||
]);
|
||||
mockTelemetryAPI.getMetadata.andReturn({
|
||||
valuesForHints: function (hint) {
|
||||
var metadatum = {};
|
||||
metadatum[hint] = "foo";
|
||||
return [metadatum];
|
||||
}
|
||||
|
||||
mockMetadata = jasmine.createSpyObj('telemetryMetadata', [
|
||||
'valuesForHints',
|
||||
'values'
|
||||
]);
|
||||
|
||||
mockMetadata.valuesForHints.andCallFake(function (hints) {
|
||||
var hint = hints[0];
|
||||
var metadatum = {
|
||||
key: 'default' + hint
|
||||
};
|
||||
metadatum[hint] = "foo";
|
||||
return [metadatum];
|
||||
});
|
||||
|
||||
mockTelemetryAPI.getMetadata.andReturn(mockMetadata);
|
||||
|
||||
mockAPI = {
|
||||
telemetry: mockTelemetryAPI,
|
||||
time: {
|
||||
@@ -150,8 +160,8 @@ define(
|
||||
key: "testKey", // from model
|
||||
start: 42, // from argument
|
||||
domain: 'mockTimeSystem',
|
||||
domains: [{ domain: "foo" }],
|
||||
ranges: [{ range: "foo" }]
|
||||
domains: [{ domain: "foo", key: 'defaultdomain' }],
|
||||
ranges: [{ range: "foo", key: 'defaultrange' }]
|
||||
}]);
|
||||
});
|
||||
|
||||
@@ -172,8 +182,8 @@ define(
|
||||
start: 0,
|
||||
end: 1,
|
||||
domain: 'mockTimeSystem',
|
||||
domains: [{ domain: "foo" }],
|
||||
ranges: [{ range: "foo" }]
|
||||
domains: [{ domain: "foo", key: 'defaultdomain' }],
|
||||
ranges: [{ range: "foo", key: 'defaultrange' }]
|
||||
});
|
||||
});
|
||||
|
||||
@@ -191,8 +201,8 @@ define(
|
||||
start: 0,
|
||||
end: 1,
|
||||
domain: 'mockTimeSystem',
|
||||
domains: [{ domain: "foo" }],
|
||||
ranges: [{ range: "foo" }]
|
||||
domains: [{ domain: "foo", key: 'defaultdomain' }],
|
||||
ranges: [{ range: "foo", key: 'defaultrange' }]
|
||||
});
|
||||
});
|
||||
|
||||
@@ -240,6 +250,21 @@ define(
|
||||
var mockProvider = {};
|
||||
var dunzo = false;
|
||||
|
||||
mockMetadata.values.andReturn([
|
||||
{
|
||||
key: 'defaultrange',
|
||||
source: 'prop1'
|
||||
},
|
||||
{
|
||||
key: 'defaultdomain',
|
||||
source: 'prop2'
|
||||
},
|
||||
{
|
||||
key: 'prop3',
|
||||
source: 'prop3'
|
||||
}
|
||||
]);
|
||||
|
||||
mockTelemetryAPI.findRequestProvider.andReturn(mockProvider);
|
||||
mockTelemetryAPI.request.andReturn(Promise.resolve(mockTelemetry));
|
||||
|
||||
@@ -257,6 +282,18 @@ define(
|
||||
expect(returnedTelemetry.getDomainValue).toBeDefined();
|
||||
expect(returnedTelemetry.getRangeValue).toBeDefined();
|
||||
expect(returnedTelemetry.getPointCount()).toBe(2);
|
||||
// Default domain + remap should work.
|
||||
expect(returnedTelemetry.getDomainValue(0)).toBe('val2');
|
||||
expect(returnedTelemetry.getDomainValue(1)).toBe('val5');
|
||||
// explicit domain should work
|
||||
expect(returnedTelemetry.getDomainValue(0, 'prop3')).toBe('val3');
|
||||
expect(returnedTelemetry.getDomainValue(1, 'prop3')).toBe('val6');
|
||||
// default range + remap should work
|
||||
expect(returnedTelemetry.getRangeValue(0)).toBe('val1');
|
||||
expect(returnedTelemetry.getRangeValue(1)).toBe('val4');
|
||||
// explicit range should work
|
||||
expect(returnedTelemetry.getRangeValue(0, 'prop3')).toBe('val3');
|
||||
expect(returnedTelemetry.getRangeValue(1, 'prop3')).toBe('val6');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -275,8 +312,8 @@ define(
|
||||
start: 0,
|
||||
end: 1,
|
||||
domain: 'mockTimeSystem',
|
||||
domains: [{ domain: "foo" }],
|
||||
ranges: [{ range: "foo" }]
|
||||
domains: [{ domain: "foo", key: "defaultdomain" }],
|
||||
ranges: [{ range: "foo", key: "defaultrange" }]
|
||||
}]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Open MCT https://nasa.github.io/openmct/
|
||||
* Version: ${version}
|
||||
* Built: ${timestamp}
|
||||
* Revision: ${revision}
|
||||
* Branch: ${branch}
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ define([
|
||||
name: 'Name'
|
||||
});
|
||||
|
||||
metadata.domains.forEach(function (domain, index) {
|
||||
(metadata.domains || []).forEach(function (domain, index) {
|
||||
var valueMetadata = _.clone(domain);
|
||||
valueMetadata.hints = {
|
||||
domain: index + 1
|
||||
@@ -43,11 +43,11 @@ define([
|
||||
valueMetadatas.push(valueMetadata);
|
||||
});
|
||||
|
||||
metadata.ranges.forEach(function (range, index) {
|
||||
(metadata.ranges || []).forEach(function (range, index) {
|
||||
var valueMetadata = _.clone(range);
|
||||
valueMetadata.hints = {
|
||||
range: index,
|
||||
priority: index + metadata.domains.length + 1
|
||||
priority: index + (metadata.domains || []).length + 1
|
||||
};
|
||||
|
||||
if (valueMetadata.type === 'enum') {
|
||||
|
||||
@@ -25,7 +25,7 @@ define([
|
||||
], function (
|
||||
_
|
||||
) {
|
||||
|
||||
|
||||
// TODO: needs reference to formatService;
|
||||
function TelemetryValueFormatter(valueMetadata, formatService) {
|
||||
var numberFormatter = {
|
||||
@@ -33,7 +33,12 @@ define([
|
||||
return Number(x);
|
||||
},
|
||||
format: function (x) {
|
||||
return x;
|
||||
var number = parseFloat(x);
|
||||
if (isNaN(number)){
|
||||
return x;
|
||||
} else {
|
||||
return number.toFixed(2);
|
||||
}
|
||||
},
|
||||
validate: function (x) {
|
||||
return true;
|
||||
|
||||
45
src/plugins/buildInfo/plugin.js
Normal file
45
src/plugins/buildInfo/plugin.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
], function (
|
||||
) {
|
||||
return function (buildInfo) {
|
||||
return function (openmct) {
|
||||
var aliases = { timestamp: "Built" };
|
||||
var descriptions = {
|
||||
timestamp: "The date on which this version of Open MCT was built.",
|
||||
revision: "A unique revision identifier for the client sources.",
|
||||
branch: "The name of the branch that was used during the build."
|
||||
};
|
||||
|
||||
Object.keys(buildInfo).forEach(function (key) {
|
||||
openmct.legacyExtension("versions", {
|
||||
key: key,
|
||||
name: aliases[key] || (key.charAt(0).toUpperCase() + key.substring(1)),
|
||||
value: buildInfo[key],
|
||||
description: descriptions[key]
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
50
src/plugins/buildInfo/pluginSpec.js
Normal file
50
src/plugins/buildInfo/pluginSpec.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./plugin'
|
||||
], function (plugin) {
|
||||
describe("The buildInfo plugin", function () {
|
||||
var mockmct;
|
||||
var testInfo;
|
||||
|
||||
beforeEach(function () {
|
||||
mockmct = jasmine.createSpyObj('openmct', ['legacyExtension']);
|
||||
testInfo = { foo: 123, bar: "baz" };
|
||||
plugin(testInfo)(mockmct);
|
||||
});
|
||||
|
||||
it("registers versions extensions", function () {
|
||||
Object.keys(testInfo).forEach(function (key) {
|
||||
expect(mockmct.legacyExtension).toHaveBeenCalledWith(
|
||||
"versions",
|
||||
{
|
||||
key: key,
|
||||
name: jasmine.any(String),
|
||||
value: testInfo[key],
|
||||
description: undefined
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -27,8 +27,9 @@ define([
|
||||
'../../platform/features/autoflow/plugin',
|
||||
'./timeConductor/plugin',
|
||||
'../../example/imagery/plugin',
|
||||
'./summaryWidget/plugin',
|
||||
'../../platform/import-export/bundle',
|
||||
'./summaryWidget/plugin'
|
||||
'./telemetryMean/plugin'
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
@@ -37,7 +38,8 @@ define([
|
||||
TimeConductorPlugin,
|
||||
ExampleImagery,
|
||||
SummaryWidget,
|
||||
ImportExport
|
||||
ImportExport,
|
||||
TelemetryMean
|
||||
) {
|
||||
var bundleMap = {
|
||||
CouchDB: 'platform/persistence/couch',
|
||||
@@ -122,8 +124,8 @@ define([
|
||||
};
|
||||
|
||||
plugins.ExampleImagery = ExampleImagery;
|
||||
|
||||
plugins.SummaryWidget = SummaryWidget;
|
||||
plugins.TelemetryMean = TelemetryMean;
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
||||
@@ -10,7 +10,34 @@ define(['./src/SummaryWidget', './SummaryWidgetsCompositionPolicy'], function (S
|
||||
initialize: function (domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {};
|
||||
}
|
||||
domainObject.openNewTab = 'thisTab';
|
||||
},
|
||||
form: [
|
||||
{
|
||||
"key": "url",
|
||||
"name": "URL",
|
||||
"control": "textfield",
|
||||
"pattern": "^(ftp|https?)\\:\\/\\/",
|
||||
"required": false,
|
||||
"cssClass": "l-input-lg"
|
||||
},
|
||||
{
|
||||
"key": "openNewTab",
|
||||
"name": "Tab to Open Hyperlink",
|
||||
"control": "select",
|
||||
"options": [
|
||||
{
|
||||
"value": "thisTab",
|
||||
"name": "Open in this tab"
|
||||
},
|
||||
{
|
||||
"value": "newTab",
|
||||
"name": "Open in a new tab"
|
||||
}
|
||||
],
|
||||
"cssClass": "l-inline"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function initViewProvider(openmct) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<li class="t-condition">
|
||||
<label>when</label>
|
||||
<label class="t-condition-context">when</label>
|
||||
<span class="controls">
|
||||
<span class="t-configuration"> </span>
|
||||
<span class="t-value-inputs"> </span>
|
||||
</span>
|
||||
<span class="flex-elem l-condition-action-buttons-wrapper">
|
||||
<a class="s-icon-button icon-duplicate t-duplicate"></a>
|
||||
<a class="s-icon-button icon-trash t-delete"></a>
|
||||
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this condition"></a>
|
||||
<a class="s-icon-button icon-trash t-delete" title="Delete this condition"></a>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
<span class="flex-elem rule-title">Default Title</span>
|
||||
<span class="flex-elem rule-description grows">Rule description goes here</span>
|
||||
<span class="flex-elem l-rule-action-buttons-wrapper">
|
||||
<a class="s-icon-button icon-duplicate t-duplicate"></a>
|
||||
<a class="s-icon-button icon-trash t-delete"></a>
|
||||
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this rule"></a>
|
||||
<a class="s-icon-button icon-trash t-delete" title="Delete this rule"></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="widget-rule-content expanded">
|
||||
@@ -52,14 +52,14 @@
|
||||
<select class="t-trigger">
|
||||
<option value="any">any condition is met</option>
|
||||
<option value="all">all conditions are met</option>
|
||||
<option value="js">the following JavaScript evaluates to true</option>
|
||||
<!-- <option value="js">the following JavaScript evaluates to true</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<li class="t-rule-js-condition-input-holder">
|
||||
<!-- <li class="t-rule-js-condition-input-holder">
|
||||
<textarea placeholder="" class="med t-rule-js-condition-input"></textarea>
|
||||
</li>
|
||||
</li> -->
|
||||
<li>
|
||||
<label></label>
|
||||
<span class="controls">
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<span class="t-value-inputs"></span>
|
||||
</span>
|
||||
<span class="flex-elem l-widget-test-data-item-action-buttons-wrapper">
|
||||
<a class="s-icon-button icon-duplicate t-duplicate"></a>
|
||||
<a class="s-icon-button icon-trash t-delete"></a>
|
||||
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this test value"></a>
|
||||
<a class="s-icon-button icon-trash t-delete" title="Delete this test value"></a>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div>
|
||||
<div class="section-header"><span class="view-control expanded"></span>Test Data Values</div>
|
||||
<div class="t-widget-test-data-content w-widget-test-data-content expanded">
|
||||
<div class="flex-accordion-holder">
|
||||
<div class="flex-accordion-holder t-widget-test-data-content w-widget-test-data-content">
|
||||
<div class="l-enable">
|
||||
<label class="checkbox custom">Apply Test Values
|
||||
<input type="checkbox" class="t-test-data-checkbox">
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="holder l-flex-col flex-elem grows widget-edit-holder">
|
||||
<div class="widget-test-data"></div>
|
||||
<div class="section-header">Rules</div>
|
||||
<div class="holder widget-rules-wrapper flex-elem grows">
|
||||
<div class="holder l-flex-col l-flex-accordion flex-elem grows widget-edit-holder expanded-widget-test-data expanded-widget-rules">
|
||||
<div class="section-header"><span class="view-control t-view-control-test-data expanded"></span>Test Data Values</div>
|
||||
<div class="widget-test-data flex-accordion-holder"></div>
|
||||
<div class="section-header"><span class="view-control t-view-control-rules expanded"></span>Rules</div>
|
||||
<div class="holder widget-rules-wrapper flex-elem expanded">
|
||||
<div id="ruleArea" class="widget-rules"></div>
|
||||
<div class="holder add-rule-button-wrapper align-right">
|
||||
<a id="addRule" class="s-button major labeled add-rule-button icon-plus">Add Rule</a>
|
||||
|
||||
@@ -82,8 +82,8 @@ define([
|
||||
this.duplicateButton.on('click', this.duplicate);
|
||||
|
||||
this.selects.object = new ObjectSelect(this.config, this.conditionManager, [
|
||||
['any', 'Any Telemetry'],
|
||||
['all', 'All Telemetry']
|
||||
['any', 'any telemetry'],
|
||||
['all', 'all telemetry']
|
||||
]);
|
||||
this.selects.key = new KeySelect(this.config, this.selects.object, this.conditionManager);
|
||||
this.selects.operation = new OperationSelect(
|
||||
|
||||
@@ -57,7 +57,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] === input[1];
|
||||
},
|
||||
text: 'is Equal To',
|
||||
text: 'is equal to',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -68,7 +68,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] !== input[1];
|
||||
},
|
||||
text: 'is Not Equal To',
|
||||
text: 'is not equal to',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -79,7 +79,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] > input[1];
|
||||
},
|
||||
text: 'is Greater Than',
|
||||
text: 'is greater than',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -90,7 +90,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] < input[1];
|
||||
},
|
||||
text: 'is Less Than',
|
||||
text: 'is less than',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -101,7 +101,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] >= input[1];
|
||||
},
|
||||
text: 'is Greater Than or Equal To',
|
||||
text: 'is greater than or equal to',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -112,7 +112,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] <= input[1];
|
||||
},
|
||||
text: 'is Less Than or Equal To',
|
||||
text: 'is less than or equal to',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -123,7 +123,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] > input[1] && input[0] < input[2];
|
||||
},
|
||||
text: 'is Between',
|
||||
text: 'is between',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 2,
|
||||
getDescription: function (values) {
|
||||
@@ -134,7 +134,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] < input[1] || input[0] > input[2];
|
||||
},
|
||||
text: 'is Not Between',
|
||||
text: 'is not between',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 2,
|
||||
getDescription: function (values) {
|
||||
@@ -145,7 +145,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] && input[1] && input[0].includes(input[1]);
|
||||
},
|
||||
text: 'Text Contains',
|
||||
text: 'text contains',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -156,7 +156,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] && input[1] && !input[0].includes(input[1]);
|
||||
},
|
||||
text: 'Text Does Not Contain',
|
||||
text: 'text does not contain',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -167,7 +167,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0].startsWith(input[1]);
|
||||
},
|
||||
text: 'Text Starts With',
|
||||
text: 'text starts with',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -178,7 +178,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0].endsWith(input[1]);
|
||||
},
|
||||
text: 'Text Ends With',
|
||||
text: 'text ends with',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -189,7 +189,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return input[0] === input[1];
|
||||
},
|
||||
text: 'Text is Exactly',
|
||||
text: 'text is exactly',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -200,7 +200,7 @@ define([], function () {
|
||||
operation: function (input) {
|
||||
return typeof input[0] === 'undefined';
|
||||
},
|
||||
text: 'is Undefined',
|
||||
text: 'is undefined',
|
||||
appliesTo: ['string', 'number'],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
|
||||
@@ -28,8 +28,8 @@ define ([
|
||||
this.supportedCallbacks = ['add', 'remove', 'load', 'metadata', 'receiveTelemetry'];
|
||||
|
||||
this.keywordLabels = {
|
||||
any: 'Any Telemetry',
|
||||
all: 'All Telemetry'
|
||||
any: 'any Telemetry',
|
||||
all: 'all Telemetry'
|
||||
};
|
||||
|
||||
this.telemetryMetadataById = {
|
||||
|
||||
@@ -362,7 +362,10 @@ define([
|
||||
* Build {Condition} objects from configuration and rebuild associated view
|
||||
*/
|
||||
Rule.prototype.refreshConditions = function () {
|
||||
var self = this;
|
||||
var self = this,
|
||||
$condition = null,
|
||||
loopCnt = 0,
|
||||
triggerContextStr = self.config.trigger === 'any' ? ' or ' : ' and ';
|
||||
|
||||
self.conditions = [];
|
||||
$('.t-condition', this.domElement).remove();
|
||||
@@ -382,11 +385,17 @@ define([
|
||||
this.jsConditionArea.hide();
|
||||
this.addConditionButton.show();
|
||||
self.conditions.forEach(function (condition) {
|
||||
$('li:last-of-type', self.conditionArea).before(condition.getDOM());
|
||||
$condition = condition.getDOM();
|
||||
$('li:last-of-type', self.conditionArea).before($condition);
|
||||
if (loopCnt > 0) {
|
||||
$('.t-condition-context', $condition).html(triggerContextStr + ' when');
|
||||
}
|
||||
loopCnt++;
|
||||
});
|
||||
}
|
||||
|
||||
if (self.conditions.length === 1) {
|
||||
// Only one condition
|
||||
self.conditions[0].hideButtons();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,27 +47,57 @@ define([
|
||||
this.activeId = 'default';
|
||||
this.rulesById = {};
|
||||
this.domElement = $(widgetTemplate);
|
||||
this.toggleRulesControl = $('.t-view-control-rules', this.domElement);
|
||||
this.toggleTestDataControl = $('.t-view-control-test-data', this.domElement);
|
||||
this.widgetButton = this.domElement.children('#widget');
|
||||
this.editing = false;
|
||||
this.container = '';
|
||||
this.editListenerUnsubscribe = $.noop;
|
||||
|
||||
this.outerWrapper = $('.widget-edit-holder', this.domElement);
|
||||
this.ruleArea = $('#ruleArea', this.domElement);
|
||||
this.configAreaRules = $('.widget-rules-wrapper', this.domElement);
|
||||
|
||||
this.testDataArea = $('.widget-test-data', this.domElement);
|
||||
this.addRuleButton = $('#addRule', this.domElement);
|
||||
|
||||
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
|
||||
this.testDataManager = new TestDataManager(this.domainObject, this.conditionManager, this.openmct);
|
||||
|
||||
this.watchForChanges = this.watchForChanges.bind(this);
|
||||
this.show = this.show.bind(this);
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this.addRule = this.addRule.bind(this);
|
||||
this.onEdit = this.onEdit.bind(this);
|
||||
|
||||
this.addHyperlink(domainObject.url, domainObject.openNewTab);
|
||||
this.watchForChanges(openmct, domainObject);
|
||||
|
||||
var id = this.domainObject.identifier.key,
|
||||
self = this,
|
||||
oldDomainObject,
|
||||
statusCapability;
|
||||
|
||||
/**
|
||||
* Toggles the configuration area for test data in the view
|
||||
* @private
|
||||
*/
|
||||
function toggleTestData() {
|
||||
self.outerWrapper.toggleClass('expanded-widget-test-data');
|
||||
self.toggleTestDataControl.toggleClass('expanded');
|
||||
}
|
||||
this.toggleTestDataControl.on('click', toggleTestData);
|
||||
|
||||
/**
|
||||
* Toggles the configuration area for rules in the view
|
||||
* @private
|
||||
*/
|
||||
function toggleRules() {
|
||||
self.outerWrapper.toggleClass('expanded-widget-rules');
|
||||
self.toggleRulesControl.toggleClass('expanded');
|
||||
}
|
||||
this.toggleRulesControl.on('click', toggleRules);
|
||||
|
||||
openmct.$injector.get('objectService')
|
||||
.getObjects([id])
|
||||
.then(function (objs) {
|
||||
@@ -82,6 +112,40 @@ define([
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* adds or removes href to widget button and adds or removes openInNewTab
|
||||
* @param {string} url String that denotes the url to be opened
|
||||
* @param {string} openNewTab String that denotes wether to open link in new tab or not
|
||||
*/
|
||||
SummaryWidget.prototype.addHyperlink = function (url, openNewTab) {
|
||||
if (url) {
|
||||
this.widgetButton.attr('href', url);
|
||||
} else {
|
||||
this.widgetButton.removeAttr('href');
|
||||
}
|
||||
|
||||
if (openNewTab === 'newTab') {
|
||||
this.widgetButton.attr('target', '_blank');
|
||||
} else {
|
||||
this.widgetButton.removeAttr('target');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* adds a listener to the object to watch for any changes made by user
|
||||
* only executes if changes are observed
|
||||
* @param {openmct} Object Instance of OpenMCT
|
||||
* @param {domainObject} Object instance of this object
|
||||
*/
|
||||
SummaryWidget.prototype.watchForChanges = function (openmct, domainObject) {
|
||||
openmct.objects.observe(domainObject, '*', function (newDomainObject) {
|
||||
if (newDomainObject.url !== this.domainObject.url ||
|
||||
newDomainObject.openNewTab !== this.domainObject.openNewTab) {
|
||||
this.addHyperlink(newDomainObject.url, newDomainObject.openNewTab);
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the Summary Widget's DOM, performs other necessary setup, and attaches
|
||||
* this Summary Widget's view to the supplied container.
|
||||
|
||||
@@ -29,21 +29,10 @@ define([
|
||||
this.config = this.domainObject.configuration.testDataConfig;
|
||||
this.testCache = {};
|
||||
|
||||
this.configArea = $('.t-widget-test-data-content', this.domElement);
|
||||
this.itemArea = $('.t-test-data-config', this.domElement);
|
||||
this.toggleConfigButton = $('.view-control', this.domElement);
|
||||
this.addItemButton = $('.add-test-condition', this.domElement);
|
||||
this.testDataInput = $('.t-test-data-checkbox', this.domElement);
|
||||
|
||||
/**
|
||||
* Toggles the configuration area for test data in the view
|
||||
* @private
|
||||
*/
|
||||
function toggleConfig() {
|
||||
self.configArea.toggleClass('expanded');
|
||||
self.toggleConfigButton.toggleClass('expanded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles whether the associated {ConditionEvaluator} uses the actual
|
||||
* subscription cache or the test data cache
|
||||
@@ -56,7 +45,6 @@ define([
|
||||
self.updateTestCache();
|
||||
}
|
||||
|
||||
this.toggleConfigButton.on('click', toggleConfig);
|
||||
this.addItemButton.on('click', function () {
|
||||
self.initItem();
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@ define(['./Select'], function (Select) {
|
||||
items.splice(0, 0, ['',NULLVALUE]);
|
||||
this.select.setOptions(items);
|
||||
|
||||
if(this.select.options.length < 2){
|
||||
if (this.select.options.length < 2) {
|
||||
this.select.hide();
|
||||
} else if (this.select.options.length > 1) {
|
||||
this.select.show();
|
||||
|
||||
@@ -85,7 +85,7 @@ define(['./Select'], function (Select) {
|
||||
items.splice(0, 0, ['', NULLVALUE]);
|
||||
this.select.setOptions(items);
|
||||
|
||||
if(this.select.options.length < 2){
|
||||
if (this.select.options.length < 2) {
|
||||
this.select.hide();
|
||||
} else {
|
||||
this.select.show();
|
||||
|
||||
@@ -124,7 +124,7 @@ define(['../src/ConditionEvaluator'], function (ConditionEvaluator) {
|
||||
operation: function (input) {
|
||||
return input[0] > input[1];
|
||||
},
|
||||
text: 'is Greater Than',
|
||||
text: 'is greater than',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1,
|
||||
getDescription: function (values) {
|
||||
@@ -135,7 +135,7 @@ define(['../src/ConditionEvaluator'], function (ConditionEvaluator) {
|
||||
operation: function (input) {
|
||||
return input[0] < input[1];
|
||||
},
|
||||
text: 'is Less Than',
|
||||
text: 'is less than',
|
||||
appliesTo: ['number'],
|
||||
inputCount: 1
|
||||
},
|
||||
@@ -143,7 +143,7 @@ define(['../src/ConditionEvaluator'], function (ConditionEvaluator) {
|
||||
operation: function (input) {
|
||||
return input[0] && input[1] && input[0].includes(input[1]);
|
||||
},
|
||||
text: 'Text Contains',
|
||||
text: 'text contains',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1
|
||||
},
|
||||
@@ -151,7 +151,7 @@ define(['../src/ConditionEvaluator'], function (ConditionEvaluator) {
|
||||
operation: function (input) {
|
||||
return input[0] === input[1];
|
||||
},
|
||||
text: 'Text is Exactly',
|
||||
text: 'text is exactly',
|
||||
appliesTo: ['string'],
|
||||
inputCount: 1
|
||||
},
|
||||
|
||||
@@ -348,7 +348,7 @@ define(['../src/ConditionManager'], function (ConditionManager) {
|
||||
|
||||
it('gets the human-readable name of a composition object', function () {
|
||||
expect(conditionManager.getObjectName('mockCompObject1')).toEqual('Object 1');
|
||||
expect(conditionManager.getObjectName('all')).toEqual('All Telemetry');
|
||||
expect(conditionManager.getObjectName('all')).toEqual('all Telemetry');
|
||||
});
|
||||
|
||||
it('gets the human-readable name of a telemetry field', function () {
|
||||
|
||||
@@ -209,19 +209,23 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
|
||||
expect(changeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('responds to input of text properties', function () {
|
||||
var testInputs = ['name', 'label', 'message', 'jsCondition'],
|
||||
input;
|
||||
/*
|
||||
test for js condition commented out for v1
|
||||
*/
|
||||
|
||||
testInputs.forEach(function (key) {
|
||||
input = testRule.textInputs[key];
|
||||
input.prop('value', 'A new ' + key);
|
||||
input.trigger('input');
|
||||
expect(mockRuleConfig[key]).toEqual('A new ' + key);
|
||||
});
|
||||
// it('responds to input of text properties', function () {
|
||||
// var testInputs = ['name', 'label', 'message', 'jsCondition'],
|
||||
// input;
|
||||
|
||||
expect(changeSpy).toHaveBeenCalled();
|
||||
});
|
||||
// testInputs.forEach(function (key) {
|
||||
// input = testRule.textInputs[key];
|
||||
// input.prop('value', 'A new ' + key);
|
||||
// input.trigger('input');
|
||||
// expect(mockRuleConfig[key]).toEqual('A new ' + key);
|
||||
// });
|
||||
|
||||
// expect(changeSpy).toHaveBeenCalled();
|
||||
// });
|
||||
|
||||
it('allows input for when the rule triggers', function () {
|
||||
testRule.trigger.prop('value', 'all');
|
||||
@@ -248,14 +252,9 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
|
||||
expect(mockWidgetDnD.dragStart).toHaveBeenCalledWith('mockRule');
|
||||
});
|
||||
|
||||
it('shows the JavaScript conditional area when js mode is active', function () {
|
||||
testRule.trigger.prop('value', 'any');
|
||||
testRule.trigger.trigger('change');
|
||||
expect(testRule.jsConditionArea.css('display')).toEqual('none');
|
||||
testRule.trigger.prop('value', 'js');
|
||||
testRule.trigger.trigger('change');
|
||||
expect(testRule.jsConditionArea.css('display')).not.toEqual('none');
|
||||
});
|
||||
/*
|
||||
test for js condition commented out for v1
|
||||
*/
|
||||
|
||||
it('can remove a condition from its configuration', function () {
|
||||
testRule.removeCondition(0);
|
||||
|
||||
@@ -65,6 +65,7 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) {
|
||||
]);
|
||||
mockOpenMCT.composition.get.andReturn(mockComposition);
|
||||
mockOpenMCT.objects.mutate = jasmine.createSpy('mutate');
|
||||
mockOpenMCT.objects.observe = function () {};
|
||||
|
||||
summaryWidget = new SummaryWidget(mockDomainObject, mockOpenMCT);
|
||||
mockContainer = document.createElement('div');
|
||||
@@ -151,5 +152,14 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) {
|
||||
});
|
||||
expect(summaryWidget.domainObject.configuration.ruleOrder).toEqual(['default', 'rule1', 'rule0']);
|
||||
});
|
||||
|
||||
it('adds hyperlink to the widget button and sets newTab preference', function () {
|
||||
summaryWidget.addHyperlink('https://www.nasa.gov', 'newTab');
|
||||
|
||||
var widgetButton = $('#widget', mockContainer);
|
||||
|
||||
expect(widgetButton.attr('href')).toEqual('https://www.nasa.gov');
|
||||
expect(widgetButton.attr('target')).toEqual('_blank');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
60
src/plugins/telemetryMean/plugin.js
Executable file
60
src/plugins/telemetryMean/plugin.js
Executable file
@@ -0,0 +1,60 @@
|
||||
define([
|
||||
'./src/TelemetryMeanProvider',
|
||||
'./src/TelemetryMeanActionDecorator'
|
||||
],
|
||||
function (
|
||||
TelemetryMeanProvider, TelemetryMeanActionDecorator) {
|
||||
var DEFAULT_SAMPLES = 10;
|
||||
|
||||
function plugin() {
|
||||
return function install(openmct) {
|
||||
openmct.types.addType('telemetry-mean', {
|
||||
name: 'Telemetry Filter',
|
||||
description: 'Provides telemetry values that represent the mean of the last N values of a telemetry stream',
|
||||
creatable: true,
|
||||
cssClass: 'icon-telemetry',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.samples = DEFAULT_SAMPLES;
|
||||
domainObject.telemetry = {
|
||||
values: [
|
||||
{
|
||||
key: "utc",
|
||||
name: "Time",
|
||||
format: "utc",
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
name: "Value",
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
form: [
|
||||
{
|
||||
"key": "telemetryPoint",
|
||||
"name": "Telemetry Point",
|
||||
"control": "textfield",
|
||||
"required": true,
|
||||
"cssClass": "l-input-lg"
|
||||
},
|
||||
{
|
||||
"key": "samples",
|
||||
"name": "Samples to Average",
|
||||
"control": "textfield",
|
||||
"required": true,
|
||||
"cssClass": "l-input-sm"
|
||||
}
|
||||
]
|
||||
});
|
||||
openmct.telemetry.addProvider(new TelemetryMeanProvider(openmct));
|
||||
};
|
||||
}
|
||||
|
||||
return plugin;
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
define([], function () {
|
||||
function TelemetryMeanActionDecorator (openmct, actionService) {
|
||||
this.actionService = actionService;
|
||||
this.openmct = openmct;
|
||||
|
||||
[
|
||||
'decorateAction',
|
||||
'getActions',
|
||||
'updateTelemetryFromLinkedObject'
|
||||
].forEach(function (name) {
|
||||
this[name] = this[name].bind(this);
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
TelemetryMeanActionDecorator.prototype.decorateAction = function (action) {
|
||||
function update(object) {
|
||||
var domainObject = object || action.domainObject;
|
||||
return this.updateTelemetryFromLinkedObject(object)
|
||||
.then(function (modelWithTelemetry) {
|
||||
return this.mutate(domainObject, modelWithTelemetry);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
if (action.getMetadata && action.getMetadata().key === 'properties' || action.getMetadata().key === 'create'){
|
||||
var oldPerform = action.perform.bind(action);
|
||||
action.perform = function () {
|
||||
return oldPerform().then(update.bind(this), update.bind(this));
|
||||
}.bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
TelemetryMeanActionDecorator.prototype.mutate = function (domainObject, model) {
|
||||
return domainObject.useCapability('mutation', function () {
|
||||
return model
|
||||
});
|
||||
}
|
||||
|
||||
TelemetryMeanActionDecorator.prototype.getActions = function () {
|
||||
var actions = this.actionService.getActions.apply(this.actionService, arguments);
|
||||
actions.forEach(this.decorateAction);
|
||||
return actions;
|
||||
};
|
||||
|
||||
TelemetryMeanActionDecorator.prototype.updateTelemetryFromLinkedObject = function (domainObject) {
|
||||
var model = domainObject.getModel();
|
||||
var telemetryPoint = model.telemetryPoint;
|
||||
var telemetryApi = this.openmct.telemetry;
|
||||
|
||||
if (telemetryPoint) {
|
||||
return this.openmct.objects.get(telemetryPoint).then(function (referencedObject) {
|
||||
if (referencedObject.type !== 'unknown') {
|
||||
var keysForRanges = telemetryApi.getMetadata(referencedObject).valuesForHints(['range'])
|
||||
.map(function (metadatum) {
|
||||
return metadatum.source;
|
||||
});
|
||||
|
||||
model.telemetry.values = referencedObject.telemetry.values.map(function (value) {
|
||||
if (keysForRanges.indexOf(value.source) !== -1) {
|
||||
value.name = value.name + " (Mean)";
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
return model;
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
return TelemetryMeanActionDecorator;
|
||||
|
||||
});
|
||||
112
src/plugins/telemetryMean/src/TelemetryMeanProvider.js
Normal file
112
src/plugins/telemetryMean/src/TelemetryMeanProvider.js
Normal file
@@ -0,0 +1,112 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
|
||||
function TelemetryMeanProvider(openmct) {
|
||||
this.openmct = openmct;
|
||||
this.subscriptionBuffer = [];
|
||||
this.lastNValues = [];
|
||||
}
|
||||
|
||||
TelemetryMeanProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||
return domainObject.type === 'telemetry-mean';
|
||||
};
|
||||
|
||||
TelemetryMeanProvider.prototype.supportsRequest = function () {
|
||||
return false;
|
||||
}
|
||||
|
||||
TelemetryMeanProvider.prototype.supportsSubscribe =
|
||||
TelemetryMeanProvider.prototype.canProvideTelemetry;
|
||||
|
||||
TelemetryMeanProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var promiseForObject = this.getWrappedObject(domainObject)
|
||||
return this.subscribeToWrappedObject(promiseForObject, callback);
|
||||
};
|
||||
|
||||
TelemetryMeanProvider.prototype.getWrappedObject = function (domainObject) {
|
||||
var objectId = domainObject.telemetryPoint;
|
||||
return this.openmct.objects.get(objectId);
|
||||
};
|
||||
|
||||
TelemetryMeanProvider.prototype.subscribeToWrappedObject = function (promiseForObject, callback) {
|
||||
var wrappedUnsubscribe;
|
||||
var unsubscribeCalled = false;
|
||||
|
||||
promiseForObject.then(function subscribe(wrappedObject) {
|
||||
if (!unsubscribeCalled && wrappedObject){
|
||||
wrappedUnsubscribe = this.subscribeToMeanValues(wrappedObject, callback);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return function unsubscribe(){
|
||||
unsubscribeCalled = true;
|
||||
if (wrappedUnsubscribe !== undefined) {
|
||||
wrappedUnsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TelemetryMeanProvider.prototype.subscribeToMeanValues = function (object, callback) {
|
||||
var telemetryApi = this.openmct.telemetry;
|
||||
var lastNData = [];
|
||||
var rangeKey = telemetryApi.getMetadata(object).valuesForHints(['range'])
|
||||
.map(function (metadatum) {
|
||||
return metadatum.source;
|
||||
}
|
||||
)[0];
|
||||
|
||||
return telemetryApi.subscribe(object, function (telemetryDatum) {
|
||||
|
||||
lastNData.push(telemetryDatum);
|
||||
if (lastNData.length > object.samples) {
|
||||
lastNData.shift();
|
||||
}
|
||||
|
||||
var meanDatum = this.calculateMeansForDatum(telemetryDatum, rangeKey, lastNData);
|
||||
callback(meanDatum);
|
||||
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
TelemetryMeanProvider.prototype.calculateMeansForDatum = function (telemetryDatum, keyToMean, lastNData) {
|
||||
var meanDatum = {
|
||||
'utc': telemetryDatum['utc'],
|
||||
'value': this.calculateMean(lastNData, keyToMean)
|
||||
}
|
||||
return meanDatum;
|
||||
}
|
||||
|
||||
TelemetryMeanProvider.prototype.calculateMean = function (lastNData, valueToMean) {
|
||||
return lastNData.reduce(function (sum, datum){
|
||||
return sum + datum[valueToMean];
|
||||
}, 0) / lastNData.length;
|
||||
};
|
||||
|
||||
TelemetryMeanProvider.prototype.request = function (domainObject, request) {
|
||||
throw "Historical requests not supported for Telemetry Averager";
|
||||
};
|
||||
|
||||
return TelemetryMeanProvider;
|
||||
});
|
||||
@@ -37,8 +37,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
this.key = 'local';
|
||||
this.cssClass = 'icon-clock';
|
||||
this.name = 'Local Clock';
|
||||
this.description = "Updates every second, providing UTC timestamps from " +
|
||||
"user's local computer.";
|
||||
this.description = "Provides UTC timestamps every second from the local system clock.";
|
||||
|
||||
this.period = period;
|
||||
this.timeoutHandle = undefined;
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
* 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.
|
||||
*
|
||||
* Open MCT https://nasa.github.io/openmct/
|
||||
* Version: @@version
|
||||
* Built: @@timestamp
|
||||
* Revision: @@revision
|
||||
* Branch: @@branch
|
||||
*
|
||||
* @preserve
|
||||
*****************************************************************************/
|
||||
|
||||
(function (root, factory) {
|
||||
@@ -29,3 +37,9 @@
|
||||
root.openmct = factory();
|
||||
}
|
||||
}(this, function() {
|
||||
var BUILD_CONSTANTS = {
|
||||
version: "@@version",
|
||||
timestamp: "@@timestamp",
|
||||
revision: "@@revision",
|
||||
branch: "@@branch"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user