Compare commits

..

2 Commits

Author SHA1 Message Date
Andrew Henry
86b3da1e86 Addressed comments from PR 2016-06-24 19:04:42 -07:00
Henry
a910b86109 [Time Conductor] #933 Time Conductor V2 API proposal 2016-06-08 11:03:32 +01:00
159 changed files with 4196 additions and 6713 deletions

View File

@@ -1,4 +1,4 @@
# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
# Open MCT
Open MCT is a web-based platform for mission operations user interface
software.
@@ -7,7 +7,7 @@ software.
A bundle is a group of software components (including source code, declared
as AMD modules, as well as resources such as images and HTML templates)
that is intended to be added or removed as a single unit. A plug-in for
that are intended to be added or removed as a single unit. A plug-in for
Open MCT will be expressed as a bundle; platform components are also
expressed as bundles.
@@ -133,6 +133,6 @@ documentation, may presume an understanding of these terms.
it, and it is thereafter considered the _navigated_ object (until the
user makes another such choice.)
* _space_: A name used to identify a persistence store. Interactions with
persistence will generally involve a `space` parameter in some form, to
persistence with generally involve a `space` parameter in some form, to
distinguish multiple persistence stores from one another (for cases
where there are multiple valid persistence locations available.)

8
app.js
View File

@@ -75,8 +75,6 @@
// Expose everything else as static files
app.use(express['static'](options.directory));
// Finally, open the HTTP server and log the instance to the console
app.listen(options.port, function() {
console.log('Open MCT application running at localhost:' + options.port)
});
}());
// Finally, open the HTTP server
app.listen(options.port);
}());

View File

@@ -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",
"d3": "~4.1.0"
"zepto": "^1.1.6"
}
}

View File

@@ -17,7 +17,6 @@ deployment:
test:
post:
- gulp lint
- gulp checkstyle
general:
branches:

File diff suppressed because it is too large Load Diff

View File

@@ -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
main.js
View File

