Compare commits

..

1 Commits

Author SHA1 Message Date
Victor Woeltjen
d75058d5bd [Slideshow] Add slideshow type
Candidate for EOFY17 demo
2017-09-28 13:00:53 -07:00
68 changed files with 406 additions and 2530 deletions

View File

@@ -17,7 +17,7 @@
"screenfull": "^3.0.0",
"node-uuid": "^1.4.7",
"comma-separated-values": "^3.6.4",
"file-saver": "^1.3.3",
"FileSaver.js": "^0.0.2",
"zepto": "^1.1.6",
"eventemitter3": "^1.2.0",
"lodash": "3.10.1",

View File

@@ -4,6 +4,12 @@ deployment:
commands:
- npm install canvas nomnoml
- ./build-docs.sh
- git fetch --unshallow
- git push git@heroku.com:openmctweb-demo.git $CIRCLE_SHA1:refs/heads/master
openmct-demo:
branch: live_demo
heroku:
appname: openmct-demo
openmctweb-staging-deux:
branch: mobile
heroku:

View File

@@ -127,8 +127,7 @@
{ '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': 'Notebook object', 'cssClass': 'icon-notebook', 'cssContent': 'e1131', 'htmlEntity': '&#xe1131' }
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' }
];
"></div>

View File

@@ -33,7 +33,7 @@ requirejs.config({
"moment": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
"saveAs": "bower_components/file-saver/FileSaver.min",
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
"screenfull": "bower_components/screenfull/dist/screenfull.min",
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",

View File

@@ -22,7 +22,7 @@
<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 ng-attr-contenteditable="{{ controller.editable ? true : undefined }}"
<span contenteditable="true"
class='title-label flex-elem holder flex-can-shrink s-input-inline'
ng-click="controller.edit()"
ng-blur="controller.updateName($event)"

View File

@@ -32,8 +32,7 @@ define(
*/
function ObjectHeaderController($scope) {
this.$scope = $scope;
this.domainObject = $scope.domainObject;
this.editable = this.allowEdit();
$scope.editing = false;
}
/**
@@ -42,49 +41,33 @@ define(
* @param event the mouse event
*/
ObjectHeaderController.prototype.updateName = function (event) {
if (!event || !event.currentTarget) {
return;
}
if (event && (event.type === 'blur' || event.which === 13)) {
var name = event.currentTarget.innerHTML;
if (event.type === 'blur') {
this.updateModel(event);
} else if (event.which === 13) {
this.updateModel(event);
event.currentTarget.blur();
window.getSelection().removeAllRanges();
if (name.length === 0) {
name = "Unnamed " + this.$scope.domainObject.getCapability("type").typeDef.name;
event.currentTarget.innerHTML = name;
}
if (name !== this.$scope.domainObject.model.name) {
this.$scope.domainObject.getCapability('mutation').mutate(function (model) {
model.name = name;
});
}
this.$scope.editing = false;
if (event.which === 13) {
event.currentTarget.blur();
}
}
};
/**
* Updates the model.
*
* @param event the mouse event
* @param private
* Marks the status of the field as editing.
*/
ObjectHeaderController.prototype.updateModel = function (event) {
var name = event.currentTarget.textContent.replace(/\n/g, ' ');
if (name.length === 0) {
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
event.currentTarget.textContent = name;
}
if (name !== this.domainObject.getModel().name) {
this.domainObject.getCapability('mutation').mutate(function (model) {
model.name = name;
});
}
};
/**
* 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'));
ObjectHeaderController.prototype.edit = function () {
this.$scope.editing = true;
};
return ObjectHeaderController;

View File

@@ -32,27 +32,22 @@ define(
mockTypeCapability,
mockEvent,
mockCurrentTarget,
model,
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';
});
mockTypeCapability = {
typeDef: {
name: ""
}
};
mockCapabilities = {
mutation: mockMutationCapability,
type: mockTypeCapability
};
model = {
name: "Test name"
};
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "getModel"]);
mockDomainObject.getModel.andReturn(model);
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "model"]);
mockDomainObject.model = {name: "Test name"};
mockDomainObject.getCapability.andCallFake(function (key) {
return mockCapabilities[key];
});
@@ -61,7 +56,7 @@ define(
domainObject: mockDomainObject
};
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "textContent"]);
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "innerHTML"]);
mockCurrentTarget.blur.andReturn(mockCurrentTarget);
mockEvent = {
@@ -75,7 +70,7 @@ define(
it("updates the model with new name on blur", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "New name";
mockCurrentTarget.innerHTML = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
@@ -83,23 +78,23 @@ define(
it("updates the model with a default for blank names", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "";
mockCurrentTarget.innerHTML = "";
controller.updateName(mockEvent);
expect(mockCurrentTarget.textContent.length).not.toEqual(0);
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.textContent = mockDomainObject.getModel().name;
mockCurrentTarget.innerHTML = mockDomainObject.model.name;
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
});
it("updates the model on enter keypress event only", function () {
mockCurrentTarget.textContent = "New name";
mockCurrentTarget.innerHTML = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
@@ -109,29 +104,17 @@ define(
expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function));
mockMutationCapability.mutate.mostRecentCall.args[0](model);
mockMutationCapability.mutate.mostRecentCall.args[0](mockDomainObject.model);
expect(mockDomainObject.getModel().name).toBe("New name");
expect(mockDomainObject.model.name).toBe("New name");
});
it("blurs the field on enter key press", function () {
mockCurrentTarget.textContent = "New name";
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);
});
});
}
);

View File

@@ -101,15 +101,10 @@ define(
*/
EditorCapability.prototype.finish = function () {
var domainObject = this.domainObject;
if (this.transactionService.isActive()) {
return this.transactionService.cancel().then(function () {
domainObject.getCapability("status").set("editing", false);
return domainObject;
});
} else {
return Promise.resolve(domainObject);
}
return this.transactionService.cancel().then(function () {
domainObject.getCapability("status").set("editing", false);
return domainObject;
});
};
/**

View File

@@ -62,7 +62,6 @@ define(
);
mockTransactionService.commit.andReturn(fastPromise());
mockTransactionService.cancel.andReturn(fastPromise());
mockTransactionService.isActive = jasmine.createSpy('isActive');
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
@@ -142,7 +141,6 @@ define(
describe("finish", function () {
beforeEach(function () {
mockTransactionService.isActive.andReturn(true);
capability.edit();
capability.finish();
});
@@ -154,23 +152,6 @@ define(
});
});
describe("finish", function () {
beforeEach(function () {
mockTransactionService.isActive.andReturn(false);
capability.edit();
});
it("does not cancel transaction when transaction is not active", function () {
capability.finish();
expect(mockTransactionService.cancel).not.toHaveBeenCalled();
});
it("returns a promise", function () {
expect(capability.finish() instanceof Promise).toBe(true);
});
});
describe("dirty", function () {
var model = {};

View File

@@ -121,9 +121,6 @@ define([
};
UTCTimeFormat.prototype.parse = function (text) {
if (typeof text === 'number') {
return text;
}
return moment.utc(text, DATE_FORMATS).valueOf();
};

View File

@@ -2,7 +2,7 @@
"metadata": {
"name": "openmct-symbols-16px",
"lastOpened": 0,
"created": 1506973656040
"created": 1505151140023
},
"iconSets": [
{
@@ -899,14 +899,6 @@
"prevSize": 24,
"code": 921904,
"tempChar": ""
},
{
"order": 139,
"id": 117,
"name": "icon-notebook",
"prevSize": 24,
"code": 921905,
"tempChar": ""
}
],
"metadata": {
@@ -3532,29 +3524,6 @@
{}
]
}
},
{
"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": [

View File

@@ -118,5 +118,4 @@
<glyph unicode="&#xe1128;" 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="&#xe1129;" 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="&#xe1130;" 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="&#xe1131;" 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

View File

@@ -146,7 +146,6 @@ $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 */
@@ -261,7 +260,6 @@ $glyph-icon-notebook: '\e1131';
.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'); }

View File

@@ -26,6 +26,5 @@
display: block;
height: 100%;
width: 100%;
border: none;
}
}

View File

@@ -50,6 +50,7 @@
content:'';
font-family: symbolsfont;
font-size: 0.8em;
display: inline;
margin-right: $interiorMarginSm;
}
}

View File

@@ -120,11 +120,7 @@
}
.status-indicator {
background: none !important;
margin-right: $interiorMarginSm;
&[class*='s-status']:before {
font-size: 1em;
}
}
.count {

View File

@@ -108,11 +108,8 @@ define(
getMetadata();
});
var mutation = $scope.ngModel.selectedObject.getCapability('mutation');
var unlisten = mutation.listen(getMetadata);
$scope.$on('$destroy', unlisten);
}
return ObjectInspectorController;
}
);

