Merge branch 'open-master' into open1241

Merge latest from master branch into topic branch
for WTD-1241.
This commit is contained in:
Victor Woeltjen 2015-06-23 13:00:27 -07:00
commit 14fbd64ae4
123 changed files with 6757 additions and 1670 deletions

3
.gitignore vendored
View File

@ -17,3 +17,6 @@ target
# Closed source libraries # Closed source libraries
closed-lib closed-lib
# Node dependencies
node_modules

62
app.js Normal file
View File

@ -0,0 +1,62 @@
/*global require,process,console*/
/**
* Usage:
*
* npm install minimist express
* node app.js [options]
*/
(function () {
"use strict";
var BUNDLE_FILE = 'bundles.json',
options = require('minimist')(process.argv.slice(2)),
express = require('express'),
app = express(),
fs = require('fs'),
bundles = JSON.parse(fs.readFileSync(BUNDLE_FILE, 'utf8'));
// Defaults
options.port = options.port || options.p || 8080;
['include', 'exclude', 'i', 'x'].forEach(function (opt) {
options[opt] = options[opt] || [];
// Make sure includes/excludes always end up as arrays
options[opt] = Array.isArray(options[opt]) ?
options[opt] : [options[opt]];
});
options.include = options.include.concat(options.i);
options.exclude = options.exclude.concat(options.x);
// Show command line options
if (options.help || options.h) {
console.log("\nUsage: node app.js [options]\n");
console.log("Options:");
console.log(" --help, -h Show this message.");
console.log(" --port, -p <number> Specify port.");
console.log(" --include, -i <bundle> Include the specified bundle.");
console.log(" --exclude, -x <bundle> Exclude the specified bundle.");
console.log("");
process.exit(0);
}
// Handle command line inclusions/exclusions
bundles = bundles.concat(options.include);
bundles = bundles.filter(function (bundle) {
return options.exclude.indexOf(bundle) === -1;
});
bundles = bundles.filter(function (bundle, index) { // Uniquify
return bundles.indexOf(bundle) === index;
});
// Override bundles.json for HTTP requests
app.use('/' + BUNDLE_FILE, function (req, res) {
res.send(JSON.stringify(bundles));
});
// Expose everything else as static files
app.use(express['static']('.'));
// Finally, open the HTTP server
app.listen(options.port);
}());

View File

@ -7,7 +7,9 @@
"platform/commonUI/edit", "platform/commonUI/edit",
"platform/commonUI/dialog", "platform/commonUI/dialog",
"platform/commonUI/general", "platform/commonUI/general",
"platform/commonUI/inspect",
"platform/containment", "platform/containment",
"platform/execution",
"platform/telemetry", "platform/telemetry",
"platform/features/layout", "platform/features/layout",
"platform/features/pages", "platform/features/pages",
@ -15,8 +17,8 @@
"platform/features/scrolling", "platform/features/scrolling",
"platform/forms", "platform/forms",
"platform/persistence/queue", "platform/persistence/queue",
"platform/persistence/elastic",
"platform/policy", "platform/policy",
"example/persistence",
"example/generator" "example/generator"
] ]

1
example/worker/README.md Normal file
View File

@ -0,0 +1 @@
Example of running a Web Worker using the `workerService`.

View File

@ -0,0 +1,16 @@
{
"extensions": {
"indicators": [
{
"implementation": "FibonacciIndicator.js",
"depends": [ "workerService", "$rootScope" ]
}
],
"workers": [
{
"key": "example.fibonacci",
"scriptUrl": "FibonacciWorker.js"
}
]
}
}

View File

@ -0,0 +1,70 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
[],
function () {
"use strict";
/**
* Displays Fibonacci numbers in the status area.
* @constructor
*/
function FibonacciIndicator(workerService, $rootScope) {
var latest,
counter = 0,
worker = workerService.run('example.fibonacci');
function requestNext() {
worker.postMessage([counter]);
counter += 1;
}
function handleResponse(event) {
latest = event.data;
$rootScope.$apply();
requestNext();
}
worker.onmessage = handleResponse;
requestNext();
return {
getGlyph: function () {
return "?";
},
getText: function () {
return latest;
},
getGlyphClass: function () {
return "";
},
getDescription: function () {
return "";
}
};
}
return FibonacciIndicator;
}
);

View File

@ -0,0 +1,15 @@
/*global self*/
(function () {
"use strict";
// Calculate fibonacci numbers inefficiently.
// We can do this because we're on a background thread, and
// won't halt the UI.
function fib(n) {
return n < 2 ? n : (fib(n - 1) + fib(n - 2));
}
self.onmessage = function (event) {
self.postMessage(fib(event.data));
};
}());

View File

@ -2,19 +2,26 @@
"extensions": { "extensions": {
"routes": [ "routes": [
{ {
"when": "/browse", "when": "/browse/:ids*",
"templateUrl": "templates/browse.html" "templateUrl": "templates/browse.html",
"reloadOnSearch": false
}, },
{ {
"when": "", "when": "",
"templateUrl": "templates/browse.html" "templateUrl": "templates/browse.html",
"reloadOnSearch": false
} }
], ],
"controllers": [ "controllers": [
{ {
"key": "BrowseController", "key": "BrowseController",
"implementation": "BrowseController.js", "implementation": "BrowseController.js",
"depends": [ "$scope", "objectService", "navigationService" ] "depends": [ "$scope", "$route", "$location", "objectService", "navigationService" ]
},
{
"key": "BrowseObjectController",
"implementation": "BrowseObjectController.js",
"depends": [ "$scope", "$location", "$route" ]
}, },
{ {
"key": "CreateMenuController", "key": "CreateMenuController",
@ -57,7 +64,8 @@
{ {
"key": "grid-item", "key": "grid-item",
"templateUrl": "templates/items/grid-item.html", "templateUrl": "templates/items/grid-item.html",
"uses": [ "type", "action" ] "uses": [ "type", "action" ],
"gestures": [ "info", "menu" ]
}, },
{ {
"key": "object-header", "key": "object-header",
@ -150,4 +158,4 @@
} }
] ]
} }
} }

View File

@ -19,16 +19,14 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<span> <span ng-controller="BrowseObjectController">
<div class="object-browse-bar bar abs"> <div class="object-browse-bar bar abs">
<div class="items-select left abs"> <div class="items-select left abs">
<mct-representation key="'object-header'" mct-object="domainObject"> <mct-representation key="'object-header'" mct-object="domainObject">
</mct-representation> </mct-representation>
</div> </div>
<div class="view-controls sort-controls btn-bar right abs"> <div class="btn-bar right abs">
<mct-representation key="'action-group'" <mct-representation key="'action-group'"
mct-object="domainObject" mct-object="domainObject"
parameters="{ category: 'view-control' }"> parameters="{ category: 'view-control' }">
@ -39,7 +37,6 @@
ng-model="representation"> ng-model="representation">
</mct-representation> </mct-representation>
</div> </div>
</div> </div>
<div class='object-holder abs vscroll'> <div class='object-holder abs vscroll'>
@ -47,4 +44,4 @@
mct-object="representation.selected.key && domainObject"> mct-object="representation.selected.key && domainObject">
</mct-representation> </mct-representation>
</div> </div>
</span> </span>

View File

@ -20,9 +20,11 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class='object-header'> <div class='object-header'>
<span class='type-icon icon ui-symbol'>{{type.getGlyph()}}</span> <span class="label s-label">
<span ng-if="parameters.mode" class='action'>{{parameters.mode}}</span> <span class='type-icon icon ui-symbol'>{{type.getGlyph()}}</span>
<span class='type'>{{type.getName()}}</span> <span ng-if="parameters.mode" class='action'>{{parameters.mode}}</span>
<span class='title'>{{model.name}}</span> <span class='type-name'>{{type.getName()}}</span>
<a id='actions-menu' class='ui-symbol invoke-menu' onclick="alert('Not yet functional. This will display a dropdown menu of options for this object.');">v</a> <span class='title-label'>{{model.name}}</span>
<!--a id='actions-menu' class='ui-symbol context-available' onclick="alert('Not yet functional. This will display a dropdown menu of options for this object.');">v</a-->
</span>
</div> </div>

View File

@ -21,7 +21,7 @@
--> -->
<div class="menu-element wrapper" ng-controller="ClickAwayController as createController"> <div class="menu-element wrapper" ng-controller="ClickAwayController as createController">
<div class="btn btn-menu create-btn major" ng-click="createController.toggle()"> <div class="btn btn-menu create-btn major" ng-click="createController.toggle()">
<span class='ui-symbol major' href=''>+</span> Create<!--span class='ui-symbol invoke-menu'>v</span--> <span class='ui-symbol' href=''>+</span> Create
</div> </div>
<div class="menu dropdown super-menu" ng-show="createController.isActive()"> <div class="menu dropdown super-menu" ng-show="createController.isActive()">
<mct-representation mct-object="domainObject" key="'create-menu'"> <mct-representation mct-object="domainObject" key="'create-menu'">

View File

@ -29,7 +29,8 @@ define(
function () { function () {
"use strict"; "use strict";
var ROOT_OBJECT = "ROOT"; var ROOT_ID = "ROOT",
DEFAULT_PATH = "mine";
/** /**
* The BrowseController is used to populate the initial scope in Browse * The BrowseController is used to populate the initial scope in Browse
@ -40,35 +41,98 @@ define(
* *
* @constructor * @constructor
*/ */
function BrowseController($scope, objectService, navigationService) { function BrowseController($scope, $route, $location, objectService, navigationService) {
var path = [ROOT_ID].concat(
($route.current.params.ids || DEFAULT_PATH).split("/")
);
function updateRoute(domainObject) {
var context = domainObject &&
domainObject.getCapability('context'),
objectPath = context ? context.getPath() : [],
ids = objectPath.map(function (domainObject) {
return domainObject.getId();
}),
priorRoute = $route.current,
// Act as if params HADN'T changed to avoid page reload
unlisten;
unlisten = $scope.$on('$locationChangeSuccess', function () {
$route.current = priorRoute;
unlisten();
});
$location.path("/browse/" + ids.slice(1).join("/"));
}
// Callback for updating the in-scope reference to the object // Callback for updating the in-scope reference to the object
// that is currently navigated-to. // that is currently navigated-to.
function setNavigation(domainObject) { function setNavigation(domainObject) {
$scope.navigatedObject = domainObject; $scope.navigatedObject = domainObject;
$scope.treeModel.selectedObject = domainObject; $scope.treeModel.selectedObject = domainObject;
navigationService.setNavigation(domainObject); navigationService.setNavigation(domainObject);
updateRoute(domainObject);
}
function navigateTo(domainObject) {
// Check if an object has been navigated-to already...
// If not, or if an ID path has been explicitly set in the URL,
// navigate to the URL-specified object.
if (!navigationService.getNavigation() || $route.current.params.ids) {
// If not, pick a default as the last
// root-level component (usually "mine")
navigationService.setNavigation(domainObject);
$scope.navigatedObject = domainObject;
} else {
// Otherwise, just expose the currently navigated object.
$scope.navigatedObject = navigationService.getNavigation();
updateRoute($scope.navigatedObject);
}
}
function findObject(domainObjects, id) {
var i;
for (i = 0; i < domainObjects.length; i += 1) {
if (domainObjects[i].getId() === id) {
return domainObjects[i];
}
}
}
// Navigate to the domain object identified by path[index],
// which we expect to find in the composition of the passed
// domain object.
function doNavigate(domainObject, index) {
var composition = domainObject.useCapability("composition");
if (composition) {
composition.then(function (c) {
var nextObject = findObject(c, path[index]);
if (nextObject) {
if (index + 1 >= path.length) {
navigateTo(nextObject);
} else {
doNavigate(nextObject, index + 1);
}
} else {
// Couldn't find the next element of the path
// so navigate to the last path object we did find
navigateTo(domainObject);
}
});
} else {
// Similar to above case; this object has no composition,
// so navigate to it instead of subsequent path elements.
navigateTo(domainObject);
}
} }
// Load the root object, put it in the scope. // Load the root object, put it in the scope.
// Also, load its immediate children, and (possibly) // Also, load its immediate children, and (possibly)
// navigate to one of them, so that navigation state has // navigate to one of them, so that navigation state has
// a useful initial value. // a useful initial value.
objectService.getObjects([ROOT_OBJECT]).then(function (objects) { objectService.getObjects([path[0]]).then(function (objects) {
var composition = objects[ROOT_OBJECT].useCapability("composition"); $scope.domainObject = objects[path[0]];
$scope.domainObject = objects[ROOT_OBJECT]; doNavigate($scope.domainObject, 1);
if (composition) {
composition.then(function (c) {
// Check if an object has been navigated-to already...
if (!navigationService.getNavigation()) {
// If not, pick a default as the last
// root-level component (usually "mine")
navigationService.setNavigation(c[c.length - 1]);
} else {
// Otherwise, just expose it in the scope
$scope.navigatedObject = navigationService.getNavigation();
}
});
}
}); });
// Provide a model for the tree to modify // Provide a model for the tree to modify
@ -91,4 +155,4 @@ define(
return BrowseController; return BrowseController;
} }
); );

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.
*****************************************************************************/
/*global define,Promise*/
define(
[],
function () {
"use strict";
/**
* Controller for the `browse-object` representation of a domain
* object (the right-hand side of Browse mode.)
* @constructor
*/
function BrowseObjectController($scope, $location, $route) {
function setViewForDomainObject(domainObject) {
var locationViewKey = $location.search().view;
function selectViewIfMatching(view) {
if (view.key === locationViewKey) {
$scope.representation = $scope.representation || {};
$scope.representation.selected = view;
}
}
if (locationViewKey) {
((domainObject && domainObject.useCapability('view')) || [])
.forEach(selectViewIfMatching);
}
}
function updateQueryParam(viewKey) {
var unlisten, priorRoute = $route.current;
if (viewKey) {
$location.search('view', viewKey);
unlisten = $scope.$on('$locationChangeSuccess', function () {
$route.current = priorRoute;
unlisten();
});
}
}
$scope.$watch('domainObject', setViewForDomainObject);
$scope.$watch('representation.selected.key', updateQueryParam);
}
return BrowseObjectController;
}
);

View File

@ -45,8 +45,9 @@ define(
properties = type.getProperties(); properties = type.getProperties();
function validateLocation(locatingObject) { function validateLocation(locatingObject) {
var locatingType = locatingObject.getCapability('type'); var locatingType = locatingObject &&
return policyService.allow( locatingObject.getCapability('type');
return locatingType && policyService.allow(
"composition", "composition",
locatingType, locatingType,
type type

View File

@ -77,7 +77,19 @@ define(
return undefined; return undefined;
} }
return parentPersistence.persist(); return parentPersistence.persist().then(function () {
// Locate and return new Object in context of parent.
return parent
.useCapability('composition')
.then(function (children) {
var i;
for (i = 0; i < children.length; i += 1) {
if (children[i].getId() === id) {
return children[i];
}
}
});
});
}); });
} }
@ -126,4 +138,4 @@ define(
return CreationService; return CreationService;
} }
); );

View File

@ -31,10 +31,13 @@ define(
describe("The browse controller", function () { describe("The browse controller", function () {
var mockScope, var mockScope,
mockRoute,
mockLocation,
mockObjectService, mockObjectService,
mockNavigationService, mockNavigationService,
mockRootObject, mockRootObject,
mockDomainObject, mockDomainObject,
mockNextObject,
controller; controller;
function mockPromise(value) { function mockPromise(value) {
@ -50,6 +53,11 @@ define(
"$scope", "$scope",
[ "$on", "$watch" ] [ "$on", "$watch" ]
); );
mockRoute = { current: { params: {} } };
mockLocation = jasmine.createSpyObj(
"$location",
[ "path" ]
);
mockObjectService = jasmine.createSpyObj( mockObjectService = jasmine.createSpyObj(
"objectService", "objectService",
[ "getObjects" ] [ "getObjects" ]
@ -71,25 +79,38 @@ define(
"domainObject", "domainObject",
[ "getId", "getCapability", "getModel", "useCapability" ] [ "getId", "getCapability", "getModel", "useCapability" ]
); );
mockNextObject = jasmine.createSpyObj(
"nextObject",
[ "getId", "getCapability", "getModel", "useCapability" ]
);
mockObjectService.getObjects.andReturn(mockPromise({ mockObjectService.getObjects.andReturn(mockPromise({
ROOT: mockRootObject ROOT: mockRootObject
})); }));
mockRootObject.useCapability.andReturn(mockPromise([
mockDomainObject
]));
mockDomainObject.useCapability.andReturn(mockPromise([
mockNextObject
]));
mockNextObject.useCapability.andReturn(undefined);
mockNextObject.getId.andReturn("next");
mockDomainObject.getId.andReturn("mine");
controller = new BrowseController( controller = new BrowseController(
mockScope, mockScope,
mockRoute,
mockLocation,
mockObjectService, mockObjectService,
mockNavigationService mockNavigationService
); );
}); });
it("uses composition to set the navigated object, if there is none", function () { it("uses composition to set the navigated object, if there is none", function () {
mockRootObject.useCapability.andReturn(mockPromise([
mockDomainObject
]));
controller = new BrowseController( controller = new BrowseController(
mockScope, mockScope,
mockRoute,
mockLocation,
mockObjectService, mockObjectService,
mockNavigationService mockNavigationService
); );
@ -98,12 +119,11 @@ define(
}); });
it("does not try to override navigation", function () { it("does not try to override navigation", function () {
// This behavior is needed if object navigation has been
// determined by query string parameters
mockRootObject.useCapability.andReturn(mockPromise([null]));
mockNavigationService.getNavigation.andReturn(mockDomainObject); mockNavigationService.getNavigation.andReturn(mockDomainObject);
controller = new BrowseController( controller = new BrowseController(
mockScope, mockScope,
mockRoute,
mockLocation,
mockObjectService, mockObjectService,
mockNavigationService mockNavigationService
); );
@ -130,6 +150,76 @@ define(
); );
}); });
it("uses route parameters to choose initially-navigated object", function () {
mockRoute.current.params.ids = "mine/next";
controller = new BrowseController(
mockScope,
mockRoute,
mockLocation,
mockObjectService,
mockNavigationService
);
expect(mockScope.navigatedObject).toBe(mockNextObject);
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockNextObject);
});
it("handles invalid IDs by going as far as possible", function () {
// Idea here is that if we get a bad path of IDs,
// browse controller should traverse down it until
// it hits an invalid ID.
mockRoute.current.params.ids = "mine/junk";
controller = new BrowseController(
mockScope,
mockRoute,
mockLocation,
mockObjectService,
mockNavigationService
);
expect(mockScope.navigatedObject).toBe(mockDomainObject);
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDomainObject);
});
it("handles compositionless objects by going as far as possible", function () {
// Idea here is that if we get a path which passes
// through an object without a composition, browse controller
// should stop at it since remaining IDs cannot be loaded.
mockRoute.current.params.ids = "mine/next/junk";
controller = new BrowseController(
mockScope,
mockRoute,
mockLocation,
mockObjectService,
mockNavigationService
);
expect(mockScope.navigatedObject).toBe(mockNextObject);
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockNextObject);
});
it("updates the displayed route to reflect current navigation", function () {
var mockContext = jasmine.createSpyObj('context', ['getPath']),
mockUnlisten = jasmine.createSpy('unlisten');
mockContext.getPath.andReturn(
[mockRootObject, mockDomainObject, mockNextObject]
);
mockNextObject.getCapability.andCallFake(function (c) {
return c === 'context' && mockContext;
});
mockScope.$on.andReturn(mockUnlisten);
// Provide a navigation change
mockNavigationService.addListener.mostRecentCall.args[0](
mockNextObject
);
expect(mockLocation.path).toHaveBeenCalledWith("/browse/mine/next");
// Exercise the Angular workaround
mockScope.$on.mostRecentCall.args[1]();
expect(mockUnlisten).toHaveBeenCalled();
});
}); });
} }
); );

View File

@ -0,0 +1,99 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/BrowseObjectController"],
function (BrowseObjectController) {
"use strict";
describe("The browse object controller", function () {
var mockScope,
mockLocation,
mockRoute,
mockUnlisten,
controller;
// Utility function; look for a $watch on scope and fire it
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1](value);
}
});
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
[ "$on", "$watch" ]
);
mockRoute = { current: { params: {} } };
mockLocation = jasmine.createSpyObj(
"$location",
[ "path", "search" ]
);
mockUnlisten = jasmine.createSpy("unlisten");
mockScope.$on.andReturn(mockUnlisten);
controller = new BrowseObjectController(
mockScope,
mockLocation,
mockRoute
);
});
it("updates query parameters when selected view changes", function () {
fireWatch("representation.selected.key", "xyz");
expect(mockLocation.search).toHaveBeenCalledWith('view', "xyz");
// Exercise the Angular workaround
mockScope.$on.mostRecentCall.args[1]();
expect(mockUnlisten).toHaveBeenCalled();
});
it("sets the active view from query parameters", function () {
var mockDomainObject = jasmine.createSpyObj(
"domainObject",
['getId', 'getModel', 'getCapability', 'useCapability']
),
testViews = [
{ key: 'abc' },
{ key: 'def', someKey: 'some value' },
{ key: 'xyz' }
];
mockDomainObject.useCapability.andCallFake(function (c) {
return (c === 'view') && testViews;
});
mockLocation.search.andReturn({ view: 'def' });
fireWatch('domainObject', mockDomainObject);
expect(mockScope.representation.selected)
.toEqual(testViews[1]);
});
});
}
);

