Compare commits
	
		
			189 Commits
		
	
	
		
			pulse-1603
			...
			layout-iss
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					728faea4ce | ||
| 
						 | 
					59db676fd8 | ||
| 
						 | 
					f89c74dfa3 | ||
| 
						 | 
					60ece16c33 | ||
| 
						 | 
					7dd7421890 | ||
| 
						 | 
					6d6124820d | ||
| 
						 | 
					1b334086b6 | ||
| 
						 | 
					586901aee7 | ||
| 
						 | 
					449923feae | ||
| 
						 | 
					ae461f71b4 | ||
| 
						 | 
					d725e30481 | ||
| 
						 | 
					5b26264409 | ||
| 
						 | 
					5243b3748d | ||
| 
						 | 
					8466ba51a0 | ||
| 
						 | 
					c2ed84d7ca | ||
| 
						 | 
					425fbc91e2 | ||
| 
						 | 
					3dd36e7ce8 | ||
| 
						 | 
					9925c2dd38 | ||
| 
						 | 
					0239faa9a8 | ||
| 
						 | 
					e802b5344c | ||
| 
						 | 
					82a661b884 | ||
| 
						 | 
					b5f196ede8 | ||
| 
						 | 
					dbc7c910cc | ||
| 
						 | 
					f5a92f66db | ||
| 
						 | 
					0417b7e32d | ||
| 
						 | 
					7c9a6bd817 | ||
| 
						 | 
					0acf8d9edc | ||
| 
						 | 
					af5d42da1e | ||
| 
						 | 
					4dff369807 | ||
| 
						 | 
					eb999ad37b | ||
| 
						 | 
					36a0976e98 | ||
| 
						 | 
					cbc2555687 | ||
| 
						 | 
					e040abc329 | ||
| 
						 | 
					a58c484d71 | ||
| 
						 | 
					d5ca720eff | ||
| 
						 | 
					d605b2ce43 | ||
| 
						 | 
					d895b47bc6 | ||
| 
						 | 
					43b92647fb | ||
| 
						 | 
					33efe77172 | ||
| 
						 | 
					e8eb34f5c3 | ||
| 
						 | 
					bbd5dfe335 | ||
| 
						 | 
					71a1b46d69 | ||
| 
						 | 
					d86ce9794e | ||
| 
						 | 
					c2d2cbfa1e | ||
| 
						 | 
					fa57688709 | ||
| 
						 | 
					f384ca3f53 | ||
| 
						 | 
					ce858ca598 | ||
| 
						 | 
					6df5de9cce | ||
| 
						 | 
					9f59b1920b | ||
| 
						 | 
					f778f73bfc | ||
| 
						 | 
					63579668bd | ||
| 
						 | 
					9087147dda | ||
| 
						 | 
					0d61d70b8c | ||
| 
						 | 
					e1e2fc5bcd | ||
| 
						 | 
					5a3b98c731 | ||
| 
						 | 
					df01646954 | ||
| 
						 | 
					a986ddddcc | ||
| 
						 | 
					5ac377ec6a | ||
| 
						 | 
					c523480b48 | ||
| 
						 | 
					4dc09975d0 | ||
| 
						 | 
					c8eba1fbad | ||
| 
						 | 
					29a472ae5d | ||
| 
						 | 
					3e2fd8967a | ||
| 
						 | 
					9e429802c2 | ||
| 
						 | 
					6cfee100d7 | ||
| 
						 | 
					eeb214204d | ||
| 
						 | 
					b41ceab51e | ||
| 
						 | 
					10b0f43fc1 | ||
| 
						 | 
					593c1adf56 | ||
| 
						 | 
					2d1ee80322 | ||
| 
						 | 
					7555eab1e3 | ||
| 
						 | 
					4923bcbd85 | ||
| 
						 | 
					5a7fdf82ac | ||
| 
						 | 
					a5f6940d67 | ||
| 
						 | 
					4571205871 | ||
| 
						 | 
					34c3763421 | ||
| 
						 | 
					ed6ae23dc0 | ||
| 
						 | 
					23839b05b0 | ||
| 
						 | 
					6aed3bb0b5 | ||
| 
						 | 
					1ae62cde05 | ||
| 
						 | 
					4e7e5bb783 | ||
| 
						 | 
					efc46613bb | ||
| 
						 | 
					218ef16160 | ||
| 
						 | 
					fb0a577d16 | ||
| 
						 | 
					19b5e7c781 | ||
| 
						 | 
					0794c0edf7 | ||
| 
						 | 
					89515bb896 | ||
| 
						 | 
					318aecb7bc | ||
| 
						 | 
					a4b857a034 | ||
| 
						 | 
					d82230dea4 | ||
| 
						 | 
					cb242d8efb | ||
| 
						 | 
					aa8f780e4e | ||
| 
						 | 
					3ed0880c6e | ||
| 
						 | 
					40c68e6399 | ||
| 
						 | 
					65500736da | ||
| 
						 | 
					b9ab97eb7f | ||
| 
						 | 
					34ef98e0cd | ||
| 
						 | 
					825f50262c | ||
| 
						 | 
					a6079936e8 | ||
| 
						 | 
					542b7a6f20 | ||
| 
						 | 
					2a8c3977a4 | ||
| 
						 | 
					515ea7caf8 | ||
| 
						 | 
					65993bd77f | ||
| 
						 | 
					54e07ccfdd | ||
| 
						 | 
					2e6fcec1c3 | ||
| 
						 | 
					f992fcebe1 | ||
| 
						 | 
					280c838735 | ||
| 
						 | 
					445dfb3d91 | ||
| 
						 | 
					bc616ecdee | ||
| 
						 | 
					895c9b12e6 | ||
| 
						 | 
					95e68fce57 | ||
| 
						 | 
					9f4f771774 | ||
| 
						 | 
					05290593e9 | ||
| 
						 | 
					2aa2d9d4bb | ||
| 
						 | 
					7b690d0785 | ||
| 
						 | 
					39fe2fd7b6 | ||
| 
						 | 
					b661b4737e | ||
| 
						 | 
					9ca8975baf | ||
| 
						 | 
					5f7eeeae30 | ||
| 
						 | 
					537656303a | ||
| 
						 | 
					e7ba13f844 | ||
| 
						 | 
					64bf63c18a | ||
| 
						 | 
					ac3f638b35 | ||
| 
						 | 
					a3520ba51e | ||
| 
						 | 
					8e0b7fce7f | ||
| 
						 | 
					52f5bea215 | ||
| 
						 | 
					26b4e5498f | ||
| 
						 | 
					ce733628b2 | ||
| 
						 | 
					307320b3ff | ||
| 
						 | 
					504b2e1ecf | ||
| 
						 | 
					73e452edc0 | ||
| 
						 | 
					bbeb97e93c | ||
| 
						 | 
					e6d65f3549 | ||
| 
						 | 
					1ab4cf68d7 | ||
| 
						 | 
					f20c8b7d99 | ||
| 
						 | 
					17a067752f | ||
| 
						 | 
					7fcaf6510e | ||
| 
						 | 
					0713941812 | ||
| 
						 | 
					1d7d56d5e7 | ||
| 
						 | 
					ba9941891d | ||
| 
						 | 
					d3b313d8aa | ||
| 
						 | 
					e2f0f61862 | ||
| 
						 | 
					ecdcebce0c | ||
| 
						 | 
					5fbf71264e | ||
| 
						 | 
					8519e7660f | ||
| 
						 | 
					e75d1f62ec | ||
| 
						 | 
					1c9a9baf77 | ||
| 
						 | 
					44246e6973 | ||
| 
						 | 
					61f59a94e4 | ||
| 
						 | 
					a8a689f69a | ||
| 
						 | 
					e3bd22de8c | ||
| 
						 | 
					c7787aa1f0 | ||
| 
						 | 
					3870266131 | ||
| 
						 | 
					98cc19c637 | ||
| 
						 | 
					c1128c3156 | ||
| 
						 | 
					485944a782 | ||
| 
						 | 
					beb24adf7a | ||
| 
						 | 
					6de5f73d78 | ||
| 
						 | 
					35b51d151d | ||
| 
						 | 
					b2333d83d2 | ||
| 
						 | 
					7513f24ff3 | ||
| 
						 | 
					cb9231f453 | ||
| 
						 | 
					1c9230029d | ||
| 
						 | 
					e300b49c95 | ||
| 
						 | 
					f6cd35a631 | ||
| 
						 | 
					53cecb8909 | ||
| 
						 | 
					95188f6ce6 | ||
| 
						 | 
					8c7e8dab8e | ||
| 
						 | 
					305d2f60d0 | ||
| 
						 | 
					7cdb8db775 | ||
| 
						 | 
					7f14397262 | ||
| 
						 | 
					893e24ff98 | ||
| 
						 | 
					b60eb6d6ae | ||
| 
						 | 
					26a7fee869 | ||
| 
						 | 
					f8a04d0fc2 | ||
| 
						 | 
					6e1a43130d | ||
| 
						 | 
					906646704e | ||
| 
						 | 
					8fb9306272 | ||
| 
						 | 
					5aa93ba50c | ||
| 
						 | 
					26db493b0d | ||
| 
						 | 
					6459f410e7 | ||
| 
						 | 
					4072b91808 | ||
| 
						 | 
					8750bdd778 | ||
| 
						 | 
					60a8ee657a | ||
| 
						 | 
					ecf1bac5c7 | ||
| 
						 | 
					d04bdd2685 | ||
| 
						 | 
					165bd4f638 | ||
| 
						 | 
					9df59522d9 | ||
| 
						 | 
					7fc66e2de8 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -36,3 +36,5 @@ protractor/logs
 | 
			
		||||
 | 
			
		||||
# npm-debug log
 | 
			
		||||
npm-debug.log
 | 
			
		||||
 | 
			
		||||
package-lock.json
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								API.md
									
									
									
									
									
								
							@@ -372,6 +372,7 @@ Known hints:
 | 
			
		||||
 | 
			
		||||
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
 | 
			
		||||
* `range`: Indicates that the value is the "output" of a datum.  Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
 | 
			
		||||
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
 | 
			
		||||
 | 
			
		||||
##### The Time Conductor and Telemetry 
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
    "eventemitter3": "^1.2.0",
 | 
			
		||||
    "lodash": "3.10.1",
 | 
			
		||||
    "almond": "~0.3.2",
 | 
			
		||||
    "html2canvas": "^0.4.1"
 | 
			
		||||
    "html2canvas": "^0.4.1",
 | 
			
		||||
    "moment-timezone": "^0.5.13"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -933,9 +933,10 @@ Note that `templateUrl` is not supported for `containers`.
 | 
			
		||||
 | 
			
		||||
Controls provide options for the `mct-control` directive. 
 | 
			
		||||
 
 | 
			
		||||
Ten standard control types are included in the forms bundle:
 | 
			
		||||
These standard control types are included in the forms bundle:
 | 
			
		||||
 | 
			
		||||
* `textfield`: An area to enter plain text.
 | 
			
		||||
* `textfield`: A text input to enter plain text.
 | 
			
		||||
* `numberfield`: A text input to enter numbers.
 | 
			
		||||
* `select`: A drop-down list of options.
 | 
			
		||||
* `checkbox`: A box which may be checked/unchecked.
 | 
			
		||||
