topic-form-refactor (#4478)

* Form refactor (#3816)
* New form API and associated form controls
* Actions updated to use new form API.

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: charlesh88 <charles.f.hacskaylo@nasa.gov>

* Reimplementation of import export json (#4171)

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: charlesh88 <charles.f.hacskaylo@nasa.gov>
Co-authored-by: Henry Hsu <hhsu0219@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Nikhil 2021-12-07 12:27:23 -08:00 committed by GitHub
parent e20c7a17d6
commit 8acbcadd5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
126 changed files with 3275 additions and 6954 deletions

View File

@ -106,7 +106,7 @@ module.exports = (config) => {
}, },
specReporter: { specReporter: {
maxLogLines: 5, maxLogLines: 5,
suppressErrorSummary: true, suppressErrorSummary: false,
suppressFailed: false, suppressFailed: false,
suppressPassed: false, suppressPassed: false,
suppressSkipped: true, suppressSkipped: true,

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<mct-container key="c-overlay__contents"> <mct-container key="c-overlay__contents">
<div class=c-overlay__top-bar"> <div class="c-overlay__top-bar">
<div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div> <div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
<div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div> <div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div>
</div> </div>

View File

@ -26,22 +26,12 @@ define([
"./src/controllers/EditObjectController", "./src/controllers/EditObjectController",
"./src/actions/EditAndComposeAction", "./src/actions/EditAndComposeAction",
"./src/actions/EditAction", "./src/actions/EditAction",
"./src/actions/PropertiesAction",
"./src/actions/SaveAction", "./src/actions/SaveAction",
"./src/actions/SaveAndStopEditingAction", "./src/actions/SaveAndStopEditingAction",
"./src/actions/SaveAsAction",
"./src/actions/CancelAction", "./src/actions/CancelAction",
"./src/policies/EditPersistableObjectsPolicy", "./src/policies/EditPersistableObjectsPolicy",
"./src/representers/EditRepresenter", "./src/representers/EditRepresenter",
"./src/capabilities/EditorCapability", "./src/capabilities/EditorCapability",
"./src/creation/CreateMenuController",
"./src/creation/LocatorController",
"./src/creation/CreationPolicy",
"./src/creation/CreateActionProvider",
"./src/creation/CreationService",
"./res/templates/create/locator.html",
"./res/templates/create/create-button.html",
"./res/templates/create/create-menu.html",
"./res/templates/library.html", "./res/templates/library.html",
"./res/templates/edit-object.html", "./res/templates/edit-object.html",
"./res/templates/edit-action-buttons.html", "./res/templates/edit-action-buttons.html",
@ -52,22 +42,12 @@ define([
EditObjectController, EditObjectController,
EditAndComposeAction, EditAndComposeAction,
EditAction, EditAction,
PropertiesAction,
SaveAction, SaveAction,
SaveAndStopEditingAction, SaveAndStopEditingAction,
SaveAsAction,
CancelAction, CancelAction,
EditPersistableObjectsPolicy, EditPersistableObjectsPolicy,
EditRepresenter, EditRepresenter,
EditorCapability, EditorCapability,
CreateMenuController,
LocatorController,
CreationPolicy,
CreateActionProvider,
CreationService,
locatorTemplate,
createButtonTemplate,
createMenuTemplate,
libraryTemplate, libraryTemplate,
editObjectTemplate, editObjectTemplate,
editActionButtonsTemplate, editActionButtonsTemplate,
@ -100,22 +80,6 @@ define([
"$location", "$location",
"navigationService" "navigationService"
] ]
},
{
"key": "CreateMenuController",
"implementation": CreateMenuController,
"depends": [
"$scope"
]
},
{
"key": "LocatorController",
"implementation": LocatorController,
"depends": [
"$scope",
"$timeout",
"objectService"
]
} }
], ],
"actions": [ "actions": [
@ -137,22 +101,6 @@ define([
"group": "action", "group": "action",
"priority": 10 "priority": 10
}, },
{
"key": "properties",
"category": [
"contextual",
"view-control"
],
"implementation": PropertiesAction,
"cssClass": "major icon-pencil",
"name": "Edit Properties...",
"group": "action",
"priority": 10,
"description": "Edit properties of this object.",
"depends": [
"dialogService"
]
},
{ {
"key": "save-and-stop-editing", "key": "save-and-stop-editing",
"category": "save", "category": "save",
@ -177,22 +125,6 @@ define([
"notificationService" "notificationService"
] ]
}, },
{
"key": "save-as",
"category": "save",
"implementation": SaveAsAction,
"name": "Save As...",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"$injector",
"dialogService",
"copyService",
"notificationService",
"openmct"
],
"priority": "mandatory"
},
{ {
"key": "cancel", "key": "cancel",
"category": "conclude-editing", "category": "conclude-editing",
@ -210,10 +142,6 @@ define([
"category": "action", "category": "action",
"implementation": EditPersistableObjectsPolicy, "implementation": EditPersistableObjectsPolicy,
"depends": ["openmct"] "depends": ["openmct"]
},
{
"implementation": CreationPolicy,
"category": "creation"
} }
], ],
"templates": [ "templates": [
@ -243,42 +171,8 @@ define([
{ {
"key": "topbar-edit", "key": "topbar-edit",
"template": topbarEditTemplate "template": topbarEditTemplate
},
{
"key": "create-button",
"template": createButtonTemplate
},
{
"key": "create-menu",
"template": createMenuTemplate,
"uses": [
"action"
]
} }
], ],
"components": [
{
"key": "CreateActionProvider",
"provides": "actionService",
"type": "provider",
"implementation": CreateActionProvider,
"depends": [
"typeService",
"policyService"
]
},
{
"key": "CreationService",
"provides": "creationService",
"type": "provider",
"implementation": CreationService,
"depends": [
"$q",
"$log"
]
}
],
"representers": [ "representers": [
{ {
"implementation": EditRepresenter, "implementation": EditRepresenter,
@ -298,12 +192,6 @@ define([
] ]
} }
], ],
"controls": [
{
"key": "locator",
"template": locatorTemplate
}
],
"runs": [ "runs": [
{ {
depends: [ depends: [

View File

@ -1,30 +0,0 @@
<!--
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.
-->
<span ng-controller="ClickAwayController as createController">
<div class="s-menu-button major create-button" ng-click="createController.toggle()">
<span class="title-label">Create</span>
</div>
<div class="menu super-menu l-create-menu" ng-show="createController.isActive()">
<mct-representation mct-object="domainObject" key="'create-menu'">
</mct-representation>
</div>
</span>

View File

@ -1,45 +0,0 @@
<!--
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.
-->
<div class="w-menu" ng-controller="CreateMenuController">
<div class="col menu-items">
<ul>
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
ng-mouseleave="representation.activeMetadata = undefined"
class="menu-item-a {{ createAction.getMetadata().cssClass }}">
{{createAction.getMetadata().name}}
</a>
</li>
</ul>
</div>
<div class="col menu-item-description">
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
<div class="w-title-desc">
<div class="desc-area title">
{{representation.activeMetadata.name}}
</div>
<div class="desc-area description">
{{representation.activeMetadata.description}}
</div>
</div>
</div>
</div>

View File

@ -1,29 +0,0 @@
<!--
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.
-->
<div ng-controller="LocatorController" class="selector-list">
<div class="wrapper">
<mct-representation key="'tree'"
mct-object="rootObject"
ng-model="treeModel">
</mct-representation>
</div>
</div>

View File

@ -1,98 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* Edit the properties of a domain object. Shows a dialog
* which should display a set of properties similar to that
* shown in the Create wizard.
*/
define(
['./PropertiesDialog'],
function (PropertiesDialog) {
/**
* Implements the "Edit Properties" action, which prompts the user
* to modify a domain object's properties.
*
* @param {DialogService} dialogService a service which will show the dialog
* @param {DomainObject} object the object to be edited
* @param {ActionContext} context the context in which this action is performed
* @memberof platform/commonUI/edit
* @implements {Action}
* @constructor
*/
function PropertiesAction(dialogService, context) {
this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService;
}
PropertiesAction.prototype.perform = function () {
var type = this.domainObject.getCapability('type'),
domainObject = this.domainObject,
dialogService = this.dialogService;
// Update the domain object model based on user input
function updateModel(userInput, dialog) {
return domainObject.useCapability('mutation', function (model) {
dialog.updateModel(model, userInput);
});
}
function showDialog(objType) {
// Create a dialog object to generate the form structure, etc.
var dialog =
new PropertiesDialog(objType, domainObject.getModel());
// Show the dialog
return dialogService.getUserInput(
dialog.getFormStructure(),
dialog.getInitialFormValue()
).then(function (userInput) {
// Update the model, if user input was provided
return userInput && updateModel(userInput, dialog);
});
}
return type && showDialog(type);
};
/**
* Filter this action for applicability against a given context.
* This will ensure that a domain object is present in the
* context.
*/
PropertiesAction.appliesTo = function (context, view, openmct) {
let domainObject = (context || {}).domainObject;
if (!domainObject || (domainObject.model && domainObject.model.locked)) {
return false;
}
return openmct.objects.isPersistable(domainObject.id);
};
return PropertiesAction;
}
);

View File

@ -1,92 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
function () {
/**
* Construct a new Properties dialog.
*
* @param {TypeImpl} type the type of domain object for which properties
* will be specified
* @param {DomainObject} the object for which properties will be set
* @memberof platform/commonUI/edit
* @constructor
*/
function PropertiesDialog(type, model) {
this.type = type;
this.model = model;
this.properties = type.getProperties();
}
/**
* Get sections provided by this dialog.
* @return {FormStructure} the structure of this form
*/
PropertiesDialog.prototype.getFormStructure = function () {
return {
name: "Edit " + this.model.name,
sections: [{
name: "Properties",
rows: this.properties.map(function (property, index) {
// Property definition is same as form row definition
var row = JSON.parse(JSON.stringify(property.getDefinition()));
row.key = index;
return row;
}).filter(function (row) {
// Only show properties which are editable
return row.control;
})
}]
};
};
/**
* Get the initial state of the form shown by this dialog
* (based on the object model)
* @returns {object} initial state of the form
*/
PropertiesDialog.prototype.getInitialFormValue = function () {
var model = this.model;
// Start with initial values for properties
// Note that index needs to correlate to row.key
// from getFormStructure
return this.properties.map(function (property) {
return property.getValue(model);
});
};
/**
* Update a domain object model based on the value of a form.
*/
PropertiesDialog.prototype.updateModel = function (model, formValue) {
// Update all properties
this.properties.forEach(function (property, index) {
property.setValue(model, formValue[index]);
});
};
return PropertiesDialog;
}
);

View File

@ -1,210 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
'../creation/CreateWizard',
'./SaveInProgressDialog'
],
function (
CreateWizard,
SaveInProgressDialog
) {
/**
* The "Save" action; the action triggered by clicking Save from
* Edit Mode. Exits the editing user interface and invokes object
* capabilities to persist the changes that have been made.
* @constructor
* @implements {Action}
* @memberof platform/commonUI/edit
*/
function SaveAsAction(
$injector,
dialogService,
copyService,
notificationService,
openmct,
context
) {
this.domainObject = (context || {}).domainObject;
this.injectObjectService = function () {
this.objectService = $injector.get("objectService");
};
this.dialogService = dialogService;
this.copyService = copyService;
this.notificationService = notificationService;
this.openmct = openmct;
}
/**
* @private
*/
SaveAsAction.prototype.createWizard = function (parent) {
return new CreateWizard(
this.domainObject,
parent,
this.openmct
);
};
/**
* @private
*/
SaveAsAction.prototype.getObjectService = function () {
// Lazily acquire object service (avoids cyclical dependency)
if (!this.objectService) {
this.injectObjectService();
}
return this.objectService;
};
/**
* Save changes and conclude editing.
*
* @returns {Promise} a promise that will be fulfilled when
* cancellation has completed
* @memberof platform/commonUI/edit.SaveAction#
*/
SaveAsAction.prototype.perform = function () {
return this.save();
};
/**
* @private
*/
SaveAsAction.prototype.save = function () {
var self = this,
domainObject = this.domainObject,
dialog = new SaveInProgressDialog(this.dialogService);
function doWizardSave(parent) {
var wizard = self.createWizard(parent);
return self.dialogService
.getUserInput(wizard.getFormStructure(true),
wizard.getInitialFormValue())
.then(wizard.populateObjectFromInput.bind(wizard), function (failureReason) {
return Promise.reject("user canceled");
});
}
function showBlockingDialog(object) {
dialog.show();
return object;
}
function hideBlockingDialog(object) {
dialog.hide();
return object;
}
function fetchObject(objectId) {
return self.getObjectService().getObjects([objectId]).then(function (objects) {
return objects[objectId];
});
}
function getParent(object) {
return fetchObject(object.getModel().location);
}
function saveObject(object) {
//persist the object, which adds it to the transaction and then call editor.save
return object.getCapability("persistence").persist()
.then(() => {
return self.openmct.editor.save().then(() => {
return object;
});
});
}
function addSavedObjectToParent(parent) {
return parent.getCapability("composition")
.add(domainObject)
.then(function (addedObject) {
return parent.getCapability("persistence").persist()
.then(function () {
return addedObject;
});
});
}
function indexForSearch(addedObject) {
addedObject.useCapability('mutation', (model) => {
return model;
});
return addedObject;
}
function onSuccess(object) {
self.notificationService.info("Save Succeeded");
return object;
}
function onFailure(reason) {
hideBlockingDialog();
if (reason !== "user canceled") {
self.notificationService.error("Save Failed");
}
throw reason;
}
return getParent(domainObject)
.then(doWizardSave)
.then(showBlockingDialog)
.then(saveObject)
.then(getParent)
.then(addSavedObjectToParent)
.then((addedObject) => {
return fetchObject(addedObject.getId());
})
.then(indexForSearch)
.then(hideBlockingDialog)
.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
*/
SaveAsAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined
&& domainObject.hasCapability('editor')
&& domainObject.getCapability('editor').isEditContextRoot()
&& domainObject.getModel().persisted === undefined;
};
return SaveAsAction;
}
);

View File

@ -1,133 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* Module defining CreateAction. Created by vwoeltje on 11/10/14.
*/
define(
[],
function () {
/**
* The Create Action is performed to create new instances of
* domain objects of a specific type. This is the action that
* is performed when a user uses the Create menu.
*
* @memberof platform/commonUI/browse
* @implements {Action}
* @constructor
*
* @param {Type} type the type of domain object to create
* @param {DomainObject} parent the domain object that should
* act as a container for the newly-created object
* (note that the user will have an opportunity to
* override this)
* @param {ActionContext} context the context in which the
* action is being performed
*/
function CreateAction(type, parent, context, openmct) {
this.metadata = {
key: 'create',
cssClass: type.getCssClass(),
name: type.getName(),
type: type.getKey(),
description: type.getDescription(),
context: context
};
this.type = type;
this.parent = parent;
this.openmct = openmct;
}
/**
* Create a new object of the given type.
* This will prompt for user input first.
*/
CreateAction.prototype.perform = function () {
var newModel = this.type.getInitialModel(),
openmct = this.openmct,
newObject;
function onCancel() {
openmct.editor.cancel();
}
function isFirstViewEditable(domainObject, objectPath) {
let firstView = openmct.objectViews.get(domainObject, objectPath)[0];
return firstView && firstView.canEdit && firstView.canEdit(domainObject, objectPath);
}
function navigateAndEdit(object) {
let objectPath = object.getCapability('context').getPath(),
url = '#/browse/' + objectPath
.slice(1)
.map(function (o) {
return o && openmct.objects.makeKeyString(o.getId());
})
.join('/');
function editObject() {
const path = objectPath.slice(-1).map(obj => {
const objNew = obj.getCapability('adapter').invoke();
return objNew;
});
if (isFirstViewEditable(object.useCapability('adapter'), path)) {
openmct.editor.edit();
}
}
openmct.router.once('afterNavigation', editObject);
openmct.router.navigate(url);
}
newModel.type = this.type.getKey();
newModel.location = this.parent.getId();
newObject = this.parent.useCapability('instantiation', newModel);
openmct.editor.edit();
newObject.getCapability("action").perform("save-as").then(navigateAndEdit, onCancel);
// TODO: support editing object without saving object first.
// Which means we have to toggle createwizard afterwards. For now,
// We will disable this.
};
/**
* Metadata associated with a Create action.
* @typedef {ActionMetadata} CreateActionMetadata
* @property {string} type the key for the type of domain object
* to be created
*/
/**
* Get metadata about this action.
* @returns {CreateActionMetadata} metadata about this action
*/
CreateAction.prototype.getMetadata = function () {
return this.metadata;
};
return CreateAction;
}
);

View File

@ -1,80 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* Module defining CreateActionProvider.js. Created by vwoeltje on 11/10/14.
*/
define(
["./CreateAction"],
function (CreateAction) {
/**
* The CreateActionProvider is an ActionProvider which introduces
* a Create action for each creatable domain object type.
*
* @memberof platform/commonUI/browse
* @constructor
* @implements {ActionService}
*
* @param {TypeService} typeService the type service, used to discover
* available types
* @param {DialogService} dialogService the dialog service, used by
* specific Create actions to get user input to populate the
* model of the newly-created domain object.
* @param {CreationService} creationService the creation service (also
* introduced in this bundle), responsible for handling actual
* object creation.
*/
function CreateActionProvider(typeService, policyService) {
this.typeService = typeService;
this.policyService = policyService;
}
CreateActionProvider.prototype.getActions = function (actionContext) {
var context = actionContext || {},
key = context.key,
destination = context.domainObject,
self = this;
// We only provide Create actions, and we need a
// domain object to serve as the container for the
// newly-created object (although the user may later
// make a different selection)
if (key !== 'create' || !destination) {
return [];
}
// Introduce one create action per type
return this.typeService.listTypes().filter(function (type) {
return self.policyService.allow("creation", type);
}).map(function (type) {
return new CreateAction(
type,
destination,
context
);
});
};
return CreateActionProvider;
}
);

View File

@ -1,57 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* Module defining CreateMenuController. Created by vwoeltje on 11/10/14.
*/
define(
[],
function () {
/**
* Controller for the Create menu; maintains an up-to-date
* set of Create actions based on the currently-selected
* domain object.
*
* @memberof platform/commonUI/browse
* @constructor
*/
function CreateMenuController($scope) {
// Update the set of Create actions
function refreshActions() {
$scope.createActions = $scope.action
? $scope.action.getActions('create')
: [];
}
// Listen for new instances of the represented object's
// "action" capability. This is provided by the mct-representation
// for the Create button.
// A watch is needed here (instead of invoking action.getActions
// directly) because different action instances may be returned
// with each call.
$scope.$watch("action", refreshActions);
}
return CreateMenuController;
}
);

View File

@ -1,186 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
function () {
/**
* A class for capturing user input data from an object creation
* dialog, and populating a domain object with that data.
*
* @param {DomainObject} domainObject the newly created object to
* populate with user input
* @param {DomainObject} parent the domain object to serve as
* the initial parent for the created object, in the dialog
* @memberof platform/commonUI/browse
* @constructor
*/
function CreateWizard(domainObject, parent, openmct) {
this.type = domainObject.getCapability('type');
this.model = domainObject.getModel();
this.domainObject = domainObject;
this.properties = this.type.getProperties();
this.parent = parent;
this.openmct = openmct;
}
/**
* Get the form model for this wizard; this is a description
* that will be rendered to an HTML form. See the
* platform/forms bundle
* @param {boolean} includeLocation if true, a 'location' section
* will be included that will allow the user to select the location
* of the newly created object, otherwise the .location property of
* the model will be used.
* @return {FormModel} formModel the form model to
* show in the create dialog
*/
CreateWizard.prototype.getFormStructure = function (includeLocation) {
var sections = [],
domainObject = this.domainObject,
self = this;
function validateLocation(parent) {
return parent && self.openmct.composition.checkPolicy(parent.useCapability('adapter'), domainObject.useCapability('adapter'));
}
sections.push({
name: "Properties",
rows: this.properties.map(function (property, index) {
// Property definition is same as form row definition
var row = JSON.parse(JSON.stringify(property.getDefinition()));
// Use index as the key into the formValue;
// this correlates to the indexing provided by
// getInitialFormValue
row.key = index;
return row;
}).filter(function (row) {
// Only show rows which have defined controls
return row && row.control;
})
});
// Ensure there is always a "save in" section
if (includeLocation) {
sections.push({
name: 'Location',
cssClass: "grows",
rows: [{
name: "Save In",
control: "locator",
validate: validateLocation.bind(this),
key: "createParent"
}]
});
}
return {
sections: sections,
name: "Create a New " + this.type.getName()
};
};
/**
* Given some form input values and a domain object, populate the
* domain object used to create this wizard from the given form values.
* @param formValue
* @returns {DomainObject}
*/
CreateWizard.prototype.populateObjectFromInput = function (formValue) {
var parent = this.getLocation(formValue),
formModel = this.createModel(formValue);
formModel.location = parent.getId();
this.updateNamespaceFromParent(parent);
this.domainObject.useCapability("mutation", function () {
return formModel;
});
return this.domainObject;
};
/** @private */
CreateWizard.prototype.updateNamespaceFromParent = function (parent) {
let childIdentifier = this.domainObject.useCapability('adapter').identifier;
let parentIdentifier = parent.useCapability('adapter').identifier;
childIdentifier.namespace = parentIdentifier.namespace;
this.domainObject.id = this.openmct.objects.makeKeyString(childIdentifier);
};
/**
* Get the initial value for the form being described.
* This will include the values for all properties described
* in the structure.
*
* @returns {object} the initial value of the form
*/
CreateWizard.prototype.getInitialFormValue = function () {
// Start with initial values for properties
var model = this.model,
formValue = this.properties.map(function (property) {
return property.getValue(model);
});
// Include the createParent
formValue.createParent = this.parent;
return formValue;
};
/**
* Based on a populated form, get the domain object which
* should be used as a parent for the newly-created object.
* @private
* @return {DomainObject}
*/
CreateWizard.prototype.getLocation = function (formValue) {
return formValue.createParent || this.parent;
};
/**
* Create the domain object model for a newly-created object,
* based on user input read from a formModel.
* @private
* @return {object} the domain object model
*/
CreateWizard.prototype.createModel = function (formValue) {
// Clone
var newModel = JSON.parse(JSON.stringify(this.model));
// Always use the type from the type definition
newModel.type = this.type.getKey();
// Update all properties
this.properties.forEach(function (property, index) {
property.setValue(newModel, formValue[index]);
});
return newModel;
};
return CreateWizard;
}
);

View File

@ -1,107 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* Module defining CreateService. Created by vwoeltje on 11/10/14.
*/
define(
[],
function () {
var NON_PERSISTENT_WARNING =
"Tried to create an object in non-persistent container.";
/**
* The creation service is responsible for instantiating and
* persisting new domain objects. Handles all actual object
* mutation and persistence associated with domain object
* creation.
* @memberof platform/commonUI/browse
* @constructor
*/
function CreationService($q, $log) {
this.$q = $q;
this.$log = $log;
}
/**
* Create a new domain object with the provided model, as
* a member of the provided parent domain object's composition.
* This parent will additionally determine which persistence
* space an object is created within (as it is possible to
* have multiple persistence spaces attached.)
*
* Note that the model passed in for object creation may be
* modified to attach additional initial properties associated
* with domain object creation.
*
* @param {object} model the model for the newly-created
* domain object
* @param {DomainObject} parent the domain object which
* should contain the newly-created domain object
* in its composition
* @return {Promise} a promise that will resolve when the domain
* object has been created
*/
CreationService.prototype.createObject = function (model, parent) {
var persistence = parent.getCapability("persistence"),
newObject = parent.useCapability("instantiation", model),
newObjectPersistence = newObject.getCapability("persistence"),
self = this;
// Add the newly-created object's id to the parent's
// composition, so that it will subsequently appear
// as a child contained by that parent.
function addToComposition() {
var compositionCapability = parent.getCapability('composition'),
addResult = compositionCapability
&& compositionCapability.add(newObject);
return self.$q.when(addResult).then(function (result) {
if (!result) {
self.$log.error("Could not modify " + parent.getId());
return undefined;
}
return persistence.persist().then(function () {
return result;
});
});
}
// We need the parent's persistence capability to determine
// what space to create the new object's model in.
if (!persistence || !newObjectPersistence) {
self.$log.warn(NON_PERSISTENT_WARNING);
return self.$q.reject(new Error(NON_PERSISTENT_WARNING));
}
// Persist the new object, then add it to composition.
return newObjectPersistence.persist().then(addToComposition);
};
return CreationService;
}
);

View File

@ -1,98 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* Controller for the "locator" control, which provides the
* user with the ability to select a domain object as the
* destination for a newly-created object in the Create menu.
* @memberof platform/commonUI/browse
* @constructor
*/
function LocatorController($scope, $timeout, objectService) {
// Populate values needed by the locator control. These are:
// * rootObject: The top-level object, since we want to show
// the full tree
// * treeModel: The model for the embedded tree representation,
// used for bi-directional object selection.
function setLocatingObject(domainObject, priorObject) {
var context = domainObject
&& domainObject.getCapability("context"),
contextRoot = context && context.getRoot();
if (contextRoot && contextRoot !== $scope.rootObject) {
$scope.rootObject = undefined;
// Update the displayed tree on a timeout to avoid
// an infinite digest exception.
$timeout(function () {
$scope.rootObject =
(context && context.getRoot()) || $scope.rootObject;
}, 0);
} else if (!contextRoot && !$scope.rootObject) {
// Update the displayed tree on a timeout to avoid
// an infinite digest exception.
objectService.getObjects(['ROOT'])
.then(function (objects) {
$timeout(function () {
$scope.rootObject = objects.ROOT;
}, 0);
});
}
$scope.treeModel.selectedObject = domainObject;
$scope.ngModel[$scope.field] = domainObject;
// Restrict which locations can be selected
if (domainObject
&& $scope.structure
&& $scope.structure.validate) {
if (!$scope.structure.validate(domainObject)) {
setLocatingObject(priorObject, undefined);
return;
}
}
// Set validity
if ($scope.ngModelController) {
$scope.ngModelController.$setValidity(
'composition',
Boolean($scope.treeModel.selectedObject)
);
}
}
// Initial state for the tree's model
$scope.treeModel =
{ selectedObject: $scope.ngModel[$scope.field] };
// Watch for changes from the tree
$scope.$watch("treeModel.selectedObject", setLocatingObject);
}
return LocatorController;
}
);

View File

@ -1,110 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
['../../src/actions/PropertiesAction'],
function (PropertiesAction) {
describe("Properties action", function () {
var capabilities, model, object, context, input, dialogService, action, openmct;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
}
};
}
beforeEach(function () {
openmct = {
objects: {
isPersistable: jasmine.createSpy('isPersistable')
}
};
capabilities = {
type: {
getProperties: function () {
return [];
},
hasFeature: jasmine.createSpy('hasFeature')
},
mutation: jasmine.createSpy("mutation")
};
model = {};
input = {};
object = {
getId: function () {
return 'test-id';
},
getCapability: function (k) {
return capabilities[k];
},
getModel: function () {
return model;
},
useCapability: function (k, v) {
return capabilities[k](v);
},
hasCapability: function () {
return true;
}
};
context = {
someKey: "some value",
domainObject: object
};
dialogService = {
getUserInput: function () {
return mockPromise(input);
}
};
capabilities.type.hasFeature.and.returnValue(true);
capabilities.mutation.and.returnValue(true);
openmct.objects.isPersistable.and.returnValue(true);
action = new PropertiesAction(dialogService, context);
});
it("mutates an object when performed", function () {
action.perform();
expect(capabilities.mutation).toHaveBeenCalled();
capabilities.mutation.calls.mostRecent().args[0]({});
});
it("does not muate object upon cancel", function () {
input = undefined;
action.perform();
expect(capabilities.mutation).not.toHaveBeenCalled();
});
it("is only applicable when a domain object is in context", function () {
expect(PropertiesAction.appliesTo(context, undefined, openmct)).toBeTruthy();
expect(PropertiesAction.appliesTo({}, undefined, openmct)).toBeFalsy();
expect(openmct.objects.isPersistable).toHaveBeenCalled();
});
});
}
);

View File

@ -1,80 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/actions/PropertiesDialog"],
function (PropertiesDialog) {
describe("Properties dialog", function () {
var type, properties, model, dialog;
beforeEach(function () {
type = {
getProperties: function () {
return properties;
}
};
model = { x: "initial value" };
properties = ["x", "y", "z"].map(function (k) {
return {
getValue: function (m) {
return m[k];
},
setValue: function (m, v) {
m[k] = v;
},
getDefinition: function () {
return { control: 'textfield '};
}
};
});
dialog = new PropertiesDialog(type, model);
});
it("provides sections based on type properties", function () {
expect(dialog.getFormStructure().sections[0].rows.length)
.toEqual(properties.length);
});
it("pulls initial values from object model", function () {
expect(dialog.getInitialFormValue()[0])
.toEqual("initial value");
});
it("populates models with form results", function () {
dialog.updateModel(model, [
"new value",
"other new value",
42
]);
expect(model).toEqual({
x: "new value",
y: "other new value",
z: 42
});
});
});
}
);

View File

@ -1,249 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/actions/SaveAsAction"],
function (SaveAsAction) {
xdescribe("The Save As action", function () {
var mockDomainObject,
mockClonedObject,
mockEditorCapability,
mockActionCapability,
mockObjectService,
mockDialogService,
mockCopyService,
mockNotificationService,
mockParent,
actionContext,
capabilities = {},
action;
function noop() {}
function mockPromise(value) {
return (value || {}).then ? value
: {
then: function (callback) {
return mockPromise(callback(value));
},
catch: function (callback) {
return mockPromise(callback(value));
}
};
}
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getCapability",
"hasCapability",
"getModel",
"getId"
]
);
mockDomainObject.hasCapability.and.returnValue(true);
mockDomainObject.getCapability.and.callFake(function (capability) {
return capabilities[capability];
});
mockDomainObject.getModel.and.returnValue({
location: 'a',
persisted: undefined
});
mockDomainObject.getId.and.returnValue(0);
mockClonedObject = jasmine.createSpyObj(
"clonedObject",
[
"getId"
]
);
mockClonedObject.getId.and.returnValue(1);
mockParent = jasmine.createSpyObj(
"parentObject",
[
"getCapability",
"hasCapability",
"getModel"
]
);
mockEditorCapability = jasmine.createSpyObj(
"editor",
["save", "finish", "isEditContextRoot"]
);
mockEditorCapability.save.and.returnValue(mockPromise(true));
mockEditorCapability.finish.and.returnValue(mockPromise(true));
mockEditorCapability.isEditContextRoot.and.returnValue(true);
capabilities.editor = mockEditorCapability;
mockActionCapability = jasmine.createSpyObj(
"action",
["perform"]
);
capabilities.action = mockActionCapability;
mockObjectService = jasmine.createSpyObj(
"objectService",
["getObjects"]
);
mockObjectService.getObjects.and.returnValue(mockPromise({'a': mockParent}));
mockDialogService = jasmine.createSpyObj(
"dialogService",
[
"getUserInput",
"showBlockingMessage"
]
);
mockDialogService.getUserInput.and.returnValue(mockPromise(undefined));
mockCopyService = jasmine.createSpyObj(
"copyService",
[
"perform"
]
);
mockCopyService.perform.and.returnValue(mockPromise(mockClonedObject));
mockNotificationService = jasmine.createSpyObj(
"notificationService",
[
"info",
"error"
]
);
actionContext = {
domainObject: mockDomainObject
};
action = new SaveAsAction(
undefined,
undefined,
mockDialogService,
mockCopyService,
mockNotificationService,
actionContext);
spyOn(action, "getObjectService");
action.getObjectService.and.returnValue(mockObjectService);
spyOn(action, "createWizard");
action.createWizard.and.returnValue({
getFormStructure: noop,
getInitialFormValue: noop,
populateObjectFromInput: function () {
return mockDomainObject;
}
});
});
it("only applies to domain object with an editor capability", function () {
expect(SaveAsAction.appliesTo(actionContext)).toBe(true);
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.hasCapability.and.returnValue(false);
mockDomainObject.getCapability.and.returnValue(undefined);
expect(SaveAsAction.appliesTo(actionContext)).toBe(false);
});
it("only applies to domain object that has not already been"
+ " persisted", function () {
expect(SaveAsAction.appliesTo(actionContext)).toBe(true);
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.getModel.and.returnValue({persisted: 0});
expect(SaveAsAction.appliesTo(actionContext)).toBe(false);
});
it("uses the editor capability to save the object", function () {
mockEditorCapability.save.and.returnValue(Promise.resolve());
return action.perform().then(function () {
expect(mockEditorCapability.save).toHaveBeenCalled();
});
});
it("uses the editor capability to finish editing the object", function () {
return action.perform().then(function () {
expect(mockEditorCapability.finish.calls.count()).toBeGreaterThan(0);
});
});
it("returns to browse after save", function () {
spyOn(action, "save");
action.save.and.returnValue(mockPromise(mockDomainObject));
action.perform();
expect(mockActionCapability.perform).toHaveBeenCalledWith(
"navigate"
);
});
it("prompts the user for object details", function () {
action.perform();
expect(mockDialogService.getUserInput).toHaveBeenCalled();
});
describe("in order to keep the user in the loop", function () {
var mockDialogHandle;
beforeEach(function () {
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
mockDialogService.showBlockingMessage.and.returnValue(mockDialogHandle);
});
it("shows a blocking dialog indicating that saving is in progress", function () {
mockEditorCapability.save.and.returnValue(new Promise(function () {}));
action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
});
it("hides the blocking dialog after saving finishes", function () {
return action.perform().then(function () {
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
});
});
it("notifies if saving succeeded", function () {
return action.perform().then(function () {
expect(mockNotificationService.info).toHaveBeenCalled();
expect(mockNotificationService.error).not.toHaveBeenCalled();
});
});
it("notifies if saving failed", function () {
mockCopyService.perform.and.returnValue(Promise.reject("some failure reason"));
action.perform().then(function () {
expect(mockNotificationService.error).toHaveBeenCalled();
expect(mockNotificationService.info).not.toHaveBeenCalled();
});
});
});
});
}
);

