Compare commits
1 Commits
api-tutori
...
open633
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a9c940567 |
@@ -18,8 +18,6 @@
|
||||
"node-uuid": "^1.4.7",
|
||||
"comma-separated-values": "^3.6.4",
|
||||
"FileSaver.js": "^0.0.2",
|
||||
"zepto": "^1.1.6",
|
||||
"eventemitter3": "^1.2.0",
|
||||
"lodash": "3.10.1"
|
||||
"zepto": "^1.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ deployment:
|
||||
test:
|
||||
post:
|
||||
- gulp lint
|
||||
- gulp checkstyle
|
||||
|
||||
general:
|
||||
branches:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,12 +45,11 @@ define(
|
||||
function buildTaxonomy(dictionary){
|
||||
var models = {};
|
||||
|
||||
function addMeasurement(measurement, parent){
|
||||
function addMeasurement(measurement){
|
||||
var format = FORMAT_MAPPINGS[measurement.type];
|
||||
models[makeId(measurement)] = {
|
||||
type: "msl.measurement",
|
||||
name: measurement.name,
|
||||
location: parent,
|
||||
telemetry: {
|
||||
key: measurement.identifier,
|
||||
ranges: [{
|
||||
@@ -63,24 +62,17 @@ define(
|
||||
};
|
||||
}
|
||||
|
||||
function addInstrument(subsystem, spacecraftId) {
|
||||
var measurements = (subsystem.measurements || []),
|
||||
instrumentId = makeId(subsystem);
|
||||
|
||||
models[instrumentId] = {
|
||||
function addInstrument(subsystem) {
|
||||
var measurements = (subsystem.measurements || []);
|
||||
models[makeId(subsystem)] = {
|
||||
type: "msl.instrument",
|
||||
name: subsystem.name,
|
||||
location: spacecraftId,
|
||||
composition: measurements.map(makeId)
|
||||
};
|
||||
measurements.forEach(function(measurement) {
|
||||
addMeasurement(measurement, instrumentId);
|
||||
});
|
||||
measurements.forEach(addMeasurement);
|
||||
}
|
||||
|
||||
(dictionary.instruments || []).forEach(function(instrument) {
|
||||
addInstrument(instrument, "msl:curiosity");
|
||||
});
|
||||
(dictionary.instruments || []).forEach(addInstrument);
|
||||
return models;
|
||||
}
|
||||
|
||||
|
||||
11
index.html
11
index.html
@@ -31,17 +31,10 @@
|
||||
<script type="text/javascript">
|
||||
require(['main'], function (mct) {
|
||||
require([
|
||||
'./tutorials/grootprovider/groots',
|
||||
'./tutorials/todo/todo',
|
||||
'./tutorials/todo/bundle',
|
||||
'./example/imagery/bundle',
|
||||
'./example/eventGenerator/bundle',
|
||||
'./example/generator/bundle',
|
||||
], function (grootify, todoPlugin) {
|
||||
grootify(mct);
|
||||
todoPlugin(mct);
|
||||
mct.start();
|
||||
})
|
||||
'./example/generator/bundle'
|
||||
], mct.run.bind(mct));
|
||||
});
|
||||
</script>
|
||||
<link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css">
|
||||
|
||||
25
main.js
25
main.js
@@ -28,15 +28,13 @@ requirejs.config({
|
||||
"angular-route": "bower_components/angular-route/angular-route.min",
|
||||
"csv": "bower_components/comma-separated-values/csv.min",
|
||||
"es6-promise": "bower_components/es6-promise/promise.min",
|
||||
"EventEmitter": "bower_components/eventemitter3/index",
|
||||
"moment": "bower_components/moment/moment",
|
||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||
"text": "bower_components/text/text",
|
||||
"uuid": "bower_components/node-uuid/uuid",
|
||||
"zepto": "bower_components/zepto/zepto.min",
|
||||
"lodash": "bower_components/lodash/lodash"
|
||||
"zepto": "bower_components/zepto/zepto.min"
|
||||
},
|
||||
"shim": {
|
||||
"angular": {
|
||||
@@ -45,9 +43,6 @@ requirejs.config({
|
||||
"angular-route": {
|
||||
"deps": ["angular"]
|
||||
},
|
||||
"EventEmitter": {
|
||||
"exports": "EventEmitter"
|
||||
},
|
||||
"moment-duration-format": {
|
||||
"deps": ["moment"]
|
||||
},
|
||||
@@ -63,7 +58,6 @@ requirejs.config({
|
||||
define([
|
||||
'./platform/framework/src/Main',
|
||||
'legacyRegistry',
|
||||
'./src/MCT',
|
||||
|
||||
'./platform/framework/bundle',
|
||||
'./platform/core/bundle',
|
||||
@@ -99,14 +93,11 @@ define([
|
||||
'./platform/search/bundle',
|
||||
'./platform/status/bundle',
|
||||
'./platform/commonUI/regions/bundle'
|
||||
], function (Main, legacyRegistry, MCT) {
|
||||
var mct = new MCT();
|
||||
|
||||
mct.legacyRegistry = legacyRegistry;
|
||||
mct.run = mct.start;
|
||||
mct.on('start', function () {
|
||||
return new Main().run(legacyRegistry);
|
||||
});
|
||||
|
||||
return mct;
|
||||
], function (Main, legacyRegistry) {
|
||||
return {
|
||||
legacyRegistry: legacyRegistry,
|
||||
run: function () {
|
||||
return new Main().run(legacyRegistry);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -24,14 +24,23 @@ define([
|
||||
"./src/BrowseController",
|
||||
"./src/PaneController",
|
||||
"./src/BrowseObjectController",
|
||||
"./src/creation/CreateMenuController",
|
||||
"./src/creation/LocatorController",
|
||||
"./src/MenuArrowController",
|
||||
"./src/navigation/NavigationService",
|
||||
"./src/creation/CreationPolicy",
|
||||
"./src/navigation/NavigateAction",
|
||||
"./src/windowing/NewTabAction",
|
||||
"./src/windowing/FullscreenAction",
|
||||
"./src/creation/CreateActionProvider",
|
||||
"./src/creation/AddActionProvider",
|
||||
"./src/creation/CreationService",
|
||||
"./src/windowing/WindowTitler",
|
||||
"text!./res/templates/browse.html",
|
||||
"text!./res/templates/create/locator.html",
|
||||
"text!./res/templates/browse-object.html",
|
||||
"text!./res/templates/create/create-button.html",
|
||||
"text!./res/templates/create/create-menu.html",
|
||||
"text!./res/templates/items/grid-item.html",
|
||||
"text!./res/templates/browse/object-header.html",
|
||||
"text!./res/templates/menu-arrow.html",
|
||||
@@ -44,14 +53,23 @@ define([
|
||||
BrowseController,
|
||||
PaneController,
|
||||
BrowseObjectController,
|
||||
CreateMenuController,
|
||||
LocatorController,
|
||||
MenuArrowController,
|
||||
NavigationService,
|
||||
CreationPolicy,
|
||||
NavigateAction,
|
||||
NewTabAction,
|
||||
FullscreenAction,
|
||||
CreateActionProvider,
|
||||
AddActionProvider,
|
||||
CreationService,
|
||||
WindowTitler,
|
||||
browseTemplate,
|
||||
locatorTemplate,
|
||||
browseObjectTemplate,
|
||||
createButtonTemplate,
|
||||
createMenuTemplate,
|
||||
gridItemTemplate,
|
||||
objectHeaderTemplate,
|
||||
menuArrowTemplate,
|
||||
@@ -118,6 +136,22 @@ define([
|
||||
"$route"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreateMenuController",
|
||||
"implementation": CreateMenuController,
|
||||
"depends": [
|
||||
"$scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "LocatorController",
|
||||
"implementation": LocatorController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$timeout",
|
||||
"objectService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "MenuArrowController",
|
||||
"implementation": MenuArrowController,
|
||||
@@ -126,6 +160,12 @@ define([
|
||||
]
|
||||
}
|
||||
],
|
||||
"controls": [
|
||||
{
|
||||
"key": "locator",
|
||||
"template": locatorTemplate
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
{
|
||||
"key": "view-object",
|
||||
@@ -141,6 +181,17 @@ define([
|
||||
"view"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "create-button",
|
||||
"template": createButtonTemplate
|
||||
},
|
||||
{
|
||||
"key": "create-menu",
|
||||
"template": createMenuTemplate,
|
||||
"uses": [
|
||||
"action"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "grid-item",
|
||||
"template": gridItemTemplate,
|
||||
@@ -193,6 +244,12 @@ define([
|
||||
"implementation": NavigationService
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
{
|
||||
"implementation": CreationPolicy,
|
||||
"category": "creation"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "navigate",
|
||||
@@ -245,6 +302,42 @@ define([
|
||||
"editable": false
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"key": "CreateActionProvider",
|
||||
"provides": "actionService",
|
||||
"type": "provider",
|
||||
"implementation": CreateActionProvider,
|
||||
"depends": [
|
||||
"$q",
|
||||
"typeService",
|
||||
"navigationService",
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "AddActionProvider",
|
||||
"provides": "actionService",
|
||||
"type": "provider",
|
||||
"implementation": AddActionProvider,
|
||||
"depends": [
|
||||
"$q",
|
||||
"typeService",
|
||||
"dialogService",
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreationService",
|
||||
"provides": "creationService",
|
||||
"type": "provider",
|
||||
"implementation": CreationService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$log"
|
||||
]
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"implementation": WindowTitler,
|
||||
|
||||
@@ -43,8 +43,11 @@ define(
|
||||
* override this)
|
||||
* @param {ActionContext} context the context in which the
|
||||
* action is being performed
|
||||
* @param {NavigationService} navigationService the navigation service,
|
||||
* which handles changes in navigation. It allows the object
|
||||
* being browsed/edited to be set.
|
||||
*/
|
||||
function CreateAction(type, parent, context) {
|
||||
function CreateAction(type, parent, context, $q, navigationService) {
|
||||
this.metadata = {
|
||||
key: 'create',
|
||||
glyph: type.getGlyph(),
|
||||
@@ -53,8 +56,24 @@ define(
|
||||
description: type.getDescription(),
|
||||
context: context
|
||||
};
|
||||
|
||||
this.type = type;
|
||||
this.parent = parent;
|
||||
this.navigationService = navigationService;
|
||||
this.$q = $q;
|
||||
}
|
||||
|
||||
// Get a count of views which are not flagged as non-editable.
|
||||
function countEditableViews(domainObject) {
|
||||
var views = domainObject && domainObject.useCapability('view'),
|
||||
count = 0;
|
||||
|
||||
// A view is editable unless explicitly flagged as not
|
||||
(views || []).forEach(function (view) {
|
||||
count += (view.editable !== false) ? 1 : 0;
|
||||
});
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,31 +82,26 @@ define(
|
||||
*/
|
||||
CreateAction.prototype.perform = function () {
|
||||
var newModel = this.type.getInitialModel(),
|
||||
newObject,
|
||||
editAction,
|
||||
editorCapability;
|
||||
|
||||
function onSave() {
|
||||
return editorCapability.save();
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
return editorCapability.cancel();
|
||||
}
|
||||
parentObject = this.navigationService.getNavigation(),
|
||||
editorCapability,
|
||||
newObject;
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
newModel.location = this.parent.getId();
|
||||
newObject = this.parent.useCapability('instantiation', newModel);
|
||||
editorCapability = newObject.hasCapability('editor') && newObject.getCapability("editor");
|
||||
newModel.location = parentObject.getId();
|
||||
newObject = parentObject.useCapability('instantiation', newModel);
|
||||
|
||||
editAction = newObject.getCapability("action").getActions("edit")[0];
|
||||
//If an edit action is available, perform it
|
||||
if (editAction) {
|
||||
return editAction.perform();
|
||||
} else if (editorCapability) {
|
||||
//otherwise, use the save action
|
||||
editorCapability = newObject.getCapability("editor");
|
||||
|
||||
if (countEditableViews(newObject) > 0 && newObject.hasCapability('composition')) {
|
||||
this.navigationService.setNavigation(newObject);
|
||||
return newObject.getCapability("action").perform("edit");
|
||||
} else {
|
||||
editorCapability.edit();
|
||||
return newObject.getCapability("action").perform("save").then(onSave, onCancel);
|
||||
return newObject.useCapability("action").perform("save").then(function () {
|
||||
return editorCapability.save();
|
||||
}, function () {
|
||||
return editorCapability.cancel();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,8 +44,10 @@ define(
|
||||
* introduced in this bundle), responsible for handling actual
|
||||
* object creation.
|
||||
*/
|
||||
function CreateActionProvider(typeService, policyService) {
|
||||
function CreateActionProvider($q, typeService, navigationService, policyService) {
|
||||
this.typeService = typeService;
|
||||
this.navigationService = navigationService;
|
||||
this.$q = $q;
|
||||
this.policyService = policyService;
|
||||
}
|
||||
|
||||
@@ -70,7 +72,9 @@ define(
|
||||
return new CreateAction(
|
||||
type,
|
||||
destination,
|
||||
context
|
||||
context,
|
||||
self.$q,
|
||||
self.navigationService
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -29,10 +29,13 @@ define(
|
||||
|
||||
describe("The create action provider", function () {
|
||||
var mockTypeService,
|
||||
mockDialogService,
|
||||
mockNavigationService,
|
||||
mockPolicyService,
|
||||
mockCreationPolicy,
|
||||
mockPolicyMap = {},
|
||||
mockTypes,
|
||||
mockQ,
|
||||
provider;
|
||||
|
||||
function createMockType(name) {
|
||||
@@ -58,6 +61,14 @@ define(
|
||||
"typeService",
|
||||
["listTypes"]
|
||||
);
|
||||
mockDialogService = jasmine.createSpyObj(
|
||||
"dialogService",
|
||||
["getUserInput"]
|
||||
);
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
["setNavigation"]
|
||||
);
|
||||
mockPolicyService = jasmine.createSpyObj(
|
||||
"policyService",
|
||||
["allow"]
|
||||
@@ -80,7 +91,9 @@ define(
|
||||
mockTypeService.listTypes.andReturn(mockTypes);
|
||||
|
||||
provider = new CreateActionProvider(
|
||||
mockQ,
|
||||
mockTypeService,
|
||||
mockNavigationService,
|
||||
mockPolicyService
|
||||
);
|
||||
});
|
||||
130
platform/commonUI/browse/test/creation/CreateActionSpec.js
Normal file
130
platform/commonUI/browse/test/creation/CreateActionSpec.js
Normal file
@@ -0,0 +1,130 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/creation/CreateAction"],
|
||||
function (CreateAction) {
|
||||
|
||||
describe("The create action", function () {
|
||||
var mockType,
|
||||
mockParent,
|
||||
mockContext,
|
||||
mockDialogService,
|
||||
mockCreationService,
|
||||
action;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
[
|
||||
"getKey",
|
||||
"getGlyph",
|
||||
"getName",
|
||||
"getDescription",
|
||||
"getProperties",
|
||||
"getInitialModel"
|
||||
]
|
||||
);
|
||||
mockParent = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[
|
||||
"getId",
|
||||
"getModel",
|
||||
"getCapability"
|
||||
]
|
||||
);
|
||||
mockContext = {
|
||||
domainObject: mockParent
|
||||
};
|
||||
mockDialogService = jasmine.createSpyObj(
|
||||
"dialogService",
|
||||
["getUserInput"]
|
||||
);
|
||||
mockCreationService = jasmine.createSpyObj(
|
||||
"creationService",
|
||||
["createObject"]
|
||||
);
|
||||
|
||||
mockType.getKey.andReturn("test");
|
||||
mockType.getGlyph.andReturn("T");
|
||||
mockType.getDescription.andReturn("a test type");
|
||||
mockType.getName.andReturn("Test");
|
||||
mockType.getProperties.andReturn([]);
|
||||
mockType.getInitialModel.andReturn({});
|
||||
|
||||
mockDialogService.getUserInput.andReturn(mockPromise({}));
|
||||
|
||||
action = new CreateAction(
|
||||
mockType,
|
||||
mockParent,
|
||||
mockContext,
|
||||
mockDialogService,
|
||||
mockCreationService
|
||||
);
|
||||
});
|
||||
|
||||
it("exposes type-appropriate metadata", function () {
|
||||
var metadata = action.getMetadata();
|
||||
|
||||
expect(metadata.name).toEqual("Test");
|
||||
expect(metadata.description).toEqual("a test type");
|
||||
expect(metadata.glyph).toEqual("T");
|
||||
});
|
||||
|
||||
//TODO: Disabled for NEM Beta
|
||||
xit("invokes the creation service when performed", function () {
|
||||
action.perform();
|
||||
expect(mockCreationService.createObject).toHaveBeenCalledWith(
|
||||
{ type: "test" },
|
||||
mockParent
|
||||
);
|
||||
});
|
||||
|
||||
//TODO: Disabled for NEM Beta
|
||||
xit("does not create an object if the user cancels", function () {
|
||||
mockDialogService.getUserInput.andReturn({
|
||||
then: function (callback, fail) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
action.perform();
|
||||
|
||||
expect(mockCreationService.createObject)
|
||||
.not.toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -43,15 +43,6 @@ define([
|
||||
"./src/capabilities/EditorCapability",
|
||||
"./src/capabilities/TransactionCapabilityDecorator",
|
||||
"./src/services/TransactionService",
|
||||
"./src/creation/CreateMenuController",
|
||||
"./src/creation/LocatorController",
|
||||
"./src/creation/CreationPolicy",
|
||||
"./src/creation/CreateActionProvider",
|
||||
"./src/creation/AddActionProvider",
|
||||
"./src/creation/CreationService",
|
||||
"text!./res/templates/create/locator.html",
|
||||
"text!./res/templates/create/create-button.html",
|
||||
"text!./res/templates/create/create-menu.html",
|
||||
"text!./res/templates/library.html",
|
||||
"text!./res/templates/edit-object.html",
|
||||
"text!./res/templates/edit-action-buttons.html",
|
||||
@@ -81,15 +72,6 @@ define([
|
||||
EditorCapability,
|
||||
TransactionCapabilityDecorator,
|
||||
TransactionService,
|
||||
CreateMenuController,
|
||||
LocatorController,
|
||||
CreationPolicy,
|
||||
CreateActionProvider,
|
||||
AddActionProvider,
|
||||
CreationService,
|
||||
locatorTemplate,
|
||||
createButtonTemplate,
|
||||
createMenuTemplate,
|
||||
libraryTemplate,
|
||||
editObjectTemplate,
|
||||
editActionButtonsTemplate,
|
||||
@@ -130,22 +112,6 @@ define([
|
||||
"$location",
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreateMenuController",
|
||||
"implementation": CreateMenuController,
|
||||
"depends": [
|
||||
"$scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "LocatorController",
|
||||
"implementation": LocatorController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$timeout",
|
||||
"objectService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
@@ -253,13 +219,10 @@ define([
|
||||
},
|
||||
{
|
||||
"category": "navigation",
|
||||
"message": "Continuing will cause the loss of any unsaved changes.",
|
||||
"message": "There are unsaved changes.",
|
||||
"implementation": EditNavigationPolicy
|
||||
},
|
||||
{
|
||||
"implementation": CreationPolicy,
|
||||
"category": "creation"
|
||||
}
|
||||
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
@@ -298,17 +261,6 @@ define([
|
||||
{
|
||||
"key": "topbar-edit",
|
||||
"template": topbarEditTemplate
|
||||
},
|
||||
{
|
||||
"key": "create-button",
|
||||
"template": createButtonTemplate
|
||||
},
|
||||
{
|
||||
"key": "create-menu",
|
||||
"template": createMenuTemplate,
|
||||
"uses": [
|
||||
"action"
|
||||
]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
@@ -330,40 +282,7 @@ define([
|
||||
"$q",
|
||||
"$log"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreateActionProvider",
|
||||
"provides": "actionService",
|
||||
"type": "provider",
|
||||
"implementation": CreateActionProvider,
|
||||
"depends": [
|
||||
"typeService",
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "AddActionProvider",
|
||||
"provides": "actionService",
|
||||
"type": "provider",
|
||||
"implementation": AddActionProvider,
|
||||
"depends": [
|
||||
"$q",
|
||||
"typeService",
|
||||
"dialogService",
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreationService",
|
||||
"provides": "creationService",
|
||||
"type": "provider",
|
||||
"implementation": CreationService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$log"
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
"representers": [
|
||||
{
|
||||
@@ -397,12 +316,6 @@ define([
|
||||
"transactionService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"controls": [
|
||||
{
|
||||
"key": "locator",
|
||||
"template": locatorTemplate
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -74,12 +74,6 @@ define(
|
||||
self.domainObject.getCapability('editor').cancel();
|
||||
self.navigationService.removeListener(cancelEditing);
|
||||
}
|
||||
//If this is not the currently navigated object, then navigate
|
||||
// to it.
|
||||
if (this.navigationService.getNavigation() !== this.domainObject) {
|
||||
this.navigationService.setNavigation(this.domainObject);
|
||||
}
|
||||
|
||||
this.navigationService.addListener(cancelEditing);
|
||||
this.domainObject.useCapability("editor");
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
define(
|
||||
['../creation/CreateWizard'],
|
||||
['../../../browse/src/creation/CreateWizard'],
|
||||
function (CreateWizard) {
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,10 +56,7 @@ define(
|
||||
// A view is editable unless explicitly flagged as not
|
||||
(views || []).forEach(function (view) {
|
||||
if (view.editable === true ||
|
||||
(view.key === 'plot' && type.getKey() === 'telemetry.panel') ||
|
||||
(view.key === 'table' && type.getKey() === 'table') ||
|
||||
(view.key === 'rt-table' && type.getKey() === 'rttable')
|
||||
) {
|
||||
(view.key === 'plot' && type.getKey() === 'telemetry.panel')) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/creation/CreateAction"],
|
||||
function (CreateAction) {
|
||||
|
||||
describe("The create action", function () {
|
||||
var mockType,
|
||||
mockParent,
|
||||
mockContext,
|
||||
mockDomainObject,
|
||||
capabilities = {},
|
||||
mockEditAction,
|
||||
mockSaveAction,
|
||||
action;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
[
|
||||
"getKey",
|
||||
"getGlyph",
|
||||
"getName",
|
||||
"getDescription",
|
||||
"getProperties",
|
||||
"getInitialModel"
|
||||
]
|
||||
);
|
||||
mockParent = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[
|
||||
"getId",
|
||||
"getModel",
|
||||
"getCapability",
|
||||
"useCapability"
|
||||
]
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[
|
||||
"getId",
|
||||
"getModel",
|
||||
"getCapability",
|
||||
"hasCapability",
|
||||
"useCapability"
|
||||
]
|
||||
);
|
||||
mockDomainObject.hasCapability.andCallFake(function (name) {
|
||||
return !!capabilities[name];
|
||||
});
|
||||
mockDomainObject.getCapability.andCallFake(function (name) {
|
||||
return capabilities[name];
|
||||
});
|
||||
mockSaveAction = jasmine.createSpyObj(
|
||||
"saveAction",
|
||||
[
|
||||
"perform"
|
||||
]
|
||||
);
|
||||
|
||||
capabilities.action = jasmine.createSpyObj(
|
||||
"actionCapability",
|
||||
[
|
||||
"getActions",
|
||||
"perform"
|
||||
]
|
||||
);
|
||||
|
||||
capabilities.editor = jasmine.createSpyObj(
|
||||
"editorCapability",
|
||||
[
|
||||
"edit",
|
||||
"save",
|
||||
"cancel"
|
||||
]
|
||||
);
|
||||
|
||||
mockEditAction = jasmine.createSpyObj(
|
||||
"editAction",
|
||||
[
|
||||
"perform"
|
||||
]
|
||||
);
|
||||
|
||||
mockContext = {
|
||||
domainObject: mockParent
|
||||
};
|
||||
mockParent.useCapability.andReturn(mockDomainObject);
|
||||
|
||||
mockType.getKey.andReturn("test");
|
||||
mockType.getGlyph.andReturn("T");
|
||||
mockType.getDescription.andReturn("a test type");
|
||||
mockType.getName.andReturn("Test");
|
||||
mockType.getProperties.andReturn([]);
|
||||
mockType.getInitialModel.andReturn({});
|
||||
|
||||
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.glyph).toEqual("T");
|
||||
});
|
||||
|
||||
describe("the perform function", function () {
|
||||
beforeEach(function () {
|
||||
capabilities.action.getActions.andReturn([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 action if object does not have an edit action" +
|
||||
" available", function () {
|
||||
capabilities.action.getActions.andReturn([]);
|
||||
capabilities.action.perform.andReturn(mockPromise(undefined));
|
||||
action.perform();
|
||||
expect(capabilities.action.perform).toHaveBeenCalledWith("save");
|
||||
});
|
||||
|
||||
describe("uses to editor capability", function () {
|
||||
var promise = jasmine.createSpyObj("promise", ["then"]);
|
||||
beforeEach(function () {
|
||||
capabilities.action.getActions.andReturn([]);
|
||||
capabilities.action.perform.andReturn(promise);
|
||||
});
|
||||
|
||||
it("to save the edit if user saves dialog", function () {
|
||||
action.perform();
|
||||
expect(promise.then).toHaveBeenCalled();
|
||||
promise.then.mostRecentCall.args[0]();
|
||||
expect(capabilities.editor.save).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("to cancel the edit if user cancels dialog", function () {
|
||||
action.perform();
|
||||
promise.then.mostRecentCall.args[1]();
|
||||
expect(capabilities.editor.cancel).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -145,8 +145,3 @@
|
||||
.flex-justify-end {
|
||||
@include justify-content(flex-end);
|
||||
}
|
||||
|
||||
/********************************************* POPUPS */
|
||||
.t-popup {
|
||||
z-index: 75;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ $uePaneMiniTabW: 10px;
|
||||
$uePaneMiniTabCollapsedW: 11px;
|
||||
$ueEditLeftPaneW: 75%;
|
||||
$treeSearchInputBarH: 25px;
|
||||
$ueTimeControlH: (33px, 18px, 20px);
|
||||
$ueTimeControlH: (33px, 20px, 20px);
|
||||
// Panes
|
||||
$ueBrowseLeftPaneTreeMinW: 150px;
|
||||
$ueBrowseLeftPaneTreeMaxW: 35%;
|
||||
|
||||
@@ -63,10 +63,9 @@ input, textarea {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="search"] {
|
||||
input[type="text"] {
|
||||
vertical-align: baseline;
|
||||
padding: 3px 5px;
|
||||
padding: 3px 5px !important;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
|
||||
@@ -139,17 +139,6 @@
|
||||
background-size: $d $d;
|
||||
}
|
||||
|
||||
@mixin bgStripes($c: yellow, $a: 0.1, $bgsize: 5px, $angle: 90deg) {
|
||||
@include background-image(linear-gradient($angle,
|
||||
rgba($c, $a) 25%, transparent 25%,
|
||||
transparent 50%, rgba($c, $a) 50%,
|
||||
rgba($c, $a) 75%, transparent 75%,
|
||||
transparent 100%
|
||||
));
|
||||
background-repeat: repeat;
|
||||
background-size: $bgsize $bgsize;
|
||||
}
|
||||
|
||||
@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) {
|
||||
@include background-image(linear-gradient(-90deg,
|
||||
rgba($c, $a) 0%, rgba($c, $a) 50%,
|
||||
@@ -333,13 +322,13 @@
|
||||
color: $fg;
|
||||
outline: none;
|
||||
&.error {
|
||||
background-color: $colorFormFieldErrorBg;
|
||||
color: $colorFormFieldErrorFg;
|
||||
background: rgba(red, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg) {
|
||||
@include input-base($bg, $fg);
|
||||
padding: 0 $interiorMarginSm;
|
||||
}
|
||||
|
||||
@mixin contextArrow() {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
.accordion-head {
|
||||
$op: 0.2;
|
||||
border-radius: $basicCr * 0.75;
|
||||
box-sizing: border-box;
|
||||
box-sizing: "border-box";
|
||||
background: rgba($colorBodyFg, $op);
|
||||
cursor: pointer;
|
||||
font-size: 0.75em;
|
||||
@@ -396,11 +396,11 @@ input[type="search"] {
|
||||
left: auto;
|
||||
}
|
||||
.knob-l {
|
||||
@include border-left-radius($sliderKnobR);
|
||||
@include border-left-radius($sliderKnobW);
|
||||
cursor: w-resize;
|
||||
}
|
||||
.knob-r {
|
||||
@include border-right-radius($sliderKnobR);
|
||||
@include border-right-radius($sliderKnobW);
|
||||
cursor: e-resize;
|
||||
}
|
||||
.range {
|
||||
@@ -426,6 +426,7 @@ input[type="search"] {
|
||||
@include user-select(none);
|
||||
font-size: 0.8rem;
|
||||
padding: $interiorMarginLg !important;
|
||||
width: 230px;
|
||||
.l-month-year-pager {
|
||||
$pagerW: 20px;
|
||||
height: $r1H;
|
||||
@@ -517,19 +518,6 @@ input[type="search"] {
|
||||
}
|
||||
}
|
||||
|
||||
@include phone {
|
||||
.l-datetime-picker {
|
||||
padding: $interiorMargin !important;
|
||||
}
|
||||
.l-calendar {
|
||||
ul.l-cal-row {
|
||||
li {
|
||||
padding: 2px $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** TEXTAREA */
|
||||
textarea {
|
||||
@include nice-textarea($colorInputBg, $colorInputFg);
|
||||
|
||||
@@ -10,24 +10,25 @@
|
||||
$knobHOffset: 0px;
|
||||
$knobM: ($sliderKnobW + $knobHOffset) * -1;
|
||||
$rangeValPad: $interiorMargin;
|
||||
$rangeValOffset: $sliderKnobW + $interiorMargin;
|
||||
$timeRangeSliderLROffset: 150px + ($sliderKnobW * 2);
|
||||
$r1H: nth($ueTimeControlH,1); // Not currently used
|
||||
$rangeValOffset: $sliderKnobW;
|
||||
$timeRangeSliderLROffset: 130px + $sliderKnobW + $rangeValOffset;
|
||||
$r1H: nth($ueTimeControlH,1);
|
||||
$r2H: nth($ueTimeControlH,2);
|
||||
$r3H: nth($ueTimeControlH,3);
|
||||
|
||||
display: block;
|
||||
height: $r1H + $r2H + $r3H + ($interiorMargin * 2);
|
||||
min-width: $minW;
|
||||
font-size: 0.8rem;
|
||||
|
||||
|
||||
.l-time-range-inputs-holder,
|
||||
.l-time-range-slider-holder,
|
||||
.l-time-range-ticks-holder
|
||||
{
|
||||
@include absPosDefault(0, visible);
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
top: auto;
|
||||
}
|
||||
.l-time-range-slider,
|
||||
.l-time-range-ticks {
|
||||
@@ -36,21 +37,14 @@
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
height: $r1H; bottom: $r2H + $r3H + ($interiorMarginSm * 2);
|
||||
padding-top: $interiorMargin;
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
padding-top: $interiorMargin;
|
||||
&.l-flex-row,
|
||||
.l-flex-row {
|
||||
@include align-items(center);
|
||||
.flex-elem {
|
||||
height: auto;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
.type-icon {
|
||||
font-size: 120%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.l-time-range-input-w,
|
||||
.l-time-range-input,
|
||||
.l-time-range-inputs-elem {
|
||||
margin-right: $interiorMargin;
|
||||
.lbl {
|
||||
@@ -58,27 +52,13 @@
|
||||
}
|
||||
.ui-symbol.icon {
|
||||
font-size: 11px;
|
||||
width: 11px;
|
||||
}
|
||||
}
|
||||
.l-time-range-input-w {
|
||||
// Wraps a datetime text input field
|
||||
position: relative;
|
||||
input[type="text"] {
|
||||
width: 200px;
|
||||
&.picker-icon {
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
.icon-calendar {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-time-range-slider-holder {
|
||||
height: $r2H;
|
||||
height: $r2H; bottom: $r3H + ($interiorMarginSm * 1);
|
||||
.range-holder {
|
||||
box-shadow: none;
|
||||
background: none;
|
||||
@@ -93,13 +73,24 @@
|
||||
width: $myW;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: $myC;
|
||||
content: "";
|
||||
position: absolute;
|
||||
}
|
||||
&:before {
|
||||
// Vert line
|
||||
background-color: $myC;
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
|
||||
width: 1px;
|
||||
width: 2px;
|
||||
}
|
||||
&:after {
|
||||
// Circle element
|
||||
border-radius: $myW;
|
||||
@include transform(translateY(-50%));
|
||||
top: 50%; right: 0; bottom: auto; left: 0;
|
||||
width: auto;
|
||||
height: $myW;
|
||||
}
|
||||
}
|
||||
&:hover .toi-line {
|
||||
@@ -135,9 +126,9 @@
|
||||
@include webkitProp(transform, translateX(-50%));
|
||||
color: $colorPlotLabelFg;
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
font-size: 0.9em;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
top: 8px;
|
||||
white-space: nowrap;
|
||||
z-index: 2;
|
||||
}
|
||||
@@ -147,29 +138,16 @@
|
||||
|
||||
.knob {
|
||||
z-index: 2;
|
||||
&:before {
|
||||
$mTB: 2px;
|
||||
$grippyW: 3px;
|
||||
$mLR: ($sliderKnobW - $grippyW)/2;
|
||||
@include bgStripes($c: pullForward($sliderColorKnob, 20%), $a: 1, $bgsize: 4px, $angle: 0deg);
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: $mTB; right: $mLR; bottom: $mTB; left: $mLR;
|
||||
}
|
||||
.range-value {
|
||||
@include trans-prop-nice-fade(.25s);
|
||||
font-size: 0.7rem;
|
||||
padding: 0 $rangeValOffset;
|
||||
position: absolute;
|
||||
height: $r2H;
|
||||
line-height: $r2H;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
.range-value {
|
||||
color: $sliderColorKnobHov;
|
||||
}
|
||||
&:hover .range-value {
|
||||
color: $sliderColorKnobHov;
|
||||
}
|
||||
&.knob-l {
|
||||
margin-left: $knobM;
|
||||
@@ -192,7 +170,7 @@
|
||||
.l-time-domain-selector {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: $interiorMargin;
|
||||
bottom: 46px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -203,64 +181,174 @@
|
||||
padding: 1px 1px 0 $interiorMargin;
|
||||
}
|
||||
|
||||
/******************************************************************** MOBILE */
|
||||
|
||||
@include phoneandtablet {
|
||||
.l-time-controller {
|
||||
min-width: 0;
|
||||
.l-time-range-slider-holder,
|
||||
.l-time-range-ticks-holder {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.l-time-controller, .l-time-range-inputs-holder {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.l-time-controller {
|
||||
|
||||
.l-time-domain-selector {
|
||||
select {
|
||||
height: 25px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.l-time-range-slider-holder, .l-time-range-ticks-holder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.time-range-start, .time-range-end, {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
.l-time-range-input {
|
||||
display: block;
|
||||
.s-btn {
|
||||
padding-right: 18px;
|
||||
white-space: nowrap;
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.l-time-range-inputs-elem {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include phone {
|
||||
.l-time-controller {
|
||||
.l-time-range-inputs-holder {
|
||||
&.l-flex-row,
|
||||
.l-flex-row {
|
||||
@include align-items(flex-start);
|
||||
}
|
||||
.l-time-range-inputs-elem {
|
||||
&.type-icon {
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
.t-inputs-w {
|
||||
@include flex-direction(column);
|
||||
.l-time-range-input-w:not(:first-child) {
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
margin-right: 0;
|
||||
}
|
||||
.l-time-range-inputs-elem {
|
||||
&.lbl { display: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.l-time-controller {
|
||||
height: 48px;
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
bottom: 24px;
|
||||
}
|
||||
|
||||
.l-time-domain-selector {
|
||||
width: 33%;
|
||||
bottom: -9px;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
.l-time-range-input {
|
||||
margin-bottom: 5px;
|
||||
.s-btn {
|
||||
width: 66%;
|
||||
}
|
||||
}
|
||||
.l-time-range-inputs-elem {
|
||||
&.ui-symbol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.lbl {
|
||||
width: 33%;
|
||||
right: 0px;
|
||||
top: 5px;
|
||||
display: block;
|
||||
height: 25px;
|
||||
margin: 0;
|
||||
line-height: 25px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include phonePortrait {
|
||||
.l-time-controller {
|
||||
.l-time-range-inputs-holder {
|
||||
.t-inputs-w {
|
||||
@include flex(1 1 auto);
|
||||
padding-top: 25px; // Make room for the ever lovin' Time Domain Selector
|
||||
.flex-elem {
|
||||
@include flex(1 1 auto);
|
||||
width: 100%;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.l-time-domain-selector {
|
||||
right: auto;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
@include tablet {
|
||||
.l-time-controller {
|
||||
height: 17px;
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
bottom: -7px;
|
||||
left: -5px;
|
||||
}
|
||||
|
||||
.l-time-domain-selector {
|
||||
width: 23%;
|
||||
right: -4px;
|
||||
bottom: -10px;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
.l-time-range-input {
|
||||
float: left;
|
||||
.s-btn {
|
||||
width: 100%;
|
||||
padding-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include tabletLandscape {
|
||||
.l-time-controller {
|
||||
height: 17px;
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
bottom: -7px;
|
||||
}
|
||||
|
||||
.l-time-domain-selector {
|
||||
width: 23%;
|
||||
right: auto;
|
||||
bottom: -10px;
|
||||
left: 391px;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
.l-time-range-inputs-elem {
|
||||
&.ui-symbol, &.lbl {
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pane-tree-hidden .l-time-controller {
|
||||
.l-time-domain-selector {
|
||||
left: 667px;
|
||||
}
|
||||
.l-time-range-inputs-holder {
|
||||
padding-left: 277px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include tabletPortrait {
|
||||
.l-time-controller {
|
||||
height: 17px;
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
bottom: -7px;
|
||||
left: -5px;
|
||||
}
|
||||
|
||||
.l-time-domain-selector {
|
||||
width: 23%;
|
||||
right: -4px;
|
||||
bottom: -10px;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
.l-time-range-input {
|
||||
width: 38%;
|
||||
float: left;
|
||||
}
|
||||
.l-time-range-inputs-elem {
|
||||
&.ui-symbol, &.lbl {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
||||
.holder.holder-treeview-elements {
|
||||
top: $bodyMargin;
|
||||
right: 0;
|
||||
bottom: $interiorMargin;
|
||||
bottom: $bodyMargin;
|
||||
left: $bodyMargin;
|
||||
.create-btn-holder {
|
||||
&.s-status-editing {
|
||||
@@ -215,17 +215,17 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
||||
left: 0;
|
||||
.holder-object {
|
||||
top: $bodyMargin;
|
||||
bottom: $interiorMargin;
|
||||
bottom: $bodyMargin;
|
||||
}
|
||||
.holder-inspector {
|
||||
top: $bodyMargin;
|
||||
bottom: $interiorMargin;
|
||||
bottom: $bodyMargin;
|
||||
left: $bodyMargin;
|
||||
right: $bodyMargin;
|
||||
}
|
||||
.holder-elements {
|
||||
top: 0;
|
||||
bottom: $interiorMargin;
|
||||
bottom: $bodyMargin;
|
||||
left: $bodyMargin;
|
||||
right: $bodyMargin;
|
||||
}
|
||||
|
||||
@@ -19,17 +19,18 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="DateTimeFieldController">
|
||||
<span class="s-btn"
|
||||
ng-controller="DateTimeFieldController">
|
||||
<input type="text"
|
||||
ng-model="textValue"
|
||||
ng-blur="restoreTextValue(); ngBlur()"
|
||||
ng-class="{
|
||||
error: textInvalid ||
|
||||
(structure.validate &&
|
||||
!structure.validate(ngModel[field])),
|
||||
'picker-icon': structure.format === 'utc' || !structure.format
|
||||
!structure.validate(ngModel[field]))
|
||||
}">
|
||||
</input><a class="ui-symbol icon icon-calendar"
|
||||
</input>
|
||||
<a class="ui-symbol icon icon-calendar"
|
||||
ng-if="structure.format === 'utc' || !structure.format"
|
||||
ng-click="picker.active = !picker.active">
|
||||
</a>
|
||||
@@ -37,7 +38,8 @@
|
||||
<div mct-click-elsewhere="picker.active = false">
|
||||
<mct-control key="'datetime-picker'"
|
||||
ng-model="pickerModel"
|
||||
field="'value'">
|
||||
field="'value'"
|
||||
options="{ hours: true }">
|
||||
</mct-control>
|
||||
</div>
|
||||
</mct-popup>
|
||||
|
||||
@@ -19,43 +19,42 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div ng-controller="TimeRangeController as trCtrl" class="l-flex-col">
|
||||
<form class="l-time-range-inputs-holder l-flex-row flex-elem"
|
||||
<div ng-controller="TimeRangeController as trCtrl">
|
||||
<form class="l-time-range-inputs-holder"
|
||||
ng-submit="trCtrl.updateBoundsFromForm()">
|
||||
<span class="l-time-range-inputs-elem ui-symbol type-icon flex-elem">C</span>
|
||||
<span class="l-time-range-inputs-elem t-inputs-w l-flex-row flex-elem">
|
||||
<span class="l-time-range-input-w flex-elem">
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: parameters.format,
|
||||
validate: trCtrl.validateStart
|
||||
}"
|
||||
ng-model="formModel"
|
||||
ng-blur="trCtrl.updateBoundsFromForm()"
|
||||
field="'start'"
|
||||
class="time-range-start">
|
||||
</mct-control>
|
||||
</span>
|
||||
|
||||
<span class="l-time-range-inputs-elem lbl flex-elem">to</span>
|
||||
|
||||
<span class="l-time-range-input-w flex-elem" ng-controller="ToggleController as t2">
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: parameters.format,
|
||||
validate: trCtrl.validateEnd
|
||||
}"
|
||||
ng-model="formModel"
|
||||
ng-blur="trCtrl.updateBoundsFromForm()"
|
||||
field="'end'"
|
||||
class="time-range-end">
|
||||
</mct-control>
|
||||
</span>
|
||||
<span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
|
||||
<span class="l-time-range-input">
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: parameters.format,
|
||||
validate: trCtrl.validateStart
|
||||
}"
|
||||
ng-model="formModel"
|
||||
ng-blur="trCtrl.updateBoundsFromForm()"
|
||||
field="'start'"
|
||||
class="time-range-start">
|
||||
</mct-control>
|
||||
</span>
|
||||
|
||||
<span class="l-time-range-inputs-elem lbl">to</span>
|
||||
|
||||
<span class="l-time-range-input" ng-controller="ToggleController as t2">
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: parameters.format,
|
||||
validate: trCtrl.validateEnd
|
||||
}"
|
||||
ng-model="formModel"
|
||||
ng-blur="trCtrl.updateBoundsFromForm()"
|
||||
field="'end'"
|
||||
class="time-range-end">
|
||||
</mct-control>
|
||||
</span>
|
||||
|
||||
<input type="submit" class="hidden">
|
||||
</form>
|
||||
|
||||
<div class="l-time-range-slider-holder flex-elem">
|
||||
<div class="l-time-range-slider-holder">
|
||||
<div class="l-time-range-slider">
|
||||
<div class="slider"
|
||||
mct-resize="spanWidth = bounds.width">
|
||||
@@ -86,7 +85,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="l-time-range-ticks-holder flex-elem">
|
||||
<div class="l-time-range-ticks-holder">
|
||||
<div class="l-time-range-ticks">
|
||||
<div
|
||||
ng-repeat="tick in ticks track by $index"
|
||||
|
||||
@@ -49,7 +49,10 @@ define(
|
||||
position = [rect.left, rect.top],
|
||||
popup = popupService.display(div, position);
|
||||
|
||||
div.addClass('t-popup');
|
||||
// TODO: Handle in CSS;
|
||||
// https://github.com/nasa/openmctweb/issues/298
|
||||
div.css('z-index', 75);
|
||||
|
||||
transclude(function (clone) {
|
||||
div.append(clone);
|
||||
});
|
||||
|
||||
@@ -24,15 +24,7 @@ define(
|
||||
["../../src/directives/MCTPopup"],
|
||||
function (MCTPopup) {
|
||||
|
||||
var JQLITE_METHODS = [
|
||||
"on",
|
||||
"off",
|
||||
"find",
|
||||
"parent",
|
||||
"css",
|
||||
"addClass",
|
||||
"append"
|
||||
];
|
||||
var JQLITE_METHODS = ["on", "off", "find", "parent", "css", "append"];
|
||||
|
||||
describe("The mct-popup directive", function () {
|
||||
var mockCompile,
|
||||
|
||||
@@ -38,6 +38,7 @@ define(
|
||||
function InfoGestureButton($document, agentService, infoService, element, domainObject) {
|
||||
var dismissBubble,
|
||||
touchPosition,
|
||||
scopeOff,
|
||||
body = $document.find('body');
|
||||
|
||||
function trackPosition(event) {
|
||||
@@ -93,6 +94,10 @@ define(
|
||||
element.on('click', showBubble);
|
||||
}
|
||||
|
||||
// Also make sure we dismiss bubble if representation is destroyed
|
||||
// before the mouse actually leaves it
|
||||
scopeOff = element.scope().$on('$destroy', hideBubble);
|
||||
|
||||
return {
|
||||
/**
|
||||
* Detach any event handlers associated with this gesture.
|
||||
@@ -104,6 +109,7 @@ define(
|
||||
hideBubble();
|
||||
// ...and detach listeners
|
||||
element.off('click', showBubble);
|
||||
scopeOff();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -137,11 +137,6 @@ define(
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/nasa/openmct/issues/948
|
||||
it("does not try to access scope", function () {
|
||||
expect(mockElement.scope).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -32,12 +32,11 @@ $sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.1);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.3);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.5);
|
||||
$sliderColorKnob: $sliderColorBase;
|
||||
$sliderColorKnobHov: pullForward($sliderColorKnob, $ltGamma);
|
||||
$sliderColorKnob: rgba($sliderColorBase, 0.6);
|
||||
$sliderColorKnobHov: $sliderColorBase;
|
||||
$sliderColorRangeValHovBg: rgba($sliderColorBase, 0.1);
|
||||
$sliderColorRangeValHovFg: $colorKeyFg;
|
||||
$sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
$sliderKnobW: nth($ueTimeControlH,2)/2;
|
||||
$timeControllerToiLineColor: #00c2ff;
|
||||
$timeControllerToiLineColorHov: #fff;
|
||||
|
||||
@@ -70,10 +69,8 @@ $colorCreateMenuText: $colorMenuFg;
|
||||
$colorCheck: $colorKey;
|
||||
$colorFormRequired: $colorAlt1;
|
||||
$colorFormValid: #33cc33;
|
||||
$colorFormError: #990000;
|
||||
$colorFormError: #cc0000;
|
||||
$colorFormInvalid: #ff3300;
|
||||
$colorFormFieldErrorBg: $colorFormError;
|
||||
$colorFormFieldErrorFg: rgba(#fff, 0.6);
|
||||
$colorFormLines: rgba(#fff, 0.1);
|
||||
$colorFormSectionHeader: rgba(#fff, 0.1);
|
||||
$colorInputBg: rgba(#000, 0.1);
|
||||
|
||||
@@ -32,12 +32,11 @@ $sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.07);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.2);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.4);
|
||||
$sliderColorKnob: pushBack($sliderColorBase, 20%);
|
||||
$sliderColorKnob: rgba($sliderColorBase, 0.5);
|
||||
$sliderColorKnobHov: rgba($sliderColorBase, 0.7);
|
||||
$sliderColorRangeValHovBg: $sliderColorRange;
|
||||
$sliderColorRangeValHovBg: $sliderColorRange; //rgba($sliderColorBase, 0.1);
|
||||
$sliderColorRangeValHovFg: $colorBodyFg;
|
||||
$sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
$sliderKnobW: nth($ueTimeControlH,2)/2;
|
||||
$timeControllerToiLineColor: $colorBodyFg;
|
||||
$timeControllerToiLineColorHov: #0052b5;
|
||||
|
||||
@@ -70,10 +69,8 @@ $colorCreateMenuText: $colorBodyFg;
|
||||
$colorCheck: $colorKey;
|
||||
$colorFormRequired: $colorKey;
|
||||
$colorFormValid: #33cc33;
|
||||
$colorFormError: #990000;
|
||||
$colorFormError: #cc0000;
|
||||
$colorFormInvalid: #ff2200;
|
||||
$colorFormFieldErrorBg: $colorFormError;
|
||||
$colorFormFieldErrorFg: rgba(#fff, 0.6);
|
||||
$colorFormLines: rgba(#000, 0.1);
|
||||
$colorFormSectionHeader: rgba(#000, 0.05);
|
||||
$colorInputBg: $colorGenBg;
|
||||
|
||||
@@ -133,7 +133,7 @@ define([
|
||||
"telemetry"
|
||||
],
|
||||
"delegation": true,
|
||||
"editable": false
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"name": "Real-time Table",
|
||||
@@ -144,7 +144,7 @@ define([
|
||||
"telemetry"
|
||||
],
|
||||
"delegation": true,
|
||||
"editable": false
|
||||
"editable": true
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
|
||||
@@ -127,16 +127,7 @@ define([
|
||||
14400000,
|
||||
28800000,
|
||||
43200000,
|
||||
86400000,
|
||||
86400000 * 2,
|
||||
86400000 * 5,
|
||||
86400000 * 10,
|
||||
86400000 * 20,
|
||||
86400000 * 30,
|
||||
86400000 * 60,
|
||||
86400000 * 120,
|
||||
86400000 * 240,
|
||||
86400000 * 365
|
||||
86400000
|
||||
],
|
||||
"width": 200
|
||||
}
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
.l-timeline-gantt {
|
||||
min-width: 2px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: $timelineSwimlaneGanttVM; bottom: $timelineSwimlaneGanttVM;
|
||||
|
||||
.bar {
|
||||
@include ellipsize();
|
||||
height: $activityBarH;
|
||||
line-height: $activityBarH;
|
||||
line-height: $activityBarH + 2;
|
||||
padding: 0 $interiorMargin;
|
||||
|
||||
span {
|
||||
$iconW: 20px;
|
||||
@include absPosDefault();
|
||||
display: block;
|
||||
display: inline;
|
||||
&.s-activity-type {
|
||||
right: auto; width: $iconW;
|
||||
text-align: center;
|
||||
&.timeline {
|
||||
&:before {
|
||||
content:"S";
|
||||
@@ -29,9 +23,7 @@
|
||||
}
|
||||
}
|
||||
&.s-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
left: $iconW;
|
||||
text-shadow: rgba(black, 0.1) 0 1px 2px;
|
||||
}
|
||||
&.duration {
|
||||
left: auto;
|
||||
@@ -60,10 +52,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&.sm .bar span {
|
||||
// Hide icon and label if width is too small
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-mode .s-timeline-gantt,
|
||||
@@ -71,7 +59,7 @@
|
||||
.handle {
|
||||
cursor: col-resize;
|
||||
&.mid {
|
||||
cursor: ew-resize;
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,20 @@
|
||||
}
|
||||
|
||||
.s-timeline-gantt {
|
||||
$br: $controlCr;
|
||||
.bar {
|
||||
color: $colorGanttBarFg;
|
||||
@include activityBg($colorGanttBarBg);
|
||||
border-radius: $br;
|
||||
box-shadow: $shdwGanttBar;
|
||||
&.expanded {
|
||||
@include border-top-radius($br);
|
||||
@include border-bottom-radius(0);
|
||||
}
|
||||
&.leaf {
|
||||
@include border-top-radius(0);
|
||||
@include border-bottom-radius($br);
|
||||
}
|
||||
.s-toggle {
|
||||
color: $colorGanttToggle;
|
||||
}
|
||||
|
||||
@@ -52,9 +52,6 @@
|
||||
// Tree area with item title
|
||||
right: auto; // Set this to auto and uncomment width below when additional tabular columns are added
|
||||
width: $timelineTabularTitleW;
|
||||
.l-swimlanes-holder {
|
||||
bottom: $scrollbarTrackSize;
|
||||
}
|
||||
}
|
||||
&.l-tabular-r {
|
||||
// Start, end, duration, activity modes columns
|
||||
@@ -70,7 +67,6 @@
|
||||
&.l-timeline-gantt {
|
||||
.l-swimlanes-holder {
|
||||
@include scrollV(scroll);
|
||||
bottom: $scrollbarTrackSize;
|
||||
}
|
||||
}
|
||||
&.l-timeline-resource-legend {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="t-timeline-gantt l-timeline-gantt s-timeline-gantt"
|
||||
ng-class="{ sm: gantt.width(timespan, parameters.scroll, parameters.toPixels) < 25 }"
|
||||
title="{{model.name}}"
|
||||
ng-controller="TimelineGanttController as gantt"
|
||||
ng-style="timespan ? {
|
||||
|
||||
@@ -103,13 +103,6 @@
|
||||
<!-- TOP PANE GANTT BARS -->
|
||||
<div class="split-pane-component l-timeline-pane t-pane-h l-pane-top t-timeline-gantt l-timeline-gantt s-timeline-gantt">
|
||||
<div class="l-hover-btns-holder s-hover-btns-holder t-btns-zoom">
|
||||
<a class="t-btn l-btn s-btn"
|
||||
ng-click="zoomController.fit()"
|
||||
ng-show="true"
|
||||
title="Zoom to fit">
|
||||
<span class="ui-symbol icon zoom-in">I</span>
|
||||
</a>
|
||||
|
||||
<a class="t-btn l-btn s-btn"
|
||||
ng-click="zoomController.zoom(-1)"
|
||||
ng-show="true"
|
||||
@@ -128,7 +121,7 @@
|
||||
<div style="overflow: hidden; position: absolute; left: 0; top: 0; right: 0; height: 30px;" mct-scroll-x="scroll.x">
|
||||
<mct-include key="'timeline-ticks'"
|
||||
parameters="{
|
||||
fullWidth: timelineController.width(zoomController),
|
||||
fullWidth: zoomController.toPixels(zoomController.duration()),
|
||||
start: scroll.x,
|
||||
width: scroll.width,
|
||||
step: zoomController.toPixels(zoomController.zoom()),
|
||||
|
||||
@@ -97,8 +97,6 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch("configuration", swimlanePopulator.configure);
|
||||
|
||||
// Recalculate swimlane state on changes
|
||||
$scope.$watch("domainObject", swimlanePopulator.populate);
|
||||
|
||||
|
||||
@@ -32,26 +32,16 @@ define(
|
||||
var zoomLevels = ZOOM_CONFIGURATION.levels || [1000],
|
||||
zoomIndex = Math.floor(zoomLevels.length / 2),
|
||||
tickWidth = ZOOM_CONFIGURATION.width || 200,
|
||||
bounds = { x: 0, width: tickWidth },
|
||||
duration = 86400000; // Default duration in view
|
||||
|
||||
// Round a duration to a larger value, to ensure space for editing
|
||||
function roundDuration(value) {
|
||||
// Ensure there's always an extra day or so
|
||||
var tickCount = bounds.width / tickWidth,
|
||||
sz = zoomLevels[zoomLevels.length - 1] * tickCount;
|
||||
var sz = zoomLevels[zoomLevels.length - 1];
|
||||
value *= 1.25; // Add 25% padding to start
|
||||
return Math.ceil(value / sz) * sz;
|
||||
}
|
||||
|
||||
function toMillis(pixels) {
|
||||
return (pixels / tickWidth) * zoomLevels[zoomIndex];
|
||||
}
|
||||
|
||||
function toPixels(millis) {
|
||||
return tickWidth * millis / zoomLevels[zoomIndex];
|
||||
}
|
||||
|
||||
// Get/set zoom level
|
||||
function setZoomLevel(level) {
|
||||
if (!isNaN(level)) {
|
||||
@@ -63,27 +53,20 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
function initializeZoomFromTimespan(timespan) {
|
||||
var timelineDuration = timespan.getDuration();
|
||||
zoomIndex = 0;
|
||||
while (toMillis(bounds.width) < timelineDuration &&
|
||||
zoomIndex < zoomLevels.length - 1) {
|
||||
zoomIndex += 1;
|
||||
}
|
||||
bounds.x = toPixels(timespan.getStart());
|
||||
}
|
||||
|
||||
function initializeZoom() {
|
||||
if ($scope.domainObject) {
|
||||
$scope.domainObject.useCapability('timespan')
|
||||
.then(initializeZoomFromTimespan);
|
||||
// Persist current zoom level
|
||||
function storeZoom() {
|
||||
var isEditMode = $scope.commit &&
|
||||
$scope.domainObject &&
|
||||
$scope.domainObject.hasCapability('editor') &&
|
||||
$scope.domainObject.getCapability('editor').inEditContext();
|
||||
if (isEditMode) {
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
$scope.configuration.zoomLevel = zoomIndex;
|
||||
$scope.commit();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch("scroll", function (scroll) {
|
||||
bounds = scroll;
|
||||
});
|
||||
$scope.$watch("domainObject", initializeZoom);
|
||||
$scope.$watch("configuration.zoomLevel", setZoomLevel);
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -100,29 +83,27 @@ define(
|
||||
zoom: function (amount) {
|
||||
// Update the zoom level if called with an argument
|
||||
if (arguments.length > 0 && !isNaN(amount)) {
|
||||
var center = this.toMillis(bounds.x + bounds.width / 2);
|
||||
setZoomLevel(zoomIndex + amount);
|
||||
bounds.x = this.toPixels(center) - bounds.width / 2;
|
||||
storeZoom(zoomIndex);
|
||||
}
|
||||
return zoomLevels[zoomIndex];
|
||||
},
|
||||
/**
|
||||
* Set the zoom level to fit the bounds of the timeline
|
||||
* being viewed.
|
||||
*/
|
||||
fit: initializeZoom,
|
||||
/**
|
||||
* Get the width, in pixels, of a specific time duration at
|
||||
* the current zoom level.
|
||||
* @returns {number} the number of pixels
|
||||
*/
|
||||
toPixels: toPixels,
|
||||
toPixels: function (millis) {
|
||||
return tickWidth * millis / zoomLevels[zoomIndex];
|
||||
},
|
||||
/**
|
||||
* Get the time duration, in milliseconds, occupied by the
|
||||
* width (specified in pixels) at the current zoom level.
|
||||
* @returns {number} the number of pixels
|
||||
*/
|
||||
toMillis: toMillis,
|
||||
toMillis: function (pixels) {
|
||||
return (pixels / tickWidth) * zoomLevels[zoomIndex];
|
||||
},
|
||||
/**
|
||||
* Get or set the current displayed duration. If used as a
|
||||
* setter, this will typically be rounded up to ensure extra
|
||||
|
||||
@@ -43,7 +43,8 @@ define(
|
||||
var swimlanes = [],
|
||||
start = Number.POSITIVE_INFINITY,
|
||||
end = Number.NEGATIVE_INFINITY,
|
||||
assigner,
|
||||
colors = (configuration.colors || {}),
|
||||
assigner = new TimelineColorAssigner(colors),
|
||||
lastDomainObject;
|
||||
|
||||
// Track extremes of start/end times
|
||||
@@ -151,15 +152,8 @@ define(
|
||||
recalculateSwimlanes(lastDomainObject);
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
var colors = (configuration.colors || {});
|
||||
assigner = new TimelineColorAssigner(colors);
|
||||
configuration.colors = colors;
|
||||
recalculateSwimlanes(lastDomainObject);
|
||||
}
|
||||
|
||||
// Ensure colors are exposed in configuration
|
||||
initialize();
|
||||
configuration.colors = colors;
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -194,15 +188,6 @@ define(
|
||||
*/
|
||||
end: function () {
|
||||
return end;
|
||||
},
|
||||
/**
|
||||
* Pass a new configuration object (to retrieve and store
|
||||
* swimlane configuration)
|
||||
* @param newConfig
|
||||
*/
|
||||
configure: function (newConfig) {
|
||||
configuration = newConfig;
|
||||
initialize();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,14 +68,6 @@ define(
|
||||
};
|
||||
}
|
||||
|
||||
function fireWatch(expr, value) {
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
var mockA, mockB, mockUtilization, mockPromise, mockGraph, testCapabilities;
|
||||
@@ -149,15 +141,9 @@ define(
|
||||
expect(mockScope.scroll.y).toEqual(0);
|
||||
});
|
||||
|
||||
it("watches for a configuration object", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"configuration",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("repopulates when modifications are made", function () {
|
||||
var fnWatchCall;
|
||||
var fnWatchCall,
|
||||
strWatchCall;
|
||||
|
||||
// Find the $watch that was given a function
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
@@ -165,11 +151,16 @@ define(
|
||||
// white-box: we know the first call is
|
||||
// the one we're looking for
|
||||
fnWatchCall = fnWatchCall || call;
|
||||
} else if (typeof call.args[0] === 'string') {
|
||||
strWatchCall = strWatchCall || call;
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure string watch was for domainObject
|
||||
fireWatch('domainObject', mockDomainObject);
|
||||
expect(strWatchCall.args[0]).toEqual('domainObject');
|
||||
// Initially populate
|
||||
strWatchCall.args[1](mockDomainObject);
|
||||
|
||||
// There should be to swimlanes
|
||||
expect(controller.swimlanes().length).toEqual(2);
|
||||
|
||||
@@ -191,23 +182,23 @@ define(
|
||||
// order of $watch calls in TimelineController.
|
||||
|
||||
// Initially populate
|
||||
fireWatch('domainObject', mockDomainObject);
|
||||
mockScope.$watch.calls[0].args[1](mockDomainObject);
|
||||
|
||||
// Verify precondition - no graphs
|
||||
expect(controller.graphs().length).toEqual(0);
|
||||
|
||||
// Execute the watch function for graph state
|
||||
tmp = mockScope.$watch.calls[3].args[0]();
|
||||
tmp = mockScope.$watch.calls[2].args[0]();
|
||||
|
||||
// Change graph state
|
||||
testConfiguration.graph = { a: true, b: true };
|
||||
|
||||
// Verify that this would have triggered a watch
|
||||
expect(mockScope.$watch.calls[3].args[0]())
|
||||
expect(mockScope.$watch.calls[2].args[0]())
|
||||
.not.toEqual(tmp);
|
||||
|
||||
// Run the function the watch would have triggered
|
||||
mockScope.$watch.calls[3].args[1]();
|
||||
mockScope.$watch.calls[2].args[1]();
|
||||
|
||||
// Should have some graphs now
|
||||
expect(controller.graphs().length).toEqual(2);
|
||||
@@ -220,7 +211,7 @@ define(
|
||||
mockZoom.duration.andReturn(12345);
|
||||
|
||||
// Initially populate
|
||||
fireWatch('domainObject', mockDomainObject);
|
||||
mockScope.$watch.calls[0].args[1](mockDomainObject);
|
||||
|
||||
expect(controller.width(mockZoom)).toEqual(54321);
|
||||
// Verify interactions; we took zoom's duration for our start/end,
|
||||
|
||||
@@ -32,7 +32,11 @@ define(
|
||||
|
||||
beforeEach(function () {
|
||||
testConfiguration = {
|
||||
levels: [1000, 2000, 3500],
|
||||
levels: [
|
||||
1000,
|
||||
2000,
|
||||
3500
|
||||
],
|
||||
width: 12321
|
||||
};
|
||||
mockScope = jasmine.createSpyObj("$scope", ['$watch']);
|
||||
@@ -70,61 +74,32 @@ define(
|
||||
expect(controller.zoom()).toEqual(3500);
|
||||
});
|
||||
|
||||
it("observes scroll bounds", function () {
|
||||
expect(mockScope.$watch)
|
||||
.toHaveBeenCalledWith("scroll", jasmine.any(Function));
|
||||
it("does not normally persist zoom changes", function () {
|
||||
controller.zoom(1);
|
||||
expect(mockScope.commit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when watches have fired", function () {
|
||||
var mockDomainObject,
|
||||
mockPromise,
|
||||
mockTimespan,
|
||||
testStart,
|
||||
testEnd;
|
||||
|
||||
beforeEach(function () {
|
||||
testStart = 3000;
|
||||
testEnd = 5500;
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getId',
|
||||
'getModel',
|
||||
'getCapability',
|
||||
'useCapability'
|
||||
]);
|
||||
mockPromise = jasmine.createSpyObj('promise', ['then']);
|
||||
mockTimespan = jasmine.createSpyObj('timespan', [
|
||||
'getStart',
|
||||
'getEnd',
|
||||
'getDuration'
|
||||
]);
|
||||
|
||||
mockDomainObject.useCapability.andCallFake(function (c) {
|
||||
return c === 'timespan' && mockPromise;
|
||||
});
|
||||
mockPromise.then.andCallFake(function (callback) {
|
||||
callback(mockTimespan);
|
||||
});
|
||||
mockTimespan.getStart.andReturn(testStart);
|
||||
mockTimespan.getEnd.andReturn(testEnd);
|
||||
mockTimespan.getDuration.andReturn(testEnd - testStart);
|
||||
|
||||
mockScope.scroll = { x: 0, width: 20000 };
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
call.args[1](mockScope[call.args[0]]);
|
||||
});
|
||||
it("persists zoom changes in Edit mode", function () {
|
||||
mockScope.domainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['hasCapability', 'getCapability']
|
||||
);
|
||||
mockScope.domainObject.hasCapability.andCallFake(function (c) {
|
||||
return c === 'editor';
|
||||
});
|
||||
|
||||
it("zooms to fit the timeline", function () {
|
||||
var x1 = mockScope.scroll.x,
|
||||
x2 = mockScope.scroll.x + mockScope.scroll.width;
|
||||
expect(Math.round(controller.toMillis(x1)))
|
||||
.toEqual(testStart);
|
||||
expect(Math.round(controller.toMillis(x2)))
|
||||
.toBeGreaterThan(testEnd);
|
||||
mockScope.domainObject.getCapability.andCallFake(function (c) {
|
||||
if (c === 'editor') {
|
||||
return {
|
||||
inEditContext: function () {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
controller.zoom(1);
|
||||
expect(mockScope.commit).toHaveBeenCalled();
|
||||
expect(mockScope.configuration.zoomLevel)
|
||||
.toEqual(jasmine.any(Number));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -177,10 +177,6 @@ define(
|
||||
// representation to store local variables into.
|
||||
$scope.representation = {};
|
||||
|
||||
// Change templates (passing in undefined to clear
|
||||
// if we don't have enough info to show a template.)
|
||||
changeTemplate(canRepresent ? representation : undefined);
|
||||
|
||||
// Any existing representers are no longer valid; release them.
|
||||
destroyRepresenters();
|
||||
|
||||
@@ -226,6 +222,10 @@ define(
|
||||
// next change object/key pair changes
|
||||
toClear = uses.concat(['model']);
|
||||
}
|
||||
|
||||
// Change templates (passing in undefined to clear
|
||||
// if we don't have enough info to show a template.)
|
||||
changeTemplate(canRepresent ? representation : undefined);
|
||||
}
|
||||
|
||||
// Update the representation when the key changes (e.g. if a
|
||||
|
||||
@@ -194,6 +194,21 @@ define(
|
||||
.toHaveBeenCalledWith(testViews[1]);
|
||||
});
|
||||
|
||||
it("exposes configuration before changing templates", function () {
|
||||
var observedConfiguration;
|
||||
|
||||
mockChangeTemplate.andCallFake(function () {
|
||||
observedConfiguration = mockScope.configuration;
|
||||
});
|
||||
|
||||
mockScope.key = "xyz";
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
fireWatch('key', mockScope.key);
|
||||
fireWatch('domainObject', mockDomainObject);
|
||||
|
||||
expect(observedConfiguration).toBeDefined();
|
||||
});
|
||||
|
||||
it("does not load templates until there is an object", function () {
|
||||
mockScope.key = "xyz";
|
||||
|
||||
|
||||
36
src/MCT.js
36
src/MCT.js
@@ -1,36 +0,0 @@
|
||||
define([
|
||||
'EventEmitter',
|
||||
'legacyRegistry',
|
||||
'./api/api',
|
||||
'./api/objects/bundle'
|
||||
], function (
|
||||
EventEmitter,
|
||||
legacyRegistry,
|
||||
api
|
||||
) {
|
||||
function MCT() {
|
||||
EventEmitter.call(this);
|
||||
this.legacyBundle = { extensions: {} };
|
||||
}
|
||||
|
||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
Object.keys(api).forEach(function (k) {
|
||||
MCT.prototype[k] = api[k];
|
||||
});
|
||||
|
||||
MCT.prototype.type = function (key, type) {
|
||||
var legacyDef = type.toLegacyDefinition();
|
||||
legacyDef.key = key;
|
||||
this.legacyBundle.extensions.types =
|
||||
this.legacyBundle.extensions.types || [];
|
||||
this.legacyBundle.extensions.types.push(legacyDef);
|
||||
};
|
||||
|
||||
MCT.prototype.start = function () {
|
||||
legacyRegistry.register('adapter', this.legacyBundle);
|
||||
this.emit('start');
|
||||
};
|
||||
|
||||
return MCT;
|
||||
});
|
||||
@@ -1,43 +0,0 @@
|
||||
define(function () {
|
||||
/**
|
||||
* @typedef TypeDefinition
|
||||
* @property {Metadata} metadata displayable metadata about this type
|
||||
* @property {function (object)} [initialize] a function which initializes
|
||||
* the model for new domain objects of this type
|
||||
* @property {boolean} [creatable] true if users should be allowed to
|
||||
* create this type (default: false)
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {TypeDefinition} definition
|
||||
* @constructor
|
||||
*/
|
||||
function Type(definition) {
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a definition for this type that can be registered using the
|
||||
* legacy bundle format.
|
||||
* @private
|
||||
*/
|
||||
Type.prototype.toLegacyDefinition = function () {
|
||||
var def = {};
|
||||
def.name = this.definition.metadata.label;
|
||||
def.glyph = this.definition.metadata.glyph;
|
||||
def.description = this.definition.metadata.description;
|
||||
|
||||
if (this.definition.initialize) {
|
||||
def.model = {};
|
||||
this.definition.initialize(def.model);
|
||||
}
|
||||
|
||||
if (this.definition.creatable) {
|
||||
def.features = ['creation'];
|
||||
}
|
||||
return def;
|
||||
};
|
||||
|
||||
return Type;
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
define([
|
||||
'./Type',
|
||||
'./objects/ObjectAPI'
|
||||
], function (
|
||||
Type,
|
||||
ObjectAPI
|
||||
) {
|
||||
return {
|
||||
Type: Type,
|
||||
Objects: ObjectAPI
|
||||
};
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
define([
|
||||
'./object-utils',
|
||||
'./ObjectAPI'
|
||||
], function (
|
||||
utils,
|
||||
ObjectAPI
|
||||
) {
|
||||
function ObjectServiceProvider(objectService, instantiate) {
|
||||
this.objectService = objectService;
|
||||
this.instantiate = instantiate;
|
||||
}
|
||||
|
||||
ObjectServiceProvider.prototype.save = function (object) {
|
||||
var key = object.key,
|
||||
keyString = utils.makeKeyString(key),
|
||||
newObject = this.instantiate(utils.toOldFormat(object), keyString);
|
||||
|
||||
return object.getCapability('persistence')
|
||||
.persist()
|
||||
.then(function () {
|
||||
return utils.toNewFormat(object, key);
|
||||
});
|
||||
};
|
||||
|
||||
ObjectServiceProvider.prototype.delete = function (object) {
|
||||
// TODO!
|
||||
};
|
||||
|
||||
ObjectServiceProvider.prototype.get = function (key) {
|
||||
var keyString = utils.makeKeyString(key);
|
||||
return this.objectService.getObjects([keyString])
|
||||
.then(function (results) {
|
||||
var model = JSON.parse(JSON.stringify(results[keyString].getModel()));
|
||||
return utils.toNewFormat(model, key);
|
||||
});
|
||||
};
|
||||
|
||||
// Injects new object API as a decorator so that it hijacks all requests.
|
||||
// Object providers implemented on new API should just work, old API should just work, many things may break.
|
||||
function LegacyObjectAPIInterceptor(ROOTS, instantiate, objectService) {
|
||||
this.getObjects = function (keys) {
|
||||
var results = {},
|
||||
promises = keys.map(function (keyString) {
|
||||
var key = utils.parseKeyString(keyString);
|
||||
return ObjectAPI.get(key)
|
||||
.then(function (object) {
|
||||
object = utils.toOldFormat(object)
|
||||
results[keyString] = instantiate(object, keyString);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(function () {
|
||||
return results;
|
||||
});
|
||||
};
|
||||
|
||||
ObjectAPI._supersecretSetFallbackProvider(
|
||||
new ObjectServiceProvider(objectService, instantiate)
|
||||
);
|
||||
|
||||
ROOTS.forEach(function (r) {
|
||||
ObjectAPI.addRoot(utils.parseKeyString(r.id));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
return LegacyObjectAPIInterceptor;
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
define([
|
||||
'lodash',
|
||||
'./object-utils'
|
||||
], function (
|
||||
_,
|
||||
utils
|
||||
) {
|
||||
|
||||
/**
|
||||
Object API. Intercepts the existing object API while also exposing
|
||||
A new Object API.
|
||||
|
||||
MCT.objects.get('mine')
|
||||
.then(function (root) {
|
||||
console.log(root);
|
||||
MCT.objects.getComposition(root)
|
||||
.then(function (composition) {
|
||||
console.log(composition)
|
||||
})
|
||||
});
|
||||
*/
|
||||
|
||||
var Objects = {},
|
||||
ROOT_REGISTRY = [],
|
||||
PROVIDER_REGISTRY = {},
|
||||
FALLBACK_PROVIDER;
|
||||
|
||||
Objects._supersecretSetFallbackProvider = function (p) {
|
||||
FALLBACK_PROVIDER = p;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Root provider is hardcoded in; can't be skipped.
|
||||
var RootProvider = {
|
||||
'get': function () {
|
||||
return Promise.resolve({
|
||||
name: 'The root object',
|
||||
type: 'root',
|
||||
composition: ROOT_REGISTRY
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve the provider for a given key.
|
||||
function getProvider(key) {
|
||||
if (key.identifier === 'ROOT') {
|
||||
return RootProvider;
|
||||
}
|
||||
return PROVIDER_REGISTRY[key.namespace] || FALLBACK_PROVIDER;
|
||||
};
|
||||
|
||||
Objects.addProvider = function (namespace, provider) {
|
||||
PROVIDER_REGISTRY[namespace] = provider;
|
||||
};
|
||||
|
||||
[
|
||||
'save',
|
||||
'delete',
|
||||
'get'
|
||||
].forEach(function (method) {
|
||||
Objects[method] = function () {
|
||||
var key = arguments[0],
|
||||
provider = getProvider(key);
|
||||
|
||||
if (!provider) {
|
||||
throw new Error('No Provider Matched');
|
||||
}
|
||||
|
||||
if (!provider[method]) {
|
||||
throw new Error('Provider does not support [' + method + '].');
|
||||
}
|
||||
|
||||
return provider[method].apply(provider, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
Objects.addRoot = function (key) {
|
||||
ROOT_REGISTRY.unshift(key);
|
||||
};
|
||||
|
||||
Objects.removeRoot = function (key) {
|
||||
ROOT_REGISTRY = ROOT_REGISTRY.filter(function (k) {
|
||||
return (
|
||||
k.identifier !== key.identifier ||
|
||||
k.namespace !== key.namespace
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return Objects;
|
||||
});
|
||||
@@ -1,100 +0,0 @@
|
||||
# Object API - Overview
|
||||
|
||||
The object API provides methods for fetching domain objects.
|
||||
|
||||
# Keys
|
||||
Keys are a composite identifier that is used to create and persist objects. Ex:
|
||||
```javascript
|
||||
{
|
||||
namespace: 'elastic',
|
||||
identifier: 'myIdentifier'
|
||||
}
|
||||
```
|
||||
|
||||
In old MCT days, we called this an "id", and we encoded it in a single string.
|
||||
The above key would encode into the identifier, `elastic:myIdentifier`.
|
||||
|
||||
When interacting with the API you will be dealing with key objects.
|
||||
|
||||
# Configuring the Object API
|
||||
|
||||
The following methods should be used before calling run. They allow you to
|
||||
configure the persistence space of MCT.
|
||||
|
||||
* `MCT.objects.addRoot(key)` -- add a "ROOT" to Open MCT by specifying it's
|
||||
key.
|
||||
* `MCT.objects.removeRoot(key)` -- Remove a "ROOT" from Open MCT by key.
|
||||
* `MCT.objects.addProvider(namespace, provider)` -- register an object provider
|
||||
for a specific namespace. See below for documentation on the provider
|
||||
interface.
|
||||
|
||||
# Using the object API
|
||||
|
||||
The object API provides methods for getting, saving, and deleting objects.
|
||||
|
||||
* MCT.objects.get(key) -> returns promise for an object
|
||||
* MCT.objects.save(object) -> returns promise that is resolved when object
|
||||
has been saved
|
||||
* MCT.objects.delete(object) -> returns promise that is resolved when object has
|
||||
been deleted
|
||||
|
||||
## Configuration Example: Adding a groot
|
||||
|
||||
The following example adds a new root object for groot and populates it with
|
||||
some pieces of groot.
|
||||
|
||||
```javascript
|
||||
|
||||
var ROOT_KEY = {
|
||||
namespace: 'groot',
|
||||
identifier: 'groot'
|
||||
};
|
||||
|
||||
var GROOT_ROOT = {
|
||||
name: 'I am groot',
|
||||
type: 'folder',
|
||||
composition: [
|
||||
{
|
||||
namespace: 'groot',
|
||||
identifier: 'arms'
|
||||
},
|
||||
{
|
||||
namespace: 'groot',
|
||||
identifier: 'legs'
|
||||
},
|
||||
{
|
||||
namespace: 'groot',
|
||||
identifier: 'torso'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var GrootProvider = {
|
||||
get: function (key) {
|
||||
if (key.identifier === 'groot') {
|
||||
return Promise.resolve(GROOT_ROOT);
|
||||
}
|
||||
return Promise.resolve({
|
||||
name: 'Groot\'s ' + key.identifier
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
mct.Objects.addRoot(ROOT_KEY);
|
||||
|
||||
mct.Objects.addProvider('groot', GrootProvider);
|
||||
|
||||
```
|
||||
|
||||
### Making a custom provider:
|
||||
|
||||
All methods on the provider interface are optional, so you do not need
|
||||
to modify them.
|
||||
|
||||
* `provider.get(key)` -> promise for a domain object.
|
||||
* `provider.save(domainObject)` -> returns promise that is fulfilled when object
|
||||
has been saved.
|
||||
* `provider.delete(domainObject)` -> returns promise that is fulfilled when
|
||||
object has been deleted.
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define([
|
||||
'./LegacyObjectAPIInterceptor',
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
LegacyObjectAPIInterceptor,
|
||||
legacyRegistry
|
||||
) {
|
||||
legacyRegistry.register('src/api/objects', {
|
||||
name: 'Object API',
|
||||
description: 'The public Objects API',
|
||||
extensions: {
|
||||
components: [
|
||||
{
|
||||
provides: "objectService",
|
||||
type: "decorator",
|
||||
priority: "mandatory",
|
||||
implementation: LegacyObjectAPIInterceptor,
|
||||
depends: [
|
||||
"roots[]",
|
||||
"instantiate"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
define([
|
||||
|
||||
], function (
|
||||
|
||||
) {
|
||||
|
||||
// take a key string and turn it into a key object
|
||||
// 'scratch:root' ==> {namespace: 'scratch', identifier: 'root'}
|
||||
var parseKeyString = function (key) {
|
||||
if (typeof key === 'object') {
|
||||
return key;
|
||||
}
|
||||
var namespace = '',
|
||||
identifier = key;
|
||||
for (var i = 0, escaped = false, len=key.length; i < len; i++) {
|
||||
if (key[i] === ":" && !escaped) {
|
||||
namespace = key.slice(0, i);
|
||||
identifier = key.slice(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
namespace: namespace,
|
||||
identifier: identifier
|
||||
};
|
||||
};
|
||||
|
||||
// take a key and turn it into a key string
|
||||
// {namespace: 'scratch', identifier: 'root'} ==> 'scratch:root'
|
||||
var makeKeyString = function (key) {
|
||||
if (typeof key === 'string') {
|
||||
return key;
|
||||
}
|
||||
if (!key.namespace) {
|
||||
return key.identifier;
|
||||
}
|
||||
return [
|
||||
key.namespace.replace(':', '\\:'),
|
||||
key.identifier.replace(':', '\\:')
|
||||
].join(':');
|
||||
};
|
||||
|
||||
// Converts composition to use key strings instead of keys
|
||||
var toOldFormat = function (model) {
|
||||
delete model.key;
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(makeKeyString);
|
||||
}
|
||||
return model;
|
||||
};
|
||||
|
||||
// converts composition to use keys instead of key strings
|
||||
var toNewFormat = function (model, key) {
|
||||
model.key = key;
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(parseKeyString);
|
||||
}
|
||||
return model;
|
||||
};
|
||||
|
||||
return {
|
||||
toOldFormat: toOldFormat,
|
||||
toNewFormat: toNewFormat,
|
||||
makeKeyString: makeKeyString,
|
||||
parseKeyString: parseKeyString
|
||||
};
|
||||
});
|
||||
@@ -48,7 +48,6 @@ requirejs.config({
|
||||
"angular-route": "bower_components/angular-route/angular-route.min",
|
||||
"csv": "bower_components/comma-separated-values/csv.min",
|
||||
"es6-promise": "bower_components/es6-promise/promise.min",
|
||||
"EventEmitter": "bower_components/eventemitter3/index",
|
||||
"moment": "bower_components/moment/moment",
|
||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||
@@ -65,9 +64,6 @@ requirejs.config({
|
||||
"angular-route": {
|
||||
"deps": [ "angular" ]
|
||||
},
|
||||
"EventEmitter": {
|
||||
"exports": "EventEmitter"
|
||||
},
|
||||
"moment-duration-format": {
|
||||
"deps": [ "moment" ]
|
||||
},
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
/*global require,process,console*/
|
||||
|
||||
var CONFIG = {
|
||||
port: 8081,
|
||||
dictionary: "dictionary.json",
|
||||
interval: 1000
|
||||
};
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var WebSocketServer = require('ws').Server,
|
||||
fs = require('fs'),
|
||||
wss = new WebSocketServer({ port: CONFIG.port }),
|
||||
dictionary = JSON.parse(fs.readFileSync(CONFIG.dictionary, "utf8")),
|
||||
spacecraft = {
|
||||
"prop.fuel": 77,
|
||||
"prop.thrusters": "OFF",
|
||||
"comms.recd": 0,
|
||||
"comms.sent": 0,
|
||||
"pwr.temp": 245,
|
||||
"pwr.c": 8.15,
|
||||
"pwr.v": 30
|
||||
},
|
||||
histories = {},
|
||||
listeners = [];
|
||||
|
||||
function updateSpacecraft() {
|
||||
spacecraft["prop.fuel"] = Math.max(
|
||||
0,
|
||||
spacecraft["prop.fuel"] -
|
||||
(spacecraft["prop.thrusters"] === "ON" ? 0.5 : 0)
|
||||
);
|
||||
spacecraft["pwr.temp"] = spacecraft["pwr.temp"] * 0.985
|
||||
+ Math.random() * 0.25 + Math.sin(Date.now());
|
||||
spacecraft["pwr.c"] = spacecraft["pwr.c"] * 0.985;
|
||||
spacecraft["pwr.v"] = 30 + Math.pow(Math.random(), 3);
|
||||
}
|
||||
|
||||
function generateTelemetry() {
|
||||
var timestamp = Date.now(), sent = 0;
|
||||
Object.keys(spacecraft).forEach(function (id) {
|
||||
var state = { timestamp: timestamp, value: spacecraft[id] };
|
||||
histories[id] = histories[id] || []; // Initialize
|
||||
histories[id].push(state);
|
||||
spacecraft["comms.sent"] += JSON.stringify(state).length;
|
||||
});
|
||||
listeners.forEach(function (listener) {
|
||||
listener();
|
||||
});
|
||||
}
|
||||
|
||||
function update() {
|
||||
updateSpacecraft();
|
||||
generateTelemetry();
|
||||
}
|
||||
|
||||
function handleConnection(ws) {
|
||||
var subscriptions = {}, // Active subscriptions for this connection
|
||||
handlers = { // Handlers for specific requests
|
||||
dictionary: function () {
|
||||
ws.send(JSON.stringify({
|
||||
type: "dictionary",
|
||||
value: dictionary
|
||||
}));
|
||||
},
|
||||
subscribe: function (id) {
|
||||
subscriptions[id] = true;
|
||||
},
|
||||
unsubscribe: function (id) {
|
||||
delete subscriptions[id];
|
||||
},
|
||||
history: function (id) {
|
||||
ws.send(JSON.stringify({
|
||||
type: "history",
|
||||
id: id,
|
||||
value: histories[id]
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
function notifySubscribers() {
|
||||
Object.keys(subscriptions).forEach(function (id) {
|
||||
var history = histories[id];
|
||||
if (history) {
|
||||
ws.send(JSON.stringify({
|
||||
type: "data",
|
||||
id: id,
|
||||
value: history[history.length - 1]
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Listen for requests
|
||||
ws.on('message', function (message) {
|
||||
var parts = message.split(' '),
|
||||
handler = handlers[parts[0]];
|
||||
if (handler) {
|
||||
handler.apply(handlers, parts.slice(1));
|
||||
}
|
||||
});
|
||||
|
||||
// Stop sending telemetry updates for this connection when closed
|
||||
ws.on('close', function () {
|
||||
listeners = listeners.filter(function (listener) {
|
||||
return listener !== notifySubscribers;
|
||||
});
|
||||
});
|
||||
|
||||
// Notify subscribers when telemetry is updated
|
||||
listeners.push(notifySubscribers);
|
||||
}
|
||||
|
||||
update();
|
||||
setInterval(update, CONFIG.interval);
|
||||
|
||||
wss.on('connection', handleConnection);
|
||||
|
||||
console.log("Example spacecraft running on port ");
|
||||
console.log("Press Enter to toggle thruster state.");
|
||||
process.stdin.on('data', function (data) {
|
||||
spacecraft['prop.thrusters'] =
|
||||
(spacecraft['prop.thrusters'] === "OFF") ? "ON" : "OFF";
|
||||
console.log("Thrusters " + spacecraft["prop.thrusters"]);
|
||||
});
|
||||
}());
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"name": "Example Spacecraft",
|
||||
"identifier": "sc",
|
||||
"subsystems": [
|
||||
{
|
||||
"name": "Propulsion",
|
||||
"identifier": "prop",
|
||||
"measurements": [
|
||||
{
|
||||
"name": "Fuel",
|
||||
"identifier": "prop.fuel",
|
||||
"units": "kilograms",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"name": "Thrusters",
|
||||
"identifier": "prop.thrusters",
|
||||
"units": "None",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Communications",
|
||||
"identifier": "comms",
|
||||
"measurements": [
|
||||
{
|
||||
"name": "Received",
|
||||
"identifier": "comms.recd",
|
||||
"units": "bytes",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "Sent",
|
||||
"identifier": "comms.sent",
|
||||
"units": "bytes",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Power",
|
||||
"identifier": "pwr",
|
||||
"measurements": [
|
||||
{
|
||||
"name": "Generator Temperature",
|
||||
"identifier": "pwr.temp",
|
||||
"units": "\u0080C",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"name": "Generator Current",
|
||||
"identifier": "pwr.c",
|
||||
"units": "A",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"name": "Generator Voltage",
|
||||
"identifier": "pwr.v",
|
||||
"units": "V",
|
||||
"type": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
define([
|
||||
'legacyRegistry',
|
||||
'./src/controllers/BarGraphController'
|
||||
], function (
|
||||
legacyRegistry,
|
||||
BarGraphController
|
||||
) {
|
||||
legacyRegistry.register("tutorials/bargraph", {
|
||||
"name": "Bar Graph",
|
||||
"description": "Provides the Bar Graph view of telemetry elements.",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"name": "Bar Graph",
|
||||
"key": "example.bargraph",
|
||||
"glyph": "H",
|
||||
"templateUrl": "templates/bargraph.html",
|
||||
"needs": [ "telemetry" ],
|
||||
"delegation": true,
|
||||
"editable": true,
|
||||
"toolbar": {
|
||||
"sections": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"name": "Low",
|
||||
"property": "low",
|
||||
"required": true,
|
||||
"control": "textfield",
|
||||
"size": 4
|
||||
},
|
||||
{
|
||||
"name": "Middle",
|
||||
"property": "middle",
|
||||
"required": true,
|
||||
"control": "textfield",
|
||||
"size": 4
|
||||
},
|
||||
{
|
||||
"name": "High",
|
||||
"property": "high",
|
||||
"required": true,
|
||||
"control": "textfield",
|
||||
"size": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"stylesheets": [
|
||||
{
|
||||
"stylesheetUrl": "css/bargraph.css"
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "BarGraphController",
|
||||
"implementation": BarGraphController,
|
||||
"depends": [ "$scope", "telemetryHandler" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,35 +0,0 @@
|
||||
<div class="example-bargraph" ng-controller="BarGraphController">
|
||||
<div class="example-tick-labels">
|
||||
<div ng-repeat="value in [low, middle, high] track by $index"
|
||||
class="example-tick-label"
|
||||
style="bottom: {{ toPercent(value) }}%">
|
||||
{{value}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-graph-area">
|
||||
<div ng-repeat="telemetryObject in telemetryObjects"
|
||||
style="left: {{barWidth * $index}}%; width: {{barWidth}}%"
|
||||
class="example-bar-holder">
|
||||
<div class="example-bar"
|
||||
ng-style="{
|
||||
bottom: getBottom(telemetryObject) + '%',
|
||||
top: getTop(telemetryObject) + '%'
|
||||
}">
|
||||
</div>
|
||||
</div>
|
||||
<div style="bottom: {{ toPercent(middle) }}%"
|
||||
class="example-graph-tick">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-bar-labels">
|
||||
<div ng-repeat="telemetryObject in telemetryObjects"
|
||||
style="left: {{barWidth * $index}}%; width: {{barWidth}}%"
|
||||
class="example-bar-holder example-label">
|
||||
<mct-representation key="'label'"
|
||||
mct-object="telemetryObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,75 +0,0 @@
|
||||
define(function () {
|
||||
function BarGraphController($scope, telemetryHandler) {
|
||||
var handle;
|
||||
|
||||
// Expose configuration constants directly in scope
|
||||
function exposeConfiguration() {
|
||||
$scope.low = $scope.configuration.low;
|
||||
$scope.middle = $scope.configuration.middle;
|
||||
$scope.high = $scope.configuration.high;
|
||||
}
|
||||
|
||||
// Populate a default value in the configuration
|
||||
function setDefault(key, value) {
|
||||
if ($scope.configuration[key] === undefined) {
|
||||
$scope.configuration[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Getter-setter for configuration properties (for view proxy)
|
||||
function getterSetter(property) {
|
||||
return function (value) {
|
||||
value = parseFloat(value);
|
||||
if (!isNaN(value)) {
|
||||
$scope.configuration[property] = value;
|
||||
exposeConfiguration();
|
||||
}
|
||||
return $scope.configuration[property];
|
||||
};
|
||||
}
|
||||
|
||||
// Add min/max defaults
|
||||
setDefault('low', -1);
|
||||
setDefault('middle', 0);
|
||||
setDefault('high', 1);
|
||||
exposeConfiguration($scope.configuration);
|
||||
|
||||
// Expose view configuration options
|
||||
if ($scope.selection) {
|
||||
$scope.selection.proxy({
|
||||
low: getterSetter('low'),
|
||||
middle: getterSetter('middle'),
|
||||
high: getterSetter('high')
|
||||
});
|
||||
}
|
||||
|
||||
// Convert value to a percent between 0-100
|
||||
$scope.toPercent = function (value) {
|
||||
var pct = 100 * (value - $scope.low) /
|
||||
($scope.high - $scope.low);
|
||||
return Math.min(100, Math.max(0, pct));
|
||||
};
|
||||
|
||||
// Get bottom and top (as percentages) for current value
|
||||
$scope.getBottom = function (telemetryObject) {
|
||||
var value = handle.getRangeValue(telemetryObject);
|
||||
return $scope.toPercent(Math.min($scope.middle, value));
|
||||
};
|
||||
$scope.getTop = function (telemetryObject) {
|
||||
var value = handle.getRangeValue(telemetryObject);
|
||||
return 100 - $scope.toPercent(Math.max($scope.middle, value));
|
||||
};
|
||||
|
||||
// Use the telemetryHandler to get telemetry objects here
|
||||
handle = telemetryHandler.handle($scope.domainObject, function () {
|
||||
$scope.telemetryObjects = handle.getTelemetryObjects();
|
||||
$scope.barWidth =
|
||||
100 / Math.max(($scope.telemetryObjects).length, 1);
|
||||
});
|
||||
|
||||
// Release subscriptions when scope is destroyed
|
||||
$scope.$on('$destroy', handle.unsubscribe);
|
||||
}
|
||||
|
||||
return BarGraphController;
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
define(function () {
|
||||
return function grootPlugin(mct) {
|
||||
var ROOT_KEY = {
|
||||
namespace: 'groot',
|
||||
identifier: 'groot'
|
||||
};
|
||||
|
||||
var GROOT_ROOT = {
|
||||
name: 'I am groot',
|
||||
type: 'folder',
|
||||
composition: [
|
||||
{
|
||||
namespace: 'groot',
|
||||
identifier: 'arms'
|
||||
},
|
||||
{
|
||||
namespace: 'groot',
|
||||
identifier: 'legs'
|
||||
},
|
||||
{
|
||||
namespace: 'groot',
|
||||
identifier: 'torso'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var GrootProvider = {
|
||||
get: function (key) {
|
||||
if (key.identifier === 'groot') {
|
||||
return Promise.resolve(GROOT_ROOT);
|
||||
}
|
||||
return Promise.resolve({
|
||||
name: 'Groot\'s ' + key.identifier
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
mct.Objects.addRoot(ROOT_KEY);
|
||||
|
||||
mct.Objects.addProvider('groot', GrootProvider);
|
||||
|
||||
return mct;
|
||||
};
|
||||
});
|
||||
@@ -1,90 +0,0 @@
|
||||
define([
|
||||
'legacyRegistry',
|
||||
'./src/ExampleTelemetryServerAdapter',
|
||||
'./src/ExampleTelemetryInitializer',
|
||||
'./src/ExampleTelemetryModelProvider'
|
||||
], function (
|
||||
legacyRegistry,
|
||||
ExampleTelemetryServerAdapter,
|
||||
ExampleTelemetryInitializer,
|
||||
ExampleTelemetryModelProvider
|
||||
) {
|
||||
legacyRegistry.register("tutorials/telemetry", {
|
||||
"name": "Example Telemetry Adapter",
|
||||
"extensions": {
|
||||
"types": [
|
||||
{
|
||||
"name": "Spacecraft",
|
||||
"key": "example.spacecraft",
|
||||
"glyph": "o"
|
||||
},
|
||||
{
|
||||
"name": "Subsystem",
|
||||
"key": "example.subsystem",
|
||||
"glyph": "o",
|
||||
"model": { "composition": [] }
|
||||
},
|
||||
{
|
||||
"name": "Measurement",
|
||||
"key": "example.measurement",
|
||||
"glyph": "T",
|
||||
"model": { "telemetry": {} },
|
||||
"telemetry": {
|
||||
"source": "example.source",
|
||||
"domains": [
|
||||
{
|
||||
"name": "Time",
|
||||
"key": "timestamp"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"roots": [
|
||||
{
|
||||
"id": "example:sc",
|
||||
"priority": "preferred",
|
||||
"model": {
|
||||
"type": "example.spacecraft",
|
||||
"name": "My Spacecraft",
|
||||
"composition": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "example.adapter",
|
||||
"implementation": "ExampleTelemetryServerAdapter.js",
|
||||
"depends": [ "$q", "EXAMPLE_WS_URL" ]
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "EXAMPLE_WS_URL",
|
||||
"priority": "fallback",
|
||||
"value": "ws://localhost:8081"
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"implementation": "ExampleTelemetryInitializer.js",
|
||||
"depends": [ "example.adapter", "objectService" ]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"provides": "modelService",
|
||||
"type": "provider",
|
||||
"implementation": "ExampleTelemetryModelProvider.js",
|
||||
"depends": [ "example.adapter", "$q" ]
|
||||
},
|
||||
{
|
||||
"provides": "telemetryService",
|
||||
"type": "provider",
|
||||
"implementation": "ExampleTelemetryProvider.js",
|
||||
"depends": [ "example.adapter", "$q" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var TAXONOMY_ID = "example:sc",
|
||||
PREFIX = "example_tlm:";
|
||||
|
||||
function ExampleTelemetryInitializer(adapter, objectService) {
|
||||
// Generate a domain object identifier for a dictionary element
|
||||
function makeId(element) {
|
||||
return PREFIX + element.identifier;
|
||||
}
|
||||
|
||||
// When the dictionary is available, add all subsystems
|
||||
// to the composition of My Spacecraft
|
||||
function initializeTaxonomy(dictionary) {
|
||||
// Get the top-level container for dictionary objects
|
||||
// from a group of domain objects.
|
||||
function getTaxonomyObject(domainObjects) {
|
||||
return domainObjects[TAXONOMY_ID];
|
||||
}
|
||||
|
||||
// Populate
|
||||
function populateModel(taxonomyObject) {
|
||||
return taxonomyObject.useCapability(
|
||||
"mutation",
|
||||
function (model) {
|
||||
model.name =
|
||||
dictionary.name;
|
||||
model.composition =
|
||||
dictionary.subsystems.map(makeId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Look up My Spacecraft, and populate it accordingly.
|
||||
objectService.getObjects([TAXONOMY_ID])
|
||||
.then(getTaxonomyObject)
|
||||
.then(populateModel);
|
||||
}
|
||||
|
||||
adapter.dictionary().then(initializeTaxonomy);
|
||||
}
|
||||
|
||||
return ExampleTelemetryInitializer;
|
||||
}
|
||||
);
|
||||
@@ -1,78 +0,0 @@
|
||||
define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var PREFIX = "example_tlm:",
|
||||
FORMAT_MAPPINGS = {
|
||||
float: "number",
|
||||
integer: "number",
|
||||
string: "string"
|
||||
};
|
||||
|
||||
function ExampleTelemetryModelProvider(adapter, $q) {
|
||||
var modelPromise, empty = $q.when({});
|
||||
|
||||
// Check if this model is in our dictionary (by prefix)
|
||||
function isRelevant(id) {
|
||||
return id.indexOf(PREFIX) === 0;
|
||||
}
|
||||
|
||||
// Build a domain object identifier by adding a prefix
|
||||
function makeId(element) {
|
||||
return PREFIX + element.identifier;
|
||||
}
|
||||
|
||||
// Create domain object models from this dictionary
|
||||
function buildTaxonomy(dictionary) {
|
||||
var models = {};
|
||||
|
||||
// Create & store a domain object model for a measurement
|
||||
function addMeasurement(measurement) {
|
||||
var format = FORMAT_MAPPINGS[measurement.type];
|
||||
models[makeId(measurement)] = {
|
||||
type: "example.measurement",
|
||||
name: measurement.name,
|
||||
telemetry: {
|
||||
key: measurement.identifier,
|
||||
ranges: [{
|
||||
key: "value",
|
||||
name: "Value",
|
||||
units: measurement.units,
|
||||
format: format
|
||||
}]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create & store a domain object model for a subsystem
|
||||
function addSubsystem(subsystem) {
|
||||
var measurements =
|
||||
(subsystem.measurements || []);
|
||||
models[makeId(subsystem)] = {
|
||||
type: "example.subsystem",
|
||||
name: subsystem.name,
|
||||
composition: measurements.map(makeId)
|
||||
};
|
||||
measurements.forEach(addMeasurement);
|
||||
}
|
||||
|
||||
(dictionary.subsystems || []).forEach(addSubsystem);
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
// Begin generating models once the dictionary is available
|
||||
modelPromise = adapter.dictionary().then(buildTaxonomy);
|
||||
|
||||
return {
|
||||
getModels: function (ids) {
|
||||
// Return models for the dictionary only when they
|
||||
// are relevant to the request.
|
||||
return ids.some(isRelevant) ? modelPromise : empty;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return ExampleTelemetryModelProvider;
|
||||
}
|
||||
);
|
||||
@@ -1,80 +0,0 @@
|
||||
define(
|
||||
['./ExampleTelemetrySeries'],
|
||||
function (ExampleTelemetrySeries) {
|
||||
"use strict";
|
||||
|
||||
var SOURCE = "example.source";
|
||||
|
||||
function ExampleTelemetryProvider(adapter, $q) {
|
||||
var subscribers = {};
|
||||
|
||||
// Used to filter out requests for telemetry
|
||||
// from some other source
|
||||
function matchesSource(request) {
|
||||
return (request.source === SOURCE);
|
||||
}
|
||||
|
||||
// Listen for data, notify subscribers
|
||||
adapter.listen(function (message) {
|
||||
var packaged = {};
|
||||
packaged[SOURCE] = {};
|
||||
packaged[SOURCE][message.id] =
|
||||
new ExampleTelemetrySeries([message.value]);
|
||||
(subscribers[message.id] || []).forEach(function (cb) {
|
||||
cb(packaged);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
requestTelemetry: function (requests) {
|
||||
var packaged = {},
|
||||
relevantReqs = requests.filter(matchesSource);
|
||||
|
||||
// Package historical telemetry that has been received
|
||||
function addToPackage(history) {
|
||||
packaged[SOURCE][history.id] =
|
||||
new ExampleTelemetrySeries(history.value);
|
||||
}
|
||||
|
||||
// Retrieve telemetry for a specific measurement
|
||||
function handleRequest(request) {
|
||||
var key = request.key;
|
||||
return adapter.history(key).then(addToPackage);
|
||||
}
|
||||
|
||||
packaged[SOURCE] = {};
|
||||
return $q.all(relevantReqs.map(handleRequest))
|
||||
.then(function () { return packaged; });
|
||||
},
|
||||
subscribe: function (callback, requests) {
|
||||
var keys = requests.filter(matchesSource)
|
||||
.map(function (req) { return req.key; });
|
||||
|
||||
function notCallback(cb) {
|
||||
return cb !== callback;
|
||||
}
|
||||
|
||||
function unsubscribe(key) {
|
||||
subscribers[key] =
|
||||
(subscribers[key] || []).filter(notCallback);
|
||||
if (subscribers[key].length < 1) {
|
||||
adapter.unsubscribe(key);
|
||||
}
|
||||
}
|
||||
|
||||
keys.forEach(function (key) {
|
||||
subscribers[key] = subscribers[key] || [];
|
||||
adapter.subscribe(key);
|
||||
subscribers[key].push(callback);
|
||||
});
|
||||
|
||||
return function () {
|
||||
keys.forEach(unsubscribe);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return ExampleTelemetryProvider;
|
||||
}
|
||||
);
|
||||
@@ -1,23 +0,0 @@
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
function ExampleTelemetrySeries(data) {
|
||||
return {
|
||||
getPointCount: function () {
|
||||
return data.length;
|
||||
},
|
||||
getDomainValue: function (index) {
|
||||
return (data[index] || {}).timestamp;
|
||||
},
|
||||
getRangeValue: function (index) {
|
||||
return (data[index] || {}).value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return ExampleTelemetrySeries;
|
||||
}
|
||||
);
|
||||
@@ -1,60 +0,0 @@
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
function ExampleTelemetryServerAdapter($q, wsUrl) {
|
||||
var ws = new WebSocket(wsUrl),
|
||||
histories = {},
|
||||
listeners = [],
|
||||
dictionary = $q.defer();
|
||||
|
||||
// Handle an incoming message from the server
|
||||
ws.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
|
||||
switch (message.type) {
|
||||
case "dictionary":
|
||||
dictionary.resolve(message.value);
|
||||
break;
|
||||
case "history":
|
||||
histories[message.id].resolve(message);
|
||||
delete histories[message.id];
|
||||
break;
|
||||
case "data":
|
||||
listeners.forEach(function (listener) {
|
||||
listener(message);
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Request dictionary once connection is established
|
||||
ws.onopen = function () {
|
||||
ws.send("dictionary");
|
||||
};
|
||||
|
||||
return {
|
||||
dictionary: function () {
|
||||
return dictionary.promise;
|
||||
},
|
||||
history: function (id) {
|
||||
histories[id] = histories[id] || $q.defer();
|
||||
ws.send("history " + id);
|
||||
return histories[id].promise;
|
||||
},
|
||||
subscribe: function (id) {
|
||||
ws.send("subscribe " + id);
|
||||
},
|
||||
unsubscribe: function (id) {
|
||||
ws.send("unsubscribe " + id);
|
||||
},
|
||||
listen: function (callback) {
|
||||
listeners.push(callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return ExampleTelemetryServerAdapter;
|
||||
}
|
||||
);
|
||||
@@ -1,59 +0,0 @@
|
||||
define([
|
||||
'legacyRegistry',
|
||||
'./src/controllers/TodoController'
|
||||
], function (
|
||||
legacyRegistry,
|
||||
TodoController
|
||||
) {
|
||||
legacyRegistry.register("tutorials/todo", {
|
||||
"name": "To-do Plugin",
|
||||
"description": "Allows creating and editing to-do lists.",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"key": "example.todo",
|
||||
"type": "example.todo",
|
||||
"glyph": "2",
|
||||
"name": "List",
|
||||
"templateUrl": "templates/todo.html",
|
||||
"editable": true,
|
||||
"toolbar": {
|
||||
"sections": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"text": "Add Task",
|
||||
"glyph": "+",
|
||||
"method": "addTask",
|
||||
"control": "button"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"glyph": "Z",
|
||||
"method": "removeTask",
|
||||
"control": "button"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "TodoController",
|
||||
"implementation": TodoController,
|
||||
"depends": [ "$scope", "dialogService" ]
|
||||
}
|
||||
],
|
||||
"stylesheets": [
|
||||
{
|
||||
"stylesheetUrl": "css/todo.css"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
<div ng-controller="TodoController" class="example-todo">
|
||||
<div class="example-button-group">
|
||||
<a ng-class="{ selected: checkVisibility(true) }"
|
||||
ng-click="setVisibility(true)">All</a>
|
||||
<a ng-class="{ selected: checkVisibility(false, false) }"
|
||||
ng-click="setVisibility(false, false)">Incomplete</a>
|
||||
<a ng-class="{ selected: checkVisibility(false, true) }"
|
||||
ng-click="setVisibility(false, true)">Complete</a>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li ng-repeat="task in model.tasks"
|
||||
ng-class="{ 'example-task-completed': task.completed }"
|
||||
ng-if="showTask(task)">
|
||||
|
||||
<input type="checkbox"
|
||||
ng-checked="task.completed"
|
||||
ng-click="toggleCompletion($index)">
|
||||
<span ng-click="selectTask($index)"
|
||||
ng-class="{ selected: isSelected($index) }"
|
||||
class="example-task-description">
|
||||
{{task.description}}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div ng-if="model.tasks.length < 1" class="example-message">
|
||||
There are no tasks to show.
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,97 +0,0 @@
|
||||
define(function () {
|
||||
// Form to display when adding new tasks
|
||||
var NEW_TASK_FORM = {
|
||||
name: "Add a Task",
|
||||
sections: [{
|
||||
rows: [{
|
||||
name: 'Description',
|
||||
key: 'description',
|
||||
control: 'textfield',
|
||||
required: true
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
function TodoController($scope, dialogService) {
|
||||
var showAll = true,
|
||||
showCompleted;
|
||||
|
||||
// Persist changes made to a domain object's model
|
||||
function persist() {
|
||||
var persistence =
|
||||
$scope.domainObject.getCapability('persistence');
|
||||
return persistence && persistence.persist();
|
||||
}
|
||||
|
||||
// Remove a task
|
||||
function removeTaskAtIndex(taskIndex) {
|
||||
$scope.domainObject.useCapability('mutation', function (model) {
|
||||
model.tasks.splice(taskIndex, 1);
|
||||
});
|
||||
persist();
|
||||
}
|
||||
|
||||
// Add a task
|
||||
function addNewTask(task) {
|
||||
$scope.domainObject.useCapability('mutation', function (model) {
|
||||
model.tasks.push(task);
|
||||
});
|
||||
persist();
|
||||
}
|
||||
|
||||
// Change which tasks are visible
|
||||
$scope.setVisibility = function (all, completed) {
|
||||
showAll = all;
|
||||
showCompleted = completed;
|
||||
};
|
||||
|
||||
// Check if current visibility settings match
|
||||
$scope.checkVisibility = function (all, completed) {
|
||||
return showAll ? all : (completed === showCompleted);
|
||||
};
|
||||
|
||||
// Toggle the completion state of a task
|
||||
$scope.toggleCompletion = function (taskIndex) {
|
||||
$scope.domainObject.useCapability('mutation', function (model) {
|
||||
var task = model.tasks[taskIndex];
|
||||
task.completed = !task.completed;
|
||||
});
|
||||
persist();
|
||||
};
|
||||
|
||||
// Check whether a task should be visible
|
||||
$scope.showTask = function (task) {
|
||||
return showAll || (showCompleted === !!(task.completed));
|
||||
};
|
||||
|
||||
// Handle selection state in edit mode
|
||||
if ($scope.selection) {
|
||||
// Expose the ability to select tasks
|
||||
$scope.selectTask = function (taskIndex) {
|
||||
$scope.selection.select({
|
||||
removeTask: function () {
|
||||
removeTaskAtIndex(taskIndex);
|
||||
$scope.selection.deselect();
|
||||
},
|
||||
taskIndex: taskIndex
|
||||
});
|
||||
};
|
||||
|
||||
// Expose a check for current selection state
|
||||
$scope.isSelected = function (taskIndex) {
|
||||
return ($scope.selection.get() || {}).taskIndex ===
|
||||
taskIndex;
|
||||
};
|
||||
|
||||
// Expose a view-level selection proxy
|
||||
$scope.selection.proxy({
|
||||
addTask: function () {
|
||||
dialogService.getUserInput(NEW_TASK_FORM, {})
|
||||
.then(addNewTask);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return TodoController;
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
define(function () {
|
||||
return function todoPlugin(mct) {
|
||||
var todoType = new mct.Type({
|
||||
metadata: {
|
||||
label: "To-Do List",
|
||||
glyph: "2",
|
||||
description: "A list of things that need to be done."
|
||||
},
|
||||
initialize: function (model) {
|
||||
model.tasks = [];
|
||||
},
|
||||
creatable: true
|
||||
});
|
||||
|
||||
mct.type('example.todo', todoType);
|
||||
|
||||
return mct;
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user