View File

@@ -39,18 +39,10 @@ define(
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$watch", "$on"]
["$watch"]
);
mockScope.ngModel = {};
mockScope.ngModel.selectedObject = {
getCapability: function () {
return {
listen: function () {
return true;
}
};
}
};
mockScope.ngModel.selectedObject = 'mock selected object';
mockObjectService = jasmine.createSpyObj(
"objectService",

View File

@@ -23,13 +23,10 @@
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",
@@ -40,13 +37,10 @@ define([
], function (
MomentTimezone,
ClockIndicator,
FollowIndicator,
TickerService,
TimerService,
ClockController,
TimerController,
RefreshingController,
FollowTimerAction,
StartTimerAction,
RestartTimerAction,
StopTimerAction,
@@ -86,11 +80,6 @@ define([
"CLOCK_INDICATOR_FORMAT"
],
"priority": "preferred"
},
{
"implementation": FollowIndicator,
"depends": ["timerService"],
"priority": "fallback"
}
],
"services": [
@@ -101,11 +90,6 @@ define([
"$timeout",
"now"
]
},
{
"key": "timerService",
"implementation": TimerService,
"depends": ["openmct"]
}
],
"controllers": [
@@ -150,15 +134,6 @@ define([
}
],
"actions": [
{
"key": "timer.follow",
"implementation": FollowTimerAction,
"depends": ["timerService"],
"category": "contextual",
"name": "Follow Timer",
"cssClass": "icon-clock",
"priority": "optional"
},
{
"key": "timer.start",
"implementation": StartTimerAction,

View File

@@ -1,113 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['EventEmitter'], function (EventEmitter) {
/**
* Tracks the currently-followed Timer object. Used by
* timelines et al to synchronize to a particular timer.
*
* The TimerService emits `change` events when the active timer
* is changed.
*/
function TimerService(openmct) {
EventEmitter.apply(this);
this.time = openmct.time;
this.objects = openmct.objects;
}
TimerService.prototype = Object.create(EventEmitter.prototype);
/**
* Set (or clear, if `timer` is undefined) the currently active timer.
* @param {DomainObject} timer the new active timer
* @emits change
*/
TimerService.prototype.setTimer = function (timer) {
this.timer = timer;
this.emit('change');
if (this.stopObserving) {
this.stopObserving();
delete this.stopObserving;
}
if (timer) {
this.stopObserving =
this.objects.observe(timer, '*', this.setTimer.bind(this));
}
};
/**
* Get the currently active timer.
* @return {DomainObject} the active timer
* @emits change
*/
TimerService.prototype.getTimer = function () {
return this.timer;
};
/**
* Check if there is a currently active timer.
* @return {boolean} true if there is a timer
*/
TimerService.prototype.hasTimer = function () {
return !!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.timerState !== 'stopped';
if (!canConvert) {
return undefined;
}
var now = clock.currentValue();
var delta = this.timer.timerState === 'paused' ?
now - this.timer.pausedTime : 0;
var epoch = this.timer.timestamp;
return timestamp - epoch - delta;
};
/**
* Get the value of the active clock, adjusted to be relative to the active
* timer. If there is no clock or no active timer, this will return
* `undefined`.
* @return {number} milliseconds since the start of the active timer
*/
TimerService.prototype.now = function () {
var clock = this.time.clock();
return clock && this.convert(clock.currentValue());
};
return TimerService;
});

View File

@@ -1,87 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"../../src/actions/FollowTimerAction"
], function (FollowTimerAction) {
var TIMER_SERVICE_METHODS =
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
describe("The Follow Timer action", function () {
var testContext;
var testModel;
var testAdaptedObject;
beforeEach(function () {
testModel = {};
testContext = { domainObject: jasmine.createSpyObj('domainObject', [
'getModel',
'useCapability'
]) };
testAdaptedObject = { foo: 'bar' };
testContext.domainObject.getModel.andReturn(testModel);
testContext.domainObject.useCapability.andCallFake(function (c) {
return c === 'adapter' && testAdaptedObject;
});
});
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(testAdaptedObject);
});
});
});
});
});

View File

@@ -1,61 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
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);
});
});
});
});

View File

@@ -1,77 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'../../src/services/TimerService'
], function (TimerService) {
describe("TimerService", function () {
var callback;
var mockmct;
var timerService;
beforeEach(function () {
callback = jasmine.createSpy('callback');
mockmct = {
time: { clock: jasmine.createSpy('clock') },
objects: { observe: jasmine.createSpy('observe') }
};
timerService = new TimerService(mockmct);
timerService.on('change', callback);
});
it("initially emits no change events", function () {
expect(callback).not.toHaveBeenCalled();
});
it("reports no current timer", function () {
expect(timerService.getTimer()).toBeUndefined();
});
describe("setTimer", function () {
var testTimer;
beforeEach(function () {
testTimer = { name: "I am some timer; you are nobody." };
timerService.setTimer(testTimer);
});
it("emits a change event", function () {
expect(callback).toHaveBeenCalled();
});
it("reports the current timer", function () {
expect(timerService.getTimer()).toBe(testTimer);
});
it("observes changes to an object", function () {
var newTimer = { name: "I am another timer." };
expect(mockmct.objects.observe).toHaveBeenCalledWith(
testTimer,
'*',
jasmine.any(Function)
);
mockmct.objects.observe.mostRecentCall.args[2](newTimer);
expect(timerService.getTimer()).toBe(newTimer);
});
});
});
});

View File

@@ -255,8 +255,6 @@ define(
if (this.nextDatum) {
this.updateValues(this.nextDatum);
delete this.nextDatum;
} else {
this.updateValues(this.$scope.imageHistory[this.$scope.imageHistory.length - 1]);
}
this.autoScroll = true;
}

View File