View File

@ -1,118 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/creation/CreateActionProvider"],
function (CreateActionProvider) {
describe("The create action provider", function () {
var mockTypeService,
mockPolicyService,
mockCreationPolicy,
mockPolicyMap = {},
mockTypes,
provider;
function createMockType(name) {
var mockType = jasmine.createSpyObj(
"type" + name,
[
"getKey",
"getGlyph",
"getCssClass",
"getName",
"getDescription",
"getProperties",
"getInitialModel",
"hasFeature"
]
);
mockType.hasFeature.and.returnValue(true);
mockType.getName.and.returnValue(name);
return mockType;
}
beforeEach(function () {
mockTypeService = jasmine.createSpyObj(
"typeService",
["listTypes"]
);
mockPolicyService = jasmine.createSpyObj(
"policyService",
["allow"]
);
mockTypes = ["A", "B", "C"].map(createMockType);
mockTypes.forEach(function (type) {
mockPolicyMap[type.getName()] = true;
});
mockCreationPolicy = function (type) {
return mockPolicyMap[type.getName()];
};
mockPolicyService.allow.and.callFake(function (category, type) {
return Boolean(category === "creation" && mockCreationPolicy(type));
});
mockTypeService.listTypes.and.returnValue(mockTypes);
provider = new CreateActionProvider(
mockTypeService,
mockPolicyService
);
});
it("exposes one create action per type", function () {
expect(provider.getActions({
key: "create",
domainObject: {}
}).length).toEqual(3);
});
it("exposes no non-create actions", function () {
expect(provider.getActions({
key: "somethingElse",
domainObject: {}
}).length).toEqual(0);
});
it("does not expose non-creatable types", function () {
// One of the types won't have the creation feature...
mockPolicyMap[mockTypes[0].getName()] = false;
// ...so it should have been filtered out.
expect(provider.getActions({
key: "create",
domainObject: {}
}).length).toEqual(2);
// Make sure it was creation which was used to check
expect(mockPolicyService.allow)
.toHaveBeenCalledWith("creation", mockTypes[0]);
});
});
}
);

View File

@ -1,186 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/creation/CreateAction"],
function (CreateAction) {
xdescribe("The create action", function () {
var mockType,
mockParent,
mockContext,
mockDomainObject,
capabilities = {},
mockEditAction,
action;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
}
};
}
beforeEach(function () {
mockType = jasmine.createSpyObj(
"type",
[
"getKey",
"getGlyph",
"getCssClass",
"getName",
"getDescription",
"getProperties",
"getInitialModel"
]
);
mockParent = jasmine.createSpyObj(
"domainObject",
[
"getId",
"getModel",
"getCapability",
"useCapability"
]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getId",
"getModel",
"getCapability",
"hasCapability",
"useCapability"
]
);
mockDomainObject.hasCapability.and.callFake(function (name) {
return Boolean(capabilities[name]);
});
mockDomainObject.getCapability.and.callFake(function (name) {
return capabilities[name];
});
capabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"getActions",
"perform"
]
);
capabilities.editor = jasmine.createSpyObj(
"editorCapability",
[
"edit",
"save",
"finish"
]
);
mockEditAction = jasmine.createSpyObj(
"editAction",
[
"perform"
]
);
mockContext = {
domainObject: mockParent
};
mockParent.useCapability.and.returnValue(mockDomainObject);
mockType.getKey.and.returnValue("test");
mockType.getCssClass.and.returnValue("icon-telemetry");
mockType.getDescription.and.returnValue("a test type");
mockType.getName.and.returnValue("Test");
mockType.getProperties.and.returnValue([]);
mockType.getInitialModel.and.returnValue({});
action = new CreateAction(
mockType,
mockParent,
mockContext
);
});
it("exposes type-appropriate metadata", function () {
var metadata = action.getMetadata();
expect(metadata.name).toEqual("Test");
expect(metadata.description).toEqual("a test type");
expect(metadata.cssClass).toEqual("icon-telemetry");
});
describe("the perform function", function () {
var promise = jasmine.createSpyObj("promise", ["then"]);
beforeEach(function () {
capabilities.action.getActions.and.returnValue([mockEditAction]);
});
it("uses the instantiation capability when performed", function () {
action.perform();
expect(mockParent.useCapability).toHaveBeenCalledWith("instantiation", jasmine.any(Object));
});
it("uses the edit action if available", function () {
action.perform();
expect(mockEditAction.perform).toHaveBeenCalled();
});
it("uses the save-as action if object does not have an edit action"
+ " available", function () {
capabilities.action.getActions.and.returnValue([]);
capabilities.action.perform.and.returnValue(mockPromise(undefined));
capabilities.editor.save.and.returnValue(promise);
action.perform();
expect(capabilities.action.perform).toHaveBeenCalledWith("save-as");
});
describe("uses to editor capability", function () {
beforeEach(function () {
capabilities.action.getActions.and.returnValue([]);
capabilities.action.perform.and.returnValue(promise);
capabilities.editor.save.and.returnValue(promise);
});
it("to save the edit if user saves dialog", function () {
action.perform();
expect(promise.then).toHaveBeenCalled();
promise.then.calls.mostRecent().args[0]();
expect(capabilities.editor.save).toHaveBeenCalled();
});
it("to finish the edit if user cancels dialog", function () {
action.perform();
promise.then.calls.mostRecent().args[1]();
expect(capabilities.editor.finish).toHaveBeenCalled();
});
});
});
});
}
);

View File

@ -1,65 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/creation/CreateMenuController"],
function (CreateMenuController) {
describe("The create menu controller", function () {
var mockScope,
mockActions,
controller;
beforeEach(function () {
mockActions = jasmine.createSpyObj("action", ["getActions"]);
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
controller = new CreateMenuController(mockScope);
});
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 create actions", function () {
mockScope.action = mockActions;
mockActions.getActions.and.returnValue(["a", "b", "c"]);
// Call the watch
mockScope.$watch.calls.mostRecent().args[1]();
// Should have grouped and ungrouped actions in scope now
expect(mockScope.createActions.length).toEqual(3);
// Make sure the right action type was requested
expect(mockActions.getActions).toHaveBeenCalledWith("create");
});
});
}
);

View File

@ -1,197 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/creation/CreateWizard"],
function (CreateWizard) {
xdescribe("The create wizard", function () {
var mockType,
mockParent,
mockProperties,
mockPolicyService,
testModel,
mockDomainObject,
wizard;
function createMockProperty(name) {
var mockProperty = jasmine.createSpyObj(
"property" + name,
["getDefinition", "getValue", "setValue"]
);
mockProperty.getDefinition.and.returnValue({
control: "textfield"
});
mockProperty.getValue.and.returnValue(name);
return mockProperty;
}
beforeEach(function () {
mockType = jasmine.createSpyObj(
"type",
[
"getKey",
"getGlyph",
"getCssClass",
"getName",
"getDescription",
"getProperties",
"getInitialModel"
]
);
mockParent = jasmine.createSpyObj(
"domainObject",
[
"getId",
"getModel",
"getCapability"
]
);
mockProperties = ["A", "B", "C"].map(createMockProperty);
mockPolicyService = jasmine.createSpyObj('policyService', ['allow']);
testModel = { someKey: "some value" };
mockType.getKey.and.returnValue("test");
mockType.getCssClass.and.returnValue("icon-telemetry");
mockType.getDescription.and.returnValue("a test type");
mockType.getName.and.returnValue("Test");
mockType.getInitialModel.and.returnValue(testModel);
mockType.getProperties.and.returnValue(mockProperties);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getCapability', 'useCapability', 'getModel']
);
//Mocking the getCapability('type') call
mockDomainObject.getCapability.and.returnValue(mockType);
mockDomainObject.useCapability.and.returnValue();
mockDomainObject.getModel.and.returnValue(testModel);
wizard = new CreateWizard(
mockDomainObject,
mockParent,
mockPolicyService
);
});
it("creates a form model with a Properties section", function () {
expect(wizard.getFormStructure().sections[0].name)
.toEqual("Properties");
});
it("adds one row per defined type property", function () {
// Three properties were defined in the mock type
expect(wizard.getFormStructure().sections[0].rows.length)
.toEqual(3);
});
it("interprets form data using type-defined properties", function () {
// Use key names from mock properties
wizard.createModel([
"field 0",
"field 1",
"field 2"
]);
// Should have gotten a setValue call
mockProperties.forEach(function (mockProperty, i) {
expect(mockProperty.setValue).toHaveBeenCalledWith(
{
someKey: "some value",
type: 'test'
},
"field " + i
);
});
});
it("looks up initial values from properties", function () {
var initialValue = wizard.getInitialFormValue();
expect(initialValue[0]).toEqual("A");
expect(initialValue[1]).toEqual("B");
expect(initialValue[2]).toEqual("C");
// Verify that expected argument was passed
mockProperties.forEach(function (mockProperty) {
expect(mockProperty.getValue)
.toHaveBeenCalledWith(testModel);
});
});
it("populates the model on the associated object", function () {
var formValue = {
"A": "ValueA",
"B": "ValueB",
"C": "ValueC"
},
compareModel = wizard.createModel(formValue);
//populateObjectFromInput adds a .location attribute that is not added by createModel.
compareModel.location = undefined;
wizard.populateObjectFromInput(formValue);
expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function));
expect(mockDomainObject.useCapability.calls.mostRecent().args[1]()).toEqual(compareModel);
});
it("validates selection types using policy", function () {
var mockDomainObj = jasmine.createSpyObj(
'domainObject',
['getCapability']
),
mockOtherType = jasmine.createSpyObj(
'otherType',
['getKey']
),
//Create a form structure with location
structure = wizard.getFormStructure(true),
sections = structure.sections,
rows = structure.sections[sections.length - 1].rows,
locationRow = rows[rows.length - 1];
mockDomainObj.getCapability.and.returnValue(mockOtherType);
locationRow.validate(mockDomainObj);
// Should check policy to see if the user-selected location
// can actually contain objects of this type
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockDomainObj,
mockDomainObject
);
});
it("creates a form model without a location if not requested", function () {
expect(wizard.getFormStructure(false).sections.some(function (section) {
return section.name === 'Location';
})).toEqual(false);
});
});
}
);