@@ -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",
"d3": "bower_components/d3/d3.min"
"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"]
},
@@ -56,9 +51,6 @@ requirejs.config({
},
"zepto": {
"exports": "Zepto"
},
"d3": {
"exports": "d3"
}
}
});
@@ -90,7 +82,6 @@ define([
'./platform/features/pages/bundle',
'./platform/features/plot/bundle',
'./platform/features/timeline/bundle',
'./platform/features/conductor-v2/bundle',
'./platform/features/table/bundle',
'./platform/forms/bundle',
'./platform/identity/bundle',

View File

@@ -1,6 +1,6 @@
{
"name": "openmct",
"version": "0.11.0-SNAPSHOT",
"version": "0.10.2-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"express": "^4.13.1",

View File

@@ -24,15 +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/navigation/OrphanNavigationHandler",
"./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",
@@ -45,15 +53,23 @@ define([
BrowseController,
PaneController,
BrowseObjectController,
CreateMenuController,
LocatorController,
MenuArrowController,
NavigationService,
CreationPolicy,
NavigateAction,
OrphanNavigationHandler,
NewTabAction,
FullscreenAction,
CreateActionProvider,
AddActionProvider,
CreationService,
WindowTitler,
browseTemplate,
locatorTemplate,
browseObjectTemplate,
createButtonTemplate,
createMenuTemplate,
gridItemTemplate,
objectHeaderTemplate,
menuArrowTemplate,
@@ -93,9 +109,11 @@ define([
"$scope",
"$route",
"$location",
"$window",
"objectService",
"navigationService",
"urlService",
"policyService",
"DEFAULT_PATH"
]
},
@@ -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,15 +244,19 @@ define([
"implementation": NavigationService
}
],
"policies": [
{
"implementation": CreationPolicy,
"category": "creation"
}
],
"actions": [
{
"key": "navigate",
"implementation": NavigateAction,
"depends": [
"navigationService",
"$q",
"policyService",
"$window"
"$q"
]
},
{
@@ -247,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,
@@ -255,14 +346,6 @@ define([
"$rootScope",
"$document"
]
},
{
"implementation": OrphanNavigationHandler,
"depends": [
"throttle",
"topic",
"navigationService"
]
}
],
"licenses": [

View File

@@ -43,7 +43,7 @@
</mct-representation>
</div>
</div>
<div class="holder l-flex-col flex-elem grows l-object-wrapper l-controls-visible l-time-controller-visible">
<div class="holder l-flex-col flex-elem grows l-object-wrapper">
<div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
<!-- Toolbar and Save/Cancel buttons -->
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
@@ -59,9 +59,4 @@
</mct-representation>
</div>
</div>
<!-- put time conductor in here? -->
<mct-representation mct-object="domainObject"
key="'time-conductor'"
class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder">
</mct-representation>
</div>

View File

@@ -36,14 +36,14 @@
</ul>
</div>
<div class="pane right menu-item-description l-flex-col">
<div class="desc-area flex-elem holder ui-symbol icon type-icon">
<div class="pane right menu-item-description">
<div class="desc-area ui-symbol icon type-icon">
{{representation.activeMetadata.glyph}}
</div>
<div class="desc-area flex-elem holder title">
<div class="desc-area title">
{{representation.activeMetadata.name}}
</div>
<div class="desc-area flex-elem holder description">
<div class="desc-area description">
{{representation.activeMetadata.description}}
</div>
</div>

View File

@@ -44,9 +44,11 @@ define(
$scope,
$route,
$location,
$window,
objectService,
navigationService,
urlService,
policyService,
defaultPath
) {
var path = [ROOT_ID].concat(
@@ -73,10 +75,25 @@ define(
}
function setScopeObjects(domainObject, navigationAllowed) {
// Callback for updating the in-scope reference to the object
// that is currently navigated-to.
function setNavigation(domainObject) {
var navigationAllowed = true;
if (domainObject === $scope.navigatedObject) {
//do nothing;
return;
}
policyService.allow("navigation", $scope.navigatedObject, domainObject, function (message) {
navigationAllowed = $window.confirm(message + "\r\n\r\n" +
" Are you sure you want to continue?");
});
if (navigationAllowed) {
$scope.navigatedObject = domainObject;
$scope.treeModel.selectedObject = domainObject;
navigationService.setNavigation(domainObject);
updateRoute(domainObject);
} else {
//If navigation was unsuccessful (ie. blocked), reset
@@ -86,20 +103,6 @@ define(
}
}
// Callback for updating the in-scope reference to the object
// that is currently navigated-to.
function setNavigation(domainObject) {
if (domainObject === $scope.navigatedObject) {
//do nothing;
return;
}
if (domainObject) {
domainObject.getCapability("action").perform("navigate").then(setScopeObjects.bind(undefined, domainObject));
} else {
setScopeObjects(domainObject, true);
}
}
function navigateTo(domainObject) {
// Check if an object has been navigated-to already...

View File

@@ -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();
});
}
};

View File

@@ -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
);
});
};

View File

@@ -33,12 +33,10 @@ define(
* @constructor
* @implements {Action}
*/
function NavigateAction(navigationService, $q, policyService, $window, context) {
function NavigateAction(navigationService, $q, context) {
this.domainObject = context.domainObject;
this.$q = $q;
this.navigationService = navigationService;
this.policyService = policyService;
this.$window = $window;
}
/**
@@ -47,20 +45,9 @@ define(
* navigation has been updated
*/
NavigateAction.prototype.perform = function () {
var self = this,
navigationAllowed = true;
function allow() {
self.policyService.allow("navigation", self.navigationService.getNavigation(), self.domainObject, function (message) {
navigationAllowed = self.$window.confirm(message + "\r\n\r\n" +
" Are you sure you want to continue?");
});
return navigationAllowed;
}
// Set navigation, and wrap like a promise
return this.$q.when(
allow() && this.navigationService.setNavigation(this.domainObject)
this.navigationService.setNavigation(this.domainObject)
);
};

View File

@@ -1,75 +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.
*****************************************************************************/
define([], function () {
/**
* Navigates away from orphan objects whenever they are detected.
*
* An orphan object is an object whose apparent parent does not
* actually contain it. This may occur in certain circumstances, such
* as when persistence succeeds for a newly-created object but fails
* for its parent.
*
* @param throttle the `throttle` service
* @param topic the `topic` service
* @param navigationService the `navigationService`
* @constructor
*/
function OrphanNavigationHandler(throttle, topic, navigationService) {
var throttledCheckNavigation;
function getParent(domainObject) {
var context = domainObject.getCapability('context');
return context.getParent();
}
function isOrphan(domainObject) {
var parent = getParent(domainObject),
composition = parent.getModel().composition,
id = domainObject.getId();
return !composition || (composition.indexOf(id) === -1);
}
function navigateToParent(domainObject) {
var parent = getParent(domainObject);
return parent.getCapability('action').perform('navigate');
}
function checkNavigation() {
var navigatedObject = navigationService.getNavigation();
if (navigatedObject.hasCapability('context') &&
isOrphan(navigatedObject)) {
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
navigateToParent(navigatedObject);
}
}
}
throttledCheckNavigation = throttle(checkNavigation);
navigationService.addListener(throttledCheckNavigation);
topic('mutation').listen(throttledCheckNavigation);
}
return OrphanNavigationHandler;
});

View File

@@ -37,8 +37,9 @@ define(
mockUrlService,
mockDomainObject,
mockNextObject,
mockWindow,
mockPolicyService,
testDefaultRoot,
mockActionCapability,
controller;
function mockPromise(value) {
@@ -54,14 +55,25 @@ define(
mockScope,
mockRoute,
mockLocation,
mockWindow,
mockObjectService,
mockNavigationService,
mockUrlService,
mockPolicyService,
testDefaultRoot
);
}
beforeEach(function () {
mockWindow = jasmine.createSpyObj('$window', [
"confirm"
]);
mockWindow.confirm.andReturn(true);
mockPolicyService = jasmine.createSpyObj('policyService', [
'allow'
]);
testDefaultRoot = "some-root-level-domain-object";
mockScope = jasmine.createSpyObj(
@@ -116,8 +128,6 @@ define(
mockNextObject.getId.andReturn("next");
mockDomainObject.getId.andReturn(testDefaultRoot);
mockActionCapability = jasmine.createSpyObj('actionCapability', ['perform']);
instantiateController();
});
@@ -201,13 +211,8 @@ define(
mockContext.getPath.andReturn(
[mockRootObject, mockDomainObject, mockNextObject]
);
//Return true from navigate action
mockActionCapability.perform.andReturn(mockPromise(true));
mockNextObject.getCapability.andCallFake(function (c) {
return (c === 'context' && mockContext) ||
(c === 'action' && mockActionCapability);
return c === 'context' && mockContext;
});
mockScope.$on.andReturn(mockUnlisten);
// Provide a navigation change
@@ -220,7 +225,6 @@ define(
mockLocation.path.andReturn("/browse/");
mockNavigationService.setNavigation.andReturn(true);
mockActionCapability.perform.andReturn(mockPromise(true));
// Exercise the Angular workaround
mockNavigationService.addListener.mostRecentCall.args[0]();
@@ -239,9 +243,6 @@ define(
mockScope.navigatedObject = mockDomainObject;
mockNavigationService.setNavigation.andReturn(true);
mockActionCapability.perform.andReturn(mockPromise(true));
mockNextObject.getCapability.andReturn(mockActionCapability);
//Simulate a change in selected tree object
mockScope.treeModel = {selectedObject: mockDomainObject};
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
@@ -253,10 +254,11 @@ define(
it("after failed navigation event resets the selected tree" +
" object", function () {
mockScope.navigatedObject = mockDomainObject;
//Return false from navigation action
mockActionCapability.perform.andReturn(mockPromise(false));
mockNextObject.getCapability.andReturn(mockActionCapability);
mockWindow.confirm.andReturn(false);
mockPolicyService.allow.andCallFake(function (category, object, context, callback) {
callback("unsaved changes");
return false;
});
//Simulate a change in selected tree object
mockScope.treeModel = {selectedObject: mockDomainObject};

View File

@@ -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
);
});

View 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();
});
});
}
);

View File

@@ -31,8 +31,6 @@ define(
var mockNavigationService,
mockQ,
mockDomainObject,
mockPolicyService,
mockWindow,
action;
function mockPromise(value) {
@@ -46,70 +44,25 @@ define(
beforeEach(function () {
mockNavigationService = jasmine.createSpyObj(
"navigationService",
[
"setNavigation",
"getNavigation"
]
["setNavigation"]
);
mockNavigationService.getNavigation.andReturn({});
mockQ = { when: mockPromise };
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockPolicyService = jasmine.createSpyObj("policyService",
[
"allow"
]);
mockWindow = jasmine.createSpyObj("$window",
[
"confirm"
]);
action = new NavigateAction(
mockNavigationService,
mockQ,
mockPolicyService,
mockWindow,
{ domainObject: mockDomainObject }
);
});
it("invokes the policy service to determine if navigation" +
" allowed", function () {
it("invokes the navigate service when performed", function () {
action.perform();
expect(mockPolicyService.allow)
.toHaveBeenCalledWith("navigation", jasmine.any(Object), jasmine.any(Object), jasmine.any(Function));
});
it("prompts user if policy rejection", function () {
action.perform();
expect(mockPolicyService.allow).toHaveBeenCalled();
mockPolicyService.allow.mostRecentCall.args[3]();
expect(mockWindow.confirm).toHaveBeenCalled();
});
describe("shows a prompt", function () {
beforeEach(function () {
// Ensure the allow callback is called synchronously
mockPolicyService.allow.andCallFake(function () {
return arguments[3]();
});
});
it("does not navigate on prompt rejection", function () {
mockWindow.confirm.andReturn(false);
action.perform();
expect(mockNavigationService.setNavigation)
.not.toHaveBeenCalled();
});
it("does navigate on prompt acceptance", function () {
mockWindow.confirm.andReturn(true);
action.perform();
expect(mockNavigationService.setNavigation)
.toHaveBeenCalled();
});
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDomainObject);
});
it("is only applicable when a domain object is in context", function () {

View File

@@ -1,180 +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.
*****************************************************************************/
define([
'../../src/navigation/OrphanNavigationHandler'
], function (OrphanNavigationHandler) {
describe("OrphanNavigationHandler", function () {
var mockTopic,
mockThrottle,
mockMutationTopic,
mockNavigationService,
mockDomainObject,
mockParentObject,
mockContext,
mockActionCapability,
mockEditor,
testParentModel,
testId,
mockThrottledFns;
beforeEach(function () {
testId = 'some-identifier';
mockThrottledFns = [];
testParentModel = {};
mockTopic = jasmine.createSpy('topic');
mockThrottle = jasmine.createSpy('throttle');
mockNavigationService = jasmine.createSpyObj('navigationService', [
'getNavigation',
'addListener'
]);
mockMutationTopic = jasmine.createSpyObj('mutationTopic', [
'listen'
]);
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
]);
mockParentObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
]);
mockContext = jasmine.createSpyObj('context', ['getParent']);
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
mockEditor = jasmine.createSpyObj('editor', ['isEditContextRoot']);
mockThrottle.andCallFake(function (fn) {
var mockThrottledFn =
jasmine.createSpy('throttled-' + mockThrottledFns.length);
mockThrottledFn.andCallFake(fn);
mockThrottledFns.push(mockThrottledFn);
return mockThrottledFn;
});
mockTopic.andCallFake(function (k) {
return k === 'mutation' && mockMutationTopic;
});
mockDomainObject.getId.andReturn(testId);
mockDomainObject.getCapability.andCallFake(function (c) {
return {
context: mockContext,
editor: mockEditor
}[c];
});
mockDomainObject.hasCapability.andCallFake(function (c) {
return !!mockDomainObject.getCapability(c);
});
mockParentObject.getModel.andReturn(testParentModel);
mockParentObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability
}[c];
});
mockContext.getParent.andReturn(mockParentObject);
mockNavigationService.getNavigation.andReturn(mockDomainObject);
mockEditor.isEditContextRoot.andReturn(false);
return new OrphanNavigationHandler(
mockThrottle,
mockTopic,
mockNavigationService
);
});
it("listens for mutation with a throttled function", function () {
expect(mockMutationTopic.listen)
.toHaveBeenCalledWith(jasmine.any(Function));
expect(mockThrottledFns.indexOf(
mockMutationTopic.listen.mostRecentCall.args[0]
)).not.toEqual(-1);
});
it("listens for navigation changes with a throttled function", function () {
expect(mockNavigationService.addListener)
.toHaveBeenCalledWith(jasmine.any(Function));
expect(mockThrottledFns.indexOf(
mockNavigationService.addListener.mostRecentCall.args[0]
)).not.toEqual(-1);
});
[false, true].forEach(function (isOrphan) {
var prefix = isOrphan ? "" : "non-";
describe("for " + prefix + "orphan objects", function () {
beforeEach(function () {
testParentModel.composition = isOrphan ? [] : [testId];
});
[false, true].forEach(function (isEditRoot) {
var caseName = isEditRoot ?
"that are being edited" : "that are not being edited";
function itNavigatesAsExpected() {
if (isOrphan && !isEditRoot) {
it("navigates to the parent", function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
} else {
it("does nothing", function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
}
}
describe(caseName, function () {
beforeEach(function () {
mockEditor.isEditContextRoot.andReturn(isEditRoot);
});
describe("when navigation changes", function () {
beforeEach(function () {
mockNavigationService.addListener.mostRecentCall
.args[0](mockDomainObject);
});
itNavigatesAsExpected();
});
describe("when mutation occurs", function () {
beforeEach(function () {
mockMutationTopic.listen.mostRecentCall
.args[0](mockParentObject);
});
itNavigatesAsExpected();
});
});
});
});
});
});
});

View File

@@ -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": [
@@ -170,7 +136,7 @@ define([
"navigationService",
"$log"
],
"description": "Edit",
"description": "Edit this object.",
"category": "view-control",
"glyph": "p"
},
@@ -206,9 +172,7 @@ define([
"implementation": SaveAction,
"name": "Save",
"description": "Save changes made to these objects.",
"depends": [
"dialogService"
],
"depends": [],
"priority": "mandatory"
},
{
@@ -255,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": [
{
@@ -300,17 +261,6 @@ define([
{
"key": "topbar-edit",
"template": topbarEditTemplate
},
{
"key": "create-button",
"template": createButtonTemplate
},
{
"key": "create-menu",
"template": createMenuTemplate,
"uses": [
"action"
]
}
],
"components": [
@@ -332,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": [
{
@@ -399,12 +316,6 @@ define([
"transactionService"
]
}
],
"controls": [
{
"key": "locator",
"template": locatorTemplate
}
]
}
});

View File

@@ -50,24 +50,18 @@ define(
//If the object existed already, navigate to refresh view
// with previous object state.
if (domainObject.getModel().persisted) {
return domainObject.getCapability("action").perform("navigate");
domainObject.getCapability("action").perform("navigate");
} else {
//If the object was new, and user has cancelled, then
//navigate back to parent because nothing to show.
return domainObject.getCapability("location").getOriginal().then(function (original) {
domainObject.getCapability("location").getOriginal().then(function (original) {
parent = original.getCapability("context").getParent();
parent.getCapability("action").perform("navigate");
});
}
}
function cancel(allowed) {
return allowed && domainObject.getCapability("editor").cancel();
}
//Do navigation first in order to trigger unsaved changes dialog
return returnToBrowse()
.then(cancel);
return this.domainObject.getCapability("editor").cancel()
.then(returnToBrowse);
};
/**

View File

@@ -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");
};

View File

@@ -21,8 +21,8 @@
*****************************************************************************/
define(
['./SaveInProgressDialog'],
function (SaveInProgressDialog) {
[],
function () {
/**
* The "Save" action; the action triggered by clicking Save from
@@ -33,11 +33,9 @@ define(
* @memberof platform/commonUI/edit
*/
function SaveAction(
dialogService,
context
) {
this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService;
}
/**
@@ -48,8 +46,7 @@ define(
* @memberof platform/commonUI/edit.SaveAction#
*/
SaveAction.prototype.perform = function () {
var domainObject = this.domainObject,
dialog = new SaveInProgressDialog(this.dialogService);
var domainObject = this.domainObject;
function resolveWith(object) {
return function () {
@@ -75,17 +72,8 @@ define(
return object;
}
function hideBlockingDialog(object) {
dialog.hide();
return object;
}
dialog.show();
return doSave()
.then(hideBlockingDialog)
.then(returnToBrowse)
.catch(hideBlockingDialog);
//return doSave().then(returnToBrowse);
return doSave().then(returnToBrowse);
};
/**

View File

@@ -21,14 +21,9 @@
*****************************************************************************/
define([
'../creation/CreateWizard',
'./SaveInProgressDialog'
],
function (
CreateWizard,
SaveInProgressDialog
) {
define(
['../../../browse/src/creation/CreateWizard'],
function (CreateWizard) {
/**
* The "Save" action; the action triggered by clicking Save from
@@ -110,8 +105,7 @@ define([
SaveAsAction.prototype.save = function () {
var self = this,
domainObject = this.domainObject,
copyService = this.copyService,
dialog = new SaveInProgressDialog(this.dialogService);
copyService = this.copyService;
function doWizardSave(parent) {
var wizard = self.createWizard(parent);
@@ -122,16 +116,6 @@ define([
).then(wizard.populateObjectFromInput.bind(wizard));
}
function showBlockingDialog(object) {
dialog.show();
return object;
}
function hideBlockingDialog(object) {
dialog.hide();
return object;
}
function fetchObject(objectId) {
return self.getObjectService().getObjects([objectId]).then(function (objects) {
return objects[objectId];
@@ -156,22 +140,14 @@ define([
.then(resolveWith(clonedObject));
}
function onFailure() {
hideBlockingDialog();
return false;
}
return getParent(domainObject)
.then(doWizardSave)
.then(showBlockingDialog)
.then(getParent)
.then(cloneIntoParent)
.then(commitEditingAfterClone)
.then(hideBlockingDialog)
.catch(onFailure);
.catch(resolveWith(false));
};
/**
* Check if this action is applicable in a given context.
* This will ensure that a domain object is present in the context,

View File

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

View File

@@ -67,17 +67,10 @@ define(
}
function onCancel() {
if (self.domainObject.getModel().persisted !== undefined) {
//Fetch clean model from persistence
return self.persistenceCapability.refresh().then(function (result) {
self.persistPending = false;
return result;
});
} else {
return self.persistenceCapability.refresh().then(function (result) {
self.persistPending = false;
//Model is undefined in persistence, so return undefined.
return self.$q.when(undefined);
}
return result;
});
}
if (this.transactionService.isActive()) {

View File

@@ -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++;
}
});

View File

@@ -24,11 +24,12 @@ define(
["../../src/actions/CancelAction"],
function (CancelAction) {
describe("The Cancel action", function () {
var mockDomainObject,
mockParentObject,
capabilities = {},
parentCapabilities = {},
//TODO: Disabled for NEM Beta
xdescribe("The Cancel action", function () {
var mockLocation,
mockDomainObject,
mockEditorCapability,
mockUrlService,
actionContext,
action;
@@ -41,114 +42,61 @@ define(
}
beforeEach(function () {
mockLocation = jasmine.createSpyObj(
"$location",
["path"]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getCapability",
"hasCapability",
"getModel"
]
["getCapability", "hasCapability"]
);
mockDomainObject.getModel.andReturn({});
mockParentObject = jasmine.createSpyObj(
"parentObject",
[
"getCapability"
]
);
mockParentObject.getCapability.andCallFake(function (name) {
return parentCapabilities[name];
});
capabilities.editor = jasmine.createSpyObj(
mockEditorCapability = jasmine.createSpyObj(
"editor",
["save", "cancel", "isEditContextRoot"]
["save", "cancel"]
);
capabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"perform"
]
);
capabilities.location = jasmine.createSpyObj(
"locationCapability",
[
"getOriginal"
]
);
capabilities.location.getOriginal.andReturn(mockPromise(mockDomainObject));
capabilities.context = jasmine.createSpyObj(
"contextCapability",
[
"getParent"
]
);
capabilities.context.getParent.andReturn(mockParentObject);
parentCapabilities.action = jasmine.createSpyObj(
"actionCapability",
[
"perform"
]
mockUrlService = jasmine.createSpyObj(
"urlService",
["urlForLocation"]
);
actionContext = {
domainObject: mockDomainObject
};
mockDomainObject.getCapability.andCallFake(function (name) {
return capabilities[name];
});
mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andReturn(mockEditorCapability);
mockEditorCapability.cancel.andReturn(mockPromise(true));
mockDomainObject.hasCapability.andCallFake(function (name) {
return !!capabilities[name];
});
capabilities.editor.cancel.andReturn(mockPromise(true));
action = new CancelAction(actionContext);
action = new CancelAction(mockLocation, mockUrlService, actionContext);
});
it("only applies to domain object that is being edited", function () {
capabilities.editor.isEditContextRoot.andReturn(true);
it("only applies to domain object with an editor capability", function () {
expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
capabilities.editor.isEditContextRoot.andReturn(false);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
mockDomainObject.hasCapability.andReturn(false);
mockDomainObject.getCapability.andReturn(undefined);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
});
it("invokes the editor capability's cancel functionality when" +
" performed", function () {
mockDomainObject.getModel.andReturn({persisted: 1});
//Return true from navigate action
capabilities.action.perform.andReturn(mockPromise(true));
it("invokes the editor capability's save functionality when performed", function () {
// Verify precondition
expect(mockEditorCapability.cancel).not.toHaveBeenCalled();
action.perform();
// Should have called cancel
expect(capabilities.editor.cancel).toHaveBeenCalled();
expect(mockEditorCapability.cancel).toHaveBeenCalled();
// Definitely shouldn't call save!
expect(capabilities.editor.save).not.toHaveBeenCalled();
expect(mockEditorCapability.save).not.toHaveBeenCalled();
});
it("navigates to object if existing using navigate action", function () {
mockDomainObject.getModel.andReturn({persisted: 1});
//Return true from navigate action
capabilities.action.perform.andReturn(mockPromise(true));
it("returns to browse when performed", function () {
action.perform();
expect(capabilities.action.perform).toHaveBeenCalledWith("navigate");
});
it("navigates to parent if new using navigate action", function () {
mockDomainObject.getModel.andReturn({persisted: undefined});
action.perform();
expect(parentCapabilities.action.perform).toHaveBeenCalledWith("navigate");
expect(mockLocation.path).toHaveBeenCalledWith(
mockUrlService.urlForLocation("browse", mockDomainObject)
);
});
});
}

View File

@@ -28,7 +28,6 @@ define(
var mockDomainObject,
mockEditorCapability,
actionContext,
dialogService,
mockActionCapability,
capabilities = {},
action;
@@ -37,9 +36,6 @@ define(
return {
then: function (callback) {
return mockPromise(callback(value));
},
catch: function (callback) {
return mockPromise(callback(value));
}
};
}
@@ -68,10 +64,6 @@ define(
actionContext = {
domainObject: mockDomainObject
};
dialogService = jasmine.createSpyObj(
"dialogService",
["showBlockingMessage", "dismiss"]
);
mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andCallFake(function (capability) {
@@ -81,7 +73,8 @@ define(
mockEditorCapability.save.andReturn(mockPromise(true));
mockEditorCapability.isEditContextRoot.andReturn(true);
action = new SaveAction(dialogService, actionContext);
action = new SaveAction(actionContext);
});
it("only applies to domain object with an editor capability", function () {
@@ -111,19 +104,6 @@ define(
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
});
it("shows a dialog while saving", function () {
mockEditorCapability.save.andReturn(new Promise(function () {}));
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(dialogService.dismiss).not.toHaveBeenCalled();
});
it("hides a dialog when saving is complete", function () {
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(dialogService.dismiss).toHaveBeenCalled();
});
});
}
);

View File

@@ -100,9 +100,7 @@ define(
mockDialogService = jasmine.createSpyObj(
"dialogService",
[
"getUserInput",
"showBlockingMessage",
"dismiss"
"getUserInput"
]
);
mockDialogService.getUserInput.andReturn(mockPromise(undefined));
@@ -171,19 +169,6 @@ define(
expect(mockDialogService.getUserInput).toHaveBeenCalled();
});
it("shows a blocking dialog while waiting for save", function () {
mockEditorCapability.save.andReturn(new Promise(function () {}));
action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogService.dismiss).not.toHaveBeenCalled();
});
it("hides the blocking dialog after saving", function () {
action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogService.dismiss).toHaveBeenCalled();
});
});
}
);

View File

@@ -57,15 +57,6 @@ define(
);
mockPersistence.persist.andReturn(fastPromise());
mockPersistence.refresh.andReturn(fastPromise());
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[
"getModel"
]
);
mockDomainObject.getModel.andReturn({persisted: 1});
capability = new TransactionalPersistenceCapability(mockQ, mockTransactionService, mockPersistence, mockDomainObject);
});
@@ -87,20 +78,6 @@ define(
expect(mockPersistence.refresh).toHaveBeenCalled();
});
it("if transaction is active, cancel call is queued that refreshes model when appropriate", function () {
mockTransactionService.isActive.andReturn(true);
capability.persist();
expect(mockTransactionService.addToTransaction).toHaveBeenCalled();
mockDomainObject.getModel.andReturn({});
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
expect(mockPersistence.refresh).not.toHaveBeenCalled();
mockDomainObject.getModel.andReturn({persisted: 1});
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
expect(mockPersistence.refresh).toHaveBeenCalled();
});
it("persist call is only added to transaction once", function () {
mockTransactionService.isActive.andReturn(true);
capability.persist();

View File

@@ -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();
});
});
});
});
}
);

View File

@@ -23,12 +23,10 @@
define([
"./src/FormatProvider",
"./src/UTCTimeFormat",
"./src/DurationFormat",
'legacyRegistry'
], function (
FormatProvider,
UTCTimeFormat,
DurationFormat,
legacyRegistry
) {
@@ -50,10 +48,6 @@ define([
{
"key": "utc",
"implementation": UTCTimeFormat
},
{
"key": "duration",
"implementation": DurationFormat
}
],
"constants": [

View File

@@ -204,7 +204,6 @@ define([
"implementation": TimeRangeController,
"depends": [
"$scope",
"$timeout",
"formatService",
"DEFAULT_TIME_FORMAT",
"now"

View File

@@ -100,6 +100,5 @@
<glyph unicode="&#xe620;" glyph-name="icon-tabular-realtime" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM448 668l25.060-25.32c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.97 30.338 71.571 49.128 117.56 49.128s87.59-18.79 117.544-49.112l50.456-50.997v-152.2c-24.111 8.83-44.678 22.255-61.542 39.342l-75.518 76.318c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.971-30.343-71.575-49.137-117.568-49.137-20.084 0-39.331 3.584-57.137 10.146l1.145 151.831zM320 0h-192c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192zM320 256h-256v192h256v-192zM320 512h-256v192h256v-192zM640 0h-256v192h256v-192zM448 323.38v174.5c1.88-1.74 3.74-3.5 5.56-5.34l75.5-76.3c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.966 30.333 71.56 49.119 117.542 49.119 43.28 0 82.673-16.644 112.128-43.879l-0.11-174.399c-1.88 1.74-3.74 3.5-5.56 5.34l-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.966-30.333-71.56-49.119-117.542-49.119-43.28 0-82.673 16.644-112.128 43.879zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128z" />
<glyph unicode="&#xe621;" glyph-name="icon-tabular-lad" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM64 704h256v-192h-256v192zM64 448h256v-192h-256v192zM128 0c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192h-192zM384 0v192h256v-192h-256zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128zM960 448v-192h-576v192h64v64h-64v192h576v-192h-64v-64h64zM782.32 412.62l-110.32 55.16v172.22c0 17.673-14.327 32-32 32s-32-14.327-32-32v-211.78l145.68-72.84c4.172-2.133 9.1-3.383 14.32-3.383 17.675 0 32.003 14.328 32.003 32.003 0 12.454-7.114 23.247-17.501 28.536z" />
<glyph unicode="&#xe622;" glyph-name="icon-tabular-lad-set" d="M128 192v576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979l-576 0.021c-70.606 0.215-127.785 57.394-128 127.979zM896 960h-576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v576.021c-0.215 70.606-57.394 127.785-127.979 128zM256 768h192v-128h-192v128zM256 576h192v-192h-192v192zM320 192c-35.26 0.214-63.786 28.74-64 63.98v64.020h192v-128h-128zM512 192v128h192v-128h-192zM960 256c-0.214-35.26-28.74-63.786-63.98-64h-128.020v128h192v-64zM960 384h-448v384h448v-384zM832 480c0.002 0 0.005 0 0.007 0 17.673 0 32 14.327 32 32 0 14.055-9.062 25.994-21.662 30.293l-74.345 24.767v104.94c0 17.673-14.327 32-32 32s-32-14.327-32-32v-151.060l117.88-39.3c3.018-1.040 6.495-1.64 10.113-1.64 0.003 0 0.005 0 0.008 0z" />
<glyph unicode="&#xe623;" glyph-name="icon-download" d="M832 384v-255.66l-0.34-0.34-639.66 0.34v255.66h-192v-256c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v256h-192zM512 320l448 448h-256v192h-384v-192h-256l448-448z" />
<glyph unicode="&#xe642;" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -1,91 +0,0 @@
@include keyframes(rotation) {
100% { @include transform(rotate(360deg)); }
}
@include keyframes(rotation-centered) {
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
}
@include keyframes(clock-hands) {
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
}
@include keyframes(clock-hands-sticky) {
0% {
@include transform(translate(-50%, -50%) rotate(0deg));
}
7% {
@include transform(translate(-50%, -50%) rotate(0deg));
}
8% {
@include transform(translate(-50%, -50%) rotate(30deg));
}
15% {
@include transform(translate(-50%, -50%) rotate(30deg));
}
16% {
@include transform(translate(-50%, -50%) rotate(60deg));
}
24% {
@include transform(translate(-50%, -50%) rotate(60deg));
}
25% {
@include transform(translate(-50%, -50%) rotate(90deg));
}
32% {
@include transform(translate(-50%, -50%) rotate(90deg));
}
33% {
@include transform(translate(-50%, -50%) rotate(120deg));
}
40% {
@include transform(translate(-50%, -50%) rotate(120deg));
}
41% {
@include transform(translate(-50%, -50%) rotate(150deg));
}
49% {
@include transform(translate(-50%, -50%) rotate(150deg));
}
50% {
@include transform(translate(-50%, -50%) rotate(180deg));
}
57% {
@include transform(translate(-50%, -50%) rotate(180deg));
}
58% {
@include transform(translate(-50%, -50%) rotate(210deg));
}
65% {
@include transform(translate(-50%, -50%) rotate(210deg));
}
66% {
@include transform(translate(-50%, -50%) rotate(240deg));
}
74% {
@include transform(translate(-50%, -50%) rotate(240deg));
}
75% {
@include transform(translate(-50%, -50%) rotate(270deg));
}
82% {
@include transform(translate(-50%, -50%) rotate(270deg));
}
83% {
@include transform(translate(-50%, -50%) rotate(300deg));
}
90% {
@include transform(translate(-50%, -50%) rotate(300deg));
}
91% {
@include transform(translate(-50%, -50%) rotate(330deg));
}
99% {
@include transform(translate(-50%, -50%) rotate(330deg));
}
100% {
@include transform(translate(-50%, -50%) rotate(360deg));
}
}

View File

@@ -108,9 +108,6 @@
&.grows {
@include flex(1 1 auto);
}
&.contents-align-right {
text-align: right;
}
}
.flex-container {
// Apply to wrapping elements, mct-includes, etc.
@@ -124,18 +121,17 @@
.l-flex-row {
@include flex-direction(row);
&.flex-elem { @include flex(1 1 auto); }
> .flex-elem {
.flex-elem {
height: inherit;
line-height: inherit;
min-width: 0;
&.holder:not(:last-child) { margin-right: $interiorMargin; }
}
.flex-container { @include flex-direction(row); }
}
.l-flex-col {
@include flex-direction(column);
> .flex-elem {
.flex-elem {
min-height: 0;
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
}
@@ -149,8 +145,3 @@
.flex-justify-end {
@include justify-content(flex-end);
}
/********************************************* POPUPS */
.t-popup {
z-index: 75;
}

View File

@@ -48,8 +48,8 @@ $uePaneMiniTabW: 10px;
$uePaneMiniTabCollapsedW: 11px;
$ueEditLeftPaneW: 75%;
$treeSearchInputBarH: 25px;
$ueTimeControlH: (25px, 6px, 20px);
/*************** Panes */
$ueTimeControlH: (33px, 20px, 20px);
// Panes
$ueBrowseLeftPaneTreeMinW: 150px;
$ueBrowseLeftPaneTreeMaxW: 35%;
$ueBrowseLeftPaneTreeW: 25%;
@@ -57,59 +57,48 @@ $ueBrowseRightPaneInspectMinW: 200px;
$ueBrowseRightPaneInspectMaxW: 35%;
$ueBrowseRightPaneInspectW: 20%;
$ueDesktopMinW: 600px;
/*************** Overlay */
// Overlay
$ovrTopBarH: 45px;
$ovrFooterH: 24px;
$overlayMargin: 25px;
/*************** Items */
// Items
$ueBrowseGridItemLg: 200px;
$ueBrowseGridItemTopBarH: 20px;
$ueBrowseGridItemBottomBarH: 30px;
$itemPadLR: 5px;
/*************** Tree */
// Tree
$treeVCW: 10px;
$treeTypeIconH: 1.4em; // was 16px
$treeTypeIconHPx: 16px;
$treeTypeIconW: 18px;
$treeContextTriggerW: 20px;
/*************** Tabular */
// Tabular
$tabularHeaderH: 22px; //18px
$tabularTdPadLR: $itemPadLR;
$tabularTdPadTB: 3px;
/*************** Imagery */
// Imagery
$imageMainControlBarH: 25px;
$imageThumbsD: 120px;
$imageThumbsWrapperH: $imageThumbsD * 1.4;
$imageThumbPad: 1px;
/*************** Ticks */
// Ticks
$ticksH: 25px;
$tickLblVMargin: 3px;
$tickLblH: 15px;
$tickLblW: 50px;
$tickH: $ticksH - $tickLblVMargin - $tickLblH;
$tickW: 1px;
/*************** Plots */
$plotYBarW: 60px;
$plotYLabelMinH: 20px;
$plotYLabelW: 10px;
$plotXBarH: 32px;
$plotLegendH: 20px;
$plotSwatchD: 8px;
// 1: Top, 2: right, 3: bottom, 4: left
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH + $interiorMargin, $plotYBarW);
/* min plot height is based on user testing to find minimum useful height */
$plotMinH: 95px;
/*************** Bubbles */
// Bubbles
$bubbleArwSize: 10px;
$bubblePad: $interiorMargin;
$bubbleMinW: 100px;
$bubbleMaxW: 300px;
/*************** Forms */
// Forms
$reqSymbolW: 15px;
$reqSymbolM: $interiorMargin * 2;
$reqSymbolFontSize: 0.7em;
$inputTextP: 3px 5px;
/*************** Wait Spinner Defaults */
// Wait Spinner Defaults
$waitSpinnerD: 32px;
$waitSpinnerTreeD: 20px;
$waitSpinnerBorderW: 5px;
@@ -135,8 +124,6 @@ $dirImgs: $dirCommonRes + 'images/';
/************************** TIMINGS */
$controlFadeMs: 100ms;
$browseToEditAnimMs: 400ms;
$editBorderPulseMs: 500ms;
/************************** LIMITS */
$glyphLimit: '\e603';

View File

@@ -39,20 +39,15 @@
@include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7);
}
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0) {
@include keyframes($animName) {
from { #{propName}: $propValStart; }
to { #{$propName}: $propValEnd; }
@mixin pulseBorder($c: red, $dur: 500ms, $iteration: infinite, $delay: 0s, $opacity0: 0, $opacity100: 1) {
@include keyframes(pulseBorder) {
0% { border-color: rgba($c, $opacity0); }
100% { border-color: rgba($c, $opacity100); }
}
@include animToParams($animName, $dur: 500ms, $delay: 0)
}
@mixin animToParams($animName, $dur: 500ms, $delay: 0) {
@include animation-name($animName);
@include animation-name(pulseBorder);
@include animation-duration($dur);
@include animation-direction(alternate);
@include animation-iteration-count($iteration);
@include animation-timing-function(ease);
@include animation-delay($delay);
@include animation-fill-mode(both);
@include animation-direction(normal);
@include animation-iteration-count(1);
@include animation-timing-function(ease-in-out);
}
}

View File

@@ -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: $inputTextP;
padding: 3px 5px !important;
}
h1, h2, h3 {

View File

@@ -66,7 +66,8 @@
}
.menu .type-icon,
.tree-item .type-icon {
.tree-item .type-icon,
.super-menu.menu .type-icon {
position: absolute;
}

View File

@@ -21,7 +21,6 @@
*****************************************************************************/
@import "effects";
@import "global";
@import "animations";
@import "archetypes";
@import "about";
@import "text";
@@ -41,7 +40,7 @@
@import "controls/lists";
@import "controls/menus";
@import "controls/messages";
@import "controls/time-conductor";
@import "controls/time-controller";
@import "mobile/controls/menus";
/********************************* FORMS */

View File

@@ -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%,
@@ -185,15 +174,21 @@
}
@mixin sliderTrack($bg: $scrollbarTrackColorBg) {
//$b: 1px solid lighten($bg, 30%);
border-radius: 2px;
box-sizing: border-box;
@include boxIncised(0.7);
background-color: $bg;
//border-bottom: $b;
//border-right: $b;
}
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
//&:before {
//@include trans-prop-nice("border-color", 25ms);
content: '';
display: block;
//height: auto;
pointer-events: none;
position: absolute;
z-index: 2;
@@ -299,6 +294,7 @@
border-radius: $controlCr;
box-sizing: border-box;
color: $fg;
display: inline-block;
}
@mixin btnBase($bg: $colorBodyBg, $bgHovColor: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
@@ -326,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() {
@@ -341,6 +337,7 @@
display: inline-block;
font-family: 'symbolsfont';
margin-left: $interiorMarginSm;
vertical-align: top;
}
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {

View File

@@ -33,11 +33,18 @@ $pad: $interiorMargin * $baseRatio;
.s-btn {
box-sizing: border-box;
display: inline-block;
padding: 0 $pad;
font-size: 0.7rem;
vertical-align: top;
@include btnSubtle($colorBtnBg, $colorBtnBgHov, $colorBtnFg, $colorBtnIcon);
.icon {
font-size: 0.8rem;
color: $colorKey;
}
.title-label {
vertical-align: top;
}
&.lg {
font-size: 1rem;
@@ -51,14 +58,19 @@ $pad: $interiorMargin * $baseRatio;
padding: 0 ($pad / $baseRatio) / 2;
}
&.major,
&.key-edit,
&.key-properties {
&.major {
$bg: $colorBtnMajorBg;
$hc: lighten($bg, 10%);
@include btnSubtle($bg, $hc, $colorBtnMajorFg, $colorBtnMajorFg);
}
&:not(.major) {
// bg, bgHov, fg, ic
@include btnSubtle($colorBtnBg, $colorBtnBgHov, $colorBtnFg, $colorBtnIcon);
}
&.pause-play {
}
&.t-save:before {
content:'\e612';
font-family: symbolsfont;
@@ -97,22 +109,6 @@ $pad: $interiorMargin * $baseRatio;
content: "\000039";
}
}
&.t-export {
&:before {
@extend .ui-symbol;
@extend .icon;
content: '\e623';
}
}
.icon {
font-size: 0.8rem;
}
.title-label {
vertical-align: top;
}
}
.s-icon-btn {
@@ -279,3 +275,4 @@ body.desktop .mini-tab-icon {
color: $colorPausedBg !important;
}
}

View File

@@ -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 {
@@ -420,69 +420,13 @@ input[type="search"] {
}
}
@mixin sliderKnob() {
$h: 16px;
cursor: pointer;
width: floor($h/1.75);
height: $h;
margin-top: 1 + floor($h/2) * -1;
@include btnSubtle(pullForward($colorBtnBg, 10%));
//border-radius: 50% !important;
}
@mixin sliderKnobRound() {
$h: 12px;
cursor: pointer;
width: $h;
height: $h;
margin-top: 1 + floor($h/2) * -1;
@include btnSubtle(pullForward($colorBtnBg, 10%));
border-radius: 50% !important;
}
input[type="range"] {
// HTML5 range inputs
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
background: transparent; /* Otherwise white in Chrome */
&:focus {
outline: none; /* Removes the blue border. */
}
// Thumb
&::-webkit-slider-thumb {
-webkit-appearance: none;
@include sliderKnobRound();
}
&::-moz-range-thumb {
border: none;
@include sliderKnobRound();
}
&::-ms-thumb {
border: none;
@include sliderKnobRound();
}
// Track
&::-webkit-slider-runnable-track {
width: 100%;
height: 3px;
@include sliderTrack();
}
&::-moz-range-track {
width: 100%;
height: 3px;
@include sliderTrack();
}
}
/******************************************************** DATETIME PICKER */
.l-datetime-picker {
$r1H: 15px;
@include user-select(none);
font-size: 0.8rem;
padding: $interiorMarginLg !important;
width: 230px;
.l-month-year-pager {
$pagerW: 20px;
height: $r1H;
@@ -574,19 +518,6 @@ input[type="range"] {
}
}
@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);

View File

@@ -167,7 +167,7 @@
}
.pane {
box-sizing: border-box;
&.menu-items {
&.left {
border-right: 1px solid pullForward($colorMenuBg, 10%);
left: 0;
padding-right: $interiorMargin;
@@ -183,53 +183,38 @@
}
}
}
&.menu-item-description {
&.right {
left: auto;
right: 0;
padding: $interiorMargin * 5;
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%);
font-size: 0.8em;
line-height: 1.5em;
}
}
}
}
&.mini {
width: 400px;
height: 300px;
.pane {
&.menu-items {
font-size: 0.8em;
}
&.menu-item-description {
padding: $interiorMargin * 3;
.desc-area {
&.icon {
font-size: 4em;
}
&.title {
font-size: 1em;
}
}
}
}
}
.menu-item-description {
.desc-area {
&.icon {
$h: 150px;
color: $colorCreateMenuLgIcon;
position: relative;
font-size: 8em;
left: 0;
height: $h;
line-height: $h;
margin-bottom: $interiorMargin * 5;
text-align: center;
}
&.title {
color: $colorCreateMenuText;
font-size: 1.2em;
margin-bottom: 0.5em;
}
&.description {
color: $colorCreateMenuText;
font-size: 0.8em;
line-height: 1.5em;
}
}
}
}
.context-menu {
font-size: 0.80rem;
@@ -266,7 +251,3 @@
right: 0;
width: auto;
}
.menus-up .menu {
bottom: $btnStdH; top: auto;
}

View File

@@ -1,376 +0,0 @@
@mixin toiLineHovEffects() {
&:before,
&:after {
background-color: $timeControllerToiLineColorHov;
}
}
.l-time-conductor-holder {
$minW: 500px;
border-top: 1px solid $colorInteriorBorder;
min-width: $minW;
padding-top: $interiorMargin;
}
.time-conductor-icon {
$c: $colorObjHdrIc;
$d: 20px;
background: $c;
border-radius: 4px;
height: $d !important;
width: $d;
position: relative;
// Icon shape: brackets
&:before,
&:after {
content: '';
background: $colorBodyBg;
position: absolute;
}
&:before {
$in: 7px;
left: $in;
top: 0;
right: $in;
bottom: 0;
}
&:after {
$in: 4px;
left: $in;
top: $in;
right: $in;
bottom: $in;
}
// Clock hands
div[class*="hand"] {
$handW: 2px;
$handH: 8px;
@include transform(translate(-50%, -50%));
@include animation-iteration-count(infinite);
@include animation-timing-function(linear);
position: absolute;
height: $handW;
width: $handW;
left: 50%;
top: 50%;
z-index: 2;
&:before {
background-color: $c;
content: '';
display: block;
position: absolute;
width: 100%;
bottom: -1px;
}
&.hand-little {
z-index: 2;
@include animation-duration(12s);
&:before {
//background: red;
height: ceil($handH * 0.7);
}
}
&.hand-big {
z-index: 1;
@include animation-duration(1s);
&:before {
height: $handH;
}
}
}
}
.l-time-conductor {
$knobHOffset: 0px;
$rangeValPad: $interiorMargin;
$rangeValOffset: $sliderKnobW + $interiorMargin;
$r1H: nth($ueTimeControlH, 1);
$r2H: nth($ueTimeControlH, 2);
$r3H: nth($ueTimeControlH, 3);
// Glyphs Todo: replace with refactored CSS approach when that is merged into master
$glyphIconFixed: '\e604';
$glyphIconRealtime: '\43';
$glyphIconLatest: '\44';
position: relative;
> .l-row-elem {
// First order row elements
box-sizing: border-box;
width: 100%;
position: relative;
}
.mode-selector .s-menu-btn,
.time-delta {
&:before {
@extend .ui-symbol;
}
}
.time-delta {
&:before {
color: $colorTimeCondKeyBg;
}
}
.l-time-conductor-inputs-holder,
.l-time-conductor-ticks,
.l-time-conductor-zoom-w {
font-size: 0.8rem;
}
.l-time-conductor-inputs-holder {
$trInputW: 180px;
$hmInputW: 60px;
$ticksBlockerFadeW: 50px;
$iconCalendarW: 16px;
$wBgColor: $colorBodyBg;
height: $r1H;
z-index: 1;
.l-time-range-w {
// Wraps a datetime text input field
height: 100%;
position: absolute;
&.start-w {
@include background-image(linear-gradient(270deg, transparent, $wBgColor $ticksBlockerFadeW));
padding-right: $ticksBlockerFadeW;
}
&.end-w {
@include background-image(linear-gradient(90deg, transparent, $wBgColor $ticksBlockerFadeW));
padding-left: $ticksBlockerFadeW;
right: 0;
text-align: right;
}
input[type="text"] {
@include trans-prop-nice(padding, 250ms);
}
.time-range-input input {
width: $trInputW;
}
.hrs-min-input input {
width: $hmInputW;
}
.icon-calendar {
margin-top: 4px;
}
}
}
.l-time-conductor-ticks {
$c: $colorTick;
height: $r1H;
mct-conductor-axis {
display: block;
position: relative;
width: 100%;
}
.l-axis-holder {
height: $r1H;
position: relative;
width: 100%;
svg {
text-rendering: geometricPrecision;
width: 100%;
height: 100%;
> g {
font-size: 0.9em;
}
path {
// Line beneath ticks
display: none;
}
line {
// Tick marks
stroke: $c;
}
text {
// Tick labels
fill: $c;
}
}
}
}
.l-data-visualization {
background: $colorTimeCondDataVisBg;
height: $r2H;
}
.l-time-conductor-controls {
align-items: center;
margin-top: $interiorMargin;
.l-time-conductor-zoom-w {
@include justify-content(flex-end);
.time-conductor-zoom {
height: $r3H;
min-width: 100px;
width: 20%;
}
.time-conductor-zoom-current-range {
color: $colorTick;
}
}
}
// Fixed
&.fixed-mode {
.time-conductor-icon div[class*="hand"] {
&.hand-little {
@include transform(rotate(120deg));
}
}
}
// Realtime, latest modes
&.realtime-mode,
&.latest-mode {
.time-conductor-icon {
background: $colorTimeCondKeyBg;
div[class*="hand"] {
@include animation-name(clock-hands);
&:before {
background: $colorTimeCondKeyBg;
}
}
}
.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;
}
}
&.start-date {
pointer-events: none;
}
.icon-calendar {
display: none;
}
&.end-date {
display: none;
}
}
}
&.realtime-mode .time-conductor-icon div[class*="hand"] { @include animation-name(clock-hands); }
&.latest-mode .time-conductor-icon div[class*="hand"] {
@include animation-name(clock-hands-sticky);
&.hand-big { @include animation-duration(5s); }
&.hand-little { @include animation-duration(60s); }
}
.l-data-visualization {
background: $colorTimeCondDataVisRtBg !important
}
.mode-selector .s-menu-btn {
@include btnSubtle($colorTimeCondKeyBg, pullForward($colorTimeCondKeyBg, $ltGamma), $colorTimeCondKeyFg);
}
}
&.fixed-mode {
$i: $glyphIconFixed;
.mode-selector .s-menu-btn:before {
content: $i;
}
}
&.realtime-mode {
$i: $glyphIconRealtime;
.time-delta:before {
content: $i;
}
.mode-selector .s-menu-btn:before {
content: $i;
}
}
&.latest-mode {
$i: $glyphIconLatest;
.time-delta:before {
content: $i;
}
.mode-selector .s-menu-btn:before {
content: $i;
}
}
}
.s-time-range-val {
border-radius: $controlCr;
background-color: $colorInputBg;
padding: 1px 1px 0 $interiorMargin;
}
/******************************************************************** MOBILE */
@include phoneandtablet {
.l-time-conductor {
min-width: 0;
.l-time-range-slider-holder,
.l-time-conductor-ticks {
display: none;
}
}
}
@include phone {
.l-time-conductor {
.l-time-conductor-inputs-holder {
&.l-flex-row,
.l-flex-row {
@include align-items(flex-start);
}
.l-time-range-inputs-elem {
&.type-icon {
margin-top: 3px;
}
}
.l-time-conductor-inputs-holder {
@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;
}
}
}
}
}
}
@include phonePortrait {
.l-time-conductor {
.l-time-conductor-inputs-holder {
.l-time-conductor-inputs-holder {
@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;
}
}

View File

@@ -0,0 +1,354 @@
@mixin toiLineHovEffects() {
&:before,
&:after {
background-color: $timeControllerToiLineColorHov;
}
}
.l-time-controller {
$minW: 500px;
$knobHOffset: 0px;
$knobM: ($sliderKnobW + $knobHOffset) * -1;
$rangeValPad: $interiorMargin;
$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;
top: auto;
}
.l-time-range-slider,
.l-time-range-ticks {
@include absPosDefault(0, visible);
left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset;
}
.l-time-range-inputs-holder {
height: $r1H; bottom: $r2H + $r3H + ($interiorMarginSm * 2);
padding-top: $interiorMargin;
border-top: 1px solid $colorInteriorBorder;
.type-icon {
font-size: 120%;
vertical-align: middle;
}
.l-time-range-input,
.l-time-range-inputs-elem {
margin-right: $interiorMargin;
.lbl {
color: $colorPlotLabelFg;
}
.ui-symbol.icon {
font-size: 11px;
width: 11px;
}
}
}
.l-time-range-slider-holder {
height: $r2H; bottom: $r3H + ($interiorMarginSm * 1);
.range-holder {
box-shadow: none;
background: none;
border: none;
.range {
.toi-line {
$myC: $timeControllerToiLineColor;
$myW: 8px;
@include transform(translateX(50%));
position: absolute;
top: 0; right: 0; bottom: 0px; left: auto;
width: $myW;
height: auto;
z-index: 2;
&:before,
&:after {
background-color: $myC;
content: "";
position: absolute;
}
&:before {
// Vert line
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
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 {
@include toiLineHovEffects;
}
}
}
&:not(:active) {
.knob,
.range {
@include transition-property(left, right);
@include transition-duration(500ms);
@include transition-timing-function(ease-in-out);
}
}
}
.l-time-range-ticks-holder {
height: $r3H;
.l-time-range-ticks {
border-top: 1px solid $colorTick;
.tick {
background-color: $colorTick;
border:none;
height: 5px;
width: 1px;
margin-left: -1px;
position: absolute;
&:first-child {
margin-left: 0;
}
.l-time-range-tick-label {
@include webkitProp(transform, translateX(-50%));
color: $colorPlotLabelFg;
display: inline-block;
font-size: 0.9em;
position: absolute;
top: 8px;
white-space: nowrap;
z-index: 2;
}
}
}
}
.knob {
z-index: 2;
.range-value {
@include trans-prop-nice-fade(.25s);
padding: 0 $rangeValOffset;
position: absolute;
height: $r2H;
line-height: $r2H;
white-space: nowrap;
}
&:hover .range-value {
color: $sliderColorKnobHov;
}
&.knob-l {
margin-left: $knobM;
.range-value {
text-align: right;
right: $rangeValOffset;
}
}
&.knob-r {
margin-right: $knobM;
.range-value {
left: $rangeValOffset;
}
&:hover + .range-holder .range .toi-line {
@include toiLineHovEffects;
}
}
}
.l-time-domain-selector {
position: absolute;
right: 0px;
bottom: 46px;
}
}
.s-time-range-val {
border-radius: $controlCr;
background-color: $colorInputBg;
padding: 1px 1px 0 $interiorMargin;
}
@include phoneandtablet {
.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 {
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 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;
}
}
}
}
}

View File

@@ -1,10 +1,13 @@
.l-image-main-wrapper,
.l-image-main,
.l-image-main-controlbar,
.l-image-thumbs-wrapper {
@include absPosDefault(0, false);
}
/*************************************** MAIN LAYOUT */
.l-image-main-wrapper {
//@include test();
@if $enableImageryThumbs == true {
bottom: $interiorMargin*2 + $imageThumbsWrapperH;
}
@@ -12,14 +15,16 @@
min-width: 150px;
.l-image-main {
background-color: $colorPlotBg;
margin-bottom: $interiorMargin;
bottom: $imageMainControlBarH + $interiorMargin;
}
.l-image-main-controlbar {
&.l-flex-row { @include align-items(center); }
top: auto;
height: $imageMainControlBarH;
}
}
.l-image-thumbs-wrapper {
//@include test(red);
top: auto;
height: $imageThumbsWrapperH;
}
@@ -39,17 +44,24 @@
background-repeat: no-repeat;
}
.l-image-main {
//cursor: crosshair;
}
.l-image-main-controlbar {
//@include test();
font-size: 0.8em;
line-height: inherit;
line-height: $imageMainControlBarH;
.left, .right {
direction: rtl;
overflow: hidden;
}
.left {
//@include test(red);
text-align: left;
}
.right {
//@include test(green);
z-index: 2;
}
.l-date,
@@ -59,6 +71,7 @@
.l-mag {
direction: ltr;
display: inline-block;
//white-space: nowrap;
&:before {
content: "\000049";
}

View File

@@ -19,6 +19,15 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
@include keyframes(rotation) {
100% { @include transform(rotate(360deg)); }
}
@include keyframes(rotation-centered) {
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
}
@mixin spinner($b: 5px, $c: $colorKey) {
@include transform-origin(center);
@include animation-name(rotation-centered);

View File

@@ -24,10 +24,6 @@
height: 100%;
}
.tabular-holder {
@include absPosDefault();
}
.tabular,
table {
box-sizing: border-box;
@@ -166,41 +162,4 @@ table {
min-width: 150px;
}
}
}
/********************************************************** SPECIFIC TABULAR VIEWS */
.tabular-holder {
&.t-exportable {
$btnExportH: 25px;
.l-view-section {
top: $btnExportH + $interiorMargin;
}
}
}
.child-frame {
.tabular-holder {
&.t-exportable {
$btnExportH: $btnFrameH;
.s-btn.t-export {
@include trans-prop-nice(opacity, $dur: 50ms);
opacity: 0;
}
.l-view-section {
@include trans-prop-nice(top, $dur: 150ms, $delay: 50ms);
top: 0;
}
&:hover {
.s-btn.t-export {
@include trans-prop-nice(opacity, 150ms, 100ms);
opacity: 1;
}
.l-view-section {
@include trans-prop-nice(top, $dur: 150ms);
top: $btnExportH + $interiorMargin;
}
}
}
}
}

View File

@@ -19,10 +19,12 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.abs.holder-plot {
// Fend off the scrollbar when less than min-height;
right: $interiorMargin;
}
$yBarW: 60px;
$yLabelW: 10px;
$xBarH: 32px;
$legendH: 20px;
$swatchD: 8px;
$plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBarW); // Top, right, bottom, left
.gl-plot {
color: $colorPlotFg;
@@ -30,7 +32,6 @@
position: relative;
width: 100%;
height: 100%;
min-height: $plotMinH;
.gl-plot-local-controls {
@include trans-prop-nice(opacity, 150ms);
@@ -53,17 +54,17 @@
top: auto;
right: 0;
bottom: $interiorMargin;
left: $plotYBarW;
height: $plotXBarH;
left: $yBarW;
height: $xBarH;
width: auto;
overflow: hidden;
}
&.gl-plot-y {
top: $plotLegendH + $interiorMargin;
top: $legendH + $interiorMargin;
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
width: $plotYBarW;
width: $yBarW;
}
}
@@ -145,7 +146,7 @@
@include transform(translateY(-50%));
min-width: 150px; // Need this due to enclosure of .select
top: 50%;
left: $plotYLabelW + $interiorMargin * 2;
left: $yLabelW + $interiorMargin * 2;
}
.t-plot-display-controls {
@@ -173,7 +174,7 @@
right: 0;
bottom: auto;
left: 0;
height: $plotLegendH;
height: $legendH;
overflow-x: hidden;
overflow-y: auto;
}
@@ -235,8 +236,8 @@
.color-swatch {
border-radius: 2px;
display: inline-block;
height: $plotSwatchD;
width: $plotSwatchD;
height: $swatchD;
width: $swatchD;
}
}
}
@@ -248,8 +249,8 @@
padding: 0px $itemPadLR;
.plot-color-swatch {
border: 1px solid $colorBodyBg;
height: $plotSwatchD + 1;
width: $plotSwatchD + 1;
height: $swatchD + 1;
width: $swatchD + 1;
}
}
}

