Compare commits
	
		
			1 Commits
		
	
	
		
			pause-play
			...
			open90a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					63798ff756 | 
@@ -6,10 +6,12 @@
 | 
			
		||||
    "platform/commonUI/browse",
 | 
			
		||||
    "platform/commonUI/edit",
 | 
			
		||||
    "platform/commonUI/dialog",
 | 
			
		||||
    "platform/commonUI/formats",
 | 
			
		||||
    "platform/commonUI/general",
 | 
			
		||||
    "platform/commonUI/inspect",
 | 
			
		||||
    "platform/commonUI/mobile",
 | 
			
		||||
    "platform/commonUI/themes/espresso",
 | 
			
		||||
    "platform/commonUI/notification",
 | 
			
		||||
    "platform/containment",
 | 
			
		||||
    "platform/execution",
 | 
			
		||||
    "platform/telemetry",
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,23 @@
 | 
			
		||||
                "implementation": "SinewaveLimitCapability.js"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "formats": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "example.delta",
 | 
			
		||||
                "implementation": "SinewaveDeltaFormat.js"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "constants": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "TIME_CONDUCTOR_DOMAINS",
 | 
			
		||||
                "value": [
 | 
			
		||||
                    { "key": "time", "name": "Time" },
 | 
			
		||||
                    { "key": "yesterday", "name": "Yesterday" },
 | 
			
		||||
                    { "key": "delta", "name": "Delta", "format": "example.delta" }
 | 
			
		||||
                ],
 | 
			
		||||
                "priority": -1
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "types": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "generator",
 | 
			
		||||