View File

@ -1,216 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/creation/CreationService"],
function (CreationService) {
describe("The creation service", function () {
var mockQ,
mockLog,
mockParentObject,
mockNewObject,
mockMutationCapability,
mockPersistenceCapability,
mockCompositionCapability,
mockContextCapability,
mockCreationCapability,
mockCapabilities,
mockNewPersistenceCapability,
creationService;
function mockPromise(value) {
return (value && value.then) ? value : {
then: function (callback) {
return mockPromise(callback(value));
}
};
}
function mockReject(value) {
return {
then: function (callback, error) {
return mockPromise(error(value));
}
};
}
beforeEach(function () {
mockQ = {
when: mockPromise,
reject: mockReject
};
mockLog = jasmine.createSpyObj(
"$log",
["error", "warn", "info", "debug"]
);
mockParentObject = jasmine.createSpyObj(
"parentObject",
["getId", "getCapability", "useCapability"]
);
mockNewObject = jasmine.createSpyObj(
"newObject",
["getId", "getCapability", "useCapability"]
);
mockMutationCapability = jasmine.createSpyObj(
"mutation",
["invoke"]
);
mockPersistenceCapability = jasmine.createSpyObj(
"persistence",
["persist", "getSpace"]
);
mockCompositionCapability = jasmine.createSpyObj(
"composition",
["invoke", "add"]
);
mockContextCapability = jasmine.createSpyObj(
"context",
["getPath"]
);
mockCreationCapability = jasmine.createSpyObj(
"creation",
["instantiate", "invoke"]
);
mockCapabilities = {
mutation: mockMutationCapability,
persistence: mockPersistenceCapability,
composition: mockCompositionCapability,
context: mockContextCapability,
instantiation: mockCreationCapability
};
mockNewPersistenceCapability = jasmine.createSpyObj(
"new-persistence",
["persist", "getSpace"]
);
mockParentObject.getCapability.and.callFake(function (key) {
return mockCapabilities[key];
});
mockParentObject.useCapability.and.callFake(function (key, value) {
return mockCapabilities[key].invoke(value);
});
mockParentObject.getId.and.returnValue('parentId');
mockNewObject.getId.and.returnValue('newId');
mockNewObject.getCapability.and.callFake(function (c) {
return c === 'persistence'
? mockNewPersistenceCapability : undefined;
});
mockPersistenceCapability.persist
.and.returnValue(mockPromise(true));
mockNewPersistenceCapability.persist
.and.returnValue(mockPromise(true));
mockMutationCapability.invoke.and.returnValue(mockPromise(true));
mockPersistenceCapability.getSpace.and.returnValue("testSpace");
mockCompositionCapability.invoke.and.returnValue(
mockPromise([mockNewObject])
);
mockCompositionCapability.add.and.returnValue(mockPromise(true));
mockCreationCapability.instantiate.and.returnValue(mockNewObject);
mockCreationCapability.invoke.and.callFake(function (model) {
return mockCreationCapability.instantiate(model);
});
creationService = new CreationService(
mockQ,
mockLog
);
});
it("allows new objects to be created", function () {
var model = { someKey: "some value" };
creationService.createObject(model, mockParentObject);
expect(mockCreationCapability.instantiate)
.toHaveBeenCalledWith(model);
});
it("adds new objects to the parent's composition", function () {
var model = { someKey: "some value" };
creationService.createObject(model, mockParentObject);
// Verify that a new ID was added
expect(mockCompositionCapability.add)
.toHaveBeenCalledWith(mockNewObject);
});
it("provides the newly-created object", function () {
var mockDomainObject = jasmine.createSpyObj(
'newDomainObject',
['getId', 'getModel', 'getCapability']
),
mockCallback = jasmine.createSpy('callback');
// Act as if the object had been created
mockCompositionCapability.add.and.callFake(function (id) {
mockDomainObject.getId.and.returnValue(id);
mockCompositionCapability.invoke
.and.returnValue(mockPromise([mockDomainObject]));
return mockPromise(mockDomainObject);
});
// Should find it in the composition
creationService.createObject({}, mockParentObject)
.then(mockCallback);
expect(mockCallback).toHaveBeenCalledWith(mockDomainObject);
});
it("warns if parent has no persistence capability", function () {
// Callbacks
var success = jasmine.createSpy("success"),
failure = jasmine.createSpy("failure");
mockCapabilities.persistence = undefined;
creationService.createObject({}, mockParentObject).then(
success,
failure
);
// Should have warned and rejected the promise
expect(mockLog.warn).toHaveBeenCalled();
expect(success).not.toHaveBeenCalled();
expect(failure).toHaveBeenCalled();
});
it("logs an error when mutation fails", function () {
// If mutation of the parent fails, we've lost the
// created object - this is an error.
var model = { someKey: "some value" };
mockCompositionCapability.add.and.returnValue(mockPromise(false));
creationService.createObject(model, mockParentObject);
expect(mockLog.error).toHaveBeenCalled();
});
});
}
);

View File

@ -1,171 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/creation/LocatorController"],
function (LocatorController) {
describe("The locator controller", function () {
var mockScope,
mockTimeout,
mockDomainObject,
mockRootObject,
mockContext,
mockObjectService,
getObjectsPromise,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$watch"]
);
mockTimeout = jasmine.createSpy("$timeout");
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getCapability"]
);
mockRootObject = jasmine.createSpyObj(
"rootObject",
["getCapability"]
);
mockContext = jasmine.createSpyObj(
"context",
["getRoot"]
);
mockObjectService = jasmine.createSpyObj(
"objectService",
["getObjects"]
);
getObjectsPromise = jasmine.createSpyObj(
"promise",
["then"]
);
mockDomainObject.getCapability.and.returnValue(mockContext);
mockContext.getRoot.and.returnValue(mockRootObject);
mockObjectService.getObjects.and.returnValue(getObjectsPromise);
mockScope.ngModel = {};
mockScope.field = "someField";
controller = new LocatorController(mockScope, mockTimeout, mockObjectService);
});
describe("when context is available", function () {
beforeEach(function () {
mockContext.getRoot.and.returnValue(mockRootObject);
controller = new LocatorController(mockScope, mockTimeout, mockObjectService);
});
it("adds a treeModel to scope", function () {
expect(mockScope.treeModel).toBeDefined();
});
it("watches for changes to treeModel", function () {
// This is what the embedded tree representation
// will be modifying.
expect(mockScope.$watch).toHaveBeenCalledWith(
"treeModel.selectedObject",
jasmine.any(Function)
);
});
it("changes its own model on embedded model updates", function () {
// Need to pass on selection changes as updates to
// the control's value
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
mockTimeout.calls.mostRecent().args[0]();
expect(mockScope.ngModel.someField).toEqual(mockDomainObject);
expect(mockScope.rootObject).toEqual(mockRootObject);
// Verify that the capability we expect to have been used
// was used.
expect(mockDomainObject.getCapability)
.toHaveBeenCalledWith("context");
});
it("rejects changes which fail validation", function () {
mockScope.structure = { validate: jasmine.createSpy('validate') };
mockScope.structure.validate.and.returnValue(false);
// Pass selection change
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
mockTimeout.calls.mostRecent().args[0]();
expect(mockScope.structure.validate).toHaveBeenCalled();
// Change should have been rejected
expect(mockScope.ngModel.someField).not.toEqual(mockDomainObject);
});
it("treats a lack of a selection as invalid", function () {
mockScope.ngModelController = jasmine.createSpyObj(
'ngModelController',
['$setValidity']
);
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
mockTimeout.calls.mostRecent().args[0]();
expect(mockScope.ngModelController.$setValidity)
.toHaveBeenCalledWith(jasmine.any(String), true);
mockScope.$watch.calls.mostRecent().args[1](undefined);
mockTimeout.calls.mostRecent().args[0]();
expect(mockScope.ngModelController.$setValidity)
.toHaveBeenCalledWith(jasmine.any(String), false);
});
});
describe("when no context is available", function () {
var defaultRoot = "DEFAULT_ROOT";
beforeEach(function () {
mockContext.getRoot.and.returnValue(undefined);
getObjectsPromise.then.and.callFake(function (callback) {
callback({'ROOT': defaultRoot});
});
controller = new LocatorController(mockScope, mockTimeout, mockObjectService);
});
it("provides a default context where none is available", function () {
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
mockTimeout.calls.mostRecent().args[0]();
expect(mockScope.rootObject).toBe(defaultRoot);
});
it("does not issue redundant requests for the root object", function () {
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
mockTimeout.calls.mostRecent().args[0]();
mockScope.$watch.calls.mostRecent().args[1](undefined);
mockTimeout.calls.mostRecent().args[0]();
mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
mockTimeout.calls.mostRecent().args[0]();
expect(mockObjectService.getObjects.calls.count())
.toEqual(1);
});
});
});
}
);

View File

