Compare commits

...

4 Commits

803 changed files with 63055 additions and 51582 deletions

View File

@ -20,61 +20,79 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/EventTelemetryProvider" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
EventTelemetryProvider * Administration. All rights reserved.
) { *
"use strict"; * 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.
*****************************************************************************/
return { import EventTelemetryProvider from './src/EventTelemetryProvider';
name: "example/eventGenerator",
definition: { "use strict";
"name": "Event Message Generator",
"description": "For development use. Creates sample event message data that mimics a live data stream.", export default {
"extensions": { name: "example/eventGenerator",
"components": [ definition: {
{ "name": "Event Message Generator",
"implementation": EventTelemetryProvider, "description": "For development use. Creates sample event message data that mimics a live data stream.",
"type": "provider", "extensions": {
"provides": "telemetryService", "components": [
"depends": [ {
"$q", "implementation": EventTelemetryProvider,
"$timeout" "type": "provider",
"provides": "telemetryService",
"depends": [
"$q",
"$timeout"
]
}
],
"types": [
{
"key": "eventGenerator",
"name": "Event Message Generator",
"cssClass": "icon-generator-events",
"description": "For development use. Creates sample event message data that mimics a live data stream.",
"priority": 10,
"features": "creation",
"model": {
"telemetry": {}
},
"telemetry": {
"source": "eventGenerator",
"domains": [
{
"key": "utc",
"name": "Timestamp",
"format": "utc"
}
],
"ranges": [
{
"key": "message",
"name": "Message",
"format": "string"
}
] ]
} }
], }
"types": [ ]
{
"key": "eventGenerator",
"name": "Event Message Generator",
"cssClass": "icon-generator-events",
"description": "For development use. Creates sample event message data that mimics a live data stream.",
"priority": 10,
"features": "creation",
"model": {
"telemetry": {}
},
"telemetry": {
"source": "eventGenerator",
"domains": [
{
"key": "utc",
"name": "Timestamp",
"format": "utc"
}
],
"ranges": [
{
"key": "message",
"name": "Message",
"format": "string"
}
]
}
}
]
}
} }
}; }
}); };

View File

@ -25,38 +25,62 @@
* Created by chacskaylo on 06/18/2015. * Created by chacskaylo on 06/18/2015.
* Modified by shale on 06/23/2015. * Modified by shale on 06/23/2015.
*/ */
define( /*****************************************************************************
['../data/transcript.json'], * Open MCT, Copyright (c) 2014-2021, United States Government
function (messages) { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
var firstObservedTime = Date.now(); /**
* Module defining EventTelemetry.
* Created by chacskaylo on 06/18/2015.
* Modified by shale on 06/23/2015.
*/
import messages from '../data/transcript.json';
function EventTelemetry(request, interval) { "use strict";
var latestObservedTime = Date.now(), var firstObservedTime = Date.now();
count = Math.floor((latestObservedTime - firstObservedTime) / interval),
generatorData = {};
generatorData.getPointCount = function () { function EventTelemetry(request, interval) {
return count;
};
generatorData.getDomainValue = function (i, domain) { var latestObservedTime = Date.now(),
return i * interval count = Math.floor((latestObservedTime - firstObservedTime) / interval),
+ (domain !== 'delta' ? firstObservedTime : 0); generatorData = {};
};
generatorData.getRangeValue = function (i, range) { generatorData.getPointCount = function () {
var domainDelta = this.getDomainValue(i) - firstObservedTime, return count;
ind = i % messages.length; };
return messages[ind] + " - [" + domainDelta.toString() + "]"; generatorData.getDomainValue = function (i, domain) {
}; return i * interval
+ (domain !== 'delta' ? firstObservedTime : 0);
};
return generatorData; generatorData.getRangeValue = function (i, range) {
} var domainDelta = this.getDomainValue(i) - firstObservedTime,
ind = i % messages.length;
return EventTelemetry; return messages[ind] + " - [" + domainDelta.toString() + "]";
} };
);
return generatorData;
}
export default EventTelemetry;

View File

@ -23,96 +23,118 @@
/** /**
* Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015. * Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
*/ */
define( /*****************************************************************************
["./EventTelemetry"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (EventTelemetry) { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
/** /**
* * Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
* @constructor */
*/ import EventTelemetry from './EventTelemetry';
function EventTelemetryProvider($q, $timeout) {
var subscriptions = [], "use strict";
genInterval = 1000,
/**
*
* @constructor
*/
function EventTelemetryProvider($q, $timeout) {
var subscriptions = [],
genInterval = 1000,
generating = false;
//
function matchesSource(request) {
return request.source === "eventGenerator";
}
// Used internally; this will be repacked by doPackage
function generateData(request) {
return {
key: request.key,
telemetry: new EventTelemetry(request, genInterval)
};
}
//
function doPackage(results) {
var packaged = {};
results.forEach(function (result) {
packaged[result.key] = result.telemetry;
});
// Format as expected (sources -> keys -> telemetry)
return { eventGenerator: packaged };
}
function requestTelemetry(requests) {
return $timeout(function () {
return doPackage(requests.filter(matchesSource).map(generateData));
}, 0);
}
function handleSubscriptions(timeout) {
subscriptions.forEach(function (subscription) {
var requests = subscription.requests;
subscription.callback(doPackage(
requests.filter(matchesSource).map(generateData)
));
});
}
function startGenerating() {
generating = true;
$timeout(function () {
handleSubscriptions();
if (generating && subscriptions.length > 0) {
startGenerating();
} else {
generating = false; generating = false;
//
function matchesSource(request) {
return request.source === "eventGenerator";
} }
}, genInterval);
}
// Used internally; this will be repacked by doPackage function subscribe(callback, requests) {
function generateData(request) { var subscription = {
return { callback: callback,
key: request.key, requests: requests
telemetry: new EventTelemetry(request, genInterval) };
}; function unsubscribe() {
} subscriptions = subscriptions.filter(function (s) {
return s !== subscription;
// });
function doPackage(results) {
var packaged = {};
results.forEach(function (result) {
packaged[result.key] = result.telemetry;
});
// Format as expected (sources -> keys -> telemetry)
return { eventGenerator: packaged };
}
function requestTelemetry(requests) {
return $timeout(function () {
return doPackage(requests.filter(matchesSource).map(generateData));
}, 0);
}
function handleSubscriptions(timeout) {
subscriptions.forEach(function (subscription) {
var requests = subscription.requests;
subscription.callback(doPackage(
requests.filter(matchesSource).map(generateData)
));
});
}
function startGenerating() {
generating = true;
$timeout(function () {
handleSubscriptions();
if (generating && subscriptions.length > 0) {
startGenerating();
} else {
generating = false;
}
}, genInterval);
}
function subscribe(callback, requests) {
var subscription = {
callback: callback,
requests: requests
};
function unsubscribe() {
subscriptions = subscriptions.filter(function (s) {
return s !== subscription;
});
}
subscriptions.push(subscription);
if (!generating) {
startGenerating();
}
return unsubscribe;
}
return {
requestTelemetry: requestTelemetry,
subscribe: subscribe
};
} }
return EventTelemetryProvider; subscriptions.push(subscription);
if (!generating) {
startGenerating();
}
return unsubscribe;
} }
);
return {
requestTelemetry: requestTelemetry,
subscribe: subscribe
};
}
export default EventTelemetryProvider;

View File

@ -20,70 +20,90 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([], function () { /*****************************************************************************
'use strict'; * Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/** 'use strict';
* An example of using the `exportService`; queries for telemetry
* and provides the results as a CSV file. /**
* @param {platform/exporters.ExportService} exportService the * An example of using the `exportService`; queries for telemetry
* service which will handle the CSV export * and provides the results as a CSV file.
* @param {ActionContext} context the action's context * @param {platform/exporters.ExportService} exportService the
* @constructor * service which will handle the CSV export
* @memberof example/export * @param {ActionContext} context the action's context
* @implements {Action} * @constructor
*/ * @memberof example/export
function ExportTelemetryAsCSVAction(exportService, context) { * @implements {Action}
this.exportService = exportService; */
this.context = context; function ExportTelemetryAsCSVAction(exportService, context) {
this.exportService = exportService;
this.context = context;
}
ExportTelemetryAsCSVAction.prototype.perform = function () {
var context = this.context,
domainObject = context.domainObject,
telemetry = domainObject.getCapability("telemetry"),
metadata = telemetry.getMetadata(),
domains = metadata.domains,
ranges = metadata.ranges,
exportService = this.exportService;
function getName(domainOrRange) {
return domainOrRange.name;
} }
ExportTelemetryAsCSVAction.prototype.perform = function () { telemetry.requestData({}).then(function (series) {
var context = this.context, var headers = domains.map(getName).concat(ranges.map(getName)),
domainObject = context.domainObject, rows = [],
telemetry = domainObject.getCapability("telemetry"), row,
metadata = telemetry.getMetadata(), i;
domains = metadata.domains,
ranges = metadata.ranges,
exportService = this.exportService;
function getName(domainOrRange) { function copyDomainsToRow(telemetryRow, index) {
return domainOrRange.name; domains.forEach(function (domain) {
telemetryRow[domain.name] = series.getDomainValue(index, domain.key);
});
} }
telemetry.requestData({}).then(function (series) { function copyRangesToRow(telemetryRow, index) {
var headers = domains.map(getName).concat(ranges.map(getName)), ranges.forEach(function (range) {
rows = [], telemetryRow[range.name] = series.getRangeValue(index, range.key);
row, });
i; }
function copyDomainsToRow(telemetryRow, index) { for (i = 0; i < series.getPointCount(); i += 1) {
domains.forEach(function (domain) { row = {};
telemetryRow[domain.name] = series.getDomainValue(index, domain.key); copyDomainsToRow(row, i);
}); copyRangesToRow(row, i);
} rows.push(row);
}
function copyRangesToRow(telemetryRow, index) { exportService.exportCSV(rows, { headers: headers });
ranges.forEach(function (range) { });
telemetryRow[range.name] = series.getRangeValue(index, range.key); };
});
}
for (i = 0; i < series.getPointCount(); i += 1) { ExportTelemetryAsCSVAction.appliesTo = function (context) {
row = {}; return context.domainObject
copyDomainsToRow(row, i); && context.domainObject.hasCapability("telemetry");
copyRangesToRow(row, i); };
rows.push(row);
}
exportService.exportCSV(rows, { headers: headers }); export default ExportTelemetryAsCSVAction;
});
};
ExportTelemetryAsCSVAction.appliesTo = function (context) {
return context.domainObject
&& context.domainObject.hasCapability("telemetry");
};
return ExportTelemetryAsCSVAction;
});

View File

@ -20,27 +20,47 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
'./ExportTelemetryAsCSVAction' * Open MCT, Copyright (c) 2014-2021, United States Government
], function (ExportTelemetryAsCSVAction) { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
return { import ExportTelemetryAsCSVAction from './ExportTelemetryAsCSVAction';
name: "example/export",
definition: { "use strict";
"name": "Example of using CSV Export",
"extensions": { export default {
"actions": [ name: "example/export",
{ definition: {
"key": "example.export", "name": "Example of using CSV Export",
"name": "Export Telemetry as CSV", "extensions": {
"implementation": ExportTelemetryAsCSVAction, "actions": [
"category": "contextual", {
"cssClass": "icon-download", "key": "example.export",
"depends": ["exportService"] "name": "Export Telemetry as CSV",
} "implementation": ExportTelemetryAsCSVAction,
] "category": "contextual",
} "cssClass": "icon-download",
"depends": ["exportService"]
}
]
} }
}; }
}); };

View File

@ -20,34 +20,52 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/ExampleFormController" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
ExampleFormController * Administration. All rights reserved.
) { *
"use strict"; * 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.
*****************************************************************************/
return { import ExampleFormController from './src/ExampleFormController';
name: "example/forms",
definition: { "use strict";
"name": "Declarative Forms example",
"sources": "src", export default {
"extensions": { name: "example/forms",
"controllers": [ definition: {
{ "name": "Declarative Forms example",
"key": "ExampleFormController", "sources": "src",
"implementation": ExampleFormController, "extensions": {
"depends": [ "controllers": [
"$scope" {
] "key": "ExampleFormController",
} "implementation": ExampleFormController,
], "depends": [
"routes": [ "$scope"
{ ]
"templateUrl": "templates/exampleForm.html" }
} ],
] "routes": [
} {
"templateUrl": "templates/exampleForm.html"
}
]
} }
}; }
}); };

View File

@ -20,186 +20,203 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
function ExampleFormController($scope) { "use strict";
$scope.state = {
}; function ExampleFormController($scope) {
$scope.state = {
$scope.toolbar = { };
name: "An example toolbar.",
sections: [ $scope.toolbar = {
name: "An example toolbar.",
sections: [
{
description: "First section",
items: [
{ {
description: "First section", name: "X",
items: [ description: "X coordinate",
{ control: "textfield",
name: "X", pattern: "^\\d+$",
description: "X coordinate", disabled: true,
control: "textfield", size: 2,
pattern: "^\\d+$", key: "x"
disabled: true,
size: 2,
key: "x"
},
{
name: "Y",
description: "Y coordinate",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "y"
},
{
name: "W",
description: "Cell width",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "w"
},
{
name: "H",
description: "Cell height",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "h"
}
]
}, },
{ {
description: "Second section", name: "Y",
items: [ description: "Y coordinate",
{ control: "textfield",
control: "button", pattern: "^\\d+$",
csslass: "icon-save", size: 2,
click: function () { key: "y"
console.log("Save");
}
},
{
control: "button",
csslass: "icon-x",
description: "Button B",
click: function () {
console.log("Cancel");
}
},
{
control: "button",
csslass: "icon-trash",
description: "Button C",
disabled: true,
click: function () {
console.log("Delete");
}
}
]
}, },
{ {
items: [ name: "W",
{ description: "Cell width",
control: "color", control: "textfield",
key: "color" pattern: "^\\d+$",
} size: 2,
] key: "w"
},
{
name: "H",
description: "Cell height",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "h"
}
]
},
{
description: "Second section",
items: [
{
control: "button",
csslass: "icon-save",
click: function () {
console.log("Save");
}
},
{
control: "button",
csslass: "icon-x",
description: "Button B",
click: function () {
console.log("Cancel");
}
},
{
control: "button",
csslass: "icon-trash",
description: "Button C",
disabled: true,
click: function () {
console.log("Delete");
}
} }
] ]
}; },
{
$scope.form = { items: [
name: "An example form.",
sections: [
{ {
name: "First section", control: "color",
rows: [ key: "color"
{
name: "Check me",
control: "checkbox",
key: "checkMe"
},
{
name: "Enter your name",
required: true,
control: "textfield",
key: "yourName"
},
{
name: "Enter a number",
control: "textfield",
pattern: "^\\d+$",
key: "aNumber"
}
]
},
{
name: "Second section",
rows: [
{
name: "Pick a date",
required: true,
description: "Enter date in form YYYY-DDD",
control: "datetime",
key: "aDate"
},
{
name: "Choose something",
control: "select",
options: [
{
name: "Hats",
value: "hats"
},
{
name: "Bats",
value: "bats"
},
{
name: "Cats",
value: "cats"
},
{
name: "Mats",
value: "mats"
}
],
key: "aChoice"
},
{
name: "Choose something",
control: "select",
required: true,
options: [
{
name: "Hats",
value: "hats"
},
{
name: "Bats",
value: "bats"
},
{
name: "Cats",
value: "cats"
},
{
name: "Mats",
value: "mats"
}
],
key: "aRequiredChoice"
}
]
} }
] ]
}; }
} ]
};
return ExampleFormController; $scope.form = {
} name: "An example form.",
); sections: [
{
name: "First section",
rows: [
{
name: "Check me",
control: "checkbox",
key: "checkMe"
},
{
name: "Enter your name",
required: true,
control: "textfield",
key: "yourName"
},
{
name: "Enter a number",
control: "textfield",
pattern: "^\\d+$",
key: "aNumber"
}
]
},
{
name: "Second section",
rows: [
{
name: "Pick a date",
required: true,
description: "Enter date in form YYYY-DDD",
control: "datetime",
key: "aDate"
},
{
name: "Choose something",
control: "select",
options: [
{
name: "Hats",
value: "hats"
},
{
name: "Bats",
value: "bats"
},
{
name: "Cats",
value: "cats"
},
{
name: "Mats",
value: "mats"
}
],
key: "aChoice"
},
{
name: "Choose something",
control: "select",
required: true,
options: [
{
name: "Hats",
value: "hats"
},
{
name: "Bats",
value: "bats"
},
{
name: "Cats",
value: "cats"
},
{
name: "Mats",
value: "mats"
}
],
key: "aRequiredChoice"
}
]
}
]
};
}
export default ExampleFormController;

View File

@ -1,142 +1,136 @@
define([ import _ from 'lodash';
'lodash'
], function (
_
) {
var METADATA_BY_TYPE = { var METADATA_BY_TYPE = {
'generator': { 'generator': {
values: [ values: [
{ {
key: "name", key: "name",
name: "Name", name: "Name",
format: "string" format: "string"
}, },
{ {
key: "utc", key: "utc",
name: "Time", name: "Time",
format: "utc", format: "utc",
hints: { hints: {
domain: 1 domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "cos",
name: "Cosine",
unit: "deg",
formatString: '%0.2f',
hints: {
domain: 3
}
},
// Need to enable "LocalTimeSystem" plugin to make use of this
// {
// key: "local",
// name: "Time",
// format: "local-format",
// source: "utc",
// hints: {
// domain: 3
// }
// },
{
key: "sin",
name: "Sine",
unit: "Hz",
formatString: '%0.2f',
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
unit: "deg",
formatString: '%0.2f',
hints: {
range: 2
}
} }
] },
}, {
'example.state-generator': { key: "yesterday",
values: [ name: "Yesterday",
{ format: "utc",
key: "name", hints: {
name: "Name", domain: 2
format: "string"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "local",
name: "Time",
format: "utc",
source: "utc",
hints: {
domain: 2
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
enumerations: [
{
value: 0,
string: "OFF"
},
{
value: 1,
string: "ON"
}
],
hints: {
range: 1
}
},
{
key: "value",
name: "Value",
hints: {
range: 2
}
} }
] },
} {
}; key: "cos",
name: "Cosine",
function GeneratorMetadataProvider() { unit: "deg",
formatString: '%0.2f',
hints: {
domain: 3
}
},
// Need to enable "LocalTimeSystem" plugin to make use of this
// {
// key: "local",
// name: "Time",
// format: "local-format",
// source: "utc",
// hints: {
// domain: 3
// }
// },
{
key: "sin",
name: "Sine",
unit: "Hz",
formatString: '%0.2f',
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
unit: "deg",
formatString: '%0.2f',
hints: {
range: 2
}
}
]
},
'example.state-generator': {
values: [
{
key: "name",
name: "Name",
format: "string"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "local",
name: "Time",
format: "utc",
source: "utc",
hints: {
domain: 2
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
enumerations: [
{
value: 0,
string: "OFF"
},
{
value: 1,
string: "ON"
}
],
hints: {
range: 1
}
},
{
key: "value",
name: "Value",
hints: {
range: 2
}
}
]
} }
};
GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) { function GeneratorMetadataProvider() {
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) { }
return Object.assign(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
};
return GeneratorMetadataProvider; GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) {
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
};
}); GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return Object.assign(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
};
export default GeneratorMetadataProvider;

View File

@ -20,81 +20,98 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
'./WorkerInterface' * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
WorkerInterface * 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.
*****************************************************************************/
var REQUEST_DEFAULTS = { import WorkerInterface from './WorkerInterface';
amplitude: 1,
period: 10,
offset: 0,
dataRateInHz: 1,
randomness: 0,
phase: 0
};
function GeneratorProvider() { var REQUEST_DEFAULTS = {
this.workerInterface = new WorkerInterface(); amplitude: 1,
} period: 10,
offset: 0,
dataRateInHz: 1,
randomness: 0,
phase: 0
};
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) { function GeneratorProvider() {
return domainObject.type === 'generator'; this.workerInterface = new WorkerInterface();
}; }
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
GeneratorProvider.prototype.supportsSubscribe = return domainObject.type === 'generator';
GeneratorProvider.prototype.canProvideTelemetry; };
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) { GeneratorProvider.prototype.supportsRequest =
var props = [ GeneratorProvider.prototype.supportsSubscribe =
'amplitude', GeneratorProvider.prototype.canProvideTelemetry;
'period',
'offset',
'dataRateInHz',
'phase',
'randomness'
];
request = request || {}; GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
var props = [
'amplitude',
'period',
'offset',
'dataRateInHz',
'phase',
'randomness'
];
var workerRequest = {}; request = request || {};
props.forEach(function (prop) { var workerRequest = {};
if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
workerRequest[prop] = domainObject.telemetry[prop];
}
if (request && Object.prototype.hasOwnProperty.call(request, prop)) { props.forEach(function (prop) {
workerRequest[prop] = request[prop]; if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
} workerRequest[prop] = domainObject.telemetry[prop];
}
if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) { if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
workerRequest[prop] = REQUEST_DEFAULTS[prop]; workerRequest[prop] = request[prop];
} }
workerRequest[prop] = Number(workerRequest[prop]); if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
}); workerRequest[prop] = REQUEST_DEFAULTS[prop];
}
workerRequest.name = domainObject.name; workerRequest[prop] = Number(workerRequest[prop]);
});
return workerRequest; workerRequest.name = domainObject.name;
};
GeneratorProvider.prototype.request = function (domainObject, request) { return workerRequest;
var workerRequest = this.makeWorkerRequest(domainObject, request); };
workerRequest.start = request.start;
workerRequest.end = request.end;
return this.workerInterface.request(workerRequest); GeneratorProvider.prototype.request = function (domainObject, request) {
}; var workerRequest = this.makeWorkerRequest(domainObject, request);
workerRequest.start = request.start;
workerRequest.end = request.end;
GeneratorProvider.prototype.subscribe = function (domainObject, callback) { return this.workerInterface.request(workerRequest);
var workerRequest = this.makeWorkerRequest(domainObject, {}); };
return this.workerInterface.subscribe(workerRequest, callback); GeneratorProvider.prototype.subscribe = function (domainObject, callback) {
}; var workerRequest = this.makeWorkerRequest(domainObject, {});
return GeneratorProvider; return this.workerInterface.subscribe(workerRequest, callback);
}); };
export default GeneratorProvider;

View File

@ -20,155 +20,170 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
], function ( var PURPLE = {
) {
var PURPLE = {
sin: 2.2, sin: 2.2,
cos: 2.2 cos: 2.2
}, },
RED = { RED = {
sin: 0.9, sin: 0.9,
cos: 0.9 cos: 0.9
},
ORANGE = {
sin: 0.7,
cos: 0.7
},
YELLOW = {
sin: 0.5,
cos: 0.5
},
CYAN = {
sin: 0.45,
cos: 0.45
},
LIMITS = {
rh: {
cssClass: "is-limit--upr is-limit--red",
low: RED,
high: Number.POSITIVE_INFINITY,
name: "Red High"
}, },
ORANGE = { rl: {
sin: 0.7, cssClass: "is-limit--lwr is-limit--red",
cos: 0.7 high: -RED,
low: Number.NEGATIVE_INFINITY,
name: "Red Low"
}, },
YELLOW = { yh: {
sin: 0.5, cssClass: "is-limit--upr is-limit--yellow",
cos: 0.5 low: YELLOW,
high: RED,
name: "Yellow High"
}, },
CYAN = { yl: {
sin: 0.45, cssClass: "is-limit--lwr is-limit--yellow",
cos: 0.45 low: -RED,
}, high: -YELLOW,
LIMITS = { name: "Yellow Low"
rh: { }
cssClass: "is-limit--upr is-limit--red",
low: RED,
high: Number.POSITIVE_INFINITY,
name: "Red High"
},
rl: {
cssClass: "is-limit--lwr is-limit--red",
high: -RED,
low: Number.NEGATIVE_INFINITY,
name: "Red Low"
},
yh: {
cssClass: "is-limit--upr is-limit--yellow",
low: YELLOW,
high: RED,
name: "Yellow High"
},
yl: {
cssClass: "is-limit--lwr is-limit--yellow",
low: -RED,
high: -YELLOW,
name: "Yellow Low"
}
};
function SinewaveLimitProvider() {
}
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
return domainObject.type === 'generator';
}; };
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) { function SinewaveLimitProvider() {
return {
evaluate: function (datum, valueMetadata) {
var range = valueMetadata && valueMetadata.key;
if (datum[range] > RED[range]) { }
return LIMITS.rh;
}
if (datum[range] < -RED[range]) { SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
return LIMITS.rl; return domainObject.type === 'generator';
} };
if (datum[range] > YELLOW[range]) { SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
return LIMITS.yh; return {
} evaluate: function (datum, valueMetadata) {
var range = valueMetadata && valueMetadata.key;
if (datum[range] < -YELLOW[range]) { if (datum[range] > RED[range]) {
return LIMITS.yl; return LIMITS.rh;
}
} }
};
if (datum[range] < -RED[range]) {
return LIMITS.rl;
}
if (datum[range] > YELLOW[range]) {
return LIMITS.yh;
}
if (datum[range] < -YELLOW[range]) {
return LIMITS.yl;
}
}
}; };
};
SinewaveLimitProvider.prototype.getLimits = function (domainObject) { SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
return { return {
limits: function () { limits: function () {
return Promise.resolve({ return Promise.resolve({
WATCH: { WATCH: {
low: { low: {
color: "cyan", color: "cyan",
sin: -CYAN.sin, sin: -CYAN.sin,
cos: -CYAN.cos cos: -CYAN.cos
},
high: {
color: "cyan",
...CYAN
}
}, },
WARNING: { high: {
low: { color: "cyan",
color: "yellow", ...CYAN
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
color: "yellow",
...YELLOW
}
},
DISTRESS: {
low: {
color: "orange",
sin: -ORANGE.sin,
cos: -ORANGE.cos
},
high: {
color: "orange",
...ORANGE
}
},
CRITICAL: {
low: {
color: "red",
sin: -RED.sin,
cos: -RED.cos
},
high: {
color: "red",
...RED
}
},
SEVERE: {
low: {
color: "purple",
sin: -PURPLE.sin,
cos: -PURPLE.cos
},
high: {
color: "purple",
...PURPLE
}
} }
}); },
} WARNING: {
}; low: {
color: "yellow",
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
color: "yellow",
...YELLOW
}
},
DISTRESS: {
low: {
color: "orange",
sin: -ORANGE.sin,
cos: -ORANGE.cos
},
high: {
color: "orange",
...ORANGE
}
},
CRITICAL: {
low: {
color: "red",
sin: -RED.sin,
cos: -RED.cos
},
high: {
color: "red",
...RED
}
},
SEVERE: {
low: {
color: "purple",
sin: -PURPLE.sin,
cos: -PURPLE.cos
},
high: {
color: "purple",
...PURPLE
}
}
});
}
}; };
};
return SinewaveLimitProvider; export default SinewaveLimitProvider;
});

View File

@ -20,64 +20,78 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
], function ( function StateGeneratorProvider() {
) { }
function StateGeneratorProvider() { function pointForTimestamp(timestamp, duration, name) {
return {
name: name,
utc: Math.floor(timestamp / duration) * duration,
value: Math.floor(timestamp / duration) % 2
};
}
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
var duration = domainObject.telemetry.duration * 1000;
var interval = setInterval(function () {
var now = Date.now();
var datum = pointForTimestamp(now, duration, domainObject.name);
datum.value = String(datum.value);
callback(datum);
}, duration);
return function () {
clearInterval(interval);
};
};
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.request = function (domainObject, options) {
var start = options.start;
var end = Math.min(Date.now(), options.end); // no future values
var duration = domainObject.telemetry.duration * 1000;
if (options.strategy === 'latest' || options.size === 1) {
start = end;
} }
function pointForTimestamp(timestamp, duration, name) { var data = [];
return { while (start <= end && data.length < 5000) {
name: name, data.push(pointForTimestamp(start, duration, domainObject.name));
utc: Math.floor(timestamp / duration) * duration, start += duration;
value: Math.floor(timestamp / duration) % 2
};
} }
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) { return Promise.resolve(data);
return domainObject.type === 'example.state-generator'; };
};
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) { export default StateGeneratorProvider;
var duration = domainObject.telemetry.duration * 1000;
var interval = setInterval(function () {
var now = Date.now();
var datum = pointForTimestamp(now, duration, domainObject.name);
datum.value = String(datum.value);
callback(datum);
}, duration);
return function () {
clearInterval(interval);
};
};
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.request = function (domainObject, options) {
var start = options.start;
var end = Math.min(Date.now(), options.end); // no future values
var duration = domainObject.telemetry.duration * 1000;
if (options.strategy === 'latest' || options.size === 1) {
start = end;
}
var data = [];
while (start <= end && data.length < 5000) {
data.push(pointForTimestamp(start, duration, domainObject.name));
start += duration;
}
return Promise.resolve(data);
};
return StateGeneratorProvider;
});

View File

@ -20,89 +20,106 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
'raw-loader!./generatorWorker.js', * Open MCT, Copyright (c) 2014-2021, United States Government
'uuid' * as represented by the Administrator of the National Aeronautics and Space
], function ( * Administration. All rights reserved.
workerText, *
uuid * 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.
*****************************************************************************/
var workerBlob = new Blob( import workerText from 'raw-loader!./generatorWorker.js';
[workerText],
{type: 'application/javascript'}
);
var workerUrl = URL.createObjectURL(workerBlob);
function WorkerInterface() { import uuid from 'uuid';
this.worker = new Worker(workerUrl);
this.worker.onmessage = this.onMessage.bind(this); var workerBlob = new Blob(
this.callbacks = {}; [workerText],
{type: 'application/javascript'}
);
var workerUrl = URL.createObjectURL(workerBlob);
function WorkerInterface() {
this.worker = new Worker(workerUrl);
this.worker.onmessage = this.onMessage.bind(this);
this.callbacks = {};
}
WorkerInterface.prototype.onMessage = function (message) {
message = message.data;
var callback = this.callbacks[message.id];
if (callback) {
callback(message);
}
};
WorkerInterface.prototype.dispatch = function (request, data, callback) {
var message = {
request: request,
data: data,
id: uuid()
};
if (callback) {
this.callbacks[message.id] = callback;
} }
WorkerInterface.prototype.onMessage = function (message) { this.worker.postMessage(message);
message = message.data;
var callback = this.callbacks[message.id];
if (callback) {
callback(message);
}
};
WorkerInterface.prototype.dispatch = function (request, data, callback) { return message.id;
var message = { };
request: request,
data: data,
id: uuid()
};
if (callback) { WorkerInterface.prototype.request = function (request) {
this.callbacks[message.id] = callback; var deferred = {};
var promise = new Promise(function (resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
var messageId;
let self = this;
function callback(message) {
if (message.error) {
deferred.reject(message.error);
} else {
deferred.resolve(message.data);
} }
this.worker.postMessage(message); delete self.callbacks[messageId];
return message.id; }
};
WorkerInterface.prototype.request = function (request) { messageId = this.dispatch('request', request, callback.bind(this));
var deferred = {};
var promise = new Promise(function (resolve, reject) { return promise;
deferred.resolve = resolve; };
deferred.reject = reject;
WorkerInterface.prototype.subscribe = function (request, cb) {
function callback(message) {
cb(message.data);
}
var messageId = this.dispatch('subscribe', request, callback);
return function () {
this.dispatch('unsubscribe', {
id: messageId
}); });
var messageId; delete this.callbacks[messageId];
}.bind(this);
};
let self = this; export default WorkerInterface;
function callback(message) {
if (message.error) {
deferred.reject(message.error);
} else {
deferred.resolve(message.data);
}
delete self.callbacks[messageId];
}
messageId = this.dispatch('request', request, callback.bind(this));
return promise;
};
WorkerInterface.prototype.subscribe = function (request, cb) {
function callback(message) {
cb(message.data);
}
var messageId = this.dispatch('subscribe', request, callback);
return function () {
this.dispatch('unsubscribe', {
id: messageId
});
delete this.callbacks[messageId];
}.bind(this);
};
return WorkerInterface;
});

View File

@ -20,6 +20,28 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
(function () { (function () {
var FIFTEEN_MINUTES = 15 * 60 * 1000; var FIFTEEN_MINUTES = 15 * 60 * 1000;
@ -181,4 +203,4 @@
} }
}; };
}()); }());

View File

@ -20,135 +20,149 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./GeneratorProvider", * Open MCT, Copyright (c) 2014-2021, United States Government
"./SinewaveLimitProvider", * as represented by the Administrator of the National Aeronautics and Space
"./StateGeneratorProvider", * Administration. All rights reserved.
"./GeneratorMetadataProvider" *
], function ( * Open MCT is licensed under the Apache License, Version 2.0 (the
GeneratorProvider, * "License"); you may not use this file except in compliance with the License.
SinewaveLimitProvider, * You may obtain a copy of the License at
StateGeneratorProvider, * http://www.apache.org/licenses/LICENSE-2.0.
GeneratorMetadataProvider *
) { * 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.
*****************************************************************************/
return function (openmct) { import GeneratorProvider from './GeneratorProvider';
openmct.types.addType("example.state-generator", { import SinewaveLimitProvider from './SinewaveLimitProvider';
name: "State Generator", import StateGeneratorProvider from './StateGeneratorProvider';
description: "For development use. Generates test enumerated telemetry by cycling through a given set of states", import GeneratorMetadataProvider from './GeneratorMetadataProvider';
cssClass: "icon-generator-telemetry",
creatable: true, export default function (openmct) {
form: [
{ openmct.types.addType("example.state-generator", {
name: "State Duration (seconds)", name: "State Generator",
control: "numberfield", description: "For development use. Generates test enumerated telemetry by cycling through a given set of states",
cssClass: "l-input-sm l-numeric", cssClass: "icon-generator-telemetry",
key: "duration", creatable: true,
required: true, form: [
property: [ {
"telemetry", name: "State Duration (seconds)",
"duration" control: "numberfield",
] cssClass: "l-input-sm l-numeric",
} key: "duration",
], required: true,
initialize: function (object) { property: [
object.telemetry = { "telemetry",
duration: 5 "duration"
}; ]
} }
}); ],
initialize: function (object) {
object.telemetry = {
duration: 5
};
}
});
openmct.telemetry.addProvider(new StateGeneratorProvider()); openmct.telemetry.addProvider(new StateGeneratorProvider());
openmct.types.addType("generator", { openmct.types.addType("generator", {
name: "Sine Wave Generator", name: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
cssClass: "icon-generator-telemetry", cssClass: "icon-generator-telemetry",
creatable: true, creatable: true,
form: [ form: [
{ {
name: "Period", name: "Period",
control: "numberfield", control: "numberfield",
cssClass: "l-input-sm l-numeric", cssClass: "l-input-sm l-numeric",
key: "period", key: "period",
required: true, required: true,
property: [ property: [
"telemetry", "telemetry",
"period" "period"
] ]
}, },
{ {
name: "Amplitude", name: "Amplitude",
control: "numberfield", control: "numberfield",
cssClass: "l-input-sm l-numeric", cssClass: "l-input-sm l-numeric",
key: "amplitude", key: "amplitude",
required: true, required: true,
property: [ property: [
"telemetry", "telemetry",
"amplitude" "amplitude"
] ]
}, },
{ {
name: "Offset", name: "Offset",
control: "numberfield", control: "numberfield",
cssClass: "l-input-sm l-numeric", cssClass: "l-input-sm l-numeric",
key: "offset", key: "offset",
required: true, required: true,
property: [ property: [
"telemetry", "telemetry",
"offset" "offset"
] ]
}, },
{ {
name: "Data Rate (hz)", name: "Data Rate (hz)",
control: "numberfield", control: "numberfield",
cssClass: "l-input-sm l-numeric", cssClass: "l-input-sm l-numeric",
key: "dataRateInHz", key: "dataRateInHz",
required: true, required: true,
property: [ property: [
"telemetry", "telemetry",
"dataRateInHz" "dataRateInHz"
] ]
}, },
{ {
name: "Phase (radians)", name: "Phase (radians)",
control: "numberfield", control: "numberfield",
cssClass: "l-input-sm l-numeric", cssClass: "l-input-sm l-numeric",
key: "phase", key: "phase",
required: true, required: true,
property: [ property: [
"telemetry", "telemetry",
"phase" "phase"
] ]
}, },
{ {
name: "Randomness", name: "Randomness",
control: "numberfield", control: "numberfield",
cssClass: "l-input-sm l-numeric", cssClass: "l-input-sm l-numeric",
key: "randomness", key: "randomness",
required: true, required: true,
property: [ property: [
"telemetry", "telemetry",
"randomness" "randomness"
] ]
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0,
randomness: 0
};
} }
}); ],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0,
randomness: 0
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider()); openmct.telemetry.addProvider(new GeneratorProvider());
openmct.telemetry.addProvider(new GeneratorMetadataProvider()); openmct.telemetry.addProvider(new GeneratorMetadataProvider());
openmct.telemetry.addProvider(new SinewaveLimitProvider()); openmct.telemetry.addProvider(new SinewaveLimitProvider());
}; };
});

View File

@ -20,29 +20,47 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/ExampleIdentityService" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
ExampleIdentityService * Administration. All rights reserved.
) { *
"use strict"; * 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.
*****************************************************************************/
return { import ExampleIdentityService from './src/ExampleIdentityService';
name: "example/identity",
definition: { "use strict";
"extensions": {
"components": [ export default {
{ name: "example/identity",
"implementation": ExampleIdentityService, definition: {
"provides": "identityService", "extensions": {
"type": "provider", "components": [
"depends": [ {
"dialogService", "implementation": ExampleIdentityService,
"$q" "provides": "identityService",
] "type": "provider",
} "depends": [
] "dialogService",
} "$q"
]
}
]
} }
}; }
}); };

View File

@ -20,75 +20,71 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( "use strict";
function () {
"use strict";
var DEFAULT_IDENTITY = { var DEFAULT_IDENTITY = {
key: "user", key: "user",
name: "Example User" name: "Example User"
}, },
DIALOG_STRUCTURE = { DIALOG_STRUCTURE = {
name: "Identify Yourself", name: "Identify Yourself",
sections: [{ sections: [{
rows: [ rows: [
{ {
name: "User ID", name: "User ID",
control: "textfield", control: "textfield",
key: "key", key: "key",
required: true required: true
}, },
{ {
name: "Human name", name: "Human name",
control: "textfield", control: "textfield",
key: "name", key: "name",
required: true required: true
} }
] ]
}] }]
}; };
/** /**
* Example implementation of an identity service. This prompts the * Example implementation of an identity service. This prompts the
* user to enter a name and user ID; in a more realistic * user to enter a name and user ID; in a more realistic
* implementation, this would be read from a server, possibly * implementation, this would be read from a server, possibly
* prompting for a user name and password (or similar) as * prompting for a user name and password (or similar) as
* appropriate. * appropriate.
* *
* @implements {IdentityService} * @implements {IdentityService}
* @memberof platform/identity * @memberof platform/identity
*/ */
function ExampleIdentityProvider(dialogService, $q) { function ExampleIdentityProvider(dialogService, $q) {
this.dialogService = dialogService; this.dialogService = dialogService;
this.$q = $q; this.$q = $q;
this.returnUser = this.returnUser.bind(this); this.returnUser = this.returnUser.bind(this);
this.returnUndefined = this.returnUndefined.bind(this); this.returnUndefined = this.returnUndefined.bind(this);
} }
ExampleIdentityProvider.prototype.getUser = function () { ExampleIdentityProvider.prototype.getUser = function () {
if (this.user) { if (this.user) {
return this.$q.when(this.user); return this.$q.when(this.user);
} else { } else {
return this.dialogService.getUserInput(DIALOG_STRUCTURE, DEFAULT_IDENTITY) return this.dialogService.getUserInput(DIALOG_STRUCTURE, DEFAULT_IDENTITY)
.then(this.returnUser, this.returnUndefined); .then(this.returnUser, this.returnUndefined);
}
};
/**
* @private
*/
ExampleIdentityProvider.prototype.returnUser = function (user) {
return this.user = user;
};
/**
* @private
*/
ExampleIdentityProvider.prototype.returnUndefined = function () {
return undefined;
};
return ExampleIdentityProvider;
} }
); };
/**
* @private
*/
ExampleIdentityProvider.prototype.returnUser = function (user) {
return this.user = user;
};
/**
* @private
*/
ExampleIdentityProvider.prototype.returnUndefined = function () {
return undefined;
};
export default ExampleIdentityProvider;

View File

@ -20,6 +20,28 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
const DEFAULT_IMAGE_SAMPLES = [ const DEFAULT_IMAGE_SAMPLES = [
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg", "https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg", "https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
@ -235,4 +257,4 @@ function pointForTimestamp(timestamp, name, imageSamples, delay) {
heading: getCompassValues(0, 360), heading: getCompassValues(0, 360),
imageDownloadName imageDownloadName
}; };
} }

View File

@ -20,22 +20,42 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([], function () { /*****************************************************************************
"use strict"; * Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
return { "use strict";
name: "example/mobile",
definition: { export default {
"name": "Mobile", name: "example/mobile",
"description": "Allows elements with pertinence to mobile usage and development", definition: {
"extensions": { "name": "Mobile",
"stylesheets": [ "description": "Allows elements with pertinence to mobile usage and development",
{ "extensions": {
"stylesheetUrl": "css/mobile-example.css", "stylesheets": [
"priority": "mandatory" {
} "stylesheetUrl": "css/mobile-example.css",
] "priority": "mandatory"
} }
]
} }
}; }
}); };

View File

@ -20,96 +20,111 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/RemsTelemetryServerAdapter", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/RemsTelemetryModelProvider", * as represented by the Administrator of the National Aeronautics and Space
"./src/RemsTelemetryProvider" * Administration. All rights reserved.
], function ( *
RemsTelemetryServerAdapter, * Open MCT is licensed under the Apache License, Version 2.0 (the
RemsTelemetryModelProvider, * "License"); you may not use this file except in compliance with the License.
RemsTelemetryProvider * You may obtain a copy of the License at
) { * http://www.apache.org/licenses/LICENSE-2.0.
"use strict"; *
* 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.
*****************************************************************************/
return { import RemsTelemetryServerAdapter from './src/RemsTelemetryServerAdapter';
name: "example/msl",
definition: { import RemsTelemetryModelProvider from './src/RemsTelemetryModelProvider';
"name": "Mars Science Laboratory Data Adapter", import RemsTelemetryProvider from './src/RemsTelemetryProvider';
"extensions": { "use strict";
"types": [
{ export default {
name: "example/msl",
definition: {
"name": "Mars Science Laboratory Data Adapter",
"extensions": {
"types": [
{
"name": "Mars Science Laboratory",
"key": "msl.curiosity",
"cssClass": "icon-object"
},
{
"name": "Instrument",
"key": "msl.instrument",
"cssClass": "icon-object",
"model": {"composition": []}
},
{
"name": "Measurement",
"key": "msl.measurement",
"cssClass": "icon-telemetry",
"model": {"telemetry": {}},
"telemetry": {
"source": "rems.source",
"domains": [
{
"name": "Time",
"key": "utc",
"format": "utc"
}
]
}
}
],
"constants": [
{
"key": "REMS_WS_URL",
"value": "/proxyUrl?url=http://cab.inta-csic.es/rems/wp-content/plugins/marsweather-widget/api.php"
}
],
"roots": [
{
"id": "msl:curiosity"
}
],
"models": [
{
"id": "msl:curiosity",
"priority": "preferred",
"model": {
"type": "msl.curiosity",
"name": "Mars Science Laboratory", "name": "Mars Science Laboratory",
"key": "msl.curiosity", "composition": ["msl_tlm:rems"]
"cssClass": "icon-object"
},
{
"name": "Instrument",
"key": "msl.instrument",
"cssClass": "icon-object",
"model": {"composition": []}
},
{
"name": "Measurement",
"key": "msl.measurement",
"cssClass": "icon-telemetry",
"model": {"telemetry": {}},
"telemetry": {
"source": "rems.source",
"domains": [
{
"name": "Time",
"key": "utc",
"format": "utc"
}
]
}
} }
], }
"constants": [ ],
{ "services": [
"key": "REMS_WS_URL", {
"value": "/proxyUrl?url=http://cab.inta-csic.es/rems/wp-content/plugins/marsweather-widget/api.php" "key": "rems.adapter",
} "implementation": RemsTelemetryServerAdapter,
], "depends": ["$http", "$log", "REMS_WS_URL"]
"roots": [ }
{ ],
"id": "msl:curiosity" "components": [
} {
], "provides": "modelService",
"models": [ "type": "provider",
{ "implementation": RemsTelemetryModelProvider,
"id": "msl:curiosity", "depends": ["rems.adapter"]
"priority": "preferred", },
"model": { {
"type": "msl.curiosity", "provides": "telemetryService",
"name": "Mars Science Laboratory", "type": "provider",
"composition": ["msl_tlm:rems"] "implementation": RemsTelemetryProvider,
} "depends": ["rems.adapter", "$q"]
} }
], ]
"services": [
{
"key": "rems.adapter",
"implementation": RemsTelemetryServerAdapter,
"depends": ["$http", "$log", "REMS_WS_URL"]
}
],
"components": [
{
"provides": "modelService",
"type": "provider",
"implementation": RemsTelemetryModelProvider,
"depends": ["rems.adapter"]
},
{
"provides": "telemetryService",
"type": "provider",
"implementation": RemsTelemetryProvider,
"depends": ["rems.adapter", "$q"]
}
]
}
} }
}; }
}); };

View File

@ -20,59 +20,45 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( export default {
[], "name": "Mars Science Laboratory",
/** "identifier": "msl",
* A data dictionary describes the telemetry available from a data "instruments": [
* source and its data types. The data dictionary will be parsed by a custom {
* server provider for this data source (in this case "name": "rems",
* {@link RemsTelemetryServerAdapter}). "identifier": "rems",
* "measurements": [
* Typically a data dictionary would be made available alongside the
* telemetry data source itself.
*/
function () {
return {
"name": "Mars Science Laboratory",
"identifier": "msl",
"instruments": [
{ {
"name": "rems", "name": "Min. Air Temperature",
"identifier": "rems", "identifier": "min_temp",
"measurements": [ "units": "Degrees (C)",
{ "type": "float"
"name": "Min. Air Temperature", },
"identifier": "min_temp", {
"units": "Degrees (C)", "name": "Max. Air Temperature",
"type": "float" "identifier": "max_temp",
}, "units": "Degrees (C)",
{ "type": "float"
"name": "Max. Air Temperature", },
"identifier": "max_temp", {
"units": "Degrees (C)", "name": "Atmospheric Pressure",
"type": "float" "identifier": "pressure",
}, "units": "Millibars",
{ "type": "float"
"name": "Atmospheric Pressure", },
"identifier": "pressure", {
"units": "Millibars", "name": "Min. Ground Temperature",
"type": "float" "identifier": "min_gts_temp",
}, "units": "Degrees (C)",
{ "type": "float"
"name": "Min. Ground Temperature", },
"identifier": "min_gts_temp", {
"units": "Degrees (C)", "name": "Max. Ground Temperature",
"type": "float" "identifier": "max_gts_temp",
}, "units": "Degrees (C)",
{ "type": "float"
"name": "Max. Ground Temperature",
"identifier": "max_gts_temp",
"units": "Degrees (C)",
"type": "float"
}
]
} }
] ]
}; }
} ]
); };

View File

@ -20,77 +20,73 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( "use strict";
function () {
"use strict";
var PREFIX = "msl_tlm:", var PREFIX = "msl_tlm:",
FORMAT_MAPPINGS = { FORMAT_MAPPINGS = {
float: "number", float: "number",
integer: "number", integer: "number",
string: "string" string: "string"
}; };
function RemsTelemetryModelProvider(adapter) { function RemsTelemetryModelProvider(adapter) {
function isRelevant(id) { function isRelevant(id) {
return id.indexOf(PREFIX) === 0; return id.indexOf(PREFIX) === 0;
} }
function makeId(element) { function makeId(element) {
return PREFIX + element.identifier; return PREFIX + element.identifier;
} }
function buildTaxonomy(dictionary) { function buildTaxonomy(dictionary) {
var models = {}; var models = {};
function addMeasurement(measurement, parent) { function addMeasurement(measurement, parent) {
var format = FORMAT_MAPPINGS[measurement.type]; var format = FORMAT_MAPPINGS[measurement.type];
models[makeId(measurement)] = { models[makeId(measurement)] = {
type: "msl.measurement", type: "msl.measurement",
name: measurement.name, name: measurement.name,
location: parent, location: parent,
telemetry: { telemetry: {
key: measurement.identifier, key: measurement.identifier,
ranges: [{ ranges: [{
key: "value", key: "value",
name: measurement.units, name: measurement.units,
units: measurement.units, units: measurement.units,
format: format format: format
}] }]
}
};
}
function addInstrument(subsystem, spacecraftId) {
var measurements = (subsystem.measurements || []),
instrumentId = makeId(subsystem);
models[instrumentId] = {
type: "msl.instrument",
name: subsystem.name,
location: spacecraftId,
composition: measurements.map(makeId)
};
measurements.forEach(function (measurement) {
addMeasurement(measurement, instrumentId);
});
}
(dictionary.instruments || []).forEach(function (instrument) {
addInstrument(instrument, "msl:curiosity");
});
return models;
}
return {
getModels: function (ids) {
return ids.some(isRelevant) ? buildTaxonomy(adapter.dictionary) : {};
} }
}; };
} }
return RemsTelemetryModelProvider; function addInstrument(subsystem, spacecraftId) {
var measurements = (subsystem.measurements || []),
instrumentId = makeId(subsystem);
models[instrumentId] = {
type: "msl.instrument",
name: subsystem.name,
location: spacecraftId,
composition: measurements.map(makeId)
};
measurements.forEach(function (measurement) {
addMeasurement(measurement, instrumentId);
});
}
(dictionary.instruments || []).forEach(function (instrument) {
addInstrument(instrument, "msl:curiosity");
});
return models;
} }
);
return {
getModels: function (ids) {
return ids.some(isRelevant) ? buildTaxonomy(adapter.dictionary) : {};
}
};
}
export default RemsTelemetryModelProvider;

View File

@ -19,65 +19,83 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define ( /*****************************************************************************
['./RemsTelemetrySeries'], * Open MCT, Copyright (c) 2014-2021, United States Government
function (RemsTelemetrySeries) { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import RemsTelemetrySeries from './RemsTelemetrySeries';
var SOURCE = "rems.source"; "use strict";
function RemsTelemetryProvider(adapter, $q) { var SOURCE = "rems.source";
this.adapter = adapter;
this.$q = $q;
}
/** function RemsTelemetryProvider(adapter, $q) {
* Retrieve telemetry from this telemetry source. this.adapter = adapter;
* @memberOf example/msl this.$q = $q;
* @param {Array<TelemetryRequest>} requests An array of all request }
* objects (which needs to be filtered to only those relevant to this
* source)
* @returns {Promise} A {@link Promise} resolved with a {@link RemsTelemetrySeries}
* object that wraps the telemetry returned from the telemetry source.
*/
RemsTelemetryProvider.prototype.requestTelemetry = function (requests) {
var packaged = {},
relevantReqs,
adapter = this.adapter;
function matchesSource(request) { /**
return (request.source === SOURCE); * Retrieve telemetry from this telemetry source.
} * @memberOf example/msl
* @param {Array<TelemetryRequest>} requests An array of all request
* objects (which needs to be filtered to only those relevant to this
* source)
* @returns {Promise} A {@link Promise} resolved with a {@link RemsTelemetrySeries}
* object that wraps the telemetry returned from the telemetry source.
*/
RemsTelemetryProvider.prototype.requestTelemetry = function (requests) {
var packaged = {},
relevantReqs,
adapter = this.adapter;
function addToPackage(history) { function matchesSource(request) {
packaged[SOURCE][history.id] = return (request.source === SOURCE);
new RemsTelemetrySeries(history.values);
}
function handleRequest(request) {
return adapter.history(request).then(addToPackage);
}
relevantReqs = requests.filter(matchesSource);
packaged[SOURCE] = {};
return this.$q.all(relevantReqs.map(handleRequest))
.then(function () {
return packaged;
});
};
/**
* This data source does not support real-time subscriptions
*/
RemsTelemetryProvider.prototype.subscribe = function (callback, requests) {
return function () {};
};
RemsTelemetryProvider.prototype.unsubscribe = function (callback, requests) {
return function () {};
};
return RemsTelemetryProvider;
} }
);
function addToPackage(history) {
packaged[SOURCE][history.id] =
new RemsTelemetrySeries(history.values);
}
function handleRequest(request) {
return adapter.history(request).then(addToPackage);
}
relevantReqs = requests.filter(matchesSource);
packaged[SOURCE] = {};
return this.$q.all(relevantReqs.map(handleRequest))
.then(function () {
return packaged;
});
};
/**
* This data source does not support real-time subscriptions
*/
RemsTelemetryProvider.prototype.subscribe = function (callback, requests) {
return function () {};
};
RemsTelemetryProvider.prototype.unsubscribe = function (callback, requests) {
return function () {};
};
export default RemsTelemetryProvider;

View File

@ -19,66 +19,62 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( "use strict";
function () {
"use strict";
/** /**
* @typedef {Object} RemsTelemetryValue * @typedef {Object} RemsTelemetryValue
* @memberOf example/msl * @memberOf example/msl
* @property {number} date The date/time of the telemetry value. Constitutes the domain value of this value pair * @property {number} date The date/time of the telemetry value. Constitutes the domain value of this value pair
* @property {number} value The value of this telemetry datum. * @property {number} value The value of this telemetry datum.
* A floating point value representing some observable quantity (eg. * A floating point value representing some observable quantity (eg.
* temperature, air pressure, etc.) * temperature, air pressure, etc.)
*/ */
/** /**
* A representation of a collection of telemetry data. The REMS * A representation of a collection of telemetry data. The REMS
* telemetry data is time ordered, with the 'domain' value * telemetry data is time ordered, with the 'domain' value
* constituting the time stamp of each data value and the * constituting the time stamp of each data value and the
* 'range' being the value itself. * 'range' being the value itself.
* *
* TelemetrySeries will typically wrap an array of telemetry data, * TelemetrySeries will typically wrap an array of telemetry data,
* and provide an interface for retrieving individual an telemetry * and provide an interface for retrieving individual an telemetry
* value. * value.
* @memberOf example/msl * @memberOf example/msl
* @param {Array<RemsTelemetryValue>} data An array of telemetry values * @param {Array<RemsTelemetryValue>} data An array of telemetry values
* @constructor * @constructor
*/ */
function RemsTelemetrySeries(data) { function RemsTelemetrySeries(data) {
this.data = data; this.data = data;
} }
/** /**
* @returns {number} A count of the number of data values available in * @returns {number} A count of the number of data values available in
* this series * this series
*/ */
RemsTelemetrySeries.prototype.getPointCount = function () { RemsTelemetrySeries.prototype.getPointCount = function () {
return this.data.length; return this.data.length;
}; };
/** /**
* The domain value at the given index. The Rems telemetry data is * The domain value at the given index. The Rems telemetry data is
* time ordered, so the domain value is the time stamp of each data * time ordered, so the domain value is the time stamp of each data
* value. * value.
* @param index * @param index
* @returns {number} the time value in ms since 1 January 1970 * @returns {number} the time value in ms since 1 January 1970
*/ */
RemsTelemetrySeries.prototype.getDomainValue = function (index) { RemsTelemetrySeries.prototype.getDomainValue = function (index) {
return this.data[index].date; return this.data[index].date;
}; };
/** /**
* The range value of the REMS data set is the value of the thing * The range value of the REMS data set is the value of the thing
* being measured, be it temperature, air pressure, etc. * being measured, be it temperature, air pressure, etc.
* @param index The datum in the data series to return the range * @param index The datum in the data series to return the range
* value of. * value of.
* @returns {number} A floating point number * @returns {number} A floating point number
*/ */
RemsTelemetrySeries.prototype.getRangeValue = function (index) { RemsTelemetrySeries.prototype.getRangeValue = function (index) {
return this.data[index].value; return this.data[index].value;
}; };
return RemsTelemetrySeries; export default RemsTelemetrySeries;
}
);

View File

@ -21,125 +21,142 @@
*****************************************************************************/ *****************************************************************************/
/*jslint es5: true */ /*jslint es5: true */
define( /*****************************************************************************
[ * Open MCT, Copyright (c) 2014-2021, United States Government
"./MSLDataDictionary", * as represented by the Administrator of the National Aeronautics and Space
"module" * Administration. All rights reserved.
], *
function (MSLDataDictionary, module) { * Open MCT is licensed under the Apache License, Version 2.0 (the
"use strict"; * "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.
*****************************************************************************/
/*jslint es5: true */
var TERRESTRIAL_DATE = "terrestrial_date", import MSLDataDictionary from './MSLDataDictionary';
LOCAL_DATA = "../data/rems.json";
/** import module from 'module';
* Fetches historical data from the REMS instrument on the Curiosity "use strict";
* Rover.
* @memberOf example/msl
* @param $q
* @param $http
* @param REMS_WS_URL The location of the REMS telemetry data.
* @constructor
*/
function RemsTelemetryServerAdapter($http, $log, REMS_WS_URL) {
this.localDataURI = module.uri.substring(0, module.uri.lastIndexOf('/') + 1) + LOCAL_DATA;
this.REMS_WS_URL = REMS_WS_URL;
this.$http = $http;
this.$log = $log;
this.promise = undefined;
this.dataTransforms = { var TERRESTRIAL_DATE = "terrestrial_date",
//Convert from pascals to millibars LOCAL_DATA = "../data/rems.json";
'pressure': function pascalsToMillibars(pascals) {
return pascals / 100; /**
} * Fetches historical data from the REMS instrument on the Curiosity
}; * Rover.
* @memberOf example/msl
* @param $q
* @param $http
* @param REMS_WS_URL The location of the REMS telemetry data.
* @constructor
*/
function RemsTelemetryServerAdapter($http, $log, REMS_WS_URL) {
this.localDataURI = module.uri.substring(0, module.uri.lastIndexOf('/') + 1) + LOCAL_DATA;
this.REMS_WS_URL = REMS_WS_URL;
this.$http = $http;
this.$log = $log;
this.promise = undefined;
this.dataTransforms = {
//Convert from pascals to millibars
'pressure': function pascalsToMillibars(pascals) {
return pascals / 100;
} }
};
}
/** /**
* The data dictionary for this data source. * The data dictionary for this data source.
* @type {MSLDataDictionary} * @type {MSLDataDictionary}
*/
RemsTelemetryServerAdapter.prototype.dictionary = MSLDataDictionary;
/**
* Fetches historical data from source, and associates it with the
* given request ID.
* @private
*/
RemsTelemetryServerAdapter.prototype.requestHistory = function (request) {
var self = this,
id = request.key;
var dataTransforms = this.dataTransforms;
function processResponse(response) {
var data = [];
/*
* History data is organised by Sol. Iterate over sols...
*/ */
RemsTelemetryServerAdapter.prototype.dictionary = MSLDataDictionary; response.data.soles.forEach(function (solData) {
/*
/** * Check that valid data exists
* Fetches historical data from source, and associates it with the */
* given request ID. if (!isNaN(solData[id])) {
* @private var dataTransform = dataTransforms[id];
*/
RemsTelemetryServerAdapter.prototype.requestHistory = function (request) {
var self = this,
id = request.key;
var dataTransforms = this.dataTransforms;
function processResponse(response) {
var data = [];
/* /*
* History data is organised by Sol. Iterate over sols... * Append each data point to the array of values
* for this data point property (min. temp, etc).
*/ */
response.data.soles.forEach(function (solData) { data.unshift({
/* date: Date.parse(solData[TERRESTRIAL_DATE]),
* Check that valid data exists value: dataTransform ? dataTransform(solData[id]) : solData[id]
*/
if (!isNaN(solData[id])) {
var dataTransform = dataTransforms[id];
/*
* Append each data point to the array of values
* for this data point property (min. temp, etc).
*/
data.unshift({
date: Date.parse(solData[TERRESTRIAL_DATE]),
value: dataTransform ? dataTransform(solData[id]) : solData[id]
});
}
});
return data;
}
function fallbackToLocal() {
self.$log.warn("Loading REMS data failed, probably due to"
+ " cross origin policy. Falling back to local data");
return self.$http.get(self.localDataURI);
}
//Filter results to match request parameters
function filterResults(results) {
return results.filter(function (result) {
return result.date >= (request.start || Number.MIN_VALUE)
&& result.date <= (request.end || Number.MAX_VALUE);
}); });
} }
});
function packageAndResolve(results) { return data;
return {
id: id,
values: results
};
}
return (this.promise = this.promise || this.$http.get(this.REMS_WS_URL))
.catch(fallbackToLocal)
.then(processResponse)
.then(filterResults)
.then(packageAndResolve);
};
/**
* Requests historical telemetry for the named data attribute. In
* the case of REMS, this data source exposes multiple different
* data variables from the REMS instrument, including temperature
* and others
* @param id The telemetry data point key to be queried.
* @returns {Promise | Array<RemsTelemetryValue>} that resolves with an Array of {@link RemsTelemetryValue} objects for the request data key.
*/
RemsTelemetryServerAdapter.prototype.history = function (request) {
return this.requestHistory(request);
};
return RemsTelemetryServerAdapter;
} }
);
function fallbackToLocal() {
self.$log.warn("Loading REMS data failed, probably due to"
+ " cross origin policy. Falling back to local data");
return self.$http.get(self.localDataURI);
}
//Filter results to match request parameters
function filterResults(results) {
return results.filter(function (result) {
return result.date >= (request.start || Number.MIN_VALUE)
&& result.date <= (request.end || Number.MAX_VALUE);
});
}
function packageAndResolve(results) {
return {
id: id,
values: results
};
}
return (this.promise = this.promise || this.$http.get(this.REMS_WS_URL))
.catch(fallbackToLocal)
.then(processResponse)
.then(filterResults)
.then(packageAndResolve);
};
/**
* Requests historical telemetry for the named data attribute. In
* the case of REMS, this data source exposes multiple different
* data variables from the REMS instrument, including temperature
* and others
* @param id The telemetry data point key to be queried.
* @returns {Promise | Array<RemsTelemetryValue>} that resolves with an Array of {@link RemsTelemetryValue} objects for the request data key.
*/
RemsTelemetryServerAdapter.prototype.history = function (request) {
return this.requestHistory(request);
};
export default RemsTelemetryServerAdapter;

View File

@ -20,71 +20,84 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/DialogLaunchController", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/NotificationLaunchController", * as represented by the Administrator of the National Aeronautics and Space
"./src/DialogLaunchIndicator", * Administration. All rights reserved.
"./src/NotificationLaunchIndicator", *
"./res/dialog-launch.html", * Open MCT is licensed under the Apache License, Version 2.0 (the
"./res/notification-launch.html" * "License"); you may not use this file except in compliance with the License.
], function ( * You may obtain a copy of the License at
DialogLaunchController, * http://www.apache.org/licenses/LICENSE-2.0.
NotificationLaunchController, *
DialogLaunchIndicator, * Unless required by applicable law or agreed to in writing, software
NotificationLaunchIndicator, * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
DialogLaunch, * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
NotificationLaunch * License for the specific language governing permissions and limitations
) { * under the License.
"use strict"; *
* 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.
*****************************************************************************/
return { import DialogLaunchController from './src/DialogLaunchController';
name: "example/notifications",
definition: { import NotificationLaunchController from './src/NotificationLaunchController';
"extensions": { import DialogLaunchIndicator from './src/DialogLaunchIndicator';
"templates": [ import NotificationLaunchIndicator from './src/NotificationLaunchIndicator';
{ import DialogLaunch from './res/dialog-launch.html';
"key": "dialogLaunchTemplate", import NotificationLaunch from './res/notification-launch.html';
"template": DialogLaunch "use strict";
},
{ export default {
"key": "notificationLaunchTemplate", name: "example/notifications",
"template": NotificationLaunch definition: {
} "extensions": {
], "templates": [
"controllers": [ {
{ "key": "dialogLaunchTemplate",
"key": "DialogLaunchController", "template": DialogLaunch
"implementation": DialogLaunchController, },
"depends": [ {
"$scope", "key": "notificationLaunchTemplate",
"$timeout", "template": NotificationLaunch
"$log", }
"dialogService", ],
"notificationService" "controllers": [
] {
}, "key": "DialogLaunchController",
{ "implementation": DialogLaunchController,
"key": "NotificationLaunchController", "depends": [
"implementation": NotificationLaunchController, "$scope",
"depends": [ "$timeout",
"$scope", "$log",
"$timeout", "dialogService",
"$log", "notificationService"
"notificationService" ]
] },
} {
], "key": "NotificationLaunchController",
"indicators": [ "implementation": NotificationLaunchController,
{ "depends": [
"implementation": DialogLaunchIndicator, "$scope",
"priority": "fallback" "$timeout",
}, "$log",
{ "notificationService"
"implementation": NotificationLaunchIndicator, ]
"priority": "fallback" }
} ],
] "indicators": [
} {
"implementation": DialogLaunchIndicator,
"priority": "fallback"
},
{
"implementation": NotificationLaunchIndicator,
"priority": "fallback"
}
]
} }
}; }
}); };

View File

@ -20,138 +20,155 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
/** "use strict";
* A controller for the dialog launch view. This view allows manual
* launching of dialogs for demonstration and testing purposes. It
* also demonstrates the use of the DialogService.
* @param $scope
* @param $timeout
* @param $log
* @param dialogService
* @param notificationService
* @constructor
*/
function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) {
/* /**
Demonstrates launching a progress dialog and updating it * A controller for the dialog launch view. This view allows manual
periodically with the progress of an ongoing process. * launching of dialogs for demonstration and testing purposes. It
*/ * also demonstrates the use of the DialogService.
$scope.launchProgress = function (knownProgress) { * @param $scope
var dialog, * @param $timeout
model = { * @param $log
title: "Progress Dialog Example", * @param dialogService
progress: 0, * @param notificationService
hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.", * @constructor
actionText: "Calculating...", */
unknownProgress: !knownProgress, function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) {
unknownDuration: false,
severity: "info",
options: [
{
label: "Cancel Operation",
callback: function () {
$log.debug("Operation cancelled");
dialog.dismiss();
}
},
{
label: "Do something else...",
callback: function () {
$log.debug("Something else pressed");
}
}
]
};
function incrementProgress() { /*
model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30)); Demonstrates launching a progress dialog and updating it
model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" "); periodically with the progress of an ongoing process.
if (model.progress < 100) { */
$timeout(incrementProgress, 1000); $scope.launchProgress = function (knownProgress) {
} var dialog,
} model = {
title: "Progress Dialog Example",
dialog = dialogService.showBlockingMessage(model); progress: 0,
hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.",
if (dialog) { actionText: "Calculating...",
//Do processing here unknownProgress: !knownProgress,
model.actionText = "Processing 100 objects..."; unknownDuration: false,
if (knownProgress) { severity: "info",
$timeout(incrementProgress, 1000); options: [
} {
} else { label: "Cancel Operation",
$log.error("Could not display modal dialog"); callback: function () {
} $log.debug("Operation cancelled");
}; dialog.dismiss();
/*
Demonstrates launching an error dialog
*/
$scope.launchError = function () {
var dialog,
model = {
title: "Error Dialog Example",
actionText: "Something happened, and it was not good.",
severity: "error",
options: [
{
label: "Try Again",
callback: function () {
$log.debug("Try Again Pressed");
dialog.dismiss();
}
},
{
label: "Cancel",
callback: function () {
$log.debug("Cancel Pressed");
dialog.dismiss();
}
}
]
};
dialog = dialogService.showBlockingMessage(model);
if (!dialog) {
$log.error("Could not display modal dialog");
}
};
/*
Demonstrates launching an error dialog
*/
$scope.launchInfo = function () {
var dialog,
model = {
title: "Info Dialog Example",
actionText: "This is an example of a blocking info"
+ " dialog. This dialog can be used to draw the user's"
+ " attention to an event.",
severity: "info",
primaryOption: {
label: "OK",
callback: function () {
$log.debug("OK Pressed");
dialog.dismiss();
}
} }
}; },
{
dialog = dialogService.showBlockingMessage(model); label: "Do something else...",
callback: function () {
if (!dialog) { $log.debug("Something else pressed");
$log.error("Could not display modal dialog"); }
} }
]
}; };
function incrementProgress() {
model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30));
model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" ");
if (model.progress < 100) {
$timeout(incrementProgress, 1000);
}
} }
return DialogLaunchController; dialog = dialogService.showBlockingMessage(model);
}
); if (dialog) {
//Do processing here
model.actionText = "Processing 100 objects...";
if (knownProgress) {
$timeout(incrementProgress, 1000);
}
} else {
$log.error("Could not display modal dialog");
}
};
/*
Demonstrates launching an error dialog
*/
$scope.launchError = function () {
var dialog,
model = {
title: "Error Dialog Example",
actionText: "Something happened, and it was not good.",
severity: "error",
options: [
{
label: "Try Again",
callback: function () {
$log.debug("Try Again Pressed");
dialog.dismiss();
}
},
{
label: "Cancel",
callback: function () {
$log.debug("Cancel Pressed");
dialog.dismiss();
}
}
]
};
dialog = dialogService.showBlockingMessage(model);
if (!dialog) {
$log.error("Could not display modal dialog");
}
};
/*
Demonstrates launching an error dialog
*/
$scope.launchInfo = function () {
var dialog,
model = {
title: "Info Dialog Example",
actionText: "This is an example of a blocking info"
+ " dialog. This dialog can be used to draw the user's"
+ " attention to an event.",
severity: "info",
primaryOption: {
label: "OK",
callback: function () {
$log.debug("OK Pressed");
dialog.dismiss();
}
}
};
dialog = dialogService.showBlockingMessage(model);
if (!dialog) {
$log.error("Could not display modal dialog");
}
};
}
export default DialogLaunchController;

View File

@ -20,36 +20,53 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
/** "use strict";
* A tool for manually invoking dialogs. When included this
* indicator will allow for dialogs of different types to be
* launched for demonstration and testing purposes.
* @constructor
*/
function DialogLaunchIndicator() { /**
* A tool for manually invoking dialogs. When included this
* indicator will allow for dialogs of different types to be
* launched for demonstration and testing purposes.
* @constructor
*/
} function DialogLaunchIndicator() {
DialogLaunchIndicator.template = 'dialogLaunchTemplate'; }
DialogLaunchIndicator.prototype.getGlyphClass = function () { DialogLaunchIndicator.template = 'dialogLaunchTemplate';
return 'ok';
};
DialogLaunchIndicator.prototype.getText = function () { DialogLaunchIndicator.prototype.getGlyphClass = function () {
return "Launch test dialog"; return 'ok';
}; };
DialogLaunchIndicator.prototype.getDescription = function () { DialogLaunchIndicator.prototype.getText = function () {
return "Launch test dialog"; return "Launch test dialog";
}; };
return DialogLaunchIndicator; DialogLaunchIndicator.prototype.getDescription = function () {
} return "Launch test dialog";
); };
export default DialogLaunchIndicator;

View File

@ -20,107 +20,124 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
"use strict";
/**
* Allows launching of notification messages for the purposes of
* demonstration and testing. Also demonstrates use of
* the NotificationService. Notifications are non-blocking messages that
* appear at the bottom of the screen to inform the user of events
* in a non-intrusive way. For more information see the
* {@link NotificationService}
* @param $scope
* @param $timeout
* @param $log
* @param notificationService
* @constructor
*/
function NotificationLaunchController($scope, $timeout, $log, notificationService) {
var messageCounter = 1;
function getExampleActionText() {
var actionTexts = [
"Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.",
"Eros turpis, pulvinar turpis eros eu",
"Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!"
];
return actionTexts[Math.floor(Math.random() * 3)];
}
/**
* Launch a new notification with a severity level of 'Error'.
*/
$scope.newError = function () {
notificationService.notify({
title: "Example error notification " + messageCounter++,
hint: "An error has occurred",
severity: "error"
});
};
/**
* Launch a new notification with a severity of 'Alert'.
*/
$scope.newAlert = function () {
notificationService.notify({
title: "Alert notification " + (messageCounter++),
hint: "This is an alert message",
severity: "alert",
autoDismiss: true
});
};
/**
* Launch a new notification with a progress bar that is updated
* periodically, tracking an ongoing process.
*/
$scope.newProgress = function () {
let progress = 0;
var notificationModel = {
title: "Progress notification example",
severity: "info",
progress: progress,
actionText: getExampleActionText()
};
let notification;
/** /**
* Allows launching of notification messages for the purposes of * Simulate an ongoing process and update the progress bar.
* demonstration and testing. Also demonstrates use of * @param notification
* the NotificationService. Notifications are non-blocking messages that
* appear at the bottom of the screen to inform the user of events
* in a non-intrusive way. For more information see the
* {@link NotificationService}
* @param $scope
* @param $timeout
* @param $log
* @param notificationService
* @constructor
*/ */
function NotificationLaunchController($scope, $timeout, $log, notificationService) { function incrementProgress() {
var messageCounter = 1; progress = Math.min(100, Math.floor(progress + Math.random() * 30));
let progressText = ["Estimated time"
+ " remaining:"
+ " about ", 60 - Math.floor((progress / 100) * 60), " seconds"].join(" ");
notification.progress(progress, progressText);
function getExampleActionText() { if (progress < 100) {
var actionTexts = [ $timeout(function () {
"Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.", incrementProgress(notificationModel);
"Eros turpis, pulvinar turpis eros eu", }, 1000);
"Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!"
];
return actionTexts[Math.floor(Math.random() * 3)];
} }
/**
* Launch a new notification with a severity level of 'Error'.
*/
$scope.newError = function () {
notificationService.notify({
title: "Example error notification " + messageCounter++,
hint: "An error has occurred",
severity: "error"
});
};
/**
* Launch a new notification with a severity of 'Alert'.
*/
$scope.newAlert = function () {
notificationService.notify({
title: "Alert notification " + (messageCounter++),
hint: "This is an alert message",
severity: "alert",
autoDismiss: true
});
};
/**
* Launch a new notification with a progress bar that is updated
* periodically, tracking an ongoing process.
*/
$scope.newProgress = function () {
let progress = 0;
var notificationModel = {
title: "Progress notification example",
severity: "info",
progress: progress,
actionText: getExampleActionText()
};
let notification;
/**
* Simulate an ongoing process and update the progress bar.
* @param notification
*/
function incrementProgress() {
progress = Math.min(100, Math.floor(progress + Math.random() * 30));
let progressText = ["Estimated time"
+ " remaining:"
+ " about ", 60 - Math.floor((progress / 100) * 60), " seconds"].join(" ");
notification.progress(progress, progressText);
if (progress < 100) {
$timeout(function () {
incrementProgress(notificationModel);
}, 1000);
}
}
notification = notificationService.notify(notificationModel);
incrementProgress();
};
/**
* Launch a new notification with severity level of INFO.
*/
$scope.newInfo = function () {
notificationService.info({
title: "Example Info notification " + messageCounter++
});
};
} }
return NotificationLaunchController; notification = notificationService.notify(notificationModel);
} incrementProgress();
); };
/**
* Launch a new notification with severity level of INFO.
*/
$scope.newInfo = function () {
notificationService.info({
title: "Example Info notification " + messageCounter++
});
};
}
export default NotificationLaunchController;

View File

@ -20,36 +20,53 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
/** "use strict";
* A tool for manually invoking notifications. When included this
* indicator will allow for notifications of different types to be
* launched for demonstration and testing purposes.
* @constructor
*/
function NotificationLaunchIndicator() { /**
* A tool for manually invoking notifications. When included this
* indicator will allow for notifications of different types to be
* launched for demonstration and testing purposes.
* @constructor
*/
} function NotificationLaunchIndicator() {
NotificationLaunchIndicator.template = 'notificationLaunchTemplate'; }
NotificationLaunchIndicator.prototype.getGlyphClass = function () { NotificationLaunchIndicator.template = 'notificationLaunchTemplate';
return 'ok';
};
NotificationLaunchIndicator.prototype.getText = function () { NotificationLaunchIndicator.prototype.getGlyphClass = function () {
return "Launch notification"; return 'ok';
}; };
NotificationLaunchIndicator.prototype.getDescription = function () { NotificationLaunchIndicator.prototype.getText = function () {
return "Launch notification"; return "Launch notification";
}; };
return NotificationLaunchIndicator; NotificationLaunchIndicator.prototype.getDescription = function () {
} return "Launch notification";
); };
export default NotificationLaunchIndicator;

View File

@ -20,35 +20,53 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/BrowserPersistenceProvider" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
BrowserPersistenceProvider * Administration. All rights reserved.
) { *
"use strict"; * 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.
*****************************************************************************/
return { import BrowserPersistenceProvider from './src/BrowserPersistenceProvider';
name: "example/persistence",
definition: { "use strict";
"extensions": {
"components": [ export default {
{ name: "example/persistence",
"provides": "persistenceService", definition: {
"type": "provider", "extensions": {
"implementation": BrowserPersistenceProvider, "components": [
"depends": [ {
"$q", "provides": "persistenceService",
"PERSISTENCE_SPACE" "type": "provider",
] "implementation": BrowserPersistenceProvider,
} "depends": [
], "$q",
"constants": [ "PERSISTENCE_SPACE"
{ ]
"key": "PERSISTENCE_SPACE", }
"value": "mct" ],
} "constants": [
] {
} "key": "PERSISTENCE_SPACE",
"value": "mct"
}
]
} }
}; }
}); };

View File

@ -24,79 +24,100 @@
* Stubbed implementation of a persistence provider, * Stubbed implementation of a persistence provider,
* to permit objects to be created, saved, etc. * to permit objects to be created, saved, etc.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
'use strict'; * 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.
*****************************************************************************/
function BrowserPersistenceProvider($q, SPACE) { /**
var spaces = SPACE ? [SPACE] : [], * Stubbed implementation of a persistence provider,
caches = {}, * to permit objects to be created, saved, etc.
promises = { */
as: function (value) { 'use strict';
return $q.when(value);
}
};
spaces.forEach(function (space) { function BrowserPersistenceProvider($q, SPACE) {
caches[space] = {}; var spaces = SPACE ? [SPACE] : [],
}); caches = {},
promises = {
as: function (value) {
return $q.when(value);
}
};
return { spaces.forEach(function (space) {
listSpaces: function () { caches[space] = {};
return promises.as(spaces); });
},
listObjects: function (space) {
var cache = caches[space];
return promises.as( return {
cache ? Object.keys(cache) : null listSpaces: function () {
); return promises.as(spaces);
}, },
createObject: function (space, key, value) { listObjects: function (space) {
var cache = caches[space]; var cache = caches[space];
if (!cache || cache[key]) { return promises.as(
return promises.as(null); cache ? Object.keys(cache) : null
} );
},
createObject: function (space, key, value) {
var cache = caches[space];
cache[key] = value; if (!cache || cache[key]) {
return promises.as(null);
}
return promises.as(true); cache[key] = value;
},
readObject: function (space, key) {
var cache = caches[space];
return promises.as( return promises.as(true);
cache ? cache[key] : null },
); readObject: function (space, key) {
}, var cache = caches[space];
updateObject: function (space, key, value) {
var cache = caches[space];
if (!cache || !cache[key]) { return promises.as(
return promises.as(null); cache ? cache[key] : null
} );
},
updateObject: function (space, key, value) {
var cache = caches[space];
cache[key] = value; if (!cache || !cache[key]) {
return promises.as(null);
}
return promises.as(true); cache[key] = value;
},
deleteObject: function (space, key, value) {
var cache = caches[space];
if (!cache || !cache[key]) { return promises.as(true);
return promises.as(null); },
} deleteObject: function (space, key, value) {
var cache = caches[space];
delete cache[key]; if (!cache || !cache[key]) {
return promises.as(null);
}
return promises.as(true); delete cache[key];
}
};
return promises.as(true);
} }
};
return BrowserPersistenceProvider; }
}
); export default BrowserPersistenceProvider;

View File

@ -20,26 +20,44 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/ExamplePolicy" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
ExamplePolicy * Administration. All rights reserved.
) { *
"use strict"; * 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.
*****************************************************************************/
return { import ExamplePolicy from './src/ExamplePolicy';
name: "example/policy",
definition: { "use strict";
"name": "Example Policy",
"description": "Provides an example of using policies to prohibit actions.", export default {
"extensions": { name: "example/policy",
"policies": [ definition: {
{ "name": "Example Policy",
"implementation": ExamplePolicy, "description": "Provides an example of using policies to prohibit actions.",
"category": "action" "extensions": {
} "policies": [
] {
} "implementation": ExamplePolicy,
"category": "action"
}
]
} }
}; }
}); };

View File

@ -20,28 +20,45 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
function ExamplePolicy() { "use strict";
return {
/**
* Disallow the Remove action on objects whose name contains
* "foo."
*/
allow: function (action, context) {
var domainObject = (context || {}).domainObject,
model = (domainObject && domainObject.getModel()) || {},
name = model.name || "",
metadata = action.getMetadata() || {};
return metadata.key !== 'remove' || name.indexOf('foo') < 0; function ExamplePolicy() {
} return {
}; /**
* Disallow the Remove action on objects whose name contains
* "foo."
*/
allow: function (action, context) {
var domainObject = (context || {}).domainObject,
model = (domainObject && domainObject.getModel()) || {},
name = model.name || "",
metadata = action.getMetadata() || {};
return metadata.key !== 'remove' || name.indexOf('foo') < 0;
} }
};
}
return ExamplePolicy; export default ExamplePolicy;
}
);

View File

@ -20,36 +20,53 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/WatchIndicator", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/DigestIndicator" * as represented by the Administrator of the National Aeronautics and Space
], function ( * Administration. All rights reserved.
WatchIndicator, *
DigestIndicator * 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.
"use strict"; * 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.
*****************************************************************************/
return { import WatchIndicator from './src/WatchIndicator';
name: "example/profiling",
definition: { import DigestIndicator from './src/DigestIndicator';
"extensions": { "use strict";
"indicators": [
{ export default {
"implementation": WatchIndicator, name: "example/profiling",
"depends": [ definition: {
"$interval", "extensions": {
"$rootScope" "indicators": [
] {
}, "implementation": WatchIndicator,
{ "depends": [
"implementation": DigestIndicator, "$interval",
"depends": [ "$rootScope"
"$interval", ]
"$rootScope" },
] {
} "implementation": DigestIndicator,
] "depends": [
} "$interval",
"$rootScope"
]
}
]
} }
}; }
}); };

View File

@ -20,63 +20,79 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
/** "use strict";
* Displays the number of digests that have occurred since the
* indicator was first instantiated.
* @constructor
* @param $interval Angular's $interval
* @implements {Indicator}
*/
function DigestIndicator($interval, $rootScope) {
var digests = 0,
displayed = 0,
start = Date.now();
function update() { /**
var now = Date.now(), * Displays the number of digests that have occurred since the
secs = (now - start) / 1000; * indicator was first instantiated.
displayed = Math.round(digests / secs); * @constructor
start = now; * @param $interval Angular's $interval
digests = 0; * @implements {Indicator}
} */
function DigestIndicator($interval, $rootScope) {
function increment() { var digests = 0,
digests += 1; displayed = 0,
} start = Date.now();
$rootScope.$watch(increment);
// Update state every second
$interval(update, 1000);
// Provide initial state, too
update();
return {
/**
* Get the CSS class that defines the icon
* to display in this indicator. This will appear
* as a dataflow icon.
* @returns {string} the cssClass of the dataflow icon
*/
getCssClass: function () {
return "icon-connectivity";
},
getText: function () {
return displayed + " digests/sec";
},
getDescription: function () {
return "";
}
};
}
return DigestIndicator;
function update() {
var now = Date.now(),
secs = (now - start) / 1000;
displayed = Math.round(digests / secs);
start = now;
digests = 0;
} }
);
function increment() {
digests += 1;
}
$rootScope.$watch(increment);
// Update state every second
$interval(update, 1000);
// Provide initial state, too
update();
return {
/**
* Get the CSS class that defines the icon
* to display in this indicator. This will appear
* as a dataflow icon.
* @returns {string} the cssClass of the dataflow icon
*/
getCssClass: function () {
return "icon-connectivity";
},
getText: function () {
return displayed + " digests/sec";
},
getDescription: function () {
return "";
}
};
}
export default DigestIndicator;

View File

@ -20,67 +20,83 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
/** "use strict";
* Updates a count of currently-active Angular watches.
* @constructor
* @param $interval Angular's $interval
*/
function WatchIndicator($interval, $rootScope) {
var watches = 0;
function count(scope) { /**
if (scope) { * Updates a count of currently-active Angular watches.
watches += (scope.$$watchers || []).length; * @constructor
count(scope.$$childHead); * @param $interval Angular's $interval
count(scope.$$nextSibling); */
} function WatchIndicator($interval, $rootScope) {
} var watches = 0;
function update() { function count(scope) {
watches = 0; if (scope) {
count($rootScope); watches += (scope.$$watchers || []).length;
} count(scope.$$childHead);
count(scope.$$nextSibling);
// Update state every second
$interval(update, 1000);
// Provide initial state, too
update();
return {
/**
* Get the CSS class (single character used as an icon)
* to display in this indicator. This will return ".",
* which should appear as a database icon.
* @returns {string} the character of the database icon
*/
getCssClass: function () {
return "icon-database";
},
/**
* Get the text that should appear in the indicator.
* @returns {string} brief summary of connection status
*/
getText: function () {
return watches + " watches";
},
/**
* Get a longer-form description of the current connection
* space, suitable for display in a tooltip
* @returns {string} longer summary of connection status
*/
getDescription: function () {
return "";
}
};
} }
return WatchIndicator;
} }
);
function update() {
watches = 0;
count($rootScope);
}
// Update state every second
$interval(update, 1000);
// Provide initial state, too
update();
return {
/**
* Get the CSS class (single character used as an icon)
* to display in this indicator. This will return ".",
* which should appear as a database icon.
* @returns {string} the character of the database icon
*/
getCssClass: function () {
return "icon-database";
},
/**
* Get the text that should appear in the indicator.
* @returns {string} brief summary of connection status
*/
getText: function () {
return watches + " watches";
},
/**
* Get a longer-form description of the current connection
* space, suitable for display in a tooltip
* @returns {string} longer summary of connection status
*/
getDescription: function () {
return "";
}
};
}
export default WatchIndicator;

View File

@ -20,44 +20,62 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/ScratchPersistenceProvider" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
ScratchPersistenceProvider * Administration. All rights reserved.
) { *
"use strict"; * 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.
*****************************************************************************/
return { import ScratchPersistenceProvider from './src/ScratchPersistenceProvider';
name: "example/scratchpad",
definition: { "use strict";
"extensions": {
"roots": [ export default {
{ name: "example/scratchpad",
"id": "scratch:root" definition: {
} "extensions": {
], "roots": [
"models": [ {
{ "id": "scratch:root"
"id": "scratch:root", }
"model": { ],
"type": "folder", "models": [
"composition": [], {
"name": "Scratchpad" "id": "scratch:root",
}, "model": {
"priority": "preferred" "type": "folder",
} "composition": [],
], "name": "Scratchpad"
"components": [ },
{ "priority": "preferred"
"provides": "persistenceService", }
"type": "provider", ],
"implementation": ScratchPersistenceProvider, "components": [
"depends": [ {
"$q" "provides": "persistenceService",
] "type": "provider",
} "implementation": ScratchPersistenceProvider,
] "depends": [
} "$q"
]
}
]
} }
}; }
}); };

View File

@ -20,60 +20,77 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
'use strict'; * 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.
*****************************************************************************/
/** 'use strict';
* The ScratchPersistenceProvider keeps JSON documents in memory
* and provides a persistence interface, but changes are lost on reload.
* @memberof example/scratchpad
* @constructor
* @implements {PersistenceService}
* @param q Angular's $q, for promises
*/
function ScratchPersistenceProvider($q) {
this.$q = $q;
this.table = {};
}
ScratchPersistenceProvider.prototype.listSpaces = function () { /**
return this.$q.when(['scratch']); * The ScratchPersistenceProvider keeps JSON documents in memory
}; * and provides a persistence interface, but changes are lost on reload.
* @memberof example/scratchpad
* @constructor
* @implements {PersistenceService}
* @param q Angular's $q, for promises
*/
function ScratchPersistenceProvider($q) {
this.$q = $q;
this.table = {};
}
ScratchPersistenceProvider.prototype.listObjects = function (space) { ScratchPersistenceProvider.prototype.listSpaces = function () {
return this.$q.when( return this.$q.when(['scratch']);
space === 'scratch' ? Object.keys(this.table) : [] };
);
};
ScratchPersistenceProvider.prototype.createObject = function (space, key, value) { ScratchPersistenceProvider.prototype.listObjects = function (space) {
if (space === 'scratch') { return this.$q.when(
this.table[key] = JSON.stringify(value); space === 'scratch' ? Object.keys(this.table) : []
} );
};
return this.$q.when(space === 'scratch'); ScratchPersistenceProvider.prototype.createObject = function (space, key, value) {
}; if (space === 'scratch') {
this.table[key] = JSON.stringify(value);
ScratchPersistenceProvider.prototype.readObject = function (space, key) {
return this.$q.when(
(space === 'scratch' && this.table[key])
? JSON.parse(this.table[key]) : undefined
);
};
ScratchPersistenceProvider.prototype.deleteObject = function (space, key, value) {
if (space === 'scratch') {
delete this.table[key];
}
return this.$q.when(space === 'scratch');
};
ScratchPersistenceProvider.prototype.updateObject =
ScratchPersistenceProvider.prototype.createObject;
return ScratchPersistenceProvider;
} }
);
return this.$q.when(space === 'scratch');
};
ScratchPersistenceProvider.prototype.readObject = function (space, key) {
return this.$q.when(
(space === 'scratch' && this.table[key])
? JSON.parse(this.table[key]) : undefined
);
};
ScratchPersistenceProvider.prototype.deleteObject = function (space, key, value) {
if (space === 'scratch') {
delete this.table[key];
}
return this.$q.when(space === 'scratch');
};
ScratchPersistenceProvider.prototype.updateObject =
ScratchPersistenceProvider.prototype.createObject;
export default ScratchPersistenceProvider;

View File

@ -1,188 +1,175 @@
define([ import ExampleStyleGuideModelProvider from './src/ExampleStyleGuideModelProvider';
"./src/ExampleStyleGuideModelProvider", import MCTExample from './src/MCTExample';
"./src/MCTExample", import introTemplate from './res/templates/intro.html';
"./res/templates/intro.html", import standardsTemplate from './res/templates/standards.html';
"./res/templates/standards.html", import colorsTemplate from './res/templates/colors.html';
"./res/templates/colors.html", import statusTemplate from './res/templates/status.html';
"./res/templates/status.html", import glyphsTemplate from './res/templates/glyphs.html';
"./res/templates/glyphs.html", import controlsTemplate from './res/templates/controls.html';
"./res/templates/controls.html", import inputTemplate from './res/templates/input.html';
"./res/templates/input.html", import menusTemplate from './res/templates/menus.html';
"./res/templates/menus.html"
], function ( export default {
ExampleStyleGuideModelProvider, name: "example/styleguide",
MCTExample, definition: {
introTemplate, "name": "Open MCT Style Guide",
standardsTemplate, "description": "Examples and documentation illustrating UI styles in use in Open MCT.",
colorsTemplate, "extensions":
statusTemplate, {
glyphsTemplate, "types": [
controlsTemplate, {
inputTemplate, "key": "styleguide.intro",
menusTemplate "name": "Introduction",
) { "cssClass": "icon-page",
return { "description": "Introduction and overview to the style guide"
name: "example/styleguide", },
definition: { {
"name": "Open MCT Style Guide", "key": "styleguide.standards",
"description": "Examples and documentation illustrating UI styles in use in Open MCT.", "name": "Standards",
"extensions": "cssClass": "icon-page",
{ "description": ""
"types": [ },
{ {
"key": "styleguide.intro", "key": "styleguide.colors",
"name": "Introduction", "name": "Colors",
"cssClass": "icon-page", "cssClass": "icon-page",
"description": "Introduction and overview to the style guide" "description": ""
}, },
{ {
"key": "styleguide.standards", "key": "styleguide.status",
"name": "Standards", "name": "status",
"cssClass": "icon-page", "cssClass": "icon-page",
"description": "" "description": "Limits, telemetry paused, etc."
}, },
{ {
"key": "styleguide.colors", "key": "styleguide.glyphs",
"name": "Colors", "name": "Glyphs",
"cssClass": "icon-page", "cssClass": "icon-page",
"description": "" "description": "Glyphs overview"
}, },
{ {
"key": "styleguide.status", "key": "styleguide.controls",
"name": "status", "name": "Controls",
"cssClass": "icon-page", "cssClass": "icon-page",
"description": "Limits, telemetry paused, etc." "description": "Buttons, selects, HTML controls"
}, },
{ {
"key": "styleguide.glyphs", "key": "styleguide.input",
"name": "Glyphs", "name": "Text Inputs",
"cssClass": "icon-page", "cssClass": "icon-page",
"description": "Glyphs overview" "description": "Various text inputs"
}, },
{ {
"key": "styleguide.controls", "key": "styleguide.menus",
"name": "Controls", "name": "Menus",
"cssClass": "icon-page", "cssClass": "icon-page",
"description": "Buttons, selects, HTML controls" "description": "Context menus, dropdowns"
}, }
{ ],
"key": "styleguide.input", "views": [
"name": "Text Inputs", {
"cssClass": "icon-page", "key": "styleguide.intro",
"description": "Various text inputs" "type": "styleguide.intro",
}, "template": introTemplate,
{ "editable": false
"key": "styleguide.menus", },
"name": "Menus", {
"cssClass": "icon-page", "key": "styleguide.standards",
"description": "Context menus, dropdowns" "type": "styleguide.standards",
} "template": standardsTemplate,
], "editable": false
"views": [ },
{ {
"key": "styleguide.intro", "key": "styleguide.colors",
"type": "styleguide.intro", "type": "styleguide.colors",
"template": introTemplate, "template": colorsTemplate,
"editable": false "editable": false
}, },
{ {
"key": "styleguide.standards", "key": "styleguide.status",
"type": "styleguide.standards", "type": "styleguide.status",
"template": standardsTemplate, "template": statusTemplate,
"editable": false "editable": false
}, },
{ {
"key": "styleguide.colors", "key": "styleguide.glyphs",
"type": "styleguide.colors", "type": "styleguide.glyphs",
"template": colorsTemplate, "template": glyphsTemplate,
"editable": false "editable": false
}, },
{ {
"key": "styleguide.status", "key": "styleguide.controls",
"type": "styleguide.status", "type": "styleguide.controls",
"template": statusTemplate, "template": controlsTemplate,
"editable": false "editable": false
}, },
{ {
"key": "styleguide.glyphs", "key": "styleguide.input",
"type": "styleguide.glyphs", "type": "styleguide.input",
"template": glyphsTemplate, "template": inputTemplate,
"editable": false "editable": false
}, },
{ {
"key": "styleguide.controls", "key": "styleguide.menus",
"type": "styleguide.controls", "type": "styleguide.menus",
"template": controlsTemplate, "template": menusTemplate,
"editable": false "editable": false
}, }
{ ],
"key": "styleguide.input", "roots": [
"type": "styleguide.input", {
"template": inputTemplate, "id": "styleguide:home"
"editable": false }
}, ],
{ "models": [
"key": "styleguide.menus", {
"type": "styleguide.menus", "id": "styleguide:home",
"template": menusTemplate, "priority": "preferred",
"editable": false "model": {
} "type": "noneditable.folder",
], "name": "Style Guide Home",
"roots": [ "location": "ROOT",
{ "composition": [
"id": "styleguide:home" "intro",
} "standards",
], "colors",
"models": [ "status",
{ "glyphs",
"id": "styleguide:home", "styleguide:ui-elements"
"priority": "preferred",
"model": {
"type": "noneditable.folder",
"name": "Style Guide Home",
"location": "ROOT",
"composition": [
"intro",
"standards",
"colors",
"status",
"glyphs",
"styleguide:ui-elements"
]
}
},
{
"id": "styleguide:ui-elements",
"priority": "preferred",
"model": {
"type": "noneditable.folder",
"name": "UI Elements",
"location": "styleguide:home",
"composition": [
"controls",
"input",
"menus"
]
}
}
],
"directives": [
{
"key": "mctExample",
"implementation": MCTExample
}
],
"components": [
{
"provides": "modelService",
"type": "provider",
"implementation": ExampleStyleGuideModelProvider,
"depends": [
"$q"
] ]
} }
] },
} {
} "id": "styleguide:ui-elements",
}; "priority": "preferred",
}); "model": {
"type": "noneditable.folder",
"name": "UI Elements",
"location": "styleguide:home",
"composition": [
"controls",
"input",
"menus"
]
}
}
],
"directives": [
{
"key": "mctExample",
"implementation": MCTExample
}
],
"components": [
{
"provides": "modelService",
"type": "provider",
"implementation": ExampleStyleGuideModelProvider,
"depends": [
"$q"
]
}
]
}
}
};

View File

@ -20,63 +20,80 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2016, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
"use strict"; * 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.
*****************************************************************************/
function ExampleStyleGuideModelProvider($q) { "use strict";
var pages = {};
// Add pages function ExampleStyleGuideModelProvider($q) {
pages.intro = { var pages = {};
name: "Introduction",
type: "styleguide.intro",
location: "styleguide:home"
};
pages.standards = {
name: "Standards",
type: "styleguide.standards",
location: "styleguide:home"
};
pages.colors = {
name: "Colors",
type: "styleguide.colors",
location: "styleguide:home"
};
pages.glyphs = {
name: "Glyphs",
type: "styleguide.glyphs",
location: "styleguide:home"
};
pages.status = {
name: "Status Indication",
type: "styleguide.status",
location: "styleguide:home"
};
pages.controls = {
name: "Controls",
type: "styleguide.controls",
location: "styleguide:ui-elements"
};
pages.input = {
name: "Text Inputs",
type: "styleguide.input",
location: "styleguide:ui-elements"
};
pages.menus = {
name: "Menus",
type: "styleguide.menus",
location: "styleguide:ui-elements"
};
return { // Add pages
getModels: function () { pages.intro = {
return $q.when(pages); name: "Introduction",
} type: "styleguide.intro",
}; location: "styleguide:home"
};
pages.standards = {
name: "Standards",
type: "styleguide.standards",
location: "styleguide:home"
};
pages.colors = {
name: "Colors",
type: "styleguide.colors",
location: "styleguide:home"
};
pages.glyphs = {
name: "Glyphs",
type: "styleguide.glyphs",
location: "styleguide:home"
};
pages.status = {
name: "Status Indication",
type: "styleguide.status",
location: "styleguide:home"
};
pages.controls = {
name: "Controls",
type: "styleguide.controls",
location: "styleguide:ui-elements"
};
pages.input = {
name: "Text Inputs",
type: "styleguide.input",
location: "styleguide:ui-elements"
};
pages.menus = {
name: "Menus",
type: "styleguide.menus",
location: "styleguide:ui-elements"
};
return {
getModels: function () {
return $q.when(pages);
} }
};
}
return ExampleStyleGuideModelProvider; export default ExampleStyleGuideModelProvider;
}
);

View File

@ -1,30 +1,25 @@
define([ import MCTExampleTemplate from '../res/templates/mct-example.html';
'../res/templates/mct-example.html'
], function (
MCTExampleTemplate
) {
function MCTExample() { function MCTExample() {
function link($scope, $element, $attrs, controller, $transclude) { function link($scope, $element, $attrs, controller, $transclude) {
var codeEl = $element.find('pre'); var codeEl = $element.find('pre');
var exampleEl = $element.find('div'); var exampleEl = $element.find('div');
$transclude(function (clone) { $transclude(function (clone) {
exampleEl.append(clone); exampleEl.append(clone);
codeEl.text(exampleEl.html() codeEl.text(exampleEl.html()
.replace(/ class="ng-scope"/g, "") .replace(/ class="ng-scope"/g, "")
.replace(/ ng-scope"/g, '"')); .replace(/ ng-scope"/g, '"'));
}); });
}
return {
restrict: "E",
template: MCTExampleTemplate,
transclude: true,
link: link,
replace: true
};
} }
return MCTExample; return {
}); restrict: "E",
template: MCTExampleTemplate,
transclude: true,
link: link,
replace: true
};
}
export default MCTExample;

View File

@ -7,6 +7,7 @@
"@percy/cli": "^1.0.0-beta.70", "@percy/cli": "^1.0.0-beta.70",
"@percy/playwright": "^1.0.1", "@percy/playwright": "^1.0.1",
"@playwright/test": "^1.16.3", "@playwright/test": "^1.16.3",
"5to6-codemod": "^1.8.0",
"allure-playwright": "^2.0.0-beta.14", "allure-playwright": "^2.0.0-beta.14",
"angular": ">=1.8.0", "angular": ">=1.8.0",
"angular-route": "1.4.14", "angular-route": "1.4.14",
@ -36,6 +37,7 @@
"imports-loader": "^0.8.0", "imports-loader": "^0.8.0",
"istanbul-instrumenter-loader": "^3.0.1", "istanbul-instrumenter-loader": "^3.0.1",
"jasmine-core": "^3.7.1", "jasmine-core": "^3.7.1",
"jscodeshift": "^0.13.0",
"jsdoc": "^3.3.2", "jsdoc": "^3.3.2",
"karma": "6.3.9", "karma": "6.3.9",
"karma-chrome-launcher": "3.1.0", "karma-chrome-launcher": "3.1.0",

View File

@ -20,139 +20,147 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/navigation/NavigationService", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/navigation/NavigateAction", * as represented by the Administrator of the National Aeronautics and Space
"./src/navigation/OrphanNavigationHandler", * Administration. All rights reserved.
"./res/templates/browse.html", *
"./res/templates/browse-object.html", * Open MCT is licensed under the Apache License, Version 2.0 (the
"./res/templates/browse/object-header.html", * "License"); you may not use this file except in compliance with the License.
"./res/templates/browse/object-header-frame.html", * You may obtain a copy of the License at
"./res/templates/menu-arrow.html", * http://www.apache.org/licenses/LICENSE-2.0.
"./res/templates/back-arrow.html", *
"./res/templates/browse/object-properties.html", * Unless required by applicable law or agreed to in writing, software
"./res/templates/browse/inspector-region.html" * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
], function ( * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
NavigationService, * License for the specific language governing permissions and limitations
NavigateAction, * under the License.
OrphanNavigationHandler, *
browseTemplate, * Open MCT includes source code licensed under additional open source
browseObjectTemplate, * licenses. See the Open Source Licenses file (LICENSES.md) included with
objectHeaderTemplate, * this source code distribution or the Licensing information page available
objectHeaderFrameTemplate, * at runtime from the About dialog for additional information.
menuArrowTemplate, *****************************************************************************/
backArrowTemplate,
objectPropertiesTemplate,
inspectorRegionTemplate
) {
return { import NavigationService from './src/navigation/NavigationService';
name: "platform/commonUI/browse",
definition: { import NavigateAction from './src/navigation/NavigateAction';
"extensions": { import OrphanNavigationHandler from './src/navigation/OrphanNavigationHandler';
"routes": [ import browseTemplate from './res/templates/browse.html';
], import browseObjectTemplate from './res/templates/browse-object.html';
"constants": [ import objectHeaderTemplate from './res/templates/browse/object-header.html';
{ import objectHeaderFrameTemplate from './res/templates/browse/object-header-frame.html';
"key": "DEFAULT_PATH", import menuArrowTemplate from './res/templates/menu-arrow.html';
"value": "mine", import backArrowTemplate from './res/templates/back-arrow.html';
"priority": "fallback" import objectPropertiesTemplate from './res/templates/browse/object-properties.html';
} import inspectorRegionTemplate from './res/templates/browse/inspector-region.html';
],
"representations": [ export default {
{ name: "platform/commonUI/browse",
"key": "browse-object", definition: {
"template": browseObjectTemplate, "extensions": {
"gestures": [ "routes": [
"drop" ],
], "constants": [
"uses": [ {
"view" "key": "DEFAULT_PATH",
] "value": "mine",
}, "priority": "fallback"
{ }
"key": "object-header", ],
"template": objectHeaderTemplate, "representations": [
"uses": [ {
"type" "key": "browse-object",
] "template": browseObjectTemplate,
}, "gestures": [
{ "drop"
"key": "object-header-frame", ],
"template": objectHeaderFrameTemplate, "uses": [
"uses": [ "view"
"type" ]
] },
}, {
{ "key": "object-header",
"key": "menu-arrow", "template": objectHeaderTemplate,
"template": menuArrowTemplate, "uses": [
"uses": [ "type"
"action" ]
], },
"gestures": [ {
"menu" "key": "object-header-frame",
] "template": objectHeaderFrameTemplate,
}, "uses": [
{ "type"
"key": "back-arrow", ]
"uses": [ },
"context" {
], "key": "menu-arrow",
"template": backArrowTemplate "template": menuArrowTemplate,
}, "uses": [
{ "action"
"key": "object-properties", ],
"template": objectPropertiesTemplate "gestures": [
}, "menu"
{ ]
"key": "inspector-region", },
"template": inspectorRegionTemplate {
} "key": "back-arrow",
], "uses": [
"services": [ "context"
{ ],
"key": "navigationService", "template": backArrowTemplate
"implementation": NavigationService, },
"depends": [ {
"$window" "key": "object-properties",
] "template": objectPropertiesTemplate
} },
], {
"actions": [ "key": "inspector-region",
{ "template": inspectorRegionTemplate
"key": "navigate", }
"implementation": NavigateAction, ],
"depends": [ "services": [
"navigationService" {
] "key": "navigationService",
} "implementation": NavigationService,
], "depends": [
"runs": [ "$window"
{ ]
"implementation": OrphanNavigationHandler, }
"depends": [ ],
"throttle", "actions": [
"topic", {
"navigationService" "key": "navigate",
] "implementation": NavigateAction,
} "depends": [
], "navigationService"
"templates": [ ]
{ }
key: "browseRoot", ],
template: browseTemplate "runs": [
}, {
{ "implementation": OrphanNavigationHandler,
key: "browseObject", "depends": [
template: browseObjectTemplate "throttle",
}, "topic",
{ "navigationService"
key: "inspectorRegion", ]
template: inspectorRegionTemplate }
} ],
] "templates": [
} {
key: "browseRoot",
template: browseTemplate
},
{
key: "browseObject",
template: browseObjectTemplate
},
{
key: "inspectorRegion",
template: inspectorRegionTemplate
}
]
} }
}; }
}); };

View File

@ -20,48 +20,64 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[ * Open MCT, Copyright (c) 2014-2021, United States Government
'../../regions/src/Region' * as represented by the Administrator of the National Aeronautics and Space
], * Administration. All rights reserved.
function (Region) { *
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/** import Region from '../../regions/src/Region';
* Defines the a default Inspector region. Captured in a class to
* allow for modular extension and customization of regions based on
* the typical case.
* @memberOf platform/commonUI/regions
* @constructor
*/
function InspectorRegion() {
Region.call(this, {'name': 'Inspector'});
this.buildRegion(); /**
* Defines the a default Inspector region. Captured in a class to
* allow for modular extension and customization of regions based on
* the typical case.
* @memberOf platform/commonUI/regions
* @constructor
*/
function InspectorRegion() {
Region.call(this, {'name': 'Inspector'});
this.buildRegion();
}
InspectorRegion.prototype = Object.create(Region.prototype);
InspectorRegion.prototype.constructor = Region;
/**
* @private
*/
InspectorRegion.prototype.buildRegion = function () {
var metadataRegion = {
name: 'metadata',
title: 'Metadata Region',
// Which modes should the region part be visible in? If
// nothing provided here, then assumed that part is visible
// in both. The visibility or otherwise of a region part
// should be decided by a policy. In this case, 'modes' is a
// shortcut that is used by the EditableRegionPolicy.
modes: ['browse', 'edit'],
content: {
key: 'object-properties'
} }
};
this.addRegion(new Region(metadataRegion), 0);
};
InspectorRegion.prototype = Object.create(Region.prototype); export default InspectorRegion;
InspectorRegion.prototype.constructor = Region;
/**
* @private
*/
InspectorRegion.prototype.buildRegion = function () {
var metadataRegion = {
name: 'metadata',
title: 'Metadata Region',
// Which modes should the region part be visible in? If
// nothing provided here, then assumed that part is visible
// in both. The visibility or otherwise of a region part
// should be decided by a policy. In this case, 'modes' is a
// shortcut that is used by the EditableRegionPolicy.
modes: ['browse', 'edit'],
content: {
key: 'object-properties'
}
};
this.addRegion(new Region(metadataRegion), 0);
};
return InspectorRegion;
}
);

View File

@ -23,47 +23,60 @@
/** /**
* Module defining NavigateAction. Created by vwoeltje on 11/10/14. * Module defining NavigateAction. Created by vwoeltje on 11/10/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* The navigate action navigates to a specific domain object. * Module defining NavigateAction. Created by vwoeltje on 11/10/14.
* @memberof platform/commonUI/browse */
* @constructor function NavigateAction(navigationService, context) {
* @implements {Action} this.domainObject = context.domainObject;
*/ this.navigationService = navigationService;
function NavigateAction(navigationService, context) { }
this.domainObject = context.domainObject;
this.navigationService = navigationService;
}
/** /**
* Navigate to the object described in the context. * Navigate to the object described in the context.
* @returns {Promise} a promise that is resolved once the * @returns {Promise} a promise that is resolved once the
* navigation has been updated * navigation has been updated
*/ */
NavigateAction.prototype.perform = function () { NavigateAction.prototype.perform = function () {
if (this.navigationService.shouldNavigate()) { if (this.navigationService.shouldNavigate()) {
this.navigationService.setNavigation(this.domainObject, true); this.navigationService.setNavigation(this.domainObject, true);
return Promise.resolve({}); return Promise.resolve({});
}
return Promise.reject('Navigation Prevented by User');
};
/**
* Navigate as an action is only applicable when a domain object
* is described in the action context.
* @param {ActionContext} context the context in which the action
* will be performed
* @returns {boolean} true if applicable
*/
NavigateAction.appliesTo = function (context) {
return context.domainObject !== undefined;
};
return NavigateAction;
} }
);
return Promise.reject('Navigation Prevented by User');
};
/**
* Navigate as an action is only applicable when a domain object
* is described in the action context.
* @param {ActionContext} context the context in which the action
* will be performed
* @returns {boolean} true if applicable
*/
NavigateAction.appliesTo = function (context) {
return context.domainObject !== undefined;
};
export default NavigateAction;

View File

@ -23,181 +23,193 @@
/** /**
* Module defining NavigationService. Created by vwoeltje on 11/10/14. * Module defining NavigationService. Created by vwoeltje on 11/10/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* The navigation service maintains the application's current * Module defining NavigationService. Created by vwoeltje on 11/10/14.
* navigation state, and allows listening for changes thereto. */
* function NavigationService($window) {
* @memberof platform/commonUI/browse this.navigated = undefined;
* @constructor this.callbacks = [];
*/ this.checks = [];
function NavigationService($window) { this.$window = $window;
this.navigated = undefined;
this.callbacks = [];
this.checks = [];
this.$window = $window;
this.oldUnload = $window.onbeforeunload; this.oldUnload = $window.onbeforeunload;
$window.onbeforeunload = this.onBeforeUnload.bind(this); $window.onbeforeunload = this.onBeforeUnload.bind(this);
} }
/** /**
* Get the current navigation state. * Get the current navigation state.
* *
* @returns {DomainObject} the object that is navigated-to * @returns {DomainObject} the object that is navigated-to
*/ */
NavigationService.prototype.getNavigation = function () { NavigationService.prototype.getNavigation = function () {
return this.navigated; return this.navigated;
}; };
/** /**
* Navigate to a specified object. If navigation checks exist and * Navigate to a specified object. If navigation checks exist and
* return reasons to prevent navigation, it will prompt the user before * return reasons to prevent navigation, it will prompt the user before
* continuing. Trying to navigate to the currently navigated object will * continuing. Trying to navigate to the currently navigated object will
* do nothing. * do nothing.
* *
* If a truthy value is passed for `force`, it will skip navigation * If a truthy value is passed for `force`, it will skip navigation
* and will not prevent navigation to an already selected object. * and will not prevent navigation to an already selected object.
* *
* @param {DomainObject} domainObject the domain object to navigate to * @param {DomainObject} domainObject the domain object to navigate to
* @param {Boolean} force if true, force navigation to occur. * @param {Boolean} force if true, force navigation to occur.
* @returns {Boolean} true if navigation occurred, otherwise false. * @returns {Boolean} true if navigation occurred, otherwise false.
*/ */
NavigationService.prototype.setNavigation = function (domainObject, force) { NavigationService.prototype.setNavigation = function (domainObject, force) {
if (force) { if (force) {
this.doNavigation(domainObject); this.doNavigation(domainObject);
return true; return true;
}
if (this.navigated === domainObject) {
return true;
}
var doNotNavigate = this.shouldWarnBeforeNavigate();
if (doNotNavigate && !this.$window.confirm(doNotNavigate)) {
return false;
}
this.doNavigation(domainObject);
return true;
};
/**
* Listen for changes in navigation. The passed callback will
* be invoked with the new domain object of navigation when
* this changes.
*
* @param {function} callback the callback to invoke when
* navigation state changes
*/
NavigationService.prototype.addListener = function (callback) {
this.callbacks.push(callback);
};
/**
* Stop listening for changes in navigation state.
*
* @param {function} callback the callback which should
* no longer be invoked when navigation state
* changes
*/
NavigationService.prototype.removeListener = function (callback) {
this.callbacks = this.callbacks.filter(function (cb) {
return cb !== callback;
});
};
/**
* Check if navigation should proceed. May prompt a user for input
* if any checkFns return messages. Returns true if the user wishes to
* navigate, otherwise false. If using this prior to calling
* `setNavigation`, you should call `setNavigation` with `force=true`
* to prevent duplicate dialogs being displayed to the user.
*
* @returns {Boolean} true if the user wishes to navigate, otherwise false.
*/
NavigationService.prototype.shouldNavigate = function () {
var doNotNavigate = this.shouldWarnBeforeNavigate();
return !doNotNavigate || this.$window.confirm(doNotNavigate);
};
/**
* Register a check function to be called before any navigation occurs.
* Check functions should return a human readable "message" if
* there are any reasons to prevent navigation. Otherwise, they should
* return falsy. Returns a function which can be called to remove the
* check function.
*
* @param {Function} checkFn a function to call before navigation occurs.
* @returns {Function} removeCheck call to remove check
*/
NavigationService.prototype.checkBeforeNavigation = function (checkFn) {
this.checks.push(checkFn);
return function removeCheck() {
this.checks = this.checks.filter(function (fn) {
return checkFn !== fn;
});
}.bind(this);
};
/**
* Private method to actually perform navigation.
*
* @private
*/
NavigationService.prototype.doNavigation = function (value) {
this.navigated = value;
this.callbacks.forEach(function (callback) {
callback(value);
});
};
/**
* Returns either a false value, or a string that should be displayed
* to the user before navigation is allowed.
*
* @private
*/
NavigationService.prototype.shouldWarnBeforeNavigate = function () {
var reasons = [];
this.checks.forEach(function (checkFn) {
var reason = checkFn();
if (reason) {
reasons.push(reason);
}
});
if (reasons.length) {
return reasons.join('\n');
}
return false;
};
/**
* Listener for window on before unload event-- will warn before
* navigation is allowed.
*
* @private
*/
NavigationService.prototype.onBeforeUnload = function () {
var shouldWarnBeforeNavigate = this.shouldWarnBeforeNavigate();
if (shouldWarnBeforeNavigate) {
return shouldWarnBeforeNavigate;
}
if (this.oldUnload) {
return this.oldUnload.apply(undefined, [].slice.apply(arguments));
}
};
return NavigationService;
} }
);
if (this.navigated === domainObject) {
return true;
}
var doNotNavigate = this.shouldWarnBeforeNavigate();
if (doNotNavigate && !this.$window.confirm(doNotNavigate)) {
return false;
}
this.doNavigation(domainObject);
return true;
};
/**
* Listen for changes in navigation. The passed callback will
* be invoked with the new domain object of navigation when
* this changes.
*
* @param {function} callback the callback to invoke when
* navigation state changes
*/
NavigationService.prototype.addListener = function (callback) {
this.callbacks.push(callback);
};
/**
* Stop listening for changes in navigation state.
*
* @param {function} callback the callback which should
* no longer be invoked when navigation state
* changes
*/
NavigationService.prototype.removeListener = function (callback) {
this.callbacks = this.callbacks.filter(function (cb) {
return cb !== callback;
});
};
/**
* Check if navigation should proceed. May prompt a user for input
* if any checkFns return messages. Returns true if the user wishes to
* navigate, otherwise false. If using this prior to calling
* `setNavigation`, you should call `setNavigation` with `force=true`
* to prevent duplicate dialogs being displayed to the user.
*
* @returns {Boolean} true if the user wishes to navigate, otherwise false.
*/
NavigationService.prototype.shouldNavigate = function () {
var doNotNavigate = this.shouldWarnBeforeNavigate();
return !doNotNavigate || this.$window.confirm(doNotNavigate);
};
/**
* Register a check function to be called before any navigation occurs.
* Check functions should return a human readable "message" if
* there are any reasons to prevent navigation. Otherwise, they should
* return falsy. Returns a function which can be called to remove the
* check function.
*
* @param {Function} checkFn a function to call before navigation occurs.
* @returns {Function} removeCheck call to remove check
*/
NavigationService.prototype.checkBeforeNavigation = function (checkFn) {
this.checks.push(checkFn);
return function removeCheck() {
this.checks = this.checks.filter(function (fn) {
return checkFn !== fn;
});
}.bind(this);
};
/**
* Private method to actually perform navigation.
*
* @private
*/
NavigationService.prototype.doNavigation = function (value) {
this.navigated = value;
this.callbacks.forEach(function (callback) {
callback(value);
});
};
/**
* Returns either a false value, or a string that should be displayed
* to the user before navigation is allowed.
*
* @private
*/
NavigationService.prototype.shouldWarnBeforeNavigate = function () {
var reasons = [];
this.checks.forEach(function (checkFn) {
var reason = checkFn();
if (reason) {
reasons.push(reason);
}
});
if (reasons.length) {
return reasons.join('\n');
}
return false;
};
/**
* Listener for window on before unload event-- will warn before
* navigation is allowed.
*
* @private
*/
NavigationService.prototype.onBeforeUnload = function () {
var shouldWarnBeforeNavigate = this.shouldWarnBeforeNavigate();
if (shouldWarnBeforeNavigate) {
return shouldWarnBeforeNavigate;
}
if (this.oldUnload) {
return this.oldUnload.apply(undefined, [].slice.apply(arguments));
}
};
export default NavigationService;

View File

@ -20,57 +20,63 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([], function () { /*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/** function OrphanNavigationHandler(throttle, topic, navigationService) {
* Navigates away from orphan objects whenever they are detected. var throttledCheckNavigation;
*
* An orphan object is an object whose apparent parent does not
* actually contain it. This may occur in certain circumstances, such
* as when persistence succeeds for a newly-created object but fails
* for its parent.
*
* @param throttle the `throttle` service
* @param topic the `topic` service
* @param navigationService the `navigationService`
* @constructor
*/
function OrphanNavigationHandler(throttle, topic, navigationService) {
var throttledCheckNavigation;
function getParent(domainObject) { function getParent(domainObject) {
var context = domainObject.getCapability('context'); var context = domainObject.getCapability('context');
return context.getParent(); return context.getParent();
}
function preventOrphanNavigation(domainObject) {
var parent = getParent(domainObject);
parent.useCapability('composition')
.then(function (composees) {
var isOrphan = composees.every(function (c) {
return c.getId() !== domainObject.getId();
});
if (isOrphan) {
parent.getCapability('action').perform('navigate');
}
});
}
function checkNavigation() {
var navigatedObject = navigationService.getNavigation();
if (navigatedObject && navigatedObject.hasCapability('context')) {
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
preventOrphanNavigation(navigatedObject);
}
}
}
throttledCheckNavigation = throttle(checkNavigation);
navigationService.addListener(throttledCheckNavigation);
topic('mutation').listen(throttledCheckNavigation);
} }
return OrphanNavigationHandler; function preventOrphanNavigation(domainObject) {
}); var parent = getParent(domainObject);
parent.useCapability('composition')
.then(function (composees) {
var isOrphan = composees.every(function (c) {
return c.getId() !== domainObject.getId();
});
if (isOrphan) {
parent.getCapability('action').perform('navigate');
}
});
}
function checkNavigation() {
var navigatedObject = navigationService.getNavigation();
if (navigatedObject && navigatedObject.hasCapability('context')) {
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
preventOrphanNavigation(navigatedObject);
}
}
}
throttledCheckNavigation = throttle(checkNavigation);
navigationService.addListener(throttledCheckNavigation);
topic('mutation').listen(throttledCheckNavigation);
}
export default OrphanNavigationHandler;

View File

@ -23,21 +23,42 @@
/** /**
* MCTIncudeSpec. Created by vwoeltje on 11/6/14. * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
*/ */
define( /*****************************************************************************
["../src/InspectorRegion"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (InspectorRegion) { * 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.
*****************************************************************************/
describe("The inspector region", function () { /**
var inspectorRegion; * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
*/
import InspectorRegion from '../src/InspectorRegion';
beforeEach(function () { describe("The inspector region", function () {
inspectorRegion = new InspectorRegion(); var inspectorRegion;
});
it("creates default region parts", function () { beforeEach(function () {
expect(inspectorRegion.regions.length).toBe(1); inspectorRegion = new InspectorRegion();
}); });
}); it("creates default region parts", function () {
} expect(inspectorRegion.regions.length).toBe(1);
); });
});

View File

@ -23,63 +23,83 @@
/** /**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14. * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/ */
define([ /*****************************************************************************
"../../src/navigation/NavigateAction" * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
NavigateAction * 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.
*****************************************************************************/
describe("The navigate action", function () { /**
var mockNavigationService, * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
mockDomainObject, */
action; import NavigateAction from '../../src/navigation/NavigateAction';
beforeEach(function () { describe("The navigate action", function () {
mockNavigationService = jasmine.createSpyObj( var mockNavigationService,
"navigationService", mockDomainObject,
[ action;
"shouldNavigate",
"setNavigation"
]
);
mockDomainObject = {}; beforeEach(function () {
mockNavigationService = jasmine.createSpyObj(
"navigationService",
[
"shouldNavigate",
"setNavigation"
]
);
action = new NavigateAction( mockDomainObject = {};
mockNavigationService,
{ domainObject: mockDomainObject }
);
});
it("sets navigation if it is allowed", function () {
mockNavigationService.shouldNavigate.and.returnValue(true);
return action.perform()
.then(function () {
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDomainObject, true);
});
});
it("does not set navigation if it is not allowed", function () {
mockNavigationService.shouldNavigate.and.returnValue(false);
var onSuccess = jasmine.createSpy('onSuccess');
return action.perform()
.then(onSuccess, function () {
expect(onSuccess).not.toHaveBeenCalled();
expect(mockNavigationService.setNavigation)
.not
.toHaveBeenCalledWith(mockDomainObject);
});
});
it("is only applicable when a domain object is in context", function () {
expect(NavigateAction.appliesTo({})).toBeFalsy();
expect(NavigateAction.appliesTo({
domainObject: mockDomainObject
})).toBeTruthy();
});
action = new NavigateAction(
mockNavigationService,
{ domainObject: mockDomainObject }
);
}); });
});
it("sets navigation if it is allowed", function () {
mockNavigationService.shouldNavigate.and.returnValue(true);
return action.perform()
.then(function () {
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDomainObject, true);
});
});
it("does not set navigation if it is not allowed", function () {
mockNavigationService.shouldNavigate.and.returnValue(false);
var onSuccess = jasmine.createSpy('onSuccess');
return action.perform()
.then(onSuccess, function () {
expect(onSuccess).not.toHaveBeenCalled();
expect(mockNavigationService.setNavigation)
.not
.toHaveBeenCalledWith(mockDomainObject);
});
});
it("is only applicable when a domain object is in context", function () {
expect(NavigateAction.appliesTo({})).toBeFalsy();
expect(NavigateAction.appliesTo({
domainObject: mockDomainObject
})).toBeTruthy();
});
});

View File

@ -23,66 +23,87 @@
/** /**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14. * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/ */
define( /*****************************************************************************
["../../src/navigation/NavigationService"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (NavigationService) { * 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.
*****************************************************************************/
describe("The navigation service", function () { /**
var $window, * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
navigationService; */
import NavigationService from '../../src/navigation/NavigationService';
beforeEach(function () { describe("The navigation service", function () {
$window = jasmine.createSpyObj('$window', ['confirm']); var $window,
navigationService = new NavigationService($window); navigationService;
});
it("stores navigation state", function () { beforeEach(function () {
var testObject = { someKey: 42 }, $window = jasmine.createSpyObj('$window', ['confirm']);
otherObject = { someKey: "some value" }; navigationService = new NavigationService($window);
expect(navigationService.getNavigation()) });
.toBeUndefined();
navigationService.setNavigation(testObject);
expect(navigationService.getNavigation())
.toBe(testObject);
expect(navigationService.getNavigation())
.toBe(testObject);
navigationService.setNavigation(otherObject);
expect(navigationService.getNavigation())
.toBe(otherObject);
});
it("notifies listeners on change", function () { it("stores navigation state", function () {
var testObject = { someKey: 42 }, var testObject = { someKey: 42 },
callback = jasmine.createSpy("callback"); otherObject = { someKey: "some value" };
expect(navigationService.getNavigation())
.toBeUndefined();
navigationService.setNavigation(testObject);
expect(navigationService.getNavigation())
.toBe(testObject);
expect(navigationService.getNavigation())
.toBe(testObject);
navigationService.setNavigation(otherObject);
expect(navigationService.getNavigation())
.toBe(otherObject);
});
navigationService.addListener(callback); it("notifies listeners on change", function () {
expect(callback).not.toHaveBeenCalled(); var testObject = { someKey: 42 },
callback = jasmine.createSpy("callback");
navigationService.setNavigation(testObject); navigationService.addListener(callback);
expect(callback).toHaveBeenCalledWith(testObject); expect(callback).not.toHaveBeenCalled();
});
it("does not notify listeners when no changes occur", function () { navigationService.setNavigation(testObject);
var testObject = { someKey: 42 }, expect(callback).toHaveBeenCalledWith(testObject);
callback = jasmine.createSpy("callback"); });
navigationService.addListener(callback); it("does not notify listeners when no changes occur", function () {
navigationService.setNavigation(testObject); var testObject = { someKey: 42 },
navigationService.setNavigation(testObject); callback = jasmine.createSpy("callback");
expect(callback.calls.count()).toEqual(1);
});
it("stops notifying listeners after removal", function () { navigationService.addListener(callback);
var testObject = { someKey: 42 }, navigationService.setNavigation(testObject);
callback = jasmine.createSpy("callback"); navigationService.setNavigation(testObject);
expect(callback.calls.count()).toEqual(1);
});
navigationService.addListener(callback); it("stops notifying listeners after removal", function () {
navigationService.removeListener(callback); var testObject = { someKey: 42 },
callback = jasmine.createSpy("callback");
navigationService.setNavigation(testObject); navigationService.addListener(callback);
expect(callback).not.toHaveBeenCalled(); navigationService.removeListener(callback);
});
}); navigationService.setNavigation(testObject);
} expect(callback).not.toHaveBeenCalled();
); });
});

View File

@ -20,163 +20,182 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
'../../src/navigation/OrphanNavigationHandler' * Open MCT, Copyright (c) 2014-2021, United States Government
], function (OrphanNavigationHandler) { * as represented by the Administrator of the National Aeronautics and Space
describe("OrphanNavigationHandler", function () { * Administration. All rights reserved.
var mockTopic, *
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import OrphanNavigationHandler from '../../src/navigation/OrphanNavigationHandler';
describe("OrphanNavigationHandler", function () {
var mockTopic,
mockThrottle,
mockMutationTopic,
mockNavigationService,
mockDomainObject,
mockParentObject,
mockContext,
mockActionCapability,
mockEditor,
testParentComposition,
testId,
mockThrottledFns;
beforeEach(function () {
testId = 'some-identifier';
mockThrottledFns = [];
mockTopic = jasmine.createSpy('topic');
mockThrottle = jasmine.createSpy('throttle');
mockNavigationService = jasmine.createSpyObj('navigationService', [
'getNavigation',
'addListener'
]);
mockMutationTopic = jasmine.createSpyObj('mutationTopic', [
'listen'
]);
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'hasCapability'
]);
mockParentObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'useCapability'
]);
mockContext = jasmine.createSpyObj('context', ['getParent']);
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
mockEditor = jasmine.createSpyObj('editor', ['isEditContextRoot']);
mockThrottle.and.callFake(function (fn) {
var mockThrottledFn =
jasmine.createSpy('throttled-' + mockThrottledFns.length);
mockThrottledFn.and.callFake(fn);
mockThrottledFns.push(mockThrottledFn);
return mockThrottledFn;
});
mockTopic.and.returnValue(mockMutationTopic);
mockDomainObject.getId.and.returnValue(testId);
mockDomainObject.getCapability.and.callFake(function (c) {
return {
context: mockContext,
editor: mockEditor
}[c];
});
mockDomainObject.hasCapability.and.callFake(function (c) {
return Boolean(mockDomainObject.getCapability(c));
});
mockParentObject.getCapability.and.callFake(function (c) {
return {
action: mockActionCapability
}[c];
});
testParentComposition = [];
mockParentObject.useCapability.and.returnValue(Promise.resolve(testParentComposition));
mockContext.getParent.and.returnValue(mockParentObject);
mockNavigationService.getNavigation.and.returnValue(mockDomainObject);
mockEditor.isEditContextRoot.and.returnValue(false);
return new OrphanNavigationHandler(
mockThrottle, mockThrottle,
mockMutationTopic, mockTopic,
mockNavigationService, mockNavigationService
mockDomainObject, );
mockParentObject,
mockContext,
mockActionCapability,
mockEditor,
testParentComposition,
testId,
mockThrottledFns;
beforeEach(function () {
testId = 'some-identifier';
mockThrottledFns = [];
mockTopic = jasmine.createSpy('topic');
mockThrottle = jasmine.createSpy('throttle');
mockNavigationService = jasmine.createSpyObj('navigationService', [
'getNavigation',
'addListener'
]);
mockMutationTopic = jasmine.createSpyObj('mutationTopic', [
'listen'
]);
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'hasCapability'
]);
mockParentObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'useCapability'
]);
mockContext = jasmine.createSpyObj('context', ['getParent']);
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
mockEditor = jasmine.createSpyObj('editor', ['isEditContextRoot']);
mockThrottle.and.callFake(function (fn) {
var mockThrottledFn =
jasmine.createSpy('throttled-' + mockThrottledFns.length);
mockThrottledFn.and.callFake(fn);
mockThrottledFns.push(mockThrottledFn);
return mockThrottledFn;
});
mockTopic.and.returnValue(mockMutationTopic);
mockDomainObject.getId.and.returnValue(testId);
mockDomainObject.getCapability.and.callFake(function (c) {
return {
context: mockContext,
editor: mockEditor
}[c];
});
mockDomainObject.hasCapability.and.callFake(function (c) {
return Boolean(mockDomainObject.getCapability(c));
});
mockParentObject.getCapability.and.callFake(function (c) {
return {
action: mockActionCapability
}[c];
});
testParentComposition = [];
mockParentObject.useCapability.and.returnValue(Promise.resolve(testParentComposition));
mockContext.getParent.and.returnValue(mockParentObject);
mockNavigationService.getNavigation.and.returnValue(mockDomainObject);
mockEditor.isEditContextRoot.and.returnValue(false);
return new OrphanNavigationHandler(
mockThrottle,
mockTopic,
mockNavigationService
);
});
it("listens for mutation with a throttled function", function () {
expect(mockMutationTopic.listen)
.toHaveBeenCalledWith(jasmine.any(Function));
expect(mockThrottledFns.indexOf(
mockMutationTopic.listen.calls.mostRecent().args[0]
)).not.toEqual(-1);
});
it("listens for navigation changes with a throttled function", function () {
expect(mockNavigationService.addListener)
.toHaveBeenCalledWith(jasmine.any(Function));
expect(mockThrottledFns.indexOf(
mockNavigationService.addListener.calls.mostRecent().args[0]
)).not.toEqual(-1);
});
[false, true].forEach(function (isOrphan) {
var prefix = isOrphan ? "" : "non-";
describe("for " + prefix + "orphan objects", function () {
beforeEach(function () {
if (!isOrphan) {
testParentComposition.push(mockDomainObject);
}
});
[false, true].forEach(function (isEditRoot) {
var caseName = isEditRoot
? "that are being edited" : "that are not being edited";
function itNavigatesAsExpected() {
if (isOrphan && !isEditRoot) {
it("navigates to the parent", function () {
return Promise.resolve().then(function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
} else {
it("does nothing", function () {
return Promise.resolve().then(function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
});
}
}
describe(caseName, function () {
beforeEach(function () {
mockEditor.isEditContextRoot.and.returnValue(isEditRoot);
});
describe("when navigation changes", function () {
beforeEach(function () {
mockNavigationService.addListener.calls.mostRecent()
.args[0](mockDomainObject);
});
itNavigatesAsExpected();
});
describe("when mutation occurs", function () {
beforeEach(function () {
mockMutationTopic.listen.calls.mostRecent()
.args[0](mockParentObject);
});
itNavigatesAsExpected();
});
});
});
});
});
}); });
});
it("listens for mutation with a throttled function", function () {
expect(mockMutationTopic.listen)
.toHaveBeenCalledWith(jasmine.any(Function));
expect(mockThrottledFns.indexOf(
mockMutationTopic.listen.calls.mostRecent().args[0]
)).not.toEqual(-1);
});
it("listens for navigation changes with a throttled function", function () {
expect(mockNavigationService.addListener)
.toHaveBeenCalledWith(jasmine.any(Function));
expect(mockThrottledFns.indexOf(
mockNavigationService.addListener.calls.mostRecent().args[0]
)).not.toEqual(-1);
});
[false, true].forEach(function (isOrphan) {
var prefix = isOrphan ? "" : "non-";
describe("for " + prefix + "orphan objects", function () {
beforeEach(function () {
if (!isOrphan) {
testParentComposition.push(mockDomainObject);
}
});
[false, true].forEach(function (isEditRoot) {
var caseName = isEditRoot
? "that are being edited" : "that are not being edited";
function itNavigatesAsExpected() {
if (isOrphan && !isEditRoot) {
it("navigates to the parent", function () {
return Promise.resolve().then(function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
} else {
it("does nothing", function () {
return Promise.resolve().then(function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
});
}
}
describe(caseName, function () {
beforeEach(function () {
mockEditor.isEditContextRoot.and.returnValue(isEditRoot);
});
describe("when navigation changes", function () {
beforeEach(function () {
mockNavigationService.addListener.calls.mostRecent()
.args[0](mockDomainObject);
});
itNavigatesAsExpected();
});
describe("when mutation occurs", function () {
beforeEach(function () {
mockMutationTopic.listen.calls.mostRecent()
.args[0](mockParentObject);
});
itNavigatesAsExpected();
});
});
});
});
});
});

View File

@ -20,93 +20,102 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/DialogService", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/OverlayService", * as represented by the Administrator of the National Aeronautics and Space
"./res/templates/overlay-dialog.html", * Administration. All rights reserved.
"./res/templates/overlay-options.html", *
"./res/templates/dialog.html", * Open MCT is licensed under the Apache License, Version 2.0 (the
"./res/templates/overlay-blocking-message.html", * "License"); you may not use this file except in compliance with the License.
"./res/templates/message.html", * You may obtain a copy of the License at
"./res/templates/notification-message.html", * http://www.apache.org/licenses/LICENSE-2.0.
"./res/templates/overlay-message-list.html", *
"./res/templates/overlay.html" * Unless required by applicable law or agreed to in writing, software
], function ( * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
DialogService, * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
OverlayService, * License for the specific language governing permissions and limitations
overlayDialogTemplate, * under the License.
overlayOptionsTemplate, *
dialogTemplate, * Open MCT includes source code licensed under additional open source
overlayBlockingMessageTemplate, * licenses. See the Open Source Licenses file (LICENSES.md) included with
messageTemplate, * this source code distribution or the Licensing information page available
notificationMessageTemplate, * at runtime from the About dialog for additional information.
overlayMessageListTemplate, *****************************************************************************/
overlayTemplate
) {
return { import DialogService from './src/DialogService';
name: "platform/commonUI/dialog",
definition: { import OverlayService from './src/OverlayService';
"extensions": { import overlayDialogTemplate from './res/templates/overlay-dialog.html';
"services": [ import overlayOptionsTemplate from './res/templates/overlay-options.html';
{ import dialogTemplate from './res/templates/dialog.html';
"key": "dialogService", import overlayBlockingMessageTemplate from './res/templates/overlay-blocking-message.html';
"implementation": DialogService, import messageTemplate from './res/templates/message.html';
"depends": [ import notificationMessageTemplate from './res/templates/notification-message.html';
"overlayService", import overlayMessageListTemplate from './res/templates/overlay-message-list.html';
"$q", import overlayTemplate from './res/templates/overlay.html';
"$log",
"$document" export default {
] name: "platform/commonUI/dialog",
}, definition: {
{ "extensions": {
"key": "overlayService", "services": [
"implementation": OverlayService, {
"depends": [ "key": "dialogService",
"$document", "implementation": DialogService,
"$compile", "depends": [
"$rootScope", "overlayService",
"$timeout" "$q",
] "$log",
} "$document"
], ]
"templates": [ },
{ {
"key": "overlay-dialog", "key": "overlayService",
"template": overlayDialogTemplate "implementation": OverlayService,
}, "depends": [
{ "$document",
"key": "overlay-options", "$compile",
"template": overlayOptionsTemplate "$rootScope",
}, "$timeout"
{ ]
"key": "form-dialog", }
"template": dialogTemplate ],
}, "templates": [
{ {
"key": "overlay-blocking-message", "key": "overlay-dialog",
"template": overlayBlockingMessageTemplate "template": overlayDialogTemplate
}, },
{ {
"key": "message", "key": "overlay-options",
"template": messageTemplate "template": overlayOptionsTemplate
}, },
{ {
"key": "notification-message", "key": "form-dialog",
"template": notificationMessageTemplate "template": dialogTemplate
}, },
{ {
"key": "overlay-message-list", "key": "overlay-blocking-message",
"template": overlayMessageListTemplate "template": overlayBlockingMessageTemplate
} },
], {
"containers": [ "key": "message",
{ "template": messageTemplate
"key": "overlay", },
"template": overlayTemplate {
} "key": "notification-message",
] "template": notificationMessageTemplate
} },
{
"key": "overlay-message-list",
"template": overlayMessageListTemplate
}
],
"containers": [
{
"key": "overlay",
"template": overlayTemplate
}
]
} }
}; }
}); };

View File

@ -25,247 +25,262 @@
* launch dialogs for user input & notifications. * launch dialogs for user input & notifications.
* @namespace platform/commonUI/dialog * @namespace platform/commonUI/dialog
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
/** * Administration. All rights reserved.
* The dialog service is responsible for handling window-modal *
* communication with the user, such as displaying forms for user * Open MCT is licensed under the Apache License, Version 2.0 (the
* input. * "License"); you may not use this file except in compliance with the License.
* @memberof platform/commonUI/dialog * You may obtain a copy of the License at
* @constructor * http://www.apache.org/licenses/LICENSE-2.0.
*/ *
function DialogService(overlayService, $q, $log, $document) { * Unless required by applicable law or agreed to in writing, software
this.overlayService = overlayService; * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
this.$q = $q; * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
this.$log = $log; * License for the specific language governing permissions and limitations
this.activeOverlay = undefined; * 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.
*****************************************************************************/
this.findBody = function () { /**
return $document.find('body'); * This bundle implements the dialog service, which can be used to
}; * launch dialogs for user input & notifications.
} * @namespace platform/commonUI/dialog
*/
function DialogService(overlayService, $q, $log, $document) {
this.overlayService = overlayService;
this.$q = $q;
this.$log = $log;
this.activeOverlay = undefined;
/** this.findBody = function () {
* @private return $document.find('body');
*/ };
DialogService.prototype.dismissOverlay = function (overlay) { }
//Dismiss the overlay
overlay.dismiss();
//If dialog is the current active one, dismiss it /**
if (overlay === this.activeOverlay) { * @private
this.activeOverlay = undefined; */
} DialogService.prototype.dismissOverlay = function (overlay) {
}; //Dismiss the overlay
overlay.dismiss();
DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) { //If dialog is the current active one, dismiss it
// We will return this result as a promise, because user if (overlay === this.activeOverlay) {
// input is asynchronous. this.activeOverlay = undefined;
var deferred = this.$q.defer(),
self = this,
overlay,
handleEscKeydown;
// Confirm function; this will be passed in to the
// overlay-dialog template and associated with a
// OK button click
function confirm(value) {
// Pass along the result
deferred.resolve(resultGetter ? resultGetter() : value);
self.dismissOverlay(overlay);
}
// Cancel function; this will be passed in to the
// overlay-dialog template and associated with a
// Cancel or X button click
function cancel() {
deferred.reject();
self.findBody().off('keydown', handleEscKeydown);
self.dismissOverlay(overlay);
}
handleEscKeydown = function (event) {
if (event.keyCode === 27) {
cancel();
}
};
// Add confirm/cancel callbacks
model.confirm = confirm;
model.cancel = cancel;
this.findBody().on('keydown', handleEscKeydown);
if (this.canShowDialog(model)) {
// Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM
overlay = this.activeOverlay = this.overlayService.createOverlay(
key,
model,
typeClass || "t-dialog"
);
} else {
deferred.reject();
}
return deferred.promise;
};
/**
* Request user input via a window-modal dialog.
*
* @param {FormModel} formModel a description of the form
* to be shown (see platform/forms)
* @param {object} value the initial state of the form
* @returns {Promise} a promise for the form value that the
* user has supplied; this may be rejected if
* user input cannot be obtained (for instance,
* because the user cancelled the dialog)
*/
DialogService.prototype.getUserInput = function (formModel, value) {
var overlayModel = {
title: formModel.name,
message: formModel.message,
structure: formModel,
value: value
};
// Provide result from the model
function resultGetter() {
return overlayModel.value;
}
// Show the overlay-dialog
return this.getDialogResponse(
"overlay-dialog",
overlayModel,
resultGetter
);
};
/**
* Request that the user chooses from a set of options,
* which will be shown as buttons.
*
* @param dialogModel a description of the dialog to show
* @return {Promise} a promise for the user's choice
*/
DialogService.prototype.getUserChoice = function (dialogModel) {
// Show the overlay-options dialog
return this.getDialogResponse(
"overlay-options",
{ dialog: dialogModel }
);
};
/**
* Tests if a dialog can be displayed. A modal dialog may only be
* displayed if one is not already visible.
* Will log a warning message if it can't display a dialog.
* @returns {boolean} true if dialog is currently visible, false
* otherwise
*/
DialogService.prototype.canShowDialog = function (dialogModel) {
if (this.activeOverlay) {
// Only one dialog should be shown at a time.
// The application design should be such that
// we never even try to do this.
this.$log.warn([
"Dialog already showing; ",
"unable to show ",
dialogModel.title
].join(""));
return false;
} else {
return true;
}
};
/**
* A user action that can be performed from a blocking dialog. These
* actions will be rendered as buttons within a blocking dialog.
*
* @typedef DialogOption
* @property {string} label a label to be displayed as the button
* text for this action
* @property {function} callback a function to be called when the
* button is clicked
*/
/**
* @typedef DialogHandle
* @property {function} dismiss a function to dismiss the given dialog
*/
/**
* A description of the model options that may be passed to the
* showBlockingMessage method. Note that the DialogModel described
* here is shared with the Notifications framework.
* @see NotificationService
*
* @typedef DialogModel
* @property {string} title the title to use for the dialog
* @property {string} severity the severity level of this message.
* These are defined in a bundle constant with key 'dialogSeverity'
* @property {string} hint the 'hint' message to show below the title
* @property {string} actionText text that indicates a current action,
* shown above a progress bar to indicate what's happening.
* @property {number} progress a percentage value (1-100)
* indicating the completion of the blocking task
* @property {boolean} delay adds a brief delay before loading
* the dialog. Useful for removing the dialog flicker when the
* conditions for displaying the dialog change rapidly.
* @property {string} progressText the message to show below a
* progress bar to indicate progress. For example, this might be
* used to indicate time remaining, or items still to process.
* @property {boolean} unknownProgress some tasks may be
* impossible to provide an estimate for. Providing a true value for
* this attribute will indicate to the user that the progress and
* duration cannot be estimated.
* @property {DialogOption} primaryOption an action that will
* be added to the dialog as a button. The primary action can be
* used as the suggested course of action for the user. Making it
* distinct from other actions allows it to be styled differently,
* and treated preferentially in banner mode.
* @property {DialogOption[]} options a list of actions that will
* be added to the dialog as buttons.
*/
/**
* Displays a blocking (modal) dialog. This dialog can be used for
* displaying messages that require the user's
* immediate attention. The message may include an indication of
* progress, as well as a series of actions that
* the user can take if necessary
* @param {DialogModel} dialogModel defines options for the dialog
* @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
* @returns {boolean | {DialogHandle}}
*/
DialogService.prototype.showBlockingMessage = function (dialogModel) {
if (this.canShowDialog(dialogModel)) {
// Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM
var self = this,
overlay = this.overlayService.createOverlay(
"overlay-blocking-message",
dialogModel,
"t-dialog-sm"
);
this.activeOverlay = overlay;
return {
dismiss: function () {
self.dismissOverlay(overlay);
}
};
} else {
return false;
}
};
return DialogService;
} }
); };
DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
// We will return this result as a promise, because user
// input is asynchronous.
var deferred = this.$q.defer(),
self = this,
overlay,
handleEscKeydown;
// Confirm function; this will be passed in to the
// overlay-dialog template and associated with a
// OK button click
function confirm(value) {
// Pass along the result
deferred.resolve(resultGetter ? resultGetter() : value);
self.dismissOverlay(overlay);
}
// Cancel function; this will be passed in to the
// overlay-dialog template and associated with a
// Cancel or X button click
function cancel() {
deferred.reject();
self.findBody().off('keydown', handleEscKeydown);
self.dismissOverlay(overlay);
}
handleEscKeydown = function (event) {
if (event.keyCode === 27) {
cancel();
}
};
// Add confirm/cancel callbacks
model.confirm = confirm;
model.cancel = cancel;
this.findBody().on('keydown', handleEscKeydown);
if (this.canShowDialog(model)) {
// Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM
overlay = this.activeOverlay = this.overlayService.createOverlay(
key,
model,
typeClass || "t-dialog"
);
} else {
deferred.reject();
}
return deferred.promise;
};
/**
* Request user input via a window-modal dialog.
*
* @param {FormModel} formModel a description of the form
* to be shown (see platform/forms)
* @param {object} value the initial state of the form
* @returns {Promise} a promise for the form value that the
* user has supplied; this may be rejected if
* user input cannot be obtained (for instance,
* because the user cancelled the dialog)
*/
DialogService.prototype.getUserInput = function (formModel, value) {
var overlayModel = {
title: formModel.name,
message: formModel.message,
structure: formModel,
value: value
};
// Provide result from the model
function resultGetter() {
return overlayModel.value;
}
// Show the overlay-dialog
return this.getDialogResponse(
"overlay-dialog",
overlayModel,
resultGetter
);
};
/**
* Request that the user chooses from a set of options,
* which will be shown as buttons.
*
* @param dialogModel a description of the dialog to show
* @return {Promise} a promise for the user's choice
*/
DialogService.prototype.getUserChoice = function (dialogModel) {
// Show the overlay-options dialog
return this.getDialogResponse(
"overlay-options",
{ dialog: dialogModel }
);
};
/**
* Tests if a dialog can be displayed. A modal dialog may only be
* displayed if one is not already visible.
* Will log a warning message if it can't display a dialog.
* @returns {boolean} true if dialog is currently visible, false
* otherwise
*/
DialogService.prototype.canShowDialog = function (dialogModel) {
if (this.activeOverlay) {
// Only one dialog should be shown at a time.
// The application design should be such that
// we never even try to do this.
this.$log.warn([
"Dialog already showing; ",
"unable to show ",
dialogModel.title
].join(""));
return false;
} else {
return true;
}
};
/**
* A user action that can be performed from a blocking dialog. These
* actions will be rendered as buttons within a blocking dialog.
*
* @typedef DialogOption
* @property {string} label a label to be displayed as the button
* text for this action
* @property {function} callback a function to be called when the
* button is clicked
*/
/**
* @typedef DialogHandle
* @property {function} dismiss a function to dismiss the given dialog
*/
/**
* A description of the model options that may be passed to the
* showBlockingMessage method. Note that the DialogModel described
* here is shared with the Notifications framework.
* @see NotificationService
*
* @typedef DialogModel
* @property {string} title the title to use for the dialog
* @property {string} severity the severity level of this message.
* These are defined in a bundle constant with key 'dialogSeverity'
* @property {string} hint the 'hint' message to show below the title
* @property {string} actionText text that indicates a current action,
* shown above a progress bar to indicate what's happening.
* @property {number} progress a percentage value (1-100)
* indicating the completion of the blocking task
* @property {boolean} delay adds a brief delay before loading
* the dialog. Useful for removing the dialog flicker when the
* conditions for displaying the dialog change rapidly.
* @property {string} progressText the message to show below a
* progress bar to indicate progress. For example, this might be
* used to indicate time remaining, or items still to process.
* @property {boolean} unknownProgress some tasks may be
* impossible to provide an estimate for. Providing a true value for
* this attribute will indicate to the user that the progress and
* duration cannot be estimated.
* @property {DialogOption} primaryOption an action that will
* be added to the dialog as a button. The primary action can be
* used as the suggested course of action for the user. Making it
* distinct from other actions allows it to be styled differently,
* and treated preferentially in banner mode.
* @property {DialogOption[]} options a list of actions that will
* be added to the dialog as buttons.
*/
/**
* Displays a blocking (modal) dialog. This dialog can be used for
* displaying messages that require the user's
* immediate attention. The message may include an indication of
* progress, as well as a series of actions that
* the user can take if necessary
* @param {DialogModel} dialogModel defines options for the dialog
* @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
* @returns {boolean | {DialogHandle}}
*/
DialogService.prototype.showBlockingMessage = function (dialogModel) {
if (this.canShowDialog(dialogModel)) {
// Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM
var self = this,
overlay = this.overlayService.createOverlay(
"overlay-blocking-message",
dialogModel,
"t-dialog-sm"
);
this.activeOverlay = overlay;
return {
dismiss: function () {
self.dismissOverlay(overlay);
}
};
} else {
return false;
}
};
export default DialogService;

View File

@ -20,94 +20,108 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
// Template to inject into the DOM to show the dialog; really just points to var TEMPLATE = '<mct-include ng-model="overlay" key="key" ng-class="typeClass"></mct-include>';
// the a specific template that can be included via mct-include
var TEMPLATE = '<mct-include ng-model="overlay" key="key" ng-class="typeClass"></mct-include>';
/** /**
* The OverlayService is responsible for pre-pending templates to * The OverlayService is responsible for pre-pending templates to
* the body of the document, which is useful for displaying templates * the body of the document, which is useful for displaying templates
* which need to block the full screen. * which need to block the full screen.
* *
* This is intended to be used by the DialogService; by design, it * This is intended to be used by the DialogService; by design, it
* does not have any protections in place to prevent multiple overlays * does not have any protections in place to prevent multiple overlays
* from being shown at once. (The DialogService does have these * from being shown at once. (The DialogService does have these
* protections, and should be used for most overlay-type interactions, * protections, and should be used for most overlay-type interactions,
* particularly where a multiple-overlay effect is not specifically * particularly where a multiple-overlay effect is not specifically
* desired). * desired).
* *
* @memberof platform/commonUI/dialog * @memberof platform/commonUI/dialog
* @constructor * @constructor
*/ */
function OverlayService($document, $compile, $rootScope, $timeout) { function OverlayService($document, $compile, $rootScope, $timeout) {
this.$compile = $compile; this.$compile = $compile;
this.$timeout = $timeout; this.$timeout = $timeout;
// Don't include $document and $rootScope directly; // Don't include $document and $rootScope directly;
// avoids https://docs.angularjs.org/error/ng/cpws // avoids https://docs.angularjs.org/error/ng/cpws
this.findBody = function () { this.findBody = function () {
return $document.find('body'); return $document.find('body');
}; };
this.newScope = function () { this.newScope = function () {
return $rootScope.$new(); return $rootScope.$new();
}; };
} }
/** /**
* Add a new overlay to the document. This will be * Add a new overlay to the document. This will be
* prepended to the document body; the overlay's * prepended to the document body; the overlay's
* template (as pointed to by the `key` argument) is * template (as pointed to by the `key` argument) is
* responsible for having a useful z-order, and for * responsible for having a useful z-order, and for
* blocking user interactions if appropriate. * blocking user interactions if appropriate.
* *
* @param {string} key the symbolic key which identifies * @param {string} key the symbolic key which identifies
* the template of the overlay to be shown * the template of the overlay to be shown
* @param {object} overlayModel the model to pass to the * @param {object} overlayModel the model to pass to the
* included overlay template (this will be passed * included overlay template (this will be passed
* in via ng-model) * in via ng-model)
* @param {string} typeClass the element class to use in rendering * @param {string} typeClass the element class to use in rendering
* the overlay. Can be specified to provide custom styling of * the overlay. Can be specified to provide custom styling of
* overlays * overlays
*/ */
OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) { OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) {
// Create a new scope for this overlay // Create a new scope for this overlay
var scope = this.newScope(), var scope = this.newScope(),
element; element;
// Stop showing the overlay; additionally, release the scope // Stop showing the overlay; additionally, release the scope
// that it uses. // that it uses.
function dismiss() { function dismiss() {
scope.$destroy(); scope.$destroy();
element.remove(); element.remove();
}
// If no model is supplied, just fill in a default "cancel"
overlayModel = overlayModel || { cancel: dismiss };
// Populate the scope; will be passed directly to the template
scope.overlay = overlayModel;
scope.key = key;
scope.typeClass = typeClass || 't-dialog';
this.$timeout(() => {
// Create the overlay element and add it to the document's body
element = this.$compile(TEMPLATE)(scope);
// Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
// multiple overlays with the same z-index are active.
this.findBody().append(element);
});
return {
dismiss: dismiss
};
};
return OverlayService;
} }
);
// If no model is supplied, just fill in a default "cancel"
overlayModel = overlayModel || { cancel: dismiss };
// Populate the scope; will be passed directly to the template
scope.overlay = overlayModel;
scope.key = key;
scope.typeClass = typeClass || 't-dialog';
this.$timeout(() => {
// Create the overlay element and add it to the document's body
element = this.$compile(TEMPLATE)(scope);
// Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
// multiple overlays with the same z-index are active.
this.findBody().append(element);
});
return {
dismiss: dismiss
};
};
export default OverlayService;

View File

@ -23,192 +23,213 @@
/** /**
* MCTIncudeSpec. Created by vwoeltje on 11/6/14. * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
*/ */
define( /*****************************************************************************
["../src/DialogService"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (DialogService) { * 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.
*****************************************************************************/
describe("The dialog service", function () { /**
var mockOverlayService, * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
mockQ, */
mockLog, import DialogService from '../src/DialogService';
mockOverlay,
mockDeferred,
mockDocument,
mockBody,
dialogService;
beforeEach(function () { describe("The dialog service", function () {
mockOverlayService = jasmine.createSpyObj( var mockOverlayService,
"overlayService", mockQ,
["createOverlay"] mockLog,
); mockOverlay,
mockQ = jasmine.createSpyObj( mockDeferred,
"$q", mockDocument,
["defer"] mockBody,
); dialogService;
mockLog = jasmine.createSpyObj(
"$log",
["warn", "info", "debug"]
);
mockOverlay = jasmine.createSpyObj(
"overlay",
["dismiss"]
);
mockDeferred = jasmine.createSpyObj(
"deferred",
["resolve", "reject"]
);
mockDocument = jasmine.createSpyObj(
"$document",
["find"]
);
mockBody = jasmine.createSpyObj('body', ['on', 'off']);
mockDocument.find.and.returnValue(mockBody);
mockDeferred.promise = "mock promise"; beforeEach(function () {
mockOverlayService = jasmine.createSpyObj(
"overlayService",
["createOverlay"]
);
mockQ = jasmine.createSpyObj(
"$q",
["defer"]
);
mockLog = jasmine.createSpyObj(
"$log",
["warn", "info", "debug"]
);
mockOverlay = jasmine.createSpyObj(
"overlay",
["dismiss"]
);
mockDeferred = jasmine.createSpyObj(
"deferred",
["resolve", "reject"]
);
mockDocument = jasmine.createSpyObj(
"$document",
["find"]
);
mockBody = jasmine.createSpyObj('body', ['on', 'off']);
mockDocument.find.and.returnValue(mockBody);
mockQ.defer.and.returnValue(mockDeferred); mockDeferred.promise = "mock promise";
mockOverlayService.createOverlay.and.returnValue(mockOverlay);
dialogService = new DialogService( mockQ.defer.and.returnValue(mockDeferred);
mockOverlayService, mockOverlayService.createOverlay.and.returnValue(mockOverlay);
mockQ,
mockLog,
mockDocument
);
});
it("adds an overlay when user input is requested", function () { dialogService = new DialogService(
dialogService.getUserInput({}, {}); mockOverlayService,
expect(mockOverlayService.createOverlay).toHaveBeenCalled(); mockQ,
}); mockLog,
mockDocument
);
});
it("allows user input to be canceled", function () { it("adds an overlay when user input is requested", function () {
dialogService.getUserInput({}, { someKey: "some value" }); dialogService.getUserInput({}, {});
mockOverlayService.createOverlay.calls.mostRecent().args[1].cancel(); expect(mockOverlayService.createOverlay).toHaveBeenCalled();
expect(mockDeferred.reject).toHaveBeenCalled(); });
expect(mockDeferred.resolve).not.toHaveBeenCalled();
});
it("passes back the result of user input when confirmed", function () { it("allows user input to be canceled", function () {
var value = { someKey: 42 }; dialogService.getUserInput({}, { someKey: "some value" });
dialogService.getUserInput({}, value); mockOverlayService.createOverlay.calls.mostRecent().args[1].cancel();
mockOverlayService.createOverlay.calls.mostRecent().args[1].confirm(); expect(mockDeferred.reject).toHaveBeenCalled();
expect(mockDeferred.reject).not.toHaveBeenCalled(); expect(mockDeferred.resolve).not.toHaveBeenCalled();
expect(mockDeferred.resolve).toHaveBeenCalledWith(value); });
});
it("logs a warning when a dialog is already showing", function () { it("passes back the result of user input when confirmed", function () {
dialogService.getUserInput({}, {}); var value = { someKey: 42 };
expect(mockLog.warn).not.toHaveBeenCalled(); dialogService.getUserInput({}, value);
dialogService.getUserInput({}, {}); mockOverlayService.createOverlay.calls.mostRecent().args[1].confirm();
expect(mockLog.warn).toHaveBeenCalled(); expect(mockDeferred.reject).not.toHaveBeenCalled();
expect(mockDeferred.reject).toHaveBeenCalled(); expect(mockDeferred.resolve).toHaveBeenCalledWith(value);
}); });
it("can show multiple dialogs if prior ones are dismissed", function () { it("logs a warning when a dialog is already showing", function () {
dialogService.getUserInput({}, {}); dialogService.getUserInput({}, {});
expect(mockLog.warn).not.toHaveBeenCalled(); expect(mockLog.warn).not.toHaveBeenCalled();
mockOverlayService.createOverlay.calls.mostRecent().args[1].confirm(); dialogService.getUserInput({}, {});
dialogService.getUserInput({}, {}); expect(mockLog.warn).toHaveBeenCalled();
expect(mockLog.warn).not.toHaveBeenCalled(); expect(mockDeferred.reject).toHaveBeenCalled();
expect(mockDeferred.reject).not.toHaveBeenCalled(); });
});
it("provides an options dialogs", function () { it("can show multiple dialogs if prior ones are dismissed", function () {
var dialogModel = {}; dialogService.getUserInput({}, {});
dialogService.getUserChoice(dialogModel); expect(mockLog.warn).not.toHaveBeenCalled();
expect(mockOverlayService.createOverlay).toHaveBeenCalledWith( mockOverlayService.createOverlay.calls.mostRecent().args[1].confirm();
'overlay-options', dialogService.getUserInput({}, {});
{ expect(mockLog.warn).not.toHaveBeenCalled();
dialog: dialogModel, expect(mockDeferred.reject).not.toHaveBeenCalled();
confirm: jasmine.any(Function), });
cancel: jasmine.any(Function)
},
't-dialog'
);
});
it("invokes the overlay service with the correct parameters when" it("provides an options dialogs", function () {
+ " a blocking dialog is requested", function () { var dialogModel = {};
var dialogModel = {}; dialogService.getUserChoice(dialogModel);
expect(dialogService.showBlockingMessage(dialogModel)).not.toBe(false); expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
expect(mockOverlayService.createOverlay).toHaveBeenCalledWith( 'overlay-options',
"overlay-blocking-message", {
dialogModel, dialog: dialogModel,
"t-dialog-sm" confirm: jasmine.any(Function),
); cancel: jasmine.any(Function)
}); },
't-dialog'
);
});
it("adds a keydown event listener to the body", function () { it("invokes the overlay service with the correct parameters when"
dialogService.getUserInput({}, {}); + " a blocking dialog is requested", function () {
expect(mockDocument.find).toHaveBeenCalledWith("body"); var dialogModel = {};
expect(mockBody.on).toHaveBeenCalledWith("keydown", jasmine.any(Function)); expect(dialogService.showBlockingMessage(dialogModel)).not.toBe(false);
}); expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
"overlay-blocking-message",
dialogModel,
"t-dialog-sm"
);
});
it("destroys the event listener when the dialog is cancelled", function () { it("adds a keydown event listener to the body", function () {
dialogService.getUserInput({}, {}); dialogService.getUserInput({}, {});
mockOverlayService.createOverlay.calls.mostRecent().args[1].cancel(); expect(mockDocument.find).toHaveBeenCalledWith("body");
expect(mockBody.off).toHaveBeenCalledWith("keydown", jasmine.any(Function)); expect(mockBody.on).toHaveBeenCalledWith("keydown", jasmine.any(Function));
}); });
it("cancels the dialog when an escape keydown event is triggered", function () { it("destroys the event listener when the dialog is cancelled", function () {
dialogService.getUserInput({}, {}); dialogService.getUserInput({}, {});
mockBody.on.calls.mostRecent().args[1]({ mockOverlayService.createOverlay.calls.mostRecent().args[1].cancel();
keyCode: 27 expect(mockBody.off).toHaveBeenCalledWith("keydown", jasmine.any(Function));
}); });
expect(mockDeferred.reject).toHaveBeenCalled();
expect(mockDeferred.resolve).not.toHaveBeenCalled();
});
it("ignores non escape keydown events", function () {
dialogService.getUserInput({}, {});
mockBody.on.calls.mostRecent().args[1]({
keyCode: 13
});
expect(mockDeferred.reject).not.toHaveBeenCalled();
expect(mockDeferred.resolve).not.toHaveBeenCalled();
});
describe("the blocking message dialog", function () {
var dialogModel = {};
var dialogHandle;
beforeEach(function () {
dialogHandle = dialogService.showBlockingMessage(dialogModel);
});
it("returns a handle to the dialog", function () {
expect(dialogHandle).not.toBe(undefined);
});
it("dismissing the dialog dismisses the overlay", function () {
dialogHandle.dismiss();
expect(mockOverlay.dismiss).toHaveBeenCalled();
});
it("individual dialogs can be dismissed", function () {
var secondDialogHandle,
secondMockOverlay;
dialogHandle.dismiss();
secondMockOverlay = jasmine.createSpyObj(
"overlay",
["dismiss"]
);
mockOverlayService.createOverlay.and.returnValue(secondMockOverlay);
secondDialogHandle = dialogService.showBlockingMessage(dialogModel);
//Dismiss the first dialog. It should only dismiss if it
// is active
dialogHandle.dismiss();
expect(secondMockOverlay.dismiss).not.toHaveBeenCalled();
secondDialogHandle.dismiss();
expect(secondMockOverlay.dismiss).toHaveBeenCalled();
});
});
it("cancels the dialog when an escape keydown event is triggered", function () {
dialogService.getUserInput({}, {});
mockBody.on.calls.mostRecent().args[1]({
keyCode: 27
}); });
} expect(mockDeferred.reject).toHaveBeenCalled();
); expect(mockDeferred.resolve).not.toHaveBeenCalled();
});
it("ignores non escape keydown events", function () {
dialogService.getUserInput({}, {});
mockBody.on.calls.mostRecent().args[1]({
keyCode: 13
});
expect(mockDeferred.reject).not.toHaveBeenCalled();
expect(mockDeferred.resolve).not.toHaveBeenCalled();
});
describe("the blocking message dialog", function () {
var dialogModel = {};
var dialogHandle;
beforeEach(function () {
dialogHandle = dialogService.showBlockingMessage(dialogModel);
});
it("returns a handle to the dialog", function () {
expect(dialogHandle).not.toBe(undefined);
});
it("dismissing the dialog dismisses the overlay", function () {
dialogHandle.dismiss();
expect(mockOverlay.dismiss).toHaveBeenCalled();
});
it("individual dialogs can be dismissed", function () {
var secondDialogHandle,
secondMockOverlay;
dialogHandle.dismiss();
secondMockOverlay = jasmine.createSpyObj(
"overlay",
["dismiss"]
);
mockOverlayService.createOverlay.and.returnValue(secondMockOverlay);
secondDialogHandle = dialogService.showBlockingMessage(dialogModel);
//Dismiss the first dialog. It should only dismiss if it
// is active
dialogHandle.dismiss();
expect(secondMockOverlay.dismiss).not.toHaveBeenCalled();
secondDialogHandle.dismiss();
expect(secondMockOverlay.dismiss).toHaveBeenCalled();
});
});
});

View File

@ -23,82 +23,103 @@
/** /**
* MCTIncudeSpec. Created by vwoeltje on 11/6/14. * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
*/ */
define( /*****************************************************************************
["../src/OverlayService"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (OverlayService) { * 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.
*****************************************************************************/
describe("The overlay service", function () { /**
var mockDocument, * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
mockCompile, */
mockRootScope, import OverlayService from '../src/OverlayService';
mockBody,
mockTemplate,
mockElement,
mockScope,
mockTimeout,
overlayService;
beforeEach(function () { describe("The overlay service", function () {
mockDocument = jasmine.createSpyObj("$document", ["find"]); var mockDocument,
mockCompile = jasmine.createSpy("$compile"); mockCompile,
mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]); mockRootScope,
mockBody = jasmine.createSpyObj("body", ["append"]); mockBody,
mockTemplate = jasmine.createSpy("template"); mockTemplate,
mockElement = jasmine.createSpyObj("element", ["remove"]); mockElement,
mockScope = jasmine.createSpyObj("scope", ["$destroy"]); mockScope,
mockTimeout = function (callback) { mockTimeout,
callback(); overlayService;
};
mockDocument.find.and.returnValue(mockBody); beforeEach(function () {
mockCompile.and.returnValue(mockTemplate); mockDocument = jasmine.createSpyObj("$document", ["find"]);
mockRootScope.$new.and.returnValue(mockScope); mockCompile = jasmine.createSpy("$compile");
mockTemplate.and.returnValue(mockElement); mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]);
mockBody = jasmine.createSpyObj("body", ["append"]);
mockTemplate = jasmine.createSpy("template");
mockElement = jasmine.createSpyObj("element", ["remove"]);
mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
mockTimeout = function (callback) {
callback();
};
overlayService = new OverlayService( mockDocument.find.and.returnValue(mockBody);
mockDocument, mockCompile.and.returnValue(mockTemplate);
mockCompile, mockRootScope.$new.and.returnValue(mockScope);
mockRootScope, mockTemplate.and.returnValue(mockElement);
mockTimeout
);
});
it("prepends an mct-include to create overlays", function () { overlayService = new OverlayService(
overlayService.createOverlay("test", {}); mockDocument,
expect(mockCompile).toHaveBeenCalled(); mockCompile,
expect(mockCompile.calls.mostRecent().args[0].indexOf("mct-include")) mockRootScope,
.not.toEqual(-1); mockTimeout
}); );
});
it("adds the templated element to the body", function () { it("prepends an mct-include to create overlays", function () {
overlayService.createOverlay("test", {}); overlayService.createOverlay("test", {});
expect(mockBody.append).toHaveBeenCalledWith(mockElement); expect(mockCompile).toHaveBeenCalled();
}); expect(mockCompile.calls.mostRecent().args[0].indexOf("mct-include"))
.not.toEqual(-1);
});
it("places the provided model/key in its template's scope", function () { it("adds the templated element to the body", function () {
overlayService.createOverlay("test", { someKey: 42 }); overlayService.createOverlay("test", {});
expect(mockScope.overlay).toEqual({ someKey: 42 }); expect(mockBody.append).toHaveBeenCalledWith(mockElement);
expect(mockScope.key).toEqual("test"); });
// Make sure this is actually what was rendered, too it("places the provided model/key in its template's scope", function () {
expect(mockTemplate).toHaveBeenCalledWith(mockScope); overlayService.createOverlay("test", { someKey: 42 });
}); expect(mockScope.overlay).toEqual({ someKey: 42 });
expect(mockScope.key).toEqual("test");
it("removes the prepended element on request", function () { // Make sure this is actually what was rendered, too
var overlay = overlayService.createOverlay("test", {}); expect(mockTemplate).toHaveBeenCalledWith(mockScope);
});
// Verify precondition it("removes the prepended element on request", function () {
expect(mockElement.remove).not.toHaveBeenCalled(); var overlay = overlayService.createOverlay("test", {});
expect(mockScope.$destroy).not.toHaveBeenCalled();
// Dismiss the overlay // Verify precondition
overlay.dismiss(); expect(mockElement.remove).not.toHaveBeenCalled();
expect(mockScope.$destroy).not.toHaveBeenCalled();
// Now it should have been removed, and the scope destroyed // Dismiss the overlay
expect(mockElement.remove).toHaveBeenCalled(); overlay.dismiss();
expect(mockScope.$destroy).toHaveBeenCalled();
});
}); // Now it should have been removed, and the scope destroyed
} expect(mockElement.remove).toHaveBeenCalled();
); expect(mockScope.$destroy).toHaveBeenCalled();
});
});

View File

@ -20,190 +20,195 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/controllers/EditActionController", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/controllers/EditPanesController", * as represented by the Administrator of the National Aeronautics and Space
"./src/controllers/EditObjectController", * Administration. All rights reserved.
"./src/actions/EditAndComposeAction", *
"./src/actions/EditAction", * Open MCT is licensed under the Apache License, Version 2.0 (the
"./src/actions/SaveAction", * "License"); you may not use this file except in compliance with the License.
"./src/actions/SaveAndStopEditingAction", * You may obtain a copy of the License at
"./src/actions/CancelAction", * http://www.apache.org/licenses/LICENSE-2.0.
"./src/policies/EditPersistableObjectsPolicy", *
"./src/representers/EditRepresenter", * Unless required by applicable law or agreed to in writing, software
"./src/capabilities/EditorCapability", * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
"./res/templates/library.html", * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
"./res/templates/edit-object.html", * License for the specific language governing permissions and limitations
"./res/templates/edit-action-buttons.html", * under the License.
"./res/templates/topbar-edit.html" *
], function ( * Open MCT includes source code licensed under additional open source
EditActionController, * licenses. See the Open Source Licenses file (LICENSES.md) included with
EditPanesController, * this source code distribution or the Licensing information page available
EditObjectController, * at runtime from the About dialog for additional information.
EditAndComposeAction, *****************************************************************************/
EditAction,
SaveAction, import EditActionController from './src/controllers/EditActionController';
SaveAndStopEditingAction,
CancelAction, import EditPanesController from './src/controllers/EditPanesController';
EditPersistableObjectsPolicy, import EditObjectController from './src/controllers/EditObjectController';
EditRepresenter, import EditAndComposeAction from './src/actions/EditAndComposeAction';
EditorCapability, import EditAction from './src/actions/EditAction';
libraryTemplate, import SaveAction from './src/actions/SaveAction';
editObjectTemplate, import SaveAndStopEditingAction from './src/actions/SaveAndStopEditingAction';
editActionButtonsTemplate, import CancelAction from './src/actions/CancelAction';
topbarEditTemplate import EditPersistableObjectsPolicy from './src/policies/EditPersistableObjectsPolicy';
) { import EditRepresenter from './src/representers/EditRepresenter';
return { import EditorCapability from './src/capabilities/EditorCapability';
name: "platform/commonUI/edit", import libraryTemplate from './res/templates/library.html';
definition: { import editObjectTemplate from './res/templates/edit-object.html';
"extensions": { import editActionButtonsTemplate from './res/templates/edit-action-buttons.html';
"controllers": [ import topbarEditTemplate from './res/templates/topbar-edit.html';
{
"key": "EditActionController", export default {
"implementation": EditActionController, name: "platform/commonUI/edit",
"depends": [ definition: {
"$scope" "extensions": {
] "controllers": [
}, {
{ "key": "EditActionController",
"key": "EditPanesController", "implementation": EditActionController,
"implementation": EditPanesController, "depends": [
"depends": [ "$scope"
"$scope" ]
] },
}, {
{ "key": "EditPanesController",
"key": "EditObjectController", "implementation": EditPanesController,
"implementation": EditObjectController, "depends": [
"depends": [ "$scope"
"$scope", ]
"$location", },
"navigationService" {
] "key": "EditObjectController",
"implementation": EditObjectController,
"depends": [
"$scope",
"$location",
"navigationService"
]
}
],
"actions": [
{
"key": "compose",
"implementation": EditAndComposeAction
},
{
"key": "edit",
"implementation": EditAction,
"depends": [
"$location",
"navigationService",
"$log"
],
"description": "Edit",
"category": "view-control",
"cssClass": "major icon-pencil",
"group": "action",
"priority": 10
},
{
"key": "save-and-stop-editing",
"category": "save",
"implementation": SaveAndStopEditingAction,
"name": "Save and Finish Editing",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"dialogService",
"notificationService"
]
},
{
"key": "save",
"category": "save",
"implementation": SaveAction,
"name": "Save and Continue Editing",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"dialogService",
"notificationService"
]
},
{
"key": "cancel",
"category": "conclude-editing",
"implementation": CancelAction,
// Because we use the name as label for edit buttons and mct-control buttons need
// the label to be set to undefined in order to not apply the labeled CSS rule.
"name": undefined,
"cssClass": "icon-x no-label",
"description": "Discard changes made to these objects.",
"depends": []
}
],
"policies": [
{
"category": "action",
"implementation": EditPersistableObjectsPolicy,
"depends": ["openmct"]
}
],
"templates": [
{
"key": "edit-library",
"template": libraryTemplate
}
],
"representations": [
{
"key": "edit-object",
"template": editObjectTemplate,
"uses": [
"view"
],
"gestures": [
"drop"
]
},
{
"key": "edit-action-buttons",
"template": editActionButtonsTemplate,
"uses": [
"action"
]
},
{
"key": "topbar-edit",
"template": topbarEditTemplate
}
],
"representers": [
{
"implementation": EditRepresenter,
"depends": [
"$log"
]
}
],
"capabilities": [
{
"key": "editor",
"name": "Editor Capability",
"description": "Provides transactional editing capabilities",
"implementation": EditorCapability,
"depends": [
"openmct"
]
}
],
"runs": [
{
depends: [
"toolbars[]",
"openmct"
],
implementation: function (toolbars, openmct) {
toolbars.forEach(openmct.toolbars.addProvider, openmct.toolbars);
} }
], }
"actions": [ ]
{
"key": "compose",
"implementation": EditAndComposeAction
},
{
"key": "edit",
"implementation": EditAction,
"depends": [
"$location",
"navigationService",
"$log"
],
"description": "Edit",
"category": "view-control",
"cssClass": "major icon-pencil",
"group": "action",
"priority": 10
},
{
"key": "save-and-stop-editing",
"category": "save",
"implementation": SaveAndStopEditingAction,
"name": "Save and Finish Editing",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"dialogService",
"notificationService"
]
},
{
"key": "save",
"category": "save",
"implementation": SaveAction,
"name": "Save and Continue Editing",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"dialogService",
"notificationService"
]
},
{
"key": "cancel",
"category": "conclude-editing",
"implementation": CancelAction,
// Because we use the name as label for edit buttons and mct-control buttons need
// the label to be set to undefined in order to not apply the labeled CSS rule.
"name": undefined,
"cssClass": "icon-x no-label",
"description": "Discard changes made to these objects.",
"depends": []
}
],
"policies": [
{
"category": "action",
"implementation": EditPersistableObjectsPolicy,
"depends": ["openmct"]
}
],
"templates": [
{
"key": "edit-library",
"template": libraryTemplate
}
],
"representations": [
{
"key": "edit-object",
"template": editObjectTemplate,
"uses": [
"view"
],
"gestures": [
"drop"
]
},
{
"key": "edit-action-buttons",
"template": editActionButtonsTemplate,
"uses": [
"action"
]
},
{
"key": "topbar-edit",
"template": topbarEditTemplate
}
],
"representers": [
{
"implementation": EditRepresenter,
"depends": [
"$log"
]
}
],
"capabilities": [
{
"key": "editor",
"name": "Editor Capability",
"description": "Provides transactional editing capabilities",
"implementation": EditorCapability,
"depends": [
"openmct"
]
}
],
"runs": [
{
depends: [
"toolbars[]",
"openmct"
],
implementation: function (toolbars, openmct) {
toolbars.forEach(openmct.toolbars.addProvider, openmct.toolbars);
}
}
]
}
} }
}; }
}); };

View File

@ -20,71 +20,66 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /**
function () { * The "Cancel" action; the action triggered by clicking Cancel from
* Edit Mode. Exits the editing user interface and invokes object
* capabilities to persist the changes that have been made.
* @constructor
* @memberof platform/commonUI/edit
* @implements {Action}
*/
function CancelAction(context) {
this.domainObject = context.domainObject;
}
/** /**
* The "Cancel" action; the action triggered by clicking Cancel from * Cancel editing.
* Edit Mode. Exits the editing user interface and invokes object *
* capabilities to persist the changes that have been made. * @returns {Promise} a promise that will be fulfilled when
* @constructor * cancellation has completed
* @memberof platform/commonUI/edit */
* @implements {Action} CancelAction.prototype.perform = function () {
*/ var domainObject = this.domainObject;
function CancelAction(context) {
this.domainObject = context.domainObject; function returnToBrowse() {
var parent;
//If the object existed already, navigate to refresh view
// with previous object state.
if (domainObject.getModel().persisted) {
return domainObject.getCapability("action").perform("navigate");
} else {
//If the object was new, and user has cancelled, then
//navigate back to parent because nothing to show.
return domainObject.getCapability("location").getOriginal().then(function (original) {
parent = original.getCapability("context").getParent();
return parent.getCapability("action").perform("navigate");
});
} }
/**
* Cancel editing.
*
* @returns {Promise} a promise that will be fulfilled when
* cancellation has completed
*/
CancelAction.prototype.perform = function () {
var domainObject = this.domainObject;
function returnToBrowse() {
var parent;
//If the object existed already, navigate to refresh view
// with previous object state.
if (domainObject.getModel().persisted) {
return domainObject.getCapability("action").perform("navigate");
} else {
//If the object was new, and user has cancelled, then
//navigate back to parent because nothing to show.
return domainObject.getCapability("location").getOriginal().then(function (original) {
parent = original.getCapability("context").getParent();
return parent.getCapability("action").perform("navigate");
});
}
}
function cancel() {
return domainObject.getCapability("editor").finish();
}
//Do navigation first in order to trigger unsaved changes dialog
return returnToBrowse()
.then(cancel);
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,
* and that this domain object is in Edit mode.
* @returns {boolean} true if applicable
*/
CancelAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined
&& domainObject.hasCapability('editor')
&& domainObject.getCapability('editor').isEditContextRoot();
};
return CancelAction;
} }
);
function cancel() {
return domainObject.getCapability("editor").finish();
}
//Do navigation first in order to trigger unsaved changes dialog
return returnToBrowse()
.then(cancel);
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,
* and that this domain object is in Edit mode.
* @returns {boolean} true if applicable
*/
CancelAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined
&& domainObject.hasCapability('editor')
&& domainObject.getCapability('editor').isEditContextRoot();
};
export default CancelAction;

View File

@ -23,79 +23,96 @@
/** /**
* Module defining EditAction. Created by vwoeltje on 11/14/14. * Module defining EditAction. Created by vwoeltje on 11/14/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
// A no-op action to return in the event that the action cannot /**
// be completed. * Module defining EditAction. Created by vwoeltje on 11/14/14.
var NULL_ACTION = { */
perform: function () { var NULL_ACTION = {
return undefined; perform: function () {
} return undefined;
};
/**
* The Edit action is performed when the user wishes to enter Edit
* mode (typically triggered by the Edit button.) This will
* show the user interface for editing (by way of a change in
* route)
* @memberof platform/commonUI/edit
* @constructor
* @implements {Action}
*/
function EditAction($location, navigationService, $log, context) {
var domainObject = (context || {}).domainObject;
// We cannot enter Edit mode if we have no domain object to
// edit, so verify that one was defined as part of the
// context. (This is also verified in appliesTo, so this
// would indicate abnormal behavior.)
if (!domainObject) {
$log.warn([
"No domain object to edit; ",
"edit action is not valid."
].join(""));
return NULL_ACTION;
}
this.domainObject = domainObject;
this.$location = $location;
this.navigationService = navigationService;
}
/**
* Enter edit mode.
*/
EditAction.prototype.perform = function () {
//If this is not the currently navigated object, then navigate
// to it.
if (this.navigationService.getNavigation() !== this.domainObject) {
this.navigationService.setNavigation(this.domainObject);
}
this.domainObject.useCapability("editor");
};
/**
* Check for applicability; verify that a domain object is present
* for this action to be performed upon.
* @param {ActionContext} context the context in which this action
* will be performed; should contain a `domainObject` property
*/
EditAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject,
type = domainObject && domainObject.getCapability('type');
// Only allow editing of types that support it and are not already
// being edited
return type && type.hasFeature('creation')
&& domainObject.hasCapability('editor')
&& !domainObject.getCapability('editor').isEditContextRoot();
};
return EditAction;
} }
); };
/**
* The Edit action is performed when the user wishes to enter Edit
* mode (typically triggered by the Edit button.) This will
* show the user interface for editing (by way of a change in
* route)
* @memberof platform/commonUI/edit
* @constructor
* @implements {Action}
*/
function EditAction($location, navigationService, $log, context) {
var domainObject = (context || {}).domainObject;
// We cannot enter Edit mode if we have no domain object to
// edit, so verify that one was defined as part of the
// context. (This is also verified in appliesTo, so this
// would indicate abnormal behavior.)
if (!domainObject) {
$log.warn([
"No domain object to edit; ",
"edit action is not valid."
].join(""));
return NULL_ACTION;
}
this.domainObject = domainObject;
this.$location = $location;
this.navigationService = navigationService;
}
/**
* Enter edit mode.
*/
EditAction.prototype.perform = function () {
//If this is not the currently navigated object, then navigate
// to it.
if (this.navigationService.getNavigation() !== this.domainObject) {
this.navigationService.setNavigation(this.domainObject);
}
this.domainObject.useCapability("editor");
};
/**
* Check for applicability; verify that a domain object is present
* for this action to be performed upon.
* @param {ActionContext} context the context in which this action
* will be performed; should contain a `domainObject` property
*/
EditAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject,
type = domainObject && domainObject.getCapability('type');
// Only allow editing of types that support it and are not already
// being edited
return type && type.hasFeature('creation')
&& domainObject.hasCapability('editor')
&& !domainObject.getCapability('editor').isEditContextRoot();
};
export default EditAction;

View File

@ -20,40 +20,50 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function EditAndComposeAction(context) {
* Add one domain object to another's composition. this.domainObject = (context || {}).domainObject;
* @constructor this.selectedObject = (context || {}).selectedObject;
* @memberof platform/commonUI/edit }
* @implements {Action}
*/
function EditAndComposeAction(context) {
this.domainObject = (context || {}).domainObject;
this.selectedObject = (context || {}).selectedObject;
}
EditAndComposeAction.prototype.perform = function () { EditAndComposeAction.prototype.perform = function () {
var self = this, var self = this,
editAction = this.domainObject.getCapability('action').getActions("edit")[0]; editAction = this.domainObject.getCapability('action').getActions("edit")[0];
// Link these objects // Link these objects
function doLink() { function doLink() {
var composition = self.domainObject var composition = self.domainObject
&& self.domainObject.getCapability('composition'); && self.domainObject.getCapability('composition');
return composition && composition.add(self.selectedObject); return composition && composition.add(self.selectedObject);
}
if (editAction) {
editAction.perform();
}
return this.selectedObject && doLink();
};
return EditAndComposeAction;
} }
);
if (editAction) {
editAction.perform();
}
return this.selectedObject && doLink();
};
export default EditAndComposeAction;

View File

@ -20,79 +20,97 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
['./SaveInProgressDialog'], * Open MCT, Copyright (c) 2014-2021, United States Government
function (SaveInProgressDialog) { * as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/** import SaveInProgressDialog from './SaveInProgressDialog';
* The "Save" action; it invokes object capabilities to persist
* the changes that have been made.
* @constructor
* @implements {Action}
* @memberof platform/commonUI/edit
*/
function SaveAction(
dialogService,
notificationService,
context
) {
this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
}
/** /**
* Save changes. * The "Save" action; it invokes object capabilities to persist
* * the changes that have been made.
* @returns {Promise} a promise that will be fulfilled when * @constructor
* cancellation has completed * @implements {Action}
* @memberof platform/commonUI/edit.SaveAction# * @memberof platform/commonUI/edit
*/ */
SaveAction.prototype.perform = function () { function SaveAction(
var self = this, dialogService,
domainObject = this.domainObject, notificationService,
dialog = new SaveInProgressDialog(this.dialogService); context
) {
this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
}
// Invoke any save behavior introduced by the editor capability; /**
// this is introduced by EditableDomainObject which is * Save changes.
// used to insulate underlying objects from changes made *
// during editing. * @returns {Promise} a promise that will be fulfilled when
function doSave() { * cancellation has completed
return domainObject.getCapability("editor").save(); * @memberof platform/commonUI/edit.SaveAction#
} */
SaveAction.prototype.perform = function () {
var self = this,
domainObject = this.domainObject,
dialog = new SaveInProgressDialog(this.dialogService);
function onSuccess() { // Invoke any save behavior introduced by the editor capability;
dialog.hide(); // this is introduced by EditableDomainObject which is
self.notificationService.info("Save Succeeded"); // used to insulate underlying objects from changes made
} // during editing.
function doSave() {
function onFailure() { return domainObject.getCapability("editor").save();
dialog.hide();
self.notificationService.error("Save Failed");
}
dialog.show();
return doSave()
.then(onSuccess)
.catch(onFailure);
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,
* and that this domain object is in Edit mode.
* @returns true if applicable
*/
SaveAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined
&& domainObject.hasCapability('editor')
&& domainObject.getCapability('editor').isEditContextRoot()
&& domainObject.getModel().persisted !== undefined;
};
return SaveAction;
} }
);
function onSuccess() {
dialog.hide();
self.notificationService.info("Save Succeeded");
}
function onFailure() {
dialog.hide();
self.notificationService.error("Save Failed");
}
dialog.show();
return doSave()
.then(onSuccess)
.catch(onFailure);
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,
* and that this domain object is in Edit mode.
* @returns true if applicable
*/
SaveAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined
&& domainObject.hasCapability('editor')
&& domainObject.getCapability('editor').isEditContextRoot()
&& domainObject.getModel().persisted !== undefined;
};
export default SaveAction;

View File

@ -20,56 +20,74 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["./SaveAction"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (SaveAction) { * as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/** import SaveAction from './SaveAction';
* The "Save and Stop Editing" action performs a [Save action]{@link SaveAction}
* on the object under edit followed by exiting the edit user interface.
* @constructor
* @implements {Action}
* @memberof platform/commonUI/edit
*/
function SaveAndStopEditingAction(
dialogService,
notificationService,
context
) {
this.context = context;
this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
}
/** /**
* Trigger a save operation and exit edit mode. * The "Save and Stop Editing" action performs a [Save action]{@link SaveAction}
* * on the object under edit followed by exiting the edit user interface.
* @returns {Promise} a promise that will be fulfilled when * @constructor
* cancellation has completed * @implements {Action}
* @memberof platform/commonUI/edit.SaveAndStopEditingAction# * @memberof platform/commonUI/edit
*/ */
SaveAndStopEditingAction.prototype.perform = function () { function SaveAndStopEditingAction(
var domainObject = this.domainObject, dialogService,
saveAction = new SaveAction(this.dialogService, this.notificationService, this.context); notificationService,
context
) {
this.context = context;
this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
}
function closeEditor() { /**
return domainObject.getCapability("editor").finish(); * Trigger a save operation and exit edit mode.
} *
* @returns {Promise} a promise that will be fulfilled when
* cancellation has completed
* @memberof platform/commonUI/edit.SaveAndStopEditingAction#
*/
SaveAndStopEditingAction.prototype.perform = function () {
var domainObject = this.domainObject,
saveAction = new SaveAction(this.dialogService, this.notificationService, this.context);
return saveAction.perform() function closeEditor() {
.then(closeEditor) return domainObject.getCapability("editor").finish();
.catch(closeEditor);
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,
* and that this domain object is in Edit mode.
* @returns true if applicable
*/
SaveAndStopEditingAction.appliesTo = SaveAction.appliesTo;
return SaveAndStopEditingAction;
} }
);
return saveAction.perform()
.then(closeEditor)
.catch(closeEditor);
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,
* and that this domain object is in Edit mode.
* @returns true if applicable
*/
SaveAndStopEditingAction.appliesTo = SaveAction.appliesTo;
export default SaveAndStopEditingAction;

View File

@ -1,24 +1,22 @@
define([], function () { function SaveInProgressDialog(dialogService) {
function SaveInProgressDialog(dialogService) { this.dialogService = dialogService;
this.dialogService = dialogService; this.dialog = undefined;
this.dialog = undefined; }
SaveInProgressDialog.prototype.show = function () {
this.dialog = this.dialogService.showBlockingMessage({
title: "Saving",
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
unknownProgress: true,
severity: "info",
delay: true
});
};
SaveInProgressDialog.prototype.hide = function () {
if (this.dialog) {
this.dialog.dismiss();
} }
};
SaveInProgressDialog.prototype.show = function () { export default SaveInProgressDialog;
this.dialog = this.dialogService.showBlockingMessage({
title: "Saving",
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
unknownProgress: true,
severity: "info",
delay: true
});
};
SaveInProgressDialog.prototype.hide = function () {
if (this.dialog) {
this.dialog.dismiss();
}
};
return SaveInProgressDialog;
});

View File

@ -20,45 +20,52 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function EditorCapability(
* A capability that implements an editing 'session' for a domain openmct,
* object. An editing session is initiated via a call to .edit(). domainObject
* Once initiated, any persist operations will be queued pending a ) {
* subsequent call to [.save()](@link #save) or [.finish()](@link this.openmct = openmct;
* #finish). this.domainObject = domainObject;
* @param domainObject }
* @constructor
*/
function EditorCapability(
openmct,
domainObject
) {
this.openmct = openmct;
this.domainObject = domainObject;
}
/** /**
* Determines whether this object, or any of its ancestors are * Determines whether this object, or any of its ancestors are
* currently being edited. * currently being edited.
* @returns boolean * @returns boolean
*/ */
EditorCapability.prototype.inEditContext = function () { EditorCapability.prototype.inEditContext = function () {
return this.openmct.editor.isEditing(); return this.openmct.editor.isEditing();
}; };
/** /**
* Is this the root editing object (ie. the object that the user * Is this the root editing object (ie. the object that the user
* clicked 'edit' on)? * clicked 'edit' on)?
* @returns {*} * @returns {*}
*/ */
EditorCapability.prototype.isEditContextRoot = function () { EditorCapability.prototype.isEditContextRoot = function () {
return this.openmct.editor.isEditing(); return this.openmct.editor.isEditing();
}; };
return EditorCapability; export default EditorCapability;
}
);

View File

@ -23,57 +23,76 @@
/** /**
* Module defining EditActionController. Created by vwoeltje on 11/17/14. * Module defining EditActionController. Created by vwoeltje on 11/17/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
var SAVE_ACTION_CONTEXT = { category: 'save' }; /**
var OTHERS_ACTION_CONTEXT = { category: 'conclude-editing' }; * Module defining EditActionController. Created by vwoeltje on 11/17/14.
*/
var SAVE_ACTION_CONTEXT = { category: 'save' };
var OTHERS_ACTION_CONTEXT = { category: 'conclude-editing' };
/** /**
* Controller which supplies action instances for Save/Cancel. * Controller which supplies action instances for Save/Cancel.
* @memberof platform/commonUI/edit * @memberof platform/commonUI/edit
* @constructor * @constructor
*/ */
function EditActionController($scope) { function EditActionController($scope) {
function actionToMenuOption(action) { function actionToMenuOption(action) {
return { return {
key: action, key: action,
name: action.getMetadata().name, name: action.getMetadata().name,
cssClass: action.getMetadata().cssClass cssClass: action.getMetadata().cssClass
}; };
}
// Maintain all "conclude-editing" and "save" actions in the
// present context.
function updateActions() {
$scope.saveActions = $scope.action
? $scope.action.getActions(SAVE_ACTION_CONTEXT)
: [];
$scope.saveActionsAsMenuOptions = $scope.saveActions.map(actionToMenuOption);
$scope.saveActionMenuClickHandler = function (clickedAction) {
clickedAction.perform();
};
$scope.otherEditActions = $scope.action
? $scope.action.getActions(OTHERS_ACTION_CONTEXT)
: [];
// Required because Angular does not allow 'bind'
// in expressions.
$scope.actionPerformer = function (action) {
return action.perform.bind(action);
};
}
// Update set of actions whenever the action capability
// changes or becomes available.
$scope.$watch("action", updateActions);
}
return EditActionController;
} }
);
// Maintain all "conclude-editing" and "save" actions in the
// present context.
function updateActions() {
$scope.saveActions = $scope.action
? $scope.action.getActions(SAVE_ACTION_CONTEXT)
: [];
$scope.saveActionsAsMenuOptions = $scope.saveActions.map(actionToMenuOption);
$scope.saveActionMenuClickHandler = function (clickedAction) {
clickedAction.perform();
};
$scope.otherEditActions = $scope.action
? $scope.action.getActions(OTHERS_ACTION_CONTEXT)
: [];
// Required because Angular does not allow 'bind'
// in expressions.
$scope.actionPerformer = function (action) {
return action.perform.bind(action);
};
}
// Update set of actions whenever the action capability
// changes or becomes available.
$scope.$watch("action", updateActions);
}
export default EditActionController;

View File

@ -24,63 +24,83 @@
* This bundle implements Edit mode. * This bundle implements Edit mode.
* @namespace platform/commonUI/edit * @namespace platform/commonUI/edit
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
function cancelEditing(domainObject) { /**
var navigatedObject = domainObject, * This bundle implements Edit mode.
editorCapability = navigatedObject * @namespace platform/commonUI/edit
&& navigatedObject.getCapability("editor"); */
function cancelEditing(domainObject) {
var navigatedObject = domainObject,
editorCapability = navigatedObject
&& navigatedObject.getCapability("editor");
return editorCapability return editorCapability
&& editorCapability.finish(); && editorCapability.finish();
} }
/** /**
* Controller which is responsible for populating the scope for * Controller which is responsible for populating the scope for
* Edit mode * Edit mode
* @memberof platform/commonUI/edit * @memberof platform/commonUI/edit
* @constructor * @constructor
*/ */
function EditObjectController($scope, $location, navigationService) { function EditObjectController($scope, $location, navigationService) {
this.scope = $scope; this.scope = $scope;
var domainObject = $scope.domainObject; var domainObject = $scope.domainObject;
var removeCheck = navigationService var removeCheck = navigationService
.checkBeforeNavigation(function () { .checkBeforeNavigation(function () {
return "Continuing will cause the loss of any unsaved changes."; return "Continuing will cause the loss of any unsaved changes.";
}); });
$scope.$on('$destroy', function () { $scope.$on('$destroy', function () {
removeCheck(); removeCheck();
cancelEditing(domainObject); cancelEditing(domainObject);
}); });
function setViewForDomainObject() { function setViewForDomainObject() {
var locationViewKey = $location.search().view; var locationViewKey = $location.search().view;
function selectViewIfMatching(view) { function selectViewIfMatching(view) {
if (view.key === locationViewKey) { if (view.key === locationViewKey) {
$scope.representation = $scope.representation || {}; $scope.representation = $scope.representation || {};
$scope.representation.selected = view; $scope.representation.selected = view;
}
}
if (locationViewKey) {
((domainObject && domainObject.useCapability('view')) || [])
.forEach(selectViewIfMatching);
}
} }
setViewForDomainObject();
$scope.doAction = function (action) {
return $scope[action] && $scope[action]();
};
} }
return EditObjectController; if (locationViewKey) {
((domainObject && domainObject.useCapability('view')) || [])
.forEach(selectViewIfMatching);
}
} }
);
setViewForDomainObject();
$scope.doAction = function (action) {
return $scope[action] && $scope[action]();
};
}
export default EditObjectController;

View File

@ -20,47 +20,58 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function EditPanesController($scope) {
* Supports the Library and Elements panes in Edit mode. var self = this;
* @memberof platform/commonUI/edit
* @constructor
*/
function EditPanesController($scope) {
var self = this;
// Update root object based on represented object // Update root object based on represented object
function updateRoot(domainObject) { function updateRoot(domainObject) {
var root = self.rootDomainObject, var root = self.rootDomainObject,
context = domainObject context = domainObject
&& domainObject.getCapability('context'), && domainObject.getCapability('context'),
newRoot = context && context.getTrueRoot(), newRoot = context && context.getTrueRoot(),
oldId = root && root.getId(), oldId = root && root.getId(),
newId = newRoot && newRoot.getId(); newId = newRoot && newRoot.getId();
// Only update if this has actually changed, // Only update if this has actually changed,
// to avoid excessive refreshing. // to avoid excessive refreshing.
if (oldId !== newId) { if (oldId !== newId) {
self.rootDomainObject = newRoot; self.rootDomainObject = newRoot;
}
}
// Update root when represented object changes
$scope.$watch('domainObject', updateRoot);
} }
/**
* Get the root-level domain object, as reported by the
* represented domain object.
* @returns {DomainObject} the root object
*/
EditPanesController.prototype.getRoot = function () {
return this.rootDomainObject;
};
return EditPanesController;
} }
);
// Update root when represented object changes
$scope.$watch('domainObject', updateRoot);
}
/**
* Get the root-level domain object, as reported by the
* represented domain object.
* @returns {DomainObject} the root object
*/
EditPanesController.prototype.getRoot = function () {
return this.rootDomainObject;
};
export default EditPanesController;

View File

@ -20,38 +20,56 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
['objectUtils'], * Open MCT, Copyright (c) 2014-2016, United States Government
function (objectUtils) { * as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/** import objectUtils from 'objectUtils';
* Policy that prevents editing of any object from a provider that does not
* support persistence (ie. the 'save' operation). Editing is prevented
* as a subsequent save would fail, causing the loss of a user's changes.
* @param openmct
* @constructor
*/
function EditPersistableObjectsPolicy(openmct) {
this.openmct = openmct;
}
EditPersistableObjectsPolicy.prototype.allow = function (action, context) { /**
var domainObject = context.domainObject; * Policy that prevents editing of any object from a provider that does not
var key = action.getMetadata().key; * support persistence (ie. the 'save' operation). Editing is prevented
var category = (context || {}).category; * as a subsequent save would fail, causing the loss of a user's changes.
* @param openmct
* @constructor
*/
function EditPersistableObjectsPolicy(openmct) {
this.openmct = openmct;
}
// Use category to selectively block edit from the view. Edit action EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
// is also invoked during the create process which should be allowed, var domainObject = context.domainObject;
// because it may be saved elsewhere var key = action.getMetadata().key;
if ((key === 'edit' && category === 'view-control') || key === 'properties') { var category = (context || {}).category;
let identifier = this.openmct.objects.parseKeyString(domainObject.getId());
return this.openmct.objects.isPersistable(identifier); // Use category to selectively block edit from the view. Edit action
} // is also invoked during the create process which should be allowed,
// because it may be saved elsewhere
if ((key === 'edit' && category === 'view-control') || key === 'properties') {
let identifier = this.openmct.objects.parseKeyString(domainObject.getId());
return true; return this.openmct.objects.isPersistable(identifier);
};
return EditPersistableObjectsPolicy;
} }
);
return true;
};
export default EditPersistableObjectsPolicy;

View File

@ -20,80 +20,77 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function EditRepresenter($log, $scope) {
* The EditRepresenter is responsible for implementing this.$log = $log;
* representation-level behavior relevant to Edit mode. this.$scope = $scope;
* Specifically, this listens for changes to view configuration
* or to domain object models, and triggers persistence when
* these are detected.
*
* This is exposed as an extension of category `representers`,
* which mct-representation will utilize to add additional
* behavior to each representation.
*
* This will be called once per mct-representation directive,
* and may be reused for different domain objects and/or
* representations resulting from changes there.
*
* @memberof platform/commonUI/edit
* @implements {Representer}
* @constructor
*/
function EditRepresenter($log, $scope) {
this.$log = $log;
this.$scope = $scope;
this.$scope.commit = this.commit.bind(this); this.$scope.commit = this.commit.bind(this);
}
/**
* Commit any changes made to the in-scope model to the domain object.
* Also commits any changes made to $scope.configuration to the proper
* configuration value for the current representation.
*
* @param {String} message a message to log with the commit message.
*/
EditRepresenter.prototype.commit = function (message) {
var model = this.$scope.model,
configuration = this.$scope.configuration,
domainObject = this.domainObject;
this.$log.debug([
"Committing ",
domainObject && domainObject.getModel().name,
"(" + (domainObject && domainObject.getId()) + "):",
message
].join(" "));
if (this.domainObject) {
if (this.key && configuration) {
model.configuration = model.configuration || {};
model.configuration[this.key] = configuration;
} }
/** domainObject.useCapability('mutation', function () {
* Commit any changes made to the in-scope model to the domain object. return model;
* Also commits any changes made to $scope.configuration to the proper });
* configuration value for the current representation.
*
* @param {String} message a message to log with the commit message.
*/
EditRepresenter.prototype.commit = function (message) {
var model = this.$scope.model,
configuration = this.$scope.configuration,
domainObject = this.domainObject;
this.$log.debug([
"Committing ",
domainObject && domainObject.getModel().name,
"(" + (domainObject && domainObject.getId()) + "):",
message
].join(" "));
if (this.domainObject) {
if (this.key && configuration) {
model.configuration = model.configuration || {};
model.configuration[this.key] = configuration;
}
domainObject.useCapability('mutation', function () {
return model;
});
}
};
// Handle a specific representation of a specific domain object
EditRepresenter.prototype.represent = function (representation, representedObject) {
this.domainObject = representedObject;
if (representation) {
this.key = representation.key;
} else {
delete this.key;
}
};
// Respond to the destruction of the current representation.
EditRepresenter.prototype.destroy = function () {};
return EditRepresenter;
} }
); };
// Handle a specific representation of a specific domain object
EditRepresenter.prototype.represent = function (representation, representedObject) {
this.domainObject = representedObject;
if (representation) {
this.key = representation.key;
} else {
delete this.key;
}
};
// Respond to the destruction of the current representation.
EditRepresenter.prototype.destroy = function () {};
export default EditRepresenter;

View File

@ -20,136 +20,154 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/actions/CancelAction"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (CancelAction) { * 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.
*****************************************************************************/
describe("The Cancel action", function () { import CancelAction from '../../src/actions/CancelAction';
var mockDomainObject,
mockParentObject,
capabilities = {},
parentCapabilities = {},
actionContext,
action;
function mockPromise(value) { describe("The Cancel action", function () {
return { var mockDomainObject,
then: function (callback) { mockParentObject,
return mockPromise(callback(value)); capabilities = {},
} parentCapabilities = {},
}; actionContext,
action;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
} }
};
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getCapability",
"hasCapability",
"getModel"
]
);
mockDomainObject.getModel.and.returnValue({});
mockParentObject = jasmine.createSpyObj(
"parentObject",
[
"getCapability"
]
);
mockParentObject.getCapability.and.callFake(function (name) {
return parentCapabilities[name];
});
capabilities.editor = jasmine.createSpyObj(
"editor",
["save", "finish", "isEditContextRoot"]
);
capabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"perform"
]
);
capabilities.location = jasmine.createSpyObj(
"locationCapability",
[
"getOriginal"
]
);
capabilities.location.getOriginal.and.returnValue(mockPromise(mockDomainObject));
capabilities.context = jasmine.createSpyObj(
"contextCapability",
[
"getParent"
]
);
capabilities.context.getParent.and.returnValue(mockParentObject);
parentCapabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"perform"
]
);
actionContext = {
domainObject: mockDomainObject
};
mockDomainObject.getCapability.and.callFake(function (name) {
return capabilities[name];
});
mockDomainObject.hasCapability.and.callFake(function (name) {
return Boolean(capabilities[name]);
});
capabilities.editor.finish.and.returnValue(mockPromise(true));
action = new CancelAction(actionContext);
});
it("only applies to domain object that is being edited", function () {
capabilities.editor.isEditContextRoot.and.returnValue(true);
expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
capabilities.editor.isEditContextRoot.and.returnValue(false);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
mockDomainObject.hasCapability.and.returnValue(false);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
});
it("invokes the editor capability's cancel functionality when"
+ " performed", function () {
mockDomainObject.getModel.and.returnValue({persisted: 1});
//Return true from navigate action
capabilities.action.perform.and.returnValue(mockPromise(true));
action.perform();
// Should have called finish
expect(capabilities.editor.finish).toHaveBeenCalled();
// Definitely shouldn't call save!
expect(capabilities.editor.save).not.toHaveBeenCalled();
});
it("navigates to object if existing using navigate action", function () {
mockDomainObject.getModel.and.returnValue({persisted: 1});
//Return true from navigate action
capabilities.action.perform.and.returnValue(mockPromise(true));
action.perform();
expect(capabilities.action.perform).toHaveBeenCalledWith("navigate");
});
it("navigates to parent if new using navigate action", function () {
mockDomainObject.getModel.and.returnValue({persisted: undefined});
action.perform();
expect(parentCapabilities.action.perform).toHaveBeenCalledWith("navigate");
});
});
} }
);
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getCapability",
"hasCapability",
"getModel"
]
);
mockDomainObject.getModel.and.returnValue({});
mockParentObject = jasmine.createSpyObj(
"parentObject",
[
"getCapability"
]
);
mockParentObject.getCapability.and.callFake(function (name) {
return parentCapabilities[name];
});
capabilities.editor = jasmine.createSpyObj(
"editor",
["save", "finish", "isEditContextRoot"]
);
capabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"perform"
]
);
capabilities.location = jasmine.createSpyObj(
"locationCapability",
[
"getOriginal"
]
);
capabilities.location.getOriginal.and.returnValue(mockPromise(mockDomainObject));
capabilities.context = jasmine.createSpyObj(
"contextCapability",
[
"getParent"
]
);
capabilities.context.getParent.and.returnValue(mockParentObject);
parentCapabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"perform"
]
);
actionContext = {
domainObject: mockDomainObject
};
mockDomainObject.getCapability.and.callFake(function (name) {
return capabilities[name];
});
mockDomainObject.hasCapability.and.callFake(function (name) {
return Boolean(capabilities[name]);
});
capabilities.editor.finish.and.returnValue(mockPromise(true));
action = new CancelAction(actionContext);
});
it("only applies to domain object that is being edited", function () {
capabilities.editor.isEditContextRoot.and.returnValue(true);
expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
capabilities.editor.isEditContextRoot.and.returnValue(false);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
mockDomainObject.hasCapability.and.returnValue(false);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
});
it("invokes the editor capability's cancel functionality when"
+ " performed", function () {
mockDomainObject.getModel.and.returnValue({persisted: 1});
//Return true from navigate action
capabilities.action.perform.and.returnValue(mockPromise(true));
action.perform();
// Should have called finish
expect(capabilities.editor.finish).toHaveBeenCalled();
// Definitely shouldn't call save!
expect(capabilities.editor.save).not.toHaveBeenCalled();
});
it("navigates to object if existing using navigate action", function () {
mockDomainObject.getModel.and.returnValue({persisted: 1});
//Return true from navigate action
capabilities.action.perform.and.returnValue(mockPromise(true));
action.perform();
expect(capabilities.action.perform).toHaveBeenCalledWith("navigate");
});
it("navigates to parent if new using navigate action", function () {
mockDomainObject.getModel.and.returnValue({persisted: undefined});
action.perform();
expect(parentCapabilities.action.perform).toHaveBeenCalledWith("navigate");
});
});

View File

@ -20,89 +20,107 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/actions/EditAction"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (EditAction) { * 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.
*****************************************************************************/
describe("The Edit action", function () { import EditAction from '../../src/actions/EditAction';
var mockLocation,
mockNavigationService,
mockLog,
mockDomainObject,
mockType,
mockEditor,
actionContext,
capabilities,
action;
beforeEach(function () { describe("The Edit action", function () {
mockLocation = jasmine.createSpyObj( var mockLocation,
"$location", mockNavigationService,
["path"] mockLog,
); mockDomainObject,
mockNavigationService = jasmine.createSpyObj( mockType,
"navigationService", mockEditor,
["setNavigation", "getNavigation", "addListener", "removeListener"] actionContext,
); capabilities,
mockLog = jasmine.createSpyObj( action;
"$log",
["error", "warn", "info", "debug"]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
);
mockType = jasmine.createSpyObj(
"type",
["hasFeature"]
);
mockEditor = jasmine.createSpyObj(
"editorCapability",
["edit", "isEditContextRoot"]
);
capabilities = { beforeEach(function () {
type: mockType, mockLocation = jasmine.createSpyObj(
editor: mockEditor "$location",
}; ["path"]
);
mockNavigationService = jasmine.createSpyObj(
"navigationService",
["setNavigation", "getNavigation", "addListener", "removeListener"]
);
mockLog = jasmine.createSpyObj(
"$log",
["error", "warn", "info", "debug"]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
);
mockType = jasmine.createSpyObj(
"type",
["hasFeature"]
);
mockEditor = jasmine.createSpyObj(
"editorCapability",
["edit", "isEditContextRoot"]
);
mockDomainObject.getCapability.and.callFake(function (name) { capabilities = {
return capabilities[name]; type: mockType,
}); editor: mockEditor
mockDomainObject.hasCapability.and.returnValue(true); };
mockType.hasFeature.and.returnValue(true);
actionContext = { domainObject: mockDomainObject };
action = new EditAction(
mockLocation,
mockNavigationService,
mockLog,
actionContext
);
});
it("is only applicable when an editable domain object is present", function () {
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
expect(EditAction.appliesTo({})).toBeFalsy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
// Should have checked for creatability
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
});
it("is only applicable to objects not already in edit mode", function () {
mockEditor.isEditContextRoot.and.returnValue(false);
expect(EditAction.appliesTo(actionContext)).toBe(true);
mockEditor.isEditContextRoot.and.returnValue(true);
expect(EditAction.appliesTo(actionContext)).toBe(false);
});
it ("invokes the Edit capability on the object", function () {
action.perform();
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
});
mockDomainObject.getCapability.and.callFake(function (name) {
return capabilities[name];
}); });
} mockDomainObject.hasCapability.and.returnValue(true);
); mockType.hasFeature.and.returnValue(true);
actionContext = { domainObject: mockDomainObject };
action = new EditAction(
mockLocation,
mockNavigationService,
mockLog,
actionContext
);
});
it("is only applicable when an editable domain object is present", function () {
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
expect(EditAction.appliesTo({})).toBeFalsy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
// Should have checked for creatability
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
});
it("is only applicable to objects not already in edit mode", function () {
mockEditor.isEditContextRoot.and.returnValue(false);
expect(EditAction.appliesTo(actionContext)).toBe(true);
mockEditor.isEditContextRoot.and.returnValue(true);
expect(EditAction.appliesTo(actionContext)).toBe(false);
});
it ("invokes the Edit capability on the object", function () {
action.perform();
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
});
});

View File

@ -20,100 +20,118 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/actions/EditAndComposeAction"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (EditAndComposeAction) { * 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.
*****************************************************************************/
describe("The Link action", function () { import EditAndComposeAction from '../../src/actions/EditAndComposeAction';
var mockDomainObject,
mockParent,
mockContext,
mockComposition,
mockActionCapability,
mockEditAction,
mockType,
actionContext,
model,
capabilities,
action;
function mockPromise(value) { describe("The Link action", function () {
return { var mockDomainObject,
then: function (callback) { mockParent,
return mockPromise(callback(value)); mockContext,
} mockComposition,
}; mockActionCapability,
mockEditAction,
mockType,
actionContext,
model,
capabilities,
action;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
} }
};
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability"]
);
mockParent = {
getModel: function () {
return model;
},
getCapability: function (k) {
return capabilities[k];
},
useCapability: function (k, v) {
return capabilities[k].invoke(v);
}
};
mockContext = jasmine.createSpyObj("context", ["getParent"]);
mockComposition = jasmine.createSpyObj("composition", ["invoke", "add"]);
mockType = jasmine.createSpyObj("type", ["hasFeature", "getKey"]);
mockActionCapability = jasmine.createSpyObj("actionCapability", ["getActions"]);
mockEditAction = jasmine.createSpyObj("editAction", ["perform"]);
mockDomainObject.getId.and.returnValue("test");
mockDomainObject.getCapability.and.returnValue(mockContext);
mockContext.getParent.and.returnValue(mockParent);
mockType.hasFeature.and.returnValue(true);
mockType.getKey.and.returnValue("layout");
mockComposition.invoke.and.returnValue(mockPromise(true));
mockComposition.add.and.returnValue(mockPromise(true));
mockActionCapability.getActions.and.returnValue([]);
capabilities = {
composition: mockComposition,
action: mockActionCapability,
type: mockType
};
model = {
composition: ["a", "b", "c"]
};
actionContext = {
domainObject: mockParent,
selectedObject: mockDomainObject
};
action = new EditAndComposeAction(actionContext);
});
it("adds to the parent's composition when performed", function () {
action.perform();
expect(mockComposition.add)
.toHaveBeenCalledWith(mockDomainObject);
});
it("enables edit mode for objects that have an edit action", function () {
mockActionCapability.getActions.and.returnValue([mockEditAction]);
action.perform();
expect(mockEditAction.perform).toHaveBeenCalled();
});
it("Does not enable edit mode for objects that do not have an"
+ " edit action", function () {
mockActionCapability.getActions.and.returnValue([]);
action.perform();
expect(mockEditAction.perform).not.toHaveBeenCalled();
expect(mockComposition.add)
.toHaveBeenCalledWith(mockDomainObject);
});
});
} }
);
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability"]
);
mockParent = {
getModel: function () {
return model;
},
getCapability: function (k) {
return capabilities[k];
},
useCapability: function (k, v) {
return capabilities[k].invoke(v);
}
};
mockContext = jasmine.createSpyObj("context", ["getParent"]);
mockComposition = jasmine.createSpyObj("composition", ["invoke", "add"]);
mockType = jasmine.createSpyObj("type", ["hasFeature", "getKey"]);
mockActionCapability = jasmine.createSpyObj("actionCapability", ["getActions"]);
mockEditAction = jasmine.createSpyObj("editAction", ["perform"]);
mockDomainObject.getId.and.returnValue("test");
mockDomainObject.getCapability.and.returnValue(mockContext);
mockContext.getParent.and.returnValue(mockParent);
mockType.hasFeature.and.returnValue(true);
mockType.getKey.and.returnValue("layout");
mockComposition.invoke.and.returnValue(mockPromise(true));
mockComposition.add.and.returnValue(mockPromise(true));
mockActionCapability.getActions.and.returnValue([]);
capabilities = {
composition: mockComposition,
action: mockActionCapability,
type: mockType
};
model = {
composition: ["a", "b", "c"]
};
actionContext = {
domainObject: mockParent,
selectedObject: mockDomainObject
};
action = new EditAndComposeAction(actionContext);
});
it("adds to the parent's composition when performed", function () {
action.perform();
expect(mockComposition.add)
.toHaveBeenCalledWith(mockDomainObject);
});
it("enables edit mode for objects that have an edit action", function () {
mockActionCapability.getActions.and.returnValue([mockEditAction]);
action.perform();
expect(mockEditAction.perform).toHaveBeenCalled();
});
it("Does not enable edit mode for objects that do not have an"
+ " edit action", function () {
mockActionCapability.getActions.and.returnValue([]);
action.perform();
expect(mockEditAction.perform).not.toHaveBeenCalled();
expect(mockComposition.add)
.toHaveBeenCalledWith(mockDomainObject);
});
});

View File

@ -20,140 +20,158 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/actions/SaveAction"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (SaveAction) { * 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.
*****************************************************************************/
describe("The Save action", function () { import SaveAction from '../../src/actions/SaveAction';
var mockDomainObject,
mockEditorCapability,
actionContext,
mockDialogService,
mockNotificationService,
mockActionCapability,
capabilities = {},
action;
function mockPromise(value) { describe("The Save action", function () {
return { var mockDomainObject,
then: function (callback) { mockEditorCapability,
return mockPromise(callback(value)); actionContext,
}, mockDialogService,
catch: function (callback) { mockNotificationService,
return mockPromise(callback(value)); mockActionCapability,
} capabilities = {},
}; action;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
},
catch: function (callback) {
return mockPromise(callback(value));
} }
};
}
beforeEach(function () { beforeEach(function () {
mockDomainObject = jasmine.createSpyObj( mockDomainObject = jasmine.createSpyObj(
"domainObject", "domainObject",
[ [
"getCapability", "getCapability",
"hasCapability", "hasCapability",
"getModel", "getModel",
"getOriginalObject" "getOriginalObject"
] ]
); );
mockEditorCapability = jasmine.createSpyObj( mockEditorCapability = jasmine.createSpyObj(
"editor", "editor",
["save", "isEditContextRoot"] ["save", "isEditContextRoot"]
); );
mockActionCapability = jasmine.createSpyObj( mockActionCapability = jasmine.createSpyObj(
"actionCapability", "actionCapability",
["perform"] ["perform"]
); );
capabilities.editor = mockEditorCapability; capabilities.editor = mockEditorCapability;
capabilities.action = mockActionCapability; capabilities.action = mockActionCapability;
actionContext = { actionContext = {
domainObject: mockDomainObject domainObject: mockDomainObject
}; };
mockDialogService = jasmine.createSpyObj( mockDialogService = jasmine.createSpyObj(
"dialogService", "dialogService",
["showBlockingMessage"] ["showBlockingMessage"]
); );
mockNotificationService = jasmine.createSpyObj( mockNotificationService = jasmine.createSpyObj(
"notificationService", "notificationService",
["info", "error"] ["info", "error"]
); );
mockDomainObject.hasCapability.and.returnValue(true); mockDomainObject.hasCapability.and.returnValue(true);
mockDomainObject.getCapability.and.callFake(function (capability) { mockDomainObject.getCapability.and.callFake(function (capability) {
return capabilities[capability]; return capabilities[capability];
}); });
mockDomainObject.getModel.and.returnValue({persisted: 0}); mockDomainObject.getModel.and.returnValue({persisted: 0});
mockEditorCapability.save.and.returnValue(mockPromise(true)); mockEditorCapability.save.and.returnValue(mockPromise(true));
mockEditorCapability.isEditContextRoot.and.returnValue(true); mockEditorCapability.isEditContextRoot.and.returnValue(true);
action = new SaveAction(mockDialogService, mockNotificationService, actionContext); action = new SaveAction(mockDialogService, mockNotificationService, actionContext);
}); });
it("only applies to domain object with an editor capability", function () { it("only applies to domain object with an editor capability", function () {
expect(SaveAction.appliesTo(actionContext)).toBe(true); expect(SaveAction.appliesTo(actionContext)).toBe(true);
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.hasCapability.and.returnValue(false); mockDomainObject.hasCapability.and.returnValue(false);
mockDomainObject.getCapability.and.returnValue(undefined); mockDomainObject.getCapability.and.returnValue(undefined);
expect(SaveAction.appliesTo(actionContext)).toBe(false); expect(SaveAction.appliesTo(actionContext)).toBe(false);
}); });
it("only applies to domain object that has already been persisted", it("only applies to domain object that has already been persisted",
function () { function () {
mockDomainObject.getModel.and.returnValue({persisted: undefined}); mockDomainObject.getModel.and.returnValue({persisted: undefined});
expect(SaveAction.appliesTo(actionContext)).toBe(false); expect(SaveAction.appliesTo(actionContext)).toBe(false);
}); });
it("uses the editor capability to save the object", it("uses the editor capability to save the object",
function () { function () {
action.perform(); action.perform();
expect(mockEditorCapability.save).toHaveBeenCalled(); expect(mockEditorCapability.save).toHaveBeenCalled();
}); });
describe("in order to keep the user in the loop", function () { describe("in order to keep the user in the loop", function () {
var mockDialogHandle; var mockDialogHandle;
beforeEach(function () { beforeEach(function () {
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]); mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
mockDialogService.showBlockingMessage.and.returnValue(mockDialogHandle); mockDialogService.showBlockingMessage.and.returnValue(mockDialogHandle);
}); });
it("shows a dialog while saving", function () { it("shows a dialog while saving", function () {
mockEditorCapability.save.and.returnValue(new Promise(function () { mockEditorCapability.save.and.returnValue(new Promise(function () {
})); }));
action.perform(); action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled(); expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled(); expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
}); });
it("hides the dialog when saving is complete", function () { it("hides the dialog when saving is complete", function () {
action.perform(); action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled(); expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).toHaveBeenCalled(); expect(mockDialogHandle.dismiss).toHaveBeenCalled();
}); });
it("notifies if saving succeeded", function () { it("notifies if saving succeeded", function () {
var mockCallback = jasmine.createSpy("callback"); var mockCallback = jasmine.createSpy("callback");
mockEditorCapability.save.and.returnValue(Promise.resolve()); mockEditorCapability.save.and.returnValue(Promise.resolve());
return action.perform().then(mockCallback).then(function () { return action.perform().then(mockCallback).then(function () {
expect(mockNotificationService.info).toHaveBeenCalled(); expect(mockNotificationService.info).toHaveBeenCalled();
expect(mockNotificationService.error).not.toHaveBeenCalled(); expect(mockNotificationService.error).not.toHaveBeenCalled();
});
});
it("notifies if saving failed", function () {
var mockCallback = jasmine.createSpy("callback");
mockEditorCapability.save.and.returnValue(Promise.reject("some failure reason"));
return action.perform().then(mockCallback).then(function () {
expect(mockNotificationService.error).toHaveBeenCalled();
expect(mockNotificationService.info).not.toHaveBeenCalled();
});
});
}); });
}); });
}
); it("notifies if saving failed", function () {
var mockCallback = jasmine.createSpy("callback");
mockEditorCapability.save.and.returnValue(Promise.reject("some failure reason"));
return action.perform().then(mockCallback).then(function () {
expect(mockNotificationService.error).toHaveBeenCalled();
expect(mockNotificationService.info).not.toHaveBeenCalled();
});
});
});
});

View File

@ -20,109 +20,127 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/actions/SaveAndStopEditingAction"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (SaveAndStopEditingAction) { * 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.
*****************************************************************************/
describe("The Save and Stop Editing action", function () { import SaveAndStopEditingAction from '../../src/actions/SaveAndStopEditingAction';
// Some mocks appear unused because the describe("The Save and Stop Editing action", function () {
// underlying SaveAction that this action
// depends on is not mocked, so we mock some
// of SaveAction's own dependencies to make
// it run.
var mockDomainObject,
mockEditorCapability,
actionContext,
dialogService,
notificationService,
mockActionCapability,
capabilities = {},
action;
function mockPromise(value) { // Some mocks appear unused because the
return { // underlying SaveAction that this action
then: function (callback) { // depends on is not mocked, so we mock some
return mockPromise(callback(value)); // of SaveAction's own dependencies to make
}, // it run.
catch: function (callback) { var mockDomainObject,
return mockPromise(callback(value)); mockEditorCapability,
} actionContext,
}; dialogService,
notificationService,
mockActionCapability,
capabilities = {},
action;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
},
catch: function (callback) {
return mockPromise(callback(value));
} }
};
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getCapability",
"hasCapability",
"getModel",
"getOriginalObject"
]
);
mockEditorCapability = jasmine.createSpyObj(
"editor",
["save", "finish", "isEditContextRoot"]
);
mockActionCapability = jasmine.createSpyObj(
"actionCapability",
["perform"]
);
capabilities.editor = mockEditorCapability;
capabilities.action = mockActionCapability;
actionContext = {
domainObject: mockDomainObject
};
dialogService = jasmine.createSpyObj(
"dialogService",
["showBlockingMessage"]
);
notificationService = jasmine.createSpyObj(
"notificationService",
["info", "error"]
);
mockDomainObject.hasCapability.and.returnValue(true);
mockDomainObject.getCapability.and.callFake(function (capability) {
return capabilities[capability];
});
mockDomainObject.getModel.and.returnValue({ persisted: 0 });
mockEditorCapability.save.and.returnValue(mockPromise(true));
mockEditorCapability.isEditContextRoot.and.returnValue(true);
action = new SaveAndStopEditingAction(dialogService, notificationService, actionContext);
});
it("only applies to domain object with an editor capability", function () {
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(true);
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.hasCapability.and.returnValue(false);
mockDomainObject.getCapability.and.returnValue(undefined);
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
});
it("only applies to domain object that has already been persisted", function () {
mockDomainObject.getModel.and.returnValue({ persisted: undefined });
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
});
it("does not close the editor before completing the save", function () {
mockEditorCapability.save.and.returnValue(new Promise(function () {
}));
action.perform();
expect(mockEditorCapability.save).toHaveBeenCalled();
expect(mockEditorCapability.finish).not.toHaveBeenCalled();
});
it("closes the editor after saving", function () {
action.perform();
expect(mockEditorCapability.save).toHaveBeenCalled();
expect(mockEditorCapability.finish).toHaveBeenCalled();
});
});
} }
);
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getCapability",
"hasCapability",
"getModel",
"getOriginalObject"
]
);
mockEditorCapability = jasmine.createSpyObj(
"editor",
["save", "finish", "isEditContextRoot"]
);
mockActionCapability = jasmine.createSpyObj(
"actionCapability",
["perform"]
);
capabilities.editor = mockEditorCapability;
capabilities.action = mockActionCapability;
actionContext = {
domainObject: mockDomainObject
};
dialogService = jasmine.createSpyObj(
"dialogService",
["showBlockingMessage"]
);
notificationService = jasmine.createSpyObj(
"notificationService",
["info", "error"]
);
mockDomainObject.hasCapability.and.returnValue(true);
mockDomainObject.getCapability.and.callFake(function (capability) {
return capabilities[capability];
});
mockDomainObject.getModel.and.returnValue({ persisted: 0 });
mockEditorCapability.save.and.returnValue(mockPromise(true));
mockEditorCapability.isEditContextRoot.and.returnValue(true);
action = new SaveAndStopEditingAction(dialogService, notificationService, actionContext);
});
it("only applies to domain object with an editor capability", function () {
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(true);
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.hasCapability.and.returnValue(false);
mockDomainObject.getCapability.and.returnValue(undefined);
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
});
it("only applies to domain object that has already been persisted", function () {
mockDomainObject.getModel.and.returnValue({ persisted: undefined });
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
});
it("does not close the editor before completing the save", function () {
mockEditorCapability.save.and.returnValue(new Promise(function () {
}));
action.perform();
expect(mockEditorCapability.save).toHaveBeenCalled();
expect(mockEditorCapability.finish).not.toHaveBeenCalled();
});
it("closes the editor after saving", function () {
action.perform();
expect(mockEditorCapability.save).toHaveBeenCalled();
expect(mockEditorCapability.finish).toHaveBeenCalled();
});
});

View File

@ -20,87 +20,105 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/controllers/EditActionController"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (EditActionController) { * 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.
*****************************************************************************/
describe("The Edit Action controller", function () { import EditActionController from '../../src/controllers/EditActionController';
var mockSaveActionMetadata = {
name: "mocked-save-action",
cssClass: "mocked-save-action-css"
};
function fakeGetActions(actionContext) { describe("The Edit Action controller", function () {
if (actionContext.category === "save") { var mockSaveActionMetadata = {
var mockedSaveActions = [ name: "mocked-save-action",
jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]), cssClass: "mocked-save-action-css"
jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]) };
];
mockedSaveActions.forEach(function (action) {
action.getMetadata.and.returnValue(mockSaveActionMetadata);
});
return mockedSaveActions; function fakeGetActions(actionContext) {
} else if (actionContext.category === "conclude-editing") { if (actionContext.category === "save") {
return ["a", "b", "c"]; var mockedSaveActions = [
} else { jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]),
throw "EditActionController uses a context that's not covered by tests."; jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"])
} ];
} mockedSaveActions.forEach(function (action) {
action.getMetadata.and.returnValue(mockSaveActionMetadata);
var mockScope,
mockActions,
controller;
beforeEach(function () {
mockActions = jasmine.createSpyObj("action", ["getActions"]);
mockActions.getActions.and.callFake(fakeGetActions);
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockScope.action = mockActions;
controller = new EditActionController(mockScope);
}); });
function makeControllerUpdateActions() { return mockedSaveActions;
mockScope.$watch.calls.mostRecent().args[1](); } else if (actionContext.category === "conclude-editing") {
} return ["a", "b", "c"];
} else {
it("watches scope that may change applicable actions", function () { throw "EditActionController uses a context that's not covered by tests.";
// The action capability }
expect(mockScope.$watch).toHaveBeenCalledWith(
"action",
jasmine.any(Function)
);
});
it("populates the scope with 'save' actions", function () {
makeControllerUpdateActions();
expect(mockScope.saveActions.length).toEqual(2);
});
it("converts 'save' actions to their menu counterparts", function () {
makeControllerUpdateActions();
var menuOptions = mockScope.saveActionsAsMenuOptions;
expect(menuOptions.length).toEqual(2);
expect(menuOptions[0].key).toEqual(mockScope.saveActions[0]);
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
menuOptions.forEach(function (option) {
expect(option.name).toEqual(mockSaveActionMetadata.name);
expect(option.cssClass).toEqual(mockSaveActionMetadata.cssClass);
});
});
it("uses a click handler to perform the clicked action", function () {
makeControllerUpdateActions();
var sampleSaveAction = mockScope.saveActions[0];
mockScope.saveActionMenuClickHandler(sampleSaveAction);
expect(sampleSaveAction.perform).toHaveBeenCalled();
});
it("populates the scope with other editing actions", function () {
makeControllerUpdateActions();
expect(mockScope.otherEditActions).toEqual(["a", "b", "c"]);
});
});
} }
);
var mockScope,
mockActions,
controller;
beforeEach(function () {
mockActions = jasmine.createSpyObj("action", ["getActions"]);
mockActions.getActions.and.callFake(fakeGetActions);
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockScope.action = mockActions;
controller = new EditActionController(mockScope);
});
function makeControllerUpdateActions() {
mockScope.$watch.calls.mostRecent().args[1]();
}
it("watches scope that may change applicable actions", function () {
// The action capability
expect(mockScope.$watch).toHaveBeenCalledWith(
"action",
jasmine.any(Function)
);
});
it("populates the scope with 'save' actions", function () {
makeControllerUpdateActions();
expect(mockScope.saveActions.length).toEqual(2);
});
it("converts 'save' actions to their menu counterparts", function () {
makeControllerUpdateActions();
var menuOptions = mockScope.saveActionsAsMenuOptions;
expect(menuOptions.length).toEqual(2);
expect(menuOptions[0].key).toEqual(mockScope.saveActions[0]);
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
menuOptions.forEach(function (option) {
expect(option.name).toEqual(mockSaveActionMetadata.name);
expect(option.cssClass).toEqual(mockSaveActionMetadata.cssClass);
});
});
it("uses a click handler to perform the clicked action", function () {
makeControllerUpdateActions();
var sampleSaveAction = mockScope.saveActions[0];
mockScope.saveActionMenuClickHandler(sampleSaveAction);
expect(sampleSaveAction.perform).toHaveBeenCalled();
});
it("populates the scope with other editing actions", function () {
makeControllerUpdateActions();
expect(mockScope.otherEditActions).toEqual(["a", "b", "c"]);
});
});

View File

@ -20,119 +20,137 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/controllers/EditObjectController"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (EditObjectController) { * 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.
*****************************************************************************/
describe("The Edit Object controller", function () { import EditObjectController from '../../src/controllers/EditObjectController';
var mockScope,
mockObject,
testViews,
mockEditorCapability,
mockLocation,
mockNavigationService,
removeCheck,
mockStatusCapability,
mockCapabilities,
controller;
beforeEach(function () { describe("The Edit Object controller", function () {
mockScope = jasmine.createSpyObj( var mockScope,
"$scope", mockObject,
["$on", "$watch"] testViews,
); mockEditorCapability,
mockObject = jasmine.createSpyObj( mockLocation,
"domainObject", mockNavigationService,
["getId", "getModel", "getCapability", "hasCapability", "useCapability"] removeCheck,
); mockStatusCapability,
mockEditorCapability = jasmine.createSpyObj( mockCapabilities,
"mockEditorCapability", controller;
["isEditContextRoot", "dirty", "finish"]
);
mockStatusCapability = jasmine.createSpyObj('statusCapability',
["get"]
);
mockCapabilities = { beforeEach(function () {
"editor": mockEditorCapability, mockScope = jasmine.createSpyObj(
"status": mockStatusCapability "$scope",
}; ["$on", "$watch"]
);
mockObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
);
mockEditorCapability = jasmine.createSpyObj(
"mockEditorCapability",
["isEditContextRoot", "dirty", "finish"]
);
mockStatusCapability = jasmine.createSpyObj('statusCapability',
["get"]
);
mockLocation = jasmine.createSpyObj('$location', mockCapabilities = {
["search"] "editor": mockEditorCapability,
); "status": mockStatusCapability
mockLocation.search.and.returnValue({"view": "fixed"}); };
mockNavigationService = jasmine.createSpyObj('navigationService',
["checkBeforeNavigation"]
);
removeCheck = jasmine.createSpy('removeCheck'); mockLocation = jasmine.createSpyObj('$location',
mockNavigationService.checkBeforeNavigation.and.returnValue(removeCheck); ["search"]
);
mockLocation.search.and.returnValue({"view": "fixed"});
mockNavigationService = jasmine.createSpyObj('navigationService',
["checkBeforeNavigation"]
);
mockObject.getId.and.returnValue("test"); removeCheck = jasmine.createSpy('removeCheck');
mockObject.getModel.and.returnValue({ name: "Test object" }); mockNavigationService.checkBeforeNavigation.and.returnValue(removeCheck);
mockObject.getCapability.and.callFake(function (key) {
return mockCapabilities[key];
});
testViews = [
{ key: 'abc' },
{
key: 'def',
someKey: 'some value'
},
{ key: 'xyz' }
];
mockObject.useCapability.and.callFake(function (c) {
return (c === 'view') && testViews;
});
mockLocation.search.and.returnValue({ view: 'def' });
mockScope.domainObject = mockObject;
controller = new EditObjectController(
mockScope,
mockLocation,
mockNavigationService
);
});
it("adds a check before navigation", function () {
expect(mockNavigationService.checkBeforeNavigation)
.toHaveBeenCalledWith(jasmine.any(Function));
var checkFn = mockNavigationService.checkBeforeNavigation.calls.mostRecent().args[0];
mockEditorCapability.isEditContextRoot.and.returnValue(false);
mockEditorCapability.dirty.and.returnValue(false);
expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
mockEditorCapability.isEditContextRoot.and.returnValue(true);
expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
mockEditorCapability.dirty.and.returnValue(true);
expect(checkFn())
.toBe("Continuing will cause the loss of any unsaved changes.");
});
it("cleans up on destroy", function () {
expect(mockScope.$on)
.toHaveBeenCalledWith("$destroy", jasmine.any(Function));
mockScope.$on.calls.mostRecent().args[1]();
expect(mockEditorCapability.finish).toHaveBeenCalled();
expect(removeCheck).toHaveBeenCalled();
});
it("sets the active view from query parameters", function () {
expect(mockScope.representation.selected)
.toEqual(testViews[1]);
});
mockObject.getId.and.returnValue("test");
mockObject.getModel.and.returnValue({ name: "Test object" });
mockObject.getCapability.and.callFake(function (key) {
return mockCapabilities[key];
}); });
}
); testViews = [
{ key: 'abc' },
{
key: 'def',
someKey: 'some value'
},
{ key: 'xyz' }
];
mockObject.useCapability.and.callFake(function (c) {
return (c === 'view') && testViews;
});
mockLocation.search.and.returnValue({ view: 'def' });
mockScope.domainObject = mockObject;
controller = new EditObjectController(
mockScope,
mockLocation,
mockNavigationService
);
});
it("adds a check before navigation", function () {
expect(mockNavigationService.checkBeforeNavigation)
.toHaveBeenCalledWith(jasmine.any(Function));
var checkFn = mockNavigationService.checkBeforeNavigation.calls.mostRecent().args[0];
mockEditorCapability.isEditContextRoot.and.returnValue(false);
mockEditorCapability.dirty.and.returnValue(false);
expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
mockEditorCapability.isEditContextRoot.and.returnValue(true);
expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
mockEditorCapability.dirty.and.returnValue(true);
expect(checkFn())
.toBe("Continuing will cause the loss of any unsaved changes.");
});
it("cleans up on destroy", function () {
expect(mockScope.$on)
.toHaveBeenCalledWith("$destroy", jasmine.any(Function));
mockScope.$on.calls.mostRecent().args[1]();
expect(mockEditorCapability.finish).toHaveBeenCalled();
expect(removeCheck).toHaveBeenCalled();
});
it("sets the active view from query parameters", function () {
expect(mockScope.representation.selected)
.toEqual(testViews[1]);
});
});

View File

@ -20,95 +20,113 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/controllers/EditPanesController"], * Open MCT, Copyright (c) 2014-2021, United States Government
function (EditPanesController) { * 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.
*****************************************************************************/
describe("The Edit Panes controller", function () { import EditPanesController from '../../src/controllers/EditPanesController';
var mockScope,
mockDomainObject,
mockContext,
controller;
beforeEach(function () { describe("The Edit Panes controller", function () {
mockScope = jasmine.createSpyObj("$scope", ["$watch"]); var mockScope,
mockDomainObject = jasmine.createSpyObj( mockDomainObject,
'domainObject', mockContext,
['getId', 'getCapability'] controller;
);
mockContext = jasmine.createSpyObj(
'context',
['getTrueRoot']
);
mockDomainObject.getId.and.returnValue('test-id'); beforeEach(function () {
mockDomainObject.getCapability.and.returnValue(mockContext); mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getCapability']
);
mockContext = jasmine.createSpyObj(
'context',
['getTrueRoot']
);
// Return a new instance of the root object each time mockDomainObject.getId.and.returnValue('test-id');
mockContext.getTrueRoot.and.callFake(function () { mockDomainObject.getCapability.and.returnValue(mockContext);
var mockRoot = jasmine.createSpyObj('root', ['getId']);
mockRoot.getId.and.returnValue('root-id');
return mockRoot; // Return a new instance of the root object each time
}); mockContext.getTrueRoot.and.callFake(function () {
var mockRoot = jasmine.createSpyObj('root', ['getId']);
mockRoot.getId.and.returnValue('root-id');
controller = new EditPanesController(mockScope); return mockRoot;
});
it("watches for the domain object in view", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"domainObject",
jasmine.any(Function)
);
});
it("exposes the root object found via the object's context capability", function () {
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
// Verify that the correct capability was used
expect(mockDomainObject.getCapability)
.toHaveBeenCalledWith('context');
// Should have exposed the root from getRoot
expect(controller.getRoot().getId()).toEqual('root-id');
});
it("preserves the same root instance to avoid excessive refreshing", function () {
var firstRoot;
// Expose the domain object
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
firstRoot = controller.getRoot();
// Update!
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
// Should still have the same object instance, to avoid
// triggering the watch used by the template we're supporting
expect(controller.getRoot()).toBe(firstRoot);
});
// Complements the test above; the object pointed to should change
// when the actual root has changed (detected by identifier)
it("updates the root when it changes", function () {
var firstRoot;
// Expose the domain object
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
firstRoot = controller.getRoot();
// Change the exposed root
mockContext.getTrueRoot.and.callFake(function () {
var mockRoot = jasmine.createSpyObj('root', ['getId']);
mockRoot.getId.and.returnValue('other-root-id');
return mockRoot;
});
// Update!
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
// Should still have the same object instance, to avoid
// triggering the watch used by the template we're supporting
expect(controller.getRoot()).not.toBe(firstRoot);
expect(controller.getRoot().getId()).toEqual('other-root-id');
});
}); });
}
); controller = new EditPanesController(mockScope);
});
it("watches for the domain object in view", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"domainObject",
jasmine.any(Function)
);
});
it("exposes the root object found via the object's context capability", function () {
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
// Verify that the correct capability was used
expect(mockDomainObject.getCapability)
.toHaveBeenCalledWith('context');
// Should have exposed the root from getRoot
expect(controller.getRoot().getId()).toEqual('root-id');
});
it("preserves the same root instance to avoid excessive refreshing", function () {
var firstRoot;
// Expose the domain object
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
firstRoot = controller.getRoot();
// Update!
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
// Should still have the same object instance, to avoid
// triggering the watch used by the template we're supporting
expect(controller.getRoot()).toBe(firstRoot);
});
// Complements the test above; the object pointed to should change
// when the actual root has changed (detected by identifier)
it("updates the root when it changes", function () {
var firstRoot;
// Expose the domain object
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
firstRoot = controller.getRoot();
// Change the exposed root
mockContext.getTrueRoot.and.callFake(function () {
var mockRoot = jasmine.createSpyObj('root', ['getId']);
mockRoot.getId.and.returnValue('other-root-id');
return mockRoot;
});
// Update!
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
// Should still have the same object instance, to avoid
// triggering the watch used by the template we're supporting
expect(controller.getRoot()).not.toBe(firstRoot);
expect(controller.getRoot().getId()).toEqual('other-root-id');
});
});

View File

@ -20,83 +20,101 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
["../../src/policies/EditPersistableObjectsPolicy"], * Open MCT, Copyright (c) 2014-2016, United States Government
function (EditPersistableObjectsPolicy) { * 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.
*****************************************************************************/
describe("The Edit persistable objects policy", function () { import EditPersistableObjectsPolicy from '../../src/policies/EditPersistableObjectsPolicy';
var mockDomainObject,
mockEditAction,
mockPropertiesAction,
mockOtherAction,
mockAPI,
mockObjectAPI,
testContext,
policy;
beforeEach(function () { describe("The Edit persistable objects policy", function () {
mockDomainObject = jasmine.createSpyObj( var mockDomainObject,
'domainObject', mockEditAction,
[ mockPropertiesAction,
'getId' mockOtherAction,
] mockAPI,
); mockObjectAPI,
testContext,
policy;
mockObjectAPI = jasmine.createSpyObj('objectAPI', [ beforeEach(function () {
'isPersistable', mockDomainObject = jasmine.createSpyObj(
'parseKeyString' 'domainObject',
]); [
'getId'
]
);
mockAPI = { mockObjectAPI = jasmine.createSpyObj('objectAPI', [
objects: mockObjectAPI 'isPersistable',
}; 'parseKeyString'
]);
mockEditAction = jasmine.createSpyObj('edit', ['getMetadata']); mockAPI = {
mockPropertiesAction = jasmine.createSpyObj('properties', ['getMetadata']); objects: mockObjectAPI
mockOtherAction = jasmine.createSpyObj('other', ['getMetadata']); };
mockEditAction.getMetadata.and.returnValue({ key: 'edit' }); mockEditAction = jasmine.createSpyObj('edit', ['getMetadata']);
mockPropertiesAction.getMetadata.and.returnValue({ key: 'properties' }); mockPropertiesAction = jasmine.createSpyObj('properties', ['getMetadata']);
mockOtherAction.getMetadata.and.returnValue({key: 'other'}); mockOtherAction = jasmine.createSpyObj('other', ['getMetadata']);
mockDomainObject.getId.and.returnValue('test:testId'); mockEditAction.getMetadata.and.returnValue({ key: 'edit' });
mockPropertiesAction.getMetadata.and.returnValue({ key: 'properties' });
mockOtherAction.getMetadata.and.returnValue({key: 'other'});
testContext = { mockDomainObject.getId.and.returnValue('test:testId');
domainObject: mockDomainObject,
category: 'view-control'
};
policy = new EditPersistableObjectsPolicy(mockAPI); testContext = {
}); domainObject: mockDomainObject,
category: 'view-control'
};
it("Applies to edit action", function () { policy = new EditPersistableObjectsPolicy(mockAPI);
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled(); });
policy.allow(mockEditAction, testContext); it("Applies to edit action", function () {
expect(mockObjectAPI.isPersistable).toHaveBeenCalled(); expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
});
it("Applies to properties action", function () { policy.allow(mockEditAction, testContext);
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled(); expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
});
policy.allow(mockPropertiesAction, testContext); it("Applies to properties action", function () {
expect(mockObjectAPI.isPersistable).toHaveBeenCalled(); expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
});
it("does not apply to other actions", function () { policy.allow(mockPropertiesAction, testContext);
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled(); expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
});
policy.allow(mockOtherAction, testContext); it("does not apply to other actions", function () {
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled(); expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
});
it("Tests object provider for editability", function () { policy.allow(mockOtherAction, testContext);
mockObjectAPI.isPersistable.and.returnValue(false); expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
expect(policy.allow(mockEditAction, testContext)).toBe(false); });
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
mockObjectAPI.isPersistable.and.returnValue(true); it("Tests object provider for editability", function () {
expect(policy.allow(mockEditAction, testContext)).toBe(true); mockObjectAPI.isPersistable.and.returnValue(false);
}); expect(policy.allow(mockEditAction, testContext)).toBe(false);
}); expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
} mockObjectAPI.isPersistable.and.returnValue(true);
); expect(policy.allow(mockEditAction, testContext)).toBe(true);
});
});

View File

@ -20,68 +20,86 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
'../../src/representers/EditRepresenter' * Open MCT, Copyright (c) 2014-2021, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
EditRepresenter * Administration. All rights reserved.
) { *
describe('EditRepresenter', function () { * Open MCT is licensed under the Apache License, Version 2.0 (the
var $log, * "License"); you may not use this file except in compliance with the License.
$scope, * You may obtain a copy of the License at
representer; * http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import EditRepresenter from '../../src/representers/EditRepresenter';
describe('EditRepresenter', function () {
var $log,
$scope,
representer;
beforeEach(function () {
$log = jasmine.createSpyObj('$log', ['debug']);
$scope = {};
representer = new EditRepresenter($log, $scope);
});
it('injects a commit function in scope', function () {
expect($scope.commit).toEqual(jasmine.any(Function));
});
describe('representation', function () {
var domainObject,
representation;
beforeEach(function () { beforeEach(function () {
$log = jasmine.createSpyObj('$log', ['debug']); domainObject = jasmine.createSpyObj('domainObject', [
$scope = {}; 'getId',
representer = new EditRepresenter($log, $scope); 'getModel',
'useCapability'
]);
domainObject.getId.and.returnValue('anId');
domainObject.getModel.and.returnValue({name: 'anObject'});
representation = {
key: 'someRepresentation'
};
$scope.model = {name: 'anotherName'};
$scope.configuration = {some: 'config'};
representer.represent(representation, domainObject);
}); });
it('injects a commit function in scope', function () { it('logs a message when commiting', function () {
expect($scope.commit).toEqual(jasmine.any(Function)); $scope.commit('Test Message');
expect($log.debug)
.toHaveBeenCalledWith('Committing anObject (anId): Test Message');
}); });
describe('representation', function () { it('mutates the object when committing', function () {
var domainObject, $scope.commit('Test Message');
representation;
beforeEach(function () { expect(domainObject.useCapability)
domainObject = jasmine.createSpyObj('domainObject', [ .toHaveBeenCalledWith('mutation', jasmine.any(Function));
'getId',
'getModel',
'useCapability'
]);
domainObject.getId.and.returnValue('anId'); var mutateValue = domainObject.useCapability.calls.all()[0].args[1]();
domainObject.getModel.and.returnValue({name: 'anObject'});
representation = {
key: 'someRepresentation'
};
$scope.model = {name: 'anotherName'};
$scope.configuration = {some: 'config'};
representer.represent(representation, domainObject);
});
it('logs a message when commiting', function () {
$scope.commit('Test Message');
expect($log.debug)
.toHaveBeenCalledWith('Committing anObject (anId): Test Message');
});
it('mutates the object when committing', function () {
$scope.commit('Test Message');
expect(domainObject.useCapability)
.toHaveBeenCalledWith('mutation', jasmine.any(Function));
var mutateValue = domainObject.useCapability.calls.all()[0].args[1]();
expect(mutateValue.configuration.someRepresentation)
.toEqual({some: 'config'});
expect(mutateValue.name).toEqual('anotherName');
});
expect(mutateValue.configuration.someRepresentation)
.toEqual({some: 'config'});
expect(mutateValue.name).toEqual('anotherName');
}); });
}); });
});
});

View File

@ -20,53 +20,71 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/FormatProvider", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/DurationFormat" * as represented by the Administrator of the National Aeronautics and Space
], function ( * Administration. All rights reserved.
FormatProvider, *
DurationFormat * 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.
return { * You may obtain a copy of the License at
name: "platform/commonUI/formats", * http://www.apache.org/licenses/LICENSE-2.0.
definition: { *
"name": "Format Registry", * Unless required by applicable law or agreed to in writing, software
"description": "Provides a registry for formats, which allow parsing and formatting of values.", * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
"extensions": { * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
"components": [ * License for the specific language governing permissions and limitations
{ * under the License.
"provides": "formatService", *
"type": "provider", * Open MCT includes source code licensed under additional open source
"implementation": FormatProvider, * licenses. See the Open Source Licenses file (LICENSES.md) included with
"depends": [ * this source code distribution or the Licensing information page available
"formats[]" * at runtime from the About dialog for additional information.
] *****************************************************************************/
}
], import FormatProvider from './src/FormatProvider';
"formats": [
{ import DurationFormat from './src/DurationFormat';
"key": "duration",
"implementation": DurationFormat export default {
} name: "platform/commonUI/formats",
], definition: {
"constants": [ "name": "Format Registry",
{ "description": "Provides a registry for formats, which allow parsing and formatting of values.",
"key": "DEFAULT_TIME_FORMAT", "extensions": {
"value": "utc" "components": [
} {
], "provides": "formatService",
"licenses": [ "type": "provider",
{ "implementation": FormatProvider,
"name": "d3", "depends": [
"version": "3.0.0", "formats[]"
"description": "Incorporates modified code from d3 Time Scales", ]
"author": "Mike Bostock", }
"copyright": "Copyright 2010-2016 Mike Bostock. " ],
+ "All rights reserved.", "formats": [
"link": "https://github.com/d3/d3/blob/master/LICENSE" {
} "key": "duration",
] "implementation": DurationFormat
} }
],
"constants": [
{
"key": "DEFAULT_TIME_FORMAT",
"value": "utc"
}
],
"licenses": [
{
"name": "d3",
"version": "3.0.0",
"description": "Incorporates modified code from d3 Time Scales",
"author": "Mike Bostock",
"copyright": "Copyright 2010-2016 Mike Bostock. "
+ "All rights reserved.",
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
]
} }
}; }
}); };

View File

@ -20,43 +20,60 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
'moment' * Open MCT Web, Copyright (c) 2014-2015, United States Government
], function ( * as represented by the Administrator of the National Aeronautics and Space
moment * 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.
*****************************************************************************/
var DATE_FORMAT = "HH:mm:ss", import moment from 'moment';
DATE_FORMATS = [
DATE_FORMAT
];
/** var DATE_FORMAT = "HH:mm:ss",
* Formatter for duration. Uses moment to produce a date from a given DATE_FORMATS = [
* value, but output is formatted to display only time. Can be used for DATE_FORMAT
* specifying a time duration. For specifying duration, it's best to ];
* specify a date of January 1, 1970, as the ms offset will equal the
* duration represented by the time.
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function DurationFormat() {
this.key = "duration";
}
DurationFormat.prototype.format = function (value) { /**
return moment.utc(value).format(DATE_FORMAT); * Formatter for duration. Uses moment to produce a date from a given
}; * value, but output is formatted to display only time. Can be used for
* specifying a time duration. For specifying duration, it's best to
* specify a date of January 1, 1970, as the ms offset will equal the
* duration represented by the time.
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function DurationFormat() {
this.key = "duration";
}
DurationFormat.prototype.parse = function (text) { DurationFormat.prototype.format = function (value) {
return moment.duration(text).asMilliseconds(); return moment.utc(value).format(DATE_FORMAT);
}; };
DurationFormat.prototype.validate = function (text) { DurationFormat.prototype.parse = function (text) {
return moment.utc(text, DATE_FORMATS, true).isValid(); return moment.duration(text).asMilliseconds();
}; };
return DurationFormat; DurationFormat.prototype.validate = function (text) {
}); return moment.utc(text, DATE_FORMATS, true).isValid();
};
export default DurationFormat;

View File

@ -20,104 +20,49 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
], function ( function FormatProvider(formats) {
var formatMap = {};
) { function addToMap(Format) {
var key = Format.key;
/** if (key && !formatMap[key]) {
* An object used to convert between numeric values and text values, formatMap[key] = new Format();
* typically used to display these values to the user and to convert
* user input to a numeric format, particularly for time formats.
* @interface Format
*/
/**
* Parse text (typically user input) to a numeric value.
* Behavior is undefined when the text cannot be parsed;
* `validate` should be called first if the text may be invalid.
* @method Format#parse
* @memberof Format#
* @param {string} text the text to parse
* @returns {number} the parsed numeric value
*/
/**
* @property {string} key A unique identifier for this formatter.
* @memberof Format#
*/
/**
* Determine whether or not some text (typically user input) can
* be parsed to a numeric value by this format.
* @method validate
* @memberof Format#
* @param {string} text the text to parse
* @returns {boolean} true if the text can be parsed
*/
/**
* Convert a numeric value to a text value for display using
* this format.
* @method format
* @memberof Format#
* @param {number} value the numeric value to format
* @param {number} [minValue] Contextual information for scaled formatting used in linear scales such as conductor
* and plot axes. Specifies the smallest number on the scale.
* @param {number} [maxValue] Contextual information for scaled formatting used in linear scales such as conductor
* and plot axes. Specifies the largest number on the scale
* @param {number} [count] Contextual information for scaled formatting used in linear scales such as conductor
* and plot axes. The number of labels on the scale.
* @returns {string} the text representation of the value
*/
/**
* Provides access to `Format` objects which can be used to
* convert values between human-readable text and numeric
* representations.
* @interface FormatService
*/
/**
* Look up a format by its symbolic identifier.
* @method getFormat
* @memberof FormatService#
* @param {string} key the identifier for this format
* @returns {Format} the format
* @throws {Error} errors when the requested format is unrecognized
*/
/**
* Provides formats from the `formats` extension category.
* @constructor
* @implements {FormatService}
* @memberof platform/commonUI/formats
* @param {Array.<function(new : Format)>} format constructors,
* from the `formats` extension category.
*/
function FormatProvider(formats) {
var formatMap = {};
function addToMap(Format) {
var key = Format.key;
if (key && !formatMap[key]) {
formatMap[key] = new Format();
}
} }
formats.forEach(addToMap);
this.formatMap = formatMap;
} }
FormatProvider.prototype.getFormat = function (key) { formats.forEach(addToMap);
var format = this.formatMap[key]; this.formatMap = formatMap;
if (!format) { }
throw new Error("FormatProvider: No format found for " + key);
}
return format; FormatProvider.prototype.getFormat = function (key) {
}; var format = this.formatMap[key];
if (!format) {
throw new Error("FormatProvider: No format found for " + key);
}
return FormatProvider; return format;
};
}); export default FormatProvider;

View File

@ -20,50 +20,68 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
['../src/FormatProvider'], * Open MCT, Copyright (c) 2014-2021, United States Government
function (FormatProvider) { * 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.
*****************************************************************************/
var KEYS = ['a', 'b', 'c']; import FormatProvider from '../src/FormatProvider';
describe("The FormatProvider", function () { var KEYS = ['a', 'b', 'c'];
var mockFormats,
mockFormatInstances,
provider;
beforeEach(function () { describe("The FormatProvider", function () {
mockFormatInstances = KEYS.map(function (k) { var mockFormats,
return jasmine.createSpyObj( mockFormatInstances,
'format-' + k, provider;
['parse', 'validate', 'format']
);
});
// Return constructors
mockFormats = KEYS.map(function (k, i) {
function MockFormat() {
return mockFormatInstances[i];
}
MockFormat.key = k;
return MockFormat;
});
provider = new FormatProvider(mockFormats);
});
it("looks up formats by key", function () {
KEYS.forEach(function (k, i) {
expect(provider.getFormat(k))
.toEqual(mockFormatInstances[i]);
});
});
it("throws an error about unknown formats", function () {
expect(function () {
provider.getFormat('some-unknown-format');
}).toThrow();
});
beforeEach(function () {
mockFormatInstances = KEYS.map(function (k) {
return jasmine.createSpyObj(
'format-' + k,
['parse', 'validate', 'format']
);
}); });
} // Return constructors
); mockFormats = KEYS.map(function (k, i) {
function MockFormat() {
return mockFormatInstances[i];
}
MockFormat.key = k;
return MockFormat;
});
provider = new FormatProvider(mockFormats);
});
it("looks up formats by key", function () {
KEYS.forEach(function (k, i) {
expect(provider.getFormat(k))
.toEqual(mockFormatInstances[i]);
});
});
it("throws an error about unknown formats", function () {
expect(function () {
provider.getFormat('some-unknown-format');
}).toThrow();
});
});

View File

@ -20,510 +20,483 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
"./src/services/UrlService", * Open MCT, Copyright (c) 2014-2021, United States Government
"./src/services/PopupService", * as represented by the Administrator of the National Aeronautics and Space
"./src/SplashScreenManager", * Administration. All rights reserved.
"./src/StyleSheetLoader", *
"./src/controllers/TimeRangeController", * Open MCT is licensed under the Apache License, Version 2.0 (the
"./src/controllers/DateTimePickerController", * "License"); you may not use this file except in compliance with the License.
"./src/controllers/DateTimeFieldController", * You may obtain a copy of the License at
"./src/controllers/TreeNodeController", * http://www.apache.org/licenses/LICENSE-2.0.
"./src/controllers/ActionGroupController", *
"./src/controllers/ToggleController", * Unless required by applicable law or agreed to in writing, software
"./src/controllers/ClickAwayController", * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
"./src/controllers/ViewSwitcherController", * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
"./src/controllers/GetterSetterController", * License for the specific language governing permissions and limitations
"./src/controllers/SelectorController", * under the License.
"./src/controllers/ObjectInspectorController", *
"./src/controllers/BannerController", * Open MCT includes source code licensed under additional open source
"./src/directives/MCTContainer", * licenses. See the Open Source Licenses file (LICENSES.md) included with
"./src/directives/MCTDrag", * this source code distribution or the Licensing information page available
"./src/directives/MCTSelectable", * at runtime from the About dialog for additional information.
"./src/directives/MCTClickElsewhere", *****************************************************************************/
"./src/directives/MCTResize",
"./src/directives/MCTPopup",
"./src/directives/MCTScroll",
"./src/directives/MCTSplitPane",
"./src/directives/MCTSplitter",
"./src/directives/MCTTree",
"./src/directives/MCTIndicators",
"./src/filters/ReverseFilter",
"./res/templates/bottombar.html",
"./res/templates/controls/action-button.html",
"./res/templates/controls/input-filter.html",
"./res/templates/angular-indicator.html",
"./res/templates/message-banner.html",
"./res/templates/progress-bar.html",
"./res/templates/controls/time-controller.html",
"./res/templates/containers/accordion.html",
"./res/templates/subtree.html",
"./res/templates/tree.html",
"./res/templates/tree-node.html",
"./res/templates/label.html",
"./res/templates/controls/action-group.html",
"./res/templates/controls/switcher.html",
"./res/templates/object-inspector.html",
"./res/templates/controls/selector.html",
"./res/templates/controls/datetime-picker.html",
"./res/templates/controls/datetime-field.html"
], function (
UrlService,
PopupService,
SplashScreenManager,
StyleSheetLoader,
TimeRangeController,
DateTimePickerController,
DateTimeFieldController,
TreeNodeController,
ActionGroupController,
ToggleController,
ClickAwayController,
ViewSwitcherController,
GetterSetterController,
SelectorController,
ObjectInspectorController,
BannerController,
MCTContainer,
MCTDrag,
MCTSelectable,
MCTClickElsewhere,
MCTResize,
MCTPopup,
MCTScroll,
MCTSplitPane,
MCTSplitter,
MCTTree,
MCTIndicators,
ReverseFilter,
bottombarTemplate,
actionButtonTemplate,
inputFilterTemplate,
indicatorTemplate,
messageBannerTemplate,
progressBarTemplate,
timeControllerTemplate,
accordionTemplate,
subtreeTemplate,
treeTemplate,
treeNodeTemplate,
labelTemplate,
actionGroupTemplate,
switcherTemplate,
objectInspectorTemplate,
selectorTemplate,
datetimePickerTemplate,
datetimeFieldTemplate
) {
return { import UrlService from './src/services/UrlService';
name: "platform/commonUI/general",
definition: { import PopupService from './src/services/PopupService';
"name": "General UI elements", import SplashScreenManager from './src/SplashScreenManager';
"description": "General UI elements, meant to be reused across modes", import StyleSheetLoader from './src/StyleSheetLoader';
"resources": "res", import TimeRangeController from './src/controllers/TimeRangeController';
"extensions": { import DateTimePickerController from './src/controllers/DateTimePickerController';
"services": [ import DateTimeFieldController from './src/controllers/DateTimeFieldController';
{ import TreeNodeController from './src/controllers/TreeNodeController';
"key": "urlService", import ActionGroupController from './src/controllers/ActionGroupController';
"implementation": UrlService, import ToggleController from './src/controllers/ToggleController';
"depends": [ import ClickAwayController from './src/controllers/ClickAwayController';
"$location" import ViewSwitcherController from './src/controllers/ViewSwitcherController';
] import GetterSetterController from './src/controllers/GetterSetterController';
}, import SelectorController from './src/controllers/SelectorController';
{ import ObjectInspectorController from './src/controllers/ObjectInspectorController';
"key": "popupService", import BannerController from './src/controllers/BannerController';
"implementation": PopupService, import MCTContainer from './src/directives/MCTContainer';
"depends": [ import MCTDrag from './src/directives/MCTDrag';
"$document", import MCTSelectable from './src/directives/MCTSelectable';
"$window" import MCTClickElsewhere from './src/directives/MCTClickElsewhere';
] import MCTResize from './src/directives/MCTResize';
} import MCTPopup from './src/directives/MCTPopup';
], import MCTScroll from './src/directives/MCTScroll';
"runs": [ import MCTSplitPane from './src/directives/MCTSplitPane';
{ import MCTSplitter from './src/directives/MCTSplitter';
"implementation": StyleSheetLoader, import MCTTree from './src/directives/MCTTree';
"depends": [ import MCTIndicators from './src/directives/MCTIndicators';
"stylesheets[]", import ReverseFilter from './src/filters/ReverseFilter';
"$document", import bottombarTemplate from './res/templates/bottombar.html';
"THEME", import actionButtonTemplate from './res/templates/controls/action-button.html';
"ASSETS_PATH" import inputFilterTemplate from './res/templates/controls/input-filter.html';
] import indicatorTemplate from './res/templates/angular-indicator.html';
}, import messageBannerTemplate from './res/templates/message-banner.html';
{ import progressBarTemplate from './res/templates/progress-bar.html';
"implementation": SplashScreenManager, import timeControllerTemplate from './res/templates/controls/time-controller.html';
"depends": [ import accordionTemplate from './res/templates/containers/accordion.html';
"$document" import subtreeTemplate from './res/templates/subtree.html';
] import treeTemplate from './res/templates/tree.html';
} import treeNodeTemplate from './res/templates/tree-node.html';
], import labelTemplate from './res/templates/label.html';
"filters": [ import actionGroupTemplate from './res/templates/controls/action-group.html';
{ import switcherTemplate from './res/templates/controls/switcher.html';
"implementation": ReverseFilter, import objectInspectorTemplate from './res/templates/object-inspector.html';
"key": "reverse" import selectorTemplate from './res/templates/controls/selector.html';
} import datetimePickerTemplate from './res/templates/controls/datetime-picker.html';
], import datetimeFieldTemplate from './res/templates/controls/datetime-field.html';
"templates": [
{ export default {
"key": "bottombar", name: "platform/commonUI/general",
"template": bottombarTemplate definition: {
}, "name": "General UI elements",
{ "description": "General UI elements, meant to be reused across modes",
"key": "action-button", "resources": "res",
"template": actionButtonTemplate "extensions": {
}, "services": [
{ {
"key": "input-filter", "key": "urlService",
"template": inputFilterTemplate "implementation": UrlService,
}, "depends": [
{ "$location"
"key": "indicator", ]
"template": indicatorTemplate },
}, {
{ "key": "popupService",
"key": "message-banner", "implementation": PopupService,
"template": messageBannerTemplate "depends": [
}, "$document",
{ "$window"
"key": "progress-bar", ]
"template": progressBarTemplate }
}, ],
{ "runs": [
"key": "time-controller", {
"template": timeControllerTemplate "implementation": StyleSheetLoader,
} "depends": [
], "stylesheets[]",
"controllers": [ "$document",
{ "THEME",
"key": "TimeRangeController", "ASSETS_PATH"
"implementation": TimeRangeController, ]
"depends": [ },
"$scope", {
"$timeout", "implementation": SplashScreenManager,
"formatService", "depends": [
"DEFAULT_TIME_FORMAT", "$document"
"now" ]
] }
}, ],
{ "filters": [
"key": "DateTimePickerController", {
"implementation": DateTimePickerController, "implementation": ReverseFilter,
"depends": [ "key": "reverse"
"$scope", }
"now" ],
] "templates": [
}, {
{ "key": "bottombar",
"key": "DateTimeFieldController", "template": bottombarTemplate
"implementation": DateTimeFieldController, },
"depends": [ {
"$scope", "key": "action-button",
"formatService", "template": actionButtonTemplate
"DEFAULT_TIME_FORMAT" },
] {
}, "key": "input-filter",
{ "template": inputFilterTemplate
"key": "TreeNodeController", },
"implementation": TreeNodeController, {
"depends": [ "key": "indicator",
"$scope", "template": indicatorTemplate
"$timeout", },
"navigationService" {
] "key": "message-banner",
}, "template": messageBannerTemplate
{ },
"key": "ActionGroupController", {
"implementation": ActionGroupController, "key": "progress-bar",
"depends": [ "template": progressBarTemplate
"$scope" },
] {
}, "key": "time-controller",
{ "template": timeControllerTemplate
"key": "ToggleController", }
"implementation": ToggleController ],
}, "controllers": [
{ {
"key": "ClickAwayController", "key": "TimeRangeController",
"implementation": ClickAwayController, "implementation": TimeRangeController,
"depends": [ "depends": [
"$document", "$scope",
"$timeout" "$timeout",
] "formatService",
}, "DEFAULT_TIME_FORMAT",
{ "now"
"key": "ViewSwitcherController", ]
"implementation": ViewSwitcherController, },
"depends": [ {
"$scope", "key": "DateTimePickerController",
"$timeout" "implementation": DateTimePickerController,
] "depends": [
}, "$scope",
{ "now"
"key": "GetterSetterController", ]
"implementation": GetterSetterController, },
"depends": [ {
"$scope" "key": "DateTimeFieldController",
] "implementation": DateTimeFieldController,
}, "depends": [
{ "$scope",
"key": "SelectorController", "formatService",
"implementation": SelectorController, "DEFAULT_TIME_FORMAT"
"depends": [ ]
"objectService", },
"$scope" {
] "key": "TreeNodeController",
}, "implementation": TreeNodeController,
{ "depends": [
"key": "ObjectInspectorController", "$scope",
"implementation": ObjectInspectorController, "$timeout",
"depends": [ "navigationService"
"$scope", ]
"objectService" },
] {
}, "key": "ActionGroupController",
{ "implementation": ActionGroupController,
"key": "BannerController", "depends": [
"implementation": BannerController, "$scope"
"depends": [ ]
"$scope", },
"notificationService", {
"dialogService" "key": "ToggleController",
] "implementation": ToggleController
} },
], {
"directives": [ "key": "ClickAwayController",
{ "implementation": ClickAwayController,
"key": "mctContainer", "depends": [
"implementation": MCTContainer, "$document",
"depends": [ "$timeout"
"containers[]" ]
] },
}, {
{ "key": "ViewSwitcherController",
"key": "mctDrag", "implementation": ViewSwitcherController,
"implementation": MCTDrag, "depends": [
"depends": [ "$scope",
"$document", "$timeout"
"agentService" ]
] },
}, {
{ "key": "GetterSetterController",
"key": "mctSelectable", "implementation": GetterSetterController,
"implementation": MCTSelectable, "depends": [
"depends": [ "$scope"
"openmct" ]
] },
}, {
{ "key": "SelectorController",
"key": "mctClickElsewhere", "implementation": SelectorController,
"implementation": MCTClickElsewhere, "depends": [
"depends": [ "objectService",
"$document" "$scope"
] ]
}, },
{ {
"key": "mctResize", "key": "ObjectInspectorController",
"implementation": MCTResize, "implementation": ObjectInspectorController,
"depends": [ "depends": [
"$timeout" "$scope",
] "objectService"
}, ]
{ },
"key": "mctPopup", {
"implementation": MCTPopup, "key": "BannerController",
"depends": [ "implementation": BannerController,
"$compile", "depends": [
"popupService" "$scope",
] "notificationService",
}, "dialogService"
{ ]
"key": "mctScrollX", }
"implementation": MCTScroll, ],
"depends": [ "directives": [
"$parse", {
"MCT_SCROLL_X_PROPERTY", "key": "mctContainer",
"MCT_SCROLL_X_ATTRIBUTE" "implementation": MCTContainer,
] "depends": [
}, "containers[]"
{ ]
"key": "mctScrollY", },
"implementation": MCTScroll, {
"depends": [ "key": "mctDrag",
"$parse", "implementation": MCTDrag,
"MCT_SCROLL_Y_PROPERTY", "depends": [
"MCT_SCROLL_Y_ATTRIBUTE" "$document",
] "agentService"
}, ]
{ },
"key": "mctSplitPane", {
"implementation": MCTSplitPane, "key": "mctSelectable",
"depends": [ "implementation": MCTSelectable,
"$parse", "depends": [
"$log", "openmct"
"$interval", ]
"$window" },
] {
}, "key": "mctClickElsewhere",
{ "implementation": MCTClickElsewhere,
"key": "mctSplitter", "depends": [
"implementation": MCTSplitter "$document"
}, ]
{ },
"key": "mctTree", {
"implementation": MCTTree, "key": "mctResize",
"depends": ['gestureService', 'openmct'] "implementation": MCTResize,
}, "depends": [
{ "$timeout"
"key": "mctIndicators", ]
"implementation": MCTIndicators, },
"depends": ['openmct'] {
} "key": "mctPopup",
], "implementation": MCTPopup,
"constants": [ "depends": [
{ "$compile",
"key": "MCT_SCROLL_X_PROPERTY", "popupService"
"value": "scrollLeft" ]
}, },
{ {
"key": "MCT_SCROLL_X_ATTRIBUTE", "key": "mctScrollX",
"value": "mctScrollX" "implementation": MCTScroll,
}, "depends": [
{ "$parse",
"key": "MCT_SCROLL_Y_PROPERTY", "MCT_SCROLL_X_PROPERTY",
"value": "scrollTop" "MCT_SCROLL_X_ATTRIBUTE"
}, ]
{ },
"key": "MCT_SCROLL_Y_ATTRIBUTE", {
"value": "mctScrollY" "key": "mctScrollY",
}, "implementation": MCTScroll,
{ "depends": [
"key": "THEME", "$parse",
"value": "unspecified", "MCT_SCROLL_Y_PROPERTY",
"priority": "fallback" "MCT_SCROLL_Y_ATTRIBUTE"
}, ]
{ },
"key": "ASSETS_PATH", {
"value": ".", "key": "mctSplitPane",
"priority": "fallback" "implementation": MCTSplitPane,
} "depends": [
], "$parse",
"containers": [ "$log",
{ "$interval",
"key": "accordion", "$window"
"template": accordionTemplate, ]
"attributes": [ },
"label" {
] "key": "mctSplitter",
} "implementation": MCTSplitter
], },
"representations": [ {
{ "key": "mctTree",
"key": "tree", "implementation": MCTTree,
"template": subtreeTemplate, "depends": ['gestureService', 'openmct']
"uses": [ },
"composition" {
], "key": "mctIndicators",
"type": "root", "implementation": MCTIndicators,
"priority": "preferred" "depends": ['openmct']
}, }
{ ],
"key": "tree", "constants": [
"template": treeTemplate {
}, "key": "MCT_SCROLL_X_PROPERTY",
{ "value": "scrollLeft"
"key": "subtree", },
"template": subtreeTemplate, {
"uses": [ "key": "MCT_SCROLL_X_ATTRIBUTE",
"composition" "value": "mctScrollX"
] },
}, {
{ "key": "MCT_SCROLL_Y_PROPERTY",
"key": "tree-node", "value": "scrollTop"
"template": treeNodeTemplate, },
"uses": [ {
"action" "key": "MCT_SCROLL_Y_ATTRIBUTE",
] "value": "mctScrollY"
}, },
{ {
"key": "label", "key": "THEME",
"template": labelTemplate, "value": "unspecified",
"uses": [ "priority": "fallback"
"type", },
"location" {
], "key": "ASSETS_PATH",
"gestures": [ "value": ".",
"drag", "priority": "fallback"
"menu", }
"info" ],
] "containers": [
}, {
{ "key": "accordion",
"key": "node", "template": accordionTemplate,
"template": labelTemplate, "attributes": [
"uses": [ "label"
"type" ]
], }
"gestures": [ ],
"drag", "representations": [
"menu" {
] "key": "tree",
}, "template": subtreeTemplate,
{ "uses": [
"key": "action-group", "composition"
"template": actionGroupTemplate, ],
"uses": [ "type": "root",
"action" "priority": "preferred"
] },
}, {
{ "key": "tree",
"key": "switcher", "template": treeTemplate
"template": switcherTemplate, },
"uses": [ {
"view" "key": "subtree",
] "template": subtreeTemplate,
}, "uses": [
{ "composition"
"key": "object-inspector", ]
"template": objectInspectorTemplate },
} {
], "key": "tree-node",
"controls": [ "template": treeNodeTemplate,
{ "uses": [
"key": "selector", "action"
"template": selectorTemplate ]
}, },
{ {
"key": "datetime-picker", "key": "label",
"template": datetimePickerTemplate "template": labelTemplate,
}, "uses": [
{ "type",
"key": "datetime-field", "location"
"template": datetimeFieldTemplate ],
} "gestures": [
], "drag",
"licenses": [ "menu",
{ "info"
"name": "Normalize.css", ]
"version": "1.1.2", },
"description": "Browser style normalization", {
"author": "Nicolas Gallagher, Jonathan Neal", "key": "node",
"website": "http://necolas.github.io/normalize.css/", "template": labelTemplate,
"copyright": "Copyright (c) Nicolas Gallagher and Jonathan Neal", "uses": [
"license": "license-mit", "type"
"link": "https://github.com/necolas/normalize.css/blob/v1.1.2/LICENSE.md" ],
}, "gestures": [
{ "drag",
"name": "Zepto", "menu"
"version": "1.1.6", ]
"description": "DOM manipulation", },
"author": "Thomas Fuchs", {
"website": "http://zeptojs.com/", "key": "action-group",
"copyright": "Copyright (c) 2010-2016 Thomas Fuchs", "template": actionGroupTemplate,
"license": "license-mit", "uses": [
"link": "https://github.com/madrobby/zepto/blob/master/MIT-LICENSE" "action"
} ]
] },
} {
"key": "switcher",
"template": switcherTemplate,
"uses": [
"view"
]
},
{
"key": "object-inspector",
"template": objectInspectorTemplate
}
],
"controls": [
{
"key": "selector",
"template": selectorTemplate
},
{
"key": "datetime-picker",
"template": datetimePickerTemplate
},
{
"key": "datetime-field",
"template": datetimeFieldTemplate
}
],
"licenses": [
{
"name": "Normalize.css",
"version": "1.1.2",
"description": "Browser style normalization",
"author": "Nicolas Gallagher, Jonathan Neal",
"website": "http://necolas.github.io/normalize.css/",
"copyright": "Copyright (c) Nicolas Gallagher and Jonathan Neal",
"license": "license-mit",
"link": "https://github.com/necolas/normalize.css/blob/v1.1.2/LICENSE.md"
},
{
"name": "Zepto",
"version": "1.1.6",
"description": "DOM manipulation",
"author": "Thomas Fuchs",
"website": "http://zeptojs.com/",
"copyright": "Copyright (c) 2010-2016 Thomas Fuchs",
"license": "license-mit",
"link": "https://github.com/madrobby/zepto/blob/master/MIT-LICENSE"
}
]
} }
}; }
}); };

View File

@ -20,25 +20,40 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
], function ( function SplashScreenManager($document) {
var splash;
) { $document = $document[0];
splash = $document.querySelectorAll('.l-splash-holder')[0];
function SplashScreenManager($document) { if (!splash) {
var splash; return;
$document = $document[0];
splash = $document.querySelectorAll('.l-splash-holder')[0];
if (!splash) {
return;
}
splash.className += ' fadeout';
splash.addEventListener('transitionend', function () {
splash.parentNode.removeChild(splash);
});
} }
return SplashScreenManager; splash.className += ' fadeout';
}); splash.addEventListener('transitionend', function () {
splash.parentNode.removeChild(splash);
});
}
export default SplashScreenManager;

View File

@ -25,58 +25,68 @@
* platform styling. * platform styling.
* @namespace platform/commonUI/general * @namespace platform/commonUI/general
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* The StyleSheetLoader adds links to style sheets exposed from * This bundle provides various general-purpose UI elements, including
* various bundles as extensions of category `stylesheets`. * platform styling.
* @memberof platform/commonUI/general * @namespace platform/commonUI/general
* @constructor */
* @param {object[]} stylesheets stylesheet extension definitions function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) {
* @param $document Angular's jqLite-wrapped document element var head = $document.find('head'),
* @param {string} activeTheme the theme in use document = $document[0];
* @param {string} [assetPath] the directory relative to which
* stylesheets will be found
*/
function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) {
var head = $document.find('head'),
document = $document[0];
// Procedure for adding a single stylesheet // Procedure for adding a single stylesheet
function addStyleSheet(stylesheet) { function addStyleSheet(stylesheet) {
// Create a link element, and construct full path // Create a link element, and construct full path
var link = document.createElement('link'), var link = document.createElement('link'),
path = [ path = [
assetPath, assetPath,
stylesheet.bundle.path, stylesheet.bundle.path,
stylesheet.bundle.resources, stylesheet.bundle.resources,
stylesheet.stylesheetUrl stylesheet.stylesheetUrl
].join("/"); ].join("/");
// Initialize attributes on the link // Initialize attributes on the link
link.setAttribute("rel", "stylesheet"); link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css"); link.setAttribute("type", "text/css");
link.setAttribute("href", path); link.setAttribute("href", path);
// Append the link to the head element // Append the link to the head element
head.append(link); head.append(link);
}
// Stylesheets which specify themes should only be applied
// when that theme has been declared.
function matchesTheme(stylesheet) {
return stylesheet.theme === undefined
|| stylesheet.theme === activeTheme;
}
assetPath = assetPath || ".";
// Add all stylesheets from extensions
stylesheets.filter(matchesTheme).forEach(addStyleSheet);
}
return StyleSheetLoader;
} }
);
// Stylesheets which specify themes should only be applied
// when that theme has been declared.
function matchesTheme(stylesheet) {
return stylesheet.theme === undefined
|| stylesheet.theme === activeTheme;
}
assetPath = assetPath || ".";
// Add all stylesheets from extensions
stylesheets.filter(matchesTheme).forEach(addStyleSheet);
}
export default StyleSheetLoader;

View File

@ -23,82 +23,85 @@
/** /**
* Module defining ActionGroupController. Created by vwoeltje on 11/14/14. * Module defining ActionGroupController. Created by vwoeltje on 11/14/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* Controller which keeps an up-to-date list of actions of * Module defining ActionGroupController. Created by vwoeltje on 11/14/14.
* a certain category, and additionally bins them into */
* groups as described by their metadata. Used specifically function ActionGroupController($scope) {
* to support button groups.
*
* This will maintain two fields in the scope:
* * `groups`: An array of arrays. Each element in the outer
* array corresponds to a group; the inner array contains
* the actions which are in that group.
* * `ungrouped`: All actions which did not have a defined
* group.
*
* @memberof platform/commonUI/general
* @constructor
*/
function ActionGroupController($scope) {
// Separate out the actions that have been retrieved // Separate out the actions that have been retrieved
// into groups, and populate scope with this. // into groups, and populate scope with this.
function groupActions(actions) { function groupActions(actions) {
var groups = {}, var groups = {},
ungrouped = []; ungrouped = [];
function assignToGroup(action) { function assignToGroup(action) {
var metadata = action.getMetadata(), var metadata = action.getMetadata(),
group = metadata.group; group = metadata.group;
if (group) { if (group) {
groups[group] = groups[group] || []; groups[group] = groups[group] || [];
groups[group].push(action); groups[group].push(action);
} else { } else {
ungrouped.push(action); ungrouped.push(action);
}
}
(actions || []).forEach(assignToGroup);
$scope.ungrouped = ungrouped;
$scope.groups = Object.keys(groups).sort().map(function (k) {
return groups[k];
});
} }
// Callback for when state which might influence action groupings
// changes.
function updateGroups() {
var actionCapability = $scope.action,
params = $scope.parameters || {},
category = params.category;
if (actionCapability && category) {
// Get actions by capability, and group them
groupActions(actionCapability.getActions({
category: category
}));
} else {
// We don't have enough information to get any actions.
groupActions([]);
}
}
// Changes to the represented object, to its action capability, or
// to the chosen action category may all require an update.
$scope.$watch("domainObject", updateGroups);
$scope.$watch("action", updateGroups);
$scope.$watch("parameters.category", updateGroups);
// Start with empty arrays.
$scope.ungrouped = [];
$scope.groups = [];
} }
return ActionGroupController; (actions || []).forEach(assignToGroup);
$scope.ungrouped = ungrouped;
$scope.groups = Object.keys(groups).sort().map(function (k) {
return groups[k];
});
} }
);
// Callback for when state which might influence action groupings
// changes.
function updateGroups() {
var actionCapability = $scope.action,
params = $scope.parameters || {},
category = params.category;
if (actionCapability && category) {
// Get actions by capability, and group them
groupActions(actionCapability.getActions({
category: category
}));
} else {
// We don't have enough information to get any actions.
groupActions([]);
}
}
// Changes to the represented object, to its action capability, or
// to the chosen action category may all require an update.
$scope.$watch("domainObject", updateGroups);
$scope.$watch("action", updateGroups);
$scope.$watch("parameters.category", updateGroups);
// Start with empty arrays.
$scope.ungrouped = [];
$scope.groups = [];
}
export default ActionGroupController;

View File

@ -20,58 +20,62 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function BannerController($scope, notificationService, dialogService) {
* A controller for banner notifications. Banner notifications are a $scope.active = notificationService.active;
* non-blocking way of drawing the user's attention to an event such
* as system errors, or the progress or successful completion of an $scope.action = function (action, $event) {
* ongoing task. This controller provides scoped functions for /*
* dismissing and 'maximizing' notifications. See {@link NotificationService} Prevents default 'maximize' behaviour when clicking on
* for more details on Notifications. notification button
*
* @param $scope
* @param notificationService
* @param dialogService
* @constructor
*/ */
function BannerController($scope, notificationService, dialogService) { $event.stopPropagation();
$scope.active = notificationService.active;
$scope.action = function (action, $event) { return action();
/* };
Prevents default 'maximize' behaviour when clicking on
notification button
*/
$event.stopPropagation();
return action(); $scope.dismiss = function (notification, $event) {
$event.stopPropagation();
notification.dismiss();
};
$scope.maximize = function (notification) {
if (notification.model.severity !== "info") {
var dialog;
notification.model.cancel = function () {
dialog.dismiss();
}; };
$scope.dismiss = function (notification, $event) { //If the notification is dismissed by the user, close
$event.stopPropagation(); // the dialog.
notification.dismiss(); notification.on('dismiss', function () {
}; dialog.dismiss();
});
$scope.maximize = function (notification) { dialog = dialogService.showBlockingMessage(notification.model);
if (notification.model.severity !== "info") {
var dialog;
notification.model.cancel = function () {
dialog.dismiss();
};
//If the notification is dismissed by the user, close
// the dialog.
notification.on('dismiss', function () {
dialog.dismiss();
});
dialog = dialogService.showBlockingMessage(notification.model);
}
};
} }
};
}
return BannerController; export default BannerController;
});

View File

@ -20,78 +20,84 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function ClickAwayController($document, $timeout) {
* A ClickAwayController is used to toggle things (such as context var self = this;
* menus) where clicking elsewhere in the document while the toggle
* is in an active state is intended to dismiss the toggle.
*
* @memberof platform/commonUI/general
* @constructor
* @param $scope the scope in which this controller is active
* @param $document the document element, injected by Angular
*/
function ClickAwayController($document, $timeout) {
var self = this;
this.state = false; this.state = false;
this.$document = $document; this.$document = $document;
// Callback used by the document listener. Timeout ensures that // Callback used by the document listener. Timeout ensures that
// `clickaway` action occurs after `toggle` if `toggle` is // `clickaway` action occurs after `toggle` if `toggle` is
// triggered by a click/mouseup. // triggered by a click/mouseup.
this.clickaway = function () { this.clickaway = function () {
$timeout(function () { $timeout(function () {
self.deactivate(); self.deactivate();
}); });
}; };
} }
// Track state, but also attach and detach a listener for // Track state, but also attach and detach a listener for
// mouseup events on the document. // mouseup events on the document.
ClickAwayController.prototype.deactivate = function () { ClickAwayController.prototype.deactivate = function () {
this.state = false; this.state = false;
this.$document.off("mouseup", this.clickaway); this.$document.off("mouseup", this.clickaway);
}; };
ClickAwayController.prototype.activate = function () { ClickAwayController.prototype.activate = function () {
this.state = true; this.state = true;
this.$document.on("mouseup", this.clickaway); this.$document.on("mouseup", this.clickaway);
}; };
/** /**
* Get the current state of the toggle. * Get the current state of the toggle.
* @return {boolean} true if active * @return {boolean} true if active
*/ */
ClickAwayController.prototype.isActive = function () { ClickAwayController.prototype.isActive = function () {
return this.state; return this.state;
}; };
/** /**
* Set a new state for the toggle. * Set a new state for the toggle.
* @return {boolean} true to activate * @return {boolean} true to activate
*/ */
ClickAwayController.prototype.setState = function (newState) { ClickAwayController.prototype.setState = function (newState) {
if (this.state !== newState) { if (this.state !== newState) {
this.toggle(); this.toggle();
}
};
/**
* Toggle the current state; activate if it is inactive,
* deactivate if it is active.
*/
ClickAwayController.prototype.toggle = function () {
if (this.state) {
this.deactivate();
} else {
this.activate();
}
};
return ClickAwayController;
} }
); };
/**
* Toggle the current state; activate if it is inactive,
* deactivate if it is active.
*/
ClickAwayController.prototype.toggle = function () {
if (this.state) {
this.deactivate();
} else {
this.activate();
}
};
export default ClickAwayController;

View File

@ -20,93 +20,92 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function DateTimeFieldController($scope, formatService, defaultFormat) {
* Controller to support the date-time entry field. var formatter = formatService.getFormat(defaultFormat);
*
* Accepts a `format` property in the `structure` attribute
* which allows a date/time to be specified via its symbolic
* key (as will be used to look up said format from the
* `formatService`.)
*
* {@see FormatService}
* @constructor
* @memberof platform/commonUI/general
* @param $scope the Angular scope for this controller
* @param {FormatService} formatService the service to user to format
* domain values
* @param {string} defaultFormat the format to request when no
* format has been otherwise specified
*/
function DateTimeFieldController($scope, formatService, defaultFormat) {
var formatter = formatService.getFormat(defaultFormat);
function updateFromModel(value) { function updateFromModel(value) {
// Only reformat if the value is different from user // Only reformat if the value is different from user
// input (to avoid reformatting valid input while typing.) // input (to avoid reformatting valid input while typing.)
if (!formatter.validate($scope.textValue) if (!formatter.validate($scope.textValue)
|| formatter.parse($scope.textValue) !== value) { || formatter.parse($scope.textValue) !== value) {
$scope.textValue = formatter.format(value); $scope.textValue = formatter.format(value);
$scope.textInvalid = false; $scope.textInvalid = false;
$scope.lastValidValue = $scope.textValue; $scope.lastValidValue = $scope.textValue;
}
$scope.pickerModel = { value: value };
}
function updateFromView(textValue) {
$scope.textInvalid = !formatter.validate(textValue);
if (!$scope.textInvalid) {
$scope.ngModel[$scope.field] =
formatter.parse(textValue);
$scope.lastValidValue = $scope.textValue;
}
}
function updateFromPicker(value) {
if (value !== $scope.ngModel[$scope.field]) {
$scope.ngModel[$scope.field] = value;
updateFromModel(value);
if ($scope.ngBlur) {
$scope.ngBlur();
}
// If picker is active, dismiss it when valid value has been selected
// This 'if' is to avoid unnecessary validation if picker is not active
if ($scope.picker.active) {
if ($scope.structure.validate && $scope.structure.validate($scope.ngModel[$scope.field])) {
$scope.picker.active = false;
} else if (!$scope.structure.validate) {
//If picker visible, but no validation function, hide picker
$scope.picker.active = false;
}
}
}
}
function setFormat(format) {
formatter = formatService.getFormat(format || defaultFormat);
updateFromModel($scope.ngModel[$scope.field]);
}
function restoreTextValue() {
$scope.textValue = $scope.lastValidValue;
updateFromView($scope.textValue);
}
$scope.restoreTextValue = restoreTextValue;
$scope.picker = { active: false };
$scope.$watch('structure.format', setFormat);
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watch('pickerModel.value', updateFromPicker);
$scope.$watch('textValue', updateFromView);
} }
return DateTimeFieldController; $scope.pickerModel = { value: value };
} }
);
function updateFromView(textValue) {
$scope.textInvalid = !formatter.validate(textValue);
if (!$scope.textInvalid) {
$scope.ngModel[$scope.field] =
formatter.parse(textValue);
$scope.lastValidValue = $scope.textValue;
}
}
function updateFromPicker(value) {
if (value !== $scope.ngModel[$scope.field]) {
$scope.ngModel[$scope.field] = value;
updateFromModel(value);
if ($scope.ngBlur) {
$scope.ngBlur();
}
// If picker is active, dismiss it when valid value has been selected
// This 'if' is to avoid unnecessary validation if picker is not active
if ($scope.picker.active) {
if ($scope.structure.validate && $scope.structure.validate($scope.ngModel[$scope.field])) {
$scope.picker.active = false;
} else if (!$scope.structure.validate) {
//If picker visible, but no validation function, hide picker
$scope.picker.active = false;
}
}
}
}
function setFormat(format) {
formatter = formatService.getFormat(format || defaultFormat);
updateFromModel($scope.ngModel[$scope.field]);
}
function restoreTextValue() {
$scope.textValue = $scope.lastValidValue;
updateFromView($scope.textValue);
}
$scope.restoreTextValue = restoreTextValue;
$scope.picker = { active: false };
$scope.$watch('structure.format', setFormat);
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watch('pickerModel.value', updateFromPicker);
$scope.$watch('textValue', updateFromView);
}
export default DateTimeFieldController;

View File

@ -20,188 +20,206 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
['moment'], * Open MCT, Copyright (c) 2014-2021, United States Government
function (moment) { * 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.
*****************************************************************************/
var TIME_NAMES = { import moment from 'moment';
'hours': "Hour",
'minutes': "Minute",
'seconds': "Second"
},
MONTHS = moment.months(),
TIME_OPTIONS = (function makeRanges() {
var arr = [];
while (arr.length < 60) {
arr.push(arr.length);
}
return { var TIME_NAMES = {
hours: arr.slice(0, 24), 'hours': "Hour",
minutes: arr, 'minutes': "Minute",
seconds: arr 'seconds': "Second"
}; },
}()); MONTHS = moment.months(),
TIME_OPTIONS = (function makeRanges() {
/** var arr = [];
* Controller to support the date-time picker. while (arr.length < 60) {
* arr.push(arr.length);
* Adds/uses the following properties in scope:
* * `year`: Year being displayed in picker
* * `month`: Month being displayed
* * `table`: Table being displayed; array of arrays of
* * `day`: Day of month
* * `dayOfYear`: Day of year
* * `month`: Month associated with the day
* * `year`: Year associated with the day.
* * `date`: Date chosen
* * `year`: Year selected
* * `month`: Month selected (0-indexed)
* * `day`: Day of month selected
* * `time`: Chosen time (hours/minutes/seconds)
* * `hours`: Hours chosen
* * `minutes`: Minutes chosen
* * `seconds`: Seconds chosen
*
* Months are zero-indexed, day-of-months are one-indexed.
*/
function DateTimePickerController($scope, now) {
var year,
month, // For picker state, not model state
interacted = false;
function generateTable() {
var m = moment.utc({
year: year,
month: month
}).day(0),
table = [],
row,
col;
for (row = 0; row < 6; row += 1) {
table.push([]);
for (col = 0; col < 7; col += 1) {
table[row].push({
year: m.year(),
month: m.month(),
day: m.date(),
dayOfYear: m.dayOfYear()
});
m.add(1, 'days'); // Next day!
}
}
return table;
}
function updateScopeForMonth() {
$scope.month = MONTHS[month];
$scope.year = year;
$scope.table = generateTable();
}
function updateFromModel(ngModel) {
var m;
m = moment.utc(ngModel);
$scope.date = {
year: m.year(),
month: m.month(),
day: m.date()
};
$scope.time = {
hours: m.hour(),
minutes: m.minute(),
seconds: m.second()
};
//window.alert($scope.date.day + " " + ngModel);
// Zoom to that date in the picker, but
// only if the user hasn't interacted with it yet.
if (!interacted) {
year = m.year();
month = m.month();
updateScopeForMonth();
}
}
function updateFromView() {
var m = moment.utc({
year: $scope.date.year,
month: $scope.date.month,
day: $scope.date.day,
hour: $scope.time.hours,
minute: $scope.time.minutes,
second: $scope.time.seconds
});
$scope.ngModel[$scope.field] = m.valueOf();
}
$scope.isInCurrentMonth = function (cell) {
return cell.month === month;
};
$scope.isSelected = function (cell) {
var date = $scope.date || {};
return cell.day === date.day
&& cell.month === date.month
&& cell.year === date.year;
};
$scope.select = function (cell) {
$scope.date = $scope.date || {};
$scope.date.month = cell.month;
$scope.date.year = cell.year;
$scope.date.day = cell.day;
updateFromView();
};
$scope.dateEquals = function (d1, d2) {
return d1.year === d2.year
&& d1.month === d2.month
&& d1.day === d2.day;
};
$scope.changeMonth = function (delta) {
month += delta;
if (month > 11) {
month = 0;
year += 1;
}
if (month < 0) {
month = 11;
year -= 1;
}
interacted = true;
updateScopeForMonth();
};
$scope.nameFor = function (key) {
return TIME_NAMES[key];
};
$scope.optionsFor = function (key) {
return TIME_OPTIONS[key];
};
updateScopeForMonth();
// Ensure some useful default
$scope.ngModel[$scope.field] =
$scope.ngModel[$scope.field] === undefined
? now() : $scope.ngModel[$scope.field];
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watchCollection('date', updateFromView);
$scope.$watchCollection('time', updateFromView);
} }
return DateTimePickerController; return {
hours: arr.slice(0, 24),
minutes: arr,
seconds: arr
};
}());
/**
* Controller to support the date-time picker.
*
* Adds/uses the following properties in scope:
* * `year`: Year being displayed in picker
* * `month`: Month being displayed
* * `table`: Table being displayed; array of arrays of
* * `day`: Day of month
* * `dayOfYear`: Day of year
* * `month`: Month associated with the day
* * `year`: Year associated with the day.
* * `date`: Date chosen
* * `year`: Year selected
* * `month`: Month selected (0-indexed)
* * `day`: Day of month selected
* * `time`: Chosen time (hours/minutes/seconds)
* * `hours`: Hours chosen
* * `minutes`: Minutes chosen
* * `seconds`: Seconds chosen
*
* Months are zero-indexed, day-of-months are one-indexed.
*/
function DateTimePickerController($scope, now) {
var year,
month, // For picker state, not model state
interacted = false;
function generateTable() {
var m = moment.utc({
year: year,
month: month
}).day(0),
table = [],
row,
col;
for (row = 0; row < 6; row += 1) {
table.push([]);
for (col = 0; col < 7; col += 1) {
table[row].push({
year: m.year(),
month: m.month(),
day: m.date(),
dayOfYear: m.dayOfYear()
});
m.add(1, 'days'); // Next day!
}
}
return table;
} }
);
function updateScopeForMonth() {
$scope.month = MONTHS[month];
$scope.year = year;
$scope.table = generateTable();
}
function updateFromModel(ngModel) {
var m;
m = moment.utc(ngModel);
$scope.date = {
year: m.year(),
month: m.month(),
day: m.date()
};
$scope.time = {
hours: m.hour(),
minutes: m.minute(),
seconds: m.second()
};
//window.alert($scope.date.day + " " + ngModel);
// Zoom to that date in the picker, but
// only if the user hasn't interacted with it yet.
if (!interacted) {
year = m.year();
month = m.month();
updateScopeForMonth();
}
}
function updateFromView() {
var m = moment.utc({
year: $scope.date.year,
month: $scope.date.month,
day: $scope.date.day,
hour: $scope.time.hours,
minute: $scope.time.minutes,
second: $scope.time.seconds
});
$scope.ngModel[$scope.field] = m.valueOf();
}
$scope.isInCurrentMonth = function (cell) {
return cell.month === month;
};
$scope.isSelected = function (cell) {
var date = $scope.date || {};
return cell.day === date.day
&& cell.month === date.month
&& cell.year === date.year;
};
$scope.select = function (cell) {
$scope.date = $scope.date || {};
$scope.date.month = cell.month;
$scope.date.year = cell.year;
$scope.date.day = cell.day;
updateFromView();
};
$scope.dateEquals = function (d1, d2) {
return d1.year === d2.year
&& d1.month === d2.month
&& d1.day === d2.day;
};
$scope.changeMonth = function (delta) {
month += delta;
if (month > 11) {
month = 0;
year += 1;
}
if (month < 0) {
month = 11;
year -= 1;
}
interacted = true;
updateScopeForMonth();
};
$scope.nameFor = function (key) {
return TIME_NAMES[key];
};
$scope.optionsFor = function (key) {
return TIME_OPTIONS[key];
};
updateScopeForMonth();
// Ensure some useful default
$scope.ngModel[$scope.field] =
$scope.ngModel[$scope.field] === undefined
? now() : $scope.ngModel[$scope.field];
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watchCollection('date', updateFromView);
$scope.$watchCollection('time', updateFromView);
}
export default DateTimePickerController;

View File

@ -20,70 +20,53 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function GetterSetterController($scope) {
* This controller acts as an adapter to permit getter-setter
* functions to be used as ng-model arguments to controls,
* such as the input-filter. This is supported natively in
* Angular 1.3+ via `ng-model-options`, so this controller
* should be made obsolete after any upgrade to Angular 1.3.
*
* It expects to find in scope a value `ngModel` which is a
* function which, when called with no arguments, acts as a
* getter, and when called with one argument, acts as a setter.
*
* It also publishes into the scope a value `getterSetter.value`
* which is meant to be used as an assignable expression.
*
* This controller watches both of these; when one changes,
* it will update the other's value to match. Because of this,
* the `ngModel` function should be both stable and computationally
* inexpensive, as it will be invoked often.
*
* Getter-setter style models can be preferable when there
* is significant indirection between templates; "dotless"
* expressions in `ng-model` can behave unexpectedly due to the
* rules of scope, but dots are lost when passed in via `ng-model`
* (so if a control is internally implemented using regular
* form elements, it can't transparently pass through the `ng-model`
* parameter it received.) Getter-setter functions are never the
* target of a scope assignment and so avoid this problem.
*
* @memberof platform/commonUI/general
* @constructor
* @param {Scope} $scope the controller's scope
*/
function GetterSetterController($scope) {
// Update internal assignable state based on changes
// to the getter-setter function.
function updateGetterSetter() {
if (typeof $scope.ngModel === 'function') {
$scope.getterSetter.value = $scope.ngModel();
}
}
// Update the external getter-setter based on changes
// to the assignable state.
function updateNgModel() {
if (typeof $scope.ngModel === 'function') {
$scope.ngModel($scope.getterSetter.value);
}
}
// Watch for changes to both expressions
$scope.$watch("ngModel()", updateGetterSetter);
$scope.$watch("getterSetter.value", updateNgModel);
// Publish an assignable field into scope.
$scope.getterSetter = {};
// Update internal assignable state based on changes
// to the getter-setter function.
function updateGetterSetter() {
if (typeof $scope.ngModel === 'function') {
$scope.getterSetter.value = $scope.ngModel();
} }
return GetterSetterController;
} }
);
// Update the external getter-setter based on changes
// to the assignable state.
function updateNgModel() {
if (typeof $scope.ngModel === 'function') {
$scope.ngModel($scope.getterSetter.value);
}
}
// Watch for changes to both expressions
$scope.$watch("ngModel()", updateGetterSetter);
$scope.$watch("getterSetter.value", updateNgModel);
// Publish an assignable field into scope.
$scope.getterSetter = {};
}
export default GetterSetterController;

View File

@ -23,97 +23,110 @@
/** /**
* Module defining ObjectInspectorController. Created by shale on 08/21/2015. * Module defining ObjectInspectorController. Created by shale on 08/21/2015.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* The ObjectInspectorController gets and formats the data for * Module defining ObjectInspectorController. Created by shale on 08/21/2015.
* the inspector display */
* function ObjectInspectorController($scope, objectService) {
* @constructor $scope.primaryParents = [];
*/ $scope.contextutalParents = [];
function ObjectInspectorController($scope, objectService) { //$scope.isLink = false;
$scope.primaryParents = [];
$scope.contextutalParents = [];
//$scope.isLink = false;
// Gets an array of the contextual parents/ancestors of the selected object // Gets an array of the contextual parents/ancestors of the selected object
function getContextualPath() { function getContextualPath() {
var currentObj = $scope.domainObject, var currentObj = $scope.domainObject,
currentParent, currentParent,
parents = []; parents = [];
currentParent = currentObj currentParent = currentObj
&& currentObj.hasCapability('context') && currentObj.hasCapability('context')
&& currentObj.getCapability('context').getParent(); && currentObj.getCapability('context').getParent();
while (currentParent && currentParent.getModel().type !== 'root' while (currentParent && currentParent.getModel().type !== 'root'
&& currentParent.hasCapability('context')) { && currentParent.hasCapability('context')) {
// Record this object // Record this object
parents.unshift(currentParent); parents.unshift(currentParent);
// Get the next one up the tree // Get the next one up the tree
currentObj = currentParent; currentObj = currentParent;
currentParent = currentObj.getCapability('context').getParent(); currentParent = currentObj.getCapability('context').getParent();
}
$scope.contextutalParents = parents;
}
// Gets an array of the parents/ancestors of the selected object's
// primary location (locational of original non-link)
function getPrimaryPath(current) {
var location;
// If this the the initial call of this recursive function
if (!current) {
current = $scope.domainObject;
$scope.primaryParents = [];
}
location = current.getModel().location;
if (location && location !== 'root') {
objectService.getObjects([location]).then(function (obj) {
var next = obj[location];
$scope.primaryParents.unshift(next);
getPrimaryPath(next);
});
}
}
// Gets the metadata for the selected object
function getMetadata() {
$scope.metadata = $scope.domainObject
&& $scope.domainObject.hasCapability('metadata')
&& $scope.domainObject.useCapability('metadata');
}
// Set scope variables when the selected object changes
$scope.$watch('domainObject', function () {
$scope.isLink = $scope.domainObject
&& $scope.domainObject.hasCapability('location')
&& $scope.domainObject.getCapability('location').isLink();
if ($scope.isLink) {
getPrimaryPath();
getContextualPath();
} else {
$scope.primaryParents = [];
getContextualPath();
}
getMetadata();
});
var mutation = $scope.domainObject.getCapability('mutation');
var unlisten = mutation.listen(getMetadata);
$scope.$on('$destroy', unlisten);
} }
return ObjectInspectorController; $scope.contextutalParents = parents;
} }
);
// Gets an array of the parents/ancestors of the selected object's
// primary location (locational of original non-link)
function getPrimaryPath(current) {
var location;
// If this the the initial call of this recursive function
if (!current) {
current = $scope.domainObject;
$scope.primaryParents = [];
}
location = current.getModel().location;
if (location && location !== 'root') {
objectService.getObjects([location]).then(function (obj) {
var next = obj[location];
$scope.primaryParents.unshift(next);
getPrimaryPath(next);
});
}
}
// Gets the metadata for the selected object
function getMetadata() {
$scope.metadata = $scope.domainObject
&& $scope.domainObject.hasCapability('metadata')
&& $scope.domainObject.useCapability('metadata');
}
// Set scope variables when the selected object changes
$scope.$watch('domainObject', function () {
$scope.isLink = $scope.domainObject
&& $scope.domainObject.hasCapability('location')
&& $scope.domainObject.getCapability('location').isLink();
if ($scope.isLink) {
getPrimaryPath();
getContextualPath();
} else {
$scope.primaryParents = [];
getContextualPath();
}
getMetadata();
});
var mutation = $scope.domainObject.getCapability('mutation');
var unlisten = mutation.listen(getMetadata);
$scope.$on('$destroy', unlisten);
}
export default ObjectInspectorController;

View File

@ -20,145 +20,161 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
var ROOT_ID = "ROOT"; var ROOT_ID = "ROOT";
/** /**
* Controller for the domain object selector control. * Controller for the domain object selector control.
* @memberof platform/commonUI/general * @memberof platform/commonUI/general
* @constructor * @constructor
* @param {ObjectService} objectService service from which to * @param {ObjectService} objectService service from which to
* read domain objects * read domain objects
* @param $scope Angular scope for this controller * @param $scope Angular scope for this controller
*/ */
function SelectorController(objectService, $scope) { function SelectorController(objectService, $scope) {
var treeModel = {}, var treeModel = {},
listModel = {}, listModel = {},
previousSelected, previousSelected,
self = this; self = this;
// For watch; look at the user's selection in the tree // For watch; look at the user's selection in the tree
function getTreeSelection() { function getTreeSelection() {
return treeModel.selectedObject; return treeModel.selectedObject;
} }
// Store root object for subsequent exposure to template // Store root object for subsequent exposure to template
function storeRoot(objects) { function storeRoot(objects) {
self.rootObject = objects[ROOT_ID]; self.rootObject = objects[ROOT_ID];
} }
// Check that a selection is of the valid type // Check that a selection is of the valid type
function validateTreeSelection(selectedObject) { function validateTreeSelection(selectedObject) {
var type = selectedObject var type = selectedObject
&& selectedObject.getCapability('type'); && selectedObject.getCapability('type');
// Delegate type-checking to the capability... // Delegate type-checking to the capability...
if (!type || !type.instanceOf($scope.structure.type)) { if (!type || !type.instanceOf($scope.structure.type)) {
treeModel.selectedObject = previousSelected; treeModel.selectedObject = previousSelected;
}
// Track current selection to restore it if an invalid
// selection is made later.
previousSelected = treeModel.selectedObject;
}
// Update the right-hand list of currently-selected objects
function updateList(ids) {
function updateSelectedObjects(objects) {
// Look up from the
function getObject(id) {
return objects[id];
}
self.selectedObjects =
ids.filter(getObject).map(getObject);
}
// Look up objects by id, then populate right-hand list
objectService.getObjects(ids).then(updateSelectedObjects);
}
// Reject attempts to select objects of the wrong type
$scope.$watch(getTreeSelection, validateTreeSelection);
// Make sure right-hand list matches underlying model
$scope.$watchCollection(function () {
return self.getField();
}, updateList);
// Look up root object, then store it
objectService.getObjects([ROOT_ID]).then(storeRoot);
this.$scope = $scope;
this.selectedObjects = [];
// Expose tree/list model for use in template directly
this.treeModel = treeModel;
this.listModel = listModel;
} }
// Set the value of the field being edited // Track current selection to restore it if an invalid
SelectorController.prototype.setField = function (value) { // selection is made later.
this.$scope.ngModel[this.$scope.field] = value; previousSelected = treeModel.selectedObject;
};
// Get the value of the field being edited
SelectorController.prototype.getField = function () {
return this.$scope.ngModel[this.$scope.field] || [];
};
/**
* Get the root object to show in the left-hand tree.
* @returns {DomainObject} the root object
*/
SelectorController.prototype.root = function () {
return this.rootObject;
};
/**
* Add a domain object to the list of selected objects.
* @param {DomainObject} the domain object to select
*/
SelectorController.prototype.select = function (domainObject) {
var id = domainObject && domainObject.getId(),
list = this.getField() || [];
// Only select if we have a valid id,
// and it isn't already selected
if (id && list.indexOf(id) === -1) {
this.setField(list.concat([id]));
}
};
/**
* Remove a domain object from the list of selected objects.
* @param {DomainObject} the domain object to select
*/
SelectorController.prototype.deselect = function (domainObject) {
var id = domainObject && domainObject.getId(),
list = this.getField() || [];
// Only change if this was a valid id,
// for an object which was already selected
if (id && list.indexOf(id) !== -1) {
// Filter it out of the current field
this.setField(list.filter(function (otherId) {
return otherId !== id;
}));
// Clear the current list selection
delete this.listModel.selectedObject;
}
};
/**
* Get the currently-selected domain objects.
* @returns {DomainObject[]} the current selection
*/
SelectorController.prototype.selected = function () {
return this.selectedObjects;
};
return SelectorController;
} }
);
// Update the right-hand list of currently-selected objects
function updateList(ids) {
function updateSelectedObjects(objects) {
// Look up from the
function getObject(id) {
return objects[id];
}
self.selectedObjects =
ids.filter(getObject).map(getObject);
}
// Look up objects by id, then populate right-hand list
objectService.getObjects(ids).then(updateSelectedObjects);
}
// Reject attempts to select objects of the wrong type
$scope.$watch(getTreeSelection, validateTreeSelection);
// Make sure right-hand list matches underlying model
$scope.$watchCollection(function () {
return self.getField();
}, updateList);
// Look up root object, then store it
objectService.getObjects([ROOT_ID]).then(storeRoot);
this.$scope = $scope;
this.selectedObjects = [];
// Expose tree/list model for use in template directly
this.treeModel = treeModel;
this.listModel = listModel;
}
// Set the value of the field being edited
SelectorController.prototype.setField = function (value) {
this.$scope.ngModel[this.$scope.field] = value;
};
// Get the value of the field being edited
SelectorController.prototype.getField = function () {
return this.$scope.ngModel[this.$scope.field] || [];
};
/**
* Get the root object to show in the left-hand tree.
* @returns {DomainObject} the root object
*/
SelectorController.prototype.root = function () {
return this.rootObject;
};
/**
* Add a domain object to the list of selected objects.
* @param {DomainObject} the domain object to select
*/
SelectorController.prototype.select = function (domainObject) {
var id = domainObject && domainObject.getId(),
list = this.getField() || [];
// Only select if we have a valid id,
// and it isn't already selected
if (id && list.indexOf(id) === -1) {
this.setField(list.concat([id]));
}
};
/**
* Remove a domain object from the list of selected objects.
* @param {DomainObject} the domain object to select
*/
SelectorController.prototype.deselect = function (domainObject) {
var id = domainObject && domainObject.getId(),
list = this.getField() || [];
// Only change if this was a valid id,
// for an object which was already selected
if (id && list.indexOf(id) !== -1) {
// Filter it out of the current field
this.setField(list.filter(function (otherId) {
return otherId !== id;
}));
// Clear the current list selection
delete this.listModel.selectedObject;
}
};
/**
* Get the currently-selected domain objects.
* @returns {DomainObject[]} the current selection
*/
SelectorController.prototype.selected = function () {
return this.selectedObjects;
};
export default SelectorController;

View File

@ -20,294 +20,311 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([ /*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
], function () { var TICK_SPACING_PX = 150;
var TICK_SPACING_PX = 150; /* format number as percent; 0.0-1.0 to "0%"-"100%" */
function toPercent(p) {
return (100 * p) + "%";
}
/* format number as percent; 0.0-1.0 to "0%"-"100%" */ function clamp(value, low, high) {
function toPercent(p) { return Math.max(low, Math.min(high, value));
return (100 * p) + "%"; }
}
function clamp(value, low, high) { function copyBounds(bounds) {
return Math.max(low, Math.min(high, value)); return {
} start: bounds.start,
end: bounds.end
function copyBounds(bounds) {
return {
start: bounds.start,
end: bounds.end
};
}
/**
* Controller used by the `time-controller` template.
* @memberof platform/commonUI/general
* @constructor
* @param $scope the Angular scope for this controller
* @param {FormatService} formatService the service to user to format
* domain values
* @param {string} defaultFormat the format to request when no
* format has been otherwise specified
* @param {Function} now a function to return current system time
*/
function TimeRangeController($scope, $timeout, formatService, defaultFormat, now) {
this.$scope = $scope;
this.formatService = formatService;
this.defaultFormat = defaultFormat;
this.now = now;
this.tickCount = 2;
this.innerMinimumSpan = 1000; // 1 second
this.outerMinimumSpan = 1000; // 1 second
this.initialDragValue = undefined;
this.formatter = formatService.getFormat(defaultFormat);
this.formStartChanged = false;
this.formEndChanged = false;
this.$timeout = $timeout;
this.$scope.ticks = [];
this.updateViewFromModel(this.$scope.ngModel);
this.updateFormModel();
[
'updateViewFromModel',
'updateSpanWidth',
'updateOuterStart',
'updateOuterEnd',
'updateFormat',
'validateStart',
'validateEnd',
'onFormStartChange',
'onFormEndChange'
].forEach(function (boundFn) {
this[boundFn] = this[boundFn].bind(this);
}, this);
this.$scope.$watchCollection("ngModel", this.updateViewFromModel);
this.$scope.$watch("spanWidth", this.updateSpanWidth);
this.$scope.$watch("ngModel.outer.start", this.updateOuterStart);
this.$scope.$watch("ngModel.outer.end", this.updateOuterEnd);
this.$scope.$watch("parameters.format", this.updateFormat);
this.$scope.$watch("formModel.start", this.onFormStartChange);
this.$scope.$watch("formModel.end", this.onFormEndChange);
}
TimeRangeController.prototype.formatTimestamp = function (ts) {
return this.formatter.format(ts);
}; };
}
TimeRangeController.prototype.updateTicks = function () { /**
var i, p, ts, start, end, span; * Controller used by the `time-controller` template.
end = this.$scope.ngModel.outer.end; * @memberof platform/commonUI/general
start = this.$scope.ngModel.outer.start; * @constructor
span = end - start; * @param $scope the Angular scope for this controller
this.$scope.ticks = []; * @param {FormatService} formatService the service to user to format
for (i = 0; i < this.tickCount; i += 1) { * domain values
p = i / (this.tickCount - 1); * @param {string} defaultFormat the format to request when no
ts = p * span + start; * format has been otherwise specified
this.$scope.ticks.push(this.formatTimestamp(ts)); * @param {Function} now a function to return current system time
*/
function TimeRangeController($scope, $timeout, formatService, defaultFormat, now) {
this.$scope = $scope;
this.formatService = formatService;
this.defaultFormat = defaultFormat;
this.now = now;
this.tickCount = 2;
this.innerMinimumSpan = 1000; // 1 second
this.outerMinimumSpan = 1000; // 1 second
this.initialDragValue = undefined;
this.formatter = formatService.getFormat(defaultFormat);
this.formStartChanged = false;
this.formEndChanged = false;
this.$timeout = $timeout;
this.$scope.ticks = [];
this.updateViewFromModel(this.$scope.ngModel);
this.updateFormModel();
[
'updateViewFromModel',
'updateSpanWidth',
'updateOuterStart',
'updateOuterEnd',
'updateFormat',
'validateStart',
'validateEnd',
'onFormStartChange',
'onFormEndChange'
].forEach(function (boundFn) {
this[boundFn] = this[boundFn].bind(this);
}, this);
this.$scope.$watchCollection("ngModel", this.updateViewFromModel);
this.$scope.$watch("spanWidth", this.updateSpanWidth);
this.$scope.$watch("ngModel.outer.start", this.updateOuterStart);
this.$scope.$watch("ngModel.outer.end", this.updateOuterEnd);
this.$scope.$watch("parameters.format", this.updateFormat);
this.$scope.$watch("formModel.start", this.onFormStartChange);
this.$scope.$watch("formModel.end", this.onFormEndChange);
}
TimeRangeController.prototype.formatTimestamp = function (ts) {
return this.formatter.format(ts);
};
TimeRangeController.prototype.updateTicks = function () {
var i, p, ts, start, end, span;
end = this.$scope.ngModel.outer.end;
start = this.$scope.ngModel.outer.start;
span = end - start;
this.$scope.ticks = [];
for (i = 0; i < this.tickCount; i += 1) {
p = i / (this.tickCount - 1);
ts = p * span + start;
this.$scope.ticks.push(this.formatTimestamp(ts));
}
};
TimeRangeController.prototype.updateSpanWidth = function (w) {
this.tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2);
this.updateTicks();
};
TimeRangeController.prototype.updateViewForInnerSpanFromModel = function (
ngModel
) {
var span = ngModel.outer.end - ngModel.outer.start;
// Expose readable dates for the knobs
this.$scope.startInnerText = this.formatTimestamp(ngModel.inner.start);
this.$scope.endInnerText = this.formatTimestamp(ngModel.inner.end);
// And positions for the knobs
this.$scope.startInnerPct =
toPercent((ngModel.inner.start - ngModel.outer.start) / span);
this.$scope.endInnerPct =
toPercent((ngModel.outer.end - ngModel.inner.end) / span);
};
TimeRangeController.prototype.defaultBounds = function () {
var t = this.now();
return {
start: t - 24 * 3600 * 1000, // One day
end: t
};
};
TimeRangeController.prototype.updateViewFromModel = function (ngModel) {
ngModel = ngModel || {};
ngModel.outer = ngModel.outer || this.defaultBounds();
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
// Stick it back is scope (in case we just set defaults)
this.$scope.ngModel = ngModel;
this.updateViewForInnerSpanFromModel(ngModel);
this.updateTicks();
};
TimeRangeController.prototype.startLeftDrag = function () {
this.initialDragValue = this.$scope.ngModel.inner.start;
};
TimeRangeController.prototype.startRightDrag = function () {
this.initialDragValue = this.$scope.ngModel.inner.end;
};
TimeRangeController.prototype.startMiddleDrag = function () {
this.initialDragValue = {
start: this.$scope.ngModel.inner.start,
end: this.$scope.ngModel.inner.end
};
};
TimeRangeController.prototype.toMillis = function (pixels) {
var span =
this.$scope.ngModel.outer.end - this.$scope.ngModel.outer.start;
return (pixels / this.$scope.spanWidth) * span;
};
TimeRangeController.prototype.leftDrag = function (pixels) {
var delta = this.toMillis(pixels);
this.$scope.ngModel.inner.start = clamp(
this.initialDragValue + delta,
this.$scope.ngModel.outer.start,
this.$scope.ngModel.inner.end - this.innerMinimumSpan
);
this.updateViewFromModel(this.$scope.ngModel);
};
TimeRangeController.prototype.rightDrag = function (pixels) {
var delta = this.toMillis(pixels);
this.$scope.ngModel.inner.end = clamp(
this.initialDragValue + delta,
this.$scope.ngModel.inner.start + this.innerMinimumSpan,
this.$scope.ngModel.outer.end
);
this.updateViewFromModel(this.$scope.ngModel);
};
TimeRangeController.prototype.middleDrag = function (pixels) {
var delta = this.toMillis(pixels),
edge = delta < 0 ? 'start' : 'end',
opposite = delta < 0 ? 'end' : 'start';
// Adjust the position of the edge in the direction of drag
this.$scope.ngModel.inner[edge] = clamp(
this.initialDragValue[edge] + delta,
this.$scope.ngModel.outer.start,
this.$scope.ngModel.outer.end
);
// Adjust opposite knob to maintain span
this.$scope.ngModel.inner[opposite] =
this.$scope.ngModel.inner[edge]
+ this.initialDragValue[opposite]
- this.initialDragValue[edge];
this.updateViewFromModel(this.$scope.ngModel);
};
TimeRangeController.prototype.updateFormModel = function () {
this.$scope.formModel = {
start: ((this.$scope.ngModel || {}).outer || {}).start,
end: ((this.$scope.ngModel || {}).outer || {}).end
};
};
TimeRangeController.prototype.updateOuterStart = function () {
var ngModel = this.$scope.ngModel;
ngModel.inner.start =
Math.max(ngModel.outer.start, ngModel.inner.start);
ngModel.inner.end = Math.max(
ngModel.inner.start + this.innerMinimumSpan,
ngModel.inner.end
);
this.updateFormModel();
this.updateViewForInnerSpanFromModel(ngModel);
this.updateTicks();
};
TimeRangeController.prototype.updateOuterEnd = function () {
var ngModel = this.$scope.ngModel;
ngModel.inner.end =
Math.min(ngModel.outer.end, ngModel.inner.end);
ngModel.inner.start = Math.min(
ngModel.inner.end - this.innerMinimumSpan,
ngModel.inner.start
);
this.updateFormModel();
this.updateViewForInnerSpanFromModel(ngModel);
this.updateTicks();
};
TimeRangeController.prototype.updateFormat = function (key) {
this.formatter = this.formatService.getFormat(key || this.defaultFormat);
this.updateViewForInnerSpanFromModel(this.$scope.ngModel);
this.updateTicks();
};
TimeRangeController.prototype.updateBoundsFromForm = function () {
var self = this;
//Allow Angular to trigger watches and determine whether values have changed.
this.$timeout(function () {
if (self.formStartChanged) {
self.$scope.ngModel.outer.start =
self.$scope.ngModel.inner.start =
self.$scope.formModel.start;
self.formStartChanged = false;
} }
};
TimeRangeController.prototype.updateSpanWidth = function (w) { if (self.formEndChanged) {
this.tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2); self.$scope.ngModel.outer.end =
this.updateTicks(); self.$scope.ngModel.inner.end =
}; self.$scope.formModel.end;
self.formEndChanged = false;
TimeRangeController.prototype.updateViewForInnerSpanFromModel = function (
ngModel
) {
var span = ngModel.outer.end - ngModel.outer.start;
// Expose readable dates for the knobs
this.$scope.startInnerText = this.formatTimestamp(ngModel.inner.start);
this.$scope.endInnerText = this.formatTimestamp(ngModel.inner.end);
// And positions for the knobs
this.$scope.startInnerPct =
toPercent((ngModel.inner.start - ngModel.outer.start) / span);
this.$scope.endInnerPct =
toPercent((ngModel.outer.end - ngModel.inner.end) / span);
};
TimeRangeController.prototype.defaultBounds = function () {
var t = this.now();
return {
start: t - 24 * 3600 * 1000, // One day
end: t
};
};
TimeRangeController.prototype.updateViewFromModel = function (ngModel) {
ngModel = ngModel || {};
ngModel.outer = ngModel.outer || this.defaultBounds();
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
// Stick it back is scope (in case we just set defaults)
this.$scope.ngModel = ngModel;
this.updateViewForInnerSpanFromModel(ngModel);
this.updateTicks();
};
TimeRangeController.prototype.startLeftDrag = function () {
this.initialDragValue = this.$scope.ngModel.inner.start;
};
TimeRangeController.prototype.startRightDrag = function () {
this.initialDragValue = this.$scope.ngModel.inner.end;
};
TimeRangeController.prototype.startMiddleDrag = function () {
this.initialDragValue = {
start: this.$scope.ngModel.inner.start,
end: this.$scope.ngModel.inner.end
};
};
TimeRangeController.prototype.toMillis = function (pixels) {
var span =
this.$scope.ngModel.outer.end - this.$scope.ngModel.outer.start;
return (pixels / this.$scope.spanWidth) * span;
};
TimeRangeController.prototype.leftDrag = function (pixels) {
var delta = this.toMillis(pixels);
this.$scope.ngModel.inner.start = clamp(
this.initialDragValue + delta,
this.$scope.ngModel.outer.start,
this.$scope.ngModel.inner.end - this.innerMinimumSpan
);
this.updateViewFromModel(this.$scope.ngModel);
};
TimeRangeController.prototype.rightDrag = function (pixels) {
var delta = this.toMillis(pixels);
this.$scope.ngModel.inner.end = clamp(
this.initialDragValue + delta,
this.$scope.ngModel.inner.start + this.innerMinimumSpan,
this.$scope.ngModel.outer.end
);
this.updateViewFromModel(this.$scope.ngModel);
};
TimeRangeController.prototype.middleDrag = function (pixels) {
var delta = this.toMillis(pixels),
edge = delta < 0 ? 'start' : 'end',
opposite = delta < 0 ? 'end' : 'start';
// Adjust the position of the edge in the direction of drag
this.$scope.ngModel.inner[edge] = clamp(
this.initialDragValue[edge] + delta,
this.$scope.ngModel.outer.start,
this.$scope.ngModel.outer.end
);
// Adjust opposite knob to maintain span
this.$scope.ngModel.inner[opposite] =
this.$scope.ngModel.inner[edge]
+ this.initialDragValue[opposite]
- this.initialDragValue[edge];
this.updateViewFromModel(this.$scope.ngModel);
};
TimeRangeController.prototype.updateFormModel = function () {
this.$scope.formModel = {
start: ((this.$scope.ngModel || {}).outer || {}).start,
end: ((this.$scope.ngModel || {}).outer || {}).end
};
};
TimeRangeController.prototype.updateOuterStart = function () {
var ngModel = this.$scope.ngModel;
ngModel.inner.start =
Math.max(ngModel.outer.start, ngModel.inner.start);
ngModel.inner.end = Math.max(
ngModel.inner.start + this.innerMinimumSpan,
ngModel.inner.end
);
this.updateFormModel();
this.updateViewForInnerSpanFromModel(ngModel);
this.updateTicks();
};
TimeRangeController.prototype.updateOuterEnd = function () {
var ngModel = this.$scope.ngModel;
ngModel.inner.end =
Math.min(ngModel.outer.end, ngModel.inner.end);
ngModel.inner.start = Math.min(
ngModel.inner.end - this.innerMinimumSpan,
ngModel.inner.start
);
this.updateFormModel();
this.updateViewForInnerSpanFromModel(ngModel);
this.updateTicks();
};
TimeRangeController.prototype.updateFormat = function (key) {
this.formatter = this.formatService.getFormat(key || this.defaultFormat);
this.updateViewForInnerSpanFromModel(this.$scope.ngModel);
this.updateTicks();
};
TimeRangeController.prototype.updateBoundsFromForm = function () {
var self = this;
//Allow Angular to trigger watches and determine whether values have changed.
this.$timeout(function () {
if (self.formStartChanged) {
self.$scope.ngModel.outer.start =
self.$scope.ngModel.inner.start =
self.$scope.formModel.start;
self.formStartChanged = false;
}
if (self.formEndChanged) {
self.$scope.ngModel.outer.end =
self.$scope.ngModel.inner.end =
self.$scope.formModel.end;
self.formEndChanged = false;
}
});
};
TimeRangeController.prototype.onFormStartChange = function (
newValue,
oldValue
) {
if (!this.formStartChanged && newValue !== oldValue) {
this.formStartChanged = true;
} }
}; });
};
TimeRangeController.prototype.onFormEndChange = function ( TimeRangeController.prototype.onFormStartChange = function (
newValue, newValue,
oldValue oldValue
) { ) {
if (!this.formEndChanged && newValue !== oldValue) { if (!this.formStartChanged && newValue !== oldValue) {
this.formEndChanged = true; this.formStartChanged = true;
} }
}; };
TimeRangeController.prototype.validateStart = function (startValue) { TimeRangeController.prototype.onFormEndChange = function (
return startValue newValue,
<= this.$scope.formModel.end - this.outerMinimumSpan; oldValue
}; ) {
if (!this.formEndChanged && newValue !== oldValue) {
this.formEndChanged = true;
}
};
TimeRangeController.prototype.validateEnd = function (endValue) { TimeRangeController.prototype.validateStart = function (startValue) {
return endValue return startValue
>= this.$scope.formModel.start + this.outerMinimumSpan; <= this.$scope.formModel.end - this.outerMinimumSpan;
}; };
return TimeRangeController; TimeRangeController.prototype.validateEnd = function (endValue) {
}); return endValue
>= this.$scope.formModel.start + this.outerMinimumSpan;
};
export default TimeRangeController;

View File

@ -20,47 +20,56 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function ToggleController() {
* A ToggleController is used to activate/deactivate things. this.state = false;
* A common usage is for "twistie"
*
* @memberof platform/commonUI/general
* @constructor
*/
function ToggleController() {
this.state = false;
this.setState = this.setState.bind(this); this.setState = this.setState.bind(this);
} }
/** /**
* Get the current state of the toggle. * Get the current state of the toggle.
* @return {boolean} true if active * @return {boolean} true if active
*/ */
ToggleController.prototype.isActive = function () { ToggleController.prototype.isActive = function () {
return this.state; return this.state;
}; };
/** /**
* Set a new state for the toggle. * Set a new state for the toggle.
* @return {boolean} true to activate * @return {boolean} true to activate
*/ */
ToggleController.prototype.setState = function (newState) { ToggleController.prototype.setState = function (newState) {
this.state = newState; this.state = newState;
}; };
/** /**
* Toggle the current state; activate if it is inactive, * Toggle the current state; activate if it is inactive,
* deactivate if it is active. * deactivate if it is active.
*/ */
ToggleController.prototype.toggle = function () { ToggleController.prototype.toggle = function () {
this.state = !this.state; this.state = !this.state;
}; };
return ToggleController; export default ToggleController;
}
);

View File

@ -23,182 +23,170 @@
/** /**
* Module defining TreeNodeController. Created by vwoeltje on 11/10/14. * Module defining TreeNodeController. Created by vwoeltje on 11/10/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* The TreeNodeController supports the tree node representation; * Module defining TreeNodeController. Created by vwoeltje on 11/10/14.
* a tree node has a label for the current object as well as a */
* subtree which shows (and is not loaded until) the node is function TreeNodeController($scope, $timeout) {
* expanded. var self = this,
* selectedObject = ($scope.ngModel || {}).selectedObject;
* This controller tracks the following, so that the tree node
* template may update its state accordingly:
*
* * Whether or not the tree node has ever been expanded (this
* is used to lazily load, exactly once, the subtree)
* * Whether or not the node is currently the domain object
* of navigation (this gets highlighted differently to
* provide the user with visual feedback.)
*
* Additionally, this controller will automatically trigger
* node expansion when this tree node's _subtree_ will contain
* the navigated object (recursively, this becomes an
* expand-to-show-navigated-object behavior.)
*
* Finally, if a `callback` property is passed in through the
* `parameters` attribute of the `tree-node`, that callback
* will be invoked whenever a user clicks in a manner which
* would result in a selection. This callback is invoked
* even if the selection does not change (if you are only
* interested in changes, watch the `selectedObject` property
* of the object passed in `ng-model` instead.)
*
* @memberof platform/commonUI/general
* @constructor
*/
function TreeNodeController($scope, $timeout) {
var self = this,
selectedObject = ($scope.ngModel || {}).selectedObject;
// Look up the id for a domain object. A convenience // Look up the id for a domain object. A convenience
// for mapping; additionally does some undefined-checking. // for mapping; additionally does some undefined-checking.
function getId(obj) { function getId(obj) {
return obj && obj.getId && obj.getId(); return obj && obj.getId && obj.getId();
}
// Verify that id paths are equivalent, staring at
// index, ending at the end of the node path.
function checkPath(nodePath, navPath, index) {
index = index || 0;
// The paths overlap if we have made it past the
// end of the node's path; otherwise, check the
// id at the current index for equality and perform
// a recursive step for subsequent ids in the paths,
// until we exceed path length or hit a mismatch.
return (index >= nodePath.length)
|| ((navPath[index] === nodePath[index])
&& checkPath(nodePath, navPath, index + 1));
}
// Consider the currently-navigated object and update
// parameters which support display.
function checkSelection() {
var nodeObject = $scope.domainObject,
navObject = selectedObject,
nodeContext = nodeObject
&& nodeObject.getCapability('context'),
navContext = navObject
&& navObject.getCapability('context'),
nodePath,
navPath;
// Deselect; we will reselect below, iff we are
// exactly at the end of the path.
self.isSelectedFlag = false;
// Expand if necessary (if the navigated object will
// be in this node's subtree)
if (nodeContext && navContext) {
// Get the paths as arrays of identifiers
nodePath = nodeContext.getPath().map(getId);
navPath = navContext.getPath().map(getId);
// Check to see if the node's path lies entirely
// within the navigation path; otherwise, navigation
// has happened in some other subtree.
if (navPath.length >= nodePath.length
&& checkPath(nodePath, navPath)) {
// nodePath is along the navPath; if it's
// at the end of the path, highlight;
// otherwise, expand.
if (nodePath.length === navPath.length) {
self.isSelectedFlag = true;
} else { // node path is shorter: Expand!
if ($scope.toggle) {
$scope.toggle.setState(true);
}
self.trackExpansion();
}
}
}
}
// Callback for the selection updates; track the currently
// navigated object and update display parameters as needed.
function setSelection(object) {
selectedObject = object;
checkSelection();
}
this.isSelectedFlag = false;
this.hasBeenExpandedFlag = false;
this.$timeout = $timeout;
this.$scope = $scope;
// Listen for changes which will effect display parameters
$scope.$watch("ngModel.selectedObject", setSelection);
$scope.$watch("domainObject", checkSelection);
}
/**
* Select the domain object represented by this node in the tree.
* This will both update the `selectedObject` property in
* the object passed in via `ng-model`, and will fire any `callback`
* passed in via `parameters`.
*/
TreeNodeController.prototype.select = function () {
if (this.$scope.ngModel) {
this.$scope.ngModel.selectedObject =
this.$scope.domainObject;
}
if ((this.$scope.parameters || {}).callback) {
this.$scope.parameters.callback(this.$scope.domainObject);
}
};
/**
* This method should be called when a node is expanded
* to record that this has occurred, to support one-time
* lazy loading of the node's subtree.
*/
TreeNodeController.prototype.trackExpansion = function () {
var self = this;
if (!self.hasBeenExpanded()) {
// Run on a timeout; if a lot of expansion needs to
// occur (e.g. if the selection is several nodes deep) we
// want this to be spread across multiple digest cycles.
self.$timeout(function () {
self.hasBeenExpandedFlag = true;
}, 0);
}
};
/**
* Check if this not has ever been expanded.
* @returns true if it has been expanded
*/
TreeNodeController.prototype.hasBeenExpanded = function () {
return this.hasBeenExpandedFlag;
};
/**
* Check whether or not the domain object represented by
* this tree node should be highlighted.
* An object will be highlighted if it matches
* ngModel.selectedObject
* @returns true if this should be highlighted
*/
TreeNodeController.prototype.isSelected = function () {
return this.isSelectedFlag;
};
return TreeNodeController;
} }
);
// Verify that id paths are equivalent, staring at
// index, ending at the end of the node path.
function checkPath(nodePath, navPath, index) {
index = index || 0;
// The paths overlap if we have made it past the
// end of the node's path; otherwise, check the
// id at the current index for equality and perform
// a recursive step for subsequent ids in the paths,
// until we exceed path length or hit a mismatch.
return (index >= nodePath.length)
|| ((navPath[index] === nodePath[index])
&& checkPath(nodePath, navPath, index + 1));
}
// Consider the currently-navigated object and update
// parameters which support display.
function checkSelection() {
var nodeObject = $scope.domainObject,
navObject = selectedObject,
nodeContext = nodeObject
&& nodeObject.getCapability('context'),
navContext = navObject
&& navObject.getCapability('context'),
nodePath,
navPath;
// Deselect; we will reselect below, iff we are
// exactly at the end of the path.
self.isSelectedFlag = false;
// Expand if necessary (if the navigated object will
// be in this node's subtree)
if (nodeContext && navContext) {
// Get the paths as arrays of identifiers
nodePath = nodeContext.getPath().map(getId);
navPath = navContext.getPath().map(getId);
// Check to see if the node's path lies entirely
// within the navigation path; otherwise, navigation
// has happened in some other subtree.
if (navPath.length >= nodePath.length
&& checkPath(nodePath, navPath)) {
// nodePath is along the navPath; if it's
// at the end of the path, highlight;
// otherwise, expand.
if (nodePath.length === navPath.length) {
self.isSelectedFlag = true;
} else { // node path is shorter: Expand!
if ($scope.toggle) {
$scope.toggle.setState(true);
}
self.trackExpansion();
}
}
}
}
// Callback for the selection updates; track the currently
// navigated object and update display parameters as needed.
function setSelection(object) {
selectedObject = object;
checkSelection();
}
this.isSelectedFlag = false;
this.hasBeenExpandedFlag = false;
this.$timeout = $timeout;
this.$scope = $scope;
// Listen for changes which will effect display parameters
$scope.$watch("ngModel.selectedObject", setSelection);
$scope.$watch("domainObject", checkSelection);
}
/**
* Select the domain object represented by this node in the tree.
* This will both update the `selectedObject` property in
* the object passed in via `ng-model`, and will fire any `callback`
* passed in via `parameters`.
*/
TreeNodeController.prototype.select = function () {
if (this.$scope.ngModel) {
this.$scope.ngModel.selectedObject =
this.$scope.domainObject;
}
if ((this.$scope.parameters || {}).callback) {
this.$scope.parameters.callback(this.$scope.domainObject);
}
};
/**
* This method should be called when a node is expanded
* to record that this has occurred, to support one-time
* lazy loading of the node's subtree.
*/
TreeNodeController.prototype.trackExpansion = function () {
var self = this;
if (!self.hasBeenExpanded()) {
// Run on a timeout; if a lot of expansion needs to
// occur (e.g. if the selection is several nodes deep) we
// want this to be spread across multiple digest cycles.
self.$timeout(function () {
self.hasBeenExpandedFlag = true;
}, 0);
}
};
/**
* Check if this not has ever been expanded.
* @returns true if it has been expanded
*/
TreeNodeController.prototype.hasBeenExpanded = function () {
return this.hasBeenExpandedFlag;
};
/**
* Check whether or not the domain object represented by
* this tree node should be highlighted.
* An object will be highlighted if it matches
* ngModel.selectedObject
* @returns true if this should be highlighted
*/
TreeNodeController.prototype.isSelected = function () {
return this.isSelectedFlag;
};
export default TreeNodeController;

View File

@ -23,51 +23,63 @@
/** /**
* Module defining ViewSwitcherController. Created by vwoeltje on 11/7/14. * Module defining ViewSwitcherController. Created by vwoeltje on 11/7/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* Controller for the view switcher; populates and maintains a list * Module defining ViewSwitcherController. Created by vwoeltje on 11/7/14.
* of applicable views for a represented domain object. */
* @memberof platform/commonUI/general function ViewSwitcherController($scope, $timeout) {
* @constructor // If the view capability gets refreshed, try to
*/ // keep the same option chosen.
function ViewSwitcherController($scope, $timeout) { function findMatchingOption(options, selected) {
// If the view capability gets refreshed, try to var i;
// keep the same option chosen.
function findMatchingOption(options, selected) {
var i;
if (selected) { if (selected) {
for (i = 0; i < options.length; i += 1) { for (i = 0; i < options.length; i += 1) {
if (options[i].key === selected.key) { if (options[i].key === selected.key) {
return options[i]; return options[i];
}
}
}
return options[0];
}
// Get list of views, read from capability
function updateOptions(views) {
if (Array.isArray(views)) {
$timeout(function () {
$scope.ngModel.selected = findMatchingOption(
views,
($scope.ngModel || {}).selected
);
}, 0);
} }
} }
// Update view options when the in-scope results of using the
// view capability change.
$scope.$watch("view", updateOptions);
} }
return ViewSwitcherController; return options[0];
} }
);
// Get list of views, read from capability
function updateOptions(views) {
if (Array.isArray(views)) {
$timeout(function () {
$scope.ngModel.selected = findMatchingOption(
views,
($scope.ngModel || {}).selected
);
}, 0);
}
}
// Update view options when the in-scope results of using the
// view capability change.
$scope.$watch("view", updateOptions);
}
export default ViewSwitcherController;

View File

@ -20,58 +20,66 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** function MCTClickElsewhere($document) {
* The `mct-click-elsewhere` directive will evaluate its
* associated expression whenever a `mousedown` occurs anywhere
* outside of the element that has the `mct-click-elsewhere`
* directive attached. This is useful for dismissing popups
* and the like.
*/
function MCTClickElsewhere($document) {
// Link; install event handlers. // Link; install event handlers.
function link(scope, element, attrs) { function link(scope, element, attrs) {
// Keep a reference to the body, to attach/detach // Keep a reference to the body, to attach/detach
// mouse event handlers; mousedown and mouseup cannot // mouse event handlers; mousedown and mouseup cannot
// only be attached to the element being linked, as the // only be attached to the element being linked, as the
// mouse may leave this element during the drag. // mouse may leave this element during the drag.
var body = $document.find('body'); var body = $document.find('body');
function clickBody(event) { function clickBody(event) {
var x = event.clientX, var x = event.clientX,
y = event.clientY, y = event.clientY,
rect = element[0].getBoundingClientRect(), rect = element[0].getBoundingClientRect(),
xMin = rect.left, xMin = rect.left,
xMax = xMin + rect.width, xMax = xMin + rect.width,
yMin = rect.top, yMin = rect.top,
yMax = yMin + rect.height; yMax = yMin + rect.height;
if (x < xMin || x > xMax || y < yMin || y > yMax) { if (x < xMin || x > xMax || y < yMin || y > yMax) {
scope.$apply(function () { scope.$apply(function () {
scope.$eval(attrs.mctClickElsewhere); scope.$eval(attrs.mctClickElsewhere);
});
}
}
body.on("mousedown", clickBody);
scope.$on("$destroy", function () {
body.off("mousedown", clickBody);
}); });
} }
return {
// mct-drag only makes sense as an attribute
restrict: "A",
// Link function, to install event handlers
link: link
};
} }
return MCTClickElsewhere; body.on("mousedown", clickBody);
scope.$on("$destroy", function () {
body.off("mousedown", clickBody);
});
} }
);
return {
// mct-drag only makes sense as an attribute
restrict: "A",
// Link function, to install event handlers
link: link
};
}
export default MCTClickElsewhere;

View File

@ -23,69 +23,75 @@
/** /**
* Module defining MCTContainer. Created by vwoeltje on 11/17/14. * Module defining MCTContainer. Created by vwoeltje on 11/17/14.
*/ */
define( /*****************************************************************************
[], * Open MCT, Copyright (c) 2014-2021, United States Government
function () { * 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.
*****************************************************************************/
/** /**
* The mct-container is similar to the mct-include directive * Module defining MCTContainer. Created by vwoeltje on 11/17/14.
* insofar as it allows templates to be referenced by */
* symbolic keys instead of by URL. Unlike mct-include, it function MCTContainer(containers) {
* supports transclusion. var containerMap = {};
*
* Unlike mct-include, mct-container accepts a key as a
* plain string attribute, instead of as an Angular
* expression.
*
* @memberof platform/commonUI/general
* @constructor
*/
function MCTContainer(containers) {
var containerMap = {};
// Initialize container map from extensions // Initialize container map from extensions
containers.forEach(function (container) { containers.forEach(function (container) {
containerMap[container.key] = container; containerMap[container.key] = container;
}); });
return { return {
// Allow only at the element level // Allow only at the element level
restrict: 'E', restrict: 'E',
// Support transclusion // Support transclusion
transclude: true, transclude: true,
// Create a new (non-isolate) scope // Create a new (non-isolate) scope
scope: true, scope: true,
// Populate initial scope based on attributes requested // Populate initial scope based on attributes requested
// by the container definition // by the container definition
link: function (scope, element, attrs) { link: function (scope, element, attrs) {
var key = attrs.key, var key = attrs.key,
container = containerMap[key], container = containerMap[key],
alias = "container", alias = "container",
copiedAttributes = {}; copiedAttributes = {};
if (container) { if (container) {
alias = container.alias || alias; alias = container.alias || alias;
(container.attributes || []).forEach(function (attr) { (container.attributes || []).forEach(function (attr) {
copiedAttributes[attr] = attrs[attr]; copiedAttributes[attr] = attrs[attr];
}); });
} }
scope[alias] = copiedAttributes; scope[alias] = copiedAttributes;
}, },
template: function (element, attrs) { template: function (element, attrs) {
var key = attrs.key, var key = attrs.key,
container = containerMap[key]; container = containerMap[key];
return container ? container.template : ""; return container ? container.template : "";
}
};
} }
};
}
return MCTContainer; export default MCTContainer;
}
);

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