Compare commits

...

41 Commits

Author SHA1 Message Date
d7b44f8d09 Initial functional view API implementation. See #1642 2017-10-20 12:40:21 -07:00
c4cd36e15b Merge pull request #1783 from nasa/remove-circleci-push
[Build] Remove unused steps from circleci configuration
2017-10-19 19:28:02 -07:00
618a6e7e8d Remove unused steps from circleci configuration. Fixes #1782 2017-10-19 15:51:26 -07:00
63a8c91f71 Merge pull request #1781 from nasa/view-destroy
[Views] Clear regions on destroy
2017-10-19 12:17:24 -07:00
e59020fec7 [Views] Clear regions on destroy
Clear regions when a view is destroyed. This causes a view's
destroy method to be correctly invoked, allowing views to do
cleanup. Used by NSS heatmap view for sprint Alice,
https://github.com/nasa/openmct/projects/1
2017-10-19 10:57:53 -07:00
a2e424203a Merge pull request #1777 from nasa/iframe-border-1776
[Frontend] Review and integrate the death of iframe borders
2017-10-13 20:59:50 -07:00
16853644cb [Frontend] Kill iframe borders dead!
Fixes #1776
2017-10-13 17:05:37 -07:00
2272766c57 Merge pull request #1743 from nasa/layout-issue-1731
[Layout] Deselect object after removal
2017-10-13 13:48:59 -07:00
1d9cdea2d4 Merge pull request #1767 from nasa/inline-edit-bug-1757
[Edit] Prevent blur to update the model after return key is pressed
2017-10-13 13:07:05 -07:00
715219c44d [Edit] Fix the issue with currentTarget being null and HTML being added on Enter
Remove the line break that is added when the return key is pressed.

Check the current target directly.

Use textContent for inline editing instead of innerHTML, which will hoist up some HTML content implicitly created around user input for a contenteditable span.

Note that there is a removeAllRanges in addition to a blur here; see
https://stackoverflow.com/questions/4878713/how-can-i-blur-a-div-where-contenteditable-true

Fix broken tests.

Fixes #1757
2017-10-13 11:50:40 -07:00
00dc2875bf [Layout] Deselect object after removal
If the selected object is not in the composition, deselect it.

Add tests.

Fixes #1731
2017-10-10 15:39:29 -07:00
8703f363b8 Merge pull request #1744 from nasa/inspector-issue-1276
Inspector issue 1276
2017-10-10 15:33:27 -07:00
26210eaa50 make reviewer requested changes 2017-10-10 14:37:25 -07:00
a4a1cb5e05 fix tests by adding listen function, and fix lint/checkstyle errors 2017-10-10 13:32:42 -07:00
9570f2f7a1 Merge pull request #1766 from nasa/inline-edit-1746
Allow inline-editing for editable objects only
2017-10-10 12:43:07 -07:00
eb4ded39b3 [Edit] Allow inline-editing the name only if the object is editable
Made the contenteditable attribute conditional based on whether the object can be edited or not. If the object is not editable, the attribute is removed.

Add Tests.