@ -21,25 +21,21 @@
*****************************************************************************/ *****************************************************************************/
define([ define([
"./src/actions/LinkAction",
"./src/actions/SetPrimaryLocationAction", "./src/actions/SetPrimaryLocationAction",
"./src/services/LocatingCreationDecorator", "./src/services/LocatingCreationDecorator",
"./src/services/LocatingObjectDecorator", "./src/services/LocatingObjectDecorator",
"./src/policies/CopyPolicy", "./src/policies/CopyPolicy",
"./src/policies/CrossSpacePolicy", "./src/policies/CrossSpacePolicy",
"./src/capabilities/LocationCapability", "./src/capabilities/LocationCapability",
"./src/services/LinkService",
"./src/services/CopyService", "./src/services/CopyService",
"./src/services/LocationService" "./src/services/LocationService"
], function ( ], function (
LinkAction,
SetPrimaryLocationAction, SetPrimaryLocationAction,
LocatingCreationDecorator, LocatingCreationDecorator,
LocatingObjectDecorator, LocatingObjectDecorator,
CopyPolicy, CopyPolicy,
CrossSpacePolicy, CrossSpacePolicy,
LocationCapability, LocationCapability,
LinkService,
CopyService, CopyService,
LocationService LocationService
) { ) {
@ -52,21 +48,6 @@ define([
"configuration": {}, "configuration": {},
"extensions": { "extensions": {
"actions": [ "actions": [
{
"key": "link",
"name": "Create Link",
"description": "Create Link to object in another location.",
"cssClass": "icon-link",
"category": "contextual",
"group": "action",
"priority": 7,
"implementation": LinkAction,
"depends": [
"policyService",
"locationService",
"linkService"
]
},
{ {
"key": "locate", "key": "locate",
"name": "Set Primary Location", "name": "Set Primary Location",
@ -115,15 +96,6 @@ define([
} }
], ],
"services": [ "services": [
{
"key": "linkService",
"name": "Link Service",
"description": "Provides a service for linking objects",
"implementation": LinkService,
"depends": [
"openmct"
]
},
{ {
"key": "copyService", "key": "copyService",
"name": "Copy Service", "name": "Copy Service",

View File

@ -1,71 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
function () {
/**
* LinkService provides an interface for linking objects to additional
* locations. It also provides a method for determining if an object
* can be copied to a specific location.
* @constructor
* @memberof platform/entanglement
* @implements {platform/entanglement.AbstractComposeService}
*/
function LinkService(openmct) {
this.openmct = openmct;
}
LinkService.prototype.validate = function (object, parentCandidate) {
if (!parentCandidate || !parentCandidate.getId) {
return false;
}
if (parentCandidate.getId() === object.getId()) {
return false;
}
if (!parentCandidate.hasCapability('composition')) {
return false;
}
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
return false;
}
return this.openmct.composition.checkPolicy(parentCandidate.useCapability('adapter'), object.useCapability('adapter'));
};
LinkService.prototype.perform = function (object, parentObject) {
if (!this.validate(object, parentObject)) {
throw new Error(
"Tried to link objects without validating first."
);
}
return parentObject.getCapability('composition').add(object);
};
return LinkService;
}
);

View File

@ -1,178 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[
'../../src/actions/LinkAction',
'../services/MockLinkService',
'../DomainObjectFactory'
],
function (LinkAction, MockLinkService, domainObjectFactory) {
describe("Link Action", function () {
var linkAction,
policyService,
locationService,
locationServicePromise,
linkService,
context,
selectedObject,
selectedObjectContextCapability,
currentParent,
newParent;
beforeEach(function () {
policyService = jasmine.createSpyObj(
'policyService',
['allow']
);
policyService.allow.and.returnValue(true);
selectedObjectContextCapability = jasmine.createSpyObj(
'selectedObjectContextCapability',
[
'getParent'
]
);
selectedObject = domainObjectFactory({
name: 'selectedObject',
model: {
name: 'selectedObject'
},
capabilities: {
context: selectedObjectContextCapability
}
});
currentParent = domainObjectFactory({
name: 'currentParent'
});
selectedObjectContextCapability
.getParent
.and.returnValue(currentParent);
newParent = domainObjectFactory({
name: 'newParent'
});
locationService = jasmine.createSpyObj(
'locationService',
[
'getLocationFromUser'
]
);
locationServicePromise = jasmine.createSpyObj(
'locationServicePromise',
[
'then'
]
);
locationService
.getLocationFromUser
.and.returnValue(locationServicePromise);
linkService = new MockLinkService();
});
describe("with context from context-action", function () {
beforeEach(function () {
context = {
domainObject: selectedObject
};
linkAction = new LinkAction(
policyService,
locationService,
linkService,
context
);
});
it("initializes happily", function () {
expect(linkAction).toBeDefined();
});
describe("when performed it", function () {
beforeEach(function () {
linkAction.perform();
});
it("prompts for location", function () {
expect(locationService.getLocationFromUser)
.toHaveBeenCalledWith(
"Link selectedObject To a New Location",
"Link To",
jasmine.any(Function),
currentParent
);
});
it("waits for location and handles cancellation by user", function () {
expect(locationServicePromise.then)
.toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function));
});
it("links object to selected location", function () {
locationServicePromise
.then
.calls.mostRecent()
.args[0](newParent);
expect(linkService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
describe("with context from drag-drop", function () {
beforeEach(function () {
context = {
selectedObject: selectedObject,
domainObject: newParent
};
linkAction = new LinkAction(
policyService,
locationService,
linkService,
context
);
});
it("initializes happily", function () {
expect(linkAction).toBeDefined();
});
it("performs link immediately", function () {
linkAction.perform();
expect(linkService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
}
);

View File

@ -1,215 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[
'../../src/services/LinkService',
'../DomainObjectFactory',
'../ControlledPromise'
],
function (LinkService, domainObjectFactory, ControlledPromise) {
xdescribe("LinkService", function () {
var linkService,
mockPolicyService;
beforeEach(function () {
mockPolicyService = jasmine.createSpyObj(
'policyService',
['allow']
);
mockPolicyService.allow.and.returnValue(true);
linkService = new LinkService(mockPolicyService);
});
describe("validate", function () {
var object,
parentCandidate,
validate;
beforeEach(function () {
object = domainObjectFactory({
name: 'object'
});
parentCandidate = domainObjectFactory({
name: 'parentCandidate',
capabilities: {
composition: jasmine.createSpyObj(
'composition',
['invoke', 'add']
)
}
});
validate = function () {
return linkService.validate(object, parentCandidate);
};
});
it("does not allow invalid parentCandidate", function () {
parentCandidate = undefined;
expect(validate()).toBe(false);
parentCandidate = {};
expect(validate()).toBe(false);
});
it("does not allow parent to be object", function () {
parentCandidate.id = object.id = 'abc';
expect(validate()).toBe(false);
});
it("does not allow parent that contains object", function () {
object.id = 'abc';
parentCandidate.id = 'xyz';
parentCandidate.model.composition = ['abc'];
expect(validate()).toBe(false);
});
it("does not allow parents without composition", function () {
parentCandidate = domainObjectFactory({
name: 'parentCandidate'
});
object.id = 'abc';
parentCandidate.id = 'xyz';
parentCandidate.hasCapability.and.callFake(function (c) {
return c !== 'composition';
});
expect(validate()).toBe(false);
});
describe("defers to policyService", function () {
beforeEach(function () {
object.id = 'abc';
object.capabilities.type = { type: 'object' };
parentCandidate.id = 'xyz';
parentCandidate.capabilities.type = {
type: 'parentCandidate'
};
parentCandidate.model.composition = [];
});
it("calls policy service with correct args", function () {
validate();
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate,
object
);
});
it("and returns false", function () {
mockPolicyService.allow.and.returnValue(true);
expect(validate()).toBe(true);
expect(mockPolicyService.allow).toHaveBeenCalled();
});
it("and returns true", function () {
mockPolicyService.allow.and.returnValue(false);
expect(validate()).toBe(false);
expect(mockPolicyService.allow).toHaveBeenCalled();
});
});
});
describe("perform", function () {
var object,
linkedObject,
parentModel,
parentObject,
compositionPromise,
addPromise,
compositionCapability;
beforeEach(function () {
compositionPromise = new ControlledPromise();
addPromise = new ControlledPromise();
compositionCapability = jasmine.createSpyObj(
'compositionCapability',
['invoke', 'add']
);
compositionCapability.invoke.and.returnValue(compositionPromise);
compositionCapability.add.and.returnValue(addPromise);
parentModel = {
composition: []
};
parentObject = domainObjectFactory({
name: 'parentObject',
model: parentModel,
capabilities: {
mutation: {
invoke: function (mutator) {
mutator(parentModel);
return new ControlledPromise();
}
},
composition: compositionCapability
}
});
object = domainObjectFactory({
name: 'object',
id: 'xyz'
});
linkedObject = domainObjectFactory({
name: 'object-link',
id: 'xyz'
});
});
it("adds to the parent's composition", function () {
expect(compositionCapability.add).not.toHaveBeenCalled();
linkService.perform(object, parentObject);
expect(compositionCapability.add)
.toHaveBeenCalledWith(object);
});
it("returns object representing new link", function () {
var returnPromise, whenComplete;
returnPromise = linkService.perform(object, parentObject);
whenComplete = jasmine.createSpy('whenComplete');
returnPromise.then(whenComplete);
addPromise.resolve(linkedObject);
compositionPromise.resolve([linkedObject]);
expect(whenComplete).toHaveBeenCalledWith(linkedObject);
});
it("throws an error when performed on invalid inputs", function () {
function perform() {
linkService.perform(object, parentObject);
}
spyOn(linkService, 'validate');
linkService.validate.and.returnValue(true);
expect(perform).not.toThrow();
linkService.validate.and.returnValue(false);
expect(perform).toThrow();
});
});
});
}
);

View File

@ -1,133 +0,0 @@
# Overview
This bundle contains a general implementation of forms in Open MCT.
This allows forms to be expressed using a reasonably concise declarative
syntax, and rendered as Angular templates in a consistent fashion.
# Usage
To include a form with a declarative definition, use the `mct-form`
directive, e.g.:
<mct-form ng-model="myModel" structure="myStructure" name="myForm">
</mct-form>
Using toolbars is similar:
<mct-toolbar ng-model="myModel" structure="myStructure" name="myToolbar">
</mct-toolbar>
The attributes utilized by this form are as follows:
* `ng-model`: The object which should contain the full form input. Individual
fields in this model are bound to individual controls; the names used for
these fields are provided in the form structure (see below).
* `structure`: The structure of the form; e.g. sections, rows, their names,
and so forth. The value of this attribute should be an Angular expression.
* `name`: The name in the containing scope under which to publish form
"meta-state", e.g. `$valid`, `$dirty`, etc. This is as the behavior of
`ng-form`. Passed as plain text in the attribute.
## Form structure
A form's structure is described as a JavaScript object in the following form:
{
"name": ... title to display for the form, as a string ...,
"sections": [
{
"name": ... title to display for the section ...,
"rows": [
{
"name": ... title to display for this row ...,
"control": ... symbolic key for the control ...,
"key": ... field name in ng-model ...
"pattern": ... optional, reg exp to match against ...
"required": ... optional boolean ...
"options": [
"name": ... name to display (e.g. in a select) ...,
"value": ... value to store in the model ...
]
},
... and other rows ...
]
},
... and other sections ...
]
}
Note that `pattern` may be specified as a string, to simplify storing
for structures as JSON when necessary. The string should be given in
a form appropriate to pass to a `RegExp` constructor.
## Toolbar structure
A toolbar's structure is described similarly to forms, except that there
is no notion of rows; instead, there are `items`.
{
"name": ... title to display for the form, as a string ...,
"sections": [
{
"name": ... title to display for the section ...,
"items": [
{
"name": ... title to display for this row ...,
"control": ... symbolic key for the control ...,
"key": ... field name in ng-model ...
"pattern": ... optional, reg exp to match against ...
"required": ... optional boolean ...
"options": [
"name": ... name to display (e.g. in a select) ...,
"value": ... value to store in the model ...
],
"disabled": ... true if control should be disabled ...
"size": ... size of the control (for textfields) ...
"click": ... function to invoke (for buttons) ...
"glyph": ... glyph to display (for buttons) ...
"text": ... text withiin control (for buttons) ...
},
... and other rows ...
]
},
... and other sections ...
]
}
Note that `pattern` may be specified as a string, to simplify storing
for structures as JSON when necessary. The string should be given in
a form appropriate to pass to a `RegExp` constructor.
## Adding controls
These control types are included in the forms bundle:
* `textfield`: A text input to enter plain text.
* `numberfield`: A text input to enter numbers.
* `select`: A drop-down list of options.
* `checkbox`: A box which may be checked/unchecked.
* `color`: A color picker.
* `button`: A button.
* `datetime`: An input for UTC date/time entry; gives result as a
UNIX timestamp, in milliseconds since start of 1970, UTC.
New controls may be added as extensions of the `controls` category.
Extensions of this category have two properites:
* `key`: The symbolic name for this control (matched against the
`control` field in rows of the form structure).
* `templateUrl`: The URL to the control's Angular template, relative
to the resources directory of the bundle which exposes the extension.
Within the template for a control, the following variables will be
included in scope:
* `ngModel`: The model where form input will be stored. Notably we
also need to look at `field` (see below) to determine which field
in the model should be modified.
* `ngRequired`: True if input is required.
* `ngPattern`: The pattern to match against (for text entry.)
* `options`: The options for this control, as passed from the
`options` property of an individual row.
* `field`: Name of the field in `ngModel` which will hold the value
for this control.

View File

@ -1,202 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
"./src/MCTForm",
"./src/MCTControl",
"./src/MCTFileInput",
"./src/FileInputService",
"./src/controllers/AutocompleteController",
"./src/controllers/DateTimeController",
"./src/controllers/CompositeController",
"./src/controllers/ColorController",
"./src/controllers/DialogButtonController",
"./res/templates/controls/autocomplete.html",
"./res/templates/controls/checkbox.html",
"./res/templates/controls/datetime.html",
"./res/templates/controls/select.html",
"./res/templates/controls/textfield.html",
"./res/templates/controls/numberfield.html",
"./res/templates/controls/textarea.html",
"./res/templates/controls/button.html",
"./res/templates/controls/color.html",
"./res/templates/controls/composite.html",
"./res/templates/controls/menu-button.html",
"./res/templates/controls/dialog.html",
"./res/templates/controls/radio.html",
"./res/templates/controls/file-input.html"
], function (
MCTForm,
MCTControl,
MCTFileInput,
FileInputService,
AutocompleteController,
DateTimeController,
CompositeController,
ColorController,
DialogButtonController,
autocompleteTemplate,
checkboxTemplate,
datetimeTemplate,
selectTemplate,
textfieldTemplate,
numberfieldTemplate,
textareaTemplate,
buttonTemplate,
colorTemplate,
compositeTemplate,
menuButtonTemplate,
dialogTemplate,
radioTemplate,
fileInputTemplate
) {
return {
name: "platform/forms",
definition: {
"name": "MCT Forms",
"description": "Form generator; includes directive and some controls.",
"extensions": {
"directives": [
{
"key": "mctForm",
"implementation": MCTForm
},
{
"key": "mctControl",
"implementation": MCTControl,
"depends": [
"templateLinker",
"controls[]"
]
},
{
"key": "mctFileInput",
"implementation": MCTFileInput,
"depends": [
"fileInputService"
]
}
],
"controls": [
{
"key": "autocomplete",
"template": autocompleteTemplate
},
{
"key": "checkbox",
"template": checkboxTemplate
},
{
"key": "radio",
"template": radioTemplate
},
{
"key": "datetime",
"template": datetimeTemplate
},
{
"key": "select",
"template": selectTemplate
},
{
"key": "textfield",
"template": textfieldTemplate
},
{
"key": "numberfield",
"template": numberfieldTemplate
},
{
"key": "textarea",
"template": textareaTemplate
},
{
"key": "button",
"template": buttonTemplate
},
{
"key": "color",
"template": colorTemplate
},
{
"key": "composite",
"template": compositeTemplate
},
{
"key": "menu-button",
"template": menuButtonTemplate
},
{
"key": "dialog-button",
"template": dialogTemplate
},
{
"key": "file-input",
"template": fileInputTemplate
}
],
"controllers": [
{
"key": "AutocompleteController",
"implementation": AutocompleteController,
"depends": [
"$scope",
"$element"
]
},
{
"key": "DateTimeController",
"implementation": DateTimeController,
"depends": [
"$scope"
]
},
{
"key": "CompositeController",
"implementation": CompositeController
},
{
"key": "ColorController",
"implementation": ColorController
},
{
"key": "DialogButtonController",
"implementation": DialogButtonController,
"depends": [
"$scope",
"dialogService"
]
}
],
"components": [
{
"provides": "fileInputService",
"type": "provider",
"implementation": FileInputService
}
]
}
}
};
});

View File

@ -1,46 +0,0 @@
<!--
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.
-->
<div ng-controller="AutocompleteController"
class='form-control autocomplete'>
<input class="autocompleteInput"
type="text"
ng-model="ngModel[field]"
ng-change="filterOptions(ngModel[field])"
ng-click="inputClicked()"
ng-keydown="keyDown($event)"/>
<span class="icon-arrow-down"
ng-click="arrowClicked()"></span>
<div class="autocompleteOptions"
ng-init="hideOptions = true"
ng-hide="hideOptions"
mct-click-elsewhere="hideOptions = true">
<ul>
<li ng-repeat="opt in filteredOptions"
ng-click="fillInput(opt.name)"
ng-mouseover="optionMouseover(opt.optionId)"
ng-class="optionIndex === opt.optionId ? 'optionPreSelected' : ''">
<span class="optionText">{{opt.name}}</span>
</li>
</ul>
</div>
</div>

View File

@ -1,28 +0,0 @@
<!--
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.
-->
<a class="s-button {{structure.cssClass}}"
ng-class="{ labeled: structure.text }"
ng-click="structure.click()">
<span class="title-label" ng-if="structure.text">
{{structure.text}}
</span>
</a>

View File

@ -1,28 +0,0 @@
<!--
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.
-->
<label class="checkbox custom no-text">
<input type="checkbox"
name="mctControl"
ng-model="ngModel[field]"
ng-disabled="ngDisabled">
<em></em>
</label>

View File

@ -1,57 +0,0 @@
<!--
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.
-->
<div class="s-button s-menu-button menu-element t-color-palette {{structure.cssClass}}"
ng-controller="ClickAwayController as toggle">
<span class="l-click-area" ng-click="toggle.toggle()"></span>
<span class="color-swatch"
ng-class="{'no-selection':ngModel[field] === 'transparent'}"
ng-style="{
'background-color': ngModel[field]
}">
</span>
<span class="title-label" ng-if="structure.text">
{{structure.text}}
</span>
<div class="menu l-palette l-color-palette"
ng-controller="ColorController as colors"
ng-show="toggle.isActive()">
<div
class="l-palette-row l-option-row"
ng-if="!structure.mandatory">
<div class="l-palette-item s-palette-item no-selection {{ngModel[field] === 'transparent' ? 'selected' : '' }}"
ng-click="ngModel[field] = 'transparent'">
</div>
<span class="l-palette-item-label">None</span>
</div>
<div
class="l-palette-row"
ng-repeat="group in colors.groups()">
<div class="l-palette-item s-palette-item {{ngModel[field] === color ? 'selected' : '' }}"
ng-repeat="color in group"
ng-style="{ background: color }"
ng-click="ngModel[field] = color">
</div>
</div>
</div>
</div>

View File

@ -1,38 +0,0 @@
<!--
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.
-->
<span ng-controller="CompositeController as compositeCtrl">
<ng-form name="mctFormItem" ng-repeat="item in structure.items">
<div class="l-composite-control l-{{item.control}} {{item.cssClass}}">
<mct-control key="item.control"
ng-model="ngModel[field]"
ng-required="ngRequired || compositeCtrl.isNonEmpty(ngModel[field])"
ng-pattern="ngPattern"
options="item.options"
structure="item"
field="$index">
</mct-control>
<span class="composite-control-label">
{{item.name}}
</span>
</div>
</ng-form>
</span>

View File

@ -1,83 +0,0 @@
<!--
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.
-->
<div class='form-control complex datetime'>
<div class='field-hints'>
<span class='hint date'>Date</span>
<span class='hint time sm'>Hour</span>
<span class='hint time sm'>Min</span>
<span class='hint time sm'>Sec</span>
<span class='hint timezone'>Timezone</span>
</div>
<ng-form name="mctControl">
<div class='fields' ng-controller="DateTimeController">
<span class='field control date'>
<input type='text'
name='date'
placeholder="{{format}}"
ng-pattern="/\d\d\d\d-\d\d-\d\d/"
ng-model='datetime.date'
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control time sm'>
<input type='text'
name='hour'
maxlength='2'
min='0'
max='23'
integer
ng-pattern='/\d+/'
ng-model="datetime.hour"
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control time sm'>
<input type='text'
name='min'
maxlength='2'
min='0'
max='59'
integer
ng-pattern='/\d+/'
ng-model="datetime.min"
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control time sm'>
<input type='text'
name='sec'
maxlength='2'
min='0'
max='59'
integer
ng-pattern='/\d+/'
ng-model="datetime.sec"
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control timezone'>
UTC
</span>
</div>
</ng-form>
</div>

View File

@ -1,26 +0,0 @@
<!--
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.
-->
<span ng-controller="DialogButtonController as dialog">
<mct-control key="'button'"
structure="dialog.getButtonStructure()">
</mct-control>
</span>

View File

@ -1,30 +0,0 @@
<!--
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.
-->
<button class="c-button {{structure.cssClass}}"
ng-model="ngModel[field]"
ng-class="{ labeled: structure.text }"
mct-file-input>
<span class="c-button__label" ng-if="structure.text">
{{structure.text}}
</span>
</button>

View File

@ -1,39 +0,0 @@
<!--
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.
-->
<div class="s-menu-button menu-element {{ structure.cssClass }}"
ng-controller="ClickAwayController as toggle">
<span class="l-click-area" ng-click="toggle.toggle()"></span>
<span class="title-label" ng-if="structure.text">
{{structure.text}}
</span>
<div class="menu" ng-show="toggle.isActive()">
<ul>
<li ng-click="structure.click(option.key); toggle.setState(false)"
ng-repeat="option in structure.options"
class="{{ option.cssClass }}">
{{option.name}}
</li>
</ul>
</div>
</div>

View File

@ -1,34 +0,0 @@
<!--
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.
-->
<span class='form-control shell'>
<span class='field control {{structure.cssClass}}'>
<input type="number"
ng-required="ngRequired"
ng-model="ngModel[field]"
ng-blur="ngBlur()"
ng-pattern="ngPattern"
min="{{structure.min}}"
max="{{structure.max}}"
step="{{structure.step}}"
name="mctControl">
</span>
</span>

View File

@ -1,29 +0,0 @@
<!--
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.
-->
<label class="radio custom no-text">
<input type="radio"
name="mctControl"
ng-model="ngModel[field]"
ng-disabled="ngDisabled"
ng-value="structure.value">
<em></em>
</label>

View File

@ -1,30 +0,0 @@
<!--
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.
-->
<div class='form-control select'>
<select
ng-model="ngModel[field]"
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
name="mctControl">
<option value="" ng-show="!ngModel[field]">- Select One -</option>
</select>
</div>

View File

@ -1,31 +0,0 @@
<!--
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.
-->
<span class='form-control shell'>
<span class='field control {{structure.cssClass}}'>
<textarea ng-required="ngRequired"
ng-model="ngModel[field]"
ng-pattern="ngPattern"
size="{{structure.size}}"
name="mctControl">
</textarea>
</span>
</span>

View File

@ -1,32 +0,0 @@
<!--
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.
-->
<span class='form-control shell'>
<span class='field control {{structure.cssClass}}'>
<input type="text"
ng-required="ngRequired"
ng-model="ngModel[field]"
ng-blur="ngBlur()"
ng-pattern="ngPattern"
size="{{structure.size}}"
name="mctControl">
</span>
</span>

View File

@ -1,57 +0,0 @@
<!--
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.
-->
<form name="mctForm" novalidate class="form c-form" autocomplete="off">
<span ng-repeat="section in structure.sections"
class="l-form-section c-form__section {{ section.cssClass }}">
<h2 class="c-form__header" ng-if="section.name">
{{section.name}}
</h2>
<ng-form class="form-row c-form__row validates {{ section.cssClass }}"
ng-class="{
first:$index < 1,
req: row.required,
valid: mctFormInner.$dirty && mctFormInner.$valid,
invalid: mctFormInner.$dirty && !mctFormInner.$valid,
first: $index < 1,
'l-controls-first': row.layout === 'control-first',
'l-controls-under': row.layout === 'controls-under'
}"
name="mctFormInner"
ng-repeat="row in section.rows">
<div class='c-form__row__label label flex-elem' title="{{row.description}}">
{{row.name}}
</div>
<div class='c-form__row__controls controls flex-elem'>
<div class="c-form__controls-wrapper wrapper" ng-if="row.control">
<mct-control key="row.control"
ng-model="ngModel"
ng-required="row.required"
ng-pattern="getRegExp(row.pattern)"
options="row.options"
structure="row"
field="row.key">
</mct-control>
</div>
</div>
</ng-form>
</span>
</form>

View File

@ -1,96 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(["zepto"], function ($) {
/**
* The FileInputService provides an interface for triggering a file input.
*
* @constructor
* @memberof platform/forms
*/
function FileInputService() {
}
/**
* Creates, triggers, and destroys a file picker element and returns a
* promise for an object containing the chosen file's name and contents.
*
* @returns {Promise} promise for an object containing file meta-data
*/
FileInputService.prototype.getInput = function (fileType) {
var input = this.newInput();
var read = this.readFile;
var fileInfo = {};
var file;
return new Promise(function (resolve, reject) {
input.trigger("click");
input.on('change', function (event) {
// eslint-disable-next-line no-invalid-this
file = this.files[0];
input.remove();
if (file) {
if (fileType && (!file.type || (file.type !== fileType))) {
reject("Incompatible file type");
}
read(file)
.then(function (contents) {
fileInfo.name = file.name;
fileInfo.body = contents;
resolve(fileInfo);
}, function () {
reject("File read error");
});
}
});
});
};
FileInputService.prototype.readFile = function (file) {
var fileReader = new FileReader();
return new Promise(function (resolve, reject) {
fileReader.onload = function (event) {
resolve(event.target.result);
};
fileReader.onerror = function () {
return reject(event.target.result);
};
fileReader.readAsText(file);
});
};
FileInputService.prototype.newInput = function () {
var input = $(document.createElement('input'));
input.attr("type", "file");
input.css("display", "none");
$('body').append(input);
return input;
};
return FileInputService;
});

View File

@ -1,103 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* The mct-control will dynamically include the control
* for a form element based on a symbolic key. Individual
* controls are defined under the extension category
* `controls`; this allows plug-ins to introduce new form
* control types while still making use of the form
* generator to ensure an overall consistent form style.
* @constructor
* @memberof platform/forms
*/
function MCTControl(templateLinker, controls) {
var controlMap = {};
// Prepopulate controlMap for easy look up by key
controls.forEach(function (control) {
controlMap[control.key] = control;
});
function link(scope, element, attrs, ngModelController) {
var changeTemplate = templateLinker.link(scope, element);
scope.$watch("key", function (key) {
changeTemplate(controlMap[key]);
});
scope.ngModelController = ngModelController;
}
return {
// Only show at the element level
restrict: "E",
// ngOptions is terminal, so we need to be higher priority
priority: 1000,
// Get the ngModelController, so that controls can set validity
require: '?ngModel',
// Link function
link: link,
// Pass through Angular's normal input field attributes
scope: {
// Used to choose which form control to use
key: "=",
// Allow controls to trigger blur-like events
ngBlur: "&",
// Allow controls to trigger blur-like events
ngMouseup: "&",
// The state of the form value itself
ngModel: "=",
// Enabled/disabled state
ngDisabled: "=",
// Whether or not input is required
ngRequired: "=",
// Pattern (for input fields)
ngPattern: "=",
// Set of choices (if any)
options: "=",
// Structure (subtree of Form Structure)
structure: "=",
// Name, as in "<input name="...
field: "="
}
};
}
return MCTControl;
}
);

View File

@ -1,68 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
['zepto'],
function ($) {
/**
* The mct-file-input handles behavior of the file input form control.
* @constructor
* @memberof platform/forms
*/
function MCTFileInput(fileInputService) {
function link(scope, element, attrs, control) {
function setText(fileName) {
scope.structure.text = fileName.length > 20
? fileName.substr(0, 20) + "..."
: fileName;
}
function handleClick() {
fileInputService.getInput(scope.structure.type).then(function (result) {
setText(result.name);
scope.ngModel[scope.field] = result;
control.$setValidity("file-input", true);
scope.$digest();
}, function () {
setText('Select File');
control.$setValidity("file-input", false);
scope.$digest();
});
}
control.$setValidity("file-input", false);
element.on('click', handleClick);
}
return {
restrict: "A",
require: "^form",
link: link
};
}
return MCTFileInput;
}
);

View File

@ -1,80 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* This bundle implements directives for displaying and handling forms for
* user input.
* @namespace platform/forms
*/
define(
["./controllers/FormController", "../res/templates/form.html"],
function (FormController, formTemplate) {
/**
* The mct-form directive allows generation of displayable
* forms based on a declarative description of the form's
* structure.
*
* This directive accepts three attributes:
*
* * `ng-model`: The model for the form; where user input
* where be stored.
* * `structure`: The declarative structure of the form.
* Describes what controls should be shown and where
* their values should be read/written in the model.
* * `name`: The name under which to expose the form's
* dirty/valid state. This is similar to ng-form's use
* of name, except this will be made available in the
* parent scope.
*
* @memberof platform/forms
* @constructor
*/
function MCTForm() {
return {
// Only show at the element level
restrict: "E",
// Load the forms template
template: formTemplate,
// Use FormController to populate/respond to changes in scope
controller: ['$scope', FormController],
// Initial an isolate scope
scope: {
// The model: Where form input will actually go
ngModel: "=",
// Form structure; what sections/rows to show
structure: "=",
// Name under which to publish the form
name: "@"
}
};
}
return MCTForm;
}
);

View File

@ -1,140 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* Controller for the `autocomplete` form control.
*
* @memberof platform/forms
* @constructor
*/
function AutocompleteController($scope, $element) {
var key = {
down: 40,
up: 38,
enter: 13
},
autocompleteInputElement = $element[0].getElementsByClassName('autocompleteInput')[0];
if ($scope.options[0].name) {
// If "options" include name, value pair
$scope.optionNames = $scope.options.map(function (opt) {
return opt.name;
});
} else {
// If options is only an array of string.
$scope.optionNames = $scope.options;
}
function fillInputWithIndexedOption() {
if ($scope.filteredOptions[$scope.optionIndex]) {
$scope.ngModel[$scope.field] = $scope.filteredOptions[$scope.optionIndex].name;
}
}
function decrementOptionIndex() {
if ($scope.optionIndex === 0) {
$scope.optionIndex = $scope.filteredOptions.length;
}
$scope.optionIndex--;
fillInputWithIndexedOption();
}
function incrementOptionIndex() {
if ($scope.optionIndex === $scope.filteredOptions.length - 1) {
$scope.optionIndex = -1;
}
$scope.optionIndex++;
fillInputWithIndexedOption();
}
function fillInputWithString(string) {
$scope.hideOptions = true;
$scope.ngModel[$scope.field] = string;
}
function showOptions(string) {
$scope.hideOptions = false;
$scope.filterOptions(string);
$scope.optionIndex = 0;
}
$scope.keyDown = function ($event) {
if ($scope.filteredOptions) {
var keyCode = $event.keyCode;
switch (keyCode) {
case key.down:
incrementOptionIndex();
break;
case key.up:
$event.preventDefault(); // Prevents cursor jumping back and forth
decrementOptionIndex();
break;
case key.enter:
if ($scope.filteredOptions[$scope.optionIndex]) {
fillInputWithString($scope.filteredOptions[$scope.optionIndex].name);
}
}
}
};
$scope.filterOptions = function (string) {
$scope.hideOptions = false;
$scope.filteredOptions = $scope.optionNames.filter(function (option) {
return option.toLowerCase().indexOf(string.toLowerCase()) >= 0;
}).map(function (option, index) {
return {
optionId: index,
name: option
};
});
};
$scope.inputClicked = function () {
autocompleteInputElement.select();
showOptions(autocompleteInputElement.value);
};
$scope.arrowClicked = function () {
autocompleteInputElement.select();
showOptions('');
};
$scope.fillInput = function (string) {
fillInputWithString(string);
};
$scope.optionMouseover = function (optionId) {
$scope.optionIndex = optionId;
};
}
return AutocompleteController;
}
);

View File

@ -1,104 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
var BASE_COLORS = [
[136, 32, 32],
[224, 64, 64],
[240, 160, 72],
[255, 248, 96],
[128, 240, 72],
[128, 248, 248],
[88, 144, 224],
[0, 72, 240],
[136, 80, 240],
[224, 96, 248]
],
GRADIENTS = [0.75, 0.50, 0.25, -0.25, -0.50, -0.75],
GROUPS = [];
function toWebColor(triplet) {
return '#' + triplet.map(function (v) {
return (v < 16 ? '0' : '') + v.toString(16);
}).join('');
}
function toGradient(triplet, value) {
return triplet.map(function (v) {
return Math.round(value > 0
? (v + (255 - v) * value)
: (v * (1 + value))
);
});
}
function initializeGroups() {
var group;
// Ten grayscale colors
group = [];
while (group.length < 10) {
group.push(toWebColor([
Math.round(28.3333 * group.length),
Math.round(28.3333 * group.length),
Math.round(28.3333 * group.length)
]));
}
GROUPS.push(group);
// Ten basic colors
GROUPS.push(BASE_COLORS.map(toWebColor));
// ...and some gradients of those colors
group = [];
GRADIENTS.forEach(function (v) {
group = group.concat(BASE_COLORS.map(function (c) {
return toWebColor(toGradient(c, v));
}));
});
GROUPS.push(group);
}
function ColorController() {
if (GROUPS.length === 0) {
initializeGroups();
}
}
/**
* Get groups of colors to display in a color picker. These are
* given as #-prefixed color strings, in a two-dimensional array.
* Each element of the array is a group of related colors (e.g.
* grayscale colors, web colors, gradients...)
* @returns {string[][]} groups of colors
*/
ColorController.prototype.groups = function () {
return GROUPS;
};
return ColorController;
}
);

View File

@ -1,62 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* The CompositeController supports the "composite" control type,
* which provides an array of other controls. It is used specifically
* to support validation when a particular row is not marked as
* required; in this case, empty input should be allowed, but partial
* input (where some but not all of the composite controls have been
* filled in) should be disallowed. This is enforced in the template
* by an ng-required directive, but that is supported by the
* isNonEmpty check that this controller provides.
* @memberof platform/forms
* @constructor
*/
function CompositeController() {
}
// Check if an element is defined; the map step of isNonEmpty
function isDefined(element) {
return typeof element !== 'undefined';
}
/**
* Check if an array contains anything other than
* undefined elements.
* @param {Array} value the array to check
* @returns {boolean} true if any non-undefined
* element is in the array
* @memberof platform/forms.CompositeController#
*/
CompositeController.prototype.isNonEmpty = function (value) {
return Array.isArray(value) && value.some(isDefined);
};
return CompositeController;
}
);

View File

@ -1,108 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["moment"],
function (moment) {
var DATE_FORMAT = "YYYY-MM-DD";
/**
* Controller for the `datetime` form control.
* This is a composite control; it includes multiple
* input fields but outputs a single timestamp (in
* milliseconds since start of 1970) to the ngModel.
*
* @memberof platform/forms
* @constructor
*/
function DateTimeController($scope) {
// Update the
function update() {
var date = $scope.datetime.date,
hour = $scope.datetime.hour,
min = $scope.datetime.min,
sec = $scope.datetime.sec,
fullDateTime = moment.utc(date, DATE_FORMAT)
.hour(hour || 0)
.minute(min || 0)
.second(sec || 0);
if (fullDateTime.isValid()) {
$scope.ngModel[$scope.field] = fullDateTime.valueOf();
}
// If anything is complete, say so in scope; there are
// ng-required usages that will update off of this (to
// allow datetime to be optional while still permitting
// incomplete input)
$scope.partiallyComplete =
Object.keys($scope.datetime).some(function (key) {
return $scope.datetime[key];
});
// Treat empty input as an undefined value
if (!$scope.partiallyComplete) {
$scope.ngModel[$scope.field] = undefined;
}
}
function updateDateTime(value) {
var m;
if (value !== undefined) {
m = moment.utc(value);
$scope.datetime = {
date: m.format(DATE_FORMAT),
hour: m.format("H"),
min: m.format("m"),
sec: m.format("s")
};
} else {
$scope.datetime = {};
}
}
// ...and update form values when actual field in model changes
$scope.$watch("ngModel[field]", updateDateTime);
// Update value whenever any field changes.
$scope.$watch("datetime.date", update);
$scope.$watch("datetime.hour", update);
$scope.$watch("datetime.min", update);
$scope.$watch("datetime.sec", update);
// Expose format string for placeholder
$scope.format = DATE_FORMAT;
// Initialize forms values
updateDateTime(
($scope.ngModel && $scope.field)
? $scope.ngModel[$scope.field] : undefined
);
}
return DateTimeController;
}
);

View File

@ -1,93 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* Controller for the `dialog-button` control type. Provides
* structure for a button (embedded via the template) which
* will show a dialog for editing a single property when clicked.
* @memberof platform/forms
* @constructor
* @param $scope the control's Angular scope
* @param {DialogService} dialogService service to use to prompt
* for user input
*/
function DialogButtonController($scope, dialogService) {
var self = this,
buttonForm;
// Store the result of user input to the model
function storeResult(result) {
$scope.ngModel[$scope.field] = result[$scope.field];
}
// Prompt for user input
function showDialog() {
// Prepare initial state
var state = {};
state[$scope.field] = $scope.ngModel[$scope.field];
// Show dialog, then store user input (if any)
dialogService.getUserInput(buttonForm, state).then(storeResult);
}
// Refresh state based on structure for this control
function refreshStructure(structure) {
var row = Object.create(structure.dialog || {});
structure = structure || {};
// Add the key, to read back from that row
row.key = $scope.field;
// Prepare the structure for the button itself
self.buttonStructure = {};
self.buttonStructure.cssClass = structure.cssClass;
self.buttonStructure.name = structure.name;
self.buttonStructure.description = structure.description;
self.buttonStructure.click = showDialog;
// Prepare the form; a single row
buttonForm = {
name: structure.title,
sections: [{ rows: [row] }]
};
}
$scope.$watch('structure', refreshStructure);
}
/**
* Get the structure for an `mct-control` of type
* `button`; a dialog will be launched when this button
* is clicked.
* @returns dialog structure
*/
DialogButtonController.prototype.getButtonStructure = function () {
return this.buttonStructure;
};
return DialogButtonController;
}
);

View File

@ -1,77 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
// Default ng-pattern; any non whitespace
var NON_WHITESPACE = /\S/;
/**
* Controller for mct-form and mct-toolbar directives.
* @memberof platform/forms
* @constructor
*/
function FormController($scope) {
var regexps = [];
// ng-pattern seems to want a RegExp, and not a
// string (despite what documentation says) but
// we want form structure to be JSON-expressible,
// so we make RegExp's from strings as-needed
function getRegExp(pattern) {
// If undefined, don't apply a pattern
if (!pattern) {
return NON_WHITESPACE;
}
// Just echo if it's already a regexp
if (pattern instanceof RegExp) {
return pattern;
}
// Otherwise, assume a string
// Cache for easy lookup later (so we don't
// creat a new RegExp every digest cycle)
if (!regexps[pattern]) {
regexps[pattern] = new RegExp(pattern);
}
return regexps[pattern];
}
// Publish the form state under the requested
// name in the parent scope
$scope.$watch("mctForm", function (mctForm) {
if ($scope.name) {
$scope.$parent[$scope.name] = mctForm;
}
});
// Expose the regexp lookup
$scope.getRegExp = getRegExp;
}
return FormController;
}
);

View File

@ -1,74 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../src/FileInputService"],
function (FileInputService) {
describe("The FileInputService", function () {
var fileInputService,
mockInput;
beforeEach(function () {
fileInputService = new FileInputService();
mockInput = jasmine.createSpyObj('input',
[
'on',
'trigger',
'remove'
]
);
mockInput.on.and.callFake(function (event, changeHandler) {
changeHandler.apply(mockInput);
});
spyOn(fileInputService, "newInput").and.returnValue(
mockInput
);
});
it("can read a file", function () {
mockInput.files = [new File(["file content"], "file name")];
fileInputService.getInput().then(function (result) {
expect(result.name).toBe("file name");
expect(result.body).toBe("file content");
});
expect(mockInput.trigger).toHaveBeenCalledWith('click');
expect(mockInput.remove).toHaveBeenCalled();
});
it("catches file read errors", function () {
mockInput.files = ["GARBAGE"];
fileInputService.getInput().then(
function (result) {},
function (err) {
expect(err).toBe("File read error");
}
);
expect(mockInput.trigger).toHaveBeenCalledWith('click');
expect(mockInput.remove).toHaveBeenCalled();
});
});
}
);

View File

@ -1,92 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../src/MCTControl"],
function (MCTControl) {
describe("The mct-control directive", function () {
var testControls,
mockScope,
mockLinker,
mockChangeTemplate,
mctControl;
beforeEach(function () {
testControls = [
{
key: "abc",
bundle: {
path: "a",
resources: "b"
},
templateUrl: "c/template.html"
},
{
key: "xyz",
bundle: {
path: "x",
resources: "y"
},
templateUrl: "z/template.html"
}
];
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockLinker = jasmine.createSpyObj("templateLinker", ["link"]);
mockChangeTemplate = jasmine.createSpy('changeTemplate');
mockLinker.link.and.returnValue(mockChangeTemplate);
mctControl = new MCTControl(mockLinker, testControls);
});
it("is restricted to the element level", function () {
expect(mctControl.restrict).toEqual("E");
});
it("watches its passed key to choose a template", function () {
mctControl.link(mockScope);
expect(mockScope.$watch).toHaveBeenCalledWith(
"key",
jasmine.any(Function)
);
});
it("changes its template dynamically", function () {
mctControl.link(mockScope);
expect(mockChangeTemplate)
.not.toHaveBeenCalledWith(testControls[1]);
mockScope.key = "xyz";
mockScope.$watch.calls.mostRecent().args[1]("xyz");
// Should have communicated the template path to
// ng-include via the "inclusion" field in scope
expect(mockChangeTemplate)
.toHaveBeenCalledWith(testControls[1]);
});
});
}
);

View File

@ -1,95 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../src/MCTFileInput"],
function (MCTFileInput) {
xdescribe("The mct-file-input directive", function () {
var mockScope,
mockFileInputService,
mctFileInput,
element,
attrs,
control;
beforeEach(function () {
attrs = [];
control = jasmine.createSpyObj('control', ['$setValidity']);
element = jasmine.createSpyObj('element', ['on', 'trigger']);
mockFileInputService = jasmine.createSpyObj('fileInputService',
['getInput']
);
mockScope = jasmine.createSpyObj(
'$scope',
['$watch']
);
mockScope.structure = {text: 'Select File'};
mockScope.field = "file-input";
mockScope.ngModel = {"file-input": undefined};
element.on.and.callFake(function (event, clickHandler) {
clickHandler();
});
mockFileInputService.getInput.and.returnValue(
Promise.resolve({
name: "file-name",
body: "file-body"
})
);
mctFileInput = new MCTFileInput(mockFileInputService);
return new Promise(function (resolve) {
mctFileInput.link(mockScope, element, attrs, control);
setTimeout(resolve, 100);
});
});
it("is restricted to attributes", function () {
expect(mctFileInput.restrict).toEqual("A");
});
it("changes button text to match file name", function () {
expect(element.on).toHaveBeenCalledWith(
'click',
jasmine.any(Function)
);
expect(mockScope.structure.text).toEqual("file-name");
});
it("validates control on file selection", function () {
var calls = control.$setValidity.calls;
expect(calls.count()).toBe(2);
expect(calls.all()[0].args).toEqual(
['file-input', false]
);
expect(calls.all()[1].args).toEqual(
['file-input', true]
);
});
});
}
);

