Compare commits
	
		
			252 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a2bcc828eb | ||
|   | 7c6fa305ad | ||
|   | 7b68b2971e | ||
|   | f43cc60720 | ||
|   | f68ce2fc80 | ||
|   | 7b2762e034 | ||
|   | 66c68e44bd | ||
|   | 04f59750b7 | ||
|   | 58a2ec6d94 | ||
|   | 04c11612c9 | ||
|   | 030fc18c38 | ||
|   | c5ec132484 | ||
|   | c3e5d0d155 | ||
|   | d8e14a457b | ||
|   | 8ca366a009 | ||
|   | 761fbfe665 | ||
|   | 01f290dc45 | ||
|   | ff36d9ee80 | ||
|   | 9e5689f7dd | ||
|   | 0ccb696ee2 | ||
|   | d385e55e76 | ||
|   | 6bf1ef5bcc | ||
|   | abb511521b | ||
|   | 3a9b1ee901 | ||
|   | 8b390e7fb9 | ||
|   | 29bce69eea | ||
|   | aac5a6d250 | ||
|   | 06436c488a | ||
|   | 8b7af43d6c | ||
|   | abb47eed36 | ||
|   | 027d86ef4b | ||
|   | 6cb9619fbe | ||
|   | 8c3616da32 | ||
|   | ffd5faf9a2 | ||
|   | 00bf05c929 | ||
|   | b682cf8340 | ||
|   | 22a5122ab7 | ||
|   | 7a7877d7c4 | ||
|   | 69c059c943 | ||
|   | 6d58f23c0c | ||
|   | 26e368f52d | ||
|   | 1d78af8f1d | ||
|   | d48871f204 | ||
|   | 519a9333ab | ||
|   | 2fe7ba982f | ||
|   | 7f108c3b24 | ||
|   | 6322964dec | ||
|   | 1bb6e17829 | ||
|   | f34e8ba61b | ||
|   | 2fb9b65652 | ||
|   | 0c6b4a5a23 | ||
|   | 20672ad028 | ||
|   | 99ba9edb95 | ||
|   | 23a8c305c1 | ||
|   | c591ade479 | ||
|   | 8f4f0cb78e | ||
|   | e87280aa15 | ||
|   | e0a2d02d23 | ||
|   | ddc241c0d0 | ||
|   | 73012233b8 | ||
|   | 339916ccd4 | ||
|   | 70acef6905 | ||
|   | da09ffd3fa | ||
|   | 5b98da6681 | ||
|   | 2040abb768 | ||
|   | d35fccbbe8 | ||
|   | 06c6832676 | ||
|   | f49552779a | ||
|   | 200a426f17 | ||
|   | e068173f3e | ||
|   | 4441e88769 | ||
|   | 254a944d7a | ||
|   | 0c00061cbc | ||
|   | dee0613b81 | ||
|   | 6b6bada700 | ||
|   | 530b940a64 | ||
|   | e23bf5ed39 | ||
|   | 6ecea9950d | ||
|   | 4816dddf41 | ||
|   | cae775f9bc | ||
|   | a4b79cdb5b | ||
|   | 012a38cccd | ||
|   | a01f7ddd2d | ||
|   | e7e91e21fc | ||
|   | cbea842c8b | ||
|   | cc5d14deec | ||
|   | 4c5217d646 | ||
|   | 5e54b193ca | ||
|   | cd7ff8ad85 | ||
|   | fa5d59bff8 | ||
|   | ddbb72b88a | ||
|   | 88784d37fd | ||
|   | 700e605bbd | ||
|   | 594f3b8ec2 | ||
|   | b08d00ef3e | ||
|   | 0643fb1f3f | ||
|   | 289debf19d | ||
|   | 7da1a4b2a3 | ||
|   | 528169de2c | ||
|   | c13231b8e8 | ||
|   | 3e4a3aeb9b | ||
|   | 650ef9bfa4 | ||
|   | dd053f7e6e | ||
|   | cb655d486b | ||
|   | 3c52ceb71a | ||
|   | b2337dea97 | ||
|   | dea6554e04 | ||
|   | 31d8c9a48f | ||
|   | f8682a7a29 | ||
|   | e5544615cc | ||
|   | ec0cc572f6 | ||
|   | 6c2a28aba2 | ||
|   | baccd005dc | ||
|   | 1e4ff5a73f | ||
|   | 9f29382e18 | ||
|   | 5f6b4adcda | ||
|   | d6ab70447e | ||
|   | a411bac331 | ||
|   | 5624c7d545 | ||
|   | 007741b4e7 | ||
|   | e0a69744e5 | ||
|   | e7a6c34bcc | ||
|   | 7f3ac4077c | ||
|   | 7eaffdc34a | ||
|   | 5034e88656 | ||
|   | aa48044345 | ||
|   | d12111d9b8 | ||
|   | 9c9db3c24f | ||
|   | 3fe41575bd | ||
|   | 8fa030437e | ||
|   | 17faf000b0 | ||
|   | 90c82f6ef2 | ||
|   | 5e6fe16b93 | ||
|   | f0ca6fdfdb | ||
|   | ad4c456ca2 | ||
|   | b0476edb8c | ||
|   | 8f6a287fb8 | ||
|   | 07d554d114 | ||
|   | 24d2cecdcd | ||
|   | 8efa9c6aac | ||
|   | 22e98274ca | ||
|   | 471b57f47c | ||
|   | 9c775c6715 | ||
|   | 1f09b7b0ac | ||
|   | b0cf9bbd29 | ||
|   | 4a1ca25e17 | ||
|   | cc97e567b6 | ||
|   | 53f03cddb7 | ||
|   | da1e6750a0 | ||
|   | 3258342783 | ||
|   | 73b7365ae2 | ||
|   | 32a42bd679 | ||
|   | 324c2cac03 | ||
|   | 42ac657105 | ||
|   | d30532a8bc | ||
|   | 936079da92 | ||
|   | 4e89ffbe07 | ||
|   | f9ed73c55e | ||
|   | bf3b964ad2 | ||
|   | 55ae755522 | ||
|   | d522570c0b | ||
|   | d72aaf54ca | ||
|   | 8f94751a35 | ||
|   | dfb0a570a3 | ||
|   | 5d06979866 | ||
|   | 8b51ae32d2 | ||
|   | ecb37c54be | ||
|   | 43492d31ba | ||
|   | 0e1df444df | ||
|   | f2c040367b | ||
|   | 0ff360ced3 | ||
|   | fc08df4f6f | ||
|   | c5de90a951 | ||
|   | ea0b86fe72 | ||
|   | d789e91c18 | ||
|   | f7ba24c0b6 | ||
|   | 02ec6db104 | ||
|   | a3a9393d1b | ||
|   | 217e697079 | ||
|   | 82b6166408 | ||
|   | 03ab3bddc4 | ||
|   | abd5913f02 | ||
|   | 107ecfe687 | ||
|   | 4a8222a152 | ||
|   | 7ee8d0a3f7 | ||
|   | dc2b3e85cc | ||
|   | d4b15525ca | ||
|   | cbd9509260 | ||
|   | c5ab6c6c97 | ||
|   | cd84a017b8 | ||
|   | d39dea971f | ||
|   | 4f293f22a6 | ||
|   | cebf9f73da | ||
|   | 37e6b5a352 | ||
|   | ece8f7fded | ||
|   | 3b0a3733b4 | ||
|   | baab0be5af | ||
|   | d14a2a6366 | ||
|   | 377d533ec7 | ||
|   | a4c5854561 | ||
|   | 5296255fa6 | ||
|   | 5d084c2618 | ||
|   | 5208631528 | ||
|   | a56edb9ff4 | ||
|   | 7da1a218ba | ||
|   | a4eb9d6a94 | ||
|   | 20f1dcef45 | ||
|   | 4983d35ca6 | ||
|   | 7171fd01e3 | ||
|   | 0957fbc6f9 | ||
|   | bdbb045005 | ||
|   | 318df9878d | ||
|   | 406a31889e | ||
|   | 9e4e3e9c43 | ||
|   | bd7cb98a4c | ||
|   | 0d419fa5fd | ||
|   | 5ee5e7fea1 | ||
|   | 66b1a92554 | ||
|   | c23c2b84bf | ||
|   | 0c2285719e | ||
|   | fd92c5f970 | ||
|   | 938c266b4e | ||
|   | 9e6e33983b | ||
|   | 40895ec1b9 | ||
|   | 43db52fd70 | ||
|   | 75d6803c9f | ||
|   | ed679756b3 | ||
|   | dd66cb60d8 | ||
|   | d5283d57e4 | ||
|   | 7c140c06dc | ||
|   | f9ff9921a9 | ||
|   | cdac0ad67f | ||
|   | 5d771edcf7 | ||
|   | c4f1c4ad1f | ||
|   | 14b8e02f27 | ||
|   | b383921f2a | ||
|   | a509dfb840 | ||
|   | b7a44a7557 | ||
|   | 2d305415b3 | ||
|   | ff5f37dfbe | ||
|   | 18167eddf8 | ||
|   | f302bd6cb2 | ||
|   | 071a908c10 | ||
|   | 273cf1c14f | ||
|   | 303e870b0d | ||
|   | b42ccebd5a | ||
|   | a444fc01ad | ||
|   | a126e43286 | ||
|   | 32fc50bbd3 | ||
|   | 4adb075a2b | ||
|   | d8b1e570d9 | ||
|   | 5033e2cdbb | 
							
								
								
									
										62
									
								
								LICENSES.md
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								LICENSES.md
									
									
									
									
									
								
							| @@ -309,30 +309,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### Modernizr | ||||
|  | ||||
| #### Info | ||||
|  | ||||
| * Link: http://modernizr.com | ||||
|  | ||||
| * Version: 2.6.2 | ||||
|  | ||||
| * Author: Faruk Ateş | ||||
|  | ||||
| * Description: Browser/device capability finding | ||||
|  | ||||
| #### License | ||||
|  | ||||
| Copyright (c) 2009–2015 | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### Normalize.css | ||||
|  | ||||
| #### Info | ||||
| @@ -476,6 +452,44 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### Zepto | ||||
|  | ||||
| #### Info | ||||
|  | ||||
| * Link: http://zeptojs.com/ | ||||
|  | ||||
| * Version: 1.1.6 | ||||
|  | ||||
| * Authors: Thomas Fuchs | ||||
|  | ||||
| * Description: DOM manipulation | ||||
|  | ||||
| #### License | ||||
|  | ||||
| Copyright (c) 2010-2016 Thomas Fuchs | ||||
| http://zeptojs.com/ | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining | ||||
| a copy of this software and associated documentation files (the | ||||
| "Software"), to deal in the Software without restriction, including | ||||
| without limitation the rights to use, copy, modify, merge, publish, | ||||
| distribute, sublicense, and/or sell copies of the Software, and to | ||||
| permit persons to whom the Software is furnished to do so, subject to | ||||
| the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be | ||||
| included in all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||||
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||||
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||||
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### Json.NET | ||||
|  | ||||
| #### Info | ||||
|   | ||||
							
								
								
									
										20
									
								
								api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # API | ||||
