Compare commits
	
		
			230 Commits
		
	
	
		
			metrics-73
			...
			v0.10.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d385e55e76 | ||
|   | 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 | ||
|   | 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 | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
|     "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" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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": { | ||||
| @@ -126,7 +127,7 @@ define([ | ||||
|                         { | ||||
|                             "name": "Period", | ||||
|                             "control": "textfield", | ||||
|                             "cssclass": "l-small l-numeric", | ||||
|                             "cssclass": "l-input-sm l-numeric", | ||||
|                             "key": "period", | ||||
|                             "required": true, | ||||
|                             "property": [ | ||||
|   | ||||
| @@ -34,7 +34,8 @@ define( | ||||
|          * @constructor | ||||
|          */ | ||||
|         function SinewaveTelemetryProvider($q, $timeout) { | ||||
|             var subscriptions = []; | ||||
|             var subscriptions = [], | ||||
|                 generating = false; | ||||
|  | ||||
|             // | ||||
|             function matchesSource(request) { | ||||
| @@ -75,10 +76,13 @@ define( | ||||
|             } | ||||
|  | ||||
|             function startGenerating() { | ||||
|                 generating = true; | ||||
|                 $timeout(function () { | ||||
|                     handleSubscriptions(); | ||||
|                     if (subscriptions.length > 0) { | ||||
|                     if (generating && subscriptions.length > 0) { | ||||
|                         startGenerating(); | ||||
|                     } else { | ||||
|                         generating = false; | ||||
|                     } | ||||
|                 }, 1000); | ||||
|             } | ||||
| @@ -97,7 +101,7 @@ define( | ||||
|  | ||||
|                 subscriptions.push(subscription); | ||||
|  | ||||
|                 if (subscriptions.length === 1) { | ||||
|                 if (!generating) { | ||||
|                     startGenerating(); | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
							
								
								
									
										8
									
								
								main.js
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								main.js
									
									
									
									
									
								
							| @@ -33,7 +33,8 @@ 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" | ||||
|     }, | ||||
|     "shim": { | ||||
|         "angular": { | ||||
| @@ -44,6 +45,9 @@ requirejs.config({ | ||||
|         }, | ||||
|         "moment-duration-format": { | ||||
|             "deps": [ "moment" ] | ||||
|         }, | ||||
|         "zepto": { | ||||
|             "exports": "Zepto" | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| @@ -70,12 +74,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', | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "openmctweb", | ||||
|   "version": "0.9.3-SNAPSHOT", | ||||
|   "description": "The Open MCT Web core platform", | ||||
|   "version": "0.10.0", | ||||
|   "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; | ||||
|     } | ||||
| ); | ||||
| @@ -19,37 +19,34 @@ | ||||
|  * 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"; | ||||
| 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() { | ||||
|     /** | ||||
|      * 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')); | ||||
|         } | ||||
| 
 | ||||
|         NameColumn.prototype.getTitle = function () { | ||||
|             return "Name"; | ||||
|         }; | ||||
|         // Like all policies, allow by default.
 | ||||
|         return true; | ||||
|     }; | ||||
| 
 | ||||
|         NameColumn.prototype.getValue = function (domainObject) { | ||||
|             return { | ||||
|                 text: domainObject.getModel().name | ||||
|             }; | ||||
|         }; | ||||
| 
 | ||||
|         return NameColumn; | ||||
|     } | ||||
| ); | ||||
|     return EditableLinkPolicy; | ||||
| }); | ||||
| @@ -21,29 +21,31 @@ | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
| 
 | ||||
| define([ | ||||
|     "./src/CachingPersistenceDecorator", | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     CachingPersistenceDecorator, | ||||
|     legacyRegistry | ||||
| ) { | ||||
| define([], function () { | ||||
|     "use strict"; | ||||
| 
 | ||||
|     legacyRegistry.register("platform/persistence/cache", { | ||||
|         "name": "Persistence cache", | ||||
|         "description": "Cache to improve availability of persisted objects.", | ||||
|         "extensions": { | ||||
|             "components": [ | ||||
|                 { | ||||
|                     "provides": "persistenceService", | ||||
|                     "type": "decorator", | ||||
|                     "implementation": CachingPersistenceDecorator, | ||||
|                     "depends": [ | ||||
|                         "PERSISTENCE_SPACE" | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|     /** | ||||
|      * 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,8 @@ p { | ||||
| 	margin-bottom: $interiorMarginLg; | ||||
| } | ||||
|  | ||||
| ol, ul { 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; | ||||
| }); | ||||
| @@ -44,10 +44,15 @@ define( | ||||
|          * @param {IdentifierService} identifierService service to generate | ||||
|          *        new identifiers | ||||
|          */ | ||||
|         function Instantiate(capabilityService, identifierService) { | ||||
|         function Instantiate( | ||||
|             capabilityService, | ||||
|             identifierService, | ||||
|             cacheService | ||||
|         ) { | ||||
|             return function (model, id) { | ||||
|                 var capabilities = capabilityService.getCapabilities(model); | ||||
|                 id = id || identifierService.generate(); | ||||
|                 cacheService.put(id, model); | ||||
|                 return new DomainObjectImpl(id, model, capabilities); | ||||
|             }; | ||||
|         } | ||||
|   | ||||
| @@ -19,8 +19,7 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,Promise,describe,xdescribe,it,expect,beforeEach,waitsFor, | ||||
|  jasmine*/ | ||||
| /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ | ||||
|  | ||||
| /** | ||||
|  * ActionCapabilitySpec. Created by vwoeltje on 11/6/14. | ||||
| @@ -29,8 +28,8 @@ define( | ||||
|     ["../../src/actions/ActionCapability"], | ||||
|     function (ActionCapability) { | ||||
|         "use strict"; | ||||
|         //TODO: Disabled for NEM beta | ||||
|         xdescribe("The action capability", function () { | ||||
|  | ||||
|         describe("The action capability", function () { | ||||
|             var mockQ, | ||||
|                 mockAction, | ||||
|                 mockActionService, | ||||
|   | ||||
| @@ -31,6 +31,7 @@ define( | ||||
|                 mockIdentifierService, | ||||
|                 mockInstantiate, | ||||
|                 mockIdentifier, | ||||
|                 mockNow, | ||||
|                 mockDomainObject, | ||||
|                 instantiation; | ||||
|  | ||||
| @@ -57,9 +58,13 @@ define( | ||||
|                 mockIdentifierService.parse.andReturn(mockIdentifier); | ||||
|                 mockIdentifierService.generate.andReturn("some-id"); | ||||
|  | ||||
|                 mockNow = jasmine.createSpy(); | ||||
|                 mockNow.andReturn(1234321); | ||||
|  | ||||
|                 instantiation = new InstantiationCapability( | ||||
|                     mockInjector, | ||||
|                     mockIdentifierService, | ||||
|                     mockNow, | ||||
|                     mockDomainObject | ||||
|                 ); | ||||
|             }); | ||||
| @@ -81,7 +86,10 @@ define( | ||||
|                 expect(instantiation.instantiate(testModel)) | ||||
|                     .toBe(mockDomainObject); | ||||
|                 expect(mockInstantiate) | ||||
|                     .toHaveBeenCalledWith(testModel, jasmine.any(String)); | ||||
|                     .toHaveBeenCalledWith({ | ||||
|                         someKey: "some value", | ||||
|                         modified: mockNow() | ||||
|                     }, jasmine.any(String)); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|   | ||||
| @@ -36,6 +36,7 @@ define( | ||||
|                 mockDomainObject, | ||||
|                 mockIdentifier, | ||||
|                 mockNofificationService, | ||||
|                 mockCacheService, | ||||
|                 mockQ, | ||||
|                 id = "object id", | ||||
|                 model, | ||||
| @@ -81,6 +82,10 @@ define( | ||||
|                     "notificationService", | ||||
|                     ["error"] | ||||
|                 ); | ||||
|                 mockCacheService = jasmine.createSpyObj( | ||||
|                     "cacheService", | ||||
|                     [ "get", "put", "remove", "all" ] | ||||
|                 ); | ||||
|  | ||||
|                 mockDomainObject = { | ||||
|                     getId: function () { return id; }, | ||||
| @@ -96,6 +101,7 @@ define( | ||||
|                 mockIdentifierService.parse.andReturn(mockIdentifier); | ||||
|                 mockIdentifier.getSpace.andReturn(SPACE); | ||||
|                 persistence = new PersistenceCapability( | ||||
|                     mockCacheService, | ||||
|                     mockPersistenceService, | ||||
|                     mockIdentifierService, | ||||
|                     mockNofificationService, | ||||
| @@ -171,6 +177,7 @@ define( | ||||
|                     expect(mockNofificationService.error).not.toHaveBeenCalled(); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("unsuccessful persistence", function() { | ||||
|                 var sadPromise = { | ||||
|                         then: function(callback){ | ||||
|   | ||||
| @@ -22,8 +22,11 @@ | ||||
| /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/models/CachingModelDecorator"], | ||||
|     function (CachingModelDecorator) { | ||||
|     [ | ||||
|         "../../src/models/CachingModelDecorator", | ||||
|         "../../src/models/ModelCacheService" | ||||
|     ], | ||||
|     function (CachingModelDecorator, ModelCacheService) { | ||||
|         "use strict"; | ||||
|  | ||||
|         describe("The caching model decorator", function () { | ||||
| @@ -67,7 +70,10 @@ define( | ||||
|                     b: { someOtherKey: "some other value" } | ||||
|                 }; | ||||
|                 mockModelService.getModels.andReturn(asPromise(testModels)); | ||||
|                 decorator = new CachingModelDecorator(mockModelService); | ||||
|                 decorator = new CachingModelDecorator( | ||||
|                     new ModelCacheService(), | ||||
|                     mockModelService | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("loads models from its wrapped model service", function () { | ||||
| @@ -150,4 +156,4 @@ define( | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| ); | ||||
|   | ||||
							
								
								
									
										69
									
								
								platform/core/test/models/ModelCacheServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								platform/core/test/models/ModelCacheServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ | ||||
|  | ||||
| define(['../../src/models/ModelCacheService'], function (ModelCacheService) { | ||||
|     'use strict'; | ||||
|     describe("ModelCacheService", function () { | ||||
|         var testIds, | ||||
|             testModels, | ||||
|             cacheService; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             testIds = [ 'a', 'b', 'c', 'd' ]; | ||||
|             testModels = testIds.reduce(function (models, id) { | ||||
|                 models[id] = { someKey: "some value for " + id }; | ||||
|                 return models; | ||||
|             }, {}); | ||||
|             cacheService = new ModelCacheService(); | ||||
|         }); | ||||
|  | ||||
|         describe("when populated with models", function () { | ||||
|             beforeEach(function () { | ||||
|                 testIds.forEach(function (id) { | ||||
|                     cacheService.put(id, testModels[id]); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("indicates that it has these models", function () { | ||||
|                 testIds.forEach(function (id) { | ||||
|                     expect(cacheService.has(id)).toBe(true); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("provides all of these models", function () { | ||||
|                 expect(cacheService.all()).toEqual(testModels); | ||||
|             }); | ||||
|  | ||||
|             it("allows models to be retrieved", function () { | ||||
|                 testIds.forEach(function (id) { | ||||
|                     expect(cacheService.get(id)).toEqual(testModels[id]); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("allows models to be removed", function () { | ||||
|                 cacheService.remove('a'); | ||||
|                 expect(cacheService.has('a')).toBe(false); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -32,8 +32,7 @@ define( | ||||
|                 mockIdentifierService, | ||||
|                 mockCapabilityConstructor, | ||||
|                 mockCapabilityInstance, | ||||
|                 mockCapabilities, | ||||
|                 mockIdentifier, | ||||
|                 mockCacheService, | ||||
|                 idCounter, | ||||
|                 testModel, | ||||
|                 instantiate, | ||||
| @@ -62,11 +61,17 @@ define( | ||||
|                             "some-id-" + (idCounter += 1); | ||||
|                 }); | ||||
|  | ||||
|                 mockCacheService = jasmine.createSpyObj( | ||||
|                     'cacheService', | ||||
|                     [ 'get', 'put', 'remove', 'all' ] | ||||
|                 ); | ||||
|  | ||||
|                 testModel = { someKey: "some value" }; | ||||
|  | ||||
|                 instantiate = new Instantiate( | ||||
|                     mockCapabilityService, | ||||
|                     mockIdentifierService | ||||
|                     mockIdentifierService, | ||||
|                     mockCacheService | ||||
|                 ); | ||||
|                 domainObject = instantiate(testModel); | ||||
|             }); | ||||
| @@ -92,6 +97,13 @@ define( | ||||
|                 expect(instantiate(testModel).getId()) | ||||
|                     .not.toEqual(domainObject.getId()); | ||||
|             }); | ||||
|  | ||||
|             it("caches the instantiated model", function () { | ||||
|                 expect(mockCacheService.put).toHaveBeenCalledWith( | ||||
|                     domainObject.getId(), | ||||
|                     testModel | ||||
|                 ); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -29,7 +29,9 @@ define([ | ||||
|     "./src/actions/SetPrimaryLocationAction", | ||||
|     "./src/services/LocatingCreationDecorator", | ||||
|     "./src/services/LocatingObjectDecorator", | ||||
|     "./src/policies/CopyPolicy", | ||||
|     "./src/policies/CrossSpacePolicy", | ||||
|     "./src/policies/MovePolicy", | ||||
|     "./src/capabilities/LocationCapability", | ||||
|     "./src/services/MoveService", | ||||
|     "./src/services/LinkService", | ||||
| @@ -44,7 +46,9 @@ define([ | ||||
|     SetPrimaryLocationAction, | ||||
|     LocatingCreationDecorator, | ||||
|     LocatingObjectDecorator, | ||||
|     CopyPolicy, | ||||
|     CrossSpacePolicy, | ||||
|     MovePolicy, | ||||
|     LocationCapability, | ||||
|     MoveService, | ||||
|     LinkService, | ||||
| @@ -140,6 +144,14 @@ define([ | ||||
|                 { | ||||
|                     "category": "action", | ||||
|                     "implementation": CrossSpacePolicy | ||||
|                 }, | ||||
|                 { | ||||
|                     "category": "action", | ||||
|                     "implementation": CopyPolicy | ||||
|                 }, | ||||
|                 { | ||||
|                     "category": "action", | ||||
|                     "implementation": MovePolicy | ||||
|                 } | ||||
|             ], | ||||
|             "capabilities": [ | ||||
|   | ||||
| @@ -19,33 +19,39 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| .select { | ||||
| 	@include btnSubtle($colorSelectBg); | ||||
|     @if $shdwBtns != none { | ||||
| 	    margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
 | ||||
| 
 | ||||
| /*global define */ | ||||
| define([], function () { | ||||
|     'use strict'; | ||||
| 
 | ||||
|     /** | ||||
|      * Disallow duplication when the object to be duplicated is not | ||||
|      * creatable. | ||||
|      * @constructor | ||||
|      * @implements {Policy} | ||||
|      * @memberof platform/entanglement | ||||
|      */ | ||||
|     function CopyPolicy() { | ||||
|     } | ||||
| 	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
 | ||||
| 
 | ||||
|     function allowCreation(domainObject) { | ||||
|         var type = domainObject && domainObject.getCapability('type'); | ||||
|         return !!(type && type.hasFeature('creation')); | ||||
|     } | ||||
| 
 | ||||
|     function selectedObject(context) { | ||||
|         return context.selectedObject || context.domainObject; | ||||
|     } | ||||
| 
 | ||||
|     CopyPolicy.prototype.allow = function (action, context) { | ||||
|         var key = action.getMetadata().key; | ||||
| 
 | ||||
|         if (key === 'copy') { | ||||
|             return allowCreation(selectedObject(context)); | ||||
|         } | ||||
| 	} | ||||
| 	&:after { | ||||
| 		@include contextArrow(); | ||||
| 		pointer-events: none; | ||||
| 		color: rgba($colorSelectFg, percentToDecimal($contrastInvokeMenuPercent)); | ||||
| 		position: absolute; | ||||
| 		right: $interiorMargin; top: 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|         return true; | ||||
|     }; | ||||
| 
 | ||||
|     return CopyPolicy; | ||||
| }); | ||||
							
								
								
									
										63
									
								
								platform/entanglement/src/policies/MovePolicy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								platform/entanglement/src/policies/MovePolicy.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /*global define */ | ||||
| define([], function () { | ||||
|     'use strict'; | ||||
|  | ||||
|     /** | ||||
|      * Disallow moves when either the parent or the child are not | ||||
|      * modifiable by users. | ||||
|      * @constructor | ||||
|      * @implements {Policy} | ||||
|      * @memberof platform/entanglement | ||||
|      */ | ||||
|     function MovePolicy() { | ||||
|     } | ||||
|  | ||||
|     function parentOf(domainObject) { | ||||
|         var context = domainObject.getCapability('context'); | ||||
|         return context && context.getParent(); | ||||
|     } | ||||
|  | ||||
|     function allowMutation(domainObject) { | ||||
|         var type = domainObject && domainObject.getCapability('type'); | ||||
|         return !!(type && type.hasFeature('creation')); | ||||
|     } | ||||
|  | ||||
|     function selectedObject(context) { | ||||
|         return context.selectedObject || context.domainObject; | ||||
|     } | ||||
|  | ||||
|     MovePolicy.prototype.allow = function (action, context) { | ||||
|         var key = action.getMetadata().key; | ||||
|  | ||||
|         if (key === 'move') { | ||||
|             return allowMutation(selectedObject(context)) && | ||||
|                 allowMutation(parentOf(selectedObject(context))); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }; | ||||
|  | ||||
|     return MovePolicy; | ||||
| }); | ||||
							
								
								
									
										94
									
								
								platform/entanglement/test/policies/CopyPolicySpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								platform/entanglement/test/policies/CopyPolicySpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /***************************************************************************** | ||||
|  * 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,it,jasmine,expect,spyOn */ | ||||
| define([ | ||||
|     '../../src/policies/CopyPolicy', | ||||
|     '../DomainObjectFactory' | ||||
| ], function (CopyPolicy, domainObjectFactory) { | ||||
|     'use strict'; | ||||
|  | ||||
|     describe("CopyPolicy", function () { | ||||
|         var testMetadata, | ||||
|             testContext, | ||||
|             mockDomainObject, | ||||
|             mockType, | ||||
|             mockAction, | ||||
|             policy; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             mockType = | ||||
|                 jasmine.createSpyObj('type', ['hasFeature']); | ||||
|  | ||||
|             testMetadata = {}; | ||||
|  | ||||
|             mockDomainObject = domainObjectFactory({ | ||||
|                 capabilities: { type: mockType } | ||||
|             }); | ||||
|  | ||||
|             mockType.hasFeature.andCallFake(function (feature) { | ||||
|                 return feature === 'creation'; | ||||
|             }); | ||||
|  | ||||
|             mockAction = jasmine.createSpyObj('action', ['getMetadata']); | ||||
|             mockAction.getMetadata.andReturn(testMetadata); | ||||
|  | ||||
|             testContext = { domainObject: mockDomainObject }; | ||||
|  | ||||
|             policy = new CopyPolicy(); | ||||
|         }); | ||||
|  | ||||
|         describe("for copy actions", function () { | ||||
|             beforeEach(function () { | ||||
|                 testMetadata.key = 'copy'; | ||||
|             }); | ||||
|  | ||||
|             describe("when an object is non-creatable", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockType.hasFeature.andReturn(false); | ||||
|                 }); | ||||
|  | ||||
|                 it("disallows the action", function () { | ||||
|                     expect(policy.allow(mockAction, testContext)).toBe(false); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when an object is creatable", function () { | ||||
|                 it("allows the action", function () { | ||||
|                     expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("for other actions", function () { | ||||
|             beforeEach(function () { | ||||
|                 testMetadata.key = 'foo'; | ||||
|             }); | ||||
|  | ||||
|             it("simply allows the action", function () { | ||||
|                 expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|                 mockType.hasFeature.andReturn(false); | ||||
|                 expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										127
									
								
								platform/entanglement/test/policies/MovePolicySpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								platform/entanglement/test/policies/MovePolicySpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| /***************************************************************************** | ||||
|  * 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,it,jasmine,expect,spyOn */ | ||||
| define([ | ||||
|     '../../src/policies/MovePolicy', | ||||
|     '../DomainObjectFactory' | ||||
| ], function (MovePolicy, domainObjectFactory) { | ||||
|     'use strict'; | ||||
|  | ||||
|     describe("MovePolicy", function () { | ||||
|         var testMetadata, | ||||
|             testContext, | ||||
|             mockDomainObject, | ||||
|             mockParent, | ||||
|             mockParentType, | ||||
|             mockType, | ||||
|             mockAction, | ||||
|             policy; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             var mockContextCapability = | ||||
|                     jasmine.createSpyObj('context', ['getParent']); | ||||
|  | ||||
|             mockType = | ||||
|                 jasmine.createSpyObj('type', ['hasFeature']); | ||||
|             mockParentType = | ||||
|                 jasmine.createSpyObj('parent-type', ['hasFeature']); | ||||
|  | ||||
|  | ||||
|             testMetadata = {}; | ||||
|  | ||||
|             mockDomainObject = domainObjectFactory({ | ||||
|                 capabilities: { | ||||
|                     context: mockContextCapability, | ||||
|                     type: mockType | ||||
|                 } | ||||
|             }); | ||||
|             mockParent = domainObjectFactory({ | ||||
|                 capabilities: { | ||||
|                     type: mockParentType | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             mockContextCapability.getParent.andReturn(mockParent); | ||||
|  | ||||
|             mockType.hasFeature.andCallFake(function (feature) { | ||||
|                 return feature === 'creation'; | ||||
|             }); | ||||
|             mockParentType.hasFeature.andCallFake(function (feature) { | ||||
|                 return feature === 'creation'; | ||||
|             }); | ||||
|  | ||||
|             mockAction = jasmine.createSpyObj('action', ['getMetadata']); | ||||
|             mockAction.getMetadata.andReturn(testMetadata); | ||||
|  | ||||
|             testContext = { domainObject: mockDomainObject }; | ||||
|  | ||||
|             policy = new MovePolicy(); | ||||
|         }); | ||||
|  | ||||
|         describe("for move actions", function () { | ||||
|             beforeEach(function () { | ||||
|                 testMetadata.key = 'move'; | ||||
|             }); | ||||
|  | ||||
|             describe("when an object is non-modifiable", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockType.hasFeature.andReturn(false); | ||||
|                 }); | ||||
|  | ||||
|                 it("disallows the action", function () { | ||||
|                     expect(policy.allow(mockAction, testContext)).toBe(false); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when a parent is non-modifiable", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockParentType.hasFeature.andReturn(false); | ||||
|                 }); | ||||
|  | ||||
|                 it("disallows the action", function () { | ||||
|                     expect(policy.allow(mockAction, testContext)).toBe(false); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("when an object and its parent are modifiable", function () { | ||||
|                 it("allows the action", function () { | ||||
|                     expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("for other actions", function () { | ||||
|             beforeEach(function () { | ||||
|                 testMetadata.key = 'foo'; | ||||
|             }); | ||||
|  | ||||
|             it("simply allows the action", function () { | ||||
|                 expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|                 mockType.hasFeature.andReturn(false); | ||||
|                 expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|                 mockParentType.hasFeature.andReturn(false); | ||||
|                 expect(policy.allow(mockAction, testContext)).toBe(true); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -157,7 +157,9 @@ define([ | ||||
|                 { | ||||
|                     "key": "clock", | ||||
|                     "name": "Clock", | ||||
|                     "glyph": "C", | ||||
|                     "glyph": "\u0043", | ||||
|                     "description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.", | ||||
|                     "priority": 101, | ||||
|                     "features": [ | ||||
|                         "creation" | ||||
|                     ], | ||||
| @@ -182,7 +184,8 @@ define([ | ||||
|                                             "value": "hh:mm:ss", | ||||
|                                             "name": "hh:mm:ss" | ||||
|                                         } | ||||
|                                     ] | ||||
|                                     ], | ||||
|                                     "cssclass": "l-inline" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "control": "select", | ||||
| @@ -195,7 +198,8 @@ define([ | ||||
|                                             "value": "clock24", | ||||
|                                             "name": "24hr" | ||||
|                                         } | ||||
|                                     ] | ||||
|                                     ], | ||||
|                                     "cssclass": "l-inline" | ||||
|                                 } | ||||
|                             ] | ||||
|                         } | ||||
| @@ -210,7 +214,9 @@ define([ | ||||
|                 { | ||||
|                     "key": "timer", | ||||
|                     "name": "Timer", | ||||
|                     "glyph": "õ", | ||||
|                     "glyph": "\u00f5", | ||||
|                     "description": "A timer that counts up or down to a datetime. Timers can be started, stopped and reset whenever needed, and support a variety of display formats. Each Timer displays the same value to all users. Timers can be added to Display Layouts.", | ||||
|                     "priority": 100, | ||||
|                     "features": [ | ||||
|                         "creation" | ||||
|                     ], | ||||
| @@ -223,6 +229,7 @@ define([ | ||||
|                         { | ||||
|                             "key": "timerFormat", | ||||
|                             "control": "select", | ||||
|                             "name": "Display Format", | ||||
|                             "options": [ | ||||
|                                 { | ||||
|                                     "value": "long", | ||||
|   | ||||
| @@ -6,6 +6,6 @@ | ||||
|              ng-model='ngModel' | ||||
|              field="'domain'" | ||||
|              options="ngModel.options" | ||||
|              style="position: absolute; right: 0px; bottom: 46px;" | ||||
|              class="l-time-domain-selector" | ||||
|              > | ||||
| </mct-control> | ||||
|   | ||||
| @@ -1,83 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     "./src/EventListController", | ||||
|     "./src/directives/MCTDataTable", | ||||
|     "./src/policies/MessagesViewPolicy", | ||||
|     "text!./res/templates/messages.html", | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     EventListController, | ||||
|     MCTDataTable, | ||||
|     MessagesViewPolicy, | ||||
|     messagesTemplate, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     "use strict"; | ||||
|  | ||||
|     legacyRegistry.register("platform/features/events", { | ||||
|         "name": "Event Messages", | ||||
|         "description": "List of time-ordered event messages", | ||||
|         "extensions": { | ||||
|             "views": [ | ||||
|                 { | ||||
|                     "key": "messages", | ||||
|                     "name": "Messages", | ||||
|                     "glyph": "5", | ||||
|                     "description": "Scrolling list of messages.", | ||||
|                     "template": messagesTemplate, | ||||
|                     "needs": [ | ||||
|                         "telemetry" | ||||
|                     ], | ||||
|                     "delegation": true | ||||
|                 } | ||||
|             ], | ||||
|             "controllers": [ | ||||
|                 { | ||||
|                     "key": "EventListController", | ||||
|                     "implementation": EventListController, | ||||
|                     "depends": [ | ||||
|                         "$scope", | ||||
|                         "telemetryFormatter" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|             "directives": [ | ||||
|                 { | ||||
|                     "key": "mctDataTable", | ||||
|                     "implementation": MCTDataTable, | ||||
|                     "depends": [ | ||||
|                         "$window" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|             "policies": [ | ||||
|                 { | ||||
|                     "category": "view", | ||||
|                     "implementation": MessagesViewPolicy | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
| @@ -1,37 +0,0 @@ | ||||
| <!-- | ||||
|  Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  as represented by the Administrator of the National Aeronautics and Space | ||||
|  Administration. All rights reserved. | ||||
|  | ||||
|  Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  "License"); you may not use this file except in compliance with the License. | ||||
|  You may obtain a copy of the License at | ||||
|  http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  | ||||
|  Unless required by applicable law or agreed to in writing, software | ||||
|  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  License for the specific language governing permissions and limitations | ||||
|  under the License. | ||||
|  | ||||
|  Open MCT Web includes source code licensed under additional open source | ||||
|  licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <table class="tabular"> | ||||
|     <thead> | ||||
|         <tr> | ||||
|             <th ng-repeat="header in headers"> | ||||
|                 {{header}} | ||||
|             </th> | ||||
|         </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr ng-repeat="row in rows"> | ||||
|             <td ng-repeat="cell in row"> | ||||
|                 {{cell}} | ||||
|             </td> | ||||
|         </tr> | ||||
|     </tbody> | ||||
| </table> | ||||
| @@ -1,29 +0,0 @@ | ||||
| <!-- | ||||
|  Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  as represented by the Administrator of the National Aeronautics and Space | ||||
|  Administration. All rights reserved. | ||||
|  | ||||
|  Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  "License"); you may not use this file except in compliance with the License. | ||||
|  You may obtain a copy of the License at | ||||
|  http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  | ||||
|  Unless required by applicable law or agreed to in writing, software | ||||
|  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  License for the specific language governing permissions and limitations | ||||
|  under the License. | ||||
|  | ||||
|  Open MCT Web includes source code licensed under additional open source | ||||
|  licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="w1" ng-controller="TelemetryController as telemetry"> | ||||
| 	<div class="w2" | ||||
| 	     ng-controller="EventListController"> | ||||
|         <mct-data-table headers="headers" rows="rows" ascending-scroll="true"></mct-data-table> | ||||
| 	</div> | ||||
| </div> | ||||
|  | ||||
|  | ||||
| @@ -1,63 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define,moment*/ | ||||
|  | ||||
| /** | ||||
|  * Module defining DomainColumn. Created by vwoeltje on 11/18/14. | ||||
|  */ | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         /** | ||||
|          * A column which will report telemetry domain values | ||||
|          * (typically, timestamps.) Used by the ScrollingListController. | ||||
|          * | ||||
|          * @memberof platform/features/events | ||||
|          * @constructor | ||||
|          * @implements {platform/features/events.EventsColumn} | ||||
|          * @param domainMetadata an object with the machine- and human- | ||||
|          *        readable names for this domain (in `key` and `name` | ||||
|          *        fields, respectively.) | ||||
|          * @param {TelemetryFormatter} telemetryFormatter the telemetry | ||||
|          *        formatting service, for making values human-readable. | ||||
|          */ | ||||
|         function DomainColumn(domainMetadata, telemetryFormatter) { | ||||
|             this.domainMetadata = domainMetadata; | ||||
|             this.telemetryFormatter = telemetryFormatter; | ||||
|         } | ||||
|  | ||||
|         DomainColumn.prototype.getTitle = function () { | ||||
|             return this.domainMetadata.name; | ||||
|         }; | ||||
|  | ||||
|         DomainColumn.prototype.getValue = function (domainObject, data, index) { | ||||
|             var domainKey = this.domainMetadata.key; | ||||
|             return this.telemetryFormatter.formatDomainValue( | ||||
|                 data.getDomainValue(index, domainKey) | ||||
|             ); | ||||
|         }; | ||||
|  | ||||
|         return DomainColumn; | ||||
|     } | ||||
| ); | ||||
| @@ -1,164 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
|  | ||||
| /* | ||||
|  * Module defining EventListController.  | ||||
|  * Created by chacskaylo on 06/18/2015.  | ||||
|  * Modified by shale on 06/23/2015. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * This bundle implements the "Events" view of string telemetry. | ||||
|  * @namespace platform/features/events | ||||
|  */ | ||||
| define( | ||||
|     ["./DomainColumn", "./RangeColumn", "./EventListPopulator"], | ||||
|     function (DomainColumn, RangeColumn, EventListPopulator) { | ||||
|         "use strict"; | ||||
|          | ||||
|         var ROW_COUNT = 100; | ||||
|  | ||||
|         /** | ||||
|          * The EventListController is responsible for populating | ||||
|          * the contents of the event list view. | ||||
|          * @memberof platform/features/events | ||||
|          * @constructor | ||||
|          */ | ||||
|         function EventListController($scope, formatter) { | ||||
|             var populator; | ||||
|              | ||||
|             // Get a set of populated, ready-to-display rows for the | ||||
|             // latest data values. | ||||
|             function getRows(telemetry) { | ||||
|                 var datas = telemetry.getResponse(), | ||||
|                     objects = telemetry.getTelemetryObjects(); | ||||
|                  | ||||
|                 return populator.getRows(datas, objects, ROW_COUNT); | ||||
|             } | ||||
|  | ||||
|             // Update the contents | ||||
|             function updateRows() { | ||||
|                 var telemetry = $scope.telemetry; | ||||
|                 $scope.rows = telemetry ? getRows(telemetry) : []; | ||||
|             } | ||||
|  | ||||
|             // Set up columns based on telemetry metadata. This will | ||||
|             // include one column for each domain and range type, as | ||||
|             // well as a column for the domain object name. | ||||
|             function setupColumns(telemetry) { | ||||
|                 var domainKeys = {}, | ||||
|                     rangeKeys = {}, | ||||
|                     columns = [], | ||||
|                     metadata; | ||||
|  | ||||
|                 // Add a domain to the set of columns, if a domain | ||||
|                 // with the same key has not yet been inclued. | ||||
|                 function addDomain(domain) { | ||||
|                     var key = domain.key; | ||||
|                     if (key && !domainKeys[key]) { | ||||
|                         domainKeys[key] = true; | ||||
|                         columns.push(new DomainColumn(domain, formatter)); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Add a range col to the set of columns, if a range | ||||
|                 // with the same key has not yet been included. | ||||
|                 function addRange(range) { | ||||
|                     var key = range.key; | ||||
|                     if (key && !rangeKeys[key]) { | ||||
|                         rangeKeys[key] = true; | ||||
|                         columns.push(new RangeColumn(range, formatter)); | ||||
|                     } | ||||
|                 } | ||||
|                  | ||||
|                 // We cannot proceed if the telemetry controller | ||||
|                 // is not available; clear all rows/columns. | ||||
|                 if (!telemetry) { | ||||
|                     columns = []; | ||||
|                     $scope.rows = []; | ||||
|                     $scope.headers = []; | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 // Add domain, range, event msg columns | ||||
|                 metadata = telemetry.getMetadata(); | ||||
|                 (metadata || []).forEach(function (metadata) { | ||||
|                     (metadata.domains || []).forEach(addDomain); | ||||
|                 }); | ||||
|                 (metadata || []).forEach(function (metadata) { | ||||
|                     (metadata.ranges || []).forEach(addRange); | ||||
|                 }); | ||||
|                  | ||||
|                 // Add default domain and range columns if none | ||||
|                 // were described in metadata. | ||||
|                 if (Object.keys(domainKeys).length < 1) { | ||||
|                     columns.push(new DomainColumn({name: "Time"}, formatter)); | ||||
|                 } | ||||
|                 if (Object.keys(rangeKeys).length < 1) { | ||||
|                     columns.push(new RangeColumn({name: "Message"}, formatter)); | ||||
|                 } | ||||
|                  | ||||
|                 // We have all columns now; use them to initializer | ||||
|                 // the populator, which will use them to generate | ||||
|                 // actual rows and headers. | ||||
|                 populator = new EventListPopulator(columns); | ||||
|                  | ||||
|                 // Initialize headers | ||||
|                 $scope.headers = populator.getHeaders(); | ||||
|  | ||||
|                 // Fill in the contents of the rows. | ||||
|                 updateRows(); | ||||
|             } | ||||
|  | ||||
|             $scope.$on("telemetryUpdate", updateRows); | ||||
|             $scope.$watch("telemetry", setupColumns); | ||||
|         } | ||||
|  | ||||
|         return EventListController; | ||||
|  | ||||
|         /** | ||||
|          * A description of how to display a certain column of data in an | ||||
|          * Events view. | ||||
|          * @interface platform/features/events.EventColumn | ||||
|          * @private | ||||
|          */ | ||||
|         /** | ||||
|          * Get the title to display in this column's header. | ||||
|          * @returns {string} the title to display | ||||
|          * @method platform/features/events.EventColumn#getTitle | ||||
|          */ | ||||
|         /** | ||||
|          * Get the text to display inside a row under this | ||||
|          * column. | ||||
|          * @param {DomainObject} domainObject the domain object associated | ||||
|          *        with this row | ||||
|          * @param {TelemetrySeries} series the telemetry data associated | ||||
|          *        with this row | ||||
|          * @param {number} index the index of the telemetry datum associated | ||||
|          *        with this row | ||||
|          * @returns {string} the text to display | ||||
|          * @method platform/features/events.EventColumn#getValue | ||||
|          */ | ||||
|     } | ||||
| ); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user