View File

@ -1,114 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../src/MCTForm"],
function (MCTForm) {
describe("The mct-form directive", function () {
var mockScope,
mctForm;
function installController() {
var Controller = mctForm.controller[1];
return new Controller(mockScope);
}
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockScope.$parent = {};
mctForm = new MCTForm();
});
it("is restricted to elements", function () {
expect(mctForm.restrict).toEqual("E");
});
it("watches for changes in form by name", function () {
// mct-form needs to watch for the form by name
// in order to convey changes in $valid, $dirty, etc
// up to the parent scope.
installController();
expect(mockScope.$watch).toHaveBeenCalledWith(
"mctForm",
jasmine.any(Function)
);
});
it("conveys form status to parent scope", function () {
var someState = { someKey: "some value" };
mockScope.name = "someName";
installController();
mockScope.$watch.calls.mostRecent().args[1](someState);
expect(mockScope.$parent.someName).toBe(someState);
});
it("allows strings to be converted to RegExps", function () {
// This is needed to support ng-pattern in the template
installController();
// Should have added getRegExp to the scope,
// to convert strings to regular expressions
expect(mockScope.getRegExp("^\\d+$")).toEqual(/^\d+$/);
});
it("returns the same regexp instance for the same string", function () {
// Don't want new instances each digest cycle, for performance
var strRegExp = "^[a-z]\\d+$",
regExp;
// Add getRegExp to scope
installController();
regExp = mockScope.getRegExp(strRegExp);
// Same object instance each time...
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
});
it("passes RegExp objects through untouched", function () {
// Permit using forms to simply provide their own RegExp object
var regExp = /^\d+[a-d]$/;
// Add getRegExp to scope
installController();
// Should have added getRegExp to the scope,
// to convert strings to regular expressions
expect(mockScope.getRegExp(regExp)).toBe(regExp);
});
it("passes a non-whitespace regexp when no pattern is defined", function () {
// If no pattern is supplied, ng-pattern should match anything
installController();
expect(mockScope.getRegExp()).toEqual(/\S/);
expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
});
});
}
);

View File

@ -1,75 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
"../../src/controllers/AutocompleteController",
"angular"
], function (
AutocompleteController,
angular
) {
describe("The autocomplete controller", function () {
var mockScope,
mockElement,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockScope.options = ['Asia/Dhaka', 'UTC', 'Toronto', 'Asia/Shanghai', 'Hotel California'];
mockScope.ngModel = [null, null, null, null, null];
mockScope.field = 4;
mockElement = angular.element("<div></div>");
controller = new AutocompleteController(mockScope, mockElement);
});
it("makes optionNames array equal to options if options is an array of string", function () {
expect(mockScope.optionNames).toEqual(mockScope.options);
});
it("filters options by returning array containing optionId and name", function () {
mockScope.filterOptions('Asia');
var filteredOptions = [{
optionId: 0,
name: 'Asia/Dhaka'
},
{
optionId: 1,
name: 'Asia/Shanghai'
}];
expect(mockScope.filteredOptions).toEqual(filteredOptions);
});
it("fills input with given string", function () {
var str = "UTC";
mockScope.fillInput(str);
expect(mockScope.hideOptions).toEqual(true);
expect(mockScope.ngModel[mockScope.field]).toEqual(str);
});
it("sets a new optionIndex on mouse hover", function () {
mockScope.optionMouseover(1);
expect(mockScope.optionIndex).toEqual(1);
});
});
});

View File

@ -1,70 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/controllers/ColorController"],
function (ColorController) {
var COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
describe("The color picker's controller", function () {
var controller;
beforeEach(function () {
controller = new ColorController();
});
it("exposes groups of colors", function () {
var groups = controller.groups();
// Make sure that the groups array is non-empty
expect(Array.isArray(groups)).toBeTruthy();
expect(groups.length).not.toEqual(0);
groups.forEach(function (group) {
// Make sure each group is a non-empty array
expect(Array.isArray(group)).toBeTruthy();
expect(group.length).not.toEqual(0);
// Make sure they're valid web colors
group.forEach(function (color) {
expect(COLOR_REGEX.test(color)).toBeTruthy();
});
});
});
it("exposes unique colors", function () {
var count = 0, set = {};
// Count each color, and add them to the set
controller.groups().forEach(function (group) {
group.forEach(function (color) {
count += 1;
set[color] = true;
});
});
// Size of set should be number of colors if all were unique
expect(Object.keys(set).length).toEqual(count);
});
});
}
);

View File

@ -1,107 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/controllers/DateTimeController"],
function (DateTimeController) {
describe("The date-time controller", function () {
var mockScope,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
controller = new DateTimeController(mockScope);
});
it("watches for changes in fields", function () {
["date", "hour", "min", "sec"].forEach(function (fieldName) {
expect(mockScope.$watch).toHaveBeenCalledWith(
"datetime." + fieldName,
jasmine.any(Function)
);
});
});
it("converts date-time input into a timestamp", function () {
mockScope.ngModel = {};
mockScope.field = "test";
mockScope.datetime.date = "2014-11-28";
mockScope.datetime.hour = 22;
mockScope.datetime.min = 55;
mockScope.datetime.sec = 13;
mockScope.$watch.calls.mostRecent().args[1]();
expect(mockScope.ngModel.test).toEqual(1417215313000);
});
it("reports when form input is partially complete", function () {
// This is needed to flag the control's state as invalid
// when it is partially complete without having it treated
// as required.
mockScope.ngModel = {};
mockScope.field = "test";
mockScope.datetime.date = "2014-11-28";
mockScope.datetime.hour = 22;
mockScope.datetime.min = 55;
// mockScope.datetime.sec = 13;
mockScope.$watch.calls.mostRecent().args[1]();
expect(mockScope.partiallyComplete).toBeTruthy();
});
it("reports 'undefined' for empty input", function () {
mockScope.ngModel = { test: 12345 };
mockScope.field = "test";
mockScope.$watch.calls.mostRecent().args[1]();
// Clear all inputs
mockScope.datetime = {};
mockScope.$watch.calls.mostRecent().args[1]();
// Should have cleared out the time stamp
expect(mockScope.ngModel.test).toBeUndefined();
});
it("exposes date-time format for placeholder", function () {
expect(mockScope.format).toEqual(jasmine.any(String));
expect(mockScope.format.length).toBeGreaterThan(0);
});
it("initializes form fields with values from ng-model", function () {
mockScope.ngModel = { test: 1417215313000 };
mockScope.field = "test";
mockScope.$watch.calls.all().forEach(function (call) {
if (call.args[0] === 'ngModel[field]') {
call.args[1](mockScope.ngModel.test);
}
});
expect(mockScope.datetime).toEqual({
date: "2014-11-28",
hour: "22",
min: "55",
sec: "13"
});
});
});
}
);

View File

@ -1,135 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/controllers/DialogButtonController"],
function (DialogButtonController) {
describe("A dialog button controller", function () {
var mockScope,
mockDialogService,
mockPromise,
testStructure,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
'$scope',
['$watch']
);
mockDialogService = jasmine.createSpyObj(
'dialogService',
['getUserInput']
);
mockPromise = jasmine.createSpyObj(
'promise',
['then']
);
testStructure = {
name: "A Test",
cssClass: "icon-T",
description: "Test description",
control: "dialog-button",
title: "Test title",
dialog: {
"control": "textfield",
"name": "Inner control"
}
};
mockScope.field = "testKey";
mockScope.ngModel = { testKey: "initial test value" };
mockScope.structure = testStructure;
mockDialogService.getUserInput.and.returnValue(mockPromise);
controller = new DialogButtonController(
mockScope,
mockDialogService
);
});
it("provides a structure for a button control", function () {
var buttonStructure;
// Template is just a mct-control pointing to a button
// control, so this controller needs to set up all the
// logic for showing a dialog and collecting user input
// when that button gets clicked.
expect(mockScope.$watch).toHaveBeenCalledWith(
"structure", // As passed in via mct-control
jasmine.any(Function)
);
mockScope.$watch.calls.mostRecent().args[1](testStructure);
buttonStructure = controller.getButtonStructure();
expect(buttonStructure.cssClass).toEqual(testStructure.cssClass);
expect(buttonStructure.description).toEqual(testStructure.description);
expect(buttonStructure.name).toEqual(testStructure.name);
expect(buttonStructure.click).toEqual(jasmine.any(Function));
});
it("shows a dialog when clicked", function () {
mockScope.$watch.calls.mostRecent().args[1](testStructure);
// Verify precondition - no dialog shown
expect(mockDialogService.getUserInput).not.toHaveBeenCalled();
// Click!
controller.getButtonStructure().click();
// Should have shown a dialog
expect(mockDialogService.getUserInput).toHaveBeenCalled();
});
it("stores user input to the model", function () {
var key, input = {};
// Show dialog, click...
mockScope.$watch.calls.mostRecent().args[1](testStructure);
controller.getButtonStructure().click();
// Should be listening to 'then'
expect(mockPromise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
// Find the key that the dialog should return
key = mockDialogService.getUserInput.calls.mostRecent()
.args[0].sections[0].rows[0].key;
// Provide 'user input'
input[key] = "test user input";
// Resolve the promise with it
mockPromise.then.calls.mostRecent().args[0](input);
// ... should have been placed into the model
expect(mockScope.ngModel.testKey).toEqual("test user input");
});
it("supplies initial model state to the dialog", function () {
var key, state;
mockScope.$watch.calls.mostRecent().args[1](testStructure);
controller.getButtonStructure().click();
// Find the key that the dialog should return
key = mockDialogService.getUserInput.calls.mostRecent()
.args[0].sections[0].rows[0].key;
// Get the initial state provided to the dialog
state = mockDialogService.getUserInput.calls.mostRecent().args[1];
// Should have had value from ngModel stored to that key
expect(state[key]).toEqual("initial test value");
});
});
}
);

View File

@ -1,87 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/controllers/FormController"],
function (FormController) {
describe("The form controller", function () {
var mockScope,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
mockScope.$parent = {};
controller = new FormController(mockScope);
});
it("watches for changes in form by name", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"mctForm",
jasmine.any(Function)
);
});
it("conveys form status to parent scope", function () {
var someState = { someKey: "some value" };
mockScope.name = "someName";
mockScope.$watch.calls.mostRecent().args[1](someState);
expect(mockScope.$parent.someName).toBe(someState);
});
it("allows strings to be converted to RegExps", function () {
// Should have added getRegExp to the scope,
// to convert strings to regular expressions
expect(mockScope.getRegExp("^\\d+$")).toEqual(/^\d+$/);
});
it("returns the same regexp instance for the same string", function () {
// Don't want new instances each digest cycle, for performance
var strRegExp = "^[a-z]\\d+$",
regExp;
// Add getRegExp to scope
regExp = mockScope.getRegExp(strRegExp);
// Same object instance each time...
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
});
it("passes RegExp objects through untouched", function () {
// Permit using forms to simply provide their own RegExp object
var regExp = /^\d+[a-d]$/;
// Should have added getRegExp to the scope,
// to convert strings to regular expressions
expect(mockScope.getRegExp(regExp)).toBe(regExp);
});
it("passes a non-whitespace regexp when no pattern is defined", function () {
// If no pattern is supplied, ng-pattern should match anything
expect(mockScope.getRegExp()).toEqual(/\S/);
expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
});
});
}
);

View File

@ -1,14 +0,0 @@
# Import / Export Plugin
The Import/Export plugin allows objects to be exported as JSON files. This allows for sharing of objects between users
who are not using a shared persistence store. It also allows object trees to be backed up. Additionally, object trees
exported using this tool can then be exposed as read-only static root trees using the
[Static Root Plugin](../../src/plugins/staticRootPlugin/README.md).
Upon installation it will add two new context menu actions to allow import and export of objects. Initiating the Export
action on an object will produce a JSON file that includes the object and all of its composed children. Selecting Import
on an object will allow the user to import a previously exported object tree as a child of the selected object.
## Installation
```js
openmct.install(openmct.plugins.ImportExport())
```

View File