* `color`: A color picker.
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@
 | 
			
		||||
 | 
			
		||||
        var data = [];
 | 
			
		||||
 | 
			
		||||
        for (; nextStep < end; nextStep += step) {
 | 
			
		||||
        for (; nextStep < end && data.length < 5000; nextStep += step) {
 | 
			
		||||
            data.push({
 | 
			
		||||
                utc: nextStep,
 | 
			
		||||
                yesterday: nextStep - 60*60*24*1000,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 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([
 | 
			
		||||
    "./src/ImageTelemetryProvider",
 | 
			
		||||
    'legacyRegistry'
 | 
			
		||||
], function (
 | 
			
		||||
    ImageTelemetryProvider,
 | 
			
		||||
    legacyRegistry
 | 
			
		||||
) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
    legacyRegistry.register("example/imagery", {
 | 
			
		||||
        "name": "Imagery",
 | 
			
		||||
        "description": "Example of a component that produces image telemetry.",
 | 
			
		||||
        "extensions": {
 | 
			
		||||
            "components": [
 | 
			
		||||
                {
 | 
			
		||||
                    "implementation": ImageTelemetryProvider,
 | 
			
		||||
                    "type": "provider",
 | 
			
		||||
                    "provides": "telemetryService",
 | 
			
		||||
                    "depends": [
 | 
			
		||||
                        "$q",
 | 
			
		||||
                        "$timeout"
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "types": [
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "imagery",
 | 
			
		||||
                    "name": "Example Imagery",
 | 
			
		||||
                    "cssClass": "icon-image",
 | 
			
		||||
                    "features": "creation",
 | 
			
		||||
                    "description": "For development use. Creates example imagery data that mimics a live imagery stream.",
 | 
			
		||||
                    "priority": 10,
 | 
			
		||||
                    "model": {
 | 
			
		||||
                        "telemetry": {}
 | 
			
		||||
                    },
 | 
			
		||||
                    "telemetry": {
 | 
			
		||||
                        "source": "imagery",
 | 
			
		||||
                        "domains": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "name": "Time",
 | 
			
		||||
                                "key": "time",
 | 
			
		||||
                                "format": "utc"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "ranges": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "name": "Image",
 | 
			
		||||
                                "key": "url",
 | 
			
		||||
                                "format": "imageUrl"
 | 
			
		||||
                            }
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										140
									
								
								example/imagery/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								example/imagery/plugin.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
 | 
			
		||||
], function(
 | 
			
		||||
 | 
			
		||||
) {
 | 
			
		||||
    function ImageryPlugin() {
 | 
			
		||||
 | 
			
		||||
        var IMAGE_SAMPLES = [
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
        function pointForTimestamp(timestamp) {
 | 
			
		||||
            return {
 | 
			
		||||
                utc: Math.floor(timestamp / 5000) * 5000,
 | 
			
		||||
                url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var realtimeProvider = {
 | 
			
		||||
            supportsSubscribe: function (domainObject) {
 | 
			
		||||
                return domainObject.type === 'example.imagery';
 | 
			
		||||
            },
 | 
			
		||||
            subscribe: function (domainObject, callback) {
 | 
			
		||||
                var interval = setInterval(function () {
 | 
			
		||||
                    callback(pointForTimestamp(Date.now()));
 | 
			
		||||
                }, 5000);
 | 
			
		||||
 | 
			
		||||
                return function (interval) {
 | 
			
		||||
                    clearInterval(interval);
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var historicalProvider = {
 | 
			
		||||
            supportsRequest: function (domainObject, options) {
 | 
			
		||||
                return domainObject.type === 'example.imagery'
 | 
			
		||||
                    && options.strategy !== 'latest';
 | 
			
		||||
            },
 | 
			
		||||
            request: function (domainObject, options) {
 | 
			
		||||
                var start = options.start;
 | 
			
		||||
                var end = options.end;
 | 
			
		||||
                var data = [];
 | 
			
		||||
                while (start < end && data.length < 5000) {
 | 
			
		||||
                    data.push(pointForTimestamp(start));
 | 
			
		||||
                    start += 5000;
 | 
			
		||||
                }
 | 
			
		||||
                return Promise.resolve(data);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var ladProvider = {
 | 
			
		||||
            supportsRequest: function (domainObject, options) {
 | 
			
		||||
                return domainObject.type === 'example.imagery' &&
 | 
			
		||||
                    options.strategy === 'latest';
 | 
			
		||||
            },
 | 
			
		||||
            request: function (domainObject, options) {
 | 
			
		||||
                return Promise.resolve([pointForTimestamp(Date.now())]);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return function install(openmct) {
 | 
			
		||||
            openmct.types.addType('example.imagery', {
 | 
			
		||||
                key: 'example.imagery',
 | 
			
		||||
                name: 'Example Imagery',
 | 
			
		||||
                cssClass: 'icon-image',
 | 
			
		||||
                description: 'For development use. Creates example imagery ' +
 | 
			
		||||
                    'data that mimics a live imagery stream.',
 | 
			
		||||
                creatable: true,
 | 
			
		||||
                initialize: function (object) {
 | 
			
		||||
                    object.telemetry = {
 | 
			
		||||
                        values: [
 | 
			
		||||
                            {
 | 
			
		||||
                                name: 'Time',
 | 
			
		||||
                                key: 'utc',
 | 
			
		||||
                                format: 'utc',
 | 
			
		||||
                                hints: {
 | 
			
		||||
                                    domain: 1
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                name: 'Image',
 | 
			
		||||
                                key: 'url',
 | 
			
		||||
                                format: 'image',
 | 
			
		||||
                                hints: {
 | 
			
		||||
                                    image: 1
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            openmct.telemetry.addProvider(realtimeProvider);
 | 
			
		||||
            openmct.telemetry.addProvider(historicalProvider);
 | 
			
		||||
            openmct.telemetry.addProvider(ladProvider);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ImageryPlugin;
 | 
			
		||||
});
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 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*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining ImageTelemetry. Created by vwoeltje on 06/22/15.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var firstObservedTime = Date.now(),
 | 
			
		||||
            images = [
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg",
 | 
			
		||||
                "https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
 | 
			
		||||
 | 
			
		||||
            ].map(function (url, index) {
 | 
			
		||||
                return {
 | 
			
		||||
                    timestamp: firstObservedTime + 1000 * index,
 | 
			
		||||
                    url: url
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function ImageTelemetry() {
 | 
			
		||||
            return {
 | 
			
		||||
                getPointCount: function () {
 | 
			
		||||
                    return Math.floor((Date.now() - firstObservedTime) / 1000);
 | 
			
		||||
                },
 | 
			
		||||
                getDomainValue: function (i, domain) {
 | 
			
		||||
                    return images[i % images.length].timestamp;
 | 
			
		||||
                },
 | 
			
		||||
                getRangeValue: function (i, range) {
 | 
			
		||||
                    return images[i % images.length].url;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ImageTelemetry;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,115 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 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*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining ImageTelemetryProvider. Created by vwoeltje on 06/22/15.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ["./ImageTelemetry"],
 | 
			
		||||
    function (ImageTelemetry) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function ImageTelemetryProvider($q, $timeout) {
 | 
			
		||||
            var subscriptions = [];
 | 
			
		||||
 | 
			
		||||
            //
 | 
			
		||||
            function matchesSource(request) {
 | 
			
		||||
                return request.source === "imagery";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Used internally; this will be repacked by doPackage
 | 
			
		||||
            function generateData(request) {
 | 
			
		||||
                return {
 | 
			
		||||
                    key: request.key,
 | 
			
		||||
                    telemetry: new ImageTelemetry()
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //
 | 
			
		||||
            function doPackage(results) {
 | 
			
		||||
                var packaged = {};
 | 
			
		||||
                results.forEach(function (result) {
 | 
			
		||||
                    packaged[result.key] = result.telemetry;
 | 
			
		||||
                });
 | 
			
		||||
                // Format as expected (sources -> keys -> telemetry)
 | 
			
		||||
                return { imagery: packaged };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function requestTelemetry(requests) {
 | 
			
		||||
                return $timeout(function () {
 | 
			
		||||
                    return doPackage(requests.filter(matchesSource).map(generateData));
 | 
			
		||||
                }, 0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function handleSubscriptions() {
 | 
			
		||||
                subscriptions.forEach(function (subscription) {
 | 
			
		||||
                    var requests = subscription.requests;
 | 
			
		||||
                    subscription.callback(doPackage(
 | 
			
		||||
                        requests.filter(matchesSource).map(generateData)
 | 
			
		||||
                    ));
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function startGenerating() {
 | 
			
		||||
                $timeout(function () {
 | 
			
		||||
                    handleSubscriptions();
 | 
			
		||||
                    if (subscriptions.length > 0) {
 | 
			
		||||
                        startGenerating();
 | 
			
		||||
                    }
 | 
			
		||||
                }, 1000);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function subscribe(callback, requests) {
 | 
			
		||||
                var subscription = {
 | 
			
		||||
                    callback: callback,
 | 
			
		||||
                    requests: requests
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                function unsubscribe() {
 | 
			
		||||
                    subscriptions = subscriptions.filter(function (s) {
 | 
			
		||||
                        return s !== subscription;
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                subscriptions.push(subscription);
 | 
			
		||||
 | 
			
		||||
                if (subscriptions.length === 1) {
 | 
			
		||||
                    startGenerating();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return unsubscribe;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                requestTelemetry: requestTelemetry,
 | 
			
		||||
                subscribe: subscribe
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ImageTelemetryProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -143,6 +143,17 @@
 | 
			
		||||
 | 
			
		||||
    // Example grid of glyphs
 | 
			
		||||
    .items-holder.grid {
 | 
			
		||||
        table.details {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
            td {
 | 
			
		||||
                font-size: inherit;
 | 
			
		||||
                &.label {
 | 
			
		||||
                    color: pushBack($colorBodyFg, 10%);
 | 
			
		||||
                    text-transform: uppercase;
 | 
			
		||||
                    white-space: nowrap;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .item.glyph-item,
 | 
			
		||||
        .item.swatch-item {
 | 
			
		||||
            margin-bottom: 50px;
 | 
			
		||||
@@ -155,15 +166,6 @@
 | 
			
		||||
                margin: $interiorMarginLg 0;
 | 
			
		||||
                text-align: center;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            table.details td {
 | 
			
		||||
                font-size: inherit;
 | 
			
		||||
                &.label {
 | 
			
		||||
                    color: pushBack($colorBodyFg, 10%);
 | 
			
		||||
                    text-transform: uppercase;
 | 
			
		||||
                    white-space: nowrap;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .item.glyph-item {
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,9 @@
 | 
			
		||||
{ 'meaning': 'General usage plus symbol; used in timer object', 'cssClass': 'icon-plus', 'cssContent': 'e926', 'htmlEntity': '&#xe926' },
 | 
			
		||||
{ 'meaning': 'Delete', 'cssClass': 'icon-trash', 'cssContent': 'e927', 'htmlEntity': '&#xe927' },
 | 
			
		||||
{ 'meaning': 'Close, remove', 'cssClass': 'icon-x', 'cssContent': 'e928', 'htmlEntity': '&#xe928' },
 | 
			
		||||
{ 'meaning': 'Enclosing, inclusive; used in Time Conductor', 'cssClass': 'icon-brackets', 'cssContent': 'e929', 'htmlEntity': '&#xe929' }
 | 
			
		||||
{ 'meaning': 'Enclosing, inclusive; used in Time Conductor', 'cssClass': 'icon-brackets', 'cssContent': 'e929', 'htmlEntity': '&#xe929' },
 | 
			
		||||
{ 'meaning': 'Something is targeted', 'cssClass': 'icon-crosshair', 'cssContent': 'e930', 'htmlEntity': '&#xe930' },
 | 
			
		||||
{ 'meaning': 'Draggable', 'cssClass': 'icon-grippy', 'cssContent': 'e931', 'htmlEntity': '&#xe931' }
 | 
			
		||||
]; controls= [{ 'meaning': 'Reset zoom/pam', 'cssClass': 'icon-arrows-out', 'cssContent': 'e1000', 'htmlEntity': '&#xe1000' },
 | 
			
		||||
{ 'meaning': 'Expand vertically', 'cssClass': 'icon-arrows-right-left', 'cssContent': 'e1001', 'htmlEntity': '&#xe1001' },
 | 
			
		||||
{ 'meaning': 'View scrolling', 'cssClass': 'icon-arrows-up-down', 'cssContent': 'e1002', 'htmlEntity': '&#xe1002' },
 | 
			
		||||
@@ -84,10 +86,17 @@
 | 
			
		||||
{ 'meaning': 'Image thumbs strip; view items grid', 'cssClass': 'icon-thumbs-strip', 'cssContent': 'e1033', 'htmlEntity': '&#xe1033' },
 | 
			
		||||
{ 'meaning': 'Two part item, both parts', 'cssClass': 'icon-two-parts-both', 'cssContent': 'e1034', 'htmlEntity': '&#xe1034' },
 | 
			
		||||
{ 'meaning': 'Two part item, one only', 'cssClass': 'icon-two-parts-one-only', 'cssContent': 'e1035', 'htmlEntity': '&#xe1035' },
 | 
			
		||||
{ 'meaning': 'Resync', 'cssClass': 'icon-resync', 'cssContent': 'e1036', 'htmlEntity': '&#xe1036' },
 | 
			
		||||
{ 'meaning': 'Reset', 'cssClass': 'icon-reset', 'cssContent': 'e1037', 'htmlEntity': '&#xe1037' },
 | 
			
		||||
{ 'meaning': 'Clear', 'cssClass': 'icon-x-in-circle', 'cssContent': 'e1038', 'htmlEntity': '&#xe1038' },
 | 
			
		||||
{ 'meaning': 'Brightness', 'cssClass': 'icon-brightness', 'cssContent': 'e1039', 'htmlEntity': '&#xe1039' },
 | 
			
		||||
{ 'meaning': 'Contrast', 'cssClass': 'icon-contrast', 'cssContent': 'e1040', 'htmlEntity': '&#xe1040' },
 | 
			
		||||
{ 'meaning': 'Expand', 'cssClass': 'icon-expand', 'cssContent': 'e1041', 'htmlEntity': '&#xe1041' }
 | 
			
		||||
{ 'meaning': 'Expand', 'cssClass': 'icon-expand', 'cssContent': 'e1041', 'htmlEntity': '&#xe1041' },
 | 
			
		||||
{ 'meaning': 'View items in a tabular list', 'cssClass': 'icon-list-view', 'cssContent': 'e1042', 'htmlEntity': '&#xe1042' },
 | 
			
		||||
{ 'meaning': 'Snap an object corner to a grid', 'cssClass': 'icon-grid-snap-to', 'cssContent': 'e1043', 'htmlEntity': '&#xe1043' },
 | 
			
		||||
{ 'meaning': 'Do not snap an object corner to a grid', 'cssClass': 'icon-grid-snap-no', 'cssContent': 'e1044', 'htmlEntity': '&#xe1044' },
 | 
			
		||||
{ 'meaning': 'Show an object frame in a Display Layout', 'cssClass': 'icon-frame-show', 'cssContent': 'e1045', 'htmlEntity': '&#xe1045' },
 | 
			
		||||
{ 'meaning': 'Do not show an object frame in a Display Layout', 'cssClass': 'icon-frame-hide', 'cssContent': 'e1046', 'htmlEntity': '&#xe1046' }
 | 
			
		||||
]; objects= [{ 'meaning': 'Activity', 'cssClass': 'icon-activity', 'cssContent': 'e1100', 'htmlEntity': '&#xe1100' },
 | 
			
		||||
{ 'meaning': 'Activity Mode', 'cssClass': 'icon-activity-mode', 'cssContent': 'e1101', 'htmlEntity': '&#xe1101' },
 | 
			
		||||
{ 'meaning': 'Auto-flow Tabular view', 'cssClass': 'icon-autoflow-tabular', 'cssContent': 'e1102', 'htmlEntity': '&#xe1102' },
 | 
			
		||||
@@ -117,7 +126,8 @@
 | 
			
		||||
{ 'meaning': 'Timeline object', 'cssClass': 'icon-timeline', 'cssContent': 'e1126', 'htmlEntity': '&#xe1126' },
 | 
			
		||||
{ 'meaning': 'Timer object', 'cssClass': 'icon-timer', 'cssContent': 'e1127', 'htmlEntity': '&#xe1127' },
 | 
			
		||||
{ 'meaning': 'Data Topic', 'cssClass': 'icon-topic', 'cssContent': 'e1128', 'htmlEntity': '&#xe1128' },
 | 
			
		||||
{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&#xe1129' }
 | 
			
		||||
{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&#xe1129' },
 | 
			
		||||
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' }
 | 
			
		||||
];
 | 
			
		||||
"></div>
 | 
			
		||||
 | 
			
		||||
@@ -141,8 +151,8 @@
 | 
			
		||||
        <h2>How to Use Glyphs</h2>
 | 
			
		||||
        <div class="cols cols1-1">
 | 
			
		||||
            <div class="col">
 | 
			
		||||
                <p>The easiest way to use a glyph is to include its CSS class in element. The CSS adds a psuedo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
 | 
			
		||||
                <p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach for some reason.</p>
 | 
			
		||||
                <p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a psuedo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
 | 
			
		||||
                <p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach.</p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-example><a class="s-button icon-gear" title="Settings"></a>
 | 
			
		||||
<br /><br />
 | 
			
		||||
@@ -160,8 +170,8 @@
 | 
			
		||||
            <div class="item glyph-item" ng-repeat="glyph in general">
 | 
			
		||||
                <div class="glyph" ng-class="glyph.cssClass"></div>
 | 
			
		||||
                <table class="details">
 | 
			
		||||
                    <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">Class</td><td class="value">.{{glyph.cssClass}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">CSS Content</td><td class="value">\{{glyph.cssContent}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">HTML Entity</td><td class="value">{{glyph.htmlEntity}}</td></tr>
 | 
			
		||||
                </table>
 | 
			
		||||
@@ -176,8 +186,8 @@
 | 
			
		||||
            <div class="item glyph-item" ng-repeat="glyph in controls">
 | 
			
		||||
                <div class="glyph" ng-class="glyph.cssClass"></div>
 | 
			
		||||
                <table class="details">
 | 
			
		||||
                    <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">Class</td><td class="value">.{{glyph.cssClass}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">CSS Content</td><td class="value">\{{glyph.cssContent}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">HTML Entity</td><td class="value">{{glyph.htmlEntity}}</td></tr>
 | 
			
		||||
                </table>
 | 
			
		||||
@@ -192,8 +202,8 @@
 | 
			
		||||
            <div class="item glyph-item" ng-repeat="glyph in objects">
 | 
			
		||||
                <div class="glyph" ng-class="glyph.cssClass"></div>
 | 
			
		||||
                <table class="details">
 | 
			
		||||
                    <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">Class</td><td class="value">.{{glyph.cssClass}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">CSS Content</td><td class="value">\{{glyph.cssContent}}</td></tr>
 | 
			
		||||
                    <tr><td class="label">HTML Entity</td><td class="value">{{glyph.htmlEntity}}</td></tr>
 | 
			
		||||
                </table>
 | 
			
		||||
 
 | 
			
		||||
@@ -77,11 +77,15 @@ if (process.env.NODE_ENV === 'development') {
 | 
			
		||||
gulp.task('scripts', function () {
 | 
			
		||||
    var requirejsOptimize = require('gulp-requirejs-optimize');
 | 
			
		||||
    var replace = require('gulp-replace-task');
 | 
			
		||||
    var header = require('gulp-header');
 | 
			
		||||
    var comment = fs.readFileSync('src/about.frag');
 | 
			
		||||
 | 
			
		||||
    return gulp.src(paths.main)
 | 
			
		||||
        .pipe(sourcemaps.init())
 | 
			
		||||
        .pipe(requirejsOptimize(options.requirejsOptimize))
 | 
			
		||||
        .pipe(sourcemaps.write('.'))
 | 
			
		||||
        .pipe(replace(options.replace))
 | 
			
		||||
        .pipe(header(comment, options.replace.variables))
 | 
			
		||||
        .pipe(gulp.dest(paths.dist));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								index.html
									
									
									
									
									
								
							@@ -32,7 +32,6 @@
 | 
			
		||||
 | 
			
		||||
        require(['openmct'], function (openmct) {
 | 
			
		||||
            [
 | 
			
		||||
                'example/imagery',
 | 
			
		||||
                'example/eventGenerator',
 | 
			
		||||
                'example/styleguide'
 | 
			
		||||
            ].forEach(
 | 
			
		||||
@@ -42,7 +41,29 @@
 | 
			
		||||
            openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
            openmct.install(openmct.plugins.Espresso());
 | 
			
		||||
            openmct.install(openmct.plugins.Generator());
 | 
			
		||||
            openmct.install(openmct.plugins.ExampleImagery());
 | 
			
		||||
            openmct.install(openmct.plugins.UTCTimeSystem());
 | 
			
		||||
            openmct.install(openmct.plugins.Conductor({
 | 
			
		||||
                menuOptions: [
 | 
			
		||||
                    {
 | 
			
		||||
                        name: "Fixed",
 | 
			
		||||
                        timeSystem: 'utc',
 | 
			
		||||
                        bounds: {
 | 
			
		||||
                            start: Date.now() - 30 * 60 * 1000,
 | 
			
		||||
                            end: Date.now()
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        name: "Realtime",
 | 
			
		||||
                        timeSystem: 'utc',
 | 
			
		||||
                        clock: 'local',
 | 
			
		||||
                        clockOffsets: {
 | 
			
		||||
                            start: -25 * 60 * 1000,
 | 
			
		||||
                            end: 5 * 60 * 1000
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }));
 | 
			
		||||
            openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
 | 
			
		||||
            openmct.time.timeSystem('utc');
 | 
			
		||||
            openmct.start();
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ requirejs.config({
 | 
			
		||||
        "html2canvas": "bower_components/html2canvas/build/html2canvas.min",
 | 
			
		||||
        "moment": "bower_components/moment/moment",
 | 
			
		||||
        "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
 | 
			
		||||
        "moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
 | 
			
		||||
        "saveAs": "bower_components/FileSaver.js/FileSaver.min",
 | 
			
		||||
        "screenfull": "bower_components/screenfull/dist/screenfull.min",
 | 
			
		||||
        "text": "bower_components/text/text",
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
    "git-rev-sync": "^1.4.0",
 | 
			
		||||
    "glob": ">= 3.0.0",
 | 
			
		||||
    "gulp": "^3.9.0",
 | 
			
		||||
    "gulp-header": "^1.8.8",
 | 
			
		||||
    "gulp-jscs": "^3.0.2",
 | 
			
		||||
    "gulp-jshint": "^2.0.0",
 | 
			
		||||
    "gulp-jshint-html-reporter": "^0.1.3",
 | 
			
		||||
 
 | 
			
		||||
@@ -240,7 +240,7 @@ define([
 | 
			
		||||
            "views": [
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "items",
 | 
			
		||||
                    "name": "Items",
 | 
			
		||||
                    "name": "Grid",
 | 
			
		||||
                    "cssClass": "icon-thumbs-strip",
 | 
			
		||||
                    "description": "Grid of available items",
 | 
			
		||||
                    "template": itemsTemplate,
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
         ng-controller="PaneController as modelPaneTree"
 | 
			
		||||
         ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'">
 | 
			
		||||
        <mct-split-pane class='abs contents'
 | 
			
		||||
                        anchor='left'>
 | 
			
		||||
                        anchor='left' alias="leftSide">
 | 
			
		||||
            <div class='split-pane-component treeview pane left'>
 | 
			
		||||
                <div class="abs holder l-flex-col holder-treeview-elements">
 | 
			
		||||
                    <mct-representation key="'create-button'"
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
                     ng-controller="InspectorPaneController as modelPaneInspect"
 | 
			
		||||
                     ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'">
 | 
			
		||||
 | 
			
		||||
                    <mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
 | 
			
		||||
                    <mct-split-pane class='l-object-and-inspector contents abs' anchor='right' alias="rightSide">
 | 
			
		||||
                        <div class='split-pane-component t-object pane primary-pane left'>
 | 
			
		||||
                            <mct-representation mct-object="navigatedObject"
 | 
			
		||||
                                                key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
 | 
			
		||||
@@ -87,4 +87,3 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <mct-include key="'bottombar'"></mct-include>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,4 @@
 | 
			
		||||
        key="'menu-arrow'"
 | 
			
		||||
        mct-object='domainObject'
 | 
			
		||||
        class="flex-elem context-available-w"></mct-representation>
 | 
			
		||||
</span>
 | 
			
		||||
<a class="s-button icon-expand t-btn-view-large"
 | 
			
		||||
   title="View large"
 | 
			
		||||
   mct-trigger-modal>
 | 
			
		||||
</a>
 | 
			
		||||
</span>
 | 
			
		||||
@@ -25,8 +25,8 @@
 | 
			
		||||
 * @namespace platform/commonUI/browse
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
    ['lodash'],
 | 
			
		||||
    function (_) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The BrowseController is used to populate the initial scope in Browse
 | 
			
		||||
@@ -157,12 +157,28 @@ define(
 | 
			
		||||
            // (e.g. bookmarks to pages in OpenMCT) and prevent them.  Instead,
 | 
			
		||||
            // navigate to the path ourselves, which results in it being
 | 
			
		||||
            // properly set.
 | 
			
		||||
            $scope.$on('$routeChangeStart', function (event, route) {
 | 
			
		||||
            $scope.$on('$routeChangeStart', function (event, route, oldRoute) {
 | 
			
		||||
                if (route.$$route === $route.current.$$route) {
 | 
			
		||||
                    if (route.pathParams.ids &&
 | 
			
		||||
                        route.pathParams.ids !== $route.current.pathParams.ids) {
 | 
			
		||||
 | 
			
		||||
                        var otherParams = _.omit(route.params, 'ids');
 | 
			
		||||
                        var oldOtherParams = _.omit(oldRoute.params, 'ids');
 | 
			
		||||
                        var deletedParams = _.omit(oldOtherParams, _.keys(otherParams));
 | 
			
		||||
 | 
			
		||||
                        event.preventDefault();
 | 
			
		||||
                        navigateToPath(route.pathParams.ids.split('/'));
 | 
			
		||||
 | 
			
		||||
                        navigateToPath(route.pathParams.ids.split('/'))
 | 
			
		||||
                            .then(function () {
 | 
			
		||||
                                if (!_.isEqual(otherParams, oldOtherParams)) {
 | 
			
		||||
                                    _.forEach(otherParams, function (v, k) {
 | 
			
		||||
                                        $location.search(k, v);
 | 
			
		||||
                                    });
 | 
			
		||||
                                    _.forEach(deletedParams, function (k) {
 | 
			
		||||
                                        $location.search(k, null);
 | 
			
		||||
                                    });
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        navigateToPath([]);
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -374,7 +374,8 @@ define([
 | 
			
		||||
                    "depends": [
 | 
			
		||||
                        "$parse",
 | 
			
		||||
                        "$log",
 | 
			
		||||
                        "$interval"
 | 
			
		||||
                        "$interval",
 | 
			
		||||
                        "$window"
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,207 +1,144 @@
 | 
			
		||||
{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"name": "openmct-symbols-12px",
 | 
			
		||||
		"lastOpened": 1467322505818,
 | 
			
		||||
		"created": 1467322503679
 | 
			
		||||
	},
 | 
			
		||||
	"iconSets": [
 | 
			
		||||
		{
 | 
			
		||||
			"selection": [
 | 
			
		||||
				{
 | 
			
		||||
					"order": 4,
 | 
			
		||||
					"id": 2,
 | 
			
		||||
					"prevSize": 12,
 | 
			
		||||
					"code": 59671,
 | 
			
		||||
					"name": "icon12-eye-open",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 7,
 | 
			
		||||
					"id": 5,
 | 
			
		||||
					"prevSize": 12,
 | 
			
		||||
					"code": 921607,
 | 
			
		||||
					"name": "icon12-pane-collapse-left",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 8,
 | 
			
		||||
					"id": 4,
 | 
			
		||||
					"prevSize": 12,
 | 
			
		||||
					"code": 921608,
 | 
			
		||||
					"name": "icon12-pane-collapse-right",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 6,
 | 
			
		||||
					"id": 3,
 | 
			
		||||
					"prevSize": 12,
 | 
			
		||||
					"code": 921865,
 | 
			
		||||
					"name": "icon12-folder",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			"id": 0,
 | 
			
		||||
			"metadata": {
 | 
			
		||||
				"name": "openmct-symbols-12px",
 | 
			
		||||
				"importSize": {
 | 
			
		||||
					"width": 320,
 | 
			
		||||
					"height": 384
 | 
			
		||||
				},
 | 
			
		||||
				"designer": "Charles Hacskaylo"
 | 
			
		||||
			},
 | 
			
		||||
			"height": 1024,
 | 
			
		||||
			"prevSize": 12,
 | 
			
		||||
			"icons": [
 | 
			
		||||
				{
 | 
			
		||||
					"id": 2,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M512 86c-257.92 0-471.28 185.147-506.667 426 35.493 240.853 248.853 426 506.667 426s471.28-185.147 506.667-426c-35.387-240.853-248.747-426-506.667-426zM738.373 685.2c-131.048 109.517-321.699 109.517-452.747 0-53.328-44.749-90.832-105.477-106.96-173.2 16.101-67.707 53.565-128.435 106.853-173.2 131.048-109.517 321.699-109.517 452.747 0 53.368 44.733 90.909 105.464 107.067 173.2-16.128 67.723-53.632 128.451-106.96 173.2z",
 | 
			
		||||
						"M682.667 512c0 94.257-76.41 170.667-170.667 170.667s-170.667-76.41-170.667-170.667c0-94.257 76.41-170.667 170.667-170.667s170.667 76.41 170.667 170.667z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon12-eye-open"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"1161751": [
 | 
			
		||||
							{
 | 
			
		||||
								"f": 0
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								"f": 0
 | 
			
		||||
							}
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": 5,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M0 0h170.667v1024h-170.667v-1024z",
 | 
			
		||||
						"M853.333 256h-256v-256l-341.333 426.667 341.333 426.667v-256h256v-341.333z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [
 | 
			
		||||
						{
 | 
			
		||||
							"opacity": 1
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							"opacity": 1
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"width": 853,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon12-pane-collapse-left"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"1161751": [
 | 
			
		||||
							{},
 | 
			
		||||
							{}
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": 4,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M682.667 0h170.667v1024h-170.667v-1024z",
 | 
			
		||||
						"M0 256h256v-256l341.333 426.667-341.333 426.667v-256h-256v-341.333z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [
 | 
			
		||||
						{
 | 
			
		||||
							"opacity": 1
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							"opacity": 1
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"width": 853,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon12-pane-collapse-right"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"1161751": [
 | 
			
		||||
							{},
 | 
			
		||||
							{}
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": 3,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M938.667 170.667h-341.333l-110.32-110.32c-33.2-33.2-98.667-60.347-145.68-60.347h-256c-47.073 0.136-85.197 38.26-85.333 85.32l-0 341.346c0.136-47.073 38.26-85.197 85.32-85.333l853.346-0c47.073 0.136 85.197 38.26 85.333 85.32l0-170.654c-0.136-47.073-38.26-85.197-85.32-85.333z",
 | 
			
		||||
						"M85.333 426.667h853.333c47.128 0 85.333 38.205 85.333 85.333v426.667c0 47.128-38.205 85.333-85.333 85.333h-853.333c-47.128 0-85.333-38.205-85.333-85.333v-426.667c0-47.128 38.205-85.333 85.333-85.333z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon12-folder"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"1161751": [
 | 
			
		||||
							{
 | 
			
		||||
								"f": 0
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								"f": 0
 | 
			
		||||
							}
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			"invisible": false,
 | 
			
		||||
			"colorThemes": [
 | 
			
		||||
				[
 | 
			
		||||
					[
 | 
			
		||||
						0,
 | 
			
		||||
						0,
 | 
			
		||||
						0,
 | 
			
		||||
						1
 | 
			
		||||
					],
 | 
			
		||||
					[
 | 
			
		||||
						0,
 | 
			
		||||
						161,
 | 
			
		||||
						75,
 | 
			
		||||
						1
 | 
			
		||||
					]
 | 
			
		||||
				]
 | 
			
		||||
			],
 | 
			
		||||
			"colorThemeIdx": 0
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
	"preferences": {
 | 
			
		||||
		"showGlyphs": true,
 | 
			
		||||
		"showCodes": true,
 | 
			
		||||
		"showQuickUse": true,
 | 
			
		||||
		"showQuickUse2": true,
 | 
			
		||||
		"showSVGs": true,
 | 
			
		||||
		"fontPref": {
 | 
			
		||||
			"prefix": "icon-",
 | 
			
		||||
			"metadata": {
 | 
			
		||||
				"fontFamily": "openmct-symbols-12px",
 | 
			
		||||
				"majorVersion": 1,
 | 
			
		||||
				"minorVersion": 0
 | 
			
		||||
			},
 | 
			
		||||
			"metrics": {
 | 
			
		||||
				"emSize": 1024,
 | 
			
		||||
				"baseline": 6.25,
 | 
			
		||||
				"whitespace": 50
 | 
			
		||||
			},
 | 
			
		||||
			"embed": false
 | 
			
		||||
		},
 | 
			
		||||
		"imagePref": {
 | 
			
		||||
			"prefix": "icon-",
 | 
			
		||||
			"png": true,
 | 
			
		||||
			"useClassSelector": true,
 | 
			
		||||
			"color": 0,
 | 
			
		||||
			"bgColor": 16777215
 | 
			
		||||
		},
 | 
			
		||||
		"historySize": 100,
 | 
			
		||||
		"gridSize": 16
 | 
			
		||||
	},
 | 
			
		||||
	"uid": -1
 | 
			
		||||
}
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "name": "openmct-symbols-12px",
 | 
			
		||||
    "lastOpened": 0,
 | 
			
		||||
    "created": 1502213994889
 | 
			
		||||
  },
 | 
			
		||||
  "iconSets": [
 | 
			
		||||
    {
 | 
			
		||||
      "selection": [
 | 
			
		||||
        {
 | 
			
		||||
          "order": 9,
 | 
			
		||||
          "id": 6,
 | 
			
		||||
          "name": "icon12-crosshair",
 | 
			
		||||
          "prevSize": 12,
 | 
			
		||||
          "code": 59696,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 6,
 | 
			
		||||
          "id": 3,
 | 
			
		||||
          "prevSize": 12,
 | 
			
		||||
          "code": 921865,
 | 
			
		||||
          "name": "icon12-folder",
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "id": 0,
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "name": "openmct-symbols-12px",
 | 
			
		||||
        "importSize": {
 | 
			
		||||
          "width": 384,
 | 
			
		||||
          "height": 384
 | 
			
		||||
        },
 | 
			
		||||
        "designer": "Charles Hacskaylo"
 | 
			
		||||
      },
 | 
			
		||||
      "height": 1024,
 | 
			
		||||
      "prevSize": 12,
 | 
			
		||||
      "icons": [
 | 
			
		||||
        {
 | 
			
		||||
          "id": 6,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M597.333 0h-170.667v256h170.667v-256z",
 | 
			
		||||
            "M1024 426.667h-256v170.667h256v-170.667z",
 | 
			
		||||
            "M597.333 768h-170.667v256h170.667v-256z",
 | 
			
		||||
            "M256 426.667h-256v170.667h256v-170.667z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "grid": 0,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon12-crosshair"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 3,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M938.667 170.667h-341.333l-110.32-110.32c-33.2-33.2-98.667-60.347-145.68-60.347h-256c-47.073 0.136-85.197 38.26-85.333 85.32l-0 341.346c0.136-47.073 38.26-85.197 85.32-85.333l853.346-0c47.073 0.136 85.197 38.26 85.333 85.32l0-170.654c-0.136-47.073-38.26-85.197-85.32-85.333z",
 | 
			
		||||
            "M85.333 426.667h853.333c47.128 0 85.333 38.205 85.333 85.333v426.667c0 47.128-38.205 85.333-85.333 85.333h-853.333c-47.128 0-85.333-38.205-85.333-85.333v-426.667c0-47.128 38.205-85.333 85.333-85.333z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "grid": 0,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon12-folder"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751": [
 | 
			
		||||
              {
 | 
			
		||||
                "f": 0
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                "f": 0
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "invisible": false,
 | 
			
		||||
      "colorThemes": [
 | 
			
		||||
        [
 | 
			
		||||
          [
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            1
 | 
			
		||||
          ],
 | 
			
		||||
          [
 | 
			
		||||
            0,
 | 
			
		||||
            161,
 | 
			
		||||
            75,
 | 
			
		||||
            1
 | 
			
		||||
          ]
 | 
			
		||||
        ]
 | 
			
		||||
      ],
 | 
			
		||||
      "colorThemeIdx": 0
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "preferences": {
 | 
			
		||||
    "showGlyphs": true,
 | 
			
		||||
    "showCodes": true,
 | 
			
		||||
    "showQuickUse": true,
 | 
			
		||||
    "showQuickUse2": true,
 | 
			
		||||
    "showSVGs": true,
 | 
			
		||||
    "fontPref": {
 | 
			
		||||
      "prefix": "icon-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "fontFamily": "openmct-symbols-12px",
 | 
			
		||||
        "majorVersion": 1,
 | 
			
		||||
        "minorVersion": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metrics": {
 | 
			
		||||
        "emSize": 1024,
 | 
			
		||||
        "baseline": 6.25,
 | 
			
		||||
        "whitespace": 50
 | 
			
		||||
      },
 | 
			
		||||
      "embed": false
 | 
			
		||||
    },
 | 
			
		||||
    "imagePref": {
 | 
			
		||||
      "prefix": "icon-",
 | 
			
		||||
      "png": true,
 | 
			
		||||
      "useClassSelector": true,
 | 
			
		||||
      "color": 0,
 | 
			
		||||
      "bgColor": 16777215
 | 
			
		||||
    },
 | 
			
		||||
    "historySize": 100,
 | 
			
		||||
    "gridSize": 16
 | 
			
		||||
  },
 | 
			
		||||
  "uid": -1,
 | 
			
		||||
  "time": 1502216581486
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "name": "openmct-symbols-16px",
 | 
			
		||||
    "lastOpened": 1487197651675,
 | 
			
		||||
    "created": 1487194069058
 | 
			
		||||
    "lastOpened": 0,
 | 
			
		||||
    "created": 1502487054429
 | 
			
		||||
  },
 | 
			
		||||
  "iconSets": [
 | 
			
		||||
    {
 | 
			
		||||
@@ -252,13 +252,29 @@
 | 
			
		||||
          "code": 59689,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 134,
 | 
			
		||||
          "id": 114,
 | 
			
		||||
          "name": "icon-crosshair",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 59696,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 127,
 | 
			
		||||
          "id": 109,
 | 
			
		||||
          "name": "icon-grippy-v2",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 59697,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 48,
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "name": "icon-arrows-out",
 | 
			
		||||
          "id": 43,
 | 
			
		||||
          "code": 921600,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 49,
 | 
			
		||||
@@ -266,7 +282,7 @@
 | 
			
		||||
          "name": "icon-arrows-right-left",
 | 
			
		||||
          "id": 44,
 | 
			
		||||
          "code": 921601,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 50,
 | 
			
		||||
@@ -274,7 +290,7 @@
 | 
			
		||||
          "name": "icon-arrows-up-down",
 | 
			
		||||
          "id": 45,
 | 
			
		||||
          "code": 921602,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 9,
 | 
			
		||||
@@ -282,7 +298,7 @@
 | 
			
		||||
          "name": "icon-bullet",
 | 
			
		||||
          "id": 4,
 | 
			
		||||
          "code": 921604,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 25,
 | 
			
		||||
@@ -290,7 +306,7 @@
 | 
			
		||||
          "name": "icon-calendar",
 | 
			
		||||
          "id": 20,
 | 
			
		||||
          "code": 921605,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 30,
 | 
			
		||||
@@ -298,7 +314,7 @@
 | 
			
		||||
          "name": "icon-chain-links",
 | 
			
		||||
          "id": 25,
 | 
			
		||||
          "code": 921606,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 109,
 | 
			
		||||
@@ -306,7 +322,7 @@
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921607,
 | 
			
		||||
          "name": "icon-pane-collapse-left",
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 110,
 | 
			
		||||
@@ -314,7 +330,7 @@
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921608,
 | 
			
		||||
          "name": "icon-pane-collapse-right",
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 5,
 | 
			
		||||
@@ -322,7 +338,7 @@
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921609,
 | 
			
		||||
          "name": "icon-download",
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 60,
 | 
			
		||||
@@ -330,7 +346,7 @@
 | 
			
		||||
          "name": "icon-duplicate",
 | 
			
		||||
          "id": 55,
 | 
			
		||||
          "code": 921616,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 61,
 | 
			
		||||
@@ -338,7 +354,7 @@
 | 
			
		||||
          "name": "icon-folder-new",
 | 
			
		||||
          "id": 56,
 | 
			
		||||
          "code": 921617,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 64,
 | 
			
		||||
@@ -346,7 +362,7 @@
 | 
			
		||||
          "name": "icon-fullscreen-expand",
 | 
			
		||||
          "id": 59,
 | 
			
		||||
          "code": 921618,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 63,
 | 
			
		||||
@@ -354,7 +370,7 @@
 | 
			
		||||
          "name": "icon-fullscreen-collapse",
 | 
			
		||||
          "id": 58,
 | 
			
		||||
          "code": 921619,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 67,
 | 
			
		||||
@@ -362,7 +378,7 @@
 | 
			
		||||
          "name": "icon-layers",
 | 
			
		||||
          "id": 62,
 | 
			
		||||
          "code": 921620,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 69,
 | 
			
		||||
@@ -370,7 +386,7 @@
 | 
			
		||||
          "name": "icon-line-horz",
 | 
			
		||||
          "id": 64,
 | 
			
		||||
          "code": 921621,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 73,
 | 
			
		||||
@@ -378,7 +394,7 @@
 | 
			
		||||
          "name": "icon-magnify",
 | 
			
		||||
          "id": 68,
 | 
			
		||||
          "code": 921622,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 71,
 | 
			
		||||
@@ -386,7 +402,7 @@
 | 
			
		||||
          "name": "icon-magnify-in",
 | 
			
		||||
          "id": 66,
 | 
			
		||||
          "code": 921623,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 72,
 | 
			
		||||
@@ -394,7 +410,7 @@
 | 
			
		||||
          "name": "icon-magnify-out",
 | 
			
		||||
          "id": 67,
 | 
			
		||||
          "code": 921624,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 74,
 | 
			
		||||
@@ -402,7 +418,7 @@
 | 
			
		||||
          "name": "icon-menu",
 | 
			
		||||
          "id": 69,
 | 
			
		||||
          "code": 921625,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 75,
 | 
			
		||||
@@ -410,7 +426,7 @@
 | 
			
		||||
          "name": "icon-move",
 | 
			
		||||
          "id": 70,
 | 
			
		||||
          "code": 921632,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 76,
 | 
			
		||||
@@ -418,7 +434,7 @@
 | 
			
		||||
          "name": "icon-new-window",
 | 
			
		||||
          "id": 71,
 | 
			
		||||
          "code": 921633,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 26,
 | 
			
		||||
@@ -426,7 +442,7 @@
 | 
			
		||||
          "name": "icon-paint-bucket",
 | 
			
		||||
          "id": 21,
 | 
			
		||||
          "code": 921634,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 81,
 | 
			
		||||
@@ -434,7 +450,7 @@
 | 
			
		||||
          "name": "icon-pause",
 | 
			
		||||
          "id": 76,
 | 
			
		||||
          "code": 921635,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 82,
 | 
			
		||||
@@ -442,7 +458,7 @@
 | 
			
		||||
          "name": "icon-pencil",
 | 
			
		||||
          "id": 77,
 | 
			
		||||
          "code": 921636,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 84,
 | 
			
		||||
@@ -450,7 +466,7 @@
 | 
			
		||||
          "name": "icon-play",
 | 
			
		||||
          "id": 79,
 | 
			
		||||
          "code": 921637,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 85,
 | 
			
		||||
@@ -458,7 +474,7 @@
 | 
			
		||||
          "name": "icon-plot-resource",
 | 
			
		||||
          "id": 80,
 | 
			
		||||
          "code": 921638,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 27,
 | 
			
		||||
@@ -466,7 +482,7 @@
 | 
			
		||||
          "name": "icon-pointer-left",
 | 
			
		||||
          "id": 22,
 | 
			
		||||
          "code": 921639,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 28,
 | 
			
		||||
@@ -474,7 +490,7 @@
 | 
			
		||||
          "name": "icon-pointer-right",
 | 
			
		||||
          "id": 23,
 | 
			
		||||
          "code": 921640,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 32,
 | 
			
		||||
@@ -482,7 +498,7 @@
 | 
			
		||||
          "name": "icon-refresh",
 | 
			
		||||
          "id": 27,
 | 
			
		||||
          "code": 921641,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 16,
 | 
			
		||||
@@ -490,7 +506,7 @@
 | 
			
		||||
          "name": "icon-save",
 | 
			
		||||
          "id": 11,
 | 
			
		||||
          "code": 921648,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 88,
 | 
			
		||||
@@ -498,7 +514,7 @@
 | 
			
		||||
          "name": "icon-sine",
 | 
			
		||||
          "id": 83,
 | 
			
		||||
          "code": 921649,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 102,
 | 
			
		||||
@@ -506,7 +522,7 @@
 | 
			
		||||
          "name": "icon-T",
 | 
			
		||||
          "id": 84,
 | 
			
		||||
          "code": 921650,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 92,
 | 
			
		||||
@@ -514,7 +530,7 @@
 | 
			
		||||
          "name": "icon-thumbs-strip",
 | 
			
		||||
          "id": 87,
 | 
			
		||||
          "code": 921651,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 96,
 | 
			
		||||
@@ -522,7 +538,7 @@
 | 
			
		||||
          "name": "icon-two-parts-both",
 | 
			
		||||
          "id": 91,
 | 
			
		||||
          "code": 921652,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 97,
 | 
			
		||||
@@ -530,7 +546,7 @@
 | 
			
		||||
          "name": "icon-two-parts-one-only",
 | 
			
		||||
          "id": 92,
 | 
			
		||||
          "code": 921653,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 21,
 | 
			
		||||
@@ -538,7 +554,7 @@
 | 
			
		||||
          "name": "icon-resync",
 | 
			
		||||
          "id": 16,
 | 
			
		||||
          "code": 921654,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 120,
 | 
			
		||||
@@ -546,7 +562,7 @@
 | 
			
		||||
          "name": "icon-reset",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921655,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 121,
 | 
			
		||||
@@ -554,7 +570,7 @@
 | 
			
		||||
          "name": "icon-x-in-circle",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921656,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 118,
 | 
			
		||||
@@ -562,7 +578,7 @@
 | 
			
		||||
          "name": "icon-brightness",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921657,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 119,
 | 
			
		||||
@@ -570,15 +586,55 @@
 | 
			
		||||
          "name": "icon-contrast",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921664,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 122,
 | 
			
		||||
          "order": 124,
 | 
			
		||||
          "id": 106,
 | 
			
		||||
          "name": "icon-expand",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921665,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 125,
 | 
			
		||||
          "id": 107,
 | 
			
		||||
          "name": "icon-list-view",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921666,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 133,
 | 
			
		||||
          "id": 112,
 | 
			
		||||
          "name": "icon-grid-snap-to",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921667,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 128,
 | 
			
		||||
          "id": 113,
 | 
			
		||||
          "name": "icon-grid-snap-no",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921668,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 131,
 | 
			
		||||
          "id": 110,
 | 
			
		||||
          "name": "icon-frame-show",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921669,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 130,
 | 
			
		||||
          "id": 111,
 | 
			
		||||
          "name": "icon-frame-hide",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921670,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 37,
 | 
			
		||||
@@ -586,7 +642,7 @@
 | 
			
		||||
          "name": "icon-activity",
 | 
			
		||||
          "id": 32,
 | 
			
		||||
          "code": 921856,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 36,
 | 
			
		||||
@@ -594,7 +650,7 @@
 | 
			
		||||
          "name": "icon-activity-mode",
 | 
			
		||||
          "id": 31,
 | 
			
		||||
          "code": 921857,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 52,
 | 
			
		||||
@@ -602,7 +658,7 @@
 | 
			
		||||
          "name": "icon-autoflow-tabular",
 | 
			
		||||
          "id": 47,
 | 
			
		||||
          "code": 921858,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 55,
 | 
			
		||||
@@ -610,7 +666,7 @@
 | 
			
		||||
          "name": "icon-clock",
 | 
			
		||||
          "id": 50,
 | 
			
		||||
          "code": 921859,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 58,
 | 
			
		||||
@@ -618,7 +674,7 @@
 | 
			
		||||
          "name": "icon-database",
 | 
			
		||||
          "id": 53,
 | 
			
		||||
          "code": 921860,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 57,
 | 
			
		||||
@@ -626,7 +682,7 @@
 | 
			
		||||
          "name": "icon-database-query",
 | 
			
		||||
          "id": 52,
 | 
			
		||||
          "code": 921861,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 17,
 | 
			
		||||
@@ -634,7 +690,7 @@
 | 
			
		||||
          "name": "icon-dataset",
 | 
			
		||||
          "id": 12,
 | 
			
		||||
          "code": 921862,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 22,
 | 
			
		||||
@@ -642,7 +698,7 @@
 | 
			
		||||
          "name": "icon-datatable",
 | 
			
		||||
          "id": 17,
 | 
			
		||||
          "code": 921863,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 59,
 | 
			
		||||
@@ -650,7 +706,7 @@
 | 
			
		||||
          "name": "icon-dictionary",
 | 
			
		||||
          "id": 54,
 | 
			
		||||
          "code": 921864,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 62,
 | 
			
		||||
@@ -658,7 +714,7 @@
 | 
			
		||||
          "name": "icon-folder",
 | 
			
		||||
          "id": 57,
 | 
			
		||||
          "code": 921865,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 66,
 | 
			
		||||
@@ -666,7 +722,7 @@
 | 
			
		||||
          "name": "icon-image",
 | 
			
		||||
          "id": 61,
 | 
			
		||||
          "code": 921872,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 68,
 | 
			
		||||
@@ -674,7 +730,7 @@
 | 
			
		||||
          "name": "icon-layout",
 | 
			
		||||
          "id": 63,
 | 
			
		||||
          "code": 921873,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 77,
 | 
			
		||||
@@ -682,7 +738,7 @@
 | 
			
		||||
          "name": "icon-object",
 | 
			
		||||
          "id": 72,
 | 
			
		||||
          "code": 921874,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 78,
 | 
			
		||||
@@ -690,7 +746,7 @@
 | 
			
		||||
          "name": "icon-object-unknown",
 | 
			
		||||
          "id": 73,
 | 
			
		||||
          "code": 921875,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 79,
 | 
			
		||||
@@ -698,7 +754,7 @@
 | 
			
		||||
          "name": "icon-packet",
 | 
			
		||||
          "id": 74,
 | 
			
		||||
          "code": 921876,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 80,
 | 
			
		||||
@@ -706,15 +762,15 @@
 | 
			
		||||
          "name": "icon-page",
 | 
			
		||||
          "id": 75,
 | 
			
		||||
          "code": 921877,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 114,
 | 
			
		||||
          "order": 135,
 | 
			
		||||
          "id": 99,
 | 
			
		||||
          "name": "icon-plot-overlay",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921878,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 113,
 | 
			
		||||
@@ -722,7 +778,7 @@
 | 
			
		||||
          "name": "icon-plot-stacked",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921879,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 10,
 | 
			
		||||
@@ -730,7 +786,7 @@
 | 
			
		||||
          "name": "icon-session",
 | 
			
		||||
          "id": 5,
 | 
			
		||||
          "code": 921880,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 24,
 | 
			
		||||
@@ -738,7 +794,7 @@
 | 
			
		||||
          "name": "icon-tabular",
 | 
			
		||||
          "id": 19,
 | 
			
		||||
          "code": 921881,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 7,
 | 
			
		||||
@@ -746,7 +802,7 @@
 | 
			
		||||
          "name": "icon-tabular-lad",
 | 
			
		||||
          "id": 2,
 | 
			
		||||
          "code": 921888,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 6,
 | 
			
		||||
@@ -754,7 +810,7 @@
 | 
			
		||||
          "name": "icon-tabular-lad-set",
 | 
			
		||||
          "id": 1,
 | 
			
		||||
          "code": 921889,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 8,
 | 
			
		||||
@@ -762,7 +818,7 @@
 | 
			
		||||
          "name": "icon-tabular-realtime",
 | 
			
		||||
          "id": 3,
 | 
			
		||||
          "code": 921890,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 23,
 | 
			
		||||
@@ -770,7 +826,7 @@
 | 
			
		||||
          "name": "icon-tabular-scrolling",
 | 
			
		||||
          "id": 18,
 | 
			
		||||
          "code": 921891,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 112,
 | 
			
		||||
@@ -778,7 +834,7 @@
 | 
			
		||||
          "name": "icon-telemetry",
 | 
			
		||||
          "id": 86,
 | 
			
		||||
          "code": 921892,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 90,
 | 
			
		||||
@@ -786,7 +842,7 @@
 | 
			
		||||
          "name": "icon-telemetry-panel",
 | 
			
		||||
          "id": 85,
 | 
			
		||||
          "code": 921893,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 93,
 | 
			
		||||
@@ -794,7 +850,7 @@
 | 
			
		||||
          "name": "icon-timeline",
 | 
			
		||||
          "id": 88,
 | 
			
		||||
          "code": 921894,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 116,
 | 
			
		||||
@@ -802,7 +858,7 @@
 | 
			
		||||
          "name": "icon-timer-v1.5",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921895,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 11,
 | 
			
		||||
@@ -810,7 +866,7 @@
 | 
			
		||||
          "name": "icon-topic",
 | 
			
		||||
          "id": 6,
 | 
			
		||||
          "code": 921896,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 115,
 | 
			
		||||
@@ -818,7 +874,15 @@
 | 
			
		||||
          "name": "icon-box-with-dashed-lines",
 | 
			
		||||
          "id": 29,
 | 
			
		||||
          "code": 921897,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 126,
 | 
			
		||||
          "id": 108,
 | 
			
		||||
          "name": "icon-summary-widget",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
          "code": 921904,
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "metadata": {
 | 
			
		||||
@@ -1465,6 +1529,92 @@
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 114,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M574-2h-128v320h128v-320z",
 | 
			
		||||
            "M1022 446h-320v128h320v-128z",
 | 
			
		||||
            "M574 702h-128v320h128v-320z",
 | 
			
		||||
            "M318 446h-320v128h320v-128z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-crosshair"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 109,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M146.4 182.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M146.4 402.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M146.4 621.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M146.4 841.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M365.8 73.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M365.8 292.6c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M365.8 512c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M365.8 731.4c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M365.8 950.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M585.2 182.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M585.2 402.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M585.2 621.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
 | 
			
		||||
            "M585.2 841.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "width": 586,
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-grippy-v2"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M0 512l256 256v-512z",
 | 
			
		||||
@@ -2363,6 +2513,176 @@
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 107,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M0 64h1024v128h-1024v-128z",
 | 
			
		||||
            "M0 320h1024v128h-1024v-128z",
 | 
			
		||||
            "M0 576h1024v128h-1024v-128z",
 | 
			
		||||
            "M0 832h1024v128h-1024v-128z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {
 | 
			
		||||
              "fill": "rgb(0, 161, 75)"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              "fill": "rgb(0, 161, 75)"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              "fill": "rgb(0, 161, 75)"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              "fill": "rgb(0, 161, 75)"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-list-view"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {
 | 
			
		||||
                "f": 1
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                "f": 1
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                "f": 1
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                "f": 1
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 112,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M382 830h448v-448h-448v448zM510 510h192v192h-192v-192z",
 | 
			
		||||
            "M-2 574h320v64h-320v-64z",
 | 
			
		||||
            "M894 574h128v64h-128v-64z",
 | 
			
		||||
            "M574-2h64v320h-64v-320z",
 | 
			
		||||
            "M574 894h64v128h-64v-128z",
 | 
			
		||||
            "M574 574h64v64h-64v-64z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-grid-snap-to"
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 113,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M768 576h192v64h-192v-64z",
 | 
			
		||||
            "M256 576h192v64h-192v-64z",
 | 
			
		||||
            "M0 576h192v64h-192v-64z",
 | 
			
		||||
            "M640 512h-64v64h-64v64h64v64h64v-64h64v-64h-64z",
 | 
			
		||||
            "M576 256h64v192h-64v-192z",
 | 
			
		||||
            "M576 0h64v192h-64v-192z",
 | 
			
		||||
            "M576 768h64v192h-64v-192z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-grid-snap-no"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 110,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M0 64v896h1024v-896h-1024zM896 832h-768v-640h768v640z",
 | 
			
		||||
            "M192 256h384v128h-384v-128z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-frame-show"
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 111,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M128 190h420l104-128h-652v802.4l128-157.4z",
 | 
			
		||||
            "M896 830h-420l-104 128h652v-802.4l-128 157.4z",
 | 
			
		||||
            "M832-2l-832 1024h192l832-1024z",
 | 
			
		||||
            "M392 382l104-128h-304v128z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-frame-hide"
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M576 64h-256l320 320h-290.256c-44.264-76.516-126.99-128-221.744-128h-128v512h128c94.754 0 177.48-51.484 221.744-128h290.256l-320 320h256l448-448-448-448z"
 | 
			
		||||
@@ -3122,6 +3442,26 @@
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 108,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM847.8 610.4l-82.6 143.2-189.6-131.6 19.2 230h-165.4l19.2-230-189.6 131.6-82.6-143.2 208.6-98.4-208.8-98.4 82.6-143.2 189.6 131.6-19.2-230h165.4l-19.2 230 189.6-131.6 82.6 143.2-208.6 98.4 208.8 98.4z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-summary-widget"
 | 
			
		||||
          ],
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "1161751207457516161751": [
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "colorThemes": [
 | 
			
		||||
@@ -3237,4 +3577,4 @@
 | 
			
		||||
    "showGrid": true
 | 
			
		||||
  },
 | 
			
		||||
  "uid": -1
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -7,8 +7,6 @@
 | 
			
		||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
 | 
			
		||||
<missing-glyph horiz-adv-x="1024" />
 | 
			
		||||
<glyph unicode=" " horiz-adv-x="512" d="" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon12-eye-open" d="M512 852.667c-257.92 0-471.28-185.147-506.667-426 35.493-240.853 248.853-426 506.667-426s471.28 185.147 506.667 426c-35.387 240.853-248.747 426-506.667 426zM738.373 253.467c-131.048-109.517-321.699-109.517-452.747 0-53.328 44.749-90.832 105.477-106.96 173.2 16.101 67.707 53.565 128.435 106.853 173.2 131.048 109.517 321.699 109.517 452.747 0 53.368-44.733 90.909-105.464 107.067-173.2-16.128-67.723-53.632-128.451-106.96-173.2zM682.667 426.667c0-94.257-76.41-170.667-170.667-170.667s-170.667 76.41-170.667 170.667c0 94.257 76.41 170.667 170.667 170.667s170.667-76.41 170.667-170.667z" />
 | 
			
		||||
<glyph unicode="󡀇" glyph-name="icon12-pane-collapse-left" horiz-adv-x="853" d="M0 938.667h170.667v-1024h-170.667v1024zM853.333 682.667h-256v256l-341.333-426.667 341.333-426.667v256h256v341.333z" />
 | 
			
		||||
<glyph unicode="󡀈" glyph-name="icon12-pane-collapse-right" horiz-adv-x="853" d="M682.667 938.667h170.667v-1024h-170.667v1024zM0 682.667h256v256l341.333-426.667-341.333-426.667v256h-256v341.333z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon12-crosshair" d="M597.333 938.667h-170.667v-256h170.667v256zM1024 512h-256v-170.667h256v170.667zM597.333 170.667h-170.667v-256h170.667v256zM256 512h-256v-170.667h256v170.667z" />
 | 
			
		||||
<glyph unicode="󡄉" glyph-name="icon12-folder" d="M938.667 768h-341.333l-110.32 110.32c-33.2 33.2-98.667 60.347-145.68 60.347h-256c-47.073-0.136-85.197-38.26-85.333-85.32v-341.346c0.136 47.073 38.26 85.197 85.32 85.333h853.346c47.073-0.136 85.197-38.26 85.333-85.32v170.654c-0.136 47.073-38.26 85.197-85.32 85.333zM85.333 512h853.333c47.128 0 85.333-38.205 85.333-85.333v-426.667c0-47.128-38.205-85.333-85.333-85.333h-853.333c-47.128 0-85.333 38.205-85.333 85.333v426.667c0 47.128 38.205 85.333 85.333 85.333z" />
 | 
			
		||||
</font></defs></svg>
 | 
			
		||||
</font></defs></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.2 KiB  | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -37,6 +37,8 @@
 | 
			
		||||
<glyph unicode="" glyph-name="icon-trash" d="M832 832h-192.36v64c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-64h-191.64c-105.6 0-192-72-192-160s0-160 0-160h64v-384c0-105.6 86.4-192 192-192h512c105.6 0 192 86.4 192 192v384h64c0 0 0 72 0 160s-86.4 160-192 160zM320 128h-128v384h128v-384zM576 128h-128v384h128v-384zM832 128h-128v384h128v-384z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-brackets" d="M832 960h-192v-192h191.66l0.34-0.34v-639.32l-0.34-0.34h-191.66v-192h192c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM384 128h-191.66l-0.34 0.34v639.32l0.34 0.34h191.66v192h-192c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h192v192z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-crosshair" d="M574 962h-128v-320h128v320zM1022 514h-320v-128h320v128zM574 258h-128v-320h128v320zM318 514h-320v-128h320v128z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-grippy-v2" horiz-adv-x="586" d="M146.4 777.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM146.4 557.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM146.4 338.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM146.4 118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 886.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 667.4c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 448c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 228.6c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 9.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 777.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 557.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 338.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2z" />
 | 
			
		||||
<glyph unicode="󡀀" glyph-name="icon-arrows-out" d="M0 448l256-256v512zM512 960l-256-256h512zM512-64l256 256h-512zM768 704v-512l256 256z" />
 | 
			
		||||
<glyph unicode="󡀁" glyph-name="icon-arrows-right-left" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
 | 
			
		||||
<glyph unicode="󡀂" glyph-name="icon-arrows-up-down" d="M512 960l512-448h-1024zM0 384l512-448 512 448z" />
 | 
			
		||||
@@ -78,6 +80,11 @@
 | 
			
		||||
<glyph unicode="󡀹" glyph-name="icon-brightness" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l191.586-30.216c8.145 56.552 29.998 106.879 62.068 149.006zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l154.18 117.665c-31.476 41.347-53.309 91.675-61.231 146.504zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l117.665-154.18c41.347 31.476 91.675 53.309 146.504 61.231zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-30.216-191.586c56.552-8.145 106.879-29.998 149.006-62.068zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l-154.18-117.665c31.476-41.347 53.309-91.675 61.231-146.504zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l30.216 191.586c-56.552 8.145-106.879 29.998-149.006 62.068zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l-191.586 30.216c-8.125-56.564-29.966-106.906-62.028-149.049zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-117.665 154.18c-41.347-31.476-91.675-53.309-146.504-61.231zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
 | 
			
		||||
<glyph unicode="󡁀" glyph-name="icon-contrast" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001l0.055 768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52z" />
 | 
			
		||||
<glyph unicode="󡁁" glyph-name="icon-expand" d="M960 960c0 0 0 0 0 0h-320v-128h165.4l-210.6-210.8c-25-25-25-65.6 0-90.6 12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8l210.8 210.8v-165.4h128v384h-64zM896 154.6l-210.8 210.6c-25 25-65.6 25-90.6 0s-25-65.6 0-90.6l210.8-210.6h-165.4v-128h384v384h-128v-165.4zM218.6 832h165.4v128h-320c0 0 0 0 0 0h-64v-384h128v165.4l210.8-210.8c12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8c25 25 25 65.6 0 90.6l-210.6 210.8zM338.8 365.2l-210.8-210.6v165.4h-128v-384h384v128h-165.4l210.8 210.8c25 25 25 65.6 0 90.6-25.2 24.8-65.6 24.8-90.6-0.2z" />
 | 
			
		||||
<glyph unicode="󡁂" glyph-name="icon-list-view" d="M0 896h1024v-128h-1024v128zM0 640h1024v-128h-1024v128zM0 384h1024v-128h-1024v128zM0 128h1024v-128h-1024v128z" />
 | 
			
		||||
<glyph unicode="󡁃" glyph-name="icon-grid-snap-to" d="M382 130h448v448h-448v-448zM510 450h192v-192h-192v192zM-2 386h320v-64h-320v64zM894 386h128v-64h-128v64zM574 962h64v-320h-64v320zM574 66h64v-128h-64v128zM574 386h64v-64h-64v64z" />
 | 
			
		||||
<glyph unicode="󡁄" glyph-name="icon-grid-snap-no" d="M768 384h192v-64h-192v64zM256 384h192v-64h-192v64zM0 384h192v-64h-192v64zM640 448h-64v-64h-64v-64h64v-64h64v64h64v64h-64zM576 704h64v-192h-64v192zM576 960h64v-192h-64v192zM576 192h64v-192h-64v192z" />
 | 
			
		||||
<glyph unicode="󡁅" glyph-name="icon-frame-show" d="M0 896v-896h1024v896h-1024zM896 128h-768v640h768v-640zM192 704h384v-128h-384v128z" />
 | 
			
		||||
<glyph unicode="󡁆" glyph-name="icon-frame-hide" d="M128 770h420l104 128h-652v-802.4l128 157.4zM896 130h-420l-104-128h652v802.4l-128-157.4zM832 962l-832-1024h192l832 1024zM392 578l104 128h-304v-128z" />
 | 
			
		||||
<glyph unicode="󡄀" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
 | 
			
		||||
<glyph unicode="󡄁" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
 | 
			
		||||
<glyph unicode="󡄂" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
 | 
			
		||||
@@ -108,4 +115,5 @@
 | 
			
		||||
<glyph unicode="󡄧" glyph-name="icon-timer-v1.5" horiz-adv-x="896" d="M576 813.4v82.58c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM468 363.98l-263.76-211c-57.105 59.935-92.24 141.251-92.24 230.772 0 0.080 0 0.16 0 0.24 0 185.268 150.72 335.988 336 335.988 6.72 0 13.38-0.22 20-0.62v-355.38z" />
 | 
			
		||||
<glyph unicode="󡄨" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
 | 
			
		||||
<glyph unicode="󡄩" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
 | 
			
		||||
</font></defs></svg>
 | 
			
		||||
<glyph unicode="󡄰" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
 | 
			
		||||
</font></defs></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 43 KiB  | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -21,32 +21,11 @@
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
.t-fixed-position {
 | 
			
		||||
	&.l-fixed-position {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: 0;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
		width: auto;
 | 
			
		||||
		height: auto;
 | 
			
		||||
 | 
			
		||||
		.l-grid-holder {
 | 
			
		||||
			position: relative;
 | 
			
		||||
			height: 100%;
 | 
			
		||||
			width: 100%;
 | 
			
		||||
			.l-grid {
 | 
			
		||||
				position: absolute;
 | 
			
		||||
				height: 100%;
 | 
			
		||||
				width: 100%;
 | 
			
		||||
				pointer-events: none;
 | 
			
		||||
				z-index: 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		@extend .abs;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.l-fixed-position-item {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		border: 1px solid transparent;
 | 
			
		||||
 | 
			
		||||
		&.s-not-selected {
 | 
			
		||||
			opacity: 0.8;
 | 
			
		||||
		}
 | 
			
		||||
@@ -105,37 +84,8 @@
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.l-fixed-position-item-handle {
 | 
			
		||||
		$brd: 1px solid $colorKey;
 | 
			
		||||
		background: rgba($colorKey, 0.5);
 | 
			
		||||
		cursor: crosshair;
 | 
			
		||||
		border: $brd;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.edit-mode .t-fixed-position {
 | 
			
		||||
	&.l-fixed-position {
 | 
			
		||||
		.l-grid-holder {
 | 
			
		||||
			.l-grid {
 | 
			
		||||
				&.l-grid-x {
 | 
			
		||||
					@include bgTicks($colorGridLines, 'x');
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				&.l-grid-y {
 | 
			
		||||
					@include bgTicks($colorGridLines, 'y');
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.l-fixed-position-item {
 | 
			
		||||
		&:not(.s-selected) {
 | 
			
		||||
			border: 1px dotted rgba($colorKey, 0.75);
 | 
			
		||||
			&:hover {
 | 
			
		||||
				border: 1px dotted rgba($colorKey, 1.0);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
.s-status-editing {
 | 
			
		||||
    .l-fixed-position-item-handle.edit-corner { display: block; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -81,12 +81,6 @@ input, textarea {
 | 
			
		||||
    letter-spacing: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[type="text"],
 | 
			
		||||
input[type="search"] {
 | 
			
		||||
    vertical-align: baseline;
 | 
			
		||||
    padding: $inputTextP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1, h2, h3 {
 | 
			
		||||
    letter-spacing: 0.04em;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,8 @@ $glyph-icon-plus: '\e926';
 | 
			
		||||
$glyph-icon-trash: '\e927';
 | 
			
		||||
$glyph-icon-x: '\e928';
 | 
			
		||||
$glyph-icon-brackets: '\e929';
 | 
			
		||||
$glyph-icon-crosshair: '\e930';
 | 
			
		||||
$glyph-icon-grippy: '\e931';
 | 
			
		||||
$glyph-icon-arrows-out: '\e1000';
 | 
			
		||||
$glyph-icon-arrows-right-left: '\e1001';
 | 
			
		||||
$glyph-icon-arrows-up-down: '\e1002';
 | 
			
		||||
@@ -85,6 +87,11 @@ $glyph-icon-x-in-circle: '\e1038';
 | 
			
		||||
$glyph-icon-brightness: '\e1039';
 | 
			
		||||
$glyph-icon-contrast: '\e1040';
 | 
			
		||||
$glyph-icon-expand: '\e1041';
 | 
			
		||||
$glyph-icon-list-view: '\e1042';
 | 
			
		||||
$glyph-icon-grid-snap-to: '\e1043';
 | 
			
		||||
$glyph-icon-grid-snap-no: '\e1044';
 | 
			
		||||
$glyph-icon-frame-show: '\e1045';
 | 
			
		||||
$glyph-icon-frame-hide: '\e1046';
 | 
			
		||||
$glyph-icon-activity: '\e1100';
 | 
			
		||||
$glyph-icon-activity-mode: '\e1101';
 | 
			
		||||
$glyph-icon-autoflow-tabular: '\e1102';
 | 
			
		||||
@@ -115,6 +122,7 @@ $glyph-icon-timeline: '\e1126';
 | 
			
		||||
$glyph-icon-timer: '\e1127';
 | 
			
		||||
$glyph-icon-topic: '\e1128';
 | 
			
		||||
$glyph-icon-box-with-dashed-lines: '\e1129';
 | 
			
		||||
$glyph-icon-summary-widget: '\e1130';
 | 
			
		||||
 | 
			
		||||
/************************** 16 PX CLASSES */
 | 
			
		||||
 | 
			
		||||
@@ -148,6 +156,8 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
 | 
			
		||||
.icon-trash {  @include glyphBefore($glyph-icon-trash); }
 | 
			
		||||
.icon-x {  @include glyphBefore($glyph-icon-x); }
 | 
			
		||||
.icon-brackets {  @include glyphBefore($glyph-icon-brackets); }
 | 
			
		||||
.icon-crosshair {  @include glyphBefore($glyph-icon-crosshair); }
 | 
			
		||||
.icon-grippy {  @include glyphBefore($glyph-icon-grippy); }
 | 
			
		||||
.icon-arrows-out {  @include glyphBefore($glyph-icon-arrows-out); }
 | 
			
		||||
.icon-arrows-right-left {  @include glyphBefore($glyph-icon-arrows-right-left); }
 | 
			
		||||
.icon-arrows-up-down {  @include glyphBefore($glyph-icon-arrows-up-down); }
 | 
			
		||||
@@ -189,6 +199,11 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
 | 
			
		||||
.icon-brightness {  @include glyphBefore($glyph-icon-brightness); }
 | 
			
		||||
.icon-contrast {  @include glyphBefore($glyph-icon-contrast); }
 | 
			
		||||
.icon-expand {  @include glyphBefore($glyph-icon-expand); }
 | 
			
		||||
.icon-list-view {  @include glyphBefore($glyph-icon-list-view); }
 | 
			
		||||
.icon-grid-snap-to {  @include glyphBefore($glyph-icon-grid-snap-to); }
 | 
			
		||||
.icon-grid-snap-no {  @include glyphBefore($glyph-icon-grid-snap-no); }
 | 
			
		||||
.icon-frame-show {  @include glyphBefore($glyph-icon-frame-show); }
 | 
			
		||||
.icon-frame-hide {  @include glyphBefore($glyph-icon-frame-hide); }
 | 
			
		||||
.icon-activity {  @include glyphBefore($glyph-icon-activity); }
 | 
			
		||||
.icon-activity-mode {  @include glyphBefore($glyph-icon-activity-mode); }
 | 
			
		||||
.icon-autoflow-tabular {  @include glyphBefore($glyph-icon-autoflow-tabular); }
 | 
			
		||||
@@ -219,10 +234,8 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
 | 
			
		||||
.icon-timer {  @include glyphBefore($glyph-icon-timer); }
 | 
			
		||||
.icon-topic {  @include glyphBefore($glyph-icon-topic); }
 | 
			
		||||
.icon-box-with-dashed-lines {  @include glyphBefore($glyph-icon-box-with-dashed-lines); }
 | 
			
		||||
.icon-summary-widget {  @include glyphBefore($glyph-icon-summary-widget); }
 | 
			
		||||
 | 
			
		||||
/************************** 12 PX CLASSES */
 | 
			
		||||
 | 
			
		||||
.icon-eye-open-12px {  @include glyphBefore($glyph-icon-eye-open,'symbolsfont-12px'); }
 | 
			
		||||
.icon-collapse-pane-left-12px {  @include glyphBefore($glyph-icon-collapse-pane-left,'symbolsfont-12px'); }
 | 
			
		||||
.icon-collapse-pane-right-12px {  @include glyphBefore($glyph-icon-collapse-pane-right,'symbolsfont-12px'); }
 | 
			
		||||
.icon-crosshair-12px {  @include glyphBefore($glyph-icon-crosshair,'symbolsfont-12px'); }
 | 
			
		||||
.icon-folder-12px {  @include glyphBefore($glyph-icon-folder,'symbolsfont-12px'); }
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,15 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-hyperlink {
 | 
			
		||||
    // Hyperlink objects
 | 
			
		||||
    &:not(.s-button) {
 | 
			
		||||
        color: $colorKey;
 | 
			
		||||
        font-size: 0.8rem;
 | 
			
		||||
        &:hover { color: $colorKeyHov; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-icon-button {
 | 
			
		||||
    // Clickable icon elements that have hover
 | 
			
		||||
    @extend .ui-symbol;
 | 
			
		||||
@@ -224,18 +233,28 @@ textarea {
 | 
			
		||||
 | 
			
		||||
/******************************************************** INPUTS */
 | 
			
		||||
input[type="text"],
 | 
			
		||||
input[type="search"] {
 | 
			
		||||
input[type="search"],
 | 
			
		||||
input[type="number"] {
 | 
			
		||||
    @include nice-input();
 | 
			
		||||
    vertical-align: baseline;
 | 
			
		||||
    padding: $inputTextP;
 | 
			
		||||
    &.numeric {
 | 
			
		||||
        text-align: right;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-input-sm {
 | 
			
		||||
    input[type="text"],
 | 
			
		||||
    input[type="search"],
 | 
			
		||||
    input[type="number"] {
 | 
			
		||||
        width: 50px !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-input-lg input[type="text"],
 | 
			
		||||
input[type="text"].lg {  width: 100% !important; }
 | 
			
		||||
.l-input-med input[type="text"],
 | 
			
		||||
input[type="text"].med { width: 200px !important; }
 | 
			
		||||
.l-input-sm input[type="text"],
 | 
			
		||||
input[type="text"].sm {  width: 50px !important; }
 | 
			
		||||
.l-numeric input[type="text"],
 | 
			
		||||
input[type="text"].numeric { text-align: right; }
 | 
			
		||||
@@ -297,6 +316,40 @@ textarea.lg { position: relative; height: 300px; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** AUTOCOMPLETE */
 | 
			
		||||
.autocomplete {
 | 
			
		||||
    input {
 | 
			
		||||
        width: 226px;
 | 
			
		||||
        padding: 5px 0px 5px 7px;
 | 
			
		||||
    }
 | 
			
		||||
    .icon-arrow-down {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 8px;
 | 
			
		||||
        left: 210px;
 | 
			
		||||
        font-size: 10px;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
    .autocompleteOptions {
 | 
			
		||||
        border: 1px solid $colorFormLines;
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        width: 224px;
 | 
			
		||||
        max-height: 170px;
 | 
			
		||||
        overflow-y: auto;
 | 
			
		||||
        overflow-x: hidden;
 | 
			
		||||
        li {
 | 
			
		||||
            border: 1px solid $colorFormLines;
 | 
			
		||||
            padding: 8px 0px 8px 5px;
 | 
			
		||||
            .optionText {
 | 
			
		||||
                cursor: pointer;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .optionPreSelected {
 | 
			
		||||
            background-color: $colorInspectorSectionHeaderBg;
 | 
			
		||||
            color: $colorInspectorSectionHeaderFg;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** OBJECT-HEADER */
 | 
			
		||||
.object-header {
 | 
			
		||||
    font-size: 1em;
 | 
			
		||||
@@ -651,11 +704,6 @@ textarea {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.view-switcher,
 | 
			
		||||
.t-btn-view-large {
 | 
			
		||||
    @include trans-prop-nice-fade($controlFadeMs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** BROWSER ELEMENTS */
 | 
			
		||||
body.desktop {
 | 
			
		||||
    ::-webkit-scrollbar {
 | 
			
		||||
 
 | 
			
		||||
@@ -368,7 +368,6 @@ body.desktop .t-message-list {
 | 
			
		||||
.s-unsynced {
 | 
			
		||||
    $c: $colorPausedBg;
 | 
			
		||||
    border: 1px solid $c;
 | 
			
		||||
    @include animTo($animName: pulsePaused, $propName: border-color, $propValStart: rgba($c, 0.8), $propValEnd: rgba($c, 0.5), $dur: $animPausedPulseDur, $dir: alternate, $count: infinite);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-status-timeconductor-unsynced {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,24 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
.s-status-editing .l-object-wrapper,
 | 
			
		||||
.edit-main {
 | 
			
		||||
    // .s-status-editing .l-object-wrapper is relevant to New Edit Mode;
 | 
			
		||||
    // .edit-main is legacy for old edit mode.
 | 
			
		||||
    $handleD: 15px;
 | 
			
		||||
    $cr: 5px;
 | 
			
		||||
.t-edit-handle-holder { display: none; }
 | 
			
		||||
 | 
			
		||||
.l-grid-holder {
 | 
			
		||||
    display: none;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    .l-grid {
 | 
			
		||||
        @extend .abs;
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
        z-index: 0;
 | 
			
		||||
        &.l-grid-y { background-position: 0 1px; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-status-editing {
 | 
			
		||||
    $handleD: 5px;
 | 
			
		||||
    .t-edit-handle-holder { display: block; }
 | 
			
		||||
    .edit-corner,
 | 
			
		||||
    .edit-handle {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
@@ -32,81 +44,59 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .edit-corner {
 | 
			
		||||
        background: rgba($colorSelectableSelectedPrimary, 0.5);
 | 
			
		||||
        cursor: crosshair;
 | 
			
		||||
        display: none; // Hide by default
 | 
			
		||||
        border: 1px solid $colorSelectableSelectedPrimary;
 | 
			
		||||
        width: $handleD;
 | 
			
		||||
        height: $handleD;
 | 
			
		||||
        $o: (-1 * $handleD) + 1px;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            z-index: 11;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-nw {
 | 
			
		||||
            border-bottom-right-radius: $cr;
 | 
			
		||||
            cursor: nw-resize;
 | 
			
		||||
            top: 0; left: 0;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-ne {
 | 
			
		||||
            border-bottom-left-radius: $cr;
 | 
			
		||||
            cursor: ne-resize;
 | 
			
		||||
            top: 0; right: 0;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-se {
 | 
			
		||||
            border-top-left-radius: $cr;
 | 
			
		||||
            cursor: se-resize;
 | 
			
		||||
            bottom: 0; right: 0;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-sw {
 | 
			
		||||
            border-top-right-radius: $cr;
 | 
			
		||||
            cursor: sw-resize;
 | 
			
		||||
            bottom: 0; left: 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.edit-resize-nw { top: $o; left: $o; }
 | 
			
		||||
        &.edit-resize-ne { top: $o; right: $o; }
 | 
			
		||||
        &.edit-resize-se { bottom: $o; right: $o; }
 | 
			
		||||
        &.edit-resize-sw { bottom: $o; left: $o; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .edit-handle {
 | 
			
		||||
        top: $handleD; right: $handleD; bottom: $handleD; left: $handleD;
 | 
			
		||||
        &.edit-move {
 | 
			
		||||
            $m: 0; //$handleD;
 | 
			
		||||
            cursor: move;
 | 
			
		||||
            left: $m;
 | 
			
		||||
            right: $m;
 | 
			
		||||
            top: $m;
 | 
			
		||||
            bottom: $m;
 | 
			
		||||
            z-index: 1;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-n {
 | 
			
		||||
            top: 0px; bottom: auto;
 | 
			
		||||
            height: $handleD;
 | 
			
		||||
            cursor: n-resize;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-e {
 | 
			
		||||
            right: 0px; left: auto;
 | 
			
		||||
            width: $handleD;
 | 
			
		||||
            cursor: e-resize;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-s {
 | 
			
		||||
            bottom: 0px; top: auto;
 | 
			
		||||
            height: $handleD;
 | 
			
		||||
            cursor: s-resize;
 | 
			
		||||
        }
 | 
			
		||||
        &.edit-resize-w {
 | 
			
		||||
            left: 0px; right: auto;
 | 
			
		||||
            width: $handleD;
 | 
			
		||||
            cursor: w-resize;
 | 
			
		||||
        }
 | 
			
		||||
    .edit-handle.edit-move {
 | 
			
		||||
        // main move box for the whole frame element
 | 
			
		||||
        $m: 0;
 | 
			
		||||
        left: $m;
 | 
			
		||||
        right: $m;
 | 
			
		||||
        top: $m;
 | 
			
		||||
        bottom: $m;
 | 
			
		||||
        z-index: 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .frame.child-frame.panel {
 | 
			
		||||
        &:hover {
 | 
			
		||||
            @include boxShdwLarge();
 | 
			
		||||
            border-color: $colorSelectableSelectedPrimary;
 | 
			
		||||
            .view-switcher {
 | 
			
		||||
                opacity: 1;
 | 
			
		||||
            }
 | 
			
		||||
            .edit-corner {
 | 
			
		||||
                background-color: rgba($colorKey, 0.8);
 | 
			
		||||
                &:hover {
 | 
			
		||||
                    background-color: rgba($colorKey, 1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Editing Grids
 | 
			
		||||
    .l-grid-holder {
 | 
			
		||||
        display: block;
 | 
			
		||||
        .l-grid {
 | 
			
		||||
            &.l-grid-x { @include bgTicks($colorGridLines, 'x'); }
 | 
			
		||||
            &.l-grid-y { @include bgTicks($colorGridLines, 'y'); }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Prevent nested frames from showing their grids
 | 
			
		||||
    .t-frame-outer .l-grid-holder { display: none !important; }
 | 
			
		||||
 | 
			
		||||
    // Prevent nested elements from showing s-hover-border
 | 
			
		||||
    .t-frame-outer .s-hover-border {
 | 
			
		||||
        border: none !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Prevent nested frames from being selectable until we have proper sub-object editing
 | 
			
		||||
    .t-frame-outer .t-frame-outer {
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -83,12 +83,10 @@
 | 
			
		||||
 | 
			
		||||
.l-image-thumbs-wrapper {
 | 
			
		||||
	//@include test(green);
 | 
			
		||||
	direction: rtl;
 | 
			
		||||
	overflow-x: auto;
 | 
			
		||||
	overflow-y: hidden;
 | 
			
		||||
	padding-bottom: $interiorMargin;
 | 
			
		||||
	white-space: nowrap;
 | 
			
		||||
	z-index: 70;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-image-thumb-item {
 | 
			
		||||
@@ -114,7 +112,7 @@
 | 
			
		||||
	width: $imageThumbsD + $imageThumbPad*2;
 | 
			
		||||
	white-space: normal;
 | 
			
		||||
	&:hover {
 | 
			
		||||
		background: rgba(#fff, 0.2);
 | 
			
		||||
		background: $colorThumbHoverBg;
 | 
			
		||||
		.l-date,
 | 
			
		||||
		.l-time {
 | 
			
		||||
			color: #fff;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,36 @@
 | 
			
		||||
.l-time-display {
 | 
			
		||||
	$transTime: 200ms;
 | 
			
		||||
    $controlSize: 14px;
 | 
			
		||||
    $control1ControlW: $controlSize + $interiorMargin;
 | 
			
		||||
    $control2ControlW: $control1ControlW * 2;
 | 
			
		||||
    line-height: 140%;
 | 
			
		||||
	&:hover {
 | 
			
		||||
		.l-btn.control {
 | 
			
		||||
		.l-btn.controls {
 | 
			
		||||
			opacity: 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&.l-timer {
 | 
			
		||||
        .l-value:before,
 | 
			
		||||
        .control {
 | 
			
		||||
            font-size: 0.8em;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .l-value:before {
 | 
			
		||||
            // Direction +/- element
 | 
			
		||||
            font-size: $controlSize;
 | 
			
		||||
            margin-right: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        .control {
 | 
			
		||||
        .controls {
 | 
			
		||||
            @include trans-prop-nice((width, opacity), $transTime);
 | 
			
		||||
            font-size: $controlSize;
 | 
			
		||||
            line-height: inherit;
 | 
			
		||||
            margin-right: 0;
 | 
			
		||||
            opacity: 0;
 | 
			
		||||
            width: 0;
 | 
			
		||||
            .flex-elem {
 | 
			
		||||
                margin-right: $interiorMargin;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
		&:hover .control {
 | 
			
		||||
            margin-right: $interiorMargin;
 | 
			
		||||
		&:hover .controls {
 | 
			
		||||
            opacity: 1;
 | 
			
		||||
			width: 1em;
 | 
			
		||||
			width: $control2ControlW;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -35,4 +38,34 @@
 | 
			
		||||
        color: pullForward($colorBodyFg, 50%);
 | 
			
		||||
        font-weight: 400;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // States
 | 
			
		||||
    &.s-state-stopped,
 | 
			
		||||
    &.s-state-paused {
 | 
			
		||||
        .l-value {
 | 
			
		||||
            opacity: 0.4;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.s-state-started {
 | 
			
		||||
        .l-value {
 | 
			
		||||
            opacity: 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.s-state-stopped {
 | 
			
		||||
        // Hide Stop button, 1controlW
 | 
			
		||||
        .t-btn-stop {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        &:hover .controls {  width: $control1ControlW;  }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.s-state-paused {
 | 
			
		||||
        // Paused, do something visual
 | 
			
		||||
        .l-value {
 | 
			
		||||
            &:before { @extend .pulse; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -132,5 +132,33 @@
 | 
			
		||||
	        }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.list-view {
 | 
			
		||||
    $s: 1.2em;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    th, td {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
    tr:hover td {
 | 
			
		||||
        background-color: $colorItemBg;
 | 
			
		||||
        color: $colorItemFg;
 | 
			
		||||
    }
 | 
			
		||||
    td {
 | 
			
		||||
        $p: $interiorMargin;
 | 
			
		||||
        @include ellipsize;
 | 
			
		||||
        color: $colorItemFg;
 | 
			
		||||
        font-size: 1em;
 | 
			
		||||
        line-height: $s;
 | 
			
		||||
        max-width: 0;
 | 
			
		||||
        padding-top: $p;
 | 
			
		||||
        padding-bottom: $p;
 | 
			
		||||
    }
 | 
			
		||||
    .t-item-icon {
 | 
			
		||||
        font-size: $s;
 | 
			
		||||
        margin-right: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
    .t-title-label {
 | 
			
		||||
        @include ellipsize; // Yep, need it here too as well as the <td>
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -193,6 +193,7 @@
 | 
			
		||||
 | 
			
		||||
            @include animToParams(overlayIn, $dur: $durLargeViewExpand, $delay: 0);
 | 
			
		||||
            background: $colorBodyBg;
 | 
			
		||||
            z-index: 101;
 | 
			
		||||
 | 
			
		||||
            .abs.inner-holder {
 | 
			
		||||
                opacity: 0;
 | 
			
		||||
@@ -203,10 +204,19 @@
 | 
			
		||||
                @include animToParams(contentsIn, $dur: 50ms, $delay: $durLargeViewExpand * 1.25);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Hide View Large button
 | 
			
		||||
            .t-btn-view-large {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
            z-index: 101;
 | 
			
		||||
 | 
			
		||||
            // But show View Large button when it's nested inside a Layout
 | 
			
		||||
            .t-frame-inner .t-frame-inner .t-btn-view-large { display: block; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // When multiple Large Views are visible, hide the blocker for all but the first
 | 
			
		||||
    & + .l-large-view {
 | 
			
		||||
        .blocker {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,28 +23,43 @@
 | 
			
		||||
    $ohH: $btnFrameH;
 | 
			
		||||
    $bc: $colorInteriorBorder;
 | 
			
		||||
    &.child-frame.panel {
 | 
			
		||||
        background: $colorBodyBg;
 | 
			
		||||
        border: 1px solid $bc;
 | 
			
		||||
        z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
 | 
			
		||||
        &:hover {
 | 
			
		||||
            border-color: lighten($bc, 10%);
 | 
			
		||||
        &:not(.no-frame) {
 | 
			
		||||
            background: $colorBodyBg;
 | 
			
		||||
            border: 1px solid $bc;
 | 
			
		||||
            &:hover {
 | 
			
		||||
                border-color: lighten($bc, 10%);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .object-browse-bar {
 | 
			
		||||
        font-size: 0.75em;
 | 
			
		||||
        height: $ohH;
 | 
			
		||||
        line-height: $ohH;
 | 
			
		||||
        .right {
 | 
			
		||||
            @include trans-prop-nice-fade($controlFadeMs);
 | 
			
		||||
            padding-left: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.t-object-type-timer,
 | 
			
		||||
    &.t-object-type-clock,
 | 
			
		||||
    &.t-object-type-hyperlink {
 | 
			
		||||
        // Hide the right side buttons for objects where they don't make sense
 | 
			
		||||
        // Note that this will hide the view Switcher button if applied
 | 
			
		||||
        // to an object that it.
 | 
			
		||||
        .object-browse-bar .right { display: none; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > .object-holder.abs {
 | 
			
		||||
        top: $ohH + $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
    .contents {
 | 
			
		||||
        $myM: $interiorMargin;
 | 
			
		||||
        top: $myM;
 | 
			
		||||
        right: $myM;
 | 
			
		||||
        bottom: $myM;
 | 
			
		||||
        left: $myM;
 | 
			
		||||
        $m: $interiorMargin;
 | 
			
		||||
        top: $m;
 | 
			
		||||
        right: $m;
 | 
			
		||||
        bottom: $m;
 | 
			
		||||
        left: $m;
 | 
			
		||||
    }
 | 
			
		||||
    &.frame-template {
 | 
			
		||||
        .s-button,
 | 
			
		||||
@@ -67,19 +82,67 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .view-switcher {
 | 
			
		||||
        margin-left: $interiorMargin; // Kick other top bar elements away when I'm present.
 | 
			
		||||
        margin-right: $interiorMargin; // Kick other top bar elements away when I'm present.
 | 
			
		||||
        // Hide the name when the view switcher is in a frame context
 | 
			
		||||
        .title-label {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.no-frame {
 | 
			
		||||
        background: transparent !important;
 | 
			
		||||
        border: none !important;
 | 
			
		||||
        .object-browse-bar .right {
 | 
			
		||||
            $m: 0; // $interiorMarginSm;
 | 
			
		||||
            background: rgba(black, 0.3);
 | 
			
		||||
            border-radius: $basicCr;
 | 
			
		||||
            padding: $interiorMarginSm;
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            top: $m; right: $m;
 | 
			
		||||
            z-index: 2;
 | 
			
		||||
        }
 | 
			
		||||
        &.t-frame-outer > .t-rep-frame {
 | 
			
		||||
            &.contents {
 | 
			
		||||
                $m: 2px;
 | 
			
		||||
                top: $m;
 | 
			
		||||
                right: $m;
 | 
			
		||||
                bottom: $m;
 | 
			
		||||
                left: $m;
 | 
			
		||||
            }
 | 
			
		||||
            > .t-frame-inner {
 | 
			
		||||
                > .object-browse-bar .left {
 | 
			
		||||
                    display: none;
 | 
			
		||||
                }
 | 
			
		||||
                > .object-holder.abs {
 | 
			
		||||
                    top: 0 !important;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /********************************************************** OBJECT TYPES */
 | 
			
		||||
    .t-object-type-hyperlink {
 | 
			
		||||
        .s-hyperlink.s-button {
 | 
			
		||||
            // When a hyperlink is a button in a frame, make it expand to fill out to the object-holder
 | 
			
		||||
            @extend .abs;
 | 
			
		||||
            .label {
 | 
			
		||||
                @include ellipsize();
 | 
			
		||||
                @include transform(translateY(-50%));
 | 
			
		||||
                padding: 0 $interiorMargin;
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                min-width: 0;
 | 
			
		||||
                left: 0; right: 0;
 | 
			
		||||
                text-align: center;
 | 
			
		||||
                top: 50%;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body.desktop .frame {
 | 
			
		||||
    // Hide local controls initially and show it them on hover when they're in an element that's in a frame context
 | 
			
		||||
    // Frame template is used because we need to target the lowest nested frame
 | 
			
		||||
    .view-switcher,
 | 
			
		||||
    .t-btn-view-large {
 | 
			
		||||
    .right {
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
    }
 | 
			
		||||
@@ -87,8 +150,7 @@ body.desktop .frame {
 | 
			
		||||
    // Target the first descendant so that we only show the elements in the outermost container.
 | 
			
		||||
    // Handles the case where we have layouts in layouts.
 | 
			
		||||
    &:hover > .object-browse-bar {
 | 
			
		||||
        .view-switcher,
 | 
			
		||||
        .t-btn-view-large {
 | 
			
		||||
        .right {
 | 
			
		||||
            opacity: 1;
 | 
			
		||||
            pointer-events: inherit;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -227,8 +227,13 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
 | 
			
		||||
    top: $ueTopBarH + $interiorMarginLg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-object-wrapper-inner {
 | 
			
		||||
    @include trans-prop-nice-resize(0.25s);
 | 
			
		||||
.l-object-wrapper {
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    @include trans-prop-nice((padding), 0.25s);
 | 
			
		||||
    .l-edit-controls {
 | 
			
		||||
        @include trans-prop-nice((height), 0.5s);
 | 
			
		||||
        height: 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.object-browse-bar .s-button,
 | 
			
		||||
@@ -345,46 +350,14 @@ body.desktop {
 | 
			
		||||
 | 
			
		||||
.s-status-editing {
 | 
			
		||||
    .l-object-wrapper {
 | 
			
		||||
        $t2Dur: $browseToEditAnimMs;
 | 
			
		||||
        $t1Dur: $t2Dur / 2;
 | 
			
		||||
        $pulseDur: $editBorderPulseMs;
 | 
			
		||||
        $bC0: rgba($colorEditAreaFg, 0.5);
 | 
			
		||||
        $bC100: rgba($colorEditAreaFg, 1);
 | 
			
		||||
 | 
			
		||||
        background-color: $colorEditAreaBg;
 | 
			
		||||
        border-radius: $controlCr;
 | 
			
		||||
        border: 1px dotted $bC0;
 | 
			
		||||
 | 
			
		||||
        // Transition 1
 | 
			
		||||
        @include keyframes(wrapperIn) {
 | 
			
		||||
            from { border: 0px dotted transparent; padding: 0; }
 | 
			
		||||
            to { border: 1px dotted $bC0; padding: 5px; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Do last
 | 
			
		||||
        @include keyframes(pulseNew) {
 | 
			
		||||
            from  { border-color: $bC0; }
 | 
			
		||||
            to { border-color: $bC100; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @include animation-name(wrapperIn, pulseNew);
 | 
			
		||||
        @include animation-duration($t1Dur, $pulseDur);
 | 
			
		||||
        @include animation-delay(0s, $t1Dur + $t2Dur);
 | 
			
		||||
        @include animation-direction(normal, alternate);
 | 
			
		||||
        @include animation-fill-mode(both, none);
 | 
			
		||||
        @include animation-iteration-count(1, infinite);
 | 
			
		||||
        @include animation-timing-function(ease-in-out, linear);
 | 
			
		||||
 | 
			
		||||
        border: 1px dotted $colorEditAreaFg;
 | 
			
		||||
        padding: $interiorMargin;
 | 
			
		||||
 | 
			
		||||
        .l-edit-controls {
 | 
			
		||||
            height: 0;
 | 
			
		||||
            height: $ueEditToolBarH + $interiorMargin; margin-bottom: $interiorMargin;
 | 
			
		||||
            border-bottom: 1px solid $colorInteriorBorder;
 | 
			
		||||
            // Transition 2: reveal edit controls
 | 
			
		||||
            @include keyframes(editIn) {
 | 
			
		||||
                from { border-bottom: 0px solid transparent; height: 0; margin-bottom: 0; }
 | 
			
		||||
                to { border-bottom: 1px solid $colorInteriorBorder; height: $ueEditToolBarH + $interiorMargin; margin-bottom: $interiorMargin; }
 | 
			
		||||
            }
 | 
			
		||||
            @include animToParams(editIn, $dur: $t2Dur, $delay: $t1Dur);
 | 
			
		||||
            .tool-bar {
 | 
			
		||||
                right: $interiorMargin;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,21 +19,36 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
.s-selectable {
 | 
			
		||||
    border: 1px solid transparent;
 | 
			
		||||
.s-hover-border {
 | 
			
		||||
    border: 1px dotted transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    &.s-hover {
 | 
			
		||||
        // Styles when hovering over a selectable object
 | 
			
		||||
        border-color: $colorSelectableHov !important;
 | 
			
		||||
.s-status-editing {
 | 
			
		||||
    // Limit to editing mode until we have sub-object selection
 | 
			
		||||
    .s-hover-border {
 | 
			
		||||
        // Show a border by default so user can see object bounds and empty objects
 | 
			
		||||
        border: 1px dotted rgba($colorSelectableSelectedPrimary, 0.3) !important;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            border-color: rgba($colorSelectableSelectedPrimary, 0.7) !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.s-selected {
 | 
			
		||||
    .s-selected > .s-hover-border,
 | 
			
		||||
    .s-selected.s-hover-border {
 | 
			
		||||
        // Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
 | 
			
		||||
        border-color: $colorSelectableSelectedPrimary !important;
 | 
			
		||||
        @include boxShdwLarge();
 | 
			
		||||
        // Show edit-corners if you got 'em
 | 
			
		||||
        .edit-corner {
 | 
			
		||||
            display: block;
 | 
			
		||||
            &:hover {
 | 
			
		||||
                background-color: rgba($colorKey, 1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.s-moveable {
 | 
			
		||||
        @include boxShdwLarge();
 | 
			
		||||
    .s-selected > .s-moveable,
 | 
			
		||||
    .s-selected.s-moveable {
 | 
			
		||||
        cursor: move;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -26,9 +26,11 @@
 | 
			
		||||
	.l-control-group {
 | 
			
		||||
		height: $btnToolbarH;
 | 
			
		||||
	}
 | 
			
		||||
	input[type="text"] {
 | 
			
		||||
	input[type="text"],
 | 
			
		||||
    input[type="search"],
 | 
			
		||||
    input[type="number"] {
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		font-size: .9em;
 | 
			
		||||
		font-size: .8em;
 | 
			
		||||
		height: $btnToolbarH;
 | 
			
		||||
		margin-bottom: 1px;
 | 
			
		||||
		position: relative;
 | 
			
		||||
 
 | 
			
		||||
@@ -83,13 +83,14 @@ define(
 | 
			
		||||
                // Callback to fire after each timeout;
 | 
			
		||||
                // update bounds and schedule another timeout
 | 
			
		||||
                function onInterval() {
 | 
			
		||||
                    if (!active) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    fireEval({
 | 
			
		||||
                        width: element[0].offsetWidth,
 | 
			
		||||
                        height: element[0].offsetHeight
 | 
			
		||||
                    });
 | 
			
		||||
                    if (active) {
 | 
			
		||||
                        $timeout(onInterval, currentInterval(), false);
 | 
			
		||||
                    }
 | 
			
		||||
                    $timeout(onInterval, currentInterval(), false);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Stop running in the background
 | 
			
		||||
 
 | 
			
		||||
@@ -93,13 +93,21 @@ define(
 | 
			
		||||
         * @memberof platform/commonUI/general
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function MCTSplitPane($parse, $log, $interval) {
 | 
			
		||||
        function MCTSplitPane($parse, $log, $interval, $window) {
 | 
			
		||||
            function controller($scope, $element, $attrs) {
 | 
			
		||||
                var anchorKey = $attrs.anchor || DEFAULT_ANCHOR,
 | 
			
		||||
                    positionParsed = $parse($attrs.position),
 | 
			
		||||
                    anchor,
 | 
			
		||||
                    activeInterval,
 | 
			
		||||
                    positionParsed = $parse($attrs.position),
 | 
			
		||||
                    position; // Start undefined, until explicitly set
 | 
			
		||||
                    position,
 | 
			
		||||
                    splitterSize,
 | 
			
		||||
 | 
			
		||||
                    alias = $attrs.alias !== undefined ?
 | 
			
		||||
                      "mctSplitPane-" + $attrs.alias : undefined,
 | 
			
		||||
 | 
			
		||||
                    //convert string to number from localStorage
 | 
			
		||||
                    userWidthPreference = $window.localStorage.getItem(alias) === null ?
 | 
			
		||||
                      undefined : Number($window.localStorage.getItem(alias));
 | 
			
		||||
 | 
			
		||||
                // Get relevant size (height or width) of DOM element
 | 
			
		||||
                function getSize(domElement) {
 | 
			
		||||
@@ -114,11 +122,11 @@ define(
 | 
			
		||||
                    var first = children.eq(anchor.reversed ? 2 : 0),
 | 
			
		||||
                        splitter = children.eq(1),
 | 
			
		||||
                        last = children.eq(anchor.reversed ? 0 : 2),
 | 
			
		||||
                        splitterSize = getSize(splitter[0]),
 | 
			
		||||
                        firstSize;
 | 
			
		||||
 | 
			
		||||
                    splitterSize = getSize(splitter[0]);
 | 
			
		||||
                    first.css(anchor.edge, "0px");
 | 
			
		||||
                    first.css(anchor.dimension, (position - splitterSize) + 'px');
 | 
			
		||||
                    first.css(anchor.dimension, (userWidthPreference || position) + 'px');
 | 
			
		||||
 | 
			
		||||
                    // Get actual size (to obey min-width etc.)
 | 
			
		||||
                    firstSize = getSize(first[0]);
 | 
			
		||||
@@ -126,9 +134,8 @@ define(
 | 
			
		||||
                    splitter.css(anchor.edge, firstSize + 'px');
 | 
			
		||||
                    splitter.css(anchor.opposite, "auto");
 | 
			
		||||
 | 
			
		||||
                    last.css(anchor.edge, (firstSize + splitterSize) + 'px');
 | 
			
		||||
                    last.css(anchor.edge, firstSize + splitterSize + 'px');
 | 
			
		||||
                    last.css(anchor.opposite, "0px");
 | 
			
		||||
 | 
			
		||||
                    position = firstSize + splitterSize;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -169,6 +176,17 @@ define(
 | 
			
		||||
                    return position;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function setUserWidthPreference(value) {
 | 
			
		||||
                    userWidthPreference = value - splitterSize;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function persistToLocalStorage(value) {
 | 
			
		||||
                    if (alias) {
 | 
			
		||||
                        userWidthPreference = value - splitterSize;
 | 
			
		||||
                        $window.localStorage.setItem(alias, userWidthPreference);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Dynamically apply a CSS class to elements when the user
 | 
			
		||||
                // is actively resizing
 | 
			
		||||
                function toggleClass(classToToggle) {
 | 
			
		||||
@@ -196,18 +214,31 @@ define(
 | 
			
		||||
                activeInterval = $interval(function () {
 | 
			
		||||
                    getSetPosition(getSetPosition());
 | 
			
		||||
                }, POLLING_INTERVAL, 0, false);
 | 
			
		||||
 | 
			
		||||
                // ...and stop polling when we're destroyed.
 | 
			
		||||
                $scope.$on('$destroy', function () {
 | 
			
		||||
                    $interval.cancel(activeInterval);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                // Interface exposed by controller, for mct-splitter to user
 | 
			
		||||
                return {
 | 
			
		||||
                    position: getSetPosition,
 | 
			
		||||
                    toggleClass: toggleClass,
 | 
			
		||||
                    anchor: function () {
 | 
			
		||||
                        return anchor;
 | 
			
		||||
                    },
 | 
			
		||||
                    position: function (value) {
 | 
			
		||||
                        if (arguments.length > 0) {
 | 
			
		||||
                            setUserWidthPreference(value);
 | 
			
		||||
                            return getSetPosition(value);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            return getSetPosition();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    startResizing: function () {
 | 
			
		||||
                        toggleClass('resizing');
 | 
			
		||||
                    },
 | 
			
		||||
                    endResizing: function (finalPosition) {
 | 
			
		||||
                        persistToLocalStorage(finalPosition);
 | 
			
		||||
                        toggleClass('resizing');
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
@@ -219,9 +250,7 @@ define(
 | 
			
		||||
                controller: ['$scope', '$element', '$attrs', controller]
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MCTSplitPane;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,15 +37,16 @@ define(
 | 
			
		||||
         */
 | 
			
		||||
        function MCTSplitter() {
 | 
			
		||||
            function link(scope, element, attrs, mctSplitPane) {
 | 
			
		||||
                var initialPosition;
 | 
			
		||||
                var initialPosition,
 | 
			
		||||
                    newPosition;
 | 
			
		||||
 | 
			
		||||
                element.addClass("splitter");
 | 
			
		||||
 | 
			
		||||
                scope.splitter = {
 | 
			
		||||
                    // Begin moving this splitter
 | 
			
		||||
                    startMove: function () {
 | 
			
		||||
                        mctSplitPane.startResizing();
 | 
			
		||||
                        initialPosition = mctSplitPane.position();
 | 
			
		||||
                        mctSplitPane.toggleClass('resizing');
 | 
			
		||||
                    },
 | 
			
		||||
                    // Handle user changes to splitter position
 | 
			
		||||
                    move: function (delta) {
 | 
			
		||||
@@ -55,12 +56,13 @@ define(
 | 
			
		||||
                                (anchor.reversed ? -1 : 1);
 | 
			
		||||
 | 
			
		||||
                        // Update the position of this splitter
 | 
			
		||||
                        mctSplitPane.position(initialPosition + pixelDelta);
 | 
			
		||||
                        newPosition =  initialPosition + pixelDelta;
 | 
			
		||||
                        mctSplitPane.position(newPosition);
 | 
			
		||||
                    },
 | 
			
		||||
                    // Grab the event when the user is done moving
 | 
			
		||||
                    // the splitter and pass it on
 | 
			
		||||
                    endMove: function () {
 | 
			
		||||
                        mctSplitPane.toggleClass('resizing');
 | 
			
		||||
                        mctSplitPane.endResizing(newPosition);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
@@ -83,4 +85,3 @@ define(
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -102,11 +102,16 @@ define(
 | 
			
		||||
                // Broadcast a destroy event
 | 
			
		||||
                mockScope.$on.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                testElement.offsetWidth = 300;
 | 
			
		||||
                testElement.offsetHeight = 350;
 | 
			
		||||
                mockScope.$eval.reset();
 | 
			
		||||
 | 
			
		||||
                // Fire the timeout
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
 | 
			
		||||
                // Should NOT have scheduled another timeout
 | 
			
		||||
                expect(mockTimeout.calls.length).toEqual(2);
 | 
			
		||||
                expect(mockScope.$eval).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("triggers a digest cycle when size changes", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,8 @@ define(
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockInterval,
 | 
			
		||||
                mockParsed,
 | 
			
		||||
                mctSplitPane;
 | 
			
		||||
                mctSplitPane,
 | 
			
		||||
                mockWindow = {};
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockParse = jasmine.createSpy('$parse');
 | 
			
		||||
@@ -48,13 +49,23 @@ define(
 | 
			
		||||
                mockInterval.cancel = jasmine.createSpy('mockCancel');
 | 
			
		||||
                mockParsed = jasmine.createSpy('parsed');
 | 
			
		||||
                mockParsed.assign = jasmine.createSpy('assign');
 | 
			
		||||
 | 
			
		||||
                mockParse.andReturn(mockParsed);
 | 
			
		||||
 | 
			
		||||
                mockWindow.localStorage =  {
 | 
			
		||||
                    store: {},
 | 
			
		||||
                    setItem: function (key, value) {
 | 
			
		||||
                        this.store[key] = value;
 | 
			
		||||
                    },
 | 
			
		||||
                    getItem: function (key) {
 | 
			
		||||
                        return this.store[key];
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mctSplitPane = new MCTSplitPane(
 | 
			
		||||
                    mockParse,
 | 
			
		||||
                    mockLog,
 | 
			
		||||
                    mockInterval
 | 
			
		||||
                    mockInterval,
 | 
			
		||||
                    mockWindow
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +96,7 @@ define(
 | 
			
		||||
                        jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']);
 | 
			
		||||
                    mockElement =
 | 
			
		||||
                        jasmine.createSpyObj('element', JQLITE_METHODS);
 | 
			
		||||
                    testAttrs = {};
 | 
			
		||||
                    testAttrs = {alias: 'rightSide'};
 | 
			
		||||
                    mockChildren =
 | 
			
		||||
                        jasmine.createSpyObj('children', JQLITE_METHODS);
 | 
			
		||||
                    mockFirstPane =
 | 
			
		||||
@@ -142,10 +153,14 @@ define(
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("allows classes to be toggled on contained elements", function () {
 | 
			
		||||
                    controller.toggleClass('resizing');
 | 
			
		||||
                    expect(mockChildren.toggleClass)
 | 
			
		||||
                        .toHaveBeenCalledWith('resizing');
 | 
			
		||||
                it("applies resizing class to children when resizing", function () {
 | 
			
		||||
                    controller.startResizing();
 | 
			
		||||
                    expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("removes resizing class from children when resizing action ends", function () {
 | 
			
		||||
                    controller.endResizing(0);
 | 
			
		||||
                    expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("allows positions to be set", function () {
 | 
			
		||||
@@ -198,6 +213,12 @@ define(
 | 
			
		||||
                    fireOn('$destroy');
 | 
			
		||||
                    expect(mockInterval.cancel).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("saves user preference to localStorage when user is done resizing", function () {
 | 
			
		||||
                    controller.endResizing(100);
 | 
			
		||||
                    expect(Number(mockWindow.localStorage.getItem('mctSplitPane-rightSide'))).toEqual(100 - mockSplitter[0].offsetWidth);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ define(
 | 
			
		||||
                    testAttrs = {};
 | 
			
		||||
                    mockSplitPane = jasmine.createSpyObj(
 | 
			
		||||
                        'mctSplitPane',
 | 
			
		||||
                        ['position', 'toggleClass', 'anchor']
 | 
			
		||||
                        ['position', 'startResizing', 'endResizing', 'anchor']
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    mctSplitter.link(
 | 
			
		||||
@@ -86,9 +86,9 @@ define(
 | 
			
		||||
                        mockScope.splitter.startMove();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("adds a 'resizing' class", function () {
 | 
			
		||||
                        expect(mockSplitPane.toggleClass)
 | 
			
		||||
                            .toHaveBeenCalledWith('resizing');
 | 
			
		||||
                    it("tell's the splitter when it is resizing", function () {
 | 
			
		||||
                        expect(mockSplitPane.startResizing)
 | 
			
		||||
                            .toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("repositions during drag", function () {
 | 
			
		||||
@@ -97,11 +97,10 @@ define(
 | 
			
		||||
                            .toHaveBeenCalledWith(testPosition + 10);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("removes the 'resizing' class when finished", function () {
 | 
			
		||||
                        mockSplitPane.toggleClass.reset();
 | 
			
		||||
                    it("tell's the splitter when it is done resizing", function () {
 | 
			
		||||
                        mockScope.splitter.move([10,0]);
 | 
			
		||||
                        mockScope.splitter.endMove();
 | 
			
		||||
                        expect(mockSplitPane.toggleClass)
 | 
			
		||||
                            .toHaveBeenCalledWith('resizing');
 | 
			
		||||
                        expect(mockSplitPane.endResizing).toHaveBeenCalledWith(testPosition + 10);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
 
 | 
			
		||||
@@ -191,6 +191,9 @@ $colorItemTreeVCHover: pullForward($colorItemTreeVC, 20%);
 | 
			
		||||
$colorItemTreeSelectedVC: $colorItemTreeVC;
 | 
			
		||||
$shdwItemTreeIcon: 0.6;
 | 
			
		||||
 | 
			
		||||
// Images
 | 
			
		||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
 | 
			
		||||
 | 
			
		||||
// Scrollbar
 | 
			
		||||
$scrollbarTrackSize: 10px;
 | 
			
		||||
$scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px;
 | 
			
		||||
 
 | 
			
		||||
@@ -191,6 +191,9 @@ $colorItemTreeVCHover: $colorKey;
 | 
			
		||||
$colorItemTreeSelectedVC: $colorBodyBg;
 | 
			
		||||
$shdwItemTreeIcon: none;
 | 
			
		||||
 | 
			
		||||
// Images
 | 
			
		||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
 | 
			
		||||
 | 
			
		||||
// Scrollbar
 | 
			
		||||
$scrollbarTrackSize: 10px;
 | 
			
		||||
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    "moment-timezone",
 | 
			
		||||
    "./src/indicators/ClockIndicator",
 | 
			
		||||
    "./src/services/TickerService",
 | 
			
		||||
    "./src/controllers/ClockController",
 | 
			
		||||
@@ -28,10 +29,13 @@ define([
 | 
			
		||||
    "./src/controllers/RefreshingController",
 | 
			
		||||
    "./src/actions/StartTimerAction",
 | 
			
		||||
    "./src/actions/RestartTimerAction",
 | 
			
		||||
    "./src/actions/StopTimerAction",
 | 
			
		||||
    "./src/actions/PauseTimerAction",
 | 
			
		||||
    "text!./res/templates/clock.html",
 | 
			
		||||
    "text!./res/templates/timer.html",
 | 
			
		||||
    'legacyRegistry'
 | 
			
		||||
], function (
 | 
			
		||||
    MomentTimezone,
 | 
			
		||||
    ClockIndicator,
 | 
			
		||||
    TickerService,
 | 
			
		||||
    ClockController,
 | 
			
		||||
@@ -39,6 +43,8 @@ define([
 | 
			
		||||
    RefreshingController,
 | 
			
		||||
    StartTimerAction,
 | 
			
		||||
    RestartTimerAction,
 | 
			
		||||
    StopTimerAction,
 | 
			
		||||
    PauseTimerAction,
 | 
			
		||||
    clockTemplate,
 | 
			
		||||
    timerTemplate,
 | 
			
		||||
    legacyRegistry
 | 
			
		||||
@@ -139,6 +145,17 @@ define([
 | 
			
		||||
                    "cssClass": "icon-play",
 | 
			
		||||
                    "priority": "preferred"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "timer.pause",
 | 
			
		||||
                    "implementation": PauseTimerAction,
 | 
			
		||||
                    "depends": [
 | 
			
		||||
                        "now"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "category": "contextual",
 | 
			
		||||
                    "name": "Pause",
 | 
			
		||||
                    "cssClass": "icon-pause",
 | 
			
		||||
                    "priority": "preferred"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "timer.restart",
 | 
			
		||||
                    "implementation": RestartTimerAction,
 | 
			
		||||
@@ -149,6 +166,17 @@ define([
 | 
			
		||||
                    "name": "Restart at 0",
 | 
			
		||||
                    "cssClass": "icon-refresh",
 | 
			
		||||
                    "priority": "preferred"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "timer.stop",
 | 
			
		||||
                    "implementation": StopTimerAction,
 | 
			
		||||
                    "depends": [
 | 
			
		||||
                        "now"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "category": "contextual",
 | 
			
		||||
                    "name": "Stop",
 | 
			
		||||
                    "cssClass": "icon-box",
 | 
			
		||||
                    "priority": "preferred"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "types": [
 | 
			
		||||
@@ -200,13 +228,20 @@ define([
 | 
			
		||||
                                    "cssClass": "l-inline"
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            "key": "timezone",
 | 
			
		||||
                            "name": "Timezone",
 | 
			
		||||
                            "control": "autocomplete",
 | 
			
		||||
                            "options": MomentTimezone.tz.names()
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    "model": {
 | 
			
		||||
                        "clockFormat": [
 | 
			
		||||
                            "YYYY/MM/DD hh:mm:ss",
 | 
			
		||||
                            "clock12"
 | 
			
		||||
                        ]
 | 
			
		||||
                        ],
 | 
			
		||||
                        "timezone": "UTC"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,11 +19,16 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="l-time-display l-digital l-timer s-timer" ng-controller="TimerController as timer">
 | 
			
		||||
<div class="l-time-display l-digital l-timer s-timer s-state-{{timer.timerState}}" ng-controller="TimerController as timer">
 | 
			
		||||
	<div class="l-elem-wrapper l-flex-row">
 | 
			
		||||
		<a ng-click="timer.clickButton()"
 | 
			
		||||
			title="{{timer.buttonText()}}"
 | 
			
		||||
			class="flex-elem control s-icon-button {{timer.buttonCssClass()}}"></a>
 | 
			
		||||
        <div class="l-elem-wrapper l-flex-row controls">
 | 
			
		||||
            <a ng-click="timer.clickStopButton()"
 | 
			
		||||
               title="Stop"
 | 
			
		||||
               class="flex-elem s-icon-button t-btn-stop icon-box"></a>
 | 
			
		||||
            <a ng-click="timer.clickButton()"
 | 
			
		||||
               title="{{timer.buttonText()}}"
 | 
			
		||||
               class="flex-elem s-icon-button t-btn-pauseplay {{timer.buttonCssClass()}}"></a>
 | 
			
		||||
        </div>
 | 
			
		||||
	    <span class="flex-elem l-value {{timer.signClass()}}">
 | 
			
		||||
	        <span class="value"
 | 
			
		||||
		        ng-class="{ active:timer.text() }">{{timer.text() || "--:--:--"}}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,13 +25,10 @@ define(
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements the "Start" and "Restart" action for timers.
 | 
			
		||||
         * Implements the "Pause" action for timers.
 | 
			
		||||
         *
 | 
			
		||||
         * Sets the reference timestamp in a timer to the current
 | 
			
		||||
         * time, such that it begins counting up.
 | 
			
		||||
         *
 | 
			
		||||
         * Both "Start" and "Restart" share this implementation, but
 | 
			
		||||
         * control their visibility with different `appliesTo` behavior.
 | 
			
		||||
         * Sets the reference pausedTime in a timer to the current
 | 
			
		||||
         * time, such that it stops counting up.
 | 
			
		||||
         *
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @memberof platform/features/clock
 | 
			
		||||
@@ -40,22 +37,35 @@ define(
 | 
			
		||||
         *        time (typically wrapping `Date.now`)
 | 
			
		||||
         * @param {ActionContext} context the context for this action
 | 
			
		||||
         */
 | 
			
		||||
        function AbstractStartTimerAction(now, context) {
 | 
			
		||||
        function PauseTimerAction(now, context) {
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        AbstractStartTimerAction.prototype.perform = function () {
 | 
			
		||||
        PauseTimerAction.appliesTo = function (context) {
 | 
			
		||||
            var model =
 | 
			
		||||
                (context.domainObject && context.domainObject.getModel()) ||
 | 
			
		||||
                {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // We show this variant for timers which have
 | 
			
		||||
            // a target time, or is in a playing state.
 | 
			
		||||
            return model.type === 'timer' &&
 | 
			
		||||
                    model.timerState === 'started';
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        PauseTimerAction.prototype.perform = function () {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
                now = this.now;
 | 
			
		||||
 | 
			
		||||
            function setTimestamp(model) {
 | 
			
		||||
                model.timestamp = now();
 | 
			
		||||
            function updateModel(model) {
 | 
			
		||||
                model.timerState = 'paused';
 | 
			
		||||
                model.pausedTime = now();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return domainObject.useCapability('mutation', setTimestamp);
 | 
			
		||||
            return domainObject.useCapability('mutation', updateModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return AbstractStartTimerAction;
 | 
			
		||||
        return PauseTimerAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -21,8 +21,8 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./AbstractStartTimerAction'],
 | 
			
		||||
    function (AbstractStartTimerAction) {
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements the "Restart at 0" action.
 | 
			
		||||
@@ -30,7 +30,6 @@ define(
 | 
			
		||||
         * Behaves the same as (and delegates functionality to)
 | 
			
		||||
         * the "Start" action.
 | 
			
		||||
         *
 | 
			
		||||
         * @extends {platform/features/clock.AbstractTimerAction}
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @memberof platform/features/clock
 | 
			
		||||
         * @constructor
 | 
			
		||||
@@ -39,24 +38,33 @@ define(
 | 
			
		||||
         * @param {ActionContext} context the context for this action
 | 
			
		||||
         */
 | 
			
		||||
        function RestartTimerAction(now, context) {
 | 
			
		||||
            AbstractStartTimerAction.apply(this, [now, context]);
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RestartTimerAction.prototype =
 | 
			
		||||
            Object.create(AbstractStartTimerAction.prototype);
 | 
			
		||||
 | 
			
		||||
        RestartTimerAction.appliesTo = function (context) {
 | 
			
		||||
            var model =
 | 
			
		||||
                (context.domainObject && context.domainObject.getModel()) ||
 | 
			
		||||
                {};
 | 
			
		||||
 | 
			
		||||
            // We show this variant for timers which already have
 | 
			
		||||
            // a target time.
 | 
			
		||||
            // We show this variant for timers which already have a target time.
 | 
			
		||||
            return model.type === 'timer' &&
 | 
			
		||||
                    model.timestamp !== undefined;
 | 
			
		||||
                model.timerState !== 'stopped';
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        RestartTimerAction.prototype.perform = function () {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
                now = this.now;
 | 
			
		||||
 | 
			
		||||
            function updateModel(model) {
 | 
			
		||||
                model.timestamp = now();
 | 
			
		||||
                model.timerState = 'started';
 | 
			
		||||
                model.pausedTime = undefined;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return domainObject.useCapability('mutation', updateModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return RestartTimerAction;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./AbstractStartTimerAction'],
 | 
			
		||||
    function (AbstractStartTimerAction) {
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements the "Start" action for timers.
 | 
			
		||||
@@ -30,7 +30,6 @@ define(
 | 
			
		||||
         * Sets the reference timestamp in a timer to the current
 | 
			
		||||
         * time, such that it begins counting up.
 | 
			
		||||
         *
 | 
			
		||||
         * @extends {platform/features/clock.AbstractTimerAction}
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @memberof platform/features/clock
 | 
			
		||||
         * @constructor
 | 
			
		||||
@@ -39,12 +38,10 @@ define(
 | 
			
		||||
         * @param {ActionContext} context the context for this action
 | 
			
		||||
         */
 | 
			
		||||
        function StartTimerAction(now, context) {
 | 
			
		||||
            AbstractStartTimerAction.apply(this, [now, context]);
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        StartTimerAction.prototype =
 | 
			
		||||
            Object.create(AbstractStartTimerAction.prototype);
 | 
			
		||||
 | 
			
		||||
        StartTimerAction.appliesTo = function (context) {
 | 
			
		||||
            var model =
 | 
			
		||||
                (context.domainObject && context.domainObject.getModel()) ||
 | 
			
		||||
@@ -53,10 +50,28 @@ define(
 | 
			
		||||
            // We show this variant for timers which do not yet have
 | 
			
		||||
            // a target time.
 | 
			
		||||
            return model.type === 'timer' &&
 | 
			
		||||
                    model.timestamp === undefined;
 | 
			
		||||
                    model.timerState !== 'started';
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        StartTimerAction.prototype.perform = function () {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
                now = this.now;
 | 
			
		||||
 | 
			
		||||
            function updateModel(model) {
 | 
			
		||||
                //if we are resuming
 | 
			
		||||
                if (model.pausedTime) {
 | 
			
		||||
                    var timeShift = now() - model.pausedTime;
 | 
			
		||||
                    model.timestamp = model.timestamp + timeShift;
 | 
			
		||||
                } else {
 | 
			
		||||
                    model.timestamp = now();
 | 
			
		||||
                }
 | 
			
		||||
                model.timerState = 'started';
 | 
			
		||||
                model.pausedTime = undefined;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return domainObject.useCapability('mutation', updateModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return StartTimerAction;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										71
									
								
								platform/features/clock/src/actions/StopTimerAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								platform/features/clock/src/actions/StopTimerAction.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements the "Stop" action for timers.
 | 
			
		||||
         *
 | 
			
		||||
         * Sets the reference timestamp in a timer undefined,
 | 
			
		||||
         * such that it is reset and makes no movements.
 | 
			
		||||
         *
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @memberof platform/features/clock
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {Function} now a function which returns the current
 | 
			
		||||
         *        time (typically wrapping `Date.now`)
 | 
			
		||||
         * @param {ActionContext} context the context for this action
 | 
			
		||||
         */
 | 
			
		||||
        function StopTimerAction(now, context) {
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        StopTimerAction.appliesTo = function (context) {
 | 
			
		||||
            var model =
 | 
			
		||||
                (context.domainObject && context.domainObject.getModel()) ||
 | 
			
		||||
                {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // We show this variant for timers which do not yet have
 | 
			
		||||
            // a target time.
 | 
			
		||||
            return model.type === 'timer' &&
 | 
			
		||||
                    model.timerState !== 'stopped';
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        StopTimerAction.prototype.perform = function () {
 | 
			
		||||
            var domainObject = this.domainObject;
 | 
			
		||||
 | 
			
		||||
            function updateModel(model) {
 | 
			
		||||
                model.timestamp = undefined;
 | 
			
		||||
                model.timerState = 'stopped';
 | 
			
		||||
                model.pausedTime = undefined;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return domainObject.useCapability('mutation', updateModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return StopTimerAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -20,9 +20,14 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['moment'],
 | 
			
		||||
    function (moment) {
 | 
			
		||||
define([
 | 
			
		||||
    'moment',
 | 
			
		||||
    'moment-timezone'
 | 
			
		||||
    ],
 | 
			
		||||
    function (
 | 
			
		||||
        moment,
 | 
			
		||||
        momentTimezone
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Controller for views of a Clock domain object.
 | 
			
		||||
@@ -37,10 +42,13 @@ define(
 | 
			
		||||
            var lastTimestamp,
 | 
			
		||||
                unlisten,
 | 
			
		||||
                timeFormat,
 | 
			
		||||
                zoneName,
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            function update() {
 | 
			
		||||
                var m = moment.utc(lastTimestamp);
 | 
			
		||||
                var m = zoneName ?
 | 
			
		||||
                    moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp);
 | 
			
		||||
                self.zoneAbbr = m.zoneAbbr();
 | 
			
		||||
                self.textValue = timeFormat && m.format(timeFormat);
 | 
			
		||||
                self.ampmValue = m.format("A"); // Just the AM or PM part
 | 
			
		||||
            }
 | 
			
		||||
@@ -50,21 +58,23 @@ define(
 | 
			
		||||
                update();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateFormat(clockFormat) {
 | 
			
		||||
            function updateModel(model) {
 | 
			
		||||
                var baseFormat;
 | 
			
		||||
                if (model !== undefined) {
 | 
			
		||||
                    baseFormat = model.clockFormat[0];
 | 
			
		||||
 | 
			
		||||
                if (clockFormat !== undefined) {
 | 
			
		||||
                    baseFormat = clockFormat[0];
 | 
			
		||||
 | 
			
		||||
                    self.use24 = clockFormat[1] === 'clock24';
 | 
			
		||||
                    self.use24 = model.clockFormat[1] === 'clock24';
 | 
			
		||||
                    timeFormat = self.use24 ?
 | 
			
		||||
                            baseFormat.replace('hh', "HH") : baseFormat;
 | 
			
		||||
 | 
			
		||||
                    // If wrong timezone is provided, the UTC will be used
 | 
			
		||||
                    zoneName = momentTimezone.tz.names().includes(model.timezone) ?
 | 
			
		||||
                        model.timezone : "UTC";
 | 
			
		||||
                    update();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // Pull in the clock format from the domain object model
 | 
			
		||||
            $scope.$watch('model.clockFormat', updateFormat);
 | 
			
		||||
 | 
			
		||||
            // Pull in the model (clockFormat and timezone) from the domain object model
 | 
			
		||||
            $scope.$watch('model', updateModel);
 | 
			
		||||
 | 
			
		||||
            // Listen for clock ticks ... and stop listening on destroy
 | 
			
		||||
            unlisten = tickerService.listen(tick);
 | 
			
		||||
@@ -76,7 +86,7 @@ define(
 | 
			
		||||
         * @returns {string}
 | 
			
		||||
         */
 | 
			
		||||
        ClockController.prototype.zone = function () {
 | 
			
		||||
            return "UTC";
 | 
			
		||||
            return this.zoneAbbr;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,7 @@ define(
 | 
			
		||||
                active = true,
 | 
			
		||||
                relativeTimestamp,
 | 
			
		||||
                lastTimestamp,
 | 
			
		||||
                relativeTimerState,
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            function update() {
 | 
			
		||||
@@ -51,12 +52,9 @@ define(
 | 
			
		||||
                    self.textValue = formatter(timeDelta);
 | 
			
		||||
                    self.signValue = timeDelta < 0 ? "-" :
 | 
			
		||||
                        timeDelta >= 1000 ? "+" : "";
 | 
			
		||||
                    self.signCssClass = timeDelta < 0 ? "icon-minus" :
 | 
			
		||||
                        timeDelta >= 1000 ? "icon-plus" : "";
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.textValue = "";
 | 
			
		||||
                    self.signValue = "";
 | 
			
		||||
                    self.signCssClass = "";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -68,19 +66,50 @@ define(
 | 
			
		||||
                relativeTimestamp = timestamp;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateTimerState(timerState) {
 | 
			
		||||
                self.timerState = relativeTimerState = timerState;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateActions(actionCapability, actionKey) {
 | 
			
		||||
                self.relevantAction = actionCapability &&
 | 
			
		||||
                    actionCapability.getActions(actionKey)[0];
 | 
			
		||||
 | 
			
		||||
                self.stopAction = relativeTimerState !== 'stopped' ?
 | 
			
		||||
                    actionCapability && actionCapability.getActions('timer.stop')[0] : undefined;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function isPaused() {
 | 
			
		||||
                return relativeTimerState === 'paused';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function handleLegacyTimer(model) {
 | 
			
		||||
                if (model.timerState === undefined) {
 | 
			
		||||
                    model.timerState = model.timestamp === undefined ?
 | 
			
		||||
                        'stopped' : 'started';
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateObject(domainObject) {
 | 
			
		||||
                var model = domainObject.getModel(),
 | 
			
		||||
                    timestamp = model.timestamp,
 | 
			
		||||
                var model = domainObject.getModel();
 | 
			
		||||
                handleLegacyTimer(model);
 | 
			
		||||
 | 
			
		||||
                var timestamp = model.timestamp,
 | 
			
		||||
                    formatKey = model.timerFormat,
 | 
			
		||||
                    timerState = model.timerState,
 | 
			
		||||
                    actionCapability = domainObject.getCapability('action'),
 | 
			
		||||
                    actionKey = (timestamp === undefined) ?
 | 
			
		||||
                            'timer.start' : 'timer.restart';
 | 
			
		||||
                    actionKey = (timerState !== 'started') ?
 | 
			
		||||
                        'timer.start' : 'timer.pause';
 | 
			
		||||
 | 
			
		||||
                updateFormat(formatKey);
 | 
			
		||||
                updateTimestamp(timestamp);
 | 
			
		||||
                updateTimerState(timerState);
 | 
			
		||||
                updateActions(actionCapability, actionKey);
 | 
			
		||||
 | 
			
		||||
                self.relevantAction = actionCapability &&
 | 
			
		||||
                    actionCapability.getActions(actionKey)[0];
 | 
			
		||||
                //if paused on startup show last known position
 | 
			
		||||
                if (isPaused() && !lastTimestamp) {
 | 
			
		||||
                    lastTimestamp = model.pausedTime;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                update();
 | 
			
		||||
            }
 | 
			
		||||
@@ -98,8 +127,16 @@ define(
 | 
			
		||||
            function tick() {
 | 
			
		||||
                var lastSign = self.signValue,
 | 
			
		||||
                    lastText = self.textValue;
 | 
			
		||||
                lastTimestamp = now();
 | 
			
		||||
                update();
 | 
			
		||||
 | 
			
		||||
                if (!isPaused()) {
 | 
			
		||||
                    lastTimestamp = now();
 | 
			
		||||
                    update();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (relativeTimerState === undefined) {
 | 
			
		||||
                    handleModification();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // We're running in an animation frame, not in a digest cycle.
 | 
			
		||||
                // We need to trigger a digest cycle if our displayable data
 | 
			
		||||
                // changes.
 | 
			
		||||
@@ -130,27 +167,27 @@ define(
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the CSS class to display the right icon
 | 
			
		||||
         * for the start/restart button.
 | 
			
		||||
         * @returns {string} cssClass to display
 | 
			
		||||
         * for the start/pause button.
 | 
			
		||||
         * @returns {string} cssclass to display
 | 
			
		||||
         */
 | 
			
		||||
        TimerController.prototype.buttonCssClass = function () {
 | 
			
		||||
            return this.relevantAction ?
 | 
			
		||||
                    this.relevantAction.getMetadata().cssClass : "";
 | 
			
		||||
                this.relevantAction.getMetadata().cssClass : "";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the text to show for the start/restart button
 | 
			
		||||
         * Get the text to show for the start/pause button
 | 
			
		||||
         * (e.g. in a tooltip)
 | 
			
		||||
         * @returns {string} name of the action
 | 
			
		||||
         */
 | 
			
		||||
        TimerController.prototype.buttonText = function () {
 | 
			
		||||
            return this.relevantAction ?
 | 
			
		||||
                    this.relevantAction.getMetadata().name : "";
 | 
			
		||||
                this.relevantAction.getMetadata().name : "";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Perform the action associated with the start/restart button.
 | 
			
		||||
         * Perform the action associated with the start/pause button.
 | 
			
		||||
         */
 | 
			
		||||
        TimerController.prototype.clickButton = function () {
 | 
			
		||||
            if (this.relevantAction) {
 | 
			
		||||
@@ -159,6 +196,16 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Perform the action associated with the stop button.
 | 
			
		||||
         */
 | 
			
		||||
        TimerController.prototype.clickStopButton = function () {
 | 
			
		||||
            if (this.stopAction) {
 | 
			
		||||
                this.stopAction.perform();
 | 
			
		||||
                this.updateObject(this.$scope.domainObject);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the sign (+ or -) of the current timer value, as
 | 
			
		||||
         * displayable text.
 | 
			
		||||
@@ -168,15 +215,6 @@ define(
 | 
			
		||||
            return this.signValue;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the sign (+ or -) of the current timer value, as
 | 
			
		||||
         * a CSS class.
 | 
			
		||||
         * @returns {string} sign of the current timer value
 | 
			
		||||
         */
 | 
			
		||||
        TimerController.prototype.signClass = function () {
 | 
			
		||||
            return this.signCssClass;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the text to display for the current timer value.
 | 
			
		||||
         * @returns {string} current timer value
 | 
			
		||||
 
 | 
			
		||||
@@ -21,28 +21,41 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/actions/AbstractStartTimerAction"],
 | 
			
		||||
    function (AbstractStartTimerAction) {
 | 
			
		||||
    ["../../src/actions/PauseTimerAction"],
 | 
			
		||||
    function (PauseTimerAction) {
 | 
			
		||||
 | 
			
		||||
        describe("A timer's start/restart action", function () {
 | 
			
		||||
        describe("A timer's Pause action", function () {
 | 
			
		||||
            var mockNow,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                testModel,
 | 
			
		||||
                testContext,
 | 
			
		||||
                action;
 | 
			
		||||
 | 
			
		||||
            function asPromise(value) {
 | 
			
		||||
                return (value || {}).then ? value : {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return asPromise(callback(value));
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                        then: function (callback) {
 | 
			
		||||
                            return asPromise(callback(value));
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function testState(type, timerState, timestamp, expected) {
 | 
			
		||||
                testModel.type = type;
 | 
			
		||||
                testModel.timerState = timerState;
 | 
			
		||||
                testModel.timestamp = timestamp;
 | 
			
		||||
 | 
			
		||||
                if (expected) {
 | 
			
		||||
                    expect(PauseTimerAction.appliesTo(testContext)).toBeTruthy();
 | 
			
		||||
                } else {
 | 
			
		||||
                    expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockNow = jasmine.createSpy('now');
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    'domainObject',
 | 
			
		||||
                    ['getCapability', 'useCapability']
 | 
			
		||||
                    ['getCapability', 'useCapability', 'getModel']
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockDomainObject.useCapability.andCallFake(function (c, v) {
 | 
			
		||||
@@ -51,24 +64,41 @@ define(
 | 
			
		||||
                        return asPromise(true);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                mockDomainObject.getModel.andCallFake(function () {
 | 
			
		||||
                    return testModel;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                testModel = {};
 | 
			
		||||
                testContext = {domainObject: mockDomainObject};
 | 
			
		||||
 | 
			
		||||
                action = new AbstractStartTimerAction(mockNow, {
 | 
			
		||||
                    domainObject: mockDomainObject
 | 
			
		||||
                });
 | 
			
		||||
                action = new PauseTimerAction(mockNow, testContext);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates the model with a timestamp", function () {
 | 
			
		||||
            it("updates the model with a timerState", function () {
 | 
			
		||||
                testModel.timerState = 'started';
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timerState).toEqual('paused');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates the model with a pausedTime", function () {
 | 
			
		||||
                testModel.pausedTime = undefined;
 | 
			
		||||
                mockNow.andReturn(12000);
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timestamp).toEqual(12000);
 | 
			
		||||
                expect(testModel.pausedTime).toEqual(12000);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not truncate milliseconds", function () {
 | 
			
		||||
                mockNow.andReturn(42321);
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timestamp).toEqual(42321);
 | 
			
		||||
            it("applies only to timers in a playing state", function () {
 | 
			
		||||
                //in a stopped state
 | 
			
		||||
                testState('timer', 'stopped', undefined, false);
 | 
			
		||||
 | 
			
		||||
                //in a paused state
 | 
			
		||||
                testState('timer', 'paused', 12000, false);
 | 
			
		||||
 | 
			
		||||
                //in a playing state
 | 
			
		||||
                testState('timer', 'started', 12000, true);
 | 
			
		||||
 | 
			
		||||
                //not a timer
 | 
			
		||||
                testState('clock', 'started', 12000, false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -39,6 +39,18 @@ define(
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function testState(type, timerState, timestamp, expected) {
 | 
			
		||||
                testModel.type = type;
 | 
			
		||||
                testModel.timerState = timerState;
 | 
			
		||||
                testModel.timestamp = timestamp;
 | 
			
		||||
 | 
			
		||||
                if (expected) {
 | 
			
		||||
                    expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy();
 | 
			
		||||
                } else {
 | 
			
		||||
                    expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockNow = jasmine.createSpy('now');
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
@@ -63,23 +75,36 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates the model with a timestamp", function () {
 | 
			
		||||
                testModel.pausedTime = 12000;
 | 
			
		||||
                mockNow.andReturn(12000);
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timestamp).toEqual(12000);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("applies only to timers with a target time", function () {
 | 
			
		||||
                testModel.type = 'timer';
 | 
			
		||||
                testModel.timestamp = 12000;
 | 
			
		||||
                expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy();
 | 
			
		||||
            it("updates the model with a pausedTime", function () {
 | 
			
		||||
                testModel.pausedTime = 12000;
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.pausedTime).toEqual(undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
                testModel.type = 'timer';
 | 
			
		||||
                testModel.timestamp = undefined;
 | 
			
		||||
                expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
            it("updates the model with a timerState", function () {
 | 
			
		||||
                testModel.timerState = 'stopped';
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timerState).toEqual('started');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
                testModel.type = 'clock';
 | 
			
		||||
                testModel.timestamp = 12000;
 | 
			
		||||
                expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
            it("applies only to timers in a non-stopped state", function () {
 | 
			
		||||
                //in a stopped state
 | 
			
		||||
                testState('timer', 'stopped', undefined, false);
 | 
			
		||||
 | 
			
		||||
                //in a paused state
 | 
			
		||||
                testState('timer', 'paused', 12000, true);
 | 
			
		||||
 | 
			
		||||
                //in a playing state
 | 
			
		||||
                testState('timer', 'started', 12000, true);
 | 
			
		||||
 | 
			
		||||
                //not a timer
 | 
			
		||||
                testState('clock', 'paused', 12000, false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,10 +33,22 @@ define(
 | 
			
		||||
 | 
			
		||||
            function asPromise(value) {
 | 
			
		||||
                return (value || {}).then ? value : {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return asPromise(callback(value));
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                        then: function (callback) {
 | 
			
		||||
                            return asPromise(callback(value));
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function testState(type, timerState, timestamp, expected) {
 | 
			
		||||
                testModel.type = type;
 | 
			
		||||
                testModel.timerState = timerState;
 | 
			
		||||
                testModel.timestamp = timestamp;
 | 
			
		||||
 | 
			
		||||
                if (expected) {
 | 
			
		||||
                    expect(StartTimerAction.appliesTo(testContext)).toBeTruthy();
 | 
			
		||||
                } else {
 | 
			
		||||
                    expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
@@ -57,7 +69,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                testModel = {};
 | 
			
		||||
                testContext = { domainObject: mockDomainObject };
 | 
			
		||||
                testContext = {domainObject: mockDomainObject};
 | 
			
		||||
 | 
			
		||||
                action = new StartTimerAction(mockNow, testContext);
 | 
			
		||||
            });
 | 
			
		||||
@@ -68,18 +80,30 @@ define(
 | 
			
		||||
                expect(testModel.timestamp).toEqual(12000);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("applies only to timers without a target time", function () {
 | 
			
		||||
                testModel.type = 'timer';
 | 
			
		||||
                testModel.timestamp = 12000;
 | 
			
		||||
                expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
            it("updates the model with a pausedTime", function () {
 | 
			
		||||
                testModel.pausedTime = 12000;
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.pausedTime).toEqual(undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
                testModel.type = 'timer';
 | 
			
		||||
                testModel.timestamp = undefined;
 | 
			
		||||
                expect(StartTimerAction.appliesTo(testContext)).toBeTruthy();
 | 
			
		||||
            it("updates the model with a timerState", function () {
 | 
			
		||||
                testModel.timerState = undefined;
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timerState).toEqual('started');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
                testModel.type = 'clock';
 | 
			
		||||
                testModel.timestamp = 12000;
 | 
			
		||||
                expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
            it("applies only to timers not in a playing state", function () {
 | 
			
		||||
                //in a stopped state
 | 
			
		||||
                testState('timer', 'stopped', undefined, true);
 | 
			
		||||
 | 
			
		||||
                //in a paused state
 | 
			
		||||
                testState('timer', 'paused', 12000, true);
 | 
			
		||||
 | 
			
		||||
                //in a playing state
 | 
			
		||||
                testState('timer', 'started', 12000, false);
 | 
			
		||||
 | 
			
		||||
                //not a timer
 | 
			
		||||
                testState('clock', 'paused', 12000, false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										110
									
								
								platform/features/clock/test/actions/StopTimerActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								platform/features/clock/test/actions/StopTimerActionSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/actions/StopTimerAction"],
 | 
			
		||||
    function (StopTimerAction) {
 | 
			
		||||
 | 
			
		||||
        describe("A timer's stop action", function () {
 | 
			
		||||
            var mockNow,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                testModel,
 | 
			
		||||
                testContext,
 | 
			
		||||
                action;
 | 
			
		||||
 | 
			
		||||
            function asPromise(value) {
 | 
			
		||||
                return (value || {}).then ? value : {
 | 
			
		||||
                        then: function (callback) {
 | 
			
		||||
                            return asPromise(callback(value));
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function testState(type, timerState, timestamp, expected) {
 | 
			
		||||
                testModel.type = type;
 | 
			
		||||
                testModel.timerState = timerState;
 | 
			
		||||
                testModel.timestamp = timestamp;
 | 
			
		||||
 | 
			
		||||
                if (expected) {
 | 
			
		||||
                    expect(StopTimerAction.appliesTo(testContext)).toBeTruthy();
 | 
			
		||||
                } else {
 | 
			
		||||
                    expect(StopTimerAction.appliesTo(testContext)).toBeFalsy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockNow = jasmine.createSpy('now');
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    'domainObject',
 | 
			
		||||
                    ['getCapability', 'useCapability', 'getModel']
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockDomainObject.useCapability.andCallFake(function (c, v) {
 | 
			
		||||
                    if (c === 'mutation') {
 | 
			
		||||
                        testModel = v(testModel) || testModel;
 | 
			
		||||
                        return asPromise(true);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                mockDomainObject.getModel.andCallFake(function () {
 | 
			
		||||
                    return testModel;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                testModel = {};
 | 
			
		||||
                testContext = {domainObject: mockDomainObject};
 | 
			
		||||
 | 
			
		||||
                action = new StopTimerAction(mockNow, testContext);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates the model with a timestamp", function () {
 | 
			
		||||
                mockNow.andReturn(12000);
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timestamp).toEqual(undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates the model with a pausedTime", function () {
 | 
			
		||||
                testModel.pausedTime = 12000;
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.pausedTime).toEqual(undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates the model with a timerState", function () {
 | 
			
		||||
                testModel.timerState = 'started';
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(testModel.timerState).toEqual('stopped');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("applies only to timers in a non-stopped state", function () {
 | 
			
		||||
                //in a stopped state
 | 
			
		||||
                testState('timer', 'stopped', undefined, false);
 | 
			
		||||
 | 
			
		||||
                //in a paused state
 | 
			
		||||
                testState('timer', 'paused', 12000, true);
 | 
			
		||||
 | 
			
		||||
                //in a playing state
 | 
			
		||||
                testState('timer', 'started', 12000, true);
 | 
			
		||||
 | 
			
		||||
                //not a timer
 | 
			
		||||
                testState('clock', 'paused', 12000, false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
@@ -43,9 +43,9 @@ define(
 | 
			
		||||
                controller = new ClockController(mockScope, mockTicker);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches for clock format from the domain object model", function () {
 | 
			
		||||
            it("watches for model (clockFormat and timezone) from the domain object model", function () {
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "model.clockFormat",
 | 
			
		||||
                    "model",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
@@ -67,29 +67,35 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("formats using the format string from the model", function () {
 | 
			
		||||
                mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP);
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1]([
 | 
			
		||||
                    "YYYY-DDD hh:mm:ss",
 | 
			
		||||
                    "clock24"
 | 
			
		||||
                ]);
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1]({
 | 
			
		||||
                    "clockFormat": [
 | 
			
		||||
                        "YYYY-DDD hh:mm:ss",
 | 
			
		||||
                        "clock24"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "timezone": "Canada/Eastern"
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.zone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.text()).toEqual("2015-154 17:56:14");
 | 
			
		||||
                expect(controller.zone()).toEqual("EDT");
 | 
			
		||||
                expect(controller.text()).toEqual("2015-154 13:56:14");
 | 
			
		||||
                expect(controller.ampm()).toEqual("");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("formats 12-hour time", function () {
 | 
			
		||||
                mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP);
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1]([
 | 
			
		||||
                    "YYYY-DDD hh:mm:ss",
 | 
			
		||||
                    "clock12"
 | 
			
		||||
                ]);
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1]({
 | 
			
		||||
                    "clockFormat": [
 | 
			
		||||
                        "YYYY-DDD hh:mm:ss",
 | 
			
		||||
                        "clock12"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "timezone": ""
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.zone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.text()).toEqual("2015-154 05:56:14");
 | 
			
		||||
                expect(controller.ampm()).toEqual("PM");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not throw exceptions when clockFormat is undefined", function () {
 | 
			
		||||
            it("does not throw exceptions when model is undefined", function () {
 | 
			
		||||
                mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP);
 | 
			
		||||
                expect(function () {
 | 
			
		||||
                    mockScope.$watch.mostRecentCall.args[1](undefined);
 | 
			
		||||
 
 | 
			
		||||
@@ -34,13 +34,14 @@ define(
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockActionCapability,
 | 
			
		||||
                mockStart,
 | 
			
		||||
                mockRestart,
 | 
			
		||||
                mockPause,
 | 
			
		||||
                mockStop,
 | 
			
		||||
                testModel,
 | 
			
		||||
                controller;
 | 
			
		||||
 | 
			
		||||
            function invokeWatch(expr, value) {
 | 
			
		||||
                mockScope.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] ===  expr) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](value);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
@@ -67,8 +68,12 @@ define(
 | 
			
		||||
                    'start',
 | 
			
		||||
                    ['getMetadata', 'perform']
 | 
			
		||||
                );
 | 
			
		||||
                mockRestart = jasmine.createSpyObj(
 | 
			
		||||
                    'restart',
 | 
			
		||||
                mockPause = jasmine.createSpyObj(
 | 
			
		||||
                    'paused',
 | 
			
		||||
                    ['getMetadata', 'perform']
 | 
			
		||||
                );
 | 
			
		||||
                mockStop = jasmine.createSpyObj(
 | 
			
		||||
                    'stopped',
 | 
			
		||||
                    ['getMetadata', 'perform']
 | 
			
		||||
                );
 | 
			
		||||
                mockNow = jasmine.createSpy('now');
 | 
			
		||||
@@ -82,11 +87,14 @@ define(
 | 
			
		||||
                mockActionCapability.getActions.andCallFake(function (k) {
 | 
			
		||||
                    return [{
 | 
			
		||||
                        'timer.start': mockStart,
 | 
			
		||||
                        'timer.restart': mockRestart
 | 
			
		||||
                        'timer.pause': mockPause,
 | 
			
		||||
                        'timer.stop': mockStop
 | 
			
		||||
                    }[k]];
 | 
			
		||||
                });
 | 
			
		||||
                mockStart.getMetadata.andReturn({ cssClass: "icon-play", name: "Start" });
 | 
			
		||||
                mockRestart.getMetadata.andReturn({ cssClass: "icon-refresh", name: "Restart" });
 | 
			
		||||
 | 
			
		||||
                mockStart.getMetadata.andReturn({cssClass: "icon-play", name: "Start"});
 | 
			
		||||
                mockPause.getMetadata.andReturn({cssClass: "icon-pause", name: "Pause"});
 | 
			
		||||
                mockStop.getMetadata.andReturn({cssClass: "icon-box", name: "Stop"});
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
                testModel = {};
 | 
			
		||||
@@ -144,28 +152,37 @@ define(
 | 
			
		||||
                expect(controller.text()).toEqual("0D 00:00:00");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("shows cssClass & name for the applicable start/restart action", function () {
 | 
			
		||||
            it("shows cssClass & name for the applicable start/pause action", function () {
 | 
			
		||||
                invokeWatch('domainObject', mockDomainObject);
 | 
			
		||||
                expect(controller.buttonCssClass()).toEqual("icon-play");
 | 
			
		||||
                expect(controller.buttonText()).toEqual("Start");
 | 
			
		||||
 | 
			
		||||
                testModel.timestamp = 12321;
 | 
			
		||||
                testModel.timerState = 'started';
 | 
			
		||||
                invokeWatch('model.modified', 1);
 | 
			
		||||
                expect(controller.buttonCssClass()).toEqual("icon-refresh");
 | 
			
		||||
                expect(controller.buttonText()).toEqual("Restart");
 | 
			
		||||
                expect(controller.buttonCssClass()).toEqual("icon-pause");
 | 
			
		||||
                expect(controller.buttonText()).toEqual("Pause");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("performs correct start/restart action on click", function () {
 | 
			
		||||
            it("performs correct start/pause/stop action on click", function () {
 | 
			
		||||
                //test start
 | 
			
		||||
                invokeWatch('domainObject', mockDomainObject);
 | 
			
		||||
                expect(mockStart.perform).not.toHaveBeenCalled();
 | 
			
		||||
                controller.clickButton();
 | 
			
		||||
                expect(mockStart.perform).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                //test pause
 | 
			
		||||
                testModel.timestamp = 12321;
 | 
			
		||||
                testModel.timerState = 'started';
 | 
			
		||||
                invokeWatch('model.modified', 1);
 | 
			
		||||
                expect(mockRestart.perform).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockPause.perform).not.toHaveBeenCalled();
 | 
			
		||||
                controller.clickButton();
 | 
			
		||||
                expect(mockRestart.perform).toHaveBeenCalled();
 | 
			
		||||
                expect(mockPause.perform).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                //test stop
 | 
			
		||||
                expect(mockStop.perform).not.toHaveBeenCalled();
 | 
			
		||||
                controller.clickStopButton();
 | 
			
		||||
                expect(mockStop.perform).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("stops requesting animation frames when destroyed", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
    <div class="pane left menu-items">
 | 
			
		||||
        <ul>
 | 
			
		||||
            <li ng-repeat="metadata in ngModel.options"
 | 
			
		||||
                ng-click="ngModel.selected = metadata">
 | 
			
		||||
                ng-click="ngModel.select(metadata)">
 | 
			
		||||
                <a ng-mouseover="ngModel.activeMetadata = metadata"
 | 
			
		||||
                   ng-mouseleave="ngModel.activeMetadata = undefined"
 | 
			
		||||
                   class="menu-item-a {{metadata.cssClass}}">
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,8 @@ define(
 | 
			
		||||
            var options = this.optionsFromConfig(config);
 | 
			
		||||
            this.menu = {
 | 
			
		||||
                selected: undefined,
 | 
			
		||||
                options: options
 | 
			
		||||
                options: options,
 | 
			
		||||
                select: this.selectMenuOption
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            //Set the initial state of the UI from the conductor state
 | 
			
		||||
@@ -132,8 +133,6 @@ define(
 | 
			
		||||
                this.setViewFromBounds(bounds);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$scope.$watch("tcController.menu.selected", this.selectMenuOption);
 | 
			
		||||
 | 
			
		||||
            this.conductorViewService.on('pan', this.onPan);
 | 
			
		||||
            this.conductorViewService.on('pan-stop', this.onPanStop);
 | 
			
		||||
 | 
			
		||||
@@ -164,26 +163,28 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @private
 | 
			
		||||
         * @param newOption
 | 
			
		||||
         * @param oldOption
 | 
			
		||||
         */
 | 
			
		||||
        TimeConductorController.prototype.selectMenuOption = function (newOption, oldOption) {
 | 
			
		||||
            if (newOption !== oldOption) {
 | 
			
		||||
                var config = this.getConfig(this.timeAPI.timeSystem(), newOption.clock);
 | 
			
		||||
                if (!config) {
 | 
			
		||||
                    // Clock does not support this timeSystem, fallback to first
 | 
			
		||||
                    // option provided for clock.
 | 
			
		||||
                    config = this.config.menuOptions.filter(function (menuOption) {
 | 
			
		||||
                        return menuOption.clock === (newOption.clock && newOption.clock.key);
 | 
			
		||||
                    })[0];
 | 
			
		||||
                }
 | 
			
		||||
        TimeConductorController.prototype.selectMenuOption = function (newOption) {
 | 
			
		||||
            if (this.menu.selected.key === newOption.key) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            this.menu.selected = newOption;
 | 
			
		||||
 | 
			
		||||
                if (config.clock) {
 | 
			
		||||
                    this.timeAPI.clock(config.clock, config.clockOffsets);
 | 
			
		||||
                    this.timeAPI.timeSystem(config.timeSystem);
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.timeAPI.stopClock();
 | 
			
		||||
                    this.timeAPI.timeSystem(config.timeSystem, config.bounds);
 | 
			
		||||
                }
 | 
			
		||||
            var config = this.getConfig(this.timeAPI.timeSystem(), newOption.clock);
 | 
			
		||||
            if (!config) {
 | 
			
		||||
                // Clock does not support this timeSystem, fallback to first
 | 
			
		||||
                // option provided for clock.
 | 
			
		||||
                config = this.config.menuOptions.filter(function (menuOption) {
 | 
			
		||||
                    return menuOption.clock === (newOption.clock && newOption.clock.key);
 | 
			
		||||
                })[0];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (config.clock) {
 | 
			
		||||
                this.timeAPI.clock(config.clock, config.clockOffsets);
 | 
			
		||||
                this.timeAPI.timeSystem(config.timeSystem);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.timeAPI.stopClock();
 | 
			
		||||
                this.timeAPI.timeSystem(config.timeSystem, config.bounds);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,90 @@ define([
 | 
			
		||||
                                            "cssClass": "l-input-lg",
 | 
			
		||||
                                            "required": true
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "items": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editX",
 | 
			
		||||
                                        "text": "X",
 | 
			
		||||
                                        "name": "X",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control": "numberfield",
 | 
			
		||||
                                        "min": "0"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editY",
 | 
			
		||||
                                        "text": "Y",
 | 
			
		||||
                                        "name": "Y",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control": "numberfield",
 | 
			
		||||
                                        "min": "0"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editX1",
 | 
			
		||||
                                        "text": "X1",
 | 
			
		||||
                                        "name": "X1",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control" : "numberfield",
 | 
			
		||||
                                        "min": "0"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editY1",
 | 
			
		||||
                                        "text": "Y1",
 | 
			
		||||
                                        "name": "Y1",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control" : "numberfield",
 | 
			
		||||
                                        "min": "0"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editX2",
 | 
			
		||||
                                        "text": "X2",
 | 
			
		||||
                                        "name": "X2",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control" : "numberfield",
 | 
			
		||||
                                        "min": "0"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editY2",
 | 
			
		||||
                                        "text": "Y2",
 | 
			
		||||
                                        "name": "Y2",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control" : "numberfield",
 | 
			
		||||
                                        "min": "0"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editHeight",
 | 
			
		||||
                                        "text": "H",
 | 
			
		||||
                                        "name": "H",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control": "numberfield",
 | 
			
		||||
                                        "description": "Resize object height",
 | 
			
		||||
                                        "min": "1"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "editWidth",
 | 
			
		||||
                                        "text": "W",
 | 
			
		||||
                                        "name": "W",
 | 
			
		||||
                                        "cssClass": "l-input-sm",
 | 
			
		||||
                                        "control": "numberfield",
 | 
			
		||||
                                        "description": "Resize object width",
 | 
			
		||||
                                        "min": "1"
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "items": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "useGrid",
 | 
			
		||||
                                        "name": "Snap to Grid",
 | 
			
		||||
                                        "control": "checkbox"
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "items": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "property": "text",
 | 
			
		||||
                                        "cssClass": "icon-gear",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								platform/features/hyperlink/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								platform/features/hyperlink/bundle.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './src/HyperlinkController',
 | 
			
		||||
    'legacyRegistry',
 | 
			
		||||
    'text!./res/templates/hyperlink.html'
 | 
			
		||||
], function (
 | 
			
		||||
    HyperlinkController,
 | 
			
		||||
    legacyRegistry,
 | 
			
		||||
    hyperlinkTemplate
 | 
			
		||||
) {
 | 
			
		||||
    legacyRegistry.register("platform/features/hyperlink", {
 | 
			
		||||
        "name": "Hyperlink",
 | 
			
		||||
        "description": "Insert a hyperlink to reference a link",
 | 
			
		||||
        "extensions": {
 | 
			
		||||
        "types": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "hyperlink",
 | 
			
		||||
                "name": "Hyperlink",
 | 
			
		||||
                "cssClass": "icon-chain-links",
 | 
			
		||||
                "description": "A hyperlink to redirect to a different link",
 | 
			
		||||
                "features": ["creation"],
 | 
			
		||||
                "properties": [
 | 
			
		||||
                  {
 | 
			
		||||
                      "key": "url",
 | 
			
		||||
                      "name": "URL",
 | 
			
		||||
                      "control": "textfield",
 | 
			
		||||
                      "pattern": "^(ftp|https?)\\:\\/\\/",
 | 
			
		||||
                      "required": true,
 | 
			
		||||
                      "cssClass": "l-input-lg"
 | 
			
		||||
                  },
 | 
			
		||||
 | 
			
		||||
                  {
 | 
			
		||||
                      "key": "displayText",
 | 
			
		||||
                      "name": "Text to Display",
 | 
			
		||||
                      "control": "textfield",
 | 
			
		||||
                      "required": true,
 | 
			
		||||
                      "cssClass": "l-input-lg"
 | 
			
		||||
                  },
 | 
			
		||||
                {
 | 
			
		||||
                      "key": "displayFormat",
 | 
			
		||||
                      "name": "Display Format",
 | 
			
		||||
                      "control": "select",
 | 
			
		||||
                      "options": [
 | 
			
		||||
                              {
 | 
			
		||||
                                  "name": "Link",
 | 
			
		||||
                                  "value": "link"
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                  "value": "button",
 | 
			
		||||
                                  "name": "Button"
 | 
			
		||||
                              }
 | 
			
		||||
                          ],
 | 
			
		||||
                      "cssClass": "l-inline"
 | 
			
		||||
                  },
 | 
			
		||||
                  {
 | 
			
		||||
                      "key": "openNewTab",
 | 
			
		||||
                      "name": "Tab to Open Hyperlink",
 | 
			
		||||
                      "control": "select",
 | 
			
		||||
                      "options": [
 | 
			
		||||
                              {
 | 
			
		||||
                                  "name": "Open in this tab",
 | 
			
		||||
                                  "value": "thisTab"
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                  "value": "newTab",
 | 
			
		||||
                                  "name": "Open in a new tab"
 | 
			
		||||
                              }
 | 
			
		||||
                          ],
 | 
			
		||||
                      "cssClass": "l-inline"
 | 
			
		||||
 | 
			
		||||
                  }
 | 
			
		||||
                ],
 | 
			
		||||
                "model": {
 | 
			
		||||
                    "displayFormat": "link",
 | 
			
		||||
                    "openNewTab": "thisTab",
 | 
			
		||||
                    "removeTitle": true
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
        "views": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "hyperlink",
 | 
			
		||||
                "type": "hyperlink",
 | 
			
		||||
                "name": "Hyperlink Display",
 | 
			
		||||
                "template": hyperlinkTemplate,
 | 
			
		||||
                "editable": false
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "HyperlinkController",
 | 
			
		||||
                "implementation": HyperlinkController,
 | 
			
		||||
                "depends": ["$scope"]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										28
									
								
								platform/features/hyperlink/res/templates/hyperlink.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								platform/features/hyperlink/res/templates/hyperlink.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
 Open MCT 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 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.
 | 
			
		||||
-->
 | 
			
		||||
<a class="s-hyperlink" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
 | 
			
		||||
   ng-attr-target="{{hyperlink.openNewTab() ? '_blank' : undefined}}"
 | 
			
		||||
   ng-class="{
 | 
			
		||||
       's-button': hyperlink.isButton()
 | 
			
		||||
   }">
 | 
			
		||||
    <div class="label">{{domainObject.getModel().displayText}}</div>
 | 
			
		||||
</a>
 | 
			
		||||
							
								
								
									
										58
									
								
								platform/features/hyperlink/src/HyperlinkController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								platform/features/hyperlink/src/HyperlinkController.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This bundle adds the Hyperlink object type, which can be used to add hyperlinks as a domain Object type
 | 
			
		||||
 and into display Layouts as either a button or link that can be chosen to open in either the same tab or
 | 
			
		||||
 create a new tab to open the link in
 | 
			
		||||
 * @namespace platform/features/hyperlink
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        function HyperlinkController($scope) {
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**Function to analyze the location in which to open the hyperlink
 | 
			
		||||
        @returns true if the hyperlink is chosen to open in a different tab, false if the same tab
 | 
			
		||||
        **/
 | 
			
		||||
        HyperlinkController.prototype.openNewTab = function () {
 | 
			
		||||
            if (this.$scope.domainObject.getModel().openNewTab === "thisTab") {
 | 
			
		||||
                return false;
 | 
			
		||||
            } else {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        /**Function to specify the format in which the hyperlink should be created
 | 
			
		||||
        @returns true if the hyperlink is chosen to be created as a button, false if a link
 | 
			
		||||
        **/
 | 
			
		||||
        HyperlinkController.prototype.isButton = function () {
 | 
			
		||||
            if (this.$scope.domainObject.getModel().displayFormat === "link") {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
        return HyperlinkController;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  );
 | 
			
		||||
							
								
								
									
										89
									
								
								platform/features/hyperlink/test/HyperlinkControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								platform/features/hyperlink/test/HyperlinkControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/HyperlinkController"],
 | 
			
		||||
    function (HyperlinkController) {
 | 
			
		||||
 | 
			
		||||
        describe("The controller for hyperlinks", function () {
 | 
			
		||||
            var domainObject,
 | 
			
		||||
                controller,
 | 
			
		||||
                scope;
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                scope = jasmine.createSpyObj(
 | 
			
		||||
                  "$scope",
 | 
			
		||||
                  ["domainObject"]
 | 
			
		||||
              );
 | 
			
		||||
                domainObject = jasmine.createSpyObj(
 | 
			
		||||
                        "domainObject",
 | 
			
		||||
                        ["getModel"]
 | 
			
		||||
                  );
 | 
			
		||||
                scope.domainObject = domainObject;
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it should open a new tab", function () {
 | 
			
		||||
                scope.domainObject.getModel.andReturn({
 | 
			
		||||
                  "displayFormat": "link",
 | 
			
		||||
                  "openNewTab": "newTab",
 | 
			
		||||
                  "showTitle": false
 | 
			
		||||
              }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.openNewTab())
 | 
			
		||||
                    .toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it is a button", function () {
 | 
			
		||||
                scope.domainObject.getModel.andReturn({
 | 
			
		||||
                  "displayFormat": "button",
 | 
			
		||||
                  "openNewTab": "thisTab",
 | 
			
		||||
                  "showTitle": false
 | 
			
		||||
              }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.isButton())
 | 
			
		||||
                    .toEqual(true);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it should open in the same tab", function () {
 | 
			
		||||
                scope.domainObject.getModel.andReturn({
 | 
			
		||||
                  "displayFormat": "link",
 | 
			
		||||
                  "openNewTab": "thisTab",
 | 
			
		||||
                  "showTitle": false
 | 
			
		||||
              }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.openNewTab())
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it is a link", function () {
 | 
			
		||||
                scope.domainObject.getModel.andReturn({
 | 
			
		||||
                  "displayFormat": "link",
 | 
			
		||||
                  "openNewTab": "thisTab",
 | 
			
		||||
                  "showTitle": false
 | 
			
		||||
              }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.openNewTab())
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -25,12 +25,14 @@ define([
 | 
			
		||||
    "./src/controllers/ImageryController",
 | 
			
		||||
    "./src/directives/MCTBackgroundImage",
 | 
			
		||||
    "text!./res/templates/imagery.html",
 | 
			
		||||
    "text!./res/templates/imageryTimeline.html",
 | 
			
		||||
    'legacyRegistry'
 | 
			
		||||
], function (
 | 
			
		||||
    ImageryViewPolicy,
 | 
			
		||||
    ImageryController,
 | 
			
		||||
    MCTBackgroundImage,
 | 
			
		||||
    imageryTemplate,
 | 
			
		||||
    imageryTimelineTemplate,
 | 
			
		||||
    legacyRegistry
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
@@ -48,12 +50,26 @@ define([
 | 
			
		||||
                        "telemetry"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "editable": false
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Historical Imagery",
 | 
			
		||||
                    "key": "historical-imagery",
 | 
			
		||||
                    "cssClass": "icon-image",
 | 
			
		||||
                    "template": imageryTimelineTemplate,
 | 
			
		||||
                    "priority": "preferred",
 | 
			
		||||
                    "needs": [
 | 
			
		||||
                        "telemetry"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "editable": false
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "policies": [
 | 
			
		||||
                {
 | 
			
		||||
                    "category": "view",
 | 
			
		||||
                    "implementation": ImageryViewPolicy
 | 
			
		||||
                    "implementation": ImageryViewPolicy,
 | 
			
		||||
                    "depends": [
 | 
			
		||||
                        "openmct"
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "controllers": [
 | 
			
		||||
@@ -62,7 +78,9 @@ define([
 | 
			
		||||
                    "implementation": ImageryController,
 | 
			
		||||
                    "depends": [
 | 
			
		||||
                        "$scope",
 | 
			
		||||
                        "telemetryHandler"
 | 
			
		||||
                        "$window",
 | 
			
		||||
                        "$element",
 | 
			
		||||
                        "openmct"
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,8 @@
 | 
			
		||||
        <div class="l-image-main-controlbar flex-elem l-flex-row">
 | 
			
		||||
            <div class="left flex-elem grows">
 | 
			
		||||
                <a class="s-button show-thumbs sm hidden icon-thumbs-strip"
 | 
			
		||||
                    ng-click="showThumbsBubble = (showThumbsBubble)? false:true"></a>
 | 
			
		||||
                <span class="l-timezone">{{imagery.getZone()}}</span>
 | 
			
		||||
                    ng-click="showThumbsBubble = (showThumbsBubble) ? false:true"></a>
 | 
			
		||||
                <span class="l-time">{{imagery.getTime()}}</span>
 | 
			
		||||
                <span class="l-date">{{imagery.getDate()}}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="right flex-elem">
 | 
			
		||||
                <a class="s-button pause-play"
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
<div class="l-image-thumbs-wrapper" ng-controller="ImageryController as imagery">
 | 
			
		||||
	<div class="l-image-thumb-item" ng-repeat="image in imageHistory track by $index">
 | 
			
		||||
		<img class="l-thumb" ng-init="imagery.scrollToRight()"
 | 
			
		||||
			ng-src={{imagery.getImageUrl(image)}} >
 | 
			
		||||
			<div class="l-time">{{imagery.getTime(image)}}</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -24,12 +24,13 @@
 | 
			
		||||
 * This bundle implements views of image telemetry.
 | 
			
		||||
 * @namespace platform/features/imagery
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ['moment'],
 | 
			
		||||
    function (moment) {
 | 
			
		||||
 | 
			
		||||
        var DATE_FORMAT = "YYYY-MM-DD",
 | 
			
		||||
            TIME_FORMAT = "HH:mm:ss.SSS";
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        'zepto',
 | 
			
		||||
        'lodash'
 | 
			
		||||
    ],
 | 
			
		||||
    function ($, _) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Controller for the "Imagery" view of a domain object which
 | 
			
		||||
@@ -37,99 +38,202 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/features/imagery
 | 
			
		||||
         */
 | 
			
		||||
        function ImageryController($scope, telemetryHandler) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            function releaseSubscription() {
 | 
			
		||||
                if (self.handle) {
 | 
			
		||||
                    self.handle.unsubscribe();
 | 
			
		||||
                    self.handle = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        function ImageryController($scope, $window, element, openmct) {
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
            this.$window = $window;
 | 
			
		||||
            this.openmct = openmct;
 | 
			
		||||
            this.date = "";
 | 
			
		||||
            this.time = "";
 | 
			
		||||
            this.zone = "";
 | 
			
		||||
            this.imageUrl = "";
 | 
			
		||||
            this.requestCount = 0;
 | 
			
		||||
            this.scrollable = $(element[0]);
 | 
			
		||||
            this.autoScroll = openmct.time.clock() ? true : false;
 | 
			
		||||
 | 
			
		||||
            function updateValuesCallback() {
 | 
			
		||||
                return self.updateValues();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Create a new subscription; telemetrySubscriber gets
 | 
			
		||||
            // to do the meaningful work here.
 | 
			
		||||
            function subscribe(domainObject) {
 | 
			
		||||
                releaseSubscription();
 | 
			
		||||
                self.date = "";
 | 
			
		||||
                self.time = "";
 | 
			
		||||
                self.zone = "";
 | 
			
		||||
                self.imageUrl = "";
 | 
			
		||||
                self.handle = domainObject && telemetryHandler.handle(
 | 
			
		||||
                    domainObject,
 | 
			
		||||
                    updateValuesCallback,
 | 
			
		||||
                    true // Lossless
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $scope.filters = {
 | 
			
		||||
            this.$scope.imageHistory = [];
 | 
			
		||||
            this.$scope.filters = {
 | 
			
		||||
                brightness: 100,
 | 
			
		||||
                contrast: 100
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Subscribe to telemetry when a domain object becomes available
 | 
			
		||||
            $scope.$watch('domainObject', subscribe);
 | 
			
		||||
            this.subscribe = this.subscribe.bind(this);
 | 
			
		||||
            this.stopListening = this.stopListening.bind(this);
 | 
			
		||||
            this.updateValues = this.updateValues.bind(this);
 | 
			
		||||
            this.updateHistory = this.updateHistory.bind(this);
 | 
			
		||||
            this.onBoundsChange = this.onBoundsChange.bind(this);
 | 
			
		||||
            this.onScroll = this.onScroll.bind(this);
 | 
			
		||||
 | 
			
		||||
            // Unsubscribe when the plot is destroyed
 | 
			
		||||
            $scope.$on("$destroy", releaseSubscription);
 | 
			
		||||
            this.subscribe(this.$scope.domainObject);
 | 
			
		||||
 | 
			
		||||
            this.$scope.$on('$destroy', this.stopListening);
 | 
			
		||||
            this.openmct.time.on('bounds', this.onBoundsChange);
 | 
			
		||||
            this.scrollable.on('scroll', this.onScroll);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Update displayable values to reflect latest image telemetry
 | 
			
		||||
        ImageryController.prototype.updateValues = function () {
 | 
			
		||||
            var imageObject =
 | 
			
		||||
                    this.handle && this.handle.getTelemetryObjects()[0],
 | 
			
		||||
                timestamp,
 | 
			
		||||
                m;
 | 
			
		||||
            if (imageObject && !this.isPaused) {
 | 
			
		||||
                timestamp = this.handle.getDomainValue(imageObject);
 | 
			
		||||
                m = timestamp !== undefined ?
 | 
			
		||||
                        moment.utc(timestamp) :
 | 
			
		||||
                        undefined;
 | 
			
		||||
                this.date = m ? m.format(DATE_FORMAT) : "";
 | 
			
		||||
                this.time = m ? m.format(TIME_FORMAT) : "";
 | 
			
		||||
                this.zone = m ? "UTC" : "";
 | 
			
		||||
                this.imageUrl = this.handle.getRangeValue(imageObject);
 | 
			
		||||
        ImageryController.prototype.subscribe = function (domainObject) {
 | 
			
		||||
            this.date = "";
 | 
			
		||||
            this.imageUrl = "";
 | 
			
		||||
            this.openmct.objects.get(domainObject.getId())
 | 
			
		||||
                .then(function (object) {
 | 
			
		||||
                    this.domainObject = object;
 | 
			
		||||
                    var metadata = this.openmct
 | 
			
		||||
                        .telemetry
 | 
			
		||||
                        .getMetadata(this.domainObject);
 | 
			
		||||
                    var timeKey = this.openmct.time.timeSystem().key;
 | 
			
		||||
                    this.timeFormat = this.openmct
 | 
			
		||||
                        .telemetry
 | 
			
		||||
                        .getValueFormatter(metadata.value(timeKey));
 | 
			
		||||
                    this.imageFormat = this.openmct
 | 
			
		||||
                        .telemetry
 | 
			
		||||
                        .getValueFormatter(metadata.valuesForHints(['image'])[0]);
 | 
			
		||||
                    this.unsubscribe = this.openmct.telemetry
 | 
			
		||||
                        .subscribe(this.domainObject, function (datum) {
 | 
			
		||||
                            this.updateHistory(datum);
 | 
			
		||||
                            this.updateValues(datum);
 | 
			
		||||
                        }.bind(this));
 | 
			
		||||
                    this.requestLad(false);
 | 
			
		||||
                    this.requestHistory(this.openmct.time.bounds());
 | 
			
		||||
                }.bind(this));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ImageryController.prototype.requestHistory = function (bounds) {
 | 
			
		||||
            this.requestCount++;
 | 
			
		||||
            this.$scope.imageHistory = [];
 | 
			
		||||
            var requestId = this.requestCount;
 | 
			
		||||
            this.openmct.telemetry
 | 
			
		||||
                .request(this.domainObject, bounds)
 | 
			
		||||
                    .then(function (values) {
 | 
			
		||||
                        if (this.requestCount > requestId) {
 | 
			
		||||
                            return Promise.resolve('Stale request');
 | 
			
		||||
                        }
 | 
			
		||||
                        values.forEach(function (datum) {
 | 
			
		||||
                            this.updateHistory(datum);
 | 
			
		||||
                        }, this);
 | 
			
		||||
                        this.requestLad(true);
 | 
			
		||||
                    }.bind(this));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Makes a request for the most recent datum in the
 | 
			
		||||
         * telelmetry store. Optional addToHistory argument
 | 
			
		||||
         * determines whether the requested telemetry should
 | 
			
		||||
         * be added to history or only used to update the current
 | 
			
		||||
         * image url and timestamp.
 | 
			
		||||
         * @private
 | 
			
		||||
         * @param {boolean} [addToHistory] if true, adds to history
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.requestLad = function (addToHistory) {
 | 
			
		||||
            this.openmct.telemetry
 | 
			
		||||
                .request(this.domainObject, {
 | 
			
		||||
                    strategy: 'latest',
 | 
			
		||||
                    size: 1
 | 
			
		||||
                })
 | 
			
		||||
                .then(function (values) {
 | 
			
		||||
                    this.updateValues(values[0]);
 | 
			
		||||
                    if (addToHistory !== false) {
 | 
			
		||||
                        this.updateHistory(values[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                }.bind(this));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ImageryController.prototype.stopListening = function () {
 | 
			
		||||
            this.openmct.time.off('bounds', this.onBoundsChange);
 | 
			
		||||
            this.scrollable.off('scroll', this.onScroll);
 | 
			
		||||
            if (this.unsubscribe) {
 | 
			
		||||
                this.unsubscribe();
 | 
			
		||||
                delete this.unsubscribe;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Responds to bound change event be requesting new
 | 
			
		||||
         * historical data if the bound change was manual.
 | 
			
		||||
         * @private
 | 
			
		||||
         * @param {object} [newBounds] new bounds object
 | 
			
		||||
         * @param {boolean} [tick] true when change is automatic
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.onBoundsChange = function (newBounds, tick) {
 | 
			
		||||
            if (this.domainObject && !tick) {
 | 
			
		||||
                this.requestHistory(newBounds);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Updates displayable values to match those of the most
 | 
			
		||||
         * recently recieved datum.
 | 
			
		||||
         * @param {object} [datum] the datum
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.updateValues = function (datum) {
 | 
			
		||||
            if (this.isPaused) {
 | 
			
		||||
                this.nextDatum = datum;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.time = this.timeFormat.format(datum);
 | 
			
		||||
            this.imageUrl = this.imageFormat.format(datum);
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Appends given imagery datum to running history.
 | 
			
		||||
         * @private
 | 
			
		||||
         * @param {object} [datum] target telemetry datum
 | 
			
		||||
         * @returns {boolean} falsy when a duplicate datum is given
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.updateHistory = function (datum) {
 | 
			
		||||
            if (this.$scope.imageHistory.length === 0 ||
 | 
			
		||||
                !_.isEqual(this.$scope.imageHistory.slice(-1)[0], datum)) {
 | 
			
		||||
 | 
			
		||||
                var index = _.sortedIndex(this.$scope.imageHistory, datum, 'utc');
 | 
			
		||||
                this.$scope.imageHistory.splice(index, 0, datum);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ImageryController.prototype.onScroll = function (event) {
 | 
			
		||||
            this.$window.requestAnimationFrame(function () {
 | 
			
		||||
                if (this.scrollable[0].scrollLeft <
 | 
			
		||||
                    (this.scrollable[0].scrollWidth - this.scrollable[0].clientWidth) - 20) {
 | 
			
		||||
                    this.autoScroll = false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.autoScroll = true;
 | 
			
		||||
                }
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ImageryController.prototype.scrollToRight = function () {
 | 
			
		||||
            if (this.autoScroll) {
 | 
			
		||||
                this.scrollable[0].scrollLeft = this.scrollable[0].scrollWidth;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the time portion (hours, minutes, seconds) of the
 | 
			
		||||
         * timestamp associated with the incoming image telemetry.
 | 
			
		||||
         * timestamp associated with the incoming image telemetry
 | 
			
		||||
         * if no parameter is given, or of a provided datum.
 | 
			
		||||
         * @param {object} [datum] target telemetry datum
 | 
			
		||||
         * @returns {string} the time
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.getTime = function () {
 | 
			
		||||
            return this.time;
 | 
			
		||||
        ImageryController.prototype.getTime = function (datum) {
 | 
			
		||||
            return datum ?
 | 
			
		||||
                this.timeFormat.format(datum) :
 | 
			
		||||
                this.time;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the date portion (month, year) of the
 | 
			
		||||
         * timestamp associated with the incoming image telemetry.
 | 
			
		||||
         * @returns {string} the date
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.getDate = function () {
 | 
			
		||||
            return this.date;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the time zone for the displayed time/date corresponding
 | 
			
		||||
         * to the timestamp associated with the incoming image
 | 
			
		||||
         * telemetry.
 | 
			
		||||
         * @returns {string} the time
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.getZone = function () {
 | 
			
		||||
            return this.zone;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the URL of the image telemetry to display.
 | 
			
		||||
         * Get the URL of the most recent image telemetry if no
 | 
			
		||||
         * parameter is given, or of a provided datum.
 | 
			
		||||
         * @param {object} [datum] target telemetry datum
 | 
			
		||||
         * @returns {string} URL for telemetry image
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.getImageUrl = function () {
 | 
			
		||||
            return this.imageUrl;
 | 
			
		||||
        ImageryController.prototype.getImageUrl = function (datum) {
 | 
			
		||||
            return datum ?
 | 
			
		||||
                this.imageFormat.format(datum) :
 | 
			
		||||
                this.imageUrl;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -139,15 +243,16 @@ define(
 | 
			
		||||
         * @returns {boolean} the current state
 | 
			
		||||
         */
 | 
			
		||||
        ImageryController.prototype.paused = function (state) {
 | 
			
		||||
            if (arguments.length > 0 && state !== this.isPaused) {
 | 
			
		||||
                this.isPaused = state;
 | 
			
		||||
                // Switch to latest image
 | 
			
		||||
                this.updateValues();
 | 
			
		||||
            }
 | 
			
		||||
            return this.isPaused;
 | 
			
		||||
        };
 | 
			
		||||
                if (arguments.length > 0 && state !== this.isPaused) {
 | 
			
		||||
                    this.isPaused = state;
 | 
			
		||||
                    if (this.nextDatum) {
 | 
			
		||||
                        this.updateValues(this.nextDatum);
 | 
			
		||||
                        delete this.nextDatum;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return this.isPaused;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        return ImageryController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,39 +20,40 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Policy preventing the Imagery view from being made available for
 | 
			
		||||
         * domain objects which do not have associated image telemetry.
 | 
			
		||||
         * @implements {Policy.<View, DomainObject>}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function ImageryViewPolicy() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function hasImageTelemetry(domainObject) {
 | 
			
		||||
            var telemetry = domainObject &&
 | 
			
		||||
                    domainObject.getCapability('telemetry'),
 | 
			
		||||
                metadata = telemetry ? telemetry.getMetadata() : {},
 | 
			
		||||
                ranges = metadata.ranges || [];
 | 
			
		||||
 | 
			
		||||
            return ranges.some(function (range) {
 | 
			
		||||
                return range.format === 'imageUrl' ||
 | 
			
		||||
                    range.format === 'image';
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImageryViewPolicy.prototype.allow = function (view, domainObject) {
 | 
			
		||||
            if (view.key === 'imagery') {
 | 
			
		||||
                return hasImageTelemetry(domainObject);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return ImageryViewPolicy;
 | 
			
		||||
define([
 | 
			
		||||
    '../../../../../src/api/objects/object-utils'
 | 
			
		||||
], function (
 | 
			
		||||
    objectUtils
 | 
			
		||||
) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Policy preventing the Imagery view from being made available for
 | 
			
		||||
     * domain objects which do not have associated image telemetry.
 | 
			
		||||
     * @implements {Policy.<View, DomainObject>}
 | 
			
		||||
     * @constructor
 | 
			
		||||
     */
 | 
			
		||||
    function ImageryViewPolicy(openmct) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
    ImageryViewPolicy.prototype.hasImageTelemetry = function (domainObject) {
 | 
			
		||||
        var newDO = objectUtils.toNewFormat(
 | 
			
		||||
            domainObject.getModel(),
 | 
			
		||||
            domainObject.getId()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        var metadata = this.openmct.telemetry.getMetadata(newDO);
 | 
			
		||||
        var values = metadata.valuesForHints(['image']);
 | 
			
		||||
        return values.length >= 1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ImageryViewPolicy.prototype.allow = function (view, domainObject) {
 | 
			
		||||
        if (view.key === 'imagery' || view.key === 'historical-imagery') {
 | 
			
		||||
            return this.hasImageTelemetry(domainObject);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return ImageryViewPolicy;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,141 +21,216 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/controllers/ImageryController"],
 | 
			
		||||
    function (ImageryController) {
 | 
			
		||||
    [
 | 
			
		||||
        "zepto",
 | 
			
		||||
        "../../src/controllers/ImageryController"
 | 
			
		||||
    ],
 | 
			
		||||
    function ($, ImageryController) {
 | 
			
		||||
 | 
			
		||||
        var MOCK_ELEMENT_TEMPLATE =
 | 
			
		||||
            '<div class="l-image-thumbs-wrapper"></div>';
 | 
			
		||||
 | 
			
		||||
        describe("The Imagery controller", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                mockTelemetryHandler,
 | 
			
		||||
                mockHandle,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                controller;
 | 
			
		||||
 | 
			
		||||
            function invokeWatch(expr, value) {
 | 
			
		||||
                mockScope.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](value);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            var $scope,
 | 
			
		||||
                openmct,
 | 
			
		||||
                oldDomainObject,
 | 
			
		||||
                newDomainObject,
 | 
			
		||||
                unsubscribe,
 | 
			
		||||
                metadata,
 | 
			
		||||
                prefix,
 | 
			
		||||
                controller,
 | 
			
		||||
                hasLoaded,
 | 
			
		||||
                mockWindow,
 | 
			
		||||
                mockElement;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$on', '$watch']);
 | 
			
		||||
                mockTelemetryHandler = jasmine.createSpyObj(
 | 
			
		||||
                    'telemetryHandler',
 | 
			
		||||
                    ['handle']
 | 
			
		||||
                );
 | 
			
		||||
                mockHandle = jasmine.createSpyObj(
 | 
			
		||||
                    'handle',
 | 
			
		||||
                    [
 | 
			
		||||
                        'getDomainValue',
 | 
			
		||||
                        'getRangeValue',
 | 
			
		||||
                        'getTelemetryObjects',
 | 
			
		||||
                        'unsubscribe'
 | 
			
		||||
                    ]
 | 
			
		||||
                );
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                $scope = jasmine.createSpyObj('$scope', ['$on', '$watch']);
 | 
			
		||||
                oldDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    'domainObject',
 | 
			
		||||
                    ['getId', 'getModel', 'getCapability']
 | 
			
		||||
                    ['getId']
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockTelemetryHandler.handle.andReturn(mockHandle);
 | 
			
		||||
                mockHandle.getTelemetryObjects.andReturn([mockDomainObject]);
 | 
			
		||||
                newDomainObject = { name: 'foo' };
 | 
			
		||||
                oldDomainObject.getId.andReturn('testID');
 | 
			
		||||
                openmct = {
 | 
			
		||||
                    objects: jasmine.createSpyObj('objectAPI', [
 | 
			
		||||
                        'get'
 | 
			
		||||
                    ]),
 | 
			
		||||
                    time: jasmine.createSpyObj('timeAPI', [
 | 
			
		||||
                        'timeSystem',
 | 
			
		||||
                        'clock',
 | 
			
		||||
                        'on',
 | 
			
		||||
                        'off'
 | 
			
		||||
                    ]),
 | 
			
		||||
                    telemetry: jasmine.createSpyObj('telemetryAPI', [
 | 
			
		||||
                        'subscribe',
 | 
			
		||||
                        'request',
 | 
			
		||||
                        'getValueFormatter',
 | 
			
		||||
                        'getMetadata'
 | 
			
		||||
                    ])
 | 
			
		||||
                };
 | 
			
		||||
                metadata = jasmine.createSpyObj('metadata', [
 | 
			
		||||
                    'value',
 | 
			
		||||
                    'valuesForHints'
 | 
			
		||||
                ]);
 | 
			
		||||
                prefix = "formatted ";
 | 
			
		||||
                unsubscribe = jasmine.createSpy('unsubscribe');
 | 
			
		||||
                openmct.telemetry.subscribe.andReturn(unsubscribe);
 | 
			
		||||
                openmct.time.timeSystem.andReturn({
 | 
			
		||||
                    key: 'testKey'
 | 
			
		||||
                });
 | 
			
		||||
                $scope.domainObject = oldDomainObject;
 | 
			
		||||
                openmct.objects.get.andReturn(Promise.resolve(newDomainObject));
 | 
			
		||||
                openmct.telemetry.getMetadata.andReturn(metadata);
 | 
			
		||||
                openmct.telemetry.getValueFormatter.andCallFake(function (property) {
 | 
			
		||||
                    var formatter =
 | 
			
		||||
                        jasmine.createSpyObj("formatter-" + property, ['format']);
 | 
			
		||||
                    var isTime = (property === "timestamp");
 | 
			
		||||
                    formatter.format.andCallFake(function (datum) {
 | 
			
		||||
                        return (isTime ? prefix : "") + datum[property];
 | 
			
		||||
                    });
 | 
			
		||||
                    return formatter;
 | 
			
		||||
                });
 | 
			
		||||
                hasLoaded = false;
 | 
			
		||||
                openmct.telemetry.request.andCallFake(function () {
 | 
			
		||||
                    setTimeout(function () {
 | 
			
		||||
                        hasLoaded = true;
 | 
			
		||||
                    }, 10);
 | 
			
		||||
                    return Promise.resolve([{
 | 
			
		||||
                            timestamp: 1434600258123,
 | 
			
		||||
                            value: 'some/url'
 | 
			
		||||
                        }]);
 | 
			
		||||
                });
 | 
			
		||||
                metadata.value.andReturn("timestamp");
 | 
			
		||||
                metadata.valuesForHints.andReturn(["value"]);
 | 
			
		||||
                mockElement = $(MOCK_ELEMENT_TEMPLATE);
 | 
			
		||||
                mockWindow = jasmine.createSpyObj('$window', ['requestAnimationFrame']);
 | 
			
		||||
                mockWindow.requestAnimationFrame.andCallFake(function (f) {
 | 
			
		||||
                    return f();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                controller = new ImageryController(
 | 
			
		||||
                    mockScope,
 | 
			
		||||
                    mockTelemetryHandler
 | 
			
		||||
                    $scope,
 | 
			
		||||
                    mockWindow,
 | 
			
		||||
                    mockElement,
 | 
			
		||||
                    openmct
 | 
			
		||||
                );
 | 
			
		||||
                invokeWatch('domainObject', mockDomainObject);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("unsubscribes when scope is destroyed", function () {
 | 
			
		||||
                expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
 | 
			
		||||
            describe("when loaded", function () {
 | 
			
		||||
                var callback,
 | 
			
		||||
                    boundsListener;
 | 
			
		||||
 | 
			
		||||
                // Find the $destroy listener and call it
 | 
			
		||||
                mockScope.$on.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === '$destroy') {
 | 
			
		||||
                        call.args[1]();
 | 
			
		||||
                    }
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    waitsFor(function () {
 | 
			
		||||
                        return hasLoaded;
 | 
			
		||||
                    }, 500);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    runs(function () {
 | 
			
		||||
                        openmct.time.on.calls.forEach(function (call) {
 | 
			
		||||
                            if (call.args[0] === "bounds") {
 | 
			
		||||
                                boundsListener = call.args[1];
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                        callback =
 | 
			
		||||
                            openmct.telemetry.subscribe.mostRecentCall.args[1];
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
                expect(mockHandle.unsubscribe).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes the latest telemetry values", function () {
 | 
			
		||||
                // 06/18/2015 4:04am UTC
 | 
			
		||||
                var testTimestamp = 1434600258123,
 | 
			
		||||
                    testUrl = "some/url",
 | 
			
		||||
                    nextTimestamp = 1434600259456, // 4:05.456
 | 
			
		||||
                    nextUrl = "some/other/url";
 | 
			
		||||
                it("uses LAD telemetry", function () {
 | 
			
		||||
                    expect(openmct.telemetry.request).toHaveBeenCalledWith(
 | 
			
		||||
                        newDomainObject,
 | 
			
		||||
                        {
 | 
			
		||||
                            strategy: 'latest',
 | 
			
		||||
                            size: 1
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                    expect(controller.getTime()).toEqual(prefix + 1434600258123);
 | 
			
		||||
                    expect(controller.getImageUrl()).toEqual('some/url');
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockHandle.getDomainValue.andReturn(testTimestamp);
 | 
			
		||||
                mockHandle.getRangeValue.andReturn(testUrl);
 | 
			
		||||
 | 
			
		||||
                // Call the subscription listener
 | 
			
		||||
                mockTelemetryHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
                it("exposes the latest telemetry values", function () {
 | 
			
		||||
                    callback({
 | 
			
		||||
                        timestamp: 1434600259456,
 | 
			
		||||
                        value: "some/other/url"
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                expect(controller.getTime()).toEqual("04:04:18.123");
 | 
			
		||||
                expect(controller.getDate()).toEqual("2015-06-18");
 | 
			
		||||
                expect(controller.getZone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.getImageUrl()).toEqual(testUrl);
 | 
			
		||||
                    expect(controller.getTime()).toEqual(prefix + 1434600259456);
 | 
			
		||||
                    expect(controller.getImageUrl()).toEqual("some/other/url");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockHandle.getDomainValue.andReturn(nextTimestamp);
 | 
			
		||||
                mockHandle.getRangeValue.andReturn(nextUrl);
 | 
			
		||||
                mockTelemetryHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
                it("allows updates to be paused and unpaused", function () {
 | 
			
		||||
                    var newTimestamp = 1434600259456,
 | 
			
		||||
                        newUrl = "some/other/url",
 | 
			
		||||
                        initialTimestamp = controller.getTime(),
 | 
			
		||||
                        initialUrl = controller.getImageUrl();
 | 
			
		||||
 | 
			
		||||
                expect(controller.getTime()).toEqual("04:04:19.456");
 | 
			
		||||
                expect(controller.getDate()).toEqual("2015-06-18");
 | 
			
		||||
                expect(controller.getZone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.getImageUrl()).toEqual(nextUrl);
 | 
			
		||||
            });
 | 
			
		||||
                    expect(initialTimestamp).not.toBe(prefix + newTimestamp);
 | 
			
		||||
                    expect(initialUrl).not.toBe(newUrl);
 | 
			
		||||
                    expect(controller.paused()).toBeFalsy();
 | 
			
		||||
 | 
			
		||||
            it("allows updates to be paused", function () {
 | 
			
		||||
                // 06/18/2015 4:04am UTC
 | 
			
		||||
                var testTimestamp = 1434600258123,
 | 
			
		||||
                    testUrl = "some/url",
 | 
			
		||||
                    nextTimestamp = 1434600259456, // 4:05.456
 | 
			
		||||
                    nextUrl = "some/other/url";
 | 
			
		||||
                    controller.paused(true);
 | 
			
		||||
                    expect(controller.paused()).toBeTruthy();
 | 
			
		||||
                    callback({ timestamp: newTimestamp, value: newUrl });
 | 
			
		||||
 | 
			
		||||
                // As above, but pause in between. Expect details
 | 
			
		||||
                // not to change this time
 | 
			
		||||
                    expect(controller.getTime()).toEqual(initialTimestamp);
 | 
			
		||||
                    expect(controller.getImageUrl()).toEqual(initialUrl);
 | 
			
		||||
 | 
			
		||||
                mockHandle.getDomainValue.andReturn(testTimestamp);
 | 
			
		||||
                mockHandle.getRangeValue.andReturn(testUrl);
 | 
			
		||||
                    controller.paused(false);
 | 
			
		||||
                    expect(controller.paused()).toBeFalsy();
 | 
			
		||||
                    expect(controller.getTime()).toEqual(prefix + newTimestamp);
 | 
			
		||||
                    expect(controller.getImageUrl()).toEqual(newUrl);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Call the subscription listener
 | 
			
		||||
                mockTelemetryHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
                it("subscribes to telemetry", function () {
 | 
			
		||||
                    expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
 | 
			
		||||
                        newDomainObject,
 | 
			
		||||
                        jasmine.any(Function)
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.getTime()).toEqual("04:04:18.123");
 | 
			
		||||
                expect(controller.getDate()).toEqual("2015-06-18");
 | 
			
		||||
                expect(controller.getZone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.getImageUrl()).toEqual(testUrl);
 | 
			
		||||
                it("requests telemetry", function () {
 | 
			
		||||
                    expect(openmct.telemetry.request).toHaveBeenCalledWith(
 | 
			
		||||
                        newDomainObject,
 | 
			
		||||
                        jasmine.any(Object)
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.paused()).toBeFalsy();
 | 
			
		||||
                controller.paused(true); // Pause!
 | 
			
		||||
                expect(controller.paused()).toBeTruthy();
 | 
			
		||||
                it("unsubscribes and unlistens when scope is destroyed", function () {
 | 
			
		||||
                    expect(unsubscribe).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                mockHandle.getDomainValue.andReturn(nextTimestamp);
 | 
			
		||||
                mockHandle.getRangeValue.andReturn(nextUrl);
 | 
			
		||||
                mockTelemetryHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
                    $scope.$on.calls.forEach(function (call) {
 | 
			
		||||
                        if (call.args[0] === '$destroy') {
 | 
			
		||||
                            call.args[1]();
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    expect(unsubscribe).toHaveBeenCalled();
 | 
			
		||||
                    expect(openmct.time.off)
 | 
			
		||||
                        .toHaveBeenCalledWith('bounds', jasmine.any(Function));
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.getTime()).toEqual("04:04:18.123");
 | 
			
		||||
                expect(controller.getDate()).toEqual("2015-06-18");
 | 
			
		||||
                expect(controller.getZone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.getImageUrl()).toEqual(testUrl);
 | 
			
		||||
                it("listens for bounds event and responds to tick and manual change", function () {
 | 
			
		||||
                    var mockBounds = {start: 1434600000000, end: 1434600500000};
 | 
			
		||||
                    expect(openmct.time.on).toHaveBeenCalled();
 | 
			
		||||
                    openmct.telemetry.request.reset();
 | 
			
		||||
                    boundsListener(mockBounds, true);
 | 
			
		||||
                    expect(openmct.telemetry.request).not.toHaveBeenCalled();
 | 
			
		||||
                    boundsListener(mockBounds, false);
 | 
			
		||||
                    expect(openmct.telemetry.request).toHaveBeenCalledWith(newDomainObject, mockBounds);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it ("doesnt append duplicate datum", function () {
 | 
			
		||||
                    var mockDatum = {url: 'image/url', utc: 1434600000000};
 | 
			
		||||
                    expect(controller.updateHistory(mockDatum)).toBe(true);
 | 
			
		||||
                    expect(controller.updateHistory(mockDatum)).toBe(false);
 | 
			
		||||
                    expect(controller.updateHistory(mockDatum)).toBe(false);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("initially shows an empty string for date/time", function () {
 | 
			
		||||
                // Call the subscription listener while domain/range
 | 
			
		||||
                // values are still undefined
 | 
			
		||||
                mockHandle.getDomainValue.andReturn(undefined);
 | 
			
		||||
                mockHandle.getRangeValue.andReturn(undefined);
 | 
			
		||||
                mockTelemetryHandler.handle.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                // Should have empty strings for date/time/zone
 | 
			
		||||
                expect(controller.getTime()).toEqual("");
 | 
			
		||||
                expect(controller.getDate()).toEqual("");
 | 
			
		||||
                expect(controller.getZone()).toEqual("");
 | 
			
		||||
                expect(controller.getImageUrl()).toBeUndefined();
 | 
			
		||||
                expect(controller.getImageUrl()).toEqual("");
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,14 +26,17 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("Imagery view policy", function () {
 | 
			
		||||
            var testView,
 | 
			
		||||
                openmct,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockTelemetry,
 | 
			
		||||
                testMetadata,
 | 
			
		||||
                mockMetadata,
 | 
			
		||||
                policy;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testView = { key: "imagery" };
 | 
			
		||||
                testMetadata = {};
 | 
			
		||||
                mockMetadata = jasmine.createSpyObj('metadata', [
 | 
			
		||||
                    "valuesForHints"
 | 
			
		||||
                ]);
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    'domainObject',
 | 
			
		||||
                    ['getId', 'getModel', 'getCapability']
 | 
			
		||||
@@ -45,30 +48,33 @@ define(
 | 
			
		||||
                mockDomainObject.getCapability.andCallFake(function (c) {
 | 
			
		||||
                    return c === 'telemetry' ? mockTelemetry : undefined;
 | 
			
		||||
                });
 | 
			
		||||
                mockTelemetry.getMetadata.andReturn(testMetadata);
 | 
			
		||||
                mockDomainObject.getId.andReturn("some-id");
 | 
			
		||||
                mockDomainObject.getModel.andReturn({ name: "foo" });
 | 
			
		||||
                mockTelemetry.getMetadata.andReturn(mockMetadata);
 | 
			
		||||
                mockMetadata.valuesForHints.andReturn(["bar"]);
 | 
			
		||||
 | 
			
		||||
                policy = new ImageryViewPolicy();
 | 
			
		||||
                openmct = { telemetry: mockTelemetry };
 | 
			
		||||
 | 
			
		||||
                policy = new ImageryViewPolicy(openmct);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("checks for hints indicating image telemetry", function () {
 | 
			
		||||
                policy.allow(testView, mockDomainObject);
 | 
			
		||||
                expect(mockMetadata.valuesForHints)
 | 
			
		||||
                    .toHaveBeenCalledWith(["image"]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows the imagery view for domain objects with image telemetry", function () {
 | 
			
		||||
                testMetadata.ranges = [{ key: "foo", format: "imageUrl" }];
 | 
			
		||||
                expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("disallows the imagery view for domain objects without image telemetry", function () {
 | 
			
		||||
                testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
 | 
			
		||||
                expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("disallows the imagery view for domain objects without telemetry", function () {
 | 
			
		||||
                testMetadata.ranges = [{ key: "foo", format: "imageUrl" }];
 | 
			
		||||
                mockDomainObject.getCapability.andReturn(undefined);
 | 
			
		||||
                mockMetadata.valuesForHints.andReturn([]);
 | 
			
		||||
                expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows other views", function () {
 | 
			
		||||
                testView.key = "somethingElse";
 | 
			
		||||
                testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
 | 
			
		||||
                expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,29 @@ define([
 | 
			
		||||
                    "type": "layout",
 | 
			
		||||
                    "template": layoutTemplate,
 | 
			
		||||
                    "editable": true,
 | 
			
		||||
                    "uses": []
 | 
			
		||||
                    "uses": [],
 | 
			
		||||
                    "toolbar": {
 | 
			
		||||
                        "sections": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "items": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "method": "showFrame",
 | 
			
		||||
                                        "cssClass": "icon-frame-show",
 | 
			
		||||
                                        "control": "button",
 | 
			
		||||
                                        "title": "Show frame",
 | 
			
		||||
                                        "description": "Show frame"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "method": "hideFrame",
 | 
			
		||||
                                        "cssClass": "icon-frame-hide",
 | 
			
		||||
                                        "control": "button",
 | 
			
		||||
                                        "title": "Hide frame",
 | 
			
		||||
                                        "description": "Hide frame"
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            }
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "fixed",
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,12 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<svg ng-attr-width="{{parameters.gridSize[0] * ngModel.width()}}"
 | 
			
		||||
     ng-attr-height="{{parameters.gridSize[1] * ngModel.height()}}">
 | 
			
		||||
    <line ng-attr-x1="{{parameters.gridSize[0] * ngModel.x1() + 1}}"
 | 
			
		||||
          ng-attr-y1="{{parameters.gridSize[1] * ngModel.y1() + 1}}"
 | 
			
		||||
          ng-attr-x2="{{parameters.gridSize[0] * ngModel.x2() + 1}}"
 | 
			
		||||
          ng-attr-y2="{{parameters.gridSize[1] * ngModel.y2() + 1}}"
 | 
			
		||||
<svg ng-attr-width="{{ngModel.getGridSize()[0] * ngModel.width()}}"
 | 
			
		||||
     ng-attr-height="{{ngModel.getGridSize()[1] * ngModel.height()}}">
 | 
			
		||||
    <line ng-attr-x1="{{ngModel.getGridSize()[0] * ngModel.x1() + 1}}"
 | 
			
		||||
          ng-attr-y1="{{ngModel.getGridSize()[1] * ngModel.y1() + 1}}"
 | 
			
		||||
          ng-attr-x2="{{ngModel.getGridSize()[0] * ngModel.x2() + 1}}"
 | 
			
		||||
          ng-attr-y2="{{ngModel.getGridSize()[1] * ngModel.y2() + 1}}"
 | 
			
		||||
          ng-attr-stroke="{{ngModel.stroke()}}"
 | 
			
		||||
          stroke-width="2">
 | 
			
		||||
    </line>
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB  | 
@@ -25,15 +25,20 @@
 | 
			
		||||
    <!-- Background grid -->
 | 
			
		||||
    <div class="l-grid-holder" ng-click="controller.clearSelection()">
 | 
			
		||||
        <div class="l-grid l-grid-x"
 | 
			
		||||
             ng-if="!controller.getGridSize()[0] < 3"
 | 
			
		||||
             ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
 | 
			
		||||
        <div class="l-grid l-grid-y"
 | 
			
		||||
             ng-if="!controller.getGridSize()[1] < 3"
 | 
			
		||||
             ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Fixed position elements -->
 | 
			
		||||
    <div ng-repeat="element in controller.getElements()"
 | 
			
		||||
         class="l-fixed-position-item"
 | 
			
		||||
         ng-class="{ 's-not-selected': controller.selected() && !controller.selected(element) }"
 | 
			
		||||
         class="l-fixed-position-item s-selectable s-moveable s-hover-border"
 | 
			
		||||
         ng-class="{
 | 
			
		||||
            's-not-selected': controller.selected() && !controller.selected(element),
 | 
			
		||||
            's-selected': controller.selected(element)
 | 
			
		||||
         }"
 | 
			
		||||
         ng-style="element.style"
 | 
			
		||||
         ng-click="controller.select(element)">
 | 
			
		||||
        <mct-include key="element.template"
 | 
			
		||||
@@ -43,15 +48,15 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Selection highlight, handles -->
 | 
			
		||||
    <span ng-if="controller.selected()">
 | 
			
		||||
        <div class="l-fixed-position-item s-selectable s-selected s-moveable"
 | 
			
		||||
    <span class="s-selected s-moveable" ng-if="controller.selected()">
 | 
			
		||||
        <div class="l-fixed-position-item t-edit-handle-holder"
 | 
			
		||||
             mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
 | 
			
		||||
             mct-drag="controller.moveHandle().continueDrag(delta)"
 | 
			
		||||
             mct-drag-up="controller.moveHandle().endDrag()"
 | 
			
		||||
             ng-style="controller.selected().style">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div ng-repeat="handle in controller.handles()"
 | 
			
		||||
             class="l-fixed-position-item-handle"
 | 
			
		||||
             class="l-fixed-position-item-handle edit-corner"
 | 
			
		||||
             ng-style="handle.style()"
 | 
			
		||||
             mct-drag-down="handle.startDrag()"
 | 
			
		||||
             mct-drag="handle.continueDrag(delta)"
 | 
			
		||||
 
 | 
			
		||||
@@ -19,25 +19,31 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="frame frame-template abs">
 | 
			
		||||
<div class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
 | 
			
		||||
    <div class="abs object-browse-bar l-flex-row">
 | 
			
		||||
        <div class="left flex-elem l-flex-row grows">
 | 
			
		||||
            <mct-representation
 | 
			
		||||
                key="'object-header'"
 | 
			
		||||
                mct-object="domainObject"
 | 
			
		||||
                class="l-flex-row flex-elem object-header grows">
 | 
			
		||||
                    key="'object-header'"
 | 
			
		||||
                    mct-object="domainObject"
 | 
			
		||||
                    class="l-flex-row flex-elem object-header grows">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
 | 
			
		||||
            <mct-representation key="'switcher'"
 | 
			
		||||
                                ng-model="representation"
 | 
			
		||||
                                mct-object="domainObject">
 | 
			
		||||
            <mct-representation
 | 
			
		||||
                    key="'switcher'"
 | 
			
		||||
                    ng-model="representation"
 | 
			
		||||
                    mct-object="domainObject">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
            <a class="s-button icon-expand t-btn-view-large"
 | 
			
		||||
               title="View large"
 | 
			
		||||
               mct-trigger-modal>
 | 
			
		||||
            </a>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="abs object-holder">
 | 
			
		||||
        <mct-representation key="representation.selected.key"
 | 
			
		||||
                            mct-object="representation.selected.key && domainObject">
 | 
			
		||||
        <mct-representation
 | 
			
		||||
                key="representation.selected.key"
 | 
			
		||||
                mct-object="representation.selected.key && domainObject">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,19 +19,33 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="l-layout"
 | 
			
		||||
     ng-controller="LayoutController as controller">
 | 
			
		||||
 | 
			
		||||
    <div class='frame child-frame panel abs'
 | 
			
		||||
<div class="abs l-layout"
 | 
			
		||||
     ng-controller="LayoutController as controller"
 | 
			
		||||
     ng-click="controller.clearSelection()">
 | 
			
		||||
 | 
			
		||||
    <!-- Background grid -->
 | 
			
		||||
    <div class="l-grid-holder" ng-click="controller.clearSelection()">
 | 
			
		||||
        <div class="l-grid l-grid-x"
 | 
			
		||||
             ng-if="!controller.getGridSize()[0] < 3"
 | 
			
		||||
             ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
 | 
			
		||||
        <div class="l-grid l-grid-y"
 | 
			
		||||
             ng-if="!controller.getGridSize()[1] < 3"
 | 
			
		||||
             ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
 | 
			
		||||
         ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
 | 
			
		||||
         ng-repeat="childObject in composition"
 | 
			
		||||
         ng-click="controller.select($event, childObject.getId())"
 | 
			
		||||
         ng-style="controller.getFrameStyle(childObject.getId())">
 | 
			
		||||
 | 
			
		||||
        <mct-representation key="'frame'"
 | 
			
		||||
                            class="frame child-frame holder contents abs"
 | 
			
		||||
                            class="t-rep-frame holder contents abs"
 | 
			
		||||
                            mct-object="childObject">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
        <!-- Drag handles -->
 | 
			
		||||
        <span ng-show="domainObject.hasCapability('editor')">
 | 
			
		||||
        <span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
 | 
			
		||||
            <span class="edit-handle edit-move"
 | 
			
		||||
                  mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
 | 
			
		||||
                  mct-drag="controller.continueDrag(delta)"
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ define(
 | 
			
		||||
            // Convert from element x/y/width/height to an
 | 
			
		||||
            // appropriate ng-style argument, to position elements.
 | 
			
		||||
            function convertPosition(elementProxy) {
 | 
			
		||||
                var gridSize = self.gridSize;
 | 
			
		||||
                var gridSize = elementProxy.getGridSize();
 | 
			
		||||
                // Multiply position/dimensions by grid size
 | 
			
		||||
                return {
 | 
			
		||||
                    left: (gridSize[0] * elementProxy.x()) + 'px',
 | 
			
		||||
@@ -114,6 +114,7 @@ define(
 | 
			
		||||
                self.gridSize = layoutGrid;
 | 
			
		||||
 | 
			
		||||
                self.elementProxies.forEach(function (elementProxy) {
 | 
			
		||||
                    elementProxy.setGridSize(self.gridSize);
 | 
			
		||||
                    elementProxy.style = convertPosition(elementProxy);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@@ -121,7 +122,7 @@ define(
 | 
			
		||||
            // Decorate an element for display
 | 
			
		||||
            function makeProxyElement(element, index, elements) {
 | 
			
		||||
                var ElementProxy = ElementProxies[element.type],
 | 
			
		||||
                    e = ElementProxy && new ElementProxy(element, index, elements);
 | 
			
		||||
                    e = ElementProxy && new ElementProxy(element, index, elements, self.gridSize);
 | 
			
		||||
 | 
			
		||||
                if (e) {
 | 
			
		||||
                    // Provide a displayable position (convert from grid to px)
 | 
			
		||||
@@ -254,7 +255,8 @@ define(
 | 
			
		||||
                    color: "",
 | 
			
		||||
                    titled: true,
 | 
			
		||||
                    width: DEFAULT_DIMENSIONS[0],
 | 
			
		||||
                    height: DEFAULT_DIMENSIONS[1]
 | 
			
		||||
                    height: DEFAULT_DIMENSIONS[1],
 | 
			
		||||
                    useGrid: true
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                //Re-initialize objects, and subscribe to new object
 | 
			
		||||
@@ -518,4 +520,3 @@ define(
 | 
			
		||||
        return FixedController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,9 +47,10 @@ define(
 | 
			
		||||
         * @memberof platform/features/layout.FixedDragHandle#
 | 
			
		||||
         */
 | 
			
		||||
        FixedDragHandle.prototype.style = function () {
 | 
			
		||||
            var gridSize = this.elementHandle.getGridSize();
 | 
			
		||||
            // Adjust from grid to pixel coordinates
 | 
			
		||||
            var x = this.elementHandle.x() * this.gridSize[0],
 | 
			
		||||
                y = this.elementHandle.y() * this.gridSize[1];
 | 
			
		||||
            var x = this.elementHandle.x() * gridSize[0],
 | 
			
		||||
                y = this.elementHandle.y() * gridSize[1];
 | 
			
		||||
 | 
			
		||||
            // Convert to a CSS style centered on that point
 | 
			
		||||
            return {
 | 
			
		||||
@@ -78,13 +79,14 @@ define(
 | 
			
		||||
         *                   started
 | 
			
		||||
         */
 | 
			
		||||
        FixedDragHandle.prototype.continueDrag = function (delta) {
 | 
			
		||||
            var gridSize = this.elementHandle.getGridSize();
 | 
			
		||||
            if (this.dragging) {
 | 
			
		||||
                // Update x/y positions (snapping to grid)
 | 
			
		||||
                this.elementHandle.x(
 | 
			
		||||
                    this.dragging.x + Math.round(delta[0] / this.gridSize[0])
 | 
			
		||||
                    this.dragging.x + Math.round(delta[0] / gridSize[0])
 | 
			
		||||
                );
 | 
			
		||||
                this.elementHandle.y(
 | 
			
		||||
                    this.dragging.y + Math.round(delta[1] / this.gridSize[1])
 | 
			
		||||
                    this.dragging.y + Math.round(delta[1] / gridSize[1])
 | 
			
		||||
                );
 | 
			
		||||
                // Invoke update callback
 | 
			
		||||
                if (this.update) {
 | 
			
		||||
 
 | 
			
		||||
@@ -61,6 +61,7 @@ define(
 | 
			
		||||
                element.width = element.width || 1;
 | 
			
		||||
                element.height = element.height || 1;
 | 
			
		||||
                element.type = type;
 | 
			
		||||
                element.useGrid = true;
 | 
			
		||||
 | 
			
		||||
                // Finally, add it to the view's configuration
 | 
			
		||||
                addElementCallback(element);
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,9 @@ define(
 | 
			
		||||
            DEFAULT_GRID_SIZE = [32, 32],
 | 
			
		||||
            MINIMUM_FRAME_SIZE = [320, 180];
 | 
			
		||||
 | 
			
		||||
        // Method names to expose from this controller
 | 
			
		||||
        var HIDE = 'hideFrame', SHOW = 'showFrame';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The LayoutController is responsible for supporting the
 | 
			
		||||
         * Layout view. It arranges frames according to saved configuration
 | 
			
		||||
@@ -79,6 +82,11 @@ define(
 | 
			
		||||
                    ],
 | 
			
		||||
                    dimensions: self.defaultDimensions()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                // Store the id so that the newly-dropped object
 | 
			
		||||
                // gets selected during refresh composition
 | 
			
		||||
                self.droppedFrameId = id;
 | 
			
		||||
 | 
			
		||||
                // Mark change as persistable
 | 
			
		||||
                if ($scope.commit) {
 | 
			
		||||
                    $scope.commit("Dropped a frame.");
 | 
			
		||||
@@ -111,6 +119,13 @@ define(
 | 
			
		||||
 | 
			
		||||
                        $scope.composition = composition;
 | 
			
		||||
                        self.layoutPanels(ids);
 | 
			
		||||
                        self.setDefaultFrame();
 | 
			
		||||
 | 
			
		||||
                        // If there is a newly-dropped object, select it.
 | 
			
		||||
                        if (self.droppedFrameId) {
 | 
			
		||||
                            self.select(null, self.droppedFrameId);
 | 
			
		||||
                            delete self.droppedFrameId;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@@ -123,13 +138,21 @@ define(
 | 
			
		||||
                // saved by the EditRepresenter.
 | 
			
		||||
                $scope.configuration =
 | 
			
		||||
                    $scope.configuration || {};
 | 
			
		||||
 | 
			
		||||
                // Make sure there is a "panels" field in the
 | 
			
		||||
                // view configuration.
 | 
			
		||||
                $scope.configuration.panels =
 | 
			
		||||
                    $scope.configuration.panels || {};
 | 
			
		||||
                // Store the position of this panel.
 | 
			
		||||
 | 
			
		||||
                $scope.configuration.panels[self.activeDragId] =
 | 
			
		||||
                    self.rawPositions[self.activeDragId];
 | 
			
		||||
                    $scope.configuration.panels[self.activeDragId] || {};
 | 
			
		||||
 | 
			
		||||
                // Store the position and dimensions of this panel.
 | 
			
		||||
                $scope.configuration.panels[self.activeDragId].position =
 | 
			
		||||
                    self.rawPositions[self.activeDragId].position;
 | 
			
		||||
                $scope.configuration.panels[self.activeDragId].dimensions =
 | 
			
		||||
                    self.rawPositions[self.activeDragId].dimensions;
 | 
			
		||||
 | 
			
		||||
                // Mark this object as dirty to encourage persistence
 | 
			
		||||
                if ($scope.commit) {
 | 
			
		||||
                    $scope.commit("Moved frame.");
 | 
			
		||||
@@ -144,6 +167,10 @@ define(
 | 
			
		||||
            // Watch for changes to the grid size in the model
 | 
			
		||||
            $scope.$watch("model.layoutGrid", updateGridSize);
 | 
			
		||||
 | 
			
		||||
            $scope.$watch("selection", function (selection) {
 | 
			
		||||
                this.selection = selection;
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
 | 
			
		||||
            // Update composed objects on screen, and position panes
 | 
			
		||||
            $scope.$watchCollection("model.composition", refreshComposition);
 | 
			
		||||
 | 
			
		||||
@@ -151,6 +178,20 @@ define(
 | 
			
		||||
            $scope.$on("mctDrop", handleDrop);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set a default value for hasFrame property on a panel.
 | 
			
		||||
        // A 'hyperlink' object should have no frame by default.
 | 
			
		||||
        LayoutController.prototype.setDefaultFrame = function () {
 | 
			
		||||
            var panels = this.$scope.configuration.panels;
 | 
			
		||||
 | 
			
		||||
            this.$scope.composition.forEach(function (object) {
 | 
			
		||||
                var id = object.getId();
 | 
			
		||||
 | 
			
		||||
                if (panels[id] && panels[id].hasFrame === undefined) {
 | 
			
		||||
                    panels[id].hasFrame = object.getModel().type === 'hyperlink' ? false : true;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Convert from { positions: ..., dimensions: ... } to an
 | 
			
		||||
        // appropriate ng-style argument, to position frames.
 | 
			
		||||
        LayoutController.prototype.convertPosition = function (raw) {
 | 
			
		||||
@@ -293,9 +334,100 @@ define(
 | 
			
		||||
         * view configuration.
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.endDrag = function () {
 | 
			
		||||
            this.frameMoved = true;
 | 
			
		||||
 | 
			
		||||
            setTimeout(function () {
 | 
			
		||||
                this.frameMoved = false;
 | 
			
		||||
            }.bind(this), 0);
 | 
			
		||||
 | 
			
		||||
            this.endDragInScope();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the object is currently selected.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {string} obj the object to check for selection
 | 
			
		||||
         * @returns {boolean} true if selected, otherwise false
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.selected = function (obj) {
 | 
			
		||||
            return this.selectedId && this.selectedId === obj.getId();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Set the active user selection in this view.
 | 
			
		||||
         *
 | 
			
		||||
         * @param event the mouse event
 | 
			
		||||
         * @param {string} id the object id
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.select = function (event, id) {
 | 
			
		||||
            if (event) {
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
                if (this.selection) {
 | 
			
		||||
                    event.preventDefault();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var self = this;
 | 
			
		||||
            var selectedObj = {};
 | 
			
		||||
            var configuration = this.$scope.configuration;
 | 
			
		||||
 | 
			
		||||
            this.selectedId = id;
 | 
			
		||||
 | 
			
		||||
            // Toggle the visibility of the object frame
 | 
			
		||||
            function toggle() {
 | 
			
		||||
                // create new selection object so toolbar updates.
 | 
			
		||||
                selectedObj = {};
 | 
			
		||||
 | 
			
		||||
                configuration.panels[id].hasFrame =
 | 
			
		||||
                    !configuration.panels[id].hasFrame;
 | 
			
		||||
 | 
			
		||||
                // Change which method is exposed, to influence
 | 
			
		||||
                // which button is shown in the toolbar
 | 
			
		||||
                selectedObj[configuration.panels[id].hasFrame ? HIDE : SHOW] = toggle;
 | 
			
		||||
                self.selection.deselect();
 | 
			
		||||
                self.selection.select(selectedObj);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Expose initial toggle
 | 
			
		||||
            selectedObj[configuration.panels[id].hasFrame ? HIDE : SHOW] = toggle;
 | 
			
		||||
 | 
			
		||||
            if (this.selection) {
 | 
			
		||||
                this.selection.select(selectedObj);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Clear the current user selection.
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.clearSelection = function (event) {
 | 
			
		||||
            // Keep the selection if the frame is moved.
 | 
			
		||||
            if (this.frameMoved) {
 | 
			
		||||
                this.frameMoved = false;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.selection) {
 | 
			
		||||
                this.selection.deselect();
 | 
			
		||||
                delete this.selectedId;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the object has frame.
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.hasFrame = function (obj) {
 | 
			
		||||
            return this.$scope.configuration.panels[obj.getId()].hasFrame;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the size of the grid, in pixels. The returned array
 | 
			
		||||
         * is in the form `[x, y]`.
 | 
			
		||||
         * @returns {number[]} the grid size
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.getGridSize = function () {
 | 
			
		||||
            return this.gridSize;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return LayoutController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@ define([
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    var OVERLAY_TEMPLATE = '' +
 | 
			
		||||
'<div class="abs overlay l-large-view">' +
 | 
			
		||||
'    <div class="abs blocker"></div>' +
 | 
			
		||||
'    <div class="abs outer-holder">' +
 | 
			
		||||
'       <a class="close icon-x-in-circle"></a>' +
 | 
			
		||||
@@ -37,8 +36,7 @@ define([
 | 
			
		||||
'               <a class="t-done s-button major">Done</a>' +
 | 
			
		||||
'           </div>' +
 | 
			
		||||
'       </div>' +
 | 
			
		||||
'    </div>' +
 | 
			
		||||
'</div>';
 | 
			
		||||
'    </div>';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * MCT Trigger Modal is intended for use in only one location: inside the
 | 
			
		||||
@@ -81,7 +79,8 @@ define([
 | 
			
		||||
            function openOverlay() {
 | 
			
		||||
                // Remove frame classes from being applied in a non-frame context
 | 
			
		||||
                $(frame).removeClass('frame frame-template');
 | 
			
		||||
                overlay = document.createElement('span');
 | 
			
		||||
                overlay = document.createElement('div');
 | 
			
		||||
                $(overlay).addClass('abs overlay l-large-view');
 | 
			
		||||
                overlay.innerHTML = OVERLAY_TEMPLATE;
 | 
			
		||||
                overlayContainer = overlay.querySelector('.t-contents');
 | 
			
		||||
                closeButton = overlay.querySelector('a.close');
 | 
			
		||||
 
 | 
			
		||||
@@ -37,10 +37,11 @@ define(
 | 
			
		||||
         * @param element the fixed position element, as stored in its
 | 
			
		||||
         *        configuration
 | 
			
		||||
         * @param index the element's index within its array
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] from
 | 
			
		||||
         * @param {Array} elements the full array of elements
 | 
			
		||||
         */
 | 
			
		||||
        function BoxProxy(element, index, elements) {
 | 
			
		||||
            var proxy = new ElementProxy(element, index, elements);
 | 
			
		||||
        function BoxProxy(element, index, elements, gridSize) {
 | 
			
		||||
            var proxy = new ElementProxy(element, index, elements, gridSize);
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get/set this element's fill color. (Omitting the
 | 
			
		||||
@@ -52,6 +53,12 @@ define(
 | 
			
		||||
             */
 | 
			
		||||
            proxy.fill = new AccessorMutator(element, 'fill');
 | 
			
		||||
 | 
			
		||||
            //Expose x,y, width and height for editing
 | 
			
		||||
            proxy.editWidth = new AccessorMutator(element, 'width');
 | 
			
		||||
            proxy.editHeight = new AccessorMutator(element, 'height');
 | 
			
		||||
            proxy.editX = new AccessorMutator(element, 'x');
 | 
			
		||||
            proxy.editY = new AccessorMutator(element, 'y');
 | 
			
		||||
 | 
			
		||||
            return proxy;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./AccessorMutator', './ResizeHandle'],
 | 
			
		||||
    function (AccessorMutator, ResizeHandle) {
 | 
			
		||||
    ['./AccessorMutator', './ResizeHandle', './UnitAccessorMutator'],
 | 
			
		||||
    function (AccessorMutator, ResizeHandle, UnitAccessorMutator) {
 | 
			
		||||
 | 
			
		||||
        // Index deltas for changes in order
 | 
			
		||||
        var ORDERS = {
 | 
			
		||||
@@ -32,6 +32,10 @@ define(
 | 
			
		||||
            bottom: Number.NEGATIVE_INFINITY
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Mininmum pixel height and width for objects
 | 
			
		||||
        var MIN_WIDTH = 10;
 | 
			
		||||
        var MIN_HEIGHT = 10;
 | 
			
		||||
 | 
			
		||||
        // Ensure a value is non-negative (for x/y setters)
 | 
			
		||||
        function clamp(value) {
 | 
			
		||||
            return Math.max(value, 0);
 | 
			
		||||
@@ -51,17 +55,29 @@ define(
 | 
			
		||||
         * @param element the fixed position element, as stored in its
 | 
			
		||||
         *        configuration
 | 
			
		||||
         * @param index the element's index within its array
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] from
 | 
			
		||||
         * @param {Array} elements the full array of elements
 | 
			
		||||
         */
 | 
			
		||||
        function ElementProxy(element, index, elements) {
 | 
			
		||||
            this.resizeHandles = [new ResizeHandle(element, 1, 1)];
 | 
			
		||||
 | 
			
		||||
        function ElementProxy(element, index, elements, gridSize) {
 | 
			
		||||
            /**
 | 
			
		||||
             * The element as stored in the view configuration.
 | 
			
		||||
             * @memberof platform/features/layout.ElementProxy#
 | 
			
		||||
             */
 | 
			
		||||
            this.element = element;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The current grid size of the layout.
 | 
			
		||||
             * @memberof platform/features/layout.ElementProxy#
 | 
			
		||||
             */
 | 
			
		||||
            this.gridSize = gridSize || [1,1]; //Ensure a reasonable default
 | 
			
		||||
 | 
			
		||||
            this.resizeHandles = [new ResizeHandle(
 | 
			
		||||
                                    this.element,
 | 
			
		||||
                                    this.getMinWidth(),
 | 
			
		||||
                                    this.getMinHeight(),
 | 
			
		||||
                                    this.getGridSize()
 | 
			
		||||
                                  )];
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get and/or set the x position of this element.
 | 
			
		||||
             * Units are in fixed position grid space.
 | 
			
		||||
@@ -106,6 +122,8 @@ define(
 | 
			
		||||
             */
 | 
			
		||||
            this.height = new AccessorMutator(element, 'height');
 | 
			
		||||
 | 
			
		||||
            this.useGrid = new UnitAccessorMutator(this);
 | 
			
		||||
 | 
			
		||||
            this.index = index;
 | 
			
		||||
            this.elements = elements;
 | 
			
		||||
        }
 | 
			
		||||
@@ -156,6 +174,51 @@ define(
 | 
			
		||||
            return this.resizeHandles;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns which grid size the element is currently using.
 | 
			
		||||
         * @return {number[]} The current grid size in [x,y] form if the element
 | 
			
		||||
         *                    is currently using the grid, [1,1] if it is using
 | 
			
		||||
         *                    pixels.
 | 
			
		||||
         */
 | 
			
		||||
        ElementProxy.prototype.getGridSize = function () {
 | 
			
		||||
            var gridSize;
 | 
			
		||||
            // Default to using the grid if useGrid was not defined
 | 
			
		||||
            if (typeof this.element.useGrid === 'undefined') {
 | 
			
		||||
                this.element.useGrid = true;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.element.useGrid) {
 | 
			
		||||
                gridSize = this.gridSize;
 | 
			
		||||
            } else {
 | 
			
		||||
                gridSize = [1,1];
 | 
			
		||||
            }
 | 
			
		||||
            return gridSize;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Set the current grid size stored by this element proxy
 | 
			
		||||
         * @param {number[]} gridSize The current layout grid size in [x,y] form
 | 
			
		||||
         */
 | 
			
		||||
        ElementProxy.prototype.setGridSize = function (gridSize) {
 | 
			
		||||
            this.gridSize = gridSize;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the current minimum element width in grid units
 | 
			
		||||
         * @return {number} The current minimum element width
 | 
			
		||||
         */
 | 
			
		||||
        ElementProxy.prototype.getMinWidth = function () {
 | 
			
		||||
            return Math.ceil(MIN_WIDTH / this.getGridSize()[0]);
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the current minimum element height in grid units
 | 
			
		||||
         * @return {number} The current minimum element height
 | 
			
		||||
         */
 | 
			
		||||
        ElementProxy.prototype.getMinHeight = function () {
 | 
			
		||||
            return Math.ceil(MIN_HEIGHT / this.getGridSize()[1]);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return ElementProxy;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -36,10 +36,11 @@ define(
 | 
			
		||||
         *        configuration
 | 
			
		||||
         * @param index the element's index within its array
 | 
			
		||||
         * @param {Array} elements the full array of elements
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] from
 | 
			
		||||
         * @augments {platform/features/layout.ElementProxy}
 | 
			
		||||
         */
 | 
			
		||||
        function ImageProxy(element, index, elements) {
 | 
			
		||||
            var proxy = new ElementProxy(element, index, elements);
 | 
			
		||||
        function ImageProxy(element, index, elements, gridSize) {
 | 
			
		||||
            var proxy = new ElementProxy(element, index, elements, gridSize);
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get and/or set the displayed text of this element.
 | 
			
		||||
@@ -49,6 +50,12 @@ define(
 | 
			
		||||
             */
 | 
			
		||||
            proxy.url = new AccessorMutator(element, 'url');
 | 
			
		||||
 | 
			
		||||
            //Expose x,y, width and height properties for editing
 | 
			
		||||
            proxy.editWidth = new AccessorMutator(element, 'width');
 | 
			
		||||
            proxy.editHeight = new AccessorMutator(element, 'height');
 | 
			
		||||
            proxy.editX = new AccessorMutator(element, 'x');
 | 
			
		||||
            proxy.editY = new AccessorMutator(element, 'y');
 | 
			
		||||
 | 
			
		||||
            return proxy;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,14 +35,16 @@ define(
 | 
			
		||||
         * @param {string} yProperty field which stores x position
 | 
			
		||||
         * @param {string} xOther field which stores x of other end
 | 
			
		||||
         * @param {string} yOther field which stores y of other end
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] from
 | 
			
		||||
         * @implements {platform/features/layout.ElementHandle}
 | 
			
		||||
         */
 | 
			
		||||
        function LineHandle(element, xProperty, yProperty, xOther, yOther) {
 | 
			
		||||
        function LineHandle(element, xProperty, yProperty, xOther, yOther, gridSize) {
 | 
			
		||||
            this.element = element;
 | 
			
		||||
            this.xProperty = xProperty;
 | 
			
		||||
            this.yProperty = yProperty;
 | 
			
		||||
            this.xOther = xOther;
 | 
			
		||||
            this.yOther = yOther;
 | 
			
		||||
            this.gridSize = gridSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LineHandle.prototype.x = function (value) {
 | 
			
		||||
@@ -83,6 +85,10 @@ define(
 | 
			
		||||
            return element[yProperty];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        LineHandle.prototype.getGridSize = function () {
 | 
			
		||||
            return this.gridSize;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return LineHandle;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./ElementProxy', './LineHandle'],
 | 
			
		||||
    function (ElementProxy, LineHandle) {
 | 
			
		||||
    ['./ElementProxy', './LineHandle', './AccessorMutator'],
 | 
			
		||||
    function (ElementProxy, LineHandle, AccessorMutator) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Selection/diplay proxy for line elements of a fixed position
 | 
			
		||||
@@ -33,13 +33,14 @@ define(
 | 
			
		||||
         *        configuration
 | 
			
		||||
         * @param index the element's index within its array
 | 
			
		||||
         * @param {Array} elements the full array of elements
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] from
 | 
			
		||||
         * @augments {platform/features/layout.ElementProxy}
 | 
			
		||||
         */
 | 
			
		||||
        function LineProxy(element, index, elements) {
 | 
			
		||||
            var proxy = new ElementProxy(element, index, elements),
 | 
			
		||||
        function LineProxy(element, index, elements, gridSize) {
 | 
			
		||||
            var proxy = new ElementProxy(element, index, elements, gridSize),
 | 
			
		||||
                handles = [
 | 
			
		||||
                    new LineHandle(element, 'x', 'y', 'x2', 'y2'),
 | 
			
		||||
                    new LineHandle(element, 'x2', 'y2', 'x', 'y')
 | 
			
		||||
                    new LineHandle(element, 'x', 'y', 'x2', 'y2', proxy.getGridSize()),
 | 
			
		||||
                    new LineHandle(element, 'x2', 'y2', 'x', 'y', proxy.getGridSize())
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
@@ -148,6 +149,12 @@ define(
 | 
			
		||||
                return handles;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Expose endpoint coordinates for editing
 | 
			
		||||
            proxy.editX1 = new AccessorMutator(element, 'x');
 | 
			
		||||
            proxy.editY1 = new AccessorMutator(element, 'y');
 | 
			
		||||
            proxy.editX2 = new AccessorMutator(element, 'x2');
 | 
			
		||||
            proxy.editY2 = new AccessorMutator(element, 'y2');
 | 
			
		||||
 | 
			
		||||
            return proxy;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,12 +35,14 @@ define(
 | 
			
		||||
         * @memberof platform/features/layout
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function ResizeHandle(element, minWidth, minHeight) {
 | 
			
		||||
        function ResizeHandle(element, minWidth, minHeight, gridSize) {
 | 
			
		||||
            this.element = element;
 | 
			
		||||
 | 
			
		||||
            // Ensure reasonable defaults
 | 
			
		||||
            this.minWidth = minWidth || 0;
 | 
			
		||||
            this.minHeight = minHeight || 0;
 | 
			
		||||
 | 
			
		||||
            this.gridSize = gridSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ResizeHandle.prototype.x = function (value) {
 | 
			
		||||
@@ -65,6 +67,10 @@ define(
 | 
			
		||||
            return element.y + element.height;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ResizeHandle.prototype.getGridSize = function () {
 | 
			
		||||
            return this.gridSize;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return ResizeHandle;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -39,10 +39,11 @@ define(
 | 
			
		||||
         *        configuration
 | 
			
		||||
         * @param index the element's index within its array
 | 
			
		||||
         * @param {Array} elements the full array of elements
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] form
 | 
			
		||||
         * @augments {platform/features/layout.ElementProxy}
 | 
			
		||||
         */
 | 
			
		||||
        function TelemetryProxy(element, index, elements) {
 | 
			
		||||
            var proxy = new TextProxy(element, index, elements);
 | 
			
		||||
        function TelemetryProxy(element, index, elements, gridSize) {
 | 
			
		||||
            var proxy = new TextProxy(element, index, elements, gridSize);
 | 
			
		||||
 | 
			
		||||
            // Toggle the visibility of the title
 | 
			
		||||
            function toggle() {
 | 
			
		||||
 
 | 
			
		||||
@@ -36,10 +36,11 @@ define(
 | 
			
		||||
         *        configuration
 | 
			
		||||
         * @param index the element's index within its array
 | 
			
		||||
         * @param {Array} elements the full array of elements
 | 
			
		||||
         * @param {number[]} gridSize the current layout grid size in [x,y] from
 | 
			
		||||
         * @augments {platform/features/layout.ElementProxy}
 | 
			
		||||
         */
 | 
			
		||||
        function TextProxy(element, index, elements) {
 | 
			
		||||
            var proxy = new BoxProxy(element, index, elements);
 | 
			
		||||
        function TextProxy(element, index, elements, gridSize) {
 | 
			
		||||
            var proxy = new BoxProxy(element, index, elements, gridSize);
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get and/or set the text color of this element.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								platform/features/layout/src/elements/UnitAccessorMutator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								platform/features/layout/src/elements/UnitAccessorMutator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT 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 includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Variant of AccessorMutator to handle the specific case of updating
 | 
			
		||||
         * useGrid, in order update the positions appropriately from within
 | 
			
		||||
         * the scope of UnitAccessorMutator
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/features/layout
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {ElementProxy} proxy ElementProxy object to perform the update
 | 
			
		||||
         *                             upon
 | 
			
		||||
         */
 | 
			
		||||
        function UnitAccessorMutator(elementProxy) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            this.elementProxy = elementProxy;
 | 
			
		||||
            return function (useGrid) {
 | 
			
		||||
                var current = elementProxy.element.useGrid;
 | 
			
		||||
                if (arguments.length > 0) {
 | 
			
		||||
                    elementProxy.element.useGrid = useGrid;
 | 
			
		||||
                    if (useGrid && !current) {
 | 
			
		||||
                        self.convertCoordsTo('grid');
 | 
			
		||||
                    } else if (!useGrid && current) {
 | 
			
		||||
                        self.convertCoordsTo('px');
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return elementProxy.element.useGrid;
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * For the elementProxy object called upon, convert its element's
 | 
			
		||||
         * coordinates and size from pixels to grid units, or vice-versa.
 | 
			
		||||
         * @param {string} unit When called with 'px', converts grid units to
 | 
			
		||||
         *                      pixels; when called with 'grid', snaps element
 | 
			
		||||
         *                      to grid units
 | 
			
		||||
         */
 | 
			
		||||
        UnitAccessorMutator.prototype.convertCoordsTo = function (unit) {
 | 
			
		||||
            var proxy = this.elementProxy,
 | 
			
		||||
                gridSize = proxy.gridSize,
 | 
			
		||||
                element = proxy.element,
 | 
			
		||||
                minWidth = proxy.getMinWidth(),
 | 
			
		||||
                minHeight = proxy.getMinHeight();
 | 
			
		||||
            if (unit === 'px') {
 | 
			
		||||
                element.x = element.x * gridSize[0];
 | 
			
		||||
                element.y = element.y * gridSize[1];
 | 
			
		||||
                element.width = element.width * gridSize[0];
 | 
			
		||||
                element.height = element.height * gridSize[1];
 | 
			
		||||
                if (element.x2 && element.y2) {
 | 
			
		||||
                    element.x2 = element.x2 * gridSize[0];
 | 
			
		||||
                    element.y2 = element.y2 * gridSize[1];
 | 
			
		||||
                }
 | 
			
		||||
            } else if (unit === 'grid') {
 | 
			
		||||
                element.x = Math.round(element.x / gridSize[0]);
 | 
			
		||||
                element.y = Math.round(element.y / gridSize[1]);
 | 
			
		||||
                element.width = Math.max(Math.round(element.width / gridSize[0]), minWidth);
 | 
			
		||||
                element.height = Math.max(Math.round(element.height / gridSize[1]), minHeight);
 | 
			
		||||
                if (element.x2 && element.y2) {
 | 
			
		||||
                    element.x2 = Math.round(element.x2 / gridSize[0]);
 | 
			
		||||
                    element.y2 = Math.round(element.y2 / gridSize[1]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return UnitAccessorMutator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -157,9 +157,9 @@ define(
 | 
			
		||||
                };
 | 
			
		||||
                testValues = { a: 10, b: 42, c: 31.42 };
 | 
			
		||||
                testConfiguration = { elements: [
 | 
			
		||||
                    { type: "fixed.telemetry", id: 'a', x: 1, y: 1 },
 | 
			
		||||
                    { type: "fixed.telemetry", id: 'b', x: 1, y: 1 },
 | 
			
		||||
                    { type: "fixed.telemetry", id: 'c', x: 1, y: 1 }
 | 
			
		||||
                    { type: "fixed.telemetry", id: 'a', x: 1, y: 1, useGrid: true},
 | 
			
		||||
                    { type: "fixed.telemetry", id: 'b', x: 1, y: 1, useGrid: true},
 | 
			
		||||
                    { type: "fixed.telemetry", id: 'c', x: 1, y: 1, useGrid: true}
 | 
			
		||||
                ]};
 | 
			
		||||
 | 
			
		||||
                mockChildren = testModel.composition.map(makeMockDomainObject);
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user