View File

@@ -54,8 +54,7 @@
height: $ohH;
line-height: $ohH;
padding: 0 $interiorMargin;
> span,
&:before {
> span {
font-size: 0.65rem;
}
}

View File

@@ -129,8 +129,6 @@
}
.primary-pane {
// Clip element that have min-widths
overflow: hidden;
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
z-index: 4;
}
@@ -196,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 {
@@ -217,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;
}
@@ -239,10 +237,30 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
top: $ueTopBarH + $interiorMarginLg;
}
.l-object-wrapper {
@extend .abs;
.object-holder-main {
@extend .abs;
}
.l-edit-controls {
//@include trans-prop-nice((opacity, height), 0.25s);
border-bottom: 1px solid $colorInteriorBorder;
line-height: $ueEditToolBarH;
height: 0px;
opacity: 0;
.tool-bar {
right: $interiorMargin;
}
}
}
.l-object-wrapper-inner {
@include trans-prop-nice-resize(0.25s);
}
.object-browse-bar .s-btn,
.top-bar .buttons-main .s-btn,
.top-bar .s-menu-btn,
@@ -270,9 +288,8 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
.left {
padding-right: $interiorMarginLg;
.l-back {
.l-back:not(.s-status-editing) {
margin-right: $interiorMarginLg;
&.s-status-editing { display: none; }
}
}
}
@@ -359,49 +376,19 @@ body.desktop {
.s-status-editing {
.l-object-wrapper {
$t2Dur: $browseToEditAnimMs;
$t1Dur: $t2Dur / 2;
$pulseDur: $editBorderPulseMs;
$bC0: rgba($colorEditAreaFg, 0.5);
$bC100: rgba($colorEditAreaFg, 1);
background-color: $colorEditAreaBg;
@include pulseBorder($colorEditAreaFg, $dur: 1s, $opacity0: 0.3);
border-radius: $controlCr;
border: 1px dotted $bC0;
// Transition 1
@include keyframes(wrapperIn) {
from { border: 0px dotted transparent; padding: 0; }
to { border: 1px dotted $bC0; padding: 5px; }
background-color: $colorEditAreaBg;
border-color: $colorEditAreaFg;
border-width: 2px;
border-style: dotted;
.l-object-wrapper-inner {
@include absPosDefault(3px, hidden);
}
// Do last
@include keyframes(pulseNew) {
from { border-color: $bC0; }
to { border-color: $bC100; }
}
@include animation-name(wrapperIn, pulseNew);
@include animation-duration($t1Dur, $pulseDur);
@include animation-delay(0s, $t1Dur + $t2Dur);
@include animation-direction(normal, alternate);
@include animation-fill-mode(both, none);
@include animation-iteration-count(1, infinite);
@include animation-timing-function(ease-in-out, linear);
.l-edit-controls {
height: 0;
border-bottom: 1px solid $colorInteriorBorder;
// Transition 2: reveal edit controls
@include keyframes(editIn) {
from { border-bottom: 0px solid transparent; height: 0; margin-bottom: 0; }
to { border-bottom: 1px solid $colorInteriorBorder; height: $ueEditToolBarH + $interiorMargin; margin-bottom: $interiorMargin; }
}
@include animToParams(editIn, $dur: $t2Dur, $delay: $t1Dur);
.tool-bar {
right: $interiorMargin;
}
height: $ueEditToolBarH + $interiorMargin;
margin-bottom: $interiorMargin;
opacity: 1;
}
}
}

View File

@@ -19,28 +19,27 @@
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-mouseup="ngMouseup()"
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"
ng-if="!picker.active && (structure.format === 'utc' || !structure.format)"
ng-click="picker.active = !picker.active"></a>
<!-- If picker active show icon with no onclick to prevent double registration of clicks -->
<a class="ui-symbol icon icon-calendar" ng-if="picker.active"></a>
ng-if="structure.format === 'utc' || !structure.format"
ng-click="picker.active = !picker.active">
</a>
<mct-popup ng-if="picker.active">
<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>

View File

@@ -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-conductor-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">&#x43;</span>
<span class="l-time-range-inputs-elem 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">&#x43;</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>&nbsp;
</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-conductor-ticks flex-elem">
<div class="l-time-range-ticks-holder">
<div class="l-time-range-ticks">
<div
ng-repeat="tick in ticks track by $index"

View File

@@ -72,17 +72,6 @@ define(
if ($scope.ngBlur) {
$scope.ngBlur();
}
// If picker is active, dismiss it when valid value has been selected
// This 'if' is to avoid unnecessary validation if picker is not active
if ($scope.picker.active) {
if ($scope.structure.validate && $scope.structure.validate($scope.ngModel[$scope.field])) {
$scope.picker.active = false;
} else if (!$scope.structure.validate) {
//If picker visible, but no validation function, hide picker
$scope.picker.active = false;
}
}
}
}
@@ -104,6 +93,7 @@ define(
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watch('pickerModel.value', updateFromPicker);
$scope.$watch('textValue', updateFromView);
}
return DateTimeFieldController;

View File

@@ -53,7 +53,7 @@ define([
* format has been otherwise specified
* @param {Function} now a function to return current system time
*/
function TimeRangeController($scope, $timeout, formatService, defaultFormat, now) {
function TimeRangeController($scope, formatService, defaultFormat, now) {
this.$scope = $scope;
this.formatService = formatService;
this.defaultFormat = defaultFormat;
@@ -66,7 +66,6 @@ define([
this.formatter = formatService.getFormat(defaultFormat);
this.formStartChanged = false;
this.formEndChanged = false;
this.$timeout = $timeout;
this.$scope.ticks = [];
@@ -260,23 +259,18 @@ define([
};
TimeRangeController.prototype.updateBoundsFromForm = function () {
var self = this;
//Allow Angular to trigger watches and determine whether values have changed.
this.$timeout(function () {
if (self.formStartChanged) {
self.$scope.ngModel.outer.start =
self.$scope.ngModel.inner.start =
self.$scope.formModel.start;
self.formStartChanged = false;
}
if (self.formEndChanged) {
self.$scope.ngModel.outer.end =
self.$scope.ngModel.inner.end =
self.$scope.formModel.end;
self.formEndChanged = false;
}
});
if (this.formStartChanged) {
this.$scope.ngModel.outer.start =
this.$scope.ngModel.inner.start =
this.$scope.formModel.start;
this.formStartChanged = false;
}
if (this.formEndChanged) {
this.$scope.ngModel.outer.end =
this.$scope.ngModel.inner.end =
this.$scope.formModel.end;
this.formEndChanged = false;
}
};
TimeRangeController.prototype.onFormStartChange = function (

View File

@@ -51,9 +51,7 @@ define(
yMax = yMin + rect.height;
if (x < xMin || x > xMax || y < yMin || y > yMax) {
scope.$apply(function () {
scope.$eval(attrs.mctClickElsewhere);
});
scope.$eval(attrs.mctClickElsewhere);
}
}

View File

@@ -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);
});

View File

@@ -33,7 +33,6 @@ define(
var mockScope,
mockFormatService,
testDefaultFormat,
mockTimeout,
mockNow,
mockFormat,
controller;
@@ -55,10 +54,6 @@ define(
}
beforeEach(function () {
mockTimeout = function (fn) {
return fn();
};
mockScope = jasmine.createSpyObj(
"$scope",
["$apply", "$watch", "$watchCollection"]
@@ -83,7 +78,6 @@ define(
controller = new TimeRangeController(
mockScope,
mockTimeout,
mockFormatService,
testDefaultFormat,
mockNow

View File

@@ -104,8 +104,6 @@ define(
});
it("triggers an evaluation of its related Angular expression", function () {
expect(mockScope.$apply).toHaveBeenCalled();
mockScope.$apply.mostRecentCall.args[0]();
expect(mockScope.$eval)
.toHaveBeenCalledWith(testAttrs.mctClickElsewhere);
});

View File

@@ -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,

View File

@@ -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();
}
};
}

View File

@@ -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();
});
});
}
);

View File

@@ -14,7 +14,7 @@ $colorAHov: #fff;
$contrastRatioPercent: 7%;
$hoverRatioPercent: 10%;
$basicCr: 3px;
$controlCr: 2px;
$controlCr: 3px;
$smallCr: 2px;
// Buttons and Controls
@@ -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;
@@ -52,7 +51,7 @@ $colorGridLines: rgba(#fff, 0.05);
$colorInvokeMenu: #fff;
$colorObjHdrTxt: $colorBodyFg;
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
$colorTick: pullForward($colorBodyBg, 20%);
$colorTick: rgba(white, 0.2);
// Menu colors
$colorMenuBg: pullForward($colorBodyBg, 23%);
@@ -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);
@@ -183,13 +180,12 @@ $scrollbarThumbColorOverlay: lighten($colorOvrBg, 10%);
$scrollbarThumbColorOverlayHov: lighten($scrollbarThumbColorOverlay, 2%);
// Splitter
$splitterD: 17px; // splitterD and $splitterHandleD should both be odd, or even
$splitterD: 25px; // splitterD and HandleD should both be odd, or even
$splitterHandleD: 1px;
$splitterDSm: 17px; // Smaller splitter, used inside elements like a Timeline view
$colorSplitterBg: rgba(#fff, 0.1); //pullForward($colorBodyBg, 5%);
$splitterShdw: rgba(black, 0.4) 0 0 3px;
$splitterEndCr: none;
$colorSplitterHover: pullForward($colorBodyBg, 40%);
$colorSplitterHover: pullForward($colorBodyBg, 15%);
$colorSplitterActive: $colorKey;
// Mobile
@@ -207,10 +203,4 @@ $colorAboutLink: #84b3ff;
// Loading
$colorLoadingFg: $colorAlt1;
$colorLoadingBg: rgba($colorBodyFg, 0.2);
// Time Conductor
$colorTimeCondKeyBg: #4e70dc;
$colorTimeCondKeyFg: #fff;
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 10%);
$colorLoadingBg: rgba($colorBodyFg, 0.2);

View File

@@ -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;
@@ -52,7 +51,7 @@ $colorGridLines: rgba(#000, 0.05);
$colorInvokeMenu: #fff;
$colorObjHdrTxt: $colorBodyFg;
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
$colorTick: pullForward($colorBodyBg, 30%);
$colorTick: rgba(black, 0.2);
// Menu colors
$colorMenuBg: pushBack($colorBodyBg, 10%);
@@ -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;
@@ -183,13 +180,12 @@ $scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
// Splitter
$splitterD: 16px; // splitterD and $splitterHandleD should both be odd, or even
$splitterD: 24px;
$splitterHandleD: 2px;
$splitterDSm: 16px; // Smaller splitter, used inside elements like a Timeline view
$colorSplitterBg: pullForward($colorBodyBg, 10%);
$splitterShdw: none;
$splitterEndCr: none;
$colorSplitterHover: pullForward($colorBodyBg, 30%);
$colorSplitterHover: none;
$colorSplitterActive: $colorKey;
// Mobile
@@ -208,9 +204,3 @@ $colorAboutLink: #84b3ff;
// Loading
$colorLoadingFg: $colorAlt1;
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
// Time Conductor
$colorTimeCondKeyBg: #6178dc;
$colorTimeCondKeyFg: #fff;
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 30%);

View File

@@ -60,6 +60,11 @@ define(
this.$q = $q;
}
function getKey(id) {
var parts = id.split(":");
return parts.length > 1 ? parts.slice(1).join(":") : id;
}
/**
* Checks if the value returned is falsey, and if so returns a
* rejected promise
@@ -126,7 +131,7 @@ define(
// ...and persist
return persistenceFn.apply(persistenceService, [
this.getSpace(),
this.getKey(),
getKey(domainObject.getId()),
domainObject.getModel()
]).then(function (result) {
return rejectIfFalsey(result, self.$q);
@@ -154,7 +159,7 @@ define(
return this.persistenceService.readObject(
this.getSpace(),
this.getKey()
this.domainObject.getId()
).then(updateModel);
};
@@ -173,17 +178,6 @@ define(
return this.identifierService.parse(id).getSpace();
};
/**
* Get the key for this domain object in the given space.
*
* @returns {string} the key of the object in it's space.
*/
PersistenceCapability.prototype.getKey = function () {
var id = this.domainObject.getId();
return this.identifierService.parse(id).getKey();
};
return PersistenceCapability;
}
);