@ -1,82 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
"./src/actions/ExportAsJSONAction",
"./src/actions/ImportAsJSONAction"
], function (
ExportAsJSONAction,
ImportAsJSONAction
) {
return function ImportExportPlugin() {
return function (openmct) {
ExportAsJSONAction.prototype.appliesTo = function (context) {
return this.openmct.$injector.get('policyService')
.allow("creation", context.domainObject.getCapability("type")
);
};
openmct.legacyRegistry.register("platform/import-export", {
"name": "Import-export plugin",
"description": "Allows importing / exporting of domain objects as JSON.",
"extensions": {
"actions": [
{
"key": "export.JSON",
"name": "Export as JSON",
"implementation": ExportAsJSONAction,
"category": "contextual",
"cssClass": "icon-export",
"group": "json",
"priority": 2,
"depends": [
"openmct",
"exportService",
"policyService",
"identifierService",
"typeService"
]
},
{
"key": "import.JSON",
"name": "Import from JSON",
"implementation": ImportAsJSONAction,
"category": "contextual",
"cssClass": "icon-import",
"group": "json",
"priority": 2,
"depends": [
"exportService",
"identifierService",
"dialogService",
"openmct"
]
}
]
}
});
openmct.legacyRegistry.enable('platform/import-export');
};
};
});

View File

@ -1,202 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(['lodash'], function (_) {
/**
* The ExportAsJSONAction is available from context menus and allows a user
* to export any creatable domain object as a JSON file.
*
* @implements {Action}
* @constructor
* @memberof platform/import-export
*/
function ExportAsJSONAction(
openmct,
exportService,
policyService,
identifierService,
typeService,
context
) {
this.openmct = openmct;
this.root = {};
this.tree = {};
this.calls = 0;
this.context = context;
this.externalIdentifiers = [];
this.exportService = exportService;
this.policyService = policyService;
this.identifierService = identifierService;
this.typeService = typeService;
this.idMap = {};
}
ExportAsJSONAction.prototype.perform = function () {
var root = this.context.domainObject.useCapability('adapter');
this.root = this.copyObject(root);
var rootId = this.getId(this.root);
this.tree[rootId] = this.root;
this.saveAs = function (completedTree) {
this.exportService.exportJSON(
completedTree,
{filename: this.root.name + '.json'}
);
};
this.write(this.root);
};
/**
* Traverses object hierarchy and populates tree object with models and
* identifiers.
*
* @private
* @param {Object} parent
*/
ExportAsJSONAction.prototype.write = function (parent) {
this.calls++;
var composition = this.openmct.composition.get(parent);
if (composition !== undefined) {
composition.load()
.then(function (children) {
children.forEach(function (child, index) {
// Only export if object is creatable
if (this.isCreatable(child)) {
// Prevents infinite export of self-contained objs
if (!Object.prototype.hasOwnProperty.call(this.tree, this.getId(child))) {
// If object is a link to something absent from
// tree, generate new id and treat as new object
if (this.isExternal(child, parent)) {
child = this.rewriteLink(child, parent);
} else {
this.tree[this.getId(child)] = child;
}
this.write(child);
}
}
}.bind(this));
this.calls--;
if (this.calls === 0) {
this.rewriteReferences();
this.saveAs(this.wrapTree());
}
}.bind(this));
} else {
this.calls--;
if (this.calls === 0) {
this.rewriteReferences();
this.saveAs(this.wrapTree());
}
}
};
/**
* Exports an externally linked object as an entirely new object in the
* case where the original is not present in the exported tree.
*
* @private
*/
ExportAsJSONAction.prototype.rewriteLink = function (child, parent) {
this.externalIdentifiers.push(this.getId(child));
var index = parent.composition.findIndex(id => {
return _.isEqual(child.identifier, id);
});
var copyOfChild = this.copyObject(child);
copyOfChild.identifier.key = this.identifierService.generate();
var newIdString = this.getId(copyOfChild);
var parentId = this.getId(parent);
this.idMap[this.getId(child)] = newIdString;
copyOfChild.location = parentId;
parent.composition[index] = copyOfChild.identifier;
this.tree[newIdString] = copyOfChild;
this.tree[parentId].composition[index] = copyOfChild.identifier;
return copyOfChild;
};
ExportAsJSONAction.prototype.copyObject = function (object) {
var jsonString = JSON.stringify(object);
return JSON.parse(jsonString);
};
ExportAsJSONAction.prototype.isExternal = function (child, parent) {
if (child.location !== this.getId(parent)
&& !Object.keys(this.tree).includes(child.location)
&& this.getId(child) !== this.getId(this.root)
|| this.externalIdentifiers.includes(this.getId(child))) {
return true;
}
return false;
};
/**
* Wraps root object for identification on reimport and wraps entire
* exported JSON construct for validation.
*
* @private
*/
ExportAsJSONAction.prototype.wrapTree = function () {
return {
"openmct": this.tree,
"rootId": this.getId(this.root)
};
};
ExportAsJSONAction.prototype.isCreatable = function (domainObject) {
var type = this.typeService.getType(domainObject.type);
return this.policyService.allow(
"creation",
type
);
};
/**
* @private
*/
ExportAsJSONAction.prototype.getId = function (domainObject) {
return this.openmct.objects.makeKeyString(domainObject.identifier);
};
/**
* @private
*/
ExportAsJSONAction.prototype.rewriteReferences = function () {
var treeString = JSON.stringify(this.tree);
Object.keys(this.idMap).forEach(function (oldId) {
var newId = this.idMap[oldId];
treeString = treeString.split(oldId).join(newId);
}.bind(this));
this.tree = JSON.parse(treeString);
};
return ExportAsJSONAction;
});

View File

@ -1,238 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(['zepto', 'objectUtils'], function ($, objectUtils) {
/**
* The ImportAsJSONAction is available from context menus and allows a user
* to import a previously exported domain object into any domain object
* that has the composition capability.
*
* @implements {Action}
* @constructor
* @memberof platform/import-export
*/
function ImportAsJSONAction(
exportService,
identifierService,
dialogService,
openmct,
context
) {
this.openmct = openmct;
this.context = context;
this.exportService = exportService;
this.dialogService = dialogService;
this.identifierService = identifierService;
this.instantiate = openmct.$injector.get("instantiate");
}
ImportAsJSONAction.prototype.perform = function () {
this.dialogService.getUserInput(this.getFormModel(), {})
.then(function (form) {
var objectTree = form.selectFile.body;
if (this.validateJSON(objectTree)) {
this.importObjectTree(JSON.parse(objectTree));
} else {
this.displayError();
}
}.bind(this));
};
ImportAsJSONAction.prototype.importObjectTree = function (objTree) {
var parent = this.context.domainObject;
var namespace = parent.useCapability('adapter').identifier.namespace;
var tree = this.generateNewIdentifiers(objTree, namespace);
var rootId = tree.rootId;
var rootModel = tree.openmct[rootId];
delete rootModel.persisted;
var rootObj = this.instantiate(rootModel, rootId);
var newStyleParent = parent.useCapability('adapter');
var newStyleRootObj = rootObj.useCapability('adapter');
if (this.openmct.composition.checkPolicy(newStyleParent, newStyleRootObj)) {
// Instantiate all objects in tree with their newly generated ids,
// adding each to its rightful parent's composition
rootObj.getCapability("location").setPrimaryLocation(parent.getId());
this.deepInstantiate(rootObj, tree.openmct, []);
parent.getCapability("composition").add(rootObj);
} else {
var dialog = this.openmct.overlays.dialog({
iconClass: 'alert',
message: "We're sorry, but you cannot import that object type into this object.",
buttons: [
{
label: "Ok",
emphasis: true,
callback: function () {
dialog.dismiss();
}
}
]
});
}
};
ImportAsJSONAction.prototype.deepInstantiate = function (parent, tree, seen) {
// Traverses object tree, instantiates all domain object w/ new IDs and
// adds to parent's composition
if (parent.hasCapability("composition")) {
var parentModel = parent.getModel();
var newObj;
seen.push(parent.getId());
parentModel.composition.forEach(function (childId) {
let keystring = this.openmct.objects.makeKeyString(childId);
if (!tree[keystring] || seen.includes(keystring)) {
return;
}
let newModel = tree[keystring];
delete newModel.persisted;
newObj = this.instantiate(newModel, keystring);
newObj.getCapability("location")
.setPrimaryLocation(tree[keystring].location);
this.deepInstantiate(newObj, tree, seen);
}, this);
}
};
ImportAsJSONAction.prototype.generateNewIdentifiers = function (tree, namespace) {
// For each domain object in the file, generate new ID, replace in tree
Object.keys(tree.openmct).forEach(function (domainObjectId) {
let newId = {
namespace: namespace,
key: this.identifierService.generate()
};
let oldId = objectUtils.parseKeyString(domainObjectId);
tree = this.rewriteId(oldId, newId, tree);
}, this);
return tree;
};
ImportAsJSONAction.prototype.getKeyString = function (identifier) {
return this.openmct.objects.makeKeyString(identifier);
};
/**
* Rewrites all instances of a given id in the tree with a newly generated
* replacement to prevent collision.
*
* @private
*/
ImportAsJSONAction.prototype.rewriteId = function (oldId, newId, tree) {
let newIdKeyString = this.openmct.objects.makeKeyString(newId);
let oldIdKeyString = this.openmct.objects.makeKeyString(oldId);
tree = JSON.stringify(tree).replace(new RegExp(oldIdKeyString, 'g'), newIdKeyString);
return JSON.parse(tree, (key, value) => {
if (value !== undefined
&& value !== null
&& Object.prototype.hasOwnProperty.call(value, 'key')
&& Object.prototype.hasOwnProperty.call(value, 'namespace')
&& value.key === oldId.key
&& value.namespace === oldId.namespace) {
return newId;
} else {
return value;
}
});
};
ImportAsJSONAction.prototype.getFormModel = function () {
return {
name: "Import as JSON",
sections: [
{
name: "Import A File",
rows: [
{
name: 'Select File',
key: 'selectFile',
control: 'file-input',
required: true,
text: 'Select File...'
}
]
}
]
};
};
ImportAsJSONAction.prototype.validateJSON = function (jsonString) {
var json;
try {
json = JSON.parse(jsonString);
} catch (e) {
return false;
}
if (!json.openmct || !json.rootId) {
return false;
}
return true;
};
ImportAsJSONAction.prototype.displayError = function () {
var dialog,
model = {
title: "Invalid File",
actionText: "The selected file was either invalid JSON or was "
+ "not formatted properly for import into Open MCT.",
severity: "error",
options: [
{
label: "Ok",
callback: function () {
dialog.dismiss();
}
}
]
};
dialog = this.dialogService.showBlockingMessage(model);
};
ImportAsJSONAction.appliesTo = function (context, view, openmct) {
let domainObject = (context || {}).domainObject;
if (!domainObject || (domainObject.model && domainObject.model.locked)) {
return false;
}
let isPersistable = openmct.objects.isPersistable(domainObject.id);
let hasComposition = domainObject.hasCapability('composition');
return hasComposition && isPersistable;
};
return ImportAsJSONAction;
});

View File

@ -46,7 +46,10 @@ define([
'./plugins/licenses/plugin', './plugins/licenses/plugin',
'./plugins/remove/plugin', './plugins/remove/plugin',
'./plugins/move/plugin', './plugins/move/plugin',
'./plugins/linkAction/plugin',
'./plugins/duplicate/plugin', './plugins/duplicate/plugin',
'./plugins/importFromJSONAction/plugin',
'./plugins/exportAsJSONAction/plugin',
'vue' 'vue'
], function ( ], function (
EventEmitter, EventEmitter,
@ -74,7 +77,10 @@ define([
LicensesPlugin, LicensesPlugin,
RemoveActionPlugin, RemoveActionPlugin,
MoveActionPlugin, MoveActionPlugin,
LinkActionPlugin,
DuplicateActionPlugin, DuplicateActionPlugin,
ImportFromJSONAction,
ExportAsJSONAction,
Vue Vue
) { ) {
/** /**
@ -254,6 +260,7 @@ define([
this.priority = api.PriorityAPI; this.priority = api.PriorityAPI;
this.router = new ApplicationRouter(this); this.router = new ApplicationRouter(this);
this.forms = new api.FormsAPI.default(this);
this.branding = BrandingAPI.default; this.branding = BrandingAPI.default;
@ -270,14 +277,17 @@ define([
this.install(LicensesPlugin.default()); this.install(LicensesPlugin.default());
this.install(RemoveActionPlugin.default()); this.install(RemoveActionPlugin.default());
this.install(MoveActionPlugin.default()); this.install(MoveActionPlugin.default());
this.install(LinkActionPlugin.default());
this.install(DuplicateActionPlugin.default()); this.install(DuplicateActionPlugin.default());
this.install(ExportAsJSONAction.default());
this.install(ImportFromJSONAction.default());
this.install(this.plugins.FormActions.default());
this.install(this.plugins.FolderView()); this.install(this.plugins.FolderView());
this.install(this.plugins.Tabs()); this.install(this.plugins.Tabs());
this.install(ImageryPlugin.default()); this.install(ImageryPlugin.default());
this.install(this.plugins.FlexibleLayout()); this.install(this.plugins.FlexibleLayout());
this.install(this.plugins.GoToOriginalAction()); this.install(this.plugins.GoToOriginalAction());
this.install(this.plugins.OpenInNewTabAction()); this.install(this.plugins.OpenInNewTabAction());
this.install(this.plugins.ImportExport());
this.install(this.plugins.WebPage()); this.install(this.plugins.WebPage());
this.install(this.plugins.Condition()); this.install(this.plugins.Condition());
this.install(this.plugins.ConditionWidget()); this.install(this.plugins.ConditionWidget());

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import _ from 'lodash'; import _ from 'lodash';
const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"]; const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"];
const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "link", "remove", "locate"]; const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "move", "link", "remove", "locate"];
export default class LegacyContextMenuAction { export default class LegacyContextMenuAction {
constructor(openmct, LegacyAction) { constructor(openmct, LegacyAction) {

View File

@ -21,44 +21,47 @@
*****************************************************************************/ *****************************************************************************/
define([ define([
'./time/TimeAPI',
'./objects/ObjectAPI',
'./composition/CompositionAPI',
'./types/TypeRegistry',
'./telemetry/TelemetryAPI',
'./indicators/IndicatorAPI',
'./notifications/NotificationAPI',
'./Editor',
'./menu/MenuAPI',
'./actions/ActionsAPI', './actions/ActionsAPI',
'./composition/CompositionAPI',
'./Editor',
'./forms/FormsAPI',
'./indicators/IndicatorAPI',
'./menu/MenuAPI',
'./notifications/NotificationAPI',
'./objects/ObjectAPI',
'./priority/PriorityAPI',
'./status/StatusAPI', './status/StatusAPI',
'./priority/PriorityAPI' './telemetry/TelemetryAPI',
'./time/TimeAPI',
'./types/TypeRegistry'
], function ( ], function (
TimeAPI,
ObjectAPI,
CompositionAPI,
TypeRegistry,
TelemetryAPI,
IndicatorAPI,
NotificationAPI,
EditorAPI,
MenuAPI,
ActionsAPI, ActionsAPI,
CompositionAPI,
EditorAPI,
FormsAPI,
IndicatorAPI,
MenuAPI,
NotificationAPI,
ObjectAPI,
PriorityAPI,
StatusAPI, StatusAPI,
PriorityAPI TelemetryAPI,
TimeAPI,
TypeRegistry
) { ) {
return { return {
TimeAPI: TimeAPI.default,
ObjectAPI: ObjectAPI,
CompositionAPI: CompositionAPI,
TypeRegistry: TypeRegistry,
TelemetryAPI: TelemetryAPI,
IndicatorAPI: IndicatorAPI,
NotificationAPI: NotificationAPI.default,
EditorAPI: EditorAPI,
MenuAPI: MenuAPI.default,
ActionsAPI: ActionsAPI.default, ActionsAPI: ActionsAPI.default,
CompositionAPI: CompositionAPI,
EditorAPI: EditorAPI,
FormsAPI: FormsAPI,
IndicatorAPI: IndicatorAPI,
MenuAPI: MenuAPI.default,
NotificationAPI: NotificationAPI.default,
ObjectAPI: ObjectAPI,
PriorityAPI: PriorityAPI.default,
StatusAPI: StatusAPI.default, StatusAPI: StatusAPI.default,
PriorityAPI: PriorityAPI.default TelemetryAPI: TelemetryAPI,
TimeAPI: TimeAPI.default,
TypeRegistry: TypeRegistry
}; };
}); });

View File

@ -0,0 +1,93 @@
import AutoCompleteField from './components/controls/AutoCompleteField.vue';
import ClockDisplayFormatField from './components/controls/ClockDisplayFormatField.vue';
import Datetime from './components/controls/Datetime.vue';
import FileInput from './components/controls/FileInput.vue';
import Locator from './components/controls/Locator.vue';
import NumberField from './components/controls/NumberField.vue';
import SelectField from './components/controls/SelectField.vue';
import TextAreaField from './components/controls/TextAreaField.vue';
import TextField from './components/controls/TextField.vue';
import Vue from 'vue';
export const DEFAULT_CONTROLS_MAP = {
'autocomplete': AutoCompleteField,
'composite': ClockDisplayFormatField,
'datetime': Datetime,
'file-input': FileInput,
'locator': Locator,
'numberfield': NumberField,
'select': SelectField,
'textarea': TextAreaField,
'textfield': TextField
};
export default class FormControl {
constructor(openmct) {
this.openmct = openmct;
this.controls = {};
this._addDefaultFormControls();
}
addControl(controlName, controlViewProvider) {
const control = this.controls[controlName];
if (control) {
console.warn(`Error: provided form control '${controlName}', already exists`);
return;
}
this.controls[controlName] = controlViewProvider;
}
getControl(controlName) {
const control = this.controls[controlName];
if (!control) {
console.error(`Error: form control '${controlName}', does not exist`);
}
return control;
}
/**
* @private
*/
_addDefaultFormControls() {
Object.keys(DEFAULT_CONTROLS_MAP).forEach(control => {
const controlViewProvider = this._getControlViewProvider(control);
this.addControl(control, controlViewProvider);
});
}
/**
* @private
*/
_getControlViewProvider(control) {
const self = this;
return {
show(element, model, onChange) {
const rowComponent = new Vue({
el: element,
components: {
FormControlComponent: DEFAULT_CONTROLS_MAP[control]
},
provide: {
openmct: self.openmct
},
data() {
return {
model,
onChange
};
},
template: `<FormControlComponent :model="model" @onChange="onChange"></FormControlComponent>`
});
return rowComponent;
}
};
}
}

185
src/api/forms/FormsAPI.js Normal file
View File

@ -0,0 +1,185 @@
/*****************************************************************************
* 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.
*****************************************************************************/
import FormController from './FormController';
import FormProperties from './components/FormProperties.vue';
import Vue from 'vue';
export default class FormsAPI {
constructor(openmct) {
this.openmct = openmct;
this.formController = new FormController(openmct);
}
/**
* Control View Provider definition for a form control
* @typedef ControlViewProvider
* @property {function} show a function renders view in place of given element
* This function accepts element, model and onChange function
* element - html element (place holder) to render a row view
* model - row data for rendering name, value etc for given row type
* onChange - an onChange event callback funtion to keep track of any change in value
* @property {function} destroy a callback function when a vue component gets destroyed
*/
/**
* Create a new form control definition with a formControlViewProvider
* this formControlViewProvider is used inside form overlay to show/render a form row
*
* @public
* @param {String} controlName a form structure, array of section
* @param {ControlViewProvider} controlViewProvider
*/
addNewFormControl(controlName, controlViewProvider) {
this.formController.addControl(controlName, controlViewProvider);
}
/**
* Get a ControlViewProvider for a given/stored form controlName
*
* @public
* @param {String} controlName a form structure, array of section
* @return {ControlViewProvider}
*/
getFormControl(controlName) {
return this.formController.getControl(controlName);
}
/**
* Section definition for formStructure
* @typedef Section
* @property {object} name Name of the section to display on Form
* @property {string} cssClass class name for styling section
* @property {array<Row>} rows collection of rows inside a section
*/
/**
* Row definition for Section
* @typedef Row
* @property {string} control represents type of row to render
* eg:autocomplete,composite,datetime,file-input,locator,numberfield,select,textarea,textfield
* @property {string} cssClass class name for styling this row
* @property {module:openmct.DomainObject} domainObject object to be used by row
* @property {string} key id for this row
* @property {string} name Name of the row to display on Form
* @property {module:openmct.DomainObject} parent parent object to be used by row
* @property {boolean} required is this row mandatory
* @property {function} validate a function to validate this row on any changes
*/
/**
* Show form inside an Overlay dialog with given form structure
*
* @public
* @param {Array<Section>} formStructure a form structure, array of section
* @param {Object} options
* @property {HTMLElement} element Parent Element to render a Form
* @property {function} onChange a callback function when any changes detected
* @property {function} onSave a callback function when form is submitted
* @property {function} onDismiss a callback function when form is dismissed
*/
showForm(formStructure, {
element,
onChange
} = {}) {
const changes = {};
let overlay;
let onDismiss;
let onSave;
const promise = new Promise((resolve, reject) => {
onSave = onFormSave(resolve);
onDismiss = onFormDismiss(reject);
});
const vm = new Vue({
components: { FormProperties },
provide: {
openmct: this.openmct
},
data() {
return {
formStructure,
onChange: onFormPropertyChange,
onDismiss,
onSave
};
},
template: '<FormProperties :model="formStructure" @onChange="onChange" @onDismiss="onDismiss" @onSave="onSave"></FormProperties>'
}).$mount();
const formElement = vm.$el;
if (element) {
element.append(formElement);
} else {
overlay = this.openmct.overlays.overlay({
element: vm.$el,
size: 'small',
onDestroy: () => vm.$destroy()
});
}
function onFormPropertyChange(data) {
if (onChange) {
onChange(data);
}
if (data.model) {
const property = data.model.property;
let key = data.model.key;
if (property && property.length) {
key = property.join('.');
}
changes[key] = data.value;
}
}
function onFormDismiss(dismiss) {
return () => {
if (element) {
formElement.remove();
} else {
overlay.dismiss();
}
if (dismiss) {
dismiss();
}
};
}
function onFormSave(save) {
return () => {
overlay.dismiss();
if (save) {
save(changes);
}
};
}
return promise;
}
}

View File

@ -0,0 +1,137 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div class="c-form">
<div class="c-overlay__top-bar c-form__top-bar">
<div class="c-overlay__dialog-title">{{ model.title }}</div>
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
</div>
<form name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
>
<div v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
>
<h2 v-if="section.name"
class="c-form__section-header"
>
{{ section.name }}
</h2>
<div v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
>
<FormRow :css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
/>
</div>
</div>
</form>
<div class="mct-form__controls c-overlay__button-bar c-form__bottom-bar">
<button tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
>
OK
</button>
<button tabindex="0"
class="c-button"
@click="onDismiss"
>
Cancel
</button>
</div>
</div>
</template>
<script>
import FormRow from "@/api/forms/components/FormRow.vue";
import uuid from 'uuid';
export default {
components: {
FormRow
},
inject: ['openmct'],
props: {
model: {
type: Object,
required: true
},
value: {
type: Object,
default() {
return {};
}
}
},
data() {
return {
invalidProperties: {},
formSections: []
};
},
computed: {
isInvalid() {
return Object.entries(this.invalidProperties)
.some(([key, value]) => {
return value;
});
}
},
mounted() {
this.formSections = this.model.sections.map(section => {
section.id = uuid();
section.rows = section.rows.map(row => {
row.id = uuid();
return row;
});
return section;
});
},
methods: {
onChange(data) {
this.$set(this.invalidProperties, data.model.key, data.invalid);
this.$emit('onChange', data);
},
onDismiss() {
this.$emit('onDismiss');
},
onSave() {
this.$emit('onSave');
}
}
};
</script>

View File

@ -0,0 +1,146 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div class="form-row c-form__row"
:class="[{ 'first': first }]"
@onChange="onChange"
>
<div class="c-form-row__label"
:title="row.description"
>
{{ row.name }}
</div>
<div class="c-form-row__state-indicator"
:class="rowClass"
>
</div>
<div v-if="row.control"
class="c-form-row__controls"
>
<div ref="rowElement"></div>
</div>
</div>
</template>
<script>
export default {
name: 'FormRow',
components: {
},
inject: ['openmct'],
props: {
cssClass: {
type: String,
default: '',
required: true
},
first: {
type: Boolean,
default: false,
required: true
},
row: {
type: Object,
required: true
}
},
data() {
return {
formControl: this.openmct.forms.getFormControl(this.row.control),
valid: undefined,
visited: false
};
},
computed: {
rowClass() {
let cssClass = this.cssClass;
if (this.row.required) {
cssClass = `${cssClass} req`;
}
if (this.visited && this.valid !== undefined) {
if (this.valid === true) {
cssClass = `${cssClass} valid`;
} else {
cssClass = `${cssClass} invalid`;
}
}
return cssClass;
}
},
mounted() {
if (this.row.required) {
const data = {
model: this.row,
value: this.row.value
};
this.onChange(data, false);
}
this.formControl.show(this.$refs.rowElement, this.row, this.onChange);
},
destroyed() {
const destroy = this.formControl.destroy;
if (destroy) {
destroy();
}
},
methods: {
onChange(data, visited = true) {
this.visited = visited;
this.valid = this.validateRow(data);
data.invalid = !this.valid;
this.$emit('onChange', data);
},
validateRow(data) {
let valid = true;
if (this.row.required) {
valid = data.value !== undefined
&& data.value !== null
&& data.value !== '';
}
if (this.row.required && !valid) {
return false;
}
const pattern = data.model.pattern;
if (valid && pattern) {
const regex = new RegExp(pattern);
valid = regex.test(data.value);
}
const validate = data.model.validate;
if (valid && validate) {
valid = validate(data);
}
return Boolean(valid);
}
}
};
</script>

View File

@ -0,0 +1,181 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div class="form-control autocomplete">
<input v-model="field"
class="autocompleteInput"
type="text"
@click="inputClicked()"
@keydown="keyDown($event)"
>
<span class="icon-arrow-down"
@click="arrowClicked()"
></span>
<div class="autocompleteOptions"
@blur="hideOptions = true"
>
<ul v-if="!hideOptions">
<li v-for="opt in filteredOptions"
:key="opt.optionId"
:class="{'optionPreSelected': optionIndex === opt.optionId}"
@click="fillInputWithString(opt.name)"
@mouseover="optionMouseover(opt.optionId)"
>
<span class="optionText">{{ opt.name }}</span>
</li>
</ul>
</div>
</div>
</template>
<script>
const key = {
down: 40,
up: 38,
enter: 13
};
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
hideOptions: true,
optionIndex: 0,
field: this.model.value
};
},
computed: {
filteredOptions() {
const options = this.optionNames || [];
return options
.filter(option => {
return option.toLowerCase().indexOf(this.field.toLowerCase()) >= 0;
}).map((option, index) => {
return {
optionId: index,
name: option
};
});
}
},
watch: {
field(newValue, oldValue) {
if (newValue !== oldValue) {
const data = {
model: this.model,
value: newValue
};
this.$emit('onChange', data);
}
}
},
mounted() {
this.options = this.model.options;
this.autocompleteInputElement = this.$el.getElementsByClassName('autocompleteInput')[0];
if (this.options[0].name) {
// If "options" include name, value pair
this.optionNames = this.options.map((opt) => {
return opt.name;
});
} else {
// If options is only an array of string.
this.optionNames = this.options;
}
},
methods: {
decrementOptionIndex() {
if (this.optionIndex === 0) {
this.optionIndex = this.filteredOptions.length;
}
this.optionIndex--;
this.scrollIntoView();
},
incrementOptionIndex() {
if (this.optionIndex === this.filteredOptions.length - 1) {
this.optionIndex = -1;
}
this.optionIndex++;
this.scrollIntoView();
},
fillInputWithString(string) {
this.hideOptions = true;
this.field = string;
},
showOptions(string) {
this.hideOptions = false;
this.optionIndex = 0;
},
keyDown($event) {
if (this.filteredOptions) {
let keyCode = $event.keyCode;
switch (keyCode) {
case key.down:
this.incrementOptionIndex();
break;
case key.up:
$event.preventDefault(); // Prevents cursor jumping back and forth
this.decrementOptionIndex();
break;
case key.enter:
if (this.filteredOptions[this.optionIndex]) {
this.fillInputWithString(this.filteredOptions[this.optionIndex].name);
}
}
}
},
inputClicked() {
this.autocompleteInputElement.select();
this.showOptions(this.autocompleteInputElement.value);
},
arrowClicked() {
this.autocompleteInputElement.select();
this.showOptions('');
},
optionMouseover(optionId) {
this.optionIndex = optionId;
},
scrollIntoView() {
setTimeout(() => {
const element = this.$el.querySelector('.optionPreSelected');
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest'
});
}
});
}
}
};
</script>