@@ -183,17 +183,6 @@ define(
expect(controller.getImageUrl()).toEqual(newUrl);
});
it("forwards large image view to latest image in history on un-pause", function () {
$scope.imageHistory = [
{ utc: 1434600258122, url: 'some/url1', selected: false},
{ utc: 1434600258123, url: 'some/url2', selected: false}
];
controller.paused(true);
controller.paused(false);
expect(controller.getImageUrl()).toEqual(controller.getImageUrl($scope.imageHistory[1]));
});
it("subscribes to telemetry", function () {
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
newDomainObject,
@@ -238,7 +227,7 @@ define(
expect(controller.updateHistory(mockDatum)).toBe(false);
});
describe("when user clicks on imagery thumbnail", function () {
describe("user clicks on imagery thumbnail", function () {
var mockDatum = { utc: 1434600258123, url: 'some/url', selected: false};
it("pauses and adds selected class to imagery thumbnail", function () {
@@ -259,7 +248,6 @@ define(
expect(controller.getTime()).toEqual(controller.timeFormat.format(mockDatum.utc));
});
});
});
it("initially shows an empty string for date/time", function () {

View File

@@ -360,47 +360,22 @@ define(
*/
FixedController.prototype.updateView = function (telemetryObject, datum) {
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
var rangeMetadata = metadata.valuesForHints(['range'])[0];
var rangeKey = rangeMetadata.source || rangeMetadata.key;
var valueMetadata = metadata.value(rangeKey);
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
var value = datum[valueMetadata.key];
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, rangeKey);
this.setDisplayedValue(
telemetryObject,
formattedTelemetryValue,
formatter.format(value),
alarm && alarm.cssClass
);
this.digest();
};
/**
* @private
*/
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
var valueMetadata = metadata.value(telemetryKeyToDisplay);
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
return formatter.format(datum[valueMetadata.key]);
};
/**
* @private
*/
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
// If there is a range value, show that preferentially
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
// If no range is defined, default to the highest priority non time-domain data.
if (telemetryKeyToDisplay === undefined) {
var valuesOrderedByPriority = metadata.values();
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
return !(valueMetadata.hints.domain);
})[0];
}
return telemetryKeyToDisplay.source;
};
/**
* Request the last historical data point for the given domain objects
* @param {object[]} objects
@@ -413,9 +388,7 @@ define(
objects.forEach(function (object) {
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
.then(function (data) {
if (data.length > 0) {
self.updateView(object, data[data.length - 1]);
}
self.updateView(object, data[data.length - 1]);
});
});
return objects;

View File

@@ -26,12 +26,8 @@
* @namespace platform/features/layout
*/
define(
[
'./LayoutDrag'
],
function (
LayoutDrag
) {
['./LayoutDrag'],
function (LayoutDrag) {
var DEFAULT_DIMENSIONS = [12, 8],
DEFAULT_GRID_SIZE = [32, 32],
@@ -127,8 +123,6 @@ define(
if (self.droppedIdToSelectAfterRefresh) {
self.select(null, self.droppedIdToSelectAfterRefresh);
delete self.droppedIdToSelectAfterRefresh;
} else if (composition.indexOf(self.selectedId) === -1) {
self.clearSelection();
}
}
});

View File

@@ -178,6 +178,7 @@ define(
Promise.resolve(mockChildren)
);
mockScope.model = testModel;
mockScope.configuration = testConfiguration;
mockScope.selection = jasmine.createSpyObj(
@@ -193,8 +194,7 @@ define(
mockMetadata = jasmine.createSpyObj('mockMetadata', [
'valuesForHints',
'value',
'values'
'value'
]);
mockMetadata.value.andReturn({
key: 'value'
@@ -653,39 +653,6 @@ define(
});
});
it("selects an range value to display, if available", function () {
mockMetadata.valuesForHints.andReturn([
{
key: 'range',
source: 'range'
}
]);
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('range');
});
it("selects the first non-domain value to display, if no range available", function () {
mockMetadata.valuesForHints.andReturn([]);
mockMetadata.values.andReturn([
{
key: 'domain',
source: 'domain',
hints: {
domain: 1
}
},
{
key: 'image',
source: 'image',
hints: {
image: 1
}
}
]);
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('image');
});
it("reflects limit status", function () {
mockLimitEvaluator.evaluate.andReturn({cssClass: "alarm-a"});
controller.updateView(mockTelemetryObject, [{

View File

@@ -424,17 +424,6 @@ define(
expect(selectedObj.showFrame).toEqual(jasmine.any(Function));
});
it("deselects the object that is no longer in the composition", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
controller.select(mockEvent, childObj.getId());
var composition = ["b", "c"];
mockScope.$watchCollection.mostRecentCall.args[1](composition);
expect(controller.selected(childObj)).toBe(false);
});
});
}
);

View File

@@ -49,7 +49,7 @@
{
"key": "ListViewController",
"implementation": ListViewController,
"depends": ["$scope", "formatService"]
"depends": ["$scope"]
}
],
"directives": [

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define(function () {
function ListViewController($scope, formatService) {
function ListViewController($scope) {
this.$scope = $scope;
$scope.orderByField = 'title';
$scope.reverseSort = false;
@@ -30,8 +30,6 @@ define(function () {
var unlisten = $scope.domainObject.getCapability('mutation')
.listen(this.updateView.bind(this));
this.utc = formatService.getFormat('utc');
$scope.$on('$destroy', function () {
unlisten();
});
@@ -52,13 +50,17 @@ define(function () {
icon: child.getCapability('type').getCssClass(),
title: child.getModel().name,
type: child.getCapability('type').getName(),
persisted: this.utc.format(child.getModel().persisted),
modified: this.utc.format(child.getModel().modified),
persisted: new Date(
child.getModel().persisted
).toUTCString(),
modified: new Date(
child.getModel().modified
).toUTCString(),
asDomainObject: child,
location: child.getCapability('location'),
action: child.getCapability('action')
};
}, this);
});
};
return ListViewController;

View File

@@ -31,9 +31,7 @@ define(
controller,
childModel,
typeCapability,
mutationCapability,
formatService;
mutationCapability;
beforeEach(function () {
unlistenFunc = jasmine.createSpy("unlisten");
@@ -43,18 +41,6 @@ define(
);
mutationCapability.listen.andReturn(unlistenFunc);
formatService = jasmine.createSpyObj(
"formatService",
["getFormat"]
);
formatService.getFormat.andReturn(jasmine.createSpyObj(
'utc',
["format"]
));
formatService.getFormat().format.andCallFake(function (v) {
return "formatted " + v;
});
typeCapability = jasmine.createSpyObj(
"typeCapability",
["getCssClass", "getName"]
@@ -108,27 +94,20 @@ define(
);
scope.domainObject = domainObject;
controller = new ListViewController(scope, formatService);
controller = new ListViewController(scope);
waitsFor(function () {
return scope.children;
});
});
it("uses the UTC time format", function () {
expect(formatService.getFormat).toHaveBeenCalledWith('utc');
});
it("updates the view", function () {
expect(scope.children[0]).toEqual(
{
icon: "icon-folder",
title: "Battery Charge Status",
type: "Folder",
persisted: formatService.getFormat('utc')
.format(childModel.persisted),
modified: formatService.getFormat('utc')
.format(childModel.modified),
persisted: "Wed, 07 Jun 2017 20:34:57 GMT",
modified: "Wed, 07 Jun 2017 20:34:57 GMT",
asDomainObject: childObject,
location: ''
}

View File

@@ -170,9 +170,6 @@ define(
* @param rows
*/
TelemetryTableController.prototype.addRowsToTable = function (rows) {
rows.forEach(function (row) {
this.$scope.rows.push(row);
}, this);
this.$scope.$broadcast('add:rows', rows);
};

View File

@@ -436,28 +436,5 @@ define(
expect(mockScope.$broadcast).toHaveBeenCalledWith("remove:rows", discardedRows);
});
describe('when telemetry is added', function () {
var testRows;
var expectedRows;
beforeEach(function () {
testRows = [{ a: 0 }, { a: 1 }, { a: 2 }];
mockScope.rows = [{ a: -1 }];
expectedRows = mockScope.rows.concat(testRows);
spyOn(controller.telemetry, "on").andCallThrough();
controller.registerChangeListeners();
controller.telemetry.on.calls.forEach(function (call) {
if (call.args[0] === 'added') {
call.args[1](testRows);
}
});
});
it("adds it to rows in scope", function () {
expect(mockScope.rows).toEqual(expectedRows);
});
});
});
});

View File

@@ -29,7 +29,6 @@ define([
"./src/controllers/TimelineTickController",
"./src/controllers/TimelineTableController",
"./src/controllers/TimelineGanttController",
"./src/controllers/TimelineTOIController",
"./src/controllers/ActivityModeValuesController",
"./src/capabilities/ActivityTimespanCapability",
"./src/capabilities/TimelineTimespanCapability",
@@ -38,7 +37,6 @@ define([
"./src/capabilities/CostCapability",
"./src/directives/MCTSwimlaneDrop",
"./src/directives/MCTSwimlaneDrag",
"./src/directives/MCTResourceGraphDrop",
"./src/services/ObjectLoader",
"./src/chart/MCTTimelineChart",
"text!./res/templates/values.html",
@@ -61,7 +59,6 @@ define([
TimelineTickController,
TimelineTableController,
TimelineGanttController,
TimelineTOIController,
ActivityModeValuesController,
ActivityTimespanCapability,
TimelineTimespanCapability,
@@ -70,7 +67,6 @@ define([
CostCapability,
MCTSwimlaneDrop,
MCTSwimlaneDrag,
MCTResourceGraphDrop,
ObjectLoader,
MCTTimelineChart,
valuesTemplate,
@@ -506,15 +502,6 @@ define([
"TIMELINE_MAXIMUM_OFFSCREEN"
]
},
{
"key": "TimelineTOIController",
"implementation": TimelineTOIController,
"depends": [
"openmct",
"timerService",
"$scope"
]
},
{
"key": "ActivityModeValuesController",
"implementation": ActivityModeValuesController,
@@ -579,13 +566,6 @@ define([
"$interval",
"$log"
]
},
{
"key": "mctResourceGraphDrop",
"implementation": MCTResourceGraphDrop,
"depends": [
"dndService"
]
}
],
"services": [

View File

@@ -29,44 +29,6 @@
}
}
}
// 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 {
@@ -146,9 +108,10 @@
}
.s-hover-btns-holder {
$bg: $timelineHeaderColorBg;
$bga: 1;
$l: 5%;
@include user-select(none);
@include background-image(linear-gradient(-90deg, rgba($bg, 1), rgba($bg, 1) 70%, rgba($bg, 0) 100%));
@include background-image(linear-gradient(-90deg, rgba($bg, $bga), rgba($bg, $bga) 70%, rgba($bg, 0) 100%));
.s-button {
height: 16px;
line-height: 16px;
@@ -166,27 +129,4 @@
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)));
}
}
}
}

View File

@@ -38,10 +38,6 @@
.l-timeline-pane {
@include absPosDefault();
&.drop-over {
background-color: lighten($colorEditAreaBg, 5%);
}
.l-width-control {
position: relative;
}
@@ -79,10 +75,6 @@
}
}
&.l-timeline-gantt {
.abs.l-timeline-gantt-header-w {
overflow: hidden;
height: $timelineTopPaneHeaderH;
}
.l-swimlanes-holder {
@include scrollV(scroll);
bottom: $scrollbarTrackSize;

View File

@@ -24,7 +24,6 @@ $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";

View File

@@ -24,7 +24,6 @@ $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";

View File

@@ -24,7 +24,6 @@ $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";

View File

@@ -77,8 +77,7 @@
<mct-splitter></mct-splitter>
<!-- BOTTOM PANE RESOURCE LEGEND -->
<div mct-resource-graph-drop
class="split-pane-component abs l-timeline-pane t-pane-h l-pane-btm s-timeline-resource-legend l-timeline-resource-legend">
<div class="split-pane-component abs l-timeline-pane t-pane-h l-pane-btm s-timeline-resource-legend l-timeline-resource-legend">
<div class="l-title s-title">{{ngModel.title}}Resource Graph Legend</div>
<div class="l-legend-items legend">
<mct-include key="'timeline-legend-item'"
@@ -97,124 +96,109 @@
<!-- RIGHT PANE: GANTT AND RESOURCE PLOTS -->
<span ng-controller="TimelineZoomController as zoomController" class="abs">
<span class="toi-control-holder temp" ng-controller="TimelineTOIController as toiController">
<mct-split-pane anchor="bottom"
<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-timer"
ng-click="scroll.follow = true"
ng-show="!toiController.isFollowing() && toiController.isActive()"
title="Follow time bounds">
</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-arrows-out"
ng-click="zoomController.fit()"
ng-show="true"
title="Zoom to fit">
</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-in"
ng-click="zoomController.zoom(-1)"
ng-show="true"
title="Zoom in">
</a>
<a class="s-button icon-magnify-in"
ng-click="scroll.follow = false; zoomController.zoom(-1)"
ng-show="true"
title="Zoom in">
</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-out"
ng-click="scroll.follow = false; zoomController.zoom(1)"
ng-show="true"
title="Zoom out">
</a>
</div>
<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 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>
<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 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">
<mct-representation key="'gantt'"
mct-object="swimlane.domainObject"
parameters="{
scroll: scroll,
toPixels: zoomController.toPixels
}">
</mct-representation>
<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>
<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>
<!-- 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>
</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>
<!-- 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>
</mct-split-pane>
</span>
</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>
</span>
</mct-split-pane>
</div>

View File

@@ -1,111 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], 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;
});

View File

@@ -32,8 +32,7 @@ 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,
lastWidth = Number.MAX_VALUE; // Don't constrain prematurely
tickWidth = ZOOM_CONFIGURATION.width || 200;
function toMillis(pixels) {
return (pixels / tickWidth) * zoomLevels[zoomIndex];
@@ -56,29 +55,19 @@ define(
function setScroll(x) {
$window.requestAnimationFrame(function () {
$scope.scroll.x = Math.min(
Math.max(x, 0),
lastWidth - $scope.scroll.width
);
$scope.scroll.x = x;
$scope.$apply();
});
}
function initializeZoomFromStartEnd(start, end) {
var duration = end - start;
function initializeZoomFromTimespan(timespan) {
var timelineDuration = timespan.getDuration();
zoomIndex = 0;
while (toMillis($scope.scroll.width) < duration &&
while (toMillis($scope.scroll.width) < timelineDuration &&
zoomIndex < zoomLevels.length - 1) {
zoomIndex += 1;
}
setScroll(toPixels(start));
}
function initializeZoomFromTimespan(timespan) {
return initializeZoomFromStartEnd(
timespan.getStart(),
timespan.getEnd()
);
setScroll(toPixels(timespan.getStart()));
}
function initializeZoom() {
@@ -112,13 +101,6 @@ 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.
@@ -137,14 +119,14 @@ define(
*/
toMillis: toMillis,
/**
* Set the maximum timestamp value to be displayed, and get
* the pixel width necessary to display this value.
* Get the pixel width necessary to fit the specified
* timestamp, expressed as an offset in milliseconds from
* the start of the timeline.
* @param {number} timestamp the time to display
*/
width: function (timestamp) {
var pixels = Math.ceil(toPixels(timestamp * (1 + PADDING)));
lastWidth = Math.max($scope.scroll.width, pixels);
return lastWidth;
return Math.max($scope.scroll.width, pixels);
}
};
}

View File

@@ -1,81 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./SwimlaneDragConstants'],
function (SwimlaneDragConstants) {
/**
* Defines the `mct-resource-graph-drop` directive. When a drop occurs
* on an element with this attribute, the swimlane targeted by the drop
* will receive the dropped domain object (at which point it can handle
* the drop, typically by toggling the swimlane graph.)
* @param {DndService} dndService drag-and-drop service
*/
function MCTResourceGraphDrop(dndService) {
function link(scope, element, attrs) {
// Handle dragover
element.on('dragover', function (e) {
var swimlane = dndService.getData(
SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE
);
if (typeof swimlane !== "undefined" && !swimlane.graph()) {
element.addClass('drop-over');
scope.$apply();
e.preventDefault();
}
});
// Handle drops
element.on('drop', function (e) {
var swimlane = dndService.getData(
SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE
);
element.removeClass('drop-over');
// Only toggle if the graph isn't already set
if (typeof swimlane !== "undefined" && !swimlane.graph()) {
swimlane.toggleGraph();
e.preventDefault();
}
});
// Clear highlights when drag leaves this swimlane
element.on('dragleave', function (e) {
element.removeClass('drop-over');
scope.$apply();
e.preventDefault();
});
}
return {
// Applies to attributes
restrict: "A",
// Link using above function
link: link
};
}
return MCTResourceGraphDrop;
}
);

View File

@@ -1,138 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"../../src/controllers/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();
});
});
});
});