View File

@@ -35,8 +35,7 @@ define(
mockNofificationService,
mockCacheService,
mockQ,
key = "persistence key",
id = "object identifier",
id = "object id",
model,
SPACE = "some space",
persistence,
@@ -102,7 +101,6 @@ define(
});
mockIdentifierService.parse.andReturn(mockIdentifier);
mockIdentifier.getSpace.andReturn(SPACE);
mockIdentifier.getKey.andReturn(key);
persistence = new PersistenceCapability(
mockCacheService,
mockPersistenceService,
@@ -126,7 +124,7 @@ define(
expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
SPACE,
key,
id,
model
);
});
@@ -140,7 +138,7 @@ define(
expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
SPACE,
key,
id,
model
);
});

View File

@@ -82,6 +82,16 @@ define(
expect(result.a.getModel()).toEqual(model);
});
//TODO: Disabled for NEM Beta
xit("provides a new, fully constituted domain object for a" +
" provided model", function () {
var model = { someKey: "some value"},
result;
result = provider.newObject("a", model);
expect(result.getId()).toEqual("a");
expect(result.getModel()).toEqual(model);
});
});
}
);

View File

@@ -81,7 +81,6 @@ define(
if (phase.toLowerCase() === 'preparing' && !this.dialog) {
this.dialog = this.dialogService.showBlockingMessage({
title: "Preparing to copy objects",
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
unknownProgress: true,
severity: "info"
});

View File

@@ -24,7 +24,10 @@ define(
[],
function () {
var DISALLOWED_ACTIONS = ["move"];
var DISALLOWED_ACTIONS = [
"move",
"copy"
];
/**
* This policy prevents performing move/copy/link actions across

View File

@@ -70,25 +70,27 @@ define(
policy = new CrossSpacePolicy();
});
describe("for move actions", function () {
beforeEach(function () {
testActionMetadata.key = 'move';
});
['move', 'copy'].forEach(function (key) {
describe("for " + key + " actions", function () {
beforeEach(function () {
testActionMetadata.key = key;
});
it("allows same-space changes", function () {
expect(policy.allow(mockAction, sameSpaceContext))
.toBe(true);
});
it("allows same-space changes", function () {
expect(policy.allow(mockAction, sameSpaceContext))
.toBe(true);
});
it("disallows cross-space changes", function () {
expect(policy.allow(mockAction, crossSpaceContext))
.toBe(false);
});
it("disallows cross-space changes", function () {
expect(policy.allow(mockAction, crossSpaceContext))
.toBe(false);
});
it("allows actions with no selectedObject", function () {
expect(policy.allow(mockAction, {
domainObject: makeObject('a')
})).toBe(true);
it("allows actions with no selectedObject", function () {
expect(policy.allow(mockAction, {
domainObject: makeObject('a')
})).toBe(true);
});
});
});

View File

@@ -0,0 +1,150 @@
## Notes
API is notional for now, based on use-cases identified below. Possible the
use cases are not sufficient, so please include in comments
any other use cases you'd like to see.
Plan now is to start building out test suite for the use cases identified below
in order to get the API functional. Need to discuss how UI aspects of timeline will be implemented.
Propose in place refactoring of existing timeline rather than starting again.
Some caveats / open questions
* I don't understand the use case shown on page 52 of UI sketches. It shows RT/FT, with deltas,
with inner interval unlocked. Not sure what result would be, has inner end switched to fixed?
Also example on page 55 in real-time where inner end < now. Is there a use case for this? Semantically, it's saying
show me real time, but stale data. Why would a user want this? My feeling is that if the inner
OR outer ends are moved behind NOW in real-time mode then you drop into historical mode.
* For the API itself, have ignored question of how it's namespaced / exposed.
Examples assume global namespace and availability from window object.
For now API implemented as standard standard Require JS AMDs. Could attach
to window from bundle.js. Perhaps attaching to window not best approach though...
* Have not included validation (eg. start time < end time) or any other
business logic such as what happens when outer interval gets dragged
within range of inner interval. Focus is on teasing out the public API
right now.
* Time systems are vague right now also, I don't know how they're going
to work or whether any API has yet been specified.
* Not clear on the differences between real-time and follow-time as it
concerns the time conductor? For now the API has an end bounds mode
of FOLLOW which automatically tracks current time, and a start time mode
of RELATIVE. I can envision a real-time plot that is not in follow time mode,
but not sure what implication is for time conductor itself and how it
differs from an historical plot?
* Should the time conductor be responsible for choosing time system / domain? Currently
it is.
## Use Cases
1. Historical session is loaded and system sets time bounds on conductor
2. Real-time session is loaded, setting custom start and end deltas
3. User changes time of interest
4. Plot controller listens for change to TOI
5. Plot Controller updated on tick
6. Plot Controller updated when user changes bounds (eg to reset plot zoom)
7. Conductor controller needs to update bounds and mode on TC when user changes bounds
### Additional possible use-cases
1. Telemetry adapter wants to indicate presence of data at a particular time
2. Time conductor controller wants to paint map of data availability.
These use-cases could be features of the TimeConductor, but perhaps makes
sense to make knowledge of data availability the sole preserve of telemetry
adapters, not TimeConductor itself. Adapters will be ultimately responsible
for providing these data so doesn't make much sense to duplicate elsewhere.
The TimeConductorController - which knows tick interval on scale (which
TimeConductor API does not) - could simply request data availability from
telemetry API and paint it into the Time Conductor UI
## Example implementations of use cases
### 1. Real time session is loaded (outside of TC) and system sets time bounds on conductor
``` javascript
function loadSession(telemetryMetadata) {
var tc = MCT.conductor;
tc.timeSystem(session.timeSystem());
//Set start and end modes to fixed date
tc.mode(new FixedMode());
//Set both inner and outer bounds
tc.bounds({start: session.start(), end: session.end()});
}
```
### 2. Real-time session is loaded (outside of TC), setting custom start and end deltas
``` javascript
function loadSession(session) {
var tc = MCT.conductor;
var FIFTEEN_MINUTES = 15 * 60 * 1000;
// Could have a central ticking source somewhere, or connect to a
// remote ticking source. Should not need to be done manually with
// each session load. Actually not quite sure what to do with tick
// sources yet.
var tickSource = new LocalClock();
tickSource.attach(); // Start ticking
var mode = new RealtimeMode({
startDelta: FIFTEEN_MINUTES,
endDelta: 0 // End delta offset is from "Now" in the time system
});
tc.timeSystem(session.timeSystem());
// Set mode to realtime, specifying a tick source
tc.mode(mode);
//No need to set bounds manually, will be established by mode and the deltas specified
}
```
### 3. User changes time of interest
```javascript
//Somewhere in the TimeConductorController...
function changeTOI(newTime) {
MCT.conductor.timeOfInterest(newTime);
}
```
### 4. Plot controller listens for change to TOI
```javascript
// toi is attribute of Time Conductor object. Add a listener to the time
// conductor to be alerted to changes in value
// Time conductor is an event emitter, listen to timeOfInterest event
MCT.conductor.on("timeOfInterest", function (timeOfInterest) {
plot.setTimeOfInterest(timeOfInterest);
}
```
### 5. Plot Controller updated on tick
``` javascript
MCT.conductor.on("bounds", function (bounds) {
plotUpdater.setDomainBounds(bounds.start, bounds.end);
});
```
### 6. Plot Controller updated when user changes bounds (eg to reset plot zoom)
``` javascript
MCT.conductor.on("refresh", function (conductor) {
plot.setBounds(conductor.bounds());
//Also need to reset tick labels. if time system has changed.
}
```
### 7. Conductor controller needs to update bounds and mode on TC when user changes bounds
```javascript
var tc = MCT.conductor;
function dragStartHandle(finalPos){
var bounds = tc.bounds();
bounds.start = positionToTime(finalPos)
tc.bounds(bounds);
}
function dragEndHandle(finalPos){
var bounds = tc.bounds();
bounds.end = positionToTime(finalPos);
tc.bounds(bounds);
}
```

View File

@@ -0,0 +1,148 @@
/*****************************************************************************
* 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([
"EventEmitter",
"./UTCTimeSystem",
"./modes/RelativeMode",
"./modes/FixedMode"
], function (EventEmitter, UTCTimeSystem, RelativeMode, FixedMode) {
/**
* A class for setting and querying time conductor state.
*
* @event TimeConductor:refresh The time conductor has changed, and its values should be re-queried
* @event TimeConductor:bounds The start time, end time, or both have been updated
* @event TimeConductor:timeOfInterest The Time of Interest has moved.
* @constructor
*/
function TimeConductor() {
EventEmitter.call(this);
//The Time System
this.system = new UTCTimeSystem();
//The Time Of Interest
this.toi = undefined;
this.bounds = {
start: undefined,
end: undefined
};
//Default to fixed mode
this.modeVal = new FixedMode();
}
TimeConductor.prototype = Object.create(EventEmitter.prototype);
/**
* Validate the given bounds. This can be used for pre-validation of
* bounds, for example by views validating user inputs.
* @param bounds The start and end time of the conductor.
* @returns {string | true} A validation error, or true if valid
*/
TimeConductor.prototype.validateBounds = function (bounds) {
if (!bounds.start ||
!bounds.end ||
isNaN(bounds.start) ||
isNaN(bounds.end)
) {
return "Start and end must be specified as integer values";
} else if (bounds.start > bounds.end){
return "Specified start date exceeds end bound";
}
return true;
};
function throwOnError(validationResult) {
if (validationResult !== true) {
throw validationResult;
}
}
/**
* Set the mode of the time conductor.
* @param {FixedMode | RealtimeMode} newMode
* @fires TimeConductor#refresh
* @returns {FixedMode | RealtimeMode}
*/
TimeConductor.prototype.mode = function (newMode) {
if (arguments.length > 0) {
this.modeVal = newMode;
this.emit('refresh', this);
newMode.initialize();
}
return this.modeVal;
};
/**
* @typedef {Object} TimeConductorBounds
* @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system
* @property {number} end The end time displayed by the time conductor in ms since epoch.
*/
/**
* Set the start and end time of the time conductor. Basic validation of bounds is performed.
*
* @param {TimeConductorBounds} newBounds
* @throws {string} Validation error
* @fires TimeConductor#bounds
* @returns {TimeConductorBounds}
*/
TimeConductor.prototype.bounds = function (newBounds) {
if (arguments.length > 0) {
throwOnError(this.validateBounds(newBounds));
this.bounds = newBounds;
this.emit('bounds', this.bounds);
}
return this.bounds;
};
/**
* Set the time system of the TimeConductor. Time systems determine units, epoch, and other aspects of time representation.
* @param newTimeSystem
* @fires TimeConductor#refresh
* @returns {TimeSystem} The currently applied time system
*/
TimeConductor.prototype.timeSystem = function (newTimeSystem) {
if (arguments.length > 0) {
this.system = newTimeSystem;
this.emit('refresh', this);
}
return this.system;
};
/**
* The Time of Interest is the temporal focus of the current view. It can be manipulated by the user from the time
* conductor or from other views.
* @param newTOI
* @returns {*}
*/
TimeConductor.prototype.timeOfInterest = function (newTOI) {
if (arguments.length > 0) {
this.toi = newTOI;
this.emit('toi');
}
return this.toi;
};
return TimeConductor;
});

View File

@@ -0,0 +1,69 @@
/*****************************************************************************
* 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 () {
function TimeConductorBounds(conductor) {
this.listeners = [];
this.start = new TimeConductorLimit(this);
this.end = new TimeConductorLimit(this);
this.conductor = conductor;
}
/**
* @private
*/
TimeConductorBounds.prototype.notify = function (eventType) {
eventType = eventType || this.conductor.EventTypes.EITHER;
this.listeners.forEach(function (element){
if (element.eventType & eventType){
element.listener(this);
}
});
};
/**
* Listen for changes to the bounds
* @param listener a callback function to be called when the bounds change. The bounds object will be passed into
* the function (ie. 'this')
* @param eventType{TimeConductorBounds.EventType} The event type to listen to, ie. system, user, or Both. If not provied, will default to both.
* @returns {Function} an 'unlisten' function
*/
TimeConductorBounds.prototype.listen = function (listener, eventType) {
var self = this,
wrappedListener = {
listener: listener,
eventType: eventType
};
this.listeners.push(wrappedListener);
return function () {
self.listeners = self.listeners.filter(function (element){
return element !== wrappedListener;
});
};
};
return TimeConductorBounds;
});

