Merge branch 'open-master' into open1170

This commit is contained in:
Victor Woeltjen 2015-06-09 11:41:44 -07:00
commit bc6b3f8902
22 changed files with 366 additions and 20 deletions

3
.gitignore vendored
View File

@ -17,3 +17,6 @@ target
# Closed source libraries
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

@ -15,8 +15,8 @@
"platform/features/scrolling",
"platform/forms",
"platform/persistence/queue",
"platform/persistence/elastic",
"platform/policy",
"example/persistence",
"example/generator"
]

View File

@ -121,6 +121,12 @@
"depends": [ "typeService", "dialogService", "creationService", "policyService" ]
}
],
"runs": [
{
"implementation": "windowing/WindowTitler.js",
"depends": [ "navigationService", "$rootScope", "$document" ]
}
],
"licenses": [
{
"name": "screenfull.js",

View File

@ -46,6 +46,7 @@ define(
function setNavigation(domainObject) {
$scope.navigatedObject = domainObject;
$scope.treeModel.selectedObject = domainObject;
navigationService.setNavigation(domainObject);
}
// Load the root object, put it in the scope.

View File

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

View File

@ -45,10 +45,12 @@ define(
// Setter for navigation; invokes callbacks
function setNavigation(value) {
navigated = value;
callbacks.forEach(function (callback) {
callback(value);
});
if (navigated !== value) {
navigated = value;
callbacks.forEach(function (callback) {
callback(value);
});
}
}
// Adds a callback

View File

@ -0,0 +1,52 @@
/*****************************************************************************
* 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";
/**
* Updates the title of the current window to reflect the name
* of the currently navigated-to domain object.
* @constructor
*/
function WindowTitler(navigationService, $rootScope, $document) {
// Look up name of the navigated domain object...
function getNavigatedObjectName() {
var navigatedObject = navigationService.getNavigation();
return navigatedObject && navigatedObject.getModel().name;
}
// Set the window title...
function setTitle(name) {
$document[0].title = name;
}
// Watch the former, and invoke the latter
$rootScope.$watch(getNavigatedObjectName, setTitle);
}
return WindowTitler;
}
);

View File

@ -62,6 +62,16 @@ define(
expect(callback).toHaveBeenCalledWith(testObject);
});
it("does not notify listeners when no changes occur", function () {
var testObject = { someKey: 42 },
callback = jasmine.createSpy("callback");
navigationService.addListener(callback);
navigationService.setNavigation(testObject);
navigationService.setNavigation(testObject);
expect(callback.calls.length).toEqual(1);
});
it("stops notifying listeners after removal", function () {
var testObject = { someKey: 42 },
callback = jasmine.createSpy("callback");

View File

@ -8,5 +8,6 @@
"creation/LocatorController",
"navigation/NavigateAction",
"navigation/NavigationService",
"windowing/FullscreenAction"
"windowing/FullscreenAction",
"windowing/WindowTitler"
]

View File

@ -0,0 +1,80 @@
/*****************************************************************************
* 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*/
/**
* WindowTitlerSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/windowing/WindowTitler"],
function (WindowTitler) {
"use strict";
describe("The window titler", function () {
var mockNavigationService,
mockRootScope,
mockDocument,
mockDomainObject,
titler;
beforeEach(function () {
mockNavigationService = jasmine.createSpyObj(
'navigationService',
[ 'getNavigation' ]
);
mockRootScope = jasmine.createSpyObj(
'$rootScope',
[ '$watch' ]
);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getModel']
);
mockDocument = [{}];
mockDomainObject.getModel.andReturn({ name: 'Test name' });
mockNavigationService.getNavigation.andReturn(mockDomainObject);
titler = new WindowTitler(
mockNavigationService,
mockRootScope,
mockDocument
);
});
it("listens for changes to the name of the navigated object", function () {
expect(mockRootScope.$watch).toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Function)
);
expect(mockRootScope.$watch.mostRecentCall.args[0]())
.toEqual('Test name');
});
it("sets the title to the name of the navigated object", function () {
mockRootScope.$watch.mostRecentCall.args[1]("Some name");
expect(mockDocument[0].title).toEqual("Some name");
});
});
}
);

View File

@ -12,6 +12,11 @@
"implementation": "CompositionMutabilityPolicy.js",
"message": "Objects of this type cannot be modified."
},
{
"category": "composition",
"implementation": "CompositionModelPolicy.js",
"message": "Objects of this type cannot contain other objects."
},
{
"category": "action",
"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();
});
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();
mockType.hasFeature.andReturn(true);
expect(policy.allow(mockType)).toBeTruthy();

View File

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

View File

@ -77,7 +77,8 @@ define(
// Get the object's model and clone it, so the
// mutator function has a temporary copy to work with.
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 handleMutation(mutationResult) {
@ -94,8 +95,7 @@ define(
if (model !== result) {
copyValues(model, result);
}
model.modified = (typeof timestamp === 'number') ?
timestamp : now();
model.modified = useTimestamp ? timestamp : now();
}
// Report the result of the mutation

View File

@ -38,7 +38,7 @@
placeholder="YYYY-DDD"
ng-pattern="/\d\d\d\d-\d\d\d/"
ng-model='datetime.date'
ng-required='true'/>
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control time sm'>
<input type='text'
@ -49,7 +49,7 @@
integer
ng-pattern='/\d+/'
ng-model="datetime.hour"
ng-required='true'/>
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control time sm'>
<input type='text'
@ -60,7 +60,7 @@
integer
ng-pattern='/\d+/'
ng-model="datetime.min"
ng-required='true'/>
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control time sm'>
<input type='text'
@ -71,7 +71,7 @@
integer
ng-pattern='/\d+/'
ng-model="datetime.sec"
ng-required='true'/>
ng-required='ngRequired || partiallyComplete'/>
</span>
<span class='field control timezone'>
UTC

View File

@ -52,15 +52,51 @@ define(
if (fullDateTime.isValid()) {
$scope.ngModel[$scope.field] = fullDateTime.valueOf();
}
// If anything is complete, say so in scope; there are
// ng-required usages that will update off of this (to
// allow datetime to be optional while still permitting
// incomplete input)
$scope.partiallyComplete =
Object.keys($scope.datetime).some(function (key) {
return $scope.datetime[key];
});
// Treat empty input as an undefined value
if (!$scope.partiallyComplete) {
$scope.ngModel[$scope.field] = undefined;
}
}
function updateDateTime(value) {
var m;
if (value !== undefined) {
m = moment.utc(value);
$scope.datetime = {
date: m.format(DATE_FORMAT),
hour: m.format("H"),
min: m.format("m"),
sec: m.format("s")
};
} else {
$scope.datetime = {};
}
}
// ...and update form values when actual field in model changes
$scope.$watch("ngModel[field]", updateDateTime);
// Update value whenever any field changes.
$scope.$watch("datetime.date", update);
$scope.$watch("datetime.hour", update);
$scope.$watch("datetime.min", update);
$scope.$watch("datetime.sec", update);
$scope.datetime = {};
// Initialize forms values
updateDateTime(
($scope.ngModel && $scope.field) ?
$scope.ngModel[$scope.field] : undefined
);
}
return DateTimeController;

View File

@ -57,6 +57,33 @@ define(
expect(mockScope.ngModel.test).toEqual(1417215313000);
});
it("reports when form input is partially complete", function () {
// This is needed to flag the control's state as invalid
// when it is partially complete without having it treated
// as required.
mockScope.ngModel = {};
mockScope.field = "test";
mockScope.datetime.date = "2014-332";
mockScope.datetime.hour = 22;
mockScope.datetime.min = 55;
// mockScope.datetime.sec = 13;
mockScope.$watch.mostRecentCall.args[1]();
expect(mockScope.partiallyComplete).toBeTruthy();
});
it("reports 'undefined' for empty input", function () {
mockScope.ngModel = { test: 12345 };
mockScope.field = "test";
mockScope.$watch.mostRecentCall.args[1]();
// Clear all inputs
mockScope.datetime = {};
mockScope.$watch.mostRecentCall.args[1]();
// Should have cleared out the time stamp
expect(mockScope.ngModel.test).toBeUndefined();
});
});
}
);

View File

@ -17,15 +17,18 @@
},
{
"key": "ELASTIC_ROOT",
"value": "/elastic"
"value": "/elastic",
"priority": "fallback"
},
{
"key": "ELASTIC_PATH",
"value": "mct/domain_object"
"value": "mct/domain_object",
"priority": "fallback"
},
{
"key": "ELASTIC_INDICATOR_INTERVAL",
"value": 15000
"value": 15000,
"priority": "fallback"
}
],
"indicators": [

View File

@ -160,6 +160,8 @@
<sourceJsFolder>${basedir}</sourceJsFolder>
<excludes>
<exclude>**/lib/**</exclude>
<exclude>app.js</exclude>
<exclude>node_modules/**/*</exclude>
</excludes>
</configuration>
<executions>