View File

@@ -1,159 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['../../src/directives/MCTResourceGraphDrop', '../../src/directives/SwimlaneDragConstants'],
function (MCTResourceGraphDrop, SwimlaneDragConstants) {
describe("The mct-resource-graph-drop directive", function () {
var mockDndService,
mockScope,
mockElement,
testAttrs,
mockSwimlane,
testEvent,
handlers,
directive;
beforeEach(function () {
handlers = {};
mockDndService = jasmine.createSpyObj(
'dndService',
['setData', 'getData', 'removeData']
);
mockScope = jasmine.createSpyObj('$scope', ['$eval', '$apply']);
mockElement = jasmine.createSpyObj('element', ['on', 'addClass', 'removeClass']);
testAttrs = { mctSwimlaneDrop: "mockSwimlane" };
mockSwimlane = jasmine.createSpyObj(
"swimlane",
['graph', 'toggleGraph']
);
testEvent = {
dataTransfer: { getData: jasmine.createSpy() },
preventDefault: jasmine.createSpy(),
stopPropagation: jasmine.createSpy()
};
testEvent.dataTransfer.getData.andReturn('abc');
mockDndService.getData.andCallFake(function (key) {
return key === SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE ?
mockSwimlane : undefined;
});
mockSwimlane.graph.andReturn(false);
directive = new MCTResourceGraphDrop(mockDndService);
directive.link(mockScope, mockElement, testAttrs);
mockElement.on.calls.forEach(function (call) {
handlers[call.args[0]] = call.args[1];
});
});
it("is available as an attribute", function () {
expect(directive.restrict).toEqual("A");
});
[false, true].forEach(function (graphing) {
describe("when swimlane graph is " + (graphing ? "" : "not ") + "enabled", function () {
beforeEach(function () {
mockSwimlane.graph.andReturn(graphing);
});
describe("on dragover", function () {
var prefix = !graphing ? "does" : "does not";
beforeEach(function () {
handlers.dragover(testEvent);
});
it(prefix + " add a drop-over class", function () {
var expectAddClass = expect(mockElement.addClass);
(!graphing ? expectAddClass : expectAddClass.not)
.toHaveBeenCalledWith('drop-over');
});
it(prefix + " call $apply on scope", function () {
var expectApply = expect(mockScope.$apply);
(!graphing ? expectApply : expectApply.not)
.toHaveBeenCalled();
});
it(prefix + " prevent default", function () {
var expectPreventDefault = expect(testEvent.preventDefault);
(!graphing ? expectPreventDefault : expectPreventDefault.not)
.toHaveBeenCalled();
});
});
describe("on drop", function () {
var prefix = !graphing ? "does" : "does not";
beforeEach(function () {
handlers.drop(testEvent);
});
it("removes any drop-over class", function () {
expect(mockElement.removeClass)
.toHaveBeenCalledWith('drop-over');
});
it(prefix + " toggle the swimlane's resource graph", function () {
var expectToggle = expect(mockSwimlane.toggleGraph);
(!graphing ? expectToggle : expectToggle.not)
.toHaveBeenCalled();
});
it(prefix + " prevent default", function () {
var expectPreventDefault = expect(testEvent.preventDefault);
(!graphing ? expectPreventDefault : expectPreventDefault.not)
.toHaveBeenCalled();
});
});
describe("on dragleave", function () {
beforeEach(function () {
handlers.dragleave(testEvent);
});
it("removes any drop-over class", function () {
expect(mockElement.removeClass)
.toHaveBeenCalledWith('drop-over');
});
it("calls $apply on scope", function () {
expect(mockScope.$apply).toHaveBeenCalled();
});
it("calls preventDefault on events", function () {
expect(testEvent.preventDefault).toHaveBeenCalled();
});
});
});
});
});
}
);