View File

@@ -0,0 +1,71 @@
/*****************************************************************************
* 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 () {
/**
* Defines a limit object for a time conductor bounds, ie. start or end values. Holds time and delta values.
*
* TODO: Calculation of time from delta. Should probably be done from the 'tick' function at a higher level,
* which has start and end values in scope to do calculations.
* @param listener
* @constructor
*/
function TimeConductorLimit(listener) {
this.deltaVal = undefined;
this.timeVal = undefined;
this.listener = listener;
}
/**
* Get or set the start value for the bounds. If newVal is provided, will set the start value. May only set delta in
* RELATIVE mode.
* @param {Number} [newVal] a time in ms. A negative value describes a time in the past, positive in the future. A
* start time cannot have a positive delta offset, but an end time can.
* @param {TimeConductor.EventTypes} [eventType=TimeConductor.EventTypes.EITHER] The type of event (User, System, or
* Either)
* @returns {Number} the start date (in milliseconds since some epoch, depending on time system)
*/
TimeConductorLimit.prototype.delta = function (newVal, eventType) {
if (arguments.length > 0) {
this.deltaVal = newVal;
this.listener.notify(eventType);
}
return this.deltaVal;
};
/**
* Get or set the end value for the bounds. If newVal is provided, will set the end value. May only set time in FIXED
* mode
* @param {Number} [newVal] A time in ms relative to time system epoch.
* @param {TimeConductor.EventTypes} [eventType=TimeConductor.EventTypes.EITHER] The type of event (User, System, or Either)
* @returns {Number} the end date (in milliseconds since some epoch, depending on time system)
*/
TimeConductorLimit.prototype.time = function (newVal, eventType) {
if (arguments.length > 0) {
this.timeVal = newVal;
this.listener.notify(eventType);
}
return this.timeVal;
};
return TimeConductorLimit;
});

View File

@@ -0,0 +1,29 @@
/*****************************************************************************
* 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 () {
function FixedMode(options) {
}
return FixedMode;
});

View File

@@ -0,0 +1,71 @@
/*****************************************************************************
* 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 () {
/**
* Class representing the real-time mode of the Time Conductor. In this mode,
* the bounds are updated automatically based on a timing source.
*
* @param options
* @constructor
*/
function RealtimeMode(options) {
this.startDelta = options.startDelta;
this.endDelta = options.endDelta;
this.tickSource = options.tickSource;
this.system = undefined;
}
/**
* @private
*/
this.prototype.initialize = function (conductor) {
var self = this;
/**
* Deltas can be specified for start and end. An end delta will mean
* that the end bound is always in the future by 'endDelta' units
*/
this.startDelta = this.startDelta || conductor.timeSystem().DEFAULT_DELTA;
this.endDelta = this.endDelta || 0;
function setBounds() {
var now = conductor.timeSystem().now();
conductor.bounds({
start: now - self.startDelta,
end: now + self.endDelta
});
}
/**
* If a tick source is specified, listen for ticks
*/
if (this.tickSource) {
this.tickSource.on("tick", setBounds);
}
//Set initial bounds
setBounds();
};
return RealtimeMode;
});

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