View File

@ -0,0 +1,66 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div class="c-form-control--clock-display-format-fields">
<SelectField v-for="item in items"
:key="item.key"
:model="item"
@onChange="onChange"
/>
</div>
</template>
<script>
import SelectField from '@/api/forms/components/controls/SelectField.vue';
export default {
components: {
SelectField
},
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
items: []
};
},
mounted() {
const values = this.model.value || [];
this.items = this.model.items.map((item, index) => {
item.value = values[index];
item.key = `${this.model.key}.${index}`;
return item;
});
},
methods: {
onChange(data) {
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,57 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<span>
<CompositeItem v-for="(item, index) in model.items"
:key="item.name"
:first="index < 1"
:value="JSON.stringify(model.value[index])"
:item="item"
@onChange="onChange"
/>
</span>
</template>
<script>
import CompositeItem from "@/api/forms/components/controls/CompositeItem.vue";
export default {
components: {
CompositeItem
},
props: {
model: {
type: Object,
required: true
}
},
mounted() {
this.model.items.forEach((item, index) => item.key = `${this.model.key}.${index}`);
},
methods: {
onChange(data) {
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,75 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div :class="compositeCssClass">
<FormRow :css-class="item.cssClass"
:first="first"
:row="row"
@onChange="onChange"
/>
<span class="composite-control-label">
{{ item.name }}
</span>
</div>
</template>
<script>
export default {
components: {
FormRow: () => import('@/api/forms/components/FormRow.vue')
},
props: {
item: {
type: Object,
required: true
},
first: {
type: Boolean,
required: true
},
value: {
type: String,
default() {
return '';
}
}
},
computed: {
compositeCssClass() {
return `l-composite-control l-${this.item.control}`;
},
row() {
const row = this.item;
row.value = JSON.parse(this.value);
return row;
}
},
methods: {
onChange(data) {
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,169 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div class="c-form-control--datetime">
<div class="hint date">Date</div>
<div class="hint time sm">Hour</div>
<div class="hint time sm">Min</div>
<div class="hint time sm">Sec</div>
<div class="hint timezone">Timezone</div>
<form ref="dateTimeForm"
prevent
class="u-contents"
>
<div class="field control date">
<input v-model="date"
:pattern="/\d{4}-\d{2}-\d{2}/"
:placeholder="format"
type="date"
name="date"
@change="onChange"
>
</div>
<div class="field control hour sm">
<input v-model="hour"
:pattern="/\d+/"
type="number"
name="hour"
maxlength="10"
min="0"
max="23"
@change="onChange"
>
</div>
<div class="field control min sm">
<input v-model="min"
:pattern="/\d+/"
type="number"
name="min"
maxlength="2"
min="0"
max="59"
@change="onChange"
>
</div>
<div class="field control sec sm">
<input v-model="sec"
:pattern="/\d+/"
type="number"
name="sec"
maxlength="2"
min="0"
max="59"
@change="onChange"
>
</div>
<div class="field control timezone">
UTC
</div>
</form>
</div>
</template>
<script>
const DATE_FORMAT = 'YYYY-MM-DD';
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
format: DATE_FORMAT,
date: '',
hour: 0,
min: 0,
sec: 0
};
},
mounted() {
this.formatDatetime();
},
methods: {
convertToDatetime(timestamp) {
const dateValue = new Date(timestamp);
const date = dateValue.toISOString().slice(0, 10);
const hour = dateValue.getUTCHours() || 0;
const min = dateValue.getUTCMinutes() || 0;
const sec = dateValue.getUTCSeconds() || 0;
return {
date,
hour,
min,
sec
};
},
convertToTimestamp() {
const date = new Date(this.date);
date.setUTCHours(this.hour || 0);
date.setUTCMinutes(this.min || 0);
date.setUTCSeconds(this.sec || 0);
return date.getTime();
},
formatDatetime(timestamp = this.model.value) {
if (!timestamp) {
this.resetValues();
return;
}
const datetime = this.convertToDatetime(timestamp);
this.setDatetime(datetime.date, datetime.hour, datetime.min, datetime.sec);
},
onChange() {
const timestamp = this.convertToTimestamp();
const model = this.model;
model.validate = () => this.validate(timestamp);
const data = {
model,
value: timestamp
};
this.$emit('onChange', data);
},
resetValues() {
this.setDatetime();
},
setDatetime(date = '', hour = 0, min = 0, sec = 0) {
this.date = date.toString();
this.hour = hour;
this.min = min;
this.sec = sec;
},
validate(timestamp) {
const valid = timestamp > 0 && this.$refs.dateTimeForm.checkValidity();
if (!valid) {
this.$refs.dateTimeForm.reportValidity();
}
return valid;
}
}
};
</script>

View File

@ -0,0 +1,100 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<span class="form-control shell">
<span class="field control"
:class="model.cssClass"
>
<input id="fileElem"
ref="fileInput"
type="file"
accept=".json"
style="display:none"
>
<button id="fileSelect"
class="c-button"
@click="selectFile"
>
{{ name }}
</button>
</span>
</span>
</template>
<script>
export default {
inject: ['openmct'],
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
fileInfo: undefined
};
},
computed: {
name() {
const fileInfo = this.fileInfo || this.model.value;
return fileInfo && fileInfo.name || this.model.text;
}
},
mounted() {
this.$refs.fileInput.addEventListener("change", this.handleFiles, false);
},
methods: {
handleFiles() {
const fileList = this.$refs.fileInput.files;
this.readFile(fileList[0]);
},
readFile(file) {
const self = this;
const fileReader = new FileReader();
const fileInfo = {};
fileInfo.name = file.name;
fileReader.onload = function (event) {
fileInfo.body = event.target.result;
self.fileInfo = fileInfo;
const data = {
model: self.model,
value: fileInfo
};
self.$emit('onChange', data);
};
fileReader.onerror = function (error) {
console.error('fileReader error', error);
};
fileReader.readAsText(file);
},
selectFile() {
this.$refs.fileInput.click();
}
}
};
</script>

View File

@ -0,0 +1,56 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<SelectorDialogTree :ignore-type-check="true"
:css-class="`form-locator c-form-control--locator`"
:parent="model.parent"
@treeItemSelected="handleItemSelection"
/>
</template>
<script>
import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
export default {
components: {
SelectorDialogTree
},
inject: ['openmct'],
props: {
model: {
type: Object,
required: true
}
},
methods: {
handleItemSelection({ parentObjectPath }) {
const data = {
model: this.model,
value: parentObjectPath
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,63 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<span class="form-control shell">
<span class="field control"
:class="model.cssClass"
>
<input v-model="field"
type="number"
:min="model.min"
:max="model.max"
:step="model.step"
@blur="blur()"
>
</span>
</span>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
field: this.model.value
};
},
methods: {
blur() {
const data = {
model: this.model,
value: this.field
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,64 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<div class="form-control select-field">
<select v-model="selected"
required="model.required"
name="mctControl"
@change="onChange($event)"
>
<option v-for="option in model.options"
:key="option.name"
:value="option.value"
>
{{ option.name }}
</option>
</select>
</div>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
selected: this.model.value
};
},
methods: {
onChange() {
const data = {
model: this.model,
value: this.selected
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<span class="form-control shell">
<span class="field control"
:class="model.cssClass"
>
<textarea v-model="field"
type="text"
:size="model.size"
@blur="blur()"
>
</textarea>
</span>
</span>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
field: this.model.value
};
},
methods: {
blur() {
const data = {
model: this.model,
value: this.field
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -0,0 +1,61 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<span class="form-control shell">
<span class="field control"
:class="model.cssClass"
>
<input v-model="field"
type="text"
:size="model.size"
@blur="blur()"
>
</span>
</span>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
field: this.model.value
};
},
methods: {
blur() {
const data = {
model: this.model,
value: this.field
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@ -20,24 +20,15 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define( import {saveAs} from 'file-saver/FileSaver';
[],
function () {
/** class JSONExporter {
* A policy for determining whether objects of a given type can be export(obj, options) {
* created. let filename = (options && options.filename) || "test-export.json";
* @constructor let jsonText = JSON.stringify(obj);
* @implements {Policy} let blob = new Blob([jsonText], {type: "application/json"});
* @memberof platform/commonUI/browse saveAs(blob, filename);
*/
function CreationPolicy() {
} }
}
CreationPolicy.prototype.allow = function (type) { export default JSONExporter;
return type.hasFeature("creation");
};
return CreationPolicy;
}
);

View File

@ -35,7 +35,6 @@ const DEFAULTS = [
'platform/containment', 'platform/containment',
'platform/exporters', 'platform/exporters',
'platform/telemetry', 'platform/telemetry',
'platform/forms',
'platform/identity', 'platform/identity',
'platform/persistence/aggregator', 'platform/persistence/aggregator',
'platform/policy', 'platform/policy',
@ -73,7 +72,6 @@ define([
'../platform/entanglement/bundle', '../platform/entanglement/bundle',
'../platform/exporters/bundle', '../platform/exporters/bundle',
'../platform/features/static-markup/bundle', '../platform/features/static-markup/bundle',
'../platform/forms/bundle',
'../platform/framework/bundle', '../platform/framework/bundle',
'../platform/framework/src/load/Bundle', '../platform/framework/src/load/Bundle',
'../platform/identity/bundle', '../platform/identity/bundle',

View File

@ -101,12 +101,12 @@
<script> <script>
import StyleEditor from "./StyleEditor.vue"; import StyleEditor from "./StyleEditor.vue";
import ConditionSetSelectorDialog from "./ConditionSetSelectorDialog.vue"; import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue"; import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
import ConditionError from "@/plugins/condition/components/ConditionError.vue"; import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import Vue from 'vue'; import Vue from 'vue';
import PreviewAction from "@/ui/preview/PreviewAction.js"; import PreviewAction from "@/ui/preview/PreviewAction.js";
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils"; import { getApplicableStylesForItem } from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
export default { export default {
@ -224,7 +224,7 @@ export default {
let conditionSetDomainObject; let conditionSetDomainObject;
let self = this; let self = this;
function handleItemSelection(item) { function handleItemSelection({ item }) {
if (item) { if (item) {
conditionSetDomainObject = item; conditionSetDomainObject = item;
} }
@ -240,16 +240,17 @@ export default {
} }
let vm = new Vue({ let vm = new Vue({
components: {ConditionSetSelectorDialog}, components: { SelectorDialogTree },
provide: { provide: {
openmct: this.openmct openmct: this.openmct
}, },
data() { data() {
return { return {
handleItemSelection handleItemSelection,
title: 'Select Condition Set'
}; };
}, },
template: '<condition-set-selector-dialog @conditionSetSelected="handleItemSelection"></condition-set-selector-dialog>' template: '<selector-dialog-tree :title="title" @treeItemSelected="handleItemSelection"></selector-dialog-tree>'
}).$mount(); }).$mount();
let overlay = this.openmct.overlays.overlay({ let overlay = this.openmct.overlays.overlay({

View File

@ -127,7 +127,7 @@ import FontStyleEditor from '@/ui/inspector/styles/FontStyleEditor.vue';
import StyleEditor from "./StyleEditor.vue"; import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js"; import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils"; import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
import ConditionSetSelectorDialog from "@/plugins/condition/components/inspector/ConditionSetSelectorDialog.vue"; import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
import ConditionError from "@/plugins/condition/components/ConditionError.vue"; import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue"; import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
import Vue from 'vue'; import Vue from 'vue';
@ -539,7 +539,7 @@ export default {
addConditionSet() { addConditionSet() {
let conditionSetDomainObject; let conditionSetDomainObject;
let self = this; let self = this;
function handleItemSelection(item) { function handleItemSelection({ item }) {
if (item) { if (item) {
conditionSetDomainObject = item; conditionSetDomainObject = item;
} }
@ -556,16 +556,17 @@ export default {
} }
let vm = new Vue({ let vm = new Vue({
components: {ConditionSetSelectorDialog}, components: { SelectorDialogTree },
provide: { provide: {
openmct: this.openmct openmct: this.openmct
}, },
data() { data() {
return { return {
handleItemSelection handleItemSelection,
title: 'Select Condition Set'
}; };
}, },
template: '<condition-set-selector-dialog @conditionSetSelected="handleItemSelection"></condition-set-selector-dialog>' template: '<SelectorDialogTree :title="title" @treeItemSelected="handleItemSelection"></SelectorDialogTree>'
}).$mount(); }).$mount();
let overlay = this.openmct.overlays.overlay({ let overlay = this.openmct.overlays.overlay({

View File

@ -42,7 +42,7 @@ define(['lodash'], function (_) {
toolbar: function (selectedObjects) { toolbar: function (selectedObjects) {
const DIALOG_FORM = { const DIALOG_FORM = {
'text': { 'text': {
name: "Text Element Properties", title: "Text Element Properties",
sections: [ sections: [
{ {
rows: [ rows: [
@ -50,14 +50,15 @@ define(['lodash'], function (_) {
key: "text", key: "text",
control: "textfield", control: "textfield",
name: "Text", name: "Text",
required: true required: true,
cssClass: "l-input-lg"
} }
] ]
} }
] ]
}, },
'image': { 'image': {
name: "Image Properties", title: "Image Properties",
sections: [ sections: [
{ {
rows: [ rows: [
@ -65,7 +66,7 @@ define(['lodash'], function (_) {
key: "url", key: "url",
control: "textfield", control: "textfield",
name: "Image URL", name: "Image URL",
"cssClass": "l-input-lg", cssClass: "l-input-lg",
required: true required: true
} }
] ]
@ -126,10 +127,6 @@ define(['lodash'], function (_) {
] ]
}; };
function getUserInput(form) {
return openmct.$injector.get('dialogService').getUserInput(form, {});
}
function getPath(selectionPath) { function getPath(selectionPath) {
return `configuration.items[${selectionPath[0].context.index}]`; return `configuration.items[${selectionPath[0].context.index}]`;
} }
@ -167,8 +164,7 @@ define(['lodash'], function (_) {
let name = option.name.toLowerCase(); let name = option.name.toLowerCase();
let form = DIALOG_FORM[name]; let form = DIALOG_FORM[name];
if (form) { if (form) {
getUserInput(form) showForm(form, name, selectionPath);
.then(element => selectionPath[0].context.addElement(name, element));
} else { } else {
selectionPath[0].context.addElement(name); selectionPath[0].context.addElement(name);
} }
@ -643,10 +639,18 @@ define(['lodash'], function (_) {
&& !selectionPath[0].context.layoutItem; && !selectionPath[0].context.layoutItem;
} }
function showForm(formStructure, name, selectionPath) {
openmct.forms.showForm(formStructure)
.then(changes => {
selectionPath[0].context.addElement(name, changes);
});
}
if (isMainLayoutSelected(selectedObjects[0])) { if (isMainLayoutSelected(selectedObjects[0])) {
return [ return [
getToggleGridButton(selectedObjects), getToggleGridButton(selectedObjects),
getAddButton(selectedObjects)]; getAddButton(selectedObjects)
];
} }
let toolbar = { let toolbar = {

View File

@ -33,96 +33,78 @@ export default class DuplicateAction {
this.openmct = openmct; this.openmct = openmct;
} }
async invoke(objectPath) { invoke(objectPath) {
let duplicationTask = new DuplicateTask(this.openmct); this.object = objectPath[0];
let originalObject = objectPath[0]; this.parent = objectPath[1];
let parent = objectPath[1];
let userInput;
try { this.showForm(this.object, this.parent);
userInput = await this.getUserInput(originalObject, parent);
} catch (error) {
// user most likely canceled
return;
} }
let newParent = userInput.location; inNavigationPath() {
let inNavigationPath = this.inNavigationPath(originalObject); return this.openmct.router.path
.some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, this.object.identifier));
// legacy check
if (this.isLegacyDomainObject(newParent)) {
newParent = await this.convertFromLegacy(newParent);
} }
// if editing, save onSave(changes) {
let inNavigationPath = this.inNavigationPath();
if (inNavigationPath && this.openmct.editor.isEditing()) { if (inNavigationPath && this.openmct.editor.isEditing()) {
this.openmct.editor.save(); this.openmct.editor.save();
} }
// duplicate let duplicationTask = new DuplicateTask(this.openmct);
let newObject = await duplicationTask.duplicate(originalObject, newParent); if (changes.name && (changes.name !== this.object.name)) {
this.updateNameCheck(newObject, userInput.name); duplicationTask.changeName(changes.name);
return;
} }
async getUserInput(originalObject, parent) { const parentDomainObjectpath = changes.location || [this.parent];
let dialogService = this.openmct.$injector.get('dialogService'); const parent = parentDomainObjectpath[0];
let dialogForm = this.getDialogForm(originalObject, parent);
let formState = {
name: originalObject.name
};
let userInput = await dialogService.getUserInput(dialogForm, formState);
return userInput; return duplicationTask.duplicate(this.object, parent);
} }
updateNameCheck(object, name) { showForm(domainObject, parentDomainObject) {
if (object.name !== name) { const formStructure = {
object.name = name; title: "Duplicate Item",
this.openmct.objects.save(object);
}
}
inNavigationPath(object) {
return this.openmct.router.path
.some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, object.identifier));
}
getDialogForm(object, parent) {
return {
name: "Duplicate Item",
sections: [ sections: [
{ {
rows: [ rows: [
{ {
key: "name", key: "name",
control: "textfield", control: "textfield",
name: "Name", name: "Title",
pattern: "\\S+", pattern: "\\S+",
required: true, required: true,
cssClass: "l-input-lg" cssClass: "l-input-lg",
value: domainObject.name
}, },
{ {
name: "Location", name: "Location",
cssClass: "grows", cssClass: "grows",
control: "locator", control: "locator",
validate: this.validate(object, parent), required: true,
parent: parentDomainObject,
validate: this.validate(parentDomainObject),
key: 'location' key: 'location'
} }
] ]
} }
] ]
}; };
this.openmct.forms.showForm(formStructure)
.then(this.onSave.bind(this));
} }
validate(object, currentParent) { validate(currentParent) {
return (parentCandidate) => { return (data) => {
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier); const parentCandidatePath = data.value;
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.getId()); const parentCandidate = parentCandidatePath[0];
let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
if (!parentCandidate || !currentParentKeystring) { let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
if (!parentCandidateKeystring || !currentParentKeystring) {
return false; return false;
} }
@ -130,24 +112,15 @@ export default class DuplicateAction {
return false; return false;
} }
return this.openmct.composition.checkPolicy( const parentCandidateComposition = parentCandidate.composition;
parentCandidate.useCapability('adapter'), if (parentCandidateComposition && parentCandidateComposition.indexOf(objectKeystring) !== -1) {
object return false;
); }
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
}; };
} }
isLegacyDomainObject(domainObject) {
return domainObject.getCapability !== undefined;
}
async convertFromLegacy(legacyDomainObject) {
let objectContext = legacyDomainObject.getCapability('context');
let domainObject = await this.openmct.objects.get(objectContext.domainObject.id);
return domainObject;
}
appliesTo(objectPath) { appliesTo(objectPath) {
let parent = objectPath[1]; let parent = objectPath[1];
let parentType = parent && this.openmct.types.get(parent.type); let parentType = parent && this.openmct.types.get(parent.type);

View File

@ -34,7 +34,6 @@ import uuid from 'uuid';
* @constructor * @constructor
*/ */
export default class DuplicateTask { export default class DuplicateTask {
constructor(openmct) { constructor(openmct) {
this.domainObject = undefined; this.domainObject = undefined;
this.parent = undefined; this.parent = undefined;
@ -43,10 +42,15 @@ export default class DuplicateTask {
this.persisted = 0; this.persisted = 0;
this.clones = []; this.clones = [];
this.idMap = {}; this.idMap = {};
this.name = undefined;
this.openmct = openmct; this.openmct = openmct;
} }
changeName(name) {
this.name = name;
}
/** /**
* Execute the duplicate/copy task with the objects provided. * Execute the duplicate/copy task with the objects provided.
* @returns {promise} Which will resolve with a clone of the object * @returns {promise} Which will resolve with a clone of the object
@ -79,11 +83,14 @@ export default class DuplicateTask {
*/ */
async buildDuplicationPlan() { async buildDuplicationPlan() {
let domainObjectClone = await this.duplicateObject(this.domainObject); let domainObjectClone = await this.duplicateObject(this.domainObject);
if (domainObjectClone !== this.domainObject) { if (domainObjectClone !== this.domainObject) {
domainObjectClone.location = this.getKeyString(this.parent); domainObjectClone.location = this.getKeyString(this.parent);
} }
if (this.name) {
domainObjectClone.name = this.name;
}
this.firstClone = domainObjectClone; this.firstClone = domainObjectClone;
return; return;

View File

@ -20,7 +20,6 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import DuplicateActionPlugin from './plugin.js'; import DuplicateActionPlugin from './plugin.js';
import DuplicateAction from './DuplicateAction.js';
import DuplicateTask from './DuplicateTask.js'; import DuplicateTask from './DuplicateTask.js';
import { import {
createOpenMct, createOpenMct,
@ -29,7 +28,6 @@ import {
} from 'utils/testing'; } from 'utils/testing';
describe("The Duplicate Action plugin", () => { describe("The Duplicate Action plugin", () => {
let openmct; let openmct;
let duplicateTask; let duplicateTask;
let childObject; let childObject;
@ -52,6 +50,7 @@ describe("The Duplicate Action plugin", () => {
} }
} }
}).folder; }).folder;
parentObject = getMockObjects({ parentObject = getMockObjects({
objectKeyStrings: ['folder'], objectKeyStrings: ['folder'],
overwrite: { overwrite: {
@ -62,6 +61,7 @@ describe("The Duplicate Action plugin", () => {
} }
} }
}).folder; }).folder;
anotherParentObject = getMockObjects({ anotherParentObject = getMockObjects({
objectKeyStrings: ['folder'], objectKeyStrings: ['folder'],
overwrite: { overwrite: {
@ -120,7 +120,6 @@ describe("The Duplicate Action plugin", () => {
}); });
describe("when moving an object to a new parent", () => { describe("when moving an object to a new parent", () => {
beforeEach(async () => { beforeEach(async () => {
duplicateTask = new DuplicateTask(openmct); duplicateTask = new DuplicateTask(openmct);
await duplicateTask.duplicate(parentObject, anotherParentObject); await duplicateTask.duplicate(parentObject, anotherParentObject);
@ -142,16 +141,14 @@ describe("The Duplicate Action plugin", () => {
}); });
describe("when a new name is provided for the duplicated object", () => { describe("when a new name is provided for the duplicated object", () => {
it("the name is updated", () => { it("the name is updated", async () => {
const NEW_NAME = 'New Name'; const NEW_NAME = 'New Name';
let childName;
duplicateTask = new DuplicateAction(openmct); duplicateTask = new DuplicateTask(openmct);
duplicateTask.updateNameCheck(parentObject, NEW_NAME); duplicateTask.changeName(NEW_NAME);
const child = await duplicateTask.duplicate(childObject, anotherParentObject);
childName = parentObject.name; expect(child.name).toEqual(NEW_NAME);
expect(childName).toEqual(NEW_NAME);
}); });
}); });

View File

@ -0,0 +1,202 @@
/*****************************************************************************
* 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.
*****************************************************************************/
import JSONExporter from '/src/exporters/JSONExporter.js';
import _ from 'lodash';
import { saveAs } from 'saveAs';
import uuid from "uuid";
export default class ExportAsJSONAction {
constructor(openmct) {
this.openmct = openmct;
this.name = 'Export as JSON';
this.key = 'export.JSON';
this.description = '';
this.cssClass = "icon-export";
this.group = "json";
this.priority = 1;
this.externalIdentifiers = [];
this.tree = {};
this.calls = 0;
this.idMap = {};
this.JSONExportService = new JSONExporter(saveAs);
}
// Public
/**
*
* @param {object} objectPath
* @returns {boolean}
*/
appliesTo(objectPath) {
let domainObject = objectPath[0];
return this._isCreatable(domainObject);
}
/**
*
* @param {object} objectpath
*/
invoke(objectpath) {
const root = objectpath[0];
this.root = JSON.parse(JSON.stringify(root));
const rootId = this._getId(this.root);
this.tree[rootId] = this.root;
this._write(this.root);
}
/**
* @private
* @param {object} domainObject
* @returns {string} A string representation of the given identifier, including namespace and key
*/
_getId(domainObject) {
return this.openmct.objects.makeKeyString(domainObject.identifier);
}
/**
* @private
* @param {object} domainObject
* @returns {boolean}
*/
_isCreatable(domainObject) {
const type = this.openmct.types.get(domainObject.type);
return type && type.definition.creatable;
}
/**
* @private
* @param {object} child
* @param {object} parent
* @returns {boolean}
*/
_isLinkedObject(child, parent) {
if (child.location !== this._getId(parent)
&& !Object.keys(this.tree).includes(child.location)
&& this._getId(child) !== this._getId(this.root)
|| this.externalIdentifiers.includes(this._getId(child))) {
return true;
}
return false;
}
/**
* @private
* @param {object} child
* @param {object} parent
* @returns {object}
*/
_rewriteLink(child, parent) {
this.externalIdentifiers.push(this._getId(child));
const index = parent.composition.findIndex(id => {
return _.isEqual(child.identifier, id);
});
const copyOfChild = JSON.parse(JSON.stringify(child));
copyOfChild.identifier.key = uuid();
const newIdString = this._getId(copyOfChild);
const parentId = this._getId(parent);
this.idMap[this._getId(child)] = newIdString;
copyOfChild.location = parentId;
parent.composition[index] = copyOfChild.identifier;
this.tree[newIdString] = copyOfChild;
this.tree[parentId].composition[index] = copyOfChild.identifier;
return copyOfChild;
}
/**
* @private
*/
_rewriteReferences() {
let treeString = JSON.stringify(this.tree);
Object.keys(this.idMap).forEach(function (oldId) {
const newId = this.idMap[oldId];
treeString = treeString.split(oldId).join(newId);
}.bind(this));
this.tree = JSON.parse(treeString);
}
/**
* @private
* @param {object} completedTree
*/
_saveAs(completedTree) {
this.JSONExportService.export(
completedTree,
{ filename: this.root.name + '.json' }
);
}
/**
* @private
* @returns {object}
*/
_wrapTree() {
return {
"openmct": this.tree,
"rootId": this._getId(this.root)
};
}
/**
* @private
* @param {object} parent
*/
_write(parent) {
this.calls++;
const composition = this.openmct.composition.get(parent);
if (composition !== undefined) {
composition.load()
.then((children) => {
children.forEach((child, index) => {
// Only export if object is creatable
if (this._isCreatable(child)) {
// Prevents infinite export of self-contained objs
if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
// If object is a link to something absent from
// tree, generate new id and treat as new object
if (this._isLinkedObject(child, parent)) {
child = this._rewriteLink(child, parent);
} else {
this.tree[this._getId(child)] = child;
}
this._write(child);
}
}
});
this.calls--;
if (this.calls === 0) {
this._rewriteReferences();
this._saveAs(this._wrapTree());
}
});
} else {
this.calls--;
if (this.calls === 0) {
this._rewriteReferences();
this._saveAs(this._wrapTree());
}
}
}
}

View File

@ -31,16 +31,16 @@ define(
describe("The export JSON action", function () { describe("The export JSON action", function () {
var context, let context;
action, let action;
exportService, let exportService;
identifierService, let identifierService;
typeService, let typeService;
openmct, let openmct;
policyService, let policyService;
mockType, let mockType;
mockObjectProvider, let mockObjectProvider;
exportedTree; let exportedTree;
beforeEach(function () { beforeEach(function () {
openmct = new MCT(); openmct = new MCT();
@ -93,7 +93,7 @@ define(
}); });
function invokeAdapter() { function invokeAdapter() {
var newStyleObject = new AdapterCapability(context.domainObject).invoke(); let newStyleObject = new AdapterCapability(context.domainObject).invoke();
return newStyleObject; return newStyleObject;
} }
@ -103,7 +103,7 @@ define(
}); });
xit("doesn't export non-creatable objects in tree", function () { xit("doesn't export non-creatable objects in tree", function () {
var nonCreatableType = { let nonCreatableType = {
hasFeature: hasFeature:
function (feature) { function (feature) {
return feature !== 'creation'; return feature !== 'creation';
@ -112,7 +112,7 @@ define(
typeService.getType.and.returnValue(nonCreatableType); typeService.getType.and.returnValue(nonCreatableType);
var parent = domainObjectFactory({ let parent = domainObjectFactory({
name: 'parent', name: 'parent',
model: { model: {
name: 'parent', name: 'parent',
@ -127,7 +127,7 @@ define(
} }
}); });
var child = { let child = {
identifier: { identifier: {
namespace: '', namespace: '',
key: 'childId' key: 'childId'
@ -150,7 +150,7 @@ define(
}); });
xit("can export self-containing objects", function () { xit("can export self-containing objects", function () {
var parent = domainObjectFactory({ let parent = domainObjectFactory({
name: 'parent', name: 'parent',
model: { model: {
name: 'parent', name: 'parent',
@ -165,7 +165,7 @@ define(
} }
}); });
var child = { let child = {
identifier: { identifier: {
namespace: '', namespace: '',
key: 'infiniteChildId' key: 'infiniteChildId'
@ -192,7 +192,7 @@ define(
}); });
xit("exports links to external objects as new objects", function () { xit("exports links to external objects as new objects", function () {
var parent = domainObjectFactory({ let parent = domainObjectFactory({
name: 'parent', name: 'parent',
model: { model: {
name: 'parent', name: 'parent',
@ -207,7 +207,7 @@ define(
} }
}); });
var externalObject = { let externalObject = {
name: 'external', name: 'external',
location: 'outsideOfTree', location: 'outsideOfTree',
identifier: { identifier: {

View File

@ -19,30 +19,10 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import ExportAsJSONAction from './ExportAsJSONAction';
define( export default function () {
['./AbstractComposeAction'], return function (openmct) {
function (AbstractComposeAction) { openmct.actions.register(new ExportAsJSONAction(openmct));
};
/** }
* The LinkAction is available from context menus and allows a user to
* link an object to another location of their choosing.
*
* @implements {Action}
* @constructor
* @memberof platform/entanglement
*/
function LinkAction(policyService, locationService, linkService, context) {
AbstractComposeAction.apply(
this,
[policyService, locationService, linkService, context, "Link"]
);
}
LinkAction.prototype = Object.create(AbstractComposeAction.prototype);
LinkAction.appliesTo = AbstractComposeAction.appliesTo;
return LinkAction;
}
);

View File

@ -0,0 +1,150 @@
/*****************************************************************************
* 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.
*****************************************************************************/
import PropertiesAction from './PropertiesAction';
import CreateWizard from './CreateWizard';
import uuid from 'uuid';
export default class CreateAction extends PropertiesAction {
constructor(openmct, type, parentDomainObject) {
super(openmct);
this.type = type;
this.parentDomainObject = parentDomainObject;
}
invoke() {
this._showCreateForm(this.type);
}
/**
* @private
*/
async _onSave(changes) {
let parentDomainObjectPath;
Object.entries(changes).forEach(([key, value]) => {
if (key === 'location') {
parentDomainObjectPath = value;
return;
}
const properties = key.split('.');
let object = this.domainObject;
const propertiesLength = properties.length;
properties.forEach((property, index) => {
const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
if (isComplexProperty && object[property] !== null) {
object = object[property];
} else {
object[property] = value;
}
});
object = value;
});
const parentDomainObject = parentDomainObjectPath[0];
this.domainObject.modified = Date.now();
this.domainObject.location = this.openmct.objects.makeKeyString(parentDomainObject.identifier);
this.domainObject.identifier.namespace = parentDomainObject.identifier.namespace;
// Show saving progress dialog
let dialog = this.openmct.overlays.progressDialog({
progressPerc: 'unknown',
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
iconClass: 'info',
title: 'Saving'
});
const success = await this.openmct.objects.save(this.domainObject);
if (success) {
const compositionCollection = await this.openmct.composition.get(parentDomainObject);
compositionCollection.add(this.domainObject);
this._navigateAndEdit(this.domainObject, parentDomainObjectPath);
this.openmct.notifications.info('Save successful');
} else {
this.openmct.notifications.error('Error saving objects');
}
dialog.dismiss();
}
/**
* @private
*/
async _navigateAndEdit(domainObject, parentDomainObjectpath) {
let objectPath;
if (parentDomainObjectpath) {
objectPath = parentDomainObjectpath && [domainObject].concat(parentDomainObjectpath);
} else {
objectPath = await this.openmct.objects.getOriginalPath(domainObject.identifier);
}
const url = '#/browse/' + objectPath
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
.reverse()
.join('/');
this.openmct.router.navigate(url);
const objectView = this.openmct.objectViews.get(domainObject, objectPath)[0];
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
if (canEdit) {
this.openmct.editor.edit();
}
}
/**
* @private
*/
_showCreateForm(type) {
const typeDefinition = this.openmct.types.get(type);
const definition = typeDefinition.definition;
const domainObject = {
name: `Unnamed ${definition.name}`,
type,
identifier: {
key: uuid(),
namespace: this.parentDomainObject.identifier.namespace
}
};
this.domainObject = domainObject;
if (definition.initialize) {
definition.initialize(domainObject);
}
const createWizard = new CreateWizard(this.openmct, domainObject, this.parentDomainObject);
const formStructure = createWizard.getFormStructure(true);
formStructure.title = 'Create a New ' + definition.name;
this.openmct.forms.showForm(formStructure)
.then(this._onSave.bind(this));
}
}

View File

@ -0,0 +1,135 @@
/*****************************************************************************
* 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.
*****************************************************************************/
export default class CreateWizard {
constructor(openmct, domainObject, parent) {
this.openmct = openmct;
this.domainObject = domainObject;
this.type = openmct.types.get(domainObject.type);
this.model = domainObject;
this.parent = parent;
this.properties = this.type.definition.form || [];
}
addNotes(sections) {
const row = {
control: 'textarea',
cssClass: 'l-input-lg',
key: 'notes',
name: 'Notes',
required: false,
value: this.domainObject.notes
};
sections.forEach(section => {
if (section.name !== 'Properties') {
return;
}
section.rows.unshift(row);
});
}
addTitle(sections) {
const row = {
control: 'textfield',
cssClass: 'l-input-lg',
key: 'name',
name: 'Title',
pattern: `\\S+`,
required: true,
value: this.domainObject.name
};
sections.forEach(section => {
if (section.name !== 'Properties') {
return;
}
section.rows.unshift(row);
});
}
/**
* Get the form model for this wizard; this is a description
* that will be rendered to an HTML form. See the
* platform/forms bundle
* @param {boolean} includeLocation if true, a 'location' section
* will be included that will allow the user to select the location
* of the newly created object, otherwise the .location property of
* the model will be used.
*/
getFormStructure(includeLocation) {
let sections = [];
let domainObject = this.domainObject;
let self = this;
sections.push({
name: 'Properties',
rows: this.properties.map(property => {
const row = JSON.parse(JSON.stringify(property));
row.value = this.getValue(row);
return row;
}).filter(row => row && row.control)
});
this.addNotes(sections);
this.addTitle(sections);
// Ensure there is always a 'save in' section
if (includeLocation) {
function validateLocation(data) {
return self.openmct.composition.checkPolicy(data.value[0], domainObject);
}
sections.push({
name: 'Location',
cssClass: 'grows',
rows: [{
name: 'Save In',
cssClass: 'grows',
control: 'locator',
domainObject,
required: true,
parent: this.parent,
validate: validateLocation.bind(this),
key: 'location'
}]
});
}
return {
sections
};
}
getValue(row) {
if (row.property) {
return row.property.reduce((acc, property) => acc && acc[property], this.domainObject);
} else {
return this.domainObject[row.key];
}
}
}

View File

@ -0,0 +1,102 @@
/*****************************************************************************
* 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.
*****************************************************************************/
import PropertiesAction from './PropertiesAction';
import CreateWizard from './CreateWizard';
export default class EditPropertiesAction extends PropertiesAction {
constructor(openmct) {
super(openmct);
this.name = 'Edit Properties...';
this.key = 'properties';
this.description = 'Edit properties of this object.';
this.cssClass = 'major icon-pencil';
this.hideInDefaultMenu = true;
this.group = 'action';
this.priority = 10;
this.formProperties = {};
}
appliesTo(objectPath) {
const definition = this._getTypeDefinition(objectPath[0].type);
return definition && definition.creatable;
}
invoke(objectPath) {
this._showEditForm(objectPath);
}
/**
* @private
*/
async _onSave(changes) {
Object.entries(changes).forEach(([key, value]) => {
const properties = key.split('.');
let object = this.domainObject;
const propertiesLength = properties.length;
properties.forEach((property, index) => {
const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
if (isComplexProperty && object[property] !== null) {
object = object[property];
} else {
object[property] = value;
}
});
object = value;
});
this.domainObject.modified = Date.now();
// Show saving progress dialog
let dialog = this.openmct.overlays.progressDialog({
progressPerc: 'unknown',
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
iconClass: 'info',
title: 'Saving'
});
const success = await this.openmct.objects.save(this.domainObject);
if (success) {
this.openmct.notifications.info('Save successful');
} else {
this.openmct.notifications.error('Error saving objects');
}
dialog.dismiss();
}
/**
* @private
*/
_showEditForm(objectPath) {
this.domainObject = objectPath[0];
const createWizard = new CreateWizard(this.openmct, this.domainObject, objectPath[1]);
const formStructure = createWizard.getFormStructure(false);
formStructure.title = 'Edit ' + this.domainObject.name;
this.openmct.forms.showForm(formStructure)
.then(this._onSave.bind(this));
}
}

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