View File

@@ -27,7 +27,7 @@
ng-required="ngRequired || compositeCtrl.isNonEmpty(ngModel[field])"
ng-pattern="ngPattern"
options="item.options"
structure="item"
structure="row"
field="$index">
</mct-control>
<span class="composite-control-label">

View File

@@ -106,9 +106,9 @@ define([
*
* @type {module:openmct.ViewRegistry}
* @memberof module:openmct.MCT#
* @name objectViews
* @name mainViews
*/
this.objectViews = new ViewRegistry();
this.mainViews = new ViewRegistry();
/**
* Registry for views which should appear in the Inspector area.
@@ -255,18 +255,6 @@ define([
this.legacyExtension('types', legacyDefinition);
}.bind(this));
this.objectViews.getAllProviders().forEach(function (p) {
this.legacyExtension('views', {
key: p.key,
provider: p,
name: p.name,
cssClass: p.cssClass,
description: p.description,
editable: p.editable,
template: '<mct-view mct-provider-key="' + p.key + '"/>'
});
}, this);
legacyRegistry.register('adapter', this.legacyBundle);
legacyRegistry.enable('adapter');
/**

View File

@@ -24,6 +24,7 @@ define([
'legacyRegistry',
'./actions/ActionDialogDecorator',
'./capabilities/AdapterCapability',
'./controllers/AdaptedViewController',
'./directives/MCTView',
'./services/Instantiate',
'./services/MissingModelCompatibilityDecorator',
@@ -31,11 +32,13 @@ define([
'./policies/AdapterCompositionPolicy',
'./policies/AdaptedViewPolicy',
'./runs/AlternateCompositionInitializer',
'./runs/TimeSettingsURLHandler'
'./runs/TimeSettingsURLHandler',
'text!./templates/adapted-view-template.html'
], function (
legacyRegistry,
ActionDialogDecorator,
AdapterCapability,
AdaptedViewController,
MCTView,
Instantiate,
MissingModelCompatibilityDecorator,
@@ -43,15 +46,15 @@ define([
AdapterCompositionPolicy,
AdaptedViewPolicy,
AlternateCompositionInitializer,
TimeSettingsURLHandler
TimeSettingsURLHandler,
adaptedViewTemplate
) {
legacyRegistry.register('src/adapter', {
"extensions": {
"directives": [
{
key: "mctView",
implementation: MCTView,
depends: ["openmct"]
implementation: MCTView
}
],
capabilities: [
@@ -60,6 +63,16 @@ define([
implementation: AdapterCapability
}
],
controllers: [
{
key: "AdaptedViewController",
implementation: AdaptedViewController,
depends: [
'$scope',
'openmct'
]
}
],
services: [
{
key: "instantiate",
@@ -122,6 +135,12 @@ define([
depends: ["openmct", "$location", "$rootScope"]
}
],
views: [
{
key: "adapted-view",
template: adaptedViewTemplate
}
],
licenses: [
{
"name": "almond",

View File

@@ -22,12 +22,10 @@
define([
'./synchronizeMutationCapability',
'./AlternateCompositionCapability',
'./patchViewCapability'
'./AlternateCompositionCapability'
], function (
synchronizeMutationCapability,
AlternateCompositionCapability,
patchViewCapability
AlternateCompositionCapability
) {
/**
@@ -48,9 +46,6 @@ define([
capabilities.mutation =
synchronizeMutationCapability(capabilities.mutation);
}
if (capabilities.view) {
capabilities.view = patchViewCapability(capabilities.view);
}
if (AlternateCompositionCapability.appliesTo(model, id)) {
capabilities.composition = function (domainObject) {
return new AlternateCompositionCapability(this.$injector, domainObject);

View File

@@ -1,60 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'lodash'
], function (
_
) {
function patchViewCapability(viewConstructor) {
return function makeCapability(domainObject) {
var capability = viewConstructor(domainObject);
var oldInvoke = capability.invoke.bind(capability);
capability.invoke = function () {
var availableViews = oldInvoke();
var newDomainObject = capability
.domainObject
.useCapability('adapter');
return _(availableViews).map(function (v, i) {
var vd = {
view: v,
priority: i + 100 // arbitrary to allow new views to
// be defaults by returning priority less than 100.
};
if (v.provider && v.provider.priority) {
vd.priority = v.provider.priority(newDomainObject);
}
return vd;
})
.sortBy('priority')
.map('view')
.value();
};
return capability;
};
}
return patchViewCapability;
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,38 +20,21 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['moment'],
function (moment) {
var NO_TIMER = "No timer being followed";
define([], function () {
function AdaptedViewController($scope, openmct) {
function refresh(legacyObject) {
if (!legacyObject) {
$scope.view = undefined;
return;
}
/**
* Indicator that displays the active timer, as well as its
* current state.
* @implements {Indicator}
* @memberof platform/features/clock
*/
function FollowIndicator(timerService) {
this.timerService = timerService;
var domainObject = legacyObject.useCapability('adapter');
var providers = openmct.mainViews.get(domainObject);
$scope.view = providers[0] && providers[0].view(domainObject);
}
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;
$scope.$watch('domainObject', refresh);
}
);
return AdaptedViewController;
});

View File