View File

@ -34,8 +34,10 @@ define(
mockQ, mockQ,
mockLog, mockLog,
mockParentObject, mockParentObject,
mockNewObject,
mockMutationCapability, mockMutationCapability,
mockPersistenceCapability, mockPersistenceCapability,
mockCompositionCapability,
mockCapabilities, mockCapabilities,
creationService; creationService;
@ -69,6 +71,10 @@ define(
"parentObject", "parentObject",
[ "getId", "getCapability", "useCapability" ] [ "getId", "getCapability", "useCapability" ]
); );
mockNewObject = jasmine.createSpyObj(
"newObject",
[ "getId" ]
);
mockMutationCapability = jasmine.createSpyObj( mockMutationCapability = jasmine.createSpyObj(
"mutation", "mutation",
[ "invoke" ] [ "invoke" ]
@ -77,9 +83,14 @@ define(
"persistence", "persistence",
[ "persist", "getSpace" ] [ "persist", "getSpace" ]
); );
mockCompositionCapability = jasmine.createSpyObj(
"composition",
["invoke"]
);
mockCapabilities = { mockCapabilities = {
mutation: mockMutationCapability, mutation: mockMutationCapability,
persistence: mockPersistenceCapability persistence: mockPersistenceCapability,
composition: mockCompositionCapability
}; };
mockPersistenceService.createObject.andReturn( mockPersistenceService.createObject.andReturn(
@ -93,8 +104,15 @@ define(
return mockCapabilities[key].invoke(value); return mockCapabilities[key].invoke(value);
}); });
mockPersistenceCapability.persist.andReturn(
mockPromise(true)
);
mockMutationCapability.invoke.andReturn(mockPromise(true)); mockMutationCapability.invoke.andReturn(mockPromise(true));
mockPersistenceCapability.getSpace.andReturn("testSpace"); mockPersistenceCapability.getSpace.andReturn("testSpace");
mockCompositionCapability.invoke.andReturn(
mockPromise([mockNewObject])
);
creationService = new CreationService( creationService = new CreationService(
mockPersistenceService, mockPersistenceService,

View File

@ -1,5 +1,6 @@
[ [
"BrowseController", "BrowseController",
"BrowseObjectController",
"creation/CreateAction", "creation/CreateAction",
"creation/CreateActionProvider", "creation/CreateActionProvider",
"creation/CreateMenuController", "creation/CreateMenuController",
@ -10,4 +11,4 @@
"navigation/NavigationService", "navigation/NavigationService",
"windowing/FullscreenAction", "windowing/FullscreenAction",
"windowing/WindowTitler" "windowing/WindowTitler"
] ]

View File

@ -25,7 +25,7 @@
<a href="" <a href=""
ng-click="ngModel.cancel()" ng-click="ngModel.cancel()"
ng-if="ngModel.cancel" ng-if="ngModel.cancel"
class="btn normal outline ui-symbol close"> class="btn normal ui-symbol close">
x x
</a> </a>
<div class="abs contents" ng-transclude> <div class="abs contents" ng-transclude>

View File

@ -33,8 +33,6 @@
<mct-representation key="'edit-action-buttons'" <mct-representation key="'edit-action-buttons'"
mct-object="domainObject" mct-object="domainObject"
class='conclude-editing'> class='conclude-editing'>
<!--a class='btn major' href=''>Save<span id='save-actions-menu' class='ui-symbol invoke-menu'>v</span></a>
<a class='btn subtle' href=''>Cancel</a-->
</mct-representation> </mct-representation>
</div> </div>
</div> </div>

View File

@ -181,7 +181,7 @@
"key": "label", "key": "label",
"templateUrl": "templates/label.html", "templateUrl": "templates/label.html",
"uses": [ "type" ], "uses": [ "type" ],
"gestures": [ "drag", "menu" ] "gestures": [ "drag", "menu", "info" ]
}, },
{ {
"key": "node", "key": "node",

View File

@ -61,6 +61,15 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*
@mixin invokeMenu($baseColor: $colorBodyFg) {
$c: $baseColor;
color: $c;
&:hover {
color: lighten($c, $ltGamma);
}
}
*/
/***************************************************************************** /*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government * Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space * as represented by the Administrator of the National Aeronautics and Space
@ -186,9 +195,9 @@
color: #666666; } color: #666666; }
/* line 116, ../sass/forms/_elems.scss */ /* line 116, ../sass/forms/_elems.scss */
.form .form-row .selector-list { .form .form-row .selector-list {
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
@ -225,9 +234,9 @@ label.form-control.checkbox input {
vertical-align: top; } vertical-align: top; }
/* line 159, ../sass/forms/_elems.scss */ /* line 159, ../sass/forms/_elems.scss */
.l-result div.s-hint { .l-result div.s-hint {
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
background: rgba(255, 153, 0, 0.8); background: rgba(255, 153, 0, 0.8);
display: block; display: block;
color: #ffd699; color: #ffd699;
@ -258,9 +267,9 @@ label.form-control.checkbox input {
.edit-main textarea { .edit-main textarea {
-moz-appearance: none; -moz-appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
@ -302,13 +311,12 @@ label.form-control.checkbox input {
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/* line 22, ../sass/forms/_text-input.scss */ /* line 22, ../sass/forms/_text-input.scss */
input[type="text"], input[type="text"] {
input[type="date"] {
-moz-appearance: none; -moz-appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
@ -322,32 +330,10 @@ input[type="date"] {
outline: none; outline: none;
padding: 0 3px; } padding: 0 3px; }
/* line 33, ../sass/forms/_mixins.scss */ /* line 33, ../sass/forms/_mixins.scss */
input[type="text"].error, input[type="text"].error {
input[type="date"].error {
background: rgba(255, 0, 0, 0.5); } background: rgba(255, 0, 0, 0.5); }
/* line 61, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/css3/_user-interface.scss */ /* line 29, ../sass/forms/_text-input.scss */
input[type="text"]:-moz-placeholder, input[type="text"].numeric {
input[type="date"]:-moz-placeholder {
color: gray;
font-style: italic; }
/* line 64, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/css3/_user-interface.scss */
input[type="text"]::-moz-placeholder,
input[type="date"]::-moz-placeholder {
color: gray;
font-style: italic; }
/* line 67, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/css3/_user-interface.scss */
input[type="text"]:-ms-input-placeholder,
input[type="date"]:-ms-input-placeholder {
color: gray;
font-style: italic; }
/* line 56, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/css3/_user-interface.scss */
input[type="text"]::-webkit-input-placeholder,
input[type="date"]::-webkit-input-placeholder {
color: gray;
font-style: italic; }
/* line 34, ../sass/forms/_text-input.scss */
input[type="text"].numeric,
input[type="date"].numeric {
text-align: right; } text-align: right; }
/***************************************************************************** /*****************************************************************************
@ -373,23 +359,23 @@ input[type="date"] {
*****************************************************************************/ *****************************************************************************/
/* line 22, ../sass/forms/_selects.scss */ /* line 22, ../sass/forms/_selects.scss */
.form-control.select { .form-control.select {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzQwNDA0MCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzUyNTI1MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzQ1NDU0NSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%; background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4d4d4d), color-stop(100%, #404040)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #525252), color-stop(100%, #454545));
background-image: -moz-linear-gradient(#4d4d4d, #404040); background-image: -moz-linear-gradient(#525252, #454545);
background-image: -webkit-linear-gradient(#4d4d4d, #404040); background-image: -webkit-linear-gradient(#525252, #454545);
background-image: linear-gradient(#4d4d4d, #404040); background-image: linear-gradient(#525252, #454545);
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
border: none; border: none;
border-top: 1px solid #666666; border-top: 1px solid #575757;
color: #999; color: #999;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
@ -397,14 +383,21 @@ input[type="date"] {
margin: 0 0 2px 2px; margin: 0 0 2px 2px;
overflow: hidden; overflow: hidden;
position: relative; } position: relative; }
/* line 148, ../sass/_mixins.scss */ /* line 152, ../sass/_mixins.scss */
.form-control.select:not(.disabled):hover { .form-control.select:not(.disabled):hover {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzY2NjY2NiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzYzNjM2MyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzU3NTc1NyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%; background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #666666), color-stop(100%, #4d4d4d)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #636363), color-stop(100%, #575757));
background-image: -moz-linear-gradient(#666666, #4d4d4d); background-image: -moz-linear-gradient(#636363, #575757);
background-image: -webkit-linear-gradient(#666666, #4d4d4d); background-image: -webkit-linear-gradient(#636363, #575757);
background-image: linear-gradient(#666666, #4d4d4d); } background-image: linear-gradient(#636363, #575757);
color: #bdbdbd; }
/* line 155, ../sass/_mixins.scss */
.form-control.select:not(.disabled):hover.btn-menu .invoke-menu {
color: #878787; }
/* line 160, ../sass/_mixins.scss */
.form-control.select.btn-menu .invoke-menu {
color: #757575; }
/* line 29, ../sass/forms/_selects.scss */ /* line 29, ../sass/forms/_selects.scss */
.form-control.select select { .form-control.select select {
-moz-appearance: none; -moz-appearance: none;
@ -461,9 +454,9 @@ input[type="date"] {
.channel-selector .treeview { .channel-selector .treeview {
-moz-appearance: none; -moz-appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;

View File

@ -61,6 +61,15 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*
@mixin invokeMenu($baseColor: $colorBodyFg) {
$c: $baseColor;
color: $c;
&:hover {
color: lighten($c, $ltGamma);
}
}
*/
/***************************************************************************** /*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government * Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space * as represented by the Administrator of the National Aeronautics and Space
@ -92,23 +101,23 @@
top: 0; } top: 0; }
/* line 29, ../sass/items/_item.scss */ /* line 29, ../sass/items/_item.scss */
.items-holder .item.grid-item { .items-holder .item.grid-item {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzVlNWU1ZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzUyNTI1MiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%; background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #595959), color-stop(100%, #4d4d4d)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5e5e5e), color-stop(100%, #525252));
background-image: -moz-linear-gradient(#595959, #4d4d4d); background-image: -moz-linear-gradient(#5e5e5e, #525252);
background-image: -webkit-linear-gradient(#595959, #4d4d4d); background-image: -webkit-linear-gradient(#5e5e5e, #525252);
background-image: linear-gradient(#595959, #4d4d4d); background-image: linear-gradient(#5e5e5e, #525252);
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
border: none; border: none;
border-top: 1px solid #737373; border-top: 1px solid #636363;
color: #999; color: #999;
display: inline-block; display: inline-block;
box-sizing: border-box; box-sizing: border-box;
@ -119,14 +128,21 @@
margin-bottom: 3px; margin-bottom: 3px;
margin-right: 3px; margin-right: 3px;
position: relative; } position: relative; }
/* line 148, ../sass/_mixins.scss */ /* line 152, ../sass/_mixins.scss */
.items-holder .item.grid-item:not(.disabled):hover { .items-holder .item.grid-item:not(.disabled):hover {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzczNzM3MyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzcwNzA3MCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzYzNjM2MyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%; background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #737373), color-stop(100%, #595959)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #707070), color-stop(100%, #636363));
background-image: -moz-linear-gradient(#737373, #595959); background-image: -moz-linear-gradient(#707070, #636363);
background-image: -webkit-linear-gradient(#737373, #595959); background-image: -webkit-linear-gradient(#707070, #636363);
background-image: linear-gradient(#737373, #595959); } background-image: linear-gradient(#707070, #636363);
color: #bdbdbd; }
/* line 155, ../sass/_mixins.scss */
.items-holder .item.grid-item:not(.disabled):hover.btn-menu .invoke-menu {
color: #949494; }
/* line 160, ../sass/_mixins.scss */
.items-holder .item.grid-item.btn-menu .invoke-menu {
color: #828282; }
/* line 42, ../sass/items/_item.scss */ /* line 42, ../sass/items/_item.scss */
.items-holder .item.grid-item:hover .item-main .item-type { .items-holder .item.grid-item:hover .item-main .item-type {
color: #0099cc !important; } color: #0099cc !important; }
@ -184,40 +200,41 @@
font-size: 0.8em; } font-size: 0.8em; }
/* line 104, ../sass/items/_item.scss */ /* line 104, ../sass/items/_item.scss */
.items-holder .item.grid-item.selected { .items-holder .item.grid-item.selected {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwYmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwYWNlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzBhYzJmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwYjRmMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%; background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #00bfff), color-stop(100%, #00ace6)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #0ac2ff), color-stop(100%, #00b4f0));
background-image: -moz-linear-gradient(#00bfff, #00ace6); background-image: -moz-linear-gradient(#0ac2ff, #00b4f0);
background-image: -webkit-linear-gradient(#00bfff, #00ace6); background-image: -webkit-linear-gradient(#0ac2ff, #00b4f0);
background-image: linear-gradient(#00bfff, #00ace6); background-image: linear-gradient(#0ac2ff, #00b4f0);
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
border: none; border: none;
border-top: 1px solid #33ccff; border-top: 1px solid #14c4ff;
color: #999; color: #999;
display: inline-block; display: inline-block;
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzMzY2NmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwOTljYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #33ccff), color-stop(100%, #0099cc));
background-image: -moz-linear-gradient(#33ccff, #0099cc);
background-image: -webkit-linear-gradient(#33ccff, #0099cc);
background-image: linear-gradient(#33ccff, #0099cc);
color: #80dfff; } color: #80dfff; }
/* line 156, ../sass/_mixins.scss */ /* line 152, ../sass/_mixins.scss */
.items-holder .item.grid-item.selected:not(.disabled):hover { .items-holder .item.grid-item.selected:not(.disabled):hover {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzY2ZDlmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwYmZmZiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzJlY2JmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzE0YzRmZiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
background-size: 100%; background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #66d9ff), color-stop(100%, #00bfff)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #2ecbff), color-stop(100%, #14c4ff));
background-image: -moz-linear-gradient(#66d9ff, #00bfff); background-image: -moz-linear-gradient(#2ecbff, #14c4ff);
background-image: -webkit-linear-gradient(#66d9ff, #00bfff); background-image: -webkit-linear-gradient(#2ecbff, #14c4ff);
background-image: linear-gradient(#66d9ff, #00bfff); } background-image: linear-gradient(#2ecbff, #14c4ff);
color: #bdbdbd; }
/* line 155, ../sass/_mixins.scss */
.items-holder .item.grid-item.selected:not(.disabled):hover.btn-menu .invoke-menu {
color: #75ddff; }
/* line 160, ../sass/_mixins.scss */
.items-holder .item.grid-item.selected.btn-menu .invoke-menu {
color: #52d4ff; }
/* line 109, ../sass/items/_item.scss */ /* line 109, ../sass/items/_item.scss */
.items-holder .item.grid-item.selected .item-type, .items-holder .item.grid-item.selected .top-bar .icon:not(.alert) { .items-holder .item.grid-item.selected .item-type, .items-holder .item.grid-item.selected .top-bar .icon:not(.alert) {
color: #80dfff; } color: #80dfff; }

View File

@ -1,350 +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.
*****************************************************************************/
/*****************************************************************************
* 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.
*****************************************************************************/
/*****************************************************************************
* 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.
*****************************************************************************/
/*****************************************************************************
* 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.
*****************************************************************************/
/* line 31, ../sass/plots/_plots-main.scss */
.gl-plot {
color: #999;
font-size: 0.7rem;
position: relative;
width: 100%;
height: 100%;
/****************************** Limits and Out-of-Bounds data */ }
/* line 38, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-axis-area {
position: absolute; }
/* line 41, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-axis-area.gl-plot-x {
top: auto;
right: 0;
bottom: 5px;
left: 60px;
height: 32px;
width: auto;
overflow: hidden; }
/* line 50, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-axis-area.gl-plot-y {
top: 29px;
right: auto;
bottom: 37px;
left: 0;
width: 60px; }
/* line 59, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-coords {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background: black;
color: #e6e6e6;
padding: 2px 5px;
position: absolute;
top: 39px;
right: auto;
bottom: auto;
left: 70px;
z-index: 10; }
/* line 71, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-coords:empty {
display: none; }
/* line 76, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-display-area {
position: absolute;
top: 29px;
right: 0;
bottom: 37px;
left: 60px;
cursor: crosshair;
border: 1px solid #4d4d4d; }
/* line 86, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-label,
.gl-plot .l-plot-label {
color: #cccccc;
position: absolute;
text-align: center; }
/* line 94, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-label.gl-plot-x-label, .gl-plot .gl-plot-label.l-plot-x-label,
.gl-plot .l-plot-label.gl-plot-x-label,
.gl-plot .l-plot-label.l-plot-x-label {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: auto; }
/* line 103, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-label.gl-plot-y-label, .gl-plot .gl-plot-label.l-plot-y-label,
.gl-plot .l-plot-label.gl-plot-y-label,
.gl-plot .l-plot-label.l-plot-y-label {
-moz-transform-origin: 50% 0;
-ms-transform-origin: 50% 0;
-webkit-transform-origin: 50% 0;
transform-origin: 50% 0;
-moz-transform: translateX(-50%) rotate(-90deg);
-ms-transform: translateX(-50%) rotate(-90deg);
-webkit-transform: translateX(-50%) rotate(-90deg);
transform: translateX(-50%) rotate(-90deg);
display: inline-block;
margin-left: 5px;
left: 0;
top: 50%;
white-space: nowrap; }
/* line 117, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-y-options {
position: absolute;
top: 50%;
right: auto;
bottom: auto;
left: auto5px;
margin-top: -16px;
height: auto;
min-height: 32px;
width: 32px; }
/* line 131, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-hash {
position: absolute;
border: 0 rgba(255, 255, 255, 0.3) dashed; }
/* line 134, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-hash.hash-v {
border-right-width: 1px;
height: 100%; }
/* line 138, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-hash.hash-h {
border-bottom-width: 1px;
width: 100%; }
/* line 144, ../sass/plots/_plots-main.scss */
.gl-plot .gl-plot-legend {
position: absolute;
top: 0;
right: 0;
bottom: auto;
left: 0;
height: 24px;
overflow-x: hidden;
overflow-y: auto; }
/* line 157, ../sass/plots/_plots-main.scss */
.gl-plot .l-limit-bar,
.gl-plot .l-oob-data {
position: absolute;
left: 0;
right: 0;
width: auto; }
/* line 165, ../sass/plots/_plots-main.scss */
.gl-plot .l-limit-bar {
height: auto;
z-index: 0; }
/* line 173, ../sass/plots/_plots-main.scss */
.gl-plot .l-limit-bar.s-limit-yellow {
background: rgba(157, 117, 0, 0.2); }
/* line 174, ../sass/plots/_plots-main.scss */
.gl-plot .l-limit-bar.s-limit-red {
background: rgba(170, 0, 0, 0.2); }
/* line 177, ../sass/plots/_plots-main.scss */
.gl-plot .l-oob-data {
overflow: hidden;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: auto;
height: auto;
pointer-events: none;
height: 10px;
z-index: 1; }
/* line 185, ../sass/plots/_plots-main.scss */
.gl-plot .l-oob-data.l-oob-data-up {
top: 0;
bottom: auto;
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjEuMCIgeDI9IjAuNSIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzc3NDhkNiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM3NzQ4ZDYiIHN0b3Atb3BhY2l0eT0iMC41Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
background-size: 100%;
background-image: -moz-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
background-image: -webkit-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
background-image: linear-gradient(0deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); }
/* line 190, ../sass/plots/_plots-main.scss */
.gl-plot .l-oob-data.l-oob-data-dwn {
bottom: 0;
top: auto;
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzc3NDhkNiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM3NzQ4ZDYiIHN0b3Atb3BhY2l0eT0iMC41Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
background-size: 100%;
background-image: -moz-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
background-image: -webkit-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
background-image: linear-gradient(180deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); }
/* line 200, ../sass/plots/_plots-main.scss */
.gl-plot-legend .plot-legend-item,
.gl-plot-legend .legend-item,
.legend .plot-legend-item,
.legend .legend-item {
display: inline-block;
margin-right: 10px; }
/* line 204, ../sass/plots/_plots-main.scss */
.gl-plot-legend .plot-legend-item span,
.gl-plot-legend .legend-item span,
.legend .plot-legend-item span,
.legend .legend-item span {
vertical-align: middle; }
/* line 207, ../sass/plots/_plots-main.scss */
.gl-plot-legend .plot-legend-item .plot-color-swatch,
.gl-plot-legend .plot-legend-item .color-swatch,
.gl-plot-legend .legend-item .plot-color-swatch,
.gl-plot-legend .legend-item .color-swatch,
.legend .plot-legend-item .plot-color-swatch,
.legend .plot-legend-item .color-swatch,
.legend .legend-item .plot-color-swatch,
.legend .legend-item .color-swatch {
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
display: inline-block;
height: 8px;
width: 8px; }
/* line 220, ../sass/plots/_plots-main.scss */
.gl-plot-legend .plot-legend-item {
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
color: #fff;
line-height: 1.5em;
padding: 0px 5px; }
/* line 226, ../sass/plots/_plots-main.scss */
.gl-plot-legend .plot-legend-item .plot-color-swatch {
border: 1px solid #333;
height: 9px;
width: 9px; }
/* line 234, ../sass/plots/_plots-main.scss */
.tick {
position: absolute;
border: 0 rgba(255, 255, 255, 0.3) solid; }
/* line 237, ../sass/plots/_plots-main.scss */
.tick.tick-x {
border-right-width: 1px;
height: 100%; }
/* line 243, ../sass/plots/_plots-main.scss */
.gl-plot-tick,
.tick-label {
font-size: 0.7rem;
position: absolute;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }
/* line 251, ../sass/plots/_plots-main.scss */
.gl-plot-tick.gl-plot-x-tick-label, .gl-plot-tick.tick-label-x,
.tick-label.gl-plot-x-tick-label,
.tick-label.tick-label-x {
right: auto;
bottom: auto;
left: auto;
height: auto;
width: 20%;
margin-left: -10%;
text-align: center; }
/* line 261, ../sass/plots/_plots-main.scss */
.gl-plot-tick.gl-plot-y-tick-label, .gl-plot-tick.tick-label-y,
.tick-label.gl-plot-y-tick-label,
.tick-label.tick-label-y {
top: auto;
height: 1em;
width: auto;
margin-bottom: -0.5em;
text-align: right; }
/* line 273, ../sass/plots/_plots-main.scss */
.gl-plot-tick.gl-plot-x-tick-label {
top: 5px; }
/* line 276, ../sass/plots/_plots-main.scss */
.gl-plot-tick.gl-plot-y-tick-label {
right: 5px;
left: 5px; }
/* line 283, ../sass/plots/_plots-main.scss */
.tick-label.tick-label-x {
top: 0; }
/* line 286, ../sass/plots/_plots-main.scss */
.tick-label.tick-label-y {
right: 0;
left: 0; }

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,15 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*
@mixin invokeMenu($baseColor: $colorBodyFg) {
$c: $baseColor;
color: $c;
&:hover {
color: lighten($c, $ltGamma);
}
}
*/
/***************************************************************************** /*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government * Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space * as represented by the Administrator of the National Aeronautics and Space
@ -86,7 +95,7 @@
ul.tree { ul.tree {
margin: 0; margin: 0;
padding: 0; } padding: 0; }
/* line 208, ../sass/_mixins.scss */ /* line 264, ../sass/_mixins.scss */
ul.tree li { ul.tree li {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
@ -97,17 +106,17 @@ ul.tree {
position: relative; } position: relative; }
/* line 27, ../sass/tree/_tree.scss */ /* line 27, ../sass/tree/_tree.scss */
ul.tree li span.tree-item { ul.tree li span.tree-item {
-moz-border-radius: 3px; -moz-border-radius: 2px;
-webkit-border-radius: 3px; -webkit-border-radius: 2px;
border-radius: 3px; border-radius: 2px;
-moz-transition: background-color 0.25s; -moz-transition: background-color 0.25s;
-o-transition: background-color 0.25s; -o-transition: background-color 0.25s;
-webkit-transition: background-color 0.25s; -webkit-transition: background-color 0.25s;
transition: background-color 0.25s; transition: background-color 0.25s;
display: block; display: block;
font-size: 0.80rem; font-size: 0.80rem;
height: 1.5rem; height: 1.4rem;
line-height: 1.5rem; line-height: 1.4rem;
margin-bottom: 3px; margin-bottom: 3px;
position: relative; } position: relative; }
/* line 38, ../sass/tree/_tree.scss */ /* line 38, ../sass/tree/_tree.scss */
@ -168,7 +177,7 @@ ul.tree {
width: auto; width: auto;
height: auto; height: auto;
display: block; display: block;
left: 25px; left: 20px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; } white-space: nowrap; }
@ -207,17 +216,16 @@ ul.tree {
/* line 125, ../sass/tree/_tree.scss */ /* line 125, ../sass/tree/_tree.scss */
ul.tree li span.tree-item:not(.loading) { ul.tree li span.tree-item:not(.loading) {
cursor: pointer; } cursor: pointer; }
/* line 130, ../sass/tree/_tree.scss */ /* line 129, ../sass/tree/_tree.scss */
ul.tree li span.tree-item .context-trigger { ul.tree li span.tree-item .context-trigger {
display: none;
top: -1px; top: -1px;
position: absolute; position: absolute;
right: 3px; } right: 3px; }
/* line 136, ../sass/tree/_tree.scss */ /* line 135, ../sass/tree/_tree.scss */
ul.tree li span.tree-item .context-trigger .btn-invoke-menu { ul.tree li span.tree-item .context-trigger .invoke-menu {
font-size: 0.75em; font-size: 0.75em;
height: 0.9rem; height: 0.9rem;
line-height: 0.9rem; } line-height: 0.9rem; }
/* line 145, ../sass/tree/_tree.scss */ /* line 144, ../sass/tree/_tree.scss */
ul.tree ul.tree { ul.tree ul.tree {
margin-left: 15px; } margin-left: 15px; }

View File

@ -2,7 +2,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg> <svg>
<metadata> <metadata>
Created by FontForge 20090622 at Mon May 4 20:21:42 2015 Created by FontForge 20090622 at Tue Jun 9 23:00:40 2015
By deploy user By deploy user
Copyright 2015 Adobe Systems Incorporated. All rights reserved. Copyright 2015 Adobe Systems Incorporated. All rights reserved.
</metadata> </metadata>
@ -62,8 +62,10 @@ q52 -180 95 -231q42 53 88 212z" />
d="M193 787v-193h-193v193h193zM193 491v-195h-193v195h193zM193 193v-193h-193v193h193zM671 732v-82h-388v82h388zM671 435v-81h-388v81h388zM671 138v-82h-388v82h388z" /> d="M193 787v-193h-193v193h193zM193 491v-195h-193v195h193zM193 193v-193h-193v193h193zM671 732v-82h-388v82h388zM671 435v-81h-388v81h388zM671 138v-82h-388v82h388z" />
<glyph glyph-name="eight" unicode="8" horiz-adv-x="636" <glyph glyph-name="eight" unicode="8" horiz-adv-x="636"
d="M625 735v-318h-625v318h625zM625 315v-315h-625v315h625z" /> d="M625 735v-318h-625v318h625zM625 315v-315h-625v315h625z" />
<glyph glyph-name="nine" unicode="9" horiz-adv-x="636" <glyph glyph-name="nine" unicode="9"
d="M267 735v-319h-267v319h267zM267 315v-315h-267v315h267zM630 735v-319h-267v319h267zM630 315v-315h-267v315h267z" /> d="M328 469q0 -20 -13.5 -33.5t-33.5 -13.5h-234q-20 0 -33.5 13.5t-13.5 33.5v234q0 19 14 33t33 14h234q20 0 33.5 -13.5t13.5 -33.5v-234zM750 469q0 -20 -13.5 -33.5t-33.5 -13.5h-234q-20 0 -33.5 13.5t-13.5 33.5v234q0 20 13.5 33.5t33.5 13.5h234q19 0 33 -14
t14 -33v-234zM328 47q0 -20 -13.5 -33.5t-33.5 -13.5h-234q-19 0 -33 14t-14 33v234q0 20 13.5 33.5t33.5 13.5h234q20 0 33.5 -13.5t13.5 -33.5v-234zM750 47q0 -19 -14 -33t-33 -14h-234q-20 0 -33.5 13.5t-13.5 33.5v234q0 20 13.5 33.5t33.5 13.5h234q20 0 33.5 -13.5
t13.5 -33.5v-234z" />
<glyph glyph-name="colon" unicode=":" horiz-adv-x="625" <glyph glyph-name="colon" unicode=":" horiz-adv-x="625"
d="M625 -19l-397 397l397 397v-794zM173 756v-735h-173v735h173z" /> d="M625 -19l-397 397l397 397v-794zM173 756v-735h-173v735h173z" />
<glyph glyph-name="semicolon" unicode=";" horiz-adv-x="624" <glyph glyph-name="semicolon" unicode=";" horiz-adv-x="624"
@ -163,8 +165,8 @@ d="M123 367l-123 123v147l123 -123l220 221v-147zM123 0l-123 123v147l123 -123l220
d="M123 93l-123 123v147l123 -123l220 220v-147zM485 262v71h313v-71h-313z" /> d="M123 93l-123 123v147l123 -123l220 220v-147zM485 262v71h313v-71h-313z" />
<glyph glyph-name="l" unicode="l" horiz-adv-x="617" <glyph glyph-name="l" unicode="l" horiz-adv-x="617"
d="M551 386h66v-386h-617v386h63v106q0 100 72 171.5t173 71.5q99 0 171 -71.5t72 -171.5v-106zM173 492v-106h267v106q0 56 -38.5 94.5t-93.5 38.5q-56 0 -95.5 -39t-39.5 -94z" /> d="M551 386h66v-386h-617v386h63v106q0 100 72 171.5t173 71.5q99 0 171 -71.5t72 -171.5v-106zM173 492v-106h267v106q0 56 -38.5 94.5t-93.5 38.5q-56 0 -95.5 -39t-39.5 -94z" />
<glyph glyph-name="m" unicode="m" horiz-adv-x="804" <glyph glyph-name="m" unicode="m" horiz-adv-x="751"
d="M804 729v-156h-804v156h804zM804 445v-160h-804v160h804zM804 158v-158h-804v158h804z" /> d="M751 563h-751v188h751v-188zM751 282h-751v187h751v-187zM751 0h-751v188h751v-188z" />
<glyph glyph-name="n" unicode="n" horiz-adv-x="738" <glyph glyph-name="n" unicode="n" horiz-adv-x="738"
d="M690 697q48 -50 48 -88q0 -18 -10 -26l-243 -243l-3 -3l-4 -2l-227 -78l77 228l2 4l3 3l242 243q22 19 57 3q29 -14 58 -41zM461 371l1 2l-3 50l-34 2h-9v10v28l-51 6l-2 -1l-32 -97l33 -33zM249 512l-134 -389l304 104v-215q-136 -8 -246.5 29t-172.5 93v561 d="M690 697q48 -50 48 -88q0 -18 -10 -26l-243 -243l-3 -3l-4 -2l-227 -78l77 228l2 4l3 3l242 243q22 19 57 3q29 -14 58 -41zM461 371l1 2l-3 50l-34 2h-9v10v28l-51 6l-2 -1l-32 -97l33 -33zM249 512l-134 -389l304 104v-215q-136 -8 -246.5 29t-172.5 93v561
q48 -42 125 -75.5t172 -43.5l-24 -25l-17 -17z" /> q48 -42 125 -75.5t172 -43.5l-24 -25l-17 -17z" />
@ -232,6 +234,22 @@ d="M748 750l-375 -750l-374 750h749z" />
d="M-1 0l375 750l374 -750h-749z" /> d="M-1 0l375 750l374 -750h-749z" />
<glyph glyph-name="icircumflex" unicode="&#xee;" horiz-adv-x="748" <glyph glyph-name="icircumflex" unicode="&#xee;" horiz-adv-x="748"
d="M748 750l-375 -375l-374 375h749zM748 375l-375 -374l-374 374h749z" /> d="M748 750l-375 -375l-374 375h749zM748 375l-375 -374l-374 374h749z" />
<glyph glyph-name="idieresis" unicode="&#xef;"
d="M0 749l750 -375l-750 -374v749z" />
<glyph glyph-name="ntilde" unicode="&#xf1;"
d="M283 751v-751h-188v751h188zM658 751v-751h-188v751h188z" />
<glyph glyph-name="ograve" unicode="&#xf2;" horiz-adv-x="751"
d="M469 563h-102q-74 0 -126.5 -52.5t-52.5 -126.5v-102h-94q-38 0 -66 27.5t-28 65.5v282q0 38 28 66t66 28h281q39 0 66.5 -28t27.5 -66v-94zM657 469q38 0 66 -27.5t28 -66.5v-281q0 -38 -28 -66t-66 -28h-282q-38 0 -65.5 28t-27.5 66v281q0 39 27.5 66.5t65.5 27.5h282
z" />
<glyph glyph-name="oacute" unicode="&#xf3;" horiz-adv-x="751"
d="M216 375l187 -187h-309q-38 0 -66 27.5t-28 66.5v375q0 38 28 66t66 28h375q39 0 66.5 -28t27.5 -66v-309l-188 187zM751 422v-422h-422v94h234l-281 281l93 94l282 -281v234h94z" />
<glyph glyph-name="ocircumflex" unicode="&#xf4;" horiz-adv-x="751"
d="M610 751q58 0 99.5 -41.5t41.5 -99.5v-469q0 -59 -41.5 -100t-99.5 -41h-469q-59 0 -100 41t-41 100v469q0 58 41 99.5t100 41.5h469zM610 282v328h-328l93 -94l-234 -375l375 234z" />
<glyph glyph-name="otilde" unicode="&#xf5;" horiz-adv-x="657"
d="M329 657q136 0 232 -96t96 -232t-96 -232.5t-232 -96.5q-137 0 -233 96t-96 233q0 136 96.5 232t232.5 96zM329 329v246q-101 0 -175 -72zM422 704q0 -20 -13.5 -33.5t-33.5 -13.5h-46h-47q-20 0 -33.5 13.5t-13.5 33.5t13.5 33.5t33.5 13.5h93q20 0 33.5 -13.5
t13.5 -33.5z" />
<glyph glyph-name="odieresis" unicode="&#xf6;"
d="M328 562v-375h-328v375h328zM750 562v-375h-328v375h328z" />
<glyph glyph-name="fraction" unicode="&#x2044;" horiz-adv-x="761" <glyph glyph-name="fraction" unicode="&#x2044;" horiz-adv-x="761"
d="M380 751q158 0 269.5 -111.5t111.5 -268.5q0 -133 -82.5 -236.5t-209.5 -134.5l-4 491l-176 -490q-126 31 -207.5 134.5t-81.5 235.5q0 157 111.5 268.5t268.5 111.5zM168 488l55 20l-34 94l-56 -20zM410 530v100h-59v-100h59zM593 488l34 94l-55 20l-34 -94z" /> d="M380 751q158 0 269.5 -111.5t111.5 -268.5q0 -133 -82.5 -236.5t-209.5 -134.5l-4 491l-176 -490q-126 31 -207.5 134.5t-81.5 235.5q0 157 111.5 268.5t268.5 111.5zM168 488l55 20l-34 94l-56 -20zM410 530v100h-59v-100h59zM593 488l34 94l-55 20l-34 -94z" />
<glyph glyph-name="H.002" horiz-adv-x="803" <glyph glyph-name="H.002" horiz-adv-x="803"

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -19,13 +19,17 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
// Features
$enableImageryThumbs: false; // Set to true if historical imagery thumbnails are supported
// Margins, spacing, radii // Margins, spacing, radii
$bodyMargin: 10px; $bodyMargin: 10px;
$interiorMargin: 5px; $interiorMargin: 5px;
$interiorMarginLg: $interiorMargin * 2; $interiorMarginLg: $interiorMargin * 2;
$interiorMarginSm: 3px; $interiorMarginSm: 3px;
$basicCr: 3px; $basicCr: 2px;
$controlCr: $basicCr; $controlCr: 2px;
$smallCr: 2px; $smallCr: 2px;
$badgeW: 35px; $badgeW: 35px;
@ -34,9 +38,12 @@ $colorBodyBg: #333;
$colorBodyFg: #999; $colorBodyFg: #999;
$colorFooterBg: #000; $colorFooterBg: #000;
$colorKey: #0099cc; $colorKey: #0099cc;
$colorKeySelectedBg: #005177;
$colorKeyFg: #fff; $colorKeyFg: #fff;
$colorAlt1: #ffc700; $colorAlt1: #ffc700;
$colorAlert: #ff3c00; $colorAlert: #ff3c00;
$colorPausedBg: #c56f01;
$colorPausedFg: #fff;
$colorCheck: $colorKey; $colorCheck: $colorKey;
$colorCreateBtn: $colorKey; $colorCreateBtn: $colorKey;
$colorInteriorBorder: lighten($colorBodyBg, 10%); $colorInteriorBorder: lighten($colorBodyBg, 10%);
@ -50,7 +57,18 @@ $colorLimitYellow: #9d7500;
$colorLimitRed: #aa0000; $colorLimitRed: #aa0000;
$colorTelemFresh: #fff; $colorTelemFresh: #fff;
$colorTelemStale: #888; $colorTelemStale: #888;
$styleTelemStale: italic;
$colorInfoBubbleFg: #666;
$colorInfoBubbleBg: #ddd;
$colorThumbsBubbleFg: lighten($colorBodyFg, 10%);
$colorThumbsBubbleBg: lighten($colorBodyBg, 10%);
$colorLimitYellow: #9d7500;
$colorLimitRed: #aa0000;
$colorTelemFresh: #fff;
$colorTelemStale: #888;
$styleTelemState: italic; $styleTelemState: italic;
$colorInfoBubbleFg: #666;
$colorInfoBubbleBg: #ddd;
// Ratios // Ratios
$ltGamma: 20%; $ltGamma: 20%;
@ -108,7 +126,7 @@ $controlDisabledOpacity: 0.3;
$formLabelW: 20%; $formLabelW: 20%;
$formInputH: 22px; $formInputH: 22px;
$formRowCtrlsH: 14px; $formRowCtrlsH: 14px;
$menuLineH: 1.5rem; $menuLineH: 1.4rem;
$scrollbarTrackSize: 10px; $scrollbarTrackSize: 10px;
$scrollbarTrackColorBg: rgba(#000, 0.4); $scrollbarTrackColorBg: rgba(#000, 0.4);
$btnStdH: 25px; $btnStdH: 25px;
@ -124,3 +142,19 @@ $tickLblH: 15px;
$tickLblW: 50px; $tickLblW: 50px;
$tickH: $ticksH - $tickLblVMargin - $tickLblH; $tickH: $ticksH - $tickLblVMargin - $tickLblH;
$tickW: 1px; $tickW: 1px;
// Imagery
$imageMainControlBarH: 20px;
$imageThumbsD: 120px;
$imageThumbsWrapperH: $imageThumbsD * 1.4;
$imageThumbPad: 1px;
// Bubbles
$bubbleArwSize: 10px;
$bubblePad: $interiorMargin;
$bubbleMinW: 100px;
$bubbleMaxW: 300px;
// Timing
$controlFadeMs: 100ms;

View File

@ -44,7 +44,7 @@ a.disabled {
} }
@include keyframes(pulse) { @include keyframes(pulse) {
0% { opacity: 0.2; } 0% { opacity: 0.5; }
100% { opacity: 1; } 100% { opacity: 1; }
} }
@ -57,5 +57,5 @@ a.disabled {
} }
.pulse { .pulse {
@include pulse(1000ms); @include pulse(750ms);
} }

View File

@ -69,6 +69,10 @@ span {
*/ */
} }
mct-container {
display: block;
}
.abs { .abs {
position: absolute; position: absolute;
top: 0; top: 0;
@ -117,6 +121,13 @@ span {
display: none !important; display: none !important;
} }
.paused {
&:not(.s-btn) {
border-color: $colorPausedBg !important;
color: $colorPausedBg !important;
}
}
.sep { .sep {
color: rgba(#fff, 0.2); color: rgba(#fff, 0.2);
} }

View File

@ -36,7 +36,7 @@
// Don't pad in from top and bottom // Don't pad in from top and bottom
//top: 0; bottom: 0; //top: 0; bottom: 0;
.object-browse-bar { .object-browse-bar {
.t-btn.key-window { .btn.key-window {
// Hide the Open in New Window button // Hide the Open in New Window button
display: none; display: none;
} }

View File

@ -42,12 +42,6 @@
&.major { &.major {
font-size: 1.65em; font-size: 1.65em;
} }
&:hover {
// color: lighten($c, $ltGamma);
.invoke-menu {
// color: lighten($colorBodyBg, $ltGamma * 2);
}
}
} }
} }
@ -56,36 +50,35 @@
} }
.invoke-menu { .invoke-menu {
@include invokeMenu($colorKey); //@include invokeMenu(); // $colorKey
text-shadow: none;
display: inline-block; display: inline-block;
font-size: 1rem; font-size: 0.8rem;
vertical-align: middle; vertical-align: middle;
} }
.btn-menu .invoke-menu, .btn-menu .invoke-menu,
.icon.major .invoke-menu { .icon.major .invoke-menu {
margin-left: $interiorMargin; margin-left: $interiorMarginSm;
}
.icon-buttons-main .invoke-menu {
@include invokeMenu(lighten($colorBodyBg, $ltGamma));
}
.menu-element .invoke-menu {
} }
/*
.object-header .type-icon { .object-header .type-icon {
color: $colorKey; color: $colorKey;
margin-right: $interiorMargin; margin-right: $interiorMarginSm;
} }
*/
.menu .type-icon, .menu .type-icon,
.tree-item .type-icon, .tree-item .type-icon,
.icon-btn .menu.dropdown .icon, .super-menu.menu .type-icon {
.super-menu.menu.dropdown .icon { font-size: $menuLineH * 0.8; //.93
font-size: $menuLineH * 0.93;
line-height: $menuLineH * 1.13; line-height: $menuLineH * 1.13;
position: absolute; position: absolute;
} }
.super-menu.menu.dropdown .icon {
font-size: $menuLineH * 0.95
}

View File

@ -46,6 +46,9 @@
@import "controls/lists"; @import "controls/lists";
@import "controls/menus"; @import "controls/menus";
@import "controls/time-controller"; @import "controls/time-controller";
@import "edit/editor";
@import "features/imagery";
@import "features/time-display";
@import "forms/mixins"; @import "forms/mixins";
@import "forms/elems"; @import "forms/elems";
@import "forms/validation"; @import "forms/validation";

View File

@ -26,6 +26,16 @@
width: auto; height: auto; width: auto; height: auto;
} }
@mixin trans-prop-nice($props, $t) {
@if $t == 0 {
@include transition-property(none);
} @else {
@include transition-property($props);
@include transition-duration($t);
@include transition-timing-function(ease-in-out);
}
}
@mixin trans-prop-nice-fade($t: 0.5s) { @mixin trans-prop-nice-fade($t: 0.5s) {
@if $t == 0 { @if $t == 0 {
@include transition-property(none); @include transition-property(none);
@ -42,6 +52,12 @@
@include transition-timing-function(ease-in-out); @include transition-timing-function(ease-in-out);
} }
@mixin trans-prop-nice-resize-w($t: 0.5s) {
@include transition-property(width, left, right);
@include transition-duration($t);
@include transition-timing-function(ease-in-out);
}
@mixin triangle-right($size, $color) { @mixin triangle-right($size, $color) {
$size: $size/2; $size: $size/2;
$ratio: 1; $ratio: 1;
@ -62,6 +78,31 @@
border-right: $size/$ratio solid transparent; border-right: $size/$ratio solid transparent;
} }
@mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) {
//$size: $size*2;
width: 0;
height: 0;
$slopedB: $size/$ratio solid transparent;
$straightB: $size solid $color;
@if $dir == "up" {
border-left: $slopedB;
border-right: $slopedB;
border-bottom: $straightB;
} @else if $dir == "right" {
border-top: $slopedB;
border-bottom: $slopedB;
border-left: $straightB;
} @else if $dir == "down" {
border-left: $slopedB;
border-right: $slopedB;
border-top: $straightB;
} @else {
border-top: $slopedB;
border-bottom: $slopedB;
border-right: $straightB;
}
}
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) { @mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
@include background-image(linear-gradient(-45deg, @include background-image(linear-gradient(-45deg,
rgba($c, $a) 25%, transparent 25%, rgba($c, $a) 25%, transparent 25%,
@ -90,20 +131,35 @@
} }
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg, $hover: false) { @mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg, $hover: false) {
@include background-image(linear-gradient(lighten($bg, 10%), lighten($bg, 5%))); $ltnRatio: 7%;
$gradRatio: 5%;
$hovRatio: 7%;
$bgBase: lighten($bg, $ltnRatio);
$fgBase: lighten($fg, $ltnRatio);
$gradC1: lighten($bgBase, $gradRatio);
$gradC2: $bgBase;
$cInvokeBase: lighten($gradC1, $ltnRatio*2);
@include background-image(linear-gradient($gradC1, $gradC2));
@include border-radius($controlCr); @include border-radius($controlCr);
@include box-sizing(border-box); @include box-sizing(border-box);
// @include box-shadow(rgba(black, 0.3) 0 1px 2px);
@include boxShdwSubtle(); @include boxShdwSubtle();
border: none; border: none;
border-top: 1px solid lighten($bg, 20%); border-top: 1px solid lighten($gradC1, 2%);
color: $fg; color: $fg;
display: inline-block; display: inline-block;
@if $hover == true { @if $hover == true {
&:hover { &:not(.disabled):hover {
@include background-image(linear-gradient(lighten($bg, 20%), lighten($bg, 15%))); @include background-image(linear-gradient(lighten($gradC1, $hovRatio), lighten($gradC2, $hovRatio)));
color: lighten($fgBase, $hovRatio);
&.btn-menu .invoke-menu {
color: lighten($cInvokeBase, $hovRatio);
}
} }
} }
&.btn-menu .invoke-menu {
color: $cInvokeBase;
}
} }
@mixin sliderTrack($bg: $scrollbarTrackColorBg) { @mixin sliderTrack($bg: $scrollbarTrackColorBg) {
@ -144,18 +200,16 @@
} }
@mixin btnSubtle($bg: $colorBodyBg, $fg: $colorBodyFg) { @mixin btnSubtle($bg: $colorBodyBg, $fg: $colorBodyFg) {
@include containerSubtle($bg, $fg); @include containerSubtle($bg, $fg, true);
&:not(.disabled):hover {
@include background-image(linear-gradient(lighten($bg, 20%), lighten($bg, 10%)));
}
} }
@mixin btnNoticeable($bg: $colorBodyBg, $fg: $colorBodyFg) { @mixin btnNoticeable($bg: $colorBodyBg, $fg: $colorBodyFg) {
@include containerSubtle($bg, $fg); // No longer should be used; use btnSubtle instead
@include background-image(linear-gradient(lighten($bg, 20%), $bg)); //@include containerSubtle($bg, $fg, true);
&:not(.disabled):hover { //@include background-image(linear-gradient(lighten($bg, 20%), $bg));
/* &:not(.disabled):hover {
@include background-image(linear-gradient(lighten($bg, 30%), lighten($bg, 10%))); @include background-image(linear-gradient(lighten($bg, 30%), lighten($bg, 10%)));
} }*/
} }
@mixin boxIncised($sVal: 0.6) { @mixin boxIncised($sVal: 0.6) {
@ -166,8 +220,8 @@
border: 1px solid $c; border: 1px solid $c;
} }
@mixin boxShdwSubtle($sVal: 0.3) { @mixin boxShdwSubtle($sVal: 0.2) {
@include box-shadow(rgba(black, $sVal) 0 1px 3px); @include box-shadow(rgba(black, $sVal) 0 1px 2px);
} }
@mixin boxShdwLarge($sVal: 0.7) { @mixin boxShdwLarge($sVal: 0.7) {
@ -194,13 +248,15 @@
} }
@mixin invokeMenu($baseColor) { /*
@mixin invokeMenu($baseColor: $colorBodyFg) {
$c: $baseColor; $c: $baseColor;
color: $c; color: $c;
&:hover { &:hover {
color: lighten($c, $ltGamma); color: lighten($c, $ltGamma);
} }
} }
*/
@mixin menuUlReset() { @mixin menuUlReset() {
margin: 0; margin: 0;

View File

@ -19,37 +19,97 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
$pad: $interiorMargin * 2; $baseRatio: 1.5;
$pad: $interiorMargin * $baseRatio;
/*********************************** TYPE STYLES */ /******* LAYOUT AND SIZING */
.t-btn { .btn,
cursor: pointer; .l-btn {
line-height: 1.25em;
padding: 0 $pad;
text-decoration: none;
&.lg,
&.create-btn {
$h: $ueTopBarH - $interiorMargin;
height: $h;
line-height: $h;
padding: 0 $pad * 3;
}
&.create-btn {
.menu {
margin-left: $pad * -1;
}
>.ui-symbol {
font-size: 1.1em;
}
}
&.sm {
padding: 0 $pad / $baseRatio;
}
&.vsm {
padding: 0 ($pad / $baseRatio) / 2;
}
} }
/*********************************** STYLE STYLES */ /*********************************** STYLE STYLES */
.btn,
.s-btn { .s-btn {
$base: lighten($colorBodyBg, 20%); $base: lighten($colorBodyBg, 20%); // Moved to s-btn
@include border-radius($controlCr); @include border-radius($controlCr);
@include box-sizing(border-box); @include box-sizing(border-box);
@include text-shadow(rgba(black, 0.3) 0 1px 1px); @include text-shadow(rgba(black, 0.3) 0 1px 1px);
cursor: pointer;
line-height: 1.2em; line-height: 1.2em;
padding: 0 $pad;
text-decoration: none; text-decoration: none;
&.major {
$bg: $colorKey;
@include btnSubtle($bg);
$fg: lighten($bg, 50%);
color: $fg;
&:hover {
@include btnSubtle(lighten($bg, 5%), $fg);
//color: $fg;
}
.invoke-menu {
color: $fg;
}
}
&.subtle {
@include btnSubtle($base, lighten($base, 40%));
}
&.very-subtle,
&.s-very-subtle { &.s-very-subtle {
@include containerSubtle($colorBodyBg, $colorBodyFg, true); @include containerSubtle($colorBodyBg, $colorBodyFg, true);
&.paused {
@include containerSubtle($colorPausedBg, $colorPausedFg, true);
.icon:before {
content:"\0000EF";
}
}
} }
} }
.icon-btn,
.s-icon-btn { .s-icon-btn {
@extend .s-btn; @extend .s-btn;
font-size: 1.2em; font-size: 1em;
.icon { .icon {
color: $colorKey; color: $colorKey;
} }
&.paused {
.icon {
color: $colorPausedFg;
}
}
&:not(.disabled):hover .icon { &:not(.disabled) {
color: lighten($colorKey, $ltGamma); &:not(.paused) {
&:hover {
.icon {
color: lighten($colorKey, $ltGamma);
}
}
}
} }
&.labeled { &.labeled {
@ -61,12 +121,30 @@ $pad: $interiorMargin * 2;
margin-left: $interiorMargin; margin-left: $interiorMargin;
} }
} }
&.pause-play {
&.paused {
@include pulse(500ms);
}
.icon:before {
content:"\0000F1";
}
}
&.show-thumbs {
.icon:before {
content:"\000039";
}
}
} }
/*********************************** LAYOUT STYLES */ /*********************************** LAYOUT STYLES */
span.btn,
span.btn span,
span.l-btn, span.l-btn,
span.l-btn span, span.l-btn span,
a.btn,
a.btn span,
a.l-btn, a.l-btn,
a.l-btn span { a.l-btn span {
display: inline-block; display: inline-block;

View File

@ -19,7 +19,8 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
.control { /*.control {
// UNUSED?
&.view-control { &.view-control {
.icon { .icon {
display: inline-block; display: inline-block;
@ -45,10 +46,10 @@
} }
} }
} }
} }*/
.accordion { .accordion {
$accordionHeadH: 18px; $accordionHeadH: 18px;
margin-top: $interiorMargin; margin-top: $interiorMargin;
&:first-child { &:first-child {
margin-top: 0; margin-top: 0;
@ -56,16 +57,20 @@
.accordion-head { .accordion-head {
$op: 0.2; $op: 0.2;
@include border-radius($basicCr * 0.75); @include border-radius($basicCr * 0.75);
@include box-sizing("border-box"); @include box-sizing("border-box");
background: rgba($colorBodyFg, $op); background: rgba($colorBodyFg, $op);
cursor: pointer; cursor: pointer;
font-size: 0.75em; font-size: 0.75em;
line-height: $accordionHeadH; line-height: $accordionHeadH;
margin-bottom: $interiorMargin; margin-bottom: $interiorMargin;
padding: 0 $interiorMargin; padding: 0 $interiorMargin;
position: absolute; position: absolute;
top: 0; right: 0; bottom: auto; left: 0; top: 0;
width: auto; height: $accordionHeadH; right: 0;
bottom: auto;
left: 0;
width: auto;
height: $accordionHeadH;
text-transform: uppercase; text-transform: uppercase;
&:hover { &:hover {
background: rgba($colorBodyFg, $op * 2); background: rgba($colorBodyFg, $op * 2);
@ -84,79 +89,14 @@
content: "v"; content: "v";
} }
} }
.accordion-contents { .accordion-contents {
position: absolute; position: absolute;
top: $accordionHeadH + $interiorMargin; right: 0; bottom: 0; left: 0; top: $accordionHeadH + $interiorMargin;
overflow-y: auto; right: 0;
overflow-x: hidden; bottom: 0;
} left: 0;
} overflow-y: auto;
overflow-x: hidden;
.btn {
$base: lighten($colorBodyBg, 20%); // Moved to s-btn
$p: 10px; // Moved to s-btn
@include border-radius($controlCr); // Moved to s-btn
@include box-sizing(border-box); // Moved to s-btn
@include text-shadow(rgba(black, 0.3) 0 1px 1px); // Moved to s-btn
// display: inline-block;
// margin-right: 10px;
padding: 0 ($interiorMargin * 2); // Moved to s-btn
text-decoration: none; // Moved to s-btn
&.create-btn {
$h: $ueTopBarH - $interiorMargin; //$btnStdH * 1.5;;
$p: $p * 2.25;
height: $h;
line-height: $h;
//font-size: 1.1em;
padding: 0 $p;
.menu {
margin-left: $p * -1;
}
.ui-symbol.major {
font-size: 1.1em;
}
}
&.major {
$bg: $colorKey;
@include btnNoticeable($bg);
$fg: lighten($bg, 50%);
color: $fg;
&:hover {
@include btnNoticeable(lighten($bg, 5%));
color: $fg;
}
.invoke-menu {
color: $fg;
}
}
&.normal {
padding: $p * 0.5 $p * 0.7;
}
&.outline {
&:hover {
background: rgba(#fff, 0.1);
}
}
&.subtle {
@include btnSubtle($base, lighten($base, 40%));
}
&.very-subtle {
@include btnSubtle($colorBodyBg, lighten($colorBodyBg, 50%));
}
&.lg {
@include border-radius($controlCr * 1.5);
font-size: 1.2em;
padding: 7px 25px;
}
&.icon-btn {
.icon {
color: $colorKey;
}
&:not(.disabled):hover .icon {
color: lighten($colorKey, $ltGamma);
}
} }
} }
@ -166,12 +106,12 @@
.btn-set, .btn-set,
.t-btn { .t-btn {
display: inline-block; display: inline-block;
// margin-left: $interiorMargin; // margin-left: $interiorMargin;
} }
.btn, .btn,
.t-btn { .t-btn {
&:first-child { &:first-child {
// margin-left: 0; // margin-left: 0;
} }
} }
} }
@ -199,6 +139,20 @@
} }
} }
.l-local-controls {
// Control shown when hovering over an object, like plots and imagery
// Default position is upper right
$p: $interiorMargin;
position: absolute;
top: $p;
right: $p;
z-index: 5;
}
.s-local-controls {
font-size: 0.7rem;
}
.btn-set { .btn-set {
// Buttons that have a very tight conceptual grouping - no internal space between them. // Buttons that have a very tight conceptual grouping - no internal space between them.
display: inline-block; display: inline-block;
@ -230,8 +184,8 @@
height: $h; height: $h;
line-height: $h; line-height: $h;
.icon:not(.invoke-menu) { .icon:not(.invoke-menu) {
// position: relative; // position: relative;
// top: -0.04em; // top: -0.04em;
font-size: 150%; font-size: 150%;
vertical-align: middle; vertical-align: middle;
} }
@ -318,37 +272,43 @@ label.checkbox.custom {
.btn-menu { .btn-menu {
$h: 20px; $h: 20px;
$p: $interiorMargin * 2; $p: $interiorMarginSm * 2;
$c: $colorBodyFg; $c: $colorBodyFg;
@include btnSubtle($colorBodyBg); @include btnSubtle($colorBodyBg);
height: $h; /* height: $h;
line-height: $h; line-height: $h;
&.dropdown { &.dropdown {
// padding-left: $p; padding-left: $p;
padding-left: $p; padding-right: $p;
padding-right: $p; }*/
}
&:not(.disabled):hover { &:not(.disabled):hover {
color: lighten($c, 20%); color: lighten($c, 20%);
} }
&.btn-invoke-menu { /* &.context-available {
$c: $colorKey; // An element like the invoke-menu triangle;
color: $c; // Indicates that this element has a dropdown menu available;
padding: 0 5px; // Currently unused
&:hover { $c: $colorKey;
color: lighten($c, 10%); color: $c;
} padding: 0 5px;
&:hover {
color: lighten($c, 10%);
}
}*/
span.l-click-area {
// In markup, this element should not enclose anything.
@extend .abs;
} }
span.l-click-area {
// In markup, this element should not enclose anything.
@extend .abs;
}
.type-icon { .type-icon {
margin-right: $interiorMargin; //margin-right: $interiorMargin;
}
.name {
margin-left: $interiorMargin;
} }
.menu { .menu {
// margin-left: (-1 * $p); // margin-left: (-1 * $p);
@ -388,17 +348,23 @@ label.checkbox.custom {
} }
} }
.view-switcher {
@include trans-prop-nice-fade($controlFadeMs);
}
/******************************************************** OBJECT-HEADER */ /******************************************************** OBJECT-HEADER */
.object-header { .object-header {
display: inline-block; display: inline-block;
font-size: 1em; font-size: 1em;
.title { .label {
color: lighten($colorBodyFg, 40%); .title-label {
} color: lighten($colorBodyFg, 40%);
.type-icon { }
font-size: 1.5em; .type-icon {
margin-right: $interiorMargin; font-size: 1.5em;
vertical-align: middle; margin-right: $interiorMargin;
vertical-align: middle;
}
} }
} }
@ -412,86 +378,53 @@ label.checkbox.custom {
} }
} }
/******************************************************** VIEW-CONTROLS */
.view-controls .view-type {
$d: 20px;
$p: 5px;
@include border-radius($controlCr);
box-sizing: border-box;
display: inline-block;
margin-left: $interiorMargin;
height: $d;
line-height: $d;
padding-left: $p;
padding-right: $p;
&.cur {
background: lighten($colorBodyBg, $ltGamma);
}
}
.edit-mode .top-bar .control-set.edit-view-controls {
// Used in templates/edit-view-controls.html
margin-right: $interiorMargin * 10;
}
/******************************************************** SLIDERS */ /******************************************************** SLIDERS */
.wrapper-slider {
position: relative;
}
.slider { .slider {
//$knobH: 70%; //14px; $knobH: 100%; //14px;
$knobW: 12px; $knobW: 12px;
$slotH: 80%; $slotH: 50%;
$rangeO: 0.3;
.slot { .slot {
// @include border-radius($basicCr * .75); // @include border-radius($basicCr * .75);
@include sliderTrack(); @include sliderTrack();
height: auto; height: $slotH;
width: auto; width: auto;
position: absolute; position: absolute;
//top: ($knobH - $slotH) / 2; top: ($knobH - $slotH) / 2;
top: (100% - $slotH)/2;
right: 0; right: 0;
bottom: (100% - $slotH)/2; bottom: auto;
left: 0; left: 0;
z-index: 0;
.range {
background: rgba($colorKey, $rangeO);
cursor: ew-resize;
position: absolute;
top: 0;
right: auto;
bottom: 0;
left: auto;
height: auto;
width: auto;
z-index: 1;
&:hover {
background: rgba($colorKey, $rangeO + 0.2);
}
}
} }
.knob { .knob {
@include btnSubtle(); @include btnSubtle();
@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid); @include controlGrippy(rgba(black, 0.3), vertical, 1px, solid);
@include border-radius(2px);
cursor: ew-resize; cursor: ew-resize;
position: absolute; position: absolute;
height: auto; height: $knobH;
width: $knobW; width: $knobW;
top: 0; top: 0;
bottom: 0; auto: 0;
bottom: auto;
left: auto; left: auto;
z-index: 2;
&.knob-l { margin-left: $knobW / -2; }
&.knob-r { margin-right: $knobW / -2; }
&:before { &:before {
top: 1px; top: 1px;
bottom: 3px; bottom: 3px;
//left: ($knobW / 2) - 1; left: ($knobW / 2) - 1;
//margin-left: -1px; }
left: 45%;
}
.range {
background: rgba($colorKey, 0.6);
cursor: ew-resize;
position: absolute;
top: 0;
right: auto;
bottom: 0;
left: auto;
height: auto;
width: auto;
&:hover {
background: rgba($colorKey, 0.7);
} }
} }
} }
@ -499,23 +432,23 @@ label.checkbox.custom {
/******************************************************** BROWSER ELEMENTS */ /******************************************************** BROWSER ELEMENTS */
::-webkit-scrollbar { ::-webkit-scrollbar {
@include sliderTrack(); @include sliderTrack();
height: $scrollbarTrackSize; height: $scrollbarTrackSize;
width: $scrollbarTrackSize; width: $scrollbarTrackSize;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
$bg: lighten($colorBodyBg, 10%); $bg: lighten($colorBodyBg, 10%);
@include background-image(linear-gradient(lighten($bg, 10%), lighten($bg, 5%) 20px)); @include background-image(linear-gradient(lighten($bg, 10%), lighten($bg, 5%) 20px));
@include border-radius(1px); @include border-radius(1px);
@include box-sizing(border-box); @include box-sizing(border-box);
@include boxShdwSubtle(); @include boxShdwSubtle();
border-top: 1px solid lighten($bg, 20%); border-top: 1px solid lighten($bg, 20%);
&:hover { &:hover {
@include background-image(linear-gradient(lighten($bg, 20%), lighten($bg, 15%) 20px)); @include background-image(linear-gradient(lighten($bg, 20%), lighten($bg, 15%) 20px));
} }
} }
::-webkit-scrollbar-corner { ::-webkit-scrollbar-corner {
background: rgba(#000, 0.4); background: rgba(#000, 0.4);
} }

View File

@ -41,7 +41,7 @@
@include box-sizing(border-box); @include box-sizing(border-box);
border-top: 1px solid lighten($bg, 20%); border-top: 1px solid lighten($bg, 20%);
line-height: $menuLineH; line-height: $menuLineH;
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 3) + $treeTypeIconW; padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
white-space: nowrap; white-space: nowrap;
&:first-child { &:first-child {
border: none; border: none;
@ -168,7 +168,7 @@
position: absolute; position: absolute;
height: 200px; height: 200px;
width: 170px; width: 170px;
z-index: 59; z-index: 70;
.context-menu-wrapper { .context-menu-wrapper {
position: absolute; position: absolute;
height: 100%; height: 100%;

View File

@ -19,3 +19,86 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
.edit-main {
$handleD: 15px;
$cr: 5px;
.edit-corner,
.edit-handle {
position: absolute;
z-index: 2;
}
.edit-corner {
width: $handleD;
height: $handleD;
&.edit-resize-nw {
@include border-bottom-right-radius($cr);
cursor: nw-resize;
top: 0; left: 0;
}
&.edit-resize-se {
@include border-top-left-radius($cr);
cursor: se-resize;
bottom: 0; right: 0;
}
&.edit-resize-sw {
@include border-top-right-radius($cr);
cursor: sw-resize;
bottom: 0; left: 0;
}
}
.edit-handle {
top: $handleD; right: $handleD; bottom: $handleD; left: $handleD;
&.edit-move {
$m: 0; //$handleD;
cursor: move;
left: $m;
right: $m;
top: $m;
bottom: $m;
z-index: 1;
}
&.edit-resize-n {
top: 0px; bottom: auto;
height: $handleD;
cursor: n-resize;
}
&.edit-resize-e {
right: 0px; left: auto;
width: $handleD;
cursor: e-resize;
}
&.edit-resize-s {
bottom: 0px; top: auto;
height: $handleD;
cursor: s-resize;
}
&.edit-resize-w {
left: 0px; right: auto;
width: $handleD;
cursor: w-resize;
}
}
.frame.child-frame.panel {
&:hover {
@include boxShdwLarge();
border-color: $colorKey;
z-index: 2;
.view-switcher {
opacity: 1;
}
.edit-corner {
background-color: rgba($colorKey, 0.8);
&:hover {
background-color: rgba($colorKey, 1);
}
}
}
}
}

View File

@ -0,0 +1,171 @@
.l-image-main-wrapper,
.l-image-main,
.l-image-main-controlbar,
.l-image-main-controlbar .left,
.l-image-main-controlbar .right,
.l-image-thumbs-wrapper {
@include absPosDefault(0, false);
}
/*************************************** MAIN LAYOUT */
.l-image-main-wrapper {
//@include test();
@if $enableImageryThumbs == true {
bottom: $interiorMargin*2 + $imageThumbsWrapperH;
}
min-height: 100px;
min-width: 150px;
.l-image-main {
background-color: rgba(#fff, 0.1);
bottom: $imageMainControlBarH + $interiorMargin;
}
.l-image-main-controlbar {
top: auto;
height: $imageMainControlBarH;
}
}
.l-image-thumbs-wrapper {
//@include test(red);
top: auto;
height: $imageThumbsWrapperH;
}
.l-date,
.l-time,
.l-timezone {
display: inline-block;
}
/*************************************** MAIN IMAGE */
.l-image-main,
.l-image-thumb-item .l-thumb {
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.l-image-main {
cursor: crosshair;
}
.l-image-main-controlbar {
//@include test();
font-size: 0.8em;
line-height: $imageMainControlBarH;
.left, .right {
direction: rtl;
overflow: hidden;
}
.left {
//@include test(red);
text-align: left;
width: 75% !important;
}
.right {
//@include test(green);
min-width: 40px;
width: 25% !important;
z-index: 2;
}
.l-date,
.l-time {
color: #fff;
}
.l-mag {
direction: ltr;
display: inline-block;
//white-space: nowrap;
&:before {
content: "\000049";
}
}
.s-mag {
color: darken($colorBodyFg, 20%);
}
.l-btn.show-thumbs {
display: none;
}
}
.s-image-main {
border: 1px solid transparent;
&.paused {
border-color: $colorPausedBg;
}
}
/*************************************** THUMBS */
.l-image-thumbs-wrapper {
//@include test(green);
direction: rtl;
overflow-x: auto;
overflow-y: hidden;
padding-bottom: $interiorMargin;
white-space: nowrap;
z-index: 70;
}
.l-image-thumb-item {
@include single-transition(background-color, 0.25s);
@include box-sizing(border-box);
padding: 1px;
position: relative;
.l-thumb,
.l-date,
.l-time {
display: inline-block;
}
.l-date,
.l-time {
padding: 2px 3px;
}
cursor: pointer;
direction: ltr;
display: inline-block;
font-size: 0.8em;
margin-left: $interiorMarginSm;
text-align: left;
width: $imageThumbsD + $imageThumbPad*2;
white-space: normal;
&:hover {
background: rgba(#fff, 0.2);
.l-date,
.l-time {
color: #fff;
}
}
&.selected {
background: $colorKeySelectedBg;
.l-date,
.l-time {
color: #fff;
}
}
.l-thumb {
background-color: rgba(#fff, 0.1);
height: $imageThumbsD;
width: $imageThumbsD;
margin-top: 0;
}
}
/*************************************** WHEN IN FRAME */
.frame .t-imagery {
.l-image-main-wrapper {
bottom: 0;
.l-image-main-controlbar {
font-size: 0.7em;
}
@if $enableImageryThumbs == true {
.l-btn.show-thumbs {
display: inline-block;
}
}
}
.l-image-thumbs-wrapper {
display: none;
}
}

View File

@ -0,0 +1,44 @@
.l-time-display {
$transTime: 200ms;
// Layout
&:hover {
.l-btn.control {
//display: inline-block;
opacity: 1;
}
}
.l-elem-wrapper {
position: relative;
}
.l-elem {
display: inline-block;
}
&.l-timer {
.l-elem.l-value {
@include trans-prop-nice(left, $transTime);
position: absolute;
left: 0;
z-index: 1;
}
&:hover .l-elem.l-value {
left: 20px;
}
}
// Look-and-feel
.l-elem {
.value.active,
&.value.active {
color: $colorKeyFg;
}
}
.l-btn.control {
@include trans-prop-nice-fade($transTime);
//display: none;
opacity: 0;
font-size: 0.9em;
line-height: 1em;
}
}

View File

@ -19,18 +19,13 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
input[type="text"], input[type="text"] {
input[type="date"] {
@include nice-input(); @include nice-input();
@include input-placeholder { &.filter {
color: darken($colorBodyFg, 10%); &.ng-dirty {
font-style: italic; // background: red;
} }
&.filter { }
&.ng-dirty {
// background: red;
}
}
&.numeric { &.numeric {
text-align: right; text-align: right;
} }

View File

@ -19,19 +19,24 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
//************************************************* LAYOUT
$infoBubbleFg: #666; //************************************************* GENERAL
$infoBubbleBg: #ddd; .bubble-container {
pointer-events: none;
}
//************************************************* LAYOUT
.l-infobubble-wrapper { .l-infobubble-wrapper {
$arwSize: 5px; $arwSize: 5px;
@include box-shadow(rgba(black, 0.4) 0 1px 5px); @include box-shadow(rgba(black, 0.4) 0 1px 5px);
position: absolute; position: relative;
z-index: 70; z-index: 50;
.l-infobubble { .l-infobubble {
display: inline-block; display: inline-block;
max-width: 250px; min-width: $bubbleMinW;
max-width: $bubbleMaxW;
padding: 5px 10px; padding: 5px 10px;
&:before { &:before {
content:""; content:"";
@ -71,34 +76,30 @@ $infoBubbleBg: #ddd;
} }
&.arw-left { &.arw-left {
margin-left: $arwSize*2; margin-left: $bubbleArwSize*2;
.l-infobubble::before { .l-infobubble::before {
right: 100%; right: 100%;
border-top: $arwSize solid transparent; @include triangle('left', $bubbleArwSize, 1.5, $colorInfoBubbleBg);
border-bottom: $arwSize solid transparent;
border-right: ($arwSize * 1.5) solid $infoBubbleBg;
} }
} }
&.arw-right { &.arw-right {
margin-right: $arwSize*2; margin-right: $bubbleArwSize*2;
.l-infobubble::before { .l-infobubble::before {
left: 100%; left: 100%;
border-top: $arwSize solid transparent; @include triangle('right', $bubbleArwSize, 1.5, $colorInfoBubbleBg);
border-bottom: $arwSize solid transparent;
border-left: ($arwSize * 1.5) solid $infoBubbleBg;
} }
} }
&.arw-top { &.arw-top {
.l-infobubble::before { .l-infobubble::before {
top: $arwSize * 2; top: $bubbleArwSize * 2;
} }
} }
&.arw-btm { &.arw-btm {
.l-infobubble::before { .l-infobubble::before {
bottom: $arwSize * 2; bottom: $bubbleArwSize * 2;
} }
} }
@ -110,18 +111,32 @@ $infoBubbleBg: #ddd;
margin-left: -1 * $arwSize; margin-left: -1 * $arwSize;
border-left: $arwSize solid transparent; border-left: $arwSize solid transparent;
border-right: $arwSize solid transparent; border-right: $arwSize solid transparent;
border-top: ($arwSize * 1.5) solid $infoBubbleBg; border-top: ($arwSize * 1.5) solid $colorInfoBubbleBg;
} }
} }
.arw {
z-index: 2;
}
&.arw-up .arw.arw-down,
&.arw-down .arw.arw-up { display: none; }
} }
//************************************************* LOOK AND FEEL //************************************************* LOOK AND FEEL
.l-thumbsbubble-wrapper {
.arw-up {
@include triangle('up', $bubbleArwSize, 1.5, $colorThumbsBubbleBg);
}
.arw-down {
@include triangle('down', $bubbleArwSize, 1.5, $colorThumbsBubbleBg);
}
}
.s-infobubble { .s-infobubble {
$emFg: darken($infoBubbleFg, 20%); $emFg: darken($colorInfoBubbleFg, 20%);
@include border-radius($basicCr); @include border-radius($basicCr);
background: $infoBubbleBg; @include box-shadow(rgba(black, 0.4) 0 1px 5px);
color: $infoBubbleFg; background: $colorInfoBubbleBg;
color: $colorInfoBubbleFg;
font-size: 0.8rem; font-size: 0.8rem;
.title { .title {
color: $emFg; color: $emFg;
@ -129,7 +144,7 @@ $infoBubbleBg: #ddd;
} }
tr { tr {
td { td {
border-top: 1px solid darken($infoBubbleBg, 10%); border-top: 1px solid darken($colorInfoBubbleBg, 10%);
font-size: 0.9em; font-size: 0.9em;
} }
&:first-child td { &:first-child td {
@ -139,5 +154,9 @@ $infoBubbleBg: #ddd;
.value { .value {
color: $emFg; color: $emFg;
} }
}
.s-thumbsbubble {
background: $colorThumbsBubbleBg;
color: $colorThumbsBubbleFg;
} }

View File

@ -104,7 +104,7 @@
&.selected { &.selected {
$cfg: lighten($colorItemSelected, 35%); $cfg: lighten($colorItemSelected, 35%);
$cfgh: lighten($cfg, 30%); $cfgh: lighten($cfg, 30%);
@include btnNoticeable($colorItemSelected); @include btnSubtle($colorItemSelected);
color: $cfg; color: $cfg;
.item-type, .top-bar .icon:not(.alert) { color: $cfg } .item-type, .top-bar .icon:not(.alert) { color: $cfg }
.item-main .item-open { color: $cfg } .item-main .item-open { color: $cfg }

View File

@ -75,8 +75,8 @@ ul.tree {
.title-label { .title-label {
@include absPosDefault(); @include absPosDefault();
display: block; display: block;
left: $runningItemW + ($interiorMargin * 2); left: $runningItemW + ($interiorMargin);
// right: $treeContextTriggerW + $interiorMargin; //Disabling as context trigger not being used //right: $treeContextTriggerW + $interiorMargin;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
// height: $menuLineH; // height: $menuLineH;
@ -99,7 +99,7 @@ ul.tree {
&.selected { &.selected {
$c: #fff; $c: #fff;
background: #005177; background: $colorKeySelectedBg;
color: $c; color: $c;
.view-control { .view-control {
color: $colorItemTreeIcon; color: $colorItemTreeIcon;
@ -124,16 +124,15 @@ ul.tree {
&:not(.loading) { &:not(.loading) {
cursor: pointer; cursor: pointer;
// @include tree-item-hover();
} }
.context-trigger { .context-trigger {
$h: 0.9rem; $h: 0.9rem;
display: none; //display: none;
top: -1px; top: -1px;
position: absolute; position: absolute;
right: $interiorMarginSm; right: $interiorMarginSm;
.btn-invoke-menu { .invoke-menu {
font-size: 0.75em; font-size: 0.75em;
height: $h; height: $h;
line-height: $h; line-height: $h;

View File

@ -27,6 +27,7 @@
border: 1px solid $bc; border: 1px solid $bc;
&:hover { &:hover {
border-color: lighten($bc, 10%); border-color: lighten($bc, 10%);
z-index: 2;
} }
.contents { .contents {
// overflow: hidden; // overflow: hidden;
@ -46,12 +47,25 @@
bottom: $myM; bottom: $myM;
left: $myM; left: $myM;
} }
} &.frame-template {
// Hide the view switcher by default when it's in an element that's in a frame context
.edit-main .frame.child-frame.panel { // Frame template is used because we need to target the lowest nested frame
&:hover { // This has the effect of hiding the view switcher in nested frames in edit mode, which is desirable currently (as it's non-functional)
border-color: $colorKey; .view-switcher {
@include boxShdwLarge(); //display: none;
opacity: 0;
z-index: 10;
}
&:hover .view-switcher {
// Show the view switcher on frame hover
//display: inline-block !important;
opacity: 1;
}
}
.view-switcher {
// Hide the name when the view switcher is in a frame context
.name {
display: none;
}
} }
} }

View File

@ -70,23 +70,19 @@
&.abs { &.abs {
text-wrap: none; text-wrap: none;
white-space: nowrap; white-space: nowrap;
&.left, }
.left { &.left,
width: 45%; .left {
right: auto; width: 45% !important;
} right: auto !important;
&.right, }
.right { &.right,
width: 45%; .right {
left: auto; width: 45% !important;
right: 0; left: auto !important;
text-align: right; text-align: right;
.icon.major { .icon.major {
margin-left: $interiorMargin * 3; margin-left: $interiorMargin * 3;
}
// .icon.major {
// margin-left: $interiorMargin;
// }
} }
} }
} }

View File

@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<a class="t-btn l-btn s-btn s-icon-btn s-very-subtle key-{{parameters.action.getMetadata().key}}" <a class="btn s-btn s-icon-btn s-very-subtle key-{{parameters.action.getMetadata().key}}"
ng-class="{ labeled: parameters.labeled }" ng-class="{ labeled: parameters.labeled }"
title="{{parameters.action.getMetadata().description}}" title="{{parameters.action.getMetadata().description}}"
ng-click="parameters.action.perform()"> ng-click="parameters.action.perform()">

View File

@ -21,25 +21,29 @@
--> -->
<span ng-controller="ViewSwitcherController"> <span ng-controller="ViewSwitcherController">
<div class="menu-element btn icon-btn very-subtle btn-menu dropdown click-invoke" <div
ng-if="view.length > 1" class="view-switcher menu-element btn btn-menu dropdown click-invoke"
ng-controller="ClickAwayController as toggle"> ng-if="view.length > 1"
ng-controller="ClickAwayController as toggle"
>
<span class="l-click-area" ng-click="toggle.toggle()"></span> <span
class="l-click-area"
ng-click="toggle.toggle()"
title="{{ngModel.selected.name}}"
></span>
<span class="ui-symbol icon type-icon">{{ngModel.selected.glyph}}</span> <span class="ui-symbol icon type-icon">{{ngModel.selected.glyph}}</span>
<span>{{ngModel.selected.name}}</span> <span class="name">{{ngModel.selected.name}}</span>
<span class='ui-symbol icon invoke-menu'>v</span> <span class='ui-symbol invoke-menu'>v</span>
<div class="menu dropdown" ng-show="toggle.isActive()"> <div class="menu dropdown" ng-show="toggle.isActive()">
<ul> <ul>
<li ng-repeat="option in view"> <li ng-repeat="option in view">
<a href="" ng-click="ngModel.selected = option; toggle.setState(false)"> <a ng-click="ngModel.selected = option; toggle.setState(false)">
<span class="ui-symbol type-icon icon"> <span class="ui-symbol type-icon icon">{{option.glyph}}</span>
{{option.glyph}} {{option.name}}
</span>
{{option.name}}
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -53,12 +53,14 @@ define(
// Get list of views, read from capability // Get list of views, read from capability
function updateOptions(views) { function updateOptions(views) {
$timeout(function () { if (Array.isArray(views)) {
$scope.ngModel.selected = findMatchingOption( $timeout(function () {
views || [], $scope.ngModel.selected = findMatchingOption(
($scope.ngModel || {}).selected views,
); ($scope.ngModel || {}).selected
}, 0); );
}, 0);
}
} }
// Update view options when the in-scope results of using the // Update view options when the in-scope results of using the
@ -68,4 +70,4 @@ define(
return ViewSwitcherController; return ViewSwitcherController;
} }
); );

View File

@ -1,20 +1,50 @@
{ {
"extensions": { "extensions": {
"types": [ "templates": [
{ {
"key": "infobubble", "key": "info-table",
"name": "Info Bubble", "templateUrl": "info-table.html"
"glyph": "\u00EA", },
"description": "Static markup for info bubbles", {
"features": [ "creation" ] "key": "info-bubble",
"templateUrl": "info-bubble.html"
} }
], ],
"views": [ "containers": [
{ {
"templateUrl": "infobubble.html", "key": "bubble",
"name": "Info Bubble", "templateUrl": "bubble.html",
"type": "infobubble", "attributes": [ "bubbleTitle", "bubbleLayout" ],
"key": "infobubble" "alias": "bubble"
}
],
"gestures": [
{
"key": "info",
"implementation": "gestures/InfoGesture.js",
"depends": [
"$timeout",
"infoService",
"INFO_HOVER_DELAY"
]
}
],
"services": [
{
"key": "infoService",
"implementation": "services/InfoService.js",
"depends": [
"$compile",
"$document",
"$window",
"$rootScope"
]
}
],
"constants": [
{
"key": "INFO_HOVER_DELAY",
"value": 2000
} }
] ]
} }

View File

@ -0,0 +1,9 @@
<div class="t-infobubble s-infobubble l-infobubble-wrapper {{bubble.bubbleLayout}}">
<div class="l-infobubble">
<div ng-show="bubble.bubbleTitle.length > 0"
class="title">
{{bubble.bubbleTitle}}
</div>
<span ng-transclude></span>
</div>
</div>

View File

@ -0,0 +1,9 @@
<mct-container
key="bubble"
bubble-title="{{parameters.title}}"
bubble-layout="{{parameters.layout}}"
>
<mct-include key="info-table"
ng-model="ngModel">
</mct-include>
</mct-container>

View File

@ -0,0 +1,8 @@
<table>
<tr ng-repeat="property in ngModel">
<td class="label">{{property.name}}</td>
<td title="{{property.value}}" class="value align-{{property.align}}">
{{property.value}}
</td>
</tr>
</table>

View File

@ -19,12 +19,18 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
@import "compass"; /*global define*/
@import "compass/css3"; define({
@import "compass/css3/border-radius"; BUBBLE_TEMPLATE: "<mct-container key=\"bubble\" " +
@import "compass/css3/opacity"; "bubble-title=\"{{bubbleTitle}}\" " +
@import "compass/utilities"; "bubble-layout=\"{{bubbleLayout}}\" " +
"class=\"bubble-container\">" +
@import "constants"; "<mct-include key=\"bubbleTemplate\" ng-model=\"bubbleModel\">" +
@import "mixins"; "</mct-include>" +
@import "plots/plots-main"; "</mct-container>",
// Pixel offset for bubble, to align arrow position
BUBBLE_OFFSET: [ 0, -26 ],
// Max width and margins allowed for bubbles; defined in /platform/commonUI/general/res/sass/_constants.scss
BUBBLE_MARGIN_LR: 10,
BUBBLE_MAX_WIDTH: 300
});

View File

@ -0,0 +1,121 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
[],
function () {
"use strict";
/**
* The `info` gesture displays domain object metadata in a
* bubble on hover.
*
* @constructor
* @param $timeout Angular's `$timeout`
* @param {InfoService} infoService a service which shows info bubbles
* @param {number} DELAY delay, in milliseconds, before bubble appears
* @param element jqLite-wrapped DOM element
* @param {DomainObject} domainObject the domain object for which to
* show information
*/
function InfoGesture($timeout, infoService, DELAY, element, domainObject) {
var dismissBubble,
pendingBubble,
mousePosition,
scopeOff;
function trackPosition(event) {
// Record mouse position, so bubble can be shown at latest
// mouse position (not just where the mouse entered)
mousePosition = [ event.clientX, event.clientY ];
}
function hideBubble() {
// If a bubble is showing, dismiss it
if (dismissBubble) {
dismissBubble();
element.off('mouseleave', hideBubble);
dismissBubble = undefined;
}
// If a bubble will be shown on a timeout, cancel that
if (pendingBubble) {
$timeout.cancel(pendingBubble);
element.off('mousemove', trackPosition);
element.off('mouseleave', hideBubble);
pendingBubble = undefined;
}
// Also clear mouse position so we don't have a ton of tiny
// arrays allocated while user mouses over things
mousePosition = undefined;
}
function showBubble(event) {
trackPosition(event);
// Also need to track position during hover
element.on('mousemove', trackPosition);
// Show the bubble, after a suitable delay (if mouse has
// left before this time is up, this will be canceled.)
pendingBubble = $timeout(function () {
dismissBubble = infoService.display(
"info-table",
domainObject.getModel().name,
domainObject.useCapability('metadata'),
mousePosition
);
element.off('mousemove', trackPosition);
pendingBubble = undefined;
}, DELAY);
element.on('mouseleave', hideBubble);
}
// Show bubble (on a timeout) on mouse over
element.on('mouseenter', 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.
* @memberof InfoGesture
* @method
*/
destroy: function () {
// Dismiss any active bubble...
hideBubble();
// ...and detach listeners
element.off('mouseenter', showBubble);
scopeOff();
}
};
}
return InfoGesture;
}
);

View File

@ -0,0 +1,95 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
['../InfoConstants'],
function (InfoConstants) {
"use strict";
var BUBBLE_TEMPLATE = InfoConstants.BUBBLE_TEMPLATE,
OFFSET = InfoConstants.BUBBLE_OFFSET;
/**
* Displays informative content ("info bubbles") for the user.
* @constructor
*/
function InfoService($compile, $document, $window, $rootScope) {
function display(templateKey, title, content, position) {
var body = $document.find('body'),
scope = $rootScope.$new(),
winDim = [$window.innerWidth, $window.innerHeight],
bubbleSpaceLR = InfoConstants.BUBBLE_MARGIN_LR + InfoConstants.BUBBLE_MAX_WIDTH,
goLeft = position[0] > (winDim[0] - bubbleSpaceLR),
goUp = position[1] > (winDim[1] / 2),
bubble;
// Pass model & container parameters into the scope
scope.bubbleModel = content;
scope.bubbleTemplate = templateKey;
scope.bubbleLayout = (goUp ? 'arw-btm' : 'arw-top') + ' ' +
(goLeft ? 'arw-right' : 'arw-left');
scope.bubbleTitle = title;
// Create the context menu
bubble = $compile(BUBBLE_TEMPLATE)(scope);
// Position the bubble
bubble.css('position', 'absolute');
if (goLeft) {
bubble.css('right', (winDim[0] - position[0] + OFFSET[0]) + 'px');
} else {
bubble.css('left', position[0] + OFFSET[0] + 'px');
}
if (goUp) {
bubble.css('bottom', (winDim[1] - position[1] + OFFSET[1]) + 'px');
} else {
bubble.css('top', position[1] + OFFSET[1] + 'px');
}
// Add the menu to the body
body.append(bubble);
// Return a function to dismiss the bubble
return function () { bubble.remove(); };
}
return {
/**
* Display an info bubble at the specified location.
* @param {string} templateKey template to place in bubble
* @param {string} title title for the bubble
* @param {*} content content to pass to the template, via
* `ng-model`
* @param {number[]} x,y position of the info bubble, in
* pixel coordinates.
* @returns {Function} a function that may be invoked to
* dismiss the info bubble
*/
display: display
};
}
return InfoService;
}
);

View File

@ -0,0 +1,157 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
['../../src/gestures/InfoGesture'],
function (InfoGesture) {
"use strict";
describe("The info gesture", function () {
var mockTimeout,
mockInfoService,
testDelay = 12321,
mockElement,
mockDomainObject,
mockScope,
mockOff,
testMetadata,
mockPromise,
mockHide,
gesture;
function fireEvent(evt, value) {
mockElement.on.calls.forEach(function (call) {
if (call.args[0] === evt) {
call.args[1](value);
}
});
}
beforeEach(function () {
mockTimeout = jasmine.createSpy('$timeout');
mockTimeout.cancel = jasmine.createSpy('cancel');
mockInfoService = jasmine.createSpyObj(
'infoService',
[ 'display' ]
);
mockElement = jasmine.createSpyObj(
'element',
[ 'on', 'off', 'scope', 'css' ]
);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
[ 'getId', 'getCapability', 'useCapability', 'getModel' ]
);
mockScope = jasmine.createSpyObj('$scope', [ '$on' ]);
mockOff = jasmine.createSpy('$off');
testMetadata = [ { name: "Test name", value: "Test value" } ];
mockPromise = jasmine.createSpyObj('promise', ['then']);
mockHide = jasmine.createSpy('hide');
mockDomainObject.getModel.andReturn({ name: "Test Object" });
mockDomainObject.useCapability.andCallFake(function (c) {
return (c === 'metadata') ? testMetadata : undefined;
});
mockElement.scope.andReturn(mockScope);
mockScope.$on.andReturn(mockOff);
mockTimeout.andReturn(mockPromise);
mockInfoService.display.andReturn(mockHide);
gesture = new InfoGesture(
mockTimeout,
mockInfoService,
testDelay,
mockElement,
mockDomainObject
);
});
it("listens for mouseenter on the representation", function () {
expect(mockElement.on)
.toHaveBeenCalledWith('mouseenter', jasmine.any(Function));
});
it("displays an info bubble on a delay after mouseenter", function () {
fireEvent("mouseenter", { clientX: 1977, clientY: 42 });
expect(mockTimeout)
.toHaveBeenCalledWith(jasmine.any(Function), testDelay);
mockTimeout.mostRecentCall.args[0]();
expect(mockInfoService.display).toHaveBeenCalledWith(
jasmine.any(String),
"Test Object",
testMetadata,
[ 1977, 42 ]
);
});
it("does not display info bubble if mouse leaves too soon", function () {
fireEvent("mouseenter", { clientX: 1977, clientY: 42 });
fireEvent("mouseleave", { clientX: 1977, clientY: 42 });
expect(mockTimeout.cancel).toHaveBeenCalledWith(mockPromise);
expect(mockInfoService.display).not.toHaveBeenCalled();
});
it("hides a shown bubble when mouse leaves", function () {
fireEvent("mouseenter", { clientX: 1977, clientY: 42 });
mockTimeout.mostRecentCall.args[0]();
expect(mockHide).not.toHaveBeenCalled(); // verify precondition
fireEvent("mouseleave", {});
expect(mockHide).toHaveBeenCalled();
});
it("tracks mouse position", function () {
fireEvent("mouseenter", { clientX: 1977, clientY: 42 });
fireEvent("mousemove", { clientX: 1999, clientY: 11 });
fireEvent("mousemove", { clientX: 1984, clientY: 11 });
mockTimeout.mostRecentCall.args[0]();
// Should have displayed at the latest observed mouse position
expect(mockInfoService.display).toHaveBeenCalledWith(
jasmine.any(String),
"Test Object",
testMetadata,
[ 1984, 11 ]
);
});
it("hides shown bubbles when destroyed", function () {
fireEvent("mouseenter", { clientX: 1977, clientY: 42 });
mockTimeout.mostRecentCall.args[0]();
expect(mockHide).not.toHaveBeenCalled(); // verify precondition
gesture.destroy();
expect(mockHide).toHaveBeenCalled();
});
it("detaches listeners when destroyed", function () {
fireEvent("mouseenter", { clientX: 1977, clientY: 42 });
gesture.destroy();
mockElement.on.calls.forEach(function (call) {
expect(mockElement.off).toHaveBeenCalledWith(
call.args[0],
call.args[1]
);
});
});
});
}
);

View File

@ -0,0 +1,131 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
['../../src/services/InfoService', '../../src/InfoConstants'],
function (InfoService, InfoConstants) {
"use strict";
describe("The info service", function () {
var mockCompile,
mockDocument,
testWindow,
mockRootScope,
mockCompiledTemplate,
testScope,
mockBody,
mockElement,
service;
beforeEach(function () {
mockCompile = jasmine.createSpy('$compile');
mockDocument = jasmine.createSpyObj('$document', ['find']);
testWindow = { innerWidth: 1000, innerHeight: 100 };
mockRootScope = jasmine.createSpyObj('$rootScope', ['$new']);
mockCompiledTemplate = jasmine.createSpy('template');
testScope = {};
mockBody = jasmine.createSpyObj('body', ['append']);
mockElement = jasmine.createSpyObj('element', ['css', 'remove']);
mockDocument.find.andCallFake(function (tag) {
return tag === 'body' ? mockBody : undefined;
});
mockCompile.andReturn(mockCompiledTemplate);
mockCompiledTemplate.andReturn(mockElement);
mockRootScope.$new.andReturn(testScope);
service = new InfoService(
mockCompile,
mockDocument,
testWindow,
mockRootScope
);
});
it("creates elements and appends them to the body to display", function () {
service.display('', '', {}, [0, 0]);
expect(mockBody.append).toHaveBeenCalledWith(mockElement);
});
it("provides a function to remove displayed info bubbles", function () {
var fn = service.display('', '', {}, [0, 0]);
expect(mockElement.remove).not.toHaveBeenCalled();
fn();
expect(mockElement.remove).toHaveBeenCalled();
});
describe("depending on mouse position", function () {
// Positioning should vary based on quadrant in window,
// which is 1000 x 100 in this test case.
it("displays from the top-left in the top-left quadrant", function () {
service.display('', '', {}, [250, 25]);
expect(mockElement.css).toHaveBeenCalledWith(
'left',
(250 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
);
expect(mockElement.css).toHaveBeenCalledWith(
'top',
(25 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
);
});
it("displays from the top-right in the top-right quadrant", function () {
service.display('', '', {}, [700, 25]);
expect(mockElement.css).toHaveBeenCalledWith(
'right',
(300 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
);
expect(mockElement.css).toHaveBeenCalledWith(
'top',
(25 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
);
});
it("displays from the bottom-left in the bottom-left quadrant", function () {
service.display('', '', {}, [250, 70]);
expect(mockElement.css).toHaveBeenCalledWith(
'left',
(250 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
);
expect(mockElement.css).toHaveBeenCalledWith(
'bottom',
(30 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
);
});
it("displays from the bottom-right in the bottom-right quadrant", function () {
service.display('', '', {}, [800, 60]);
expect(mockElement.css).toHaveBeenCalledWith(
'right',
(200 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
);
expect(mockElement.css).toHaveBeenCalledWith(
'bottom',
(40 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
);
});
});
});
}
);

View File

@ -0,0 +1,4 @@
[
"gestures/InfoGesture",
"services/InfoService"
]

View File

@ -12,6 +12,11 @@
"implementation": "CompositionMutabilityPolicy.js", "implementation": "CompositionMutabilityPolicy.js",
"message": "Objects of this type cannot be modified." "message": "Objects of this type cannot be modified."
}, },
{
"category": "composition",
"implementation": "CompositionModelPolicy.js",
"message": "Objects of this type cannot contain other objects."
},
{ {
"category": "action", "category": "action",
"implementation": "ComposeActionPolicy.js", "implementation": "ComposeActionPolicy.js",

View File

@ -0,0 +1,28 @@
/*global define*/
define(
[],
function () {
"use strict";
/**
* Policy allowing composition only for domain object types which
* have a composition property.
*/
function CompositionModelPolicy() {
return {
/**
* Is the type identified by the candidate allowed to
* contain the type described by the context?
*/
allow: function (candidate, context) {
return Array.isArray(
(candidate.getInitialModel() || {}).composition
);
}
};
}
return CompositionModelPolicy;
}
);

View File

@ -0,0 +1,26 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/CompositionModelPolicy"],
function (CompositionModelPolicy) {
"use strict";
describe("The composition model policy", function () {
var mockType,
policy;
beforeEach(function () {
mockType = jasmine.createSpyObj('type', ['getInitialModel']);
policy = new CompositionModelPolicy();
});
it("only allows composition for types which will have a composition property", function () {
mockType.getInitialModel.andReturn({});
expect(policy.allow(mockType)).toBeFalsy();
mockType.getInitialModel.andReturn({ composition: [] });
expect(policy.allow(mockType)).toBeTruthy();
});
});
}
);

View File

@ -35,7 +35,7 @@ define(
policy = new CompositionMutabilityPolicy(); policy = new CompositionMutabilityPolicy();
}); });
it("only allows composition for types which will have a composition capability", function () { it("only allows composition for types which can be created/modified", function () {
expect(policy.allow(mockType)).toBeFalsy(); expect(policy.allow(mockType)).toBeFalsy();
mockType.hasFeature.andReturn(true); mockType.hasFeature.andReturn(true);
expect(policy.allow(mockType)).toBeTruthy(); expect(policy.allow(mockType)).toBeTruthy();

View File

@ -1,6 +1,7 @@
[ [
"CapabilityTable", "CapabilityTable",
"ComposeActionPolicy", "ComposeActionPolicy",
"CompositionModelPolicy",
"CompositionMutabilityPolicy", "CompositionMutabilityPolicy",
"CompositionPolicy", "CompositionPolicy",
"ContainmentTable" "ContainmentTable"

View File

@ -176,6 +176,10 @@
"implementation": "capabilities/PersistenceCapability.js", "implementation": "capabilities/PersistenceCapability.js",
"depends": [ "persistenceService", "PERSISTENCE_SPACE" ] "depends": [ "persistenceService", "PERSISTENCE_SPACE" ]
}, },
{
"key": "metadata",
"implementation": "capabilities/MetadataCapability.js"
},
{ {
"key": "mutation", "key": "mutation",
"implementation": "capabilities/MutationCapability.js", "implementation": "capabilities/MutationCapability.js",
@ -191,6 +195,11 @@
{ {
"key": "now", "key": "now",
"implementation": "services/Now.js" "implementation": "services/Now.js"
},
{
"key": "throttle",
"implementation": "services/Throttle.js",
"depends": [ "$timeout" ]
} }
], ],
"roots": [ "roots": [

View File

@ -0,0 +1,92 @@
/*global define*/
define(
['moment'],
function (moment) {
"use strict";
/**
* A piece of information about a domain object.
* @typedef {Object} MetadataProperty
* @property {string} name the human-readable name of this property
* @property {string} value the human-readable value of this property,
* for this specific domain object
*/
var TIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
/**
* Implements the `metadata` capability of a domain object, providing
* properties of that object for display.
*
* Usage: `domainObject.useCapability("metadata")`
*
* ...which will return an array of objects containing `name` and
* `value` properties describing that domain object (suitable for
* display.)
*
* @constructor
*/
function MetadataCapability(domainObject) {
var model = domainObject.getModel();
function hasDisplayableValue(metadataProperty) {
var t = typeof metadataProperty.value;
return (t === 'string' || t === 'number');
}
function formatTimestamp(timestamp) {
return typeof timestamp === 'number' ?
(moment.utc(timestamp).format(TIME_FORMAT) + " UTC") :
undefined;
}
function getProperties() {
var type = domainObject.getCapability('type');
function lookupProperty(typeProperty) {
return {
name: typeProperty.getDefinition().name,
value: typeProperty.getValue(model)
};
}
return (type ? type.getProperties() : []).map(lookupProperty);
}
function getCommonMetadata() {
var type = domainObject.getCapability('type');
// Note that invalid values will be filtered out later
return [
{
name: "Updated",
value: formatTimestamp(model.modified)
},
{
name: "Type",
value: type && type.getName()
},
{
name: "ID",
value: domainObject.getId()
}
];
}
function getMetadata() {
return getProperties().concat(getCommonMetadata())
.filter(hasDisplayableValue);
}
return {
/**
* Get metadata about this object.
* @returns {MetadataProperty[]} metadata about this object
*/
invoke: getMetadata
};
}
return MetadataCapability;
}
);

View File

@ -77,7 +77,8 @@ define(
// Get the object's model and clone it, so the // Get the object's model and clone it, so the
// mutator function has a temporary copy to work with. // mutator function has a temporary copy to work with.
var model = domainObject.getModel(), var model = domainObject.getModel(),
clone = JSON.parse(JSON.stringify(model)); clone = JSON.parse(JSON.stringify(model)),
useTimestamp = arguments.length > 1;
// Function to handle copying values to the actual // Function to handle copying values to the actual
function handleMutation(mutationResult) { function handleMutation(mutationResult) {
@ -94,8 +95,7 @@ define(
if (model !== result) { if (model !== result) {
copyValues(model, result); copyValues(model, result);
} }
model.modified = (typeof timestamp === 'number') ? model.modified = useTimestamp ? timestamp : now();
timestamp : now();
} }
// Report the result of the mutation // Report the result of the mutation

View File

@ -0,0 +1,63 @@
/*global define*/
define(
[],
function () {
"use strict";
/**
* Throttler for function executions, registered as the `throttle`
* service.
*
* Usage:
*
* throttle(fn, delay, [apply])
*
* Returns a function that, when invoked, will invoke `fn` after
* `delay` milliseconds, only if no other invocations are pending.
* The optional argument `apply` determines whether.
*
* The returned function will itself return a `Promise` which will
* resolve to the returned value of `fn` whenever that is invoked.
*
* @returns {Function}
*/
function Throttle($timeout) {
/**
* Throttle this function.
* @param {Function} fn the function to throttle
* @param {number} [delay] the delay, in milliseconds, before
* executing this function; defaults to 0.
* @param {boolean} apply true if a `$apply` call should be
* invoked after this function executes; defaults to
* `false`.
*/
return function (fn, delay, apply) {
var activeTimeout;
// Clear active timeout, so that next invocation starts
// a new one.
function clearActiveTimeout() {
activeTimeout = undefined;
}
// Defaults
delay = delay || 0;
apply = apply || false;
return function () {
// Start a timeout if needed
if (!activeTimeout) {
activeTimeout = $timeout(fn, delay, apply);
activeTimeout.then(clearActiveTimeout);
}
// Return whichever timeout is active (to get
// a promise for the results of fn)
return activeTimeout;
};
};
}
return Throttle;
}
);

View File

@ -0,0 +1,101 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
['../../src/capabilities/MetadataCapability'],
function (MetadataCapability) {
"use strict";
describe("The metadata capability", function () {
var mockDomainObject,
mockType,
mockProperties,
testModel,
metadata;
function getCapability(key) {
return key === 'type' ? mockType : undefined;
}
function findValue(properties, name) {
var i;
for (i = 0; i < properties.length; i += 1) {
if (properties[i].name === name) {
return properties[i].value;
}
}
}
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getCapability', 'useCapability', 'getModel']
);
mockType = jasmine.createSpyObj(
'type',
['getProperties', 'getName']
);
mockProperties = ['a', 'b', 'c'].map(function (k) {
var mockProperty = jasmine.createSpyObj(
'property-' + k,
['getValue', 'getDefinition']
);
mockProperty.getValue.andReturn("Value " + k);
mockProperty.getDefinition.andReturn({ name: "Property " + k});
return mockProperty;
});
testModel = { name: "" };
mockDomainObject.getId.andReturn("Test id");
mockDomainObject.getModel.andReturn(testModel);
mockDomainObject.getCapability.andCallFake(getCapability);
mockDomainObject.useCapability.andCallFake(getCapability);
mockType.getProperties.andReturn(mockProperties);
mockType.getName.andReturn("Test type");
metadata = new MetadataCapability(mockDomainObject);
});
it("reads properties from the domain object model", function () {
metadata.invoke();
mockProperties.forEach(function (mockProperty) {
expect(mockProperty.getValue).toHaveBeenCalledWith(testModel);
});
});
it("reports type-specific properties", function () {
var properties = metadata.invoke();
expect(findValue(properties, 'Property a')).toEqual("Value a");
expect(findValue(properties, 'Property b')).toEqual("Value b");
expect(findValue(properties, 'Property c')).toEqual("Value c");
});
it("reports generic properties", function () {
var properties = metadata.invoke();
expect(findValue(properties, 'ID')).toEqual("Test id");
expect(findValue(properties, 'Type')).toEqual("Test type");
});
});
}
);

View File

@ -0,0 +1,49 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../../src/services/Throttle"],
function (Throttle) {
"use strict";
describe("The 'throttle' service", function () {
var throttle,
mockTimeout,
mockFn,
mockPromise;
beforeEach(function () {
mockTimeout = jasmine.createSpy("$timeout");
mockPromise = jasmine.createSpyObj("promise", ["then"]);
mockFn = jasmine.createSpy("fn");
mockTimeout.andReturn(mockPromise);
throttle = new Throttle(mockTimeout);
});
it("provides functions which run on a timeout", function () {
var throttled = throttle(mockFn);
// Verify precondition: Not called at throttle-time
expect(mockTimeout).not.toHaveBeenCalled();
expect(throttled()).toEqual(mockPromise);
expect(mockTimeout).toHaveBeenCalledWith(mockFn, 0, false);
});
it("schedules only one timeout at a time", function () {
var throttled = throttle(mockFn);
throttled();
throttled();
throttled();
expect(mockTimeout.calls.length).toEqual(1);
});
it("schedules additional invocations after resolution", function () {
var throttled = throttle(mockFn);
throttled();
mockPromise.then.mostRecentCall.args[0](); // Resolve timeout
throttled();
mockPromise.then.mostRecentCall.args[0]();
throttled();
expect(mockTimeout.calls.length).toEqual(3);
});
});
}
);

View File

@ -9,6 +9,7 @@
"capabilities/ContextualDomainObject", "capabilities/ContextualDomainObject",
"capabilities/CoreCapabilityProvider", "capabilities/CoreCapabilityProvider",
"capabilities/DelegationCapability", "capabilities/DelegationCapability",
"capabilities/MetadataCapability",
"capabilities/MutationCapability", "capabilities/MutationCapability",
"capabilities/PersistenceCapability", "capabilities/PersistenceCapability",
"capabilities/RelationshipCapability", "capabilities/RelationshipCapability",
@ -23,6 +24,7 @@
"objects/DomainObjectProvider", "objects/DomainObjectProvider",
"services/Now", "services/Now",
"services/Throttle",
"types/MergeModels", "types/MergeModels",
"types/TypeCapability", "types/TypeCapability",

View File

@ -0,0 +1,24 @@
# Entanglement
Entanglement is the process of moving, copying, and linking domain objects
in such a way that their relationships are impossible to discern.
This bundle provides move, copy, and link functionality. Acheiving a state of
entanglement is left up to the end user.
## Services implement logic
Each method (move, copy, link) is implemented as a service, and each service
provides two functions: `validate` and `perform`.
`validate(object, parentCandidate)` returns true if the `object` can be
move/copy/linked into the `parentCandidate`'s composition.
`perform(object, parentObject)` move/copy/links the `object` into the
`parentObject`'s composition.
## Actions implement user interactions
Actions are used to expose move/copy/link to the user. They prompt for input
where necessary, and complete the actions.

View File

@ -0,0 +1,75 @@
{
"name": "Entanglement",
"description": "Tools to assist you in entangling the world of WARP.",
"configuration": {},
"extensions": {
"actions": [
{
"key": "move",
"name": "Move",
"description": "Move object to another location.",
"glyph": "f",
"category": "contextual",
"implementation": "actions/MoveAction.js",
"depends": ["locationService", "moveService"]
},
{
"key": "copy",
"name": "Duplicate",
"description": "Duplicate object to another location.",
"glyph": "+",
"category": "contextual",
"implementation": "actions/CopyAction.js",
"depends": ["locationService", "copyService"]
},
{
"key": "link",
"name": "Create Link",
"description": "Create Link to object in another location.",
"glyph": "\u00E8",
"category": "contextual",
"implementation": "actions/LinkAction.js",
"depends": ["locationService", "linkService"]
}
],
"components": [
],
"controllers": [
],
"capabilities": [
],
"services": [
{
"key": "moveService",
"name": "Move Service",
"description": "Provides a service for moving objects",
"implementation": "services/MoveService.js",
"depends": ["policyService", "linkService"]
},
{
"key": "linkService",
"name": "Link Service",
"description": "Provides a service for linking objects",
"implementation": "services/LinkService.js",
"depends": ["policyService"]
},
{
"key": "copyService",
"name": "Copy Service",
"description": "Provides a service for copying objects",
"implementation": "services/CopyService.js",
"depends": ["$q", "creationService", "policyService"]
},
{
"key": "locationService",
"name": "Location Service",
"description": "Provides a service for prompting a user for locations.",
"implementation": "services/LocationService.js",
"depends": ["dialogService"]
}
],
"licenses": [
]
}
}

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* The CopyAction is available from context menus and allows a user to
* deep copy an object to another location of their choosing.
*
* @implements Action
*/
function CopyAction(locationService, copyService, context) {
var object,
newParent,
currentParent;
if (context.selectedObject) {
newParent = context.domainObject;
object = context.selectedObject;
} else {
object = context.domainObject;
}
currentParent = object
.getCapability('context')
.getParent();
return {
perform: function () {
if (newParent) {
return copyService
.perform(object, newParent);
}
var dialogTitle,
label,
validateLocation;
dialogTitle = [
"Duplicate ",
object.getModel().name,
" to a location"
].join("");
label = "Duplicate To";
validateLocation = function (newParent) {
return copyService
.validate(object, newParent);
};
return locationService.getLocationFromUser(
dialogTitle,
label,
validateLocation,
currentParent
).then(function (newParent) {
return copyService
.perform(object, newParent);
});
}
};
}
return CopyAction;
}
);

View File

@ -0,0 +1,89 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* The LinkAction is available from context menus and allows a user to
* link an object to another location of their choosing.
*
* @implements Action
*/
function LinkAction(locationService, linkService, context) {
var object,
newParent,
currentParent;
if (context.selectedObject) {
newParent = context.domainObject;
object = context.selectedObject;
} else {
object = context.domainObject;
}
currentParent = object
.getCapability('context')
.getParent();
return {
perform: function () {
if (newParent) {
return linkService
.perform(object, newParent);
}
var dialogTitle,
label,
validateLocation;
dialogTitle = [
"Link ",
object.getModel().name,
" to a new location"
].join("");
label = "Link To";
validateLocation = function (newParent) {
return linkService
.validate(object, newParent);
};
return locationService.getLocationFromUser(
dialogTitle,
label,
validateLocation,
currentParent
).then(function (newParent) {
return linkService
.perform(object, newParent);
});
}
};
}
return LinkAction;
}
);

View File

@ -0,0 +1,90 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* The MoveAction is available from context menus and allows a user to
* move an object to another location of their choosing.
*
* @implements Action
*/
function MoveAction(locationService, moveService, context) {
var object,
newParent,
currentParent;
if (context.selectedObject) {
newParent = context.domainObject;
object = context.selectedObject;
} else {
object = context.domainObject;
}
currentParent = object
.getCapability('context')
.getParent();
return {
perform: function () {
if (newParent) {
return moveService
.perform(object, newParent);
}
var dialogTitle,
label,
validateLocation;
dialogTitle = [
"Move ",
object.getModel().name,
" to a new location"
].join("");
label = "Move To";
validateLocation = function (newParent) {
return moveService
.validate(object, newParent);
};
return locationService.getLocationFromUser(
dialogTitle,
label,
validateLocation,
currentParent
).then(function (newParent) {
return moveService
.perform(object, newParent);
});
}
};
}
return MoveAction;
}
);

View File

@ -0,0 +1,106 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* CopyService provides an interface for deep copying objects from one
* location to another. It also provides a method for determining if
* an object can be copied to a specific location.
*/
function CopyService($q, creationService, policyService) {
/**
* duplicateObject duplicates a `domainObject` into the composition
* of `parent`, and then duplicates the composition of
* `domainObject` into the new object.
*
* This function is a recursive deep copy.
*
* @param {DomainObject} domainObject - the domain object to
* duplicate.
* @param {DomainObject} parent - the parent domain object to
* create the duplicate in.
* @returns {Promise} A promise that is fulfilled when the
* duplicate operation has completed.
*/
function duplicateObject(domainObject, parent) {
var model = JSON.parse(JSON.stringify(domainObject.getModel()));
if (domainObject.hasCapability('composition')) {
model.composition = [];
}
return creationService
.createObject(model, parent)
.then(function (newObject) {
if (!domainObject.hasCapability('composition')) {
return;
}
return domainObject
.useCapability('composition')
.then(function (composees) {
// Duplicate composition serially to prevent
// write conflicts.
return composees.reduce(function (promise, composee) {
return promise.then(function () {
return duplicateObject(composee, newObject);
});
}, $q.when(undefined));
});
});
}
return {
/**
* Returns true if `object` can be copied into
* `parentCandidate`'s composition.
*/
validate: function (object, parentCandidate) {
if (!parentCandidate || !parentCandidate.getId) {
return false;
}
if (parentCandidate.getId() === object.getId()) {
return false;
}
return policyService.allow(
"composition",
parentCandidate.getCapability('type'),
object.getCapability('type')
);
},
/**
* Wrapper, @see {@link duplicateObject} for implementation.
*/
perform: function (object, parentObject) {
return duplicateObject(object, parentObject);
}
};
}
return CopyService;
}
);

View File

@ -0,0 +1,76 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* LinkService provides an interface for linking objects to additional
* locations. It also provides a method for determining if an object
* can be copied to a specific location.
*/
function LinkService(policyService) {
return {
/**
* Returns `true` if `object` can be linked into
* `parentCandidate`'s composition.
*/
validate: function (object, parentCandidate) {
if (!parentCandidate || !parentCandidate.getId) {
return false;
}
if (parentCandidate.getId() === object.getId()) {
return false;
}
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
return false;
}
return policyService.allow(
"composition",
parentCandidate.getCapability('type'),
object.getCapability('type')
);
},
/**
* Link `object` into `parentObject`'s composition.
*
* @returns {Promise} A promise that is fulfilled when the
* linking operation has completed.
*/
perform: function (object, parentObject) {
return parentObject.useCapability('mutation', function (model) {
if (model.composition.indexOf(object.getId()) === -1) {
model.composition.push(object.getId());
}
}).then(function () {
return parentObject.getCapability('persistence').persist();
});
}
};
}
return LinkService;
}
);

View File

@ -0,0 +1,83 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* The LocationService allows for easily prompting the user for a
* location in the root tree.
*/
function LocationService(dialogService) {
return {
/** Prompt the user to select a location. Returns a promise
* that is resolved with a domainObject representing the
* location selected by the user.
*
* @param {string} title - title of location dialog
* @param {string} label - label for location input field
* @param {function} validate - function that validates
* selections.
* @param {domainObject} initialLocation - tree location to
* display at start
* @returns {Promise} promise for a domain object.
*/
getLocationFromUser: function (title, label, validate, initialLocation) {
var formStructure,
formState;
formStructure = {
sections: [
{
name: 'Location',
rows: [
{
name: label,
control: "locator",
validate: validate,
key: 'location'
}
]
}
],
name: title
};
formState = {
location: initialLocation
};
return dialogService
.getUserInput(formStructure, formState)
.then(function (formState) {
return formState.location;
});
}
};
}
return LocationService;
}
);

View File

@ -0,0 +1,83 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* MoveService provides an interface for moving objects from one
* location to another. It also provides a method for determining if
* an object can be copied to a specific location.
*/
function MoveService(policyService, linkService) {
return {
/**
* Returns `true` if `object` can be moved into
* `parentCandidate`'s composition.
*/
validate: function (object, parentCandidate) {
var currentParent = object
.getCapability('context')
.getParent();
if (!parentCandidate || !parentCandidate.getId) {
return false;
}
if (parentCandidate.getId() === currentParent.getId()) {
return false;
}
if (parentCandidate.getId() === object.getId()) {
return false;
}
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
return false;
}
return policyService.allow(
"composition",
parentCandidate.getCapability('type'),
object.getCapability('type')
);
},
/**
* Move `object` into `parentObject`'s composition.
*
* @returns {Promise} A promise that is fulfilled when the
* move operation has completed.
*/
perform: function (object, parentObject) {
return linkService
.perform(object, parentObject)
.then(function () {
return object
.getCapability('action')
.perform('remove');
});
}
};
}
return MoveService;
}
);

View File

@ -0,0 +1,158 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define, jasmine, */
define(
function () {
"use strict";
/**
* @typedef DomainObjectConfig
* @type {object}
* @property {string} [name] a name for the underlying jasmine spy
* object mockDomainObject. Used as
* @property {string} [id] initial id value for the domainOBject.
* @property {object} [model] initial values for the object's model.
* @property {object} [capabilities] an object containing
* capability definitions.
*/
var configObjectProps = ['model', 'capabilities'];
/**
* Internal function for ensuring an object is an instance of a
* DomainObjectConfig.
*/
function ensureValidConfigObject(config) {
if (!config || !config.hasOwnProperty) {
config = {};
}
if (!config.name) {
config.name = 'domainObject';
}
configObjectProps.forEach(function (prop) {
if (!config[prop] || !config[prop].hasOwnProperty) {
config[prop] = {};
}
});
return config;
}
/**
* Defines a factory function which takes a `config` object and returns
* a mock domainObject. The config object is an easy way to provide
* initial properties for the domainObject-- they can be changed at any
* time by directly modifying the domainObject's properties.
*
* @param {Object} [config] initial configuration for a domain object.
* @returns {Object} mockDomainObject
*/
function domainObjectFactory(config) {
config = ensureValidConfigObject(config);
var domainObject = jasmine.createSpyObj(config.name, [
'getId',
'getModel',
'getCapability',
'hasCapability',
'useCapability'
]);
domainObject.model = JSON.parse(JSON.stringify(config.model));
domainObject.capabilities = config.capabilities;
domainObject.id = config.id;
/**
* getId: Returns `domainObject.id`.
*
* @returns {string} id
*/
domainObject.getId.andCallFake(function () {
return domainObject.id;
});
/**
* getModel: Returns `domainObject.model`.
*
* @returns {object} model
*/
domainObject.getModel.andCallFake(function () {
return domainObject.model;
});
/**
* getCapability: returns a `capability` object defined in
* domainObject.capabilities. Returns undefined if capability
* does not exist.
*
* @param {string} capability name of the capability to return.
* @returns {*} capability object
*/
domainObject.getCapability.andCallFake(function (capability) {
if (config.capabilities.hasOwnProperty(capability)) {
return config.capabilities[capability];
}
});
/**
* hasCapability: return true if domainObject.capabilities has a
* property named `capability`, otherwise returns false.
*
* @param {string} capability name of the capability to test for
* existence of.
* @returns {boolean}
*/
domainObject.hasCapability.andCallFake(function (capability) {
return config.capabilities.hasOwnProperty(capability);
});
/**
* useCapability: find a capability in domainObject.capabilities
* and call that capabilities' invoke method. If the capability
* does not have an invoke method, will throw an error.
*
* @param {string} capability name of a capability to invoke.
* @param {...*} params to pass to the capability's `invoke` method.
* @returns {*} result whatever was returned by `invoke`.
*/
domainObject.useCapability.andCallFake(function (capability) {
if (config.capabilities.hasOwnProperty(capability)) {
if (!config.capabilities[capability].invoke) {
throw new Error(
capability + ' missing invoke function.'
);
}
var passThroughArgs = [].slice.call(arguments, 1);
return config
.capabilities[capability]
.invoke
.apply(null, passThroughArgs);
}
});
return domainObject;
}
return domainObjectFactory;
}
);

View File

@ -0,0 +1,174 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/actions/CopyAction',
'../services/MockCopyService',
'../DomainObjectFactory'
],
function (CopyAction, MockCopyService, domainObjectFactory) {
"use strict";
describe("Copy Action", function () {
var copyAction,
locationService,
locationServicePromise,
copyService,
context,
selectedObject,
selectedObjectContextCapability,
currentParent,
newParent;
beforeEach(function () {
selectedObjectContextCapability = jasmine.createSpyObj(
'selectedObjectContextCapability',
[
'getParent'
]
);
selectedObject = domainObjectFactory({
name: 'selectedObject',
model: {
name: 'selectedObject'
},
capabilities: {
context: selectedObjectContextCapability
}
});
currentParent = domainObjectFactory({
name: 'currentParent'
});
selectedObjectContextCapability
.getParent
.andReturn(currentParent);
newParent = domainObjectFactory({
name: 'newParent'
});
locationService = jasmine.createSpyObj(
'locationService',
[
'getLocationFromUser'
]
);
locationServicePromise = jasmine.createSpyObj(
'locationServicePromise',
[
'then'
]
);
locationService
.getLocationFromUser
.andReturn(locationServicePromise);
copyService = new MockCopyService();
});
describe("with context from context-action", function () {
beforeEach(function () {
context = {
domainObject: selectedObject
};
copyAction = new CopyAction(
locationService,
copyService,
context
);
});
it("initializes happily", function () {
expect(copyAction).toBeDefined();
});
describe("when performed it", function () {
beforeEach(function () {
copyAction.perform();
});
it("prompts for location", function () {
expect(locationService.getLocationFromUser)
.toHaveBeenCalledWith(
"Duplicate selectedObject to a location",
"Duplicate To",
jasmine.any(Function),
currentParent
);
});
it("waits for location from user", function () {
expect(locationServicePromise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("copys object to selected location", function () {
locationServicePromise
.then
.mostRecentCall
.args[0](newParent);
expect(copyService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
describe("with context from drag-drop", function () {
beforeEach(function () {
context = {
selectedObject: selectedObject,
domainObject: newParent
};
copyAction = new CopyAction(
locationService,
copyService,
context
);
});
it("initializes happily", function () {
expect(copyAction).toBeDefined();
});
it("performs copy immediately", function () {
copyAction.perform();
expect(copyService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
}
);

View File

@ -0,0 +1,174 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/actions/LinkAction',
'../services/MockLinkService',
'../DomainObjectFactory'
],
function (LinkAction, MockLinkService, domainObjectFactory) {
"use strict";
describe("Link Action", function () {
var linkAction,
locationService,
locationServicePromise,
linkService,
context,
selectedObject,
selectedObjectContextCapability,
currentParent,
newParent;
beforeEach(function () {
selectedObjectContextCapability = jasmine.createSpyObj(
'selectedObjectContextCapability',
[
'getParent'
]
);
selectedObject = domainObjectFactory({
name: 'selectedObject',
model: {
name: 'selectedObject'
},
capabilities: {
context: selectedObjectContextCapability
}
});
currentParent = domainObjectFactory({
name: 'currentParent'
});
selectedObjectContextCapability
.getParent
.andReturn(currentParent);
newParent = domainObjectFactory({
name: 'newParent'
});
locationService = jasmine.createSpyObj(
'locationService',
[
'getLocationFromUser'
]
);
locationServicePromise = jasmine.createSpyObj(
'locationServicePromise',
[
'then'
]
);
locationService
.getLocationFromUser
.andReturn(locationServicePromise);
linkService = new MockLinkService();
});
describe("with context from context-action", function () {
beforeEach(function () {
context = {
domainObject: selectedObject
};
linkAction = new LinkAction(
locationService,
linkService,
context
);
});
it("initializes happily", function () {
expect(linkAction).toBeDefined();
});
describe("when performed it", function () {
beforeEach(function () {
linkAction.perform();
});
it("prompts for location", function () {
expect(locationService.getLocationFromUser)
.toHaveBeenCalledWith(
"Link selectedObject to a new location",
"Link To",
jasmine.any(Function),
currentParent
);
});
it("waits for location from user", function () {
expect(locationServicePromise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("links object to selected location", function () {
locationServicePromise
.then
.mostRecentCall
.args[0](newParent);
expect(linkService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
describe("with context from drag-drop", function () {
beforeEach(function () {
context = {
selectedObject: selectedObject,
domainObject: newParent
};
linkAction = new LinkAction(
locationService,
linkService,
context
);
});
it("initializes happily", function () {
expect(linkAction).toBeDefined();
});
it("performs link immediately", function () {
linkAction.perform();
expect(linkService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
}
);

View File

@ -0,0 +1,174 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/actions/MoveAction',
'../services/MockMoveService',
'../DomainObjectFactory'
],
function (MoveAction, MockMoveService, domainObjectFactory) {
"use strict";
describe("Move Action", function () {
var moveAction,
locationService,
locationServicePromise,
moveService,
context,
selectedObject,
selectedObjectContextCapability,
currentParent,
newParent;
beforeEach(function () {
selectedObjectContextCapability = jasmine.createSpyObj(
'selectedObjectContextCapability',
[
'getParent'
]
);
selectedObject = domainObjectFactory({
name: 'selectedObject',
model: {
name: 'selectedObject'
},
capabilities: {
context: selectedObjectContextCapability
}
});
currentParent = domainObjectFactory({
name: 'currentParent'
});
selectedObjectContextCapability
.getParent
.andReturn(currentParent);
newParent = domainObjectFactory({
name: 'newParent'
});
locationService = jasmine.createSpyObj(
'locationService',
[
'getLocationFromUser'
]
);
locationServicePromise = jasmine.createSpyObj(
'locationServicePromise',
[
'then'
]
);
locationService
.getLocationFromUser
.andReturn(locationServicePromise);
moveService = new MockMoveService();
});
describe("with context from context-action", function () {
beforeEach(function () {
context = {
domainObject: selectedObject
};
moveAction = new MoveAction(
locationService,
moveService,
context
);
});
it("initializes happily", function () {
expect(moveAction).toBeDefined();
});
describe("when performed it", function () {
beforeEach(function () {
moveAction.perform();
});
it("prompts for location", function () {
expect(locationService.getLocationFromUser)
.toHaveBeenCalledWith(
"Move selectedObject to a new location",
"Move To",
jasmine.any(Function),
currentParent
);
});
it("waits for location from user", function () {
expect(locationServicePromise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("moves object to selected location", function () {
locationServicePromise
.then
.mostRecentCall
.args[0](newParent);
expect(moveService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
describe("with context from drag-drop", function () {
beforeEach(function () {
context = {
selectedObject: selectedObject,
domainObject: newParent
};
moveAction = new MoveAction(
locationService,
moveService,
context
);
});
it("initializes happily", function () {
expect(moveAction).toBeDefined();
});
it("performs move immediately", function () {
moveAction.perform();
expect(moveService.perform)
.toHaveBeenCalledWith(selectedObject, newParent);
});
});
});
}
);

View File

@ -0,0 +1,272 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect,spyOn */
define(
[
'../../src/services/CopyService',
'../DomainObjectFactory'
],
function (CopyService, domainObjectFactory) {
"use strict";
function synchronousPromise(value) {
var promise = {
then: function (callback) {
return synchronousPromise(callback(value));
}
};
spyOn(promise, 'then').andCallThrough();
return promise;
}
describe("CopyService", function () {
describe("validate", function () {
var policyService,
copyService,
object,
parentCandidate,
validate;
beforeEach(function () {
policyService = jasmine.createSpyObj(
'policyService',
['allow']
);
copyService = new CopyService(
null,
null,
policyService
);
object = domainObjectFactory({
name: 'object',
capabilities: {
type: { type: 'object' }
}
});
parentCandidate = domainObjectFactory({
name: 'parentCandidate',
capabilities: {
type: { type: 'parentCandidate' }
}
});
validate = function () {
return copyService.validate(object, parentCandidate);
};
});
it("does not allow invalid parentCandidate", function () {
parentCandidate = undefined;
expect(validate()).toBe(false);
parentCandidate = {};
expect(validate()).toBe(false);
});
it("does not allow copying into source object", function () {
object.id = parentCandidate.id = 'abc';
expect(validate()).toBe(false);
});
describe("defers to policyService", function () {
beforeEach(function () {
object.id = 'a';
parentCandidate.id = 'b';
});
it("calls policy service with correct args", function () {
validate();
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
);
});
it("and returns false", function () {
policyService.allow.andReturn(false);
expect(validate()).toBe(false);
});
it("and returns true", function () {
policyService.allow.andReturn(true);
expect(validate()).toBe(true);
});
});
});
describe("perform", function () {
var mockQ,
creationService,
createObjectPromise,
copyService,
object,
newParent,
copyResult,
copyFinished;
describe("on domain object without composition", function () {
beforeEach(function () {
object = domainObjectFactory({
name: 'object',
id: 'abc',
model: {
name: 'some object'
}
});
newParent = domainObjectFactory({
name: 'newParent',
id: '456',
model: {
composition: []
}
});
creationService = jasmine.createSpyObj(
'creationService',
['createObject']
);
createObjectPromise = synchronousPromise(undefined);
creationService.createObject.andReturn(createObjectPromise);
copyService = new CopyService(null, creationService);
copyResult = copyService.perform(object, newParent);
copyFinished = jasmine.createSpy('copyFinished');
copyResult.then(copyFinished);
});
it("uses creation service", function () {
expect(creationService.createObject)
.toHaveBeenCalledWith(jasmine.any(Object), newParent);
expect(createObjectPromise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("deep clones object model", function () {
var newModel = creationService
.createObject
.mostRecentCall
.args[0];
expect(newModel).toEqual(object.model);
expect(newModel).not.toBe(object.model);
});
it("returns a promise", function () {
expect(copyResult).toBeDefined();
expect(copyFinished).toHaveBeenCalled();
});
});
describe("on domainObject with composition", function () {
var childObject,
compositionCapability,
compositionPromise;
beforeEach(function () {
mockQ = jasmine.createSpyObj('mockQ', ['when']);
mockQ.when.andCallFake(synchronousPromise);
childObject = domainObjectFactory({
name: 'childObject',
id: 'def',
model: {
name: 'a child object'
}
});
compositionCapability = jasmine.createSpyObj(
'compositionCapability',
['invoke']
);
compositionPromise = jasmine.createSpyObj(
'compositionPromise',
['then']
);
compositionCapability
.invoke
.andReturn(compositionPromise);
object = domainObjectFactory({
name: 'object',
id: 'abc',
model: {
name: 'some object',
composition: ['def']
},
capabilities: {
composition: compositionCapability
}
});
newParent = domainObjectFactory({
name: 'newParent',
id: '456',
model: {
composition: []
}
});
creationService = jasmine.createSpyObj(
'creationService',
['createObject']
);
createObjectPromise = synchronousPromise(undefined);
creationService.createObject.andReturn(createObjectPromise);
copyService = new CopyService(mockQ, creationService);
copyResult = copyService.perform(object, newParent);
copyFinished = jasmine.createSpy('copyFinished');
copyResult.then(copyFinished);
});
it("uses creation service", function () {
expect(creationService.createObject)
.toHaveBeenCalledWith(jasmine.any(Object), newParent);
expect(createObjectPromise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("clears model composition", function () {
var newModel = creationService
.createObject
.mostRecentCall
.args[0];
expect(newModel.composition.length).toBe(0);
expect(newModel.name).toBe('some object');
});
it("recursively clones it's children", function () {
expect(creationService.createObject.calls.length).toBe(1);
expect(compositionCapability.invoke).toHaveBeenCalled();
compositionPromise.then.mostRecentCall.args[0]([childObject]);
expect(creationService.createObject.calls.length).toBe(2);
});
it("returns a promise", function () {
expect(copyResult.then).toBeDefined();
expect(copyFinished).toHaveBeenCalled();
});
});
});
});
}
);

View File

@ -0,0 +1,183 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/services/LinkService',
'../DomainObjectFactory'
],
function (LinkService, domainObjectFactory) {
"use strict";
describe("LinkService", function () {
var linkService,
mockPolicyService;
beforeEach(function () {
mockPolicyService = jasmine.createSpyObj(
'policyService',
['allow']
);
linkService = new LinkService(mockPolicyService);
});
describe("validate", function () {
var object,
parentCandidate,
validate;
beforeEach(function () {
object = domainObjectFactory({
name: 'object'
});
parentCandidate = domainObjectFactory({
name: 'parentCandidate'
});
validate = function () {
return linkService.validate(object, parentCandidate);
};
});
it("does not allow invalid parentCandidate", function () {
parentCandidate = undefined;
expect(validate()).toBe(false);
parentCandidate = {};
expect(validate()).toBe(false);
});
it("does not allow parent to be object", function () {
parentCandidate.id = object.id = 'abc';
expect(validate()).toBe(false);
});
it("does not allow parent that contains object", function () {
object.id = 'abc';
parentCandidate.id = 'xyz';
parentCandidate.model.composition = ['abc'];
expect(validate()).toBe(false);
});
describe("defers to policyService", function () {
beforeEach(function () {
object.id = 'abc';
object.capabilities.type = { type: 'object' };
parentCandidate.id = 'xyz';
parentCandidate.capabilities.type = {
type: 'parentCandidate'
};
parentCandidate.model.composition = [];
});
it("calls policy service with correct args", function () {
validate();
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
);
});
it("and returns false", function () {
mockPolicyService.allow.andReturn(true);
expect(validate()).toBe(true);
expect(mockPolicyService.allow).toHaveBeenCalled();
});
it("and returns true", function () {
mockPolicyService.allow.andReturn(false);
expect(validate()).toBe(false);
expect(mockPolicyService.allow).toHaveBeenCalled();
});
});
});
describe("perform", function () {
var object,
parentModel,
parentObject,
mutationPromise,
persistenceCapability;
beforeEach(function () {
mutationPromise = jasmine.createSpyObj(
'promise',
['then']
);
persistenceCapability = jasmine.createSpyObj(
'persistenceCapability',
['persist']
);
parentModel = {
composition: []
};
parentObject = domainObjectFactory({
name: 'parentObject',
model: parentModel,
capabilities: {
mutation: {
invoke: function (mutator) {
mutator(parentModel);
return mutationPromise;
}
},
persistence: persistenceCapability
}
});
object = domainObjectFactory({
name: 'object',
id: 'xyz'
});
parentObject.getCapability.andReturn(persistenceCapability);
});
it("modifies parent model composition", function () {
expect(parentModel.composition.length).toBe(0);
linkService.perform(object, parentObject);
expect(parentObject.useCapability).toHaveBeenCalledWith(
'mutation',
jasmine.any(Function)
);
expect(parentModel.composition).toContain('xyz');
});
it("persists parent", function () {
linkService.perform(object, parentObject);
expect(mutationPromise.then).toHaveBeenCalled();
mutationPromise.then.calls[0].args[0]();
expect(parentObject.getCapability)
.toHaveBeenCalledWith('persistence');
expect(persistenceCapability.persist).toHaveBeenCalled();
});
});
});
}
);

View File

@ -0,0 +1,151 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/services/LocationService'
],
function (LocationService) {
"use strict";
describe("LocationService", function () {
var dialogService,
locationService,
dialogServicePromise,
chainedPromise;
beforeEach(function () {
dialogService = jasmine.createSpyObj(
'dialogService',
['getUserInput']
);
dialogServicePromise = jasmine.createSpyObj(
'dialogServicePromise',
['then']
);
chainedPromise = jasmine.createSpyObj(
'chainedPromise',
['then']
);
dialogServicePromise.then.andReturn(chainedPromise);
dialogService.getUserInput.andReturn(dialogServicePromise);
locationService = new LocationService(dialogService);
});
describe("getLocationFromUser", function () {
var title,
label,
validate,
initialLocation,
locationResult,
formStructure,
formState;
beforeEach(function () {
title = "Get a location to do something";
label = "a location";
validate = function () { return true; };
initialLocation = { key: "a key" };
locationResult = locationService.getLocationFromUser(
title,
label,
validate,
initialLocation
);
formStructure = dialogService
.getUserInput
.mostRecentCall
.args[0];
formState = dialogService
.getUserInput
.mostRecentCall
.args[1];
});
it("calls through to dialogService", function () {
expect(dialogService.getUserInput).toHaveBeenCalledWith(
jasmine.any(Object),
jasmine.any(Object)
);
expect(formStructure.name).toBe(title);
});
it("returns a promise", function () {
expect(locationResult.then).toBeDefined();
});
describe("formStructure", function () {
var locationSection,
inputRow;
beforeEach(function () {
locationSection = formStructure.sections[0];
inputRow = locationSection.rows[0];
});
it("has a location section", function () {
expect(locationSection).toBeDefined();
expect(locationSection.name).toBe('Location');
});
it("has a input row", function () {
expect(inputRow.control).toBe('locator');
expect(inputRow.key).toBe('location');
expect(inputRow.name).toBe(label);
expect(inputRow.validate).toBe(validate);
});
});
describe("formState", function () {
it("has an initial location", function () {
expect(formState.location).toBe(initialLocation);
});
});
describe("resolution of dialog service promise", function () {
var resolution,
resolver,
dialogResult,
selectedLocation;
beforeEach(function () {
resolver =
dialogServicePromise.then.mostRecentCall.args[0];
selectedLocation = { key: "i'm a location key" };
dialogResult = {
location: selectedLocation
};
resolution = resolver(dialogResult);
});
it("returns selectedLocation", function () {
expect(resolution).toBe(selectedLocation);
});
});
});
});
}
);

View File

@ -0,0 +1,99 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,jasmine */
define(
function () {
"use strict";
/**
* MockCopyService provides the same interface as the copyService,
* returning promises where it would normally do so. At it's core,
* it is a jasmine spy object, but it also tracks the promises it
* returns and provides shortcut methods for resolving those promises
* synchronously.
*
* Usage:
*
* ```javascript
* var copyService = new MockCopyService();
*
* // validate is a standard jasmine spy.
* copyService.validate.andReturn(true);
* var isValid = copyService.validate(object, parentCandidate);
* expect(isValid).toBe(true);
*
* // perform returns promises and tracks them.
* var whenCopied = jasmine.createSpy('whenCopied');
* copyService.perform(object, parentObject).then(whenCopied);
* expect(whenCopied).not.toHaveBeenCalled();
* copyService.perform.mostRecentCall.resolve('someArg');
* expect(whenCopied).toHaveBeenCalledWith('someArg');
* ```
*/
function MockCopyService() {
// track most recent call of a function,
// perform automatically returns
var mockCopyService = jasmine.createSpyObj(
'MockCopyService',
[
'validate',
'perform'
]
);
mockCopyService.perform.andCallFake(function () {
var performPromise,
callExtensions,
spy;
performPromise = jasmine.createSpyObj(
'performPromise',
['then']
);
callExtensions = {
promise: performPromise,
resolve: function (resolveWith) {
performPromise.then.calls.forEach(function (call) {
call.args[0](resolveWith);
});
}
};
spy = this.perform;
Object.keys(callExtensions).forEach(function (key) {
spy.mostRecentCall[key] = callExtensions[key];
spy.calls[spy.calls.length - 1][key] = callExtensions[key];
});
return performPromise;
});
return mockCopyService;
}
return MockCopyService;
}
);

View File

@ -0,0 +1,99 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,jasmine */
define(
function () {
"use strict";
/**
* MockLinkService provides the same interface as the linkService,
* returning promises where it would normally do so. At it's core,
* it is a jasmine spy object, but it also tracks the promises it
* returns and provides shortcut methods for resolving those promises
* synchronously.
*
* Usage:
*
* ```javascript
* var linkService = new MockLinkService();
*
* // validate is a standard jasmine spy.
* linkService.validate.andReturn(true);
* var isValid = linkService.validate(object, parentObject);
* expect(isValid).toBe(true);
*
* // perform returns promises and tracks them.
* var whenLinked = jasmine.createSpy('whenLinked');
* linkService.perform(object, parentObject).then(whenLinked);
* expect(whenLinked).not.toHaveBeenCalled();
* linkService.perform.mostRecentCall.resolve('someArg');
* expect(whenLinked).toHaveBeenCalledWith('someArg');
* ```
*/
function MockLinkService() {
// track most recent call of a function,
// perform automatically returns
var mockLinkService = jasmine.createSpyObj(
'MockLinkService',
[
'validate',
'perform'
]
);
mockLinkService.perform.andCallFake(function () {
var performPromise,
callExtensions,
spy;
performPromise = jasmine.createSpyObj(
'performPromise',
['then']
);
callExtensions = {
promise: performPromise,
resolve: function (resolveWith) {
performPromise.then.calls.forEach(function (call) {
call.args[0](resolveWith);
});
}
};
spy = this.perform;
Object.keys(callExtensions).forEach(function (key) {
spy.mostRecentCall[key] = callExtensions[key];
spy.calls[spy.calls.length - 1][key] = callExtensions[key];
});
return performPromise;
});
return mockLinkService;
}
return MockLinkService;
}
);

View File

@ -0,0 +1,99 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,jasmine */
define(
function () {
"use strict";
/**
* MockMoveService provides the same interface as the moveService,
* returning promises where it would normally do so. At it's core,
* it is a jasmine spy object, but it also tracks the promises it
* returns and provides shortcut methods for resolving those promises
* synchronously.
*
* Usage:
*
* ```javascript
* var moveService = new MockMoveService();
*
* // validate is a standard jasmine spy.
* moveService.validate.andReturn(true);
* var isValid = moveService.validate(object, parentCandidate);
* expect(isValid).toBe(true);
*
* // perform returns promises and tracks them.
* var whenCopied = jasmine.createSpy('whenCopied');
* moveService.perform(object, parentObject).then(whenCopied);
* expect(whenCopied).not.toHaveBeenCalled();
* moveService.perform.mostRecentCall.resolve('someArg');
* expect(whenCopied).toHaveBeenCalledWith('someArg');
* ```
*/
function MockMoveService() {
// track most recent call of a function,
// perform automatically returns
var mockMoveService = jasmine.createSpyObj(
'MockMoveService',
[
'validate',
'perform'
]
);
mockMoveService.perform.andCallFake(function () {
var performPromise,
callExtensions,
spy;
performPromise = jasmine.createSpyObj(
'performPromise',
['then']
);
callExtensions = {
promise: performPromise,
resolve: function (resolveWith) {
performPromise.then.calls.forEach(function (call) {
call.args[0](resolveWith);
});
}
};
spy = this.perform;
Object.keys(callExtensions).forEach(function (key) {
spy.mostRecentCall[key] = callExtensions[key];
spy.calls[spy.calls.length - 1][key] = callExtensions[key];
});
return performPromise;
});
return mockMoveService;
}
return MockMoveService;
}
);

View File

@ -0,0 +1,189 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/services/MoveService',
'../services/MockLinkService',
'../DomainObjectFactory'
],
function (MoveService, MockLinkService, domainObjectFactory) {
"use strict";
describe("MoveService", function () {
var moveService,
policyService,
linkService;
beforeEach(function () {
policyService = jasmine.createSpyObj(
'policyService',
['allow']
);
linkService = new MockLinkService();
moveService = new MoveService(policyService, linkService);
});
describe("validate", function () {
var object,
objectContextCapability,
currentParent,
parentCandidate,
validate;
beforeEach(function () {
objectContextCapability = jasmine.createSpyObj(
'objectContextCapability',
[
'getParent'
]
);
object = domainObjectFactory({
name: 'object',
id: 'a',
capabilities: {
context: objectContextCapability,
type: { type: 'object' }
}
});
currentParent = domainObjectFactory({
name: 'currentParent',
id: 'b'
});
objectContextCapability.getParent.andReturn(currentParent);
parentCandidate = domainObjectFactory({
name: 'parentCandidate',
model: { composition: [] },
id: 'c',
capabilities: {
type: { type: 'parentCandidate' }
}
});
validate = function () {
return moveService.validate(object, parentCandidate);
};
});
it("does not allow an invalid parent", function () {
parentCandidate = undefined;
expect(validate()).toBe(false);
parentCandidate = {};
expect(validate()).toBe(false);
});
it("does not allow moving to current parent", function () {
parentCandidate.id = currentParent.id = 'xyz';
expect(validate()).toBe(false);
});
it("does not allow moving to self", function () {
object.id = parentCandidate.id = 'xyz';
expect(validate()).toBe(false);
});
it("does not allow moving to the same location", function () {
object.id = 'abc';
parentCandidate.model.composition = ['abc'];
expect(validate()).toBe(false);
});
describe("defers to policyService", function () {
it("calls policy service with correct args", function () {
validate();
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
);
});
it("and returns false", function () {
policyService.allow.andReturn(false);
expect(validate()).toBe(false);
});
it("and returns true", function () {
policyService.allow.andReturn(true);
expect(validate()).toBe(true);
});
});
});
describe("perform", function () {
var object,
parentObject,
actionCapability;
beforeEach(function () {
actionCapability = jasmine.createSpyObj(
'actionCapability',
['perform']
);
object = domainObjectFactory({
name: 'object',
capabilities: {
action: actionCapability
}
});
parentObject = domainObjectFactory({
name: 'parentObject'
});
moveService.perform(object, parentObject);
});
it("links object to parentObject", function () {
expect(linkService.perform).toHaveBeenCalledWith(
object,
parentObject
);
});
it("waits for result of link", function () {
expect(linkService.perform.mostRecentCall.promise.then)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("removes object when link is completed", function () {
linkService.perform.mostRecentCall.resolve();
expect(object.getCapability)
.toHaveBeenCalledWith('action');
expect(actionCapability.perform)
.toHaveBeenCalledWith('remove');
});
});
});
}
);

View File

@ -0,0 +1,9 @@
[
"actions/CopyAction",
"actions/LinkAction",
"actions/MoveAction",
"services/CopyService",
"services/LinkService",
"services/MoveService",
"services/LocationService"
]

View File

@ -0,0 +1 @@
Contains services which manage execution and flow control (e.g. for concurrency.)

View File

@ -0,0 +1,11 @@
{
"extensions": {
"services": [
{
"key": "workerService",
"implementation": "WorkerService.js",
"depends": [ "$window", "workers[]" ]
}
]
}
}

View File

@ -0,0 +1,68 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
[],
function () {
"use strict";
/**
* Handles the execution of WebWorkers.
* @constructor
*/
function WorkerService($window, workers) {
var workerUrls = {},
Worker = $window.Worker;
function addWorker(worker) {
var key = worker.key;
if (!workerUrls[key]) {
workerUrls[key] = [
worker.bundle.path,
worker.bundle.sources,
worker.scriptUrl
].join("/");
}
}
(workers || []).forEach(addWorker);
return {
/**
* Start running a new web worker. This will run a worker
* that has been registered under the `workers` category
* of extension.
*
* @param {string} key symbolic identifier for the worker
* @returns {Worker} the running Worker
*/
run: function (key) {
var scriptUrl = workerUrls[key];
return scriptUrl && Worker && new Worker(scriptUrl);
}
};
}
return WorkerService;
}
);

View File

@ -0,0 +1,77 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,jasmine*/
define(
["../src/WorkerService"],
function (WorkerService) {
"use strict";
describe("The worker service", function () {
var mockWindow,
testWorkers,
mockWorker,
service;
beforeEach(function () {
mockWindow = jasmine.createSpyObj('$window', ['Worker']);
testWorkers = [
{
key: 'abc',
scriptUrl: 'c.js',
bundle: { path: 'a', sources: 'b' }
},
{
key: 'xyz',
scriptUrl: 'z.js',
bundle: { path: 'x', sources: 'y' }
},
{
key: 'xyz',
scriptUrl: 'bad.js',
bundle: { path: 'bad', sources: 'bad' }
}
];
mockWorker = {};
mockWindow.Worker.andReturn(mockWorker);
service = new WorkerService(mockWindow, testWorkers);
});
it("instantiates workers at registered paths", function () {
expect(service.run('abc')).toBe(mockWorker);
expect(mockWindow.Worker).toHaveBeenCalledWith('a/b/c.js');
});
it("prefers the first worker when multiple keys are found", function () {
expect(service.run('xyz')).toBe(mockWorker);
expect(mockWindow.Worker).toHaveBeenCalledWith('x/y/z.js');
});
it("returns undefined for unknown workers", function () {
expect(service.run('def')).toBeUndefined();
});
});
}
);

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