|  | ||||
| This directory is for draft API documentation and design.  The API is organized into a few major components, which are documented in their own READMEs.  See the following: | ||||
|  | ||||
| * Domain Objects | ||||
|     Capabilities | ||||
|     Events | ||||
|     Mutation | ||||
|     Etc | ||||
|  | ||||
| * [Object API](object-api/README.md) (encapsulates persistence), should include roots | ||||
| * [Region API](region-api/README.md)  | ||||
| * [Telemetry API](telemetry-api/README.md) | ||||
| * [Type API](type-api/README.md) | ||||
|  | ||||
| Not yet started: | ||||
|  | ||||
| * [Action API](action-api/README.md) | ||||
| * [Indicators API](indicators-api/README.md) -- potentially compress into regions? | ||||
| * [Plugin API](plugin-api/README.md) | ||||
							
								
								
									
										93
									
								
								api/composition-api/CompositionAPI.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								api/composition-api/CompositionAPI.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| define([ | ||||
|  | ||||
| ], function ( | ||||
|  | ||||
| ) { | ||||
|  | ||||
|  | ||||
|     var PROVIDER_REGISTRY = []; | ||||
|  | ||||
|     function getProvider (object) { | ||||
|         return PROVIDER_REGISTRY.filter(function (p) { | ||||
|             return p.appliesTo(object); | ||||
|         })[0]; | ||||
|     }; | ||||
|  | ||||
|     function composition(object) { | ||||
|         var provider = getProvider(object); | ||||
|  | ||||
|         if (!provider) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         return new CompositionCollection(object, provider); | ||||
|     }; | ||||
|  | ||||
|     composition.addProvider = function (provider) { | ||||
|         PROVIDER_REGISTRY.unshift(provider); | ||||
|     }; | ||||
|  | ||||
|     window.MCT = window.MCT || {}; | ||||
|     window.MCT.composition = composition; | ||||
|  | ||||
|     function CompositionCollection(domainObject, provider) { | ||||
|         this.domainObject = domainObject; | ||||
|         this.provider = provider; | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype.add = function (child, skipMutate) { | ||||
|         if (!this._children) { | ||||
|             throw new Error("Must load composition before you can add!"); | ||||
|         } | ||||
|         // we probably should not add until we have loaded. | ||||
|         // todo: should we modify parent? | ||||
|         if (!skipMutate) { | ||||
|             this.provider.add(this.domainObject, child); | ||||
|         } | ||||
|         this.children.push(child); | ||||
|         this.emit('add', child); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype.load = function () { | ||||
|         return this.provider.load(this.domainObject) | ||||
|             .then(function (children) { | ||||
|                 this._children = []; | ||||
|                 children.map(function (c) { | ||||
|                     this.add(c, true); | ||||
|                 }, this); | ||||
|                 this.emit('load'); | ||||
|                 // Todo: set up listener for changes via provider? | ||||
|             }.bind(this)); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype.remove = function (child) { | ||||
|         var index = this.children.indexOf(child); | ||||
|         if (index === -1) { | ||||
|             throw new Error("Unable to remove child: not found in composition"); | ||||
|         } | ||||
|         this.provider.remove(this.domainObject, child); | ||||
|         this.children.splice(index, 1); | ||||
|         this.emit('remove', index, child); | ||||
|     }; | ||||
|  | ||||
|     var DefaultCompositionProvider = { | ||||
|         appliesTo: function (domainObject) { | ||||
|             return !!domainObject.composition; | ||||
|         }, | ||||
|         load: function (domainObject) { | ||||
|             return Promise.all(domainObject.composition.map(MCT.objects.get)); | ||||
|         }, | ||||
|         add: function (domainObject, child) { | ||||
|             domainObject.composition.push(child.key); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     composition.addProvider(DefaultCompositionProvider); | ||||
|  | ||||
|     function Injector() { | ||||
|         console.log('composition api injected!'); | ||||
|     } | ||||
|  | ||||
|     return Injector; | ||||
|  | ||||
| }); | ||||
							
								
								
									
										35
									
								
								api/composition-api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								api/composition-api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| # Composition API - Overview | ||||
|  | ||||
| The composition API is straightforward: | ||||
|  | ||||
| MCT.composition(object) -- returns a `CompositionCollection` if the object has  | ||||
| composition, returns undefined if it doesn't. | ||||
|  | ||||
| ## CompositionCollection | ||||
|  | ||||
| Has three events: | ||||
| * `load`: when the collection has completed loading. | ||||
| * `add`: when a new object has been added to the collection. | ||||
| * `remove` when an object has been removed from the collection. | ||||
|  | ||||
| Has three methods: | ||||
|  | ||||
| `Collection.load()` -- returns a promise that is fulfilled when the composition  | ||||
|     has loaded. | ||||
| `Collection.add(object)` -- add a domain object to the composition. | ||||
| `Collection.remove(object)` -- remove the object from the composition. | ||||
|  | ||||
| ## Composition providers | ||||
| composition providers are anything that meets the following interface: | ||||
|  | ||||
| * `provider.appliesTo(domainObject)` -> return true if this provider can provide  | ||||
|     composition for a given domain object. | ||||
| * `provider.add(domainObject, childObject)` -> adds object  | ||||
| * `provider.remove(domainObject, childObject)` -> immediately removes objects | ||||
| * `provider.load(domainObject)` -> returns promise for array of children | ||||
|  | ||||
| There is a default composition provider which handles loading composition for  | ||||
| any object with a `composition` property.  If you want specialized composition  | ||||
| loading behavior, implement your own composition provider and register it with | ||||
|  | ||||
| `MCT.composition.addProvider(myProvider)` | ||||
| @@ -22,28 +22,26 @@ | ||||
| /*global define*/ | ||||
| 
 | ||||
| define([ | ||||
|     "./src/CachingPersistenceDecorator", | ||||
|     './CompositionAPI', | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     CachingPersistenceDecorator, | ||||
|     CompositionAPI, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     "use strict"; | ||||
| 
 | ||||
|     legacyRegistry.register("platform/persistence/cache", { | ||||
|         "name": "Persistence cache", | ||||
|         "description": "Cache to improve availability of persisted objects.", | ||||
|         "extensions": { | ||||
|             "components": [ | ||||
|     legacyRegistry.register('api/composition-api', { | ||||
|         name: 'Composition API', | ||||
|         description: 'The public Composition API', | ||||
|         extensions: { | ||||
|             runs: [ | ||||
|                 { | ||||
|                     "provides": "persistenceService", | ||||
|                     "type": "decorator", | ||||
|                     "implementation": CachingPersistenceDecorator, | ||||
|                     "depends": [ | ||||
|                         "PERSISTENCE_SPACE" | ||||
|                     key: "CompositionAPI", | ||||
|                     priority: "mandatory", | ||||
|                     implementation: CompositionAPI, | ||||
|                     depends: [ | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
| }); | ||||
							
								
								
									
										200
									
								
								api/object-api/ObjectAPI.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								api/object-api/ObjectAPI.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| define([ | ||||
|     'lodash' | ||||
| ], function ( | ||||
|     _ | ||||
| ) { | ||||
|  | ||||
|     /** | ||||
|         Object API.  Intercepts the existing object API while also exposing | ||||
|         A new Object API. | ||||
|  | ||||
|         MCT.objects.get('mine') | ||||
|             .then(function (root) { | ||||
|                 console.log(root); | ||||
|                 MCT.objects.getComposition(root) | ||||
|                     .then(function (composition) { | ||||
|                         console.log(composition) | ||||
|                     }) | ||||
|             }); | ||||
|     */ | ||||
|  | ||||
|     var Objects = {}, | ||||
|         ROOT_REGISTRY = [], | ||||
|         PROVIDER_REGISTRY = {}, | ||||
|         FALLBACK_PROVIDER; | ||||
|  | ||||
|     window.MCT = window.MCT || {}; | ||||
|     window.MCT.objects = Objects; | ||||
|  | ||||
|     // take a key string and turn it into a key object | ||||
|     // 'scratch:root' ==> {namespace: 'scratch', identifier: 'root'} | ||||
|     function parseKeyString(key) { | ||||
|         if (typeof key === 'object') { | ||||
|             return key; | ||||
|         } | ||||
|         var namespace = '', | ||||
|             identifier = key; | ||||
|         for (var i = 0, escaped = false, len=key.length; i < len; i++) { | ||||
|             if (key[i] === ":" && !escaped) { | ||||
|                 namespace = key.slice(0, i); | ||||
|                 identifier = key.slice(i + 1); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return { | ||||
|             namespace: namespace, | ||||
|             identifier: identifier | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     // take a key and turn it into a key string | ||||
|     // {namespace: 'scratch', identifier: 'root'} ==> 'scratch:root' | ||||
|     function makeKeyString(key) { | ||||
|         if (typeof key === 'string') { | ||||
|             return key; | ||||
|         } | ||||
|         if (!key.namespace) { | ||||
|             return key.identifier; | ||||
|         } | ||||
|         return [ | ||||
|             key.namespace.replace(':', '\\:'), | ||||
|             key.identifier.replace(':', '\\:') | ||||
|         ].join(':'); | ||||
|     }; | ||||
|  | ||||
|     // Converts composition to use key strings instead of keys | ||||
|     function toOldFormat(model) { | ||||
|         delete model.key; | ||||
|         if (model.composition) { | ||||
|             model.composition = model.composition.map(makeKeyString); | ||||
|         } | ||||
|         return model; | ||||
|     }; | ||||
|  | ||||
|     // converts composition to use keys instead of key strings | ||||
|     function toNewFormat(model, key) { | ||||
|         model.key = key; | ||||
|         if (model.composition) { | ||||
|             model.composition = model.composition.map(parseKeyString); | ||||
|         } | ||||
|         return model; | ||||
|     }; | ||||
|  | ||||
|     // Root provider is hardcoded in; can't be skipped. | ||||
|     var RootProvider = { | ||||
|         'get': function () { | ||||
|             return Promise.resolve({ | ||||
|                 name: 'The root object', | ||||
|                 type: 'root', | ||||
|                 composition: ROOT_REGISTRY | ||||
|             }); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     // Retrieve the provider for a given key. | ||||
|     function getProvider(key) { | ||||
|         if (key.identifier === 'ROOT') { | ||||
|             return RootProvider; | ||||
|         } | ||||
|         return PROVIDER_REGISTRY[key.namespace] || FALLBACK_PROVIDER; | ||||
|     }; | ||||
|  | ||||
|     Objects.addProvider = function (namespace, provider) { | ||||
|         PROVIDER_REGISTRY[namespace] = provider; | ||||
|     }; | ||||
|  | ||||
|     [ | ||||
|         'save', | ||||
|         'delete', | ||||
|         'get' | ||||
|     ].forEach(function (method) { | ||||
|         Objects[method] = function () { | ||||
|             var key = arguments[0], | ||||
|                 provider = getProvider(key); | ||||
|  | ||||
|             if (!provider) { | ||||
|                 throw new Error('No Provider Matched'); | ||||
|             } | ||||
|  | ||||
|             if (!provider[method]) { | ||||
|                 throw new Error('Provider does not support [' + method + '].'); | ||||
|             } | ||||
|  | ||||
|             return provider[method].apply(provider, arguments); | ||||
|         }; | ||||
|     }); | ||||
|  | ||||
|     Objects.addRoot = function (key) { | ||||
|         ROOT_REGISTRY.unshift(key); | ||||
|     }; | ||||
|  | ||||
|     Objects.removeRoot = function (key) { | ||||
|         ROOT_REGISTRY = ROOT_REGISTRY.filter(function (k) { | ||||
|             return ( | ||||
|                 k.identifier !== key.identifier || | ||||
|                 k.namespace !== key.namespace | ||||
|             ); | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     function ObjectServiceProvider(objectService, instantiate) { | ||||
|         this.objectService = objectService; | ||||
|         this.instantiate = instantiate; | ||||
|     } | ||||
|  | ||||
|     ObjectServiceProvider.prototype.save = function (object) { | ||||
|         var key = object.key, | ||||
|             keyString = makeKeyString(key), | ||||
|             newObject = this.instantiate(toOldFormat(object), keyString); | ||||
|  | ||||
|         return object.getCapability('persistence') | ||||
|                 .persist() | ||||
|                 .then(function () { | ||||
|                     return toNewFormat(object, key); | ||||
|                 }); | ||||
|     }; | ||||
|  | ||||
|     ObjectServiceProvider.prototype.delete = function (object) { | ||||
|         // TODO! | ||||
|     }; | ||||
|  | ||||
|     ObjectServiceProvider.prototype.get = function (key) { | ||||
|         var keyString = makeKeyString(key); | ||||
|         return this.objectService.getObjects([keyString]) | ||||
|             .then(function (results) { | ||||
|                 var model = JSON.parse(JSON.stringify(results[keyString].getModel())); | ||||
|                 return toNewFormat(model, key); | ||||
|             }); | ||||
|     }; | ||||
|  | ||||
|     // Injects new object API as a decorator so that it hijacks all requests. | ||||
|     // Object providers implemented on new API should just work, old API should just work, many things may break. | ||||
|     function ObjectAPIInjector(ROOTS, instantiate, objectService) { | ||||
|         this.getObjects = function (keys) { | ||||
|             var results = {}, | ||||
|                 promises = keys.map(function (keyString) { | ||||
|                     var key = parseKeyString(keyString); | ||||
|                     return Objects.get(key) | ||||
|                         .then(function (object) { | ||||
|                             object = toOldFormat(object) | ||||
|                             results[keyString] = instantiate(object, keyString); | ||||
|                         }); | ||||
|                 }); | ||||
|  | ||||
|             return Promise.all(promises) | ||||
|                 .then(function () { | ||||
|                     return results; | ||||
|                 }); | ||||
|         }; | ||||
|  | ||||
|         FALLBACK_PROVIDER = new ObjectServiceProvider(objectService, instantiate); | ||||
|  | ||||
|         ROOTS.forEach(function (r) { | ||||
|             ROOT_REGISTRY.push(parseKeyString(r.id)); | ||||
|         }); | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     return ObjectAPIInjector; | ||||
| }); | ||||
							
								
								
									
										101
									
								
								api/object-api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								api/object-api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| # Object API - Overview | ||||
|  | ||||
| The object API provides methods for fetching domain objects. | ||||
|  | ||||
| # Keys | ||||
| Keys are a composite identifier that is used to create and persist objects.  Ex: | ||||
| ```javascript | ||||
| { | ||||
|     namespace: 'elastic', | ||||
|     identifier: 'myIdentifier' | ||||
| } | ||||
| ``` | ||||
|  | ||||
| In old MCT days, we called this an "id", and we encoded it in a single string.   | ||||
| The above key would encode into the identifier, `elastic:myIdentifier`. | ||||
|  | ||||
| When interacting with the API you will be dealing with key objects. | ||||
|  | ||||
| # Configuring the Object API | ||||
|  | ||||
| The following methods should be used before calling run.  They allow you to  | ||||
| configure the persistence space of MCT. | ||||
|  | ||||
| * `MCT.objects.addRoot(key)` -- add a "ROOT" to Open MCT by specifying it's  | ||||
|     key. | ||||
| * `MCT.objects.removeRoot(key)` -- Remove a "ROOT" from Open MCT by key. | ||||
| * `MCT.objects.addProvider(namespace, provider)` -- register an object provider | ||||
|     for a specific namespace.  See below for documentation on the provider  | ||||
|     interface. | ||||
|      | ||||
| # Using the object API | ||||
|  | ||||
| The object API provides methods for getting, saving, and deleting objects. | ||||
|  | ||||
| * MCT.objects.get(key) -> returns promise for an object | ||||
| * MCT.objects.save(object) -> returns promise that is resolved when object | ||||
|     has been saved | ||||
| * MCT.objects.delete(object) -> returns promise that is resolved when object has  | ||||
|     been deleted | ||||
|  | ||||
| ## Configuration Example: Adding a groot | ||||
|  | ||||
| The following example adds a new root object for groot and populates it with | ||||
| some pieces of groot. | ||||
|  | ||||
| ```javascript | ||||
|  | ||||
| var ROOT_KEY = { | ||||
|     namespace: 'groot', | ||||
|     identifier: 'groot' | ||||
| }; | ||||
|  | ||||
| var GROOT_ROOT = { | ||||
|     name: 'I am groot', | ||||
|     type: 'folder', | ||||
|     composition: [ | ||||
|         { | ||||
|             namespace: 'groot', | ||||
|             identifier: 'arms' | ||||
|         }, | ||||
|         { | ||||
|             namespace: 'groot', | ||||
|             identifier: 'legs' | ||||
|         }, | ||||
|         { | ||||
|             namespace: 'groot', | ||||
|             identifier: 'torso' | ||||
|         } | ||||
|     ] | ||||
| }; | ||||
|  | ||||
| var GrootProvider = { | ||||
|     get: function (key) { | ||||
|         if (key.identifier === 'groot') { | ||||
|             return Promise.resolve(GROOT_ROOT); | ||||
|         } | ||||
|         return Promise.resolve({ | ||||
|             name: 'Groot\'s ' + key.identifier | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| MCT.objects.addRoot(ROOT_KEY); | ||||
|  | ||||
| MCT.objects.addProvider('groot', GrootProvider); | ||||
|  | ||||
| MCT.run(); | ||||
| ``` | ||||
|  | ||||
| ### Making a custom provider: | ||||
|  | ||||
| All methods on the provider interface are optional, so you do not need | ||||
| to modify them. | ||||
|  | ||||
| * `provider.get(key)` -> promise for a domain object. | ||||
| * `provider.save(domainObject)` -> returns promise that is fulfilled when object  | ||||
|     has been saved. | ||||
| * `provider.delete(domainObject)` -> returns promise that is fulfilled when  | ||||
|     object has been deleted. | ||||
|  | ||||
|  | ||||
| @@ -19,37 +19,32 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,Promise*/ | ||||
| /*global define*/ | ||||
| 
 | ||||
| /** | ||||
|  * Module defining NameColumn. Created by vwoeltje on 11/18/14. | ||||
|  */ | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
| 
 | ||||
|         /** | ||||
|          * A column which will report the name of the domain object | ||||
|          * which exposed specific telemetry values. | ||||
|          * | ||||
|          * @memberof platform/features/scrolling | ||||
|          * @implements {platform/features/scrolling.ScrollingColumn} | ||||
|          * @constructor | ||||
|          */ | ||||
|         function NameColumn() { | ||||
| define([ | ||||
|     './ObjectAPI', | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     ObjectAPI, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     legacyRegistry.register('api/object-api', { | ||||
|         name: 'Object API', | ||||
|         description: 'The public Objects API', | ||||
|         extensions: { | ||||
|             components: [ | ||||
|                 { | ||||
|                     provides: "objectService", | ||||
|                     type: "decorator", | ||||
|                     priority: "mandatory", | ||||
|                     implementation: ObjectAPI, | ||||
|                     depends: [ | ||||
|                         "roots[]", | ||||
|                         "instantiate" | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|         NameColumn.prototype.getTitle = function () { | ||||
|             return "Name"; | ||||
|         }; | ||||
| 
 | ||||
|         NameColumn.prototype.getValue = function (domainObject) { | ||||
|             return { | ||||
|                 text: domainObject.getModel().name | ||||
|             }; | ||||
|         }; | ||||
| 
 | ||||
|         return NameColumn; | ||||
|     } | ||||
| ); | ||||
| }); | ||||
							
								
								
									
										64
									
								
								api/region-api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								api/region-api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| # Region API - Overview | ||||
|  | ||||
| The region API provides a method for specifying which views should display in a given region for a given domain object.  As such, they also define the basic view interface that components must define. | ||||
|  | ||||
| ### MCT.region.Region | ||||
|  | ||||
| The base region type, all regions implement this interface. | ||||
|  | ||||
| `register(view)` | ||||
|  | ||||
| `getViews(domainObject)` | ||||
|  | ||||
| Additionally, Regions may have subregions for different modes of the application.  Specifying a view for a region  | ||||
|  | ||||
| ### MCT.region.View | ||||
|  | ||||
| The basic type for views.  You can extend this to implement your own functionality, or you can create your own object so long as it meets the interface. | ||||
|  | ||||
| `attribute` | `type` | `note` | ||||
| --- | --- | --- | ||||
| `label` | `string` or `Function` | The name of the view.  Used in the view selector when more than one view exists for an object. | ||||
| `glyph` | `string` or `Function` | The glyph to associate with this view.  Used in the view selector when more than one view exists for an object. | ||||
| `instantiate` | `Function` | constructor for a view.  Will be invoked with two arguments, `container` and `domainObject`.  It should return an object with a `destroy` method that is called when the view is removed. | ||||
| `appliesTo` | `Function` | Determines if a view applies to a specific domain object.  Will be invoked with a domainObject.  Should return a number, `priority` if the view applies to a given object.  If multiple views return a truthy value for a given object, they will be ordered by priority, and the largest priority value will be the default view for the object.  Return `false` if a view should not apply to an object. | ||||
|  | ||||
| Basic Hello World: | ||||
|  | ||||
| ```javascript | ||||
|  | ||||
| function HelloWorldView(container, domainObject) { | ||||
|     container.innerHTML = 'Hello World!'; | ||||
| } | ||||
|  | ||||
| HelloWorldView.label = 'Hello World'; | ||||
| HelloWorldView.glyph = 'whatever'; | ||||
|  | ||||
| HelloWorldView.appliesTo = function (domainObject) { | ||||
|     return 10; | ||||
| }; | ||||
|  | ||||
| HelloWorldView.prototype.destroy = function () { | ||||
|     // clean up outstanding handlers; | ||||
| }; | ||||
|  | ||||
| MCT.regions.Main.register(HelloWorldView); | ||||
|  | ||||
| ``` | ||||
|  | ||||
| ## Region Hierarchy | ||||
|  | ||||
| Regions are organized in a hierarchy, with the most specific region taking precedence over less specific regions. | ||||
|  | ||||
| If you specify a view for the Main Region, it will be used for both Edit and View modes.  You can override the Main Region view for a specific mode by registering the view with that specific mode. | ||||
|  | ||||
| ### MCT.regions.Tree | ||||
| ### MCT.regions.Main | ||||
| ### MCT.regions.Main.View | ||||
| ### MCT.regions.Main.Edit | ||||
| ### MCT.regions.Inspector | ||||
| ### MCT.regions.Inspector.View | ||||
| ### MCT.regions.Inspector.Edit | ||||
| ### MCT.regions.Toolbar | ||||
| ### MCT.regions.Toolbar.View | ||||
| ### MCT.regions.Toolbar.Edit | ||||
							
								
								
									
										11
									
								
								api/region-api/RegionAPI.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								api/region-api/RegionAPI.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| define([ | ||||
|      | ||||
| ], function () { | ||||
|      | ||||
|     function RegionAPI() { | ||||
|         window.MCT = window.MCT || {}; | ||||
|         window.MCT.regions = {}; | ||||
|     } | ||||
|      | ||||
|     return RegionAPI; | ||||
| }) | ||||
							
								
								
									
										45
									
								
								api/region-api/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								api/region-api/bundle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './RegionAPI', | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     RegionAPI, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     legacyRegistry.register('api/region-api', { | ||||
|         name: 'Region API', | ||||
|         description: 'The public Region API', | ||||
|         extensions: { | ||||
|             runs: [ | ||||
|                 { | ||||
|                     key: "RegionAPI", | ||||
|                     implementation: RegionAPI, | ||||
|                     depends: [ | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
|  | ||||
| }); | ||||
							
								
								
									
										71
									
								
								api/telemetry-api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								api/telemetry-api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| # Telemetry API - Overview | ||||
|  | ||||
| The Telemetry API provides basic methods for retrieving historical and realtime telemetry data, retrieving telemetry metadata, and registering additional telemetry providers.    | ||||
|  | ||||
| The Telemetry API also provides a set of helpers built upon these basics-- TelemetryFormatters help you format telemetry values for display purposes, LimitEvaluators help you display evaluate and display alarm states, while TelemetryCollections provide a method for seamlessly combining historical and realtime data, while supporting more advanced client side filtering and interactivity. | ||||
|  | ||||
|  | ||||
| ## Getting Telemetry Data | ||||
|  | ||||
|  | ||||
| ### `MCT.telemetry.request(domainObject, options)` | ||||
|  | ||||
| Request historical telemetry for a domain object.  Options allows you to specify filters (start, end, etc.), sort order, and strategies for retrieving telemetry (aggregation, latest available, etc.). | ||||
|  | ||||
| Returns a `Promise` for an array of telemetry values. | ||||
|  | ||||
| ### `MCT.telemetry.subscribe(domainObject, callback, options)` | ||||
|  | ||||
| Subscribe to realtime telemetry for a specific domain object.  callback will be called whenever data is received from a realtime provider.  Options allows you to specify ??? | ||||
|  | ||||
| ## Understanding Telemetry | ||||
|  | ||||
| ### `MCT.telemetry.getMetadata(domainObject)` | ||||
|  | ||||
| Retrieve telemetry metadata for a domain object.  Telemetry metadata helps you understand the sort of telemetry data a domain object can provide-- for instances, the possible enumerations or states, the units, and more. | ||||
|  | ||||
| ### `MCT.telemetry.Formatter` | ||||
|  | ||||
| Telemetry formatters help you format telemetry values for display.  Under the covers, they use telemetry metadata to interpret your telemetry data, and then they use the format API to format that data for display. | ||||
|  | ||||
|  | ||||
| ### `MCT.telemetry.LimitEvaluator` | ||||
|  | ||||
| Limit Evaluators help you evaluate limit and alarm status of individual telemetry datums for display purposes without having to interact directly with the Limit API.   | ||||
|  | ||||
| ## Adding new telemetry sources | ||||
|  | ||||
| ### `MCT.telemetry.registerProvider(telemetryProvider)` | ||||
|  | ||||
| Register a telemetry provider with the telemetry service.  This allows you to connect alternative telemetry sources to   For more information, see the `MCT.telemetry.BaseProvider` | ||||
|  | ||||
| ### `MCT.telemetry.BaseProvider` | ||||
|  | ||||
| The base provider is a great starting point for developers who would like to implement their own telemetry provider.  At the same time, you can implement your own telemetry provider as long as it meets the TelemetryProvider (see other docs). | ||||
|  | ||||
| ## Other tools | ||||
|  | ||||
| ### `MCT.telemetry.TelemetryCollection` | ||||
|  | ||||
| The TelemetryCollection is a useful tool for building advanced displays.  It helps you seamlessly handle both historical and realtime telemetry data, while making it easier to deal with large data sets and interactive displays that need to frequently requery data. | ||||
|  | ||||
|  | ||||
|  | ||||
| # API Reference (TODO) | ||||
|  | ||||
| * Telemetry Metadata | ||||
| * Request Options | ||||
|     -- start | ||||
|     -- end | ||||
|     -- sort | ||||
|     -- ??? | ||||
|     -- strategies -- specify which strategies you want.  an array provides for fallback strategies without needing decoration.  Design fallbacks into API. | ||||
|  | ||||
| ### `MCT.telemetry.request(domainObject, options)` | ||||
| ### `MCT.telemetry.subscribe(domainObject, callback, options)` | ||||
| ### `MCT.telemetry.getMetadata(domainObject)` | ||||
| ### `MCT.telemetry.Formatter` | ||||
| ### `MCT.telemetry.LimitEvaluator` | ||||
| ### `MCT.telemetry.registerProvider(telemetryProvider)` | ||||
| ### `MCT.telemetry.BaseProvider` | ||||
| ### `MCT.telemetry.TelemetryCollection` | ||||
							
								
								
									
										239
									
								
								api/telemetry-api/TelemetryAPI.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								api/telemetry-api/TelemetryAPI.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| /*global define,window,console,MCT*/ | ||||
|  | ||||
| /** | ||||
|  | ||||
| var key = '114ced6c-deb7-4169-ae71-68c571665514'; | ||||
| MCT.objects.getObject([key]) | ||||
|     .then(function (results) { | ||||
|         console.log('got results'); | ||||
|         return results[key]; | ||||
|     }) | ||||
|     .then(function (domainObject) { | ||||
|         console.log('got object'); | ||||
|         MCT.telemetry.subscribe(domainObject, function (datum) { | ||||
|             console.log('gotData!', datum); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|  | ||||
| */ | ||||
|  | ||||
| define([ | ||||
|     'lodash', | ||||
|     'eventemitter2' | ||||
| ], function ( | ||||
|     _, | ||||
|     EventEmitter | ||||
| ) { | ||||
|  | ||||
|     // format map is a placeholder until we figure out format service. | ||||
|     var FORMAT_MAP = { | ||||
|         generic: function (range) { | ||||
|             return function (datum) { | ||||
|                 return datum[range.key]; | ||||
|             }; | ||||
|         }, | ||||
|         enum: function (range) { | ||||
|             var enumMap = _.indexBy(range.enumerations, 'value'); | ||||
|             return function (datum) { | ||||
|                 try { | ||||
|                     return enumMap[datum[range.valueKey]].text; | ||||
|                 } catch (e) { | ||||
|                     return datum[range.valueKey]; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     FORMAT_MAP.number = | ||||
|         FORMAT_MAP.float = | ||||
|         FORMAT_MAP.integer = | ||||
|         FORMAT_MAP.ascii = | ||||
|         FORMAT_MAP.generic; | ||||
|  | ||||
|  | ||||
|  | ||||
|     function TelemetryAPI( | ||||
|         formatService | ||||
|     ) { | ||||
|  | ||||
|         var FORMATTER_CACHE = new WeakMap(), | ||||
|             EVALUATOR_CACHE = new WeakMap(); | ||||
|  | ||||
|         function testAPI() { | ||||
|             var key = '114ced6c-deb7-4169-ae71-68c571665514'; | ||||
|             window.MCT.objects.getObjects([key]) | ||||
|                 .then(function (results) { | ||||
|                     console.log('got results'); | ||||
|                     return results[key]; | ||||
|                 }) | ||||
|                 .then(function (domainObject) { | ||||
|                     var formatter = new MCT.telemetry.Formatter(domainObject); | ||||
|                     console.log('got object'); | ||||
|                     window.MCT.telemetry.subscribe(domainObject, function (datum) { | ||||
|                         var formattedValues = {}; | ||||
|                         Object.keys(datum).forEach(function (key) { | ||||
|                             formattedValues[key] = formatter.format(datum, key); | ||||
|                         }); | ||||
|                         console.log( | ||||
|                             'datum:', | ||||
|                             datum, | ||||
|                             'formatted:', | ||||
|                             formattedValues | ||||
|                         ); | ||||
|                     }); | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         function getFormatter(range) { | ||||
|             if (FORMAT_MAP[range.type]) { | ||||
|                 return FORMAT_MAP[range.type](range); | ||||
|             } | ||||
|             try { | ||||
|                 var format = formatService.getFormat(range.type).format.bind( | ||||
|                         formatService.getFormat(range.type) | ||||
|                     ), | ||||
|                     formatter = function (datum) { | ||||
|                         return format(datum[range.key]); | ||||
|                     }; | ||||
|                 return formatter; | ||||
|             } catch (e) { | ||||
|                 console.log('could not retrieve format', range, e, e.message); | ||||
|                 return FORMAT_MAP.generic(range); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function TelemetryFormatter(domainObject) { | ||||
|             this.metadata = domainObject.getCapability('telemetry').getMetadata(); | ||||
|             this.formats = {}; | ||||
|             var ranges = this.metadata.ranges.concat(this.metadata.domains); | ||||
|  | ||||
|             ranges.forEach(function (range) { | ||||
|                 this.formats[range.key] = getFormatter(range); | ||||
|             }, this); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Retrieve the 'key' from the datum and format it accordingly to | ||||
|          * telemetry metadata in domain object. | ||||
|          */ | ||||
|         TelemetryFormatter.prototype.format = function (datum, key) { | ||||
|             return this.formats[key](datum); | ||||
|         }; | ||||
|  | ||||
|         function LimitEvaluator(domainObject) { | ||||
|             this.domainObject = domainObject; | ||||
|             this.evaluator = domainObject.getCapability('limit'); | ||||
|             if (!this.evaluator) { | ||||
|                 this.evalute = function () { | ||||
|                     return ''; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /** TODO: Do we need a telemetry parser, or do we assume telemetry | ||||
|         is numeric by default? */ | ||||
|  | ||||
|         LimitEvaluator.prototype.evaluate = function (datum, key) { | ||||
|             return this.evaluator.evaluate(datum, key); | ||||
|         }; | ||||
|  | ||||
|         /** Basic telemetry collection, needs more magic. **/ | ||||
|         function TelemetryCollection(domainObject) { | ||||
|             this.domainObject = domainObject; | ||||
|             this.data = []; | ||||
|         } | ||||
|  | ||||
|         _.extend(TelemetryCollection.prototype, EventEmitter.prototype); | ||||
|  | ||||
|         TelemetryCollection.prototype.request = function (options) { | ||||
|             request(this.domainObject, options).then(function (data) { | ||||
|                 data.forEach(function (datum) { | ||||
|                     this.addDatum(datum); | ||||
|                 }, this); | ||||
|             }.bind(this)); | ||||
|         }; | ||||
|  | ||||
|         TelemetryCollection.prototype.addDatum = function (datum) { | ||||
|             this.data.push(datum); | ||||
|             this.emit('add', datum); | ||||
|         }; | ||||
|  | ||||
|         TelemetryCollection.prototype.subscribe = function (options) { | ||||
|             if (this.unsubscribe) { | ||||
|                 this.unsubscribe(); | ||||
|                 delete this.unsubscribe; | ||||
|             } | ||||
|  | ||||
|             this.unsubscribe = subscribe( | ||||
|                 this.domainObject, | ||||
|                 function (telemetrySeries) { | ||||
|                     telemetrySeries.getData().forEach(this.addDatum, this); | ||||
|                 }.bind(this), | ||||
|                 options | ||||
|             ); | ||||
|         }; | ||||
|  | ||||
|         function registerProvider(provider) { | ||||
|             // Not yet implemented. | ||||
|             console.log('registering provider', provider); | ||||
|         } | ||||
|  | ||||
|         function registerEvaluator(evaluator) { | ||||
|             // not yet implemented. | ||||
|             console.log('registering evaluator', evaluator); | ||||
|         } | ||||
|  | ||||
|         function request(domainObject, options) { | ||||
|             return domainObject.getCapability('telemetry') | ||||
|                 .requestData(options) | ||||
|                 .then(function (telemetrySeries) { | ||||
|                     return telemetrySeries.getData(); | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         function subscribe(domainObject, callback, options) { | ||||
|             return domainObject.getCapability('telemetry') | ||||
|                 .subscribe(function (series) { | ||||
|                     series.getData().forEach(callback); | ||||
|                 }, options); | ||||
|         } | ||||
|  | ||||
|         var Telemetry = { | ||||
|             registerProvider: registerProvider, | ||||
|             registerEvaluator: registerEvaluator, | ||||
|             request: request, | ||||
|             subscribe: subscribe, | ||||
|             getMetadata: function (domainObject) { | ||||
|                 return domainObject.getCapability('telemetry').getMetadata(); | ||||
|             }, | ||||
|             Formatter: function (domainObject) { | ||||
|                 if (!FORMATTER_CACHE.has(domainObject)) { | ||||
|                     FORMATTER_CACHE.set( | ||||
|                         domainObject, | ||||
|                         new TelemetryFormatter(domainObject) | ||||
|                     ); | ||||
|                 } | ||||
|                 return FORMATTER_CACHE.get(domainObject); | ||||
|             }, | ||||
|             LimitEvaluator: function (domainObject) { | ||||
|                 if (!EVALUATOR_CACHE.has(domainObject)) { | ||||
|                     EVALUATOR_CACHE.set( | ||||
|                         domainObject, | ||||
|                         new LimitEvaluator(domainObject) | ||||
|                     ); | ||||
|                 } | ||||
|                 return EVALUATOR_CACHE.get(domainObject); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         window.MCT = window.MCT || {}; | ||||
|         window.MCT.telemetry = Telemetry; | ||||
|         window.testAPI = testAPI; | ||||
|  | ||||
|         return Telemetry; | ||||
|     } | ||||
|  | ||||
|     return TelemetryAPI; | ||||
| }); | ||||
							
								
								
									
										46
									
								
								api/telemetry-api/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								api/telemetry-api/bundle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     './TelemetryAPI', | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     TelemetryAPI, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     legacyRegistry.register('api/telemetry-api', { | ||||
|         name: 'Telemetry API', | ||||
|         description: 'The public Telemetry API', | ||||
|         extensions: { | ||||
|             runs: [ | ||||
|                 { | ||||
|                     key: "TelemetryAPI", | ||||
|                     implementation: TelemetryAPI, | ||||
|                     depends: [ | ||||
|                         'formatService' | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										31
									
								
								api/type-api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								api/type-api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| # Type API - Overview | ||||
|  | ||||
| The Type API allows you to register type information for domain objects, and allows you to retrieve type information about a given domain object.  Crucially, type information allows you to add new creatible object types. | ||||
|  | ||||
| ### MCT.types.Type | ||||
|  | ||||
| The basic interface for a type.  You can extend this to implement your own type, | ||||
| or you can provide an object that implements the same interface. | ||||
|  | ||||
| `attribute` | `type` | `note` | ||||
| --- | --- | --- | ||||
| `label` | `String` | The human readible name of the type. | ||||
| `key` | `String` | The unique identifier for this type.   | ||||
| `glyph` | `String` | The glyph identifier for the type.  Displayed in trees, labels, and other locations. | ||||
| `description` | `String` | A basic description of the type visible in the create menu. | ||||
| `isCreatible` | `Boolean`, `Number`, or `Function` | If truthy, this type will be visible in the create menu.  Note that objects not in the create menu can be instantiated via other means. | ||||
| `namespace` | `String` | The object namespace that provides instances of this type.  This allows you to implement custom object providers for specific types while still utilizing other namespaces for persistence. | ||||
| `properties` | `Object` | Object defining properties of an instance of this class.  Properties are used for automatic form generation and automated metadata display.  For more information on the definition of this object, look at (some resource-- jsonschema?) | ||||
| `canContain` | `Function` | determins whether objects of this type can contain other objects.  Will be invoked with a domain object.  Return true to allow composition, return false to disallow composition. | ||||
|  | ||||
| ### MCT.types.register(type) | ||||
|  | ||||
| Register a type with the type API.  Registering a type with the same key as another type will replace the original type definition. | ||||
|  | ||||
| ### MCT.types.getType(typeKey) | ||||
|  | ||||
| Returns the type definition for a given typeKey.  returns undefined if type does not exist. | ||||
|  | ||||
| ### MCT.types.getType(domainObject) | ||||
|  | ||||
| Return the type definition for a given domain object. | ||||
							
								
								
									
										10
									
								
								api/type-api/TypeAPI.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								api/type-api/TypeAPI.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| define([ | ||||
|      | ||||
| ], function () { | ||||
|     function TypeAPI() { | ||||
|         window.MCT = window.MCT || {}; | ||||
|         window.MCT.types = {}; | ||||
|     } | ||||
|      | ||||
|     return TypeAPI; | ||||
| }); | ||||
| @@ -22,43 +22,26 @@ | ||||
| /*global define*/ | ||||
| 
 | ||||
| define([ | ||||
|     "./src/ScrollingListController", | ||||
|     "text!./res/templates/scrolling.html", | ||||
|     './TypeAPI', | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     ScrollingListController, | ||||
|     scrollingTemplate, | ||||
|     TypeAPI, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     "use strict"; | ||||
| 
 | ||||
|     legacyRegistry.register("platform/features/scrolling", { | ||||
|         "name": "Scrolling Lists", | ||||
|         "description": "Time-ordered list of latest data.", | ||||
|         "extensions": { | ||||
|             "views": [ | ||||
|     legacyRegistry.register('api/type-api', { | ||||
|         name: 'Type API', | ||||
|         description: 'The public Type API', | ||||
|         extensions: { | ||||
|             runs: [ | ||||
|                 { | ||||
|                     "key": "scrolling", | ||||
|                     "name": "Scrolling", | ||||
|                     "glyph": "5", | ||||
|                     "description": "Scrolling list of data values.", | ||||
|                     "template": scrollingTemplate, | ||||
|                     "needs": [ | ||||
|                         "telemetry" | ||||
|                     ], | ||||
|                     "delegation": true | ||||
|                 } | ||||
|             ], | ||||
|             "controllers": [ | ||||
|                 { | ||||
|                     "key": "ScrollingListController", | ||||
|                     "implementation": ScrollingListController, | ||||
|                     "depends": [ | ||||
|                         "$scope", | ||||
|                         "telemetryFormatter" | ||||
|                     key: "TypeAPI", | ||||
|                     implementation: TypeAPI, | ||||
|                     depends: [ | ||||
|                         'typeService' | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
| }); | ||||
| @@ -17,6 +17,9 @@ | ||||
|     "screenfull": "^3.0.0", | ||||
|     "node-uuid": "^1.4.7", | ||||
|     "comma-separated-values": "^3.6.4", | ||||
|     "FileSaver.js": "^0.0.2" | ||||
|     "FileSaver.js": "^0.0.2", | ||||
|     "zepto": "^1.1.6", | ||||
|     "eventemitter2": "^1.0.0", | ||||
|     "lodash": "3.10.1" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -6,12 +6,13 @@ Victor Woeltjen | ||||
| September 23, 2015   | ||||
| Document Version 1.1   | ||||
|  | ||||
| Date                | Version   | Summary of Changes      | Author | ||||
| ------------------- | --------- | ----------------------- | --------------- | ||||
| April 29, 2015      | 0         | Initial Draft           | Victor Woeltjen | ||||
| May 12, 2015        | 0.1       |                         | Victor Woeltjen | ||||
| June 4, 2015        | 1.0       | Name Changes            | Victor Woeltjen | ||||
| October 4, 2015     | 1.1       | Conversion to MarkDown  | Andrew Henry | ||||
| Date                | Version   | Summary of Changes        | Author | ||||
| ------------------- | --------- | ------------------------- | --------------- | ||||
| April 29, 2015      | 0         | Initial Draft             | Victor Woeltjen | ||||
| May 12, 2015        | 0.1       |                           | Victor Woeltjen | ||||
| June 4, 2015        | 1.0       | Name Changes              | Victor Woeltjen | ||||
| October 4, 2015     | 1.1       | Conversion to MarkDown    | Andrew Henry | ||||
| April 5, 2016       | 1.2       | Added Mct-table directive | Andrew Henry | ||||
|  | ||||
| # Introduction | ||||
| The purpose of this guide is to familiarize software developers with the Open | ||||
| @@ -1600,6 +1601,61 @@ there are items . | ||||
|         ]  | ||||
|     } | ||||
|  | ||||
| ## Table | ||||
|  | ||||
| The `mct-table` directive provides a generic table component, with optional  | ||||
| sorting and filtering capabilities. The table can be pre-populated with data  | ||||
| by setting the `rows` parameter, and it can be updated in real-time using the | ||||
| `add:row` and `remove:row` broadcast events. The table will expand to occupy  | ||||
| 100% of the size of its containing element. The table is highly optimized for  | ||||
| very large data sets. | ||||
|  | ||||
| ### Events | ||||
|  | ||||
| The table supports two events for notifying that the rows have changed. For  | ||||
| performance reasons, the table does not monitor the content of `rows`  | ||||
| constantly.  | ||||
|  | ||||
| * `add:row`: A `$broadcast` event that will notify the table that a new row  | ||||
| has been added to the table. | ||||
|  | ||||
| eg. The code below adds a new row, and alerts the table using the `add:row`  | ||||
| event. Sorting and filtering will be applied automatically by the table component. | ||||
|  | ||||
| ``` | ||||
| $scope.rows.push(newRow); | ||||
| $scope.$broadcast('add:row', $scope.rows.length-1); | ||||
| ``` | ||||
|  | ||||
| * `remove:row`: A `$broadcast` event that will notify the table that a row  | ||||
| should be removed from the table. | ||||
|  | ||||
| eg. The code below removes a row from the rows array, and then alerts the table  | ||||
| to its removal. | ||||
|      | ||||
| ``` | ||||
| $scope.rows.slice(5, 1); | ||||
| $scope.$broadcast('remove:row', 5); | ||||
| ``` | ||||
|  | ||||
| ### Parameters | ||||
|  | ||||
| * `headers`: An array of string values which will constitute the column titles | ||||
|  that appear at the top of the table. Corresponding values are specified in  | ||||
|  the rows using the header title provided here. | ||||
| * `rows`: An array of objects containing row values. Each element in the  | ||||
| array must be an associative array, where the key corresponds to a column header.  | ||||
| * `enableFilter`: A boolean that if true, will enable searching and result  | ||||
| filtering. When enabled, each column will have a text input field that can be | ||||
| used to filter the table rows in real time. | ||||
| * `enableSort`: A boolean determining whether rows can be sorted. If true,  | ||||
| sorting will be enabled allowing sorting by clicking on column headers. Only  | ||||
| one column may be sorted at a time. | ||||
| * `autoScroll`: A boolean value that if true, will cause the table to automatically  | ||||
| scroll to the bottom as new data arrives. Auto-scroll can be disengaged manually  | ||||
| by scrolling away from the bottom of the table, and can also be enabled manually  | ||||
| by scrolling to the bottom of the table rows. | ||||
|  | ||||
| # Services  | ||||
|  | ||||
| The Open MCT Web platform provides a variety of services which can be retrieved  | ||||
|   | ||||
| @@ -151,11 +151,9 @@ emphasis on testing. | ||||
|   ensuring software passes that testing in order to ship on time; | ||||
|   may prefer to disable malfunctioning components and fix them | ||||
|   in a subsequent sprint, for example. | ||||
| * __Ship.__ Tag a code snapshot that has passed acceptance | ||||
|   testing and deploy that version. (Only true if acceptance | ||||
|   testing has passed by this point; if acceptance testing has not | ||||
| * [__Ship.__](version.md) Tag a code snapshot that has passed release/sprint | ||||
|   testing and deploy that version. (Only true if relevant | ||||
|   testing has passed by this point; if testing has not | ||||
|   been passed, will need to make ad hoc decisions with stakeholders, | ||||
|   e.g. "extend the sprint" or "defer shipment until end of next | ||||
|   sprint.") | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -3,8 +3,10 @@ | ||||
| The process used to develop Open MCT Web is described in the following | ||||
| documents: | ||||
|  | ||||
| * [Development Cycle](cycle.md): Describes how and when specific | ||||
| * The [Development Cycle](cycle.md) describes how and when specific | ||||
|   process points are repeated during development. | ||||
| * The [Version Guide](version.md) describes version numbering for | ||||
|   Open MCT (both semantics and process.) | ||||
| * Testing is described in two documents: | ||||
|   * The [Test Plan](testing/plan.md) summarizes the approaches used | ||||
|     to test Open MCT Web. | ||||
|   | ||||
							
								
								
									
										142
									
								
								docs/src/process/version.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								docs/src/process/version.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| # Version Guide | ||||
|  | ||||
| This document describes semantics and processes for providing version | ||||
| numbers for Open MCT, and additionally provides guidelines for dependent | ||||
| projects developed by the same team. | ||||
|  | ||||
| Versions are incremented at specific points in Open MCT's | ||||
| [Development Cycle](cycle.md); see that document for a description of | ||||
| sprints and releases. | ||||
|  | ||||
| ## Audience | ||||
|  | ||||
| Individuals interested in consuming version numbers can be categorized as | ||||
| follows: | ||||
|  | ||||
| * _Users_: Generally disinterested, occasionally wish to identify version | ||||
|   to cross-reference against documentation, or to report issues. | ||||
| * _Testers_: Want to identify which version of the software they are | ||||
|   testing, e.g. to file issues for defects. | ||||
| * _Internal developers_: Often, inverse of testers; want to identify which | ||||
|   version of software was/is in use when certain behavior is observed. Want | ||||
|   to be able to correlate versions in use with “streams” of development | ||||
|   (e.g. dev vs. prod), when possible. | ||||
| * _External developers_: Need to understand which version of software is | ||||
|   in use when developing/maintaining plug-ins, in order to ensure | ||||
|   compatibility of their software. | ||||
|  | ||||
| ## Version Reporting | ||||
|  | ||||
| Software versions should be reflected in the user interface of the | ||||
| application in three ways: | ||||
|  | ||||
| * _Version number_: A semantic version (see below) which serves both to | ||||
|   uniquely identify releases, as well as to inform plug-in developers | ||||
|   about compatibility with previous releases. | ||||
| * _Revision identifier_: While using git, the commit hash. Supports | ||||
|   internal developers and testers by uniquely identifying client | ||||
|   software snapshots. | ||||
| * _Branding_: Identifies which variant is in use. (Typically, Open MCT | ||||
|   is re-branded when deployed for a specific mission or center.) | ||||
|  | ||||
| ## Version Numbering | ||||
|  | ||||
| Open MCT shall provide version numbers consistent with | ||||
| [Semantic Versioning 2.0.0](http://semver.org/). In summary, versions | ||||
| are expressed in a "major.minor.patch" form, and incremented based on | ||||
| nature of changes to external API. Breaking changes require a "major" | ||||
| version increment; backwards-compatible changes require a "minor" | ||||
| version increment; neutral changes (such as bug fixes) require a "patch" | ||||
| version increment. A hyphen-separated suffix indicates a pre-release | ||||
| version, which may be unstable or may not fully meet compatibility | ||||
| requirements. | ||||
|  | ||||
| Additionally, the following project-specific standards will be used: | ||||
|  | ||||
| * During development, a "-SNAPSHOT" suffix shall be appended to the | ||||
|   version number. The version number before the suffix shall reflect | ||||
|   the next expected version number for release. | ||||
| * Prior to a 1.0.0 release, the _minor_ version will be incremented | ||||
|   on a per-release basis; the _patch_ version will be incremented on a | ||||
|   per-sprint basis. | ||||
| * Starting at version 1.0.0, version numbers will be updated with each | ||||
|   completed sprint. The version number for the sprint shall be | ||||
|   determined relative to the previous released version; the decision | ||||
|   to increment the _major_, _minor_, or _patch_ version should be | ||||
|   made based on the nature of changes during that release. (It is | ||||
|   recommended that these numbers are incremented as changes are | ||||
|   introduced, such that at end of release the version number may | ||||
|   be chosen by simply removing the suffix.) | ||||
| * The first three sprints in a release may be unstable; in these cases, a | ||||
|   unique version identifier should still be generated, but a suffix | ||||
|   should be included to indicate that the version is not necessarily | ||||
|   production-ready. Recommended suffixes are: | ||||
|  | ||||
|  Sprint |  Suffix | ||||
| :------:|:--------: | ||||
|    1    | `-alpha` | ||||
|    2    | `-beta` | ||||
|    3    | `-rc` | ||||
|  | ||||
| ### Scope of External API | ||||
|  | ||||
| "External API" refers to the API exposed to, documented for, and used by | ||||
| plug-in developers. Changes to interfaces used internally by Open MCT | ||||
| (or otherwise not documented for use externally) require only a _patch_ | ||||
| version bump. | ||||
|  | ||||
| ## Incrementing Versions | ||||
|  | ||||
| At the end of a sprint, the [project manager](cycle.md#roles) | ||||
| should update (or delegate the task of updating) Open MCT version | ||||
| numbers by the following process: | ||||
|  | ||||
| 1. Update version number in `package.json` | ||||
|   1. Remove `-SNAPSHOT` suffix. | ||||
|   2. Verify that resulting version number meets semantic versioning | ||||
|      requirements relative to previous stable version. Increment if | ||||
|      necessary. | ||||
|   3. If version is considered unstable (which may be the case during | ||||
|      the first three sprints of a release), apply a new suffix per | ||||
|      [Version Numbering](#version-numbering) guidance above. | ||||
| 2. Tag the release. | ||||
|   1. Commit changes to `package.json` on the `master` branch. | ||||
|      The commit message should reference the sprint being closed, | ||||
|      preferably by a URL reference to the associated Milestone in | ||||
|      GitHub. | ||||
|   2. Verify that build still completes, that application passes | ||||
|      smoke-testing, and that only differences from tested versions | ||||
|      are the changes to version number above. | ||||
|   3. Push the `master` branch. | ||||
|   4. Tag this commit with the version number, prepending the letter "v". | ||||
|      (e.g. `git tag v0.9.3-alpha`) | ||||
|   5. Push the tag to GitHub. (e.g. `git push origin v0.9.3-alpha`). | ||||
| 3. Upload a release archive. | ||||
|   1. Run `npm pack` to generate the archive. | ||||
|   2. Use the [GitHub release interface](https://github.com/nasa/openmct/releases) | ||||
|      to draft a new release. | ||||
|   3. Choose the existing tag for the new version (created and pushed above.) | ||||
|      Enter the tag name as the release name as well; see existing releases | ||||
|      for examples. | ||||
|   4. Attach the release archive. | ||||
|   5. Designate the release as a "pre-release" as appropriate (for instance, | ||||
|      when the version number has been suffixed as unstable, or when | ||||
|      the version number is below 1.0.0.) | ||||
| 4. Restore snapshot status in `package.json` | ||||
|   1. Remove any suffix from the version number, or increment the | ||||
|      _patch_ version if there is no suffix. | ||||
|   2. Append a `-SNAPSHOT` suffix. | ||||
|   3. Commit changes to `package.json` on the `master` branch. | ||||
|      The commit message should reference the sprint being opened, | ||||
|      preferably by a URL reference to the associated Milestone in | ||||
|      GitHub. | ||||
|   4. Verify that build still completes, that application passes | ||||
|      smoke-testing. | ||||
|   5. Push the `master` branch. | ||||
|  | ||||
| Projects dependent on Open MCT being co-developed by the Open MCT | ||||
| team should follow a similar process, except that they should | ||||
| additionally update their dependency on Open MCT to point to the | ||||
| latest archive when removing their `-SNAPSHOT` status, and | ||||
| that they should be pointed back to the `master` branch after | ||||
| this has completed. | ||||
| @@ -32,7 +32,7 @@ define([ | ||||
|  | ||||
|     legacyRegistry.register("example/eventGenerator", { | ||||
|         "name": "Event Message Generator", | ||||
|         "description": "Example of a component that produces event data.", | ||||
|         "description": "For development use. Creates sample event message data that mimics a live data stream.", | ||||
|         "extensions": { | ||||
|             "components": [ | ||||
|                 { | ||||
| @@ -49,16 +49,26 @@ define([ | ||||
|                 { | ||||
|                     "key": "eventGenerator", | ||||
|                     "name": "Event Message Generator", | ||||
|                     "glyph": "f", | ||||
|                     "description": "An event message generator", | ||||
|                     "glyph": "\u0066", | ||||
|                     "description": "For development use. Creates sample event message data that mimics a live data stream.", | ||||
|                     "priority": 10, | ||||
|                     "features": "creation", | ||||
|                     "model": { | ||||
|                         "telemetry": {} | ||||
|                     }, | ||||
|                     "telemetry": { | ||||
|                         "source": "eventGenerator", | ||||
|                         "domains": [ | ||||
|                             { | ||||
|                                 "key": "time", | ||||
|                                 "name": "Time", | ||||
|                                 "format": "utc" | ||||
|                             } | ||||
|                         ], | ||||
|                         "ranges": [ | ||||
|                             { | ||||
|                                 "key": "message", | ||||
|                                 "name": "Message", | ||||
|                                 "format": "string" | ||||
|                             } | ||||
|                         ] | ||||
|   | ||||
| @@ -37,7 +37,8 @@ define( | ||||
|             var | ||||
| 	            subscriptions = [], | ||||
| 	            genInterval = 1000, | ||||
| 	            startTime = Date.now(); | ||||
|                 generating = false, | ||||
|                 id = Math.random() * 100000; | ||||
|  | ||||
|             // | ||||
|             function matchesSource(request) { | ||||
| @@ -79,11 +80,13 @@ define( | ||||
|             } | ||||
|  | ||||
|             function startGenerating() { | ||||
|                 generating = true; | ||||
|                 $timeout(function () { | ||||
| 	                //console.log("startGenerating... " + Date.now()); | ||||
|                     handleSubscriptions(); | ||||
|                     if (subscriptions.length > 0) { | ||||
|                     if (generating && subscriptions.length > 0) { | ||||
|                         startGenerating(); | ||||
|                     } else { | ||||
|                         generating = false; | ||||
|                     } | ||||
|                 }, genInterval); | ||||
|             } | ||||
| @@ -93,7 +96,6 @@ define( | ||||
|                     callback: callback, | ||||
|                     requests: requests | ||||
|                 }; | ||||
|  | ||||
|                 function unsubscribe() { | ||||
|                     subscriptions = subscriptions.filter(function (s) { | ||||
|                         return s !== subscription; | ||||
| @@ -101,8 +103,7 @@ define( | ||||
|                 } | ||||
|  | ||||
|                 subscriptions.push(subscription); | ||||
|  | ||||
|                 if (subscriptions.length === 1) { | ||||
|                 if (!generating) { | ||||
|                     startGenerating(); | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -36,7 +36,7 @@ define([ | ||||
|  | ||||
|     legacyRegistry.register("example/generator", { | ||||
|         "name": "Sine Wave Generator", | ||||
|         "description": "Example of a component that produces dataa.", | ||||
|         "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", | ||||
|         "extensions": { | ||||
|             "components": [ | ||||
|                 { | ||||
| @@ -86,8 +86,9 @@ define([ | ||||
|                 { | ||||
|                     "key": "generator", | ||||
|                     "name": "Sine Wave Generator", | ||||
|                     "glyph": "T", | ||||
|                     "description": "A sine wave generator", | ||||
|                     "glyph": "\u0054", | ||||
|                     "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", | ||||
|                     "priority": 10, | ||||
|                     "features": "creation", | ||||
|                     "model": { | ||||
|                         "telemetry": { | ||||
| @@ -99,26 +100,46 @@ define([ | ||||
|                         "domains": [ | ||||
|                             { | ||||
|                                 "key": "time", | ||||
|                                 "name": "Time" | ||||
|                                 "name": "Time", | ||||
|                                 "type": "utc" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "key": "yesterday", | ||||
|                                 "name": "Yesterday" | ||||
|                                 "name": "Yesterday", | ||||
|                                 "type": "utc" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "key": "delta", | ||||
|                                 "name": "Delta", | ||||
|                                 "format": "example.delta" | ||||
|                                 "type": "example.delta" | ||||
|                             } | ||||
|                         ], | ||||
|                         "ranges": [ | ||||
|                             { | ||||
|                                 "key": "sin", | ||||
|                                 "name": "Sine" | ||||
|                                 "name": "Sine", | ||||
|                                 "type": "generic" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "key": "cos", | ||||
|                                 "name": "Cosine" | ||||
|                                 "name": "Cosine", | ||||
|                                 "type": "generic" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "key": "positive", | ||||
|                                 "name": "Positive Sine?", | ||||
|                                 "type": "enum", | ||||
|                                 "valueKey": "positive", | ||||
|                                 "enumerations": [ | ||||
|                                     { | ||||
|                                         "value": 0, | ||||
|                                         "text": "FALSE" | ||||
|                                     }, | ||||
|                                     { | ||||
|                                         "value": 1, | ||||
|                                         "text": "TRUE" | ||||
|                                     } | ||||
|                                 ] | ||||
|                             } | ||||
|                         ] | ||||
|                     }, | ||||
| @@ -126,7 +147,7 @@ define([ | ||||
|                         { | ||||
|                             "name": "Period", | ||||
|                             "control": "textfield", | ||||
|                             "cssclass": "l-small l-numeric", | ||||
|                             "cssclass": "l-input-sm l-numeric", | ||||
|                             "key": "period", | ||||
|                             "required": true, | ||||
|                             "property": [ | ||||
|   | ||||
| @@ -62,6 +62,9 @@ define( | ||||
|                 }, | ||||
|                 evaluate: function (datum, range) { | ||||
|                     range = range || 'sin'; | ||||
|                     if (['sin', 'cos'].indexOf(range) === -1) { | ||||
|                         return ''; | ||||
|                     } | ||||
|                     if (datum[range] > RED) { | ||||
|                         return LIMITS.rh; | ||||
|                     } | ||||
| @@ -84,4 +87,4 @@ define( | ||||
|  | ||||
|         return SinewaveLimitCapability; | ||||
|     } | ||||
| ); | ||||
| ); | ||||
|   | ||||
| @@ -19,10 +19,11 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,Promise*/ | ||||
| /*global define,setInterval,clearInterval*/ | ||||
|  | ||||
| /** | ||||
|  * Module defining SinewaveTelemetryProvider. Created by vwoeltje on 11/12/14. | ||||
|  * Rewritten by larkin on 05/06/2016. | ||||
|  */ | ||||
| define( | ||||
|     ["./SinewaveTelemetrySeries"], | ||||
| @@ -34,82 +35,109 @@ define( | ||||
|          * @constructor | ||||
|          */ | ||||
|         function SinewaveTelemetryProvider($q, $timeout) { | ||||
|             var subscriptions = []; | ||||
|  | ||||
|             // | ||||
|             function matchesSource(request) { | ||||
|                 return request.source === "generator"; | ||||
|             } | ||||
|  | ||||
|             // Used internally; this will be repacked by doPackage | ||||
|             function generateData(request) { | ||||
|                 return { | ||||
|                     key: request.key, | ||||
|                     telemetry: new SinewaveTelemetrySeries(request) | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             // | ||||
|             function doPackage(results) { | ||||
|                 var packaged = {}; | ||||
|                 results.forEach(function (result) { | ||||
|                     packaged[result.key] = result.telemetry; | ||||
|                 }); | ||||
|                 // Format as expected (sources -> keys -> telemetry) | ||||
|                 return { generator: packaged }; | ||||
|             } | ||||
|  | ||||
|             function requestTelemetry(requests) { | ||||
|                 return $timeout(function () { | ||||
|                     return doPackage(requests.filter(matchesSource).map(generateData)); | ||||
|                 }, 0); | ||||
|             } | ||||
|  | ||||
|             function handleSubscriptions() { | ||||
|                 subscriptions.forEach(function (subscription) { | ||||
|                     var requests = subscription.requests; | ||||
|                     subscription.callback(doPackage( | ||||
|                         requests.filter(matchesSource).map(generateData) | ||||
|                     )); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             function startGenerating() { | ||||
|                 $timeout(function () { | ||||
|                     handleSubscriptions(); | ||||
|                     if (subscriptions.length > 0) { | ||||
|                         startGenerating(); | ||||
|                     } | ||||
|                 }, 1000); | ||||
|             } | ||||
|  | ||||
|             function subscribe(callback, requests) { | ||||
|                 var subscription = { | ||||
|                     callback: callback, | ||||
|                     requests: requests | ||||
|                 }; | ||||
|  | ||||
|                 function unsubscribe() { | ||||
|                     subscriptions = subscriptions.filter(function (s) { | ||||
|                         return s !== subscription; | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 subscriptions.push(subscription); | ||||
|  | ||||
|                 if (subscriptions.length === 1) { | ||||
|                     startGenerating(); | ||||
|                 } | ||||
|  | ||||
|                 return unsubscribe; | ||||
|             } | ||||
|  | ||||
|             return { | ||||
|                 requestTelemetry: requestTelemetry, | ||||
|                 subscribe: subscribe | ||||
|             }; | ||||
|             this.$q = $q; | ||||
|             this.$timeout = $timeout; | ||||
|         } | ||||
|  | ||||
|         SinewaveTelemetryProvider.prototype.canHandleRequest = function (request) { | ||||
|             return request.source === 'generator'; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetryProvider.prototype.requestTelemetry = function (requests) { | ||||
|             var sinewaveRequests = requests.filter(this.canHandleRequest, this), | ||||
|                 response = { | ||||
|                     generator: {} | ||||
|                 }; | ||||
|  | ||||
|             sinewaveRequests.forEach(function (request) { | ||||
|                 response.generator[request.key] = this.singleRequest(request); | ||||
|             }, this); | ||||
|  | ||||
|             return response; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetryProvider.prototype.subscribe = function (callback, requests) { | ||||
|             var sinewaveRequests = requests.filter(this.canHandleRequest, this), | ||||
|                 unsubscribers = sinewaveRequests.map(function (request) { | ||||
|                     return this.singleSubscribe( | ||||
|                         function (series) { | ||||
|                             var response = { | ||||
|                                 generator: {} | ||||
|                             }; | ||||
|                             response.generator[request.key] = series; | ||||
|                             callback(response); | ||||
|                         }, | ||||
|                         request | ||||
|                     ); | ||||
|                 }, this); | ||||
|  | ||||
|             return function () { | ||||
|                 unsubscribers.forEach(function (unsubscribe) { | ||||
|                     unsubscribe(); | ||||
|                 }); | ||||
|             }; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetryProvider.prototype.singleRequest = function (request) { | ||||
|             var start = Math.floor(request.start / 1000) * 1000, | ||||
|                 end = Math.floor(request.end / 1000) * 1000, | ||||
|                 period = request.period || 30, | ||||
|                 data = [], | ||||
|                 current, | ||||
|                 i; | ||||
|  | ||||
|             for (current = start; current <= end; current += 2000) { | ||||
|                 i = Math.floor((current - start) / 1000); | ||||
|                 data.push({ | ||||
|                     sin: Math.sin(i * Math.PI * 2 / period), | ||||
|                     cos: Math.cos(i * Math.PI * 2 / period), | ||||
|                     positive: Math.sin(i * Math.PI * 2 / period) >= 0, | ||||
|                     time: current, | ||||
|                     yesterday: current - (60 * 60 * 24 * 1000), | ||||
|                     delta: current | ||||
|                 }); | ||||
|             } | ||||
|             return new SinewaveTelemetrySeries(data); | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         SinewaveTelemetryProvider.prototype.singleSubscribe = function (callback, options) { | ||||
|             // calculate interval position based on start - end; such that this data will line up with data generated by singleRequest. | ||||
|             var start = Math.floor((options.start || Date.now()) / 1000) * 1000, | ||||
|                 currentTime = Math.floor((options.end || Date.now()) / 1000) * 1000, | ||||
|                 period = options.period || 30, | ||||
|                 unsubscribe, | ||||
|                 generatePoint, | ||||
|                 interval; | ||||
|  | ||||
|             generatePoint = function () { | ||||
|                 var i = Math.floor((currentTime - start) / 1000), | ||||
|                     point = { | ||||
|                         sin: Math.sin(i * Math.PI * 2 / period), | ||||
|                         cos: Math.cos(i * Math.PI * 2 / period), | ||||
|                         positive: Math.sin(i * Math.PI * 2 / period) >= 0, | ||||
|                         time: currentTime, | ||||
|                         yesterday: currentTime - (60 * 60 * 24 * 1000), | ||||
|                         delta: currentTime | ||||
|                     }; | ||||
|                 currentTime += 1000; | ||||
|                 return point; | ||||
|             }; | ||||
|  | ||||
|             interval = setInterval(function () { | ||||
|                 var series = new SinewaveTelemetrySeries(generatePoint()); | ||||
|                 callback(series); | ||||
|             }, 1000); | ||||
|  | ||||
|             unsubscribe = function () { | ||||
|                 clearInterval(interval); | ||||
|             }; | ||||
|  | ||||
|             return unsubscribe; | ||||
|         }; | ||||
|  | ||||
|  | ||||
|  | ||||
|         return SinewaveTelemetryProvider; | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -32,47 +32,37 @@ define( | ||||
|         var ONE_DAY = 60 * 60 * 24, | ||||
|             firstObservedTime = Math.floor(SinewaveConstants.START_TIME / 1000); | ||||
|  | ||||
|         /** | ||||
|          * | ||||
|          * @constructor | ||||
|          */ | ||||
|         function SinewaveTelemetrySeries(request) { | ||||
|             var timeOffset = (request.domain === 'yesterday') ? ONE_DAY : 0, | ||||
|                 latestTime = Math.floor(Date.now() / 1000) - timeOffset, | ||||
|                 firstTime = firstObservedTime - timeOffset, | ||||
|                 endTime = (request.end !== undefined) ? | ||||
|                         Math.floor(request.end / 1000) : latestTime, | ||||
|                 count = Math.min(endTime, latestTime) - firstTime, | ||||
|                 period = +request.period || 30, | ||||
|                 generatorData = {}, | ||||
|                 requestStart = (request.start === undefined) ? firstTime : | ||||
|                         Math.max(Math.floor(request.start / 1000), firstTime), | ||||
|                 offset = requestStart - firstTime; | ||||
|  | ||||
|             if (request.size !== undefined) { | ||||
|                 offset = Math.max(offset, count - request.size); | ||||
|         function SinewaveTelemetrySeries(data) { | ||||
|             if (!Array.isArray(data)) { | ||||
|                 data = [data]; | ||||
|             } | ||||
|  | ||||
|             generatorData.getPointCount = function () { | ||||
|                 return count - offset; | ||||
|             }; | ||||
|  | ||||
|             generatorData.getDomainValue = function (i, domain) { | ||||
|                 // delta uses the same numeric values as the default domain, | ||||
|                 // so it's not checked for here, just formatted for display | ||||
|                 // differently. | ||||
|                 return (i + offset) * 1000 + firstTime * 1000 - | ||||
|                     (domain === 'yesterday' ? (ONE_DAY * 1000) : 0); | ||||
|             }; | ||||
|  | ||||
|             generatorData.getRangeValue = function (i, range) { | ||||
|                 range = range || "sin"; | ||||
|                 return Math[range]((i + offset) * Math.PI * 2 / period); | ||||
|             }; | ||||
|  | ||||
|             return generatorData; | ||||
|             this.data = data; | ||||
|         } | ||||
|  | ||||
|         SinewaveTelemetrySeries.prototype.getPointCount = function () { | ||||
|             return this.data.length; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetrySeries.prototype.getDomainValue = function (i, domain) { | ||||
|             return this.getDatum(i)[domain]; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetrySeries.prototype.getRangeValue = function (i, range) { | ||||
|             return this.getDatum(i)[range]; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetrySeries.prototype.getDatum = function (i) { | ||||
|             if (i >= this.data.length || i < 0) { | ||||
|                 throw new Error('IndexOutOfRange: index not available in series.'); | ||||
|             } | ||||
|             return this.data[i]; | ||||
|         }; | ||||
|  | ||||
|         SinewaveTelemetrySeries.prototype.getData = function () { | ||||
|             return this.data; | ||||
|         }; | ||||
|  | ||||
|         return SinewaveTelemetrySeries; | ||||
|  | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -49,8 +49,10 @@ define([ | ||||
|                 { | ||||
|                     "key": "imagery", | ||||
|                     "name": "Example Imagery", | ||||
|                     "glyph": "T", | ||||
|                     "glyph": "\u00e3", | ||||
|                     "features": "creation", | ||||
|                     "description": "For development use. Creates example imagery data that mimics a live imagery stream.", | ||||
|                     "priority": 10, | ||||
|                     "model": { | ||||
|                         "telemetry": {} | ||||
|                     }, | ||||
|   | ||||
| @@ -54,7 +54,7 @@ define([ | ||||
|             { | ||||
|                 "name": "Measurement", | ||||
|                 "key": "msl.measurement", | ||||
|                 "glyph": "T", | ||||
|                 "glyph": "\u0054", | ||||
|                 "model": {"telemetry": {}}, | ||||
|                 "telemetry": { | ||||
|                     "source": "rems.source", | ||||
|   | ||||
| @@ -80,9 +80,10 @@ define([ | ||||
|             "types": [ | ||||
|                 { | ||||
|                     "key": "plot", | ||||
|                     "name": "Telemetry Plot", | ||||
|                     "glyph": "t", | ||||
|                     "description": "A plot for displaying telemetry", | ||||
|                     "name": "Example Telemetry Plot", | ||||
|                     "glyph": "\u0074", | ||||
|                     "description": "For development use. A plot for displaying telemetry.", | ||||
|                     "priority": 10, | ||||
|                     "delegates": [ | ||||
|                         "telemetry" | ||||
|                     ], | ||||
|   | ||||
| @@ -60,7 +60,8 @@ var gulp = require('gulp'), | ||||
|             singleRun: true | ||||
|         }, | ||||
|         sass: { | ||||
|             includePaths: bourbon.includePaths | ||||
|             includePaths: bourbon.includePaths, | ||||
|             sourceComments: true | ||||
|         }, | ||||
|         replace: { | ||||
|             variables: { | ||||
| @@ -90,7 +91,8 @@ gulp.task('stylesheets', function () { | ||||
|         .pipe(sourcemaps.init()) | ||||
|         .pipe(sass(options.sass).on('error', sass.logError)) | ||||
|         .pipe(rename(function (file) { | ||||
|             file.dirname = file.dirname.replace('/sass', '/css'); | ||||
|             file.dirname = | ||||
|                 file.dirname.replace(path.sep + 'sass', path.sep + 'css'); | ||||
|             return file; | ||||
|         })) | ||||
|         .pipe(sourcemaps.write('.')) | ||||
|   | ||||
| @@ -81,7 +81,12 @@ module.exports = function(config) { | ||||
|         coverageReporter: { | ||||
|             dir: process.env.CIRCLE_ARTIFACTS ? | ||||
|                 process.env.CIRCLE_ARTIFACTS + '/coverage' : | ||||
|                 "dist/coverage" | ||||
|                 "dist/coverage", | ||||
|             check: { | ||||
|                 global: { | ||||
|                     lines: 80 | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         // HTML test reporting. | ||||
|   | ||||
							
								
								
									
										16
									
								
								main.js
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								main.js
									
									
									
									
									
								
							| @@ -33,7 +33,10 @@ requirejs.config({ | ||||
|         "saveAs": "bower_components/FileSaver.js/FileSaver.min", | ||||
|         "screenfull": "bower_components/screenfull/dist/screenfull.min", | ||||
|         "text": "bower_components/text/text", | ||||
|         "uuid": "bower_components/node-uuid/uuid" | ||||
|         "uuid": "bower_components/node-uuid/uuid", | ||||
|         "zepto": "bower_components/zepto/zepto.min", | ||||
|         "eventemitter2": "bower_components/eventemitter2/lib/eventemitter2", | ||||
|         "lodash": "bower_components/lodash/lodash" | ||||
|     }, | ||||
|     "shim": { | ||||
|         "angular": { | ||||
| @@ -44,6 +47,9 @@ requirejs.config({ | ||||
|         }, | ||||
|         "moment-duration-format": { | ||||
|             "deps": [ "moment" ] | ||||
|         }, | ||||
|         "zepto": { | ||||
|             "exports": "Zepto" | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| @@ -70,12 +76,10 @@ define([ | ||||
|     './platform/exporters/bundle', | ||||
|     './platform/telemetry/bundle', | ||||
|     './platform/features/clock/bundle', | ||||
|     './platform/features/events/bundle', | ||||
|     './platform/features/imagery/bundle', | ||||
|     './platform/features/layout/bundle', | ||||
|     './platform/features/pages/bundle', | ||||
|     './platform/features/plot/bundle', | ||||
|     './platform/features/scrolling/bundle', | ||||
|     './platform/features/timeline/bundle', | ||||
|     './platform/features/table/bundle', | ||||
|     './platform/forms/bundle', | ||||
| @@ -88,6 +92,10 @@ define([ | ||||
|     './platform/search/bundle', | ||||
|     './platform/status/bundle', | ||||
|     './platform/commonUI/regions/bundle', | ||||
|     './api/telemetry-api/bundle', | ||||
|     './api/object-api/bundle', | ||||
|     './api/composition-api/bundle', | ||||
|     './example/scratchpad/bundle', | ||||
|  | ||||
|     './example/imagery/bundle', | ||||
|     './example/eventGenerator/bundle', | ||||
| @@ -101,4 +109,4 @@ define([ | ||||
|             return new Main().run(legacyRegistry); | ||||
|         } | ||||
|     }; | ||||
| }); | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "openmctweb", | ||||
|   "version": "0.9.2", | ||||
|   "description": "The Open MCT Web core platform", | ||||
|   "version": "0.10.1-SNAPSHOT", | ||||
|   "description": "The Open MCT core platform", | ||||
|   "dependencies": { | ||||
|     "express": "^4.13.1", | ||||
|     "minimist": "^1.1.1", | ||||
| @@ -38,7 +38,7 @@ | ||||
|     "moment": "^2.11.1", | ||||
|     "node-bourbon": "^4.2.3", | ||||
|     "phantomjs-prebuilt": "^2.1.0", | ||||
|     "requirejs": "^2.1.17", | ||||
|     "requirejs": "2.1.x", | ||||
|     "split": "^1.0.0" | ||||
|   }, | ||||
|   "scripts": { | ||||
| @@ -49,7 +49,7 @@ | ||||
|     "jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api", | ||||
|     "otherdoc": "node docs/gendocs.js --in docs/src --out target/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'", | ||||
|     "docs": "npm run jsdoc ; npm run otherdoc", | ||||
|     "prepublish": "./node_modules/bower/bin/bower install && ./node_modules/gulp/bin/gulp.js install" | ||||
|     "prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|   | ||||
| @@ -44,22 +44,7 @@ | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="holder l-flex-col flex-elem grows l-object-wrapper"> | ||||
|         <div ng-if="isEditable" class="holder l-flex-col flex-elem grows l-object-wrapper-inner"> | ||||
|             <!-- Toolbar and Save/Cancel buttons --> | ||||
|             <div class="l-edit-controls flex-elem l-flex-row flex-align-end"> | ||||
|                 <mct-representation key="'edit-action-buttons'" | ||||
|                                     mct-object="domainObject" | ||||
|                                     class='flex-elem conclude-editing'> | ||||
|                 </mct-representation> | ||||
|  | ||||
|             </div> | ||||
|             <mct-representation key="representation.selected.key" | ||||
|                                 mct-object="representation.selected.key && domainObject" | ||||
|                                 class="abs flex-elem grows object-holder-main scroll" | ||||
|                                 toolbar="toolbar"> | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|         <div ng-if="!isEditable" class="holder l-flex-col flex-elem grows l-object-wrapper-inner"> | ||||
|         <div class="holder l-flex-col flex-elem grows l-object-wrapper-inner"> | ||||
|             <!-- Toolbar and Save/Cancel buttons --> | ||||
|             <div class="l-edit-controls flex-elem l-flex-row flex-align-end"> | ||||
|                 <mct-representation key="'edit-action-buttons'" | ||||
|   | ||||
| @@ -32,6 +32,7 @@ | ||||
|         </li> | ||||
|         <li ng-if="contextutalParents.length > 0"> | ||||
|             <em class="t-inspector-part-header" title="The location of this linked object.">Location</em> | ||||
|             <div ng-if="primaryParents.length > 0" class="section-header">This Object</div> | ||||
|             <span class="inspector-location" | ||||
|                   ng-repeat="parent in contextutalParents" | ||||
|                   ng-class="{ last:($index + 1) === contextualParents.length }"> | ||||
| @@ -44,7 +45,7 @@ | ||||
|             </span> | ||||
|         </li> | ||||
|         <li ng-if="primaryParents.length > 0"> | ||||
|             <em class="t-inspector-part-header" title="The location of the original object that this was linked from.">Original Location</em> | ||||
|             <div class="section-header">Object's Original</div> | ||||
|             <span class="inspector-location" | ||||
|                   ng-repeat="parent in primaryParents" | ||||
|                   ng-class="{ last:($index + 1) === primaryParents.length }"> | ||||
|   | ||||
| @@ -29,5 +29,5 @@ | ||||
|  --> | ||||
| <mct-representation mct-object="domainObject" | ||||
|                     key="viewObjectTemplate || 'browse-object'" | ||||
|                     class="abs holder holder-object"> | ||||
|                     class="abs holder"> | ||||
| </mct-representation> | ||||
|   | ||||
| @@ -93,27 +93,23 @@ define( | ||||
|                 return wizard.populateObjectFromInput(formValue, newObject); | ||||
|             } | ||||
|  | ||||
|             function addToParent (populatedObject) { | ||||
|                 parentObject.getCapability('composition').add(populatedObject); | ||||
|                 return parentObject.getCapability('persistence').persist().then(function(){ | ||||
|                     return parentObject; | ||||
|                 }); | ||||
|             function persistAndReturn(domainObject) { | ||||
|                 return domainObject.getCapability('persistence') | ||||
|                     .persist() | ||||
|                     .then(function () { | ||||
|                         return domainObject; | ||||
|                     }); | ||||
|             } | ||||
|  | ||||
|             function save(object) { | ||||
|                 /* | ||||
|                 It's necessary to persist the new sub-object in order | ||||
|                  that it can be retrieved for composition in the parent. | ||||
|                  Future refactoring that allows temporary objects to be | ||||
|                  retrieved from object services will make this unnecessary. | ||||
|                  */ | ||||
|                 return object.getCapability('editor').save(true); | ||||
|             function addToParent (populatedObject) { | ||||
|                 parentObject.getCapability('composition').add(populatedObject); | ||||
|                 return persistAndReturn(parentObject); | ||||
|             } | ||||
|  | ||||
|             return this.dialogService | ||||
|                 .getUserInput(wizard.getFormStructure(false), wizard.getInitialFormValue()) | ||||
|                 .then(populateObjectFromInput) | ||||
|                 .then(save) | ||||
|                 .then(persistAndReturn) | ||||
|                 .then(addToParent); | ||||
|  | ||||
|         }; | ||||
|   | ||||
| @@ -32,9 +32,13 @@ define([ | ||||
|     "./src/actions/PropertiesAction", | ||||
|     "./src/actions/RemoveAction", | ||||
|     "./src/actions/SaveAction", | ||||
|     "./src/actions/SaveAsAction", | ||||
|     "./src/actions/CancelAction", | ||||
|     "./src/policies/EditActionPolicy", | ||||
|     "./src/policies/EditableLinkPolicy", | ||||
|     "./src/policies/EditableMovePolicy", | ||||
|     "./src/policies/EditNavigationPolicy", | ||||
|     "./src/policies/EditContextualActionPolicy", | ||||
|     "./src/representers/EditRepresenter", | ||||
|     "./src/representers/EditToolbarRepresenter", | ||||
|     "text!./res/templates/library.html", | ||||
| @@ -54,9 +58,13 @@ define([ | ||||
|     PropertiesAction, | ||||
|     RemoveAction, | ||||
|     SaveAction, | ||||
|     SaveAsAction, | ||||
|     CancelAction, | ||||
|     EditActionPolicy, | ||||
|     EditableLinkPolicy, | ||||
|     EditableMovePolicy, | ||||
|     EditNavigationPolicy, | ||||
|     EditContextualActionPolicy, | ||||
|     EditRepresenter, | ||||
|     EditToolbarRepresenter, | ||||
|     libraryTemplate, | ||||
| @@ -161,6 +169,15 @@ define([ | ||||
|                     "implementation": SaveAction, | ||||
|                     "name": "Save", | ||||
|                     "description": "Save changes made to these objects.", | ||||
|                     "depends": [], | ||||
|                     "priority": "mandatory" | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "save", | ||||
|                     "category": "conclude-editing", | ||||
|                     "implementation": SaveAsAction, | ||||
|                     "name": "Save", | ||||
|                     "description": "Save changes made to these objects.", | ||||
|                     "depends": [ | ||||
|                         "$injector", | ||||
|                         "policyService", | ||||
| @@ -187,6 +204,19 @@ define([ | ||||
|                     "category": "action", | ||||
|                     "implementation": EditActionPolicy | ||||
|                 }, | ||||
|                 { | ||||
|                     "category": "action", | ||||
|                     "implementation": EditContextualActionPolicy, | ||||
|                     "depends": ["navigationService", "editModeBlacklist", "nonEditContextBlacklist"] | ||||
|                 }, | ||||
|                 { | ||||
|                     "category": "action", | ||||
|                     "implementation": EditableMovePolicy | ||||
|                 }, | ||||
|                 { | ||||
|                     "category": "action", | ||||
|                     "implementation": EditableLinkPolicy | ||||
|                 }, | ||||
|                 { | ||||
|                     "category": "navigation", | ||||
|                     "message": "There are unsaved changes.", | ||||
| @@ -244,6 +274,16 @@ define([ | ||||
|                 { | ||||
|                     "implementation": EditToolbarRepresenter | ||||
|                 } | ||||
|             ], | ||||
|             "constants": [ | ||||
|                 { | ||||
|                     "key":"editModeBlacklist", | ||||
|                     "value": ["copy", "follow", "window", "link", "locate"] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "nonEditContextBlacklist", | ||||
|                     "value": ["copy", "follow", "properties", "move", "link", "remove", "locate"] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
|   | ||||
| @@ -19,16 +19,19 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div ng-controller="ElementsController"> | ||||
| <div ng-controller="ElementsController" class="flex-elem l-flex-col holder grows"> | ||||
|     <mct-include key="'input-filter'" | ||||
|                  class="flex-elem holder" | ||||
|                  ng-model="filterBy"> | ||||
|     </mct-include> | ||||
|     <div class="current-elements abs" style="height: 100%;"> | ||||
|     <div class="flex-elem grows vscroll"> | ||||
|         <ul class="tree"> | ||||
|             <li ng-repeat="containedObject in composition | filter:searchText"> | ||||
|                 <span class="tree-item"> | ||||
|                     <mct-representation key="'label'" mct-object="containedObject"> | ||||
|                     <mct-representation | ||||
|                             class="rep-object-label" | ||||
|                             key="'label'" | ||||
|                             mct-object="containedObject"> | ||||
|                     </mct-representation> | ||||
|                 </span> | ||||
|             </li> | ||||
|   | ||||
| @@ -24,8 +24,8 @@ | ||||
|  | ||||
|  | ||||
| define( | ||||
|     ['../../../browse/src/creation/CreateWizard'], | ||||
|     function (CreateWizard) { | ||||
|     [], | ||||
|     function () { | ||||
|         'use strict'; | ||||
|  | ||||
|         /** | ||||
| @@ -37,31 +37,11 @@ define( | ||||
|          * @memberof platform/commonUI/edit | ||||
|          */ | ||||
|         function SaveAction( | ||||
|             $injector, | ||||
|             policyService, | ||||
|             dialogService, | ||||
|             creationService, | ||||
|             copyService, | ||||
|             context | ||||
|         ) { | ||||
|             this.domainObject = (context || {}).domainObject; | ||||
|             this.injectObjectService = function(){ | ||||
|                 this.objectService = $injector.get("objectService"); | ||||
|             }; | ||||
|             this.policyService = policyService; | ||||
|             this.dialogService = dialogService; | ||||
|             this.creationService = creationService; | ||||
|             this.copyService = copyService; | ||||
|         } | ||||
|  | ||||
|         SaveAction.prototype.getObjectService = function(){ | ||||
|             // Lazily acquire object service (avoids cyclical dependency) | ||||
|             if (!this.objectService) { | ||||
|                 this.injectObjectService(); | ||||
|             } | ||||
|             return this.objectService; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Save changes and conclude editing. | ||||
|          * | ||||
| @@ -70,9 +50,7 @@ define( | ||||
|          * @memberof platform/commonUI/edit.SaveAction# | ||||
|          */ | ||||
|         SaveAction.prototype.perform = function () { | ||||
|             var domainObject = this.domainObject, | ||||
|                 copyService = this.copyService, | ||||
|                 self = this; | ||||
|             var domainObject = this.domainObject; | ||||
|  | ||||
|             function resolveWith(object){ | ||||
|                 return function () { | ||||
| @@ -80,64 +58,13 @@ define( | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             function doWizardSave(parent) { | ||||
|                 var context = domainObject.getCapability("context"), | ||||
|                     wizard = new CreateWizard( | ||||
|                         domainObject, | ||||
|                         parent, | ||||
|                         self.policyService | ||||
|                     ); | ||||
|  | ||||
|                 return self.dialogService | ||||
|                     .getUserInput( | ||||
|                         wizard.getFormStructure(true), | ||||
|                         wizard.getInitialFormValue() | ||||
|                     ) | ||||
|                     .then(wizard.populateObjectFromInput.bind(wizard)); | ||||
|             } | ||||
|  | ||||
|             function fetchObject(objectId){ | ||||
|                 return self.getObjectService().getObjects([objectId]).then(function(objects){ | ||||
|                     return objects[objectId]; | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             function getParent(object){ | ||||
|                 return fetchObject(object.getModel().location); | ||||
|             } | ||||
|  | ||||
|             function allowClone(objectToClone) { | ||||
|                 return (objectToClone.getId() === domainObject.getId()) || | ||||
|                     objectToClone.getCapability('location').isOriginal(); | ||||
|             } | ||||
|  | ||||
|             function cloneIntoParent(parent) { | ||||
|                 return copyService.perform(domainObject, parent, allowClone); | ||||
|             } | ||||
|  | ||||
|             function cancelEditingAfterClone(clonedObject) { | ||||
|                 return domainObject.getCapability("editor").cancel() | ||||
|                     .then(resolveWith(clonedObject)); | ||||
|             } | ||||
|  | ||||
|             // Invoke any save behavior introduced by the editor capability; | ||||
|             // this is introduced by EditableDomainObject which is | ||||
|             // used to insulate underlying objects from changes made | ||||
|             // during editing. | ||||
|             function doSave() { | ||||
|                 //This is a new 'virtual object' that has not been persisted | ||||
|                 // yet. | ||||
|                 if (domainObject.getModel().persisted === undefined){ | ||||
|                     return getParent(domainObject) | ||||
|                         .then(doWizardSave) | ||||
|                         .then(getParent) | ||||
|                         .then(cloneIntoParent) | ||||
|                         .then(cancelEditingAfterClone) | ||||
|                         .catch(resolveWith(false)); | ||||
|                 } else { | ||||
|                     return domainObject.getCapability("editor").save() | ||||
|                         .then(resolveWith(domainObject.getOriginalObject())); | ||||
|                 } | ||||
|                 return domainObject.getCapability("editor").save() | ||||
|                     .then(resolveWith(domainObject.getOriginalObject())); | ||||
|             } | ||||
|  | ||||
|             // Discard the current root view (which will be the editing | ||||
| @@ -162,7 +89,8 @@ define( | ||||
|         SaveAction.appliesTo = function (context) { | ||||
|             var domainObject = (context || {}).domainObject; | ||||
|             return domainObject !== undefined && | ||||
|                 domainObject.hasCapability("editor"); | ||||
|                 domainObject.hasCapability("editor") && | ||||
|                 domainObject.getModel().persisted !== undefined; | ||||
|         }; | ||||
|  | ||||
|         return SaveAction; | ||||
|   | ||||
							
								
								
									
										169
									
								
								platform/commonUI/edit/src/actions/SaveAsAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								platform/commonUI/edit/src/actions/SaveAsAction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
| /*jslint es5: true */ | ||||
|  | ||||
|  | ||||
| define( | ||||
|     ['../../../browse/src/creation/CreateWizard'], | ||||
|     function (CreateWizard) { | ||||
|         'use strict'; | ||||
|  | ||||
|         /** | ||||
|          * The "Save" action; the action triggered by clicking Save from | ||||
|          * Edit Mode. Exits the editing user interface and invokes object | ||||
|          * capabilities to persist the changes that have been made. | ||||
|          * @constructor | ||||
|          * @implements {Action} | ||||
|          * @memberof platform/commonUI/edit | ||||
|          */ | ||||
|         function SaveAsAction( | ||||
|             $injector, | ||||
|             policyService, | ||||
|             dialogService, | ||||
|             creationService, | ||||
|             copyService, | ||||
|             context | ||||
|         ) { | ||||
|             this.domainObject = (context || {}).domainObject; | ||||
|             this.injectObjectService = function(){ | ||||
|                 this.objectService = $injector.get("objectService"); | ||||
|             }; | ||||
|             this.policyService = policyService; | ||||
|             this.dialogService = dialogService; | ||||
|             this.creationService = creationService; | ||||
|             this.copyService = copyService; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * @private | ||||
|          */ | ||||
|         SaveAsAction.prototype.createWizard = function (parent) { | ||||
|             return new CreateWizard( | ||||
|                 this.domainObject, | ||||
|                 parent, | ||||
|                 this.policyService | ||||
|             ); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * @private | ||||
|          */ | ||||
|         SaveAsAction.prototype.getObjectService = function(){ | ||||
|             // Lazily acquire object service (avoids cyclical dependency) | ||||
|             if (!this.objectService) { | ||||
|                 this.injectObjectService(); | ||||
|             } | ||||
|             return this.objectService; | ||||
|         }; | ||||
|  | ||||
|         function resolveWith(object){ | ||||
|             return function () { | ||||
|                 return object; | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Save changes and conclude editing. | ||||
|          * | ||||
|          * @returns {Promise} a promise that will be fulfilled when | ||||
|          *          cancellation has completed | ||||
|          * @memberof platform/commonUI/edit.SaveAction# | ||||
|          */ | ||||
|         SaveAsAction.prototype.perform = function () { | ||||
|             // Discard the current root view (which will be the editing | ||||
|             // UI, which will have been pushed atop the Browse UI.) | ||||
|             function returnToBrowse(object) { | ||||
|                 if (object) { | ||||
|                     object.getCapability("action").perform("navigate"); | ||||
|                 } | ||||
|                 return object; | ||||
|             } | ||||
|  | ||||
|             return this.save().then(returnToBrowse); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * @private | ||||
|          */ | ||||
|         SaveAsAction.prototype.save = function () { | ||||
|             var self = this, | ||||
|                 domainObject = this.domainObject, | ||||
|                 copyService = this.copyService; | ||||
|  | ||||
|             function doWizardSave(parent) { | ||||
|                 var wizard = self.createWizard(parent); | ||||
|  | ||||
|                 return self.dialogService | ||||
|                     .getUserInput(wizard.getFormStructure(true), | ||||
|                         wizard.getInitialFormValue() | ||||
|                     ).then(wizard.populateObjectFromInput.bind(wizard)); | ||||
|             } | ||||
|  | ||||
|             function fetchObject(objectId){ | ||||
|                 return self.getObjectService().getObjects([objectId]).then(function(objects){ | ||||
|                     return objects[objectId]; | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             function getParent(object){ | ||||
|                 return fetchObject(object.getModel().location); | ||||
|             } | ||||
|  | ||||
|             function allowClone(objectToClone) { | ||||
|                 return (objectToClone.getId() === domainObject.getId()) || | ||||
|                     objectToClone.getCapability('location').isOriginal(); | ||||
|             } | ||||
|  | ||||
|             function cloneIntoParent(parent) { | ||||
|                 return copyService.perform(domainObject, parent, allowClone); | ||||
|             } | ||||
|  | ||||
|             function cancelEditingAfterClone(clonedObject) { | ||||
|                 return domainObject.getCapability("editor").cancel() | ||||
|                     .then(resolveWith(clonedObject)); | ||||
|             } | ||||
|  | ||||
|             return getParent(domainObject) | ||||
|                 .then(doWizardSave) | ||||
|                 .then(getParent) | ||||
|                 .then(cloneIntoParent) | ||||
|                 .then(cancelEditingAfterClone) | ||||
|                 .catch(resolveWith(false)); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if this action is applicable in a given context. | ||||
|          * This will ensure that a domain object is present in the context, | ||||
|          * and that this domain object is in Edit mode. | ||||
|          * @returns true if applicable | ||||
|          */ | ||||
|         SaveAsAction.appliesTo = function (context) { | ||||
|             var domainObject = (context || {}).domainObject; | ||||
|             return domainObject !== undefined && | ||||
|                 domainObject.hasCapability("editor") && | ||||
|                 domainObject.getModel().persisted === undefined; | ||||
|         }; | ||||
|  | ||||
|         return SaveAsAction; | ||||
|     } | ||||
| ); | ||||
| @@ -38,14 +38,6 @@ define( | ||||
|             this.policyService = policyService; | ||||
|         } | ||||
|  | ||||
|         function applicableView(key){ | ||||
|             return ['plot', 'scrolling'].indexOf(key) >= 0; | ||||
|         } | ||||
|  | ||||
|         function editableType(key){ | ||||
|             return key === 'telemetry.panel'; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Get a count of views which are not flagged as non-editable. | ||||
|          * @private | ||||
| @@ -65,7 +57,8 @@ define( | ||||
|  | ||||
|             // A view is editable unless explicitly flagged as not | ||||
|             (views || []).forEach(function (view) { | ||||
|                 if (view.editable === true || (applicableView(view.key) && editableType(type.getKey()))) { | ||||
|                 if (view.editable === true || | ||||
|                     (view.key === 'plot' && type.getKey() === 'telemetry.panel')) { | ||||
|                     count++; | ||||
|                 } | ||||
|             }); | ||||
|   | ||||
| @@ -0,0 +1,74 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         /** | ||||
|          * Policy controlling whether the context menu is visible when | ||||
|          * objects are being edited | ||||
|          * @param navigationService | ||||
|          * @param editModeBlacklist A blacklist of actions disallowed from | ||||
|          * context menu when navigated object is being edited | ||||
|          * @param nonEditContextBlacklist A blacklist of actions disallowed | ||||
|          * from context menu of non-editable objects, when navigated object | ||||
|          * is being edited | ||||
|          * @constructor | ||||
|          */ | ||||
|         function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) { | ||||
|             this.navigationService = navigationService; | ||||
|  | ||||
|             //The list of objects disallowed on target object when in edit mode | ||||
|             this.editModeBlacklist = editModeBlacklist; | ||||
|             //The list of objects disallowed on target object that is not in | ||||
|             // edit mode (ie. the context menu in the tree on the LHS). | ||||
|             this.nonEditContextBlacklist = nonEditContextBlacklist; | ||||
|         } | ||||
|  | ||||
|         function isParentEditable(object) { | ||||
|             var parent = object.hasCapability("context") && object.getCapability("context").getParent(); | ||||
|             return !!parent && parent.hasCapability("editor"); | ||||
|         } | ||||
|  | ||||
|         EditContextualActionPolicy.prototype.allow = function (action, context) { | ||||
|             var selectedObject = context.domainObject, | ||||
|                 navigatedObject = this.navigationService.getNavigation(), | ||||
|                 actionMetadata = action.getMetadata ? action.getMetadata() : {}; | ||||
|  | ||||
|             if (navigatedObject.hasCapability('editor')) { | ||||
|                 if (selectedObject.hasCapability('editor') || isParentEditable(selectedObject)){ | ||||
|                     return this.editModeBlacklist.indexOf(actionMetadata.key) === -1; | ||||
|                 } else { | ||||
|                     //Target is in the context menu | ||||
|                     return this.nonEditContextBlacklist.indexOf(actionMetadata.key) === -1; | ||||
|                 } | ||||
|             } else { | ||||
|                 return true; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         return EditContextualActionPolicy; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										52
									
								
								platform/commonUI/edit/src/policies/EditableLinkPolicy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								platform/commonUI/edit/src/policies/EditableLinkPolicy.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([], function () { | ||||
|     "use strict"; | ||||
|  | ||||
|     /** | ||||
|      * Policy suppressing links when the linked-to domain object is in | ||||
|      * edit mode. Domain objects being edited may not have been persisted, | ||||
|      * so creating links to these can result in inconsistent state. | ||||
|      * | ||||
|      * @memberof platform/commonUI/edit | ||||
|      * @constructor | ||||
|      * @implements {Policy.<View, DomainObject>} | ||||
|      */ | ||||
|     function EditableLinkPolicy() { | ||||
|     } | ||||
|  | ||||
|     EditableLinkPolicy.prototype.allow = function (action, context) { | ||||
|         var key = action.getMetadata().key; | ||||
|  | ||||
|         if (key === 'link') { | ||||
|             return !((context.selectedObject || context.domainObject) | ||||
|                 .hasCapability('editor')); | ||||
|         } | ||||
|  | ||||
|         // Like all policies, allow by default. | ||||
|         return true; | ||||
|     }; | ||||
|  | ||||
|     return EditableLinkPolicy; | ||||
| }); | ||||
							
								
								
									
										51
									
								
								platform/commonUI/edit/src/policies/EditableMovePolicy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								platform/commonUI/edit/src/policies/EditableMovePolicy.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([], function () { | ||||
|     "use strict"; | ||||
|  | ||||
|     /** | ||||
|      * Policy suppressing move actions among editable and non-editable | ||||
|      * domain objects. | ||||
|      * @memberof platform/commonUI/edit | ||||
|      * @constructor | ||||
|      * @implements {Policy.<View, DomainObject>} | ||||
|      */ | ||||
|     function EditableMovePolicy() { | ||||
|     } | ||||
|  | ||||
|     EditableMovePolicy.prototype.allow = function (action, context) { | ||||
|         var domainObject = context.domainObject, | ||||
|             selectedObject = context.selectedObject, | ||||
|             key = action.getMetadata().key; | ||||
|  | ||||
|         if (key === 'move' && domainObject.hasCapability('editor')) { | ||||
|             return !!selectedObject && selectedObject.hasCapability('editor'); | ||||
|         } | ||||
|  | ||||
|         // Like all policies, allow by default. | ||||
|         return true; | ||||
|     }; | ||||
|  | ||||
|     return EditableMovePolicy; | ||||
| }); | ||||
| @@ -118,8 +118,6 @@ define( | ||||
|             // Track the represented object | ||||
|             this.domainObject = representedObject; | ||||
|  | ||||
|             this.scope.isEditable = representedObject.getCapability('status').get('editing'); | ||||
|  | ||||
|             // Ensure existing watches are released | ||||
|             this.destroy(); | ||||
|  | ||||
|   | ||||
| @@ -27,11 +27,11 @@ define( | ||||
|         "use strict"; | ||||
|  | ||||
|         describe("The Save action", function () { | ||||
|             var mockLocation, | ||||
|                 mockDomainObject, | ||||
|             var mockDomainObject, | ||||
|                 mockEditorCapability, | ||||
|                 mockUrlService, | ||||
|                 actionContext, | ||||
|                 mockActionCapability, | ||||
|                 capabilities = {}, | ||||
|                 action; | ||||
|  | ||||
|             function mockPromise(value) { | ||||
| @@ -43,65 +43,62 @@ define( | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockLocation = jasmine.createSpyObj( | ||||
|                     "$location", | ||||
|                     [ "path" ] | ||||
|                 ); | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     [ "getCapability", "hasCapability" ] | ||||
|                     [ | ||||
|                         "getCapability", | ||||
|                         "hasCapability", | ||||
|                         "getModel", | ||||
|                         "getOriginalObject" | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockEditorCapability = jasmine.createSpyObj( | ||||
|                     "editor", | ||||
|                     [ "save", "cancel" ] | ||||
|                 ); | ||||
|                 mockUrlService = jasmine.createSpyObj( | ||||
|                     "urlService", | ||||
|                     ["urlForLocation"] | ||||
|                 mockActionCapability = jasmine.createSpyObj( | ||||
|                     "actionCapability", | ||||
|                     [ "perform"] | ||||
|                 ); | ||||
|  | ||||
|                 capabilities.editor = mockEditorCapability; | ||||
|                 capabilities.action = mockActionCapability; | ||||
|  | ||||
|                 actionContext = { | ||||
|                     domainObject: mockDomainObject | ||||
|                 }; | ||||
|  | ||||
|                 mockDomainObject.hasCapability.andReturn(true); | ||||
|                 mockDomainObject.getCapability.andReturn(mockEditorCapability); | ||||
|                 mockDomainObject.getCapability.andCallFake(function (capability) { | ||||
|                     return capabilities[capability]; | ||||
|                 }); | ||||
|                 mockDomainObject.getModel.andReturn({persisted: 0}); | ||||
|                 mockEditorCapability.save.andReturn(mockPromise(true)); | ||||
|                 mockDomainObject.getOriginalObject.andReturn(mockDomainObject); | ||||
|  | ||||
|                 action = new SaveAction(mockLocation, mockUrlService, actionContext); | ||||
|                 action = new SaveAction(actionContext); | ||||
|  | ||||
|             }); | ||||
|  | ||||
|             it("only applies to domain object with an editor capability", function () { | ||||
|                 expect(SaveAction.appliesTo(actionContext)).toBeTruthy(); | ||||
|                 expect(SaveAction.appliesTo(actionContext)).toBe(true); | ||||
|                 expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); | ||||
|  | ||||
|                 mockDomainObject.hasCapability.andReturn(false); | ||||
|                 mockDomainObject.getCapability.andReturn(undefined); | ||||
|                 expect(SaveAction.appliesTo(actionContext)).toBeFalsy(); | ||||
|                 expect(SaveAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             //TODO: Disabled for NEM Beta | ||||
|             xit("invokes the editor capability's save functionality when performed", function () { | ||||
|                 // Verify precondition | ||||
|                 expect(mockEditorCapability.save).not.toHaveBeenCalled(); | ||||
|                 action.perform(); | ||||
|  | ||||
|                 // Should have called cancel | ||||
|                 expect(mockEditorCapability.save).toHaveBeenCalled(); | ||||
|  | ||||
|                 // Also shouldn't call cancel | ||||
|                 expect(mockEditorCapability.cancel).not.toHaveBeenCalled(); | ||||
|             it("only applies to domain object that has already been persisted", | ||||
|                 function () { | ||||
|                     mockDomainObject.getModel.andReturn({persisted: undefined}); | ||||
|                     expect(SaveAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             //TODO: Disabled for NEM Beta | ||||
|             xit("returns to browse when performed", function () { | ||||
|                 action.perform(); | ||||
|                 expect(mockLocation.path).toHaveBeenCalledWith( | ||||
|                     mockUrlService.urlForLocation("browse", mockDomainObject) | ||||
|                 ); | ||||
|             }); | ||||
|             it("uses the editor capability to save the object", | ||||
|                 function () { | ||||
|                     action.perform(); | ||||
|                     expect(mockEditorCapability.save).toHaveBeenCalled(); | ||||
|                 }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										174
									
								
								platform/commonUI/edit/test/actions/SaveAsActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								platform/commonUI/edit/test/actions/SaveAsActionSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,describe,it,expect,beforeEach,jasmine,xit,xdescribe*/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/actions/SaveAsAction"], | ||||
|     function (SaveAsAction) { | ||||
|         "use strict"; | ||||
|  | ||||
|         describe("The Save As action", function () { | ||||
|             var mockDomainObject, | ||||
|                 mockEditorCapability, | ||||
|                 mockActionCapability, | ||||
|                 mockObjectService, | ||||
|                 mockDialogService, | ||||
|                 mockCopyService, | ||||
|                 mockParent, | ||||
|                 mockUrlService, | ||||
|                 actionContext, | ||||
|                 capabilities = {}, | ||||
|                 action; | ||||
|  | ||||
|             function noop () {} | ||||
|  | ||||
|             function mockPromise(value) { | ||||
|                 return (value || {}).then ? value : | ||||
|                     { | ||||
|                         then: function (callback) { | ||||
|                             return mockPromise(callback(value)); | ||||
|                         }, | ||||
|                         catch: function (callback) { | ||||
|                             return mockPromise(callback(value)); | ||||
|                         } | ||||
|                 }   ; | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     [ | ||||
|                         "getCapability", | ||||
|                         "hasCapability", | ||||
|                         "getModel" | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockDomainObject.hasCapability.andReturn(true); | ||||
|                 mockDomainObject.getCapability.andCallFake(function (capability) { | ||||
|                     return capabilities[capability]; | ||||
|                 }); | ||||
|                 mockDomainObject.getModel.andReturn({location: 'a', persisted: undefined}); | ||||
|  | ||||
|                 mockParent = jasmine.createSpyObj( | ||||
|                     "parentObject", | ||||
|                     [ | ||||
|                         "getCapability", | ||||
|                         "hasCapability", | ||||
|                         "getModel" | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 mockEditorCapability = jasmine.createSpyObj( | ||||
|                     "editor", | ||||
|                     [ "save", "cancel" ] | ||||
|                 ); | ||||
|                 mockEditorCapability.cancel.andReturn(mockPromise(undefined)); | ||||
|                 mockEditorCapability.save.andReturn(mockPromise(true)); | ||||
|                 capabilities.editor = mockEditorCapability; | ||||
|  | ||||
|                 mockActionCapability = jasmine.createSpyObj( | ||||
|                     "action", | ||||
|                     ["perform"] | ||||
|                 ); | ||||
|                 capabilities.action = mockActionCapability; | ||||
|  | ||||
|                 mockObjectService = jasmine.createSpyObj( | ||||
|                     "objectService", | ||||
|                     ["getObjects"] | ||||
|                 ); | ||||
|                 mockObjectService.getObjects.andReturn(mockPromise({'a': mockParent})); | ||||
|  | ||||
|                 mockDialogService = jasmine.createSpyObj( | ||||
|                     "dialogService", | ||||
|                     [ | ||||
|                         "getUserInput" | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockDialogService.getUserInput.andReturn(mockPromise(undefined)); | ||||
|  | ||||
|                 mockCopyService = jasmine.createSpyObj( | ||||
|                     "copyService", | ||||
|                     [ | ||||
|                         "perform" | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 mockUrlService = jasmine.createSpyObj( | ||||
|                     "urlService", | ||||
|                     ["urlForLocation"] | ||||
|                 ); | ||||
|  | ||||
|                 actionContext = { | ||||
|                     domainObject: mockDomainObject | ||||
|                 }; | ||||
|  | ||||
|                 action = new SaveAsAction(undefined, undefined, mockDialogService, undefined, mockCopyService, actionContext); | ||||
|  | ||||
|                 spyOn(action, "getObjectService"); | ||||
|                 action.getObjectService.andReturn(mockObjectService); | ||||
|  | ||||
|                 spyOn(action, "createWizard"); | ||||
|                 action.createWizard.andReturn({ | ||||
|                     getFormStructure: noop, | ||||
|                     getInitialFormValue: noop, | ||||
|                     populateObjectFromInput: function() { | ||||
|                         return mockDomainObject; | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|             }); | ||||
|  | ||||
|             it("only applies to domain object with an editor capability", function () { | ||||
|                 expect(SaveAsAction.appliesTo(actionContext)).toBe(true); | ||||
|                 expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); | ||||
|  | ||||
|                 mockDomainObject.hasCapability.andReturn(false); | ||||
|                 mockDomainObject.getCapability.andReturn(undefined); | ||||
|                 expect(SaveAsAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("only applies to domain object that has not already been" + | ||||
|                 " persisted", function () { | ||||
|                 expect(SaveAsAction.appliesTo(actionContext)).toBe(true); | ||||
|                 expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); | ||||
|  | ||||
|                 mockDomainObject.getModel.andReturn({persisted: 0}); | ||||
|                 expect(SaveAsAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("returns to browse after save", function () { | ||||
|                 spyOn(action, "save"); | ||||
|                 action.save.andReturn(mockPromise(mockDomainObject)); | ||||
|                 action.perform(); | ||||
|                 expect(mockActionCapability.perform).toHaveBeenCalledWith( | ||||
|                     "navigate" | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("prompts the user for object details", function () { | ||||
|                 action.perform(); | ||||
|                 expect(mockDialogService.getUserInput).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -35,19 +35,43 @@ define( | ||||
|                 mockDomainObject, | ||||
|                 mockEditAction, | ||||
|                 mockPropertiesAction, | ||||
|                 mockTypeCapability, | ||||
|                 mockStatusCapability, | ||||
|                 capabilities, | ||||
|                 plotView, | ||||
|                 policy; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     'domainObject', | ||||
|                     [ 'useCapability' ] | ||||
|                     [ | ||||
|                         'useCapability', | ||||
|                         'hasCapability', | ||||
|                         'getCapability' | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockStatusCapability = jasmine.createSpyObj('statusCapability', ['get']); | ||||
|                 mockStatusCapability.get.andReturn(false); | ||||
|                 mockTypeCapability = jasmine.createSpyObj('type', ['getKey']); | ||||
|                 capabilities = { | ||||
|                     'status': mockStatusCapability, | ||||
|                     'type': mockTypeCapability | ||||
|                 }; | ||||
|  | ||||
|                 mockEditAction = jasmine.createSpyObj('edit', ['getMetadata']); | ||||
|                 mockPropertiesAction = jasmine.createSpyObj('edit', ['getMetadata']); | ||||
|  | ||||
|                 mockDomainObject.getCapability.andCallFake(function(capability){ | ||||
|                     return capabilities[capability]; | ||||
|                 }); | ||||
|                 mockDomainObject.hasCapability.andCallFake(function(capability){ | ||||
|                     return !!capabilities[capability]; | ||||
|                 }); | ||||
|  | ||||
|                 editableView = { editable: true }; | ||||
|                 nonEditableView = { editable: false }; | ||||
|                 undefinedView = { someKey: "some value" }; | ||||
|                 plotView = { key: "plot", editable: false }; | ||||
|                 testViews = []; | ||||
|  | ||||
|                 mockDomainObject.useCapability.andCallFake(function (c) { | ||||
| @@ -66,38 +90,53 @@ define( | ||||
|                 policy = new EditActionPolicy(); | ||||
|             }); | ||||
|  | ||||
|             //TODO: Disabled for NEM Beta | ||||
|             xit("allows the edit action when there are editable views", function () { | ||||
|             it("allows the edit action when there are editable views", function () { | ||||
|                 testViews = [ editableView ]; | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBeTruthy(); | ||||
|                 // No edit flag defined; should be treated as editable | ||||
|                 testViews = [ undefinedView, undefinedView ]; | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBeTruthy(); | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             //TODO: Disabled for NEM Beta | ||||
|             xit("allows the edit properties action when there are no editable views", function () { | ||||
|             it("allows the edit properties action when there are no editable views", function () { | ||||
|                 testViews = [ nonEditableView, nonEditableView ]; | ||||
|                 expect(policy.allow(mockPropertiesAction, testContext)).toBeTruthy(); | ||||
|                 expect(policy.allow(mockPropertiesAction, testContext)).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             //TODO: Disabled for NEM Beta | ||||
|             xit("disallows the edit action when there are no editable views", function () { | ||||
|             it("disallows the edit action when there are no editable views", function () { | ||||
|                 testViews = [ nonEditableView, nonEditableView ]; | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBeFalsy(); | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             //TODO: Disabled for NEM Beta | ||||
|             xit("disallows the edit properties action when there are" + | ||||
|             it("disallows the edit properties action when there are" + | ||||
|                 " editable views", function () { | ||||
|                 testViews = [ editableView ]; | ||||
|                 expect(policy.allow(mockPropertiesAction, testContext)).toBeFalsy(); | ||||
|                 expect(policy.allow(mockPropertiesAction, testContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("disallows the edit action when object is already being" + | ||||
|                 " edited", function () { | ||||
|                 testViews = [ editableView ]; | ||||
|                 mockStatusCapability.get.andReturn(true); | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("allows editing of panels in plot view", function () { | ||||
|                 testViews = [ plotView ]; | ||||
|                 mockTypeCapability.getKey.andReturn('telemetry.panel'); | ||||
|  | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             it("disallows editing of plot view when object not a panel type", function () { | ||||
|                 testViews = [ plotView ]; | ||||
|                 mockTypeCapability.getKey.andReturn('something.else'); | ||||
|  | ||||
|                 expect(policy.allow(mockEditAction, testContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|  | ||||
|             it("allows the edit properties outside of the 'view-control' category", function () { | ||||
|                 testViews = [ nonEditableView ]; | ||||
|                 testContext.category = "something-else"; | ||||
|                 expect(policy.allow(mockPropertiesAction, testContext)).toBeTruthy(); | ||||
|                 expect(policy.allow(mockPropertiesAction, testContext)).toBe(true); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -0,0 +1,109 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,describe,it,expect,beforeEach,jasmine,xit,xdescribe*/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/policies/EditContextualActionPolicy"], | ||||
|     function (EditContextualActionPolicy) { | ||||
|         "use strict"; | ||||
|  | ||||
|         describe("The Edit contextual action policy", function () { | ||||
|             var policy, | ||||
|                 navigationService, | ||||
|                 mockAction, | ||||
|                 context, | ||||
|                 navigatedObject, | ||||
|                 mockDomainObject, | ||||
|                 metadata, | ||||
|                 editModeBlacklist = ["copy", "follow", "window", "link", "locate"], | ||||
|                 nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"]; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability"]); | ||||
|                 navigatedObject.hasCapability.andReturn(false); | ||||
|  | ||||
|                 mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]); | ||||
|                 mockDomainObject.hasCapability.andReturn(false); | ||||
|  | ||||
|                 navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]); | ||||
|                 navigationService.getNavigation.andReturn(navigatedObject); | ||||
|  | ||||
|                 metadata = {key: "move"}; | ||||
|                 mockAction = jasmine.createSpyObj("action", ["getMetadata"]); | ||||
|                 mockAction.getMetadata.andReturn(metadata); | ||||
|  | ||||
|                 context = {domainObject: mockDomainObject}; | ||||
|  | ||||
|                 policy = new EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist); | ||||
|             }); | ||||
|  | ||||
|             it('Allows all actions when navigated object not in edit mode', function() { | ||||
|                 expect(policy.allow(mockAction, context)).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             it('Allows "window" action when navigated object in edit mode,' + | ||||
|                 ' but selected object not in edit mode ', function() { | ||||
|                 navigatedObject.hasCapability.andReturn(true); | ||||
|                 metadata.key = "window"; | ||||
|                 expect(policy.allow(mockAction, context)).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             it('Allows "remove" action when navigated object in edit mode,' + | ||||
|                 ' and selected object not editable, but its parent is.', | ||||
|                 function() { | ||||
|                     var mockParent = jasmine.createSpyObj("parentObject", ["hasCapability"]), | ||||
|                         mockContextCapability = jasmine.createSpyObj("contextCapability", ["getParent"]); | ||||
|  | ||||
|                     mockParent.hasCapability.andReturn(true); | ||||
|                     mockContextCapability.getParent.andReturn(mockParent); | ||||
|                     navigatedObject.hasCapability.andReturn(true); | ||||
|  | ||||
|                     mockDomainObject.getCapability.andReturn(mockContextCapability); | ||||
|                     mockDomainObject.hasCapability.andCallFake(function (capability) { | ||||
|                         switch (capability) { | ||||
|                             case "editor": return false; | ||||
|                             case "context": return true; | ||||
|                         } | ||||
|                     }); | ||||
|                     metadata.key = "remove"; | ||||
|  | ||||
|                     expect(policy.allow(mockAction, context)).toBe(true); | ||||
|             }); | ||||
|  | ||||
|             it('Disallows "move" action when navigated object in edit mode,' + | ||||
|                 ' but selected object not in edit mode ', function() { | ||||
|                 navigatedObject.hasCapability.andReturn(true); | ||||
|                 metadata.key = "move"; | ||||
|                 expect(policy.allow(mockAction, context)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it('Disallows copy action when navigated object and' + | ||||
|                 ' selected object in edit mode', function() { | ||||
|                 navigatedObject.hasCapability.andReturn(true); | ||||
|                 mockDomainObject.hasCapability.andReturn(true); | ||||
|                 metadata.key = "copy"; | ||||
|                 expect(policy.allow(mockAction, context)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -28,9 +28,10 @@ define([ | ||||
| ) { | ||||
|     "use strict"; | ||||
|  | ||||
|     var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", | ||||
|     var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS", | ||||
|         DATE_FORMATS = [ | ||||
|             DATE_FORMAT, | ||||
|             "YYYY-MM-DD HH:mm:ss", | ||||
|             "YYYY-MM-DD HH:mm", | ||||
|             "YYYY-MM-DD" | ||||
|         ]; | ||||
| @@ -48,7 +49,7 @@ define([ | ||||
|     } | ||||
|  | ||||
|     UTCTimeFormat.prototype.format = function (value) { | ||||
|         return moment.utc(value).format(DATE_FORMAT); | ||||
|         return moment.utc(value).format(DATE_FORMAT) + "Z"; | ||||
|     }; | ||||
|  | ||||
|     UTCTimeFormat.prototype.parse = function (text) { | ||||
|   | ||||
| @@ -40,6 +40,12 @@ define( | ||||
|                 expect(moment.utc(formatted).valueOf()).toEqual(timestamp); | ||||
|             }); | ||||
|  | ||||
|             it("displays with millisecond precision", function () { | ||||
|                 var timestamp = 12345670789, | ||||
|                     formatted = format.format(timestamp); | ||||
|                 expect(moment.utc(formatted).valueOf()).toEqual(timestamp); | ||||
|             }); | ||||
|  | ||||
|             it("validates time inputs", function () { | ||||
|                 expect(format.validate("1977-05-25 11:21:22")).toBe(true); | ||||
|                 expect(format.validate("garbage text")).toBe(false); | ||||
|   | ||||
| @@ -49,6 +49,7 @@ define([ | ||||
|     "./src/directives/MCTScroll", | ||||
|     "./src/directives/MCTSplitPane", | ||||
|     "./src/directives/MCTSplitter", | ||||
|     "./src/directives/MCTTree", | ||||
|     "text!./res/templates/bottombar.html", | ||||
|     "text!./res/templates/controls/action-button.html", | ||||
|     "text!./res/templates/controls/input-filter.html", | ||||
| @@ -97,6 +98,7 @@ define([ | ||||
|     MCTScroll, | ||||
|     MCTSplitPane, | ||||
|     MCTSplitter, | ||||
|     MCTTree, | ||||
|     bottombarTemplate, | ||||
|     actionButtonTemplate, | ||||
|     inputFilterTemplate, | ||||
| @@ -175,10 +177,6 @@ define([ | ||||
|                 { | ||||
|                     "stylesheetUrl": "css/normalize.min.css", | ||||
|                     "priority": "mandatory" | ||||
|                 }, | ||||
|                 { | ||||
|                     "stylesheetUrl": "css/reset.css", | ||||
|                     "priority": "mandatory" | ||||
|                 } | ||||
|             ], | ||||
|             "templates": [ | ||||
| @@ -389,6 +387,11 @@ define([ | ||||
|                 { | ||||
|                     "key": "mctSplitter", | ||||
|                     "implementation": MCTSplitter | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "mctTree", | ||||
|                     "implementation": MCTTree, | ||||
|                     "depends": [ '$parse', 'gestureService' ] | ||||
|                 } | ||||
|             ], | ||||
|             "constants": [ | ||||
| @@ -516,16 +519,6 @@ define([ | ||||
|                 } | ||||
|             ], | ||||
|             "licenses": [ | ||||
|                 { | ||||
|                     "name": "Modernizr", | ||||
|                     "version": "2.6.2", | ||||
|                     "description": "Browser/device capability finding", | ||||
|                     "author": "Faruk Ateş", | ||||
|                     "website": "http://modernizr.com", | ||||
|                     "copyright": "Copyright (c) 2009–2015", | ||||
|                     "license": "license-mit", | ||||
|                     "link": "http://modernizr.com/license/" | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "Normalize.css", | ||||
|                     "version": "1.1.2", | ||||
| @@ -535,6 +528,16 @@ define([ | ||||
|                     "copyright": "Copyright (c) Nicolas Gallagher and Jonathan Neal", | ||||
|                     "license": "license-mit", | ||||
|                     "link": "https://github.com/necolas/normalize.css/blob/v1.1.2/LICENSE.md" | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "Zepto", | ||||
|                     "version": "1.1.6", | ||||
|                     "description": "DOM manipulation", | ||||
|                     "author": "Thomas Fuchs", | ||||
|                     "website": "http://zeptojs.com/", | ||||
|                     "copyright": "Copyright (c) 2010-2016 Thomas Fuchs", | ||||
|                     "license": "license-mit", | ||||
|                     "link": "https://github.com/madrobby/zepto/blob/master/MIT-LICENSE" | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -80,7 +80,7 @@ | ||||
| <glyph unicode="" glyph-name="icon-datatable" d="M1024 768c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 448c-282.8 0-512 86-512 192v-512c0-106 229.2-192 512-192s512 86 512 192v512c0-106-229.2-192-512-192zM896 385v-256c-36.6-15.6-79.8-28.8-128-39.4v256c48.2 10.6 91.4 23.8 128 39.4zM256 345.6v-256c-48.2 10.4-91.4 23.8-128 39.4v256c36.6-15.6 79.8-28.8 128-39.4zM384 70v256c41-4 83.8-6 128-6s87 2.2 128 6v-256c-41-4-83.8-6-128-6s-87 2.2-128 6z" /> | ||||
| <glyph unicode="" glyph-name="icon-tabular-scrolling" d="M64 960c-35.2 0-64-28.8-64-64v-192h448v256h-384zM1024 704v192c0 35.2-28.8 64-64 64h-384v-256h448zM0 576v-192c0-35.2 28.8-64 64-64h384v256h-448zM960 320c35.2 0 64 28.8 64 64v192h-448v-256h384zM512-64l-256 256h512z" /> | ||||
| <glyph unicode="" glyph-name="icon-alert-triangle" d="M998.208 111.136l-422.702 739.728c-34.928 61.124-92.084 61.124-127.012 0l-422.702-739.728c-34.928-61.126-5.906-111.136 64.494-111.136h843.428c70.4 0 99.422 50.010 64.494 111.136zM512 128c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64c0-35.2-28.8-64-64-64zM627.448 577.242l-38.898-194.486c-6.902-34.516-41.35-62.756-76.55-62.756s-69.648 28.24-76.552 62.758l-38.898 194.486c-6.902 34.516 16.25 62.756 51.45 62.756h128c35.2 0 58.352-28.24 51.448-62.758z" /> | ||||
| <glyph unicode="" glyph-name="icon-tabular" d="M0 896v-192h448v256h-384c-35.2 0-64-28.8-64-64zM960 960h-384v-256h448v192c0 35.2-28.8 64-64 64zM576 576h448v-256h-448v256zM0 576h448v-256h-448v256zM0 0c0-35.2 28.8-64 64-64h384v256h-448v-192zM576-64h384c35.2 0 64 28.8 64 64v192h-448v-256z" /> | ||||
| <glyph unicode="" glyph-name="icon-tabular" 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 128zM640 512h-256v192h256v-192zM384 448h256v-192h-256v192zM320 256h-256v192h256v-192zM320 704v-192h-256v192h256zM128 0c-17 0-33 6.6-45.2 18.8s-18.8 28.2-18.8 45.2v128h256v-192h-192zM384 0v192h256v-192h-256zM960 64c0-17-6.6-33-18.8-45.2s-28.2-18.8-45.2-18.8h-192v192h256v-128zM960 256h-256v192h256v-192zM960 512h-256v192h256v-192z" /> | ||||
| <glyph unicode="" glyph-name="icon-calendar" 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 128zM640 512h-256v192h256v-192zM384 448h256v-192h-256v192zM320 256h-256v192h256v-192zM320 704v-192h-256v192h256zM128 0c-17 0-33 6.6-45.2 18.8s-18.8 28.2-18.8 45.2v128h256v-192h-192zM384 0v192h256v-192h-256zM960 64c0-17-6.6-33-18.8-45.2s-28.2-18.8-45.2-18.8h-192v192h256v-128zM960 256h-256v192h256v-192zM960 512h-256v192h256v-192z" /> | ||||
| <glyph unicode="" glyph-name="icon-paint-bucket" d="M544 736v-224c0-88.4-71.6-160-160-160s-160 71.6-160 160v97.2l-197.4-196.4c-50-50-12.4-215.2 112.4-340s290-162.4 340-112.4l417 423.6-352 352zM896-64c70.6 0 128 57.4 128 128 0 108.6-128 192-128 192s-128-83.4-128-192c0-70.6 57.4-128 128-128zM384 448c-35.4 0-64 28.6-64 64v384c0 35.4 28.6 64 64 64s64-28.6 64-64v-384c0-35.4-28.6-64-64-64z" /> | ||||
| <glyph unicode="" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" /> | ||||
| @@ -94,8 +94,11 @@ | ||||
| <glyph unicode="" glyph-name="icon-collapse-pane-right" d="M768 960h256v-1024h-256c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192zM512 640l-512-320v640z" /> | ||||
| <glyph unicode="" glyph-name="icon-eye-open" d="M512 896c-261 0-480.6-195.4-512-448 31.4-252.6 251-448 512-448s480.6 195.4 512 448c-31.4 252.6-251 448-512 448zM768.2 225.4c-71.4-62.8-162.8-97.4-257.6-97.4s-186.2 34.6-257.6 97.4c-66.6 58.6-110.6 137.2-125 222.6 0 0 0 0.2 0 0.2 76.8 154 220.8 257.6 384 257.6s307.2-103.8 384-257.6c0 0 0-0.2 0-0.2-14.4-85.4-61.2-164-127.8-222.6zM512 672c-123.8 0-224-100.2-224-224s100.2-224 224-224 224 100.2 224 224-100.2 224-224 224z" /> | ||||
| <glyph unicode="" glyph-name="icon-eye-open-no-gleam" d="M512 896c-261 0-480.6-195.4-512-448 31.4-252.6 251-448 512-448s480.6 195.4 512 448c-31.4 252.6-251 448-512 448zM768.2 225.4c-71.4-62.8-162.8-97.4-257.6-97.4s-186.2 34.6-257.6 97.4c-66.6 58.6-110.6 137.2-125 222.6 0 0 0 0.2 0 0.2 76.8 154 220.8 257.6 384 257.6s307.2-103.8 384-257.6c0 0 0-0.2 0-0.2-14.4-85.4-61.2-164-127.8-222.6zM512 672c-123.8 0-224-100.2-224-224s100.2-224 224-224 224 100.2 224 224-100.2 224-224 224zM576 416c-53 0-96 43-96 96s43 96 96 96 96-43 96-96c0-53-43-96-96-96z" /> | ||||
| <glyph unicode="" glyph-name="icon-topic" d="M546.4 528.8l32 24c31.6 23.8 91.6 23.8 123.2 0l32-24c10.8-8 22.2-15.2 34.4-21.4v201.2c-38 19.6-82.2 30-128 30-60.4 0-118.2-18.2-162.4-51.4l-32-24c-31.6-23.8-91.6-23.8-123.2 0l-32 24c-10.8 8-22.2 15.2-34.4 21.4v-201.2c38-19.6 82.2-30 128-30 60.4 0 118.2 18.2 162.4 51.4zM640 418.6c-60.4 0-118.2-18.2-162.4-51.4l-32-24c-31.6-23.8-91.6-23.8-123.2 0l-32 24c-10.8 8-22.2 15.2-34.4 21.4v-201.2c38-19.6 82.2-30 128-30 60.4 0 118.2 18.2 162.4 51.4l32 24c31.6 23.8 91.6 23.8 123.2 0l32-24c10.8-8 22.2-15.2 34.4-21.4v201.2c-38 19.6-82.2 30-128 30zM832 960h-128v-192h127.6c0.2 0 0.2-0.2 0.4-0.4v-639.4c0-0.2-0.2-0.2-0.4-0.4h-127.6v-192h128c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192zM320 128h-127.6c-0.2 0-0.2 0.2-0.4 0.4v639.4c0 0.2 0.2 0.2 0.4 0.4h127.6v191.8h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" /> | ||||
| <glyph unicode="" glyph-name="icon-session" d="M923 521.8l-151 100.6c-36 24-103.8 24-139.8 0l-151-100.6c-44.6-29.8-102.6-46.2-163-46.2s-118.4 16.4-163 46.2l-151.4 100.6c-1.8 1.2-3.8 2.4-5.8 3.6v-208c36.6-7.4 70.6-20.8 99-39.8l151-100.6c36-24 103.8-24 139.8 0l151 100.6c44.6 29.8 102.6 46.2 163 46.2s118.4-16.4 163-46.2l151-100.6c1.8-1.2 3.8-2.4 5.8-3.6v208c-36.2 7.2-70.2 20.8-98.6 39.8zM923 137.8l-151 100.6c-36 24-103.8 24-139.8 0l-151-100.6c-44.6-29.8-102.6-46.2-163-46.2s-118.4 16.4-163 46.2l-151.4 100.6c-1.8 1.2-3.8 2.4-5.8 3.6v-112c0-105.6 86.4-192 192-192h640c94.8 0 174.2 69.8 189.4 160.4-35.6 7.4-68.6 20.8-96.4 39.4zM97 762.2l151-100.6c36-24 103.8-24 139.8 0l151 100.6c44.8 29.8 102.6 46.2 163.2 46.2s118.4-16.4 163-46.2l151-100.6c1.8-1.2 3.8-2.4 5.8-3.6v112c0 105.6-86.4 192-192 192h-639.8c-94.8 0-174.2-69.8-189.4-160.4 35.6-7.4 68.6-20.8 96.4-39.4z" /> | ||||
| <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-session" d="M635.6 435.6c6.6-4.2 13.2-8.6 19.2-13.6l120.4-96.4c29.6-23.8 83.8-23.8 113.4 0l135.2 108c0.2 4.8 0.2 9.4 0.2 14.2 0 52.2-7.8 102.4-22.2 149.8l-154.8-123.6c-58.2-46.6-140.2-59.2-211.4-38.4zM248.6 325.8l120.4 96.4c58 46.4 140 59.2 211.2 38.4-6.6 4.2-13.2 8.6-19.2 13.6l-120.4 96.4c-29.6 23.8-83.8 23.8-113.4 0l-120.2-96.6c-40-32-91.4-48-143-48-21.6 0-43 2.8-63.8 8.4 0-0.6 0-1.2 0-1.6 5-3.4 10-6.8 14.6-10.6l120.4-96.4c29.8-23.8 83.8-23.8 113.4 0zM120.6 581.8l120.4 96.4c80.2 64.2 205.6 64.2 285.8 0l120.4-96.4c29.6-23.8 83.8-23.8 113.4 0l181 144.8c-91.2 140.4-249.6 233.4-429.6 233.4-238.6 0-439.2-163.2-496-384.2 30.8-17.6 77.8-15.6 104.6 6zM689 218l-120.4 96.4c-29.6 23.8-83.8 23.8-113.4 0l-120.2-96.4c-40-32-91.4-48-143-48-47.8 0-95.4 13.8-134.2 41.4 85.6-163.6 256.8-275.4 454.2-275.4s368.6 111.8 454.2 275.4c-80.4-57.4-199.8-55.2-277.2 6.6z" /> | ||||
| <glyph unicode="" glyph-name="icon-bullet" d="M832 208c0-44-36-80-80-80h-480c-44 0-80 36-80 80v480c0 44 36 80 80 80h480c44 0 80-36 80-80v-480z" /> | ||||
| <glyph unicode="" glyph-name="icon-tabular-realtime" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM448 668l25.060-25.32c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.97 30.338 71.571 49.128 117.56 49.128s87.59-18.79 117.544-49.112l50.456-50.997v-152.2c-24.111 8.83-44.678 22.255-61.542 39.342l-75.518 76.318c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.971-30.343-71.575-49.137-117.568-49.137-20.084 0-39.331 3.584-57.137 10.146l1.145 151.831zM320 0h-192c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192zM320 256h-256v192h256v-192zM320 512h-256v192h256v-192zM640 0h-256v192h256v-192zM448 323.38v174.5c1.88-1.74 3.74-3.5 5.56-5.34l75.5-76.3c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.966 30.333 71.56 49.119 117.542 49.119 43.28 0 82.673-16.644 112.128-43.879l-0.11-174.399c-1.88 1.74-3.74 3.5-5.56 5.34l-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.966-30.333-71.56-49.119-117.542-49.119-43.28 0-82.673 16.644-112.128 43.879zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128z" /> | ||||
| <glyph unicode="" glyph-name="icon-tabular-lad" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM64 704h256v-192h-256v192zM64 448h256v-192h-256v192zM128 0c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192h-192zM384 0v192h256v-192h-256zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128zM960 448v-192h-576v192h64v64h-64v192h576v-192h-64v-64h64zM782.32 412.62l-110.32 55.16v172.22c0 17.673-14.327 32-32 32s-32-14.327-32-32v-211.78l145.68-72.84c4.172-2.133 9.1-3.383 14.32-3.383 17.675 0 32.003 14.328 32.003 32.003 0 12.454-7.114 23.247-17.501 28.536z" /> | ||||
| <glyph unicode="" glyph-name="icon-tabular-lad-set" d="M128 192v576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979l-576 0.021c-70.606 0.215-127.785 57.394-128 127.979zM896 960h-576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v576.021c-0.215 70.606-57.394 127.785-127.979 128zM256 768h192v-128h-192v128zM256 576h192v-192h-192v192zM320 192c-35.26 0.214-63.786 28.74-64 63.98v64.020h192v-128h-128zM512 192v128h192v-128h-192zM960 256c-0.214-35.26-28.74-63.786-63.98-64h-128.020v128h192v-64zM960 384h-448v384h448v-384zM832 480c0.002 0 0.005 0 0.007 0 17.673 0 32 14.327 32 32 0 14.055-9.062 25.994-21.662 30.293l-74.345 24.767v104.94c0 17.673-14.327 32-32 32s-32-14.327-32-32v-151.060l117.88-39.3c3.018-1.040 6.495-1.64 10.113-1.64 0.003 0 0.005 0 0.008 0z" /> | ||||
| <glyph unicode="" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" /> | ||||
| </font></defs></svg> | ||||
| Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 35 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -98,6 +98,11 @@ $bubbleMaxW: 300px; | ||||
| $reqSymbolW: 15px; | ||||
| $reqSymbolM: $interiorMargin * 2; | ||||
| $reqSymbolFontSize: 0.7em; | ||||
| // Wait Spinner Defaults | ||||
| $waitSpinnerD: 32px; | ||||
| $waitSpinnerTreeD: 20px; | ||||
| $waitSpinnerBorderW: 5px; | ||||
| $waitSpinnerTreeBorderW: 4px; | ||||
|  | ||||
| /************************** CONTROLS */ | ||||
| $controlDisabledOpacity: 0.3; | ||||
|   | ||||
| @@ -53,7 +53,6 @@ body, html { | ||||
| 	font-weight: 200; | ||||
| 	height: 100%; | ||||
| 	width: 100%; | ||||
| 	overflow: hidden; | ||||
| } | ||||
|  | ||||
| em { | ||||
| @@ -85,6 +84,12 @@ p { | ||||
| 	margin-bottom: $interiorMarginLg; | ||||
| } | ||||
|  | ||||
| ol, ul { | ||||
|     list-style: none; | ||||
|     margin: 0; | ||||
|     padding-left: 0; | ||||
| } | ||||
|  | ||||
| mct-container { | ||||
| 	display: block; | ||||
| } | ||||
|   | ||||
| @@ -61,9 +61,24 @@ | ||||
|     .l-inspector-part { | ||||
|         box-sizing: border-box; | ||||
|         padding-right: $interiorMargin; | ||||
|         .form { | ||||
|         .tree .form { | ||||
|             margin-left: $treeVCW + $interiorMarginLg; | ||||
|             margin-bottom: $interiorMarginLg; | ||||
|         } | ||||
|         .section-header { | ||||
|             background: none; | ||||
|             color: $colorInspectorPropName; | ||||
|             border-radius: unset; | ||||
|             font-size: inherit; | ||||
|             padding: $interiorMarginSm 0; | ||||
|         } | ||||
|  | ||||
|         mct-form:not(:last-child) .form { | ||||
|             border-bottom: 1px solid $colorInspectorSectionHeaderBg; | ||||
|         } | ||||
|  | ||||
|         .form { | ||||
|             margin-bottom: $interiorMarginSm; | ||||
|             padding-bottom: $interiorMarginLg; | ||||
|             .form-section { | ||||
|                 margin-bottom: 0; | ||||
|                 &:not(.first) { | ||||
| @@ -72,7 +87,14 @@ | ||||
|                 .form-row { | ||||
|                     @include align-items(center); | ||||
|                     border: none; | ||||
|                     padding: 0; | ||||
|                     padding: $interiorMarginSm 0; | ||||
|                     .label { | ||||
|                         min-width: 80px; | ||||
|                     } | ||||
|                     input[type='text'], | ||||
|                     input[type='search'] { | ||||
|                         width: 100%; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -45,7 +45,6 @@ | ||||
|  | ||||
| /********************************* FORMS */ | ||||
| @import "forms/elems"; | ||||
| @import "forms/selects"; | ||||
| @import "forms/channel-selector"; | ||||
| @import "forms/datetime"; | ||||
| @import "forms/validation"; | ||||
|   | ||||
| @@ -35,15 +35,15 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| mct-representation { | ||||
| mct-representation, | ||||
| .rep-object-label { | ||||
|     &.s-status-pending { | ||||
|         .t-object-label { | ||||
|             .t-item-icon { | ||||
|                 &:before { | ||||
|                     $spinBW: 4px; | ||||
|                     @include spinner($spinBW); | ||||
|                     @include spinner($waitSpinnerTreeBorderW,  $colorLoadingFg); | ||||
|                     content: ""; | ||||
|                     padding: 30%; | ||||
|                     height: $waitSpinnerTreeD; width: $waitSpinnerTreeD; | ||||
|                 } | ||||
|                 .t-item-icon-glyph { | ||||
|                     display: none; | ||||
| @@ -57,7 +57,10 @@ mct-representation { | ||||
|     } | ||||
| } | ||||
|  | ||||
| .selected mct-representation.s-status-pending .t-object-label .t-item-icon:before { | ||||
|     border-color: rgba($colorItemTreeSelectedFg, 0.25) !important; | ||||
|     border-top-color: rgba($colorItemTreeSelectedFg, 1.0) !important; | ||||
| .selected mct-representation, | ||||
| .selected .rep-object-label { | ||||
|     .s-status-pending .t-object-label .t-item-icon:before { | ||||
|         border-color: rgba($colorItemTreeSelectedFg, 0.25) !important; | ||||
|         border-top-color: rgba($colorItemTreeSelectedFg, 1.0) !important; | ||||
|     } | ||||
| } | ||||
| @@ -76,6 +76,11 @@ $pad: $interiorMargin * $baseRatio; | ||||
|         font-family: symbolsfont; | ||||
|         margin-right: $interiorMarginSm; | ||||
|     } | ||||
|     &.t-save-as:before { | ||||
|         content:'\e612'; | ||||
|         font-family: symbolsfont; | ||||
|         margin-right: $interiorMarginSm; | ||||
|     } | ||||
|     &.t-cancel { | ||||
|         .title-label { display: none; } | ||||
|         &:before { | ||||
|   | ||||
| @@ -74,6 +74,12 @@ | ||||
|  | ||||
| .l-composite-control { | ||||
|     vertical-align: middle; | ||||
|     &:not(.l-inline) { | ||||
|         margin-bottom: $interiorMargin; | ||||
|     } | ||||
|     &.l-inline { | ||||
|         display: inline-block; | ||||
|     } | ||||
|     &.l-checkbox { | ||||
|         .composite-control-label { | ||||
|             line-height: 18px; | ||||
| @@ -108,12 +114,14 @@ | ||||
|     font-size: 0.7rem; | ||||
| } | ||||
|  | ||||
| /******************************************************** CUSTOM CHECKBOXES */ | ||||
| label.checkbox.custom, | ||||
| label.radio.custom { | ||||
|     $bg: pullForward($colorBodyBg, 10%); | ||||
|     $d: $formRowCtrlsH; | ||||
|     cursor: pointer; | ||||
|     display: inline-block; | ||||
|     line-height: 120%; | ||||
|     margin-right: $interiorMargin * 4; | ||||
|     padding-left: $d + $interiorMargin; | ||||
|     position: relative; | ||||
| @@ -161,7 +169,40 @@ label.radio.custom { | ||||
| label.checkbox.custom input:checked ~ em:before { content: "\32"; } | ||||
| label.radio.custom input:checked ~ em:before { content: "\e619"; } | ||||
|  | ||||
| .s-menu-btn label.checkbox.custom { | ||||
|     margin-left: 5px; | ||||
| } | ||||
|  | ||||
| .item .checkbox { | ||||
|     &.checked label { | ||||
|         box-shadow: none; | ||||
|         border-bottom: none; | ||||
|     } | ||||
| } | ||||
|  | ||||
| label.form-control.checkbox { | ||||
|     input { | ||||
|         margin-right: $interiorMargin; | ||||
|         vertical-align: top; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** INPUTS */ | ||||
| input[type="text"], | ||||
| input[type="search"] { | ||||
|     @include nice-input(); | ||||
|     &.numeric { | ||||
|         text-align: right; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .l-input-lg input[type="text"] {  width: 100% !important; } | ||||
| .l-input-med input[type="text"] { width: 200px !important; } | ||||
| .l-input-sm input[type="text"] {  width: 50px !important; } | ||||
| .l-numeric input[type="text"] { text-align: right; } | ||||
|  | ||||
| .input-labeled { | ||||
|     // Used in toolbar | ||||
|     margin-left: $interiorMargin; | ||||
|     label { | ||||
|         display: inline-block; | ||||
| @@ -175,28 +216,36 @@ label.radio.custom input:checked ~ em:before { content: "\e619"; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .s-menu-btn label.checkbox.custom { | ||||
|     margin-left: 5px; | ||||
| } | ||||
|  | ||||
| .item .checkbox { | ||||
|     &.checked label { | ||||
|         box-shadow: none; | ||||
|         border-bottom: none; | ||||
| /******************************************************** SELECTS */ | ||||
| .select { | ||||
|     @include btnSubtle($colorSelectBg); | ||||
|     @if $shdwBtns != none { | ||||
|         margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers | ||||
|     } | ||||
| } | ||||
|  | ||||
| .context-available, | ||||
| .s-icon-btn { | ||||
|     $c: $colorKey; | ||||
|     color: $c; | ||||
|     &:hover { | ||||
|         color: lighten($c, 10%); | ||||
|     padding: 0 $interiorMargin; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
|     line-height: $formInputH; | ||||
|     select { | ||||
|         @include appearance(none); | ||||
|         box-sizing: border-box; | ||||
|         background: none; | ||||
|         color: $colorSelectFg; | ||||
|         cursor: pointer; | ||||
|         border: none !important; | ||||
|         padding: 4px 25px 2px 0px; | ||||
|         width: 130%; | ||||
|         option { | ||||
|             margin: $interiorMargin 0; // Firefox | ||||
|         } | ||||
|     } | ||||
|     &:after { | ||||
|         @include contextArrow(); | ||||
|         pointer-events: none; | ||||
|         color: rgba($colorSelectFg, percentToDecimal($contrastInvokeMenuPercent)); | ||||
|         position: absolute; | ||||
|         right: $interiorMargin; top: 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .view-switcher { | ||||
|     @include trans-prop-nice-fade($controlFadeMs); | ||||
| } | ||||
|  | ||||
| /******************************************************** OBJECT-HEADER */ | ||||
| @@ -330,7 +379,6 @@ body.desktop .object-header { | ||||
| } | ||||
|  | ||||
| /******************************************************** SLIDERS */ | ||||
|  | ||||
| .slider { | ||||
|     $knobH: 100%; | ||||
|     .slot { | ||||
| @@ -424,7 +472,7 @@ body.desktop .object-header { | ||||
|         border-top: 1px solid $colorInteriorBorder | ||||
|     } | ||||
|     .l-time-selects { | ||||
|         line-height: $formInputH; | ||||
|         line-height: inherit; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -478,11 +526,31 @@ body.desktop .object-header { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** BROWSER ELEMENTS */ | ||||
| /******************************************************** TEXTAREA */ | ||||
| textarea { | ||||
|     @include nice-textarea($colorInputBg, $colorInputFg); | ||||
|     position: absolute; | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| /******************************************************** MISC */ | ||||
| .context-available, | ||||
| .s-icon-btn { | ||||
|     $c: $colorKey; | ||||
|     color: $c; | ||||
|     &:hover { | ||||
|         color: lighten($c, 10%); | ||||
|     } | ||||
| } | ||||
|  | ||||
| .view-switcher { | ||||
|     @include trans-prop-nice-fade($controlFadeMs); | ||||
| } | ||||
|  | ||||
| /******************************************************** BROWSER ELEMENTS */ | ||||
| body.desktop { | ||||
|     ::-webkit-scrollbar { | ||||
|         border-radius: 2px; | ||||
|         box-sizing: border-box; | ||||
|         box-shadow: inset $scrollbarTrackShdw; | ||||
|         background-color: $scrollbarTrackColorBg; | ||||
| @@ -491,15 +559,15 @@ body.desktop { | ||||
|     } | ||||
|  | ||||
|     ::-webkit-scrollbar-thumb { | ||||
|         $bg: $scrollbarThumbColor; | ||||
|         $hc: $scrollbarThumbColorHov; | ||||
|         $gr: 5%; | ||||
|         @include background-image(linear-gradient(lighten($bg, $gr), $bg 20px)); | ||||
|         border-radius: 2px; | ||||
|         box-sizing: border-box; | ||||
|         &:hover { | ||||
|             @include background-image(linear-gradient(lighten($hc, $gr), $hc 20px)); | ||||
|         } | ||||
|         background: $scrollbarThumbColor; | ||||
|         &:hover { background: $scrollbarThumbColorHov; } | ||||
|     } | ||||
|  | ||||
|     .overlay ::-webkit-scrollbar-thumb { | ||||
|         $lr: 15%; | ||||
|         background: $scrollbarThumbColorOverlay; | ||||
|         &:hover { background: $scrollbarThumbColorOverlayHov; } | ||||
|     } | ||||
|  | ||||
|     ::-webkit-scrollbar-corner { | ||||
|   | ||||
| @@ -51,9 +51,6 @@ | ||||
| 		.title-label { | ||||
| 			font-size: 1rem; | ||||
| 		} | ||||
| 		//&:after { | ||||
| 		//	color: rgba($colorInvokeMenu, 0.5); | ||||
| 		//} | ||||
| 	} | ||||
|  | ||||
| 	.menu { | ||||
| @@ -113,12 +110,13 @@ | ||||
|  | ||||
| .menu, | ||||
| .context-menu, | ||||
| .super-menu { | ||||
| .super-menu, | ||||
| .s-menu-btn .menu { | ||||
| 	pointer-events: auto; | ||||
| 	ul li { | ||||
| 		//padding-left: 25px; | ||||
| 		a { | ||||
| 			color: $colorMenuFg; | ||||
|             display: block; | ||||
| 		} | ||||
| 		.icon { | ||||
| 			color: $colorMenuIc; | ||||
| @@ -126,9 +124,6 @@ | ||||
| 		.type-icon { | ||||
| 			left: $interiorMargin; | ||||
| 		} | ||||
| 		&:hover .icon { | ||||
| 			//color: lighten($colorMenuIc, 5%); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -146,7 +141,7 @@ | ||||
| 				height: $d; | ||||
| 				width: $d; | ||||
| 				&:before { | ||||
| 					font-size: 7px !important;// $d/2; | ||||
| 					font-size: 7px !important; | ||||
| 					height: $d; | ||||
| 					width: $d; | ||||
| 					line-height: $d; | ||||
| @@ -173,7 +168,6 @@ | ||||
| 	.pane { | ||||
| 		box-sizing: border-box; | ||||
| 		&.left { | ||||
| 			//@include test(); | ||||
| 			border-right: 1px solid pullForward($colorMenuBg, 10%); | ||||
| 			left: 0; | ||||
| 			padding-right: $interiorMargin; | ||||
| @@ -190,7 +184,6 @@ | ||||
| 			} | ||||
| 		} | ||||
| 		&.right { | ||||
| 			//@include test(red); | ||||
| 			left: auto; | ||||
| 			right: 0; | ||||
| 			padding: $interiorMargin * 5; | ||||
| @@ -216,7 +209,6 @@ | ||||
| 				margin-bottom: 0.5em; | ||||
| 			} | ||||
| 			&.description { | ||||
| 				//color: lighten($colorMenuBg, 30%); | ||||
| 				color: $colorCreateMenuText; | ||||
| 				font-size: 0.8em; | ||||
| 				line-height: 1.5em; | ||||
| @@ -258,4 +250,4 @@ | ||||
| 	left: auto; | ||||
| 	right: 0; | ||||
| 	width: auto; | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,54 +1,42 @@ | ||||
| @mixin toiLineHovEffects() { | ||||
| 	//@include pulse(.25s); | ||||
| 	&:before, | ||||
| 	&:after { | ||||
| 		background-color: $timeControllerToiLineColorHov; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| mct-include.l-time-controller { | ||||
| .l-time-controller { | ||||
| 	$minW: 500px; | ||||
| 	$knobHOffset: 0px; | ||||
| 	$knobM: ($sliderKnobW + $knobHOffset) * -1; | ||||
| 	$rangeValPad: $interiorMargin; | ||||
| 	$rangeValOffset: $sliderKnobW; | ||||
| 	//$knobCr: $sliderKnobW; | ||||
| 	$timeRangeSliderLROffset: 130px + $sliderKnobW + $rangeValOffset; | ||||
| 	$r1H: nth($ueTimeControlH,1); | ||||
| 	$r2H: nth($ueTimeControlH,2); | ||||
| 	$r3H: nth($ueTimeControlH,3); | ||||
|  | ||||
|     //@include absPosDefault(); | ||||
|     //@include test(); | ||||
|     display: block; | ||||
|     //top: auto; | ||||
| 	height: $r1H + $r2H + $r3H + ($interiorMargin * 2); | ||||
|     min-width: $minW; | ||||
|     font-size: 0.8rem; | ||||
|  | ||||
| 	.l-time-range-inputs-holder, | ||||
| 	.l-time-range-slider { | ||||
| 		//font-size: 0.8em; | ||||
| 	} | ||||
|  | ||||
| 	.l-time-range-inputs-holder, | ||||
| 	.l-time-range-slider-holder, | ||||
| 	.l-time-range-ticks-holder | ||||
| 	{ | ||||
| 		//@include test(); | ||||
| 		@include absPosDefault(0, visible); | ||||
| 		box-sizing: border-box; | ||||
| 		top: auto; | ||||
| 	} | ||||
| 	.l-time-range-slider, | ||||
| 	.l-time-range-ticks { | ||||
| 		//@include test(red, 0.1); | ||||
| 		@include absPosDefault(0, visible); | ||||
| 		left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset; | ||||
| 	} | ||||
|  | ||||
| 	.l-time-range-inputs-holder { | ||||
| 		//@include test(red); | ||||
| 		height: $r1H; bottom: $r2H + $r3H + ($interiorMarginSm * 2); | ||||
| 		padding-top: $interiorMargin; | ||||
| 		border-top: 1px solid $colorInteriorBorder; | ||||
| @@ -70,7 +58,6 @@ mct-include.l-time-controller { | ||||
| 	} | ||||
|  | ||||
| 	.l-time-range-slider-holder { | ||||
| 		//@include test(green); | ||||
| 		height: $r2H; bottom: $r3H + ($interiorMarginSm * 1); | ||||
| 		.range-holder { | ||||
| 			box-shadow: none; | ||||
| @@ -82,7 +69,6 @@ mct-include.l-time-controller { | ||||
| 					$myW: 8px; | ||||
| 					@include transform(translateX(50%)); | ||||
| 					position: absolute; | ||||
| 					//@include test(); | ||||
| 					top: 0; right: 0; bottom: 0px; left: auto; | ||||
| 					width: $myW; | ||||
| 					height: auto; | ||||
| @@ -97,7 +83,6 @@ mct-include.l-time-controller { | ||||
| 						// Vert line | ||||
| 						top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1; | ||||
| 						width: 2px; | ||||
| 						//top: 0; right: 3px; bottom: 0; left: 3px; | ||||
| 					} | ||||
| 					&:after { | ||||
| 						// Circle element | ||||
| @@ -114,7 +99,6 @@ mct-include.l-time-controller { | ||||
| 			} | ||||
| 		} | ||||
| 		&:not(:active) { | ||||
| 			//@include test(#ff00cc); | ||||
| 			.knob, | ||||
| 			.range { | ||||
| 				@include transition-property(left, right); | ||||
| @@ -155,7 +139,6 @@ mct-include.l-time-controller { | ||||
| 	.knob { | ||||
| 		z-index: 2; | ||||
| 		.range-value { | ||||
| 			//@include test($sliderColorRange); | ||||
| 			@include trans-prop-nice-fade(.25s); | ||||
| 			padding: 0 $rangeValOffset; | ||||
| 			position: absolute; | ||||
| @@ -167,7 +150,6 @@ mct-include.l-time-controller { | ||||
| 			color: $sliderColorKnobHov; | ||||
| 		} | ||||
| 		&.knob-l { | ||||
| 			//border-bottom-left-radius: $knobCr; // MOVED TO _CONTROLS.SCSS | ||||
| 			margin-left: $knobM; | ||||
| 			.range-value { | ||||
| 				text-align: right; | ||||
| @@ -175,7 +157,6 @@ mct-include.l-time-controller { | ||||
| 			} | ||||
| 		} | ||||
| 		&.knob-r { | ||||
| 			//border-bottom-right-radius: $knobCr; | ||||
| 			margin-right: $knobM; | ||||
| 			.range-value { | ||||
| 				left: $rangeValOffset; | ||||
| @@ -185,15 +166,189 @@ mct-include.l-time-controller { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	.l-time-domain-selector { | ||||
| 		position: absolute; | ||||
| 		right: 0px; | ||||
| 		bottom: 46px; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| //.slot.range-holder { | ||||
| //	background-color: $sliderColorRangeHolder; | ||||
| //} | ||||
|  | ||||
| .s-time-range-val { | ||||
| 	//@include test(); | ||||
| 	border-radius: $controlCr; | ||||
| 	background-color: $colorInputBg; | ||||
| 	padding: 1px 1px 0 $interiorMargin; | ||||
| } | ||||
| } | ||||
|  | ||||
| @include phoneandtablet { | ||||
| 	.l-time-controller, .l-time-range-inputs-holder { | ||||
| 		min-width: 0px; | ||||
| 	} | ||||
|  | ||||
| 	.l-time-controller { | ||||
|  | ||||
| 		.l-time-domain-selector { | ||||
| 			select { | ||||
| 				height: 25px; | ||||
| 				margin-bottom: 0px; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		.l-time-range-slider-holder, .l-time-range-ticks-holder { | ||||
| 			display: none; | ||||
| 		} | ||||
|  | ||||
| 		.time-range-start, .time-range-end, { | ||||
| 			width: 100%; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			.l-time-range-input { | ||||
| 				display: block; | ||||
| 				.s-btn { | ||||
| 					padding-right: 18px; | ||||
| 					white-space: nowrap; | ||||
| 					input { | ||||
| 						width: 100%; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			.l-time-range-inputs-elem { | ||||
|  | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @include phone { | ||||
| 	.l-time-controller { | ||||
| 		height: 48px; | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			bottom: 24px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-domain-selector { | ||||
| 			width: 33%; | ||||
| 			bottom: -9px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			.l-time-range-input { | ||||
| 				margin-bottom: 5px; | ||||
| 				.s-btn { | ||||
| 					width: 66%; | ||||
| 				} | ||||
| 			} | ||||
| 			.l-time-range-inputs-elem { | ||||
| 				&.ui-symbol { | ||||
| 					display: none; | ||||
| 				} | ||||
|  | ||||
| 				&.lbl { | ||||
| 					width: 33%; | ||||
| 					right: 0px; | ||||
| 					top: 5px; | ||||
| 					display: block; | ||||
| 					height: 25px; | ||||
| 					margin: 0; | ||||
| 					line-height: 25px; | ||||
| 					position: absolute; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @include tablet { | ||||
| 	.l-time-controller { | ||||
| 		height: 17px; | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			bottom: -7px; | ||||
| 			left: -5px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-domain-selector { | ||||
| 			width: 23%; | ||||
| 			right: -4px; | ||||
| 			bottom: -10px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			.l-time-range-input { | ||||
| 				float: left; | ||||
| 				.s-btn { | ||||
| 					width: 100%; | ||||
| 					padding-left: 4px; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @include tabletLandscape { | ||||
| 	.l-time-controller { | ||||
| 		height: 17px; | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			bottom: -7px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-domain-selector { | ||||
| 			width: 23%; | ||||
| 			right: auto; | ||||
| 			bottom: -10px; | ||||
| 			left: 391px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			.l-time-range-inputs-elem { | ||||
| 				&.ui-symbol, &.lbl { | ||||
| 					display: block; | ||||
| 					float: left; | ||||
| 					line-height: 25px; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	.pane-tree-hidden .l-time-controller { | ||||
| 		.l-time-domain-selector { | ||||
| 			left: 667px; | ||||
| 		} | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			padding-left: 277px; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @include tabletPortrait { | ||||
| 	.l-time-controller { | ||||
| 		height: 17px; | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			bottom: -7px; | ||||
| 			left: -5px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-domain-selector { | ||||
| 			width: 23%; | ||||
| 			right: -4px; | ||||
| 			bottom: -10px; | ||||
| 		} | ||||
|  | ||||
| 		.l-time-range-inputs-holder { | ||||
| 			.l-time-range-input { | ||||
| 				width: 38%; | ||||
| 				float: left; | ||||
| 			} | ||||
| 			.l-time-range-inputs-elem { | ||||
| 				&.ui-symbol, &.lbl { | ||||
| 					display: none; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -20,13 +20,7 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| .section-header { | ||||
| 	border-radius: $basicCr; | ||||
| 	background: $colorFormSectionHeader; | ||||
| 	$c: lighten($colorBodyFg, 20%); | ||||
| 	color: $c; | ||||
| 	font-size: 0.8em; | ||||
| 	padding: $formTBPad $formLRPad; | ||||
| 	text-transform: uppercase; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
|  | ||||
| .form { | ||||
| @@ -37,12 +31,20 @@ | ||||
| 		margin-bottom: $interiorMarginLg * 2; | ||||
| 	} | ||||
|  | ||||
|     .section-header { | ||||
|         border-radius: $basicCr; | ||||
|         background: $colorFormSectionHeader; | ||||
|         $c: lighten($colorBodyFg, 20%); | ||||
|         color: $c; | ||||
|         font-size: 0.8em; | ||||
|         padding: $formTBPad $formLRPad; | ||||
|     } | ||||
|  | ||||
| 	.form-row { | ||||
| 		$m: $interiorMargin; | ||||
| 		box-sizing: border-box; | ||||
| 		@include clearfix; | ||||
| 		border-top: 1px solid $colorFormLines; | ||||
| 		margin-top: $m; | ||||
| 		padding: $formTBPad 0; | ||||
| 		position: relative; | ||||
| 		&.first { | ||||
| @@ -52,10 +54,7 @@ | ||||
| 		>.label, | ||||
| 		>.controls { | ||||
| 			box-sizing: border-box; | ||||
| 			@include clearfix; | ||||
| 			font-size: 0.8rem; | ||||
| 			line-height: $formInputH; | ||||
|             min-height: $formInputH; | ||||
| 		} | ||||
|  | ||||
| 		>.label { | ||||
| @@ -83,27 +82,12 @@ | ||||
| 					margin-right: 5px; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			.l-med input[type="text"] { | ||||
| 				width: 200px; | ||||
| 			} | ||||
|  | ||||
| 			.l-small input[type="text"] { | ||||
| 				width: 50px; | ||||
| 			} | ||||
|  | ||||
| 			.l-numeric input[type="text"] { | ||||
| 				text-align: right; | ||||
| 			} | ||||
|  | ||||
| 			.select { | ||||
| 				margin-right: $interiorMargin; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		.field-hints { | ||||
| 			color: darken($colorBodyFg, 20%); | ||||
| 		} | ||||
| 		.hint, .field-hints {  color: $colorFieldHint;  } | ||||
|  | ||||
| 		.selector-list { | ||||
| 			// Used in create overlay to display tree view | ||||
| @@ -124,25 +108,31 @@ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| .l-controls-first { | ||||
|     .form .form-row { | ||||
|         margin-top: $interiorMarginSm; | ||||
|         >.label, | ||||
|         >.controls { | ||||
|             line-height: inherit; | ||||
|             min-height: inherit;; | ||||
|         } | ||||
|         >.label { | ||||
|             @include flex(1 1 auto); | ||||
|             min-width: 0; | ||||
|             width: auto; | ||||
|             order: 2; | ||||
|         } | ||||
|         >.controls { | ||||
|             @include flex(0 0 auto); | ||||
|             margin-right: $interiorMargin; | ||||
|             order: 1; | ||||
|         } | ||||
| .l-controls-first .form .form-row, | ||||
| .form .form-row.l-controls-first { | ||||
|     >.label, | ||||
|     >.controls { | ||||
|         line-height: inherit; | ||||
|         min-height: inherit;; | ||||
|     } | ||||
|     >.label { | ||||
|         @include flex(1 1 auto); | ||||
|         min-width: 0; | ||||
|         width: auto; | ||||
|         order: 2; | ||||
|     } | ||||
|     >.controls { | ||||
|         @include flex(0 0 auto); | ||||
|         margin-right: $interiorMargin; | ||||
|         order: 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .l-controls-under.l-flex-row { | ||||
|     // Change to use column layout | ||||
|     @include flex-direction(column); | ||||
|     .flex-elem { | ||||
|         margin-bottom: $interiorMarginLg; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -155,13 +145,6 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| label.form-control.checkbox { | ||||
| 	input { | ||||
| 		margin-right: $interiorMargin; | ||||
| 		vertical-align: top; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| .hint, | ||||
| .s-hint { | ||||
| 	font-size: 0.9em; | ||||
| @@ -181,19 +164,4 @@ label.form-control.checkbox { | ||||
| 		color: lighten($colorFormInvalid, 30%); | ||||
| 		padding: $interiorMargin; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| input[type="text"], | ||||
| input[type="search"] { | ||||
| 	@include nice-input(); | ||||
| 	&.numeric { | ||||
| 		text-align: right; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| textarea { | ||||
| 	@include nice-textarea($colorInputBg, $colorInputFg); | ||||
| 	position: absolute; | ||||
| 	height: 100%; | ||||
| 	width: 100%; | ||||
| } | ||||
| @@ -23,9 +23,13 @@ | ||||
|     > .label { | ||||
|         padding-right: $reqSymbolM; // Keep room for validation element | ||||
|         &::after { | ||||
| 	        float: right; | ||||
|             position: absolute; | ||||
|             right: $interiorMargin; | ||||
|             font-family: symbolsfont; | ||||
|             font-size: $reqSymbolFontSize; | ||||
|             height: 100%; | ||||
|             line-height: 200%; | ||||
|  | ||||
|         } | ||||
|     } | ||||
|     &.invalid, | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|     100% { @include transform(translate(-50%, -50%) rotate(360deg)); } | ||||
| } | ||||
|  | ||||
| @mixin spinner($b: 5px, $c: $colorKey) { | ||||
| @mixin  spinner($b: 5px, $c: $colorKey) { | ||||
|     @include transform-origin(center); | ||||
|     @include animation-name(rotation-centered); | ||||
|     @include animation-duration(0.5s); | ||||
| @@ -46,10 +46,7 @@ | ||||
| } | ||||
|  | ||||
| .wait-spinner { | ||||
| 	$d: 5%; | ||||
| 	@include spinner(0.5em, $colorKey); | ||||
| 	height: auto; width: auto; | ||||
| 	padding: $d; // Will size object based on parent container WIDTH | ||||
| 	@include spinner($waitSpinnerBorderW, $colorKey); | ||||
| 	pointer-events: none; | ||||
| 	z-index: 2; | ||||
| 	&.inline { | ||||
| @@ -60,15 +57,6 @@ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| .treeview .wait-spinner { | ||||
|     // Only used in subtree.html, which I don't think this is actually being used | ||||
| 	$d: 10px; | ||||
| 	height: $d; width: $d; | ||||
| 	margin: 0 !important; | ||||
| 	padding: 0 !important; | ||||
| 	top: 2px; left: 0; | ||||
| } | ||||
|  | ||||
| .loading { | ||||
| 	// Can be applied to any block element with height and width | ||||
|     pointer-events: none; | ||||
| @@ -77,8 +65,8 @@ | ||||
|         content: ''; | ||||
|     } | ||||
| 	&:before { | ||||
|         @include spinner(5px, $colorLoadingFg); | ||||
|         padding: 5%; | ||||
|         @include spinner($waitSpinnerBorderW, $colorLoadingFg); | ||||
|         height: $waitSpinnerD; width: $waitSpinnerD; | ||||
|         z-index: 10; | ||||
| 	} | ||||
|     &:after { | ||||
| @@ -87,8 +75,22 @@ | ||||
|         display: block; | ||||
|         z-index: 9; | ||||
|     } | ||||
|     &.tree-item:before { | ||||
|         padding: $menuLineH / 4; | ||||
|         border-width: 2px; | ||||
|     &.tree-item.t-wait-node { | ||||
|         $d: $waitSpinnerTreeD; | ||||
|         $spinnerL: $treeVCW + $interiorMargin + 3px + $d/2; | ||||
|         padding-left: $spinnerL + $d/2 + $interiorMargin; | ||||
|         .t-title-label { | ||||
|             font-style: italic; | ||||
|             opacity: 0.6; | ||||
|         } | ||||
|         &:before { | ||||
|             height: $d; | ||||
|             width: $d; | ||||
|             border-width: 4px; | ||||
|             left: $spinnerL; | ||||
|         } | ||||
|         &:after { | ||||
|             display: none; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -35,7 +35,6 @@ | ||||
|         z-index: 100; | ||||
|     } | ||||
|     > .holder { | ||||
|         //$i: 15%; | ||||
|         @include containerSubtle($colorOvrBg, $colorOvrFg); | ||||
|         border-radius: $basicCr * 3; | ||||
|         color: $colorOvrFg; | ||||
| @@ -57,15 +56,8 @@ | ||||
|             right: $m; | ||||
|             bottom: $m; | ||||
|             left: $m; | ||||
|  | ||||
|             //.top-bar, | ||||
|             //.editor, | ||||
|             //.bottom-bar { | ||||
|             //    @include absPosDefault(); | ||||
|             //} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .title { | ||||
|         @include ellipsize(); | ||||
|         font-size: 1.2em; | ||||
| @@ -73,9 +65,7 @@ | ||||
|         margin-bottom: $interiorMargin; | ||||
|     } | ||||
|  | ||||
|     .hint { | ||||
|         color: pushBack($colorOvrFg, 20%); | ||||
|     } | ||||
|     .hint, .field-hints { color: $colorFieldHintOverlay !important;  } | ||||
|  | ||||
|     .abs.top-bar { | ||||
|         height: $ovrTopBarH; | ||||
| @@ -88,7 +78,7 @@ | ||||
|         left: 0; | ||||
|         right: 0; | ||||
|         overflow: auto; | ||||
|         .field.l-med { | ||||
|         .field.l-input-med { | ||||
|             input[type='text'] { | ||||
|                 width: 100%; | ||||
|             } | ||||
| @@ -120,7 +110,6 @@ | ||||
|         bottom: 0; | ||||
|         left: 0; | ||||
|         overflow: visible; | ||||
|         //font-size: 1em; | ||||
|         height: $ovrFooterH; | ||||
|     } | ||||
|  | ||||
| @@ -132,11 +121,14 @@ | ||||
|         margin: .5em 0; | ||||
|         width: 100%; | ||||
|     } | ||||
|  | ||||
|     .select { | ||||
|         box-shadow: $shdwBtnsOverlay; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .t-dialog-sm .overlay > .holder { | ||||
|     // Used for blocker and in-progress dialogs, modal alerts, etc. | ||||
|     //@include test(red); | ||||
|     $h: 225px; | ||||
|     min-height: $h; | ||||
|     height: $h; | ||||
|   | ||||
| @@ -26,6 +26,10 @@ | ||||
|     top: $m; right: $m * 1.25; bottom: $m; left: $m * 1.25; | ||||
| } | ||||
|  | ||||
| body, html { | ||||
|     overflow: hidden; | ||||
| } | ||||
|  | ||||
| .l-splash-holder { | ||||
|     // Main outer holder. | ||||
|     @include transition-property(opacity); | ||||
|   | ||||
| @@ -19,15 +19,18 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div ng-controller="TimeRangeController"> | ||||
| <div ng-controller="TimeRangeController as trCtrl"> | ||||
|     <form class="l-time-range-inputs-holder" | ||||
|           ng-submit="updateBoundsFromForm()"> | ||||
|           ng-submit="trCtrl.updateBoundsFromForm()"> | ||||
|         <span class="l-time-range-inputs-elem ui-symbol type-icon">C</span> | ||||
|         <span class="l-time-range-input"> | ||||
|             <mct-control key="'datetime-field'" | ||||
|                          structure="{ format: parameters.format, validate: validateStart }" | ||||
|                          structure="{ | ||||
|                              format: parameters.format, | ||||
|                              validate: trCtrl.validateStart | ||||
|                          }" | ||||
|                          ng-model="formModel" | ||||
|                          ng-blur="updateBoundsFromForm()" | ||||
|                          ng-blur="trCtrl.updateBoundsFromForm()" | ||||
|                          field="'start'" | ||||
|                          class="time-range-start"> | ||||
|             </mct-control> | ||||
| @@ -37,9 +40,12 @@ | ||||
|  | ||||
|         <span class="l-time-range-input" ng-controller="ToggleController as t2"> | ||||
|             <mct-control key="'datetime-field'" | ||||
|                          structure="{ format: parameters.format, validate: validateEnd }" | ||||
|                          structure="{ | ||||
|                              format: parameters.format, | ||||
|                              validate: trCtrl.validateEnd | ||||
|                          }" | ||||
|                          ng-model="formModel" | ||||
|                          ng-blur="updateBoundsFromForm()" | ||||
|                          ng-blur="trCtrl.updateBoundsFromForm()" | ||||
|                          field="'end'" | ||||
|                          class="time-range-end"> | ||||
|             </mct-control>  | ||||
| @@ -53,22 +59,25 @@ | ||||
|             <div class="slider" | ||||
|                  mct-resize="spanWidth = bounds.width"> | ||||
|                 <div class="knob knob-l" | ||||
|                      mct-drag-down="startLeftDrag()" | ||||
|                      mct-drag="leftDrag(delta[0])" | ||||
|                      mct-drag-down="trCtrl.startLeftDrag()" | ||||
|                      mct-drag="trCtrl.leftDrag(delta[0])" | ||||
|                      ng-style="{ left: startInnerPct }"> | ||||
|                     <div class="range-value">{{startInnerText}}</div> | ||||
|                 </div> | ||||
|                 <div class="knob knob-r" | ||||
|                      mct-drag-down="startRightDrag()" | ||||
|                      mct-drag="rightDrag(delta[0])" | ||||
|                      mct-drag-down="trCtrl.startRightDrag()" | ||||
|                      mct-drag="trCtrl.rightDrag(delta[0])" | ||||
|                      ng-style="{ right: endInnerPct }"> | ||||
|                     <div class="range-value">{{endInnerText}}</div> | ||||
|                 </div> | ||||
|                 <div class="slot range-holder"> | ||||
|                     <div class="range" | ||||
|                          mct-drag-down="startMiddleDrag()" | ||||
|                          mct-drag="middleDrag(delta[0])" | ||||
|                          ng-style="{ left: startInnerPct, right: endInnerPct}"> | ||||
|                          mct-drag-down="trCtrl.startMiddleDrag()" | ||||
|                          mct-drag="trCtrl.middleDrag(delta[0])" | ||||
|                          ng-style="{ | ||||
|                              left: startInnerPct, | ||||
|                              right: endInnerPct | ||||
|                          }"> | ||||
|                         <div class="toi-line"></div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|   | ||||
| @@ -40,7 +40,7 @@ | ||||
|                     <mct-representation | ||||
|                             key="'edit-elements'" | ||||
|                             mct-object="domainObject" | ||||
|                             class="flex-elem holder grows vscroll current-elements"> | ||||
|                             class="flex-elem l-flex-col holder grows current-elements"> | ||||
|                     </mct-representation> | ||||
|                 </div> | ||||
|             </div> | ||||
|   | ||||
| @@ -19,18 +19,6 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <ul class="tree"> | ||||
|     <li ng-if="!composition"> | ||||
|         <span class="tree-item"> | ||||
|             <span class="icon wait-spinner"></span> | ||||
|             <span class="title-label">Loading...</span> | ||||
|         </span> | ||||
|     </li> | ||||
|     <li ng-repeat="child in composition"> | ||||
|         <mct-representation key="'tree-node'" | ||||
|                             mct-object="child" | ||||
|                             parameters="parameters" | ||||
|                             ng-model="ngModel"> | ||||
|         </mct-representation> | ||||
|     </li> | ||||
| </ul> | ||||
| <mct-tree mct-object="domainObject" mct-model="ngModel.selectedObject"> | ||||
| </mct-tree> | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								platform/commonUI/general/res/templates/tree/node.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								platform/commonUI/general/res/templates/tree/node.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| <span class="tree-item menus-to-left"> | ||||
| </span> | ||||
| <span class="tree-item-subtree"> | ||||
| </span> | ||||
							
								
								
									
										2
									
								
								platform/commonUI/general/res/templates/tree/toggle.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								platform/commonUI/general/res/templates/tree/toggle.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| <span class='ui-symbol view-control flex-elem'> | ||||
| </span> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <span class="rep-object-label"> | ||||
|     <div class="t-object-label l-flex-row flex-elem grows"> | ||||
|         <div class="t-item-icon flex-elem"> | ||||
|             <div class="t-item-icon-glyph"></div> | ||||
|         </div> | ||||
|         <div class='t-title-label flex-elem grows'></div> | ||||
|     </div> | ||||
| </span> | ||||
| @@ -19,11 +19,6 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="w1"> | ||||
| 	<div class="w2" | ||||
| 	     ng-controller="RTEventListController as rtevent"> | ||||
|         <mct-rt-data-table headers="rtevent.headers()" rows="rtevent.rows()" ascending-scroll="true"></mct-rt-data-table> | ||||
| 	</div> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| <li class='tree-item t-wait-node loading'> | ||||
|     <span class="t-title-label">Loading...</span> | ||||
| </li> | ||||
| @@ -19,247 +19,289 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,Promise*/ | ||||
| /*global define*/ | ||||
|  | ||||
| define( | ||||
|     ['moment'], | ||||
|     function (moment) { | ||||
|         "use strict"; | ||||
| define([ | ||||
|  | ||||
|         var TICK_SPACING_PX = 150; | ||||
| ], function () { | ||||
|     "use strict"; | ||||
|  | ||||
|     var TICK_SPACING_PX = 150; | ||||
|  | ||||
|         /** | ||||
|          * Controller used by the `time-controller` template. | ||||
|          * @memberof platform/commonUI/general | ||||
|          * @constructor | ||||
|          * @param $scope the Angular scope for this controller | ||||
|          * @param {FormatService} formatService the service to user to format | ||||
|          *        domain values | ||||
|          * @param {string} defaultFormat the format to request when no | ||||
|          *        format has been otherwise specified | ||||
|          * @param {Function} now a function to return current system time | ||||
|          */ | ||||
|         function TimeRangeController($scope, formatService, defaultFormat, now) { | ||||
|             var tickCount = 2, | ||||
|                 innerMinimumSpan = 1000, // 1 second | ||||
|                 outerMinimumSpan = 1000, // 1 second | ||||
|                 initialDragValue, | ||||
|                 formatter = formatService.getFormat(defaultFormat); | ||||
|  | ||||
|             function formatTimestamp(ts) { | ||||
|                 return formatter.format(ts); | ||||
|             } | ||||
|  | ||||
|             // From 0.0-1.0 to "0%"-"100%" | ||||
|             function toPercent(p) { | ||||
|                 return (100 * p) + "%"; | ||||
|             } | ||||
|  | ||||
|             function updateTicks() { | ||||
|                 var i, p, ts, start, end, span; | ||||
|                 end = $scope.ngModel.outer.end; | ||||
|                 start = $scope.ngModel.outer.start; | ||||
|                 span = end - start; | ||||
|                 $scope.ticks = []; | ||||
|                 for (i = 0; i < tickCount; i += 1) { | ||||
|                     p = i / (tickCount - 1); | ||||
|                     ts = p * span + start; | ||||
|                     $scope.ticks.push(formatTimestamp(ts)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             function updateSpanWidth(w) { | ||||
|                 tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2); | ||||
|                 updateTicks(); | ||||
|             } | ||||
|  | ||||
|             function updateViewForInnerSpanFromModel(ngModel) { | ||||
|                 var span = ngModel.outer.end - ngModel.outer.start; | ||||
|  | ||||
|                 // Expose readable dates for the knobs | ||||
|                 $scope.startInnerText = formatTimestamp(ngModel.inner.start); | ||||
|                 $scope.endInnerText = formatTimestamp(ngModel.inner.end); | ||||
|  | ||||
|                 // And positions for the knobs | ||||
|                 $scope.startInnerPct = | ||||
|                     toPercent((ngModel.inner.start - ngModel.outer.start) / span); | ||||
|                 $scope.endInnerPct = | ||||
|                     toPercent((ngModel.outer.end - ngModel.inner.end) / span); | ||||
|             } | ||||
|  | ||||
|             function defaultBounds() { | ||||
|                 var t = now(); | ||||
|                 return { | ||||
|                     start: t - 24 * 3600 * 1000, // One day | ||||
|                     end: t | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             function copyBounds(bounds) { | ||||
|                 return { start: bounds.start, end: bounds.end }; | ||||
|             } | ||||
|  | ||||
|             function updateViewFromModel(ngModel) { | ||||
|                 ngModel = ngModel || {}; | ||||
|                 ngModel.outer = ngModel.outer || defaultBounds(); | ||||
|                 ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); | ||||
|  | ||||
|                 // Stick it back is scope (in case we just set defaults) | ||||
|                 $scope.ngModel = ngModel; | ||||
|  | ||||
|                 updateViewForInnerSpanFromModel(ngModel); | ||||
|                 updateTicks(); | ||||
|             } | ||||
|  | ||||
|             function startLeftDrag() { | ||||
|                 initialDragValue = $scope.ngModel.inner.start; | ||||
|             } | ||||
|  | ||||
|             function startRightDrag() { | ||||
|                 initialDragValue = $scope.ngModel.inner.end; | ||||
|             } | ||||
|  | ||||
|             function startMiddleDrag() { | ||||
|                 initialDragValue = { | ||||
|                     start: $scope.ngModel.inner.start, | ||||
|                     end: $scope.ngModel.inner.end | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             function toMillis(pixels) { | ||||
|                 var span = | ||||
|                     $scope.ngModel.outer.end - $scope.ngModel.outer.start; | ||||
|                 return (pixels / $scope.spanWidth) * span; | ||||
|             } | ||||
|  | ||||
|             function clamp(value, low, high) { | ||||
|                 return Math.max(low, Math.min(high, value)); | ||||
|             } | ||||
|  | ||||
|             function leftDrag(pixels) { | ||||
|                 var delta = toMillis(pixels); | ||||
|                 $scope.ngModel.inner.start = clamp( | ||||
|                     initialDragValue + delta, | ||||
|                     $scope.ngModel.outer.start, | ||||
|                     $scope.ngModel.inner.end - innerMinimumSpan | ||||
|                 ); | ||||
|                 updateViewFromModel($scope.ngModel); | ||||
|             } | ||||
|  | ||||
|             function rightDrag(pixels) { | ||||
|                 var delta = toMillis(pixels); | ||||
|                 $scope.ngModel.inner.end = clamp( | ||||
|                     initialDragValue + delta, | ||||
|                     $scope.ngModel.inner.start + innerMinimumSpan, | ||||
|                     $scope.ngModel.outer.end | ||||
|                 ); | ||||
|                 updateViewFromModel($scope.ngModel); | ||||
|             } | ||||
|  | ||||
|             function middleDrag(pixels) { | ||||
|                 var delta = toMillis(pixels), | ||||
|                     edge = delta < 0 ? 'start' : 'end', | ||||
|                     opposite = delta < 0 ? 'end' : 'start'; | ||||
|  | ||||
|                 // Adjust the position of the edge in the direction of drag | ||||
|                 $scope.ngModel.inner[edge] = clamp( | ||||
|                     initialDragValue[edge] + delta, | ||||
|                     $scope.ngModel.outer.start, | ||||
|                     $scope.ngModel.outer.end | ||||
|                 ); | ||||
|                 // Adjust opposite knob to maintain span | ||||
|                 $scope.ngModel.inner[opposite] = $scope.ngModel.inner[edge] + | ||||
|                     initialDragValue[opposite] - initialDragValue[edge]; | ||||
|  | ||||
|                 updateViewFromModel($scope.ngModel); | ||||
|             } | ||||
|  | ||||
|             function updateFormModel() { | ||||
|                 $scope.formModel = { | ||||
|                     start: (($scope.ngModel || {}).outer || {}).start, | ||||
|                     end: (($scope.ngModel || {}).outer || {}).end | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             function updateOuterStart(t) { | ||||
|                 var ngModel = $scope.ngModel; | ||||
|  | ||||
|                 ngModel.inner.start = | ||||
|                     Math.max(ngModel.outer.start, ngModel.inner.start); | ||||
|                 ngModel.inner.end = Math.max( | ||||
|                     ngModel.inner.start + innerMinimumSpan, | ||||
|                     ngModel.inner.end | ||||
|                 ); | ||||
|  | ||||
|                 updateFormModel(); | ||||
|                 updateViewForInnerSpanFromModel(ngModel); | ||||
|                 updateTicks(); | ||||
|             } | ||||
|  | ||||
|             function updateOuterEnd(t) { | ||||
|                 var ngModel = $scope.ngModel; | ||||
|  | ||||
|                 ngModel.inner.end = | ||||
|                     Math.min(ngModel.outer.end, ngModel.inner.end); | ||||
|                 ngModel.inner.start = Math.min( | ||||
|                     ngModel.inner.end - innerMinimumSpan, | ||||
|                     ngModel.inner.start | ||||
|                 ); | ||||
|  | ||||
|                 updateFormModel(); | ||||
|                 updateViewForInnerSpanFromModel(ngModel); | ||||
|                 updateTicks(); | ||||
|             } | ||||
|  | ||||
|             function updateFormat(key) { | ||||
|                 formatter = formatService.getFormat(key || defaultFormat); | ||||
|                 updateViewForInnerSpanFromModel($scope.ngModel); | ||||
|                 updateTicks(); | ||||
|             } | ||||
|  | ||||
|             function updateBoundsFromForm() { | ||||
|                 var start = $scope.formModel.start, | ||||
|                     end = $scope.formModel.end; | ||||
|                 if (end >= start + outerMinimumSpan) { | ||||
|                     $scope.ngModel = $scope.ngModel || {}; | ||||
|                     $scope.ngModel.outer = { start: start, end: end }; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             function validateStart(startValue) { | ||||
|                 return startValue <= $scope.formModel.end - outerMinimumSpan; | ||||
|             } | ||||
|  | ||||
|             function validateEnd(endValue) { | ||||
|                 return endValue >= $scope.formModel.start + outerMinimumSpan; | ||||
|             } | ||||
|  | ||||
|             $scope.startLeftDrag = startLeftDrag; | ||||
|             $scope.startRightDrag = startRightDrag; | ||||
|             $scope.startMiddleDrag = startMiddleDrag; | ||||
|             $scope.leftDrag = leftDrag; | ||||
|             $scope.rightDrag = rightDrag; | ||||
|             $scope.middleDrag = middleDrag; | ||||
|  | ||||
|             $scope.updateBoundsFromForm = updateBoundsFromForm; | ||||
|  | ||||
|             $scope.validateStart = validateStart; | ||||
|             $scope.validateEnd = validateEnd; | ||||
|  | ||||
|             $scope.ticks = []; | ||||
|  | ||||
|             // Initialize scope to defaults | ||||
|             updateViewFromModel($scope.ngModel); | ||||
|             updateFormModel(); | ||||
|  | ||||
|             $scope.$watchCollection("ngModel", updateViewFromModel); | ||||
|             $scope.$watch("spanWidth", updateSpanWidth); | ||||
|             $scope.$watch("ngModel.outer.start", updateOuterStart); | ||||
|             $scope.$watch("ngModel.outer.end", updateOuterEnd); | ||||
|             $scope.$watch("parameters.format", updateFormat); | ||||
|         } | ||||
|  | ||||
|         return TimeRangeController; | ||||
|     /* format number as percent; 0.0-1.0 to "0%"-"100%" */ | ||||
|     function toPercent(p) { | ||||
|         return (100 * p) + "%"; | ||||
|     } | ||||
| ); | ||||
|  | ||||
|     function clamp(value, low, high) { | ||||
|         return Math.max(low, Math.min(high, value)); | ||||
|     } | ||||
|  | ||||
|     function copyBounds(bounds) { | ||||
|         return { | ||||
|             start: bounds.start, | ||||
|             end: bounds.end | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Controller used by the `time-controller` template. | ||||
|      * @memberof platform/commonUI/general | ||||
|      * @constructor | ||||
|      * @param $scope the Angular scope for this controller | ||||
|      * @param {FormatService} formatService the service to user to format | ||||
|      *        domain values | ||||
|      * @param {string} defaultFormat the format to request when no | ||||
|      *        format has been otherwise specified | ||||
|      * @param {Function} now a function to return current system time | ||||
|      */ | ||||
|     function TimeRangeController($scope, formatService, defaultFormat, now) { | ||||
|         this.$scope = $scope; | ||||
|         this.formatService = formatService; | ||||
|         this.defaultFormat = defaultFormat; | ||||
|         this.now = now; | ||||
|  | ||||
|         this.tickCount = 2; | ||||
|         this.innerMinimumSpan = 1000; // 1 second | ||||
|         this.outerMinimumSpan = 1000; // 1 second | ||||
|         this.initialDragValue = undefined; | ||||
|         this.formatter = formatService.getFormat(defaultFormat); | ||||
|         this.formStartChanged = false; | ||||
|         this.formEndChanged = false; | ||||
|  | ||||
|         this.$scope.ticks = []; | ||||
|  | ||||
|         this.updateViewFromModel(this.$scope.ngModel); | ||||
|         this.updateFormModel(); | ||||
|  | ||||
|         [ | ||||
|             'updateViewFromModel', | ||||
|             'updateSpanWidth', | ||||
|             'updateOuterStart', | ||||
|             'updateOuterEnd', | ||||
|             'updateFormat', | ||||
|             'validateStart', | ||||
|             'validateEnd', | ||||
|             'onFormStartChange', | ||||
|             'onFormEndChange' | ||||
|         ].forEach(function (boundFn) { | ||||
|             this[boundFn] = this[boundFn].bind(this); | ||||
|         }, this); | ||||
|  | ||||
|         this.$scope.$watchCollection("ngModel", this.updateViewFromModel); | ||||
|         this.$scope.$watch("spanWidth", this.updateSpanWidth); | ||||
|         this.$scope.$watch("ngModel.outer.start", this.updateOuterStart); | ||||
|         this.$scope.$watch("ngModel.outer.end", this.updateOuterEnd); | ||||
|         this.$scope.$watch("parameters.format", this.updateFormat); | ||||
|         this.$scope.$watch("formModel.start", this.onFormStartChange); | ||||
|         this.$scope.$watch("formModel.end", this.onFormEndChange); | ||||
|     } | ||||
|  | ||||
|     TimeRangeController.prototype.formatTimestamp = function (ts) { | ||||
|         return this.formatter.format(ts); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateTicks = function () { | ||||
|         var i, p, ts, start, end, span; | ||||
|         end = this.$scope.ngModel.outer.end; | ||||
|         start = this.$scope.ngModel.outer.start; | ||||
|         span = end - start; | ||||
|         this.$scope.ticks = []; | ||||
|         for (i = 0; i < this.tickCount; i += 1) { | ||||
|             p = i / (this.tickCount - 1); | ||||
|             ts = p * span + start; | ||||
|             this.$scope.ticks.push(this.formatTimestamp(ts)); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateSpanWidth = function (w) { | ||||
|         this.tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2); | ||||
|         this.updateTicks(); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateViewForInnerSpanFromModel = function ( | ||||
|         ngModel | ||||
|     ) { | ||||
|         var span = ngModel.outer.end - ngModel.outer.start; | ||||
|  | ||||
|         // Expose readable dates for the knobs | ||||
|         this.$scope.startInnerText = this.formatTimestamp(ngModel.inner.start); | ||||
|         this.$scope.endInnerText = this.formatTimestamp(ngModel.inner.end); | ||||
|  | ||||
|         // And positions for the knobs | ||||
|         this.$scope.startInnerPct = | ||||
|             toPercent((ngModel.inner.start - ngModel.outer.start) / span); | ||||
|         this.$scope.endInnerPct = | ||||
|             toPercent((ngModel.outer.end - ngModel.inner.end) / span); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.defaultBounds = function () { | ||||
|         var t = this.now(); | ||||
|         return { | ||||
|             start: t - 24 * 3600 * 1000, // One day | ||||
|             end: t | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     TimeRangeController.prototype.updateViewFromModel = function (ngModel) { | ||||
|         ngModel = ngModel || {}; | ||||
|         ngModel.outer = ngModel.outer || this.defaultBounds(); | ||||
|         ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); | ||||
|  | ||||
|         // Stick it back is scope (in case we just set defaults) | ||||
|         this.$scope.ngModel = ngModel; | ||||
|  | ||||
|         this.updateViewForInnerSpanFromModel(ngModel); | ||||
|         this.updateTicks(); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.startLeftDrag = function () { | ||||
|         this.initialDragValue = this.$scope.ngModel.inner.start; | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.startRightDrag = function () { | ||||
|         this.initialDragValue = this.$scope.ngModel.inner.end; | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.startMiddleDrag = function () { | ||||
|         this.initialDragValue = { | ||||
|             start: this.$scope.ngModel.inner.start, | ||||
|             end: this.$scope.ngModel.inner.end | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.toMillis = function (pixels) { | ||||
|         var span = | ||||
|             this.$scope.ngModel.outer.end - this.$scope.ngModel.outer.start; | ||||
|         return (pixels / this.$scope.spanWidth) * span; | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.leftDrag = function (pixels) { | ||||
|         var delta = this.toMillis(pixels); | ||||
|         this.$scope.ngModel.inner.start = clamp( | ||||
|             this.initialDragValue + delta, | ||||
|             this.$scope.ngModel.outer.start, | ||||
|             this.$scope.ngModel.inner.end - this.innerMinimumSpan | ||||
|         ); | ||||
|         this.updateViewFromModel(this.$scope.ngModel); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.rightDrag = function (pixels) { | ||||
|         var delta = this.toMillis(pixels); | ||||
|         this.$scope.ngModel.inner.end = clamp( | ||||
|             this.initialDragValue + delta, | ||||
|             this.$scope.ngModel.inner.start + this.innerMinimumSpan, | ||||
|             this.$scope.ngModel.outer.end | ||||
|         ); | ||||
|         this.updateViewFromModel(this.$scope.ngModel); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.middleDrag = function (pixels) { | ||||
|         var delta = this.toMillis(pixels), | ||||
|             edge = delta < 0 ? 'start' : 'end', | ||||
|             opposite = delta < 0 ? 'end' : 'start'; | ||||
|  | ||||
|         // Adjust the position of the edge in the direction of drag | ||||
|         this.$scope.ngModel.inner[edge] = clamp( | ||||
|             this.initialDragValue[edge] + delta, | ||||
|             this.$scope.ngModel.outer.start, | ||||
|             this.$scope.ngModel.outer.end | ||||
|         ); | ||||
|         // Adjust opposite knob to maintain span | ||||
|         this.$scope.ngModel.inner[opposite] = | ||||
|             this.$scope.ngModel.inner[edge] + | ||||
|             this.initialDragValue[opposite] - | ||||
|             this.initialDragValue[edge]; | ||||
|  | ||||
|         this.updateViewFromModel(this.$scope.ngModel); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateFormModel = function () { | ||||
|         this.$scope.formModel = { | ||||
|             start: ((this.$scope.ngModel || {}).outer || {}).start, | ||||
|             end: ((this.$scope.ngModel || {}).outer || {}).end | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateOuterStart = function () { | ||||
|         var ngModel = this.$scope.ngModel; | ||||
|  | ||||
|         ngModel.inner.start = | ||||
|             Math.max(ngModel.outer.start, ngModel.inner.start); | ||||
|         ngModel.inner.end = Math.max( | ||||
|             ngModel.inner.start + this.innerMinimumSpan, | ||||
|             ngModel.inner.end | ||||
|         ); | ||||
|  | ||||
|         this.updateFormModel(); | ||||
|         this.updateViewForInnerSpanFromModel(ngModel); | ||||
|         this.updateTicks(); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateOuterEnd = function () { | ||||
|         var ngModel = this.$scope.ngModel; | ||||
|  | ||||
|         ngModel.inner.end = | ||||
|             Math.min(ngModel.outer.end, ngModel.inner.end); | ||||
|         ngModel.inner.start = Math.min( | ||||
|             ngModel.inner.end - this.innerMinimumSpan, | ||||
|             ngModel.inner.start | ||||
|         ); | ||||
|  | ||||
|         this.updateFormModel(); | ||||
|         this.updateViewForInnerSpanFromModel(ngModel); | ||||
|         this.updateTicks(); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateFormat = function (key) { | ||||
|         this.formatter = this.formatService.getFormat(key || this.defaultFormat); | ||||
|         this.updateViewForInnerSpanFromModel(this.$scope.ngModel); | ||||
|         this.updateTicks(); | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.updateBoundsFromForm = function () { | ||||
|         if (this.formStartChanged) { | ||||
|             this.$scope.ngModel.outer.start = | ||||
|                 this.$scope.ngModel.inner.start = | ||||
|                 this.$scope.formModel.start; | ||||
|             this.formStartChanged = false; | ||||
|         } | ||||
|         if (this.formEndChanged) { | ||||
|             this.$scope.ngModel.outer.end = | ||||
|                 this.$scope.ngModel.inner.end = | ||||
|                 this.$scope.formModel.end; | ||||
|             this.formEndChanged = false; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.onFormStartChange = function ( | ||||
|         newValue, | ||||
|         oldValue | ||||
|     ) { | ||||
|         if (!this.formStartChanged && newValue !== oldValue) { | ||||
|             this.formStartChanged = true; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.onFormEndChange = function ( | ||||
|         newValue, | ||||
|         oldValue | ||||
|     ) { | ||||
|         if (!this.formEndChanged && newValue !== oldValue) { | ||||
|             this.formEndChanged = true; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.validateStart = function (startValue) { | ||||
|         return startValue <= | ||||
|             this.$scope.formModel.end - this.outerMinimumSpan; | ||||
|     }; | ||||
|  | ||||
|     TimeRangeController.prototype.validateEnd = function (endValue) { | ||||
|         return endValue >= | ||||
|             this.$scope.formModel.start + this.outerMinimumSpan; | ||||
|     }; | ||||
|  | ||||
|     return TimeRangeController; | ||||
| }); | ||||
|   | ||||
| @@ -19,40 +19,36 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/ | ||||
| /*global define*/ | ||||
| 
 | ||||
| /** | ||||
|  * MergeModelsSpec. Created by vwoeltje on 11/6/14. | ||||
|  */ | ||||
| define( | ||||
|     ["../src/NameColumn"], | ||||
|     function (NameColumn) { | ||||
|         "use strict"; | ||||
| 
 | ||||
|         describe("A name column", function () { | ||||
|             var mockDomainObject, | ||||
|                 column; | ||||
| 
 | ||||
|             beforeEach(function () { | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     [ "getModel" ] | ||||
|                 ); | ||||
|                 mockDomainObject.getModel.andReturn({ | ||||
|                     name: "Test object name" | ||||
| define([ | ||||
|     'angular', | ||||
|     '../ui/TreeView' | ||||
| ], function (angular, TreeView) { | ||||
|     function MCTTree($parse, gestureService) { | ||||
|         function link(scope, element, attrs) { | ||||
|             var treeView = new TreeView(gestureService), | ||||
|                 expr = $parse(attrs.mctModel), | ||||
|                 unobserve = treeView.observe(function (domainObject) { | ||||
|                     if (domainObject !== expr(scope.$parent)) { | ||||
|                         expr.assign(scope.$parent, domainObject); | ||||
|                         scope.$apply(); | ||||
|                     } | ||||
|                 }); | ||||
|                 column = new NameColumn(); | ||||
|             }); | ||||
| 
 | ||||
|             it("reports a column header", function () { | ||||
|                 expect(column.getTitle()).toEqual("Name"); | ||||
|             }); | ||||
|             element.append(angular.element(treeView.elements())); | ||||
| 
 | ||||
|             it("looks up name from an object's model", function () { | ||||
|                 expect(column.getValue(mockDomainObject).text) | ||||
|                     .toEqual("Test object name"); | ||||
|             }); | ||||
|             scope.$parent.$watch(attrs.mctModel, treeView.value.bind(treeView)); | ||||
|             scope.$watch('mctObject', treeView.model.bind(treeView)); | ||||
|             scope.$on('$destroy', unobserve); | ||||
|         } | ||||
| 
 | ||||
|         }); | ||||
|         return { | ||||
|             restrict: "E", | ||||
|             link: link, | ||||
|             scope: { mctObject: "=" } | ||||
|         }; | ||||
|     } | ||||
| ); | ||||
| 
 | ||||
|     return MCTTree; | ||||
| }); | ||||
							
								
								
									
										65
									
								
								platform/commonUI/general/src/ui/ToggleView.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								platform/commonUI/general/src/ui/ToggleView.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     'zepto', | ||||
|     'text!../../res/templates/tree/toggle.html' | ||||
| ], function ($, toggleTemplate) { | ||||
|     function ToggleView(state) { | ||||
|         this.expanded = !!state; | ||||
|         this.callbacks = []; | ||||
|         this.el = $(toggleTemplate); | ||||
|         this.el.on('click', function () { | ||||
|             this.value(!this.expanded); | ||||
|         }.bind(this)); | ||||
|     } | ||||
|  | ||||
|     ToggleView.prototype.value = function (state) { | ||||
|         this.expanded = state; | ||||
|  | ||||
|         if (state) { | ||||
|             this.el.addClass('expanded'); | ||||
|         } else { | ||||
|             this.el.removeClass('expanded'); | ||||
|         } | ||||
|  | ||||
|         this.callbacks.forEach(function (callback) { | ||||
|             callback(state); | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     ToggleView.prototype.observe = function (callback) { | ||||
|         this.callbacks.push(callback); | ||||
|         return function () { | ||||
|             this.callbacks = this.callbacks.filter(function (c) { | ||||
|                 return c !== callback; | ||||
|             }); | ||||
|         }.bind(this); | ||||
|     }; | ||||
|  | ||||
|     ToggleView.prototype.elements = function () { | ||||
|         return this.el; | ||||
|     }; | ||||
|  | ||||
|     return ToggleView; | ||||
| }); | ||||
							
								
								
									
										90
									
								
								platform/commonUI/general/src/ui/TreeLabelView.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								platform/commonUI/general/src/ui/TreeLabelView.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     'zepto', | ||||
|     'text!../../res/templates/tree/tree-label.html' | ||||
| ], function ($, labelTemplate) { | ||||
|     'use strict'; | ||||
|  | ||||
|     function TreeLabelView(gestureService) { | ||||
|         this.el = $(labelTemplate); | ||||
|         this.gestureService = gestureService; | ||||
|     } | ||||
|  | ||||
|     function getGlyph(domainObject) { | ||||
|         var type = domainObject.getCapability('type'); | ||||
|         return type.getGlyph(); | ||||
|     } | ||||
|  | ||||
|     function isLink(domainObject) { | ||||
|         var location = domainObject.getCapability('location'); | ||||
|         return location.isLink(); | ||||
|     } | ||||
|  | ||||
|     TreeLabelView.prototype.updateView = function (domainObject) { | ||||
|         var titleEl = this.el.find('.t-title-label'), | ||||
|             glyphEl = this.el.find('.t-item-icon-glyph'), | ||||
|             iconEl = this.el.find('.t-item-icon'); | ||||
|  | ||||
|         titleEl.text(domainObject ? domainObject.getModel().name : ""); | ||||
|         glyphEl.text(domainObject ? getGlyph(domainObject) : ""); | ||||
|  | ||||
|         if (domainObject && isLink(domainObject)) { | ||||
|             iconEl.addClass('l-icon-link'); | ||||
|         } else { | ||||
|             iconEl.removeClass('l-icon-link'); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TreeLabelView.prototype.model = function (domainObject) { | ||||
|         if (this.unlisten) { | ||||
|             this.unlisten(); | ||||
|             delete this.unlisten; | ||||
|         } | ||||
|  | ||||
|         if (this.activeGestures) { | ||||
|             this.activeGestures.destroy(); | ||||
|             delete this.activeGestures; | ||||
|         } | ||||
|  | ||||
|         this.updateView(domainObject); | ||||
|  | ||||
|         if (domainObject) { | ||||
|             this.unlisten = domainObject.getCapability('mutation') | ||||
|                 .listen(this.updateView.bind(this, domainObject)); | ||||
|  | ||||
|             this.activeGestures = this.gestureService.attachGestures( | ||||
|                 this.elements(), | ||||
|                 domainObject, | ||||
|                 [ 'info', 'menu', 'drag' ] | ||||
|             ); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TreeLabelView.prototype.elements = function () { | ||||
|         return this.el; | ||||
|     }; | ||||
|  | ||||
|     return TreeLabelView; | ||||
| }); | ||||
							
								
								
									
										157
									
								
								platform/commonUI/general/src/ui/TreeNodeView.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								platform/commonUI/general/src/ui/TreeNodeView.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     'zepto', | ||||
|     'text!../../res/templates/tree/node.html', | ||||
|     './ToggleView', | ||||
|     './TreeLabelView' | ||||
| ], function ($, nodeTemplate, ToggleView, TreeLabelView) { | ||||
|     'use strict'; | ||||
|  | ||||
|     function TreeNodeView(gestureService, subtreeFactory, selectFn) { | ||||
|         this.li = $('<li>'); | ||||
|  | ||||
|         this.statusClasses = []; | ||||
|  | ||||
|         this.toggleView = new ToggleView(false); | ||||
|         this.toggleView.observe(function (state) { | ||||
|             if (state) { | ||||
|                 if (!this.subtreeView) { | ||||
|                     this.subtreeView = subtreeFactory(); | ||||
|                     this.subtreeView.model(this.activeObject); | ||||
|                     this.li.find('.tree-item-subtree').eq(0) | ||||
|                         .append($(this.subtreeView.elements())); | ||||
|                 } | ||||
|                 $(this.subtreeView.elements()).removeClass('hidden'); | ||||
|             } else if (this.subtreeView) { | ||||
|                 $(this.subtreeView.elements()).addClass('hidden'); | ||||
|             } | ||||
|         }.bind(this)); | ||||
|  | ||||
|         this.labelView = new TreeLabelView(gestureService); | ||||
|  | ||||
|         $(this.labelView.elements()).on('click', function () { | ||||
|             selectFn(this.activeObject); | ||||
|         }.bind(this)); | ||||
|  | ||||
|         this.li.append($(nodeTemplate)); | ||||
|         this.li.find('span').eq(0) | ||||
|             .append($(this.toggleView.elements())) | ||||
|             .append($(this.labelView.elements())); | ||||
|  | ||||
|         this.model(undefined); | ||||
|     } | ||||
|  | ||||
|     TreeNodeView.prototype.updateStatusClasses = function (statuses) { | ||||
|         this.statusClasses.forEach(function (statusClass) { | ||||
|             this.li.removeClass(statusClass); | ||||
|         }.bind(this)); | ||||
|  | ||||
|         this.statusClasses = statuses.map(function (status) { | ||||
|             return 's-status-' + status; | ||||
|         }); | ||||
|  | ||||
|         this.statusClasses.forEach(function (statusClass) { | ||||
|             this.li.addClass(statusClass); | ||||
|         }.bind(this)); | ||||
|     }; | ||||
|  | ||||
|     TreeNodeView.prototype.model = function (domainObject) { | ||||
|         if (this.unlisten) { | ||||
|             this.unlisten(); | ||||
|         } | ||||
|  | ||||
|         this.activeObject = domainObject; | ||||
|  | ||||
|         if (domainObject && domainObject.hasCapability('composition')) { | ||||
|             $(this.toggleView.elements()).addClass('has-children'); | ||||
|         } else { | ||||
|             $(this.toggleView.elements()).removeClass('has-children'); | ||||
|         } | ||||
|  | ||||
|         if (domainObject && domainObject.hasCapability('status')) { | ||||
|             this.unlisten = domainObject.getCapability('status') | ||||
|                 .listen(this.updateStatusClasses.bind(this)); | ||||
|             this.updateStatusClasses( | ||||
|                 domainObject.getCapability('status').list() | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         this.labelView.model(domainObject); | ||||
|         if (this.subtreeView) { | ||||
|             this.subtreeView.model(domainObject); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     function getIdPath(domainObject) { | ||||
|         var context = domainObject && domainObject.getCapability('context'); | ||||
|  | ||||
|         function getId(domainObject) { | ||||
|             return domainObject.getId(); | ||||
|         } | ||||
|  | ||||
|         return context ? context.getPath().map(getId) : []; | ||||
|     } | ||||
|  | ||||
|     TreeNodeView.prototype.value = function (domainObject) { | ||||
|         var activeIdPath = getIdPath(this.activeObject), | ||||
|             selectedIdPath = getIdPath(domainObject); | ||||
|  | ||||
|         if (this.onSelectionPath) { | ||||
|             this.li.find('.tree-item').eq(0).removeClass('selected'); | ||||
|             if (this.subtreeView) { | ||||
|                 this.subtreeView.value(undefined); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.onSelectionPath = | ||||
|             !!domainObject && | ||||
|             !!this.activeObject && | ||||
|             (activeIdPath.length <= selectedIdPath.length) && | ||||
|                 activeIdPath.every(function (id, index) { | ||||
|                     return selectedIdPath[index] === id; | ||||
|                 }); | ||||
|  | ||||
|         if (this.onSelectionPath) { | ||||
|             if (activeIdPath.length === selectedIdPath.length) { | ||||
|                 this.li.find('.tree-item').eq(0).addClass('selected'); | ||||
|             } else { | ||||
|                 // Expand to reveal the selection | ||||
|                 this.toggleView.value(true); | ||||
|                 this.subtreeView.value(domainObject); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @returns {HTMLElement[]} | ||||
|      */ | ||||
|     TreeNodeView.prototype.elements = function () { | ||||
|         return this.li; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     return TreeNodeView; | ||||
| }); | ||||
							
								
								
									
										141
									
								
								platform/commonUI/general/src/ui/TreeView.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								platform/commonUI/general/src/ui/TreeView.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     'zepto', | ||||
|     './TreeNodeView', | ||||
|     'text!../../res/templates/tree/wait-node.html' | ||||
| ], function ($, TreeNodeView, spinnerTemplate) { | ||||
|     'use strict'; | ||||
|  | ||||
|     function TreeView(gestureService, selectFn) { | ||||
|         this.ul = $('<ul class="tree"></ul>'); | ||||
|         this.nodeViews = []; | ||||
|         this.callbacks = []; | ||||
|         this.selectFn = selectFn || this.value.bind(this); | ||||
|         this.gestureService = gestureService; | ||||
|         this.pending = false; | ||||
|     } | ||||
|  | ||||
|     TreeView.prototype.newTreeView = function () { | ||||
|         return new TreeView(this.gestureService, this.selectFn); | ||||
|     }; | ||||
|  | ||||
|     TreeView.prototype.setSize = function (sz) { | ||||
|         var nodeView; | ||||
|  | ||||
|         while (this.nodeViews.length < sz) { | ||||
|             nodeView = new TreeNodeView( | ||||
|                 this.gestureService, | ||||
|                 this.newTreeView.bind(this), | ||||
|                 this.selectFn | ||||
|             ); | ||||
|             this.nodeViews.push(nodeView); | ||||
|             this.ul.append($(nodeView.elements())); | ||||
|         } | ||||
|  | ||||
|         while (this.nodeViews.length > sz) { | ||||
|             nodeView = this.nodeViews.pop(); | ||||
|             $(nodeView.elements()).remove(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TreeView.prototype.loadComposition = function () { | ||||
|         var self = this, | ||||
|             domainObject = this.activeObject; | ||||
|  | ||||
|         function addNode(domainObject, index) { | ||||
|             self.nodeViews[index].model(domainObject); | ||||
|         } | ||||
|  | ||||
|         function addNodes(domainObjects) { | ||||
|             if (self.pending) { | ||||
|                 self.pending = false; | ||||
|                 self.nodeViews = []; | ||||
|                 self.ul.empty(); | ||||
|             } | ||||
|  | ||||
|             if (domainObject === self.activeObject) { | ||||
|                 self.setSize(domainObjects.length); | ||||
|                 domainObjects.forEach(addNode); | ||||
|                 self.updateNodeViewSelection(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         domainObject.useCapability('composition') | ||||
|             .then(addNodes); | ||||
|     }; | ||||
|  | ||||
|     TreeView.prototype.model = function (domainObject) { | ||||
|         if (this.unlisten) { | ||||
|             this.unlisten(); | ||||
|         } | ||||
|  | ||||
|         this.activeObject = domainObject; | ||||
|         this.ul.empty(); | ||||
|  | ||||
|         if (domainObject && domainObject.hasCapability('composition')) { | ||||
|             this.pending = true; | ||||
|             this.ul.append($(spinnerTemplate)); | ||||
|             this.unlisten = domainObject.getCapability('mutation') | ||||
|                 .listen(this.loadComposition.bind(this)); | ||||
|             this.loadComposition(domainObject); | ||||
|         } else { | ||||
|             this.setSize(0); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TreeView.prototype.updateNodeViewSelection = function () { | ||||
|         this.nodeViews.forEach(function (nodeView) { | ||||
|             nodeView.value(this.selectedObject); | ||||
|         }.bind(this)); | ||||
|     }; | ||||
|  | ||||
|     TreeView.prototype.value = function (domainObject) { | ||||
|         this.selectedObject = domainObject; | ||||
|         this.updateNodeViewSelection(); | ||||
|         this.callbacks.forEach(function (callback) { | ||||
|             callback(domainObject); | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     TreeView.prototype.observe = function (callback) { | ||||
|         this.callbacks.push(callback); | ||||
|         return function () { | ||||
|             this.callbacks = this.callbacks.filter(function (c) { | ||||
|                 return c !== callback; | ||||
|             }); | ||||
|         }.bind(this); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @returns {HTMLElement[]} | ||||
|      */ | ||||
|     TreeView.prototype.elements = function () { | ||||
|         return this.ul; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     return TreeView; | ||||
| }); | ||||
| @@ -94,18 +94,18 @@ define( | ||||
|             it("exposes start time validator", function () { | ||||
|                 var testValue = 42000000; | ||||
|                 mockScope.formModel = { end: testValue }; | ||||
|                 expect(mockScope.validateStart(testValue + 1)) | ||||
|                 expect(controller.validateStart(testValue + 1)) | ||||
|                     .toBe(false); | ||||
|                 expect(mockScope.validateStart(testValue - 60 * 60 * 1000 - 1)) | ||||
|                 expect(controller.validateStart(testValue - 60 * 60 * 1000 - 1)) | ||||
|                     .toBe(true); | ||||
|             }); | ||||
|  | ||||
|             it("exposes end time validator", function () { | ||||
|                 var testValue = 42000000; | ||||
|                 mockScope.formModel = { start:  testValue }; | ||||
|                 expect(mockScope.validateEnd(testValue - 1)) | ||||
|                 expect(controller.validateEnd(testValue - 1)) | ||||
|                     .toBe(false); | ||||
|                 expect(mockScope.validateEnd(testValue + 60 * 60 * 1000 + 1)) | ||||
|                 expect(controller.validateEnd(testValue + 60 * 60 * 1000 + 1)) | ||||
|                     .toBe(true); | ||||
|             }); | ||||
|  | ||||
| @@ -119,25 +119,87 @@ define( | ||||
|                         start: DAY * 10000, | ||||
|                         end: DAY * 11000 | ||||
|                     }; | ||||
|                     // These watches may not exist, but Angular would fire | ||||
|                     // them if they did. | ||||
|                 }); | ||||
|  | ||||
|                 it('updates all changed bounds when requested', function () { | ||||
|                     fireWatchCollection("formModel", mockScope.formModel); | ||||
|                     fireWatch("formModel.start", mockScope.formModel.start); | ||||
|                     fireWatch("formModel.end", mockScope.formModel.end); | ||||
|                 }); | ||||
|  | ||||
|                 it("does not immediately make changes to the model", function () { | ||||
|                     expect(mockScope.ngModel.outer.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|  | ||||
|                     controller.updateBoundsFromForm(); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.start) | ||||
|                         .toEqual(mockScope.formModel.start); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .toEqual(mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.end) | ||||
|                         .toEqual(mockScope.formModel.end); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .toEqual(mockScope.formModel.end); | ||||
|                 }); | ||||
|  | ||||
|                 it('updates changed start bound when requested', function () { | ||||
|                     fireWatchCollection("formModel", mockScope.formModel); | ||||
|                     fireWatch("formModel.start", mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|  | ||||
|                     controller.updateBoundsFromForm(); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.start) | ||||
|                         .toEqual(mockScope.formModel.start); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .toEqual(mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|                 }); | ||||
|  | ||||
|                 it("updates model bounds on request", function () { | ||||
|                     mockScope.updateBoundsFromForm(); | ||||
|                 it('updates changed end bound when requested', function () { | ||||
|                     fireWatchCollection("formModel", mockScope.formModel); | ||||
|                     fireWatch("formModel.end", mockScope.formModel.end); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.start) | ||||
|                         .toEqual(mockScope.formModel.start); | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .not.toEqual(mockScope.formModel.end); | ||||
|  | ||||
|                     controller.updateBoundsFromForm(); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .not.toEqual(mockScope.formModel.start); | ||||
|  | ||||
|                     expect(mockScope.ngModel.outer.end) | ||||
|                         .toEqual(mockScope.formModel.end); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .toEqual(mockScope.formModel.end); | ||||
|                 }); | ||||
|             }); | ||||
| @@ -160,27 +222,27 @@ define( | ||||
|                 }); | ||||
|  | ||||
|                 it("updates the start time for left drags", function () { | ||||
|                     mockScope.startLeftDrag(); | ||||
|                     mockScope.leftDrag(250); | ||||
|                     controller.startLeftDrag(); | ||||
|                     controller.leftDrag(250); | ||||
|                     expect(mockScope.ngModel.inner.start) | ||||
|                         .toEqual(DAY * 1000 + HOUR * 9); | ||||
|                 }); | ||||
|  | ||||
|                 it("updates the end time for right drags", function () { | ||||
|                     mockScope.startRightDrag(); | ||||
|                     mockScope.rightDrag(-250); | ||||
|                     controller.startRightDrag(); | ||||
|                     controller.rightDrag(-250); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .toEqual(DAY * 1000 + HOUR * 15); | ||||
|                 }); | ||||
|  | ||||
|                 it("updates both start and end for middle drags", function () { | ||||
|                     mockScope.startMiddleDrag(); | ||||
|                     mockScope.middleDrag(-125); | ||||
|                     controller.startMiddleDrag(); | ||||
|                     controller.middleDrag(-125); | ||||
|                     expect(mockScope.ngModel.inner).toEqual({ | ||||
|                         start: DAY * 1000, | ||||
|                         end: DAY * 1000 + HOUR * 18 | ||||
|                     }); | ||||
|                     mockScope.middleDrag(250); | ||||
|                     controller.middleDrag(250); | ||||
|                     expect(mockScope.ngModel.inner).toEqual({ | ||||
|                         start: DAY * 1000 + HOUR * 6, | ||||
|                         end: DAY * 1001 | ||||
| @@ -188,8 +250,8 @@ define( | ||||
|                 }); | ||||
|  | ||||
|                 it("enforces a minimum inner span", function () { | ||||
|                     mockScope.startRightDrag(); | ||||
|                     mockScope.rightDrag(-9999999); | ||||
|                     controller.startRightDrag(); | ||||
|                     controller.rightDrag(-9999999); | ||||
|                     expect(mockScope.ngModel.inner.end) | ||||
|                         .toBeGreaterThan(mockScope.ngModel.inner.start); | ||||
|                 }); | ||||
|   | ||||
							
								
								
									
										95
									
								
								platform/commonUI/general/test/directives/MCTTreeSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								platform/commonUI/general/test/directives/MCTTreeSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,describe,beforeEach,jasmine,it,expect*/ | ||||
|  | ||||
| define([ | ||||
|     '../../src/directives/MCTTree' | ||||
| ], function (MCTTree) { | ||||
|     describe("The mct-tree directive", function () { | ||||
|         var mockParse, | ||||
|             mockGestureService, | ||||
|             mockExpr, | ||||
|             mctTree; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             mockGestureService = jasmine.createSpyObj( | ||||
|                 'gestureService', | ||||
|                 [ 'attachGestures' ] | ||||
|             ); | ||||
|             mockParse = jasmine.createSpy('$parse'); | ||||
|             mockExpr = jasmine.createSpy('expr'); | ||||
|             mockExpr.assign = jasmine.createSpy('assign'); | ||||
|             mockParse.andReturn(mockExpr); | ||||
|  | ||||
|             mctTree = new MCTTree(mockParse, mockGestureService); | ||||
|         }); | ||||
|  | ||||
|         it("is applicable as an element", function () { | ||||
|             expect(mctTree.restrict).toEqual("E"); | ||||
|         }); | ||||
|  | ||||
|         it("two-way binds to mctObject", function () { | ||||
|             expect(mctTree.scope).toEqual({ mctObject: "=" }); | ||||
|         }); | ||||
|  | ||||
|         describe("link", function () { | ||||
|             var mockScope, | ||||
|                 mockElement, | ||||
|                 testAttrs; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']); | ||||
|                 mockElement = jasmine.createSpyObj('element', ['append']); | ||||
|                 testAttrs = { mctModel: "some-expression" }; | ||||
|                 mockScope.$parent = | ||||
|                     jasmine.createSpyObj('$scope', ['$watch', '$on']); | ||||
|                 mctTree.link(mockScope, mockElement, testAttrs); | ||||
|             }); | ||||
|  | ||||
|             it("populates the mct-tree element", function () { | ||||
|                 expect(mockElement.append).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("watches for mct-model's expression in the parent", function () { | ||||
|                 expect(mockScope.$parent.$watch).toHaveBeenCalledWith( | ||||
|                     testAttrs.mctModel, | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("watches for changes to mct-object", function () { | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
|                     "mctObject", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("listens for the $destroy event", function () { | ||||
|                 expect(mockScope.$on).toHaveBeenCalledWith( | ||||
|                     "$destroy", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
| }); | ||||
							
								
								
									
										313
									
								
								platform/commonUI/general/test/ui/TreeViewSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								platform/commonUI/general/test/ui/TreeViewSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,313 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,describe,beforeEach,jasmine,it,expect*/ | ||||
|  | ||||
| define([ | ||||
|     '../../src/ui/TreeView', | ||||
|     'zepto' | ||||
| ], function (TreeView, $) { | ||||
|     'use strict'; | ||||
|  | ||||
|     describe("TreeView", function () { | ||||
|         var mockGestureService, | ||||
|             mockGestureHandle, | ||||
|             mockDomainObject, | ||||
|             mockMutation, | ||||
|             mockUnlisten, | ||||
|             testCapabilities, | ||||
|             treeView; | ||||
|  | ||||
|         function makeMockDomainObject(id, model, capabilities) { | ||||
|             var mockDomainObject = jasmine.createSpyObj( | ||||
|                 'domainObject-' + id, | ||||
|                 [ | ||||
|                     'getId', | ||||
|                     'getModel', | ||||
|                     'getCapability', | ||||
|                     'hasCapability', | ||||
|                     'useCapability' | ||||
|                 ] | ||||
|             ); | ||||
|             mockDomainObject.getId.andReturn(id); | ||||
|             mockDomainObject.getModel.andReturn(model); | ||||
|             mockDomainObject.hasCapability.andCallFake(function (c) { | ||||
|                 return !!(capabilities[c]); | ||||
|             }); | ||||
|             mockDomainObject.getCapability.andCallFake(function (c) { | ||||
|                 return capabilities[c]; | ||||
|             }); | ||||
|             mockDomainObject.useCapability.andCallFake(function (c) { | ||||
|                 return capabilities[c] && capabilities[c].invoke(); | ||||
|             }); | ||||
|             return mockDomainObject; | ||||
|         } | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             mockGestureService = jasmine.createSpyObj( | ||||
|                 'gestureService', | ||||
|                 [ 'attachGestures' ] | ||||
|             ); | ||||
|  | ||||
|             mockGestureHandle = jasmine.createSpyObj('gestures', ['destroy']); | ||||
|  | ||||
|             mockGestureService.attachGestures.andReturn(mockGestureHandle); | ||||
|  | ||||
|             mockMutation = jasmine.createSpyObj('mutation', ['listen']); | ||||
|             mockUnlisten = jasmine.createSpy('unlisten'); | ||||
|             mockMutation.listen.andReturn(mockUnlisten); | ||||
|  | ||||
|             testCapabilities = { mutation: mockMutation }; | ||||
|  | ||||
|             mockDomainObject = | ||||
|                 makeMockDomainObject('parent', {}, testCapabilities); | ||||
|  | ||||
|             treeView = new TreeView(mockGestureService); | ||||
|         }); | ||||
|  | ||||
|         describe("elements", function () { | ||||
|             var elements; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 elements = treeView.elements(); | ||||
|             }); | ||||
|  | ||||
|             it("is an unordered list", function () { | ||||
|                 expect(elements[0].tagName.toLowerCase()) | ||||
|                     .toEqual('ul'); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("model", function () { | ||||
|             var mockComposition; | ||||
|  | ||||
|             function makeGenericCapabilities() { | ||||
|                 var mockContext = | ||||
|                         jasmine.createSpyObj('context', [ 'getPath' ]), | ||||
|                     mockType = | ||||
|                         jasmine.createSpyObj('type', [ 'getGlyph' ]), | ||||
|                     mockLocation = | ||||
|                         jasmine.createSpyObj('location', [ 'isLink' ]), | ||||
|                     mockMutation = | ||||
|                         jasmine.createSpyObj('mutation', [ 'listen' ]), | ||||
|                     mockStatus = | ||||
|                         jasmine.createSpyObj('status', [ 'listen', 'list' ]); | ||||
|  | ||||
|                 mockStatus.list.andReturn([]); | ||||
|  | ||||
|                 return { | ||||
|                     context: mockContext, | ||||
|                     type: mockType, | ||||
|                     mutation: mockMutation, | ||||
|                     location: mockLocation, | ||||
|                     status: mockStatus | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             function waitForCompositionCallback() { | ||||
|                 var calledBack = false; | ||||
|                 testCapabilities.composition.invoke().then(function (c) { | ||||
|                     calledBack = true; | ||||
|                 }); | ||||
|                 waitsFor(function () { | ||||
|                     return calledBack; | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockComposition = ['a', 'b', 'c'].map(function (id) { | ||||
|                     var testCapabilities = makeGenericCapabilities(), | ||||
|                         mockChild = | ||||
|                             makeMockDomainObject(id, {}, testCapabilities); | ||||
|  | ||||
|                     testCapabilities.context.getPath | ||||
|                         .andReturn([mockDomainObject, mockChild]); | ||||
|  | ||||
|                     return mockChild; | ||||
|                 }); | ||||
|  | ||||
|                 testCapabilities.composition = | ||||
|                     jasmine.createSpyObj('composition', ['invoke']); | ||||
|                 testCapabilities.composition.invoke | ||||
|                     .andReturn(Promise.resolve(mockComposition)); | ||||
|  | ||||
|                 treeView.model(mockDomainObject); | ||||
|                 waitForCompositionCallback(); | ||||
|             }); | ||||
|  | ||||
|             it("adds one node per composition element", function () { | ||||
|                 expect(treeView.elements()[0].childElementCount) | ||||
|                     .toEqual(mockComposition.length); | ||||
|             }); | ||||
|  | ||||
|             it("listens for mutation", function () { | ||||
|                 expect(testCapabilities.mutation.listen) | ||||
|                     .toHaveBeenCalledWith(jasmine.any(Function)); | ||||
|             }); | ||||
|  | ||||
|             describe("when mutation occurs", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockComposition.pop(); | ||||
|                     testCapabilities.mutation.listen | ||||
|                         .mostRecentCall.args[0](mockDomainObject.getModel()); | ||||
|                     waitForCompositionCallback(); | ||||
|                 }); | ||||
|  | ||||
|                 it("continues to show one node per composition element", function () { | ||||
|                     expect(treeView.elements()[0].childElementCount) | ||||
|                         .toEqual(mockComposition.length); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when replaced with a non-compositional domain object", function () { | ||||
|                 beforeEach(function () { | ||||
|                     delete testCapabilities.composition; | ||||
|                     treeView.model(mockDomainObject); | ||||
|                 }); | ||||
|  | ||||
|                 it("stops listening for mutation", function () { | ||||
|                     expect(mockUnlisten).toHaveBeenCalled(); | ||||
|                 }); | ||||
|  | ||||
|                 it("removes all tree nodes", function () { | ||||
|                     expect(treeView.elements()[0].childElementCount) | ||||
|                         .toEqual(0); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when selection state changes", function () { | ||||
|                 var selectionIndex = 1; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     treeView.value(mockComposition[selectionIndex]); | ||||
|                 }); | ||||
|  | ||||
|                 it("communicates selection state to an appropriate node", function () { | ||||
|                     var selected = $(treeView.elements()[0]).find('.selected'); | ||||
|                     expect(selected.length).toEqual(1); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when a context-less object is selected", function () { | ||||
|                 beforeEach(function () { | ||||
|                     var testCapabilities = makeGenericCapabilities(), | ||||
|                         mockDomainObject = | ||||
|                             makeMockDomainObject('xyz', {}, testCapabilities); | ||||
|                     delete testCapabilities.context; | ||||
|                     treeView.value(mockDomainObject); | ||||
|                 }); | ||||
|  | ||||
|                 it("clears all selection state", function () { | ||||
|                     var selected = $(treeView.elements()[0]).find('.selected'); | ||||
|                     expect(selected.length).toEqual(0); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when children contain children", function () { | ||||
|                 beforeEach(function () { | ||||
|                     var newCapabilities = makeGenericCapabilities(), | ||||
|                         gcCapabilities = makeGenericCapabilities(), | ||||
|                         mockNewChild = | ||||
|                             makeMockDomainObject('d', {}, newCapabilities), | ||||
|                         mockGrandchild = | ||||
|                             makeMockDomainObject('gc', {}, gcCapabilities), | ||||
|                         calledBackInner = false; | ||||
|  | ||||
|                     newCapabilities.composition = | ||||
|                         jasmine.createSpyObj('composition', [ 'invoke' ]); | ||||
|                     newCapabilities.composition.invoke | ||||
|                         .andReturn(Promise.resolve([mockGrandchild])); | ||||
|                     mockComposition.push(mockNewChild); | ||||
|  | ||||
|                     newCapabilities.context.getPath.andReturn([ | ||||
|                         mockDomainObject, | ||||
|                         mockNewChild | ||||
|                     ]); | ||||
|                     gcCapabilities.context.getPath.andReturn([ | ||||
|                         mockDomainObject, | ||||
|                         mockNewChild, | ||||
|                         mockGrandchild | ||||
|                     ]); | ||||
|  | ||||
|                     testCapabilities.mutation.listen | ||||
|                         .mostRecentCall.args[0](mockDomainObject); | ||||
|                     waitForCompositionCallback(); | ||||
|                     runs(function () { | ||||
|                         // Select the innermost object to force expansion, | ||||
|                         // such that we can verify the subtree is present. | ||||
|                         treeView.value(mockGrandchild); | ||||
|                         newCapabilities.composition.invoke().then(function () { | ||||
|                             calledBackInner = true; | ||||
|                         }); | ||||
|                     }); | ||||
|                     waitsFor(function () { | ||||
|                         return calledBackInner; | ||||
|                     }); | ||||
|                 }); | ||||
|  | ||||
|                 it("creates inner trees", function () { | ||||
|                     expect($(treeView.elements()[0]).find('ul').length) | ||||
|                         .toEqual(1); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when status changes", function () { | ||||
|                 var testStatuses; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     var mockStatus = mockComposition[1].getCapability('status'); | ||||
|  | ||||
|                     testStatuses = [ 'foo' ]; | ||||
|  | ||||
|                     mockStatus.list.andReturn(testStatuses); | ||||
|                     mockStatus.listen.mostRecentCall.args[0](testStatuses); | ||||
|                 }); | ||||
|  | ||||
|                 it("reflects the status change in the tree", function () { | ||||
|                     expect($(treeView.elements()).find('.s-status-foo').length) | ||||
|                         .toEqual(1); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("observe", function () { | ||||
|             var mockCallback, | ||||
|                 unobserve; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockCallback = jasmine.createSpy('callback'); | ||||
|                 unobserve = treeView.observe(mockCallback); | ||||
|             }); | ||||
|  | ||||
|             it("notifies listeners when value is changed", function () { | ||||
|                 treeView.value(mockDomainObject); | ||||
|                 expect(mockCallback).toHaveBeenCalledWith(mockDomainObject); | ||||
|             }); | ||||
|  | ||||
|             it("does not notify listeners when deactivated", function () { | ||||
|                 unobserve(); | ||||
|                 treeView.value(mockDomainObject); | ||||
|                 expect(mockCallback).not.toHaveBeenCalled(); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
| }); | ||||
| @@ -85,7 +85,6 @@ define( | ||||
|                 popup.goesUp() ? 'arw-btm' : 'arw-top', | ||||
|                 popup.goesLeft() ? 'arw-right' : 'arw-left' | ||||
|             ].join(' '); | ||||
|             scope.bubbleLayout = 'arw-top arw-left'; | ||||
|  | ||||
|             // Create the info bubble, now that we know how to | ||||
|             // point the arrow... | ||||
|   | ||||
| @@ -109,6 +109,35 @@ define( | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             [ false, true ].forEach(function (goesLeft) { | ||||
|                 [ false, true].forEach(function (goesUp) { | ||||
|                     var vertical = goesUp ? "up" : "down", | ||||
|                         horizontal = goesLeft ? "left" : "right", | ||||
|                         location = [ vertical, horizontal].join('-'); | ||||
|                     describe("when bubble goes " + location, function () { | ||||
|                         var expectedLocation = [ | ||||
|                                 goesUp ? "bottom" : "top", | ||||
|                                 goesLeft ? "right" : "left" | ||||
|                             ].join('-'); | ||||
|  | ||||
|                         beforeEach(function () { | ||||
|                             mockPopup.goesUp.andReturn(goesUp); | ||||
|                             mockPopup.goesDown.andReturn(!goesUp); | ||||
|                             mockPopup.goesLeft.andReturn(goesLeft); | ||||
|                             mockPopup.goesRight.andReturn(!goesLeft); | ||||
|                             service.display('', '', {}, [ 10, 10 ]); | ||||
|                         }); | ||||
|  | ||||
|                         it("positions the arrow in the " + expectedLocation, function () { | ||||
|                             expect(mockScope.bubbleLayout).toEqual([ | ||||
|                                 goesUp ? "arw-btm" : "arw-top", | ||||
|                                 goesLeft ? "arw-right" : "arw-left" | ||||
|                             ].join(' ')); | ||||
|                         }); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -27,6 +27,7 @@ $colorBtnIcon: $colorKey; | ||||
| $colorInvokeMenu: #fff; | ||||
| $contrastInvokeMenuPercent: 20%; | ||||
| $shdwBtns: rgba(black, 0.2) 0 1px 2px; | ||||
| $shdwBtnsOverlay: rgba(black, 0.5) 0 1px 5px; | ||||
| $sliderColorBase: $colorKey; | ||||
| $sliderColorRangeHolder: rgba(black, 0.1); | ||||
| $sliderColorRange: rgba($sliderColorBase, 0.3); | ||||
| @@ -76,6 +77,7 @@ $colorInputBg: rgba(#000, 0.1); | ||||
| $colorInputFg: pullForward($colorBodyFg, 20%); | ||||
| $colorFormText: rgba(#fff, 0.5); | ||||
| $colorInputIcon: pushBack($colorBodyFg, 15%); | ||||
| $colorFieldHint: pullForward($colorBodyFg, 20%); | ||||
|  | ||||
| // Inspector | ||||
| $colorInspectorBg: pullForward($colorBodyBg, 3%); | ||||
| @@ -124,6 +126,7 @@ $colorOvrBg: pullForward($colorBodyBg, 10%); | ||||
| $colorOvrFg: pullForward($colorBodyFg, 30%); | ||||
| $colorOvrBtnBg: pullForward($colorOvrBg, 20%); | ||||
| $colorOvrBtnFg: #fff; | ||||
| $colorFieldHintOverlay: pullForward($colorOvrBg, 30%); | ||||
|  | ||||
| // Items | ||||
| $colorItemBg: lighten($colorBodyBg, 5%); | ||||
| @@ -173,6 +176,8 @@ $scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px; | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.4); | ||||
| $scrollbarThumbColor: lighten($colorBodyBg, 10%); | ||||
| $scrollbarThumbColorHov: lighten($scrollbarThumbColor, 2%); | ||||
| $scrollbarThumbColorOverlay: lighten($colorOvrBg, 10%); | ||||
| $scrollbarThumbColorOverlayHov: lighten($scrollbarThumbColorOverlay, 2%); | ||||
|  | ||||
| // Splitter | ||||
| $splitterD: 25px; // splitterD and HandleD should both be odd, or even | ||||
|   | ||||
| @@ -27,6 +27,7 @@ $colorBtnIcon: #eee; | ||||
| $colorInvokeMenu: #000; | ||||
| $contrastInvokeMenuPercent: 40%; | ||||
| $shdwBtns: none; | ||||
| $shdwBtnsOverlay: none; | ||||
| $sliderColorBase: $colorKey; | ||||
| $sliderColorRangeHolder: rgba(black, 0.07); | ||||
| $sliderColorRange: rgba($sliderColorBase, 0.2); | ||||
| @@ -76,6 +77,7 @@ $colorInputBg: $colorGenBg; | ||||
| $colorInputFg: $colorBodyFg; | ||||
| $colorFormText: pushBack($colorBodyFg, 10%); | ||||
| $colorInputIcon: pushBack($colorBodyFg, 25%); | ||||
| $colorFieldHint: pullForward($colorBodyFg, 40%); | ||||
|  | ||||
| // Inspector | ||||
| $colorInspectorBg: pullForward($colorBodyBg, 5%); | ||||
| @@ -124,6 +126,7 @@ $colorOvrBg: $colorBodyBg; | ||||
| $colorOvrFg: $colorBodyFg; | ||||
| $colorOvrBtnBg: pullForward($colorOvrBg, 40%); | ||||
| $colorOvrBtnFg: #fff; | ||||
| $colorFieldHintOverlay: pullForward($colorOvrBg, 40%); | ||||
|  | ||||
| // Items | ||||
| $colorItemBg: #ddd; | ||||
| @@ -170,9 +173,11 @@ $shdwItemTreeIcon: none; | ||||
| // Scrollbar | ||||
| $scrollbarTrackSize: 10px; | ||||
| $scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px; | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.1); | ||||
| $scrollbarThumbColor: darken($colorBodyBg, 50%);// | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.2); | ||||
| $scrollbarThumbColor: darken($colorBodyBg, 50%); | ||||
| $scrollbarThumbColorHov: $colorKey; | ||||
| $scrollbarThumbColorOverlay: darken($colorOvrBg, 50%); | ||||
| $scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov; | ||||
|  | ||||
| // Splitter | ||||
| $splitterD: 24px; | ||||
|   | ||||
| @@ -27,6 +27,7 @@ define([ | ||||
|     "./src/models/StaticModelProvider", | ||||
|     "./src/models/RootModelProvider", | ||||
|     "./src/models/ModelAggregator", | ||||
|     "./src/models/ModelCacheService", | ||||
|     "./src/models/PersistedModelProvider", | ||||
|     "./src/models/CachingModelDecorator", | ||||
|     "./src/models/MissingModelDecorator", | ||||
| @@ -58,6 +59,7 @@ define([ | ||||
|     StaticModelProvider, | ||||
|     RootModelProvider, | ||||
|     ModelAggregator, | ||||
|     ModelCacheService, | ||||
|     PersistedModelProvider, | ||||
|     CachingModelDecorator, | ||||
|     MissingModelDecorator, | ||||
| @@ -182,7 +184,10 @@ define([ | ||||
|                 { | ||||
|                     "provides": "modelService", | ||||
|                     "type": "decorator", | ||||
|                     "implementation": CachingModelDecorator | ||||
|                     "implementation": CachingModelDecorator, | ||||
|                     "depends": [ | ||||
|                         "cacheService" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "provides": "modelService", | ||||
| @@ -248,21 +253,22 @@ define([ | ||||
|                             "property": "name", | ||||
|                             "pattern": "\\S+", | ||||
|                             "required": true, | ||||
|                             "cssclass": "l-med" | ||||
|                             "cssclass": "l-input-lg" | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "root", | ||||
|                     "name": "Root", | ||||
|                     "glyph": "F" | ||||
|                     "glyph": "\u0046" | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "folder", | ||||
|                     "name": "Folder", | ||||
|                     "glyph": "F", | ||||
|                     "glyph": "\u0046", | ||||
|                     "features": "creation", | ||||
|                     "description": "Useful for storing and organizing domain objects.", | ||||
|                     "description": "Create folders to organize other objects or links to objects.", | ||||
|                     "priority": 1000, | ||||
|                     "model": { | ||||
|                         "composition": [] | ||||
|                     } | ||||
| @@ -270,11 +276,11 @@ define([ | ||||
|                 { | ||||
|                     "key": "unknown", | ||||
|                     "name": "Unknown Type", | ||||
|                     "glyph": "?" | ||||
|                     "glyph": "\u003f" | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "Unknown Type", | ||||
|                     "glyph": "?" | ||||
|                     "glyph": "\u003f" | ||||
|                 } | ||||
|             ], | ||||
|             "capabilities": [ | ||||
| @@ -319,6 +325,7 @@ define([ | ||||
|                     "key": "persistence", | ||||
|                     "implementation": PersistenceCapability, | ||||
|                     "depends": [ | ||||
|                         "cacheService", | ||||
|                         "persistenceService", | ||||
|                         "identifierService", | ||||
|                         "notificationService", | ||||
| @@ -349,11 +356,16 @@ define([ | ||||
|                     "implementation": InstantiationCapability, | ||||
|                     "depends": [ | ||||
|                         "$injector", | ||||
|                         "identifierService" | ||||
|                         "identifierService", | ||||
|                         "now" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|             "services": [ | ||||
|                 { | ||||
|                     "key": "cacheService", | ||||
|                     "implementation": ModelCacheService | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "now", | ||||
|                     "implementation": Now | ||||
| @@ -384,7 +396,8 @@ define([ | ||||
|                     "implementation": Instantiate, | ||||
|                     "depends": [ | ||||
|                         "capabilityService", | ||||
|                         "identifierService" | ||||
|                         "identifierService", | ||||
|                         "cacheService" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|   | ||||
| @@ -28,7 +28,7 @@ define( | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
|         var DISALLOWED_ACTIONS = ["move", "copy", "link", "window", "follow"]; | ||||
|  | ||||
|         /** | ||||
|          * The ActionCapability allows applicable Actions to be retrieved and | ||||
|          * performed for specific domain objects, e.g.: | ||||
| @@ -54,37 +54,22 @@ define( | ||||
|             this.domainObject = domainObject; | ||||
|         } | ||||
|  | ||||
|         function isEditable(domainObject){ | ||||
|             return domainObject.getCapability('status').get('editing'); | ||||
|         } | ||||
|  | ||||
|         function hasEditableAncestor(domainObject){ | ||||
|             return domainObject.hasCapability('context') && | ||||
|                 domainObject | ||||
|                     .getCapability('context') | ||||
|                     .getPath() | ||||
|                     .some(function isEditable (ancestor){ | ||||
|                         return ancestor.getCapability('status').get('editing'); | ||||
|                     }); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Retrieve the actions applicable to the domain object in the given | ||||
|          * context. | ||||
|          * Perform an action. This will find and perform the | ||||
|          * first matching action available for the specified | ||||
|          * context or key. | ||||
|          * | ||||
|          * @param {ActionContext|string} context the context in which | ||||
|          *       to assess the applicability of the available actions; this is | ||||
|          *       passed along to the action service to match against available | ||||
|          *       to perform the action; this is passed along to | ||||
|          *       the action service to match against available | ||||
|          *       actions. The "domainObject" field will automatically | ||||
|          *       be populated with the domain object that exposed | ||||
|          *       this capability. If given as a string, this will | ||||
|          *       be taken as the "key" field to match against | ||||
|          *       specific actions. | ||||
|          * | ||||
|          *       Additionally, this function will limit the actions | ||||
|          *       available for an object in Edit Mode | ||||
|          * @returns {Array<Action>} The actions applicable to this domain | ||||
|          * object in the given context | ||||
|          * @returns {Promise} the result of the action that was | ||||
|          *       performed, or undefined if no matching action | ||||
|          *       was found. | ||||
|          * @memberof platform/core.ActionCapability# | ||||
|          */ | ||||
|         ActionCapability.prototype.getActions = function (context) { | ||||
| @@ -93,19 +78,11 @@ define( | ||||
|             // but additionally adds a domainObject field. | ||||
|             var baseContext = typeof context === 'string' ? | ||||
|                         { key: context } : (context || {}), | ||||
|                 actionContext = Object.create(baseContext), | ||||
|                 actions; | ||||
|                 actionContext = Object.create(baseContext); | ||||
|  | ||||
|             actionContext.domainObject = this.domainObject; | ||||
|  | ||||
|             actions = this.actionService.getActions(actionContext) || []; | ||||
|             if (isEditable(this.domainObject) || hasEditableAncestor(this.domainObject)){ | ||||
|                 return actions.filter(function(action){ | ||||
|                     return DISALLOWED_ACTIONS.indexOf(action.getMetadata().key) === -1; | ||||
|                 }); | ||||
|             } else { | ||||
|                 return actions; | ||||
|             } | ||||
|             return this.actionService.getActions(actionContext); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -35,10 +35,16 @@ define( | ||||
|          * @param $injector Angular's `$injector` | ||||
|          * @implements {Capability} | ||||
|          */ | ||||
|         function InstantiationCapability($injector, identifierService, domainObject) { | ||||
|         function InstantiationCapability( | ||||
|             $injector, | ||||
|             identifierService, | ||||
|             now, | ||||
|             domainObject | ||||
|         ) { | ||||
|             this.$injector = $injector; | ||||
|             this.identifierService = identifierService; | ||||
|             this.domainObject = domainObject; | ||||
|             this.now = now; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
| @@ -57,6 +63,8 @@ define( | ||||
|                 space = parsedId.getDefinedSpace(), | ||||
|                 id = this.identifierService.generate(space); | ||||
|  | ||||
|             model.modified = this.now(); | ||||
|  | ||||
|             // Lazily initialize; instantiate depends on capabilityService, | ||||
|             // which depends on all capabilities, including this one. | ||||
|             this.instantiateFn = this.instantiateFn || | ||||
|   | ||||
| @@ -46,6 +46,7 @@ define( | ||||
|          * @implements {Capability} | ||||
|          */ | ||||
|         function PersistenceCapability( | ||||
|             cacheService, | ||||
|             persistenceService, | ||||
|             identifierService, | ||||
|             notificationService, | ||||
| @@ -56,6 +57,7 @@ define( | ||||
|             this.modified = domainObject.getModel().modified; | ||||
|  | ||||
|             this.domainObject = domainObject; | ||||
|             this.cacheService = cacheService; | ||||
|             this.identifierService = identifierService; | ||||
|             this.persistenceService = persistenceService; | ||||
|             this.notificationService = notificationService; | ||||
| @@ -130,6 +132,7 @@ define( | ||||
|                 domainObject = this.domainObject, | ||||
|                 model = domainObject.getModel(), | ||||
|                 modified = model.modified, | ||||
|                 cacheService = this.cacheService, | ||||
|                 persistenceService = this.persistenceService, | ||||
|                 persistenceFn = model.persisted !== undefined ? | ||||
|                     this.persistenceService.updateObject : | ||||
|   | ||||
| @@ -35,9 +35,8 @@ define( | ||||
|          * @param {ModelService} modelService this service to decorate | ||||
|          * @implements {ModelService} | ||||
|          */ | ||||
|         function CachingModelDecorator(modelService) { | ||||
|             this.cache = {}; | ||||
|             this.cached = {}; | ||||
|         function CachingModelDecorator(cacheService, modelService) { | ||||
|             this.cacheService = cacheService; | ||||
|             this.modelService = modelService; | ||||
|         } | ||||
|  | ||||
| @@ -51,17 +50,16 @@ define( | ||||
|         } | ||||
|  | ||||
|         CachingModelDecorator.prototype.getModels = function (ids) { | ||||
|             var cache = this.cache, | ||||
|                 cached = this.cached, | ||||
|             var cacheService = this.cacheService, | ||||
|                 neededIds = ids.filter(function notCached(id) { | ||||
|                     return !cached[id]; | ||||
|                     return !cacheService.has(id); | ||||
|                 }); | ||||
|  | ||||
|             // Update the cached instance of a model to a new value. | ||||
|             // We update in-place to ensure there is only ever one instance | ||||
|             // of any given model exposed by the modelService as a whole. | ||||
|             function updateModel(id, model) { | ||||
|                 var oldModel = cache[id]; | ||||
|                 var oldModel = cacheService.get(id); | ||||
|  | ||||
|                 // Same object instance is a possibility, so don't copy | ||||
|                 if (oldModel === model) { | ||||
| @@ -71,7 +69,7 @@ define( | ||||
|                 // If we'd previously cached an undefined value, or are now | ||||
|                 // seeing undefined, replace the item in the cache entirely. | ||||
|                 if (oldModel === undefined || model === undefined) { | ||||
|                     cache[id] = model; | ||||
|                     cacheService.put(id, model); | ||||
|                     return model; | ||||
|                 } | ||||
|  | ||||
| @@ -91,15 +89,15 @@ define( | ||||
|             // Store the provided models in our cache | ||||
|             function cacheAll(models) { | ||||
|                 Object.keys(models).forEach(function (id) { | ||||
|                     cache[id] = cached[id] ? | ||||
|                     var model = cacheService.has(id) ? | ||||
|                         updateModel(id, models[id]) : models[id]; | ||||
|                     cached[id] = true; | ||||
|                     cacheService.put(id, model); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // Expose the cache (for promise chaining) | ||||
|             function giveCache() { | ||||
|                 return cache; | ||||
|                 return cacheService.all(); | ||||
|             } | ||||
|  | ||||
|             // Look up if we have unknown IDs | ||||
| @@ -110,7 +108,7 @@ define( | ||||
|             } | ||||
|  | ||||
|             // Otherwise, just expose the cache directly | ||||
|             return fastPromise(cache); | ||||
|             return fastPromise(cacheService.all()); | ||||
|         }; | ||||
|  | ||||
|         return CachingModelDecorator; | ||||
|   | ||||
							
								
								
									
										83
									
								
								platform/core/src/models/ModelCacheService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								platform/core/src/models/ModelCacheService.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([], function () { | ||||
|     'use strict'; | ||||
|  | ||||
|     /** | ||||
|      * Provides a cache for domain object models which exist in memory, | ||||
|      * but may or may not exist in backing persistene stores. | ||||
|      * @constructor | ||||
|      * @memberof platform/core | ||||
|      */ | ||||
|     function ModelCacheService() { | ||||
|         this.cache = {}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Put a domain object model in the cache. | ||||
|      * @param {string} id the domain object's identifier | ||||
|      * @param {object} model the domain object's model | ||||
|      */ | ||||
|     ModelCacheService.prototype.put = function (id, model) { | ||||
|         this.cache[id] = model; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Retrieve a domain object model from the cache. | ||||
|      * @param {string} id the domain object's identifier | ||||
|      * @returns {object} the domain object's model | ||||
|      */ | ||||
|     ModelCacheService.prototype.get = function (id) { | ||||
|         return this.cache[id]; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Check if a domain object model is in the cache. | ||||
|      * @param {string} id the domain object's identifier | ||||
|      * @returns {boolean} true if present; false if not | ||||
|      */ | ||||
|     ModelCacheService.prototype.has = function (id) { | ||||
|         return this.cache.hasOwnProperty(id); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Remove a domain object model from the cache. | ||||
|      * @param {string} id the domain object's identifier | ||||
|      */ | ||||
|     ModelCacheService.prototype.remove = function (id) { | ||||
|         delete this.cache[id]; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Retrieve all cached domain object models. These are given | ||||
|      * as an object containing key-value pairs, where keys are | ||||
|      * domain object identifiers and values are domain object models. | ||||
|      * @returns {object} all domain object models | ||||
|      */ | ||||
|     ModelCacheService.prototype.all = function () { | ||||
|         return this.cache; | ||||
|     }; | ||||
|  | ||||
|     return ModelCacheService; | ||||
| }); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user