@@ -21,23 +21,18 @@
*****************************************************************************/
define([
'angular',
'./Region'
], function (
angular,
Region
) {
function MCTView(openmct) {
function MCTView() {
return {
restrict: 'E',
restrict: 'A',
link: function (scope, element, attrs) {
var provider = openmct.objectViews.getByProviderKey(attrs.mctProviderKey);
var view = new provider.view(scope.domainObject.useCapability('adapter'));
var domElement = element[0];
view.show(domElement);
if (view.destroy) {
scope.$on('$destroy', function () {
view.destroy(domElement);
});
}
var region = new Region(element[0]);
scope.$watch(attrs.mctView, region.show.bind(region));
}
};
}

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,37 +20,26 @@
* 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 &&
context.domainObject.useCapability('adapter');
this.perform =
timerService.setTimer.bind(timerService, domainObject);
}
FollowTimerAction.appliesTo = function (context) {
var model =
(context.domainObject && context.domainObject.getModel()) ||
{};
return model.type === 'timer';
};
return FollowTimerAction;
define([], function () {
function Region(element) {
this.activeView = undefined;
this.element = element;
}
);
Region.prototype.clear = function () {
if (this.activeView) {
this.activeView.destroy();
this.activeView = undefined;
}
};
Region.prototype.show = function (view) {
this.clear();
this.activeView = view;
if (this.activeView) {
this.activeView.show(this.element);
}
};
return Region;
});

View File

@@ -29,9 +29,9 @@ define([], function () {
view,
legacyObject
) {
if (view.hasOwnProperty('provider')) {
if (view.key === 'adapted-view') {
var domainObject = legacyObject.useCapability('adapter');
return view.provider.canView(domainObject);
return this.openmct.mainViews.get(domainObject).length > 0;
}
return true;
};

View File

@@ -50,18 +50,11 @@ define([
this.rootProvider = new RootObjectProvider(this.rootRegistry);
}
/**
* Set fallback provider, this is an internal API for legacy reasons.
* @private
*/
ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) {
this.fallbackProvider = p;
};
/**
* Retrieve the provider for a given identifier.
* @private
*/
// Retrieve the provider for a given key.
ObjectAPI.prototype.getProvider = function (identifier) {
if (identifier.key === 'ROOT') {
return this.rootProvider;
@@ -142,28 +135,27 @@ define([
* @returns {Promise} a promise which will resolve when the domain object
* has been saved, or be rejected if it cannot be saved
*/
ObjectAPI.prototype.get = function (identifier) {
identifier = utils.parseKeyString(identifier);
var provider = this.getProvider(identifier);
if (!provider) {
throw new Error('No Provider Matched');
}
[
'save',
'delete',
'get'
].forEach(function (method) {
ObjectAPI.prototype[method] = function () {
var identifier = arguments[0],
provider = this.getProvider(identifier);
if (!provider.get) {
throw new Error('Provider does not support get!');
}
if (!provider) {
throw new Error('No Provider Matched');
}
return provider.get(identifier);
};
if (!provider[method]) {
throw new Error('Provider does not support [' + method + '].');
}
ObjectAPI.prototype.delete = function () {
throw new Error('Delete not implemented');
};
ObjectAPI.prototype.save = function () {
throw new Error('Save not implemented');
};
return provider[method].apply(provider, arguments);
};
});
/**
* Add a root-level object.

View File

@@ -28,7 +28,7 @@ define([
'./timeConductor/plugin',
'../../example/imagery/plugin',
'../../platform/import-export/bundle',
'./telemetryMean/plugin'
'./slideshow/plugin'
], function (
_,
UTCTimeSystem,
@@ -37,7 +37,7 @@ define([
TimeConductorPlugin,
ExampleImagery,
ImportExport,
TelemetryMean
Slideshow
) {
var bundleMap = {
CouchDB: 'platform/persistence/couch',
@@ -60,6 +60,8 @@ define([
plugins.ImportExport = ImportExport;
plugins.Slideshow = Slideshow;
/**
* A tabular view showing the latest values of multiple telemetry points at
* once. Formatted so that labels and values are aligned.
@@ -122,7 +124,6 @@ define([
};
plugins.ExampleImagery = ExampleImagery;
plugins.TelemetryMean = TelemetryMean;
return plugins;
});

View File

@@ -0,0 +1,93 @@
/*****************************************************************************
* 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 () {
var SLIDESHOW_TEMPLATE =
'<span ng-controller="SlideshowController as slideshow">' +
'<mct-representation key="slideshow.key()" mct-object="slideshow.object()">' +
'</mct-representation></span>';
function SlideshowController($scope, $interval) {
this.composition = [];
this.counter = 0;
$scope.$watch('domainObject', this.update.bind(this));
var interval = $interval(function () {
this.counter += 1;
}.bind(this), 4000);
//$scope.$on("$destroy", interval.cancel.bind(interval));
}
SlideshowController.prototype.update = function (domainObject) {
var composition = domainObject.useCapability("composition");
if (!composition) {
this.composition = [];
return;
}
composition.then(function (composition) {
this.composition = composition;
}.bind(this));
};
SlideshowController.prototype.object = function () {
return Array.isArray(this.composition) && this.composition.length ?
this.composition[this.counter % this.composition.length] :
undefined;
};
SlideshowController.prototype.key = function () {
var object = this.object();
var view = object && object.useCapability('view')[0];
return view && view.key;
};
return function slideshow(options) {
return function install(openmct) {
openmct.types.addType('slideshow', {
name: "Slideshow",
description: "A slideshow display of other objects",
cssClass: 'icon-object',
initialize: function (slideshow) {
slideshow.composition = [];
},
creatable: true
});
openmct.legacyExtension("controllers", {
key: "SlideshowController",
implementation: SlideshowController,
depends: [ "$scope", "$interval" ]
});
openmct.legacyExtension("views", {
key: "slideshow",
type: "slideshow",
editable: true,
gestures: [ "drop" ],
template: SLIDESHOW_TEMPLATE
});
};
};
});

View File

@@ -1,76 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['./src/MeanTelemetryProvider'], function (MeanTelemetryProvider) {
var DEFAULT_SAMPLES = 10;
function plugin() {
return function install(openmct) {
openmct.types.addType('telemetry-mean', {
name: 'Telemetry Mean',
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 = {};
domainObject.telemetry.values =
openmct.time.getAllTimeSystems().map(function (timeSystem, index) {
return {
key: timeSystem.key,
name: timeSystem.name,
hints: {
domain: index + 1
}
};
});
domainObject.telemetry.values.push({
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 MeanTelemetryProvider(openmct));
};
}
return plugin;
});

View File

@@ -1,125 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*jshint latedef: nofunc */
/*global console */
define([
'../../../api/objects/object-utils',
'./TelemetryAverager'
], function (objectUtils, TelemetryAverager) {
function MeanTelemetryProvider(openmct) {
this.openmct = openmct;
this.telemetryAPI = openmct.telemetry;
this.timeAPI = openmct.time;
this.objectAPI = openmct.objects;
this.perObjectProviders = {};
}
MeanTelemetryProvider.prototype.canProvideTelemetry = function (domainObject) {
return domainObject.type === 'telemetry-mean';
};
MeanTelemetryProvider.prototype.supportsRequest =
MeanTelemetryProvider.prototype.supportsSubscribe =
MeanTelemetryProvider.prototype.canProvideTelemetry;
MeanTelemetryProvider.prototype.subscribe = function (domainObject, callback) {
var wrappedUnsubscribe;
var unsubscribeCalled = false;
var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint);
var samples = domainObject.samples;
this.objectAPI.get(objectId)
.then(function (linkedDomainObject) {
if (!unsubscribeCalled) {
wrappedUnsubscribe = this.subscribeToAverage(linkedDomainObject, samples, callback);
}
}.bind(this))
.catch(logError);
return function unsubscribe() {
unsubscribeCalled = true;
if (wrappedUnsubscribe !== undefined) {
wrappedUnsubscribe();
}
};
};
MeanTelemetryProvider.prototype.subscribeToAverage = function (domainObject, samples, callback) {
var telemetryAverager = new TelemetryAverager(this.telemetryAPI, this.timeAPI, domainObject, samples);
return this.telemetryAPI.subscribe(domainObject, function (telemetryDatum) {
var avgData = telemetryAverager.createAverageDatum(telemetryDatum);
if (telemetryAverager.sampleCount() === samples) {
callback(avgData);
}
}.bind(this));
};
MeanTelemetryProvider.prototype.request = function (domainObject, request) {
var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint);
var samples = domainObject.samples;
return this.objectAPI.get(objectId).then(function (linkedDomainObject) {
return this.requestAverageTelemetry(linkedDomainObject, request, samples);
}.bind(this));
};
/**
* @private
*/
MeanTelemetryProvider.prototype.requestAverageTelemetry = function (domainObject, request, samples) {
var averageData = [];
var telemetryAverager = new TelemetryAverager(this.telemetryAPI, this.timeAPI, domainObject, samples);
return this.telemetryAPI.request(domainObject, request).then(function (telemetryData) {
telemetryData.forEach(function (datum) {
var avgDatum = telemetryAverager.createAverageDatum(datum);
if (telemetryAverager.sampleCount() === samples) {
averageData.push(avgDatum);
}
}.bind(this));
return averageData;
}.bind(this));
};
/**
* @private
*/
MeanTelemetryProvider.prototype.getLinkedObject = function (domainObject) {
var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint);
return this.objectAPI.get(objectId);
};
function logError(error) {
if (error.stack) {
console.error(error.stack);
} else {
console.error(error);
}
}
return MeanTelemetryProvider;
});

View File

@@ -1,460 +0,0 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*jshint latedef: nofunc */
define([
"./MeanTelemetryProvider",
"./MockTelemetryApi"
], function (
MeanTelemetryProvider,
MockTelemetryApi
) {
var RANGE_KEY = 'value';
describe("The Mean Telemetry Provider", function () {
var mockApi;
var meanTelemetryProvider;
var outstandingPromises = 0;
var mockDomainObject;
var associatedObject;
beforeEach(function () {
createMockApi();
setTimeSystemTo('utc');
createMockObjects();
meanTelemetryProvider = new MeanTelemetryProvider(mockApi);
});
it("supports telemetry-mean objects only", function () {
var mockTelemetryMeanObject = mockObjectWithType('telemetry-mean');
var mockOtherObject = mockObjectWithType('other');
expect(meanTelemetryProvider.canProvideTelemetry(mockTelemetryMeanObject)).toBe(true);
expect(meanTelemetryProvider.canProvideTelemetry(mockOtherObject)).toBe(false);
});
describe("the subscribe function", function () {
var subscriptionCallback;
beforeEach(function () {
subscriptionCallback = jasmine.createSpy('subscriptionCallback');
});
it("subscribes to telemetry for the associated object", function () {
meanTelemetryProvider.subscribe(mockDomainObject);
expectObjectWasSubscribedTo(associatedObject);
});
it("returns a function that unsubscribes from the associated object", function () {
var unsubscribe = meanTelemetryProvider.subscribe(mockDomainObject);
waitsFor(allPromisesToBeResolved);
runs(unsubscribe);
expectUnsubscribeFrom(associatedObject);
});
it("returns an average only when the sample size is reached", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313}
];
setSampleSize(5);
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
feedInputTelemetry(inputTelemetry);
expectNoAverageForTelemetry(inputTelemetry);
});
it("correctly averages a sample of five values", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313},
{'utc': 5, 'defaultRange': 1.1231}
];
var expectedAverages = [{
'utc': 5, 'value': 222.44888
}];
setSampleSize(5);
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(expectedAverages, inputTelemetry);
});
it("correctly averages a sample of ten values", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313},
{'utc': 5, 'defaultRange': 1.1231},
{'utc': 6, 'defaultRange': 2323.12},
{'utc': 7, 'defaultRange': 532.12},
{'utc': 8, 'defaultRange': 453.543},
{'utc': 9, 'defaultRange': 89.2111},
{'utc': 10, 'defaultRange': 0.543}
];
var expectedAverages = [{
'utc': 10, 'value': 451.07815
}];
setSampleSize(10);
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(expectedAverages, inputTelemetry);
});
it("only averages values within its sample window", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313},
{'utc': 5, 'defaultRange': 1.1231},
{'utc': 6, 'defaultRange': 2323.12},
{'utc': 7, 'defaultRange': 532.12},
{'utc': 8, 'defaultRange': 453.543},
{'utc': 9, 'defaultRange': 89.2111},
{'utc': 10, 'defaultRange': 0.543}
];
var expectedAverages = [
{'utc': 5, 'value': 222.44888},
{'utc': 6, 'value': 662.4482599999999},
{'utc': 7, 'value': 704.6078},
{'utc': 8, 'value': 773.02748},
{'utc': 9, 'value': 679.8234399999999},
{'utc': 10, 'value': 679.70742}
];
setSampleSize(5);
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(expectedAverages, inputTelemetry);
});
describe("given telemetry input with range values", function () {
var inputTelemetry;
beforeEach(function () {
inputTelemetry = [{
'utc': 1,
'rangeKey': 5678,
'otherKey': 9999
}];
setSampleSize(1);
});
it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () {
var averageTelemetryForRangeKey = [{
'utc': 1,
'value': 5678
}];
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
mockApi.telemetry.setDefaultRangeTo('rangeKey');
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(averageTelemetryForRangeKey, inputTelemetry);
});
it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () {
var averageTelemetryForOtherKey = [{
'utc': 1,
'value': 9999
}];
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
mockApi.telemetry.setDefaultRangeTo('otherKey');
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(averageTelemetryForOtherKey, inputTelemetry);
});
});
describe("given telemetry input with range values", function () {
var inputTelemetry;
beforeEach(function () {
inputTelemetry = [{
'utc': 1,
'rangeKey': 5678,
'otherKey': 9999
}];
setSampleSize(1);
});
it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () {
var averageTelemetryForRangeKey = [{
'utc': 1,
'value': 5678
}];
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
mockApi.telemetry.setDefaultRangeTo('rangeKey');
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(averageTelemetryForRangeKey, inputTelemetry);
});
it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () {
var averageTelemetryForOtherKey = [{
'utc': 1,
'value': 9999
}];
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
mockApi.telemetry.setDefaultRangeTo('otherKey');
feedInputTelemetry(inputTelemetry);
expectAveragesForTelemetry(averageTelemetryForOtherKey, inputTelemetry);
});
});
function feedInputTelemetry(inputTelemetry) {
waitsFor(allPromisesToBeResolved);
runs(function () {
inputTelemetry.forEach(mockApi.telemetry.mockReceiveTelemetry);
});
}
function expectNoAverageForTelemetry(inputTelemetry) {
waitsFor(allPromisesToBeResolved);
runs(function () {
expect(subscriptionCallback).not.toHaveBeenCalled();
});
}
function expectAveragesForTelemetry(expectedAverages) {
waitsFor(allPromisesToBeResolved);
runs(function () {
expectedAverages.forEach(function (averageDatum) {
expect(subscriptionCallback).toHaveBeenCalledWith(averageDatum);
});
});
}
function expectObjectWasSubscribedTo(object) {
waitsFor(allPromisesToBeResolved);
runs(function () {
expect(mockApi.telemetry.subscribe).toHaveBeenCalledWith(object, jasmine.any(Function));
});
}
function expectUnsubscribeFrom() {
waitsFor(allPromisesToBeResolved);
runs(function () {
expect(mockApi.telemetry.unsubscribe).toHaveBeenCalled();
});
}
});
describe("the request function", function () {
it("requests telemetry for the associated object", function () {
meanTelemetryProvider.request(mockDomainObject);
expectTelemetryToBeRequestedFor(associatedObject);
});
it("returns an average only when the sample size is reached", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313}
];
var promiseForAverage;
setSampleSize(5);
whenTelemetryRequestedReturn(inputTelemetry);
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
expectEmptyResponse(promiseForAverage);
});
it("correctly averages a sample of five values", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313},
{'utc': 5, 'defaultRange': 1.1231}
];
var promiseForAverage;
setSampleSize(5);
whenTelemetryRequestedReturn(inputTelemetry);
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
expectAverageToBe(222.44888, promiseForAverage);
});
it("correctly averages a sample of ten values", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313},
{'utc': 5, 'defaultRange': 1.1231},
{'utc': 6, 'defaultRange': 2323.12},
{'utc': 7, 'defaultRange': 532.12},
{'utc': 8, 'defaultRange': 453.543},
{'utc': 9, 'defaultRange': 89.2111},
{'utc': 10, 'defaultRange': 0.543}
];
var promiseForAverage;
setSampleSize(10);
whenTelemetryRequestedReturn(inputTelemetry);
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
expectAverageToBe(451.07815, promiseForAverage);
});
it("only averages values within its sample window", function () {
var inputTelemetry = [
{'utc': 1, 'defaultRange': 123.1231},
{'utc': 2, 'defaultRange': 321.3223},
{'utc': 3, 'defaultRange': 111.4446},
{'utc': 4, 'defaultRange': 555.2313},
{'utc': 5, 'defaultRange': 1.1231},
{'utc': 6, 'defaultRange': 2323.12},
{'utc': 7, 'defaultRange': 532.12},
{'utc': 8, 'defaultRange': 453.543},
{'utc': 9, 'defaultRange': 89.2111},
{'utc': 10, 'defaultRange': 0.543}
];
var promiseForAverage;
setSampleSize(5);
whenTelemetryRequestedReturn(inputTelemetry);
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
expectAverageToBe(679.70742, promiseForAverage);
});
function expectAverageToBe(expectedValue, promiseForAverage) {
var averageData;
promiseForAverage.then(function (data) {
averageData = data;
});
waitsFor(function () {
return averageData !== undefined;
}, 'data to return from request', 1);
runs(function () {
var averageDatum = averageData[averageData.length - 1];
expect(averageDatum[RANGE_KEY]).toBe(expectedValue);
});
}
function expectEmptyResponse(promiseForAverage) {
var averageData;
promiseForAverage.then(function (data) {
averageData = data;
});
waitsFor(function () {
return averageData !== undefined;
}, 'data to return from request', 1);
runs(function () {
expect(averageData.length).toBe(0);
});
}
function whenTelemetryRequestedReturn(telemetry) {
mockApi.telemetry.request.andReturn(resolvePromiseWith(telemetry));
}
function expectTelemetryToBeRequestedFor(object) {
waitsFor(allPromisesToBeResolved);
runs(function () {
expect(mockApi.telemetry.request).toHaveBeenCalledWith(object, undefined);
});
}
});
function createMockObjects() {
mockDomainObject = {
telemetryPoint: 'someTelemetryPoint'
};
associatedObject = {};
mockApi.objects.get.andReturn(resolvePromiseWith(associatedObject));
}
function setSampleSize(sampleSize) {
mockDomainObject.samples = sampleSize;
}
function createMockApi() {
mockApi = {
telemetry: new MockTelemetryApi(),
objects: createMockObjectApi(),
time: createMockTimeApi()
};
}
function createMockObjectApi() {
return jasmine.createSpyObj('ObjectAPI', [
'get'
]);
}
function mockObjectWithType(type) {
return {
type: type
};
}
function resolvePromiseWith(value) {
outstandingPromises++;
return Promise.resolve(value).then(function () {
outstandingPromises--;
return value;
});
}
function allPromisesToBeResolved() {
return outstandingPromises === 0;
}
function createMockTimeApi() {
return jasmine.createSpyObj("timeApi", ['timeSystem']);
}
function setTimeSystemTo(timeSystemKey) {
mockApi.time.timeSystem.andReturn({
key: timeSystemKey
});
}
});
});