Fixes #1746
2017-10-10 12:32:51 -07:00
7deb3cd025 add if statement to check is objects are not equal before reassigning metadata 2017-10-03 14:34:01 -07:00
06779e6cd9 add mutation listener to Inspector Controller 2017-10-03 13:33:09 -07:00
7f43c0bf1a Add Notebook icon (#1742)
* Add Notebook icon

Fixes #1739
Added to Style Guide as well

* Add icomoon project file

Fixes #1739
2017-10-02 13:09:58 -07:00
bfa3bbcdc7 Merge pull request #1735 from nasa/import-export-1695
Review and integrate usage of new Import/Export glyphs
2017-09-25 16:50:11 -07:00
2baf3f8bb0 Merge pull request #1733 from nasa/create-menu-1729
Review and integrate super-menu fixes and enhancements
2017-09-25 16:29:54 -07:00
10ac13ac5c [Front-end] Updated to use new glyphs
Fixes #1695
2017-09-25 15:22:35 -07:00
138cb199bb [Front-end] Markup and CSS refinements
Fixes #1729
Internal markup and CSS now sets heights
internally - menu height will now not be smaller
than the list of menu items OR the description
area;
2017-09-25 14:59:12 -07:00
374c363a78 [Plot] Handle telemetry panels from plot policy (#1732)
* [Plot] Check for telemetry panels

...from plot view policy, and don't try to interrogate them
for telemetry metadata that they will not have.

Fixes #1728

* [Plot] Update test case for policy

...to provide an adapted object with expected properties

* [Plot] Add tests to very plot policy for panels

...to verify fix for #1728
2017-09-25 14:27:32 -07:00
e9cb5cd639 [Front-end] Scrollbar-related color and padding
Fixes #1729
2017-09-25 12:08:59 -07:00
bc7d92ee0d [Front-end] Menu tweaks
Fixes #1729
Mini super-menu and related description text
2017-09-25 12:08:04 -07:00
78f49784a0 [Front-end] Tweaks
Fixes #1729
CSS and markup mods to convert
to flex from abs pos;
2017-09-25 11:10:58 -07:00
8754c438cc Merge pull request #1725 from nasa/legacy-telem-source-mapping
[Telemetry] Legacy adapter handles source remap
2017-09-21 11:47:07 -07:00
1419c75503 Inline edit object names (#1700)
* Inline edit object name.

Change the title-label span to a conteneditable span to allow editing object names inline. Implement a controller to handle updaing the name. Add tests.

Fixes #1679

[Front-end] Add span contenteditable to input styling
[Front-end] Styling for contenteditable span
styling for span[contenteditable].s-status-editing in _controls.scss;
removed s-filter class;
[Front-end] min-width added to .s-inline-edit

* [Frontend] Style tweaks, cleanup and simplification

Fixes #1679

Style sanding on .s-inline-edit; added
:focus outline:0 to select in _controls.scss;

New .s-input-inline class; removed ng-class from object-header.html,
uses :focus instead; refactoring of input-related mixins;

Bring Time Conductor real-time inputs into parity

Apply .s-input-inline to TC inputs; finesse .s-input-inline selector;

Prevent nested inline inputs from editing

Fixed nested editing prevention selector

* Create an object header template for objects inside a frame.

Fix code review requests.

Fixes 1679
2017-09-21 11:16:04 -07:00
ca8cad0a74 [Telemetry] Legacy adapter handles source remap
Update the Legacy Telemetry Adapter to handle source remapping
for telemetry which has it.

fixes https://github.com/nasa/openmct/issues/1724
2017-09-21 10:51:16 -07:00
a3a55d3b48 [Build] Modify version info injection to fix sourcemaps (#1708)
* [Build] Preserve comments instead of adding header

...to avoid disrupting sourcemaps. Fixes #1707

[Build] Remove extraneous horizontal rule from header

[Build] Remove obsolete header-handling

...as this is handled by the replace task after fix for #1707

[Build] Move version headers into start.frag

...so that UMD boilerplate does not wrap it.

[Build] Handle version info entirely in start.frag

[Build] Replace build variables in start.frag explicitly

[Build] Inject build info dynamically

[Build] Test MCT.specifyBuildInfo

[Build] Mark BUILD_CONSTANTS as global for jshint

[Build] Give names to version line items

[Build] Fix specifyBuildInfo test case

* [Build] Update fix to sourcemaps for code review feedback

https://github.com/nasa/openmct/pull/1708

[Build] Move build info registration to plugin

https://github.com/nasa/openmct/pull/1708#discussion_r138999027

[Build] Use build info plugin

...instead of specifyBuildInfo

[Build] Revert changes to MCT, per code review
2017-09-20 11:50:17 -07:00
e66f818996 Merge pull request #1714 from nasa/plot-telem-fixes
Plot telem fixes
2017-09-18 15:54:52 -07:00
ae5ef33487 [Plot] Update policy to detect any range
Update policy to detect any range.  As a simple way to prevent
detecting messages, it will not apply when every range is a string
format.

fixes https://github.com/nasa/openmct/issues/1713
2017-09-16 09:58:58 -07:00
59c5430579 [Generator] API Compatibility
The telemetry API does not pass request options for subscriptions, updated
generator provider to match this API.

fixes https://github.com/nasa/openmct/issues/1705
2017-09-16 09:37:48 -07:00
6d52f094d9 Merge pull request #1703 from nasa/text-size-1496
Text size control for text and telemetry elements
2017-09-14 16:00:07 -07:00
740db8da75 [Fixed Position] Add tests and fix checkstyle error.
Fixes #1496
2017-09-14 15:15:15 -07:00
68abc15ed5 [Frontend] Fix alignment and font-size issues in tool-bar
Fixes #1496
2017-09-14 15:02:22 -07:00
bb47feb517 [Fixed Position] Text size control for text and telemetry objects
Add a select control for text and telemetry objects to allow setting text size. Set the default size to 13px.

Fixes # 1496
2017-09-14 14:49:28 -07:00
469820fb0f Merge pull request #1712 from nasa/link-1710
Review and merge fixes for Hyperlinks
2017-09-14 12:15:09 -07:00
92dd99b26c Merge pull request #1711 from nasa/pause-button-1704
Review and merge fix for hidden Imagery controls
2017-09-14 12:13:56 -07:00
9fe1923189 [Front-end] Fixes for Hyperlinks
Fixes #1710
Converted to span to confine clickable area
to text only;
Link now uses `overflow: hidden` in frame;
Normalized font-size when .s-button;
2017-09-14 11:05:05 -07:00
70 changed files with 1134 additions and 497 deletions

View File

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

View File

@ -78,8 +78,8 @@ define([
return this.workerInterface.request(workerRequest);
};
GeneratorProvider.prototype.subscribe = function (domainObject, callback, request) {
var workerRequest = this.makeWorkerRequest(domainObject, request);
GeneratorProvider.prototype.subscribe = function (domainObject, callback) {
var workerRequest = this.makeWorkerRequest(domainObject, {});
return this.workerInterface.subscribe(workerRequest, callback);
};

View File

@ -127,7 +127,8 @@
{ 'meaning': 'Timer object', 'cssClass': 'icon-timer', 'cssContent': 'e1127', 'htmlEntity': '&#xe1127' },
{ 'meaning': 'Data Topic', 'cssClass': 'icon-topic', 'cssContent': 'e1128', 'htmlEntity': '&#xe1128' },
{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&#xe1129' },
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' }
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' },
{ 'meaning': 'Notebook object', 'cssClass': 'icon-notebook', 'cssContent': 'e1131', 'htmlEntity': '&#xe1131' }
];
"></div>

View File

@ -46,9 +46,22 @@ var gulp = require('gulp'),
name: 'bower_components/almond/almond.js',
include: paths.main.replace('.js', ''),
wrap: {
startFile: "src/start.frag",
start: (function () {
var buildVariables = {
version: project.version,
timestamp: moment.utc(Date.now()).format(),
revision: fs.existsSync('.git') ? git.long() : 'Unknown',
branch: fs.existsSync('.git') ? git.branch() : 'Unknown'
};
return fs.readFileSync("src/start.frag", 'utf-8')
.replace(/@@(\w+)/g, function (match, key) {
return buildVariables[key];
});;
}()),
endFile: "src/end.frag"
},
optimize: 'uglify2',
uglify2: { output: { comments: /@preserve/ } },
mainConfigFile: paths.main,
wrapShim: true
},
@ -58,14 +71,6 @@ var gulp = require('gulp'),
},
sass: {
sourceComments: true
},
replace: {
variables: {
version: project.version,
timestamp: moment.utc(Date.now()).format(),
revision: fs.existsSync('.git') ? git.long() : 'Unknown',
branch: fs.existsSync('.git') ? git.branch() : 'Unknown'
}
}
};
@ -76,16 +81,11 @@ if (process.env.NODE_ENV === 'development') {
gulp.task('scripts', function () {
var requirejsOptimize = require('gulp-requirejs-optimize');
var replace = require('gulp-replace-task');
var header = require('gulp-header');
var comment = fs.readFileSync('src/about.frag');
return gulp.src(paths.main)
.pipe(sourcemaps.init())
.pipe(requirejsOptimize(options.requirejsOptimize))
.pipe(sourcemaps.write('.'))
.pipe(replace(options.replace))
.pipe(header(comment, options.replace.variables))
.pipe(gulp.dest(paths.dist));
});

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global requirejs*/
/*global requirejs,BUILD_CONSTANTS*/
requirejs.config({
"paths": {
@ -91,12 +91,17 @@ requirejs.config({
define([
'./platform/framework/src/Main',
'./src/defaultRegistry',
'./src/MCT'
], function (Main, defaultRegistry, MCT) {
'./src/MCT',
'./src/plugins/buildInfo/plugin'
], function (Main, defaultRegistry, MCT, buildInfo) {
var openmct = new MCT();
openmct.legacyRegistry = defaultRegistry;
if (typeof BUILD_CONSTANTS !== 'undefined') {
openmct.install(buildInfo(BUILD_CONSTANTS));
}
openmct.on('start', function () {
return new Main().run(defaultRegistry);
});

View File

@ -22,12 +22,10 @@
"git-rev-sync": "^1.4.0",
"glob": ">= 3.0.0",
"gulp": "^3.9.1",
"gulp-header": "^1.8.8",
"gulp-jscs": "^3.0.2",
"gulp-jshint": "^2.0.0",
"gulp-jshint-html-reporter": "^0.1.3",
"gulp-rename": "^1.2.2",
"gulp-replace-task": "^0.11.0",
"gulp-requirejs-optimize": "^0.3.1",
"gulp-sass": "^2.2.0",
"gulp-sourcemaps": "^1.6.0",

View File

@ -26,6 +26,7 @@ define([
"./src/InspectorPaneController",
"./src/BrowseObjectController",
"./src/MenuArrowController",
"./src/ObjectHeaderController",
"./src/navigation/NavigationService",
"./src/navigation/NavigateAction",
"./src/navigation/OrphanNavigationHandler",
@ -36,6 +37,7 @@ define([
"text!./res/templates/browse-object.html",
"text!./res/templates/items/grid-item.html",
"text!./res/templates/browse/object-header.html",
"text!./res/templates/browse/object-header-frame.html",
"text!./res/templates/menu-arrow.html",
"text!./res/templates/back-arrow.html",
"text!./res/templates/items/items.html",
@ -48,6 +50,7 @@ define([
InspectorPaneController,
BrowseObjectController,
MenuArrowController,
ObjectHeaderController,
NavigationService,
NavigateAction,
OrphanNavigationHandler,
@ -58,6 +61,7 @@ define([
browseObjectTemplate,
gridItemTemplate,
objectHeaderTemplate,
objectHeaderFrameTemplate,
menuArrowTemplate,
backArrowTemplate,
itemsTemplate,
@ -140,6 +144,13 @@ define([
"$location",
"$attrs"
]
},
{
"key": "ObjectHeaderController",
"implementation": ObjectHeaderController,
"depends": [
"$scope"
]
}
],
"representations": [
@ -173,6 +184,13 @@ define([
"type"
]
},
{
"key": "object-header-frame",
"template": objectHeaderFrameTemplate,
"uses": [
"type"
]
},
{
"key": "menu-arrow",
"template": menuArrowTemplate,

View File

@ -0,0 +1,31 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
<span class='title-label flex-elem holder flex-can-shrink s-input-inline'>{{model.name}}</span>
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
<mct-representation
key="'menu-arrow'"
mct-object='domainObject'
class="flex-elem context-available-w"></mct-representation>
</span>

View File

@ -20,9 +20,13 @@
at runtime from the About dialog for additional information.
-->
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
<span class="l-elem-wrapper l-flex-row flex-elem grows">
<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
<span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span>
<span ng-attr-contenteditable="{{ controller.editable ? true : undefined }}"
class='title-label flex-elem holder flex-can-shrink s-input-inline'
ng-click="controller.edit()"
ng-blur="controller.updateName($event)"
ng-keypress="controller.updateName($event)">{{model.name}}</span>
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
<mct-representation
key="'menu-arrow'"

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Controller to provide the ability to inline edit an object name.
*
* @constructor
* @memberof platform/commonUI/browse
*/
function ObjectHeaderController($scope) {
this.$scope = $scope;
this.domainObject = $scope.domainObject;
this.editable = this.allowEdit();
}
/**
* Updates the object name on blur and enter keypress events.
*
* @param event the mouse event
*/
ObjectHeaderController.prototype.updateName = function (event) {
if (!event || !event.currentTarget) {
return;
}
if (event.type === 'blur') {
this.updateModel(event);
} else if (event.which === 13) {
this.updateModel(event);
event.currentTarget.blur();
window.getSelection().removeAllRanges();
}
};
/**
* Updates the model.
*
* @param event the mouse event
* @param private
*/
ObjectHeaderController.prototype.updateModel = function (event) {
var name = event.currentTarget.textContent.replace(/\n/g, ' ');
if (name.length === 0) {
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
event.currentTarget.textContent = name;
}
if (name !== this.domainObject.getModel().name) {
this.domainObject.getCapability('mutation').mutate(function (model) {
model.name = name;
});
}
};
/**
* Checks if the domain object is editable.
*
* @private
* @return true if object is editable
*/
ObjectHeaderController.prototype.allowEdit = function () {
var type = this.domainObject && this.domainObject.getCapability('type');
return !!(type && type.hasFeature('creation'));
};
return ObjectHeaderController;
}
);

View File

@ -0,0 +1,137 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/ObjectHeaderController"],
function (ObjectHeaderController) {
describe("The object header controller", function () {
var mockScope,
mockDomainObject,
mockCapabilities,
mockMutationCapability,
mockTypeCapability,
mockEvent,
mockCurrentTarget,
model,
controller;
beforeEach(function () {
mockMutationCapability = jasmine.createSpyObj("mutation", ["mutate"]);
mockTypeCapability = jasmine.createSpyObj("type", ["typeDef", "hasFeature"]);
mockTypeCapability.typeDef = { name: ""};
mockTypeCapability.hasFeature.andCallFake(function (feature) {
return feature === 'creation';
});
mockCapabilities = {
mutation: mockMutationCapability,
type: mockTypeCapability
};
model = {
name: "Test name"
};
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "getModel"]);
mockDomainObject.getModel.andReturn(model);
mockDomainObject.getCapability.andCallFake(function (key) {
return mockCapabilities[key];
});
mockScope = {
domainObject: mockDomainObject
};
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "textContent"]);
mockCurrentTarget.blur.andReturn(mockCurrentTarget);
mockEvent = {
which: {},
type: {},
currentTarget: mockCurrentTarget
};
controller = new ObjectHeaderController(mockScope);
});
it("updates the model with new name on blur", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
});
it("updates the model with a default for blank names", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "";
controller.updateName(mockEvent);
expect(mockCurrentTarget.textContent.length).not.toEqual(0);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
});
it("does not update the model if the same name", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = mockDomainObject.getModel().name;
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
});
it("updates the model on enter keypress event only", function () {
mockCurrentTarget.textContent = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
mockEvent.which = 13;
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function));
mockMutationCapability.mutate.mostRecentCall.args[0](model);
expect(mockDomainObject.getModel().name).toBe("New name");
});
it("blurs the field on enter key press", function () {
mockCurrentTarget.textContent = "New name";
mockEvent.which = 13;
controller.updateName(mockEvent);
expect(mockEvent.currentTarget.blur).toHaveBeenCalled();
});
it("allows editting name when object is creatable", function () {
expect(controller.allowEdit()).toBe(true);
});
it("disallows editting name when object is non-creatable", function () {
mockTypeCapability.hasFeature.andReturn(false);
expect(controller.allowEdit()).toBe(false);
});
});
}
);

View File

@ -23,7 +23,7 @@
<div class="s-menu-button major create-button" ng-click="createController.toggle()">
<span class="title-label">Create</span>
</div>
<div class="menu super-menu" ng-show="createController.isActive()">
<div class="menu super-menu l-create-menu" ng-show="createController.isActive()">
<mct-representation mct-object="domainObject" key="'create-menu'">
</mct-representation>
</div>

View File

@ -19,8 +19,8 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="contents" ng-controller="CreateMenuController">
<div class="pane left menu-items">
<div class="w-menu" ng-controller="CreateMenuController">
<div class="col menu-items">
<ul>
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
@ -31,13 +31,15 @@
</li>
</ul>
</div>
<div class="pane right menu-item-description">
<div class="col menu-item-description">
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
<div class="desc-area title">
{{representation.activeMetadata.name}}
</div>
<div class="desc-area description">
{{representation.activeMetadata.description}}
<div class="w-title-desc">
<div class="desc-area title">
{{representation.activeMetadata.name}}
</div>
<div class="desc-area description">
{{representation.activeMetadata.description}}
</div>
</div>
</div>
</div>

View File

@ -2,7 +2,7 @@
"metadata": {
"name": "openmct-symbols-16px",
"lastOpened": 0,
"created": 1505151140023
"created": 1506973656040
},
"iconSets": [
{
@ -899,6 +899,14 @@
"prevSize": 24,
"code": 921904,
"tempChar": ""
},
{
"order": 139,
"id": 117,
"name": "icon-notebook",
"prevSize": 24,
"code": 921905,
"tempChar": ""
}
],
"metadata": {
@ -3524,6 +3532,29 @@
{}
]
}
},
{
"id": 117,
"paths": [
"M896 110.8c0-79.8-55.4-127.4-123-105.4l-773 250.6h896v-145.2z",
"M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM832 832h-384v-320h384v320z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-notebook"
],
"colorPermutations": {
"1161751207457516161751": [
{},
{}
]
}
}
],
"colorThemes": [

View File

@ -118,4 +118,5 @@
<glyph unicode="&#xe1128;" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
<glyph unicode="&#xe1129;" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
<glyph unicode="&#xe1130;" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
<glyph unicode="&#xe1131;" glyph-name="icon-notebook" d="M896 849.2c0 79.8-55.4 127.4-123 105.4l-773-250.6h896v145.2zM896 640h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM832 128h-384v320h384v-320z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -111,7 +111,9 @@ $bubbleMaxW: 300px;
$reqSymbolW: 15px;
$reqSymbolM: $interiorMargin * 2;
$reqSymbolFontSize: 0.75em;
$inputTextP: 3px 5px;
$inputTextPTopBtm: 3px;
$inputTextPLeftRight: 5px;
$inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
/*************** Wait Spinner Defaults */
$waitSpinnerD: 32px;
$waitSpinnerTreeD: 20px;

View File

@ -52,6 +52,7 @@
font-size: 0.8rem;
$p: 1px;
line-height: 100%;
overflow: hidden;
&.l-static-text {
padding: $p;
}

View File

@ -146,6 +146,7 @@ $glyph-icon-timer: '\e1127';
$glyph-icon-topic: '\e1128';
$glyph-icon-box-with-dashed-lines: '\e1129';
$glyph-icon-summary-widget: '\e1130';
$glyph-icon-notebook: '\e1131';
/************************** 16 PX CLASSES */
@ -260,6 +261,7 @@ $glyph-icon-summary-widget: '\e1130';
.icon-topic { @include glyphBefore($glyph-icon-topic); }
.icon-box-with-dashed-lines { @include glyphBefore($glyph-icon-box-with-dashed-lines); }
.icon-summary-widget { @include glyphBefore($glyph-icon-summary-widget); }
.icon-notebook { @include glyphBefore($glyph-icon-notebook); }
/************************** 12 PX CLASSES */
.icon-crosshair-12px { @include glyphBefore($glyph-icon-crosshair,'symbolsfont-12px'); }

View File

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

View File

@ -316,23 +316,28 @@
text-shadow: $shdwItemText;
}
@mixin input-base($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
@mixin input-base() {
@include appearance(none);
border-radius: $controlCr;
box-sizing: border-box;
box-shadow: inset $shdw;
background: $bg;
border: none;
color: $fg;
outline: none;
&:focus { outline: 0; }
&.error {
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
}
}
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg) {
@include input-base($bg, $fg);
@mixin s-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
@include input-base();
background: $bg;
box-shadow: inset $shdw;
color: $fg;
}
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
@include s-input($bg, $fg, $shdw);
border: none;
outline: none;
}
@mixin contextArrow() {
@ -344,7 +349,7 @@
}
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {
@include input-base($bg, $fg);
@include nice-input($bg, $fg);
padding: $interiorMargin;
}

View File

@ -33,7 +33,6 @@ $pad: $interiorMargin * $baseRatio;
height: $btnStdH;
line-height: $btnStdH;
padding: 0 $pad;
vertical-align: top;
&.labeled:before {
// Icon when it's included

View File

@ -72,11 +72,13 @@
}
}
// Hyperlink objects
.s-hyperlink {
// Hyperlink objects
.label {
font-size: 0.8rem !important;
}
&:not(.s-button) {
color: $colorKey;
font-size: 0.8rem;
&:hover { color: $colorKeyHov; }
}
}
@ -232,12 +234,16 @@ textarea {
}
/******************************************************** INPUTS */
%input-base {
@include input-base();
}
input[type="text"],
input[type="search"],
input[type="number"] {
@include nice-input();
vertical-align: baseline;
padding: $inputTextP;
padding: $inputTextPTopBtm $inputTextPLeftRight;
&.numeric {
text-align: right;
}
@ -281,6 +287,32 @@ textarea.lg { position: relative; height: 300px; }
}
}
*[contenteditable].s-input-inline,
input[type="text"].s-input-inline,
.s-input-inline input[type="text"] {
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
@extend %input-base;
@include trans-prop-nice((padding, box-shadow), 250ms);
background: none;
box-shadow: none;
border: 1px solid transparent;
min-width: 20px;
padding-left: 0;
padding-right: 0;
&:hover,
&:focus {
padding-left: $inputTextPLeftRight;
padding-right: $inputTextPLeftRight;
}
&:hover {
border-color: rgba($colorBodyFg, 0.2);
}
&:focus {
@include s-input();
border-color: transparent;
}
}
/******************************************************** SELECTS */
.select {
@include btnSubtle($bg: $colorSelectBg);
@ -296,6 +328,7 @@ textarea.lg { position: relative; height: 300px; }
select {
@include appearance(none);
box-sizing: border-box;
&:focus { outline: 0; }
background: none;
color: $colorSelectFg;
cursor: pointer;
@ -307,9 +340,8 @@ textarea.lg { position: relative; height: 300px; }
}
}
&:before {
//@include contextArrow();
pointer-events: none;
color: rgba($colorSelectFg, percentToDecimal($contrastInvokeMenuPercent));
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
display: block;
position: absolute;
right: $interiorMargin; top: 0;
@ -721,11 +753,15 @@ body.desktop {
}
.overlay ::-webkit-scrollbar-thumb {
$lr: 15%;
background: $scrollbarThumbColorOverlay;
&:hover { background: $scrollbarThumbColorOverlayHov; }
}
.menu ::-webkit-scrollbar-thumb {
background: $scrollbarThumbColorMenu;
&:hover { background: $scrollbarThumbColorMenuHov; }
}
::-webkit-scrollbar-corner {
background: $scrollbarTrackColorBg;
}

View File

@ -21,92 +21,87 @@
*****************************************************************************/
/******************************************************** MENU BUTTONS */
.s-menu-button {
// Formerly .btn-menu
@extend .s-button;
span.l-click-area {
// In markup, this element should not enclose anything.
@extend .abs;
}
// Formerly .btn-menu
@extend .s-button;
span.l-click-area {
// In markup, this element should not enclose anything.
@extend .abs;
}
.icon {
font-size: 16px; //120%;
}
.icon {
font-size: 16px; //120%;
}
.title-label {
margin-left: $interiorMarginSm;
}
.title-label {
margin-left: $interiorMarginSm;
}
.color-swatch {
// Used in color menu buttons in toolbar
$d: 10px;
display: inline-block;
border: 1px solid rgba($colorBtnFg, 0.2);
height: $d; width: $d;
height: $d;
width: $d;
vertical-align: middle;
margin-left: $interiorMarginSm;
margin-top: -2px;
}
&:after {
// Adds the downward facing 'context available / invoke menu' arrow element
@include contextArrow();
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
}
&:after {
// Adds the downward facing 'context available / invoke menu' arrow element
@include contextArrow();
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
}
&.create-button {
&.create-button {
@extend .icon-plus;
.title-label {
font-size: 1rem;
}
}
.title-label {
font-size: 1rem;
}
}
.menu {
left: 0;
text-align: left;
}
.menu {
left: 0;
text-align: left;
}
}
/******************************************************** MENUS THEMSELVES */
.menu-element {
cursor: pointer;
position: relative;
}
.s-menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);
@include boxShdw($shdwMenu);
@include txtShdw($shdwMenuText);
padding: $interiorMarginSm 0;
cursor: pointer;
position: relative;
}
.menu {
// TODO: reduce size of icons
@extend .s-menu;
display: block;
position: absolute;
z-index: 10;
ul {
@include menuUlReset();
li {
box-sizing: border-box;
border-top: 1px solid pullForward($colorMenuBg, 10%);
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);
@include boxShdw($shdwMenu);
@include txtShdw($shdwMenuText);
padding: $interiorMarginSm 0;
display: block;
position: absolute;
z-index: 10;
ul {
@include menuUlReset();
li {
box-sizing: border-box;
border-top: 1px solid pullForward($colorMenuBg, 10%);
color: $colorMenuFg;
//color: pullForward($colorMenuBg, 60%);
line-height: $menuLineH;
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
position: relative;
white-space: nowrap;
&:first-child {
border: none;
}
&:hover {
background: $colorMenuHovBg;
color: $colorMenuHovFg;
line-height: $menuLineH;
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
position: relative;
white-space: nowrap;
&:first-child {
border: none;
}
&:hover {
background: $colorMenuHovBg;
color: $colorMenuHovFg;
&:before {
color: $colorMenuHovIc;
}
}
}
&:before {
@extend .ui-symbol;
@extend .type-icon;
@ -114,8 +109,8 @@
display: inline-block;
left: $interiorMargin * 2;
}
}
}
}
}
}
.menu,
@ -123,94 +118,97 @@
.context-menu,
.super-menu,
.s-menu-button .menu {
pointer-events: auto;
ul li {
a.menu-item-a {
pointer-events: auto;
ul li {
a.menu-item-a {
color: $colorMenuFg;
display: block;
}
}
&:before,
a.menu-item-a:before {
color: $colorMenuIc;
left: $interiorMargin;
}
}
}
}
.checkbox-menu {
// Used in search dropdown in tree
@extend .context-menu;
ul li {
padding-left: 50px;
.checkbox {
$d: 0.7rem;
position: absolute;
left: $interiorMargin;
top: ($menuLineH - $d) / 1.5;
em {
height: $d;
width: $d;
&:before {
font-size: 7px !important;
height: $d;
width: $d;
line-height: $d;
}
}
}
&:before {
// Used in search dropdown in tree
@extend .context-menu;
ul li {
padding-left: 50px;
.checkbox {
$d: 0.7rem;
position: absolute;
left: $interiorMargin;
top: ($menuLineH - $d) / 1.5;
em {
height: $d;
width: $d;
&:before {
font-size: 7px !important;
height: $d;
width: $d;
line-height: $d;
}
}
}
&:before {
// Type icon
left: 25px;
}
}
left: 25px;
}
}
}
.super-menu,
.super-menu > mct-representation,
.super-menu > .contents {
box-sizing: border-box;
display: block;
position: relative;
}
.super-menu {
$w: 500px;
$h: $w - 20;
$plw: 50%;
$prw: 50%;
display: block;
width: $w;
height: $h;
.contents {
@include absPosDefault($interiorMargin);
}
.pane {
box-sizing: border-box;
&.menu-items {
border-right: 1px solid pullForward($colorMenuBg, 10%);
left: 0;
padding-right: $interiorMargin;
right: auto;
width: $plw;
overflow-x: hidden;
overflow-y: auto;
ul {
li {
border-radius: $controlCr;
padding-left: 30px;
border-top: none;
}
}
}
&.menu-item-description {
left: auto;
right: 0;
padding: $interiorMargin * 5;
width: $prw;
$plw: 50%;
$prw: 100% - $plw;
position: absolute;
.w-menu {
align-items: stretch;
display: flex;
flex-direction: row;
margin: $interiorMarginLg;
}
.col {
box-sizing: border-box;
flex: 1 1 auto;
overflow-x: hidden;
&.menu-items {
border-right: 1px solid pullForward($colorMenuBg, 10%);
overflow-y: auto;
padding-right: $interiorMargin;
width: $plw;
ul {
li {
border-radius: $controlCr;
padding-left: 30px;
border-top: none;
}
}
}
&.menu-item-description {
$p: $interiorMargin * 3;
overflow-y: hidden;
padding: $p $p 0 $p;
width: $prw;
.desc-area {
&.icon {
color: $colorCreateMenuLgIcon;
font-size: 8em;
margin-bottom: $interiorMargin * 3;
position: relative;
text-align: center;
}
&.title {
color: $colorCreateMenuText;
font-size: 1.2em;
margin-bottom: $interiorMargin * 2;
}
&.description {
color: pushBack($colorCreateMenuText, 20%);
@ -218,67 +216,104 @@
line-height: 1.5em;
}
}
}
}
}
}
.w-title-desc {
display: flex;
flex-direction: column;
overflow: hidden; // Height set in specific menu instances
}
// Specific menu instances
&.l-create-menu {
width: 500px;
.col {
max-height: 70vh;
}
.w-title-desc {
height: 190px;
}
.desc-area {
&.icon {
font-size: 8em;
height: 135px;
margin-bottom: $interiorMargin * 3;
}
&.title {
font-size: 1.2em;
margin-bottom: $interiorMargin * 2;
}
}
}
&.mini {
width: 400px;
height: 300px;
.pane {
.col {
max-height: 50vh;
&.menu-items {
font-size: 0.8em;
}
&.menu-item-description {
padding: $interiorMargin * 3;
.desc-area {
&.icon {
font-size: 4em;
}
&.title {
font-size: 1em;
}
}
$p: $interiorMargin * 2;
padding: $p $p 0 $p;
}
}
.w-title-desc {
height: 180px;
}
.desc-area {
&.icon {
font-size: 4em;
height: 70px;
margin-bottom: $interiorMargin * 3;
}
&.title {
font-size: 1em;
margin-bottom: $interiorMargin * 2;
}
}
}
}
.context-menu {
font-size: 0.80rem;
font-size: 0.80rem;
}
.context-menu-holder,
.menu-holder {
position: absolute;
z-index: 120;
.context-menu-wrapper {
position: absolute;
height: 100%;
width: 100%;
}
&.go-left .context-menu,
&.go-left .menu {
right: 0;
}
&.go-up .context-menu,
&.go-up .menu {
bottom: 0;
}
position: absolute;
z-index: 120;
.context-menu-wrapper {
position: absolute;
height: 100%;
width: 100%;
}
&.go-left .context-menu,
&.go-left .menu {
right: 0;
}
&.go-up .context-menu,
&.go-up .menu {
bottom: 0;
}
}
.context-menu-holder {
pointer-events: none;
height: 200px;
width: 170px;
pointer-events: none;
height: 200px;
width: 170px;
}
.btn-bar.right .menu,
.menus-to-left .menu {
z-index: 79;
left: auto;
right: 0;
width: auto;
left: auto;
right: 0;
width: auto;
}
.menus-up .menu {
bottom: $btnStdH; top: auto;
bottom: $btnStdH;
top: auto;
}

View File

@ -129,9 +129,6 @@
}
.s-filter {
input[type="search"] {
@include input-base();
}
.clear-icon,
.menu-icon,
&:before {

View File

@ -156,6 +156,8 @@
left: 0;
right: 0;
overflow: auto;
padding-right: $interiorMargin;
padding-bottom: $interiorMargin;
.field.l-input-med {
input[type='text'] {
width: 100%;

View File

@ -47,7 +47,7 @@
&.t-object-type-hyperlink {
// Hide the right side buttons for objects where they don't make sense
// Note that this will hide the view Switcher button if applied
// to an object that it.
// to an object that has it.
.object-browse-bar .right { display: none; }
}
@ -120,9 +120,17 @@
}
}
/********************************************************** OBJECT TYPES */
&.t-frame-outer .s-input-inline {
// Prevent inline inputs from being edited when nested in a Layout
pointer-events: none !important;
}
/********************************************************** OBJECT TYPES */
.t-object-type-hyperlink {
.s-hyperlink.s-button {
.object-holder {
overflow: hidden;
}
.l-hyperlink.s-button {
// When a hyperlink is a button in a frame, make it expand to fill out to the object-holder
@extend .abs;
.label {

View File

@ -240,7 +240,9 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
.top-bar .buttons-main .s-button,
.top-bar .s-menu-button,
.tool-bar .s-button,
.tool-bar .s-menu-button {
.tool-bar .s-menu-button,
.tool-bar .select,
.tool-bar .input-labeled {
$h: $btnToolbarH;
height: $h;
line-height: $h;

View File

@ -20,6 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.tool-bar {
font-size: 0.7rem;
&.btn-bar {
white-space: nowrap;
}
@ -30,9 +31,7 @@
input[type="search"],
input[type="number"] {
box-sizing: border-box;
font-size: .8em;
height: $btnToolbarH;
margin-bottom: 1px;
position: relative;
&.sm {
width: $btnToolbarH;

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<!-- look at action-button for example -->
<span class="t-filter l-filter s-filter"
<span class="t-filter l-filter"
ng-controller="GetterSetterController">
<input type="search"
class="t-filter-input"

View File

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

View File

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

View File

@ -201,13 +201,15 @@ $shdwItemTreeIcon: 0.6;
$colorThumbHoverBg: $colorItemTreeHoverBg;
// Scrollbar
$scrollbarTrackSize: 10px;
$scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px;
$scrollbarTrackColorBg: rgba(#000, 0.4);
$scrollbarTrackSize: 7px;
$scrollbarTrackShdw: rgba(#000, 0.5) 0 1px 5px;
$scrollbarTrackColorBg: transparent; //rgba(#000, 0.4);
$scrollbarThumbColor: pullForward($colorBodyBg, 10%);
$scrollbarThumbColorHov: pullForward($scrollbarThumbColor, 2%);
$scrollbarThumbColorOverlay: pullForward($colorOvrBg, 10%);
$scrollbarThumbColorOverlayHov: pullForward($scrollbarThumbColorOverlay, 2%);
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 20%);
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
// Splitter
// All splitterD* values MUST both be either odd or even numbers

View File

@ -201,13 +201,15 @@ $shdwItemTreeIcon: none;
$colorThumbHoverBg: $colorItemTreeHoverBg;
// Scrollbar
$scrollbarTrackSize: 10px;
$scrollbarTrackSize: 7px;
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
$scrollbarTrackColorBg: rgba(#000, 0.2);
$scrollbarThumbColor: darken($colorBodyBg, 50%);
$scrollbarThumbColorHov: $colorKey;
$scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%);
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
// Splitter
// All splitterD* values MUST both be either odd or even numbers

View File

@ -94,31 +94,6 @@ define([
}
},
"extensions": {
"versions": [
{
"name": "Version",
"value": "@@version",
"priority": 999
},
{
"name": "Built",
"value": "@@timestamp",
"description": "The date on which this version of the client was built.",
"priority": 990
},
{
"name": "Revision",
"value": "@@revision",
"description": "A unique revision identifier for the client sources.",
"priority": 995
},
{
"name": "Branch",
"value": "@@branch",
"description": "The name of the branch that was used during the build.",
"priority": 994
}
],
"components": [
{
"provides": "objectService",

View File

@ -1,7 +1,7 @@
$ueTimeConductorH: (25px, 16px, 20px); // Heights for Ticks, Data Visualization, Controls elements
$ueTimeConductorRtH: (25px, 3px, 20px); // Heights for elements in Real-time mode
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
$timeCondInputDeltaDefW: 60px; // Default width for delta value inputs, typically 00:00:00
$timeCondInputDeltaDefW: 65px; // Default width for delta value inputs, typically 00:00:00
$timeCondTOIIconD: 12px; // height and width of icon used for TOI indicator
$timeCondTOIValOffset: 0px;
$ticksBlockerFadeW: 50px;

View File

@ -162,9 +162,6 @@
.l-time-conductor-inputs {
pointer-events: auto;
}
input[type="text"] {
@include trans-prop-nice(padding, 250ms);
}
.time-range-input input[type="text"] {
width: $timeCondInputTimeSysDefW;
}
@ -290,18 +287,6 @@
.l-time-conductor-inputs-holder {
.l-time-range-input-w {
input[type="text"]:not(.error) {
background: transparent;
box-shadow: none;
border-radius: 0;
padding-left: 0;
padding-right: 0;
&:hover,
&:focus {
@include nice-input();
padding: $inputTextP;
}
}
.icon-calendar {
display: none;
}
@ -309,8 +294,11 @@
display: none;
}
&.end-date {
// Displays the current time
pointer-events: none;
input[type="text"] {
background: none;
box-shadow: none;
color: pullForward($colorTimeCondKeyBg, 5%);
margin-right: $interiorMargin;
tab-index: -1;

View File

@ -19,8 +19,8 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="contents">
<div class="pane left menu-items">
<div class="w-menu">
<div class="col menu-items">
<ul>
<li ng-repeat="metadata in ngModel.options"
ng-click="ngModel.select(metadata)">
@ -32,13 +32,15 @@
</li>
</ul>
</div>
<div class="pane right menu-item-description">
<div class="col menu-item-description">
<div class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssClass}}"></div>
<div class="desc-area title">
{{ngModel.activeMetadata.name}}
</div>
<div class="desc-area description">
{{ngModel.activeMetadata.description}}
<div class="w-title-desc">
<div class="desc-area title">
{{ngModel.activeMetadata.name}}
</div>
<div class="desc-area description">
{{ngModel.activeMetadata.description}}
</div>
</div>
</div>
</div>

View File

@ -24,7 +24,7 @@
ng-click="modeController.toggle()">
<span class="title-label">{{ngModel.selected.name}}</span>
</div>
<div class="menu super-menu mini mode-selector-menu"
<div class="menu super-menu mini l-mode-selector-menu"
ng-show="modeController.isActive()">
<mct-include key="'mode-menu'"
ng-model="ngModel">

View File

@ -38,7 +38,7 @@
ng-model="boundsModel"
ng-blur="tcController.setOffsetsFromView(boundsModel)"
field="'startOffset'"
class="hrs-min-input">
class="s-input-inline hrs-min-input">
</mct-control>
</span>
</span>
@ -71,7 +71,7 @@
ng-model="boundsModel"
ng-blur="tcController.setOffsetsFromView(boundsModel)"
field="'endOffset'"
class="hrs-min-input">
class="s-input-inline hrs-min-input">
</mct-control>
</span>
</span>

View File

@ -201,7 +201,7 @@ define(
var options = [{
key: 'fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes',
description: 'Query and explore data that falls between two fixed datetimes.',
cssClass: 'icon-calendar'
}];
var clocks = {};

View File

@ -122,14 +122,6 @@ define([
"description": "Set border color",
"control": "color"
},
{
"property": "color",
"cssClass": "icon-T",
"title": "Text color",
"description": "Set text color",
"mandatory": true,
"control": "color"
},
{
"property": "url",
"cssClass": "icon-image",
@ -145,6 +137,27 @@ define([
}
]
},
{
"items": [
{
"property": "color",
"cssClass": "icon-T",
"title": "Text color",
"description": "Set text color",
"mandatory": true,
"control": "color"
},
{
"property": "size",
"title": "Text size",
"description": "Set text size",
"control": "select",
"options": [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
return { "name": size + " px", "value": size + "px" };
})
}
]
},
{
"items": [
{
@ -212,11 +225,7 @@ define([
"control": "numberfield",
"description": "Resize object width",
"min": "1"
}
]
},
{
"items": [
},
{
"property": "useGrid",
"name": "Snap to Grid",

View File

@ -19,10 +19,10 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<a class="s-hyperlink" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
<a class="l-hyperlink s-hyperlink" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
ng-attr-target="{{hyperlink.openNewTab() ? '_blank' : undefined}}"
ng-class="{
's-button': hyperlink.isButton()
}">
<div class="label">{{domainObject.getModel().displayText}}</div>
<span class="label">{{domainObject.getModel().displayText}}</span>
</a>

View File

@ -21,7 +21,7 @@
-->
<div
class="l-fixed-position-text l-telemetry"
ng-style="{ background: ngModel.fill(), 'border-color': ngModel.stroke(), color: ngModel.color() }"
ng-style="{ background: ngModel.fill(), 'border-color': ngModel.stroke(), color: ngModel.color(), 'font-size': ngModel.size() }"
>
<span
class="l-elem l-value l-obj-val-format"

View File

@ -21,7 +21,7 @@
-->
<div
class="l-fixed-position-text l-static-text"
ng-style="{ background: ngModel.fill(), 'border-color': ngModel.stroke(), color: ngModel.color() }"
ng-style="{ background: ngModel.fill(), 'border-color': ngModel.stroke(), color: ngModel.color(), 'font-size': ngModel.size() }"
>
{{ngModel.element.text}}
</div>

View File

@ -23,7 +23,7 @@
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header'"
key="'object-header-frame'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>

View File

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

View File

@ -58,6 +58,19 @@ define(
*/
proxy.text = new AccessorMutator(element, 'text');
/**
* Get and/or set the text size of this element.
*
* @param {string} [size] the new text size (if setting)
* @returns {string} the text size
* @memberof platform/features/layout.TextProxy#
*/
proxy.size = new AccessorMutator(element, 'size');
if (proxy.size() === undefined) {
proxy.size("13px");
}
return proxy;
}

View File

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

View File

@ -35,7 +35,8 @@ define(
y: 2,
width: 42,
height: 24,
fill: "transparent"
fill: "transparent",
size: "20px"
};
testElements = [{}, {}, testElement, {}];
proxy = new TextProxy(
@ -50,6 +51,20 @@ define(
expect(proxy.fill('#FFF')).toEqual('#FFF');
expect(proxy.fill()).toEqual('#FFF');
});
it("provides getter/setter for text size", function () {
expect(proxy.size()).toEqual('20px');
expect(proxy.size('12px')).toEqual('12px');
expect(proxy.size()).toEqual('12px');
});
it("defaults to 13px for unspecified text size", function () {
testElement = {x: 1, y: 2};
proxy = new TextProxy(testElement, 0, [testElement]);
expect(proxy.size()).toEqual('13px');
});
});
}
);

View File

@ -118,7 +118,10 @@ define([
"policies": [
{
"category": "view",
"implementation": PlotViewPolicy
"implementation": PlotViewPolicy,
"depends": [
"openmct"
]
}
],
"representations": [

View File

@ -30,30 +30,32 @@ define(
* @constructor
* @memberof platform/features/plot
*/
function PlotViewPolicy() {
function PlotViewPolicy(openmct) {
this.openmct = openmct;
}
function hasNumericTelemetry(domainObject) {
var telemetry = domainObject &&
domainObject.getCapability('telemetry'),
metadata = telemetry ? telemetry.getMetadata() : {},
ranges = metadata.ranges || [];
PlotViewPolicy.prototype.hasNumericTelemetry = function (domainObject) {
var adaptedObject = domainObject.useCapability('adapter');
// Generally, we want to allow Plot for telemetry-providing
// objects (most telemetry is plottable.) We only want to
// suppress this for telemetry which only has explicitly
// non-numeric values.
return ranges.length === 0 || ranges.some(function (range) {
// Assume format is numeric if it is undefined
// (numeric telemetry is the common case)
return range.format === undefined ||
range.format === 'number';
});
}
if (!adaptedObject.telemetry) {
return domainObject.hasCapability('delegation') &&
domainObject.getCapability('delegation')
.doesDelegateCapability('telemetry');
}
var metadata = this.openmct.telemetry.getMetadata(adaptedObject);
var rangeValues = metadata.valuesForHints(['range']);
if (rangeValues.length === 0) {
return false;
}
return !rangeValues.every(function (value) {
return value.format === 'string';
});
};
PlotViewPolicy.prototype.allow = function (view, domainObject) {
if (view.key === 'plot') {
return hasNumericTelemetry(domainObject);
return this.hasNumericTelemetry(domainObject);
}
return true;

View File

@ -27,51 +27,97 @@ define(
describe("Plot view policy", function () {
var testView,
mockDomainObject,
mockTelemetry,
testMetadata,
testAdaptedObject,
openmct,
telemetryMetadata,
policy;
beforeEach(function () {
testView = { key: "plot" };
testMetadata = {};
testAdaptedObject = { telemetry: {} };
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getModel', 'getCapability']
['useCapability', 'hasCapability', 'getCapability']
);
mockTelemetry = jasmine.createSpyObj(
'telemetry',
['getMetadata']
);
mockDomainObject.getCapability.andCallFake(function (c) {
return c === 'telemetry' ? mockTelemetry : undefined;
mockDomainObject.useCapability.andReturn(testAdaptedObject);
openmct = {
telemetry: jasmine.createSpyObj('telemetryAPI', [
'getMetadata'
])
};
telemetryMetadata = jasmine.createSpyObj('telemetryMetadata', [
'valuesForHints'
]);
telemetryMetadata.valuesForHints.andReturn([]);
openmct.telemetry.getMetadata.andReturn(telemetryMetadata);
policy = new PlotViewPolicy(openmct);
});
it('fetches metadata from telem api', function () {
policy.allow(testView, mockDomainObject);
expect(mockDomainObject.useCapability)
.toHaveBeenCalledWith('adapter');
expect(openmct.telemetry.getMetadata)
.toHaveBeenCalledWith(testAdaptedObject);
expect(telemetryMetadata.valuesForHints)
.toHaveBeenCalledWith(['range']);
});
it('returns false if no ranges exist', function () {
telemetryMetadata.valuesForHints.andReturn([]);
expect(policy.allow(testView, mockDomainObject)).toBe(false);
});
it('returns true if any ranges exist', function () {
telemetryMetadata.valuesForHints.andReturn([{}]);
expect(policy.allow(testView, mockDomainObject)).toBe(true);
});
it('returns false if all ranges are strings', function () {
telemetryMetadata.valuesForHints.andReturn([{
format: 'string'
}, {
format: 'string'
}]);
expect(policy.allow(testView, mockDomainObject)).toBe(false);
});
it('returns true if only some ranges are strings', function () {
telemetryMetadata.valuesForHints.andReturn([{
format: 'string'
}, {}]);
expect(policy.allow(testView, mockDomainObject)).toBe(true);
});
it('returns true for telemetry delegators', function () {
delete testAdaptedObject.telemetry;
mockDomainObject.hasCapability.andCallFake(function (c) {
return c === 'delegation';
});
mockTelemetry.getMetadata.andReturn(testMetadata);
policy = new PlotViewPolicy();
mockDomainObject.getCapability.andReturn(
jasmine.createSpyObj('delegation', [
'doesDelegateCapability'
])
);
mockDomainObject.getCapability('delegation')
.doesDelegateCapability.andCallFake(function (c) {
return c === 'telemetry';
});
expect(policy.allow(testView, mockDomainObject)).toBe(true);
expect(openmct.telemetry.getMetadata).not.toHaveBeenCalled();
});
it("allows the imagery view for domain objects with numeric telemetry", function () {
testMetadata.ranges = [{ key: "foo", format: "number" }];
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
});
it("allows the imagery view for domain objects with unspecified telemetry", function () {
testMetadata.ranges = [{ key: "foo" }];
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
});
it("disallows the imagery view for domain objects without image telemetry", function () {
testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
it('returns true for non-telemetry non-delegators', function () {
delete testAdaptedObject.telemetry;
mockDomainObject.hasCapability.andReturn(false);
expect(policy.allow(testView, mockDomainObject)).toBe(false);
});
it("allows other views", function () {
testView.key = "somethingElse";
testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
expect(policy.allow(testView, mockDomainObject)).toBe(true);
});
});
}
);

View File

@ -47,7 +47,7 @@ define([
"name": "Export as JSON",
"implementation": ExportAsJSONAction,
"category": "contextual",
"cssClass": "icon-save",
"cssClass": "icon-export",
"depends": [
"exportService",
"policyService",
@ -59,7 +59,7 @@ define([
"name": "Import from JSON",
"implementation": ImportAsJSONAction,
"category": "contextual",
"cssClass": "icon-download",
"cssClass": "icon-import",
"depends": [
"exportService",
"identifierService",

View File

@ -25,10 +25,12 @@
*/
define(
[
'../../../src/api/objects/object-utils'
'../../../src/api/objects/object-utils',
'lodash'
],
function (
objectUtils
objectUtils,
_
) {
var ZERO = function () {
@ -189,14 +191,17 @@ define(
return fullRequest;
};
function asSeries(telemetry, defaultDomain, defaultRange, sourceMap) {
function getValue(index, key) {
return telemetry[index][sourceMap[key].source];
}
function asSeries(telemetry, defaultDomain, defaultRange) {
return {
getRangeValue: function (index, range) {
return telemetry[index][range || defaultRange];
return getValue(index, range || defaultRange);
},
getDomainValue: function (index, domain) {
return telemetry[index][domain || defaultDomain];
return getValue(index, domain || defaultDomain);
},
getPointCount: function () {
return telemetry.length;
@ -223,9 +228,11 @@ define(
var telemetryAPI = this.openmct.telemetry;
var metadata = telemetryAPI.getMetadata(domainObject);
var defaultDomain = metadata.valuesForHints(['domain'])[0].source;
var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.source : undefined;
defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.indexBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
telemetryAPI.legacyProvider;
@ -250,7 +257,7 @@ define(
requestTelemetryFromService().then(getRelevantResponse);
} else {
return telemetryAPI.request(domainObject, fullRequest).then(function (telemetry) {
return asSeries(telemetry, defaultDomain, defaultRange);
return asSeries(telemetry, defaultDomain, defaultRange, sourceMap);
});
}
};
@ -286,15 +293,17 @@ define(
var telemetryAPI = this.openmct.telemetry;
var metadata = telemetryAPI.getMetadata(domainObject);
var defaultDomain = metadata.valuesForHints(['domain'])[0].source;
var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.source : undefined;
defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.indexBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
telemetryAPI.legacyProvider;
function update(telemetry) {
callback(asSeries([telemetry], defaultDomain, defaultRange));
callback(asSeries([telemetry], defaultDomain, defaultRange, sourceMap));
}
// Unpack the relevant telemetry series

View File

@ -34,6 +34,7 @@ define(
mockUnsubscribe,
telemetry,
mockTelemetryAPI,
mockMetadata,
mockAPI;
function mockPromise(value) {
@ -90,14 +91,23 @@ define(
"findRequestProvider",
"findSubscriptionProvider"
]);
mockTelemetryAPI.getMetadata.andReturn({
valuesForHints: function (hint) {
var metadatum = {};
metadatum[hint] = "foo";
return [metadatum];
}
mockMetadata = jasmine.createSpyObj('telemetryMetadata', [
'valuesForHints',
'values'
]);
mockMetadata.valuesForHints.andCallFake(function (hints) {
var hint = hints[0];
var metadatum = {
key: 'default' + hint
};
metadatum[hint] = "foo";
return [metadatum];
});
mockTelemetryAPI.getMetadata.andReturn(mockMetadata);
mockAPI = {
telemetry: mockTelemetryAPI,
time: {
@ -150,8 +160,8 @@ define(
key: "testKey", // from model
start: 42, // from argument
domain: 'mockTimeSystem',
domains: [{ domain: "foo" }],
ranges: [{ range: "foo" }]
domains: [{ domain: "foo", key: 'defaultdomain' }],
ranges: [{ range: "foo", key: 'defaultrange' }]
}]);
});
@ -172,8 +182,8 @@ define(
start: 0,
end: 1,
domain: 'mockTimeSystem',
domains: [{ domain: "foo" }],
ranges: [{ range: "foo" }]
domains: [{ domain: "foo", key: 'defaultdomain' }],
ranges: [{ range: "foo", key: 'defaultrange' }]
});
});
@ -191,8 +201,8 @@ define(
start: 0,
end: 1,
domain: 'mockTimeSystem',
domains: [{ domain: "foo" }],
ranges: [{ range: "foo" }]
domains: [{ domain: "foo", key: 'defaultdomain' }],
ranges: [{ range: "foo", key: 'defaultrange' }]
});
});
@ -240,6 +250,21 @@ define(
var mockProvider = {};
var dunzo = false;
mockMetadata.values.andReturn([
{
key: 'defaultrange',
source: 'prop1'
},
{
key: 'defaultdomain',
source: 'prop2'
},
{
key: 'prop3',
source: 'prop3'
}
]);
mockTelemetryAPI.findRequestProvider.andReturn(mockProvider);
mockTelemetryAPI.request.andReturn(Promise.resolve(mockTelemetry));
@ -257,6 +282,18 @@ define(
expect(returnedTelemetry.getDomainValue).toBeDefined();
expect(returnedTelemetry.getRangeValue).toBeDefined();
expect(returnedTelemetry.getPointCount()).toBe(2);
// Default domain + remap should work.
expect(returnedTelemetry.getDomainValue(0)).toBe('val2');
expect(returnedTelemetry.getDomainValue(1)).toBe('val5');
// explicit domain should work
expect(returnedTelemetry.getDomainValue(0, 'prop3')).toBe('val3');
expect(returnedTelemetry.getDomainValue(1, 'prop3')).toBe('val6');
// default range + remap should work
expect(returnedTelemetry.getRangeValue(0)).toBe('val1');
expect(returnedTelemetry.getRangeValue(1)).toBe('val4');
// explicit range should work
expect(returnedTelemetry.getRangeValue(0, 'prop3')).toBe('val3');
expect(returnedTelemetry.getRangeValue(1, 'prop3')).toBe('val6');
});
});
@ -275,8 +312,8 @@ define(
start: 0,
end: 1,
domain: 'mockTimeSystem',
domains: [{ domain: "foo" }],
ranges: [{ range: "foo" }]
domains: [{ domain: "foo", key: "defaultdomain" }],
ranges: [{ range: "foo", key: "defaultrange" }]
}]
);

View File

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

View File

@ -1,7 +0,0 @@
/**
* Open MCT https://nasa.github.io/openmct/
* Version: ${version}
* Built: ${timestamp}
* Revision: ${revision}
* Branch: ${branch}
*/

View File

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

View File

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

View File

@ -20,26 +20,41 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
function Region(element) {
this.activeView = undefined;
this.element = element;
define([
'lodash'
], function (
_
) {
function patchViewCapability(viewConstructor) {
return function makeCapability(domainObject) {
var capability = viewConstructor(domainObject);
var oldInvoke = capability.invoke.bind(capability);
capability.invoke = function () {
var availableViews = oldInvoke();
var newDomainObject = capability
.domainObject
.useCapability('adapter');
return _(availableViews).map(function (v, i) {
var vd = {
view: v,
priority: i + 100 // arbitrary to allow new views to
// be defaults by returning priority less than 100.
};
if (v.provider && v.provider.priority) {
vd.priority = v.provider.priority(newDomainObject);
}
return vd;
})
.sortBy('priority')
.map('view')
.value();
};
return capability;
};
}
Region.prototype.clear = function () {
if (this.activeView) {
this.activeView.destroy();
this.activeView = undefined;
}
};
Region.prototype.show = function (view) {
this.clear();
this.activeView = view;
if (this.activeView) {
this.activeView.show(this.element);
}
};
return Region;
return patchViewCapability;
});

View File

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

View File

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

View File

@ -0,0 +1,45 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
], function (
) {
return function (buildInfo) {
return function (openmct) {
var aliases = { timestamp: "Built" };
var descriptions = {
timestamp: "The date on which this version of Open MCT was built.",
revision: "A unique revision identifier for the client sources.",
branch: "The name of the branch that was used during the build."
};
Object.keys(buildInfo).forEach(function (key) {
openmct.legacyExtension("versions", {
key: key,
name: aliases[key] || (key.charAt(0).toUpperCase() + key.substring(1)),
value: buildInfo[key],
description: descriptions[key]
});
});
};
};
});

View File

@ -20,21 +20,31 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
function AdaptedViewController($scope, openmct) {
function refresh(legacyObject) {
if (!legacyObject) {
$scope.view = undefined;
return;
}
define([
'./plugin'
], function (plugin) {
describe("The buildInfo plugin", function () {
var mockmct;
var testInfo;
var domainObject = legacyObject.useCapability('adapter');
var providers = openmct.mainViews.get(domainObject);
$scope.view = providers[0] && providers[0].view(domainObject);
}
beforeEach(function () {
mockmct = jasmine.createSpyObj('openmct', ['legacyExtension']);
testInfo = { foo: 123, bar: "baz" };
plugin(testInfo)(mockmct);
});
$scope.$watch('domainObject', refresh);
}
return AdaptedViewController;
it("registers versions extensions", function () {
Object.keys(testInfo).forEach(function (key) {
expect(mockmct.legacyExtension).toHaveBeenCalledWith(
"versions",
{
key: key,
name: jasmine.any(String),
value: testInfo[key],
description: undefined
}
);
});
});
});
});

View File

@ -37,8 +37,7 @@ define(['EventEmitter'], function (EventEmitter) {
this.key = 'local';
this.cssClass = 'icon-clock';
this.name = 'Local Clock';
this.description = "Updates every second, providing UTC timestamps from " +
"user's local computer.";
this.description = "Provides UTC timestamps every second from the local system clock.";
this.period = period;
this.timeoutHandle = undefined;

View File

@ -18,6 +18,14 @@
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*
* Open MCT https://nasa.github.io/openmct/
* Version: @@version
* Built: @@timestamp
* Revision: @@revision
* Branch: @@branch
*
* @preserve
*****************************************************************************/
(function (root, factory) {
@ -29,3 +37,9 @@
root.openmct = factory();
}
}(this, function() {
var BUILD_CONSTANTS = {
version: "@@version",
timestamp: "@@timestamp",
revision: "@@revision",
branch: "@@branch"
};

View File

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