Compare commits
	
		
			141 Commits
		
	
	
		
			summary-wi
			...
			activity-v
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b7a94a9dd5 | ||
|   | 2b11b8d63c | ||
|   | 006e99fb07 | ||
|   | 63dc4b6253 | ||
|   | 85868f690e | ||
|   | b1f34f7cd7 | ||
|   | 8785d9a9d7 | ||
|   | 3a6e1fd301 | ||
|   | fcef4274e5 | ||
|   | 744a5340d3 | ||
|   | d140051054 | ||
|   | 8161e4fc89 | ||
|   | 8da74f2665 | ||
|   | abf7654027 | ||
|   | 8fba707321 | ||
|   | 8ad5cca936 | ||
|   | 754d484501 | ||
|   | 74717b59c3 | ||
|   | ffdb19787b | ||
|   | 5dc0d8c7f8 | ||
|   | 2390278b97 | ||
|   | 8a66731271 | ||
|   | 0a9ea48355 | ||
|   | 01d93306f3 | ||
|   | 0588f9190a | ||
|   | 1378b57567 | ||
|   | 9e12886c66 | ||
|   | 2d352ac574 | ||
|   | 284dec4903 | ||
|   | 5a0656c700 | ||
|   | 425655bae0 | ||
|   | 50b4d5cb28 | ||
|   | bc62d7d5ae | ||
|   | c0dcf4495e | ||
|   | a51b9bc63f | ||
|   | ff003c3dab | ||
|   | de7c4d2ce3 | ||
|   | 4b07930305 | ||
|   | 5a49ac16b1 | ||
|   | 91b150c064 | ||
|   | 9506d309b0 | ||
|   | c9bd60f50e | ||
|   | cf15ff5c07 | ||
|   | 6bbdfcdfbe | ||
|   | 06e93ff520 | ||
|   | 550e7a15e6 | ||
|   | 71c54cd541 | ||
|   | e81b8e53dc | ||
|   | 84e6928f54 | ||
|   | ba688fe62c | ||
|   | c533e10352 | ||
|   | 04f47b3db6 | ||
|   | 717fa5edf4 | ||
|   | 14f5f048fb | ||
|   | 72929500d3 | ||
|   | 471adde923 | ||
|   | 6c5d5f3d00 | ||
|   | 2262fef29b | ||
|   | bda30f1475 | ||
|   | bfec434369 | ||
|   | 14df350994 | ||
|   | 80582f5e8d | ||
|   | 7442768ced | ||
|   | 77c7bdfdec | ||
|   | 07d9769966 | ||
|   | 385b6177b2 | ||
|   | 7f68d26433 | ||
|   | d7b44f8d09 | ||
|   | c4cd36e15b | ||
|   | 618a6e7e8d | ||
|   | 63a8c91f71 | ||
|   | e59020fec7 | ||
|   | a2e424203a | ||
|   | 16853644cb | ||
|   | 2272766c57 | ||
|   | 1d9cdea2d4 | ||
|   | 715219c44d | ||
|   | 00dc2875bf | ||
|   | 8703f363b8 | ||
|   | 26210eaa50 | ||
|   | a4a1cb5e05 | ||
|   | 9570f2f7a1 | ||
|   | eb4ded39b3 | ||
|   | 7deb3cd025 | ||
|   | 06779e6cd9 | ||
|   | 7f43c0bf1a | ||
|   | bfa3bbcdc7 | ||
|   | 2baf3f8bb0 | ||
|   | 10ac13ac5c | ||
|   | 138cb199bb | ||
|   | 374c363a78 | ||
|   | e9cb5cd639 | ||
|   | bc7d92ee0d | ||
|   | 78f49784a0 | ||
|   | 8754c438cc | ||
|   | 1419c75503 | ||
|   | ca8cad0a74 | ||
|   | a3a55d3b48 | ||
|   | e66f818996 | ||
|   | ae5ef33487 | ||
|   | 59c5430579 | ||
|   | 6d52f094d9 | ||
|   | 740db8da75 | ||
|   | 68abc15ed5 | ||
|   | bb47feb517 | ||
|   | 469820fb0f | ||
|   | 92dd99b26c | ||
|   | 9fe1923189 | ||
|   | 42ddb38629 | ||
|   | fe60d7abbc | ||
|   | 54b975f242 | ||
|   | 7f75e089e8 | ||
|   | 8e8c66280f | ||
|   | d3d874e209 | ||
|   | ce561e1598 | ||
|   | 7290601a37 | ||
|   | ea5a85ffd1 | ||
|   | eb196ea521 | ||
|   | b6a8078634 | ||
|   | e53b34ed60 | ||
|   | 13ffa3e3c4 | ||
|   | 0e3b629d90 | ||
|   | 23216e5aee | ||
|   | 5b366e91c1 | ||
|   | aa336dfd57 | ||
|   | 556296096d | ||
|   | e4aaa860a3 | ||
|   | 2079a74ab2 | ||
|   | 4b86439b8a | ||
|   | a4d8e8ff90 | ||
|   | 3674808a13 | ||
|   | 10c0c29f64 | ||
|   | fa1a942184 | ||
|   | aa36417590 | ||
|   | e6c78f6d8b | ||
|   | 747afa6200 | ||
|   | 008f1387ed | ||
|   | 6ed76708ec | ||
|   | c2ff81bad1 | ||
|   | a6c3d98ddd | ||
|   | 603e990755 | 
							
								
								
									
										17
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								API.md
									
									
									
									
									
								
							| @@ -505,7 +505,7 @@ MCT, it will be pre-configured to use the UTC time system, which is installed an | ||||