View File

@@ -1,106 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global jasmine, spyOn */
define([], function () {
function MockTelemetryApi() {
this.createSpy('subscribe');
this.createSpy('getMetadata');
this.metadata = this.createMockMetadata();
this.setDefaultRangeTo('defaultRange');
this.unsubscribe = jasmine.createSpy('unsubscribe');
this.mockReceiveTelemetry = this.mockReceiveTelemetry.bind(this);
}
MockTelemetryApi.prototype.subscribe = function () {
return this.unsubscribe;
};
MockTelemetryApi.prototype.getMetadata = function (object) {
return this.metadata;
};
MockTelemetryApi.prototype.request = jasmine.createSpy('request');
MockTelemetryApi.prototype.getValueFormatter = function (valueMetadata) {
var mockValueFormatter = jasmine.createSpyObj("valueFormatter", [
"parse"
]);
mockValueFormatter.parse.andCallFake(function (value) {
return value[valueMetadata.key];
});
return mockValueFormatter;
};
MockTelemetryApi.prototype.mockReceiveTelemetry = function (newTelemetryDatum) {
var subscriptionCallback = this.subscribe.mostRecentCall.args[1];
subscriptionCallback(newTelemetryDatum);
};
/**
* @private
*/
MockTelemetryApi.prototype.onRequestReturn = function (telemetryData) {
this.requestTelemetry = telemetryData;
};
/**
* @private
*/
MockTelemetryApi.prototype.setDefaultRangeTo = function (rangeKey) {
var mockMetadataValue = {
key: rangeKey
};
this.metadata.valuesForHints.andReturn([mockMetadataValue]);
};
/**
* @private
*/
MockTelemetryApi.prototype.createMockMetadata = function () {
var mockMetadata = jasmine.createSpyObj("metadata", [
'value',
'valuesForHints'
]);
mockMetadata.value.andCallFake(function (key) {
return {
key: key
};
});
return mockMetadata;
};
/**
* @private
*/
MockTelemetryApi.prototype.createSpy = function (functionName) {
this[functionName] = this[functionName].bind(this);
spyOn(this, functionName);
this[functionName].andCallThrough();
};
return MockTelemetryApi;
});

