diff --git a/.gitignore b/.gitignore index e034b4100b..4c2c6ce8a0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ target # Closed source libraries closed-lib +# Node dependencies +node_modules + diff --git a/app.js b/app.js new file mode 100644 index 0000000000..4ebc9c3e94 --- /dev/null +++ b/app.js @@ -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 Specify port."); + console.log(" --include, -i Include the specified bundle."); + console.log(" --exclude, -x 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); +}()); \ No newline at end of file diff --git a/bundles.json b/bundles.json index 486d4cda5b..0486bdf24b 100644 --- a/bundles.json +++ b/bundles.json @@ -16,8 +16,8 @@ "platform/features/scrolling", "platform/forms", "platform/persistence/queue", - "platform/persistence/elastic", "platform/policy", + "example/persistence", "example/generator" ] diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json index 41f521f5cb..28d96c5ec2 100644 --- a/platform/commonUI/browse/bundle.json +++ b/platform/commonUI/browse/bundle.json @@ -122,6 +122,12 @@ "depends": [ "typeService", "dialogService", "creationService", "policyService" ] } ], + "runs": [ + { + "implementation": "windowing/WindowTitler.js", + "depends": [ "navigationService", "$rootScope", "$document" ] + } + ], "licenses": [ { "name": "screenfull.js", diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js index 15e979ae40..e2c2484e1b 100644 --- a/platform/commonUI/browse/src/BrowseController.js +++ b/platform/commonUI/browse/src/BrowseController.js @@ -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. diff --git a/platform/commonUI/browse/src/creation/CreateWizard.js b/platform/commonUI/browse/src/creation/CreateWizard.js index 7f60bdd883..29fe953e18 100644 --- a/platform/commonUI/browse/src/creation/CreateWizard.js +++ b/platform/commonUI/browse/src/creation/CreateWizard.js @@ -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 diff --git a/platform/commonUI/browse/src/navigation/NavigationService.js b/platform/commonUI/browse/src/navigation/NavigationService.js index 642c5f1bc4..c8c76857b8 100644 --- a/platform/commonUI/browse/src/navigation/NavigationService.js +++ b/platform/commonUI/browse/src/navigation/NavigationService.js @@ -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 diff --git a/platform/commonUI/browse/src/windowing/WindowTitler.js b/platform/commonUI/browse/src/windowing/WindowTitler.js new file mode 100644 index 0000000000..9db65e5b0e --- /dev/null +++ b/platform/commonUI/browse/src/windowing/WindowTitler.js @@ -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; + } +); \ No newline at end of file diff --git a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js index 212419ca17..6d7f3fd20d 100644 --- a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js +++ b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js @@ -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"); diff --git a/platform/commonUI/browse/test/suite.json b/platform/commonUI/browse/test/suite.json index 2c74311faa..21d76dae05 100644 --- a/platform/commonUI/browse/test/suite.json +++ b/platform/commonUI/browse/test/suite.json @@ -8,5 +8,6 @@ "creation/LocatorController", "navigation/NavigateAction", "navigation/NavigationService", - "windowing/FullscreenAction" + "windowing/FullscreenAction", + "windowing/WindowTitler" ] \ No newline at end of file diff --git a/platform/commonUI/browse/test/windowing/WindowTitlerSpec.js b/platform/commonUI/browse/test/windowing/WindowTitlerSpec.js new file mode 100644 index 0000000000..d9c71a86dd --- /dev/null +++ b/platform/commonUI/browse/test/windowing/WindowTitlerSpec.js @@ -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"); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index ec2a5d2aa7..ead610c963 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -43,6 +43,10 @@ { "key": "indicator", "templateUrl": "templates/indicator.html" + }, + { + "key": "time-controller", + "templateUrl": "templates/controls/time-controller.html" } ], "controllers": [ diff --git a/platform/commonUI/general/res/css/forms.css b/platform/commonUI/general/res/css/forms.css index 113a906725..4fee272bb9 100644 --- a/platform/commonUI/general/res/css/forms.css +++ b/platform/commonUI/general/res/css/forms.css @@ -302,7 +302,8 @@ label.form-control.checkbox input { * at runtime from the About dialog for additional information. *****************************************************************************/ /* line 22, ../sass/forms/_text-input.scss */ -input[type="text"] { +input[type="text"], +input[type="date"] { -moz-appearance: none; -webkit-appearance: none; -moz-border-radius: 3px; @@ -321,10 +322,32 @@ input[type="text"] { outline: none; padding: 0 3px; } /* 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); } - /* line 29, ../sass/forms/_text-input.scss */ - input[type="text"].numeric { + /* line 61, ../../../../../../../../../../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 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; } /***************************************************************************** diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index eeda438f84..64aa7e083a 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -84,7 +84,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -105,38 +105,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -2050,7 +2050,11 @@ label.checkbox.custom { margin-right: 50px; } /******************************************************** SLIDERS */ -/* line 444, ../sass/controls/_controls.scss */ +/* line 439, ../sass/controls/_controls.scss */ +.wrapper-slider { + position: relative; } + +/* line 447, ../sass/controls/_controls.scss */ .slider .slot { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2064,14 +2068,30 @@ label.checkbox.custom { background-color: rgba(0, 0, 0, 0.4); border-bottom: 1px solid rgba(77, 77, 77, 0.4); border-right: 1px solid rgba(77, 77, 77, 0.4); - height: 50%; + height: auto; width: auto; position: absolute; - top: 25%; + top: 10%; right: 0; - bottom: auto; - left: 0; } -/* line 455, ../sass/controls/_controls.scss */ + bottom: 10%; + left: 0; + z-index: 0; } + /* line 459, ../sass/controls/_controls.scss */ + .slider .slot .range { + background: rgba(0, 153, 204, 0.3); + cursor: ew-resize; + position: absolute; + top: 0; + right: auto; + bottom: 0; + left: auto; + height: auto; + width: auto; + z-index: 1; } + /* line 470, ../sass/controls/_controls.scss */ + .slider .slot .range:hover { + background: rgba(0, 153, 204, 0.5); } +/* line 475, ../sass/controls/_controls.scss */ .slider .knob { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzQwNDA0MCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2092,14 +2112,17 @@ label.checkbox.custom { border-top: 1px solid #666666; color: #999; display: inline-block; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; cursor: ew-resize; position: absolute; - height: 100%; + height: auto; width: 12px; top: 0; - auto: 0; - bottom: auto; - left: auto; } + bottom: 0; + left: auto; + z-index: 2; } /* line 148, ../sass/_mixins.scss */ .slider .knob:not(.disabled):hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzY2NjY2NiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); @@ -2122,28 +2145,20 @@ label.checkbox.custom { /* line 141, ../sass/_mixins.scss */ .slider .knob:not(.disabled):hover:before { border-color: rgba(0, 153, 204, 0.9); } - /* line 466, ../sass/controls/_controls.scss */ + /* line 487, ../sass/controls/_controls.scss */ + .slider .knob.knob-l { + margin-left: -6px; } + /* line 488, ../sass/controls/_controls.scss */ + .slider .knob.knob-r { + margin-right: -6px; } + /* line 489, ../sass/controls/_controls.scss */ .slider .knob:before { top: 1px; bottom: 3px; - left: 5px; } -/* line 473, ../sass/controls/_controls.scss */ -.slider .range { - background: rgba(0, 153, 204, 0.6); - cursor: ew-resize; - position: absolute; - top: 0; - right: auto; - bottom: 0; - left: auto; - height: auto; - width: auto; } - /* line 483, ../sass/controls/_controls.scss */ - .slider .range:hover { - background: rgba(0, 153, 204, 0.7); } + left: 45%; } /******************************************************** BROWSER ELEMENTS */ -/* line 491, ../sass/controls/_controls.scss */ +/* line 501, ../sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2160,7 +2175,7 @@ label.checkbox.custom { height: 10px; width: 10px; } -/* line 497, ../sass/controls/_controls.scss */ +/* line 507, ../sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzY2NjY2NiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2178,7 +2193,7 @@ label.checkbox.custom { -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px; border-top: 1px solid gray; } - /* line 504, ../sass/controls/_controls.scss */ + /* line 514, ../sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzgwODA4MCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzczNzM3MyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2187,7 +2202,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#808080, #737373 20px); background-image: linear-gradient(#808080, #737373 20px); } -/* line 509, ../sass/controls/_controls.scss */ +/* line 519, ../sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.4); } @@ -2448,6 +2463,103 @@ label.checkbox.custom { right: 0; width: auto; } +/* line 1, ../sass/controls/_time-controller.scss */ +.l-time-controller { + position: relative; + margin: 10px 0; + min-width: 400px; } + /* line 12, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-inputs-holder, + .l-time-controller .l-time-range-slider { + font-size: 0.8em; } + /* line 17, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-inputs-holder, + .l-time-controller .l-time-range-slider-holder, + .l-time-controller .l-time-range-ticks-holder { + margin-bottom: 5px; + position: relative; } + /* line 24, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-slider, + .l-time-controller .l-time-range-ticks { + overflow: visible; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; } + /* line 30, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-inputs-holder { + height: 20px; } + /* line 34, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-slider, + .l-time-controller .l-time-range-ticks { + left: 90px; + right: 90px; } + /* line 40, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-slider-holder { + height: 30px; } + /* line 42, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-slider-holder .range-holder { + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; + background: none; + border: none; + height: 75%; } + /* line 50, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-ticks-holder { + height: 10px; } + /* line 52, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-ticks-holder .l-time-range-ticks { + border-top: 1px solid #4d4d4d; } + /* line 54, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick { + background-color: #4d4d4d; + border: none; + width: 1px; + margin-left: -1px; } + /* line 59, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick:first-child { + margin-left: 0; } + /* line 62, ../sass/controls/_time-controller.scss */ + .l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick .l-time-range-tick-label { + color: gray; + font-size: 0.7em; + position: absolute; + margin-left: -25px; + text-align: center; + top: 10px; + width: 50px; + z-index: 2; } + /* line 76, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob { + width: 9px; } + /* line 78, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob .range-value { + position: absolute; + top: 50%; + margin-top: -7px; + white-space: nowrap; + width: 75px; } + /* line 87, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob:hover .range-value { + color: #0099cc; } + /* line 90, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob.knob-l { + margin-left: -4.5px; } + /* line 92, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob.knob-l .range-value { + text-align: right; + right: 14px; } + /* line 97, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob.knob-r { + margin-right: -4.5px; } + /* line 99, ../sass/controls/_time-controller.scss */ + .l-time-controller .knob.knob-r .range-value { + left: 14px; } + /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -2697,7 +2809,8 @@ span.req { * at runtime from the About dialog for additional information. *****************************************************************************/ /* line 22, ../sass/forms/_text-input.scss */ -input[type="text"] { +input[type="text"], +input[type="date"] { -moz-appearance: none; -webkit-appearance: none; -moz-border-radius: 3px; @@ -2716,10 +2829,32 @@ input[type="text"] { outline: none; padding: 0 3px; } /* 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); } - /* line 29, ../sass/forms/_text-input.scss */ - input[type="text"].numeric { + /* line 61, ../../../../../../../../../../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 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; } /***************************************************************************** diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot index 6caed6eec3..bf781e614b 100644 Binary files a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot and b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot differ diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg index 47dc61951f..2ca77321a4 100644 --- a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg +++ b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg @@ -2,7 +2,7 @@ -Created by FontForge 20090622 at Tue Apr 28 20:52:23 2015 +Created by FontForge 20090622 at Mon May 4 20:21:42 2015 By deploy user Copyright 2015 Adobe Systems Incorporated. All rights reserved. @@ -224,6 +224,14 @@ d="M188 235h-160q-28 67 -28 140q0 156 110 265.5t265 109.5q88 0 164 -37t129 -103h q39 0 66 27.5t27 66.5v93h2h186q39 0 66.5 27.5t27.5 66.5v94h1z" /> + + + + + + \ No newline at end of file diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html new file mode 100644 index 0000000000..fe1b6ff14a --- /dev/null +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -0,0 +1,69 @@ + + +
+ +
+
+ Start: + End: +
+ +
+
+
+
+
+
+
+
05/22 14:46
+
+
+
07/22 01:21
+
+
+
+
+ +
+
+
+ {{tick}} +
+
+
+
\ No newline at end of file diff --git a/platform/containment/bundle.json b/platform/containment/bundle.json index e61085f66f..e6e24f0f79 100644 --- a/platform/containment/bundle.json +++ b/platform/containment/bundle.json @@ -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", diff --git a/platform/containment/src/CompositionModelPolicy.js b/platform/containment/src/CompositionModelPolicy.js new file mode 100644 index 0000000000..74f1200530 --- /dev/null +++ b/platform/containment/src/CompositionModelPolicy.js @@ -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; + } +); \ No newline at end of file diff --git a/platform/containment/test/CompositionModelPolicySpec.js b/platform/containment/test/CompositionModelPolicySpec.js new file mode 100644 index 0000000000..bace49246d --- /dev/null +++ b/platform/containment/test/CompositionModelPolicySpec.js @@ -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(); + }); + }); + + } +); diff --git a/platform/containment/test/CompositionMutabilityPolicySpec.js b/platform/containment/test/CompositionMutabilityPolicySpec.js index c537a5000f..1f49883939 100644 --- a/platform/containment/test/CompositionMutabilityPolicySpec.js +++ b/platform/containment/test/CompositionMutabilityPolicySpec.js @@ -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(); diff --git a/platform/containment/test/suite.json b/platform/containment/test/suite.json index 987ef9a86c..81218a969e 100644 --- a/platform/containment/test/suite.json +++ b/platform/containment/test/suite.json @@ -1,6 +1,7 @@ [ "CapabilityTable", "ComposeActionPolicy", + "CompositionModelPolicy", "CompositionMutabilityPolicy", "CompositionPolicy", "ContainmentTable" diff --git a/platform/core/src/capabilities/MutationCapability.js b/platform/core/src/capabilities/MutationCapability.js index 431c8d5d19..c20c90deab 100644 --- a/platform/core/src/capabilities/MutationCapability.js +++ b/platform/core/src/capabilities/MutationCapability.js @@ -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 diff --git a/platform/features/scrolling/res/templates/scrolling.html b/platform/features/scrolling/res/templates/scrolling.html index 802ae0790f..2d5d19fabc 100644 --- a/platform/features/scrolling/res/templates/scrolling.html +++ b/platform/features/scrolling/res/templates/scrolling.html @@ -20,27 +20,31 @@ at runtime from the About dialog for additional information. -->
- -
-
- -
- -
- {{header}}{{header}} -
-
- -
-
- {{cell}} -
-
- -
-
+
+ + + + + + + + + + + + + +
+ {{header}} +
+ {{cell}} +
+
diff --git a/platform/forms/res/templates/controls/datetime.html b/platform/forms/res/templates/controls/datetime.html index e2accf7c8f..6dae89eb8a 100644 --- a/platform/forms/res/templates/controls/datetime.html +++ b/platform/forms/res/templates/controls/datetime.html @@ -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'/> + ng-required='ngRequired || partiallyComplete'/> + ng-required='ngRequired || partiallyComplete'/> + ng-required='ngRequired || partiallyComplete'/> UTC diff --git a/platform/forms/src/controllers/DateTimeController.js b/platform/forms/src/controllers/DateTimeController.js index bf122c3f9c..c026e98935 100644 --- a/platform/forms/src/controllers/DateTimeController.js +++ b/platform/forms/src/controllers/DateTimeController.js @@ -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; diff --git a/platform/forms/test/controllers/DateTimeControllerSpec.js b/platform/forms/test/controllers/DateTimeControllerSpec.js index e3e5c7b693..11c97ada07 100644 --- a/platform/forms/test/controllers/DateTimeControllerSpec.js +++ b/platform/forms/test/controllers/DateTimeControllerSpec.js @@ -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(); + }); }); } ); \ No newline at end of file diff --git a/platform/persistence/elastic/bundle.json b/platform/persistence/elastic/bundle.json index 53f8571e1a..371b8aa17a 100644 --- a/platform/persistence/elastic/bundle.json +++ b/platform/persistence/elastic/bundle.json @@ -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": [ diff --git a/pom.xml b/pom.xml index 783cc8ce45..b8cc32ca06 100644 --- a/pom.xml +++ b/pom.xml @@ -160,6 +160,8 @@ ${basedir} **/lib/** + app.js + node_modules/**/*