|  | ||||
| The time bounds of an Open MCT application are defined as numbers, and a Time  | ||||
| System gives meaning and context to these numbers so that they can be correctly  | ||||
| interpreted. Time Systems are javscript objects that provide some information  | ||||
| interpreted. Time Systems are JavaScript objects that provide some information  | ||||
| about the current time reference frame. An example of defining and registering  | ||||
| a new time system is given below: | ||||
|  | ||||
| @@ -879,6 +879,21 @@ openmct.install(openmct.plugins.CouchDB('http://localhost:9200')) | ||||
| * `openmct.plugins.Espresso` and `openmct.plugins.Snow` are two different | ||||
|   themes (dark and light) available for Open MCT. Note that at least one | ||||
|   of these themes must be installed for Open MCT to appear correctly. | ||||
| * `openmct.plugins.URLIndicatorPlugin` adds an indicator which shows the | ||||
| availability of a URL with the following options:  | ||||
|   - `url` : URL to indicate the status of | ||||
|   - `cssClass`: Icon to show in the status bar, defaults to `icon-database`, [list of all icons](https://nasa.github.io/openmct/style-guide/#/browse/styleguide:home?view=items) | ||||
|   - `interval`: Interval between checking the connection, defaults to `10000` | ||||
|   - `label` Name showing up as text in the status bar, defaults to url | ||||
| ```javascript | ||||
| openmct.install(openmct.plugins.URLIndicatorPlugin({ | ||||
|   url: 'http://google.com', | ||||
|   cssClass: 'check', | ||||
|   interval: 10000, | ||||
|   label: 'Google' | ||||
|  }) | ||||
| ); | ||||
| ``` | ||||
| * `openmct.plugins.LocalStorage` provides persistence of user-created | ||||
|   objects in browser-local storage. This is particularly useful in | ||||
|   development environments. | ||||
|   | ||||
| @@ -17,8 +17,8 @@ | ||||
|     "screenfull": "^3.0.0", | ||||
|     "node-uuid": "^1.4.7", | ||||
|     "comma-separated-values": "^3.6.4", | ||||
|     "FileSaver.js": "^0.0.2", | ||||
|     "zepto": "1.2.0", | ||||
|     "file-saver": "^1.3.3", | ||||
|     "zepto": "^1.1.6", | ||||
|     "eventemitter3": "^1.2.0", | ||||
|     "lodash": "3.10.1", | ||||
|     "almond": "~0.3.2", | ||||
|   | ||||
| @@ -4,12 +4,6 @@ deployment: | ||||
|     commands: | ||||
|         - npm install canvas nomnoml | ||||
|         - ./build-docs.sh | ||||
|         - git fetch --unshallow | ||||
|         - git push git@heroku.com:openmctweb-demo.git $CIRCLE_SHA1:refs/heads/master | ||||
|   openmct-demo: | ||||
|     branch: live_demo | ||||
|     heroku: | ||||
|       appname: openmct-demo | ||||
|   openmctweb-staging-deux: | ||||
|     branch: mobile | ||||
|     heroku: | ||||
|   | ||||
| @@ -78,8 +78,8 @@ define([ | ||||
|         return this.workerInterface.request(workerRequest); | ||||
|     }; | ||||
|  | ||||
|     GeneratorProvider.prototype.subscribe = function (domainObject, callback, request) { | ||||
|         var workerRequest = this.makeWorkerRequest(domainObject, request); | ||||
|     GeneratorProvider.prototype.subscribe = function (domainObject, callback) { | ||||
|         var workerRequest = this.makeWorkerRequest(domainObject, {}); | ||||
|         return this.workerInterface.subscribe(workerRequest, callback); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -44,9 +44,7 @@ define([ | ||||
|         message = message.data; | ||||
|         var callback = this.callbacks[message.id]; | ||||
|         if (callback) { | ||||
|             if (callback(message)) { | ||||
|                 delete this.callbacks[message.id]; | ||||
|             } | ||||
|             callback(message); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -72,6 +70,7 @@ define([ | ||||
|             deferred.resolve = resolve; | ||||
|             deferred.reject = reject; | ||||
|         }); | ||||
|         var messageId; | ||||
|  | ||||
|         function callback(message) { | ||||
|             if (message.error) { | ||||
| @@ -79,33 +78,27 @@ define([ | ||||
|             } else { | ||||
|                 deferred.resolve(message.data); | ||||
|             } | ||||
|             return true; | ||||
|             delete this.callbacks[messageId]; | ||||
|         } | ||||
|  | ||||
|         this.dispatch('request', request, callback); | ||||
|         messageId = this.dispatch('request', request, callback.bind(this)); | ||||
|  | ||||
|         return promise; | ||||
|     }; | ||||
|  | ||||
|     WorkerInterface.prototype.subscribe = function (request, cb) { | ||||
|         var isCancelled = false; | ||||
|  | ||||
|         var callback = function (message) { | ||||
|             if (isCancelled) { | ||||
|                 return true; | ||||
|             } | ||||
|         function callback(message) { | ||||
|             cb(message.data); | ||||
|         }; | ||||
|  | ||||
|         var messageId = this.dispatch('subscribe', request, callback) | ||||
|         var messageId = this.dispatch('subscribe', request, callback); | ||||
|  | ||||
|         return function () { | ||||
|             isCancelled = true; | ||||
|             this.dispatch('unsubscribe', { | ||||
|                 id: messageId | ||||
|             }); | ||||
|             delete this.callbacks[messageId]; | ||||
|         }.bind(this); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ define([ | ||||
|                 { "key": "styleguide.intro", "name": "Introduction", "cssClass": "icon-page", "description": "Introduction and overview to the style guide" }, | ||||
|                 { "key": "styleguide.standards", "name": "Standards", "cssClass": "icon-page", "description": "" }, | ||||
|                 { "key": "styleguide.colors", "name": "Colors", "cssClass": "icon-page", "description": "" }, | ||||
|                 { "key": "styleguide.status", "name": "status", "cssClass": "icon-page", "description": "Limits, telemetry paused, etc." }, | ||||
|                 { "key": "styleguide.glyphs", "name": "Glyphs", "cssClass": "icon-page", "description": "Glyphs overview" }, | ||||
|                 { "key": "styleguide.controls", "name": "Controls", "cssClass": "icon-page", "description": "Buttons, selects, HTML controls" }, | ||||
|                 { "key": "styleguide.input", "name": "Text Inputs", "cssClass": "icon-page", "description": "Various text inputs" }, | ||||
| @@ -25,6 +26,7 @@ define([ | ||||
|                 { "key": "styleguide.intro", "type": "styleguide.intro", "templateUrl": "templates/intro.html", "editable": false }, | ||||
|                 { "key": "styleguide.standards", "type": "styleguide.standards", "templateUrl": "templates/standards.html", "editable": false }, | ||||
|                 { "key": "styleguide.colors", "type": "styleguide.colors", "templateUrl": "templates/colors.html", "editable": false }, | ||||
|                 { "key": "styleguide.status", "type": "styleguide.status", "templateUrl": "templates/status.html", "editable": false }, | ||||
|                 { "key": "styleguide.glyphs", "type": "styleguide.glyphs", "templateUrl": "templates/glyphs.html", "editable": false }, | ||||
|                 { "key": "styleguide.controls", "type": "styleguide.controls", "templateUrl": "templates/controls.html", "editable": false }, | ||||
|                 { "key": "styleguide.input", "type": "styleguide.input", "templateUrl": "templates/input.html", "editable": false }, | ||||
| @@ -47,6 +49,7 @@ define([ | ||||
|                             "intro", | ||||
|                             "standards", | ||||
|                             "colors", | ||||
|                             "status", | ||||
|                             "glyphs", | ||||
|                             "styleguide:ui-elements" | ||||
|                         ] | ||||
|   | ||||
| @@ -28,8 +28,8 @@ | ||||
|         color: $colorKey; | ||||
|     } | ||||
|  | ||||
|     h1, h2 { | ||||
|         color: pullForward($colorBodyFg, 20%); | ||||
|     h1, h2, strong, b { | ||||
|         color: pullForward($colorBodyFg, 50%); | ||||
|     } | ||||
|  | ||||
|     h2 { | ||||
| @@ -45,6 +45,10 @@ | ||||
|         text-transform: uppercase; | ||||
|     } | ||||
|  | ||||
|     strong, b { | ||||
|         font-weight: normal; | ||||
|     } | ||||
|  | ||||
|     .w-markup { | ||||
|         //Wrap markup example "pre" element | ||||
|         background-color: $colorCode; | ||||
| @@ -54,6 +58,12 @@ | ||||
|         position: relative; | ||||
|     } | ||||
|  | ||||
|     .w-mct-example { | ||||
|         div { | ||||
|             margin-bottom: $interiorMarginLg; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     code, | ||||
|     pre { | ||||
|         font-size: 0.8rem; | ||||
|   | ||||
| @@ -127,7 +127,8 @@ | ||||
| { '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': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' } | ||||
| { 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' }, | ||||
| { 'meaning': 'Notebook object', 'cssClass': 'icon-notebook', 'cssContent': 'e1131', 'htmlEntity': '&#xe1131' } | ||||
| ]; | ||||
| "></div> | ||||
|  | ||||
|   | ||||
| @@ -4,5 +4,5 @@ | ||||
|         <pre></pre> | ||||
|     </span> | ||||
|     <h3>Example</h3> | ||||
|     <div></div> | ||||
|     <div class="w-mct-example"></div> | ||||
| </div> | ||||
|   | ||||
							
								
								
									
										142
									
								
								example/styleguide/res/templates/status.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								example/styleguide/res/templates/status.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| <!-- | ||||
|  Open MCT, Copyright (c) 2014-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. | ||||
| --> | ||||
| <style> | ||||
|     .w-mct-example div[class*="s-limit"], | ||||
|     .w-mct-example div[class*="s-status"], | ||||
|     .w-mct-example div[class*="s-unsynced"], | ||||
|     .w-mct-example span[class*="s-limit"] { | ||||
|         border-radius: 4px; | ||||
|         padding: 3px 7px; | ||||
|     } | ||||
|     .w-mct-example table { | ||||
|         width: 100%; | ||||
|     } | ||||
| </style> | ||||
| <div class="l-style-guide s-text"> | ||||
|     <p class="doc-title">Open MCT Style Guide</p> | ||||
|     <h1>Status Indication</h1> | ||||
|  | ||||
|     <div class="l-section"> | ||||
|         <h2>Overview</h2> | ||||
|         <p>Many elements in Open MCT need to articulate a dynamic status; Open MCT provides the following styles and conventions to handle this:</p> | ||||
|         <ul> | ||||
|             <li><strong>Limits</strong>: when telemetry values exceed minimum or maximum values, they can be violating limits. Limit styles include both color and iconography; color is used to indicate severity while icons are used to indicate direction, upper or lower.</li> | ||||
|             <li><strong>Status</strong>: Open MCT also provides a number or built-in Status styles allowing telemetry or other displayed information to be visually classified by type. Common uses for these classes are to visually denote event records.</li> | ||||
|             <li><strong>Synchronization</strong>: When the system is displaying real-time data, it is very important that displays clearly indicate when they are not doing so, such as when a plot if frozen while panning or zooming. Open MCT provides a style for this.</li> | ||||
|         </ul> | ||||
|     </div> | ||||
|  | ||||
|     <div class="l-section"> | ||||
|         <h2>Limits</h2> | ||||
|         <div class="cols cols1-1"> | ||||
|             <div class="col"> | ||||
|                 <p>Limit CSS classes can be applied to any block or inline element. Open MCT limit classes set color and optionally an icon, but don't effect other properties. Yellow and red limit classes can be used as is, or allow the application of any custom icon available in Open MCT's glyphs library. "Level" limit classes - upper and lower - always use an icon in addition to a color; Open MCT doesn't support level limits without color.</p> | ||||
|                 <ul> | ||||
|                     <li>Color only</li> | ||||
|                     <ul> | ||||
|                         <li><code>s-limit-yellow</code>: A yellow limit.</li> | ||||
|                         <li><code>s-limit-red</code>: A red limit.</li> | ||||
|                     </ul> | ||||
|                     <li>Color and icon</li> | ||||
|                     <ul> | ||||
|                         <li><code>s-limit-yellow-icon</code>: A yellow limit with icon.</li> | ||||
|                         <li><code>s-limit-red-icon</code>: A red limit with icon.</li> | ||||
|                     </ul> | ||||
|                     <li>Upper and lower limit indicators. Must be used with a color limit class to be visible.</li> | ||||
|                     <ul> | ||||
|                         <li><code>s-limit-upr</code>: Upper limit. | ||||
|                         </li> | ||||
|                         <li><code>s-limit-lwr</code>: Lower limit. | ||||
|                         </li> | ||||
|                     </ul> | ||||
|                 </ul> | ||||
|             </div> | ||||
| <mct-example><div class="s-limit-yellow">Yellow limit</div> | ||||
| <div class="s-limit-red">Red limit</div> | ||||
| <div class="s-limit-yellow-icon">Yellow limit with icon</div> | ||||
| <div class="s-limit-red-icon">Red limit with icon</div> | ||||
| <div class="s-limit-yellow s-limit-lwr">Lower yellow limit</div> | ||||
| <div class="s-limit-red s-limit-upr">Upper red limit</div> | ||||
| <div class="s-limit-red icon-bell">Red Limit with a custom icon</div> | ||||
| <div>Some text with an <span class="s-limit-yellow-icon">inline element</span> showing a yellow limit.</div> | ||||
|  | ||||
| <!-- Limits applied in a table --> | ||||
| <table> | ||||
|     <tr class='header'><td>Name</td><td>Value 1</td><td>Value 2</td></tr> | ||||
|     <tr><td>ENG_PWR 4991</td><td>7.023</td><td class="s-limit-yellow s-limit-upr">70.23</td></tr> | ||||
|     <tr><td>ENG_PWR 4992</td><td>49.784</td><td class="s-limit-red s-limit-lwr">-121.22</td></tr> | ||||
|     <tr><td>ENG_PWR 4993</td><td class="s-limit-yellow icon-bell">0.451</td><td>1.007</td></tr> | ||||
| </table> | ||||
| </mct-example> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="l-section"> | ||||
|         <h2>Status</h2> | ||||
|         <div class="cols cols1-1"> | ||||
|             <div class="col"> | ||||
|                 <p>Classes here can be applied to elements as needed.</p> | ||||
|                 <ul> | ||||
|                     <li>Color only</li> | ||||
|                     <ul> | ||||
|                         <li><code>s-status-warning-hi</code></li> | ||||
|                         <li><code>s-status-warning-lo</code></li> | ||||
|                         <li><code>s-status-diagnostic</code></li> | ||||
|                         <li><code>s-status-info</code></li> | ||||
|                         <li><code>s-status-ok</code></li> | ||||
|                     </ul> | ||||
|                     <li>Color and icon</li> | ||||
|                     <ul> | ||||
|                         <li><code>s-status-warning-hi-icon</code></li> | ||||
|                         <li><code>s-status-warning-lo-icon</code></li> | ||||
|                         <li><code>s-status-diagnostic-icon</code></li> | ||||
|                         <li><code>s-status-info-icon</code></li> | ||||
|                         <li><code>s-status-ok-icon</code></li> | ||||
|                     </ul> | ||||
|                 </ul> | ||||
|             </div> | ||||
| <mct-example><div class="s-status-warning-hi">WARNING HI</div> | ||||
| <div class="s-status-warning-lo">WARNING LOW</div> | ||||
| <div class="s-status-diagnostic">DIAGNOSTIC</div> | ||||
| <div class="s-status-info">INFO</div> | ||||
| <div class="s-status-ok">OK</div> | ||||
| <div class="s-status-warning-hi-icon">WARNING HI with icon</div> | ||||
| <div class="s-status-warning-lo-icon">WARNING LOW with icon</div> | ||||
| <div class="s-status-diagnostic-icon">DIAGNOSTIC with icon</div> | ||||
| <div class="s-status-info-icon">INFO with icon</div> | ||||
| <div class="s-status-ok-icon">OK with icon</div> | ||||
| <div class="s-status-warning-hi icon-gear">WARNING HI with custom icon</div> | ||||
| </mct-example> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="l-section"> | ||||
|         <h2>Synchronization</h2> | ||||
|         <div class="cols cols1-1"> | ||||
|             <div class="col"> | ||||
|                 <p>When the system is operating in real-time streaming mode, it is important for views that display real-time data to clearly articulate when they are not, such as when a user zooms or pans a plot view, freezing that view. In that case, the CSS class <code>s-unsynced</code> should be applied to that view.</p> | ||||
|             </div> | ||||
| <mct-example><div class="s-unsynced">This element is unsynced</div> | ||||
| </mct-example> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| @@ -34,6 +34,7 @@ define( | ||||
|             pages['standards'] = { name: "Standards", type: "styleguide.standards", location: "styleguide:home" }; | ||||
|             pages['colors'] = { name: "Colors", type: "styleguide.colors", location: "styleguide:home" }; | ||||
|             pages['glyphs'] = { name: "Glyphs", type: "styleguide.glyphs", location: "styleguide:home" }; | ||||
|             pages['status'] = { name: "Status Indication", type: "styleguide.status", location: "styleguide:home" }; | ||||
|             pages['controls'] = { name: "Controls", type: "styleguide.controls", location: "styleguide:ui-elements" }; | ||||
|             pages['input'] = { name: "Text Inputs", type: "styleguide.input", location: "styleguide:ui-elements" }; | ||||
|             pages['menus'] = { name: "Menus", type: "styleguide.menus", location: "styleguide:ui-elements" }; | ||||
|   | ||||
							
								
								
									
										28
									
								
								gulpfile.js
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								gulpfile.js
									
									
									
									
									
								
							| @@ -46,9 +46,22 @@ var gulp = require('gulp'), | ||||
|             name: 'bower_components/almond/almond.js', | ||||
|             include: paths.main.replace('.js', ''), | ||||
|             wrap: { | ||||
|                 startFile: "src/start.frag", | ||||
|                 start: (function () { | ||||
|                     var buildVariables = { | ||||
|                         version: project.version, | ||||
|                         timestamp: moment.utc(Date.now()).format(), | ||||
|                         revision: fs.existsSync('.git') ? git.long() : 'Unknown', | ||||
|                         branch: fs.existsSync('.git') ? git.branch() : 'Unknown' | ||||
|                     }; | ||||
|                     return fs.readFileSync("src/start.frag", 'utf-8') | ||||
|                         .replace(/@@(\w+)/g, function (match, key) { | ||||
|                             return buildVariables[key]; | ||||
|                         });; | ||||
|                 }()), | ||||
|                 endFile: "src/end.frag" | ||||
|             }, | ||||
|             optimize: 'uglify2', | ||||
|             uglify2: { output: { comments: /@preserve/ } }, | ||||
|             mainConfigFile: paths.main, | ||||
|             wrapShim: true | ||||
|         }, | ||||
| @@ -58,14 +71,6 @@ var gulp = require('gulp'), | ||||
|         }, | ||||
|         sass: { | ||||
|             sourceComments: true | ||||
|         }, | ||||
|         replace: { | ||||
|             variables: { | ||||
|                 version: project.version, | ||||
|                 timestamp: moment.utc(Date.now()).format(), | ||||
|                 revision: fs.existsSync('.git') ? git.long() : 'Unknown', | ||||
|                 branch: fs.existsSync('.git') ? git.branch() : 'Unknown' | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -76,16 +81,11 @@ 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)); | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -42,13 +42,17 @@ | ||||
|             openmct.install(openmct.plugins.Generator()); | ||||
|             openmct.install(openmct.plugins.ExampleImagery()); | ||||
|             openmct.install(openmct.plugins.UTCTimeSystem()); | ||||
|             openmct.install(openmct.plugins.ImportExport()); | ||||
|             openmct.install(openmct.plugins.AutoflowView({ | ||||
|                 type: "telemetry.panel" | ||||
|             })); | ||||
|             openmct.install(openmct.plugins.Conductor({ | ||||
|                 menuOptions: [ | ||||
|                     { | ||||
|                         name: "Fixed", | ||||
|                         timeSystem: 'utc', | ||||
|                         bounds: { | ||||
|                             start: Date.now() - 30 * 60 * 1000, | ||||
|                             start: Date.now() - THIRTY_MINUTES, | ||||
|                             end: Date.now() | ||||
|                         } | ||||
|                     }, | ||||
| @@ -64,6 +68,7 @@ | ||||
|                 ] | ||||
|             })); | ||||
|             openmct.install(openmct.plugins.SummaryWidget()); | ||||
|             openmct.install(openmct.plugins.ActivityModes()); | ||||
|             openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0}); | ||||
|             openmct.time.timeSystem('utc'); | ||||
|             openmct.start(); | ||||
|   | ||||
| @@ -36,6 +36,7 @@ module.exports = function(config) { | ||||
|         files: [ | ||||
|             {pattern: 'bower_components/**/*.js', included: false}, | ||||
|             {pattern: 'node_modules/d3-*/**/*.js', included: false}, | ||||
|             {pattern: 'node_modules/vue/**/*.js', included: false}, | ||||
|             {pattern: 'src/**/*.js', included: false}, | ||||
|             {pattern: 'example/**/*.html', included: false}, | ||||
|             {pattern: 'example/**/*.js', included: false}, | ||||
|   | ||||
							
								
								
									
										20
									
								
								openmct.js
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								openmct.js
									
									
									
									
									
								
							| @@ -19,7 +19,7 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global requirejs*/ | ||||
| /*global requirejs,BUILD_CONSTANTS*/ | ||||
|  | ||||
| requirejs.config({ | ||||
|     "paths": { | ||||
| @@ -33,10 +33,11 @@ requirejs.config({ | ||||
|         "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", | ||||
|         "saveAs": "bower_components/file-saver/FileSaver.min", | ||||
|         "screenfull": "bower_components/screenfull/dist/screenfull.min", | ||||
|         "text": "bower_components/text/text", | ||||
|         "uuid": "bower_components/node-uuid/uuid", | ||||
|         "vue": "node_modules/vue/dist/vue.min", | ||||
|         "zepto": "bower_components/zepto/zepto.min", | ||||
|         "lodash": "bower_components/lodash/lodash", | ||||
|         "d3-selection": "node_modules/d3-selection/build/d3-selection.min", | ||||
| @@ -48,7 +49,8 @@ requirejs.config({ | ||||
|         "d3-format": "node_modules/d3-format/build/d3-format.min", | ||||
|         "d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min", | ||||
|         "d3-time": "node_modules/d3-time/build/d3-time.min", | ||||
|         "d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min" | ||||
|         "d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min", | ||||
|         "d3-dsv": "node_modules/d3-dsv/build/d3-dsv.min" | ||||
|     }, | ||||
|     "shim": { | ||||
|         "angular": { | ||||
| @@ -66,6 +68,9 @@ requirejs.config({ | ||||
|         "moment-duration-format": { | ||||
|             "deps": ["moment"] | ||||
|         }, | ||||
|         "saveAs": { | ||||
|             "exports": "saveAs" | ||||
|         }, | ||||
|         "screenfull": { | ||||
|             "exports": "screenfull" | ||||
|         }, | ||||
| @@ -91,12 +96,17 @@ requirejs.config({ | ||||
| define([ | ||||
|     './platform/framework/src/Main', | ||||
|     './src/defaultRegistry', | ||||
|     './src/MCT' | ||||
| ], function (Main, defaultRegistry, MCT) { | ||||
|     './src/MCT', | ||||
|     './src/plugins/buildInfo/plugin' | ||||
| ], function (Main, defaultRegistry, MCT, buildInfo) { | ||||
|     var openmct = new MCT(); | ||||
|  | ||||
|     openmct.legacyRegistry = defaultRegistry; | ||||
|  | ||||
|     if (typeof BUILD_CONSTANTS !== 'undefined') { | ||||
|         openmct.install(buildInfo(BUILD_CONSTANTS)); | ||||
|     } | ||||
|  | ||||
|     openmct.on('start', function () { | ||||
|         return new Main().run(defaultRegistry); | ||||
|     }); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|     "d3-axis": "^1.0.4", | ||||
|     "d3-collection": "^1.0.2", | ||||
|     "d3-color": "^1.0.2", | ||||
|     "d3-dsv": "^1.0.8", | ||||
|     "d3-format": "^1.0.2", | ||||
|     "d3-interpolate": "^1.1.3", | ||||
|     "d3-scale": "^1.0.4", | ||||
| @@ -15,19 +16,18 @@ | ||||
|     "d3-time-format": "^2.0.3", | ||||
|     "express": "^4.13.1", | ||||
|     "minimist": "^1.1.1", | ||||
|     "request": "^2.69.0" | ||||
|     "request": "^2.69.0", | ||||
|     "vue": "^2.5.6" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "bower": "^1.7.7", | ||||
|     "git-rev-sync": "^1.4.0", | ||||
|     "glob": ">= 3.0.0", | ||||
|     "gulp": "^3.9.1", | ||||
|     "gulp-header": "^1.8.8", | ||||
|     "gulp-jscs": "^3.0.2", | ||||
|     "gulp-jshint": "^2.0.0", | ||||
|     "gulp-jshint-html-reporter": "^0.1.3", | ||||
|     "gulp-rename": "^1.2.2", | ||||
|     "gulp-replace-task": "^0.11.0", | ||||
|     "gulp-requirejs-optimize": "^0.3.1", | ||||
|     "gulp-sass": "^2.2.0", | ||||
|     "gulp-sourcemaps": "^1.6.0", | ||||
|   | ||||
| @@ -26,6 +26,7 @@ define([ | ||||
|     "./src/InspectorPaneController", | ||||
|     "./src/BrowseObjectController", | ||||
|     "./src/MenuArrowController", | ||||
|     "./src/ObjectHeaderController", | ||||
|     "./src/navigation/NavigationService", | ||||
|     "./src/navigation/NavigateAction", | ||||
|     "./src/navigation/OrphanNavigationHandler", | ||||
| @@ -36,6 +37,7 @@ define([ | ||||
|     "text!./res/templates/browse-object.html", | ||||
|     "text!./res/templates/items/grid-item.html", | ||||
|     "text!./res/templates/browse/object-header.html", | ||||
|     "text!./res/templates/browse/object-header-frame.html", | ||||
|     "text!./res/templates/menu-arrow.html", | ||||
|     "text!./res/templates/back-arrow.html", | ||||
|     "text!./res/templates/items/items.html", | ||||
| @@ -48,6 +50,7 @@ define([ | ||||
|     InspectorPaneController, | ||||
|     BrowseObjectController, | ||||
|     MenuArrowController, | ||||
|     ObjectHeaderController, | ||||
|     NavigationService, | ||||
|     NavigateAction, | ||||
|     OrphanNavigationHandler, | ||||
| @@ -58,6 +61,7 @@ define([ | ||||
|     browseObjectTemplate, | ||||
|     gridItemTemplate, | ||||
|     objectHeaderTemplate, | ||||
|     objectHeaderFrameTemplate, | ||||
|     menuArrowTemplate, | ||||
|     backArrowTemplate, | ||||
|     itemsTemplate, | ||||
| @@ -140,6 +144,13 @@ define([ | ||||
|                         "$location", | ||||
|                         "$attrs" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "ObjectHeaderController", | ||||
|                     "implementation": ObjectHeaderController, | ||||
|                     "depends": [ | ||||
|                         "$scope" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|             "representations": [ | ||||
| @@ -173,6 +184,13 @@ define([ | ||||
|                         "type" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "object-header-frame", | ||||
|                     "template": objectHeaderFrameTemplate, | ||||
|                     "uses": [ | ||||
|                         "type" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "menu-arrow", | ||||
|                     "template": menuArrowTemplate, | ||||
|   | ||||
| @@ -57,7 +57,12 @@ | ||||
|             </div> | ||||
|             <mct-representation key="representation.selected.key" | ||||
|                                 mct-object="representation.selected.key && domainObject" | ||||
|                                 class="abs flex-elem grows object-holder-main scroll"> | ||||
|                                 class="abs flex-elem grows object-holder-main scroll" | ||||
|                                 mct-selectable="{ | ||||
|                                     item: domainObject.useCapability('adapter'), | ||||
|                                     oldItem: domainObject | ||||
|                                 }" | ||||
|                                 mct-init-select> | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|     </div> | ||||
|   | ||||
| @@ -19,12 +19,21 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div ng-controller="InspectorController"> | ||||
|     <div ng-repeat="region in regions"> | ||||
| <div ng-controller="InspectorController as controller"> | ||||
|         <mct-representation | ||||
|                 key="region.content.key" | ||||
|                 mct-object="domainObject" | ||||
|                 key="'object-properties'" | ||||
|                 mct-object="controller.selectedItem()" | ||||
|                 ng-model="ngModel"> | ||||
|         </mct-representation> | ||||
|     </div> | ||||
|  | ||||
|         <div ng-if="!controller.hasProviderView()"> | ||||
|             <mct-representation | ||||
|                     key="inspectorKey" | ||||
|                     mct-object="controller.selectedItem()" | ||||
|                     ng-model="ngModel"> | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|  | ||||
|         <div class='inspector-provider-view'> | ||||
|         </div> | ||||
| </div> | ||||
|   | ||||
| @@ -0,0 +1,31 @@ | ||||
| <!-- | ||||
|  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. | ||||
| --> | ||||
| <span class='type-icon flex-elem {{type.getCssClass()}}'></span> | ||||
| <span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller"> | ||||
|     <span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span> | ||||
|     <span class='title-label flex-elem holder flex-can-shrink s-input-inline'>{{model.name}}</span> | ||||
|     <span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span> | ||||
|     <mct-representation | ||||
|         key="'menu-arrow'" | ||||
|         mct-object='domainObject' | ||||
|         class="flex-elem context-available-w"></mct-representation> | ||||
| </span> | ||||
| @@ -20,9 +20,13 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span class='type-icon flex-elem {{type.getCssClass()}}'></span> | ||||
| <span class="l-elem-wrapper l-flex-row flex-elem grows"> | ||||
| <span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller"> | ||||
|     <span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span> | ||||
|     <span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span> | ||||
|     <span ng-attr-contenteditable="{{ controller.editable ? true : undefined }}" | ||||
| 		class='title-label flex-elem holder flex-can-shrink s-input-inline' | ||||
| 		ng-click="controller.edit()" | ||||
| 		ng-blur="controller.updateName($event)" | ||||
| 		ng-keypress="controller.updateName($event)">{{model.name}}</span> | ||||
|     <span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span> | ||||
|     <mct-representation | ||||
|         key="'menu-arrow'" | ||||
|   | ||||
| @@ -38,8 +38,6 @@ | ||||
|                   ng-class="{ last:($index + 1) === contextualParents.length }"> | ||||
|                 <mct-representation key="'label'" | ||||
|                                     mct-object="parent" | ||||
|                                     ng-model="ngModel" | ||||
|                                     ng-click="ngModel.selectedObject = parent" | ||||
|                                     class="location-item"> | ||||
|                 </mct-representation> | ||||
|             </span> | ||||
| @@ -51,8 +49,6 @@ | ||||
|                   ng-class="{ last:($index + 1) === primaryParents.length }"> | ||||
|                 <mct-representation key="'label'" | ||||
|                                     mct-object="parent" | ||||
|                                     ng-model="ngModel" | ||||
|                                     ng-click="ngModel.selectedObject = parent" | ||||
|                                     class="location-item"> | ||||
|                 </mct-representation> | ||||
|             </span> | ||||
|   | ||||
							
								
								
									
										92
									
								
								platform/commonUI/browse/src/ObjectHeaderController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								platform/commonUI/browse/src/ObjectHeaderController.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 () { | ||||
|  | ||||
|         /** | ||||
|          * Controller to provide the ability to inline edit an object name. | ||||
|          * | ||||
|          * @constructor | ||||
|          * @memberof platform/commonUI/browse | ||||
|          */ | ||||
|         function ObjectHeaderController($scope) { | ||||
|             this.$scope = $scope; | ||||
|             this.domainObject = $scope.domainObject; | ||||
|             this.editable = this.allowEdit(); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Updates the object name on blur and enter keypress events. | ||||
|          * | ||||
|          * @param event the mouse event | ||||
|          */ | ||||
|         ObjectHeaderController.prototype.updateName = function (event) { | ||||
|             if (!event || !event.currentTarget) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (event.type === 'blur') { | ||||
|                 this.updateModel(event); | ||||
|             } else if (event.which === 13) { | ||||
|                 this.updateModel(event); | ||||
|                 event.currentTarget.blur(); | ||||
|                 window.getSelection().removeAllRanges(); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Updates the model. | ||||
|          * | ||||
|          * @param event the mouse event | ||||
|          * @param private | ||||
|          */ | ||||
|         ObjectHeaderController.prototype.updateModel = function (event) { | ||||
|             var name = event.currentTarget.textContent.replace(/\n/g, ' '); | ||||
|  | ||||
|             if (name.length === 0) { | ||||
|                 name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name; | ||||
|                 event.currentTarget.textContent = name; | ||||
|             } | ||||
|  | ||||
|             if (name !== this.domainObject.getModel().name) { | ||||
|                 this.domainObject.getCapability('mutation').mutate(function (model) { | ||||
|                     model.name = name; | ||||
|                 }); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Checks if the domain object is editable. | ||||
|          * | ||||
|          * @private | ||||
|          * @return true if object is editable | ||||
|          */ | ||||
|         ObjectHeaderController.prototype.allowEdit = function () { | ||||
|             var type = this.domainObject && this.domainObject.getCapability('type'); | ||||
|             return !!(type && type.hasFeature('creation')); | ||||
|         }; | ||||
|  | ||||
|         return ObjectHeaderController; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										137
									
								
								platform/commonUI/browse/test/ObjectHeaderControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								platform/commonUI/browse/test/ObjectHeaderControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| /***************************************************************************** | ||||
|  * 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( | ||||
|     ["../src/ObjectHeaderController"], | ||||
|     function (ObjectHeaderController) { | ||||
|  | ||||
|         describe("The object header controller", function () { | ||||
|             var mockScope, | ||||
|                 mockDomainObject, | ||||
|                 mockCapabilities, | ||||
|                 mockMutationCapability, | ||||
|                 mockTypeCapability, | ||||
|                 mockEvent, | ||||
|                 mockCurrentTarget, | ||||
|                 model, | ||||
|                 controller; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockMutationCapability = jasmine.createSpyObj("mutation", ["mutate"]); | ||||
|                 mockTypeCapability = jasmine.createSpyObj("type", ["typeDef", "hasFeature"]); | ||||
|                 mockTypeCapability.typeDef = { name: ""}; | ||||
|                 mockTypeCapability.hasFeature.andCallFake(function (feature) { | ||||
|                     return feature === 'creation'; | ||||
|                 }); | ||||
|  | ||||
|                 mockCapabilities = { | ||||
|                     mutation: mockMutationCapability, | ||||
|                     type: mockTypeCapability | ||||
|                 }; | ||||
|  | ||||
|                 model = { | ||||
|                     name: "Test name" | ||||
|                 }; | ||||
|                 mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "getModel"]); | ||||
|                 mockDomainObject.getModel.andReturn(model); | ||||
|                 mockDomainObject.getCapability.andCallFake(function (key) { | ||||
|                     return mockCapabilities[key]; | ||||
|                 }); | ||||
|  | ||||
|                 mockScope = { | ||||
|                     domainObject: mockDomainObject | ||||
|                 }; | ||||
|  | ||||
|                 mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "textContent"]); | ||||
|                 mockCurrentTarget.blur.andReturn(mockCurrentTarget); | ||||
|  | ||||
|                 mockEvent = { | ||||
|                     which: {}, | ||||
|                     type: {}, | ||||
|                     currentTarget: mockCurrentTarget | ||||
|                 }; | ||||
|  | ||||
|                 controller = new ObjectHeaderController(mockScope); | ||||
|             }); | ||||
|  | ||||
|             it("updates the model with new name on blur", function () { | ||||
|                 mockEvent.type = "blur"; | ||||
|                 mockCurrentTarget.textContent = "New name"; | ||||
|                 controller.updateName(mockEvent); | ||||
|  | ||||
|                 expect(mockMutationCapability.mutate).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("updates the model with a default for blank names", function () { | ||||
|                 mockEvent.type = "blur"; | ||||
|                 mockCurrentTarget.textContent = ""; | ||||
|                 controller.updateName(mockEvent); | ||||
|  | ||||
|                 expect(mockCurrentTarget.textContent.length).not.toEqual(0); | ||||
|                 expect(mockMutationCapability.mutate).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("does not update the model if the same name", function () { | ||||
|                 mockEvent.type = "blur"; | ||||
|                 mockCurrentTarget.textContent = mockDomainObject.getModel().name; | ||||
|                 controller.updateName(mockEvent); | ||||
|  | ||||
|                 expect(mockMutationCapability.mutate).not.toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("updates the model on enter keypress event only", function () { | ||||
|                 mockCurrentTarget.textContent = "New name"; | ||||
|                 controller.updateName(mockEvent); | ||||
|  | ||||
|                 expect(mockMutationCapability.mutate).not.toHaveBeenCalled(); | ||||
|  | ||||
|                 mockEvent.which = 13; | ||||
|                 controller.updateName(mockEvent); | ||||
|  | ||||
|                 expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function)); | ||||
|  | ||||
|                 mockMutationCapability.mutate.mostRecentCall.args[0](model); | ||||
|  | ||||
|                 expect(mockDomainObject.getModel().name).toBe("New name"); | ||||
|             }); | ||||
|  | ||||
|             it("blurs the field on enter key press", function () { | ||||
|                 mockCurrentTarget.textContent = "New name"; | ||||
|                 mockEvent.which = 13; | ||||
|                 controller.updateName(mockEvent); | ||||
|  | ||||
|                 expect(mockEvent.currentTarget.blur).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("allows editting name when object is creatable", function () { | ||||
|                 expect(controller.allowEdit()).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             it("disallows editting name when object is non-creatable", function () { | ||||
|                 mockTypeCapability.hasFeature.andReturn(false); | ||||
|  | ||||
|                 expect(controller.allowEdit()).toBe(false); | ||||
|  | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -20,7 +20,7 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="abs top-bar"> | ||||
|     <div class="title">{{ngModel.title}}</div> | ||||
|     <div class="dialog-title">{{ngModel.title}}</div> | ||||
|     <div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div> | ||||
| </div> | ||||
| <div class='abs editor'> | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| <div class="l-message" | ||||
|      ng-class="'message-severity-' + ngModel.severity"> | ||||
|     <div class="ui-symbol type-icon message-type"></div> | ||||
|     <div class="message-contents"> | ||||
|     <div class="w-message-contents"> | ||||
|         <div class="top-bar"> | ||||
|             <div class="title">{{ngModel.title}}</div> | ||||
|             <div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div> | ||||
|         </div> | ||||
|         <div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div> | ||||
|         <div class="message-body"> | ||||
|             <div class="message-action"> | ||||
|                 {{ngModel.actionText}} | ||||
| @@ -25,8 +24,6 @@ | ||||
|                ng-click="ngModel.primaryOption.callback()"> | ||||
|                 {{ngModel.primaryOption.label}} | ||||
|             </a> | ||||
|  | ||||
|         </div> | ||||
|  | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| <mct-container key="overlay" class="t-message-list"> | ||||
|     <div class="message-contents"> | ||||
|         <div class="abs top-bar"> | ||||
|             <div class="title">{{ngModel.dialog.title}}</div> | ||||
| <mct-container key="overlay"> | ||||
|     <div class="t-message-list"> | ||||
|         <div class="top-bar"> | ||||
|             <div class="dialog-title">{{ngModel.dialog.title}}</div> | ||||
|             <div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 || | ||||
|                                                                                                   ngModel.dialog.messages.length == 0">s</span> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="abs message-body"> | ||||
|         <div class="w-messages"> | ||||
|             <mct-include | ||||
|                     ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'" | ||||
|                     key="'message'" ng-model="msg.model"></mct-include> | ||||
|                 ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'" | ||||
|                 key="'message'" ng-model="msg.model"></mct-include> | ||||
|         </div> | ||||
|         <div class="abs bottom-bar"> | ||||
|         <div class="bottom-bar"> | ||||
|             <a ng-repeat="dialogAction in ngModel.dialog.actions" | ||||
|                class="s-button major" | ||||
|                ng-click="dialogAction.action()"> | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
| --> | ||||
| <mct-container key="overlay"> | ||||
|     <div class="abs top-bar"> | ||||
|         <div class="title">{{ngModel.dialog.title}}</div> | ||||
|         <div class="dialog-title">{{ngModel.dialog.title}}</div> | ||||
|         <div class="hint">{{ngModel.dialog.hint}}</div> | ||||
|     </div> | ||||
|     <div class='abs editor'> | ||||
|   | ||||
| @@ -121,7 +121,8 @@ define([ | ||||
|                     "key": "ElementsController", | ||||
|                     "implementation": ElementsController, | ||||
|                     "depends": [ | ||||
|                         "$scope" | ||||
|                         "$scope", | ||||
|                         "openmct" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
| @@ -299,9 +300,6 @@ define([ | ||||
|                 { | ||||
|                     "key": "edit-elements", | ||||
|                     "template": elementsTemplate, | ||||
|                     "uses": [ | ||||
|                         "composition" | ||||
|                     ], | ||||
|                     "gestures": [ | ||||
|                         "drop" | ||||
|                     ] | ||||
| @@ -385,7 +383,10 @@ define([ | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "implementation": EditToolbarRepresenter | ||||
|                     "implementation": EditToolbarRepresenter, | ||||
|                     "depends": [ | ||||
|                         "openmct" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|             "constants": [ | ||||
|   | ||||
| @@ -23,7 +23,7 @@ | ||||
|     <div class="s-menu-button major create-button" ng-click="createController.toggle()"> | ||||
| 		<span class="title-label">Create</span> | ||||
|     </div> | ||||
|     <div class="menu super-menu" ng-show="createController.isActive()"> | ||||
|     <div class="menu super-menu l-create-menu" ng-show="createController.isActive()"> | ||||
|         <mct-representation mct-object="domainObject" key="'create-menu'"> | ||||
|         </mct-representation> | ||||
|     </div> | ||||
|   | ||||
| @@ -19,8 +19,8 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="contents" ng-controller="CreateMenuController"> | ||||
|     <div class="pane left menu-items"> | ||||
| <div class="w-menu" ng-controller="CreateMenuController"> | ||||
|     <div class="col menu-items"> | ||||
|         <ul> | ||||
|             <li ng-repeat="createAction in createActions" ng-click="createAction.perform()"> | ||||
|                 <a ng-mouseover="representation.activeMetadata = createAction.getMetadata()" | ||||
| @@ -31,13 +31,15 @@ | ||||
|             </li> | ||||
|         </ul> | ||||
|     </div> | ||||
|     <div class="pane right menu-item-description"> | ||||
|     <div class="col menu-item-description"> | ||||
|         <div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div> | ||||
|         <div class="desc-area title"> | ||||
|             {{representation.activeMetadata.name}} | ||||
|         </div> | ||||
|         <div class="desc-area description"> | ||||
|             {{representation.activeMetadata.description}} | ||||
|         <div class="w-title-desc"> | ||||
|             <div class="desc-area title"> | ||||
|                 {{representation.activeMetadata.name}} | ||||
|             </div> | ||||
|             <div class="desc-area description"> | ||||
|                 {{representation.activeMetadata.description}} | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -61,7 +61,12 @@ | ||||
|             <mct-representation key="representation.selected.key" | ||||
|                                 mct-object="representation.selected.key && domainObject" | ||||
|                                 class="abs flex-elem grows object-holder-main scroll" | ||||
|                                 toolbar="toolbar"> | ||||
|                                 toolbar="toolbar" | ||||
|                                 mct-selectable="{ | ||||
|                                     item: domainObject.useCapability('adapter'), | ||||
|                                     oldItem: domainObject | ||||
|                                 }" | ||||
|                                 mct-init-select> | ||||
|             </mct-representation> | ||||
|         </div><!--/ l-object-wrapper-inner --> | ||||
|     </div> | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|                  ng-model="filterBy"> | ||||
|     </mct-include> | ||||
|     <div class="flex-elem grows vscroll"> | ||||
|         <ul class="tree"> | ||||
|         <ul class="tree" ng-if="composition.length > 0"> | ||||
|             <li ng-repeat="containedObject in composition | filter:searchElements"> | ||||
|                 <span class="tree-item"> | ||||
|                     <mct-representation | ||||
| @@ -36,5 +36,6 @@ | ||||
|                 </span> | ||||
|             </li> | ||||
|         </ul> | ||||
|         <div ng-if="composition.length === 0">No contained elements</div>     | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -101,10 +101,15 @@ define( | ||||
|          */ | ||||
|         EditorCapability.prototype.finish = function () { | ||||
|             var domainObject = this.domainObject; | ||||
|             return this.transactionService.cancel().then(function () { | ||||
|                 domainObject.getCapability("status").set("editing", false); | ||||
|                 return domainObject; | ||||
|             }); | ||||
|  | ||||
|             if (this.transactionService.isActive()) { | ||||
|                 return this.transactionService.cancel().then(function () { | ||||
|                     domainObject.getCapability("status").set("editing", false); | ||||
|                     return domainObject; | ||||
|                 }); | ||||
|             } else { | ||||
|                 return Promise.resolve(domainObject); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -29,7 +29,11 @@ define( | ||||
|          * | ||||
|          * @constructor | ||||
|          */ | ||||
|         function ElementsController($scope) { | ||||
|         function ElementsController($scope, openmct) { | ||||
|             this.scope = $scope; | ||||
|             this.scope.composition = []; | ||||
|             var self = this; | ||||
|  | ||||
|             function filterBy(text) { | ||||
|                 if (typeof text === 'undefined') { | ||||
|                     return $scope.searchText; | ||||
| @@ -47,10 +51,44 @@ define( | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             function setSelection(selection) { | ||||
|                 self.scope.selection = selection; | ||||
|                 self.refreshComposition(selection); | ||||
|             } | ||||
|  | ||||
|             $scope.filterBy = filterBy; | ||||
|             $scope.searchElements = searchElements; | ||||
|  | ||||
|             openmct.selection.on('change', setSelection); | ||||
|             setSelection(openmct.selection.get()); | ||||
|  | ||||
|             $scope.$on("$destroy", function () { | ||||
|                 openmct.selection.off("change", setSelection); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Gets the composition for the selected object and populates the scope with it. | ||||
|          * | ||||
|          * @param selection the selection object | ||||
|          * @private | ||||
|          */ | ||||
|         ElementsController.prototype.refreshComposition = function (selection) { | ||||
|             if (!selection[0]) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             var selectedObjectComposition = selection[0].context.oldItem.useCapability('composition'); | ||||
|  | ||||
|             if (selectedObjectComposition) { | ||||
|                 selectedObjectComposition.then(function (composition) { | ||||
|                     this.scope.composition = composition; | ||||
|                 }.bind(this)); | ||||
|             } else { | ||||
|                 this.scope.composition = []; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         return ElementsController; | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -38,7 +38,7 @@ define( | ||||
|          * @constructor | ||||
|          * @implements {Representer} | ||||
|          */ | ||||
|         function EditToolbarRepresenter(scope, element, attrs) { | ||||
|         function EditToolbarRepresenter(openmct, scope, element, attrs) { | ||||
|             var self = this; | ||||
|  | ||||
|             // Mark changes as ready to persist | ||||
| @@ -109,6 +109,7 @@ define( | ||||
|             this.updateSelection = updateSelection; | ||||
|             this.toolbar = undefined; | ||||
|             this.toolbarObject = {}; | ||||
|             this.openmct = openmct; | ||||
|  | ||||
|             // If this representation exposes a toolbar, set up watches | ||||
|             // to synchronize with it. | ||||
| @@ -146,7 +147,7 @@ define( | ||||
|             // Expose the toolbar object to the parent scope | ||||
|             initialize(definition); | ||||
|             // Create a selection scope | ||||
|             this.setSelection(new EditToolbarSelection()); | ||||
|             this.setSelection(new EditToolbarSelection(this.openmct)); | ||||
|             // Initialize toolbar to an empty selection | ||||
|             this.updateSelection([]); | ||||
|         }; | ||||
|   | ||||
| @@ -38,10 +38,18 @@ define( | ||||
|          * @memberof platform/commonUI/edit | ||||
|          * @constructor | ||||
|          */ | ||||
|         function EditToolbarSelection() { | ||||
|         function EditToolbarSelection(openmct) { | ||||
|             this.selection = [{}]; | ||||
|             this.selecting = false; | ||||
|             this.selectedObj = undefined; | ||||
|  | ||||
|             openmct.selection.on('change', function (selection) { | ||||
|                 if (selection[0] && selection[0].context.toolbar) { | ||||
|                     this.select(selection[0].context.toolbar); | ||||
|                 } else { | ||||
|                     this.deselect(); | ||||
|                 } | ||||
|             }.bind(this)); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -62,6 +62,7 @@ define( | ||||
|                 ); | ||||
|                 mockTransactionService.commit.andReturn(fastPromise()); | ||||
|                 mockTransactionService.cancel.andReturn(fastPromise()); | ||||
|                 mockTransactionService.isActive = jasmine.createSpy('isActive'); | ||||
|  | ||||
|                 mockStatusCapability = jasmine.createSpyObj( | ||||
|                     "statusCapability", | ||||
| @@ -141,6 +142,7 @@ define( | ||||
|  | ||||
|             describe("finish", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockTransactionService.isActive.andReturn(true); | ||||
|                     capability.edit(); | ||||
|                     capability.finish(); | ||||
|                 }); | ||||
| @@ -152,6 +154,23 @@ define( | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("finish", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockTransactionService.isActive.andReturn(false); | ||||
|                     capability.edit(); | ||||
|                 }); | ||||
|  | ||||
|                 it("does not cancel transaction when transaction is not active", function () { | ||||
|                     capability.finish(); | ||||
|                     expect(mockTransactionService.cancel).not.toHaveBeenCalled(); | ||||
|                 }); | ||||
|  | ||||
|                 it("returns a promise", function () { | ||||
|                     expect(capability.finish() instanceof Promise).toBe(true); | ||||
|                 }); | ||||
|  | ||||
|             }); | ||||
|  | ||||
|             describe("dirty", function () { | ||||
|                 var model = {}; | ||||
|  | ||||
|   | ||||
| @@ -27,11 +27,23 @@ define( | ||||
|  | ||||
|         describe("The Elements Pane controller", function () { | ||||
|             var mockScope, | ||||
|                 mockOpenMCT, | ||||
|                 mockSelection, | ||||
|                 controller; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpy("$scope"); | ||||
|                 controller = new ElementsController(mockScope); | ||||
|                 mockScope = jasmine.createSpyObj("$scope", ['$on']); | ||||
|                 mockSelection = jasmine.createSpyObj("selection", [ | ||||
|                     'on', | ||||
|                     'off', | ||||
|                     'get' | ||||
|                 ]); | ||||
|                 mockSelection.get.andReturn([]); | ||||
|                 mockOpenMCT = { | ||||
|                     selection: mockSelection | ||||
|                 }; | ||||
|  | ||||
|                 controller = new ElementsController(mockScope, mockOpenMCT); | ||||
|             }); | ||||
|  | ||||
|             function getModel(model) { | ||||
|   | ||||
| @@ -29,7 +29,9 @@ define( | ||||
|                 mockElement, | ||||
|                 testAttrs, | ||||
|                 mockUnwatch, | ||||
|                 representer; | ||||
|                 representer, | ||||
|                 mockOpenMCT, | ||||
|                 mockSelection; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpyObj( | ||||
| @@ -46,7 +48,18 @@ define( | ||||
|  | ||||
|                 mockScope.$parent.$watchCollection.andReturn(mockUnwatch); | ||||
|  | ||||
|                 mockSelection = jasmine.createSpyObj("selection", [ | ||||
|                     'on', | ||||
|                     'off', | ||||
|                     'get' | ||||
|                 ]); | ||||
|                 mockSelection.get.andReturn([]); | ||||
|                 mockOpenMCT = { | ||||
|                     selection: mockSelection | ||||
|                 }; | ||||
|  | ||||
|                 representer = new EditToolbarRepresenter( | ||||
|                     mockOpenMCT, | ||||
|                     mockScope, | ||||
|                     mockElement, | ||||
|                     testAttrs | ||||
|   | ||||
| @@ -28,13 +28,25 @@ define( | ||||
|             var testProxy, | ||||
|                 testElement, | ||||
|                 otherElement, | ||||
|                 selection; | ||||
|                 selection, | ||||
|                 mockSelection, | ||||
|                 mockOpenMCT; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 testProxy = { someKey: "some value" }; | ||||
|                 testElement = { someOtherKey: "some other value" }; | ||||
|                 otherElement = { yetAnotherKey: 42 }; | ||||
|                 selection = new EditToolbarSelection(); | ||||
|                 mockSelection = jasmine.createSpyObj("selection", [ | ||||
|                     // 'select', | ||||
|                     'on', | ||||
|                     'off', | ||||
|                     'get' | ||||
|                 ]); | ||||
|                 mockSelection.get.andReturn([]); | ||||
|                 mockOpenMCT = { | ||||
|                     selection: mockSelection | ||||
|                 }; | ||||
|                 selection = new EditToolbarSelection(mockOpenMCT); | ||||
|                 selection.proxy(testProxy); | ||||
|             }); | ||||
|  | ||||
|   | ||||
| @@ -121,6 +121,9 @@ define([ | ||||
|     }; | ||||
|  | ||||
|     UTCTimeFormat.prototype.parse = function (text) { | ||||
|         if (typeof text === 'number') { | ||||
|             return text; | ||||
|         } | ||||
|         return moment.utc(text, DATE_FORMATS).valueOf(); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -41,6 +41,7 @@ define([ | ||||
|     "./src/controllers/BannerController", | ||||
|     "./src/directives/MCTContainer", | ||||
|     "./src/directives/MCTDrag", | ||||
|     "./src/directives/MCTSelectable", | ||||
|     "./src/directives/MCTClickElsewhere", | ||||
|     "./src/directives/MCTResize", | ||||
|     "./src/directives/MCTPopup", | ||||
| @@ -90,6 +91,7 @@ define([ | ||||
|     BannerController, | ||||
|     MCTContainer, | ||||
|     MCTDrag, | ||||
|     MCTSelectable, | ||||
|     MCTClickElsewhere, | ||||
|     MCTResize, | ||||
|     MCTPopup, | ||||
| @@ -328,6 +330,13 @@ define([ | ||||
|                         "$document" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "mctSelectable", | ||||
|                     "implementation": MCTSelectable, | ||||
|                     "depends": [ | ||||
|                         "openmct" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "mctClickElsewhere", | ||||
|                     "implementation": MCTClickElsewhere, | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "metadata": { | ||||
|     "name": "openmct-symbols-16px", | ||||
|     "lastOpened": 0, | ||||
|     "created": 1502487054429 | ||||
|     "created": 1506973656040 | ||||
|   }, | ||||
|   "iconSets": [ | ||||
|     { | ||||
| @@ -636,13 +636,29 @@ | ||||
|           "code": 921670, | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 138, | ||||
|           "id": 115, | ||||
|           "name": "icon-import", | ||||
|           "prevSize": 24, | ||||
|           "code": 921671, | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 136, | ||||
|           "id": 116, | ||||
|           "name": "icon-export", | ||||
|           "prevSize": 24, | ||||
|           "code": 921672, | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 37, | ||||
|           "prevSize": 24, | ||||
|           "name": "icon-activity", | ||||
|           "id": 32, | ||||
|           "code": 921856, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 36, | ||||
| @@ -650,7 +666,7 @@ | ||||
|           "name": "icon-activity-mode", | ||||
|           "id": 31, | ||||
|           "code": 921857, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 52, | ||||
| @@ -658,7 +674,7 @@ | ||||
|           "name": "icon-autoflow-tabular", | ||||
|           "id": 47, | ||||
|           "code": 921858, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 55, | ||||
| @@ -666,7 +682,7 @@ | ||||
|           "name": "icon-clock", | ||||
|           "id": 50, | ||||
|           "code": 921859, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 58, | ||||
| @@ -674,7 +690,7 @@ | ||||
|           "name": "icon-database", | ||||
|           "id": 53, | ||||
|           "code": 921860, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 57, | ||||
| @@ -682,7 +698,7 @@ | ||||
|           "name": "icon-database-query", | ||||
|           "id": 52, | ||||
|           "code": 921861, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 17, | ||||
| @@ -690,7 +706,7 @@ | ||||
|           "name": "icon-dataset", | ||||
|           "id": 12, | ||||
|           "code": 921862, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 22, | ||||
| @@ -698,7 +714,7 @@ | ||||
|           "name": "icon-datatable", | ||||
|           "id": 17, | ||||
|           "code": 921863, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 59, | ||||
| @@ -706,7 +722,7 @@ | ||||
|           "name": "icon-dictionary", | ||||
|           "id": 54, | ||||
|           "code": 921864, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 62, | ||||
| @@ -714,7 +730,7 @@ | ||||
|           "name": "icon-folder", | ||||
|           "id": 57, | ||||
|           "code": 921865, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 66, | ||||
| @@ -722,7 +738,7 @@ | ||||
|           "name": "icon-image", | ||||
|           "id": 61, | ||||
|           "code": 921872, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 68, | ||||
| @@ -730,7 +746,7 @@ | ||||
|           "name": "icon-layout", | ||||
|           "id": 63, | ||||
|           "code": 921873, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 77, | ||||
| @@ -738,7 +754,7 @@ | ||||
|           "name": "icon-object", | ||||
|           "id": 72, | ||||
|           "code": 921874, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 78, | ||||
| @@ -746,7 +762,7 @@ | ||||
|           "name": "icon-object-unknown", | ||||
|           "id": 73, | ||||
|           "code": 921875, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 79, | ||||
| @@ -754,7 +770,7 @@ | ||||
|           "name": "icon-packet", | ||||
|           "id": 74, | ||||
|           "code": 921876, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 80, | ||||
| @@ -762,7 +778,7 @@ | ||||
|           "name": "icon-page", | ||||
|           "id": 75, | ||||
|           "code": 921877, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 135, | ||||
| @@ -770,7 +786,7 @@ | ||||
|           "name": "icon-plot-overlay", | ||||
|           "prevSize": 24, | ||||
|           "code": 921878, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 113, | ||||
| @@ -778,7 +794,7 @@ | ||||
|           "name": "icon-plot-stacked", | ||||
|           "prevSize": 24, | ||||
|           "code": 921879, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 10, | ||||
| @@ -786,7 +802,7 @@ | ||||
|           "name": "icon-session", | ||||
|           "id": 5, | ||||
|           "code": 921880, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 24, | ||||
| @@ -794,7 +810,7 @@ | ||||
|           "name": "icon-tabular", | ||||
|           "id": 19, | ||||
|           "code": 921881, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 7, | ||||
| @@ -802,7 +818,7 @@ | ||||
|           "name": "icon-tabular-lad", | ||||
|           "id": 2, | ||||
|           "code": 921888, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 6, | ||||
| @@ -810,7 +826,7 @@ | ||||
|           "name": "icon-tabular-lad-set", | ||||
|           "id": 1, | ||||
|           "code": 921889, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 8, | ||||
| @@ -818,7 +834,7 @@ | ||||
|           "name": "icon-tabular-realtime", | ||||
|           "id": 3, | ||||
|           "code": 921890, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 23, | ||||
| @@ -826,7 +842,7 @@ | ||||
|           "name": "icon-tabular-scrolling", | ||||
|           "id": 18, | ||||
|           "code": 921891, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 112, | ||||
| @@ -834,7 +850,7 @@ | ||||
|           "name": "icon-telemetry", | ||||
|           "id": 86, | ||||
|           "code": 921892, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 90, | ||||
| @@ -842,7 +858,7 @@ | ||||
|           "name": "icon-telemetry-panel", | ||||
|           "id": 85, | ||||
|           "code": 921893, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 93, | ||||
| @@ -850,7 +866,7 @@ | ||||
|           "name": "icon-timeline", | ||||
|           "id": 88, | ||||
|           "code": 921894, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 116, | ||||
| @@ -858,7 +874,7 @@ | ||||
|           "name": "icon-timer-v1.5", | ||||
|           "prevSize": 24, | ||||
|           "code": 921895, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 11, | ||||
| @@ -866,7 +882,7 @@ | ||||
|           "name": "icon-topic", | ||||
|           "id": 6, | ||||
|           "code": 921896, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 115, | ||||
| @@ -874,7 +890,7 @@ | ||||
|           "name": "icon-box-with-dashed-lines", | ||||
|           "id": 29, | ||||
|           "code": 921897, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 126, | ||||
| @@ -882,7 +898,15 @@ | ||||
|           "name": "icon-summary-widget", | ||||
|           "prevSize": 24, | ||||
|           "code": 921904, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 139, | ||||
|           "id": 117, | ||||
|           "name": "icon-notebook", | ||||
|           "prevSize": 24, | ||||
|           "code": 921905, | ||||
|           "tempChar": "" | ||||
|         } | ||||
|       ], | ||||
|       "metadata": { | ||||
| @@ -2683,6 +2707,52 @@ | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": 115, | ||||
|           "paths": [ | ||||
|             "M832 192.4v639.4c0 0.2-0.2 0.2-0.4 0.4h-319.6v192h320c105.6 0 192-86.4 192-192v-640.2c0-105.6-86.4-192-192-192h-320v192h319.6c0.2 0 0.4 0.2 0.4 0.4z", | ||||
|             "M192 704v192l384-384-384-384v192h-192v384z" | ||||
|           ], | ||||
|           "attrs": [ | ||||
|             {}, | ||||
|             {} | ||||
|           ], | ||||
|           "grid": 16, | ||||
|           "tags": [ | ||||
|             "icon-import" | ||||
|           ], | ||||
|           "isMulticolor": false, | ||||
|           "isMulticolor2": false, | ||||
|           "colorPermutations": { | ||||
|             "1161751207457516161751": [ | ||||
|               {}, | ||||
|               {} | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": 116, | ||||
|           "paths": [ | ||||
|             "M192 831.66v-639.32l0.34-0.34h319.66v-192h-320c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h320v-192h-319.66z", | ||||
|             "M1024 512l-384-384v192h-192v384h192v192l384-384z" | ||||
|           ], | ||||
|           "attrs": [ | ||||
|             {}, | ||||
|             {} | ||||
|           ], | ||||
|           "isMulticolor": false, | ||||
|           "isMulticolor2": false, | ||||
|           "grid": 16, | ||||
|           "tags": [ | ||||
|             "icon-export" | ||||
|           ], | ||||
|           "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" | ||||
| @@ -3462,6 +3532,29 @@ | ||||
|               {} | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": 117, | ||||
|           "paths": [ | ||||
|             "M896 110.8c0-79.8-55.4-127.4-123-105.4l-773 250.6h896v-145.2z", | ||||
|             "M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM832 832h-384v-320h384v320z" | ||||
|           ], | ||||
|           "attrs": [ | ||||
|             {}, | ||||
|             {} | ||||
|           ], | ||||
|           "isMulticolor": false, | ||||
|           "isMulticolor2": false, | ||||
|           "grid": 16, | ||||
|           "tags": [ | ||||
|             "icon-notebook" | ||||
|           ], | ||||
|           "colorPermutations": { | ||||
|             "1161751207457516161751": [ | ||||
|               {}, | ||||
|               {} | ||||
|             ] | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       "colorThemes": [ | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -85,6 +85,8 @@ | ||||
| <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-import" d="M832 767.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 256v-192l384 384-384 384v-192h-192v-384z" /> | ||||
| <glyph unicode="󡁈" glyph-name="icon-export" d="M192 128.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 448l-384 384v-192h-192v-384h192v-192l384 384z" /> | ||||
| <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" /> | ||||
| @@ -116,4 +118,5 @@ | ||||
| <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" /> | ||||
| <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" /> | ||||
| <glyph unicode="󡄱" glyph-name="icon-notebook" d="M896 849.2c0 79.8-55.4 127.4-123 105.4l-773-250.6h896v145.2zM896 640h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM832 128h-384v320h384v-320z" /> | ||||
| </font></defs></svg> | ||||
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -21,7 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /********************************************* COLUMN LAYOUTS STYLES */ | ||||
| /*@mixin cols($totalCols, $span) { | ||||
| @mixin cols($totalCols, $span) { | ||||
|     $cw: 100% / $totalCols; | ||||
|     min-width: (500px / $totalCols) * $span; | ||||
|     @if ($totalCols != $span) { | ||||
| @@ -89,7 +89,7 @@ | ||||
|         @include clearfix; | ||||
|         padding: $interiorMargin 0; | ||||
|     } | ||||
| }*/ | ||||
| } | ||||
|  | ||||
| /********************************************* FLEX STYLES */ | ||||
| .l-flex-row, | ||||
| @@ -137,6 +137,11 @@ | ||||
|         min-height: 0; | ||||
|         &.holder:not(:last-child) { margin-bottom: $interiorMarginLg; } | ||||
|     } | ||||
|     &.l-flex-accordion .flex-accordion-holder { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         //overflow: hidden !important; | ||||
|     } | ||||
|     .flex-container { @include flex-direction(column); } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /************************** FEATURES */ | ||||
| $enableImageryThumbs: false; // Set to true if historical imagery thumbnails are supported | ||||
| $enableImageryThumbs: true; // Set to true if historical imagery thumbnails are supported | ||||
|  | ||||
| /************************** VERY INFLUENTIAL GLOBAL DIMENSIONS */ | ||||
| $bodyMargin: 10px; | ||||
| @@ -82,7 +82,7 @@ $tabularTdPadTB: 2px; | ||||
| /*************** Imagery */ | ||||
| $imageMainControlBarH: 25px; | ||||
| $imageThumbsD: 120px; | ||||
| $imageThumbsWrapperH: $imageThumbsD * 1.4; | ||||
| $imageThumbsWrapperH: 155px; | ||||
| $imageThumbPad: 1px; | ||||
| /*************** Ticks */ | ||||
| $ticksH: 25px; | ||||
| @@ -111,7 +111,9 @@ $bubbleMaxW: 300px; | ||||
| $reqSymbolW: 15px; | ||||
| $reqSymbolM: $interiorMargin * 2; | ||||
| $reqSymbolFontSize: 0.75em; | ||||
| $inputTextP: 3px 5px; | ||||
| $inputTextPTopBtm: 3px; | ||||
| $inputTextPLeftRight: 5px; | ||||
| $inputTextP: $inputTextPTopBtm $inputTextPLeftRight; | ||||
| /*************** Wait Spinner Defaults */ | ||||
| $waitSpinnerD: 32px; | ||||
| $waitSpinnerTreeD: 20px; | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| 	} | ||||
|  | ||||
| 	.l-fixed-position-item { | ||||
|         border-width: 1px; | ||||
| 		position: absolute; | ||||
| 		&.s-not-selected { | ||||
| 			opacity: 0.8; | ||||
| @@ -52,6 +53,7 @@ | ||||
| 			font-size: 0.8rem; | ||||
| 			$p: 1px; | ||||
| 			line-height: 100%; | ||||
|             overflow: hidden; | ||||
| 			&.l-static-text { | ||||
| 				padding: $p; | ||||
| 			} | ||||
|   | ||||
| @@ -1,3 +1,24 @@ | ||||
| /***************************************************************************** | ||||
|  * 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. | ||||
|  *****************************************************************************/ | ||||
| @mixin glyphBefore($unicode, $family: 'symbolsfont') { | ||||
|     &:before { | ||||
|         content: $unicode; | ||||
| @@ -92,6 +113,8 @@ $glyph-icon-grid-snap-to: '\e1043'; | ||||
| $glyph-icon-grid-snap-no: '\e1044'; | ||||
| $glyph-icon-frame-show: '\e1045'; | ||||
| $glyph-icon-frame-hide: '\e1046'; | ||||
| $glyph-icon-import: '\e1047'; | ||||
| $glyph-icon-export: '\e1048'; | ||||
| $glyph-icon-activity: '\e1100'; | ||||
| $glyph-icon-activity-mode: '\e1101'; | ||||
| $glyph-icon-autoflow-tabular: '\e1102'; | ||||
| @@ -123,6 +146,7 @@ $glyph-icon-timer: '\e1127'; | ||||
| $glyph-icon-topic: '\e1128'; | ||||
| $glyph-icon-box-with-dashed-lines: '\e1129'; | ||||
| $glyph-icon-summary-widget: '\e1130'; | ||||
| $glyph-icon-notebook: '\e1131'; | ||||
|  | ||||
| /************************** 16 PX CLASSES */ | ||||
|  | ||||
| @@ -204,6 +228,8 @@ $glyph-icon-summary-widget: '\e1130'; | ||||
| .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-import {  @include glyphBefore($glyph-icon-import); } | ||||
| .icon-export {  @include glyphBefore($glyph-icon-export); } | ||||
| .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); } | ||||
| @@ -235,6 +261,7 @@ $glyph-icon-summary-widget: '\e1130'; | ||||
| .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); } | ||||
| .icon-notebook {  @include glyphBefore($glyph-icon-notebook); } | ||||
|  | ||||
| /************************** 12 PX CLASSES */ | ||||
| .icon-crosshair-12px {  @include glyphBefore($glyph-icon-crosshair,'symbolsfont-12px'); } | ||||
|   | ||||
| @@ -26,5 +26,6 @@ | ||||
|         display: block; | ||||
|         height: 100%; | ||||
|         width: 100%; | ||||
|         border: none; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,39 +0,0 @@ | ||||
| @mixin limitGlyph($iconColor, $glyph: $glyph-icon-alert-triangle) { | ||||
|     &:before { | ||||
|         color: $iconColor; | ||||
|         content: $glyph; | ||||
|         font-family: symbolsfont; | ||||
|         font-size: 0.8em; | ||||
|         display: inline; | ||||
|         margin-right: $interiorMarginSm; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| .s-limit-red { background: $colorLimitRedBg !important; } | ||||
| .s-limit-yellow { background: $colorLimitYellowBg !important; } | ||||
|  | ||||
| // Handle limit when applied to a tr | ||||
| tr[class*="s-limit"] { | ||||
|     &.s-limit-red td:first-child { | ||||
|         @include limitGlyph($colorLimitRedIc); | ||||
|     } | ||||
|     &.s-limit-yellow td:first-child { | ||||
|         @include limitGlyph($colorLimitYellowIc); | ||||
|     } | ||||
|     &.s-limit-upr td:first-child:before { content: $glyph-icon-arrow-double-up; } | ||||
|     &.s-limit-lwr td:first-child:before { content: $glyph-icon-arrow-double-down; } | ||||
| } | ||||
|  | ||||
| // Handle limit when applied directly to a non-tr element | ||||
| // Assume this is applied to the element that displays the limit value | ||||
| :not(tr)[class*="s-limit"] { | ||||
|     &.s-limit-red { | ||||
|         @include limitGlyph($colorLimitRedIc); | ||||
|     } | ||||
|     &.s-limit-yellow { | ||||
|         @include limitGlyph($colorLimitYellowIc); | ||||
|     } | ||||
|     &.s-limit-upr:before { content: $glyph-icon-arrow-double-up; } | ||||
|     &.s-limit-lwr:before { content: $glyph-icon-arrow-double-down; } | ||||
| } | ||||
| @@ -27,7 +27,7 @@ | ||||
| @import "about"; | ||||
| @import "text"; | ||||
| @import "icons"; | ||||
| @import "limits"; | ||||
| @import "status"; | ||||
| @import "data-status"; | ||||
| @import "helpers/bubbles"; | ||||
| @import "helpers/splitter"; | ||||
|   | ||||
| @@ -20,8 +20,8 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| @mixin absPosDefault($offset: 0px, $overflow: hidden) { | ||||
|     overflow: $overflow; | ||||
| @mixin absPosDefault($offset: 0px, $overflowHidden: hidden) { | ||||
|     overflow: $overflowHidden; | ||||
|     position: absolute; | ||||
|     top: $offset; | ||||
|     right: $offset; | ||||
| @@ -316,23 +316,28 @@ | ||||
|     text-shadow: $shdwItemText; | ||||
| } | ||||
|  | ||||
| @mixin input-base($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) { | ||||
| @mixin input-base() { | ||||
| 	@include appearance(none); | ||||
| 	border-radius: $controlCr; | ||||
| 	box-sizing: border-box; | ||||
| 	box-shadow: inset $shdw; | ||||
| 	background: $bg; | ||||
| 	border: none; | ||||
| 	color: $fg; | ||||
| 	outline: none; | ||||
|     &:focus { outline: 0; } | ||||
| 	&.error { | ||||
| 		background-color: $colorFormFieldErrorBg; | ||||
|         color: $colorFormFieldErrorFg; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg) { | ||||
| 	@include input-base($bg, $fg); | ||||
| @mixin s-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) { | ||||
|     @include input-base(); | ||||
|     background: $bg; | ||||
|     box-shadow: inset $shdw; | ||||
|     color: $fg; | ||||
| } | ||||
|  | ||||
| @mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) { | ||||
|     @include s-input($bg, $fg, $shdw); | ||||
|     border: none; | ||||
|     outline: none; | ||||
| } | ||||
|  | ||||
| @mixin contextArrow() { | ||||
| @@ -344,7 +349,7 @@ | ||||
| } | ||||
|  | ||||
| @mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) { | ||||
|     @include input-base($bg, $fg); | ||||
|     @include nice-input($bg, $fg); | ||||
|     padding: $interiorMargin; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										85
									
								
								platform/commonUI/general/res/sass/_status.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								platform/commonUI/general/res/sass/_status.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| /***************************************************************************** | ||||
|  * 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. | ||||
|  *****************************************************************************/ | ||||
| /*************************************************** MIXINS */ | ||||
| @mixin formulateStatusColors($c) { | ||||
|     // Sets bg and icon colors for elements | ||||
|     background: rgba($c, 0.4) !important; | ||||
|     &:before { color: $c !important; } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /*************************************************** GENERAL */ | ||||
| .s-limit-yellow, | ||||
| .s-limit-red, | ||||
| .s-limit-yellow-icon, | ||||
| .s-limit-red-icon, | ||||
| .s-status-warning-lo, | ||||
| .s-status-warning-hi, | ||||
| .s-status-diagnostic, | ||||
| .s-status-command, | ||||
| .s-status-info, | ||||
| .s-status-ok, | ||||
| .s-status-warning-lo-icon, | ||||
| .s-status-warning-hi-icon, | ||||
| .s-status-diagnostic-icon, | ||||
| .s-status-command-icon, | ||||
| .s-status-info-icon, | ||||
| .s-status-ok-icon { | ||||
|     @include trans-prop-nice($props: background, $dur: 500ms); | ||||
|     &:before { | ||||
|         content:''; | ||||
|         font-family: symbolsfont; | ||||
|         font-size: 0.8em; | ||||
|         margin-right: $interiorMarginSm; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /*************************************************** LIMITS */ | ||||
| .s-limit-yellow, .s-limit-yellow-icon { | ||||
|     @include formulateStatusColors($colorWarningLo); | ||||
| } | ||||
|  | ||||
| .s-limit-red, .s-limit-red-icon { | ||||
|     @include formulateStatusColors($colorWarningHi); | ||||
| } | ||||
|  | ||||
| .s-limit-upr:before { content: $glyph-icon-arrow-double-up; } | ||||
| .s-limit-lwr:before { content: $glyph-icon-arrow-double-down; } | ||||
| .s-limit-yellow-icon:before, | ||||
| .s-limit-red-icon:before { content: $glyph-icon-alert-triangle; } | ||||
|  | ||||
| /*************************************************** STATUS */ | ||||
| .s-status-warning-hi, .s-status-warning-hi-icon {  @include formulateStatusColors($colorWarningHi); } | ||||
| .s-status-warning-lo, .s-status-warning-lo-icon {  @include formulateStatusColors($colorWarningLo); } | ||||
| .s-status-diagnostic, .s-status-diagnostic-icon {  @include formulateStatusColors($colorDiagnostic); } | ||||
| .s-status-info, .s-status-info-icon {  @include formulateStatusColors($colorInfo); } | ||||
| .s-status-ok, .s-status-ok-icon {  @include formulateStatusColors($colorOk); } | ||||
|  | ||||
| .s-status-warning-hi-icon:before { content: $glyph-icon-alert-triangle; } | ||||
| .s-status-warning-lo-icon:before { content: $glyph-icon-alert-rect; } | ||||
| .s-status-diagnostic-icon:before { content: $glyph-icon-eye-open; } | ||||
| .s-status-info-icon:before { content: $glyph-icon-info; } | ||||
| .s-status-ok-icon:before { content: $glyph-icon-check; } | ||||
|  | ||||
|  | ||||
| @@ -26,22 +26,26 @@ | ||||
|     @include ellipsize(); | ||||
|     display: inline-block; | ||||
|     text-align: center; | ||||
|     .widget-label:before { | ||||
|         // Widget icon | ||||
|         font-size: 0.9em; | ||||
|         margin-right: $interiorMarginSm; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .s-summary-widget { | ||||
|     // Widget style classes here | ||||
|     @include boxShdw($shdwBtns); | ||||
|     border-radius: $basicCr; | ||||
|     border-style: solid; | ||||
|     border-width: 1px; | ||||
|     box-sizing: border-box; | ||||
|     cursor: default; | ||||
|     font-size: 0.8rem; | ||||
|     padding: $interiorMarginLg $interiorMarginLg * 2; | ||||
| } | ||||
|  | ||||
| .l-widget-outer-w, | ||||
| .widget-holder, | ||||
| .widget-edit-holder { | ||||
|     // In browse mode, all these things should be at .abs default | ||||
|     @extend .abs; | ||||
|     &[href] { | ||||
|         cursor: pointer; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .widget-edit-holder { | ||||
| @@ -60,19 +64,42 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| .widget-rule-content { | ||||
|     @include trans-prop-nice($props: (height, min-height), $dur: 250ms); | ||||
| .widget-rules-wrapper, | ||||
| .widget-rule-content, | ||||
| .w-widget-test-data-content { | ||||
|     @include trans-prop-nice($props: (height, min-height, opacity), $dur: 250ms); | ||||
|     min-height: 0; | ||||
|     height: 0; | ||||
|     opacity: 0; | ||||
|     overflow: hidden; | ||||
|     &.expanded { | ||||
|         min-height: 50px; | ||||
|         height: auto; | ||||
|         overflow: visible; | ||||
|     pointer-events: none; | ||||
| } | ||||
|  | ||||
| .widget-rules-wrapper { | ||||
|     flex: 1 1 auto !important; | ||||
| } | ||||
|  | ||||
| .widget-rule-content.expanded { | ||||
|     overflow: visible !important; | ||||
|     min-height: 50px; | ||||
|     height: auto; | ||||
|     opacity: 1; | ||||
|     pointer-events: inherit; | ||||
| } | ||||
|  | ||||
| .w-widget-test-data-content { | ||||
|     .l-enable { | ||||
|         padding: $interiorMargin 0; | ||||
|     } | ||||
|  | ||||
|     .w-widget-test-data-items { | ||||
|         max-height: 20vh; | ||||
|         overflow-y: scroll !important; | ||||
|         padding-right: $interiorMargin; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .l-widget-thumb-w, | ||||
| .l-widget-thumb-wrapper, | ||||
| .l-compact-form label { | ||||
|     $ruleLabelW: 40%; | ||||
|     $ruleLabelMaxW: 150px; | ||||
| @@ -81,133 +108,199 @@ | ||||
|     width: $ruleLabelW; | ||||
| } | ||||
|  | ||||
| /********************************************************** EDITING A WIDGET */ | ||||
| .s-status-editing .l-widget-outer-w { | ||||
|     $widgetHolderH: 100px; | ||||
|     @extend .abs; | ||||
|     //overflow: hidden; // Overflow scroll is handled by internal elements | ||||
| .t-message-widget-no-data { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
|     .widget-holder { | ||||
|         bottom: auto; | ||||
|         height: $widgetHolderH; | ||||
|         .l-summary-widget { | ||||
|             display: inline-block; | ||||
|             position: absolute; | ||||
|             top: 50%; left: 50%; | ||||
|             @include transform(translateX(-50%) translateY(-50%)); | ||||
|         } | ||||
| /********************************************************** EDITING A WIDGET */ | ||||
| .s-status-editing > mct-view > .w-summary-widget { | ||||
|     // Classes for editor layout while editing a widget | ||||
|     // This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout | ||||
|     // being edited. | ||||
|     @include absPosDefault(); | ||||
|     @extend .l-flex-col; | ||||
|  | ||||
|     > .l-summary-widget { | ||||
|         // Main view of the summary widget | ||||
|         // Give some airspace and center the widget in the area | ||||
|         margin: 30px auto; | ||||
|     } | ||||
|  | ||||
|     .widget-edit-holder { | ||||
|         @extend .l-flex-col; | ||||
|         display: block; // Needed to counteract display: none while browsing | ||||
|         overflow-y: scroll; | ||||
|         padding-right: $interiorMargin; | ||||
|         top: $widgetHolderH + $interiorMargin; | ||||
|     } | ||||
|  | ||||
|  | ||||
|      | ||||
|     .widget-test-data, | ||||
|     .widget-rules-w { | ||||
|         @include test(blue, 0.1); | ||||
|     } | ||||
|      | ||||
|     .widget-test-data { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .widget-rules-w { | ||||
|         // Wrapper area that holds n rules | ||||
|         @extend .flex-elem; | ||||
|         box-sizing: border-box; | ||||
|         font-size: 0.8em; | ||||
|     } | ||||
|  | ||||
|     .l-widget-rule { | ||||
|         box-sizing: border-box; | ||||
|         margin-bottom: $interiorMargin; | ||||
|         padding: $interiorMarginLg; | ||||
|     } | ||||
|  | ||||
|     .l-widget-thumb-w { | ||||
|         @extend .l-flex-row; | ||||
|         @include align-items(center); | ||||
|         > span { display: block; } | ||||
|         .grippy-holder, | ||||
|         .view-control { | ||||
|             margin-right: $interiorMargin; | ||||
|             width: 1em; | ||||
|             height: 1em; | ||||
|         display: flex; // Overrides `display: none` during Browse mode | ||||
|         .flex-accordion-holder { | ||||
|             // Needed because otherwise accordion elements "creep" when contents expand and contract | ||||
|             display: block !important; | ||||
|         } | ||||
|  | ||||
|         .widget-thumb { | ||||
|             @include flex(1 1 auto); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .rule-title { | ||||
|         @include flex(0 1 auto); | ||||
|         color: pullForward($colorBodyFg, 50%); | ||||
|     } | ||||
|  | ||||
|     .rule-description { | ||||
|         @include flex(1 1 auto); | ||||
|         @include ellipsize(); | ||||
|         color: pushBack($colorBodyFg, 20%); | ||||
|     } | ||||
|  | ||||
|     .s-widget-rule { | ||||
|         background-color: rgba($colorBodyFg, 0.1); | ||||
|         border-radius: $basicCr; | ||||
|     } | ||||
|  | ||||
|     .widget-thumb { | ||||
|         @include ellipsize(); | ||||
|         @extend .s-summary-widget; | ||||
|         @extend .l-summary-widget; | ||||
|         padding: $interiorMarginSm $interiorMargin; | ||||
|         .widget-icon { | ||||
|             font-size: 0.8em; | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     // Hide and show elements in the rule-header on hover | ||||
|     .l-widget-rule { | ||||
|         .grippy, | ||||
|         .l-rule-action-buttons-w, | ||||
|         .l-condition-action-buttons-w { | ||||
|             @include trans-prop-nice($props: opacity, $dur: 500ms); | ||||
|             opacity: 0; | ||||
|         } | ||||
|         &:hover { | ||||
|             .grippy, | ||||
|             .l-rule-action-buttons-w, | ||||
|             .l-condition-action-buttons-w { | ||||
|                 @include trans-prop-nice($props: opacity, $dur: 0); | ||||
|         &.expanded-widget-test-data { | ||||
|             .w-widget-test-data-content { | ||||
|                 min-height: 50px; | ||||
|                 height: auto; | ||||
|                 opacity: 1; | ||||
|                 pointer-events: inherit; | ||||
|             } | ||||
|             &:not(.expanded-widget-rules) { | ||||
|                 // Test data is expanded and rules are collapsed | ||||
|                 // Make text data take up all the vertical space | ||||
|                 .flex-accordion-holder { display: flex; } | ||||
|                 .widget-test-data { | ||||
|                     flex-grow: 999999; | ||||
|                 } | ||||
|                 .w-widget-test-data-items { | ||||
|                     max-height: inherit; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         &.expanded-widget-rules { | ||||
|             .widget-rules-wrapper { | ||||
|                 min-height: 50px; | ||||
|                 height: auto; | ||||
|                 opacity: 1; | ||||
|                 pointer-events: inherit; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &.s-status-no-data { | ||||
|         .widget-edit-holder { | ||||
|             opacity: 0.3; | ||||
|             pointer-events: none; | ||||
|         } | ||||
|         .t-message-widget-no-data { | ||||
|             display: flex; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     .l-compact-form { | ||||
|         // Overrides | ||||
|         ul li { | ||||
|             padding: $interiorMargin 0; | ||||
|             &:not(.widget-rule-header) { | ||||
|                 &:not(.connects-to-previous) { | ||||
|                     border-top: 1px solid $colorFormLines; | ||||
|         // Overrides on .l-compact-form | ||||
|         ul { | ||||
|             &:last-child { margin: 0; } | ||||
|             li { | ||||
|                 @include align-items(flex-start); | ||||
|                 @include flex-wrap(nowrap); | ||||
|                 line-height: 230%; // Provide enough space when controls wrap | ||||
|                 padding: 2px 0; | ||||
|                 &:not(.widget-rule-header) { | ||||
|                     &:not(.connects-to-previous) { | ||||
|                         border-top: 1px solid $colorFormLines; | ||||
|                     } | ||||
|                 } | ||||
|                 &.connects-to-previous { | ||||
|                     padding: $interiorMargin 0; | ||||
|                 } | ||||
|  | ||||
|                 > label { | ||||
|                     display: block; // Needed to align text to right | ||||
|                     text-align: right; | ||||
|                 } | ||||
|             } | ||||
|             &.connects-to-previous { | ||||
|                 padding: $interiorMargin 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         label { | ||||
|             display: block; // Needed to align text to right | ||||
|             text-align: right; | ||||
|         &.s-widget-test-data-item { | ||||
|             // Single line of ul li label span, etc. | ||||
|             ul { | ||||
|                 li { | ||||
|                     border: none !important; | ||||
|                     > label { | ||||
|                         display: inline-block; | ||||
|                         width: auto; | ||||
|                         text-align: left; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .widget-edit-holder { | ||||
|     font-size: 0.8rem; | ||||
| } | ||||
|  | ||||
| .widget-rules-wrapper { | ||||
|     // Wrapper area that holds n rules | ||||
|     box-sizing: border-box; | ||||
|     overflow-y: scroll; | ||||
|     padding-right: $interiorMargin; | ||||
| } | ||||
|  | ||||
| .l-widget-rule, | ||||
| .l-widget-test-data-item { | ||||
|     box-sizing: border-box; | ||||
|     margin-bottom: $interiorMarginSm; | ||||
|     padding: $interiorMargin $interiorMarginLg; | ||||
| } | ||||
|  | ||||
| .l-widget-thumb-wrapper { | ||||
|     @extend .l-flex-row; | ||||
|     @include align-items(center); | ||||
|     > span { display: block; } | ||||
|     .grippy-holder, | ||||
|     .view-control { | ||||
|         margin-right: $interiorMargin; | ||||
|         width: 1em; | ||||
|         height: 1em; | ||||
|     } | ||||
|  | ||||
|     .widget-thumb { | ||||
|         @include flex(1 1 auto); | ||||
|         width: 100%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .rule-title { | ||||
|     @include flex(0 1 auto); | ||||
|     color: pullForward($colorBodyFg, 50%); | ||||
| } | ||||
|  | ||||
| .rule-description { | ||||
|     @include flex(1 1 auto); | ||||
|     @include ellipsize(); | ||||
|     color: pushBack($colorBodyFg, 20%); | ||||
| } | ||||
|  | ||||
| .s-widget-rule, | ||||
| .s-widget-test-data-item { | ||||
|     background-color: rgba($colorBodyFg, 0.1); | ||||
|     border-radius: $basicCr; | ||||
| } | ||||
|  | ||||
| .widget-thumb { | ||||
|     @include ellipsize(); | ||||
|     @extend .s-summary-widget; | ||||
|     @extend .l-summary-widget; | ||||
|     padding: $interiorMarginSm $interiorMargin; | ||||
| } | ||||
|  | ||||
| // Hide and show elements in the rule-header on hover | ||||
| .l-widget-rule, | ||||
| .l-widget-test-data-item { | ||||
|     .grippy, | ||||
|     .l-rule-action-buttons-wrapper, | ||||
|     .l-condition-action-buttons-wrapper, | ||||
|     .l-widget-test-data-item-action-buttons-wrapper { | ||||
|         @include trans-prop-nice($props: opacity, $dur: 500ms); | ||||
|         opacity: 0; | ||||
|     } | ||||
|     &:hover { | ||||
|         .grippy, | ||||
|         .l-rule-action-buttons-wrapper, | ||||
|         .l-widget-test-data-item-action-buttons-wrapper { | ||||
|             @include trans-prop-nice($props: opacity, $dur: 0); | ||||
|             opacity: 1; | ||||
|         } | ||||
|     } | ||||
|     .l-rule-action-buttons-wrapper { | ||||
|           .t-delete { | ||||
|             margin-left: 10px; | ||||
|           } | ||||
|     } | ||||
|     .t-condition { | ||||
|         &:hover { | ||||
|             .l-condition-action-buttons-wrapper { | ||||
|                 @include trans-prop-nice($props: opacity, $dur: 0); | ||||
|                 opacity: 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -33,7 +33,6 @@ $pad: $interiorMargin * $baseRatio; | ||||
|     height: $btnStdH; | ||||
|     line-height: $btnStdH; | ||||
|     padding: 0 $pad; | ||||
|     vertical-align: top; | ||||
|  | ||||
|     &.labeled:before { | ||||
|         // Icon when it's included | ||||
|   | ||||
| @@ -72,11 +72,13 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Hyperlink objects | ||||
| .s-hyperlink { | ||||
|     // Hyperlink objects | ||||
|     .label { | ||||
|         font-size: 0.8rem !important; | ||||
|     } | ||||
|     &:not(.s-button) { | ||||
|         color: $colorKey; | ||||
|         font-size: 0.8rem; | ||||
|         &:hover { color: $colorKeyHov; } | ||||
|     } | ||||
| } | ||||
| @@ -232,12 +234,16 @@ textarea { | ||||
| } | ||||
|  | ||||
| /******************************************************** INPUTS */ | ||||
| %input-base { | ||||
|     @include input-base(); | ||||
| } | ||||
|  | ||||
| input[type="text"], | ||||
| input[type="search"], | ||||
| input[type="number"] { | ||||
|     @include nice-input(); | ||||
|     vertical-align: baseline; | ||||
|     padding: $inputTextP; | ||||
|     padding: $inputTextPTopBtm $inputTextPLeftRight; | ||||
|     &.numeric { | ||||
|         text-align: right; | ||||
|     } | ||||
| @@ -255,7 +261,7 @@ input[type="number"] { | ||||
| input[type="text"].lg {  width: 100% !important; } | ||||
| .l-input-med input[type="text"], | ||||
| input[type="text"].med { width: 200px !important; } | ||||
| input[type="text"].sm {  width: 50px !important; } | ||||
| input[type="text"].sm, input[type="number"].sm {  width: 50px !important; } | ||||
| .l-numeric input[type="text"], | ||||
| input[type="text"].numeric { text-align: right; } | ||||
|  | ||||
| @@ -281,20 +287,44 @@ textarea.lg { position: relative; height: 300px; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| *[contenteditable].s-input-inline, | ||||
| input[type="text"].s-input-inline, | ||||
| .s-input-inline input[type="text"] { | ||||
|     // A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus | ||||
|     @extend %input-base; | ||||
|     @include trans-prop-nice((padding, box-shadow), 250ms); | ||||
|     background: none; | ||||
|     box-shadow: none; | ||||
|     border: 1px solid transparent; | ||||
|     min-width: 20px; | ||||
|     padding-left: 0; | ||||
|     padding-right: 0; | ||||
|     &:hover, | ||||
|     &:focus { | ||||
|         padding-left: $inputTextPLeftRight; | ||||
|         padding-right: $inputTextPLeftRight; | ||||
|     } | ||||
|     &:hover { | ||||
|         border-color: rgba($colorBodyFg, 0.2); | ||||
|     } | ||||
|     &:focus { | ||||
|         @include s-input(); | ||||
|         border-color: transparent; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** SELECTS */ | ||||
| .select { | ||||
|     @include btnSubtle($bg: $colorSelectBg); | ||||
|     @extend .icon-arrow-down; // Context arrow | ||||
|     //@if $shdwBtns != none { margin: 0 0 2px 0; } // Needed to avoid dropshadow from being clipped by parent containers | ||||
|     display: inline-block; | ||||
|     padding: 0 $interiorMargin; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
|     height: $btnStdH; | ||||
|     line-height: $btnStdH; | ||||
|     select { | ||||
|         @include appearance(none); | ||||
|         box-sizing: border-box; | ||||
|         &:focus { outline: 0; } | ||||
|         background: none; | ||||
|         color: $colorSelectFg; | ||||
|         cursor: pointer; | ||||
| @@ -364,8 +394,7 @@ textarea.lg { position: relative; height: 300px; } | ||||
|     .l-elem-wrapper { | ||||
|         mct-representation { | ||||
|             // Holds the context-available item | ||||
|             // Must have min-width to make flex work properly | ||||
|             // in Safari | ||||
|             // Must have min-width to make flex work properly in Safari | ||||
|             min-width: 0.7em; | ||||
|         } | ||||
|     } | ||||
| @@ -531,7 +560,6 @@ textarea.lg { position: relative; height: 300px; } | ||||
|     height: $h; | ||||
|     margin-top: 1 + floor($h/2) * -1; | ||||
|     @include btnSubtle(pullForward($colorBtnBg, 10%)); | ||||
|     //border-radius: 50% !important; | ||||
| } | ||||
|  | ||||
| @mixin sliderKnobRound() { | ||||
| @@ -546,7 +574,6 @@ textarea.lg { position: relative; height: 300px; } | ||||
|  | ||||
| input[type="range"] { | ||||
|     // HTML5 range inputs | ||||
|  | ||||
|     -webkit-appearance: none; /* Hides the slider so that custom slider can be made */ | ||||
|     background: transparent; /* Otherwise white in Chrome */ | ||||
|     &:focus { | ||||
| @@ -745,11 +772,15 @@ body.desktop { | ||||
|     } | ||||
|  | ||||
|     .overlay ::-webkit-scrollbar-thumb { | ||||
|         $lr: 15%; | ||||
|         background: $scrollbarThumbColorOverlay; | ||||
|         &:hover { background: $scrollbarThumbColorOverlayHov; } | ||||
|     } | ||||
|  | ||||
|     .menu ::-webkit-scrollbar-thumb { | ||||
|         background: $scrollbarThumbColorMenu; | ||||
|         &:hover { background: $scrollbarThumbColorMenuHov; } | ||||
|     } | ||||
|  | ||||
|     ::-webkit-scrollbar-corner { | ||||
|         background: $scrollbarTrackColorBg; | ||||
|     } | ||||
|   | ||||
| @@ -21,20 +21,20 @@ | ||||
|  *****************************************************************************/ | ||||
| /******************************************************** MENU BUTTONS */ | ||||
| .s-menu-button { | ||||
| 	// Formerly .btn-menu | ||||
| 	@extend .s-button; | ||||
| 	span.l-click-area { | ||||
| 		// In markup, this element should not enclose anything. | ||||
| 		@extend .abs; | ||||
| 	} | ||||
|     // Formerly .btn-menu | ||||
|     @extend .s-button; | ||||
|     span.l-click-area { | ||||
|         // In markup, this element should not enclose anything. | ||||
|         @extend .abs; | ||||
|     } | ||||
|  | ||||
| 	.icon { | ||||
| 		font-size: 16px; //120%; | ||||
| 	} | ||||
|     .icon { | ||||
|         font-size: 16px; | ||||
|     } | ||||
|  | ||||
| 	.title-label { | ||||
| 		margin-left: $interiorMarginSm; | ||||
| 	} | ||||
|     .title-label { | ||||
|         margin-left: $interiorMarginSm; | ||||
|     } | ||||
|  | ||||
|     .icon-swatch, | ||||
|     .color-swatch { | ||||
| @@ -52,66 +52,60 @@ | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 	&:after { | ||||
| 		// Adds the downward facing 'context available / invoke menu' arrow element | ||||
| 		@include contextArrow(); | ||||
| 		color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent)); | ||||
| 	} | ||||
|     &:after { | ||||
|         // Adds the downward facing 'context available / invoke menu' arrow element | ||||
|         @include contextArrow(); | ||||
|         color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent)); | ||||
|     } | ||||
|  | ||||
| 	&.create-button { | ||||
|     &.create-button { | ||||
|         @extend .icon-plus; | ||||
| 		.title-label { | ||||
| 			font-size: 1rem; | ||||
| 		} | ||||
| 	} | ||||
|         .title-label { | ||||
|             font-size: 1rem; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 	.menu { | ||||
| 		left: 0; | ||||
| 		text-align: left; | ||||
| 	} | ||||
|     .menu { | ||||
|         left: 0; | ||||
|         text-align: left; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** MENUS THEMSELVES */ | ||||
| .menu-element { | ||||
| 	cursor: pointer; | ||||
| 	position: relative; | ||||
| } | ||||
|  | ||||
| .s-menu { | ||||
| 	border-radius: $basicCr; | ||||
| 	@include containerSubtle($colorMenuBg, $colorMenuFg); | ||||
| 	@include boxShdw($shdwMenu); | ||||
| 	@include txtShdw($shdwMenuText); | ||||
| 	padding: $interiorMarginSm 0; | ||||
|     cursor: pointer; | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| .menu { | ||||
|     // TODO: reduce size of icons | ||||
| 	@extend .s-menu; | ||||
| 	display: block; | ||||
| 	position: absolute; | ||||
| 	z-index: 10; | ||||
| 	ul { | ||||
| 		@include menuUlReset(); | ||||
| 		li { | ||||
| 			box-sizing: border-box; | ||||
| 			border-top: 1px solid pullForward($colorMenuBg, 10%); | ||||
|     border-radius: $basicCr; | ||||
|     @include containerSubtle($colorMenuBg, $colorMenuFg); | ||||
|     @include boxShdw($shdwMenu); | ||||
|     @include txtShdw($shdwMenuText); | ||||
|     padding: $interiorMarginSm 0; | ||||
|     display: block; | ||||
|     position: absolute; | ||||
|     z-index: 10; | ||||
|     ul { | ||||
|         @include menuUlReset(); | ||||
|         li { | ||||
|             box-sizing: border-box; | ||||
|             border-top: 1px solid pullForward($colorMenuBg, 10%); | ||||
|             color: $colorMenuFg; | ||||
| 			//color: pullForward($colorMenuBg, 60%); | ||||
| 			line-height: $menuLineH; | ||||
| 			padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW; | ||||
| 			position: relative; | ||||
| 			white-space: nowrap; | ||||
| 			&:first-child { | ||||
| 				border: none; | ||||
| 			} | ||||
| 			&:hover { | ||||
| 				background: $colorMenuHovBg; | ||||
| 				color: $colorMenuHovFg; | ||||
|             line-height: $menuLineH; | ||||
|             padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW; | ||||
|             position: relative; | ||||
|             white-space: nowrap; | ||||
|             &:first-child { | ||||
|                 border: none; | ||||
|             } | ||||
|             &:hover { | ||||
|                 background: $colorMenuHovBg; | ||||
|                 color: $colorMenuHovFg; | ||||
|                 &:before { | ||||
|                     color: $colorMenuHovIc; | ||||
|                 } | ||||
| 			} | ||||
|             } | ||||
|             &:before { | ||||
|                 @extend .ui-symbol; | ||||
|                 @extend .type-icon; | ||||
| @@ -119,8 +113,8 @@ | ||||
|                 display: inline-block; | ||||
|                 left: $interiorMargin * 2; | ||||
|             } | ||||
| 		} | ||||
| 	} | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .menu, | ||||
| @@ -128,94 +122,97 @@ | ||||
| .context-menu, | ||||
| .super-menu, | ||||
| .s-menu-button .menu { | ||||
| 	pointer-events: auto; | ||||
| 	ul li { | ||||
| 		a.menu-item-a { | ||||
|     pointer-events: auto; | ||||
|     ul li { | ||||
|         a.menu-item-a { | ||||
|             color: $colorMenuFg; | ||||
|             display: block; | ||||
| 		} | ||||
|         } | ||||
|         &:before, | ||||
|         a.menu-item-a:before { | ||||
|             color: $colorMenuIc; | ||||
|             left: $interiorMargin; | ||||
|         } | ||||
| 	} | ||||
|     } | ||||
| } | ||||
|  | ||||
| .checkbox-menu { | ||||
| 	// Used in search dropdown in tree | ||||
| 	@extend .context-menu; | ||||
| 	ul li { | ||||
| 		padding-left: 50px; | ||||
| 		.checkbox { | ||||
| 			$d: 0.7rem; | ||||
| 			position: absolute; | ||||
| 			left: $interiorMargin; | ||||
| 			top: ($menuLineH - $d) / 1.5; | ||||
| 			em { | ||||
| 				height: $d; | ||||
| 				width: $d; | ||||
| 				&:before { | ||||
| 					font-size: 7px !important; | ||||
| 					height: $d; | ||||
| 					width: $d; | ||||
| 					line-height: $d; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		&:before { | ||||
|     // Used in search dropdown in tree | ||||
|     @extend .context-menu; | ||||
|     ul li { | ||||
|         padding-left: 50px; | ||||
|         .checkbox { | ||||
|             $d: 0.7rem; | ||||
|             position: absolute; | ||||
|             left: $interiorMargin; | ||||
|             top: ($menuLineH - $d) / 1.5; | ||||
|             em { | ||||
|                 height: $d; | ||||
|                 width: $d; | ||||
|                 &:before { | ||||
|                     font-size: 7px !important; | ||||
|                     height: $d; | ||||
|                     width: $d; | ||||
|                     line-height: $d; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         &:before { | ||||
|             // Type icon | ||||
| 			left: 25px; | ||||
| 		} | ||||
| 	} | ||||
|             left: 25px; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .super-menu, | ||||
| .super-menu > mct-representation, | ||||
| .super-menu > .contents { | ||||
|     box-sizing: border-box; | ||||
|     display: block; | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| .super-menu { | ||||
| 	$w: 500px; | ||||
| 	$h: $w - 20; | ||||
| 	$plw: 50%; | ||||
| 	$prw: 50%; | ||||
| 	display: block; | ||||
| 	width: $w; | ||||
| 	height: $h; | ||||
| 	.contents { | ||||
| 		@include absPosDefault($interiorMargin); | ||||
| 	} | ||||
| 	.pane { | ||||
| 		box-sizing: border-box; | ||||
| 		&.menu-items { | ||||
| 			border-right: 1px solid pullForward($colorMenuBg, 10%); | ||||
| 			left: 0; | ||||
| 			padding-right: $interiorMargin; | ||||
| 			right: auto; | ||||
| 			width: $plw; | ||||
| 			overflow-x: hidden; | ||||
| 			overflow-y: auto; | ||||
| 			ul { | ||||
| 				li { | ||||
| 					border-radius: $controlCr; | ||||
| 					padding-left: 30px; | ||||
| 					border-top: none; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		&.menu-item-description { | ||||
| 			left: auto; | ||||
| 			right: 0; | ||||
| 			padding: $interiorMargin * 5; | ||||
| 			width: $prw; | ||||
|     $plw: 50%; | ||||
|     $prw: 100% - $plw; | ||||
|     position: absolute; | ||||
|     .w-menu { | ||||
|         align-items: stretch; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         margin: $interiorMarginLg; | ||||
|     } | ||||
|     .col { | ||||
|         box-sizing: border-box; | ||||
|         flex: 1 1 auto; | ||||
|         overflow-x: hidden; | ||||
|         &.menu-items { | ||||
|             border-right: 1px solid pullForward($colorMenuBg, 10%); | ||||
|             overflow-y: auto; | ||||
|             padding-right: $interiorMargin; | ||||
|             width: $plw; | ||||
|             ul { | ||||
|                 li { | ||||
|                     border-radius: $controlCr; | ||||
|                     padding-left: 30px; | ||||
|                     border-top: none; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         &.menu-item-description { | ||||
|             $p: $interiorMargin * 3; | ||||
|             overflow-y: hidden; | ||||
|             padding: $p $p 0 $p; | ||||
|             width: $prw; | ||||
|  | ||||
|             .desc-area { | ||||
|                 &.icon { | ||||
|                     color: $colorCreateMenuLgIcon; | ||||
|                     font-size: 8em; | ||||
|                     margin-bottom: $interiorMargin * 3; | ||||
|                     position: relative; | ||||
|                     text-align: center; | ||||
|                 } | ||||
|                 &.title { | ||||
|                     color: $colorCreateMenuText; | ||||
|                     font-size: 1.2em; | ||||
|                     margin-bottom: $interiorMargin * 2; | ||||
|                 } | ||||
|                 &.description { | ||||
|                     color: pushBack($colorCreateMenuText, 20%); | ||||
| @@ -223,67 +220,104 @@ | ||||
|                     line-height: 1.5em; | ||||
|                 } | ||||
|             } | ||||
| 		} | ||||
| 	} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .w-title-desc { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         overflow: hidden; // Height set in specific menu instances | ||||
|     } | ||||
|  | ||||
|     // Specific menu instances | ||||
|     &.l-create-menu { | ||||
|         width: 500px; | ||||
|         .col { | ||||
|             max-height: 70vh; | ||||
|         } | ||||
|         .w-title-desc { | ||||
|             height: 190px; | ||||
|         } | ||||
|         .desc-area { | ||||
|             &.icon { | ||||
|                 font-size: 8em; | ||||
|                 height: 135px; | ||||
|                 margin-bottom: $interiorMargin * 3; | ||||
|             } | ||||
|             &.title { | ||||
|                 font-size: 1.2em; | ||||
|                 margin-bottom: $interiorMargin * 2; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &.mini { | ||||
|         width: 400px; | ||||
|         height: 300px; | ||||
|         .pane { | ||||
|         .col { | ||||
|             max-height: 50vh; | ||||
|             &.menu-items { | ||||
|                 font-size: 0.8em; | ||||
|             } | ||||
|             &.menu-item-description { | ||||
|                 padding: $interiorMargin * 3; | ||||
|                 .desc-area { | ||||
|                     &.icon { | ||||
|                         font-size: 4em; | ||||
|                     } | ||||
|                     &.title { | ||||
|                         font-size: 1em; | ||||
|                     } | ||||
|                 } | ||||
|                 $p: $interiorMargin * 2; | ||||
|                 padding: $p $p 0 $p; | ||||
|             } | ||||
|         } | ||||
|         .w-title-desc { | ||||
|             height: 180px; | ||||
|         } | ||||
|         .desc-area { | ||||
|             &.icon { | ||||
|                 font-size: 4em; | ||||
|                 height: 70px; | ||||
|                 margin-bottom: $interiorMargin * 3; | ||||
|             } | ||||
|             &.title { | ||||
|                 font-size: 1em; | ||||
|                 margin-bottom: $interiorMargin * 2; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .context-menu { | ||||
| 	font-size: 0.80rem; | ||||
|     font-size: 0.80rem; | ||||
| } | ||||
|  | ||||
| .context-menu-holder, | ||||
| .menu-holder { | ||||
| 	position: absolute; | ||||
| 	z-index: 120; | ||||
| 	.context-menu-wrapper { | ||||
| 		position: absolute; | ||||
| 		height: 100%; | ||||
| 		width: 100%; | ||||
| 	} | ||||
| 	&.go-left .context-menu, | ||||
| 	&.go-left .menu { | ||||
| 		right: 0; | ||||
| 	} | ||||
| 	&.go-up .context-menu, | ||||
| 	&.go-up .menu { | ||||
| 		bottom: 0; | ||||
| 	} | ||||
|     position: absolute; | ||||
|     z-index: 120; | ||||
|     .context-menu-wrapper { | ||||
|         position: absolute; | ||||
|         height: 100%; | ||||
|         width: 100%; | ||||
|     } | ||||
|     &.go-left .context-menu, | ||||
|     &.go-left .menu { | ||||
|         right: 0; | ||||
|     } | ||||
|     &.go-up .context-menu, | ||||
|     &.go-up .menu { | ||||
|         bottom: 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .context-menu-holder { | ||||
| 	pointer-events: none; | ||||
| 	height: 200px; | ||||
| 	width: 170px; | ||||
|     pointer-events: none; | ||||
|     height: 200px; | ||||
|     width: 170px; | ||||
| } | ||||
|  | ||||
| .btn-bar.right .menu, | ||||
| .menus-to-left .menu { | ||||
|     z-index: 79; | ||||
| 	left: auto; | ||||
| 	right: 0; | ||||
| 	width: auto; | ||||
|     left: auto; | ||||
|     right: 0; | ||||
|     width: auto; | ||||
| } | ||||
|  | ||||
| .menus-up .menu { | ||||
|     bottom: $btnStdH; top: auto; | ||||
|     bottom: $btnStdH; | ||||
|     top: auto; | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /******************************************************************* STATUS BLOCK ELEMS */ | ||||
| @mixin statusBannerColors($bg, $fg: $colorStatusFg) { | ||||
| 	$bgPb: 30%; | ||||
| 	$bgPbD: 10%; | ||||
| @@ -39,7 +39,7 @@ | ||||
| // Status coloring | ||||
| .ok, .info { | ||||
|     .status-indicator { | ||||
|         color: $colorStatusInfo; | ||||
|         color: $colorInfo; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -120,7 +120,11 @@ | ||||
| 	} | ||||
|  | ||||
| 	.status-indicator { | ||||
|         background: none !important; | ||||
| 		margin-right: $interiorMarginSm; | ||||
|         &[class*='s-status']:before { | ||||
|             font-size: 1em; | ||||
|         } | ||||
| 	} | ||||
|  | ||||
| 	.count { | ||||
| @@ -136,7 +140,7 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Styles for messages and message banners */ | ||||
| /******************************************************************* MESSAGE BANNERS */ | ||||
| .message { | ||||
| 	&.block { | ||||
| 		border-radius: $basicCr; | ||||
| @@ -192,7 +196,6 @@ | ||||
| 		padding: 0 $interiorMargin; | ||||
| 	} | ||||
|     .close { | ||||
| 		//@include test(red, 0.7); | ||||
| 		cursor: pointer; | ||||
|         font-size: 7px; | ||||
| 		width: 8px; | ||||
| @@ -224,144 +227,159 @@ | ||||
| 	} | ||||
| 	&.ok, | ||||
|     &.info { | ||||
| 		@include statusBannerColors($colorStatusInfo); | ||||
| 		@include statusBannerColors($colorInfo); | ||||
| 	} | ||||
| 	&.caution, | ||||
|     &.warning, | ||||
|     &.alert { | ||||
| 		@include statusBannerColors($colorStatusAlert); | ||||
| 		@include statusBannerColors($colorWarningLo); | ||||
| 	} | ||||
|     &.error { | ||||
|         @include statusBannerColors($colorStatusError); | ||||
|         @include statusBannerColors($colorWarningHi); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @mixin messageBlock($iconW: 32px) { | ||||
|     .type-icon.message-type { | ||||
| /******************************************************************* MESSAGES */ | ||||
|  | ||||
| /* Contexts: | ||||
|     In .t-message-list | ||||
|     In .overlay as a singleton | ||||
|     Inline in the view area | ||||
|  */ | ||||
|  | ||||
| // Archetypal message | ||||
| .l-message { | ||||
|     $iconW: 32px; | ||||
|     @include display(flex); | ||||
|     @include flex-direction(row); | ||||
|     @include align-items(stretch); | ||||
|     padding: $interiorMarginLg; | ||||
|  | ||||
|     &:before { | ||||
|         // Icon | ||||
|         @include flex(0 1 auto); | ||||
|         @include txtShdw($shdwStatusIc); | ||||
|         @extend .icon-bell; | ||||
|         color: $colorStatusDefault; | ||||
|         font-size: $iconW; | ||||
|         padding: 1px; | ||||
|         width: $iconW + 2; | ||||
|         margin-right: $interiorMarginLg; | ||||
|     } | ||||
|  | ||||
|     .message-severity-info .type-icon.message-type { | ||||
|     &.message-severity-info:before { | ||||
|         @extend .icon-info; | ||||
|         color: $colorStatusInfo; | ||||
|         color: $colorInfo; | ||||
|     } | ||||
|     .message-severity-alert .type-icon.message-type { | ||||
|         @extend .icon-bell; | ||||
|         color: $colorStatusAlert; | ||||
|  | ||||
|     &.message-severity-alert:before { | ||||
|         color: $colorWarningLo; | ||||
|     } | ||||
|     .message-severity-error .type-icon.message-type { | ||||
|  | ||||
|     &.message-severity-error:before { | ||||
|         @extend .icon-alert-rect; | ||||
|         color: $colorStatusError; | ||||
|         color: $colorWarningHi; | ||||
|     } | ||||
| } | ||||
| /* Paths: | ||||
|     t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > | ||||
|         message-type > (icon) | ||||
|         message-contents > | ||||
|         top-bar > | ||||
|             title | ||||
|             hint | ||||
|         editor > | ||||
|             (if displaying list of messages) | ||||
|             ul > li > l-message > | ||||
|                 ... same as above | ||||
|         bottom-bar | ||||
| */ | ||||
|  | ||||
| .l-message { | ||||
|  | ||||
| .w-message-contents { | ||||
|     @include flex(1 1 auto); | ||||
|     @include display(flex); | ||||
|     @include flex-direction(row); | ||||
|     @include align-items(stretch); | ||||
|     .type-icon.message-type { | ||||
|         @include flex(0 1 auto); | ||||
|         position: relative; | ||||
|     } | ||||
|     .message-contents { | ||||
|         @include flex(1 1 auto); | ||||
|         margin-left: $overlayMargin; | ||||
|         position: relative; | ||||
|     @include flex-direction(column); | ||||
|  | ||||
|         .top-bar, | ||||
|     > div, | ||||
|     > span { | ||||
|         //@include test(red); | ||||
|         margin-bottom: $interiorMargin; | ||||
|     } | ||||
|  | ||||
|     .message-body { | ||||
|         @include flex(1 1 100%); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Singleton in an overlay dialog | ||||
| .t-message-single .l-message, | ||||
| .t-message-single.l-message { | ||||
|         $iconW: 80px; | ||||
|         @include absPosDefault(); | ||||
|         padding: 0; | ||||
|         &:before { | ||||
|             font-size: $iconW; | ||||
|             width: $iconW + 2; | ||||
|         } | ||||
|         .title { | ||||
|             font-size: 1.2em; | ||||
|         } | ||||
| } | ||||
|  | ||||
| // Singleton inline in a view | ||||
| .t-message-inline .l-message, | ||||
| .t-message-inline.l-message { | ||||
|     border-radius: $controlCr; | ||||
|     &.message-severity-info { background-color: rgba($colorInfo, 0.3); } | ||||
|     &.message-severity-alert { background-color: rgba($colorWarningLo, 0.3); } | ||||
|     &.message-severity-error { background-color: rgba($colorWarningHi, 0.3); } | ||||
|  | ||||
|     .w-message-contents.l-message-body-only { | ||||
|         .message-body { | ||||
|             margin-bottom: $interiorMarginLg * 2; | ||||
|             margin-top: $interiorMargin; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // In a list | ||||
| .t-message-list { | ||||
|     @include absPosDefault(); | ||||
|     @include display(flex); | ||||
|     @include flex-direction(column); | ||||
|  | ||||
| // Message as singleton | ||||
| .t-message-single { | ||||
|     @include messageBlock(80px); | ||||
| } | ||||
|  | ||||
| body.desktop .t-message-single { | ||||
|     .l-message, | ||||
|     .bottom-bar { | ||||
|         @include absPosDefault(); | ||||
|     > div, | ||||
|     > span { | ||||
|         margin-bottom: $interiorMargin; | ||||
|     } | ||||
|  | ||||
|     .bottom-bar { | ||||
|         top: auto; | ||||
|         height: $ovrFooterH; | ||||
|     .w-messages { | ||||
|         @include flex(1 1 100%); | ||||
|         overflow-y: auto; | ||||
|         padding-right: $interiorMargin; | ||||
|     } | ||||
|     // Each message | ||||
|     .l-message { | ||||
|         border-radius: $controlCr; | ||||
|         background: rgba($colorOvrFg, 0.1); | ||||
|         margin-bottom: $interiorMargin; | ||||
|         .hint, | ||||
|         .bottom-bar { | ||||
|             text-align: left; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @include phonePortrait { | ||||
|     .t-message-single { | ||||
|         .l-message { | ||||
|             @include flex-direction(column); | ||||
|             .message-contents { margin-left: 0; } | ||||
|         } | ||||
|         .type-icon.message-type { | ||||
|     .t-message-single .l-message, | ||||
|     .t-message-single.l-message { | ||||
|         @include flex-direction(column); | ||||
|         &:before { | ||||
|             margin-right: 0; | ||||
|             margin-bottom: $interiorMarginLg; | ||||
|             width: 100%; | ||||
|             text-align: center; | ||||
|             width: 100%; | ||||
|         } | ||||
|  | ||||
|         .bottom-bar { | ||||
|             text-align: center !important; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Messages in list | ||||
| .t-message-list { | ||||
|     @include messageBlock(32px); | ||||
|  | ||||
|     .message-contents { | ||||
|         .l-message { | ||||
|             border-radius: $controlCr; | ||||
|             background: rgba($colorOvrFg, 0.1); | ||||
|             margin-bottom: $interiorMargin; | ||||
|             padding: $interiorMarginLg; | ||||
|  | ||||
|             .message-contents, | ||||
|             .bottom-bar { | ||||
|                 position: relative; | ||||
|             } | ||||
|  | ||||
|             .message-contents { | ||||
|                 font-size: 0.9em; | ||||
|                 margin-left: $interiorMarginLg; | ||||
|                 .message-action { color: pushBack($colorOvrFg, 20%); } | ||||
|                 .bottom-bar { text-align: left; } | ||||
|             } | ||||
|  | ||||
|             .top-bar, | ||||
|             .message-body { | ||||
|                 margin-bottom: $interiorMarginLg; | ||||
|             text-align: center; | ||||
|             .s-button { | ||||
|                 display: block; | ||||
|                 width: 100%; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| body.desktop .t-message-list { | ||||
|     .message-contents .l-message { margin-right: $interiorMarginLg; } | ||||
|     .w-message-contents { padding-right: $interiorMargin; } | ||||
| } | ||||
|  | ||||
| // Alert elements in views | ||||
|   | ||||
| @@ -80,23 +80,32 @@ | ||||
|  | ||||
|     // 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; | ||||
|     // Display grid when selected or selection parent. | ||||
|     .s-selected .l-grid-holder, | ||||
|     .s-selected-parent .l-grid-holder { | ||||
|         display: block; | ||||
|     } | ||||
|  | ||||
|     // Prevent nested frames from being selectable until we have proper sub-object editing | ||||
|     .t-frame-outer .t-frame-outer { | ||||
|         pointer-events: none; | ||||
|     // Display in nested frames... | ||||
|     .t-frame-outer { | ||||
|         // ...when drilled in or selection parent... | ||||
|         &.s-drilled-in, &.s-selected-parent { | ||||
|             .l-grid-holder { | ||||
|                 display: block; | ||||
|             } | ||||
|             .t-frame-outer:not(.s-drilled-in) .l-grid-holder { | ||||
|                 display: none; | ||||
|             } | ||||
|         } | ||||
|         // ...but hide otherwise. | ||||
|         .l-grid-holder { | ||||
|             display: none; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
| 	@if $enableImageryThumbs == true { | ||||
| 		bottom: $interiorMargin*2 +  $imageThumbsWrapperH; | ||||
| 	} | ||||
| 	min-height: 100px; | ||||
| 	min-width: 150px; | ||||
| 	.l-image-main { | ||||
| 		background-color: $colorPlotBg; | ||||
| @@ -22,7 +21,9 @@ | ||||
|  | ||||
| .l-image-thumbs-wrapper { | ||||
| 	top: auto; | ||||
| 	height: $imageThumbsWrapperH; | ||||
| 	min-height: $imageThumbsWrapperH; | ||||
| 	max-height: 60%; | ||||
| 	box-sizing: border-box; | ||||
| } | ||||
|  | ||||
| .l-date, | ||||
| @@ -43,14 +44,16 @@ | ||||
| .l-image-main-controlbar { | ||||
| 	font-size: 0.8em; | ||||
| 	line-height: inherit; | ||||
| 	.left, .right { | ||||
| 	.l-datetime-w, .l-controls-w { | ||||
| 		direction: rtl; | ||||
| 		overflow: hidden; | ||||
| 	} | ||||
| 	.left { | ||||
| 	.l-datetime-w { | ||||
|         @include ellipsize(); | ||||
|         margin-right: $interiorMarginSm; | ||||
| 		text-align: left; | ||||
| 	} | ||||
| 	.right { | ||||
| 	.l-controls-w { | ||||
| 		z-index: 2; | ||||
| 	} | ||||
| 	.l-date, | ||||
| @@ -82,9 +85,8 @@ | ||||
| /*************************************** THUMBS */ | ||||
|  | ||||
| .l-image-thumbs-wrapper { | ||||
| 	//@include test(green); | ||||
| 	overflow-x: auto; | ||||
| 	overflow-y: hidden; | ||||
| 	overflow-x: hidden; | ||||
| 	overflow-y: auto; | ||||
| 	padding-bottom: $interiorMargin; | ||||
| 	white-space: nowrap; | ||||
| } | ||||
| @@ -92,8 +94,17 @@ | ||||
| .l-image-thumb-item { | ||||
| 	@include transition(background-color, 0.25s); | ||||
| 	box-sizing: border-box; | ||||
|     cursor: pointer; | ||||
|     direction: ltr; | ||||
|     display: inline-block; | ||||
|     float: left; | ||||
|     font-size: 0.8em; | ||||
| 	padding: 1px; | ||||
| 	position: relative; | ||||
|     margin-left: $interiorMarginSm; | ||||
|     position: relative; | ||||
|     text-align: left; | ||||
|     width: $imageThumbsD + $imageThumbPad*2; | ||||
|     white-space: normal; | ||||
| 	.l-thumb, | ||||
| 	.l-date, | ||||
| 	.l-time { | ||||
| @@ -103,14 +114,7 @@ | ||||
| 	.l-time { | ||||
| 		padding: 2px 3px; | ||||
| 	} | ||||
| 	cursor: pointer; | ||||
| 	direction: ltr; | ||||
| 	display: inline-block; | ||||
| 	font-size: 0.8em; | ||||
| 	margin-left: $interiorMarginSm; | ||||
| 	text-align: left; | ||||
| 	width: $imageThumbsD + $imageThumbPad*2; | ||||
| 	white-space: normal; | ||||
|  | ||||
| 	&:hover { | ||||
| 		background: $colorThumbHoverBg; | ||||
| 		.l-date, | ||||
| @@ -136,6 +140,7 @@ | ||||
| /*************************************** LOCAL CONTROLS */ | ||||
| .l-local-controls { | ||||
|     max-width: 200px; | ||||
|     min-width: 100px; | ||||
|     width: 35%; | ||||
|     input[type="range"] { | ||||
|         display: block; | ||||
| @@ -184,7 +189,8 @@ | ||||
| /*************************************** WHEN IN FRAME */ | ||||
| .frame .t-imagery { | ||||
| 	.l-image-main-wrapper { | ||||
| 		bottom: 0; | ||||
| 		bottom: 0 !important; | ||||
|         height: 100% !important; | ||||
| 		.l-image-main-controlbar { | ||||
| 			font-size: 0.7em; | ||||
| 		} | ||||
| @@ -194,7 +200,8 @@ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	.l-image-thumbs-wrapper { | ||||
| 	.l-image-thumbs-wrapper, | ||||
|     mct-splitter { | ||||
| 		display: none; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -23,10 +23,16 @@ | ||||
|     border-radius: $basicCr; | ||||
|     background: $colorFormSectionHeader; | ||||
|     color: lighten($colorBodyFg, 20%); | ||||
|     font-size: 0.8em; | ||||
|     font-size: inherit; | ||||
|     margin: $interiorMargin 0; | ||||
|     padding: $formTBPad $formLRPad; | ||||
|     text-transform: uppercase; | ||||
|     .view-control { | ||||
|         display: inline-block; | ||||
|         margin-right: $interiorMargin; | ||||
|         width: 1em; | ||||
|         height: 1em; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .form { | ||||
| @@ -54,9 +60,6 @@ | ||||
|         margin-bottom: $interiorMarginLg * 2; | ||||
| 		padding: $formTBPad 0; | ||||
| 		position: relative; | ||||
|         //&ng-form { | ||||
|         //    display: block; | ||||
|         //} | ||||
|  | ||||
| 		&.first { | ||||
| 			border-top: none; | ||||
| @@ -193,16 +196,23 @@ | ||||
|             label, | ||||
|             .control { | ||||
|                 @include display(flex); | ||||
|                 //min-width: $minW; | ||||
|             } | ||||
|             label { | ||||
|                 line-height: inherit; | ||||
|                 padding: $interiorMarginSm 0; | ||||
|                 width: $labelW; | ||||
|             } | ||||
|             .controls { | ||||
|                 @include flex-grow(1); | ||||
|                 margin-left: $interiorMargin    ; | ||||
|                 margin-left: $interiorMargin; | ||||
|                 input[type="text"], | ||||
|                 input[type="search"], | ||||
|                 input[type="number"], | ||||
|                 .select { | ||||
|                     height: $btnStdH; | ||||
|                     line-height: $btnStdH; | ||||
|                     vertical-align: middle; | ||||
|                 } | ||||
|  | ||||
|                 .e-control { | ||||
|                     // Individual form controls | ||||
|                     &:not(:first-child) { | ||||
| @@ -211,12 +221,6 @@ | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             &:not(.section-header) { | ||||
|                 &:not(.connects-to-previous) { | ||||
|                     //border-top: 1px solid $colorFormLines; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             &.connects-to-previous { | ||||
|                 padding-top: 0; | ||||
|             } | ||||
|   | ||||
| @@ -129,9 +129,6 @@ | ||||
| } | ||||
|  | ||||
| .s-filter { | ||||
|     input[type="search"] { | ||||
|         @include input-base(); | ||||
|     } | ||||
|     .clear-icon, | ||||
|     .menu-icon, | ||||
|     &:before { | ||||
|   | ||||
| @@ -79,6 +79,7 @@ | ||||
|  | ||||
|     // Dialog boxes, size constrained and centered in desktop/tablet | ||||
|     &.l-dialog { | ||||
|         font-size: 0.8rem; | ||||
|         .s-button { | ||||
|             &:not(.major) { | ||||
|                 @include btnSubtle($bg: $colorOvrBtnBg, $bgHov: pullForward($colorOvrBtnBg, 10%), $fg: $colorOvrBtnFg, $fgHov: $colorOvrBtnFg, $ic: $colorOvrBtnFg, $icHov: $colorOvrBtnFg); | ||||
| @@ -125,9 +126,9 @@ | ||||
|             @include containerSubtle($colorOvrBg, $colorOvrFg); | ||||
|         } | ||||
|  | ||||
|         .title { | ||||
|         .dialog-title { | ||||
|             @include ellipsize(); | ||||
|             font-size: 1.2em; | ||||
|             font-size: 1.5em; | ||||
|             line-height: 120%; | ||||
|             margin-bottom: $interiorMargin; | ||||
|         } | ||||
| @@ -156,6 +157,8 @@ | ||||
|             left: 0; | ||||
|             right: 0; | ||||
|             overflow: auto; | ||||
|             padding-right: $interiorMargin; | ||||
|             padding-bottom: $interiorMargin; | ||||
|             .field.l-input-med { | ||||
|                 input[type='text'] { | ||||
|                     width: 100%; | ||||
|   | ||||
| @@ -23,15 +23,14 @@ | ||||
|     $ohH: $btnFrameH; | ||||
|     $bc: $colorInteriorBorder; | ||||
|     &.child-frame.panel { | ||||
|         border: 1px solid transparent; | ||||
|         z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above | ||||
|         &:not(.no-frame) { | ||||
|             background: $colorBodyBg; | ||||
|             border: 1px solid $bc; | ||||
|             &:hover { | ||||
|                 border-color: lighten($bc, 10%); | ||||
|             } | ||||
|             border-color: $bc; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .object-browse-bar { | ||||
|         font-size: 0.75em; | ||||
|         height: $ohH; | ||||
| @@ -44,10 +43,11 @@ | ||||
|  | ||||
|     &.t-object-type-timer, | ||||
|     &.t-object-type-clock, | ||||
|     &.t-object-type-hyperlink { | ||||
|     &.t-object-type-hyperlink, | ||||
|     &.t-object-type-summary-widget { | ||||
|         // 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. | ||||
|         // to an object that has it. | ||||
|         .object-browse-bar .right { display: none; } | ||||
|     } | ||||
|  | ||||
| @@ -91,9 +91,9 @@ | ||||
|  | ||||
|     &.no-frame { | ||||
|         background: transparent !important; | ||||
|         border: none !important; | ||||
|         border: none; | ||||
|         .object-browse-bar .right { | ||||
|             $m: 0; // $interiorMarginSm; | ||||
|             $m: 0; | ||||
|             background: rgba(black, 0.3); | ||||
|             border-radius: $basicCr; | ||||
|             padding: $interiorMarginSm; | ||||
| @@ -103,7 +103,7 @@ | ||||
|         } | ||||
|         &.t-frame-outer > .t-rep-frame { | ||||
|             &.contents { | ||||
|                 $m: 2px; | ||||
|                 $m: 0px; | ||||
|                 top: $m; | ||||
|                 right: $m; | ||||
|                 bottom: $m; | ||||
| @@ -114,17 +114,33 @@ | ||||
|                     display: none; | ||||
|                 } | ||||
|                 > .object-holder.abs { | ||||
|                     overflow: hidden; | ||||
|                     top: 0 !important; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &.t-frame-outer .s-input-inline { | ||||
|         // Prevent inline inputs from being edited when nested in a Layout | ||||
|         pointer-events: none !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 | ||||
|     .t-object-type-hyperlink, | ||||
|     .t-object-type-summary-widget { | ||||
|         .object-holder { | ||||
|             overflow: hidden; | ||||
|         } | ||||
|         .w-summary-widget, | ||||
|         .l-summary-widget, | ||||
|         .l-hyperlink.s-button { | ||||
|             // Some object types expand to the full size of the object-holder. | ||||
|             @extend .abs; | ||||
|         } | ||||
|  | ||||
|         .l-summary-widget, | ||||
|         .l-hyperlink.s-button { | ||||
|             .label { | ||||
|                 @include ellipsize(); | ||||
|                 @include transform(translateY(-50%)); | ||||
| @@ -142,7 +158,7 @@ | ||||
| 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 | ||||
|     .right { | ||||
|     .object-browse-bar .btn-bar { | ||||
|         opacity: 0; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| @@ -150,7 +166,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 { | ||||
|         .right { | ||||
|         .btn-bar { | ||||
|             opacity: 1; | ||||
|             pointer-events: inherit; | ||||
|         } | ||||
|   | ||||
| @@ -240,7 +240,9 @@ body.desktop .pane .mini-tab-icon.toggle-pane { | ||||
| .top-bar .buttons-main .s-button, | ||||
| .top-bar .s-menu-button, | ||||
| .tool-bar .s-button, | ||||
| .tool-bar .s-menu-button { | ||||
| .tool-bar .s-menu-button, | ||||
| .tool-bar .select, | ||||
| .tool-bar .input-labeled { | ||||
|     $h: $btnToolbarH; | ||||
|     height: $h; | ||||
|     line-height: $h; | ||||
|   | ||||
| @@ -20,35 +20,51 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| .s-hover-border { | ||||
|     border: 1px dotted transparent; | ||||
|     &:hover { | ||||
|         border-color: rgba($colorSelectableSelectedPrimary, 0.5) !important; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .s-status-editing { | ||||
|     // Limit to editing mode until we have sub-object selection | ||||
|     // Limit to editing mode | ||||
|     $o: 0.5; | ||||
|     $oHover: 0.8; | ||||
|     $bc: $colorSelectableSelectedPrimary; | ||||
|     .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; | ||||
|         border-color: rgba($bc, $o) !important; | ||||
|         border-style: dotted !important; | ||||
|  | ||||
|         &:hover { | ||||
|             border-color: rgba($colorSelectableSelectedPrimary, 0.7) !important; | ||||
|             border-color: rgba($bc, $oHover) !important; | ||||
|         } | ||||
|  | ||||
|         &.t-object-type-layout { | ||||
|             border-style: dashed !important; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .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-selected { | ||||
|         &.s-moveable { | ||||
|             &:not(.s-drilled-in) { | ||||
|                 cursor: move; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     .s-selected > .s-moveable, | ||||
|     .s-selected.s-moveable { | ||||
|         cursor: move; | ||||
| .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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| .tool-bar { | ||||
|     font-size: 0.7rem; | ||||
|     &.btn-bar { | ||||
|         white-space: nowrap; | ||||
|     } | ||||
| @@ -30,9 +31,7 @@ | ||||
|     input[type="search"], | ||||
|     input[type="number"] { | ||||
| 		box-sizing: border-box; | ||||
| 		font-size: .8em; | ||||
| 		height: $btnToolbarH; | ||||
| 		margin-bottom: 1px; | ||||
| 		position: relative; | ||||
| 		&.sm { | ||||
| 			width: $btnToolbarH; | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span ng-controller="DateTimeFieldController"> | ||||
|     <input type="text" | ||||
|     <input type="text" autocorrect="off" spellcheck="false" | ||||
|            ng-model="textValue" | ||||
|            ng-blur="restoreTextValue(); ngBlur()" | ||||
|            ng-mouseup="ngMouseup()" | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <!-- look at action-button for example --> | ||||
| <span class="t-filter l-filter s-filter" | ||||
| <span class="t-filter l-filter" | ||||
|       ng-controller="GetterSetterController"> | ||||
| 	<input type="search" | ||||
|            class="t-filter-input" | ||||
|   | ||||
| @@ -40,7 +40,7 @@ define( | ||||
|  | ||||
|             // Gets an array of the contextual parents/ancestors of the selected object | ||||
|             function getContextualPath() { | ||||
|                 var currentObj = $scope.ngModel.selectedObject, | ||||
|                 var currentObj = $scope.domainObject, | ||||
|                     currentParent, | ||||
|                     parents = []; | ||||
|  | ||||
| @@ -68,7 +68,7 @@ define( | ||||
|  | ||||
|                 // If this the the initial call of this recursive function | ||||
|                 if (!current) { | ||||
|                     current = $scope.ngModel.selectedObject; | ||||
|                     current = $scope.domainObject; | ||||
|                     $scope.primaryParents = []; | ||||
|                 } | ||||
|  | ||||
| @@ -87,16 +87,16 @@ define( | ||||
|  | ||||
|             // Gets the metadata for the selected object | ||||
|             function getMetadata() { | ||||
|                 $scope.metadata = $scope.ngModel.selectedObject && | ||||
|                     $scope.ngModel.selectedObject.hasCapability('metadata') && | ||||
|                     $scope.ngModel.selectedObject.useCapability('metadata'); | ||||
|                 $scope.metadata = $scope.domainObject && | ||||
|                     $scope.domainObject.hasCapability('metadata') && | ||||
|                     $scope.domainObject.useCapability('metadata'); | ||||
|             } | ||||
|  | ||||
|             // Set scope variables when the selected object changes | ||||
|             $scope.$watch('ngModel.selectedObject', function () { | ||||
|                 $scope.isLink = $scope.ngModel.selectedObject && | ||||
|                     $scope.ngModel.selectedObject.hasCapability('location') && | ||||
|                     $scope.ngModel.selectedObject.getCapability('location').isLink(); | ||||
|             $scope.$watch('domainObject', function () { | ||||
|                 $scope.isLink = $scope.domainObject && | ||||
|                     $scope.domainObject.hasCapability('location') && | ||||
|                     $scope.domainObject.getCapability('location').isLink(); | ||||
|  | ||||
|                 if ($scope.isLink) { | ||||
|                     getPrimaryPath(); | ||||
| @@ -108,8 +108,11 @@ define( | ||||
|  | ||||
|                 getMetadata(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|             var mutation = $scope.domainObject.getCapability('mutation'); | ||||
|             var unlisten = mutation.listen(getMetadata); | ||||
|             $scope.$on('$destroy', unlisten); | ||||
|         } | ||||
|         return ObjectInspectorController; | ||||
|     } | ||||
| ); | ||||
|   | ||||
							
								
								
									
										60
									
								
								platform/commonUI/general/src/directives/MCTSelectable.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								platform/commonUI/general/src/directives/MCTSelectable.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| /***************************************************************************** | ||||
|  * 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 () { | ||||
|  | ||||
|         /** | ||||
|          * The mct-selectable directive allows selection functionality | ||||
|          * (click) to be attached to specific elements. | ||||
|          * | ||||
|          * @memberof platform/commonUI/general | ||||
|          * @constructor | ||||
|          */ | ||||
|         function MCTSelectable(openmct) { | ||||
|  | ||||
|             // Link; install event handlers. | ||||
|             function link(scope, element, attrs) { | ||||
|                 var removeSelectable = openmct.selection.selectable( | ||||
|                     element[0], | ||||
|                     scope.$eval(attrs.mctSelectable), | ||||
|                     attrs.hasOwnProperty('mctInitSelect') && scope.$eval(attrs.mctInitSelect) !== false | ||||
|                 ); | ||||
|  | ||||
|                 scope.$on("$destroy", function () { | ||||
|                     removeSelectable(); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             return { | ||||
|                 // mct-selectable only makes sense as an attribute | ||||
|                 restrict: "A", | ||||
|                 // Link function, to install event handlers | ||||
|                 link: link | ||||
|             }; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return MCTSelectable; | ||||
|     } | ||||
| ); | ||||
| @@ -117,6 +117,8 @@ define( | ||||
|  | ||||
|                 // Apply styles to child elements | ||||
|                 function updateChildren(children) { | ||||
|                     position = userWidthPreference || position; | ||||
|  | ||||
|                     // Pick out correct elements to update, flowing from | ||||
|                     // selected anchor edge. | ||||
|                     var first = children.eq(anchor.reversed ? 2 : 0), | ||||
| @@ -126,7 +128,7 @@ define( | ||||
|  | ||||
|                     splitterSize = getSize(splitter[0]); | ||||
|                     first.css(anchor.edge, "0px"); | ||||
|                     first.css(anchor.dimension, (userWidthPreference || position) + 'px'); | ||||
|                     first.css(anchor.dimension, position + 'px'); | ||||
|  | ||||
|                     // Get actual size (to obey min-width etc.) | ||||
|                     firstSize = getSize(first[0]); | ||||
| @@ -135,8 +137,8 @@ define( | ||||
|                     splitter.css(anchor.opposite, "auto"); | ||||
|  | ||||
|                     last.css(anchor.edge, firstSize + splitterSize + 'px'); | ||||
|                     last.css(anchor.opposite, "0px"); | ||||
|                     position = firstSize + splitterSize; | ||||
|                     last.css(anchor.opposite, '0px'); | ||||
|                     position = firstSize; | ||||
|                 } | ||||
|  | ||||
|                 // Update positioning of contained elements | ||||
| @@ -173,17 +175,19 @@ define( | ||||
|                             positionParsed.assign($scope, position); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     return position; | ||||
|                 } | ||||
|  | ||||
|                 function setUserWidthPreference(value) { | ||||
|                     userWidthPreference = value - splitterSize; | ||||
|                     if (alias) { | ||||
|                         userWidthPreference = value; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function persistToLocalStorage(value) { | ||||
|                     if (alias) { | ||||
|                         userWidthPreference = value - splitterSize; | ||||
|                         $window.localStorage.setItem(alias, userWidthPreference); | ||||
|                         $window.localStorage.setItem(alias, value); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @@ -225,13 +229,13 @@ define( | ||||
|                     anchor: function () { | ||||
|                         return anchor; | ||||
|                     }, | ||||
|                     position: function (value) { | ||||
|                         if (arguments.length > 0) { | ||||
|                             setUserWidthPreference(value); | ||||
|                             return getSetPosition(value); | ||||
|                         } else { | ||||
|                     position: function (newPosition) { | ||||
|                         if (arguments.length === 0) { | ||||
|                             return getSetPosition(); | ||||
|                         } | ||||
|  | ||||
|                         setUserWidthPreference(newPosition); | ||||
|                         return getSetPosition(newPosition); | ||||
|                     }, | ||||
|                     startResizing: function () { | ||||
|                         toggleClass('resizing'); | ||||
|   | ||||
| @@ -57,7 +57,10 @@ define( | ||||
|  | ||||
|                         // Update the position of this splitter | ||||
|                         newPosition =  initialPosition + pixelDelta; | ||||
|                         mctSplitPane.position(newPosition); | ||||
|  | ||||
|                         if (initialPosition !== newPosition) { | ||||
|                             mctSplitPane.position(newPosition); | ||||
|                         } | ||||
|                     }, | ||||
|                     // Grab the event when the user is done moving | ||||
|                     // the splitter and pass it on | ||||
|   | ||||
| @@ -39,10 +39,8 @@ define( | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpyObj( | ||||
|                     "$scope", | ||||
|                     ["$watch"] | ||||
|                     ["$watch", "$on"] | ||||
|                 ); | ||||
|                 mockScope.ngModel = {}; | ||||
|                 mockScope.ngModel.selectedObject = 'mock selected object'; | ||||
|  | ||||
|                 mockObjectService = jasmine.createSpyObj( | ||||
|                     "objectService", | ||||
| @@ -69,22 +67,27 @@ define( | ||||
|                     "location capability", | ||||
|                     ["isLink"] | ||||
|                 ); | ||||
|  | ||||
|                 mockDomainObject.getCapability.andCallFake(function (param) { | ||||
|                     if (param === 'location') { | ||||
|                         return mockLocationCapability; | ||||
|                     } else if (param === 'context') { | ||||
|                         return mockContextCapability; | ||||
|                     } else if (param === 'mutation') { | ||||
|                         return { | ||||
|                             listen: function () { | ||||
|                                 return true; | ||||
|                             } | ||||
|                         }; | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|                 controller = new ObjectInspectorController(mockScope, mockObjectService); | ||||
|  | ||||
|                 // Change the selected object to trigger the watch call | ||||
|                 mockScope.ngModel.selectedObject = mockDomainObject; | ||||
|             }); | ||||
|  | ||||
|             it("watches for changes to the selected object", function () { | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith('ngModel.selectedObject', jasmine.any(Function)); | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith('domainObject', jasmine.any(Function)); | ||||
|             }); | ||||
|  | ||||
|             it("looks for contextual parent objects", function () { | ||||
|   | ||||
| @@ -140,7 +140,7 @@ define( | ||||
|  | ||||
|                 it("exposes its splitter's initial position", function () { | ||||
|                     expect(controller.position()).toEqual( | ||||
|                         mockFirstPane[0].offsetWidth + mockSplitter[0].offsetWidth | ||||
|                         mockFirstPane[0].offsetWidth | ||||
|                     ); | ||||
|                 }); | ||||
|  | ||||
| @@ -168,7 +168,7 @@ define( | ||||
|                     controller.position(testValue); | ||||
|                     expect(mockFirstPane.css).toHaveBeenCalledWith( | ||||
|                         'width', | ||||
|                         (testValue - mockSplitter[0].offsetWidth) + 'px' | ||||
|                         (testValue) + 'px' | ||||
|                     ); | ||||
|                 }); | ||||
|  | ||||
| @@ -200,11 +200,11 @@ define( | ||||
|                     mockFirstPane[0].offsetWidth += 100; | ||||
|                     // Should not reflect the change yet | ||||
|                     expect(controller.position()).not.toEqual( | ||||
|                         mockFirstPane[0].offsetWidth + mockSplitter[0].offsetWidth | ||||
|                         mockFirstPane[0].offsetWidth | ||||
|                     ); | ||||
|                     mockInterval.mostRecentCall.args[0](); | ||||
|                     expect(controller.position()).toEqual( | ||||
|                         mockFirstPane[0].offsetWidth + mockSplitter[0].offsetWidth | ||||
|                         mockFirstPane[0].offsetWidth | ||||
|                     ); | ||||
|                 }); | ||||
|  | ||||
| @@ -216,7 +216,7 @@ define( | ||||
|  | ||||
|                 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); | ||||
|                     expect(Number(mockWindow.localStorage.getItem('mctSplitPane-rightSide'))).toEqual(100); | ||||
|                 }); | ||||
|  | ||||
|             }); | ||||
|   | ||||
| @@ -38,7 +38,8 @@ define([ | ||||
|                     "implementation": InspectorController, | ||||
|                     "depends": [ | ||||
|                         "$scope", | ||||
|                         "policyService" | ||||
|                         "openmct", | ||||
|                         "$document" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|   | ||||
| @@ -21,44 +21,69 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ['../../browse/src/InspectorRegion'], | ||||
|     function (InspectorRegion) { | ||||
|     [], | ||||
|     function () { | ||||
|  | ||||
|         /** | ||||
|          * The InspectorController adds region data for a domain object's type | ||||
|          * to the scope. | ||||
|          * The InspectorController listens for the selection changes and adds the selection | ||||
|          * object to the scope. | ||||
|          * | ||||
|          * @constructor | ||||
|          */ | ||||
|         function InspectorController($scope, policyService) { | ||||
|             var domainObject = $scope.domainObject, | ||||
|                 typeCapability = domainObject.getCapability('type'), | ||||
|                 statusListener; | ||||
|         function InspectorController($scope, openmct, $document) { | ||||
|             var self = this; | ||||
|             self.$scope = $scope; | ||||
|  | ||||
|             /** | ||||
|              * Filters region parts to only those allowed by region policies | ||||
|              * @param regions | ||||
|              * @returns {{}} | ||||
|              * Callback handler for the selection change event. | ||||
|              * Adds the selection object to the scope. If the selected item has an inspector view, | ||||
|              * it puts the key in the scope. If provider view exists, it shows the view. | ||||
|              */ | ||||
|             function filterRegions(inspector) { | ||||
|                 //Dupe so we're not modifying the type definition. | ||||
|                 return inspector.regions && inspector.regions.filter(function (region) { | ||||
|                     return policyService.allow('region', region, domainObject); | ||||
|                 }); | ||||
|             function setSelection(selection) { | ||||
|                 if (selection[0]) { | ||||
|                     var view = openmct.inspectorViews.get(selection); | ||||
|                     var container = $document[0].querySelectorAll('.inspector-provider-view')[0]; | ||||
|                     container.innerHTML = ""; | ||||
|  | ||||
|                     if (view) { | ||||
|                         self.providerView = true; | ||||
|                         view.show(container); | ||||
|                     } else { | ||||
|                         self.providerView = false; | ||||
|                         $scope.inspectorKey = selection[0].context.oldItem.getCapability("type").typeDef.inspector; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 self.$scope.selection = selection; | ||||
|             } | ||||
|  | ||||
|             function setRegions() { | ||||
|                 $scope.regions = filterRegions(typeCapability.getDefinition().inspector || new InspectorRegion()); | ||||
|             } | ||||
|             openmct.selection.on("change", setSelection); | ||||
|  | ||||
|             setSelection(openmct.selection.get()); | ||||
|  | ||||
|             statusListener = domainObject.getCapability("status").listen(setRegions); | ||||
|             $scope.$on("$destroy", function () { | ||||
|                 statusListener(); | ||||
|                 openmct.selection.off("change", setSelection); | ||||
|             }); | ||||
|  | ||||
|             setRegions(); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Gets the selected item. | ||||
|          * | ||||
|          * @returns a domain object | ||||
|          */ | ||||
|         InspectorController.prototype.selectedItem = function () { | ||||
|             return this.$scope.selection[0].context.oldItem; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Checks if a provider view exists. | ||||
|          * | ||||
|          * @returns 'true' if provider view exists, 'false' otherwise | ||||
|          */ | ||||
|         InspectorController.prototype.hasProviderView = function () { | ||||
|             return this.providerView; | ||||
|         }; | ||||
|  | ||||
|         return InspectorController; | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -27,82 +27,93 @@ define( | ||||
|         describe("The inspector controller ", function () { | ||||
|             var mockScope, | ||||
|                 mockDomainObject, | ||||
|                 mockTypeCapability, | ||||
|                 mockTypeDefinition, | ||||
|                 mockPolicyService, | ||||
|                 mockStatusCapability, | ||||
|                 capabilities = {}, | ||||
|                 controller; | ||||
|                 mockOpenMCT, | ||||
|                 mockSelection, | ||||
|                 mockInspectorViews, | ||||
|                 mockTypeDef, | ||||
|                 controller, | ||||
|                 container, | ||||
|                 $document = [], | ||||
|                 selectable = []; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockTypeDefinition = { | ||||
|                     inspector: | ||||
|                         { | ||||
|                             'regions': [ | ||||
|                                 {'name': 'Part One'}, | ||||
|                                 {'name': 'Part Two'} | ||||
|                             ] | ||||
|                         } | ||||
|                 mockTypeDef = { | ||||
|                     typeDef: { | ||||
|                         inspector: "some-key" | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 mockTypeCapability = jasmine.createSpyObj('typeCapability', [ | ||||
|                     'getDefinition' | ||||
|                 ]); | ||||
|                 mockTypeCapability.getDefinition.andReturn(mockTypeDefinition); | ||||
|                 capabilities.type = mockTypeCapability; | ||||
|  | ||||
|                 mockStatusCapability = jasmine.createSpyObj('statusCapability', [ | ||||
|                     'listen' | ||||
|                 ]); | ||||
|                 capabilities.status = mockStatusCapability; | ||||
|  | ||||
|                 mockDomainObject = jasmine.createSpyObj('domainObject', [ | ||||
|                     'getCapability' | ||||
|                 ]); | ||||
|                 mockDomainObject.getCapability.andCallFake(function (name) { | ||||
|                     return capabilities[name]; | ||||
|                 }); | ||||
|  | ||||
|                 mockPolicyService = jasmine.createSpyObj('policyService', [ | ||||
|                    'allow' | ||||
|                 ]); | ||||
|                 mockDomainObject.getCapability.andReturn(mockTypeDef); | ||||
|  | ||||
|                 mockScope = jasmine.createSpyObj('$scope', | ||||
|                     ['$on'] | ||||
|                     ['$on', 'selection'] | ||||
|                 ); | ||||
|  | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|                 selectable[0] = { | ||||
|                     context: { | ||||
|                         oldItem: mockDomainObject | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 mockSelection = jasmine.createSpyObj("selection", [ | ||||
|                     'on', | ||||
|                     'off', | ||||
|                     'get' | ||||
|                 ]); | ||||
|                 mockSelection.get.andReturn(selectable); | ||||
|  | ||||
|                 mockInspectorViews = jasmine.createSpyObj('inspectorViews', ['get']); | ||||
|                 mockOpenMCT = { | ||||
|                     selection: mockSelection, | ||||
|                     inspectorViews: mockInspectorViews | ||||
|                 }; | ||||
|  | ||||
|                 container = jasmine.createSpy('container', ['innerHTML']); | ||||
|                 $document[0] = jasmine.createSpyObj("$document", ['querySelectorAll']); | ||||
|                 $document[0].querySelectorAll.andReturn([container]); | ||||
|  | ||||
|                 controller = new InspectorController(mockScope, mockOpenMCT, $document); | ||||
|             }); | ||||
|  | ||||
|             it("filters out regions disallowed by region policy", function () { | ||||
|                 mockPolicyService.allow.andReturn(false); | ||||
|                 controller = new InspectorController(mockScope, mockPolicyService); | ||||
|                 expect(mockScope.regions.length).toBe(0); | ||||
|             it("listens for selection change event", function () { | ||||
|                 expect(mockOpenMCT.selection.on).toHaveBeenCalledWith( | ||||
|                     'change', | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|  | ||||
|                 expect(controller.selectedItem()).toEqual(mockDomainObject); | ||||
|  | ||||
|                 var mockItem = jasmine.createSpyObj('domainObject', [ | ||||
|                     'getCapability' | ||||
|                 ]); | ||||
|                 mockItem.getCapability.andReturn(mockTypeDef); | ||||
|                 selectable[0].context.oldItem = mockItem; | ||||
|  | ||||
|                 mockOpenMCT.selection.on.mostRecentCall.args[1](selectable); | ||||
|  | ||||
|                 expect(controller.selectedItem()).toEqual(mockItem); | ||||
|             }); | ||||
|  | ||||
|             it("does not filter out regions allowed by region policy", function () { | ||||
|                 mockPolicyService.allow.andReturn(true); | ||||
|                 controller = new InspectorController(mockScope, mockPolicyService); | ||||
|                 expect(mockScope.regions.length).toBe(2); | ||||
|             it("cleans up on scope destroy", function () { | ||||
|                 expect(mockScope.$on).toHaveBeenCalledWith( | ||||
|                     '$destroy', | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|  | ||||
|                 mockScope.$on.calls[0].args[1](); | ||||
|  | ||||
|                 expect(mockOpenMCT.selection.off).toHaveBeenCalledWith( | ||||
|                     'change', | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("Responds to status changes", function () { | ||||
|                 mockPolicyService.allow.andReturn(true); | ||||
|                 controller = new InspectorController(mockScope, mockPolicyService); | ||||
|                 expect(mockScope.regions.length).toBe(2); | ||||
|                 expect(mockStatusCapability.listen).toHaveBeenCalled(); | ||||
|                 mockPolicyService.allow.andReturn(false); | ||||
|                 mockStatusCapability.listen.mostRecentCall.args[0](); | ||||
|                 expect(mockScope.regions.length).toBe(0); | ||||
|             }); | ||||
|  | ||||
|             it("Unregisters status listener", function () { | ||||
|                 var mockListener = jasmine.createSpy('listener'); | ||||
|                 mockStatusCapability.listen.andReturn(mockListener); | ||||
|                 controller = new InspectorController(mockScope, mockPolicyService); | ||||
|                 expect(mockScope.$on).toHaveBeenCalledWith("$destroy", jasmine.any(Function)); | ||||
|                 mockScope.$on.mostRecentCall.args[1](); | ||||
|                 expect(mockListener).toHaveBeenCalled(); | ||||
|             it("adds selection object to scope", function () { | ||||
|                 expect(mockScope.selection).toEqual(selectable); | ||||
|                 expect(controller.selectedItem()).toEqual(mockDomainObject); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -53,9 +53,15 @@ $timeControllerToiLineColor: #00c2ff; | ||||
| $timeControllerToiLineColorHov: #fff; | ||||
| $colorTransLucBg: #666; // Used as a visual blocking element over variable backgrounds, like imagery | ||||
|  | ||||
| // General Colors | ||||
| // Foundation Colors | ||||
| $colorAlt1: #ffc700; | ||||
| $colorAlert: #ff3c00; | ||||
| $colorWarningHi: #cc0000; | ||||
| $colorWarningLo: #ff9900; | ||||
| $colorDiagnostic: #a4b442; | ||||
| $colorCommand: #3693bd; | ||||
| $colorInfo: #2294a2; | ||||
| $colorOk: #33cc33; | ||||
| $colorIconLink: #49dedb; | ||||
| $colorPausedBg: #c56f01; | ||||
| $colorPausedFg: #fff; | ||||
| @@ -84,8 +90,8 @@ $colorCreateMenuText: $colorMenuFg; | ||||
| // Form colors | ||||
| $colorCheck: $colorKey; | ||||
| $colorFormRequired: $colorAlt1; | ||||
| $colorFormValid: #33cc33; | ||||
| $colorFormError: #990000; | ||||
| $colorFormValid: $colorOk; | ||||
| $colorFormError: $colorWarningHi; | ||||
| $colorFormInvalid: #ff3300; | ||||
| $colorFormFieldErrorBg: $colorFormError; | ||||
| $colorFormFieldErrorFg: rgba(#fff, 0.6); | ||||
| @@ -109,8 +115,8 @@ $colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%); | ||||
| // Status colors, mainly used for messaging and item ancillary symbols | ||||
| $colorStatusFg: #ccc; | ||||
| $colorStatusDefault: #ccc; | ||||
| $colorStatusInfo: #62ba72; | ||||
| $colorStatusAlert: #ffa66d; | ||||
| $colorStatusInfo: $colorInfo; | ||||
| $colorStatusAlert: $colorAlert; | ||||
| $colorStatusError: #d4585c; | ||||
| $colorStatusAvailable: $colorKey; | ||||
| $colorStatusBtnBg: $colorBtnBg; | ||||
| @@ -125,14 +131,14 @@ $animPausedPulseDur: 500ms; | ||||
| $colorSelectBg: $colorBtnBg; | ||||
| $colorSelectFg: $colorBtnFg; | ||||
|  | ||||
| // Limits and staleness colors | ||||
| // Limits, status and staleness colors | ||||
| $colorTelemFresh: pullForward($colorBodyFg, 20%); | ||||
| $colorTelemStale: pushBack($colorBodyFg, 20%); | ||||
| $styleTelemStale: italic; | ||||
| $colorLimitYellowBg: rgba(#ffaa00, 0.3); | ||||
| $colorLimitYellowIc: #ffaa00; | ||||
| $colorLimitRedBg: rgba(red, 0.3); | ||||
| $colorLimitRedIc: red; | ||||
| $colorLimitYellowBg: rgba($colorWarningLo, 0.3); | ||||
| $colorLimitYellowIc: $colorWarningLo; | ||||
| $colorLimitRedBg: rgba($colorWarningHi, 0.3); | ||||
| $colorLimitRedIc: $colorWarningHi; | ||||
|  | ||||
| // Bubble colors | ||||
| $colorInfoBubbleBg: #ddd; | ||||
| @@ -195,13 +201,15 @@ $shdwItemTreeIcon: 0.6; | ||||
| $colorThumbHoverBg: $colorItemTreeHoverBg; | ||||
|  | ||||
| // Scrollbar | ||||
| $scrollbarTrackSize: 10px; | ||||
| $scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px; | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.4); | ||||
| $scrollbarTrackSize: 7px; | ||||
| $scrollbarTrackShdw: rgba(#000, 0.5) 0 1px 5px; | ||||
| $scrollbarTrackColorBg: transparent; //rgba(#000, 0.4); | ||||
| $scrollbarThumbColor: pullForward($colorBodyBg, 10%); | ||||
| $scrollbarThumbColorHov: pullForward($scrollbarThumbColor, 2%); | ||||
| $scrollbarThumbColorOverlay: pullForward($colorOvrBg, 10%); | ||||
| $scrollbarThumbColorOverlayHov: pullForward($scrollbarThumbColorOverlay, 2%); | ||||
| $scrollbarThumbColorMenu: pullForward($colorMenuBg, 20%); | ||||
| $scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%); | ||||
|  | ||||
| // Splitter | ||||
| // All splitterD* values MUST both be either odd or even numbers | ||||
|   | ||||
| @@ -13,3 +13,10 @@ | ||||
|     // For dark interfaces, darker things move back - opposite for light interfaces | ||||
|     @return darken($c, $p); | ||||
| } | ||||
|  | ||||
| @function bgFg($c) { | ||||
|     // Given a single color, return valid background and foreground versions of that color | ||||
|     $bg: darken($c, 20%); | ||||
|     $fg: lighten($c, 20%); | ||||
|     @return $bg, $fg; | ||||
| } | ||||
|   | ||||
| @@ -53,9 +53,15 @@ $timeControllerToiLineColor: $colorBodyFg; | ||||
| $timeControllerToiLineColorHov: #0052b5; | ||||
| $colorTransLucBg: #666; // Used as a visual blocking element over variable backgrounds, like imagery | ||||
|  | ||||
| // General Colors | ||||
| // Foundation Colors | ||||
| $colorAlt1: #776ba2; | ||||
| $colorAlert: #ff3c00; | ||||
| $colorWarningHi: #990000; | ||||
| $colorWarningLo: #ff9900; | ||||
| $colorDiagnostic: #a4b442; | ||||
| $colorCommand: #3693bd; | ||||
| $colorInfo: #2294a2; | ||||
| $colorOk: #33cc33; | ||||
| $colorIconLink: #49dedb; | ||||
| $colorPausedBg: #ff9900; | ||||
| $colorPausedFg: #fff; | ||||
| @@ -84,8 +90,8 @@ $colorCreateMenuText: $colorBodyFg; | ||||
| // Form colors | ||||
| $colorCheck: $colorKey; | ||||
| $colorFormRequired: $colorKey; | ||||
| $colorFormValid: #33cc33; | ||||
| $colorFormError: #990000; | ||||
| $colorFormValid: $colorOk; | ||||
| $colorFormError: $colorWarningHi; | ||||
| $colorFormInvalid: #ff2200; | ||||
| $colorFormFieldErrorBg: $colorFormError; | ||||
| $colorFormFieldErrorFg: rgba(#fff, 0.6); | ||||
| @@ -107,7 +113,7 @@ $colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%); | ||||
| $colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%); | ||||
|  | ||||
| // Status colors, mainly used for messaging and item ancillary symbols | ||||
| $colorStatusFg: #fff; | ||||
| $colorStatusFg: #999; | ||||
| $colorStatusDefault: #ccc; | ||||
| $colorStatusInfo: #60ba7b; | ||||
| $colorStatusAlert: #ffb66c; | ||||
| @@ -195,13 +201,15 @@ $shdwItemTreeIcon: none; | ||||
| $colorThumbHoverBg: $colorItemTreeHoverBg; | ||||
|  | ||||
| // Scrollbar | ||||
| $scrollbarTrackSize: 10px; | ||||
| $scrollbarTrackSize: 7px; | ||||
| $scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px; | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.2); | ||||
| $scrollbarThumbColor: darken($colorBodyBg, 50%); | ||||
| $scrollbarThumbColorHov: $colorKey; | ||||
| $scrollbarThumbColorOverlay: darken($colorOvrBg, 50%); | ||||
| $scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov; | ||||
| $scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%); | ||||
| $scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%); | ||||
|  | ||||
| // Splitter | ||||
| // All splitterD* values MUST both be either odd or even numbers | ||||
|   | ||||
| @@ -12,3 +12,11 @@ | ||||
|     // For dark interfaces, darker things move back - opposite for light interfaces | ||||
|     @return lighten($c, $p); | ||||
| } | ||||
|  | ||||
| @function bgFg($c) { | ||||
|     // Given a single color, return valid background and foreground versions of that color | ||||
|     $bg: darken($c, 20%); | ||||
|     $fg: lighten($c, 20%); | ||||
|     @return $bg, $fg; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -94,31 +94,6 @@ define([ | ||||
|             } | ||||
|         }, | ||||
|         "extensions": { | ||||
|             "versions": [ | ||||
|                 { | ||||
|                     "name": "Version", | ||||
|                     "value": "@@version", | ||||
|                     "priority": 999 | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "Built", | ||||
|                     "value": "@@timestamp", | ||||
|                     "description": "The date on which this version of the client was built.", | ||||
|                     "priority": 990 | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "Revision", | ||||
|                     "value": "@@revision", | ||||
|                     "description": "A unique revision identifier for the client sources.", | ||||
|                     "priority": 995 | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "Branch", | ||||
|                     "value": "@@branch", | ||||
|                     "description": "The name of the branch that was used during the build.", | ||||
|                     "priority": 994 | ||||
|                 } | ||||
|             ], | ||||
|             "components": [ | ||||
|                 { | ||||
|                     "provides": "objectService", | ||||
|   | ||||
| @@ -65,6 +65,20 @@ define(['csv'], function (CSV) { | ||||
|         this.saveAs(blob, filename); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Export an object as a JSON file. Triggers a download using the function | ||||
|      * provided when the ExportService was instantiated. | ||||
|      * | ||||
|      * @param {Object} obj an object to be exported as JSON | ||||
|      * @param {ExportOptions} [options] additional parameters for the file | ||||
|      *        export | ||||
|      */ | ||||
|     ExportService.prototype.exportJSON = function (obj, options) { | ||||
|         var filename = (options && options.filename) || "test-export.json"; | ||||
|         var jsonText = JSON.stringify(obj); | ||||
|         var blob = new Blob([jsonText], {type: "application/json"}); | ||||
|         this.saveAs(blob, filename); | ||||
|     }; | ||||
|     /** | ||||
|      * Additional parameters for file export. | ||||
|      * @typedef ExportOptions | ||||
|   | ||||
| @@ -1,54 +0,0 @@ | ||||
| define([ | ||||
|     'text!./res/templates/autoflow-tabular.html', | ||||
|     './src/AutoflowTabularController', | ||||
|     './src/MCTAutoflowTable' | ||||
| ], function ( | ||||
|     autoflowTabularTemplate, | ||||
|     AutoflowTabularController, | ||||
|     MCTAutoflowTable | ||||
| ) { | ||||
|     return function (options) { | ||||
|         return function (openmct) { | ||||
|             openmct.legacyRegistry.register("platform/features/autoflow", { | ||||
|                 "name": "WARP Telemetry Adapter", | ||||
|                 "description": "Retrieves telemetry from the WARP Server and provides related types and views.", | ||||
|                 "resources": "res", | ||||
|                 "extensions": { | ||||
|                     "views": [ | ||||
|                         { | ||||
|                             "key": "autoflow", | ||||
|                             "name": "Autoflow Tabular", | ||||
|                             "cssClass": "icon-packet", | ||||
|                             "description": "A tabular view of packet contents.", | ||||
|                             "template": autoflowTabularTemplate, | ||||
|                             "type": options && options.type, | ||||
|                             "needs": [ | ||||
|                                 "telemetry" | ||||
|                             ], | ||||
|                             "delegation": true | ||||
|                         } | ||||
|                     ], | ||||
|                     "controllers": [ | ||||
|                         { | ||||
|                             "key": "AutoflowTabularController", | ||||
|                             "implementation": AutoflowTabularController, | ||||
|                             "depends": [ | ||||
|                                 "$scope", | ||||
|                                 "$timeout", | ||||
|                                 "telemetrySubscriber" | ||||
|                             ] | ||||
|                         } | ||||
|                     ], | ||||
|                     "directives": [ | ||||
|                         { | ||||
|                             "key": "mctAutoflowTable", | ||||
|                             "implementation": MCTAutoflowTable | ||||
|                         } | ||||
|                     ] | ||||
|                 } | ||||
|             }); | ||||
|             openmct.legacyRegistry.enable("platform/features/autoflow"); | ||||
|         }; | ||||
|     }; | ||||
| }); | ||||
|  | ||||
| @@ -1,26 +0,0 @@ | ||||
| <div class="items-holder abs contents autoflow obj-value-format" | ||||
|      ng-controller="AutoflowTabularController as autoflow"> | ||||
|     <div class="abs l-flex-row holder t-autoflow-header l-autoflow-header"> | ||||
|         <mct-include key="'input-filter'" | ||||
|                      ng-model="autoflow.filter" | ||||
|                      class="flex-elem"> | ||||
|         </mct-include> | ||||
|         <div class="flex-elem grows t-last-update" title="Last Update">{{autoflow.updated()}}</div> | ||||
|         <a title="Change column width" | ||||
|             class="s-button flex-elem icon-arrows-right-left change-column-width" | ||||
|             ng-click="autoflow.increaseColumnWidth()"></a> | ||||
|     </div> | ||||
|     <div class="abs t-autoflow-items l-autoflow-items" | ||||
|          mct-resize="autoflow.setBounds(bounds)" | ||||
|          mct-resize-interval="50"> | ||||
|         <mct-autoflow-table values="autoflow.rangeValues()" | ||||
|                             objects="autoflow.getTelemetryObjects()" | ||||
|                             rows="autoflow.getRows()" | ||||
|                             classes="autoflow.classes()" | ||||
|                             updated="autoflow.updated()" | ||||
|                             column-width="autoflow.columnWidth()" | ||||
|                             counter="autoflow.counter()" | ||||
|                             > | ||||
|         </mct-autoflow-table> | ||||
|     </div> | ||||
| </div> | ||||
| @@ -1,169 +0,0 @@ | ||||
| /*global angular*/ | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|  | ||||
|         /** | ||||
|          * The link step for the `mct-autoflow-table` directive; | ||||
|          * watches scope and updates the DOM appropriately. | ||||
|          * See documentation in `MCTAutoflowTable.js` for the rationale | ||||
|          * for including this directive, as well as for an explanation | ||||
|          * of which values are placed in scope. | ||||
|          * | ||||
|          * @constructor | ||||
|          * @param {Scope} scope the scope for this usage of the directive | ||||
|          * @param element the jqLite-wrapped element which used this directive | ||||
|          */ | ||||
|         function AutoflowTableLinker(scope, element) { | ||||
|             var objects, // Domain objects at last structure refresh | ||||
|                 rows, // Number of rows from last structure refresh | ||||
|                 priorClasses = {}, | ||||
|                 valueSpans = {}; // Span elements to put data values in | ||||
|  | ||||
|             // Create a new name-value pair in the specified column | ||||
|             function createListItem(domainObject, ul) { | ||||
|                 // Create a new li, and spans to go in it. | ||||
|                 var li = angular.element('<li>'), | ||||
|                     titleSpan = angular.element('<span>'), | ||||
|                     valueSpan = angular.element('<span>'); | ||||
|  | ||||
|                 // Place spans in the li, and li into the column. | ||||
|                 // valueSpan must precede titleSpan in the DOM due to new CSS float approach | ||||
|                 li.append(valueSpan).append(titleSpan); | ||||
|                 ul.append(li); | ||||
|  | ||||
|                 // Style appropriately | ||||
|                 li.addClass('l-autoflow-row'); | ||||
|                 titleSpan.addClass('l-autoflow-item l'); | ||||
|                 valueSpan.addClass('l-autoflow-item r l-obj-val-format'); | ||||
|  | ||||
|                 // Set text/tooltip for the name-value row | ||||
|                 titleSpan.text(domainObject.getModel().name); | ||||
|                 titleSpan.attr("title", domainObject.getModel().name); | ||||
|  | ||||
|                 // Keep a reference to the span which will hold the | ||||
|                 // data value, to populate in the next refreshValues call | ||||
|                 valueSpans[domainObject.getId()] = valueSpan; | ||||
|  | ||||
|                 return li; | ||||
|             } | ||||
|  | ||||
|             // Create a new column of name-value pairs in this table. | ||||
|             function createColumn(el) { | ||||
|                 // Create a ul | ||||
|                 var ul = angular.element('<ul>'); | ||||
|  | ||||
|                 // Add it into the mct-autoflow-table | ||||
|                 el.append(ul); | ||||
|  | ||||
|                 // Style appropriately | ||||
|                 ul.addClass('l-autoflow-col'); | ||||
|  | ||||
|                 // Get the current col width and apply at time of column creation | ||||
|                 // Important to do this here, as new columns could be created after | ||||
|                 // the user has changed the width. | ||||
|                 ul.css('width', scope.columnWidth + 'px'); | ||||
|  | ||||
|                 // Return it, so some li elements can be added | ||||
|                 return ul; | ||||
|             } | ||||
|  | ||||
|             // Change the width of the columns when user clicks the resize button. | ||||
|             function resizeColumn() { | ||||
|                 element.find('ul').css('width', scope.columnWidth + 'px'); | ||||
|             } | ||||
|  | ||||
|             // Rebuild the DOM associated with this table. | ||||
|             function rebuild(domainObjects, rowCount) { | ||||
|                 var activeColumn; | ||||
|  | ||||
|                 // Empty out our cached span elements | ||||
|                 valueSpans = {}; | ||||
|  | ||||
|                 // Start with an empty DOM beneath this directive | ||||
|                 element.html(""); | ||||
|  | ||||
|                 // Add DOM elements for each domain object being displayed | ||||
|                 // in this table. | ||||
|                 domainObjects.forEach(function (object, index) { | ||||
|                     // Start a new column if we'd run out of room | ||||
|                     if (index % rowCount === 0) { | ||||
|                         activeColumn = createColumn(element); | ||||
|                     } | ||||
|                     // Add the DOM elements for that object to whichever | ||||
|                     // column (a `ul` element) is current. | ||||
|                     createListItem(object, activeColumn); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // Update spans with values, as made available via the | ||||
|             // `values` attribute of this directive. | ||||
|             function refreshValues() { | ||||
|                 // Get the available values | ||||
|                 var values = scope.values || {}, | ||||
|                     classes = scope.classes || {}; | ||||
|  | ||||
|                 // Populate all spans with those values (or clear | ||||
|                 // those spans if no value is available) | ||||
|                 (objects || []).forEach(function (object) { | ||||
|                     var id = object.getId(), | ||||
|                         span = valueSpans[id], | ||||
|                         value; | ||||
|  | ||||
|                     if (span) { | ||||
|                         // Look up the value... | ||||
|                         value = values[id]; | ||||
|                         // ...and convert to empty string if it's undefined | ||||
|                         value = value === undefined ? "" : value; | ||||
|                         span.attr("data-value", value); | ||||
|  | ||||
|                         // Update the span | ||||
|                         span.text(value); | ||||
|                         span.attr("title", value); | ||||
|                         span.removeClass(priorClasses[id]); | ||||
|                         span.addClass(classes[id]); | ||||
|                         priorClasses[id] = classes[id]; | ||||
|                     } | ||||
|                     // Also need stale/alert/ok class | ||||
|                     // on span | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // Refresh the DOM for this table, if necessary | ||||
|             function refreshStructure() { | ||||
|                 // Only rebuild if number of rows or set of objects | ||||
|                 // has changed; otherwise, our structure is still valid. | ||||
|                 if (scope.objects !== objects || | ||||
|                         scope.rows !== rows) { | ||||
|  | ||||
|                     // Track those values to support future refresh checks | ||||
|                     objects = scope.objects; | ||||
|                     rows = scope.rows; | ||||
|  | ||||
|                     // Rebuild the DOM | ||||
|                     rebuild(objects || [], rows || 1); | ||||
|  | ||||
|                     // Refresh all data values shown | ||||
|                     refreshValues(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Changing the domain objects in use or the number | ||||
|             // of rows should trigger a structure change (DOM rebuild) | ||||
|             scope.$watch("objects", refreshStructure); | ||||
|             scope.$watch("rows", refreshStructure); | ||||
|  | ||||
|             // When the current column width has been changed, resize the column | ||||
|             scope.$watch('columnWidth', resizeColumn); | ||||
|  | ||||
|             // When the last-updated time ticks, | ||||
|             scope.$watch("updated", refreshValues); | ||||
|  | ||||
|             // Update displayed values when the counter changes. | ||||
|             scope.$watch("counter", refreshValues); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return AutoflowTableLinker; | ||||
|     } | ||||
| ); | ||||
| @@ -1,324 +0,0 @@ | ||||
|  | ||||
| define( | ||||
|     ['moment'], | ||||
|     function (moment) { | ||||
|  | ||||
|         var ROW_HEIGHT = 16, | ||||
|             SLIDER_HEIGHT = 10, | ||||
|             INITIAL_COLUMN_WIDTH = 225, | ||||
|             MAX_COLUMN_WIDTH = 525, | ||||
|             COLUMN_WIDTH_STEP = 25, | ||||
|             DEBOUNCE_INTERVAL = 100, | ||||
|             DATE_FORMAT = "YYYY-DDD HH:mm:ss.SSS\\Z", | ||||
|             NOT_UPDATED = "No updates", | ||||
|             EMPTY_ARRAY = []; | ||||
|  | ||||
|         /** | ||||
|          * Responsible for supporting the autoflow tabular view. | ||||
|          * Implements the all-over logic which drives that view, | ||||
|          * mediating between template-provided areas, the included | ||||
|          * `mct-autoflow-table` directive, and the underlying | ||||
|          * domain object model. | ||||
|          * @constructor | ||||
|          */ | ||||
|         function AutflowTabularController( | ||||
|             $scope, | ||||
|             $timeout, | ||||
|             telemetrySubscriber | ||||
|         ) { | ||||
|             var filterValue = "", | ||||
|                 filterValueLowercase = "", | ||||
|                 subscription, | ||||
|                 filteredObjects = [], | ||||
|                 lastUpdated = {}, | ||||
|                 updateText = NOT_UPDATED, | ||||
|                 rangeValues = {}, | ||||
|                 classes = {}, | ||||
|                 limits = {}, | ||||
|                 updatePending = false, | ||||
|                 lastBounce = Number.NEGATIVE_INFINITY, | ||||
|                 columnWidth = INITIAL_COLUMN_WIDTH, | ||||
|                 rows = 1, | ||||
|                 counter = 0; | ||||
|  | ||||
|             // Trigger an update of the displayed table by incrementing | ||||
|             // the counter that it watches. | ||||
|             function triggerDisplayUpdate() { | ||||
|                 counter += 1; | ||||
|             } | ||||
|  | ||||
|             // Check whether or not an object's name matches the | ||||
|             // user-entered filter value. | ||||
|             function filterObject(domainObject) { | ||||
|                 return (domainObject.getModel().name || "") | ||||
|                     .toLowerCase() | ||||
|                     .indexOf(filterValueLowercase) !== -1; | ||||
|             } | ||||
|  | ||||
|             // Comparator for sorting points back into packet order | ||||
|             function compareObject(objectA, objectB) { | ||||
|                 var indexA = objectA.getModel().index || 0, | ||||
|                     indexB = objectB.getModel().index || 0; | ||||
|                 return indexA - indexB; | ||||
|             } | ||||
|  | ||||
|             // Update the list of currently-displayed objects; these | ||||
|             // will be the subset of currently subscribed-to objects | ||||
|             // which match a user-entered filter. | ||||
|             function doUpdateFilteredObjects() { | ||||
|                 // Generate the list | ||||
|                 filteredObjects = ( | ||||
|                     subscription ? | ||||
|                             subscription.getTelemetryObjects() : | ||||
|                             [] | ||||
|                 ).filter(filterObject).sort(compareObject); | ||||
|  | ||||
|                 // Clear the pending flag | ||||
|                 updatePending = false; | ||||
|  | ||||
|                 // Track when this occurred, so that we can wait | ||||
|                 // a whole before updating again. | ||||
|                 lastBounce = Date.now(); | ||||
|  | ||||
|                 triggerDisplayUpdate(); | ||||
|             } | ||||
|  | ||||
|             // Request an update to the list of current objects; this may | ||||
|             // run on a timeout to avoid excessive calls, e.g. while the user | ||||
|             // is typing a filter. | ||||
|             function updateFilteredObjects() { | ||||
|                 // Don't do anything if an update is already scheduled | ||||
|                 if (!updatePending) { | ||||
|                     if (Date.now() > lastBounce + DEBOUNCE_INTERVAL) { | ||||
|                         // Update immediately if it's been long enough | ||||
|                         doUpdateFilteredObjects(); | ||||
|                     } else { | ||||
|                         // Otherwise, update later, and track that we have | ||||
|                         // an update pending so that subsequent calls can | ||||
|                         // be ignored. | ||||
|                         updatePending = true; | ||||
|                         $timeout(doUpdateFilteredObjects, DEBOUNCE_INTERVAL); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Track the latest data values for this domain object | ||||
|             function recordData(telemetryObject) { | ||||
|                 // Get latest domain/range values for this object. | ||||
|                 var id = telemetryObject.getId(), | ||||
|                     domainValue = subscription.getDomainValue(telemetryObject), | ||||
|                     rangeValue = subscription.getRangeValue(telemetryObject); | ||||
|  | ||||
|                 // Track the most recent timestamp change observed... | ||||
|                 if (domainValue !== undefined && domainValue !== lastUpdated[id]) { | ||||
|                     lastUpdated[id] = domainValue; | ||||
|                     // ... and update the displayable text for that timestamp | ||||
|                     updateText = isNaN(domainValue) ? "" : | ||||
|                             moment.utc(domainValue).format(DATE_FORMAT); | ||||
|                 } | ||||
|  | ||||
|                 // Store data values into the rangeValues structure, which | ||||
|                 // will be used to populate the table itself. | ||||
|                 // Note that we want full precision here. | ||||
|                 rangeValues[id] = rangeValue; | ||||
|  | ||||
|                 // Update limit states as well | ||||
|                 classes[id] = limits[id] && (limits[id].evaluate({ | ||||
|                     // This relies on external knowledge that the | ||||
|                     // range value of a telemetry point is encoded | ||||
|                     // in its datum as "value." | ||||
|                     value: rangeValue | ||||
|                 }) || {}).cssClass; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             // Look at telemetry objects from the subscription; this is watched | ||||
|             // to detect changes from the subscription. | ||||
|             function subscribedTelemetry() { | ||||
|                 return subscription ? | ||||
|                         subscription.getTelemetryObjects() : EMPTY_ARRAY; | ||||
|             } | ||||
|  | ||||
|             // Update the data values which will be used to populate the table | ||||
|             function updateValues() { | ||||
|                 subscribedTelemetry().forEach(recordData); | ||||
|                 triggerDisplayUpdate(); | ||||
|             } | ||||
|  | ||||
|             // Getter-setter function for user-entered filter text. | ||||
|             function filter(value) { | ||||
|                 // If value was specified, we're a setter | ||||
|                 if (value !== undefined) { | ||||
|                     // Store the new value | ||||
|                     filterValue = value; | ||||
|                     filterValueLowercase = value.toLowerCase(); | ||||
|                     // Change which objects appear in the table | ||||
|                     updateFilteredObjects(); | ||||
|                 } | ||||
|  | ||||
|                 // Always act as a getter | ||||
|                 return filterValue; | ||||
|             } | ||||
|  | ||||
|             // Update the bounds (width and height) of this view; | ||||
|             // called from the mct-resize directive. Recalculates how | ||||
|             // many rows should appear in the contained table. | ||||
|             function setBounds(bounds) { | ||||
|                 var availableSpace = bounds.height - SLIDER_HEIGHT; | ||||
|                 rows = Math.max(1, Math.floor(availableSpace / ROW_HEIGHT)); | ||||
|             } | ||||
|  | ||||
|             // Increment the current column width, up to the defined maximum. | ||||
|             // When the max is hit, roll back to the default. | ||||
|             function increaseColumnWidth() { | ||||
|                 columnWidth += COLUMN_WIDTH_STEP; | ||||
|                 // Cycle down to the initial width instead of exceeding max | ||||
|                 columnWidth = columnWidth > MAX_COLUMN_WIDTH ? | ||||
|                         INITIAL_COLUMN_WIDTH : columnWidth; | ||||
|             } | ||||
|  | ||||
|             // Get displayable text for last-updated value | ||||
|             function updated() { | ||||
|                 return updateText; | ||||
|             } | ||||
|  | ||||
|             // Unsubscribe, if a subscription is active. | ||||
|             function releaseSubscription() { | ||||
|                 if (subscription) { | ||||
|                     subscription.unsubscribe(); | ||||
|                     subscription = undefined; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Update set of telemetry objects managed by this view | ||||
|             function updateTelemetryObjects(telemetryObjects) { | ||||
|                 updateFilteredObjects(); | ||||
|                 limits = {}; | ||||
|                 telemetryObjects.forEach(function (telemetryObject) { | ||||
|                     var id = telemetryObject.getId(); | ||||
|                     limits[id] = telemetryObject.getCapability('limit'); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // Create a subscription for the represented domain object. | ||||
|             // This will resolve capability delegation as necessary. | ||||
|             function makeSubscription(domainObject) { | ||||
|                 // Unsubscribe, if there is an existing subscription | ||||
|                 releaseSubscription(); | ||||
|  | ||||
|                 // Clear updated timestamp | ||||
|                 lastUpdated = {}; | ||||
|                 updateText = NOT_UPDATED; | ||||
|  | ||||
|                 // Create a new subscription; telemetrySubscriber gets | ||||
|                 // to do the meaningful work here. | ||||
|                 subscription = domainObject && telemetrySubscriber.subscribe( | ||||
|                     domainObject, | ||||
|                     updateValues | ||||
|                 ); | ||||
|  | ||||
|                 // Our set of in-view telemetry objects may have changed, | ||||
|                 // so update the set that is being passed down to the table. | ||||
|                 updateFilteredObjects(); | ||||
|             } | ||||
|  | ||||
|             // Watch for changes to the set of objects which have telemetry | ||||
|             $scope.$watch(subscribedTelemetry, updateTelemetryObjects); | ||||
|  | ||||
|             // Watch for the represented domainObject (this field will | ||||
|             // be populated by mct-representation) | ||||
|             $scope.$watch("domainObject", makeSubscription); | ||||
|  | ||||
|             // Make sure we unsubscribe when this view is destroyed. | ||||
|             $scope.$on("$destroy", releaseSubscription); | ||||
|  | ||||
|             return { | ||||
|                 /** | ||||
|                  * Get the number of rows which should be shown in this table. | ||||
|                  * @return {number} the number of rows to show | ||||
|                  */ | ||||
|                 getRows: function () { | ||||
|                     return rows; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Get the objects which should currently be displayed in | ||||
|                  * this table. This will be watched, so the return value | ||||
|                  * should be stable when this list is unchanging. Only | ||||
|                  * objects which match the user-entered filter value should | ||||
|                  * be returned here. | ||||
|                  * @return {DomainObject[]} the domain objects to include in | ||||
|                  *         this table. | ||||
|                  */ | ||||
|                 getTelemetryObjects: function () { | ||||
|                     return filteredObjects; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Set the bounds (width/height) of this autoflow tabular view. | ||||
|                  * The template must ensure that these bounds are tracked on | ||||
|                  * the table area only. | ||||
|                  * @param bounds the bounds; and object with `width` and | ||||
|                  *        `height` properties, both as numbers, in pixels. | ||||
|                  */ | ||||
|                 setBounds: setBounds, | ||||
|                 /** | ||||
|                  * Increments the width of the autoflow column. | ||||
|                  * Setting does not yet persist. | ||||
|                  */ | ||||
|                 increaseColumnWidth: increaseColumnWidth, | ||||
|                 /** | ||||
|                  * Get-or-set the user-supplied filter value. | ||||
|                  * @param {string} [value] the new filter value; omit to use | ||||
|                  *        as a getter | ||||
|                  * @returns {string} the user-supplied filter value | ||||
|                  */ | ||||
|                 filter: filter, | ||||
|                 /** | ||||
|                  * Get all range values for use in this table. These will be | ||||
|                  * returned as an object of key-value pairs, where keys are | ||||
|                  * domain object IDs, and values are the most recently observed | ||||
|                  * data values associated with those objects, formatted for | ||||
|                  * display. | ||||
|                  * @returns {object.<string,string>} most recent values | ||||
|                  */ | ||||
|                 rangeValues: function () { | ||||
|                     return rangeValues; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Get CSS classes to apply to specific rows, representing limit | ||||
|                  * states and/or stale states. These are returned as key-value | ||||
|                  * pairs where keys are domain object IDs, and values are CSS | ||||
|                  * classes to display for domain objects with those IDs. | ||||
|                  * @returns {object.<string,string>} CSS classes | ||||
|                  */ | ||||
|                 classes: function () { | ||||
|                     return classes; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Get the "last updated" text for this view; this will be | ||||
|                  * the most recent timestamp observed for any telemetry- | ||||
|                  * providing object, formatted for display. | ||||
|                  * @returns {string} the time of the most recent update | ||||
|                  */ | ||||
|                 updated: updated, | ||||
|                 /** | ||||
|                  * Get the current column width, in pixels. | ||||
|                  * @returns {number} column width | ||||
|                  */ | ||||
|                 columnWidth: function () { | ||||
|                     return columnWidth; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Keep a counter and increment this whenever the display | ||||
|                  * should be updated; this will be watched by the | ||||
|                  * `mct-autoflow-table`. | ||||
|                  * @returns {number} a counter value | ||||
|                  */ | ||||
|                 counter: function () { | ||||
|                     return counter; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         return AutflowTabularController; | ||||
|     } | ||||
| ); | ||||
| @@ -1,60 +0,0 @@ | ||||
|  | ||||
| define( | ||||
|     ["./AutoflowTableLinker"], | ||||
|     function (AutoflowTableLinker) { | ||||
|  | ||||
|         /** | ||||
|          * The `mct-autoflow-table` directive specifically supports | ||||
|          * autoflow tabular views; it is not intended for use outside | ||||
|          * of that view. | ||||
|          * | ||||
|          * This directive is responsible for creating the structure | ||||
|          * of the table in this view, and for updating its values. | ||||
|          * While this is achievable using a regular Angular template, | ||||
|          * this is undesirable from the perspective of performance | ||||
|          * due to the number of watches that can be involved for large | ||||
|          * tables. Instead, this directive will maintain a small number | ||||
|          * of watches, rebuilding table structure only when necessary, | ||||
|          * and updating displayed values in the more common case of | ||||
|          * new data arriving. | ||||
|          * | ||||
|          * @constructor | ||||
|          */ | ||||
|         function MCTAutoflowTable() { | ||||
|             return { | ||||
|                 // Only applicable at the element level | ||||
|                 restrict: "E", | ||||
|  | ||||
|                 // The link function; handles DOM update/manipulation | ||||
|                 link: AutoflowTableLinker, | ||||
|  | ||||
|                 // Parameters to pass from attributes into scope | ||||
|                 scope: { | ||||
|                     // Set of domain objects to show in the table | ||||
|                     objects: "=", | ||||
|  | ||||
|                     // Values for those objects, by ID | ||||
|                     values: "=", | ||||
|  | ||||
|                     // CSS classes to show for objects, by ID | ||||
|                     classes: "=", | ||||
|  | ||||
|                     // Number of rows to show before autoflowing | ||||
|                     rows: "=", | ||||
|  | ||||
|                     // Time of last update; watched to refresh values | ||||
|                     updated: "=", | ||||
|  | ||||
|                     // Current width of the autoflow column | ||||
|                     columnWidth: "=", | ||||
|  | ||||
|                     // A counter used to trigger display updates | ||||
|                     counter: "=" | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         return MCTAutoflowTable; | ||||
|  | ||||
|     } | ||||
| ); | ||||
| @@ -1,178 +0,0 @@ | ||||
|  | ||||
| define( | ||||
|     ["../src/AutoflowTableLinker"], | ||||
|     function (AutoflowTableLinker) { | ||||
|  | ||||
|         describe("The mct-autoflow-table linker", function () { | ||||
|             var cachedAngular, | ||||
|                 mockAngular, | ||||
|                 mockScope, | ||||
|                 mockElement, | ||||
|                 mockElements, | ||||
|                 linker; | ||||
|  | ||||
|             // Utility function to generate more mock elements | ||||
|             function createMockElement(html) { | ||||
|                 var mockEl = jasmine.createSpyObj( | ||||
|                     "element-" + html, | ||||
|                     [ | ||||
|                         "append", | ||||
|                         "addClass", | ||||
|                         "removeClass", | ||||
|                         "text", | ||||
|                         "attr", | ||||
|                         "html", | ||||
|                         "css", | ||||
|                         "find" | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockEl.testHtml = html; | ||||
|                 mockEl.append.andReturn(mockEl); | ||||
|                 mockElements.push(mockEl); | ||||
|                 return mockEl; | ||||
|             } | ||||
|  | ||||
|             function createMockDomainObject(id) { | ||||
|                 var mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject-" + id, | ||||
|                     ["getId", "getModel"] | ||||
|                 ); | ||||
|                 mockDomainObject.getId.andReturn(id); | ||||
|                 mockDomainObject.getModel.andReturn({name: id.toUpperCase()}); | ||||
|                 return mockDomainObject; | ||||
|             } | ||||
|  | ||||
|             function fireWatch(watchExpression, value) { | ||||
|                 mockScope.$watch.calls.forEach(function (call) { | ||||
|                     if (call.args[0] === watchExpression) { | ||||
|                         call.args[1](value); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // AutoflowTableLinker accesses Angular in the global | ||||
|             // scope, since it is not injectable; we simulate that | ||||
|             // here by adding/removing it to/from the window object. | ||||
|             beforeEach(function () { | ||||
|                 mockElements = []; | ||||
|  | ||||
|                 mockAngular = jasmine.createSpyObj("angular", ["element"]); | ||||
|                 mockScope = jasmine.createSpyObj("scope", ["$watch"]); | ||||
|                 mockElement = createMockElement('<div>'); | ||||
|  | ||||
|                 mockAngular.element.andCallFake(createMockElement); | ||||
|  | ||||
|                 if (window.angular !== undefined) { | ||||
|                     cachedAngular = window.angular; | ||||
|                 } | ||||
|                 window.angular = mockAngular; | ||||
|  | ||||
|                 linker = new AutoflowTableLinker(mockScope, mockElement); | ||||
|             }); | ||||
|  | ||||
|             afterEach(function () { | ||||
|                 if (cachedAngular !== undefined) { | ||||
|                     window.angular = cachedAngular; | ||||
|                 } else { | ||||
|                     delete window.angular; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             it("watches for changes in inputs", function () { | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
|                     "objects", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
|                     "rows", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
|                     "counter", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("changes structure when domain objects change", function () { | ||||
|                 // Set up scope | ||||
|                 mockScope.rows = 4; | ||||
|                 mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f'] | ||||
|                     .map(createMockDomainObject); | ||||
|  | ||||
|                 // Fire an update to the set of objects | ||||
|                 fireWatch("objects"); | ||||
|  | ||||
|                 // Should have rebuilt with two columns of | ||||
|                 // four and two rows each; first, by clearing... | ||||
|                 expect(mockElement.html).toHaveBeenCalledWith(""); | ||||
|  | ||||
|                 // Should have appended two columns... | ||||
|                 expect(mockElement.append.calls.length).toEqual(2); | ||||
|  | ||||
|                 // ...which should have received two and four rows each | ||||
|                 expect(mockElement.append.calls[0].args[0].append.calls.length) | ||||
|                     .toEqual(4); | ||||
|                 expect(mockElement.append.calls[1].args[0].append.calls.length) | ||||
|                     .toEqual(2); | ||||
|             }); | ||||
|  | ||||
|             it("updates values", function () { | ||||
|                 var mockSpans; | ||||
|  | ||||
|                 mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f'] | ||||
|                     .map(createMockDomainObject); | ||||
|                 mockScope.values = { a: 0 }; | ||||
|  | ||||
|                 // Fire an update to the set of values | ||||
|                 fireWatch("objects"); | ||||
|                 fireWatch("updated"); | ||||
|  | ||||
|                 // Get all created spans | ||||
|                 mockSpans = mockElements.filter(function (mockElem) { | ||||
|                     return mockElem.testHtml === '<span>'; | ||||
|                 }); | ||||
|  | ||||
|                 // First span should be a, should have gotten this value. | ||||
|                 // This test detects, in particular, WTD-749 | ||||
|                 expect(mockSpans[0].text).toHaveBeenCalledWith('A'); | ||||
|                 expect(mockSpans[1].text).toHaveBeenCalledWith(0); | ||||
|             }); | ||||
|  | ||||
|             it("listens for changes in column width", function () { | ||||
|                 var mockUL = createMockElement("<ul>"); | ||||
|                 mockElement.find.andReturn(mockUL); | ||||
|                 mockScope.columnWidth = 200; | ||||
|                 fireWatch("columnWidth", mockScope.columnWidth); | ||||
|                 expect(mockUL.css).toHaveBeenCalledWith("width", "200px"); | ||||
|             }); | ||||
|  | ||||
|             it("updates CSS classes", function () { | ||||
|                 var mockSpans; | ||||
|  | ||||
|                 mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f'] | ||||
|                     .map(createMockDomainObject); | ||||
|                 mockScope.values = { a: "a value to find" }; | ||||
|                 mockScope.classes = { a: 'class-a' }; | ||||
|  | ||||
|                 // Fire an update to the set of values | ||||
|                 fireWatch("objects"); | ||||
|                 fireWatch("updated"); | ||||
|  | ||||
|                 // Figure out which span holds the relevant value... | ||||
|                 mockSpans = mockElements.filter(function (mockElem) { | ||||
|                     return mockElem.testHtml === '<span>'; | ||||
|                 }).filter(function (mockSpan) { | ||||
|                     var attrCalls = mockSpan.attr.calls; | ||||
|                     return attrCalls.some(function (call) { | ||||
|                         return call.args[0] === 'title' && | ||||
|                                 call.args[1] === mockScope.values.a; | ||||
|                     }); | ||||
|                 }); | ||||
|  | ||||
|                 // ...and make sure it also has had its class applied | ||||
|                 expect(mockSpans[0].addClass) | ||||
|                     .toHaveBeenCalledWith(mockScope.classes.a); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -1,341 +0,0 @@ | ||||
|  | ||||
| define( | ||||
|     ["../src/AutoflowTabularController"], | ||||
|     function (AutoflowTabularController) { | ||||
|  | ||||
|         describe("The autoflow tabular controller", function () { | ||||
|             var mockScope, | ||||
|                 mockTimeout, | ||||
|                 mockSubscriber, | ||||
|                 mockDomainObject, | ||||
|                 mockSubscription, | ||||
|                 controller; | ||||
|  | ||||
|             // Fire watches that are registered as functions. | ||||
|             function fireFnWatches() { | ||||
|                 mockScope.$watch.calls.forEach(function (call) { | ||||
|                     if (typeof call.args[0] === 'function') { | ||||
|                         call.args[1](call.args[0]()); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpyObj( | ||||
|                     "$scope", | ||||
|                     ["$on", "$watch"] | ||||
|                 ); | ||||
|                 mockTimeout = jasmine.createSpy("$timeout"); | ||||
|                 mockSubscriber = jasmine.createSpyObj( | ||||
|                     "telemetrySubscriber", | ||||
|                     ["subscribe"] | ||||
|                 ); | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     ["getId", "getModel", "getCapability"] | ||||
|                 ); | ||||
|                 mockSubscription = jasmine.createSpyObj( | ||||
|                     "subscription", | ||||
|                     [ | ||||
|                         "unsubscribe", | ||||
|                         "getTelemetryObjects", | ||||
|                         "getDomainValue", | ||||
|                         "getRangeValue" | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 mockSubscriber.subscribe.andReturn(mockSubscription); | ||||
|                 mockDomainObject.getModel.andReturn({name: "something"}); | ||||
|  | ||||
|                 controller = new AutoflowTabularController( | ||||
|                     mockScope, | ||||
|                     mockTimeout, | ||||
|                     mockSubscriber | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("listens for the represented domain object", function () { | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
|                     "domainObject", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("provides a getter-setter function for filtering", function () { | ||||
|                 expect(controller.filter()).toEqual(""); | ||||
|                 controller.filter("something"); | ||||
|                 expect(controller.filter()).toEqual("something"); | ||||
|             }); | ||||
|  | ||||
|             it("tracks bounds and adjust number of rows accordingly", function () { | ||||
|                 // Rows are 15px high, and need room for an 10px slider | ||||
|                 controller.setBounds({ width: 700, height: 120 }); | ||||
|                 expect(controller.getRows()).toEqual(6); // 110 usable height / 16px | ||||
|                 controller.setBounds({ width: 700, height: 240 }); | ||||
|                 expect(controller.getRows()).toEqual(14); // 230 usable height / 16px | ||||
|             }); | ||||
|  | ||||
|             it("subscribes to a represented object's telemetry", function () { | ||||
|                 // Set up subscription, scope | ||||
|                 mockSubscription.getTelemetryObjects | ||||
|                     .andReturn([mockDomainObject]); | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|  | ||||
|                 // Invoke the watcher with represented domain object | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|  | ||||
|                 // Should have subscribed to it | ||||
|                 expect(mockSubscriber.subscribe).toHaveBeenCalledWith( | ||||
|                     mockDomainObject, | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|  | ||||
|                 // Should report objects as reported from subscription | ||||
|                 expect(controller.getTelemetryObjects()) | ||||
|                     .toEqual([mockDomainObject]); | ||||
|             }); | ||||
|  | ||||
|             it("releases subscriptions on destroy", function () { | ||||
|                 // Set up subscription... | ||||
|                 mockSubscription.getTelemetryObjects | ||||
|                     .andReturn([mockDomainObject]); | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|  | ||||
|                 // Verify precondition | ||||
|                 expect(mockSubscription.unsubscribe).not.toHaveBeenCalled(); | ||||
|  | ||||
|                 // Make sure we're listening for $destroy | ||||
|                 expect(mockScope.$on).toHaveBeenCalledWith( | ||||
|                     "$destroy", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|  | ||||
|                 // Fire a destroy event | ||||
|                 mockScope.$on.mostRecentCall.args[1](); | ||||
|  | ||||
|                 // Should have unsubscribed | ||||
|                 expect(mockSubscription.unsubscribe).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("presents latest values and latest update state", function () { | ||||
|                 // Make sure values are available | ||||
|                 mockSubscription.getDomainValue.andReturn(402654321123); | ||||
|                 mockSubscription.getRangeValue.andReturn(789); | ||||
|                 mockDomainObject.getId.andReturn('testId'); | ||||
|  | ||||
|                 // Set up subscription... | ||||
|                 mockSubscription.getTelemetryObjects | ||||
|                     .andReturn([mockDomainObject]); | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|  | ||||
|                 // Fire subscription callback | ||||
|                 mockSubscriber.subscribe.mostRecentCall.args[1](); | ||||
|  | ||||
|                 // ...and exposed the results for template to consume | ||||
|                 expect(controller.updated()).toEqual("1982-278 08:25:21.123Z"); | ||||
|                 expect(controller.rangeValues().testId).toEqual(789); | ||||
|             }); | ||||
|  | ||||
|             it("sorts domain objects by index", function () { | ||||
|                 var testIndexes = { a: 2, b: 1, c: 3, d: 0 }, | ||||
|                     mockDomainObjects = Object.keys(testIndexes).sort().map(function (id) { | ||||
|                         var mockDomainObj = jasmine.createSpyObj( | ||||
|                             "domainObject", | ||||
|                             ["getId", "getModel"] | ||||
|                         ); | ||||
|  | ||||
|                         mockDomainObj.getId.andReturn(id); | ||||
|                         mockDomainObj.getModel.andReturn({ index: testIndexes[id] }); | ||||
|  | ||||
|                         return mockDomainObj; | ||||
|                     }); | ||||
|  | ||||
|                 // Expose those domain objects... | ||||
|                 mockSubscription.getTelemetryObjects.andReturn(mockDomainObjects); | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|  | ||||
|                 // Fire subscription callback | ||||
|                 mockSubscriber.subscribe.mostRecentCall.args[1](); | ||||
|  | ||||
|                 // Controller should expose same objects, but sorted by index from model | ||||
|                 expect(controller.getTelemetryObjects()).toEqual([ | ||||
|                     mockDomainObjects[3], // d, index=0 | ||||
|                     mockDomainObjects[1], // b, index=1 | ||||
|                     mockDomainObjects[0], // a, index=2 | ||||
|                     mockDomainObjects[2]  // c, index=3 | ||||
|                 ]); | ||||
|             }); | ||||
|  | ||||
|             it("uses a timeout to throttle update", function () { | ||||
|                 // Set up subscription... | ||||
|                 mockSubscription.getTelemetryObjects | ||||
|                     .andReturn([mockDomainObject]); | ||||
|                 mockScope.domainObject = mockDomainObject; | ||||
|  | ||||
|                 // Set the object in view; should not need a timeout | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                 expect(mockTimeout.calls.length).toEqual(0); | ||||
|  | ||||
|                 // Next call should schedule an update on a timeout | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                 expect(mockTimeout.calls.length).toEqual(1); | ||||
|  | ||||
|                 // ...but this last one should not, since existing | ||||
|                 // timeout will cover it | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                 expect(mockTimeout.calls.length).toEqual(1); | ||||
|             }); | ||||
|  | ||||
|             it("allows changing column width", function () { | ||||
|                 var initialWidth = controller.columnWidth(); | ||||
|                 controller.increaseColumnWidth(); | ||||
|                 expect(controller.columnWidth()).toBeGreaterThan(initialWidth); | ||||
|             }); | ||||
|  | ||||
|             describe("filter", function () { | ||||
|                 var doFilter, | ||||
|                     filteredObjects, | ||||
|                     filteredObjectNames; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     var telemetryObjects, | ||||
|                         updateFilteredObjects; | ||||
|  | ||||
|                     telemetryObjects = [ | ||||
|                         'DEF123', | ||||
|                         'abc789', | ||||
|                         '456abc', | ||||
|                         '4ab3cdef', | ||||
|                         'hjs[12].*(){}^\\' | ||||
|                     ].map(function (objectName, index) { | ||||
|                         var mockTelemetryObject = jasmine.createSpyObj( | ||||
|                             objectName, | ||||
|                             ["getId", "getModel"] | ||||
|                         ); | ||||
|  | ||||
|                         mockTelemetryObject.getId.andReturn(objectName); | ||||
|                         mockTelemetryObject.getModel.andReturn({ | ||||
|                             name: objectName, | ||||
|                             index: index | ||||
|                         }); | ||||
|  | ||||
|                         return mockTelemetryObject; | ||||
|                     }); | ||||
|  | ||||
|                     mockSubscription | ||||
|                         .getTelemetryObjects | ||||
|                         .andReturn(telemetryObjects); | ||||
|  | ||||
|                     // Trigger domainObject change to create subscription. | ||||
|                     mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|  | ||||
|                     updateFilteredObjects = function () { | ||||
|                         filteredObjects = controller.getTelemetryObjects(); | ||||
|                         filteredObjectNames = filteredObjects.map(function (o) { | ||||
|                             return o.getModel().name; | ||||
|                         }); | ||||
|                     }; | ||||
|  | ||||
|                     doFilter = function (term) { | ||||
|                         controller.filter(term); | ||||
|                         // Filter is debounced so we have to force it to occur. | ||||
|                         mockTimeout.mostRecentCall.args[0](); | ||||
|                         updateFilteredObjects(); | ||||
|                     }; | ||||
|  | ||||
|                     updateFilteredObjects(); | ||||
|                 }); | ||||
|  | ||||
|                 it("initially shows all objects", function () { | ||||
|                     expect(filteredObjectNames).toEqual([ | ||||
|                         'DEF123', | ||||
|                         'abc789', | ||||
|                         '456abc', | ||||
|                         '4ab3cdef', | ||||
|                         'hjs[12].*(){}^\\' | ||||
|                     ]); | ||||
|                 }); | ||||
|  | ||||
|                 it("by blank string matches all objects", function () { | ||||
|                     doFilter(''); | ||||
|                     expect(filteredObjectNames).toEqual([ | ||||
|                         'DEF123', | ||||
|                         'abc789', | ||||
|                         '456abc', | ||||
|                         '4ab3cdef', | ||||
|                         'hjs[12].*(){}^\\' | ||||
|                     ]); | ||||
|                 }); | ||||
|  | ||||
|                 it("exactly matches an object name", function () { | ||||
|                     doFilter('4ab3cdef'); | ||||
|                     expect(filteredObjectNames).toEqual(['4ab3cdef']); | ||||
|                 }); | ||||
|  | ||||
|                 it("partially matches object names", function () { | ||||
|                     doFilter('abc'); | ||||
|                     expect(filteredObjectNames).toEqual([ | ||||
|                         'abc789', | ||||
|                         '456abc' | ||||
|                     ]); | ||||
|                 }); | ||||
|  | ||||
|                 it("matches case insensitive names", function () { | ||||
|                     doFilter('def'); | ||||
|                     expect(filteredObjectNames).toEqual([ | ||||
|                         'DEF123', | ||||
|                         '4ab3cdef' | ||||
|                     ]); | ||||
|                 }); | ||||
|  | ||||
|                 it("works as expected with special characters", function () { | ||||
|                     doFilter('[12]'); | ||||
|                     expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']); | ||||
|                     doFilter('.*'); | ||||
|                     expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']); | ||||
|                     doFilter('.*()'); | ||||
|                     expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']); | ||||
|                     doFilter('.*?'); | ||||
|                     expect(filteredObjectNames).toEqual([]); | ||||
|                     doFilter('.+'); | ||||
|                     expect(filteredObjectNames).toEqual([]); | ||||
|                 }); | ||||
|  | ||||
|                 it("exposes CSS classes from limits", function () { | ||||
|                     var id = mockDomainObject.getId(), | ||||
|                         testClass = "some-css-class", | ||||
|                         mockLimitCapability = | ||||
|                             jasmine.createSpyObj('limit', ['evaluate']); | ||||
|  | ||||
|                     mockDomainObject.getCapability.andCallFake(function (key) { | ||||
|                         return key === 'limit' && mockLimitCapability; | ||||
|                     }); | ||||
|                     mockLimitCapability.evaluate | ||||
|                         .andReturn({ cssClass: testClass }); | ||||
|  | ||||
|                     mockSubscription.getTelemetryObjects | ||||
|                         .andReturn([mockDomainObject]); | ||||
|  | ||||
|                     fireFnWatches(); | ||||
|                     mockSubscriber.subscribe.mostRecentCall.args[1](); | ||||
|  | ||||
|                     expect(controller.classes()[id]).toEqual(testClass); | ||||
|                 }); | ||||
|  | ||||
|                 it("exposes a counter that changes with each update", function () { | ||||
|                     var i, prior; | ||||
|  | ||||
|                     for (i = 0; i < 10; i += 1) { | ||||
|                         prior = controller.counter(); | ||||
|                         expect(controller.counter()).toEqual(prior); | ||||
|                         mockSubscriber.subscribe.mostRecentCall.args[1](); | ||||
|                         expect(controller.counter()).not.toEqual(prior); | ||||
|                     } | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -1,39 +0,0 @@ | ||||
|  | ||||
| define( | ||||
|     ["../src/MCTAutoflowTable"], | ||||
|     function (MCTAutoflowTable) { | ||||
|  | ||||
|         describe("The mct-autoflow-table directive", function () { | ||||
|             var mctAutoflowTable; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mctAutoflowTable = new MCTAutoflowTable(); | ||||
|             }); | ||||
|  | ||||
|             // Real functionality is contained/tested in the linker, | ||||
|             // so just check to make sure we're exposing the directive | ||||
|             // appropriately. | ||||
|             it("is applicable at the element level", function () { | ||||
|                 expect(mctAutoflowTable.restrict).toEqual("E"); | ||||
|             }); | ||||
|  | ||||
|             it("two-ways binds needed scope variables", function () { | ||||
|                 expect(mctAutoflowTable.scope).toEqual({ | ||||
|                     objects: "=", | ||||
|                     values: "=", | ||||
|                     rows: "=", | ||||
|                     updated: "=", | ||||
|                     classes: "=", | ||||
|                     columnWidth: "=", | ||||
|                     counter: "=" | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("provides a link function", function () { | ||||
|                 expect(mctAutoflowTable.link).toEqual(jasmine.any(Function)); | ||||
|             }); | ||||
|  | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -23,10 +23,13 @@ | ||||
| define([ | ||||
|     "moment-timezone", | ||||
|     "./src/indicators/ClockIndicator", | ||||
|     "./src/indicators/FollowIndicator", | ||||
|     "./src/services/TickerService", | ||||
|     "./src/services/TimerService", | ||||
|     "./src/controllers/ClockController", | ||||
|     "./src/controllers/TimerController", | ||||
|     "./src/controllers/RefreshingController", | ||||
|     "./src/actions/FollowTimerAction", | ||||
|     "./src/actions/StartTimerAction", | ||||
|     "./src/actions/RestartTimerAction", | ||||
|     "./src/actions/StopTimerAction", | ||||
| @@ -37,10 +40,13 @@ define([ | ||||
| ], function ( | ||||
|     MomentTimezone, | ||||
|     ClockIndicator, | ||||
|     FollowIndicator, | ||||
|     TickerService, | ||||
|     TimerService, | ||||
|     ClockController, | ||||
|     TimerController, | ||||
|     RefreshingController, | ||||
|     FollowTimerAction, | ||||
|     StartTimerAction, | ||||
|     RestartTimerAction, | ||||
|     StopTimerAction, | ||||
| @@ -80,6 +86,11 @@ define([ | ||||
|                         "CLOCK_INDICATOR_FORMAT" | ||||
|                     ], | ||||
|                     "priority": "preferred" | ||||
|                 }, | ||||
|                 { | ||||
|                     "implementation": FollowIndicator, | ||||
|                     "depends": ["timerService"], | ||||
|                     "priority": "fallback" | ||||
|                 } | ||||
|             ], | ||||
|             "services": [ | ||||
| @@ -90,6 +101,11 @@ define([ | ||||
|                         "$timeout", | ||||
|                         "now" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "timerService", | ||||
|                     "implementation": TimerService, | ||||
|                     "depends": ["openmct"] | ||||
|                 } | ||||
|             ], | ||||
|             "controllers": [ | ||||
| @@ -134,6 +150,15 @@ define([ | ||||
|                 } | ||||
|             ], | ||||
|             "actions": [ | ||||
|                 { | ||||
|                     "key": "timer.follow", | ||||
|                     "implementation": FollowTimerAction, | ||||
|                     "depends": ["timerService"], | ||||
|                     "category": "contextual", | ||||
|                     "name": "Follow Timer", | ||||
|                     "cssClass": "icon-clock", | ||||
|                     "priority": "optional" | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "timer.start", | ||||
|                     "implementation": StartTimerAction, | ||||
|   | ||||
							
								
								
									
										56
									
								
								platform/features/clock/src/actions/FollowTimerAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								platform/features/clock/src/actions/FollowTimerAction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| /***************************************************************************** | ||||
|  * 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 () { | ||||
|  | ||||
|         /** | ||||
|          * Designates a specific timer for following. Timelines, for example, | ||||
|          * use the actively followed timer to display a time-of-interest line | ||||
|          * and interpret time conductor bounds in the Timeline's relative | ||||
|          * time frame. | ||||
|          * | ||||
|          * @implements {Action} | ||||
|          * @memberof platform/features/clock | ||||
|          * @constructor | ||||
|          * @param {ActionContext} context the context for this action | ||||
|          */ | ||||
|         function FollowTimerAction(timerService, context) { | ||||
|             var domainObject = | ||||
|                 context.domainObject && | ||||
|                 context.domainObject.useCapability('adapter'); | ||||
|             this.perform = | ||||
|                 timerService.setTimer.bind(timerService, domainObject); | ||||
|         } | ||||
|  | ||||
|         FollowTimerAction.appliesTo = function (context) { | ||||
|             var model = | ||||
|                 (context.domainObject && context.domainObject.getModel()) || | ||||
|                 {}; | ||||
|  | ||||
|             return model.type === 'timer'; | ||||
|         }; | ||||
|  | ||||
|         return FollowTimerAction; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										57
									
								
								platform/features/clock/src/indicators/FollowIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								platform/features/clock/src/indicators/FollowIndicator.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /***************************************************************************** | ||||
|  * 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( | ||||
|     ['moment'], | ||||
|     function (moment) { | ||||
|         var NO_TIMER = "No timer being followed"; | ||||
|  | ||||
|         /** | ||||
|          * Indicator that displays the active timer, as well as its | ||||
|          * current state. | ||||
|          * @implements {Indicator} | ||||
|          * @memberof platform/features/clock | ||||
|          */ | ||||
|         function FollowIndicator(timerService) { | ||||
|             this.timerService = timerService; | ||||
|         } | ||||
|  | ||||
|         FollowIndicator.prototype.getGlyphClass = function () { | ||||
|             return ""; | ||||
|         }; | ||||
|  | ||||
|         FollowIndicator.prototype.getCssClass = function () { | ||||
|             return (this.timerService.getTimer()) ? "icon-timer s-status-ok" : "icon-timer"; | ||||
|         }; | ||||
|  | ||||
|         FollowIndicator.prototype.getText = function () { | ||||
|             var timer = this.timerService.getTimer(); | ||||
|             return timer ? ('Following timer ' + timer.name) : NO_TIMER; | ||||
|         }; | ||||
|  | ||||
|         FollowIndicator.prototype.getDescription = function () { | ||||
|             return ""; | ||||
|         }; | ||||
|  | ||||
|         return FollowIndicator; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										113
									
								
								platform/features/clock/src/services/TimerService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								platform/features/clock/src/services/TimerService.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| /***************************************************************************** | ||||
|  * 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(['EventEmitter'], function (EventEmitter) { | ||||
|  | ||||
|     /** | ||||
|      * Tracks the currently-followed Timer object. Used by | ||||
|      * timelines et al to synchronize to a particular timer. | ||||
|      * | ||||
|      * The TimerService emits `change` events when the active timer | ||||
|      * is changed. | ||||
|      */ | ||||
|     function TimerService(openmct) { | ||||
|         EventEmitter.apply(this); | ||||
|         this.time = openmct.time; | ||||
|         this.objects = openmct.objects; | ||||
|     } | ||||
|  | ||||
|     TimerService.prototype = Object.create(EventEmitter.prototype); | ||||
|  | ||||
|     /** | ||||
|      * Set (or clear, if `timer` is undefined) the currently active timer. | ||||
|      * @param {DomainObject} timer the new active timer | ||||
|      * @emits change | ||||
|      */ | ||||
|     TimerService.prototype.setTimer = function (timer) { | ||||
|         this.timer = timer; | ||||
|         this.emit('change'); | ||||
|  | ||||
|         if (this.stopObserving) { | ||||
|             this.stopObserving(); | ||||
|             delete this.stopObserving; | ||||
|         } | ||||
|  | ||||
|         if (timer) { | ||||
|             this.stopObserving = | ||||
|                 this.objects.observe(timer, '*', this.setTimer.bind(this)); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get the currently active timer. | ||||
|      * @return {DomainObject} the active timer | ||||
|      * @emits change | ||||
|      */ | ||||
|     TimerService.prototype.getTimer = function () { | ||||
|         return this.timer; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Check if there is a currently active timer. | ||||
|      * @return {boolean} true if there is a timer | ||||
|      */ | ||||
|     TimerService.prototype.hasTimer = function () { | ||||
|         return !!this.timer; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Convert the provided timestamp to milliseconds relative to | ||||
|      * the active timer. | ||||
|      * @return {number} milliseconds since timer start | ||||
|      */ | ||||
|     TimerService.prototype.convert = function (timestamp) { | ||||
|         var clock = this.time.clock(); | ||||
|         var canConvert = this.hasTimer() && | ||||
|             !!clock && | ||||
|             this.timer.timerState !== 'stopped'; | ||||
|  | ||||
|         if (!canConvert) { | ||||
|             return undefined; | ||||
|         } | ||||
|  | ||||
|         var now = clock.currentValue(); | ||||
|         var delta = this.timer.timerState === 'paused' ? | ||||
|             now - this.timer.pausedTime : 0; | ||||
|         var epoch = this.timer.timestamp; | ||||
|  | ||||
|         return timestamp - epoch - delta; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get the value of the active clock, adjusted to be relative to the active | ||||
|      * timer. If there is no clock or no active timer, this will return | ||||
|      * `undefined`. | ||||
|      * @return {number} milliseconds since the start of the active timer | ||||
|      */ | ||||
|     TimerService.prototype.now = function () { | ||||
|         var clock = this.time.clock(); | ||||
|         return clock && this.convert(clock.currentValue()); | ||||
|     }; | ||||
|  | ||||
|     return TimerService; | ||||
| }); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user