View File

@@ -1,112 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
function TelemetryAverager(telemetryAPI, timeAPI, domainObject, samples) {
this.telemetryAPI = telemetryAPI;
this.timeAPI = timeAPI;
this.domainObject = domainObject;
this.samples = samples;
this.averagingWindow = [];
this.rangeKey = undefined;
this.rangeFormatter = undefined;
this.setRangeKeyAndFormatter();
// Defined dynamically based on current time system
this.domainKey = undefined;
this.domainFormatter = undefined;
}
TelemetryAverager.prototype.createAverageDatum = function (telemetryDatum) {
this.setDomainKeyAndFormatter();
var timeValue = this.domainFormatter.parse(telemetryDatum);
var rangeValue = this.rangeFormatter.parse(telemetryDatum);
this.averagingWindow.push(rangeValue);
if (this.averagingWindow.length > this.samples) {
this.averagingWindow.shift();
}
var averageValue = this.calculateMean();
var meanDatum = {};
meanDatum[this.domainKey] = timeValue;
meanDatum.value = averageValue;
return meanDatum;
};
/**
* @private
*/
TelemetryAverager.prototype.calculateMean = function () {
var sum = 0;
var i = 0;
for (; i < this.averagingWindow.length; i++) {
sum += this.averagingWindow[i];
}
return sum / this.averagingWindow.length;
};
/**
* @private
*/
TelemetryAverager.prototype.setDomainKeyAndFormatter = function () {
var domainKey = this.timeAPI.timeSystem().key;
if (domainKey !== this.domainKey) {
this.domainKey = domainKey;
this.domainFormatter = this.getFormatter(domainKey);
}
};
/**
* @private
*/
TelemetryAverager.prototype.setRangeKeyAndFormatter = function () {
var metadatas = this.telemetryAPI.getMetadata(this.domainObject);
var rangeValues = metadatas.valuesForHints(['range']);
this.rangeKey = rangeValues[0].key;
this.rangeFormatter = this.getFormatter(this.rangeKey);
};
/**
* @private
*/
TelemetryAverager.prototype.getFormatter = function (key) {
var objectMetadata = this.telemetryAPI.getMetadata(this.domainObject);
var valueMetadata = objectMetadata.value(key);
return this.telemetryAPI.getValueFormatter(valueMetadata);
};
TelemetryAverager.prototype.sampleCount = function () {
return this.averagingWindow.length;
};
return TelemetryAverager;
});

View File

@@ -19,7 +19,6 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global console */
define([], function () {
/**
@@ -29,7 +28,7 @@ define([], function () {
* @memberof module:openmct
*/
function ViewRegistry() {
this.providers = {};
this.providers = [];
}
@@ -40,17 +39,9 @@ define([], function () {
* which can provide views of this object
*/
ViewRegistry.prototype.get = function (item) {
return this.getAllProviders()
.filter(function (provider) {
return provider.canView(item);
});
};
/**
* @private
*/
ViewRegistry.prototype.getAllProviders = function () {
return Object.values(this.providers);
return this.providers.filter(function (provider) {
return provider.canView(item);
});
};
/**
@@ -61,22 +52,7 @@ define([], function () {
* @memberof module:openmct.ViewRegistry#
*/
ViewRegistry.prototype.addProvider = function (provider) {
var key = provider.key;
if (key === undefined) {
throw "View providers must have a unique 'key' property defined";
}
if (this.providers[key] !== undefined) {
console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key);
}
this.providers[key] = provider;
};
/**
* @private
*/
ViewRegistry.prototype.getByProviderKey = function (key) {
return this.providers[key];
this.providers.push(provider);
};
/**
@@ -115,12 +91,6 @@ define([], function () {
* Exposes types of views in Open MCT.
*
* @interface ViewProvider
* @property {string} key a unique identifier for this view
* @property {string} name the human-readable name of this view
* @property {string} [description] a longer-form description (typically
* a single sentence or short paragraph) of this kind of view
* @property {string} [cssClass] the CSS class to apply to labels for this
* view (to add icons, for instance)
* @memberof module:openmct
*/
@@ -130,28 +100,15 @@ define([], function () {
* When called by Open MCT, this may include additional arguments
* which are on the path to the object to be viewed; for instance,
* when viewing "A Folder" within "My Items", this method will be
* invoked with "A Folder" (as a domain object) as the first argument
* invoked with "A Folder" (as a domain object) as the first argument,
* and "My Items" as the second argument.
*
* @method canView
* @memberof module:openmct.ViewProvider#
* @param {module:openmct.DomainObject} domainObject the domain object
* to be viewed
* @returns {boolean} 'true' if the view applies to the provided object,
* otherwise 'false'.
*/
/**
* Optional method determining the priority of a given view. If this
* function is not defined on a view provider, then a default priority
* of 100 will be applicable for all objects supported by this view.
*
* @method priority
* @memberof module:openmct.ViewProvider#
* @param {module:openmct.DomainObject} domainObject the domain object
* to be viewed
* @returns {number} The priority of the view. If multiple views could apply
* to an object, the view that returns the lowest number will be
* the default view.
* @returns {boolean} true if this domain object can be viewed using
* this provider
*/
/**
@@ -169,6 +126,27 @@ define([], function () {
* @returns {module:openmct.View} a view of this domain object
*/
/**
* Get metadata associated with this view provider. This may be used
* to populate the user interface with options associated with this
* view provider.
*
* @method metadata
* @memberof module:openmct.ViewProvider#
* @returns {module:openmct.ViewProvider~ViewMetadata} view metadata
*/
/**
* @typedef ViewMetadata
* @memberof module:openmct.ViewProvider~
* @property {string} name the human-readable name of this view
* @property {string} key a machine-readable name for this view
* @property {string} [description] a longer-form description (typically
* a single sentence or short paragraph) of this kind of view
* @property {string} cssClass the CSS class to apply to labels for this
* view (to add icons, for instance)
*/
return ViewRegistry;
});

View File

@@ -59,7 +59,7 @@ requirejs.config({
"moment": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
"saveAs": "bower_components/file-saver/FileSaver.min",
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
"screenfull": "bower_components/screenfull/dist/screenfull.min",
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",