@@ -38,6 +55,11 @@
 | 
			
		||||
                        {
 | 
			
		||||
                            "key": "yesterday",
 | 
			
		||||
                            "name": "Yesterday"
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            "key": "delta",
 | 
			
		||||
                            "name": "Delta",
 | 
			
		||||
                            "format": "example.delta"
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    "ranges": [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								example/generator/src/SinewaveConstants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								example/generator/src/SinewaveConstants.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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({
 | 
			
		||||
    START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day.
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										68
									
								
								example/generator/src/SinewaveDeltaFormat.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								example/generator/src/SinewaveDeltaFormat.js
									
									
									
									
									
										Normal 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,Promise*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./SinewaveConstants', 'moment'],
 | 
			
		||||
    function (SinewaveConstants, moment) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var START_TIME = SinewaveConstants.START_TIME,
 | 
			
		||||
            FORMAT_REGEX = /^-?\d+:\d+:\d+$/,
 | 
			
		||||
            SECOND = 1000,
 | 
			
		||||
            MINUTE = SECOND * 60,
 | 
			
		||||
            HOUR = MINUTE * 60;
 | 
			
		||||
 | 
			
		||||
        function SinewaveDeltaFormat() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function twoDigit(v) {
 | 
			
		||||
            return v >= 10 ? String(v) : ('0' + v);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SinewaveDeltaFormat.prototype.format = function (value) {
 | 
			
		||||
            var delta = Math.abs(value - START_TIME),
 | 
			
		||||
                negative = value < START_TIME,
 | 
			
		||||
                seconds = Math.floor(delta / SECOND) % 60,
 | 
			
		||||
                minutes = Math.floor(delta / MINUTE) % 60,
 | 
			
		||||
                hours = Math.floor(delta / HOUR);
 | 
			
		||||
            return (negative ? "-" : "") +
 | 
			
		||||
                [ hours, minutes, seconds ].map(twoDigit).join(":");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        SinewaveDeltaFormat.prototype.validate = function (text) {
 | 
			
		||||
            return FORMAT_REGEX.test(text);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        SinewaveDeltaFormat.prototype.parse = function (text) {
 | 
			
		||||
            var negative = text[0] === "-",
 | 
			
		||||
                parts = text.replace("-", "").split(":");
 | 
			
		||||
            return [ HOUR, MINUTE, SECOND ].map(function (sz, i) {
 | 
			
		||||
                return parseInt(parts[i], 10) * sz;
 | 
			
		||||
            }).reduce(function (a, b) {
 | 
			
		||||
                return a + b;
 | 
			
		||||
            }, 0) * (negative ? -1 : 1) + START_TIME;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return SinewaveDeltaFormat;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -25,12 +25,12 @@
 | 
			
		||||
 * Module defining SinewaveTelemetry. Created by vwoeltje on 11/12/14.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
    ['./SinewaveConstants'],
 | 
			
		||||
    function (SinewaveConstants) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var ONE_DAY = 60 * 60 * 24,
 | 
			
		||||
            firstObservedTime = Math.floor(Date.now() / 1000) - ONE_DAY;
 | 
			
		||||
            firstObservedTime = Math.floor(SinewaveConstants.START_TIME / 1000);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         *
 | 
			
		||||
@@ -58,6 +58,9 @@ define(
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            generatorData.getDomainValue = function (i, domain) {
 | 
			
		||||
                // delta uses the same numeric values as the default domain,
 | 
			
		||||
                // so it's not checked for here, just formatted for display
 | 
			
		||||
                // differently.
 | 
			
		||||
                return (i + offset) * 1000 + firstTime * 1000 -
 | 
			
		||||
                    (domain === 'yesterday' ? ONE_DAY : 0);
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								example/notifications/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								example/notifications/bundle.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "templates": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "dialogLaunchTemplate",
 | 
			
		||||
                "templateUrl": "dialog-launch.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "notificationLaunchTemplate",
 | 
			
		||||
                "templateUrl": "notification-launch.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DialogLaunchController",
 | 
			
		||||
                "implementation": "DialogLaunchController.js",
 | 
			
		||||
                "depends": [
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    "$timeout",
 | 
			
		||||
                    "$log",
 | 
			
		||||
                    "dialogService",
 | 
			
		||||
                    "notificationService"
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "NotificationLaunchController",
 | 
			
		||||
                "implementation": "NotificationLaunchController.js",
 | 
			
		||||
                "depends": [
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    "$timeout",
 | 
			
		||||
                    "$log",
 | 
			
		||||
                    "notificationService"
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "indicators": [
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "DialogLaunchIndicator.js",
 | 
			
		||||
                "priority": "fallback"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "NotificationLaunchIndicator.js",
 | 
			
		||||
                "priority": "fallback"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								example/notifications/res/dialog-launch.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								example/notifications/res/dialog-launch.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
<span class="status block ok" ng-controller="DialogLaunchController">
 | 
			
		||||
    <span class="ui-symbol status-indicator"></span>
 | 
			
		||||
    <span class="label">
 | 
			
		||||
        <a ng-click="launchProgress(true)">Known</a> |
 | 
			
		||||
        <a ng-click="launchProgress(false)">Unknown</a> |
 | 
			
		||||
        <a ng-click="launchError()">Error</a> |
 | 
			
		||||
        <a ng-click="launchInfo()">Info</a>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="count">Dialogs</span>
 | 
			
		||||
</span>
 | 
			
		||||
							
								
								
									
										10
									
								
								example/notifications/res/notification-launch.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								example/notifications/res/notification-launch.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
<span class="status block ok" ng-controller="NotificationLaunchController">
 | 
			
		||||
    <span class="ui-symbol status-indicator"></span>
 | 
			
		||||
    <span class="label">
 | 
			
		||||
        <a ng-click="newInfo()">Success</a> |
 | 
			
		||||
        <a ng-click="newError()">Error</a> |
 | 
			
		||||
        <a ng-click="newAlert()">Alert</a> |
 | 
			
		||||
        <a ng-click="newProgress()">Progress</a>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="count">Notifications</span>
 | 
			
		||||
</span>
 | 
			
		||||
							
								
								
									
										150
									
								
								example/notifications/src/DialogLaunchController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								example/notifications/src/DialogLaunchController.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A controller for the dialog launch view. This view allows manual
 | 
			
		||||
         * launching of dialogs for demonstration and testing purposes. It
 | 
			
		||||
         * also demonstrates the use of the DialogService.
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param $timeout
 | 
			
		||||
         * @param $log
 | 
			
		||||
         * @param dialogService
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) {
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Demonstrates launching a progress dialog and updating it
 | 
			
		||||
             periodically with the progress of an ongoing process.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.launchProgress = function (knownProgress) {
 | 
			
		||||
                var model = {
 | 
			
		||||
                    title: "Progress Dialog Example",
 | 
			
		||||
                    progress: 0,
 | 
			
		||||
                    hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.",
 | 
			
		||||
                    actionText: "Calculating...",
 | 
			
		||||
                    unknownProgress: !knownProgress,
 | 
			
		||||
                    unknownDuration: false,
 | 
			
		||||
                    severity: "info",
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Cancel Operation",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Operation cancelled");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Do something else...",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Something else pressed");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                function incrementProgress() {
 | 
			
		||||
                    model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30));
 | 
			
		||||
                    model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" ");
 | 
			
		||||
                    if (model.progress < 100) {
 | 
			
		||||
                        $timeout(incrementProgress, 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (dialogService.showBlockingMessage(model)) {
 | 
			
		||||
                    //Do processing here
 | 
			
		||||
                    model.actionText = "Processing 100 objects...";
 | 
			
		||||
                    if (knownProgress) {
 | 
			
		||||
                        $timeout(incrementProgress, 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $log.error("Could not display modal dialog");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             Demonstrates launching an error dialog
 | 
			
		||||
             */
 | 
			
		||||
            $scope.launchError = function () {
 | 
			
		||||
                var model = {
 | 
			
		||||
                    title: "Error Dialog Example",
 | 
			
		||||
                    actionText: "Something happened, and it was not good.",
 | 
			
		||||
                    severity: "error",
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Try Again",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Try Again Pressed");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Cancel",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Cancel Pressed");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if (!dialogService.showBlockingMessage(model)) {
 | 
			
		||||
                    $log.error("Could not display modal dialog");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             Demonstrates launching an error dialog
 | 
			
		||||
             */
 | 
			
		||||
            $scope.launchInfo = function () {
 | 
			
		||||
                var model = {
 | 
			
		||||
                    title: "Info Dialog Example",
 | 
			
		||||
                    actionText: "This is an example of a blocking info" +
 | 
			
		||||
                    " dialog. This dialog can be used to draw the user's" +
 | 
			
		||||
                    " attention to an event.",
 | 
			
		||||
                    severity: "info",
 | 
			
		||||
                    primaryOption: {
 | 
			
		||||
                            label: "OK",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("OK Pressed");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if (!dialogService.showBlockingMessage(model)) {
 | 
			
		||||
                    $log.error("Could not display modal dialog");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return DialogLaunchController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										56
									
								
								example/notifications/src/DialogLaunchIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								example/notifications/src/DialogLaunchIndicator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A tool for manually invoking dialogs. When included this
 | 
			
		||||
         * indicator will allow for dialogs of different types to be
 | 
			
		||||
         * launched for demonstration and testing purposes.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function DialogLaunchIndicator() {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        DialogLaunchIndicator.template = 'dialogLaunchTemplate';
 | 
			
		||||
 | 
			
		||||
        DialogLaunchIndicator.prototype.getGlyph = function () {
 | 
			
		||||
            return "i";
 | 
			
		||||
        };
 | 
			
		||||
        DialogLaunchIndicator.prototype.getGlyphClass = function () {
 | 
			
		||||
            return 'caution';
 | 
			
		||||
        };
 | 
			
		||||
        DialogLaunchIndicator.prototype.getText = function () {
 | 
			
		||||
            return "Launch test dialog";
 | 
			
		||||
        };
 | 
			
		||||
        DialogLaunchIndicator.prototype.getDescription = function () {
 | 
			
		||||
            return "Launch test dialog";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return DialogLaunchIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										172
									
								
								example/notifications/src/NotificationLaunchController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								example/notifications/src/NotificationLaunchController.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Allows launching of notification messages for the purposes of
 | 
			
		||||
         * demonstration and testing. Also demonstrates use of
 | 
			
		||||
         * the NotificationService. Notifications are non-blocking messages that
 | 
			
		||||
         * appear at the bottom of the screen to inform the user of events
 | 
			
		||||
         * in a non-intrusive way. For more information see the
 | 
			
		||||
         * {@link NotificationService}
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param $timeout
 | 
			
		||||
         * @param $log
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function NotificationLaunchController($scope, $timeout, $log, notificationService) {
 | 
			
		||||
            var messageCounter = 1;
 | 
			
		||||
 | 
			
		||||
            function getExampleActionText() {
 | 
			
		||||
                var actionTexts = [
 | 
			
		||||
                    "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.",
 | 
			
		||||
                    "Eros turpis, pulvinar turpis eros eu",
 | 
			
		||||
                    "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!"
 | 
			
		||||
                ];
 | 
			
		||||
                return actionTexts[Math.floor(Math.random()*3)];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getExampleActions() {
 | 
			
		||||
                var actions = [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Try Again",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            $log.debug("Try Again pressed");
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Remove",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            $log.debug("Remove pressed");
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Cancel",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            $log.debug("Cancel pressed");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                // Randomly remove some actions off the top; leave at least one
 | 
			
		||||
                actions.splice(0,Math.floor(Math.random() * actions.length));
 | 
			
		||||
 | 
			
		||||
                return actions;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getExampleSeverity() {
 | 
			
		||||
                var severities = [
 | 
			
		||||
                    "info",
 | 
			
		||||
                    "alert",
 | 
			
		||||
                    "error"
 | 
			
		||||
                ];
 | 
			
		||||
                return severities[Math.floor(Math.random() * severities.length)];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with a severity level of 'Error'.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newError = function(){
 | 
			
		||||
 | 
			
		||||
                notificationService.notify({
 | 
			
		||||
                    title: "Example error notification " + messageCounter++,
 | 
			
		||||
                    hint: "An error has occurred",
 | 
			
		||||
                    severity: "error",
 | 
			
		||||
                    primaryOption: {
 | 
			
		||||
                        label: 'Retry',
 | 
			
		||||
                        callback: function() {
 | 
			
		||||
                            $log.info('Retry clicked');
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    options: getExampleActions()});
 | 
			
		||||
            };
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with a severity of 'Alert'.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newAlert = function(){
 | 
			
		||||
 | 
			
		||||
                notificationService.notify({
 | 
			
		||||
                    title: "Alert notification " + (messageCounter++),
 | 
			
		||||
                    hint: "This is an alert message",
 | 
			
		||||
                    severity: "alert",
 | 
			
		||||
                    primaryOption: {
 | 
			
		||||
                        label: 'Retry',
 | 
			
		||||
                        callback: function() {
 | 
			
		||||
                            $log.info('Retry clicked');
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    options: getExampleActions()});
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with a progress bar that is updated
 | 
			
		||||
             * periodically, tracking an ongoing process.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newProgress = function(){
 | 
			
		||||
 | 
			
		||||
                var notificationModel = {
 | 
			
		||||
                    title: "Progress notification example",
 | 
			
		||||
                    severity: "info",
 | 
			
		||||
                    progress: 0,
 | 
			
		||||
                    actionText: getExampleActionText(),
 | 
			
		||||
                    unknownProgress: false
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * Simulate an ongoing process and update the progress bar.
 | 
			
		||||
                 * @param notification
 | 
			
		||||
                 */
 | 
			
		||||
                function incrementProgress(notificationModel) {
 | 
			
		||||
                    notificationModel.progress = Math.min(100, Math.floor(notificationModel.progress + Math.random() * 30));
 | 
			
		||||
                    notificationModel.progressText = ["Estimated time" +
 | 
			
		||||
                    " remaining:" +
 | 
			
		||||
                    " about ", 60 - Math.floor((notificationModel.progress / 100) * 60), " seconds"].join(" ");
 | 
			
		||||
                    if (notificationModel.progress < 100) {
 | 
			
		||||
                        $timeout(function(){incrementProgress(notificationModel);}, 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                notificationService.notify(notificationModel);
 | 
			
		||||
                incrementProgress(notificationModel);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with severity level of INFO.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newInfo = function(){
 | 
			
		||||
 | 
			
		||||
                notificationService.info({
 | 
			
		||||
                    title: "Example Info notification " + messageCounter++
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return NotificationLaunchController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										50
									
								
								example/notifications/src/NotificationLaunchIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								example/notifications/src/NotificationLaunchIndicator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        function NotificationLaunchIndicator() {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        NotificationLaunchIndicator.template = 'notificationLaunchTemplate';
 | 
			
		||||
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getGlyph = function () {
 | 
			
		||||
            return "i";
 | 
			
		||||
        };
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getGlyphClass = function () {
 | 
			
		||||
            return 'caution';
 | 
			
		||||
        };
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getText = function () {
 | 
			
		||||
            return "Launch notification";
 | 
			
		||||
        };
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getDescription = function () {
 | 
			
		||||
            return "Launch notification";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return NotificationLaunchIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -40,10 +40,9 @@
 | 
			
		||||
                    </mct-representation>
 | 
			
		||||
                    <div class='holder search-holder abs'
 | 
			
		||||
                         ng-class="{active: treeModel.search}">
 | 
			
		||||
                        <mct-representation key="'search'"
 | 
			
		||||
                                     mct-object="domainObject"
 | 
			
		||||
                        <mct-include key="'search'"
 | 
			
		||||
                                     ng-model="treeModel">
 | 
			
		||||
                        </mct-representation>
 | 
			
		||||
                        </mct-include>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class='tree-holder abs mobile-tree-holder'
 | 
			
		||||
                         ng-hide="treeModel.search">
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,18 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "form-dialog",
 | 
			
		||||
                "templateUrl": "templates/dialog.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "overlay-blocking-message",
 | 
			
		||||
                "templateUrl": "templates/overlay-blocking-message.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "message",
 | 
			
		||||
                "templateUrl": "templates/message.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "overlay-message-list",
 | 
			
		||||
                "templateUrl": "templates/overlay-message-list.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "containers": [
 | 
			
		||||
 
 | 
			
		||||
@@ -21,17 +21,13 @@
 | 
			
		||||
-->
 | 
			
		||||
<div class="abs top-bar">
 | 
			
		||||
    <div class="title">{{ngModel.title}}</div>
 | 
			
		||||
    <div class="hint">
 | 
			
		||||
        All fields marked <span class="ui-symbol req">*</span> are required.
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="hint">All fields marked <span class="ui-symbol req">*</span> are required.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="abs form editor">
 | 
			
		||||
    <div class='abs contents l-dialog'>
 | 
			
		||||
        <mct-form ng-model="ngModel.value"
 | 
			
		||||
                  structure="ngModel.structure"
 | 
			
		||||
                  name="createForm">
 | 
			
		||||
        </mct-form>
 | 
			
		||||
    </div>
 | 
			
		||||
<div class='abs editor'>
 | 
			
		||||
    <mct-form ng-model="ngModel.value"
 | 
			
		||||
              structure="ngModel.structure"
 | 
			
		||||
              name="createForm">
 | 
			
		||||
    </mct-form>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="abs bottom-bar">
 | 
			
		||||
    <a class='s-btn major'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								platform/commonUI/dialog/res/templates/message.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								platform/commonUI/dialog/res/templates/message.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
<div class="l-message"
 | 
			
		||||
     ng-class="'message-severity-' + ngModel.severity">
 | 
			
		||||
    <div class="ui-symbol type-icon message-type"></div>
 | 
			
		||||
    <div class="message-contents">
 | 
			
		||||
        <div class="top-bar">
 | 
			
		||||
            <div class="title">{{ngModel.title}}</div>
 | 
			
		||||
            <div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="message-body">
 | 
			
		||||
            <div class="message-action">
 | 
			
		||||
                {{ngModel.actionText}}
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-include key="'progress-bar'"
 | 
			
		||||
                         ng-model="ngModel"
 | 
			
		||||
                         ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="bottom-bar">
 | 
			
		||||
            <a ng-repeat="dialogOption in ngModel.options"
 | 
			
		||||
               class="s-btn major"
 | 
			
		||||
               ng-click="dialogOption.callback()">
 | 
			
		||||
                {{dialogOption.label}}
 | 
			
		||||
            </a>
 | 
			
		||||
            <a class="s-btn major"
 | 
			
		||||
               ng-if="ngModel.primaryOption"
 | 
			
		||||
               ng-click="ngModel.primaryOption.callback()">
 | 
			
		||||
                {{ngModel.primaryOption.label}}
 | 
			
		||||
            </a>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
<!--
 | 
			
		||||
 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.
 | 
			
		||||
-->
 | 
			
		||||
<mct-container key="overlay" class="t-message-single">
 | 
			
		||||
    <mct-include key="'message'" ng-model="ngModel">
 | 
			
		||||
    </mct-include>
 | 
			
		||||
</mct-container>
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
<mct-container key="overlay" class="t-message-list">
 | 
			
		||||
    <div class="message-contents">
 | 
			
		||||
        <div class="abs top-bar">
 | 
			
		||||
            <div class="title">{{ngModel.dialog.title}}</div>
 | 
			
		||||
            <div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1">s</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="abs message-body">
 | 
			
		||||
            <mct-include ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'" key="'message'" ng-model="msg"></mct-include>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="abs bottom-bar">
 | 
			
		||||
            <a ng-repeat="dialogAction in ngModel.dialog.actions"
 | 
			
		||||
               class="s-btn major"
 | 
			
		||||
               ng-click="dialogAction.action()">
 | 
			
		||||
                {{dialogAction.label}}
 | 
			
		||||
            </a>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</mct-container>
 | 
			
		||||
@@ -24,13 +24,11 @@
 | 
			
		||||
        <div class="title">{{ngModel.dialog.title}}</div>
 | 
			
		||||
        <div class="hint">{{ngModel.dialog.hint}}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="abs form outline editor">
 | 
			
		||||
        <div class='abs contents l-dialog'>
 | 
			
		||||
            <mct-include key="ngModel.dialog.template"
 | 
			
		||||
                         parameters="ngModel.dialog.parameters"
 | 
			
		||||
                         ng-model="ngModel.dialog.model">
 | 
			
		||||
            </mct-include>
 | 
			
		||||
        </div>
 | 
			
		||||
    <div class='abs editor'>
 | 
			
		||||
        <mct-include key="ngModel.dialog.template"
 | 
			
		||||
                     parameters="ngModel.dialog.parameters"
 | 
			
		||||
                     ng-model="ngModel.dialog.model">
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="abs bottom-bar">
 | 
			
		||||
        <a ng-repeat="option in ngModel.dialog.options"
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,9 @@
 | 
			
		||||
<div class="abs overlay">
 | 
			
		||||
    <div class="abs blocker"></div>
 | 
			
		||||
    <div class="abs holder">
 | 
			
		||||
        <a href=""
 | 
			
		||||
           ng-click="ngModel.cancel()"
 | 
			
		||||
        <a ng-click="ngModel.cancel()"
 | 
			
		||||
           ng-if="ngModel.cancel"
 | 
			
		||||
           class="clk-icon icon ui-symbol close">
 | 
			
		||||
            x
 | 
			
		||||
        </a>
 | 
			
		||||
        <div class="abs contents" ng-transclude>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
           class="clk-icon icon ui-symbol close">x</a>
 | 
			
		||||
        <div class="abs contents" ng-transclude></div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -55,7 +55,7 @@ define(
 | 
			
		||||
            this.dialogVisible = false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        DialogService.prototype.getDialogResponse = function (key, model, resultGetter) {
 | 
			
		||||
        DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
 | 
			
		||||
            // We will return this result as a promise, because user
 | 
			
		||||
            // input is asynchronous.
 | 
			
		||||
            var deferred = this.$q.defer(),
 | 
			
		||||
@@ -84,27 +84,20 @@ define(
 | 
			
		||||
            model.confirm = confirm;
 | 
			
		||||
            model.cancel = cancel;
 | 
			
		||||
 | 
			
		||||
            if (this.dialogVisible) {
 | 
			
		||||
                // Only one dialog should be shown at a time.
 | 
			
		||||
                // The application design should be such that
 | 
			
		||||
                // we never even try to do this.
 | 
			
		||||
                this.$log.warn([
 | 
			
		||||
                    "Dialog already showing; ",
 | 
			
		||||
                    "unable to show ",
 | 
			
		||||
                    model.name
 | 
			
		||||
                ].join(""));
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            } else {
 | 
			
		||||
            if (this.canShowDialog(model)) {
 | 
			
		||||
                // Add the overlay using the OverlayService, which
 | 
			
		||||
                // will handle actual insertion into the DOM
 | 
			
		||||
                this.overlay = this.overlayService.createOverlay(
 | 
			
		||||
                    key,
 | 
			
		||||
                    model
 | 
			
		||||
                    model,
 | 
			
		||||
                    typeClass || "t-dialog"
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                // Track that a dialog is already visible, to
 | 
			
		||||
                // avoid spawning multiple dialogs at once.
 | 
			
		||||
                this.dialogVisible = true;
 | 
			
		||||
            } else {
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return deferred.promise;
 | 
			
		||||
@@ -157,6 +150,99 @@ define(
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Tests if a dialog can be displayed. A modal dialog may only be
 | 
			
		||||
         * displayed if one is not already visible.
 | 
			
		||||
         * Will log a warning message if it can't display a dialog.
 | 
			
		||||
         * @returns {boolean} true if dialog is currently visible, false
 | 
			
		||||
         * otherwise
 | 
			
		||||
         */
 | 
			
		||||
        DialogService.prototype.canShowDialog = function(dialogModel){
 | 
			
		||||
            if (this.dialogVisible){
 | 
			
		||||
                // Only one dialog should be shown at a time.
 | 
			
		||||
                // The application design should be such that
 | 
			
		||||
                // we never even try to do this.
 | 
			
		||||
                this.$log.warn([
 | 
			
		||||
                    "Dialog already showing; ",
 | 
			
		||||
                    "unable to show ",
 | 
			
		||||
                    dialogModel.title
 | 
			
		||||
                ].join(""));
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
            } else {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A user action that can be performed from a blocking dialog. These
 | 
			
		||||
         * actions will be rendered as buttons within a blocking dialog.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef DialogOption
 | 
			
		||||
         * @property {string} label a label to be displayed as the button
 | 
			
		||||
         * text for this action
 | 
			
		||||
         * @property {function} action a function to be called when the
 | 
			
		||||
         * button is clicked
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A description of the model options that may be passed to the
 | 
			
		||||
         * showBlockingMessage method. Note that the DialogModel desribed
 | 
			
		||||
         * here is shared with the Notifications framework.
 | 
			
		||||
         * @see NotificationService
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef DialogModel
 | 
			
		||||
         * @property {string} title the title to use for the dialog
 | 
			
		||||
         * @property {string} severity the severity level of this message.
 | 
			
		||||
         * These are defined in a bundle constant with key 'dialogSeverity'
 | 
			
		||||
         * @property {string} hint the 'hint' message to show below the title
 | 
			
		||||
         * @property {string} actionText text that indicates a current action,
 | 
			
		||||
         * shown above a progress bar to indicate what's happening.
 | 
			
		||||
         * @property {number} progress a percentage value (1-100)
 | 
			
		||||
         * indicating the completion of the blocking task
 | 
			
		||||
         * @property {string} progressText the message to show below a
 | 
			
		||||
         * progress bar to indicate progress. For example, this might be
 | 
			
		||||
         * used to indicate time remaining, or items still to process.
 | 
			
		||||
         * @property {boolean} unknownProgress some tasks may be
 | 
			
		||||
         * impossible to provide an estimate for. Providing a true value for
 | 
			
		||||
         * this attribute will indicate to the user that the progress and
 | 
			
		||||
         * duration cannot be estimated.
 | 
			
		||||
         * @property {DialogOption} primaryOption an action that will
 | 
			
		||||
         * be added to the dialog as a button. The primary action can be
 | 
			
		||||
         * used as the suggested course of action for the user. Making it
 | 
			
		||||
         * distinct from other actions allows it to be styled differently,
 | 
			
		||||
         * and treated preferentially in banner mode.
 | 
			
		||||
         * @property {DialogOption[]} options a list of actions that will
 | 
			
		||||
         * be added to the dialog as buttons.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Displays a blocking (modal) dialog. This dialog can be used for
 | 
			
		||||
         * displaying messages that require the user's
 | 
			
		||||
         * immediate attention. The message may include an indication of
 | 
			
		||||
         * progress, as well as a series of actions that
 | 
			
		||||
         * the user can take if necessary
 | 
			
		||||
         * @param {DialogModel} dialogModel defines options for the dialog
 | 
			
		||||
         * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
 | 
			
		||||
         * @returns {boolean}
 | 
			
		||||
         */
 | 
			
		||||
        DialogService.prototype.showBlockingMessage = function(dialogModel) {
 | 
			
		||||
            if (this.canShowDialog(dialogModel)) {
 | 
			
		||||
                // Add the overlay using the OverlayService, which
 | 
			
		||||
                // will handle actual insertion into the DOM
 | 
			
		||||
                this.overlay = this.overlayService.createOverlay(
 | 
			
		||||
                    "overlay-blocking-message",
 | 
			
		||||
                    dialogModel,
 | 
			
		||||
                    "t-dialog-sm"
 | 
			
		||||
                );
 | 
			
		||||
                // Track that a dialog is already visible, to
 | 
			
		||||
                // avoid spawning multiple dialogs at once.
 | 
			
		||||
                this.dialogVisible = true;
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return DialogService;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ define(
 | 
			
		||||
 | 
			
		||||
        // Template to inject into the DOM to show the dialog; really just points to
 | 
			
		||||
        // the a specific template that can be included via mct-include
 | 
			
		||||
        var TEMPLATE = '<mct-include ng-model="overlay" key="key"></mct-include>';
 | 
			
		||||
        var TEMPLATE = '<mct-include ng-model="overlay" key="key" ng-class="typeClass"></mct-include>';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -71,8 +71,11 @@ define(
 | 
			
		||||
         * @param {object} overlayModel the model to pass to the
 | 
			
		||||
         *        included overlay template (this will be passed
 | 
			
		||||
         *        in via ng-model)
 | 
			
		||||
         * @param {string} typeClass the element class to use in rendering
 | 
			
		||||
         *        the overlay. Can be specified to provide custom styling of
 | 
			
		||||
         *        overlays
 | 
			
		||||
         */
 | 
			
		||||
        OverlayService.prototype.createOverlay = function (key, overlayModel) {
 | 
			
		||||
        OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) {
 | 
			
		||||
            // Create a new scope for this overlay
 | 
			
		||||
            var scope = this.newScope(),
 | 
			
		||||
                element;
 | 
			
		||||
@@ -90,6 +93,7 @@ define(
 | 
			
		||||
            // Populate the scope; will be passed directly to the template
 | 
			
		||||
            scope.overlay = overlayModel;
 | 
			
		||||
            scope.key = key;
 | 
			
		||||
            scope.typeClass = typeClass || 't-dialog';
 | 
			
		||||
 | 
			
		||||
            // Create the overlay element and add it to the document's body
 | 
			
		||||
            element = this.$compile(TEMPLATE)(scope);
 | 
			
		||||
 
 | 
			
		||||
@@ -116,10 +116,22 @@ define(
 | 
			
		||||
                        dialog: dialogModel,
 | 
			
		||||
                        confirm: jasmine.any(Function),
 | 
			
		||||
                        cancel: jasmine.any(Function)
 | 
			
		||||
                    }
 | 
			
		||||
                    },
 | 
			
		||||
                    't-dialog'
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("invokes the overlay service with the correct parameters when" +
 | 
			
		||||
                " a blocking dialog is requested", function() {
 | 
			
		||||
                var dialogModel = {};
 | 
			
		||||
                expect(dialogService.showBlockingMessage(dialogModel)).toBe(true);
 | 
			
		||||
                expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
 | 
			
		||||
                    "overlay-blocking-message",
 | 
			
		||||
                    dialogModel,
 | 
			
		||||
                    "t-dialog-sm"
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								platform/commonUI/formats/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								platform/commonUI/formats/bundle.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Time services bundle",
 | 
			
		||||
    "description": "Defines interfaces and provides default implementations for handling different time systems.",
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "components": [
 | 
			
		||||
            {
 | 
			
		||||
                "provides": "formatService",
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "FormatProvider.js",
 | 
			
		||||
                "depends": [ "formats[]" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "formats": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "utc",
 | 
			
		||||
                "implementation": "UTCTimeFormat.js"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "constants": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DEFAULT_TIME_FORMAT",
 | 
			
		||||
                "value": "utc"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								platform/commonUI/formats/src/FormatProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								platform/commonUI/formats/src/FormatProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An object used to convert between numeric values and text values,
 | 
			
		||||
     * typically used to display these values to the user and to convert
 | 
			
		||||
     * user input to a numeric format, particularly for time formats.
 | 
			
		||||
     * @interface {Format}
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse text (typically user input) to a numeric value.
 | 
			
		||||
     * Behavior is undefined when the text cannot be parsed;
 | 
			
		||||
     * `validate` should be called first if the text may be invalid.
 | 
			
		||||
     * @method parse
 | 
			
		||||
     * @memberof Format#
 | 
			
		||||
     * @param {string} text the text to parse
 | 
			
		||||
     * @returns {number} the parsed numeric value
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine whether or not some text (typically user input) can
 | 
			
		||||
     * be parsed to a numeric value by this format.
 | 
			
		||||
     * @method validate
 | 
			
		||||
     * @memberof Format#
 | 
			
		||||
     * @param {string} text the text to parse
 | 
			
		||||
     * @returns {boolean} true if the text can be parsed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert a numeric value to a text value for display using
 | 
			
		||||
     * this format.
 | 
			
		||||
     * @method format
 | 
			
		||||
     * @memberof Format#
 | 
			
		||||
     * @param {number} value the numeric value to format
 | 
			
		||||
     * @returns {string} the text representation of the value
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Provides access to `Format` objects which can be used to
 | 
			
		||||
     * convert values between human-readable text and numeric
 | 
			
		||||
     * representations.
 | 
			
		||||
     * @interface FormatService
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Look up a format by its symbolic identifier.
 | 
			
		||||
     * @method getFormat
 | 
			
		||||
     * @memberof FormatService#
 | 
			
		||||
     * @param {string} key the identifier for this format
 | 
			
		||||
     * @returns {Format} the format
 | 
			
		||||
     * @throws {Error} errors when the requested format is unrecognized
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Provides formats from the `formats` extension category.
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @implements {FormatService}
 | 
			
		||||
     * @memberof platform/commonUI/formats
 | 
			
		||||
     * @param {Array.<function(new : Format)>} format constructors,
 | 
			
		||||
     *        from the `formats` extension category.
 | 
			
		||||
     */
 | 
			
		||||
    function FormatProvider(formats) {
 | 
			
		||||
        var formatMap = {};
 | 
			
		||||
 | 
			
		||||
        function addToMap(Format) {
 | 
			
		||||
            var key = Format.key;
 | 
			
		||||
            if (key && !formatMap[key]) {
 | 
			
		||||
                formatMap[key] = new Format();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        formats.forEach(addToMap);
 | 
			
		||||
        this.formatMap = formatMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FormatProvider.prototype.getFormat = function (key) {
 | 
			
		||||
        var format = this.formatMap[key];
 | 
			
		||||
        if (!format) {
 | 
			
		||||
            throw new Error("FormatProvider: No format found for " + key);
 | 
			
		||||
        }
 | 
			
		||||
        return format;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return FormatProvider;
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										63
									
								
								platform/commonUI/formats/src/UTCTimeFormat.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								platform/commonUI/formats/src/UTCTimeFormat.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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([
 | 
			
		||||
    'moment'
 | 
			
		||||
], function (
 | 
			
		||||
    moment
 | 
			
		||||
) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
    var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
 | 
			
		||||
        DATE_FORMATS = [
 | 
			
		||||
            DATE_FORMAT,
 | 
			
		||||
            "YYYY-MM-DD HH:mm",
 | 
			
		||||
            "YYYY-MM-DD"
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Formatter for UTC timestamps. Interprets numeric values as
 | 
			
		||||
     * milliseconds since the start of 1970.
 | 
			
		||||
     *
 | 
			
		||||
     * @implements {Format}
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @memberof platform/commonUI/formats
 | 
			
		||||
     */
 | 
			
		||||
    function UTCTimeFormat() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UTCTimeFormat.prototype.format = function (value) {
 | 
			
		||||
        return moment.utc(value).format(DATE_FORMAT);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    UTCTimeFormat.prototype.parse = function (text) {
 | 
			
		||||
        return moment.utc(text, DATE_FORMATS).valueOf();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    UTCTimeFormat.prototype.validate = function (text) {
 | 
			
		||||
        return moment.utc(text, DATE_FORMATS).isValid();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return UTCTimeFormat;
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										68
									
								
								platform/commonUI/formats/test/FormatProviderSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								platform/commonUI/formats/test/FormatProviderSpec.js
									
									
									
									
									
										Normal 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,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/FormatProvider'],
 | 
			
		||||
    function (FormatProvider) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var KEYS = [ 'a', 'b', 'c' ];
 | 
			
		||||
 | 
			
		||||
        describe("The FormatProvider", function () {
 | 
			
		||||
            var mockFormats,
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockFormatInstances,
 | 
			
		||||
                provider;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockFormatInstances = KEYS.map(function (k) {
 | 
			
		||||
                    return jasmine.createSpyObj(
 | 
			
		||||
                        'format-' + k,
 | 
			
		||||
                        [ 'parse', 'validate', 'format' ]
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
                // Return constructors
 | 
			
		||||
                mockFormats = KEYS.map(function (k, i) {
 | 
			
		||||
                    function MockFormat() { return mockFormatInstances[i]; }
 | 
			
		||||
                    MockFormat.key = k;
 | 
			
		||||
                    return MockFormat;
 | 
			
		||||
                });
 | 
			
		||||
                provider = new FormatProvider(mockFormats);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("looks up formats by key", function () {
 | 
			
		||||
                KEYS.forEach(function (k, i) {
 | 
			
		||||
                    expect(provider.getFormat(k))
 | 
			
		||||
                        .toEqual(mockFormatInstances[i]);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("throws an error about unknown formats", function () {
 | 
			
		||||
                expect(function () {
 | 
			
		||||
                    provider.getFormat('some-unknown-format');
 | 
			
		||||
                }).toThrow();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										56
									
								
								platform/commonUI/formats/test/UTCTimeFormatSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								platform/commonUI/formats/test/UTCTimeFormatSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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/UTCTimeFormat', 'moment'],
 | 
			
		||||
    function (UTCTimeFormat, moment) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("The UTCTimeFormat", function () {
 | 
			
		||||
            var format;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                format = new UTCTimeFormat();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("formats UTC timestamps", function () {
 | 
			
		||||
                var timestamp = 12345670000,
 | 
			
		||||
                    formatted = format.format(timestamp);
 | 
			
		||||
                expect(formatted).toEqual(jasmine.any(String));
 | 
			
		||||
                expect(moment.utc(formatted).valueOf()).toEqual(timestamp);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("validates time inputs", function () {
 | 
			
		||||
                expect(format.validate("1977-05-25 11:21:22")).toBe(true);
 | 
			
		||||
                expect(format.validate("garbage text")).toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("parses valid input", function () {
 | 
			
		||||
                var text = "1977-05-25 11:21:22",
 | 
			
		||||
                    parsed = format.parse(text);
 | 
			
		||||
                expect(parsed).toEqual(jasmine.any(Number));
 | 
			
		||||
                expect(parsed).toEqual(moment.utc(text).valueOf());
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										4
									
								
								platform/commonUI/formats/test/suite.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								platform/commonUI/formats/test/suite.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
[
 | 
			
		||||
    "FormatProvider",
 | 
			
		||||
    "UTCTimeFormat"
 | 
			
		||||
]
 | 
			
		||||
@@ -44,6 +44,14 @@
 | 
			
		||||
                "key": "indicator",
 | 
			
		||||
                "templateUrl": "templates/indicator.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "message-banner",
 | 
			
		||||
                "templateUrl": "templates/message-banner.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "progress-bar",
 | 
			
		||||
                "templateUrl": "templates/progress-bar.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              "key": "time-controller",
 | 
			
		||||
              "templateUrl": "templates/controls/time-controller.html"
 | 
			
		||||
@@ -53,13 +61,18 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "TimeRangeController",
 | 
			
		||||
                "implementation": "controllers/TimeRangeController.js",
 | 
			
		||||
                "depends": [ "$scope", "now" ]
 | 
			
		||||
                "depends": [ "$scope", "formatService", "DEFAULT_TIME_FORMAT", "now" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DateTimePickerController",
 | 
			
		||||
                "implementation": "controllers/DateTimePickerController.js",
 | 
			
		||||
                "depends": [ "$scope", "now" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DateTimeFieldController",
 | 
			
		||||
                "implementation": "controllers/DateTimeFieldController.js",
 | 
			
		||||
                "depends": [ "$scope", "formatService", "DEFAULT_TIME_FORMAT" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "TreeNodeController",
 | 
			
		||||
                "implementation": "controllers/TreeNodeController.js",
 | 
			
		||||
@@ -112,6 +125,11 @@
 | 
			
		||||
                "key": "ObjectInspectorController",
 | 
			
		||||
                "implementation": "controllers/ObjectInspectorController.js",
 | 
			
		||||
                "depends": [ "$scope", "objectService" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "BannerController",
 | 
			
		||||
                "implementation": "controllers/BannerController.js",
 | 
			
		||||
                "depends": ["$scope", "notificationService", "dialogService"]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "directives": [
 | 
			
		||||
@@ -251,6 +269,10 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "datetime-picker",
 | 
			
		||||
                "templateUrl": "templates/controls/datetime-picker.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "datetime-field",
 | 
			
		||||
                "templateUrl": "templates/controls/datetime-field.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "licenses": [
 | 
			
		||||
 
 | 
			
		||||
@@ -51,8 +51,8 @@ $ueEditLeftPaneW: 75%;
 | 
			
		||||
$treeSearchInputBarH: 25px;
 | 
			
		||||
$ueTimeControlH: (33px, 20px, 20px);
 | 
			
		||||
// Overlay
 | 
			
		||||
$ovrTopBarH: 60px;
 | 
			
		||||
$ovrFooterH: 30px;
 | 
			
		||||
$ovrTopBarH: 45px;
 | 
			
		||||
$ovrFooterH: 24px;
 | 
			
		||||
$overlayMargin: 25px;
 | 
			
		||||
// Items
 | 
			
		||||
$ueBrowseGridItemLg: 200px;
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,6 @@
 | 
			
		||||
@import "helpers/bubbles";
 | 
			
		||||
@import "helpers/splitter";
 | 
			
		||||
@import "helpers/wait-spinner";
 | 
			
		||||
@import "messages";
 | 
			
		||||
@import "inspector";
 | 
			
		||||
 | 
			
		||||
/********************************* CONTROLS */
 | 
			
		||||
@@ -39,6 +38,7 @@
 | 
			
		||||
@import "controls/controls";
 | 
			
		||||
@import "controls/lists";
 | 
			
		||||
@import "controls/menus";
 | 
			
		||||
@import "controls/messages";
 | 
			
		||||
@import "controls/time-controller";
 | 
			
		||||
@import "mobile/controls/menus";
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +62,6 @@
 | 
			
		||||
@import "mobile/tree";
 | 
			
		||||
@import "user-environ/frame";
 | 
			
		||||
@import "user-environ/top-bar";
 | 
			
		||||
@import "user-environ/bottom-bar";
 | 
			
		||||
@import "user-environ/tool-bar";
 | 
			
		||||
 | 
			
		||||
/********************************* VIEWS */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
/* Styles for messages */
 | 
			
		||||
 | 
			
		||||
.message {
 | 
			
		||||
	&.block {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
		padding: $interiorMarginLg;
 | 
			
		||||
	}
 | 
			
		||||
	&.error {
 | 
			
		||||
		background-color: rgba($colorAlert,0.3);
 | 
			
		||||
		color: lighten($colorAlert, 20%);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -99,7 +99,6 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) {
 | 
			
		||||
    //$size: $size*2;
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    $slopedB: $size/$ratio solid transparent;
 | 
			
		||||
@@ -134,6 +133,24 @@
 | 
			
		||||
    background-size: $d $d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) {
 | 
			
		||||
    @include background-image(linear-gradient(-90deg,
 | 
			
		||||
        rgba($c, $a) 0%, rgba($c, $a) 50%,
 | 
			
		||||
        transparent 50%, transparent 100%
 | 
			
		||||
    ));
 | 
			
		||||
    background-repeat: repeat;
 | 
			
		||||
    background-size: $d $d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgVertFuzzyStripes($c: yellow, $a: 0.1, $d: 40px) {
 | 
			
		||||
    @include background-image(linear-gradient(-90deg,
 | 
			
		||||
        rgba($c, $a) 0%, transparent 50%,
 | 
			
		||||
        transparent 50%, rgba($c, $a) 100%
 | 
			
		||||
    ));
 | 
			
		||||
    background-repeat: repeat;
 | 
			
		||||
    background-size: $d $d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgTicks($c: $colorBodyFg, $repeatDir: 'x') {
 | 
			
		||||
    $deg: 90deg;
 | 
			
		||||
    @if ($repeatDir != 'x') {
 | 
			
		||||
@@ -409,4 +426,4 @@
 | 
			
		||||
@mixin s-stale($a: 0.5) {
 | 
			
		||||
    color: rgba($colorTelemFresh, $a) !important;
 | 
			
		||||
    font-style: italic;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,34 +19,6 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*.control {
 | 
			
		||||
	// UNUSED?
 | 
			
		||||
	&.view-control {
 | 
			
		||||
		.icon {
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			margin: -1px 5px 1px 2px;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
			&.triangle-down {
 | 
			
		||||
				margin: 2px 2px -2px 0px;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.label {
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			font-size: 11px;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.toggle {
 | 
			
		||||
			@include border-radius(3px);
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			padding: 1px 6px 4px 4px;
 | 
			
		||||
			&:hover {
 | 
			
		||||
				background: rgba(white, 0.1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
.accordion {
 | 
			
		||||
	$accordionHeadH: 18px;
 | 
			
		||||
@@ -291,6 +263,88 @@ label.checkbox.custom {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** PROGRESS BAR */
 | 
			
		||||
@include keyframes(progress) {
 | 
			
		||||
    100% { background-position: $progressBarStripeW center; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgProgressAnim($c: yellow, $a: 0.1, $d: 20px) {
 | 
			
		||||
    @include background-image(linear-gradient(-90deg,
 | 
			
		||||
        rgba($c, $a) 0%, transparent 50%,
 | 
			
		||||
        transparent 50%, rgba($c, $a) 100%
 | 
			
		||||
    ));
 | 
			
		||||
    background-position: 0 center;
 | 
			
		||||
    background-repeat: repeat-x;
 | 
			
		||||
    background-size: $d 40%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-progress-bar {
 | 
			
		||||
    // Assume will be determinate by default
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	position: relative;
 | 
			
		||||
 | 
			
		||||
	.progress-amt-holder {
 | 
			
		||||
        @include absPosDefault(1px);
 | 
			
		||||
    }
 | 
			
		||||
    .progress-amt,
 | 
			
		||||
    .progress-amt:before,
 | 
			
		||||
    .progress-amt:after {
 | 
			
		||||
		@include absPosDefault();
 | 
			
		||||
        display: block;
 | 
			
		||||
        content: '';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    .progress-amt {
 | 
			
		||||
        right: auto; // Allow inline width to control }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.indeterminate {
 | 
			
		||||
        .progress-amt {
 | 
			
		||||
            width: 100% !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-progress-bar {
 | 
			
		||||
    @include border-radius($basicCr);
 | 
			
		||||
    @include boxIncised(0.3, 4px);
 | 
			
		||||
    background: $colorProgressBarOuter;
 | 
			
		||||
    //border:1px solid $colorProgressBarOuter;
 | 
			
		||||
	.progress-amt {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
		@include boxShdw();
 | 
			
		||||
		@include border-radius($basicCr - 1);
 | 
			
		||||
        @include trans-prop-nice(width);
 | 
			
		||||
        &:before {
 | 
			
		||||
            background-color: $colorProgressBarAmt;
 | 
			
		||||
        }
 | 
			
		||||
        &:after {
 | 
			
		||||
            // Sheen
 | 
			
		||||
            @include background-image(linear-gradient(
 | 
			
		||||
                    transparent 5%, rgba(#fff,0.25) 30%, transparent 100%
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    &:not(.indeterminate) {
 | 
			
		||||
        .progress-amt:before {
 | 
			
		||||
            // More subtle anim for determinate progress
 | 
			
		||||
            @include animation(progress .4s linear infinite);
 | 
			
		||||
            @include bgProgressAnim(#fff, 0.1, $progressBarStripeW);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.indeterminate .progress-amt {
 | 
			
		||||
        &:before {
 | 
			
		||||
            // More visible std diag stripe anim for indeterminate progress
 | 
			
		||||
            @include animation(progress .6s linear infinite);
 | 
			
		||||
            @include bgDiagonalStripes(#fff, 0.2, $progressBarStripeW);
 | 
			
		||||
        }
 | 
			
		||||
        &:after { display: none; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** SLIDERS */
 | 
			
		||||
 | 
			
		||||
.slider {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										306
									
								
								platform/commonUI/general/res/sass/controls/_messages.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								platform/commonUI/general/res/sass/controls/_messages.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,306 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
 | 
			
		||||
	$bgPb: 30%;
 | 
			
		||||
	$bgPbD: 10%;
 | 
			
		||||
	background-color: darken($bg, $bgPb);
 | 
			
		||||
	color: $fg;
 | 
			
		||||
	&:hover {
 | 
			
		||||
		background-color: darken($bg, $bgPb - $bgPbD);
 | 
			
		||||
	}
 | 
			
		||||
	.s-action {
 | 
			
		||||
		background-color: darken($bg, $bgPb + $bgPbD);
 | 
			
		||||
		&:hover {
 | 
			
		||||
			background-color: darken($bg, $bgPb);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status.block {
 | 
			
		||||
	color: $colorStatusDefault;
 | 
			
		||||
	cursor: default;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	margin-right: $interiorMargin;
 | 
			
		||||
	.status-indicator,
 | 
			
		||||
	.label,
 | 
			
		||||
	.count {
 | 
			
		||||
		//@include test(#00ff00);
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		vertical-align: top;
 | 
			
		||||
	}
 | 
			
		||||
	.status-indicator {
 | 
			
		||||
		margin-right: $interiorMarginSm;
 | 
			
		||||
	}
 | 
			
		||||
	&.ok .status-indicator,
 | 
			
		||||
    &.info .status-indicator {
 | 
			
		||||
		color: $colorStatusInfo;
 | 
			
		||||
	}
 | 
			
		||||
    &.alert .status-indicator,
 | 
			
		||||
	&.warning .status-indicator,
 | 
			
		||||
	&.caution .status-indicator {
 | 
			
		||||
		color: $colorStatusAlert;
 | 
			
		||||
	}
 | 
			
		||||
	&.error .status-indicator {
 | 
			
		||||
		color: $colorStatusError;
 | 
			
		||||
	}
 | 
			
		||||
	.label {
 | 
			
		||||
		// Max-width silliness is necessary for width transition
 | 
			
		||||
		@include trans-prop-nice(max-width, .25s);
 | 
			
		||||
		overflow: hidden;
 | 
			
		||||
		max-width: 0px;
 | 
			
		||||
	}
 | 
			
		||||
	.count {
 | 
			
		||||
		@include trans-prop-nice(opacity, .25s);
 | 
			
		||||
		font-weight: bold;
 | 
			
		||||
		opacity: 1;
 | 
			
		||||
	}
 | 
			
		||||
	&:hover {
 | 
			
		||||
		.label {
 | 
			
		||||
			max-width: 450px;
 | 
			
		||||
			width: auto;
 | 
			
		||||
		}
 | 
			
		||||
		.count {
 | 
			
		||||
			opacity: 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Styles for messages and message banners */
 | 
			
		||||
.message {
 | 
			
		||||
	&.block {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
		padding: $interiorMarginLg;
 | 
			
		||||
	}
 | 
			
		||||
	&.error {
 | 
			
		||||
		background-color: rgba($colorAlert,0.3);
 | 
			
		||||
		color: lighten($colorAlert, 20%);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-message-banner {
 | 
			
		||||
	$m: $interiorMarginSm;
 | 
			
		||||
	$lh: $ueFooterH - ($m*2) - 1;
 | 
			
		||||
	@include box-sizing(border-box);
 | 
			
		||||
	@include ellipsize();
 | 
			
		||||
	@include display-flex;
 | 
			
		||||
	@include flex-direction(row);
 | 
			
		||||
	@include align-items(center);
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	top: $m; right: auto; bottom: $m; left: 50%;
 | 
			
		||||
	height: auto; width: auto;
 | 
			
		||||
	line-height: $lh;
 | 
			
		||||
	max-width: 300px;
 | 
			
		||||
	padding: 0 $interiorMargin 0 $interiorMargin;
 | 
			
		||||
	@include transform(translateX(-50%));
 | 
			
		||||
 | 
			
		||||
	&.minimized {
 | 
			
		||||
        @include transition-property(left, opacity);
 | 
			
		||||
        @include transition-duration(0.3s);
 | 
			
		||||
        @include transition-timing-function(ease-in-out);
 | 
			
		||||
		left: 0;
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    &.new {
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
        &:not(.info) {
 | 
			
		||||
            @include pulse(100ms, 10);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	.banner-elem {
 | 
			
		||||
		@include flex(0 1 auto);
 | 
			
		||||
		margin-left: $interiorMargin;
 | 
			
		||||
	}
 | 
			
		||||
	a {
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
	}
 | 
			
		||||
	.l-action {
 | 
			
		||||
		line-height: $lh - 3;
 | 
			
		||||
		padding: 0 $interiorMargin;
 | 
			
		||||
	}
 | 
			
		||||
    .close {
 | 
			
		||||
		//@include test(red, 0.7);
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
        font-size: 7px;
 | 
			
		||||
		width: 8px;
 | 
			
		||||
    }
 | 
			
		||||
	.l-progress-bar {
 | 
			
		||||
		$h: $lh - 10;
 | 
			
		||||
		height: $h;
 | 
			
		||||
		line-height: $h;
 | 
			
		||||
		width: 100px;
 | 
			
		||||
	}
 | 
			
		||||
    .progress-info { display: none; }
 | 
			
		||||
	z-index: 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-message-banner {
 | 
			
		||||
    //@include transition-property(left, opacity);
 | 
			
		||||
    //@include transition-duration(0.35s);
 | 
			
		||||
    //@include transition-timing-function(ease-in-out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-message-banner {
 | 
			
		||||
	@include border-radius($controlCr);
 | 
			
		||||
	@include statusBannerColors($colorStatusDefault, $colorStatusFg);
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	a { color: inherit; }
 | 
			
		||||
	.s-action {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
        @include trans-prop-nice(background-color);
 | 
			
		||||
	}
 | 
			
		||||
	.close {
 | 
			
		||||
		opacity: 0.5;
 | 
			
		||||
		&:hover {
 | 
			
		||||
			opacity: 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	&.ok,
 | 
			
		||||
    &.info {
 | 
			
		||||
		@include statusBannerColors($colorStatusInfo);
 | 
			
		||||
	}
 | 
			
		||||
	&.caution,
 | 
			
		||||
    &.warning,
 | 
			
		||||
    &.alert {
 | 
			
		||||
		@include statusBannerColors($colorStatusAlert);
 | 
			
		||||
	}
 | 
			
		||||
    &.error {
 | 
			
		||||
        @include statusBannerColors($colorStatusError);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin messageBlock($iconW: 32px) {
 | 
			
		||||
    .type-icon.message-type {
 | 
			
		||||
        @include txtShdw($shdwStatusIc);
 | 
			
		||||
        &:before { content:"\e608"; }
 | 
			
		||||
        color: $colorStatusDefault;
 | 
			
		||||
        font-size: $iconW;
 | 
			
		||||
        padding: 1px;
 | 
			
		||||
        width: $iconW + 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .message-severity-info .type-icon.message-type {
 | 
			
		||||
        &:before { content:"\e608"; }
 | 
			
		||||
        color: $colorStatusInfo;
 | 
			
		||||
    }
 | 
			
		||||
    .message-severity-alert .type-icon.message-type {
 | 
			
		||||
        &:before { content:"\e610"; }
 | 
			
		||||
        color: $colorStatusAlert;
 | 
			
		||||
    }
 | 
			
		||||
    .message-severity-error .type-icon.message-type {
 | 
			
		||||
        &:before { content:"\21"; }
 | 
			
		||||
        color: $colorStatusError;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/* Paths:
 | 
			
		||||
    t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message >
 | 
			
		||||
        message-type > (icon)
 | 
			
		||||
        message-contents >
 | 
			
		||||
        top-bar >
 | 
			
		||||
            title
 | 
			
		||||
            hint
 | 
			
		||||
        editor >
 | 
			
		||||
            (if displaying list of messages)
 | 
			
		||||
            ul > li > l-message >
 | 
			
		||||
                ... same as above
 | 
			
		||||
        bottom-bar
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
.l-message {
 | 
			
		||||
    @include display-flex;
 | 
			
		||||
    @include flex-direction(row);
 | 
			
		||||
    @include align-items(stretch);
 | 
			
		||||
    .type-icon.message-type {
 | 
			
		||||
        //@include test(red);
 | 
			
		||||
        @include flex(0 1 auto);
 | 
			
		||||
        position: relative;
 | 
			
		||||
    }
 | 
			
		||||
    .message-contents {
 | 
			
		||||
        //@include test(blue);
 | 
			
		||||
        @include flex(1 1 auto);
 | 
			
		||||
        margin-left: $overlayMargin;
 | 
			
		||||
        position: relative;
 | 
			
		||||
 | 
			
		||||
        .top-bar,
 | 
			
		||||
        .message-body {
 | 
			
		||||
            margin-bottom: $interiorMarginLg * 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Message as singleton
 | 
			
		||||
.t-message-single {
 | 
			
		||||
    @include messageBlock(80px);
 | 
			
		||||
 | 
			
		||||
    @include desktop {
 | 
			
		||||
        .l-message,
 | 
			
		||||
        .bottom-bar {
 | 
			
		||||
            @include absPosDefault();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .bottom-bar {
 | 
			
		||||
            top: auto;
 | 
			
		||||
            height: $ovrFooterH;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messages in list
 | 
			
		||||
.t-message-list {
 | 
			
		||||
    @include messageBlock(32px);
 | 
			
		||||
 | 
			
		||||
    .message-contents {
 | 
			
		||||
        .l-message {
 | 
			
		||||
            //border-bottom: 1px solid pullForward($colorOvrBg, 20%);
 | 
			
		||||
            @include border-radius($controlCr);
 | 
			
		||||
            background: rgba($colorOvrFg, 0.1);
 | 
			
		||||
            margin-bottom: $interiorMargin;
 | 
			
		||||
            padding: $interiorMarginLg;
 | 
			
		||||
 | 
			
		||||
            .message-contents,
 | 
			
		||||
            .bottom-bar {
 | 
			
		||||
                //@include test(green);
 | 
			
		||||
                position: relative;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .message-contents {
 | 
			
		||||
                font-size: 0.9em;
 | 
			
		||||
                margin-left: $interiorMarginLg;
 | 
			
		||||
                .message-action { color: pushBack($colorOvrFg, 20%); }
 | 
			
		||||
                .bottom-bar { text-align: left; }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .top-bar,
 | 
			
		||||
            .message-body {
 | 
			
		||||
                margin-bottom: $interiorMarginLg;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @include desktop {
 | 
			
		||||
        .message-contents .l-message { margin-right: $interiorMarginLg; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
 | 
			
		||||
$mobileListIconSize: 30px;
 | 
			
		||||
$mobileTitleDescH: 35px;
 | 
			
		||||
$mobileOverlayMargin: 10px;
 | 
			
		||||
$mobileOverlayMargin: 20px;
 | 
			
		||||
$phoneItemH: floor($ueBrowseGridItemLg/4);
 | 
			
		||||
$tabletItemH: floor($ueBrowseGridItemLg/3);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,12 @@
 | 
			
		||||
@include phoneandtablet {
 | 
			
		||||
    .overlay {
 | 
			
		||||
        $m: 0;
 | 
			
		||||
        .clk-icon.close {
 | 
			
		||||
            top: $mobileOverlayMargin; right: $mobileOverlayMargin;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        > .holder {
 | 
			
		||||
            @include border-radius($m);
 | 
			
		||||
            top: $m;
 | 
			
		||||
            right: $m;
 | 
			
		||||
            bottom: $m;
 | 
			
		||||
            left: $m;
 | 
			
		||||
            height: 90%; width: 90%;
 | 
			
		||||
 | 
			
		||||
            > .contents {
 | 
			
		||||
                top: $mobileOverlayMargin;
 | 
			
		||||
                right: $mobileOverlayMargin;
 | 
			
		||||
@@ -22,35 +18,64 @@
 | 
			
		||||
                        margin-right: 1.2em;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                .form.editor {
 | 
			
		||||
                    border: none;
 | 
			
		||||
 | 
			
		||||
                    .contents {
 | 
			
		||||
                        top: 0;
 | 
			
		||||
                        right: 0;
 | 
			
		||||
                        bottom: 0;
 | 
			
		||||
                        left: 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@include phone {
 | 
			
		||||
    .overlay > .holder > .contents .form.editor .contents .form-row {
 | 
			
		||||
        > .label,
 | 
			
		||||
        > .controls {
 | 
			
		||||
            //@include test(blue);
 | 
			
		||||
            display: block;
 | 
			
		||||
            float: none;
 | 
			
		||||
            width: 100%;
 | 
			
		||||
    .overlay > .holder {
 | 
			
		||||
        //@include test(orange); // This works!
 | 
			
		||||
        $m: 0;
 | 
			
		||||
        @include border-radius($m);
 | 
			
		||||
        top: $m;
 | 
			
		||||
        right: $m;
 | 
			
		||||
        bottom: $m;
 | 
			
		||||
        left: $m;
 | 
			
		||||
        height: auto; width: auto;
 | 
			
		||||
        min-width: 200px; min-height: 200px;
 | 
			
		||||
        max-height: 100%; max-width: 100%;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        @include transform(none);
 | 
			
		||||
 | 
			
		||||
        .editor .form .form-row {
 | 
			
		||||
            > .label,
 | 
			
		||||
            > .controls {
 | 
			
		||||
                //@include test(blue);
 | 
			
		||||
                display: block;
 | 
			
		||||
                float: none;
 | 
			
		||||
                width: 100%;
 | 
			
		||||
            }
 | 
			
		||||
            > .label {
 | 
			
		||||
                &:after {
 | 
			
		||||
                    float: none;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .contents {
 | 
			
		||||
            .abs.top-bar,
 | 
			
		||||
            .abs.editor,
 | 
			
		||||
            .abs.message-body,
 | 
			
		||||
            .abs.bottom-bar {
 | 
			
		||||
                //@include test(orange);
 | 
			
		||||
                top: auto; right: auto; bottom: auto; left: auto;
 | 
			
		||||
                height: auto; width: auto;
 | 
			
		||||
                margin-bottom: $interiorMarginLg * 2;
 | 
			
		||||
                position: relative;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .t-dialog-sm .overlay > .holder {
 | 
			
		||||
        //@include test(blue);
 | 
			
		||||
        height: auto; max-height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@include phonePortrait {
 | 
			
		||||
    .overlay > .holder {
 | 
			
		||||
        .contents .bottom-bar {
 | 
			
		||||
            text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
	    > .label {
 | 
			
		||||
		    &:after {
 | 
			
		||||
			    float: none;
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,79 +20,124 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
.overlay {
 | 
			
		||||
	.blocker {
 | 
			
		||||
		background: $colorOvrBlocker;
 | 
			
		||||
		z-index: 100;
 | 
			
		||||
	}
 | 
			
		||||
    font-size: 90%;
 | 
			
		||||
    .blocker {
 | 
			
		||||
        background: $colorOvrBlocker;
 | 
			
		||||
        z-index: 100;
 | 
			
		||||
    }
 | 
			
		||||
    .clk-icon.close {
 | 
			
		||||
        font-size: 0.8rem;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: $interiorMarginLg; right: $interiorMarginLg; bottom: auto; left: auto;
 | 
			
		||||
	    z-index: 100;
 | 
			
		||||
        top: $interiorMarginLg;
 | 
			
		||||
        right: $interiorMarginLg;
 | 
			
		||||
        bottom: auto;
 | 
			
		||||
        left: auto;
 | 
			
		||||
        z-index: 100;
 | 
			
		||||
    }
 | 
			
		||||
	>.holder {
 | 
			
		||||
		$i: 15%;
 | 
			
		||||
		@include containerSubtle($colorOvrBg, $colorOvrFg);
 | 
			
		||||
		@include border-radius($basicCr * 3);
 | 
			
		||||
		color: $colorOvrFg;
 | 
			
		||||
		top: $i; right: $i; bottom: $i; left: $i;
 | 
			
		||||
		z-index: 101;
 | 
			
		||||
        >.contents {
 | 
			
		||||
    > .holder {
 | 
			
		||||
        //$i: 15%;
 | 
			
		||||
        @include containerSubtle($colorOvrBg, $colorOvrFg);
 | 
			
		||||
        @include border-radius($basicCr * 3);
 | 
			
		||||
        color: $colorOvrFg;
 | 
			
		||||
        top: 50%;
 | 
			
		||||
        right: auto;
 | 
			
		||||
        bottom: auto;
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        @include transform(translateX(-50%) translateY(-50%));
 | 
			
		||||
        height: 70%;
 | 
			
		||||
        width: 50%;
 | 
			
		||||
        min-height: 300px;
 | 
			
		||||
        max-height: 800px;
 | 
			
		||||
        min-width: 600px;
 | 
			
		||||
        max-width: 1000px;
 | 
			
		||||
        z-index: 101;
 | 
			
		||||
        > .contents {
 | 
			
		||||
            $m: $overlayMargin;
 | 
			
		||||
            top: $m; right: $m; bottom: $m; left: $m;
 | 
			
		||||
            top: $m;
 | 
			
		||||
            right: $m;
 | 
			
		||||
            bottom: $m;
 | 
			
		||||
            left: $m;
 | 
			
		||||
 | 
			
		||||
            //.top-bar,
 | 
			
		||||
            //.editor,
 | 
			
		||||
            //.bottom-bar {
 | 
			
		||||
            //    @include absPosDefault();
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
    .title {
 | 
			
		||||
	    @include ellipsize();
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
	    margin-bottom: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .top-bar {
 | 
			
		||||
 | 
			
		||||
    .title {
 | 
			
		||||
        @include ellipsize();
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
        line-height: 120%;
 | 
			
		||||
        margin-bottom: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .hint {
 | 
			
		||||
        color: pushBack($colorOvrFg, 20%);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .abs.top-bar {
 | 
			
		||||
        height: $ovrTopBarH;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .editor {
 | 
			
		||||
        top: $ovrTopBarH + ($interiorMargin * 2);
 | 
			
		||||
        bottom: $ovrFooterH + $interiorMargin * 2;
 | 
			
		||||
        left: 0; right: 0;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .bottom-bar {
 | 
			
		||||
        top: auto; right: 0; bottom: 0; left: 0;
 | 
			
		||||
	    overflow: visible;
 | 
			
		||||
        //font-size: 1em;
 | 
			
		||||
        height: $ovrFooterH;
 | 
			
		||||
        text-align: right;
 | 
			
		||||
        .s-btn {
 | 
			
		||||
	        $bg: $colorOvrBtnBg;
 | 
			
		||||
	        &:not(.major) {
 | 
			
		||||
	            @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg);
 | 
			
		||||
	        }
 | 
			
		||||
	        font-size: 95%;
 | 
			
		||||
	        height: $ovrFooterH;
 | 
			
		||||
	        line-height: $ovrFooterH;
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
	        padding: 0 $interiorMargin * 3;
 | 
			
		||||
	        //&.major {
 | 
			
		||||
		     //   @extend .s-btn.major;
 | 
			
		||||
		     //   &:hover {
 | 
			
		||||
			 //       @extend .s-btn.major:hover;
 | 
			
		||||
		     //   }
 | 
			
		||||
	        //}
 | 
			
		||||
 | 
			
		||||
    .abs.editor,
 | 
			
		||||
    .abs.message-body {
 | 
			
		||||
        top: $ovrTopBarH + $interiorMarginLg;
 | 
			
		||||
        bottom: $ovrFooterH + $interiorMarginLg;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        .field.l-med {
 | 
			
		||||
            input[type='text'] {
 | 
			
		||||
                width: 100%;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .contents.l-dialog {
 | 
			
		||||
	    $myM: $interiorMargin;
 | 
			
		||||
	    top: $myM;
 | 
			
		||||
	    right: $myM;
 | 
			
		||||
	    bottom: $myM;
 | 
			
		||||
	    left: $myM;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
	    .field.l-med {
 | 
			
		||||
		    input[type='text'] {
 | 
			
		||||
			    width: 100%;
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
    .bottom-bar {
 | 
			
		||||
        text-align: right;
 | 
			
		||||
        .s-btn {
 | 
			
		||||
            $bg: $colorOvrBtnBg;
 | 
			
		||||
            &:not(.major) {
 | 
			
		||||
                @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg);
 | 
			
		||||
            }
 | 
			
		||||
            font-size: 95%;
 | 
			
		||||
            height: $ovrFooterH;
 | 
			
		||||
            line-height: $ovrFooterH;
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
            padding: 0 $interiorMargin * 3;
 | 
			
		||||
            &:first-child {
 | 
			
		||||
                margin-left: 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .abs.bottom-bar {
 | 
			
		||||
        top: auto;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        overflow: visible;
 | 
			
		||||
        //font-size: 1em;
 | 
			
		||||
        height: $ovrFooterH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-progress-bar {
 | 
			
		||||
        $h: $progressBarHOverlay;
 | 
			
		||||
        display: block;
 | 
			
		||||
        height: $h;
 | 
			
		||||
        line-height: $h;
 | 
			
		||||
        margin: .5em 0;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.t-dialog-sm .overlay > .holder {
 | 
			
		||||
    // Used for blocker and in-progress dialogs, modal alerts, etc.
 | 
			
		||||
    //@include test(red);
 | 
			
		||||
    $h: 225px;
 | 
			
		||||
    min-height: $h;
 | 
			
		||||
    height: $h;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
.ue-bottom-bar {
 | 
			
		||||
	background: $colorFooterBg;
 | 
			
		||||
	color: lighten($colorBodyBg, 30%);
 | 
			
		||||
	font-size: .7rem;
 | 
			
		||||
	//line-height: $ueFooterH - 4px;
 | 
			
		||||
	//line-height: $ueFooterH;   // New status bar design
 | 
			
		||||
	.status-holder {
 | 
			
		||||
		//@include border-radius($basicCr * 1.75);   // New status bar design
 | 
			
		||||
		@include box-sizing(border-box);
 | 
			
		||||
		//background: $colorFooterBg;
 | 
			
		||||
        //border-bottom: 1px solid lighten($colorBodyBg, 10%);  // New status bar design
 | 
			
		||||
		@include absPosDefault($interiorMargin);
 | 
			
		||||
		@include ellipsize();
 | 
			
		||||
		line-height: $ueFooterH - ($interiorMargin * 2);
 | 
			
		||||
		right: 120px;
 | 
			
		||||
		text-transform: uppercase;
 | 
			
		||||
	}
 | 
			
		||||
	.app-logo {
 | 
			
		||||
		@include box-sizing(border-box);
 | 
			
		||||
		@include absPosDefault($interiorMargin);
 | 
			
		||||
		left: auto;
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
		//font-size: 0.8em;
 | 
			
		||||
		//line-height: $ueFooterH - 10px;
 | 
			
		||||
		//padding-top: 1px;
 | 
			
		||||
		//text-transform: uppercase;
 | 
			
		||||
		&.logo-openmctweb {
 | 
			
		||||
			background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status.block {
 | 
			
		||||
	//display: inline-block;
 | 
			
		||||
	display: inline;  // New status bar design. Inline to support ellipsis overflow
 | 
			
		||||
	margin-right: $interiorMarginLg;
 | 
			
		||||
	.status-indicator {
 | 
			
		||||
		//@include border-radius($controlCr * 0.9);
 | 
			
		||||
		//@include box-shadow(inset rgba(black, 0.5) 0 0 3px);
 | 
			
		||||
		//@include text-shadow(rgba(black, 0.3) 0 0 2px);
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		margin-right: $interiorMarginSm;
 | 
			
		||||
		color: $colorKey;
 | 
			
		||||
		&.ok {
 | 
			
		||||
			color: #009900;
 | 
			
		||||
		}
 | 
			
		||||
		&.caution {
 | 
			
		||||
			color: #ffaa00;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -125,21 +125,37 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .ue-bottom-bar {
 | 
			
		||||
        //@include absPosDefault($bodyMargin);
 | 
			
		||||
        @include absPosDefault(0); // New status bar design
 | 
			
		||||
        top: auto;
 | 
			
		||||
        height: $ueFooterH;
 | 
			
		||||
        .status-holder {
 | 
			
		||||
            //right: $ueAppLogoW + $bodyMargin; New status bar design
 | 
			
		||||
            z-index: 1;
 | 
			
		||||
        }
 | 
			
		||||
        .app-logo {
 | 
			
		||||
            left: auto;
 | 
			
		||||
            width: $ueAppLogoW;
 | 
			
		||||
            z-index: 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	// from _bottom-bar.scss
 | 
			
		||||
	.ue-bottom-bar {
 | 
			
		||||
		@include absPosDefault(0);// New status bar design
 | 
			
		||||
		top: auto;
 | 
			
		||||
		height: $ueFooterH;
 | 
			
		||||
		line-height: $ueFooterH - ($interiorMargin * 2);
 | 
			
		||||
		background: $colorFooterBg;
 | 
			
		||||
		color: lighten($colorBodyBg, 30%);
 | 
			
		||||
		font-size: .7rem;
 | 
			
		||||
 | 
			
		||||
		.status-holder {
 | 
			
		||||
			@include box-sizing(border-box);
 | 
			
		||||
			@include absPosDefault($interiorMargin);
 | 
			
		||||
			@include ellipsize();
 | 
			
		||||
			//line-height: $ueFooterH - ($interiorMargin * 2);
 | 
			
		||||
			right: 120px;
 | 
			
		||||
			text-transform: uppercase;
 | 
			
		||||
			z-index: 1;
 | 
			
		||||
		}
 | 
			
		||||
		.app-logo {
 | 
			
		||||
			@include box-sizing(border-box);
 | 
			
		||||
			@include absPosDefault($interiorMargin);
 | 
			
		||||
			cursor: pointer;
 | 
			
		||||
			left: auto;
 | 
			
		||||
			width: $ueAppLogoW;
 | 
			
		||||
			z-index: 2;
 | 
			
		||||
			&.logo-openmctweb {
 | 
			
		||||
				background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.cols {
 | 
			
		||||
@@ -518,4 +534,4 @@
 | 
			
		||||
    .pane:not(.resizing) {
 | 
			
		||||
        @include trans-prop-nice-resize-w(250ms);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,5 +26,6 @@
 | 
			
		||||
                     key="indicator.template">
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </div>
 | 
			
		||||
    <mct-include key="'message-banner'"></mct-include>
 | 
			
		||||
    <mct-include key="'about-logo'"></mct-include>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<span class="s-btn"
 | 
			
		||||
      ng-controller="DateTimeFieldController">
 | 
			
		||||
    <input type="text"
 | 
			
		||||
           ng-model="textValue"
 | 
			
		||||
           ng-class="{ error: textInvalid }">
 | 
			
		||||
    </input>
 | 
			
		||||
    <a class="ui-symbol icon icon-calendar"
 | 
			
		||||
       ng-if="structure.format === 'utc' || !structure.format"
 | 
			
		||||
       ng-click="pickerActive = !pickerActive">
 | 
			
		||||
    </a>
 | 
			
		||||
    <mct-popup ng-if="pickerActive">
 | 
			
		||||
        <div mct-click-elsewhere="pickerActive = false">
 | 
			
		||||
            <mct-control key="'datetime-picker'"
 | 
			
		||||
                         ng-model="ngModel"
 | 
			
		||||
                         field="field"
 | 
			
		||||
                         options="{ hours: true }">
 | 
			
		||||
            </mct-control>
 | 
			
		||||
        </div>
 | 
			
		||||
    </mct-popup>
 | 
			
		||||
</span>
 | 
			
		||||
@@ -22,47 +22,24 @@
 | 
			
		||||
<div ng-controller="TimeRangeController">
 | 
			
		||||
    <div class="l-time-range-inputs-holder">
 | 
			
		||||
        <span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
 | 
			
		||||
        <span class="l-time-range-input" ng-controller="ToggleController as t1">
 | 
			
		||||
            <!--<span class="lbl">Start</span>-->
 | 
			
		||||
            <span class="s-btn time-range-start">
 | 
			
		||||
                <input type="text"
 | 
			
		||||
                       ng-model="boundsModel.start"
 | 
			
		||||
                       ng-class="{ error: !boundsModel.startValid }">
 | 
			
		||||
                </input>
 | 
			
		||||
                <a class="ui-symbol icon icon-calendar" ng-click="t1.toggle()"></a>
 | 
			
		||||
                <mct-popup ng-if="t1.isActive()">
 | 
			
		||||
                    <div mct-click-elsewhere="t1.setState(false)">
 | 
			
		||||
                        <mct-control key="'datetime-picker'"
 | 
			
		||||
                                     ng-model="ngModel.outer"
 | 
			
		||||
                                     field="'start'"
 | 
			
		||||
                                     options="{ hours: true }">
 | 
			
		||||
                        </mct-control>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </mct-popup>
 | 
			
		||||
            </span>
 | 
			
		||||
        <span class="l-time-range-input">
 | 
			
		||||
            <mct-control key="'datetime-field'"
 | 
			
		||||
                         structure="{ format: parameters.format }"
 | 
			
		||||
                         ng-model="ngModel.outer"
 | 
			
		||||
                         field="'start'"
 | 
			
		||||
                         class="time-range-start">
 | 
			
		||||
            </mct-control>
 | 
			
		||||
        </span>
 | 
			
		||||
 | 
			
		||||
        <span class="l-time-range-inputs-elem lbl">to</span>
 | 
			
		||||
 | 
			
		||||
        <span class="l-time-range-input" ng-controller="ToggleController as t2">
 | 
			
		||||
            <!--<span class="lbl">End</span>-->
 | 
			
		||||
            <span class="s-btn l-time-range-input">
 | 
			
		||||
                <input type="text"
 | 
			
		||||
                       ng-model="boundsModel.end"
 | 
			
		||||
                       ng-class="{ error: !boundsModel.endValid }">
 | 
			
		||||
                </input>
 | 
			
		||||
                <a class="ui-symbol icon icon-calendar" ng-click="t2.toggle()">
 | 
			
		||||
                </a>
 | 
			
		||||
                <mct-popup ng-if="t2.isActive()">
 | 
			
		||||
                    <div mct-click-elsewhere="t2.setState(false)">
 | 
			
		||||
                        <mct-control key="'datetime-picker'"
 | 
			
		||||
                                     ng-model="ngModel.outer"
 | 
			
		||||
                                     field="'end'"
 | 
			
		||||
                                     options="{ hours: true }">
 | 
			
		||||
                        </mct-control>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </mct-popup>
 | 
			
		||||
            </span> 
 | 
			
		||||
            <mct-control key="'datetime-field'"
 | 
			
		||||
                         structure="{ format: parameters.format }"
 | 
			
		||||
                         ng-model="ngModel.outer"
 | 
			
		||||
                         field="'end'"
 | 
			
		||||
                         class="time-range-end">
 | 
			
		||||
            </mct-control> 
 | 
			
		||||
        </span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +74,7 @@
 | 
			
		||||
    <div class="l-time-range-ticks-holder">
 | 
			
		||||
        <div class="l-time-range-ticks">
 | 
			
		||||
            <div
 | 
			
		||||
                ng-repeat="tick in ticks"
 | 
			
		||||
                ng-repeat="tick in ticks track by $index"
 | 
			
		||||
                ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
 | 
			
		||||
                class="tick tick-x"
 | 
			
		||||
                >
 | 
			
		||||
 
 | 
			
		||||
@@ -19,20 +19,21 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
 | 
			
		||||
<!--<div ng-init="reps = [1,2,3]"></div>-->
 | 
			
		||||
<div class='status block'
 | 
			
		||||
     title="{{ngModel.getDescription()}}"
 | 
			
		||||
     ng-click='ngModel.configure()'
 | 
			
		||||
     ng-class='ngModel.getClass()'
 | 
			
		||||
     ng-show="ngModel.getText().length > 0">
 | 
			
		||||
    <span class="ui-symbol status-indicator"
 | 
			
		||||
          ng-class='ngModel.getGlyphClass()'>
 | 
			
		||||
        {{ngModel.getGlyph()}}
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="label"
 | 
			
		||||
          ng-class='ngModel.getTextClass()'>
 | 
			
		||||
        {{ngModel.getText()}}
 | 
			
		||||
    </span>
 | 
			
		||||
	<a href=''
 | 
			
		||||
	 title="{{ngModel.getDescription()}}"
 | 
			
		||||
	 ng-click='ngModel.configure()'
 | 
			
		||||
	 ng-class='ngModel.getGlyphClass()'
 | 
			
		||||
	 ng-show="ngModel.getText().length > 0">
 | 
			
		||||
	<span class="ui-symbol status-indicator">
 | 
			
		||||
		{{ngModel.getGlyph()}}
 | 
			
		||||
	</span><span class="label"
 | 
			
		||||
		  ng-class='ngModel.getTextClass()'>
 | 
			
		||||
		{{ngModel.getText()}}
 | 
			
		||||
	</span><span class="count">
 | 
			
		||||
		<!-- Add int count value here if this type of indicator has one or more messages associated with it -->
 | 
			
		||||
	</span><a href=''
 | 
			
		||||
	   class="ui-symbol"
 | 
			
		||||
	   ng-if="ngModel.configure">
 | 
			
		||||
		G
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								platform/commonUI/general/res/templates/message-banner.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								platform/commonUI/general/res/templates/message-banner.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
<div ng-controller="BannerController" ng-show="active.notification"
 | 
			
		||||
     class="l-message-banner s-message-banner {{active.notification.model.severity}}" ng-class="{
 | 
			
		||||
     'minimized': active.notification.model.minimized,
 | 
			
		||||
     'new': !active.notification.model.minimized}"
 | 
			
		||||
     ng-click="maximize(active.notification)">
 | 
			
		||||
    <span class="banner-elem label">
 | 
			
		||||
        {{active.notification.model.title}}
 | 
			
		||||
    </span>
 | 
			
		||||
    <span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
 | 
			
		||||
        <mct-include key="'progress-bar'" class="banner-elem"
 | 
			
		||||
                     ng-model="active.notification.model">
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </span>
 | 
			
		||||
    <a ng-hide="active.notification.model.primaryOption === undefined"
 | 
			
		||||
       class="banner-elem l-action s-action"
 | 
			
		||||
       ng-click="action(active.notification.model.primaryOption.callback, $event)">
 | 
			
		||||
        {{active.notification.model.primaryOption.label}}
 | 
			
		||||
    </a>
 | 
			
		||||
    <a class="banner-elem ui-symbol close" ng-click="dismiss(active.notification, $event)">
 | 
			
		||||
        x</a>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								platform/commonUI/general/res/templates/progress-bar.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								platform/commonUI/general/res/templates/progress-bar.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
<span class="l-progress-bar s-progress-bar"
 | 
			
		||||
      ng-class="{ indeterminate:ngModel.unknownProgress }">
 | 
			
		||||
    <span class="progress-amt-holder">
 | 
			
		||||
        <span class="progress-amt" style="width: {{ngModel.progress}}%"></span>
 | 
			
		||||
    </span>
 | 
			
		||||
</span>
 | 
			
		||||
<div class="progress-info hint" ng-hide="ngModel.progressText === undefined">
 | 
			
		||||
    <span class="progress-amt-text" ng-show="ngModel.progress > 0">{{ngModel.progress}}% complete. </span>
 | 
			
		||||
    {{ngModel.progressText}}
 | 
			
		||||
</div>
 | 
			
		||||
@@ -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";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A controller for banner notifications. Banner notifications are a
 | 
			
		||||
         * non-blocking way of drawing the user's attention to an event such
 | 
			
		||||
         * as system errors, or the progress or successful completion of an
 | 
			
		||||
         * ongoing task. This controller provides scoped functions for
 | 
			
		||||
         * dismissing and 'maximizing' notifications. See {@link NotificationService}
 | 
			
		||||
         * for more details on Notifications.
 | 
			
		||||
         *
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @param dialogService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function BannerController($scope, notificationService, dialogService) {
 | 
			
		||||
            $scope.active = notificationService.active;
 | 
			
		||||
 | 
			
		||||
            $scope.action = function (action, $event){
 | 
			
		||||
                /*
 | 
			
		||||
                 Prevents default 'maximize' behaviour when clicking on
 | 
			
		||||
                  notification button
 | 
			
		||||
                 */
 | 
			
		||||
                $event.stopPropagation();
 | 
			
		||||
                return action();
 | 
			
		||||
            };
 | 
			
		||||
            $scope.dismiss = function(notification, $event) {
 | 
			
		||||
                $event.stopPropagation();
 | 
			
		||||
                notification.dismissOrMinimize();
 | 
			
		||||
            };
 | 
			
		||||
            $scope.maximize = function(notification) {
 | 
			
		||||
                if (notification.model.severity !== "info"){
 | 
			
		||||
 | 
			
		||||
                    notification.model.cancel = function(){
 | 
			
		||||
                        dialogService.dismiss();
 | 
			
		||||
                    };
 | 
			
		||||
                    dialogService.showBlockingMessage(notification.model);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        return BannerController;
 | 
			
		||||
    });
 | 
			
		||||
@@ -0,0 +1,79 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 to support the date-time entry field.
 | 
			
		||||
         *
 | 
			
		||||
         * Accepts a `format` property in the `structure` attribute
 | 
			
		||||
         * which allows a date/time to be specified via its symbolic
 | 
			
		||||
         * key (as will be used to look up said format from the
 | 
			
		||||
         * `formatService`.)
 | 
			
		||||
         *
 | 
			
		||||
         * {@see FormatService}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/general
 | 
			
		||||
         * @param $scope the Angular scope for this controller
 | 
			
		||||
         * @param {FormatService} formatService the service to user to format
 | 
			
		||||
         *        domain values
 | 
			
		||||
         * @param {string} defaultFormat the format to request when no
 | 
			
		||||
         *        format has been otherwise specified
 | 
			
		||||
         */
 | 
			
		||||
        function DateTimeFieldController($scope, formatService, defaultFormat) {
 | 
			
		||||
            var formatter = formatService.getFormat(defaultFormat);
 | 
			
		||||
 | 
			
		||||
            function updateFromModel(value) {
 | 
			
		||||
                // Only reformat if the value is different from user
 | 
			
		||||
                // input (to avoid reformatting valid input while typing.)
 | 
			
		||||
                if (!formatter.validate($scope.textValue) ||
 | 
			
		||||
                        formatter.parse($scope.textValue) !== value) {
 | 
			
		||||
                    $scope.textValue = formatter.format(value);
 | 
			
		||||
                    $scope.textInvalid = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateFromView(textValue) {
 | 
			
		||||
                $scope.textInvalid = !formatter.validate(textValue);
 | 
			
		||||
                if (!$scope.textInvalid) {
 | 
			
		||||
                    $scope.ngModel[$scope.field] =
 | 
			
		||||
                        formatter.parse(textValue);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function setFormat(format) {
 | 
			
		||||
                formatter = formatService.getFormat(format || defaultFormat);
 | 
			
		||||
                updateFromModel($scope.ngModel[$scope.field]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $scope.$watch('structure.format', setFormat);
 | 
			
		||||
            $scope.$watch('ngModel[field]', updateFromModel);
 | 
			
		||||
            $scope.$watch('textValue', updateFromView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DateTimeFieldController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -26,33 +26,32 @@ define(
 | 
			
		||||
    function (moment) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
 | 
			
		||||
            TICK_SPACING_PX = 150;
 | 
			
		||||
        var TICK_SPACING_PX = 150;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Controller used by the `time-controller` template.
 | 
			
		||||
         * @memberof platform/commonUI/general
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $scope the Angular scope for this controller
 | 
			
		||||
         * @param {FormatService} formatService the service to user to format
 | 
			
		||||
         *        domain values
 | 
			
		||||
         * @param {string} defaultFormat the format to request when no
 | 
			
		||||
         *        format has been otherwise specified
 | 
			
		||||
         * @param {Function} now a function to return current system time
 | 
			
		||||
         */
 | 
			
		||||
        function TimeConductorController($scope, now) {
 | 
			
		||||
        function TimeRangeController($scope, formatService, defaultFormat, now) {
 | 
			
		||||
            var tickCount = 2,
 | 
			
		||||
                innerMinimumSpan = 1000, // 1 second
 | 
			
		||||
                outerMinimumSpan = 1000 * 60 * 60, // 1 hour
 | 
			
		||||
                initialDragValue;
 | 
			
		||||
                initialDragValue,
 | 
			
		||||
                formatter = formatService.getFormat(defaultFormat);
 | 
			
		||||
 | 
			
		||||
            function formatTimestamp(ts) {
 | 
			
		||||
                return moment.utc(ts).format(DATE_FORMAT);
 | 
			
		||||
                return formatter.format(ts);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function parseTimestamp(text) {
 | 
			
		||||
                var m = moment.utc(text, DATE_FORMAT);
 | 
			
		||||
                if (m.isValid()) {
 | 
			
		||||
                    return m.valueOf();
 | 
			
		||||
                } else {
 | 
			
		||||
                    throw new Error("Could not parse " + text);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // From 0.0-1.0 to "0%"-"1%"
 | 
			
		||||
            // From 0.0-1.0 to "0%"-"100%"
 | 
			
		||||
            function toPercent(p) {
 | 
			
		||||
                return (100 * p) + "%";
 | 
			
		||||
            }
 | 
			
		||||
@@ -101,41 +100,15 @@ define(
 | 
			
		||||
                return { start: bounds.start, end: bounds.end };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateBoundsTextForProperty(ngModel, property) {
 | 
			
		||||
                try {
 | 
			
		||||
                    if (!$scope.boundsModel[property] ||
 | 
			
		||||
                            parseTimestamp($scope.boundsModel[property]) !==
 | 
			
		||||
                                ngModel.outer[property]) {
 | 
			
		||||
                        $scope.boundsModel[property] =
 | 
			
		||||
                            formatTimestamp(ngModel.outer[property]);
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    // User-entered text is invalid, so leave it be
 | 
			
		||||
                    // until they fix it.
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateBoundsText(ngModel) {
 | 
			
		||||
                updateBoundsTextForProperty(ngModel, 'start');
 | 
			
		||||
                updateBoundsTextForProperty(ngModel, 'end');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateViewFromModel(ngModel) {
 | 
			
		||||
                var t = now();
 | 
			
		||||
 | 
			
		||||
                ngModel = ngModel || {};
 | 
			
		||||
                ngModel.outer = ngModel.outer || defaultBounds();
 | 
			
		||||
                ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
 | 
			
		||||
 | 
			
		||||
                // First, dates for the date pickers for outer bounds
 | 
			
		||||
                updateBoundsText(ngModel);
 | 
			
		||||
 | 
			
		||||
                // Then various updates for the inner span
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
 | 
			
		||||
                // Stick it back is scope (in case we just set defaults)
 | 
			
		||||
                $scope.ngModel = ngModel;
 | 
			
		||||
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -155,7 +128,8 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function toMillis(pixels) {
 | 
			
		||||
                var span = $scope.ngModel.outer.end - $scope.ngModel.outer.start;
 | 
			
		||||
                var span =
 | 
			
		||||
                    $scope.ngModel.outer.end - $scope.ngModel.outer.start;
 | 
			
		||||
                return (pixels / $scope.spanWidth) * span;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -243,36 +217,10 @@ define(
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateStartFromText(value) {
 | 
			
		||||
                try {
 | 
			
		||||
                    updateOuterStart(parseTimestamp(value));
 | 
			
		||||
                    updateBoundsTextForProperty($scope.ngModel, 'end');
 | 
			
		||||
                    $scope.boundsModel.startValid = true;
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    $scope.boundsModel.startValid = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateEndFromText(value) {
 | 
			
		||||
                try {
 | 
			
		||||
                    updateOuterEnd(parseTimestamp(value));
 | 
			
		||||
                    updateBoundsTextForProperty($scope.ngModel, 'start');
 | 
			
		||||
                    $scope.boundsModel.endValid = true;
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    $scope.boundsModel.endValid = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateStartFromPicker(value) {
 | 
			
		||||
                updateOuterStart(value);
 | 
			
		||||
                updateBoundsText($scope.ngModel);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateEndFromPicker(value) {
 | 
			
		||||
                updateOuterEnd(value);
 | 
			
		||||
                updateBoundsText($scope.ngModel);
 | 
			
		||||
            function updateFormat(key) {
 | 
			
		||||
                formatter = formatService.getFormat(key || defaultFormat);
 | 
			
		||||
                updateViewForInnerSpanFromModel($scope.ngModel);
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $scope.startLeftDrag = startLeftDrag;
 | 
			
		||||
@@ -282,21 +230,18 @@ define(
 | 
			
		||||
            $scope.rightDrag = rightDrag;
 | 
			
		||||
            $scope.middleDrag = middleDrag;
 | 
			
		||||
 | 
			
		||||
            $scope.state = false;
 | 
			
		||||
            $scope.ticks = [];
 | 
			
		||||
            $scope.boundsModel = {};
 | 
			
		||||
 | 
			
		||||
            // Initialize scope to defaults
 | 
			
		||||
            updateViewFromModel($scope.ngModel);
 | 
			
		||||
 | 
			
		||||
            $scope.$watchCollection("ngModel", updateViewFromModel);
 | 
			
		||||
            $scope.$watch("spanWidth", updateSpanWidth);
 | 
			
		||||
            $scope.$watch("ngModel.outer.start", updateStartFromPicker);
 | 
			
		||||
            $scope.$watch("ngModel.outer.end", updateEndFromPicker);
 | 
			
		||||
            $scope.$watch("boundsModel.start", updateStartFromText);
 | 
			
		||||
            $scope.$watch("boundsModel.end", updateEndFromText);
 | 
			
		||||
            $scope.$watch("ngModel.outer.start", updateOuterStart);
 | 
			
		||||
            $scope.$watch("ngModel.outer.end", updateOuterEnd);
 | 
			
		||||
            $scope.$watch("parameters.format", updateFormat);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TimeConductorController;
 | 
			
		||||
        return TimeRangeController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -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,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/controllers/DateTimeFieldController", "moment"],
 | 
			
		||||
    function (DateTimeFieldController, moment) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var TEST_FORMAT = "YYYY-MM-DD HH:mm:ss";
 | 
			
		||||
 | 
			
		||||
        describe("The DateTimeFieldController", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                mockFormatService,
 | 
			
		||||
                mockFormat,
 | 
			
		||||
                controller;
 | 
			
		||||
 | 
			
		||||
            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', ['$watch']);
 | 
			
		||||
                mockFormatService =
 | 
			
		||||
                    jasmine.createSpyObj('formatService', ['getFormat']);
 | 
			
		||||
                mockFormat = jasmine.createSpyObj('format', [
 | 
			
		||||
                    'parse',
 | 
			
		||||
                    'validate',
 | 
			
		||||
                    'format'
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                mockFormatService.getFormat.andReturn(mockFormat);
 | 
			
		||||
 | 
			
		||||
                mockFormat.validate.andCallFake(function (text) {
 | 
			
		||||
                    return moment.utc(text, TEST_FORMAT).isValid();
 | 
			
		||||
                });
 | 
			
		||||
                mockFormat.parse.andCallFake(function (text) {
 | 
			
		||||
                    return moment.utc(text, TEST_FORMAT).valueOf();
 | 
			
		||||
                });
 | 
			
		||||
                mockFormat.format.andCallFake(function (value) {
 | 
			
		||||
                    return moment.utc(value).format(TEST_FORMAT);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockScope.ngModel = { testField: 12321 };
 | 
			
		||||
                mockScope.field = "testField";
 | 
			
		||||
                mockScope.structure = { format: "someFormat" };
 | 
			
		||||
 | 
			
		||||
                controller = new DateTimeFieldController(
 | 
			
		||||
                    mockScope,
 | 
			
		||||
                    mockFormatService
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates models from user-entered text", function () {
 | 
			
		||||
                var newText = "1977-05-25 17:30:00";
 | 
			
		||||
 | 
			
		||||
                mockScope.textValue = newText;
 | 
			
		||||
                fireWatch("textValue", newText);
 | 
			
		||||
                expect(mockScope.ngModel.testField)
 | 
			
		||||
                    .toEqual(mockFormat.parse(newText));
 | 
			
		||||
                expect(mockScope.textInvalid).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates text from model values", function () {
 | 
			
		||||
                var testTime = mockFormat.parse("1977-05-25 17:30:00");
 | 
			
		||||
                mockScope.ngModel.testField = testTime;
 | 
			
		||||
                fireWatch("ngModel[field]", testTime);
 | 
			
		||||
                expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when user input is invalid", function () {
 | 
			
		||||
                var newText, oldValue;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    newText = "Not a date";
 | 
			
		||||
                    oldValue = mockScope.ngModel.testField;
 | 
			
		||||
                    mockScope.textValue = newText;
 | 
			
		||||
                    fireWatch("textValue", newText);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("displays error state", function () {
 | 
			
		||||
                    expect(mockScope.textInvalid).toBeTruthy();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not modify model state", function () {
 | 
			
		||||
                    expect(mockScope.ngModel.testField).toEqual(oldValue);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not modify user input", function () {
 | 
			
		||||
                    expect(mockScope.textValue).toEqual(newText);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not modify valid but irregular user input", function () {
 | 
			
		||||
                // Don't want the controller "fixing" bad or
 | 
			
		||||
                // irregularly-formatted input out from under
 | 
			
		||||
                // the user's fingertips.
 | 
			
		||||
                var newText = "2015-3-3 01:02:04",
 | 
			
		||||
                    oldValue = mockScope.ngModel.testField;
 | 
			
		||||
 | 
			
		||||
                mockFormat.validate.andReturn(true);
 | 
			
		||||
                mockFormat.parse.andReturn(42);
 | 
			
		||||
                mockScope.textValue = newText;
 | 
			
		||||
                fireWatch("textValue", newText);
 | 
			
		||||
 | 
			
		||||
                expect(mockScope.textValue).toEqual(newText);
 | 
			
		||||
                expect(mockScope.ngModel.testField).toEqual(42);
 | 
			
		||||
                expect(mockScope.ngModel.testField).not.toEqual(oldValue);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("obtains a format from the format service", function () {
 | 
			
		||||
                fireWatch('structure.format', mockScope.structure.format);
 | 
			
		||||
                expect(mockFormatService.getFormat)
 | 
			
		||||
                    .toHaveBeenCalledWith(mockScope.structure.format);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("throws an error for unknown formats", function () {
 | 
			
		||||
                mockFormatService.getFormat.andReturn(undefined);
 | 
			
		||||
                expect(function () {
 | 
			
		||||
                    fireWatch("structure.format", "some-format");
 | 
			
		||||
                }).toThrow();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("using the obtained format", function () {
 | 
			
		||||
                var testValue = 1234321,
 | 
			
		||||
                    testText = "some text";
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockFormat.validate.andReturn(true);
 | 
			
		||||
                    mockFormat.parse.andReturn(testValue);
 | 
			
		||||
                    mockFormat.format.andReturn(testText);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("parses user input", function () {
 | 
			
		||||
                    var newText = "some other new text";
 | 
			
		||||
                    mockScope.textValue = newText;
 | 
			
		||||
                    fireWatch("textValue", newText);
 | 
			
		||||
                    expect(mockFormat.parse).toHaveBeenCalledWith(newText);
 | 
			
		||||
                    expect(mockScope.ngModel.testField).toEqual(testValue);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("validates user input", function () {
 | 
			
		||||
                    var newText = "some other new text";
 | 
			
		||||
                    mockScope.textValue = newText;
 | 
			
		||||
                    fireWatch("textValue", newText);
 | 
			
		||||
                    expect(mockFormat.validate).toHaveBeenCalledWith(newText);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("formats model data for display", function () {
 | 
			
		||||
                    var newValue = 42;
 | 
			
		||||
                    mockScope.ngModel.testField = newValue;
 | 
			
		||||
                    fireWatch("ngModel[field]", newValue);
 | 
			
		||||
                    expect(mockFormat.format).toHaveBeenCalledWith(newValue);
 | 
			
		||||
                    expect(mockScope.textValue).toEqual(testText);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -33,7 +33,10 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The TimeRangeController", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                mockFormatService,
 | 
			
		||||
                testDefaultFormat,
 | 
			
		||||
                mockNow,
 | 
			
		||||
                mockFormat,
 | 
			
		||||
                controller;
 | 
			
		||||
 | 
			
		||||
            function fireWatch(expr, value) {
 | 
			
		||||
@@ -57,8 +60,30 @@ define(
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    [ "$apply", "$watch", "$watchCollection" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockFormatService = jasmine.createSpyObj(
 | 
			
		||||
                    "formatService",
 | 
			
		||||
                    [ "getFormat" ]
 | 
			
		||||
                );
 | 
			
		||||
                testDefaultFormat = 'utc';
 | 
			
		||||
                mockFormat = jasmine.createSpyObj(
 | 
			
		||||
                    "format",
 | 
			
		||||
                    [ "validate", "format", "parse" ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockFormatService.getFormat.andReturn(mockFormat);
 | 
			
		||||
 | 
			
		||||
                mockFormat.format.andCallFake(function (value) {
 | 
			
		||||
                    return moment.utc(value).format("YYYY-MM-DD HH:mm:ss");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockNow = jasmine.createSpy('now');
 | 
			
		||||
                controller = new TimeRangeController(mockScope, mockNow);
 | 
			
		||||
 | 
			
		||||
                controller = new TimeRangeController(
 | 
			
		||||
                    mockScope,
 | 
			
		||||
                    mockFormatService,
 | 
			
		||||
                    testDefaultFormat,
 | 
			
		||||
                    mockNow
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches the model that was passed in", function () {
 | 
			
		||||
@@ -167,70 +192,22 @@ define(
 | 
			
		||||
                        .toBeGreaterThan(mockScope.ngModel.inner.start);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("by typing", function () {
 | 
			
		||||
                    it("updates models", function () {
 | 
			
		||||
                        var newStart = "1977-05-25 17:30:00",
 | 
			
		||||
                            newEnd = "2015-12-18 03:30:00";
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.start = newStart;
 | 
			
		||||
                        fireWatch("boundsModel.start", newStart);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.start)
 | 
			
		||||
                            .toEqual(moment.utc(newStart).valueOf());
 | 
			
		||||
                        expect(mockScope.boundsModel.startValid)
 | 
			
		||||
                            .toBeTruthy();
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.end = newEnd;
 | 
			
		||||
                        fireWatch("boundsModel.end", newEnd);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.end)
 | 
			
		||||
                            .toEqual(moment.utc(newEnd).valueOf());
 | 
			
		||||
                        expect(mockScope.boundsModel.endValid)
 | 
			
		||||
                            .toBeTruthy();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("displays error state", function () {
 | 
			
		||||
                        var newStart = "Not a date",
 | 
			
		||||
                            newEnd = "Definitely not a date",
 | 
			
		||||
                            oldStart = mockScope.ngModel.outer.start,
 | 
			
		||||
                            oldEnd = mockScope.ngModel.outer.end;
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.start = newStart;
 | 
			
		||||
                        fireWatch("boundsModel.start", newStart);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.start)
 | 
			
		||||
                            .toEqual(oldStart);
 | 
			
		||||
                        expect(mockScope.boundsModel.startValid)
 | 
			
		||||
                            .toBeFalsy();
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.end = newEnd;
 | 
			
		||||
                        fireWatch("boundsModel.end", newEnd);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.end)
 | 
			
		||||
                            .toEqual(oldEnd);
 | 
			
		||||
                        expect(mockScope.boundsModel.endValid)
 | 
			
		||||
                            .toBeFalsy();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("does not modify user input", function () {
 | 
			
		||||
                        // Don't want the controller "fixing" bad or
 | 
			
		||||
                        // irregularly-formatted input out from under
 | 
			
		||||
                        // the user's fingertips.
 | 
			
		||||
                        var newStart = "Not a date",
 | 
			
		||||
                            newEnd = "2015-3-3 01:02:04",
 | 
			
		||||
                            oldStart = mockScope.ngModel.outer.start,
 | 
			
		||||
                            oldEnd = mockScope.ngModel.outer.end;
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.start = newStart;
 | 
			
		||||
                        fireWatch("boundsModel.start", newStart);
 | 
			
		||||
                        expect(mockScope.boundsModel.start)
 | 
			
		||||
                            .toEqual(newStart);
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.end = newEnd;
 | 
			
		||||
                        fireWatch("boundsModel.end", newEnd);
 | 
			
		||||
                        expect(mockScope.boundsModel.end)
 | 
			
		||||
                            .toEqual(newEnd);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches for changes in format selection", function () {
 | 
			
		||||
                expect(mockFormatService.getFormat)
 | 
			
		||||
                    .not.toHaveBeenCalledWith('test-format');
 | 
			
		||||
                fireWatch("parameters.format", 'test-format');
 | 
			
		||||
                expect(mockFormatService.getFormat)
 | 
			
		||||
                    .toHaveBeenCalledWith('test-format');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("throws an error for unknown formats", function () {
 | 
			
		||||
                mockFormatService.getFormat.andReturn(undefined);
 | 
			
		||||
                expect(function () {
 | 
			
		||||
                    fireWatch("parameters.format", "some-format");
 | 
			
		||||
                }).toThrow();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
    "controllers/BottomBarController",
 | 
			
		||||
    "controllers/ClickAwayController",
 | 
			
		||||
    "controllers/ContextMenuController",
 | 
			
		||||
    "controllers/DateTimeFieldController",
 | 
			
		||||
    "controllers/DateTimePickerController",
 | 
			
		||||
    "controllers/GetterSetterController",
 | 
			
		||||
    "controllers/ObjectInspectorController",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								platform/commonUI/notification/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								platform/commonUI/notification/bundle.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "constants": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DEFAULT_AUTO_DISMISS",
 | 
			
		||||
                "value": 3000
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "FORCE_AUTO_DISMISS",
 | 
			
		||||
                "value": 1000
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "MINIMIZE_TIMEOUT",
 | 
			
		||||
                "value": 300
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "templates": [
 | 
			
		||||
            {
 | 
			
		||||
                "key":"notificationIndicatorTemplate",
 | 
			
		||||
                "templateUrl": "notification-indicator.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "NotificationIndicatorController",
 | 
			
		||||
                "implementation": "NotificationIndicatorController.js",
 | 
			
		||||
                "depends": ["$scope", "notificationService", "dialogService"]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "indicators": [
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "NotificationIndicator.js",
 | 
			
		||||
                "priority": "fallback"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "services": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "notificationService",
 | 
			
		||||
                "implementation": "NotificationService.js",
 | 
			
		||||
                "depends": [ "$timeout", "DEFAULT_AUTO_DISMISS",
 | 
			
		||||
                    "MINIMIZE_TIMEOUT" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
<span ng-show="notifications.length > 0" class="status block"
 | 
			
		||||
      ng-class="highest.severity"
 | 
			
		||||
      ng-controller="NotificationIndicatorController">
 | 
			
		||||
    <span class="ui-symbol status-indicator"></span>
 | 
			
		||||
    <span class="label">
 | 
			
		||||
        <a ng-click="showNotificationsList()">{{notifications.length}}
 | 
			
		||||
            Notifications</a>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="count">{{notifications.length}}</span>
 | 
			
		||||
</span>
 | 
			
		||||
							
								
								
									
										35
									
								
								platform/commonUI/notification/src/NotificationIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								platform/commonUI/notification/src/NotificationIndicator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        function NotificationIndicator() {}
 | 
			
		||||
 | 
			
		||||
        NotificationIndicator.template = 'notificationIndicatorTemplate';
 | 
			
		||||
 | 
			
		||||
        return NotificationIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,67 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides an indicator that is visible when there are
 | 
			
		||||
         * banner notifications that have been minimized. Will also indicate
 | 
			
		||||
         * the number of notifications. Notifications can be viewed by
 | 
			
		||||
         * clicking on the indicator to launch a dialog showing a list of
 | 
			
		||||
         * notifications.
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @param dialogService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function NotificationIndicatorController($scope, notificationService, dialogService) {
 | 
			
		||||
            $scope.notifications = notificationService.notifications;
 | 
			
		||||
            $scope.highest = notificationService.highest;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a dialog showing a list of current notifications.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.showNotificationsList = function(){
 | 
			
		||||
                dialogService.getDialogResponse('overlay-message-list', {
 | 
			
		||||
                    dialog: {
 | 
			
		||||
                        title: "Messages",
 | 
			
		||||
                        //Launch the message list dialog with the models
 | 
			
		||||
                        // from the notifications
 | 
			
		||||
                        messages: notificationService.notifications && notificationService.notifications.map(function(notification){
 | 
			
		||||
                            return notification.model;
 | 
			
		||||
                        })
 | 
			
		||||
                    },
 | 
			
		||||
                    cancel: function(){
 | 
			
		||||
                        dialogService.dismiss();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        return NotificationIndicatorController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										387
									
								
								platform/commonUI/notification/src/NotificationService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								platform/commonUI/notification/src/NotificationService.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,387 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This bundle implements the notification service, which can be used to
 | 
			
		||||
 * show banner notifications to the user. Banner notifications
 | 
			
		||||
 * are used to inform users of events in a non-intrusive way. As
 | 
			
		||||
 * much as possible, notifications share a model with blocking
 | 
			
		||||
 * dialogs so that the same information can be provided in a dialog
 | 
			
		||||
 * and then minimized to a banner notification if needed.
 | 
			
		||||
 *
 | 
			
		||||
 * @namespace platform/commonUI/notification
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A representation of a user action. Options are provided to
 | 
			
		||||
         * dialogs and notifications and are shown as buttons.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef {object} NotificationOption
 | 
			
		||||
         * @property {string} label the label to appear on the button for
 | 
			
		||||
         * this action
 | 
			
		||||
         * @property {function} callback a callback function to be invoked
 | 
			
		||||
         * when the button is clicked
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A representation of a banner notification. Banner notifications
 | 
			
		||||
         * are used to inform users of events in a non-intrusive way. As
 | 
			
		||||
         * much as possible, notifications share a model with blocking
 | 
			
		||||
         * dialogs so that the same information can be provided in a dialog
 | 
			
		||||
         * and then minimized to a banner notification if needed, or vice-versa.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef {object} NotificationModel
 | 
			
		||||
         * @property {string} title The title of the message
 | 
			
		||||
         * @property {string} severity The importance of the message (one of
 | 
			
		||||
         * 'info', 'alert', or 'error' where info < alert <error)
 | 
			
		||||
         * @property {number} progress The completion status of a task
 | 
			
		||||
         * represented numerically
 | 
			
		||||
         * @property {boolean} unknownProgress a boolean indicating that the
 | 
			
		||||
         * progress of the underlying task is unknown. This will result in a
 | 
			
		||||
         * visually distinct progress bar.
 | 
			
		||||
         * @property {boolean | number} autoDismiss If truthy, dialog will
 | 
			
		||||
         * be automatically minimized or dismissed (depending on severity).
 | 
			
		||||
         * Additionally, if the provided value is a number, it will be used
 | 
			
		||||
         * as the delay period before being dismissed.
 | 
			
		||||
         * @property {NotificationOption} primaryOption the default user
 | 
			
		||||
         * response to
 | 
			
		||||
         * this message. Will be represented as a button with the provided
 | 
			
		||||
         * label and action. May be used by banner notifications to display
 | 
			
		||||
         * only the most important option to users.
 | 
			
		||||
         * @property {NotificationOption[]} options any additional
 | 
			
		||||
         * actions the user can take. Will be represented as additional buttons
 | 
			
		||||
         * that may or may not be available from a banner.
 | 
			
		||||
         * @see DialogModel
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A wrapper object that is returned as a handle to a newly created
 | 
			
		||||
         * notification. Wraps the notifications model and decorates with
 | 
			
		||||
         * functions to dismiss or minimize the notification.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef {object} Notification
 | 
			
		||||
         * @property {function} dismiss This method is added to the object
 | 
			
		||||
         * returned by {@link NotificationService#notify} and can be used to
 | 
			
		||||
         * dismiss this notification. Dismissing a notification will remove
 | 
			
		||||
         * it completely and it will not appear in the notification indicator
 | 
			
		||||
         * @property {function} minimize This method is added to the object
 | 
			
		||||
         * returned by {@link NotificationService#notify} and can be used to
 | 
			
		||||
         * minimize this notification. Minimizing a notification will send
 | 
			
		||||
         * it to the notification indicator
 | 
			
		||||
         * @property {function} dismissOrMinimize This method is added to the
 | 
			
		||||
         * object returned by {@link NotificationService#notify}. It will
 | 
			
		||||
         * hide the notification by either dismissing or minimizing it,
 | 
			
		||||
         * depending on severity. Typically this is the method that should
 | 
			
		||||
         * be used for dismissing a notification. If more control is
 | 
			
		||||
         * required, then the minimize or dismiss functions can be called
 | 
			
		||||
         * individually.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The notification service is responsible for informing the user of
 | 
			
		||||
         * events via the use of banner notifications.
 | 
			
		||||
         * @memberof platform/commonUI/notification
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $timeout the Angular $timeout service
 | 
			
		||||
         * @param DEFAULT_AUTO_DISMISS The period of time that an
 | 
			
		||||
         * auto-dismissed message will be displayed for.
 | 
			
		||||
         * @param MINIMIZE_TIMEOUT When notifications are minimized, a brief
 | 
			
		||||
         * animation is shown. This animation requires some time to execute,
 | 
			
		||||
         * so a timeout is required before the notification is hidden
 | 
			
		||||
         */
 | 
			
		||||
        function NotificationService($timeout, DEFAULT_AUTO_DISMISS, MINIMIZE_TIMEOUT) {
 | 
			
		||||
            this.notifications = [];
 | 
			
		||||
            this.$timeout = $timeout;
 | 
			
		||||
            this.highest ={ severity: "info" };
 | 
			
		||||
            this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS;
 | 
			
		||||
            this.MINIMIZE_TIMEOUT = MINIMIZE_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * A context in which to hold the active notification and a
 | 
			
		||||
             * handle to its timeout.
 | 
			
		||||
             */
 | 
			
		||||
            this.active = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Minimize a notification. The notification will still be available
 | 
			
		||||
         * from the notification list. Typically notifications with a
 | 
			
		||||
         * severity of 'info' should not be minimized, but rather
 | 
			
		||||
         * dismissed. If you're not sure which is appropriate,
 | 
			
		||||
         * use {@link Notification#dismissOrMinimize}
 | 
			
		||||
         */
 | 
			
		||||
        function minimize (service, notification) {
 | 
			
		||||
            //Check this is a known notification
 | 
			
		||||
            var index = service.notifications.indexOf(notification);
 | 
			
		||||
 | 
			
		||||
            if (service.active.timeout){
 | 
			
		||||
                /*
 | 
			
		||||
                 Method can be called manually (clicking dismiss) or
 | 
			
		||||
                 automatically from an auto-timeout. this.active.timeout
 | 
			
		||||
                 acts as a semaphore to prevent race conditions. Cancel any
 | 
			
		||||
                 timeout in progress (for the case where a manual dismiss
 | 
			
		||||
                 has shortcut an active auto-dismiss), and clear the
 | 
			
		||||
                 semaphore.
 | 
			
		||||
                 */
 | 
			
		||||
                service.$timeout.cancel(service.active.timeout);
 | 
			
		||||
                delete service.active.timeout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (index >= 0) {
 | 
			
		||||
                notification.model.minimized=true;
 | 
			
		||||
                //Add a brief timeout before showing the next notification
 | 
			
		||||
                // in order to allow the minimize animation to run through.
 | 
			
		||||
                service.$timeout(function() {
 | 
			
		||||
                    service.setActiveNotification(service.selectNextNotification());
 | 
			
		||||
                }, service.MINIMIZE_TIMEOUT);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Completely removes a notification. This will dismiss it from the
 | 
			
		||||
         * message banner and remove it from the list of notifications.
 | 
			
		||||
         * Typically only notifications with a severity of info should be
 | 
			
		||||
         * dismissed. If you're not sure whether to dismiss or minimize a
 | 
			
		||||
         * notification, use {@link Notification#dismissOrMinimize}.
 | 
			
		||||
         * dismiss
 | 
			
		||||
         */
 | 
			
		||||
        function dismiss (service, notification) {
 | 
			
		||||
            //Check this is a known notification
 | 
			
		||||
            var index = service.notifications.indexOf(notification);
 | 
			
		||||
 | 
			
		||||
            if (service.active.timeout){
 | 
			
		||||
                /* Method can be called manually (clicking dismiss) or
 | 
			
		||||
                 * automatically from an auto-timeout. this.active.timeout
 | 
			
		||||
                 * acts as a semaphore to prevent race conditions. Cancel any
 | 
			
		||||
                 * timeout in progress (for the case where a manual dismiss
 | 
			
		||||
                 * has shortcut an active auto-dismiss), and clear the
 | 
			
		||||
                 * semaphore.
 | 
			
		||||
                 */
 | 
			
		||||
 | 
			
		||||
                service.$timeout.cancel(service.active.timeout);
 | 
			
		||||
                delete service.active.timeout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (index >= 0) {
 | 
			
		||||
                service.notifications.splice(index, 1);
 | 
			
		||||
            }
 | 
			
		||||
            service.setActiveNotification(service.selectNextNotification());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Depending on the severity of the notification will selectively
 | 
			
		||||
         * dismiss or minimize where appropriate.
 | 
			
		||||
         */
 | 
			
		||||
        function dismissOrMinimize (notification){
 | 
			
		||||
 | 
			
		||||
            //For now minimize everything, and have discussion around which
 | 
			
		||||
            //kind of messages should or should not be in the minimized
 | 
			
		||||
            //notifications list
 | 
			
		||||
            notification.minimize();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns the notification that is currently visible in the banner area
 | 
			
		||||
         * @returns {Notification}
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.getActiveNotification = function (){
 | 
			
		||||
            return this.active.notification;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A convenience method for info notifications. Notifications
 | 
			
		||||
         * created via this method will be auto-dismissed after a default
 | 
			
		||||
         * wait period
 | 
			
		||||
         * @param {NotificationModel | string} message either a string for
 | 
			
		||||
         * the title of the notification message, or a {@link NotificationModel}
 | 
			
		||||
         * defining the options notification to display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to dismiss or minimize
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.info = function (message) {
 | 
			
		||||
            var notificationModel = typeof message === "string" ? {title: message} : message;
 | 
			
		||||
            notificationModel.autoDismiss = notificationModel.autoDismiss || true;
 | 
			
		||||
            notificationModel.severity = "info";
 | 
			
		||||
            return this.notify(notificationModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A convenience method for alert notifications. Notifications
 | 
			
		||||
         * created via this method will will have severity of "alert" enforced
 | 
			
		||||
         * @param {NotificationModel | string} message either a string for
 | 
			
		||||
         * the title of the alert message with default options, or a
 | 
			
		||||
         * {@link NotificationModel} defining the options notification to
 | 
			
		||||
         * display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to dismiss or minimize
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.alert = function (message) {
 | 
			
		||||
            var notificationModel = typeof message === "string" ? {title: message} : message;
 | 
			
		||||
            notificationModel.severity = "alert";
 | 
			
		||||
            return this.notify(notificationModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A convenience method for error notifications. Notifications
 | 
			
		||||
         * created via this method will will have severity of "error" enforced
 | 
			
		||||
         * @param {NotificationModel | string} message either a string for
 | 
			
		||||
         * the title of the error message with default options, or a
 | 
			
		||||
         * {@link NotificationModel} defining the options notification to
 | 
			
		||||
         * display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to dismiss or minimize
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.error = function (message) {
 | 
			
		||||
            var notificationModel = typeof message === "string" ? {title: message} : message;
 | 
			
		||||
            notificationModel.severity = "error";
 | 
			
		||||
            return this.notify(notificationModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Notifies the user of an event. If there is a banner notification
 | 
			
		||||
         * already active, then it will be dismissed or minimized automatically,
 | 
			
		||||
         * and the provided notification displayed in its place.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {NotificationModel} notificationModel The notification to
 | 
			
		||||
         * display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to {@link Notification#dismiss} or {@link Notification#minimize}
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.notify = function (notificationModel) {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                notification,
 | 
			
		||||
                ordinality = {
 | 
			
		||||
                    "info": 1,
 | 
			
		||||
                    "alert": 2,
 | 
			
		||||
                    "error": 3
 | 
			
		||||
                },
 | 
			
		||||
                activeNotification = self.active.notification;
 | 
			
		||||
 | 
			
		||||
            notification = {
 | 
			
		||||
                model: notificationModel,
 | 
			
		||||
                minimize: function() {
 | 
			
		||||
                    minimize(self, notification);
 | 
			
		||||
                },
 | 
			
		||||
                dismiss: function(){
 | 
			
		||||
                    dismiss(self, notification);
 | 
			
		||||
                },
 | 
			
		||||
                dismissOrMinimize: function(){
 | 
			
		||||
                    dismissOrMinimize(notification);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            notificationModel.severity = notificationModel.severity || "info";
 | 
			
		||||
            if (notificationModel.autoDismiss === true){
 | 
			
		||||
                notificationModel.autoDismiss = this.DEFAULT_AUTO_DISMISS;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ordinality[notificationModel.severity.toLowerCase()] > ordinality[this.highest.severity.toLowerCase()]){
 | 
			
		||||
                this.highest.severity = notificationModel.severity;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.notifications.push(notification);
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Check if there is already an active (ie. visible) notification
 | 
			
		||||
             */
 | 
			
		||||
            if (!this.active.notification){
 | 
			
		||||
                this.setActiveNotification(notification);
 | 
			
		||||
 | 
			
		||||
            } else if (!this.active.timeout){
 | 
			
		||||
                /*
 | 
			
		||||
                 If there is already an active notification, time it out. If it's
 | 
			
		||||
                 already got a timeout in progress (either because it has had
 | 
			
		||||
                 timeout forced because of a queue of messages, or it had an
 | 
			
		||||
                 autodismiss specified), leave it to run. Otherwise force a
 | 
			
		||||
                  timeout.
 | 
			
		||||
 | 
			
		||||
                 This notifcation has been added to queue and will be
 | 
			
		||||
                  serviced as soon as possible.
 | 
			
		||||
                 */
 | 
			
		||||
                this.active.timeout = this.$timeout(function () {
 | 
			
		||||
                    activeNotification.dismissOrMinimize();
 | 
			
		||||
                }, this.DEFAULT_AUTO_DISMISS);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return notification;
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Used internally by the NotificationService
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.setActiveNotification =
 | 
			
		||||
            function (notification) {
 | 
			
		||||
 | 
			
		||||
                var self = this,
 | 
			
		||||
                    timeout;
 | 
			
		||||
                this.active.notification = notification;
 | 
			
		||||
                /*
 | 
			
		||||
                If autoDismiss has been specified, OR there are other
 | 
			
		||||
                 notifications queued for display, setup a timeout to
 | 
			
		||||
                  dismiss the dialog.
 | 
			
		||||
                 */
 | 
			
		||||
                if (notification && (notification.model.autoDismiss
 | 
			
		||||
                    || this.selectNextNotification())) {
 | 
			
		||||
 | 
			
		||||
                    timeout = notification.model.autoDismiss || this.DEFAULT_AUTO_DISMISS;
 | 
			
		||||
                    this.active.timeout = this.$timeout(function () {
 | 
			
		||||
                        notification.dismissOrMinimize();
 | 
			
		||||
                    }, timeout);
 | 
			
		||||
                } else {
 | 
			
		||||
                    delete this.active.timeout;
 | 
			
		||||
                }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Used internally by the NotificationService
 | 
			
		||||
         *
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.selectNextNotification = function () {
 | 
			
		||||
            var notification,
 | 
			
		||||
                i=0;
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Loop through the notifications queue and find the first one that
 | 
			
		||||
            has not already been minimized (manually or otherwise).
 | 
			
		||||
             */
 | 
			
		||||
            for (; i< this.notifications.length; i++) {
 | 
			
		||||
                notification = this.notifications[i];
 | 
			
		||||
 | 
			
		||||
                if (!notification.model.minimized
 | 
			
		||||
                    && notification!== this.active.notification) {
 | 
			
		||||
 | 
			
		||||
                    return notification;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return NotificationService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,78 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,waitsFor,jasmine */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/NotificationIndicatorController'],
 | 
			
		||||
    function (NotificationIndicatorController) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The notification indicator controller ", function () {
 | 
			
		||||
            var mockNotificationService,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockDialogService;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function(){
 | 
			
		||||
                mockNotificationService = jasmine.createSpy("notificationService");
 | 
			
		||||
                mockScope = jasmine.createSpy("$scope");
 | 
			
		||||
                mockDialogService = jasmine.createSpyObj(
 | 
			
		||||
                    "dialogService",
 | 
			
		||||
                    ["getDialogResponse","dismiss"]
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes the highest notification severity to the template", function() {
 | 
			
		||||
                mockNotificationService.highest = {
 | 
			
		||||
                    severity: "error"
 | 
			
		||||
                };
 | 
			
		||||
                var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
 | 
			
		||||
                expect(mockScope.highest).toBeTruthy();
 | 
			
		||||
                expect(mockScope.highest.severity).toBe("error");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("invokes the dialog service to show list of messages", function() {
 | 
			
		||||
                var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
 | 
			
		||||
                expect(mockScope.showNotificationsList).toBeDefined();
 | 
			
		||||
                mockScope.showNotificationsList();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list');
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].dialog).toBeDefined();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
 | 
			
		||||
                //Invoke the cancel callback
 | 
			
		||||
                mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
 | 
			
		||||
                expect(mockDialogService.dismiss).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("provides a means of dismissing the message list", function() {
 | 
			
		||||
                var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
 | 
			
		||||
                expect(mockScope.showNotificationsList).toBeDefined();
 | 
			
		||||
                mockScope.showNotificationsList();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
 | 
			
		||||
                //Invoke the cancel callback
 | 
			
		||||
                mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
 | 
			
		||||
                expect(mockDialogService.dismiss).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										219
									
								
								platform/commonUI/notification/test/NotificationServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								platform/commonUI/notification/test/NotificationServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,waitsFor,jasmine */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/NotificationService'],
 | 
			
		||||
    function (NotificationService) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The notification service ", function () {
 | 
			
		||||
            var notificationService,
 | 
			
		||||
                mockTimeout,
 | 
			
		||||
                mockAutoDismiss,
 | 
			
		||||
                mockMinimizeTimeout,
 | 
			
		||||
                successModel,
 | 
			
		||||
                errorModel;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function(){
 | 
			
		||||
                mockTimeout = jasmine.createSpy("$timeout");
 | 
			
		||||
                mockAutoDismiss = mockMinimizeTimeout = 1000;
 | 
			
		||||
                notificationService = new NotificationService(
 | 
			
		||||
                    mockTimeout, mockAutoDismiss, mockMinimizeTimeout);
 | 
			
		||||
                successModel = {
 | 
			
		||||
                    title: "Mock Success Notification",
 | 
			
		||||
                    severity: "info"
 | 
			
		||||
                };
 | 
			
		||||
                errorModel = {
 | 
			
		||||
                    title: "Mock Error Notification",
 | 
			
		||||
                    severity: "error"
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("gets a new success notification, making" +
 | 
			
		||||
                " the notification active", function() {
 | 
			
		||||
                var activeNotification;
 | 
			
		||||
                notificationService.notify(successModel);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows specification of an info notification given just a" +
 | 
			
		||||
                " title, making the notification active", function() {
 | 
			
		||||
                var activeNotification,
 | 
			
		||||
                    notificationTitle = "Test info notification";
 | 
			
		||||
                notificationService.info(notificationTitle);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model.title).toBe(notificationTitle);
 | 
			
		||||
                expect(activeNotification.model.severity).toBe("info");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("gets a new success notification with" +
 | 
			
		||||
                " numerical auto-dismiss specified. ", function() {
 | 
			
		||||
                var activeNotification;
 | 
			
		||||
                successModel.autoDismiss = 1000;
 | 
			
		||||
                notificationService.notify(successModel);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockTimeout.calls.length).toBe(2);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("gets a new notification with" +
 | 
			
		||||
                " boolean auto-dismiss specified. ", function() {
 | 
			
		||||
                var activeNotification;
 | 
			
		||||
                successModel.autoDismiss = true;
 | 
			
		||||
                notificationService.notify(successModel);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockTimeout.calls.length).toBe(2);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows minimization of notifications", function() {
 | 
			
		||||
                var notification,
 | 
			
		||||
                    activeNotification;
 | 
			
		||||
 | 
			
		||||
                successModel.autoDismiss = false;
 | 
			
		||||
                notification = notificationService.notify(successModel);
 | 
			
		||||
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                notification.minimize();
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
                expect(notificationService.notifications.length).toBe(1);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows dismissal of notifications", function() {
 | 
			
		||||
                var notification,
 | 
			
		||||
                    activeNotification;
 | 
			
		||||
 | 
			
		||||
                successModel.autoDismiss = false;
 | 
			
		||||
                notification = notificationService.notify(successModel);
 | 
			
		||||
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                notification.dismiss();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
                expect(notificationService.notifications.length).toBe(0);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe(" gets called with multiple notifications", function(){
 | 
			
		||||
                it("auto-dismisses the previously active notification, making" +
 | 
			
		||||
                    " the new notification active", function() {
 | 
			
		||||
                    var activeNotification;
 | 
			
		||||
                    //First pre-load with a info message
 | 
			
		||||
                    notificationService.notify(successModel);
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    //Initially expect the active notification to be info
 | 
			
		||||
                    expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                    //Then notify of an error
 | 
			
		||||
                    notificationService.notify(errorModel);
 | 
			
		||||
                    //But it should be auto-dismissed and replaced with the
 | 
			
		||||
                    // error notification
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(errorModel);
 | 
			
		||||
                });
 | 
			
		||||
                it("auto-minimizes an active error notification", function() {
 | 
			
		||||
                    var activeNotification;
 | 
			
		||||
                    //First pre-load with an error message
 | 
			
		||||
                    notificationService.notify(errorModel);
 | 
			
		||||
                    //Then notify of info
 | 
			
		||||
                    notificationService.notify(successModel);
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(2);
 | 
			
		||||
                    //Mock the auto-minimize
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Previous error message should be minimized, not
 | 
			
		||||
                    // dismissed
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(2);
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                    expect(errorModel.minimized).toEqual(true);
 | 
			
		||||
                });
 | 
			
		||||
                it("auto-minimizes errors when a number of them arrive in" +
 | 
			
		||||
                    " short succession ", function() {
 | 
			
		||||
                    var activeNotification,
 | 
			
		||||
                        error2 = {
 | 
			
		||||
                            title: "Second Mock Error Notification",
 | 
			
		||||
                            severity: "error"
 | 
			
		||||
                        },
 | 
			
		||||
                        error3 = {
 | 
			
		||||
                            title: "Third Mock Error Notification",
 | 
			
		||||
                            severity: "error"
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                    //First pre-load with a info message
 | 
			
		||||
                    notificationService.notify(errorModel);
 | 
			
		||||
                    //Then notify of a third error
 | 
			
		||||
                    notificationService.notify(error2);
 | 
			
		||||
                    notificationService.notify(error3);
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(3);
 | 
			
		||||
                    //Mock the auto-minimize
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Previous error message should be minimized, not
 | 
			
		||||
                    // dismissed
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(3);
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(error2);
 | 
			
		||||
                    expect(errorModel.minimized).toEqual(true);
 | 
			
		||||
 | 
			
		||||
                    //Mock the second auto-minimize
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(error3);
 | 
			
		||||
                    expect(error2.minimized).toEqual(true);
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										4
									
								
								platform/commonUI/notification/test/suite.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								platform/commonUI/notification/test/suite.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
[
 | 
			
		||||
    "NotificationService",
 | 
			
		||||
    "NotificationIndicatorController"
 | 
			
		||||
]
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -10,7 +10,7 @@ $colorInteriorBorder: rgba($colorBodyFg, 0.1);
 | 
			
		||||
$colorA: #ccc;
 | 
			
		||||
$colorAHov: #fff;
 | 
			
		||||
$contrastRatioPercent: 7%;
 | 
			
		||||
$basicCr: 2px;
 | 
			
		||||
$basicCr: 3px;
 | 
			
		||||
$controlCr: 3px;
 | 
			
		||||
$smallCr: 2px;
 | 
			
		||||
 | 
			
		||||
@@ -68,6 +68,8 @@ $colorFormRequired: $colorAlt1;
 | 
			
		||||
$colorFormValid: #33cc33;
 | 
			
		||||
$colorFormError: #cc0000;
 | 
			
		||||
$colorFormInvalid: #ff3300;
 | 
			
		||||
$colorFormLines: rgba(#fff, 0.1);
 | 
			
		||||
$colorFormSectionHeader: rgba(#000, 0.2);
 | 
			
		||||
$colorInputBg: rgba(#fff, 0.1);
 | 
			
		||||
$colorInputFg: pullForward($colorBodyFg, 20%);
 | 
			
		||||
$colorFormText: rgba(#fff, 0.5);
 | 
			
		||||
@@ -81,6 +83,18 @@ $colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
 | 
			
		||||
$colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%);
 | 
			
		||||
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
 | 
			
		||||
 | 
			
		||||
// Status colors, mainly used for messaging and item ancillary symbols
 | 
			
		||||
$colorStatusFg: #ccc;
 | 
			
		||||
$colorStatusDefault: #ccc;
 | 
			
		||||
$colorStatusInfo: #62ba72;
 | 
			
		||||
$colorStatusAlert: #ffa66d;
 | 
			
		||||
$colorStatusError: #d4585c;
 | 
			
		||||
$colorProgressBarOuter: rgba(#000, 0.1);
 | 
			
		||||
$colorProgressBarAmt: $colorKey;
 | 
			
		||||
$progressBarHOverlay: 15px;
 | 
			
		||||
$progressBarStripeW: 20px;
 | 
			
		||||
$shdwStatusIc: rgba(black, 0.4) 0 1px 2px;
 | 
			
		||||
 | 
			
		||||
// Selects
 | 
			
		||||
$colorSelectBg: $colorBtnBg;
 | 
			
		||||
$colorSelectFg: $colorBtnFg;
 | 
			
		||||
@@ -178,4 +192,4 @@ $colorAboutLink: #84b3ff;
 | 
			
		||||
 | 
			
		||||
// Loading
 | 
			
		||||
$colorLoadingBg: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$colorLoadingFg: $colorAlt1;
 | 
			
		||||
$colorLoadingFg: $colorAlt1;
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -72,6 +72,20 @@ $colorInputBg: $colorGenBg;
 | 
			
		||||
$colorInputFg: $colorBodyFg;
 | 
			
		||||
$colorFormText: pushBack($colorBodyFg, 10%);
 | 
			
		||||
$colorInputIcon: pushBack($colorBodyFg, 25%);
 | 
			
		||||
 | 
			
		||||
// Status colors, mainly used for messaging and item ancillary symbols
 | 
			
		||||
$colorStatusFg: #fff;
 | 
			
		||||
$colorStatusDefault: #ccc;
 | 
			
		||||
$colorStatusInfo: #60ba7b;
 | 
			
		||||
$colorStatusAlert: #ffb66c;
 | 
			
		||||
$colorStatusError: #c96b68;
 | 
			
		||||
$colorProgressBarOuter: rgba(#000, 0.1);
 | 
			
		||||
$colorProgressBarAmt: #0a0;
 | 
			
		||||
$progressBarHOverlay: 15px;
 | 
			
		||||
$progressBarStripeW: 20px;
 | 
			
		||||
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
 | 
			
		||||
 | 
			
		||||
// Selects
 | 
			
		||||
$colorSelectBg: #ddd;
 | 
			
		||||
$colorSelectFg: $colorBodyFg;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,9 +36,9 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "TIME_CONDUCTOR_DOMAINS",
 | 
			
		||||
                "value": [
 | 
			
		||||
                    { "key": "time", "name": "Time" },
 | 
			
		||||
                    { "key": "yesterday", "name": "Yesterday" }
 | 
			
		||||
                    { "key": "time", "name": "UTC", "format": "utc" }
 | 
			
		||||
                ],
 | 
			
		||||
                "priority": "fallback",
 | 
			
		||||
                "comment": "Placeholder; to be replaced by inspection of available domains."
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
<mct-include key="'time-controller'"
 | 
			
		||||
             parameters='parameters'
 | 
			
		||||
             ng-model='ngModel.conductor'>
 | 
			
		||||
</mct-include>
 | 
			
		||||
<mct-control key="'select'"
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,10 @@ define(
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var TEMPLATE = [
 | 
			
		||||
                "<mct-include key=\"'time-conductor'\" ng-model='ngModel' class='l-time-controller'>",
 | 
			
		||||
                "<mct-include key=\"'time-conductor'\" ",
 | 
			
		||||
                "ng-model='ngModel' ",
 | 
			
		||||
                "parameters='parameters' ",
 | 
			
		||||
                "class='l-time-controller'>",
 | 
			
		||||
                "</mct-include>"
 | 
			
		||||
            ].join(''),
 | 
			
		||||
            THROTTLE_MS = 200,
 | 
			
		||||
@@ -74,11 +77,11 @@ define(
 | 
			
		||||
                broadcastBounds;
 | 
			
		||||
 | 
			
		||||
            // Combine start/end times into a single object
 | 
			
		||||
            function bounds(start, end) {
 | 
			
		||||
            function bounds() {
 | 
			
		||||
                return {
 | 
			
		||||
                    start: conductor.displayStart(),
 | 
			
		||||
                    end: conductor.displayEnd(),
 | 
			
		||||
                    domain: conductor.domain()
 | 
			
		||||
                    domain: conductor.domain().key
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -97,12 +100,9 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateDomain(value) {
 | 
			
		||||
                conductor.domain(value);
 | 
			
		||||
                repScope.$broadcast('telemetry:display:bounds', bounds(
 | 
			
		||||
                    conductor.displayStart(),
 | 
			
		||||
                    conductor.displayEnd(),
 | 
			
		||||
                    conductor.domain()
 | 
			
		||||
                ));
 | 
			
		||||
                var newDomain = conductor.domain(value);
 | 
			
		||||
                conductorScope.parameters.format = newDomain.format;
 | 
			
		||||
                repScope.$broadcast('telemetry:display:bounds', bounds());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // telemetry domain metadata -> option for a select control
 | 
			
		||||
@@ -130,7 +130,8 @@ define(
 | 
			
		||||
                { outer: bounds(), inner: bounds() };
 | 
			
		||||
            conductorScope.ngModel.options =
 | 
			
		||||
                conductor.domainOptions().map(makeOption);
 | 
			
		||||
            conductorScope.ngModel.domain = conductor.domain();
 | 
			
		||||
            conductorScope.ngModel.domain = conductor.domain().key;
 | 
			
		||||
            conductorScope.parameters = {};
 | 
			
		||||
 | 
			
		||||
            conductorScope
 | 
			
		||||
                .$watch('ngModel.conductor.inner.start', updateConductorInner);
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ define(
 | 
			
		||||
                request = request || {};
 | 
			
		||||
                request.start = start;
 | 
			
		||||
                request.end = end;
 | 
			
		||||
                request.domain = domain;
 | 
			
		||||
                request.domain = domain.key;
 | 
			
		||||
                return request;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ define(
 | 
			
		||||
        function TimeConductor(start, end, domains) {
 | 
			
		||||
            this.range = { start: start, end: end };
 | 
			
		||||
            this.domains = domains;
 | 
			
		||||
            this.activeDomain = domains[0].key;
 | 
			
		||||
            this.activeDomain = domains[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -73,7 +73,7 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * Get available domain options which can be used to bound time
 | 
			
		||||
         * selection.
 | 
			
		||||
         * @returns {TelemetryDomain[]} available domains
 | 
			
		||||
         * @returns {TelemetryDomainMetadata[]} available domains
 | 
			
		||||
         */
 | 
			
		||||
        TimeConductor.prototype.domainOptions = function () {
 | 
			
		||||
            return this.domains;
 | 
			
		||||
@@ -82,19 +82,21 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * Get or set (if called with an argument) the active domain.
 | 
			
		||||
         * @param {string} [key] the key identifying the domain choice
 | 
			
		||||
         * @returns {TelemetryDomain} the active telemetry domain
 | 
			
		||||
         * @returns {TelemetryDomainMetadata} the active telemetry domain
 | 
			
		||||
         */
 | 
			
		||||
        TimeConductor.prototype.domain = function (key) {
 | 
			
		||||
            function matchesKey(domain) {
 | 
			
		||||
                return domain.key === key;
 | 
			
		||||
            }
 | 
			
		||||
            var i;
 | 
			
		||||
 | 
			
		||||
            if (arguments.length > 0) {
 | 
			
		||||
                if (!this.domains.some(matchesKey)) {
 | 
			
		||||
                    throw new Error("Unknown domain " + key);
 | 
			
		||||
                for (i = 0; i < this.domains.length; i += 1) {
 | 
			
		||||
                    if (this.domains[i].key === key) {
 | 
			
		||||
                        return (this.activeDomain = this.domains[i]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                this.activeDomain = key;
 | 
			
		||||
 | 
			
		||||
                throw new Error("Unknown domain " + key);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this.activeDomain;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -129,7 +129,7 @@ define(
 | 
			
		||||
            it("exposes conductor state in scope", function () {
 | 
			
		||||
                mockConductor.displayStart.andReturn(1977);
 | 
			
		||||
                mockConductor.displayEnd.andReturn(1984);
 | 
			
		||||
                mockConductor.domain.andReturn('d');
 | 
			
		||||
                mockConductor.domain.andReturn({ key: 'd' });
 | 
			
		||||
                representer.represent(testViews[0], {});
 | 
			
		||||
 | 
			
		||||
                expect(mockNewScope.ngModel.conductor).toEqual({
 | 
			
		||||
@@ -219,7 +219,7 @@ define(
 | 
			
		||||
                representer.represent(testViews[0], null);
 | 
			
		||||
 | 
			
		||||
                expect(mockNewScope.ngModel.domain)
 | 
			
		||||
                    .toEqual(mockConductor.domain());
 | 
			
		||||
                    .toEqual(mockConductor.domain().key);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes domain options in scope", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                mockConductor.displayStart.andReturn(42);
 | 
			
		||||
                mockConductor.displayEnd.andReturn(1977);
 | 
			
		||||
                mockConductor.domain.andReturn("testDomain");
 | 
			
		||||
                mockConductor.domain.andReturn({ key: "testDomain" });
 | 
			
		||||
 | 
			
		||||
                decorator = new ConductorTelemetryDecorator(
 | 
			
		||||
                    mockConductorService,
 | 
			
		||||
@@ -104,7 +104,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("with domain selection", function () {
 | 
			
		||||
                    expect(request.domain).toEqual(mockConductor.domain());
 | 
			
		||||
                    expect(request.domain).toEqual(mockConductor.domain().key);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@@ -127,7 +127,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("with domain selection", function () {
 | 
			
		||||
                    expect(request.domain).toEqual(mockConductor.domain());
 | 
			
		||||
                    expect(request.domain).toEqual(mockConductor.domain().key);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -59,12 +59,12 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes the current domain choice", function () {
 | 
			
		||||
                expect(conductor.domain()).toEqual(testDomains[0].key);
 | 
			
		||||
                expect(conductor.domain()).toEqual(testDomains[0]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows the domain choice to be changed", function () {
 | 
			
		||||
                conductor.domain(testDomains[1].key);
 | 
			
		||||
                expect(conductor.domain()).toEqual(testDomains[1].key);
 | 
			
		||||
                expect(conductor.domain()).toEqual(testDomains[1]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("throws an error on attempts to set an invalid domain", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,10 +31,19 @@ define(
 | 
			
		||||
        "./elements/PlotPalette",
 | 
			
		||||
        "./elements/PlotAxis",
 | 
			
		||||
        "./elements/PlotLimitTracker",
 | 
			
		||||
        "./elements/PlotTelemetryFormatter",
 | 
			
		||||
        "./modes/PlotModeOptions",
 | 
			
		||||
        "./SubPlotFactory"
 | 
			
		||||
    ],
 | 
			
		||||
    function (PlotUpdater, PlotPalette, PlotAxis, PlotLimitTracker, PlotModeOptions, SubPlotFactory) {
 | 
			
		||||
    function (
 | 
			
		||||
        PlotUpdater,
 | 
			
		||||
        PlotPalette,
 | 
			
		||||
        PlotAxis,
 | 
			
		||||
        PlotLimitTracker,
 | 
			
		||||
        PlotTelemetryFormatter,
 | 
			
		||||
        PlotModeOptions,
 | 
			
		||||
        SubPlotFactory
 | 
			
		||||
    ) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var AXIS_DEFAULTS = [
 | 
			
		||||
@@ -62,7 +71,10 @@ define(
 | 
			
		||||
            PLOT_FIXED_DURATION
 | 
			
		||||
        ) {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                subPlotFactory = new SubPlotFactory(telemetryFormatter),
 | 
			
		||||
                plotTelemetryFormatter =
 | 
			
		||||
                    new PlotTelemetryFormatter(telemetryFormatter),
 | 
			
		||||
                subPlotFactory =
 | 
			
		||||
                    new SubPlotFactory(plotTelemetryFormatter),
 | 
			
		||||
                cachedObjects = [],
 | 
			
		||||
                updater,
 | 
			
		||||
                lastBounds,
 | 
			
		||||
@@ -71,10 +83,9 @@ define(
 | 
			
		||||
            // Populate the scope with axis information (specifically, options
 | 
			
		||||
            // available for each axis.)
 | 
			
		||||
            function setupAxes(metadatas) {
 | 
			
		||||
                $scope.axes = [
 | 
			
		||||
                    new PlotAxis("domain", metadatas, AXIS_DEFAULTS[0]),
 | 
			
		||||
                    new PlotAxis("range", metadatas, AXIS_DEFAULTS[1])
 | 
			
		||||
                ];
 | 
			
		||||
                $scope.axes.forEach(function (axis) {
 | 
			
		||||
                    axis.updateMetadata(metadatas);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Trigger an update of a specific subplot;
 | 
			
		||||
@@ -125,37 +136,49 @@ define(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getUpdater() {
 | 
			
		||||
                if (!updater) {
 | 
			
		||||
                    recreateUpdater();
 | 
			
		||||
                }
 | 
			
		||||
                return updater;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Handle new telemetry data in this plot
 | 
			
		||||
            function updateValues() {
 | 
			
		||||
                self.pending = false;
 | 
			
		||||
                if (handle) {
 | 
			
		||||
                    setupModes(handle.getTelemetryObjects());
 | 
			
		||||
                }
 | 
			
		||||
                if (updater) {
 | 
			
		||||
                    updater.update();
 | 
			
		||||
                    setupAxes(handle.getMetadata());
 | 
			
		||||
                    getUpdater().update();
 | 
			
		||||
                    self.modeOptions.getModeHandler().plotTelemetry(updater);
 | 
			
		||||
                }
 | 
			
		||||
                if (self.limitTracker) {
 | 
			
		||||
                    self.limitTracker.update();
 | 
			
		||||
                    self.update();
 | 
			
		||||
                }
 | 
			
		||||
                self.update();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Display new historical data as it becomes available
 | 
			
		||||
            function addHistoricalData(domainObject, series) {
 | 
			
		||||
                self.pending = false;
 | 
			
		||||
                updater.addHistorical(domainObject, series);
 | 
			
		||||
                getUpdater().addHistorical(domainObject, series);
 | 
			
		||||
                self.modeOptions.getModeHandler().plotTelemetry(updater);
 | 
			
		||||
                self.update();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Issue a new request for historical telemetry
 | 
			
		||||
            function requestTelemetry() {
 | 
			
		||||
                if (handle && updater) {
 | 
			
		||||
                if (handle) {
 | 
			
		||||
                    handle.request({}, addHistoricalData);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Requery for data entirely
 | 
			
		||||
            function replot() {
 | 
			
		||||
                if (handle) {
 | 
			
		||||
                    updater = undefined;
 | 
			
		||||
                    requestTelemetry();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Create a new subscription; telemetrySubscriber gets
 | 
			
		||||
            // to do the meaningful work here.
 | 
			
		||||
            function subscribe(domainObject) {
 | 
			
		||||
@@ -167,12 +190,7 @@ define(
 | 
			
		||||
                    updateValues,
 | 
			
		||||
                    true // Lossless
 | 
			
		||||
                );
 | 
			
		||||
                if (handle) {
 | 
			
		||||
                    setupModes(handle.getTelemetryObjects());
 | 
			
		||||
                    setupAxes(handle.getMetadata());
 | 
			
		||||
                    recreateUpdater();
 | 
			
		||||
                    requestTelemetry();
 | 
			
		||||
                }
 | 
			
		||||
                replot();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Release the current subscription (called when scope is destroyed)
 | 
			
		||||
@@ -185,12 +203,22 @@ define(
 | 
			
		||||
 | 
			
		||||
            // Respond to a display bounds change (requery for data)
 | 
			
		||||
            function changeDisplayBounds(event, bounds) {
 | 
			
		||||
                var domainAxis = $scope.axes[0];
 | 
			
		||||
 | 
			
		||||
                domainAxis.chooseOption(bounds.domain);
 | 
			
		||||
                plotTelemetryFormatter
 | 
			
		||||
                    .setDomainFormat(domainAxis.active.format);
 | 
			
		||||
 | 
			
		||||
                self.pending = true;
 | 
			
		||||
                releaseSubscription();
 | 
			
		||||
                subscribe($scope.domainObject);
 | 
			
		||||
                setBasePanZoom(bounds);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateDomainFormat(format) {
 | 
			
		||||
                plotTelemetryFormatter.setDomainFormat(format);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.modeOptions = new PlotModeOptions([], subPlotFactory);
 | 
			
		||||
            this.updateValues = updateValues;
 | 
			
		||||
 | 
			
		||||
@@ -202,6 +230,13 @@ define(
 | 
			
		||||
 | 
			
		||||
            self.pending = true;
 | 
			
		||||
 | 
			
		||||
            // Initialize axes; will get repopulated when telemetry
 | 
			
		||||
            // metadata becomes available.
 | 
			
		||||
            $scope.axes = [
 | 
			
		||||
                new PlotAxis("domains", [], AXIS_DEFAULTS[0]),
 | 
			
		||||
                new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            // Subscribe to telemetry when a domain object becomes available
 | 
			
		||||
            $scope.$watch('domainObject', subscribe);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -121,9 +121,9 @@ define(
 | 
			
		||||
            // Utility, for map/forEach loops. Index 0 is domain,
 | 
			
		||||
            // index 1 is range.
 | 
			
		||||
            function formatValue(v, i) {
 | 
			
		||||
                return (i ?
 | 
			
		||||
                    formatter.formatRangeValue :
 | 
			
		||||
                    formatter.formatDomainValue)(v);
 | 
			
		||||
                return i ?
 | 
			
		||||
                    formatter.formatRangeValue(v) :
 | 
			
		||||
                    formatter.formatDomainValue(v);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.hoverCoordinates = this.mousePosition &&
 | 
			
		||||
 
 | 
			
		||||
@@ -46,21 +46,9 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         */
 | 
			
		||||
        function PlotAxis(axisType, metadatas, defaultValue) {
 | 
			
		||||
            var keys = {},
 | 
			
		||||
                options = [];
 | 
			
		||||
 | 
			
		||||
            // Look through all metadata objects and assemble a list
 | 
			
		||||
            // of all possible domain or range options
 | 
			
		||||
            function buildOptionsForMetadata(m) {
 | 
			
		||||
                (m[axisType] || []).forEach(function (option) {
 | 
			
		||||
                    if (!keys[option.key]) {
 | 
			
		||||
                        keys[option.key] = true;
 | 
			
		||||
                        options.push(option);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            (metadatas || []).forEach(buildOptionsForMetadata);
 | 
			
		||||
            this.axisType = axisType;
 | 
			
		||||
            this.defaultValue = defaultValue;
 | 
			
		||||
            this.optionKeys = {};
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The currently chosen option for this axis. An
 | 
			
		||||
@@ -68,7 +56,7 @@ define(
 | 
			
		||||
             * directly form the plot template.
 | 
			
		||||
             * @memberof platform/features/plot.PlotAxis#
 | 
			
		||||
             */
 | 
			
		||||
            this.active = options[0] || defaultValue;
 | 
			
		||||
            this.active = defaultValue;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The set of options applicable for this axis;
 | 
			
		||||
@@ -77,9 +65,71 @@ define(
 | 
			
		||||
             * human-readable names respectively)
 | 
			
		||||
             * @memberof platform/features/plot.PlotAxis#
 | 
			
		||||
             */
 | 
			
		||||
            this.options = options;
 | 
			
		||||
            this.options = [];
 | 
			
		||||
 | 
			
		||||
            // Initialize options from metadata objects
 | 
			
		||||
            this.updateMetadata(metadatas);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Update axis options to reflect current metadata.
 | 
			
		||||
         * @param {TelemetryMetadata[]} metadata objects describing
 | 
			
		||||
         *        applicable telemetry
 | 
			
		||||
         */
 | 
			
		||||
        PlotAxis.prototype.updateMetadata = function (metadatas) {
 | 
			
		||||
            var axisType = this.axisType,
 | 
			
		||||
                optionKeys = this.optionKeys,
 | 
			
		||||
                newOptions = {},
 | 
			
		||||
                toAdd = [];
 | 
			
		||||
 | 
			
		||||
            function isValid(option) {
 | 
			
		||||
                return option && optionKeys[option.key];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            metadatas.forEach(function (m) {
 | 
			
		||||
                (m[axisType] || []).forEach(function (option) {
 | 
			
		||||
                    var key = option.key;
 | 
			
		||||
                    if (!optionKeys[key] && !newOptions[key]) {
 | 
			
		||||
                        toAdd.push(option);
 | 
			
		||||
                    }
 | 
			
		||||
                    newOptions[key] = true;
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            optionKeys = this.optionKeys = newOptions;
 | 
			
		||||
 | 
			
		||||
            // General approach here is to avoid changing object
 | 
			
		||||
            // instances unless something has really changed, since
 | 
			
		||||
            // Angular is watching; don't want to trigger extra digests.
 | 
			
		||||
            if (!this.options.every(isValid)) {
 | 
			
		||||
                this.options = this.options.filter(isValid);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (toAdd.length > 0) {
 | 
			
		||||
                this.options = this.options.concat(toAdd);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!isValid(this.active)) {
 | 
			
		||||
                this.active = this.options[0] || this.defaultValue;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Change the domain/range selection for this axis. If the
 | 
			
		||||
         * provided `key` is not recognized as an option, no change
 | 
			
		||||
         * will occur.
 | 
			
		||||
         * @param {string} key the identifier for the domain/range
 | 
			
		||||
         */
 | 
			
		||||
        PlotAxis.prototype.chooseOption = function (key) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
            this.options.forEach(function (option) {
 | 
			
		||||
                if (option.key === key) {
 | 
			
		||||
                    self.active = option;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return PlotAxis;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Wraps a `TelemetryFormatter` to provide formats for domain and
 | 
			
		||||
         * range values; provides a single place to track domain/range
 | 
			
		||||
         * formats within a plot, allowing other plot elements to simply
 | 
			
		||||
         * request that values be formatted.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/features/plot
 | 
			
		||||
         * @implements {platform/telemetry.TelemetryFormatter}
 | 
			
		||||
         * @param {TelemetryFormatter} telemetryFormatter the formatter
 | 
			
		||||
         *        to wrap.
 | 
			
		||||
         */
 | 
			
		||||
        function PlotTelemetryFormatter(telemetryFormatter) {
 | 
			
		||||
            this.telemetryFormatter = telemetryFormatter;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Specify the format to use for domain values.
 | 
			
		||||
         * @param {string} key the format's identifier
 | 
			
		||||
         */
 | 
			
		||||
        PlotTelemetryFormatter.prototype.setDomainFormat = function (key) {
 | 
			
		||||
            this.domainFormat = key;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Specify the format to use for range values.
 | 
			
		||||
         * @param {string} key the format's identifier
 | 
			
		||||
         */
 | 
			
		||||
        PlotTelemetryFormatter.prototype.setRangeFormat = function (key) {
 | 
			
		||||
            this.rangeFormat = key;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        PlotTelemetryFormatter.prototype.formatDomainValue = function (value) {
 | 
			
		||||
            return this.telemetryFormatter
 | 
			
		||||
                .formatDomainValue(value, this.domainFormat);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        PlotTelemetryFormatter.prototype.formatRangeValue = function (value) {
 | 
			
		||||
            return this.telemetryFormatter
 | 
			
		||||
                .formatRangeValue(value, this.rangeFormat);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return PlotTelemetryFormatter;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -43,6 +43,14 @@ define(
 | 
			
		||||
            this.formatter = formatter;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // For phantomjs compatibility, for headless testing
 | 
			
		||||
        // (Function.prototype.bind unsupported)
 | 
			
		||||
        function bind(fn, thisObj) {
 | 
			
		||||
            return fn.bind ? fn.bind(thisObj) : function () {
 | 
			
		||||
                return fn.apply(thisObj, arguments);
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Generate ticks; interpolate from start up to
 | 
			
		||||
        // start + span in count steps, using the provided
 | 
			
		||||
        // formatter to represent each value.
 | 
			
		||||
@@ -72,7 +80,7 @@ define(
 | 
			
		||||
                panZoom.origin[0],
 | 
			
		||||
                panZoom.dimensions[0],
 | 
			
		||||
                count,
 | 
			
		||||
                this.formatter.formatDomainValue
 | 
			
		||||
                bind(this.formatter.formatDomainValue, this.formatter)
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +95,7 @@ define(
 | 
			
		||||
                panZoom.origin[1],
 | 
			
		||||
                panZoom.dimensions[1],
 | 
			
		||||
                count,
 | 
			
		||||
                this.formatter.formatRangeValue
 | 
			
		||||
                bind(this.formatter.formatRangeValue, this.formatter)
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -169,8 +169,9 @@ define(
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                // Make an object available
 | 
			
		||||
                // Make an object available; invoke handler's callback
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
 | 
			
		||||
                mockHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                expect(controller.getModeOptions().length).toEqual(1);
 | 
			
		||||
 | 
			
		||||
@@ -181,8 +182,9 @@ define(
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                // Make an object available
 | 
			
		||||
                // Make an object available; invoke handler's callback
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
 | 
			
		||||
                mockHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                expect(controller.getModeOptions().length).toEqual(2);
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,12 @@ define(
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("A plot axis", function () {
 | 
			
		||||
            var testMetadatas = [
 | 
			
		||||
            var testMetadatas,
 | 
			
		||||
                testDefault,
 | 
			
		||||
                axis;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testMetadatas = [
 | 
			
		||||
                    {
 | 
			
		||||
                        tests: [
 | 
			
		||||
                            { key: "t0", name: "T0" },
 | 
			
		||||
@@ -52,13 +57,14 @@ define(
 | 
			
		||||
                            { key: "t6", name: "T6" }
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                testDefault = { key: "test", name: "Test" },
 | 
			
		||||
                controller = new PlotAxis("tests", testMetadatas, testDefault);
 | 
			
		||||
                ];
 | 
			
		||||
                testDefault = { key: "test", name: "Test" };
 | 
			
		||||
                axis = new PlotAxis("tests", testMetadatas, testDefault);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("pulls out a list of domain or range options", function () {
 | 
			
		||||
                // Should have filtered out duplicates, etc
 | 
			
		||||
                expect(controller.options).toEqual([
 | 
			
		||||
                expect(axis.options).toEqual([
 | 
			
		||||
                    { key: "t0", name: "T0" },
 | 
			
		||||
                    { key: "t1", name: "T1" },
 | 
			
		||||
                    { key: "t2", name: "T2" },
 | 
			
		||||
@@ -70,7 +76,7 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("chooses the first option as a default", function () {
 | 
			
		||||
                expect(controller.active).toEqual({ key: "t0", name: "T0" });
 | 
			
		||||
                expect(axis.active).toEqual({ key: "t0", name: "T0" });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("falls back to a provided default if no options are present", function () {
 | 
			
		||||
@@ -78,6 +84,26 @@ define(
 | 
			
		||||
                    .toEqual(testDefault);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows options to be chosen by key", function () {
 | 
			
		||||
                axis.chooseOption("t3");
 | 
			
		||||
                expect(axis.active).toEqual({ key: "t3", name: "T3" });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("reflects changes to applicable metadata", function () {
 | 
			
		||||
                axis.updateMetadata([ testMetadatas[1] ]);
 | 
			
		||||
                expect(axis.options).toEqual([
 | 
			
		||||
                    { key: "t0", name: "T0" },
 | 
			
		||||
                    { key: "t2", name: "T2" }
 | 
			
		||||
                ]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("returns the same array instance for unchanged metadata", function () {
 | 
			
		||||
                // ...to avoid triggering extra digest cycles.
 | 
			
		||||
                var oldInstance = axis.options;
 | 
			
		||||
                axis.updateMetadata(testMetadatas);
 | 
			
		||||
                expect(axis.options).toBe(oldInstance);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -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,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/elements/PlotTelemetryFormatter"],
 | 
			
		||||
    function (PlotTelemetryFormatter) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("The PlotTelemetryFormatter", function () {
 | 
			
		||||
            var mockFormatter,
 | 
			
		||||
                formatter;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockFormatter = jasmine.createSpyObj(
 | 
			
		||||
                    'telemetryFormatter',
 | 
			
		||||
                    ['formatDomainValue', 'formatRangeValue']
 | 
			
		||||
                );
 | 
			
		||||
                formatter = new PlotTelemetryFormatter(mockFormatter);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("using domain & range format keys", function () {
 | 
			
		||||
                var rangeFormat = "someRangeFormat",
 | 
			
		||||
                    domainFormat = "someDomainFormat";
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    formatter.setRangeFormat(rangeFormat);
 | 
			
		||||
                    formatter.setDomainFormat(domainFormat);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("includes format in formatDomainValue calls", function () {
 | 
			
		||||
                    mockFormatter.formatDomainValue.andReturn("formatted!");
 | 
			
		||||
                    expect(formatter.formatDomainValue(12321))
 | 
			
		||||
                        .toEqual("formatted!");
 | 
			
		||||
                    expect(mockFormatter.formatDomainValue)
 | 
			
		||||
                        .toHaveBeenCalledWith(12321, domainFormat);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("includes format in formatRangeValue calls", function () {
 | 
			
		||||
                    mockFormatter.formatRangeValue.andReturn("formatted!");
 | 
			
		||||
                    expect(formatter.formatRangeValue(12321))
 | 
			
		||||
                        .toEqual("formatted!");
 | 
			
		||||
                    expect(mockFormatter.formatRangeValue)
 | 
			
		||||
                        .toHaveBeenCalledWith(12321, rangeFormat);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -14,6 +14,7 @@
 | 
			
		||||
    "elements/PlotPosition",
 | 
			
		||||
    "elements/PlotPreparer",
 | 
			
		||||
    "elements/PlotSeriesWindow",
 | 
			
		||||
    "elements/PlotTelemetryFormatter",
 | 
			
		||||
    "elements/PlotTickGenerator",
 | 
			
		||||
    "elements/PlotUpdater",
 | 
			
		||||
    "modes/PlotModeOptions",
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,8 @@ define(
 | 
			
		||||
        DomainColumn.prototype.getValue = function (domainObject, datum) {
 | 
			
		||||
            return {
 | 
			
		||||
                text: this.telemetryFormatter.formatDomainValue(
 | 
			
		||||
                    datum[this.domainMetadata.key]
 | 
			
		||||
                    datum[this.domainMetadata.key],
 | 
			
		||||
                    this.domainMetadata.format
 | 
			
		||||
                )
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@ define(
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Defines the mct-representation directive. This may be used to
 | 
			
		||||
         * present domain objects as HTML (with event wiring), with the
 | 
			
		||||
@@ -96,6 +95,9 @@ define(
 | 
			
		||||
                    }),
 | 
			
		||||
                    toClear = [], // Properties to clear out of scope on change
 | 
			
		||||
                    counter = 0,
 | 
			
		||||
                    couldRepresent = false,
 | 
			
		||||
                    lastId,
 | 
			
		||||
                    lastKey,
 | 
			
		||||
                    changeTemplate = templateLinker.link($scope, element);
 | 
			
		||||
 | 
			
		||||
                // Populate scope with any capabilities indicated by the
 | 
			
		||||
@@ -141,6 +143,13 @@ define(
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function unchanged(canRepresent, id, key) {
 | 
			
		||||
                    return canRepresent &&
 | 
			
		||||
                        couldRepresent &&
 | 
			
		||||
                        id === lastId &&
 | 
			
		||||
                        key === lastKey;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // General-purpose refresh mechanism; should set up the scope
 | 
			
		||||
                // as appropriate for current representation key and
 | 
			
		||||
                // domain object.
 | 
			
		||||
@@ -149,7 +158,13 @@ define(
 | 
			
		||||
                        representation = lookup($scope.key, domainObject),
 | 
			
		||||
                        path = representation && getPath(representation),
 | 
			
		||||
                        uses = ((representation || {}).uses || []),
 | 
			
		||||
                        canRepresent = !!(path && domainObject);
 | 
			
		||||
                        canRepresent = !!(path && domainObject),
 | 
			
		||||
                        id = domainObject && domainObject.getId(),
 | 
			
		||||
                        key = $scope.key;
 | 
			
		||||
 | 
			
		||||
                    if (unchanged(canRepresent, id, key)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Create an empty object named "representation", for this
 | 
			
		||||
                    // representation to store local variables into.
 | 
			
		||||
@@ -173,6 +188,11 @@ define(
 | 
			
		||||
                        delete $scope[property];
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    // To allow simplified change detection next time around
 | 
			
		||||
                    couldRepresent = canRepresent;
 | 
			
		||||
                    lastId = id;
 | 
			
		||||
                    lastKey = key;
 | 
			
		||||
 | 
			
		||||
                    // Populate scope with fields associated with the current
 | 
			
		||||
                    // domain object (if one has been passed in)
 | 
			
		||||
                    if (canRepresent) {
 | 
			
		||||
 
 | 
			
		||||
@@ -92,10 +92,19 @@ define(
 | 
			
		||||
            var activeElement = element,
 | 
			
		||||
                activeTemplateUrl,
 | 
			
		||||
                comment = this.$compile('<!-- hidden mct element -->')(scope),
 | 
			
		||||
                activeScope,
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            function destroyScope() {
 | 
			
		||||
                if (activeScope) {
 | 
			
		||||
                    activeScope.$destroy();
 | 
			
		||||
                    activeScope = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function removeElement() {
 | 
			
		||||
                if (activeElement !== comment) {
 | 
			
		||||
                    destroyScope();
 | 
			
		||||
                    activeElement.replaceWith(comment);
 | 
			
		||||
                    activeElement = comment;
 | 
			
		||||
                }
 | 
			
		||||
@@ -110,8 +119,10 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function populateElement(template) {
 | 
			
		||||
                element.empty();
 | 
			
		||||
                element.append(self.$compile(template)(scope));
 | 
			
		||||
                destroyScope();
 | 
			
		||||
                activeScope = scope.$new(false);
 | 
			
		||||
                element.html(template);
 | 
			
		||||
                self.$compile(element.contents())(activeScope);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function badTemplate(templateUrl) {
 | 
			
		||||
@@ -120,22 +131,21 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function changeTemplate(templateUrl) {
 | 
			
		||||
                if (templateUrl !== activeTemplateUrl) {
 | 
			
		||||
                    if (templateUrl) {
 | 
			
		||||
                        addElement();
 | 
			
		||||
                        self.load(templateUrl).then(function (template) {
 | 
			
		||||
                            // Avoid race conditions
 | 
			
		||||
                            if (templateUrl === activeTemplateUrl) {
 | 
			
		||||
                                populateElement(template);
 | 
			
		||||
                            }
 | 
			
		||||
                        }, function () {
 | 
			
		||||
                            badTemplate(templateUrl);
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        removeElement();
 | 
			
		||||
                    }
 | 
			
		||||
                    activeTemplateUrl = templateUrl;
 | 
			
		||||
                if (templateUrl) {
 | 
			
		||||
                    destroyScope();
 | 
			
		||||
                    addElement();
 | 
			
		||||
                    self.load(templateUrl).then(function (template) {
 | 
			
		||||
                        // Avoid race conditions
 | 
			
		||||
                        if (templateUrl === activeTemplateUrl) {
 | 
			
		||||
                            populateElement(template);
 | 
			
		||||
                        }
 | 
			
		||||
                    }, function () {
 | 
			
		||||
                        badTemplate(templateUrl);
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    removeElement();
 | 
			
		||||
                }
 | 
			
		||||
                activeTemplateUrl = templateUrl;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (templateUrl) {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,8 @@ define(
 | 
			
		||||
    function (TemplateLinker) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var JQLITE_METHODS = [ 'replaceWith', 'empty', 'append' ];
 | 
			
		||||
        var JQLITE_METHODS = [ 'replaceWith', 'empty', 'html', 'contents' ],
 | 
			
		||||
            SCOPE_METHODS = [ '$on', '$new', '$destroy' ];
 | 
			
		||||
 | 
			
		||||
        describe("TemplateLinker", function () {
 | 
			
		||||
            var mockTemplateRequest,
 | 
			
		||||
@@ -38,6 +39,8 @@ define(
 | 
			
		||||
                mockElement,
 | 
			
		||||
                mockTemplates,
 | 
			
		||||
                mockElements,
 | 
			
		||||
                mockContents,
 | 
			
		||||
                mockNewScope,
 | 
			
		||||
                mockPromise,
 | 
			
		||||
                linker;
 | 
			
		||||
 | 
			
		||||
@@ -46,14 +49,18 @@ define(
 | 
			
		||||
                mockSce = jasmine.createSpyObj('$sce', ['trustAsResourceUrl']);
 | 
			
		||||
                mockCompile = jasmine.createSpy('$compile');
 | 
			
		||||
                mockLog = jasmine.createSpyObj('$log', ['error', 'warn']);
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$on']);
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
 | 
			
		||||
                mockNewScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
 | 
			
		||||
                mockElement = jasmine.createSpyObj('element', JQLITE_METHODS);
 | 
			
		||||
                mockPromise = jasmine.createSpyObj('promise', ['then']);
 | 
			
		||||
                mockTemplates = {};
 | 
			
		||||
                mockElements = {};
 | 
			
		||||
                mockContents = {};
 | 
			
		||||
 | 
			
		||||
                mockTemplateRequest.andReturn(mockPromise);
 | 
			
		||||
                mockCompile.andCallFake(function (html) {
 | 
			
		||||
                mockCompile.andCallFake(function (toCompile) {
 | 
			
		||||
                    var html = typeof toCompile === 'string' ?
 | 
			
		||||
                            toCompile : toCompile.testHtml;
 | 
			
		||||
                    mockTemplates[html] = jasmine.createSpy('template');
 | 
			
		||||
                    mockElements[html] =
 | 
			
		||||
                        jasmine.createSpyObj('templateEl', JQLITE_METHODS);
 | 
			
		||||
@@ -63,6 +70,17 @@ define(
 | 
			
		||||
                mockSce.trustAsResourceUrl.andCallFake(function (url) {
 | 
			
		||||
                    return { trusted: url };
 | 
			
		||||
                });
 | 
			
		||||
                mockScope.$new.andReturn(mockNewScope);
 | 
			
		||||
                mockElement.html.andCallFake(function (html) {
 | 
			
		||||
                    mockContents[html] =
 | 
			
		||||
                        jasmine.createSpyObj('contentsEl', JQLITE_METHODS);
 | 
			
		||||
                    mockContents[html].testHtml = html;
 | 
			
		||||
                });
 | 
			
		||||
                mockElement.contents.andCallFake(function () {
 | 
			
		||||
                    return mockContents[
 | 
			
		||||
                        mockElement.html.mostRecentCall.args[0]
 | 
			
		||||
                    ];
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                linker = new TemplateLinker(
 | 
			
		||||
                    mockTemplateRequest,
 | 
			
		||||
@@ -131,10 +149,11 @@ define(
 | 
			
		||||
                        }, false);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("compiles loaded templates with linked scope", function () {
 | 
			
		||||
                        expect(mockCompile).toHaveBeenCalledWith(testTemplate);
 | 
			
		||||
                    it("compiles element contents with a new scope", function () {
 | 
			
		||||
                        expect(mockCompile)
 | 
			
		||||
                            .toHaveBeenCalledWith(mockContents[testTemplate]);
 | 
			
		||||
                        expect(mockTemplates[testTemplate])
 | 
			
		||||
                            .toHaveBeenCalledWith(mockScope);
 | 
			
		||||
                            .toHaveBeenCalledWith(mockNewScope);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("replaces comments with specified element", function () {
 | 
			
		||||
@@ -142,9 +161,9 @@ define(
 | 
			
		||||
                            .toHaveBeenCalledWith(mockElement);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("appends rendered content to the specified element", function () {
 | 
			
		||||
                        expect(mockElement.append)
 | 
			
		||||
                            .toHaveBeenCalledWith(mockElements[testTemplate]);
 | 
			
		||||
                    it("inserts HTML content into the specified element", function () {
 | 
			
		||||
                        expect(mockElement.html)
 | 
			
		||||
                            .toHaveBeenCalledWith(testTemplate);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("clears templates when called with undefined", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,12 @@
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "representations": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "search-item",
 | 
			
		||||
                "templateUrl": "templates/search-item.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "templates": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "search",
 | 
			
		||||
                "templateUrl": "templates/search.html"
 | 
			
		||||
@@ -34,10 +40,6 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "search-menu",
 | 
			
		||||
                "templateUrl": "templates/search-menu.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "search-item",
 | 
			
		||||
                "templateUrl": "templates/search-item.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "components": [
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
        <a class="ui-symbol clear-icon"
 | 
			
		||||
           ng-class="{content: !(ngModel.input === '' || ngModel.input === undefined)}"
 | 
			
		||||
           ng-click="ngModel.input = ''; controller.search()">
 | 
			
		||||
	        
 | 
			
		||||
            
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
        <!-- Menu icon/button 'v' -->
 | 
			
		||||
@@ -53,12 +53,12 @@
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
        <!-- Menu -->
 | 
			
		||||
        <mct-representation key="'search-menu'"
 | 
			
		||||
        <mct-include key="'search-menu'"
 | 
			
		||||
                     class="menu-element search-menu-holder"
 | 
			
		||||
                     ng-class="{off: !toggle.isActive()}"
 | 
			
		||||
                     ng-model="ngModel"
 | 
			
		||||
                     ng-click="toggle.setState(true)">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Active filter display -->
 | 
			
		||||
@@ -71,7 +71,7 @@
 | 
			
		||||
            
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
	    Filtered by: {{ ngModel.filtersString }}
 | 
			
		||||
        Filtered by: {{ ngModel.filtersString }}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,8 @@
 | 
			
		||||
        "services": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "telemetryFormatter",
 | 
			
		||||
                "implementation": "TelemetryFormatter.js"
 | 
			
		||||
                "implementation": "TelemetryFormatter.js",
 | 
			
		||||
                "depends": [ "formatService", "DEFAULT_TIME_FORMAT" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "telemetrySubscriber",
 | 
			
		||||
@@ -63,4 +64,4 @@
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,64 @@ define(
 | 
			
		||||
                getRangeValue: ZERO
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides metadata about telemetry associated with a
 | 
			
		||||
         * given domain object.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef TelemetryMetadata
 | 
			
		||||
         * @property {string} source the machine-readable identifier for
 | 
			
		||||
         *           the source of telemetry data for this object; used by
 | 
			
		||||
         *           {@link TelemetryService} implementations to determine
 | 
			
		||||
         *           whether or not they provide data for this object.
 | 
			
		||||
         * @property {string} key the machine-readable identifier for
 | 
			
		||||
         *           telemetry data associated with this specific object,
 | 
			
		||||
         *           within that `source`.
 | 
			
		||||
         * @property {TelemetryDomainMetadata[]} domains supported domain
 | 
			
		||||
         *           options for telemetry data associated with this object,
 | 
			
		||||
         *           to use in interpreting a {@link TelemetrySeries}
 | 
			
		||||
         * @property {TelemetryRangeMetadata[]} ranges supported range
 | 
			
		||||
         *           options for telemetry data associated with this object,
 | 
			
		||||
         *           to use in interpreting a {@link TelemetrySeries}
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides metadata about range options within a telemetry series.
 | 
			
		||||
         * Range options describe distinct properties within any given datum
 | 
			
		||||
         * of a telemetry series; for instance, a telemetry series containing
 | 
			
		||||
         * both raw and uncalibrated values may provide separate ranges for
 | 
			
		||||
         * each.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef TelemetryRangeMetadata
 | 
			
		||||
         * @property {string} key machine-readable identifier for this range
 | 
			
		||||
         * @property {string} name human-readable name for this range
 | 
			
		||||
         * @property {string} [units] human-readable units for this range
 | 
			
		||||
         * @property {string} [format] data format for this range; usually,
 | 
			
		||||
         *                    one of `number`, or `string`. If `undefined`,
 | 
			
		||||
         *                    should presume to be a `number`. Custom formats
 | 
			
		||||
         *                    may be indicated here.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides metadata about domain options within a telemetry series.
 | 
			
		||||
         * Domain options describe distinct properties within any given datum
 | 
			
		||||
         * of a telemtry series; for instance, a telemetry series containing
 | 
			
		||||
         * both spacecraft event time and earth received times may provide
 | 
			
		||||
         * separate domains for each.
 | 
			
		||||
         *
 | 
			
		||||
         * Domains are typically used to represent timestamps in a telemetry
 | 
			
		||||
         * series, but more generally may express any property which will
 | 
			
		||||
         * have unique values for each datum in a series. It is this property
 | 
			
		||||
         * which makes domains distinct from ranges, as it makes these values
 | 
			
		||||
         * appropriate and meaningful for use to sort and bound a series.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef TelemetryDomainMetadata
 | 
			
		||||
         * @property {string} key machine-readable identifier for this range
 | 
			
		||||
         * @property {string} name human-readable name for this range
 | 
			
		||||
         * @property {string} [system] machine-readable identifier for the
 | 
			
		||||
         *                    time/date system associated with this domain;
 | 
			
		||||
         *                    used by {@link DateService}
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A telemetry capability provides a means of requesting telemetry
 | 
			
		||||
         * for a specific object, and for unwrapping the response (to get
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,13 @@
 | 
			
		||||
/*global define,moment*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['moment'],
 | 
			
		||||
    function (moment) {
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        // Date format to use for domain values; in particular,
 | 
			
		||||
        // use day-of-year instead of month/day
 | 
			
		||||
        var DATE_FORMAT = "YYYY-DDD HH:mm:ss",
 | 
			
		||||
            VALUE_FORMAT_DIGITS = 3;
 | 
			
		||||
        var VALUE_FORMAT_DIGITS = 3;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The TelemetryFormatter is responsible for formatting (as text
 | 
			
		||||
@@ -37,22 +36,31 @@ define(
 | 
			
		||||
         * the range (usually value) of a data series.
 | 
			
		||||
         * @memberof platform/telemetry
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {FormatService} formatService the service to user to format
 | 
			
		||||
         *        domain values
 | 
			
		||||
         * @param {string} defaultFormatKey the format to request when no
 | 
			
		||||
         *        format has been otherwise specified
 | 
			
		||||
         */
 | 
			
		||||
        function TelemetryFormatter() {
 | 
			
		||||
        function TelemetryFormatter(formatService, defaultFormatKey) {
 | 
			
		||||
            this.formatService = formatService;
 | 
			
		||||
            this.defaultFormat = formatService.getFormat(defaultFormatKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Format a domain value.
 | 
			
		||||
         * @param {number} v the domain value; a timestamp
 | 
			
		||||
         * @param {number} v the domain value; usually, a timestamp
 | 
			
		||||
         *        in milliseconds since start of 1970
 | 
			
		||||
         * @param {string} [key] the key which identifies the
 | 
			
		||||
         *        domain; if unspecified or unknown, this will
 | 
			
		||||
         *        be treated as a standard timestamp.
 | 
			
		||||
         * @param {string} [key] a key which identifies the format
 | 
			
		||||
         *        to use
 | 
			
		||||
         * @returns {string} a textual representation of the
 | 
			
		||||
         *        data and time, suitable for display.
 | 
			
		||||
         */
 | 
			
		||||
        TelemetryFormatter.prototype.formatDomainValue = function (v, key) {
 | 
			
		||||
            return isNaN(v) ? "" : moment.utc(v).format(DATE_FORMAT);
 | 
			
		||||
            var formatter = (key === undefined) ?
 | 
			
		||||
                    this.defaultFormat :
 | 
			
		||||
                    this.formatService.getFormat(key);
 | 
			
		||||
 | 
			
		||||
            return isNaN(v) ? "" : formatter.format(v);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
 
 | 
			
		||||
@@ -27,16 +27,35 @@ define(
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The telemetry formatter", function () {
 | 
			
		||||
            var formatter;
 | 
			
		||||
            var mockFormatService,
 | 
			
		||||
                mockFormat,
 | 
			
		||||
                formatter;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                formatter = new TelemetryFormatter();
 | 
			
		||||
                mockFormatService =
 | 
			
		||||
                    jasmine.createSpyObj("formatService", ["getFormat"]);
 | 
			
		||||
                mockFormat = jasmine.createSpyObj("format", [
 | 
			
		||||
                    "validate",
 | 
			
		||||
                    "parse",
 | 
			
		||||
                    "format"
 | 
			
		||||
                ]);
 | 
			
		||||
                mockFormatService.getFormat.andReturn(mockFormat);
 | 
			
		||||
                formatter = new TelemetryFormatter(mockFormatService);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("formats domains using YYYY-DDD style", function () {
 | 
			
		||||
                expect(formatter.formatDomainValue(402513731000)).toEqual(
 | 
			
		||||
                    "1982-276 17:22:11"
 | 
			
		||||
                );
 | 
			
		||||
            it("formats domains using the formatService", function () {
 | 
			
		||||
                var testValue = 12321, testResult = "some result";
 | 
			
		||||
                mockFormat.format.andReturn(testResult);
 | 
			
		||||
 | 
			
		||||
                expect(formatter.formatDomainValue(testValue))
 | 
			
		||||
                    .toEqual(testResult);
 | 
			
		||||
                expect(mockFormat.format).toHaveBeenCalledWith(testValue);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("passes format keys to the formatService", function () {
 | 
			
		||||
                formatter.formatDomainValue(12321, "someKey");
 | 
			
		||||
                expect(mockFormatService.getFormat)
 | 
			
		||||
                    .toHaveBeenCalledWith("someKey");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("formats ranges as values", function () {
 | 
			
		||||
@@ -44,4 +63,4 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user